<table border="1" cellspacing="0" cellpadding="8">
    <tr>
        <th>Issue</th>
        <td>
            <a href=https://github.com/llvm/llvm-project/issues/96438>96438</a>
        </td>
    </tr>

    <tr>
        <th>Summary</th>
        <td>
            `wasm32` queitens `half` signalling NaNs when passing/returning them
        </td>
    </tr>

    <tr>
      <th>Labels</th>
      <td>
            new issue
      </td>
    </tr>

    <tr>
      <th>Assignees</th>
      <td>
      </td>
    </tr>

    <tr>
      <th>Reporter</th>
      <td>
          beetrees
      </td>
    </tr>
</table>

<pre>
    The following IR ([compiler explorer](https://godbolt.org/z/6cP4Mq9dz)):

```llvm
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:

```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.

```rust
#![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](https://github.com/WebAssembly/tool-conventions/blob/5b40a861b04716c9bbfe4b94489ef78a335ced6f/BasicCABI.md#data-representation), 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.
</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJycV01v4zgS_TX0pWBBoiTaPvhgJzA2wExjMTvYOQYUWbK4Q5FukoqT_vULUvJnnEX3GkY3QtXHq6pXjxb3Xu0N4prUW1I_z_gQOuvWDWJwiH7WWPmx_rNDaK3W9qjMHl7-AEKXpN4K2x-URgf4ftDWoSP1M6HLLoSDJ-WG0B2hu72VjdUhs25P6O4HoTsm_ln9_n0lfxC6it9yQ_Jnkp_-Zfn41fqtH48Cd3sMEJw6aARSPgOh9Mh9X9L5YP429mjmR-4VofQ6ksRWGYSO6xZIlbfO9q-NCp7QpSoYEFq_E7oCstiO9hCPHPqUoVFBcB_gZAnBpkgnU4dhipx8pryL5wcAUogqD_aU_eR3n_4hgLNxRKAKdjGOECZ4P4fA2cHI2MU4vgepu5RYcK2_bBqr6qJMvuUWNhHkgYeAzoBtgUPkEtc6suQb__awrhT-y5Z0kRA_WeGJKNcFv3iYWClBmWAh3FD3r82_fv-Kb5FQ49Gl6HIDhJa3jTijix9tBddZZOf4yW8fp2JfX_E9oJFd61t6-xyNfG0HI4KyZuK6vc98Ovk_8gY3GOHb7j6tKmkmrPEnb1bXZf3ZhBsJ0-lnoBcylWenx5-pjovDGPITiEStz2UAwBfNT48ftucz3IdsISyhSqBYDr6zg5aRdYMzQFg-QmJ51Lv8vRV5kSTrCbiHmJuwK16wHFLD2HliLAdpQfnzLgebTKJPtEt6wvIM_mGP-IYuRrYmkRRUAGV8QH7C4xOgOqf1BRBeAyIsv2XaBdANE1gOjQ0dfB8UBjR3O-uz6wb92SkPfww-wMHZveN9zHXs0Fy27KhCF3O4wQcBRbYssnxu1L4L-iPCLEVT04JXJdCcVvOczSmNoFl-5zqfTzo_KXtU9EMBwkrM3Lm9bjBnv2gYVI9xqvQJJPaRTo4H9GnvlfcDZg-XPaacjmhJaEHqbYs8DA4JXbYFiwjrGyltDfRcmXvpJPmKe48uvOL3GGf0Ljek3Fxr55k92UXzptFdiFVuf0bi4mU8plTWnDjrByEQZYzXDOGaRS1X-sFQvdVDCpA0UvlI04OzDW_0RzxDFTp0d1pZZPDUcbPH1N4Lg2GzfYlODcIhQpPjqBJvkxAne22PcRP8eFUkfITlqqTT_KKN5z2m7YLffvv3-LxgaUcS6mMqtudBdMk85rXtNZIp168sw6B0UMZn8ALGBiVQQuh4OCdorYOCzZvYTG15SL0yNoA_oFCtulRI6m0qa8u9EvCUvKUVQ48mPPxtpEI3NJmwPaG7v7DZeI99oz8I3QVr9VxY84YmjskTumu0bQjd1U2V8yUrmrxaFEysmqbFqllV1XKF7WLJy7IWKFlL6C7heNpsX7JeElpKHvjc4cGhRxN4EsdJPoyEJ83NHqRFbwhdBPDD4WBdSC3bxbrTIM7UmghCn8BPDOrj0icOWO9Vo3FcVId77qRP_ODi72P6I8oHD6pRWoUPENYIdOZEU5rBS5tmQOhibPU5pHUg0SvHG40xeWrRiPJEgTuVbRPBQFvvNXqvPz7B-sa_JWLekgy5mYoELmWUR3wPjidB2qMBni60NHeHGt-4Cb9Cu3h5pexo_OBwpNydFoO04yyiLE56PS65PYMyeDxTeAzCHd7s0i_tQjjn_Op-mMl1KVflis9wXSyKVcEYXS5n3Voip-2ixLJuIx3rSrSlENWK1wXjsqxmah2vgJzRslgWBV1msl4sF_lq1Ygla_MFJVWOPVc6i7_94xvDLEn4esWqcjnTvEHt04sKpbHs9DD-6K-fZ24dfebNsPekyrXywV-iBBU0rqcbY6TD9wFVwPFWPRHnvvvpoouCpkx8dxn1LD4NHfazwen1_1jn9Poy_jc_OPsfFIHQXYIcl3ks6W1N_xsAAP__9e0WKw">