• Russ Cox's avatar
    math: avoid assumption of denormalized math mode in Sincos · 4a8cb4a4
    Russ Cox authored
    The extra-clever code in Sincos is trying to do
    
            if v&2 == 0 {
                    mask = 0xffffffffffffffff
            } else {
                    mask = 0
            }
    
    It does this by turning v&2 into a float64 X0 and then using
    
            MOVSD $0.0, X3
            CMPSD X0, X3, 0
    
    That CMPSD is defined to behave like:
    
            if X0 == X3 {
                    X3 = 0xffffffffffffffff
            } else {
                    X3 = 0
            }
    
    which gives the desired mask in X3. The goal in using the
    CMPSD was to avoid a conditional branch.
    
    This code fails when called from a PortAudio callback.
    In particular, the failure behavior is exactly as if the
    CMPSD always chose the 'true' execution.
    
    Notice that the comparison X0 == X3 is comparing as
    floating point values the 64-bit pattern v&2 and the actual
    floating point value zero. The only possible values for v&2
    are 0x0000000000000000 (floating point zero)
    and 0x0000000000000002 (floating point 1e-323, a denormal).
    If they are both comparing equal to zero, I conclude that
    in a PortAudio callback (whatever that means), the processor
    is running in "denormals are zero" mode.
    
    I confirmed this by placing the processor into that mode
    and running the test case in the bug; it produces the
    incorrect output reported in the bug.
    
    In general, if a Go program changes the floating point math
    modes to something other than what Go expects, the math
    library is not going to work exactly as intended, so we might
    be justified in not fixing this at all.
    
    However, it seems reasonable that the client code might
    have expected "denormals are zero" mode to only affect
    actual processing of denormals. This code has produced
    what is in effect a gratuitous denormal by being extra clever.
    There is nothing about the computation being requested
    that fundamentally requires a denormal.
    
    It is also easy to do this computation in integer math instead:
    
            mask = ((v&2)>>1)-1
    
    Do that.
    
    For the record, the other math tests that fail if you put the
    processor in "denormals are zero" mode are the tests for
    Frexp, Ilogb, Ldexp, Logb, Log2, and FloatMinMax, but all
    fail processing denormal inputs. Sincos was the only function
    for which that mode causes incorrect behavior on non-denormal inputs.
    
    The existing tests check that the new assembly is correct.
    There is no test for behavior in "denormals are zero" mode,
    because I don't want to add assembly to change that.
    
    Fixes #8623.
    
    LGTM=josharian
    R=golang-codereviews, josharian
    CC=golang-codereviews, iant, r
    https://golang.org/cl/151750043
    4a8cb4a4
