mirror of
https://github.com/hanzoai/wasm2go.git
synced 2026-10-30 15:06:48 +00:00
Go has no negative-zero literal: constant arithmetic is exact rational,
so `float64(-0)` is +0. Every emission path that rendered a finite float
constant as a decimal literal therefore collapsed f32.const/f64.const
-0.0 to +0, and the global-initializer path skipped -0 entirely behind
its `fv == 0` zero-value check. Found by go-spidermonkey's test262 run:
SpiderMonkey's CharsToNumber, fdlibm atan2 and Math.sumPrecise all
return the constant -0.0, so Number("-0") lost the sign of zero.
Route -0 through the same math.Float{32,64}frombits path NaN/Inf
already use (shared predicate floatNeedsBitsEmission), in goConstF32 /
goConstF64, floatInitExpr, and the global-initializer zero-skip. The
gcasm backend inherits the fix, since it compiles the generated Go.
Verified: new fixture cg_negzero.wat (constants direct, through calls,
global initializers, arithmetic identities, NaN payload) failed against
the wazero reference before the fix and passes after; `go test ./...`
exit 0.
Co-authored-by: Claude Fable 5 <[email protected]>
41 lines
1.8 KiB
WebAssembly Text Format
41 lines
1.8 KiB
WebAssembly Text Format
;; cg_negzero.wat — negative-zero float constants. Go has no -0.0 literal
|
|
;; (constant arithmetic is exact, so `float64(-0)` is +0): every constant
|
|
;; and global-initializer path must preserve the sign bit some other way.
|
|
;; Results are observed as raw bits via reinterpret so the fixture harness
|
|
;; compares them exactly.
|
|
(module
|
|
;; Constant emitted inside a function body (SSA OpConstF64/OpConstF32).
|
|
(func (export "f64_negzero_bits") (result i64)
|
|
(i64.reinterpret_f64 (f64.const -0)))
|
|
(func (export "f32_negzero_bits") (result i32)
|
|
(i32.reinterpret_f32 (f32.const -0)))
|
|
|
|
;; The same constants observed through a call, so a folded reinterpret
|
|
;; could not mask a broken literal.
|
|
(func $nz64 (result f64) (f64.const -0))
|
|
(func $nz32 (result f32) (f32.const -0))
|
|
(func (export "f64_negzero_call_bits") (result i64)
|
|
(i64.reinterpret_f64 (call $nz64)))
|
|
(func (export "f32_negzero_call_bits") (result i32)
|
|
(i32.reinterpret_f32 (call $nz32)))
|
|
|
|
;; Global initializers: -0 must not be treated as "zero value already
|
|
;; correct", and non-zero negatives must keep their sign.
|
|
(global $gnz64 f64 (f64.const -0))
|
|
(global $gnz32 f32 (f32.const -0))
|
|
(func (export "global_f64_negzero_bits") (result i64)
|
|
(i64.reinterpret_f64 (global.get $gnz64)))
|
|
(func (export "global_f32_negzero_bits") (result i32)
|
|
(i32.reinterpret_f32 (global.get $gnz32)))
|
|
|
|
;; -0 must survive arithmetic identities: x*1, x+(-0), min(-0,+0).
|
|
(func (export "f64_negzero_mul1_bits") (result i64)
|
|
(i64.reinterpret_f64 (f64.mul (f64.const -0) (f64.const 1))))
|
|
(func (export "f64_min_negzero_bits") (result i64)
|
|
(i64.reinterpret_f64 (f64.min (f64.const -0) (f64.const 0))))
|
|
|
|
;; NaN payload constants keep going through the frombits path.
|
|
(func (export "f64_nan_payload_bits") (result i64)
|
|
(i64.reinterpret_f64 (f64.const nan:0x123456)))
|
|
)
|