wasm32
queitens half
signalling NaNs in some situations when passing/returning them (original) (raw)
The following IR (compiler explorer):
target triple = "wasm32-unknown-wasi"
define half @from_bits(i16 %x) { %res = bitcast i16 %x to half ret half %res }
define i16 @to_bits(half %x) { %res = bitcast half %x to i16 ret i16 %res }
define i16 @roundtrip() { %h = call half @from_bits(i16 64513) ; A bitpattern of a signalling NaN %res = call i16 @to_bits(half %h) ret i16 %res }
Is compiled into the following WASM:
from_bits: # @from_bits local.get 0 call __extendhfsf2 end_function to_bits: # @to_bits local.get 0 call __truncsfhf2 i32.const 65535 i32.and end_function roundtrip: # @roundtrip i32.const 64513 call from_bits call to_bits end_function
rountrip
should return 64513
(0xfc01), as all from_bits
and to_bits
do is bitcast to and from half
. However, on WASM it instead returns 65025
(0xfe01), as __extendhfsf2
and __truncsfhf2
both quieten signalling NaNs.
This Rust program, when compiled with rustc 1.81.0-nightly (3cb521a43 2024-06-22)
with rustc --target wasm32-wasip1 code.rs
and run with wasmtime
, demonstrates the issue.
#![feature(f16)]
fn main() { assert_eq!(f16::from_bits(0xfc01).to_bits(), 0xfc01); }
The assertion should succeed, but on WASM it fails.
This solution to this is probably to either:
- Change the
half
ABI to be passed and returned in the low bits of a WASMi32
, the same as a LLVMi16
. This would match the ABI ofhalf
in the__extendhfsf2
and__truncsfhf2
builtins. I noticed that the ABI for 16-bit floats is not specified in the WASM Basic C ABI document, and Clang doesn't support_Float16
on WASM either, so this might be possible with regards to backwards compatibility concerns. - If that's not possible or desirable, convert
half
to and fromf32
losslessly with regards to NaN bits. This would mean either adding extra codegen around the relevant__extendhfsf2
and__truncsfhf2
calls to ensure that signalling NaNs don't get quietened, or adding new builtins that are the same as__extendhfsf2
and__truncsfhf2
but don't quieten signalling NaNs.