[llvm] [SPIR-V] Add lowering for G_FEXP10 (PR #182466)

via llvm-commits llvm-commits at lists.llvm.org
Fri Feb 20 08:06:44 PST 2026


https://github.com/idubinov updated https://github.com/llvm/llvm-project/pull/182466

>From 66f73e0a3a7a7e9c8cdb426ca76c75710e917128 Mon Sep 17 00:00:00 2001
From: idubinov <igor.dubinov at amd.com>
Date: Fri, 20 Feb 2026 03:53:47 -0600
Subject: [PATCH 1/4] Add exp10 support

---
 .../Target/SPIRV/SPIRVInstructionSelector.cpp | 106 ++++++++++++++++
 llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp  |   1 +
 .../SPIRV/llvm-intrinsics/exp10-glsl.ll       | 120 ++++++++++++++++++
 .../SPIRV/llvm-intrinsics/exp10-opencl.ll     | 108 ++++++++++++++++
 4 files changed, 335 insertions(+)
 create mode 100644 llvm/test/CodeGen/SPIRV/llvm-intrinsics/exp10-glsl.ll
 create mode 100644 llvm/test/CodeGen/SPIRV/llvm-intrinsics/exp10-opencl.ll

diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index 1ea8036a2a698..3531e084e0122 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -324,6 +324,19 @@ class SPIRVInstructionSelector : public InstructionSelector {
   bool selectExtInstForLRound(Register ResVReg, SPIRVTypeInst ResType,
                               MachineInstr &I,
                               const ExtInstList &ExtInsts) const;
+  bool selectExtInstWithSrcs(Register ResVReg, SPIRVTypeInst RestType,
+                             MachineInstr &I, std::vector<Register> SrcRegs,
+                             GL::GLSLExtInst GLInst) const;
+  bool selectExtInstWithSrcs(Register ResVReg, SPIRVTypeInst ResType,
+                             MachineInstr &I, std::vector<Register> SrcRegs,
+                             CL::OpenCLExtInst CLInst) const;
+  bool selectExtInstWithSrcs(Register ResVReg, SPIRVTypeInst ResType,
+                             MachineInstr &I, std::vector<Register> SrcRegs,
+                             CL::OpenCLExtInst CLInst,
+                             GL::GLSLExtInst GLInst) const;
+  bool selectExtInstWithSrcs(Register ResVReg, SPIRVTypeInst ResType,
+                             MachineInstr &I, std::vector<Register> SrcRegs,
+                             const ExtInstList &ExtInsts) const;
 
   bool selectLog10(Register ResVReg, SPIRVTypeInst ResType,
                    MachineInstr &I) const;
@@ -375,6 +388,8 @@ class SPIRVInstructionSelector : public InstructionSelector {
                    MachineInstr &I) const;
   bool selectSincos(Register ResVReg, SPIRVTypeInst ResType,
                     MachineInstr &I) const;
+  bool selectExp10(Register ResVReg, SPIRVTypeInst ResType,
+                   MachineInstr &I) const;
   bool selectDerivativeInst(Register ResVReg, SPIRVTypeInst ResType,
                             MachineInstr &I, const unsigned DPdOpCode) const;
   // Utilities
@@ -1024,6 +1039,9 @@ bool SPIRVInstructionSelector::spvSelect(Register ResVReg,
     return selectExtInst(ResVReg, ResType, I, CL::exp, GL::Exp);
   case TargetOpcode::G_FEXP2:
     return selectExtInst(ResVReg, ResType, I, CL::exp2, GL::Exp2);
+  case TargetOpcode::G_FEXP10:
+    return selectExp10(ResVReg, ResType, I);
+
   case TargetOpcode::G_FMODF:
     return selectModf(ResVReg, ResType, I);
   case TargetOpcode::G_FSINCOS:
@@ -1370,6 +1388,54 @@ bool SPIRVInstructionSelector::selectExtInst(Register ResVReg,
   }
   return false;
 }
+
+bool SPIRVInstructionSelector::selectExtInstWithSrcs(
+    Register ResVReg, SPIRVTypeInst ResType, MachineInstr &I,
+    std::vector<Register> SrcRegs, GL::GLSLExtInst GLInst) const {
+  ExtInstList ExtInsts = {{SPIRV::InstructionSet::GLSL_std_450, GLInst}};
+  return selectExtInstWithSrcs(ResVReg, ResType, I, SrcRegs, ExtInsts);
+}
+
+bool SPIRVInstructionSelector::selectExtInstWithSrcs(
+    Register ResVReg, SPIRVTypeInst ResType, MachineInstr &I,
+    std::vector<Register> SrcRegs, CL::OpenCLExtInst CLInst) const {
+  ExtInstList ExtInsts = {{SPIRV::InstructionSet::OpenCL_std, CLInst}};
+  return selectExtInstWithSrcs(ResVReg, ResType, I, SrcRegs, ExtInsts);
+}
+
+bool SPIRVInstructionSelector::selectExtInstWithSrcs(
+    Register ResVReg, SPIRVTypeInst ResType, MachineInstr &I,
+    std::vector<Register> SrcRegs, CL::OpenCLExtInst CLInst,
+    GL::GLSLExtInst GLInst) const {
+  ExtInstList ExtInsts = {{SPIRV::InstructionSet::OpenCL_std, CLInst},
+                          {SPIRV::InstructionSet::GLSL_std_450, GLInst}};
+  return selectExtInstWithSrcs(ResVReg, ResType, I, SrcRegs, ExtInsts);
+}
+
+bool SPIRVInstructionSelector::selectExtInstWithSrcs(
+    Register ResVReg, SPIRVTypeInst ResType, MachineInstr &I,
+    std::vector<Register> SrcRegs, const ExtInstList &Insts) const {
+  for (const auto &Ex : Insts) {
+    SPIRV::InstructionSet::InstructionSet Set = Ex.first;
+    if (STI.canUseExtInstSet(Set)) {
+      uint32_t Opcode = Ex.second;
+      MachineBasicBlock &BB = *I.getParent();
+      auto MIB = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpExtInst))
+                     .addDef(ResVReg)
+                     .addUse(GR.getSPIRVTypeID(ResType))
+                     .addImm(static_cast<uint32_t>(Set))
+                     .addImm(Opcode)
+                     .setMIFlags(I.getFlags());
+      for (Register SReg : SrcRegs) {
+        MIB.addUse(SReg);
+      }
+      MIB.constrainAllUses(TII, TRI, RBI);
+      return true;
+    }
+  }
+  return false;
+}
+
 bool SPIRVInstructionSelector::selectExtInstForLRound(
     Register ResVReg, SPIRVTypeInst ResType, MachineInstr &I,
     CL::OpenCLExtInst CLInst, GL::GLSLExtInst GLInst) const {
@@ -3218,6 +3284,46 @@ bool SPIRVInstructionSelector::selectFCmp(Register ResVReg,
   return selectCmp(ResVReg, ResType, CmpOp, I);
 }
 
+bool SPIRVInstructionSelector::selectExp10(Register ResVReg,
+                                           SPIRVTypeInst ResType,
+                                           MachineInstr &I) const {
+
+  if (STI.canUseExtInstSet(SPIRV::InstructionSet::OpenCL_std)) {
+    return selectExtInst(ResVReg, ResType, I, CL::exp10);
+  } else if (STI.canUseExtInstSet(SPIRV::InstructionSet::GLSL_std_450)) {
+    /// There is no exp10 in GLSL. Use exp10(x) = exp2(x * log2(10)) instead
+    /// log2(10) ~= 3.3219280948874
+
+    if (ResType->getOpcode() != SPIRV::OpTypeVector &&
+        ResType->getOpcode() != SPIRV::OpTypeFloat)
+      return false;
+
+    MachineIRBuilder MIRBuilder(I);
+
+    SPIRVTypeInst SpirvScalarType = ResType->getOpcode() == SPIRV::OpTypeVector
+                                        ? SPIRVTypeInst(GR.getSPIRVTypeForVReg(
+                                              ResType->getOperand(1).getReg()))
+                                        : ResType;
+
+    Register ConstReg = GR.buildConstantFP(APFloat(3.3219280948874f),
+                                           MIRBuilder, SpirvScalarType);
+    Register ArgReg = MRI->createVirtualRegister(GR.getRegClass(ResType));
+    auto Opcode = ResType->getOpcode() == SPIRV::OpTypeVector
+                      ? SPIRV::OpVectorTimesScalar
+                      : SPIRV::OpFMulS;
+
+    if (!selectOpWithSrcs(
+            ArgReg, ResType, I,
+            std::vector<Register>({ConstReg, I.getOperand(1).getReg()}),
+            Opcode))
+      return false;
+    return selectExtInstWithSrcs(
+        ResVReg, ResType, I, std::vector<Register>({ArgReg}), GL::Exp2);
+  }
+
+  return false;
+}
+
 Register SPIRVInstructionSelector::buildZerosVal(SPIRVTypeInst ResType,
                                                  MachineInstr &I) const {
   // OpenCL uses nulls for Zero. In HLSL we don't use null constants.
diff --git a/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp b/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
index d8f197443c6ab..93e82750c4f32 100644
--- a/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
@@ -445,6 +445,7 @@ SPIRVLegalizerInfo::SPIRVLegalizerInfo(const SPIRVSubtarget &ST) {
                                G_FMODF,
                                G_FSINCOS,
                                G_FEXP2,
+                               G_FEXP10,
                                G_FLOG,
                                G_FLOG2,
                                G_FLOG10,
diff --git a/llvm/test/CodeGen/SPIRV/llvm-intrinsics/exp10-glsl.ll b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/exp10-glsl.ll
new file mode 100644
index 0000000000000..9662df6b5bbf9
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/exp10-glsl.ll
@@ -0,0 +1,120 @@
+; 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 if llvm.exp10 is lowered to opencl::exp10 with the result correctly
+;reused by the original llvm.exp10 user.
+
+
+; CHECK-DAG: %[[#ExtInstId:]] = OpExtInstImport "GLSL.std.450"
+; CHECK-DAG: %[[#F16Ty:]] = OpTypeFloat 16
+; CHECK-DAG: %[[#F32Ty:]] = OpTypeFloat 32
+; CHECK-DAG: %[[#F64Ty:]] = OpTypeFloat 64
+; CHECK-DAG: %[[#Vec2_16Ty:]] = OpTypeVector %[[#F16Ty]] 2
+; CHECK-DAG: %[[#Vec2_32Ty:]] = OpTypeVector %[[#F32Ty]] 2
+; CHECK-DAG: %[[#Vec2_64Ty:]] = OpTypeVector %[[#F64Ty]] 2
+; CHECK-DAG: %[[#Vec4_16Ty:]] = OpTypeVector %[[#F16Ty]] 4
+; CHECK-DAG: %[[#Vec4_32Ty:]] = OpTypeVector %[[#F32Ty]] 4
+; CHECK-DAG: %[[#Vec4_64Ty:]] = OpTypeVector %[[#F64Ty]] 4
+; CHECK-DAG: %[[#Constant_s16:]] = OpConstant %[[#F16Ty:]] 3.3219280242919922
+; CHECK-DAG: %[[#Constant_s32:]] = OpConstant %[[#F32Ty:]] 3.3219280242919922
+; CHECK-DAG: %[[#Constant_s64:]] = OpConstant %[[#F64Ty:]] 3.3219280242919922
+
+; CHECK-LABEL: Begin function test_exp10_f16_scalar
+; CHECK: %[[#s16_arg:]] = OpFunctionParameter %[[#F16Ty]]
+; CHECK: %[[#s16_mul:]] = OpFMul %[[#F16Ty]] %[[#Constant_s16]] %[[#s16_arg]]
+; 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_f32_scalar
+; CHECK: %[[#s32_arg:]] = OpFunctionParameter %[[#F32Ty]]
+; CHECK: %[[#s32_mul:]] = OpFMul %[[#F32Ty]] %[[#Constant_s32]] %[[#s32_arg]]
+; CHECK: %[[#s32_ret:]] = OpExtInst %[[#F32Ty]] %[[#ExtInstId]] Exp2 %[[#s32_mul]]
+; CHECK: OpReturnValue %[[#s32_ret]]
+; CHECK-LABEL: OpFunctionEnd
+define float @test_exp10_f32_scalar(float %x) {
+    %res = call float @llvm.exp10.f32(float %x)
+    ret float %res
+}
+
+; CHECK-LABEL: Begin function test_exp10_f64_scalar
+; CHECK: %[[#s64_arg:]] = OpFunctionParameter %[[#F64Ty]]
+; CHECK: %[[#s64_mul:]] = OpFMul %[[#F64Ty]] %[[#Constant_s64]] %[[#s64_arg]]
+; CHECK: %[[#s64_ret:]] = OpExtInst %[[#F64Ty]] %[[#ExtInstId]] Exp2 %[[#s64_mul]]
+; CHECK: OpReturnValue %[[#s64_ret]]
+; CHECK-LABEL: OpFunctionEnd
+define double @test_exp10_f64_scalar(double %x) {
+    %res = call double @llvm.exp10.f64(double %x)
+    ret double %res
+}
+
+; CHECK-LABEL: Begin function test_exp10_f16_vec2
+; CHECK: %[[#v2_16_arg:]] = OpFunctionParameter %[[#Vec2_16Ty]]
+; CHECK: %[[#v2_16_mul:]] = OpVectorTimesScalar %[[#Vec2_16Ty]] %[[#Constant_s16]] %[[#v2_16_arg]]
+; CHECK: %[[#v2_16_ret:]] = OpExtInst %[[#Vec2_16Ty]] %[[#ExtInstId]] Exp2 %[[#v2_16_mul]]
+; CHECK: OpReturnValue %[[#v2_16_ret]]
+; CHECK-LABEL: OpFunctionEnd
+define <2 x half> @test_exp10_f16_vec2(<2 x half>  %x) {
+    %res = call <2 x half>  @llvm.exp10.f16(<2 x half>  %x)
+    ret <2 x half>  %res
+}
+
+; CHECK-LABEL: Begin function test_exp10_f32_vec2
+; CHECK: %[[#v2_32_arg:]] = OpFunctionParameter %[[#Vec2_32Ty]]
+; CHECK: %[[#v2_32_mul:]] = OpVectorTimesScalar %[[#Vec2_32Ty]] %[[#Constant_s32]] %[[#v2_32_arg]]
+; CHECK: %[[#v2_32_ret:]] = OpExtInst %[[#Vec2_32Ty]] %[[#ExtInstId]] Exp2 %[[#v2_32_mul]]
+; CHECK: OpReturnValue %[[#v2_32_ret]]
+; CHECK-LABEL: OpFunctionEnd
+define <2 x float> @test_exp10_f32_vec2(<2 x float>  %x) {
+    %res = call <2 x float>  @llvm.exp10.f32(<2 x float>  %x)
+    ret <2 x float>  %res
+}
+
+; CHECK-LABEL: Begin function test_exp10_f64_vec2
+; CHECK: %[[#v2_64_arg:]] = OpFunctionParameter %[[#Vec2_64Ty]]
+; CHECK: %[[#v2_64_mul:]] = OpVectorTimesScalar %[[#Vec2_64Ty]] %[[#Constant_s64]] %[[#v2_64_arg]]
+; CHECK: %[[#v2_64_ret:]] = OpExtInst %[[#Vec2_64Ty]] %[[#ExtInstId]] Exp2 %[[#v2_64_mul]]
+; CHECK: OpReturnValue %[[#v2_64_ret]]
+; CHECK-LABEL: OpFunctionEnd
+define <2 x double> @test_exp10_f64_vec2(<2 x double>  %x) {
+    %res = call <2 x double>  @llvm.exp10.f64(<2 x double>  %x)
+    ret <2 x double>  %res
+}
+
+
+; CHECK-LABEL: Begin function test_exp10_f16_vec4
+; CHECK: %[[#v4_16_arg:]] = OpFunctionParameter %[[#Vec4_16Ty]]
+; CHECK: %[[#v4_16_mul:]] = OpVectorTimesScalar %[[#Vec4_16Ty]] %[[#Constant_s16]] %[[#v4_16_arg]]
+; 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
+}
+
+; CHECK-LABEL: Begin function test_exp10_f32_vec4
+; CHECK: %[[#v4_32_arg:]] = OpFunctionParameter %[[#Vec4_32Ty]]
+; CHECK: %[[#v4_32_mul:]] = OpVectorTimesScalar %[[#Vec4_32Ty]] %[[#Constant_s32]] %[[#v4_32_arg]]
+; CHECK: %[[#v4_32_ret:]] = OpExtInst %[[#Vec4_32Ty]] %[[#ExtInstId]] Exp2 %[[#v4_32_mul]]
+; CHECK: OpReturnValue %[[#v4_32_ret]]
+; CHECK-LABEL: OpFunctionEnd
+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_f64_vec4
+; CHECK: %[[#v4_64_arg:]] = OpFunctionParameter %[[#Vec4_64Ty]]
+; CHECK: %[[#v4_64_mul:]] = OpVectorTimesScalar %[[#Vec4_64Ty]] %[[#Constant_s64]] %[[#v4_64_arg]]
+; CHECK: %[[#v4_64_ret:]] = OpExtInst %[[#Vec4_64Ty]] %[[#ExtInstId]] Exp2 %[[#v4_64_mul]]
+; CHECK: OpReturnValue %[[#v4_64_ret]]
+; CHECK-LABEL: OpFunctionEnd
+define <4 x double> @test_exp10_f64_vec4(<4 x double>  %x) {
+    %res = call <4 x double>  @llvm.exp10.f64(<4 x double>  %x)
+    ret <4 x double>  %res
+}
diff --git a/llvm/test/CodeGen/SPIRV/llvm-intrinsics/exp10-opencl.ll b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/exp10-opencl.ll
new file mode 100644
index 0000000000000..dd4340b85ab0d
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/exp10-opencl.ll
@@ -0,0 +1,108 @@
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; Test if llvm.exp10 is lowered to opencl::exp10 with the result correctly
+;reused by the original llvm.exp10 user.
+
+
+; CHECK-DAG: %[[#ExtInstId:]] = OpExtInstImport "OpenCL.std"
+; CHECK-DAG: %[[#F16Ty:]] = OpTypeFloat 16
+; CHECK-DAG: %[[#F32Ty:]] = OpTypeFloat 32
+; CHECK-DAG: %[[#F64Ty:]] = OpTypeFloat 64
+; CHECK-DAG: %[[#Vec2_16Ty:]] = OpTypeVector %[[#F16Ty]] 2
+; CHECK-DAG: %[[#Vec2_32Ty:]] = OpTypeVector %[[#F32Ty]] 2
+; CHECK-DAG: %[[#Vec2_64Ty:]] = OpTypeVector %[[#F64Ty]] 2
+; CHECK-DAG: %[[#Vec16_16Ty:]] = OpTypeVector %[[#F16Ty]] 16
+; CHECK-DAG: %[[#Vec16_32y:]] = OpTypeVector %[[#F32Ty]] 16
+; CHECK-DAG: %[[#Vec16_64Ty:]] = OpTypeVector %[[#F64Ty]] 16
+
+; CHECK-LABEL: Begin function test_exp10_f16_scalar
+; CHECK: %[[#s16_arg:]] = OpFunctionParameter %[[#F16Ty]]
+; CHECK: %[[#s16_ret:]] = OpExtInst %[[#F16Ty]] %[[#ExtInstId]] exp10 %[[#s16_arg]]
+; 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_f32_scalar
+; CHECK: %[[#s32_arg:]] = OpFunctionParameter %[[#F32Ty]]
+; CHECK: %[[#s32_ret:]] = OpExtInst %[[#F32Ty]] %[[#ExtInstId]] exp10 %[[#s32_arg]]
+; CHECK: OpReturnValue %[[#s32_ret]]
+; CHECK-LABEL: OpFunctionEnd
+define float @test_exp10_f32_scalar(float %x) {
+    %res = call float @llvm.exp10.f32(float %x)
+    ret float %res
+}
+
+; CHECK-LABEL: Begin function test_exp10_f64_scalar
+; CHECK: %[[#s64_arg:]] = OpFunctionParameter %[[#F64Ty]]
+; CHECK: %[[#s64_ret:]] = OpExtInst %[[#F64Ty]] %[[#ExtInstId]] exp10 %[[#s64_arg]]
+; CHECK: OpReturnValue %[[#s64_ret]]
+; CHECK-LABEL: OpFunctionEnd
+define double @test_exp10_f64_scalar(double %x) {
+    %res = call double @llvm.exp10.f64(double %x)
+    ret double %res
+}
+
+; CHECK-LABEL: Begin function test_exp10_f16_vec2
+; CHECK: %[[#v2_16_arg:]] = OpFunctionParameter %[[#Vec2_16Ty]]
+; CHECK: %[[#v2_16_ret:]] = OpExtInst %[[#Vec2_16Ty]] %[[#ExtInstId]] exp10 %[[#v2_16_arg]]
+; CHECK: OpReturnValue %[[#v2_16_ret]]
+; CHECK-LABEL: OpFunctionEnd
+define <2 x half> @test_exp10_f16_vec2(<2 x half>  %x) {
+    %res = call <2 x half>  @llvm.exp10.v2f16(<2 x half>  %x)
+    ret <2 x half>  %res
+}
+
+; CHECK-LABEL: Begin function test_exp10_f32_vec2
+; CHECK: %[[#v2_32_arg:]] = OpFunctionParameter %[[#Vec2_32Ty]]
+; CHECK: %[[#v2_32_ret:]] = OpExtInst %[[#Vec2_32Ty]] %[[#ExtInstId]] exp10 %[[#v2_32_arg]]
+; CHECK: OpReturnValue %[[#v2_32_ret]]
+; CHECK-LABEL: OpFunctionEnd
+define <2 x float> @test_exp10_f32_vec2(<2 x float>  %x) {
+    %res = call <2 x float>  @llvm.exp10.v2f32(<2 x float>  %x)
+    ret <2 x float>  %res
+}
+
+; CHECK-LABEL: Begin function test_exp10_f64_vec2
+; CHECK: %[[#v2_64_arg:]] = OpFunctionParameter %[[#Vec2_64Ty]]
+; CHECK: %[[#v2_64_ret:]] = OpExtInst %[[#Vec2_64Ty]] %[[#ExtInstId]] exp10 %[[#v2_64_arg]]
+; CHECK: OpReturnValue %[[#v2_64_ret]]
+; CHECK-LABEL: OpFunctionEnd
+define <2 x double> @test_exp10_f64_vec2(<2 x double>  %x) {
+    %res = call <2 x double>  @llvm.exp10.v2f64(<2 x double>  %x)
+    ret <2 x double>  %res
+}
+
+
+; CHECK-LABEL: Begin function test_exp10_f16_vec16
+; CHECK: %[[#v16_16_arg:]] = OpFunctionParameter %[[#Vec16_16Ty]]
+; CHECK: %[[#v16_16_ret:]] = OpExtInst %[[#Vec16_16Ty]] %[[#ExtInstId]] exp10 %[[#v16_16_arg]]
+; CHECK: OpReturnValue %[[#v16_16_ret]]
+; CHECK-LABEL: OpFunctionEnd
+define <16 x half> @test_exp10_f16_vec16(<16 x half>  %x) {
+    %res = call <16 x half>  @llvm.exp10.v16f16(<16 x half>  %x)
+    ret <16 x half>  %res
+}
+
+; CHECK-LABEL: Begin function test_exp10_f32_vec16
+; CHECK: %[[#v16_32_arg:]] = OpFunctionParameter %[[#Vec16_32y]]
+; CHECK: %[[#v16_32_ret:]] = OpExtInst %[[#Vec16_32y]] %[[#ExtInstId]] exp10 %[[#v16_32_arg]]
+; CHECK: OpReturnValue %[[#v16_32_ret]]
+; CHECK-LABEL: OpFunctionEnd
+define <16 x float> @test_exp10_f32_vec16(<16 x float>  %x) {
+    %res = call <16 x float>  @llvm.exp10.v16f32(<16 x float>  %x)
+    ret <16 x float>  %res
+}
+
+; CHECK-LABEL: Begin function test_exp10_f64_vec16
+; CHECK: %[[#v16_64_arg:]] = OpFunctionParameter %[[#Vec16_64Ty]]
+; CHECK: %[[#v16_64_ret:]] = OpExtInst %[[#Vec16_64Ty]] %[[#ExtInstId]] exp10 %[[#v16_64_arg]]
+; CHECK: OpReturnValue %[[#v16_64_ret]]
+; CHECK-LABEL: OpFunctionEnd
+define <16 x double> @test_exp10_f64_vec16(<16 x double>  %x) {
+    %res = call <16 x double>  @llvm.exp10.v16f64(<16 x double>  %x)
+    ret <16 x double>  %res
+}

>From 255af7df1594ecb91d441131d60c87fb879087b0 Mon Sep 17 00:00:00 2001
From: idubinov <igor.dubinov at amd.com>
Date: Fri, 20 Feb 2026 04:10:19 -0600
Subject: [PATCH 2/4] code format

---
 llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp      | 4 ++--
 llvm/test/CodeGen/SPIRV/llvm-intrinsics/exp10-glsl.ll   | 4 ++--
 llvm/test/CodeGen/SPIRV/llvm-intrinsics/exp10-opencl.ll | 2 +-
 3 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index 3531e084e0122..0262f8ee13f9b 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -3317,8 +3317,8 @@ bool SPIRVInstructionSelector::selectExp10(Register ResVReg,
             std::vector<Register>({ConstReg, I.getOperand(1).getReg()}),
             Opcode))
       return false;
-    return selectExtInstWithSrcs(
-        ResVReg, ResType, I, std::vector<Register>({ArgReg}), GL::Exp2);
+    return selectExtInstWithSrcs(ResVReg, ResType, I,
+                                 std::vector<Register>({ArgReg}), GL::Exp2);
   }
 
   return false;
diff --git a/llvm/test/CodeGen/SPIRV/llvm-intrinsics/exp10-glsl.ll b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/exp10-glsl.ll
index 9662df6b5bbf9..07042a39ee662 100644
--- a/llvm/test/CodeGen/SPIRV/llvm-intrinsics/exp10-glsl.ll
+++ b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/exp10-glsl.ll
@@ -1,8 +1,8 @@
 ; 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 if llvm.exp10 is lowered to opencl::exp10 with the result correctly
-;reused by the original llvm.exp10 user.
+; Test if llvm.exp10 is lowered with the result correctly reused by the
+; original llvm.exp10 user.
 
 
 ; CHECK-DAG: %[[#ExtInstId:]] = OpExtInstImport "GLSL.std.450"
diff --git a/llvm/test/CodeGen/SPIRV/llvm-intrinsics/exp10-opencl.ll b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/exp10-opencl.ll
index dd4340b85ab0d..f98179546b245 100644
--- a/llvm/test/CodeGen/SPIRV/llvm-intrinsics/exp10-opencl.ll
+++ b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/exp10-opencl.ll
@@ -2,7 +2,7 @@
 ; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
 
 ; Test if llvm.exp10 is lowered to opencl::exp10 with the result correctly
-;reused by the original llvm.exp10 user.
+; reused by the original llvm.exp10 user.
 
 
 ; CHECK-DAG: %[[#ExtInstId:]] = OpExtInstImport "OpenCL.std"

>From 424b1d0f2aff7902fb06ab5c51e87944d2338962 Mon Sep 17 00:00:00 2001
From: idubinov <igor.dubinov at amd.com>
Date: Fri, 20 Feb 2026 09:48:13 -0600
Subject: [PATCH 3/4] Address review comments

---
 .../Target/SPIRV/SPIRVInstructionSelector.cpp | 163 +++++++-----------
 1 file changed, 63 insertions(+), 100 deletions(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index 0262f8ee13f9b..51b5888590dcf 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -143,7 +143,7 @@ class SPIRVInstructionSelector : public InstructionSelector {
                          const MachineInstr *Init = nullptr) const;
 
   bool selectOpWithSrcs(Register ResVReg, SPIRVTypeInst ResType,
-                        MachineInstr &I, std::vector<Register> SrcRegs,
+                        MachineInstr &I, ArrayRef<Register> SrcRegs,
                         unsigned Opcode) const;
 
   bool selectUnOp(Register ResVReg, SPIRVTypeInst ResType, MachineInstr &I,
@@ -324,18 +324,9 @@ class SPIRVInstructionSelector : public InstructionSelector {
   bool selectExtInstForLRound(Register ResVReg, SPIRVTypeInst ResType,
                               MachineInstr &I,
                               const ExtInstList &ExtInsts) const;
-  bool selectExtInstWithSrcs(Register ResVReg, SPIRVTypeInst RestType,
-                             MachineInstr &I, std::vector<Register> SrcRegs,
-                             GL::GLSLExtInst GLInst) const;
-  bool selectExtInstWithSrcs(Register ResVReg, SPIRVTypeInst ResType,
-                             MachineInstr &I, std::vector<Register> SrcRegs,
-                             CL::OpenCLExtInst CLInst) const;
-  bool selectExtInstWithSrcs(Register ResVReg, SPIRVTypeInst ResType,
-                             MachineInstr &I, std::vector<Register> SrcRegs,
-                             CL::OpenCLExtInst CLInst,
-                             GL::GLSLExtInst GLInst) const;
+
   bool selectExtInstWithSrcs(Register ResVReg, SPIRVTypeInst ResType,
-                             MachineInstr &I, std::vector<Register> SrcRegs,
+                             MachineInstr &I, ArrayRef<Register> SrcRegs,
                              const ExtInstList &ExtInsts) const;
 
   bool selectLog10(Register ResVReg, SPIRVTypeInst ResType,
@@ -1363,75 +1354,48 @@ bool SPIRVInstructionSelector::selectExtInst(Register ResVReg,
                                              MachineInstr &I,
                                              const ExtInstList &Insts) const {
 
-  for (const auto &Ex : Insts) {
-    SPIRV::InstructionSet::InstructionSet Set = Ex.first;
-    uint32_t Opcode = Ex.second;
-    if (STI.canUseExtInstSet(Set)) {
-      MachineBasicBlock &BB = *I.getParent();
-      auto MIB = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpExtInst))
-                     .addDef(ResVReg)
-                     .addUse(GR.getSPIRVTypeID(ResType))
-                     .addImm(static_cast<uint32_t>(Set))
-                     .addImm(Opcode)
-                     .setMIFlags(I.getFlags());
-      const unsigned NumOps = I.getNumOperands();
-      unsigned Index = 1;
-      if (Index < NumOps &&
-          I.getOperand(Index).getType() ==
-              MachineOperand::MachineOperandType::MO_IntrinsicID)
-        Index = 2;
-      for (; Index < NumOps; ++Index)
-        MIB.add(I.getOperand(Index));
-      MIB.constrainAllUses(TII, TRI, RBI);
-      return true;
-    }
+  for (const auto &[InstructionSet, Opcode] : Insts) {
+    if (!STI.canUseExtInstSet(InstructionSet))
+      continue;
+    MachineBasicBlock &BB = *I.getParent();
+    auto MIB = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpExtInst))
+                    .addDef(ResVReg)
+                    .addUse(GR.getSPIRVTypeID(ResType))
+                    .addImm(static_cast<uint32_t>(InstructionSet))
+                    .addImm(Opcode)
+                    .setMIFlags(I.getFlags());
+    const unsigned NumOps = I.getNumOperands();
+    unsigned Index = 1;
+    if (Index < NumOps &&
+        I.getOperand(Index).getType() ==
+            MachineOperand::MachineOperandType::MO_IntrinsicID)
+      Index = 2;
+    for (; Index < NumOps; ++Index)
+      MIB.add(I.getOperand(Index));
+    MIB.constrainAllUses(TII, TRI, RBI);
+    return true;
   }
   return false;
 }
 
 bool SPIRVInstructionSelector::selectExtInstWithSrcs(
     Register ResVReg, SPIRVTypeInst ResType, MachineInstr &I,
-    std::vector<Register> SrcRegs, GL::GLSLExtInst GLInst) const {
-  ExtInstList ExtInsts = {{SPIRV::InstructionSet::GLSL_std_450, GLInst}};
-  return selectExtInstWithSrcs(ResVReg, ResType, I, SrcRegs, ExtInsts);
-}
-
-bool SPIRVInstructionSelector::selectExtInstWithSrcs(
-    Register ResVReg, SPIRVTypeInst ResType, MachineInstr &I,
-    std::vector<Register> SrcRegs, CL::OpenCLExtInst CLInst) const {
-  ExtInstList ExtInsts = {{SPIRV::InstructionSet::OpenCL_std, CLInst}};
-  return selectExtInstWithSrcs(ResVReg, ResType, I, SrcRegs, ExtInsts);
-}
-
-bool SPIRVInstructionSelector::selectExtInstWithSrcs(
-    Register ResVReg, SPIRVTypeInst ResType, MachineInstr &I,
-    std::vector<Register> SrcRegs, CL::OpenCLExtInst CLInst,
-    GL::GLSLExtInst GLInst) const {
-  ExtInstList ExtInsts = {{SPIRV::InstructionSet::OpenCL_std, CLInst},
-                          {SPIRV::InstructionSet::GLSL_std_450, GLInst}};
-  return selectExtInstWithSrcs(ResVReg, ResType, I, SrcRegs, ExtInsts);
-}
-
-bool SPIRVInstructionSelector::selectExtInstWithSrcs(
-    Register ResVReg, SPIRVTypeInst ResType, MachineInstr &I,
-    std::vector<Register> SrcRegs, const ExtInstList &Insts) const {
-  for (const auto &Ex : Insts) {
-    SPIRV::InstructionSet::InstructionSet Set = Ex.first;
-    if (STI.canUseExtInstSet(Set)) {
-      uint32_t Opcode = Ex.second;
-      MachineBasicBlock &BB = *I.getParent();
-      auto MIB = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpExtInst))
-                     .addDef(ResVReg)
-                     .addUse(GR.getSPIRVTypeID(ResType))
-                     .addImm(static_cast<uint32_t>(Set))
-                     .addImm(Opcode)
-                     .setMIFlags(I.getFlags());
-      for (Register SReg : SrcRegs) {
-        MIB.addUse(SReg);
-      }
-      MIB.constrainAllUses(TII, TRI, RBI);
-      return true;
+    ArrayRef<Register> SrcRegs, const ExtInstList &Insts) const {
+  for (const auto &[InstructionSet, Opcode] : Insts) {
+    if (!STI.canUseExtInstSet(InstructionSet))
+      continue;
+    MachineBasicBlock &BB = *I.getParent();
+    auto MIB = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpExtInst))
+                    .addDef(ResVReg)
+                    .addUse(GR.getSPIRVTypeID(ResType))
+                    .addImm(static_cast<uint32_t>(InstructionSet))
+                    .addImm(Opcode)
+                    .setMIFlags(I.getFlags());
+    for (Register SReg : SrcRegs) {
+      MIB.addUse(SReg);
     }
+    MIB.constrainAllUses(TII, TRI, RBI);
+    return true;
   }
   return false;
 }
@@ -1447,27 +1411,25 @@ bool SPIRVInstructionSelector::selectExtInstForLRound(
 bool SPIRVInstructionSelector::selectExtInstForLRound(
     Register ResVReg, SPIRVTypeInst ResType, MachineInstr &I,
     const ExtInstList &Insts) const {
-  for (const auto &Ex : Insts) {
-    SPIRV::InstructionSet::InstructionSet Set = Ex.first;
-    uint32_t Opcode = Ex.second;
-    if (STI.canUseExtInstSet(Set)) {
-      MachineBasicBlock &BB = *I.getParent();
-      auto MIB = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpExtInst))
-                     .addDef(ResVReg)
-                     .addUse(GR.getSPIRVTypeID(ResType))
-                     .addImm(static_cast<uint32_t>(Set))
-                     .addImm(Opcode);
-      const unsigned NumOps = I.getNumOperands();
-      unsigned Index = 1;
-      if (Index < NumOps &&
-          I.getOperand(Index).getType() ==
-              MachineOperand::MachineOperandType::MO_IntrinsicID)
-        Index = 2;
-      for (; Index < NumOps; ++Index)
-        MIB.add(I.getOperand(Index));
-      MIB.constrainAllUses(TII, TRI, RBI);
-      return true;
-    }
+  for (const auto &[InstructionSet, Opcode] : Insts) {
+    if (!STI.canUseExtInstSet(InstructionSet))
+      continue;
+    MachineBasicBlock &BB = *I.getParent();
+    auto MIB = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpExtInst))
+                    .addDef(ResVReg)
+                    .addUse(GR.getSPIRVTypeID(ResType))
+                    .addImm(static_cast<uint32_t>(InstructionSet))
+                    .addImm(Opcode);
+    const unsigned NumOps = I.getNumOperands();
+    unsigned Index = 1;
+    if (Index < NumOps &&
+        I.getOperand(Index).getType() ==
+            MachineOperand::MachineOperandType::MO_IntrinsicID)
+      Index = 2;
+    for (; Index < NumOps; ++Index)
+      MIB.add(I.getOperand(Index));
+    MIB.constrainAllUses(TII, TRI, RBI);
+    return true;
   }
   return false;
 }
@@ -1575,7 +1537,7 @@ bool SPIRVInstructionSelector::selectSincos(Register ResVReg,
 bool SPIRVInstructionSelector::selectOpWithSrcs(Register ResVReg,
                                                 SPIRVTypeInst ResType,
                                                 MachineInstr &I,
-                                                std::vector<Register> Srcs,
+                                                ArrayRef<Register> Srcs,
                                                 unsigned Opcode) const {
   auto MIB = BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(Opcode))
                  .addDef(ResVReg)
@@ -3287,10 +3249,10 @@ bool SPIRVInstructionSelector::selectFCmp(Register ResVReg,
 bool SPIRVInstructionSelector::selectExp10(Register ResVReg,
                                            SPIRVTypeInst ResType,
                                            MachineInstr &I) const {
-
   if (STI.canUseExtInstSet(SPIRV::InstructionSet::OpenCL_std)) {
     return selectExtInst(ResVReg, ResType, I, CL::exp10);
-  } else if (STI.canUseExtInstSet(SPIRV::InstructionSet::GLSL_std_450)) {
+  }
+  if (STI.canUseExtInstSet(SPIRV::InstructionSet::GLSL_std_450)) {
     /// There is no exp10 in GLSL. Use exp10(x) = exp2(x * log2(10)) instead
     /// log2(10) ~= 3.3219280948874
 
@@ -3314,11 +3276,12 @@ bool SPIRVInstructionSelector::selectExp10(Register ResVReg,
 
     if (!selectOpWithSrcs(
             ArgReg, ResType, I,
-            std::vector<Register>({ConstReg, I.getOperand(1).getReg()}),
+           {ConstReg, I.getOperand(1).getReg()},
             Opcode))
       return false;
     return selectExtInstWithSrcs(ResVReg, ResType, I,
-                                 std::vector<Register>({ArgReg}), GL::Exp2);
+                                 std::vector<Register>({ArgReg}),
+                                 {{SPIRV::InstructionSet::GLSL_std_450,GL::Exp2}});
   }
 
   return false;

>From 012e87adb1237c8c0961154eb5085e9f38a9d11e Mon Sep 17 00:00:00 2001
From: idubinov <igor.dubinov at amd.com>
Date: Fri, 20 Feb 2026 10:04:54 -0600
Subject: [PATCH 4/4] code format

---
 .../Target/SPIRV/SPIRVInstructionSelector.cpp | 40 +++++++++----------
 1 file changed, 19 insertions(+), 21 deletions(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index 51b5888590dcf..acd4483077d40 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -1359,11 +1359,11 @@ bool SPIRVInstructionSelector::selectExtInst(Register ResVReg,
       continue;
     MachineBasicBlock &BB = *I.getParent();
     auto MIB = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpExtInst))
-                    .addDef(ResVReg)
-                    .addUse(GR.getSPIRVTypeID(ResType))
-                    .addImm(static_cast<uint32_t>(InstructionSet))
-                    .addImm(Opcode)
-                    .setMIFlags(I.getFlags());
+                   .addDef(ResVReg)
+                   .addUse(GR.getSPIRVTypeID(ResType))
+                   .addImm(static_cast<uint32_t>(InstructionSet))
+                   .addImm(Opcode)
+                   .setMIFlags(I.getFlags());
     const unsigned NumOps = I.getNumOperands();
     unsigned Index = 1;
     if (Index < NumOps &&
@@ -1386,11 +1386,11 @@ bool SPIRVInstructionSelector::selectExtInstWithSrcs(
       continue;
     MachineBasicBlock &BB = *I.getParent();
     auto MIB = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpExtInst))
-                    .addDef(ResVReg)
-                    .addUse(GR.getSPIRVTypeID(ResType))
-                    .addImm(static_cast<uint32_t>(InstructionSet))
-                    .addImm(Opcode)
-                    .setMIFlags(I.getFlags());
+                   .addDef(ResVReg)
+                   .addUse(GR.getSPIRVTypeID(ResType))
+                   .addImm(static_cast<uint32_t>(InstructionSet))
+                   .addImm(Opcode)
+                   .setMIFlags(I.getFlags());
     for (Register SReg : SrcRegs) {
       MIB.addUse(SReg);
     }
@@ -1416,10 +1416,10 @@ bool SPIRVInstructionSelector::selectExtInstForLRound(
       continue;
     MachineBasicBlock &BB = *I.getParent();
     auto MIB = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpExtInst))
-                    .addDef(ResVReg)
-                    .addUse(GR.getSPIRVTypeID(ResType))
-                    .addImm(static_cast<uint32_t>(InstructionSet))
-                    .addImm(Opcode);
+                   .addDef(ResVReg)
+                   .addUse(GR.getSPIRVTypeID(ResType))
+                   .addImm(static_cast<uint32_t>(InstructionSet))
+                   .addImm(Opcode);
     const unsigned NumOps = I.getNumOperands();
     unsigned Index = 1;
     if (Index < NumOps &&
@@ -3274,14 +3274,12 @@ bool SPIRVInstructionSelector::selectExp10(Register ResVReg,
                       ? SPIRV::OpVectorTimesScalar
                       : SPIRV::OpFMulS;
 
-    if (!selectOpWithSrcs(
-            ArgReg, ResType, I,
-           {ConstReg, I.getOperand(1).getReg()},
-            Opcode))
+    if (!selectOpWithSrcs(ArgReg, ResType, I,
+                          {ConstReg, I.getOperand(1).getReg()}, Opcode))
       return false;
-    return selectExtInstWithSrcs(ResVReg, ResType, I,
-                                 std::vector<Register>({ArgReg}),
-                                 {{SPIRV::InstructionSet::GLSL_std_450,GL::Exp2}});
+    return selectExtInstWithSrcs(
+        ResVReg, ResType, I, std::vector<Register>({ArgReg}),
+        {{SPIRV::InstructionSet::GLSL_std_450, GL::Exp2}});
   }
 
   return false;



More information about the llvm-commits mailing list