[llvm] [SPIRV] Implement translation for llvm.modf.* intrinsics (PR #147556)

via llvm-commits llvm-commits at lists.llvm.org
Tue Jul 8 08:46:07 PDT 2025


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

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

Author: Marcos Maronas (maarquitos14)

<details>
<summary>Changes</summary>

Based on https://github.com/KhronosGroup/SPIRV-LLVM-Translator/pull/3100, I'm adding translation for `llvm.modf.*` intrinsics.

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


2 Files Affected:

- (modified) llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp (+76) 
- (modified) llvm/test/CodeGen/SPIRV/llvm-intrinsics/fp-intrinsics.ll (+22) 


``````````diff
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index 40a0bd97adaf9..ad6025804608d 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -296,6 +296,8 @@ class SPIRVInstructionSelector : public InstructionSelector {
   bool selectImageWriteIntrinsic(MachineInstr &I) const;
   bool selectResourceGetPointer(Register &ResVReg, const SPIRVType *ResType,
                                 MachineInstr &I) const;
+  bool selectModf(Register ResVReg, const SPIRVType *ResType,
+                  MachineInstr &I) const;
 
   // Utilities
   std::pair<Register, bool>
@@ -3207,6 +3209,9 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
   case Intrinsic::spv_discard: {
     return selectDiscard(ResVReg, ResType, I);
   }
+  case Intrinsic::modf: {
+    return selectModf(ResVReg, ResType, I);
+  }
   default: {
     std::string DiagMsg;
     raw_string_ostream OS(DiagMsg);
@@ -3990,6 +3995,77 @@ bool SPIRVInstructionSelector::selectLog10(Register ResVReg,
                        .constrainAllUses(TII, TRI, RBI);
 }
 
+bool SPIRVInstructionSelector::selectModf(Register ResVReg,
+                                          const SPIRVType *ResType,
+                                          MachineInstr &I) const {
+  // llvm.modf has a single arg --the number to be decomposed-- and returns a
+  // struct { restype, restype }, while OpenCLLIB::modf has two args --the
+  // number to be decomposed and a pointer--, returns the fractional part and
+  // the integral part is stored in the pointer argument. Therefore, we can't
+  // use directly the OpenCLLIB::modf intrinsic. However, we can do some
+  // scaffolding to make it work. The idea is to create an alloca instruction
+  // to get a ptr, pass this ptr to OpenCL::modf, and then load the value
+  // from this ptr to place it in the struct. llvm.modf returns the fractional
+  // part as the first element of the result, and the integral part as the
+  // second element of the result.
+
+  // At this point, the return type is not a struct anymore, but rather two
+  // independent elements of SPIRVResType. We can get each independent element
+  // from I.getDefs() or I.getOperands().
+  ExtInstList ExtInsts = {{SPIRV::InstructionSet::OpenCL_std, CL::modf},
+                          {SPIRV::InstructionSet::GLSL_std_450, GL::Modf}};
+  for (const auto &Ex : ExtInsts) {
+    SPIRV::InstructionSet::InstructionSet Set = Ex.first;
+    uint32_t Opcode = Ex.second;
+    if (STI.canUseExtInstSet(Set)) {
+      MachineIRBuilder MIRBuilder(I);
+      // Get pointer type for alloca variable.
+      const SPIRVType *PtrType = GR.getOrCreateSPIRVPointerType(
+          ResType, MIRBuilder, SPIRV::StorageClass::Input);
+      // Create new register for the pointer type of alloca variable.
+      Register NewRegister =
+          MIRBuilder.getMRI()->createVirtualRegister(&SPIRV::iIDRegClass);
+      MIRBuilder.getMRI()->setType(NewRegister, LLT::pointer(0, 64));
+      // Assign SPIR-V type of the pointer type of the alloca variable to the
+      // new register.
+      GR.assignSPIRVTypeToVReg(PtrType, NewRegister, MIRBuilder.getMF());
+      // Build the alloca variable.
+      Register Variable = GR.buildGlobalVariable(
+          NewRegister, PtrType, "placeholder", nullptr,
+          SPIRV::StorageClass::Function, nullptr, true, false,
+          SPIRV::LinkageType::Import, MIRBuilder, false);
+      // Modf must have 4 operands, the first two are the 2 parts of the result,
+      // the third is the operand, and the last one is the floating point value.
+      assert(I.getNumOperands() == 4 &&
+             "Expected 4 operands for modf instruction");
+      MachineBasicBlock &BB = *I.getParent();
+      // Create the OpenCLLIB::modf instruction.
+      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())
+                     .add(I.getOperand(3)) // Floating point value.
+                     .addUse(Variable);    // Pointer to integral part.
+      // Assign the integral part stored in the ptr to the second element of the
+      // result.
+      Register IntegralPartReg = I.getOperand(1).getReg();
+      if (IntegralPartReg.isValid()) {
+        // Load the value from the pointer to integral part.
+        auto LoadMIB = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpLoad))
+                           .addDef(IntegralPartReg)
+                           .addUse(GR.getSPIRVTypeID(ResType))
+                           .addUse(Variable);
+        return LoadMIB.constrainAllUses(TII, TRI, RBI);
+      }
+
+      return MIB.constrainAllUses(TII, TRI, RBI);
+    }
+  }
+  return false;
+}
+
 // Generate the instructions to load 3-element vector builtin input
 // IDs/Indices.
 // Like: GlobalInvocationId, LocalInvocationId, etc....
diff --git a/llvm/test/CodeGen/SPIRV/llvm-intrinsics/fp-intrinsics.ll b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/fp-intrinsics.ll
index 3d46b527bf14f..91c6174e63d77 100644
--- a/llvm/test/CodeGen/SPIRV/llvm-intrinsics/fp-intrinsics.ll
+++ b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/fp-intrinsics.ll
@@ -337,3 +337,25 @@ entry:
 }
 
 declare float @llvm.fma.f32(float, float, float)
+
+; CHECK: OpFunction
+; CHECK: %[[#d:]] = OpFunctionParameter %[[#]]
+; CHECK: %[[#fracPtr:]] = OpFunctionParameter %[[#]]
+; CHECK: %[[#integralPtr:]] = OpFunctionParameter %[[#]]
+; CHECK: %[[#varPtr:]] = OpVariable %[[#]] Function
+; CHECK: %[[#frac:]] = OpExtInst %[[#var2]] %[[#extinst_id]] modf %[[#d]] %[[#varPtr]]
+; CHECK: %[[#integral:]] = OpLoad %[[#var2]] %[[#varPtr]]
+; CHECK: OpStore %[[#fracPtr]] %[[#frac]]
+; CHECK: OpStore %[[#integralPtr]] %[[#integral]]
+; CHECK: OpFunctionEnd
+define void @foo(double %d, ptr addrspace(1) %frac, ptr addrspace(1) %integral) {
+entry:
+  %4 = tail call { double, double } @llvm.modf.f64(double %d)
+  %5 = extractvalue { double, double } %4, 0
+  %6 = extractvalue { double, double } %4, 1
+  store double %5, ptr addrspace(1) %frac, align 8
+  store double %6, ptr addrspace(1) %integral, align 8
+  ret void
+}
+
+declare { double, double } @llvm.modf.f64(double)

``````````

</details>


https://github.com/llvm/llvm-project/pull/147556


More information about the llvm-commits mailing list