[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