[llvm] [SPIR-V] Fix half precision OpConstant for log10/exp10 lowering (PR #193730)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Apr 23 05:53:50 PDT 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-backend-spir-v
Author: Arseniy Obolenskiy (aobolensk)
<details>
<summary>Changes</summary>
When lowering llvm.log10/llvm.exp10 to GLSL.std.450 sequences, the scaling constants (1/log2(10) and log2(10)) were built from float literals via APFloat(0.30103f) / APFloat(3.3219280948874f) and then emitted as OpConstant of the result's scalar type
Convert the literal to the scalar type's APFloat semantics before building the constant, and drop the 32-bit-only assert in selectExp10
resolves #<!-- -->145073
---
Full diff: https://github.com/llvm/llvm-project/pull/193730.diff
3 Files Affected:
- (modified) llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp (+19-6)
- (modified) llvm/test/CodeGen/SPIRV/hlsl-intrinsics/log10.ll (+23-4)
- (modified) llvm/test/CodeGen/SPIRV/llvm-intrinsics/exp10-glsl.ll (+25)
``````````diff
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index c6deffde24af8..a3dd4b27e50e7 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -3871,11 +3871,16 @@ bool SPIRVInstructionSelector::selectExp10(Register ResVReg,
SPIRVTypeInst SpirvScalarType = GR.getScalarOrVectorComponentType(ResType);
- assert(SpirvScalarType->getOperand(1).getImm() == 32 &&
- "only float operands supported by GLSL extended math");
-
- Register ConstReg = GR.buildConstantFP(APFloat(3.3219280948874f),
- MIRBuilder, SpirvScalarType);
+ // Match the literal precision to the scalar type so the OpConstant
+ // literal does not contain non-zero high-order bits that would fail
+ // SPIR-V validation when the type is narrower than 32 bits (e.g. half).
+ APFloat ConstVal(3.3219280948873623);
+ bool LosesInfo;
+ ConstVal.convert(getZeroFP(GR.getTypeForSPIRVType(SpirvScalarType))
+ .getSemantics(),
+ APFloat::rmNearestTiesToEven, &LosesInfo);
+ Register ConstReg =
+ GR.buildConstantFP(ConstVal, MIRBuilder, SpirvScalarType);
Register ArgReg = MRI->createVirtualRegister(GR.getRegClass(ResType));
auto Opcode = ResType->getOpcode() == SPIRV::OpTypeVector
? SPIRV::OpVectorTimesScalar
@@ -6370,8 +6375,16 @@ bool SPIRVInstructionSelector::selectLog10(Register ResVReg,
ResType->getOpcode() == SPIRV::OpTypeFloat);
// TODO: Add matrix implementation once supported by the HLSL frontend.
SPIRVTypeInst SpirvScalarType = GR.getScalarOrVectorComponentType(ResType);
+ // The literal must match the precision of the scalar type, otherwise the
+ // OpConstant will contain non-zero high-order bits and fail SPIR-V
+ // validation when the type is narrower than 32 bits (e.g. half).
+ APFloat ScaleVal(0.30103);
+ bool LosesInfo;
+ ScaleVal.convert(getZeroFP(GR.getTypeForSPIRVType(SpirvScalarType))
+ .getSemantics(),
+ APFloat::rmNearestTiesToEven, &LosesInfo);
Register ScaleReg =
- GR.buildConstantFP(APFloat(0.30103f), MIRBuilder, SpirvScalarType);
+ GR.buildConstantFP(ScaleVal, MIRBuilder, SpirvScalarType);
// Multiply log2(x) by 0.30103 to get log10(x) result.
auto Opcode = ResType->getOpcode() == SPIRV::OpTypeVector
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/log10.ll b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/log10.ll
index dceaa8c209957..c13bda8b91009 100644
--- a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/log10.ll
+++ b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/log10.ll
@@ -3,17 +3,24 @@
; CHECK: %[[#extinst:]] = OpExtInstImport "GLSL.std.450"
-; CHECK: %[[#float:]] = OpTypeFloat 32
-; CHECK: %[[#v4float:]] = OpTypeVector %[[#float]] 4
-; CHECK: %[[#float_0_30103001:]] = OpConstant %[[#float]] 0.30103000998497009
+; CHECK-DAG: %[[#half:]] = OpTypeFloat 16
+; CHECK-DAG: %[[#v4half:]] = OpTypeVector %[[#half]] 4
+; CHECK-DAG: %[[#float:]] = OpTypeFloat 32
+; CHECK-DAG: %[[#v4float:]] = OpTypeVector %[[#float]] 4
+; CHECK-DAG: %[[#float_0_30103001:]] = OpConstant %[[#float]] 0.30103000998497009
+; CHECK-DAG: %[[#half_0_30103001:]] = OpConstant %[[#half]] 13521
@logf = global float 0.0, align 4
@logf4 = global <4 x float> zeroinitializer, align 16
+ at logh = global half 0.0, align 2
+ at logh4 = global <4 x half> zeroinitializer, align 8
-define void @main(float %f, <4 x float> %f4) {
+define void @main(float %f, <4 x float> %f4, half %h, <4 x half> %h4) {
entry:
; CHECK-DAG: %[[#f:]] = OpFunctionParameter %[[#float]]
; CHECK-DAG: %[[#f4:]] = OpFunctionParameter %[[#v4float]]
+; CHECK-DAG: %[[#h:]] = OpFunctionParameter %[[#half]]
+; CHECK-DAG: %[[#h4:]] = OpFunctionParameter %[[#v4half]]
; CHECK: %[[#log2:]] = OpExtInst %[[#float]] %[[#extinst]] Log2 %[[#f]]
; CHECK: %[[#res:]] = OpFMul %[[#float]] %[[#log2]] %[[#float_0_30103001]]
@@ -25,8 +32,20 @@ entry:
%elt.log101 = call <4 x float> @llvm.log10.v4f32(<4 x float> %f4)
store <4 x float> %elt.log101, ptr @logf4, align 16
+; CHECK: %[[#log2:]] = OpExtInst %[[#half]] %[[#extinst]] Log2 %[[#h]]
+; CHECK: %[[#res:]] = OpFMul %[[#half]] %[[#log2]] %[[#half_0_30103001]]
+ %elt.log10h = call half @llvm.log10.f16(half %h)
+ store half %elt.log10h, ptr @logh, align 2
+
+; CHECK: %[[#log2:]] = OpExtInst %[[#v4half]] %[[#extinst]] Log2 %[[#h4]]
+; CHECK: %[[#res:]] = OpVectorTimesScalar %[[#v4half]] %[[#log2]] %[[#half_0_30103001]]
+ %elt.log10h4 = call <4 x half> @llvm.log10.v4f16(<4 x half> %h4)
+ store <4 x half> %elt.log10h4, ptr @logh4, align 8
+
ret void
}
declare float @llvm.log10.f32(float)
declare <4 x float> @llvm.log10.v4f32(<4 x float>)
+declare half @llvm.log10.f16(half)
+declare <4 x half> @llvm.log10.v4f16(<4 x half>)
diff --git a/llvm/test/CodeGen/SPIRV/llvm-intrinsics/exp10-glsl.ll b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/exp10-glsl.ll
index 2dfffc4a4726b..cd1562b5ab565 100644
--- a/llvm/test/CodeGen/SPIRV/llvm-intrinsics/exp10-glsl.ll
+++ b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/exp10-glsl.ll
@@ -6,8 +6,11 @@
; CHECK-DAG: %[[#ExtInstId:]] = OpExtInstImport "GLSL.std.450"
+; CHECK-DAG: %[[#F16Ty:]] = OpTypeFloat 16
+; CHECK-DAG: %[[#Vec4_16Ty:]] = OpTypeVector %[[#F16Ty]] 4
; CHECK-DAG: %[[#F32Ty:]] = OpTypeFloat 32
; CHECK-DAG: %[[#Vec4_32Ty:]] = OpTypeVector %[[#F32Ty]] 4
+; CHECK-DAG: %[[#Constant_s16:]] = OpConstant %[[#F16Ty]] 17061
; CHECK-DAG: %[[#Constant_s32:]] = OpConstant %[[#F32Ty:]] 3.3219280242919922
; CHECK-LABEL: Begin function test_exp10_f32_scalar
@@ -31,3 +34,25 @@ define <4 x float> @test_exp10_f32_vec4(<4 x float> %x) {
%res = call <4 x float> @llvm.exp10.f32(<4 x float> %x)
ret <4 x float> %res
}
+
+; CHECK-LABEL: Begin function test_exp10_f16_scalar
+; CHECK: %[[#s16_arg:]] = OpFunctionParameter %[[#F16Ty]]
+; CHECK: %[[#s16_mul:]] = OpFMul %[[#F16Ty]] %[[#s16_arg]] %[[#Constant_s16]]
+; CHECK: %[[#s16_ret:]] = OpExtInst %[[#F16Ty]] %[[#ExtInstId]] Exp2 %[[#s16_mul]]
+; CHECK: OpReturnValue %[[#s16_ret]]
+; CHECK-LABEL: OpFunctionEnd
+define half @test_exp10_f16_scalar(half %x) {
+ %res = call half @llvm.exp10.f16(half %x)
+ ret half %res
+}
+
+; CHECK-LABEL: Begin function test_exp10_f16_vec4
+; CHECK: %[[#v4_16_arg:]] = OpFunctionParameter %[[#Vec4_16Ty]]
+; CHECK: %[[#v4_16_mul:]] = OpVectorTimesScalar %[[#Vec4_16Ty]] %[[#v4_16_arg]] %[[#Constant_s16]]
+; CHECK: %[[#v4_16_ret:]] = OpExtInst %[[#Vec4_16Ty]] %[[#ExtInstId]] Exp2 %[[#v4_16_mul]]
+; CHECK: OpReturnValue %[[#v4_16_ret]]
+; CHECK-LABEL: OpFunctionEnd
+define <4 x half> @test_exp10_f16_vec4(<4 x half> %x) {
+ %res = call <4 x half> @llvm.exp10.f16(<4 x half> %x)
+ ret <4 x half> %res
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/193730
More information about the llvm-commits
mailing list