Skip to content

Conversation

@SubashBoopathi
Copy link
Contributor

Added SPIR-V support for constrained arithmetic intrinsic fmuladd, with legalization, instruction selection, and tests; lowered as a sequence of OpFMul and OpFAdd, consistent with the SPIR-V translator.

@github-actions
Copy link

github-actions bot commented Sep 8, 2025

Thank you for submitting a Pull Request (PR) to the LLVM Project!

This PR will be automatically labeled and the relevant teams will be notified.

If you wish to, you can add reviewers by using the "Reviewers" section on this page.

If this is not working for you, it is probably because you do not have write permissions for the repository. In which case you can instead tag reviewers by name in a comment by using @ followed by their GitHub username.

If you have received no comments on your PR for a week, you can request a review by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate is once a week. Please remember that you are asking for valuable time from other developers.

If you have further questions, they may be answered by the LLVM GitHub User Guide.

You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums.

@llvmbot
Copy link
Member

llvmbot commented Sep 8, 2025

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

@llvm/pr-subscribers-llvm-globalisel

Author: Subash B (SubashBoopathi)

Changes

Added SPIR-V support for constrained arithmetic intrinsic fmuladd, with legalization, instruction selection, and tests; lowered as a sequence of OpFMul and OpFAdd, consistent with the SPIR-V translator.


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

6 Files Affected:

  • (modified) llvm/include/llvm/Support/TargetOpcodes.def (+1)
  • (modified) llvm/include/llvm/Target/GenericOpcodes.td (+1)
  • (modified) llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp (+2)
  • (modified) llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp (+48)
  • (modified) llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp (+1-1)
  • (modified) llvm/test/CodeGen/SPIRV/llvm-intrinsics/constrained-arithmetic.ll (+4)
diff --git a/llvm/include/llvm/Support/TargetOpcodes.def b/llvm/include/llvm/Support/TargetOpcodes.def
index b905576b61791..dcf80c56c47b3 100644
--- a/llvm/include/llvm/Support/TargetOpcodes.def
+++ b/llvm/include/llvm/Support/TargetOpcodes.def
@@ -643,6 +643,7 @@ HANDLE_TARGET_OPCODE(G_FMA)
 
 /// Generic FP multiply and add. Behaves as separate fmul and fadd.
 HANDLE_TARGET_OPCODE(G_FMAD)
+HANDLE_TARGET_OPCODE(G_STRICT_FMULADD)
 
 /// Generic FP division.
 HANDLE_TARGET_OPCODE(G_FDIV)
diff --git a/llvm/include/llvm/Target/GenericOpcodes.td b/llvm/include/llvm/Target/GenericOpcodes.td
index ce4750db88c9a..08fbd2253edf2 100644
--- a/llvm/include/llvm/Target/GenericOpcodes.td
+++ b/llvm/include/llvm/Target/GenericOpcodes.td
@@ -1716,6 +1716,7 @@ def G_STRICT_FREM : ConstrainedInstruction<G_FREM>;
 def G_STRICT_FMA : ConstrainedInstruction<G_FMA>;
 def G_STRICT_FSQRT : ConstrainedInstruction<G_FSQRT>;
 def G_STRICT_FLDEXP : ConstrainedInstruction<G_FLDEXP>;
+def G_STRICT_FMULADD : ConstrainedInstruction<G_FMAD>;
 
 //------------------------------------------------------------------------------
 // Memory intrinsics
