Skip to content

Conversation

EbinJose2002
Copy link
Contributor

  • Make use of the OpenCL extended instruction frexp.
  • Creates a variable and passes it to OpExtInst instruction

- Make use of the OpenCL extended instruction frexp.
- Creates a variable and passes it to OpExtInst instruction
@llvmbot
Copy link
Member

llvmbot commented Sep 8, 2025

@llvm/pr-subscribers-backend-spir-v

Author: Ebin-McW (EbinJose2002)

Changes
  • Make use of the OpenCL extended instruction frexp.
  • Creates a variable and passes it to OpExtInst instruction

Full diff: https://github.com/llvm/llvm-project/pull/157436.diff

3 Files Affected:

  • (modified) llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp (+52-1)
  • (modified) llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp (+3)
  • (added) llvm/test/CodeGen/SPIRV/llvm-intrinsics/frexp.ll (+114)
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index 98c7709acf938..a18515dd14fda 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -300,7 +300,8 @@ class SPIRVInstructionSelector : public InstructionSelector {
                                 MachineInstr &I) const;
   bool selectModf(Register ResVReg, const SPIRVType *ResType,
                   MachineInstr &I) const;
-
+  bool selectFrexp(Register ResVReg, const SPIRVType *ResType,
+                   MachineInstr &I) const;
   // Utilities
   std::pair<Register, bool>
   buildI32Constant(uint32_t Val, MachineInstr &I,
@@ -806,6 +807,9 @@ bool SPIRVInstructionSelector::spvSelect(Register ResVReg,
   case TargetOpcode::G_USUBSAT:
     return selectExtInst(ResVReg, ResType, I, CL::u_sub_sat);
 
+  case TargetOpcode::G_FFREXP:
+    return selectFrexp(ResVReg, ResType, I);
+
   case TargetOpcode::G_UADDO:
     return selectOverflowArith(ResVReg, ResType, I,
                                ResType->getOpcode() == SPIRV::OpTypeVector
@@ -1045,6 +1049,53 @@ bool SPIRVInstructionSelector::selectExtInst(Register ResVReg,
   return false;
 }
 
+bool SPIRVInstructionSelector::selectFrexp(Register ResVReg,
+                                           const SPIRVType *ResType,
+                                           MachineInstr &I) const {
+  ExtInstList ExtInsts = {{SPIRV::InstructionSet::OpenCL_std, CL::frexp},
+                          {SPIRV::InstructionSet::GLSL_std_450, GL::Frexp}};
+  for (const auto &Ex : ExtInsts) {
+    SPIRV::InstructionSet::InstructionSet Set = Ex.first;
+    uint32_t Opcode = Ex.second;
+    if (!STI.canUseExtInstSet(Set))
+      continue;
+
+    MachineIRBuilder MIRBuilder(I);
+    SPIRVType *PointeeTy = GR.getSPIRVTypeForVReg(I.getOperand(1).getReg());
+    const SPIRVType *PointerType = GR.getOrCreateSPIRVPointerType(
+        PointeeTy, MIRBuilder, SPIRV::StorageClass::Function);
+    Register PointerVReg =
+        createVirtualRegister(PointerType, &GR, MRI, MRI->getMF());
+
+    auto It = getOpVariableMBBIt(I);
+    auto MIB = BuildMI(*It->getParent(), It, It->getDebugLoc(),
+                       TII.get(SPIRV::OpVariable))
+                   .addDef(PointerVReg)
+                   .addUse(GR.getSPIRVTypeID(PointerType))
+                   .addImm(static_cast<uint32_t>(SPIRV::StorageClass::Function))
+                   .constrainAllUses(TII, TRI, RBI);
+
+    MIB = MIB &
+          BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(SPIRV::OpExtInst))
+              .addDef(ResVReg)
+              .addUse(GR.getSPIRVTypeID(ResType))
+              .addImm(static_cast<uint32_t>(Ex.first))
+              .addImm(Opcode)
+              .add(I.getOperand(2))
+              .addUse(PointerVReg)
+              .constrainAllUses(TII, TRI, RBI);
+
+    MIB = MIB &
+          BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(SPIRV::OpLoad))
+              .addDef(I.getOperand(1).getReg())
+              .addUse(GR.getSPIRVTypeID(PointeeTy))
+              .addUse(PointerVReg)
+              .constrainAllUses(TII, TRI, RBI);
+    return MIB;
+  }
+  return false;
+}
+
 bool SPIRVInstructionSelector::selectOpWithSrcs(Register ResVReg,
                                                 const SPIRVType *ResType,
                                                 MachineInstr &I,
diff --git a/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp b/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
index 170bddd507e3b..06e66dd44f05a 100644
--- a/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
@@ -286,6 +286,9 @@ SPIRVLegalizerInfo::SPIRVLegalizerInfo(const SPIRVSubtarget &ST) {
   // Control-flow. In some cases (e.g. constants) s1 may be promoted to s32.
   getActionDefinitionsBuilder(G_BRCOND).legalFor({s1, s32});
 
+  getActionDefinitionsBuilder(G_FFREXP).legalForCartesianProduct(
+      allFloatScalarsAndVectors, {s32, v2s32, v3s32, v4s32, v8s32, v16s32});
+
   // TODO: Review the target OpenCL and GLSL Extended Instruction Set specs to
   // tighten these requirements. Many of these math functions are only legal on
   // specific bitwidths, so they are not selectable for
diff --git a/llvm/test/CodeGen/SPIRV/llvm-intrinsics/frexp.ll b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/frexp.ll
new file mode 100644
index 0000000000000..0a4a14abe42f8
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/frexp.ll
@@ -0,0 +1,114 @@
+; 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 %}
+
+; CHECK: %[[#extinst_id:]] = OpExtInstImport "OpenCL.std"
+; CHECK: %[[#float_32_type:]] = OpTypeFloat 32
+; CHECK: %[[#int_32_type:]] = OpTypeInt 32 0
+; CHECK: %[[#fn_ptr_type_i32:]] = OpTypePointer Function %[[#int_32_type]]
+; CHECK: %[[#const_negzero:]] = OpConstant %[[#float_32_type]] -0
+; CHECK: %[[#vec2_float_type:]] = OpTypeVector %[[#float_32_type]] 2
+; CHECK: %[[#vec2_int_type:]] = OpTypeVector %[[#int_32_type]] 2
+; CHECK: %[[#fn_ptr_type_vec2_i32:]] = OpTypePointer Function %[[#vec2_int_type]]
+; CHECK: %[[#vec2_null:]] = OpConstantNull %[[#vec2_float_type]]
+; CHECK: %[[#scalar_null:]] = OpConstantNull %[[#float_32_type]]
+; CHECK: %[[#const_composite1:]] = OpConstantComposite %[[#vec2_float_type]] %[[#scalar_null]] %[[#const_negzero]]
+; CHECK: %[[#vec4_float_type:]] = OpTypeVector %[[#float_32_type]] 4
+; CHECK: %[[#vec4_int_type:]] = OpTypeVector %[[#int_32_type]] 4
+; CHECK: %[[#fn_ptr_type_vec4_i32:]] = OpTypePointer Function %[[#vec4_int_type]]
+; CHECK: %[[#const_composite2:]] = OpConstantComposite %[[#vec4_float_type]] %[[#const_16:]] %[[#const_neg32:]] %[[#const_0:]] %[[#const_9999:]]
+; CHECK: %[[#float_64_type:]] = OpTypeFloat 64
+; CHECK: %[[#vec2_double_type:]] = OpTypeVector %[[#float_64_type]] 2
+
+; CHECK: %[[#]] = OpFunctionParameter %[[#float_32_type]]
+; CHECK: %[[#var1:]] = OpVariable %[[#fn_ptr_type_i32]] Function
+; CHECK: %[[#extinst1:]] = OpExtInst %[[#float_32_type]] %[[#extinst_id]] frexp %[[#const_negzero]] %[[#var1]]
+; CHECK: %[[#exp_part_var:]] = OpLoad %[[#int_32_type]] %[[#var1]]
+; CHECK: OpReturnValue %[[#exp_part_var]]
+define i32 @frexp_negzero(float %x) {
+  %ret = call { float, i32 } @llvm.frexp.f32.i32(float -0.0)
+  %f_part = extractvalue { float, i32 } %ret, 0
+  %exp_part = extractvalue { float, i32 } %ret, 1
+  ret i32 %exp_part
+}
+
+; CHECK: %[[#x_var4:]] = OpFunctionParameter %[[#float_32_type]]
+; CHECK: %[[#var10:]] = OpVariable %[[#fn_ptr_type_i32]] Function
+; CHECK: %[[#extinst10:]] = OpExtInst %[[#float_32_type]] %[[#extinst_id]] frexp %[[#x_var4]] %[[#var10]]
+; CHECK: %[[#exp_part_var2:]] = OpLoad %[[#int_32_type]] %[[#var10]]
+; CHECK: OpReturnValue %[[#exp_part_var2]]
+define i32 @frexp_frexp_get_int(float %x) {
+  %frexp0 = call { float, i32 } @llvm.frexp.f32.i32(float %x)
+  %f_part = extractvalue { float, i32 } %frexp0, 0
+  %exp_part = extractvalue { float, i32 } %frexp0, 1
+  ret i32 %exp_part
+}
+
+; CHECK: %[[#var3:]] = OpVariable %[[#fn_ptr_type_vec2_i32]] Function
+; CHECK: %[[#extinst3:]] = OpExtInst %[[#vec2_float_type]] %[[#extinst_id]] frexp %[[#vec2_null]] %[[#var3]]
+; CHECK: %[[#f_part_var2:]] = OpLoad %[[#vec2_int_type]] %[[#var3]]
+; CHECK: OpReturnValue %[[#extinst3]]
+define <2 x float> @frexp_zero_vector() {
+  %ret = call { <2 x float>, <2 x i32> } @llvm.frexp.v2f32.v2i32(<2 x float> zeroinitializer)
+  %f_part = extractvalue { <2 x float>, <2 x i32> } %ret, 0
+  %exp_part = extractvalue { <2 x float>, <2 x i32> } %ret, 1
+  ret <2 x float> %f_part
+}
+
+; CHECK: %[[#var4:]] = OpVariable %[[#fn_ptr_type_vec2_i32]] Function
+; CHECK: %[[#extinst4:]] = OpExtInst %[[#vec2_float_type]] %[[#extinst_id]] frexp %[[#const_composite1]] %[[#var4]]
+; CHECK: %[[#f_part_var3:]] = OpLoad %[[#vec2_int_type]] %[[#var4]]
+; CHECK: OpReturnValue %[[#extinst4]]
+define <2 x float> @frexp_zero_negzero_vector() {
+  %ret = call { <2 x float>, <2 x i32> } @llvm.frexp.v2f32.v2i32(<2 x float> <float 0.0, float -0.0>)
+  %f_part = extractvalue { <2 x float>, <2 x i32> } %ret, 0
+  %exp_part = extractvalue { <2 x float>, <2 x i32> } %ret, 1
+  ret <2 x float> %f_part
+}
+
+; CHECK: %[[#var5:]] = OpVariable %[[#fn_ptr_type_vec4_i32]] Function
+; CHECK: %[[#extinst5:]] = OpExtInst %[[#vec4_float_type]] %[[#extinst_id]] frexp %[[#const_composite2]] %[[#var5]]
+; CHECK: %[[#f_part_var4:]] = OpLoad %[[#vec4_int_type]] %[[#var5]]
+; CHECK: OpReturnValue %[[#extinst5]]
+define <4 x float> @frexp_nonsplat_vector() {
+    %ret = call { <4 x float>, <4 x i32> } @llvm.frexp.v4f32.v4i32(<4 x float> <float 16.0, float -32.0, float 0.0, float 9999.0>)
+    %f_part = extractvalue { <4 x float>, <4 x i32> } %ret, 0
+    %exp_part = extractvalue { <4 x float>, <4 x i32> } %ret, 1
+  ret <4 x float> %f_part
+}
+
+; CHECK: %[[#x_var2:]] = OpFunctionParameter %[[#float_32_type]]
+; CHECK: %[[#var6:]] = OpVariable %[[#fn_ptr_type_i32]] Function
+; CHECK: %[[#var7:]] = OpVariable %[[#fn_ptr_type_i32]] Function
+; CHECK: %[[#extinst6:]] = OpExtInst %[[#float_32_type]] %[[#extinst_id]] frexp %[[#x_var2]] %[[#var6]]
+; CHECK: %[[#load1:]] = OpLoad %[[#int_32_type]] %[[#var6]]
+; CHECK: %[[#extinst7:]] = OpExtInst %[[#float_32_type]] %[[#extinst_id]] frexp %[[#extinst6]] %[[#var7]]
+; CHECK: %[[#f_part_var5:]] = OpLoad %[[#int_32_type]] %[[#var7]]
+; CHECK: OpReturnValue %[[#extinst7]]
+define float @frexp_frexp(float %x) {
+  %frexp0 = call { float, i32 } @llvm.frexp.f32.i32(float %x)
+  %frexp0_f_part = extractvalue { float, i32 } %frexp0, 0
+  %frexp0_exp_part = extractvalue { float, i32 } %frexp0, 1
+  %frexp1 = call { float, i32 } @llvm.frexp.f32.i32(float %frexp0_f_part)
+  %frexp1_f_part = extractvalue { float, i32 } %frexp1, 0
+  %frexp1_exp_part = extractvalue { float, i32 } %frexp1, 1
+  ret float %frexp1_f_part
+}
+
+; CHECK: %[[#x_var3:]] = OpFunctionParameter %[[#vec2_double_type]]
+; CHECK: %[[#var9:]] = OpVariable %[[#fn_ptr_type_vec2_i32]] Function
+; CHECK: %[[#extinst9:]] = OpExtInst %[[#vec2_double_type]] %[[#extinst_id]] frexp %[[#x_var3]] %[[#var9]]
+; CHECK: %[[#f_part_var6:]] = OpLoad %[[#vec2_int_type]] %[[#var9]]
+; CHECK: OpReturnValue %[[#extinst9]]
+define <2 x double> @frexp_frexp_vector(<2 x double> %x) {
+  %frexp0 = call { <2 x double>, <2 x i32> } @llvm.frexp.v2f64.v2i32(<2 x double> %x)
+  %f_part = extractvalue { <2 x double>, <2 x i32> } %frexp0, 0
+  %exp_part = extractvalue { <2 x double>, <2 x i32> } %frexp0, 1
+  ret <2 x double> %f_part
+}
+
+declare { float, i32 } @llvm.frexp.f32.i32(float)
+declare { double, i32 } @llvm.frexp.f64.i32(double)
+declare { <2 x float>, <2 x i32> } @llvm.frexp.v2f32.v2i32(<2 x float>)
+declare { <4 x float>, <4 x i32> } @llvm.frexp.v4f32.v4i32(<4 x float>)
+declare { <2 x double>, <2 x i32> } @llvm.frexp.v2f64.v2i32(<2 x double>)
+declare  { float, i8 } @llvm.frexp.f32.i8(float)

@michalpaszkowski michalpaszkowski merged commit 12f42e5 into llvm:main Sep 28, 2025
9 of 10 checks passed
mahesh-attarde pushed a commit to mahesh-attarde/llvm-project that referenced this pull request Oct 3, 2025
- Make use of the OpenCL extended instruction frexp.
- Creates a variable and passes it to OpExtInst instruction
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants