[clang] [llvm] [SPIRV] Add PreLegalizer pattern matching for `faceforward` (PR #139959)

Farzon Lotfi via cfe-commits cfe-commits at lists.llvm.org
Fri Oct 17 08:49:17 PDT 2025


================
@@ -58,3 +58,106 @@ void SPIRVCombinerHelper::applySPIRVDistance(MachineInstr &MI) const {
 
   MI.eraseFromParent();
 }
+
+/// This match is part of a combine that
+/// rewrites select(fcmp(dot(I, Ng), 0), N, -N) to faceforward(N, I, Ng)
+///   (vXf32 (g_select
+///             (g_fcmp
+///                (g_intrinsic dot(vXf32 I) (vXf32 Ng)
+///                 0)
+///             (vXf32 N)
+///             (vXf32 g_fneg (vXf32 N))))
+/// ->
+///   (vXf32 (g_intrinsic faceforward
+///             (vXf32 N) (vXf32 I) (vXf32 Ng)))
+///
+/// This only works for Vulkan targets.
+///
+bool SPIRVCombinerHelper::matchSelectToFaceForward(MachineInstr &MI) const {
+  if (!STI.isShader())
+    return false;
+
+  // Match overall select pattern.
+  Register CondReg, TrueReg, FalseReg;
+  if (!mi_match(MI.getOperand(0).getReg(), MRI,
+                m_GISelect(m_Reg(CondReg), m_Reg(TrueReg), m_Reg(FalseReg))))
+    return false;
+
+  // Match the FCMP condition.
+  Register DotReg, CondZeroReg;
+  CmpInst::Predicate Pred;
+  if (!mi_match(CondReg, MRI,
+                m_GFCmp(m_Pred(Pred), m_Reg(DotReg), m_Reg(CondZeroReg))) ||
+      Pred != CmpInst::FCMP_OLT)
+    return false;
+
+  // Check if FCMP is a comparison between a dot product and 0.
+  MachineInstr *DotInstr = MRI.getVRegDef(DotReg);
+  if (DotInstr->getOpcode() != TargetOpcode::G_INTRINSIC ||
+      cast<GIntrinsic>(DotInstr)->getIntrinsicID() != Intrinsic::spv_fdot) {
+    Register DotOperand1, DotOperand2;
+    // Check for scalar dot product.
+    if (!mi_match(DotReg, MRI,
+                  m_GFMul(m_Reg(DotOperand1), m_Reg(DotOperand2))) ||
+        !MRI.getType(DotOperand1).isScalar() ||
+        !MRI.getType(DotOperand2).isScalar())
+      return false;
+  }
+
+  const ConstantFP *ZeroVal;
+  if (!mi_match(CondZeroReg, MRI, m_GFCst(ZeroVal)) || !ZeroVal->isZero())
+    return false;
+
+  // Check if select's false operand is the negation of the true operand.
+  auto AreNegatedConstants = [&](Register TrueReg, Register FalseReg) {
+    const ConstantFP *TrueVal, *FalseVal;
+    if (!mi_match(TrueReg, MRI, m_GFCst(TrueVal)) ||
+        !mi_match(FalseReg, MRI, m_GFCst(FalseVal)))
+      return false;
+    APFloat TrueValNegated = TrueVal->getValue();
+    TrueValNegated.changeSign();
+    return FalseVal->getValue().compare(TrueValNegated) == APFloat::cmpEqual;
+  };
+
+  if (!mi_match(FalseReg, MRI, m_GFNeg(m_SpecificReg(TrueReg))) &&
+      !mi_match(TrueReg, MRI, m_GFNeg(m_SpecificReg(FalseReg)))) {
+    // Check if they're constant opposites.
+    MachineInstr *TrueInstr = MRI.getVRegDef(TrueReg);
+    MachineInstr *FalseInstr = MRI.getVRegDef(FalseReg);
+    if (TrueInstr->getOpcode() == TargetOpcode::G_BUILD_VECTOR &&
+        FalseInstr->getOpcode() == TargetOpcode::G_BUILD_VECTOR &&
----------------
farzonl wrote:

Is the negated-constant handling too narrow? Thee “opposites” check handles scalars and `G_BUILD_VECTOR` of literals, but it isn'y clear if this would  catch common splat forms (e.g., splat from G_FNEG of a scalar, insert/extract builds, or fmul-by--1.0). Will all the splat of scalar to vector be converted to vectors by the time we get to the SPIRV backend?

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


More information about the cfe-commits mailing list