[llvm] [SPIR-V] Add lowering for G_FPOWI (PR #185454)

Diego Novillo via llvm-commits llvm-commits at lists.llvm.org
Tue Mar 10 10:47:51 PDT 2026


https://github.com/dnovillo updated https://github.com/llvm/llvm-project/pull/185454

>From 96554b56e130ee4d9c0289911795f350ede27ec9 Mon Sep 17 00:00:00 2001
From: Diego Novillo <dnovillo at nvidia.com>
Date: Mon, 9 Mar 2026 11:23:02 -0400
Subject: [PATCH 1/2] [SPIR-V] Add lowering for G_FPOWI

This fixes an assertion I was hitting in the fragment density map sample in
Vulkan Samples. In starfield.frag.hlsl, we have

float starCol = pow((rnd - threshhold) / (1.0 - threshhold), 16.0);

The optimizer recognizes 16.0 as a whole number and converts the call to
`llvm.powi`. The backend goes on to fail with:

fatal error: error in backend: cannot select: %46:fid(s64) = nnan ninf nsz arcp afn reassoc G_FPOWI %44:fid, %45:iid(s64) (in function: _Z9starFieldDv3_f)

On Vulkan, there is no integer-exponent for pow. This patch lowers it by
converting the exponent to float and calling GLSL.std.450's Pow.
---
 .../Target/SPIRV/SPIRVInstructionSelector.cpp | 28 +++++++++++++-
 .../SPIRV/llvm-intrinsics/powi-glsl.ll        | 37 +++++++++++++++++++
 2 files changed, 64 insertions(+), 1 deletion(-)
 create mode 100644 llvm/test/CodeGen/SPIRV/llvm-intrinsics/powi-glsl.ll

diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index 65f4856aeee68..44f03cdd4ed0d 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -333,6 +333,9 @@ class SPIRVInstructionSelector : public InstructionSelector {
   bool selectLog10(Register ResVReg, SPIRVTypeInst ResType,
                    MachineInstr &I) const;
 
+  bool selectFpowi(Register ResVReg, SPIRVTypeInst ResType,
+                   MachineInstr &I) const;
+
   bool selectSaturate(Register ResVReg, SPIRVTypeInst ResType,
                       MachineInstr &I) const;
 
@@ -1033,7 +1036,7 @@ bool SPIRVInstructionSelector::spvSelect(Register ResVReg,
   case TargetOpcode::G_FPOW:
     return selectExtInst(ResVReg, ResType, I, CL::pow, GL::Pow);
   case TargetOpcode::G_FPOWI:
-    return selectExtInst(ResVReg, ResType, I, CL::pown);
+    return selectFpowi(ResVReg, ResType, I);
 
   case TargetOpcode::G_FEXP:
     return selectExtInst(ResVReg, ResType, I, CL::exp, GL::Exp);
@@ -5557,6 +5560,29 @@ bool SPIRVInstructionSelector::selectLog10(Register ResVReg,
   return true;
 }
 
+bool SPIRVInstructionSelector::selectFpowi(Register ResVReg,
+                                           SPIRVTypeInst ResType,
+                                           MachineInstr &I) const {
+  // On OpenCL targets, pown(gentype x, intn n) maps directly.
+  if (STI.canUseExtInstSet(SPIRV::InstructionSet::OpenCL_std))
+    return selectExtInst(ResVReg, ResType, I, CL::pown);
+
+  // On GLSL (Vulkan) targets, there is no integer-exponent power instruction.
+  // Lower as: Pow(base, OpConvertSToF(exp)).
+  if (STI.canUseExtInstSet(SPIRV::InstructionSet::GLSL_std_450)) {
+    Register BaseReg = I.getOperand(1).getReg();
+    Register ExpReg = I.getOperand(2).getReg();
+    Register FloatExpReg = MRI->createVirtualRegister(GR.getRegClass(ResType));
+    if (!selectOpWithSrcs(FloatExpReg, ResType, I, {ExpReg},
+                          SPIRV::OpConvertSToF))
+      return false;
+    return selectExtInst(ResVReg, ResType, I, GL::Pow,
+                         /*setMIFlags=*/true, /*useMISrc=*/false,
+                         {BaseReg, FloatExpReg});
+  }
+  return false;
+}
+
 bool SPIRVInstructionSelector::selectModf(Register ResVReg,
                                           SPIRVTypeInst ResType,
                                           MachineInstr &I) const {
diff --git a/llvm/test/CodeGen/SPIRV/llvm-intrinsics/powi-glsl.ll b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/powi-glsl.ll
new file mode 100644
index 0000000000000..c2397d4273c2f
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/powi-glsl.ll
@@ -0,0 +1,37 @@
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv-unknown-vulkan1.3-compute %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-vulkan1.3-compute %s -o - -filetype=obj | spirv-val %}
+
+; Test that llvm.powi on Vulkan targets is lowered by converting the
+; integer exponent to float with OpConvertSToF, then calling GLSL.std.450 Pow.
+
+; CHECK-DAG: %[[#ExtInstId:]] = OpExtInstImport "GLSL.std.450"
+; CHECK-DAG: %[[#F32Ty:]] = OpTypeFloat 32
+; CHECK-DAG: %[[#I32Ty:]] = OpTypeInt 32 0
+; CHECK-DAG: %[[#I64Ty:]] = OpTypeInt 64 0
+
+; CHECK-LABEL: Begin function test_powi_f32_i32
+; CHECK: %[[#base32:]] = OpFunctionParameter %[[#F32Ty]]
+; CHECK: %[[#exp32:]] = OpFunctionParameter %[[#I32Ty]]
+; CHECK: %[[#fexp32:]] = OpConvertSToF %[[#F32Ty]] %[[#exp32]]
+; CHECK: %[[#ret32:]] = OpExtInst %[[#F32Ty]] %[[#ExtInstId]] Pow %[[#base32]] %[[#fexp32]]
+; CHECK: OpReturnValue %[[#ret32]]
+; CHECK-LABEL: OpFunctionEnd
+define float @test_powi_f32_i32(float %x, i32 %n) {
+  %res = call float @llvm.powi.f32.i32(float %x, i32 %n)
+  ret float %res
+}
+
+; CHECK-LABEL: Begin function test_powi_f32_i64
+; CHECK: %[[#base64:]] = OpFunctionParameter %[[#F32Ty]]
+; CHECK: %[[#exp64:]] = OpFunctionParameter %[[#I64Ty]]
+; CHECK: %[[#fexp64:]] = OpConvertSToF %[[#F32Ty]] %[[#exp64]]
+; CHECK: %[[#ret64:]] = OpExtInst %[[#F32Ty]] %[[#ExtInstId]] Pow %[[#base64]] %[[#fexp64]]
+; CHECK: OpReturnValue %[[#ret64]]
+; CHECK-LABEL: OpFunctionEnd
+define float @test_powi_f32_i64(float %x, i64 %n) {
+  %res = call float @llvm.powi.f32.i64(float %x, i64 %n)
+  ret float %res
+}
+
+declare float @llvm.powi.f32.i32(float, i32)
+declare float @llvm.powi.f32.i64(float, i64)

>From c34ea56e61529f3e48767eac448c42f94a444ff3 Mon Sep 17 00:00:00 2001
From: Diego Novillo <dnovillo at nvidia.com>
Date: Tue, 10 Mar 2026 11:28:48 -0400
Subject: [PATCH 2/2] Address review feedback.

---
 llvm/test/CodeGen/SPIRV/llvm-intrinsics/powi-glsl.ll | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/llvm/test/CodeGen/SPIRV/llvm-intrinsics/powi-glsl.ll b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/powi-glsl.ll
index c2397d4273c2f..155817a746ddf 100644
--- a/llvm/test/CodeGen/SPIRV/llvm-intrinsics/powi-glsl.ll
+++ b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/powi-glsl.ll
@@ -1,5 +1,5 @@
 ; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv-unknown-vulkan1.3-compute %s -o - | FileCheck %s
-; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-vulkan1.3-compute %s -o - -filetype=obj | spirv-val %}
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-vulkan1.3-compute %s -o - -filetype=obj | spirv-val --target-env vulkan1.3 %}
 
 ; Test that llvm.powi on Vulkan targets is lowered by converting the
 ; integer exponent to float with OpConvertSToF, then calling GLSL.std.450 Pow.
@@ -16,7 +16,7 @@
 ; CHECK: %[[#ret32:]] = OpExtInst %[[#F32Ty]] %[[#ExtInstId]] Pow %[[#base32]] %[[#fexp32]]
 ; CHECK: OpReturnValue %[[#ret32]]
 ; CHECK-LABEL: OpFunctionEnd
-define float @test_powi_f32_i32(float %x, i32 %n) {
+define internal float @test_powi_f32_i32(float %x, i32 %n) {
   %res = call float @llvm.powi.f32.i32(float %x, i32 %n)
   ret float %res
 }
@@ -28,7 +28,7 @@ define float @test_powi_f32_i32(float %x, i32 %n) {
 ; CHECK: %[[#ret64:]] = OpExtInst %[[#F32Ty]] %[[#ExtInstId]] Pow %[[#base64]] %[[#fexp64]]
 ; CHECK: OpReturnValue %[[#ret64]]
 ; CHECK-LABEL: OpFunctionEnd
-define float @test_powi_f32_i64(float %x, i64 %n) {
+define internal float @test_powi_f32_i64(float %x, i64 %n) {
   %res = call float @llvm.powi.f32.i64(float %x, i64 %n)
   ret float %res
 }



More information about the llvm-commits mailing list