diff --git a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp
index d7280eaba2440..3742df2f1732b 100644
--- a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp
@@ -2061,6 +2061,8 @@ static unsigned getConstrainedOpcode(Intrinsic::ID ID) {
     return TargetOpcode::G_STRICT_FSQRT;
   case Intrinsic::experimental_constrained_ldexp:
     return TargetOpcode::G_STRICT_FLDEXP;
+  case Intrinsic::experimental_constrained_fmuladd:
+    return TargetOpcode::G_STRICT_FMULADD;
   default:
     return 0;
   }
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index 6608b3f2cbefd..9d56c18f1c096 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -227,6 +227,9 @@ class SPIRVInstructionSelector : public InstructionSelector {
   bool selectExt(Register ResVReg, const SPIRVType *ResType, MachineInstr &I,
                  bool IsSigned) const;
 
+  bool selectStrictFMulAdd(Register ResVReg, const SPIRVType *ResType,
+                           MachineInstr &I) const;
+
   bool selectTrunc(Register ResVReg, const SPIRVType *ResType,
                    MachineInstr &I) const;
 
@@ -689,6 +692,9 @@ bool SPIRVInstructionSelector::spvSelect(Register ResVReg,
   case TargetOpcode::G_FMA:
     return selectExtInst(ResVReg, ResType, I, CL::fma, GL::Fma);
 
+  case TargetOpcode::G_STRICT_FMULADD:
+    return selectStrictFMulAdd(ResVReg, ResType, I);
+
   case TargetOpcode::G_STRICT_FLDEXP:
     return selectExtInst(ResVReg, ResType, I, CL::ldexp);
 
@@ -1038,6 +1044,48 @@ bool SPIRVInstructionSelector::selectOpWithSrcs(Register ResVReg,
   return MIB.constrainAllUses(TII, TRI, RBI);
 }
 
+bool SPIRVInstructionSelector::selectStrictFMulAdd(Register ResVReg,
+                                                   const SPIRVType *ResType,
+                                                   MachineInstr &I) const {
+  assert(I.getNumOperands() == 4 &&
+         "FMulAdd should have 3 operands and result");
+  assert(I.getOperand(1).isReg() && I.getOperand(2).isReg() &&
+         I.getOperand(3).isReg() && "Operands should be registers");
+  MachineBasicBlock &BB = *I.getParent();
+  Register MulLHS = I.getOperand(1).getReg();
+  Register MulRHS = I.getOperand(2).getReg();
+  Register AddRHS = I.getOperand(3).getReg();
+  SPIRVType *MulLHSType = GR.getSPIRVTypeForVReg(MulLHS);
+  SPIRVType *MulRHSType = GR.getSPIRVTypeForVReg(MulRHS);
+  SPIRVType *AddRHSType = GR.getSPIRVTypeForVReg(AddRHS);
+  if (!MulLHSType || !MulRHSType || !AddRHSType)
+    report_fatal_error("Input Type could not be determined.");
+  if (!GR.isScalarOrVectorOfType(MulLHS, SPIRV::OpTypeFloat) ||
+      !GR.isScalarOrVectorOfType(MulRHS, SPIRV::OpTypeFloat) ||
+      !GR.isScalarOrVectorOfType(AddRHS, SPIRV::OpTypeFloat)) {
+    report_fatal_error("FMulAdd requires floating-point operands");
+  }
+  bool IsScalar = (MulLHSType->getOpcode() == SPIRV::OpTypeFloat);
+  bool IsVector = (MulLHSType->getOpcode() == SPIRV::OpTypeVector);
+  if (!IsScalar && !IsVector)
+    report_fatal_error("Unsupported type for FMulAdd operation");
+  unsigned MulOpcode = IsScalar ? SPIRV::OpFMulS : SPIRV::OpFMulV;
+  unsigned AddOpcode = IsScalar ? SPIRV::OpFAddS : SPIRV::OpFAddV;
+  Register MulTemp = MRI->createVirtualRegister(MRI->getRegClass(MulLHS));
+  BuildMI(BB, I, I.getDebugLoc(), TII.get(MulOpcode))
+      .addDef(MulTemp)
+      .addUse(GR.getSPIRVTypeID(ResType))
+      .addUse(MulLHS)
+      .addUse(MulRHS)
+      .constrainAllUses(TII, TRI, RBI);
+  return BuildMI(BB, I, I.getDebugLoc(), TII.get(AddOpcode))
+      .addDef(ResVReg)
+      .addUse(GR.getSPIRVTypeID(ResType))
+      .addUse(MulTemp)
+      .addUse(AddRHS)
+      .constrainAllUses(TII, TRI, RBI);
+}
+
 bool SPIRVInstructionSelector::selectUnOp(Register ResVReg,
                                           const SPIRVType *ResType,
                                           MachineInstr &I,
diff --git a/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp b/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
index 721f64a329d31..8041ef67cfa56 100644
--- a/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
@@ -193,7 +193,7 @@ SPIRVLegalizerInfo::SPIRVLegalizerInfo(const SPIRVSubtarget &ST) {
       .legalFor(allIntScalarsAndVectors)
       .legalIf(extendedScalarsAndVectors);
 
-  getActionDefinitionsBuilder({G_FMA, G_STRICT_FMA})
+  getActionDefinitionsBuilder({G_FMA, G_STRICT_FMA, G_STRICT_FMULADD})
       .legalFor(allFloatScalarsAndVectors);
 
   getActionDefinitionsBuilder(G_STRICT_FLDEXP)
diff --git a/llvm/test/CodeGen/SPIRV/llvm-intrinsics/constrained-arithmetic.ll b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/constrained-arithmetic.ll
index 11bedfa605f9b..b2b8bbfce7321 100644
--- a/llvm/test/CodeGen/SPIRV/llvm-intrinsics/constrained-arithmetic.ll
+++ b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/constrained-arithmetic.ll
@@ -7,6 +7,7 @@
 ; CHECK-DAG: OpName %[[#r4:]] "r4"
 ; CHECK-DAG: OpName %[[#r5:]] "r5"
 ; CHECK-DAG: OpName %[[#r6:]] "r6"
+; CHECK-DAG: OpName %[[#r7:]] "r7"
 
 ; CHECK-NOT: OpDecorate %[[#r5]] FPRoundingMode
 ; CHECK-NOT: OpDecorate %[[#r6]] FPRoundingMode
@@ -22,6 +23,8 @@
 ; CHECK: OpFMul %[[#]] %[[#]]
 ; CHECK: OpExtInst %[[#]] %[[#]] fma %[[#]] %[[#]] %[[#]]
 ; CHECK: OpFRem
+; CHECK: OpFMul %[[#]] %[[#]]
+; CHECK: OpFAdd %[[#]] %[[#]]
 
 ; Function Attrs: norecurse nounwind strictfp
 define dso_local spir_kernel void @test(float %a, i32 %in, i32 %ui) {
@@ -32,6 +35,7 @@ entry:
   %r4 = tail call float @llvm.experimental.constrained.fmul.f32(float %a, float %a, metadata !"round.downward", metadata !"fpexcept.strict")
   %r5 = tail call float @llvm.experimental.constrained.fma.f32(float %a, float %a, float %a, metadata !"round.dynamic", metadata !"fpexcept.strict")
   %r6 = tail call float @llvm.experimental.constrained.frem.f32(float %a, float %a, metadata !"round.dynamic", metadata !"fpexcept.strict")
+  %r7 = tail call float @llvm.experimental.constrained.fmuladd.f32(float %a, float %a, float %a, metadata !"round.dynamic", metadata !"fpexcept.strict")
   ret void
 }
 

@llvmbot
Copy link
Member

llvmbot commented Sep 8, 2025

@llvm/pr-subscribers-llvm-support

Author: Subash B (SubashBoopathi)

Changes

Added SPIR-V support for constrained arithmetic intrinsic fmuladd, with legalization, instruction selection, and tests; lowered as a sequence of OpFMul and OpFAdd, consistent with the SPIR-V translator.


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

6 Files Affected:

  • (modified) llvm/include/llvm/Support/TargetOpcodes.def (+1)
  • (modified) llvm/include/llvm/Target/GenericOpcodes.td (+1)
  • (modified) llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp (+2)
  • (modified) llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp (+48)
  • (modified) llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp (+1-1)
  • (modified) llvm/test/CodeGen/SPIRV/llvm-intrinsics/constrained-arithmetic.ll (+4)
diff --git a/llvm/include/llvm/Support/TargetOpcodes.def b/llvm/include/llvm/Support/TargetOpcodes.def
index b905576b61791..dcf80c56c47b3 100644
--- a/llvm/include/llvm/Support/TargetOpcodes.def
+++ b/llvm/include/llvm/Support/TargetOpcodes.def
@@ -643,6 +643,7 @@ HANDLE_TARGET_OPCODE(G_FMA)
 
 /// Generic FP multiply and add. Behaves as separate fmul and fadd.
 HANDLE_TARGET_OPCODE(G_FMAD)
+HANDLE_TARGET_OPCODE(G_STRICT_FMULADD)
 
 /// Generic FP division.
 HANDLE_TARGET_OPCODE(G_FDIV)
diff --git a/llvm/include/llvm/Target/GenericOpcodes.td b/llvm/include/llvm/Target/GenericOpcodes.td
index ce4750db88c9a..08fbd2253edf2 100644
--- a/llvm/include/llvm/Target/GenericOpcodes.td
+++ b/llvm/include/llvm/Target/GenericOpcodes.td
@@ -1716,6 +1716,7 @@ def G_STRICT_FREM : ConstrainedInstruction<G_FREM>;
 def G_STRICT_FMA : ConstrainedInstruction<G_FMA>;
 def G_STRICT_FSQRT : ConstrainedInstruction<G_FSQRT>;
 def G_STRICT_FLDEXP : ConstrainedInstruction<G_FLDEXP>;
+def G_STRICT_FMULADD : ConstrainedInstruction<G_FMAD>;
 
 //------------------------------------------------------------------------------
 // Memory intrinsics
diff --git a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp
index d7280eaba2440..3742df2f1732b 100644
--- a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp
@@ -2061,6 +2061,8 @@ static unsigned getConstrainedOpcode(Intrinsic::ID ID) {
     return TargetOpcode::G_STRICT_FSQRT;
   case Intrinsic::experimental_constrained_ldexp:
     return TargetOpcode::G_STRICT_FLDEXP;
+  case Intrinsic::experimental_constrained_fmuladd:
+    return TargetOpcode::G_STRICT_FMULADD;
   default:
     return 0;
   }
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index 6608b3f2cbefd..9d56c18f1c096 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -227,6 +227,9 @@ class SPIRVInstructionSelector : public InstructionSelector {
   bool selectExt(Register ResVReg, const SPIRVType *ResType, MachineInstr &I,
                  bool IsSigned) const;
 
+  bool selectStrictFMulAdd(Register ResVReg, const SPIRVType *ResType,
+                           MachineInstr &I) const;
+
   bool selectTrunc(Register ResVReg, const SPIRVType *ResType,
                    MachineInstr &I) const;
 
@@ -689,6 +692,9 @@ bool SPIRVInstructionSelector::spvSelect(Register ResVReg,
   case TargetOpcode::G_FMA:
     return selectExtInst(ResVReg, ResType, I, CL::fma, GL::Fma);
 
+  case TargetOpcode::G_STRICT_FMULADD:
+    return selectStrictFMulAdd(ResVReg, ResType, I);
+
   case TargetOpcode::G_STRICT_FLDEXP:
     return selectExtInst(ResVReg, ResType, I, CL::ldexp);
 
@@ -1038,6 +1044,48 @@ bool SPIRVInstructionSelector::selectOpWithSrcs(Register ResVReg,
   return MIB.constrainAllUses(TII, TRI, RBI);
 }
 
+bool SPIRVInstructionSelector::selectStrictFMulAdd(Register ResVReg,
+                                                   const SPIRVType *ResType,
+                                                   MachineInstr &I) const {
+  assert(I.getNumOperands() == 4 &&
+         "FMulAdd should have 3 operands and result");
+  assert(I.getOperand(1).isReg() && I.getOperand(2).isReg() &&
+         I.getOperand(3).isReg() && "Operands should be registers");
+  MachineBasicBlock &BB = *I.getParent();
+  Register MulLHS = I.getOperand(1).getReg();
+  Register MulRHS = I.getOperand(2).getReg();
+  Register AddRHS = I.getOperand(3).getReg();
+  SPIRVType *MulLHSType = GR.getSPIRVTypeForVReg(MulLHS);
+  SPIRVType *MulRHSType = GR.getSPIRVTypeForVReg(MulRHS);
+  SPIRVType *AddRHSType = GR.getSPIRVTypeForVReg(AddRHS);
+  if (!MulLHSType || !MulRHSType || !AddRHSType)
+    report_fatal_error("Input Type could not be determined.");
+  if (!GR.isScalarOrVectorOfType(MulLHS, SPIRV::OpTypeFloat) ||
+      !GR.isScalarOrVectorOfType(MulRHS, SPIRV::OpTypeFloat) ||
+      !GR.isScalarOrVectorOfType(AddRHS, SPIRV::OpTypeFloat)) {
+    report_fatal_error("FMulAdd requires floating-point operands");
+  }
+  bool IsScalar = (MulLHSType->getOpcode() == SPIRV::OpTypeFloat);
+  bool IsVector = (MulLHSType->getOpcode() == SPIRV::OpTypeVector);
+  if (!IsScalar && !IsVector)
+    report_fatal_error("Unsupported type for FMulAdd operation");
+  unsigned MulOpcode = IsScalar ? SPIRV::OpFMulS : SPIRV::OpFMulV;
+  unsigned AddOpcode = IsScalar ? SPIRV::OpFAddS : SPIRV::OpFAddV;
+  Register MulTemp = MRI->createVirtualRegister(MRI->getRegClass(MulLHS));
+  BuildMI(BB, I, I.getDebugLoc(), TII.get(MulOpcode))
+      .addDef(MulTemp)
+      .addUse(GR.getSPIRVTypeID(ResType))
+      .addUse(MulLHS)
+      .addUse(MulRHS)
+      .constrainAllUses(TII, TRI, RBI);
+  return BuildMI(BB, I, I.getDebugLoc(), TII.get(AddOpcode))
+      .addDef(ResVReg)
+      .addUse(GR.getSPIRVTypeID(ResType))
+      .addUse(MulTemp)
+      .addUse(AddRHS)
+      .constrainAllUses(TII, TRI, RBI);
+}
+
 bool SPIRVInstructionSelector::selectUnOp(Register ResVReg,
                                           const SPIRVType *ResType,
                                           MachineInstr &I,
diff --git a/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp b/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
index 721f64a329d31..8041ef67cfa56 100644
--- a/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
@@ -193,7 +193,7 @@ SPIRVLegalizerInfo::SPIRVLegalizerInfo(const SPIRVSubtarget &ST) {
       .legalFor(allIntScalarsAndVectors)
       .legalIf(extendedScalarsAndVectors);
 
-  getActionDefinitionsBuilder({G_FMA, G_STRICT_FMA})
+  getActionDefinitionsBuilder({G_FMA, G_STRICT_FMA, G_STRICT_FMULADD})
       .legalFor(allFloatScalarsAndVectors);
 
   getActionDefinitionsBuilder(G_STRICT_FLDEXP)
diff --git a/llvm/test/CodeGen/SPIRV/llvm-intrinsics/constrained-arithmetic.ll b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/constrained-arithmetic.ll
index 11bedfa605f9b..b2b8bbfce7321 100644
--- a/llvm/test/CodeGen/SPIRV/llvm-intrinsics/constrained-arithmetic.ll
+++ b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/constrained-arithmetic.ll
@@ -7,6 +7,7 @@
 ; CHECK-DAG: OpName %[[#r4:]] "r4"
 ; CHECK-DAG: OpName %[[#r5:]] "r5"
 ; CHECK-DAG: OpName %[[#r6:]] "r6"
+; CHECK-DAG: OpName %[[#r7:]] "r7"
 
 ; CHECK-NOT: OpDecorate %[[#r5]] FPRoundingMode
 ; CHECK-NOT: OpDecorate %[[#r6]] FPRoundingMode
@@ -22,6 +23,8 @@
 ; CHECK: OpFMul %[[#]] %[[#]]
 ; CHECK: OpExtInst %[[#]] %[[#]] fma %[[#]] %[[#]] %[[#]]
 ; CHECK: OpFRem
+; CHECK: OpFMul %[[#]] %[[#]]
+; CHECK: OpFAdd %[[#]] %[[#]]
 
 ; Function Attrs: norecurse nounwind strictfp
 define dso_local spir_kernel void @test(float %a, i32 %in, i32 %ui) {
@@ -32,6 +35,7 @@ entry:
   %r4 = tail call float @llvm.experimental.constrained.fmul.f32(float %a, float %a, metadata !"round.downward", metadata !"fpexcept.strict")
   %r5 = tail call float @llvm.experimental.constrained.fma.f32(float %a, float %a, float %a, metadata !"round.dynamic", metadata !"fpexcept.strict")
   %r6 = tail call float @llvm.experimental.constrained.frem.f32(float %a, float %a, metadata !"round.dynamic", metadata !"fpexcept.strict")
+  %r7 = tail call float @llvm.experimental.constrained.fmuladd.f32(float %a, float %a, float %a, metadata !"round.dynamic", metadata !"fpexcept.strict")
   ret void
 }
 

Copy link
Contributor

@arsenm arsenm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Title should be more specific about which intrinsic

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is asserting too many things that should be verifier errors

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay @arsenm , understood. I’ve removed the unnecessary assert statements and cleaned up the function so it just generates the SPIR-V instructions for fmuladd.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This style of test is annoying to maintain, have one case per function. Also test more types, like vectors

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay @arsenm , understood. I’ve created a new dedicated test file for constrained fmuladd, with one case per function, and added coverage for more scalar and vector types.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gentle reminder @arsenm any updates on this PR?

Copy link
Contributor

@arsenm arsenm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't need the fmuladd opcode

return MIB.constrainAllUses(TII, TRI, RBI);
}

bool SPIRVInstructionSelector::selectStrictFMulAdd(Register ResVReg,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case there's no value in reporting it as legal. I'd expect to just use .lower() and have it break into the separate operations

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’ve updated the implementation to remove the legalization and instead use .lower() for G_STRICT_FMAD, allowing it to break down into separate multiply and add operations as suggested.


/// Generic FP multiply and add. Behaves as separate fmul and fadd.
HANDLE_TARGET_OPCODE(G_FMAD)
HANDLE_TARGET_OPCODE(G_STRICT_FMULADD)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SelectionDAG does not have an equivalent operation. fmuladd is an IR-only construct (there's another PR to add it, but it wouldn't apply in this situation)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My current approach of handling the constrained fmuladd by lowering it into separate fmul and fadd instructions (using .lower()) should be sufficient, right? Or would you suggest a different handling for this case?

.legalIf(extendedScalarsAndVectors);

getActionDefinitionsBuilder({G_FMA, G_STRICT_FMA})
getActionDefinitionsBuilder({G_FMA, G_STRICT_FMA, G_STRICT_FMULADD})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be lowered

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’ve updated the implementation to lower G_STRICT_FMAD instead of marking it as legal

@github-actions
Copy link

github-actions bot commented Oct 13, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the G_STRICT_FMAD is applied to a float or vector of floats, it will not be lowered. Is that what you want?

If you want it lowered all of the time, remove this line. I expect that is what you want based on earlier PRs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@s-perron I understood and I modified the legalizer info to directly lower the intrinsic as expected.

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.

4 participants