Repository navigation
LLVM miscompiles passing/returning half on several backends by using lossy conversions #97981
Description
Activity
@llvm/issue-subscribers-backend-powerpc
Author: None (beetrees)
Details
Consider the following IR: ```llvm 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): - Hexagon (`hexagon-unknown-linux-musl`) - MIPS (`mips64el-unknown-linux-gnuabi64`) - PowerPC (`powerpc64le-unknown-linux-gnu`) - SPARC (`sparc64-unknown-linux-gnu`) - WASM (`wasm32-unknown-wasi`): Already reported in #<!-- -->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). </details>@llvm/issue-subscribers-backend-mips
Author: None (beetrees)
Details
Consider the following IR: ```llvm 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): - Hexagon (`hexagon-unknown-linux-musl`) - MIPS (`mips64el-unknown-linux-gnuabi64`) - PowerPC (`powerpc64le-unknown-linux-gnu`) - SPARC (`sparc64-unknown-linux-gnu`) - WASM (`wasm32-unknown-wasi`): Already reported in #<!-- -->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). </details>@llvm/issue-subscribers-backend-hexagon
Author: None (beetrees)
Details
Consider the following IR: ```llvm 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): - Hexagon (`hexagon-unknown-linux-musl`) - MIPS (`mips64el-unknown-linux-gnuabi64`) - PowerPC (`powerpc64le-unknown-linux-gnu`) - SPARC (`sparc64-unknown-linux-gnu`) - WASM (`wasm32-unknown-wasi`): Already reported in #<!-- -->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). </details>@llvm/issue-subscribers-backend-webassembly
Author: None (beetrees)
Details
Consider the following IR: ```llvm 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): - Hexagon (`hexagon-unknown-linux-musl`) - MIPS (`mips64el-unknown-linux-gnuabi64`) - PowerPC (`powerpc64le-unknown-linux-gnu`) - SPARC (`sparc64-unknown-linux-gnu`) - WASM (`wasm32-unknown-wasi`): Already reported in #<!-- -->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). </details>FYI: On PPC atleast you need to add
zextI think. (On PPC i16 is being passed as i32 and that can cause issue)define half @to_half(zext i16 %bits) { %f = bitcast i16 %bits to half ret half %f }FYI: On PPC atleast you need to add
zextI think. (On PPC i16 is being passed as i32 and that can cause issue)I remember seeing somewhere that s390x requires either zext or sext for all integer arguments and returns, since they have different ABIs and the generated code assumes they've been sign/zero extended properly.
65 remaining items
- added 5 commits that reference this issue
on Jun 14, 2026 - added a commit that references this issue
on Jul 21, 2026 - added 9 commits that reference this issue
on Jul 30, 2026
Consider the following IR:
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_halfandfrom_halfshould be bit-for-bit identical to the value passed to them as their only argument (just a different type). However, several targets pass/returnhalfas afloat. On these targets, LLVM will use the default float conversion builtins (such as__gnu_h2f_ieeeand__gnu_f2h_ieee) to convert betweenhalfandfloat. The issue is that these builtins silence signalling NaNs which changes the NaN payload, meaning that the roundtrip ofhalf->float->halfis 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):
csky-unknown-linux-gnuabiv2with-mcpu=ck860fv -mattr=+hard-float)hexagon-unknown-linux-musl): Fixed by [HEXAGON] Add support to lower "FREEZE a half(f16)" instruction on Hexagon and fix the isel-buildvector-v2f16.ll assertion #130977loongarch64-unknown-linux-gnuwith-mattr=+f): Fixed by [loongarch][DAG][FREEZE] Fix crash when FREEZE a half(f16) type on loongarch #107791mips64el-unknown-linux-gnuabi64): Fixed by [MIPS] Use softPromoteHalf legalization for fp16 rather than PromoteFloat #110199powerpc64le-unknown-linux-gnu): Fixed by [PowerPC] Changehalfto use soft promotion rather thanPromoteFloat#152632sparc64-unknown-linux-gnu): Fixed by [SPARC] Changehalfto use soft promotion rather thanPromoteFloat#152727wasm32-unknown-wasi): Already reported inwasm32queitenshalfsignalling NaNs in some situations when passing/returning them #96438, fixed by [WebAssembly] Changehalfto use soft promotion rather thanPromoteFloat#152833As none of these target's ABI specifications (that I've been able to find) specify how
halfshould be passed (nor does Clang support_Float16on 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 betweenhalfandfloatwhen 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/returnhalfwithout converting it tofloat(probably using the same ABI asi16, but some targets might have better options).Related to #97975.