Name
Last commit
Last update
..
big Loading commit data...
cmplx Loading commit data...
rand Loading commit data...
abs.go Loading commit data...
abs_386.s Loading commit data...
abs_amd64.s Loading commit data...
abs_amd64p32.s Loading commit data...
abs_arm.s Loading commit data...
acosh.go Loading commit data...
all_test.go Loading commit data...
asin.go Loading commit data...
asin_386.s Loading commit data...
asin_amd64.s Loading commit data...
asin_amd64p32.s Loading commit data...
asin_arm.s Loading commit data...
asinh.go Loading commit data...
atan.go Loading commit data...
atan2.go Loading commit data...
atan2_386.s Loading commit data...
atan2_amd64.s Loading commit data...
atan2_amd64p32.s Loading commit data...
atan2_arm.s Loading commit data...
atan_386.s Loading commit data...
atan_amd64.s Loading commit data...
atan_amd64p32.s Loading commit data...
atan_arm.s Loading commit data...
atanh.go Loading commit data...
bits.go Loading commit data...
cbrt.go Loading commit data...
const.go Loading commit data...
copysign.go Loading commit data...
dim.go Loading commit data...
dim_386.s Loading commit data...
dim_amd64.s Loading commit data...
dim_amd64p32.s Loading commit data...
dim_arm.s Loading commit data...
erf.go Loading commit data...
exp.go Loading commit data...
exp2_386.s Loading commit data...
exp2_amd64.s Loading commit data...
exp2_amd64p32.s Loading commit data...
exp2_arm.s Loading commit data...
exp_386.s Loading commit data...
exp_amd64.s Loading commit data...
exp_amd64p32.s Loading commit data...
exp_arm.s Loading commit data...
expm1.go Loading commit data...
expm1_386.s Loading commit data...
expm1_amd64.s Loading commit data...
expm1_amd64p32.s Loading commit data...
expm1_arm.s Loading commit data...
export_test.go Loading commit data...
floor.go Loading commit data...
floor_386.s Loading commit data...
floor_amd64.s Loading commit data...
floor_amd64p32.s Loading commit data...
floor_arm.s Loading commit data...
frexp.go Loading commit data...
frexp_386.s Loading commit data...
frexp_amd64.s Loading commit data...
frexp_amd64p32.s Loading commit data...
frexp_arm.s Loading commit data...
gamma.go Loading commit data...
hypot.go Loading commit data...
hypot_386.s Loading commit data...
hypot_amd64.s Loading commit data...
hypot_amd64p32.s Loading commit data...
hypot_arm.s Loading commit data...
j0.go Loading commit data...
j1.go Loading commit data...
jn.go Loading commit data...
ldexp.go Loading commit data...
ldexp_386.s Loading commit data...
ldexp_amd64.s Loading commit data...
ldexp_amd64p32.s Loading commit data...
ldexp_arm.s Loading commit data...
lgamma.go Loading commit data...
log.go Loading commit data...
log10.go Loading commit data...
log10_386.s Loading commit data...
log10_amd64.s Loading commit data...
log10_amd64p32.s Loading commit data...
log10_arm.s Loading commit data...
log1p.go Loading commit data...
log1p_386.s Loading commit data...
log1p_amd64.s Loading commit data...
log1p_amd64p32.s Loading commit data...
log1p_arm.s Loading commit data...
log_386.s Loading commit data...
log_amd64.s Loading commit data...
log_amd64p32.s Loading commit data...
log_arm.s Loading commit data...
logb.go Loading commit data...
mod.go Loading commit data...
mod_386.s Loading commit data...
mod_amd64.s Loading commit data...
mod_amd64p32.s Loading commit data...
mod_arm.s Loading commit data...
modf.go Loading commit data...
modf_386.s Loading commit data...
modf_amd64.s Loading commit data...
modf_amd64p32.s Loading commit data...
modf_arm.s Loading commit data...
nextafter.go Loading commit data...
pow.go Loading commit data...
pow10.go Loading commit data...
remainder.go Loading commit data...
remainder_386.s Loading commit data...
remainder_amd64.s Loading commit data...
remainder_amd64p32.s Loading commit data...
remainder_arm.s Loading commit data...
signbit.go Loading commit data...
sin.go Loading commit data...
sin_386.s Loading commit data...
sin_amd64.s Loading commit data...
sin_amd64p32.s Loading commit data...
sin_arm.s Loading commit data...
sincos.go Loading commit data...
sincos_386.s Loading commit data...
sincos_amd64.s Loading commit data...
sincos_amd64p32.s Loading commit data...
sincos_arm.s Loading commit data...
sinh.go Loading commit data...
sqrt.go Loading commit data...
sqrt_386.s Loading commit data...
sqrt_amd64.s Loading commit data...
sqrt_amd64p32.s Loading commit data...
sqrt_arm.s Loading commit data...
tan.go Loading commit data...
tan_386.s Loading commit data...
tan_amd64.s Loading commit data...
tan_amd64p32.s Loading commit data...
tan_arm.s Loading commit data...
tanh.go Loading commit data...
unsafe.go Loading commit data...