[llvm] [SPIR-V] Fix half precision OpConstant for log10/exp10 lowering (PR #193730)

Arseniy Obolenskiy via llvm-commits llvm-commits at lists.llvm.org
Thu Apr 23 06:01:39 PDT 2026


https://github.com/aobolensk updated https://github.com/llvm/llvm-project/pull/193730

>From 7f269517cb388939dbd6ce2131f5bf7e828a1604 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Thu, 23 Apr 2026 14:50:27 +0200
Subject: [PATCH] [SPIR-V] Fix half precision OpConstant for log10/exp10
 lowering

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
---
 .../Target/SPIRV/SPIRVInstructionSelector.cpp | 26 +++++++++++++-----
 .../CodeGen/SPIRV/hlsl-intrinsics/log10.ll    | 27 ++++++++++++++++---
 .../SPIRV/llvm-intrinsics/exp10-glsl.ll       | 25 +++++++++++++++++
 3 files changed, 67 insertions(+), 11 deletions(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index c6deffde24af8..2a6ddddce6572 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,15 @@ bool SPIRVInstructionSelector::selectLog10(Register ResVReg,
          ResType->getOpcode() == SPIRV::OpTypeFloat);
   // TODO: Add matrix implementation once supported by the HLSL frontend.
   SPIRVTypeInst SpirvScalarType = GR.getScalarOrVectorComponentType(ResType);
-  Register ScaleReg =
-      GR.buildConstantFP(APFloat(0.30103f), MIRBuilder, SpirvScalarType);
+  // 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(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
+}



More information about the llvm-commits mailing list