Description
Consider the following IR:
define half @to_half(i16 %bits) {
%f = bitcast i16 %bits to half
ret half %f
}
define i16 @from_half(half %f) {
%bits = bitcast half %f to i16
ret i16 %bits
}
As the only operation involved is a bitcast (in particular, there are no floating point type conversions in the LLVM IR), the values returned from to_half
and from_half
should be bit-for-bit identical to the value passed to them as their only argument (just a different type). However, several targets pass/return half
as a float
. On these targets, LLVM will use the default float conversion builtins (such as __gnu_h2f_ieee
and __gnu_f2h_ieee
) to convert between half
and float
. The issue is that these builtins silence signalling NaNs which changes the NaN payload, meaning that the roundtrip of half
-> float
-> half
is not lossless and causes the generated ASM to violate the semantics of LLVM IR. This miscompilation is similar to #66803.
By inspecting the assembly LLVM emits, I've discovered that at least the following backends appear to be affected (in brackets are a specific target triple that I've checked):
- C-SKY (
csky-unknown-linux-gnuabiv2
with-mcpu=ck860fv -mattr=+hard-float
) - Hexagon (
hexagon-unknown-linux-musl
) - LoongArch (
loongarch64-unknown-linux-gnu
with-mattr=+f
): Fixed by [loongarch][DAG][FREEZE] Fix crash when FREEZE a half(f16) type on loongarch #107791 - MIPS (
mips64el-unknown-linux-gnuabi64
): Fixed by [MIPS] Use softPromoteHalf legalization for fp16 rather than PromoteFloat #110199 - PowerPC (
powerpc64le-unknown-linux-gnu
) - SPARC (
sparc64-unknown-linux-gnu
) - WASM (
wasm32-unknown-wasi
): Already reported inwasm32
queitenshalf
signalling NaNs in some situations when passing/returning them #96438
As none of these target's ABI specifications (that I've been able to find) specify how half
should be passed (nor does Clang support _Float16
on any of these targets), and given that these targets are a subset of those affected by #97975, I'm filing this as a single issue as the ABI has probably been selected as an automatic default by LLVM rather than a deliberate choice by the backends. Ultimately there are two possible solutions: either fix LLVM to codegen lossless conversions between half
and float
when needed for the ABI (one way to do this would be with a new pair of builtins that don't silence signalling NaNs), or change the ABIs to pass/return half
without converting it to float
(probably using the same ABI as i16
, but some targets might have better options).
Related to #97975.