Skip to content

wasm32 queitens half signalling NaNs in some situations when passing/returning them #96438

Open
@beetrees

Description

@beetrees

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:

  1. Change the half ABI to be passed and returned in the low bits of a WASM i32, the same as a LLVM i16. This would match the ABI of half 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.
  2. If that's not possible or desirable, convert half to and from f32 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.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions