[llvm] 4877c59 - [SPIRV] Add PreLegalizer pattern matching for `faceforward` (#139959)

via llvm-commits llvm-commits at lists.llvm.org
Tue Nov 25 09:41:38 PST 2025


Author: Kaitlin Peng
Date: 2025-11-25T09:41:35-08:00
New Revision: 4877c593a873657cd18a1ee0bd4a13f4b84c4d3b

URL: https://github.com/llvm/llvm-project/commit/4877c593a873657cd18a1ee0bd4a13f4b84c4d3b
DIFF: https://github.com/llvm/llvm-project/commit/4877c593a873657cd18a1ee0bd4a13f4b84c4d3b.diff

LOG: [SPIRV] Add PreLegalizer pattern matching for `faceforward` (#139959)

Tasks completed:
- Pattern match`select(fcmp(dot(p2, p3), 0), p1, -p1)` to
`faceforward(p1, p2, p3)`
- Add pattern matching tests to
`prelegalizercombiner-select-to-faceforward.mir` and `faceforward.ll`
- Add CL extension error test
`llvm/test/CodeGen/SPIRV/opencl/faceforward-error.ll`
- Add CL extension test for no pattern matching in
`llvm/test/CodeGen/SPIRV/opencl/faceforward.ll`

Closes #137255.

Added: 
    llvm/test/CodeGen/SPIRV/GlobalISel/InstCombine/prelegalizercombiner-select-to-faceforward.mir
    llvm/test/CodeGen/SPIRV/opencl/faceforward-error.ll
    llvm/test/CodeGen/SPIRV/opencl/faceforward.ll

Modified: 
    llvm/lib/Target/SPIRV/SPIRVCombine.td
    llvm/lib/Target/SPIRV/SPIRVCombinerHelper.cpp
    llvm/lib/Target/SPIRV/SPIRVCombinerHelper.h
    llvm/test/CodeGen/SPIRV/hlsl-intrinsics/faceforward.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Target/SPIRV/SPIRVCombine.td b/llvm/lib/Target/SPIRV/SPIRVCombine.td
index fde56c4d3632a..991a5de1c4e83 100644
--- a/llvm/lib/Target/SPIRV/SPIRVCombine.td
+++ b/llvm/lib/Target/SPIRV/SPIRVCombine.td
@@ -15,8 +15,15 @@ def vector_length_sub_to_distance_lowering : GICombineRule <
   (apply [{ Helper.applySPIRVDistance(*${root}); }])
 >;
 
+def vector_select_to_faceforward_lowering : GICombineRule <
+  (defs root:$root),
+  (match (wip_match_opcode G_SELECT):$root,
+          [{ return Helper.matchSelectToFaceForward(*${root}); }]),
+  (apply [{ Helper.applySPIRVFaceForward(*${root}); }])
+>;
+
 def SPIRVPreLegalizerCombiner
     : GICombiner<"SPIRVPreLegalizerCombinerImpl",
-                       [vector_length_sub_to_distance_lowering]> {
+                       [vector_length_sub_to_distance_lowering, vector_select_to_faceforward_lowering]> {
     let CombineAllMethodName = "tryCombineAllImpl";
 }

diff  --git a/llvm/lib/Target/SPIRV/SPIRVCombinerHelper.cpp b/llvm/lib/Target/SPIRV/SPIRVCombinerHelper.cpp
index 267794c1e0bc5..fad2b676fee04 100644
--- a/llvm/lib/Target/SPIRV/SPIRVCombinerHelper.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVCombinerHelper.cpp
@@ -58,3 +58,154 @@ 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 shader 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 || Pred == CmpInst::FCMP_ULT)) {
+    if (!(Pred == CmpInst::FCMP_OGT || Pred == CmpInst::FCMP_UGT))
+      return false;
+    std::swap(DotReg, CondZeroReg);
+  }
+
+  // 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 AreNegatedConstantsOrSplats = [&](Register TrueReg, Register FalseReg) {
+    std::optional<FPValueAndVReg> TrueVal, FalseVal;
+    if (!mi_match(TrueReg, MRI, m_GFCstOrSplat(TrueVal)) ||
+        !mi_match(FalseReg, MRI, m_GFCstOrSplat(FalseVal)))
+      return false;
+    APFloat TrueValNegated = TrueVal->Value;
+    TrueValNegated.changeSign();
+    return FalseVal->Value.compare(TrueValNegated) == APFloat::cmpEqual;
+  };
+
+  if (!mi_match(TrueReg, MRI, m_GFNeg(m_SpecificReg(FalseReg))) &&
+      !mi_match(FalseReg, MRI, m_GFNeg(m_SpecificReg(TrueReg)))) {
+    std::optional<FPValueAndVReg> MulConstant;
+    MachineInstr *TrueInstr = MRI.getVRegDef(TrueReg);
+    MachineInstr *FalseInstr = MRI.getVRegDef(FalseReg);
+    if (TrueInstr->getOpcode() == TargetOpcode::G_BUILD_VECTOR &&
+        FalseInstr->getOpcode() == TargetOpcode::G_BUILD_VECTOR &&
+        TrueInstr->getNumOperands() == FalseInstr->getNumOperands()) {
+      for (unsigned I = 1; I < TrueInstr->getNumOperands(); ++I)
+        if (!AreNegatedConstantsOrSplats(TrueInstr->getOperand(I).getReg(),
+                                         FalseInstr->getOperand(I).getReg()))
+          return false;
+    } else if (mi_match(TrueReg, MRI,
+                        m_GFMul(m_SpecificReg(FalseReg),
+                                m_GFCstOrSplat(MulConstant))) ||
+               mi_match(FalseReg, MRI,
+                        m_GFMul(m_SpecificReg(TrueReg),
+                                m_GFCstOrSplat(MulConstant))) ||
+               mi_match(TrueReg, MRI,
+                        m_GFMul(m_GFCstOrSplat(MulConstant),
+                                m_SpecificReg(FalseReg))) ||
+               mi_match(FalseReg, MRI,
+                        m_GFMul(m_GFCstOrSplat(MulConstant),
+                                m_SpecificReg(TrueReg)))) {
+      if (!MulConstant || !MulConstant->Value.isExactlyValue(-1.0))
+        return false;
+    } else if (!AreNegatedConstantsOrSplats(TrueReg, FalseReg))
+      return false;
+  }
+
+  return true;
+}
+
+void SPIRVCombinerHelper::applySPIRVFaceForward(MachineInstr &MI) const {
+  // Extract the operands for N, I, and Ng from the match criteria.
+  Register CondReg = MI.getOperand(1).getReg();
+  MachineInstr *CondInstr = MRI.getVRegDef(CondReg);
+  Register DotReg = CondInstr->getOperand(2).getReg();
+  CmpInst::Predicate Pred = cast<GFCmp>(CondInstr)->getCond();
+  if (Pred == CmpInst::FCMP_OGT || Pred == CmpInst::FCMP_UGT)
+    DotReg = CondInstr->getOperand(3).getReg();
+  MachineInstr *DotInstr = MRI.getVRegDef(DotReg);
+  Register DotOperand1, DotOperand2;
+  if (DotInstr->getOpcode() == TargetOpcode::G_FMUL) {
+    DotOperand1 = DotInstr->getOperand(1).getReg();
+    DotOperand2 = DotInstr->getOperand(2).getReg();
+  } else {
+    DotOperand1 = DotInstr->getOperand(2).getReg();
+    DotOperand2 = DotInstr->getOperand(3).getReg();
+  }
+  Register TrueReg = MI.getOperand(2).getReg();
+  Register FalseReg = MI.getOperand(3).getReg();
+  MachineInstr *TrueInstr = MRI.getVRegDef(TrueReg);
+  if (TrueInstr->getOpcode() == TargetOpcode::G_FNEG ||
+      TrueInstr->getOpcode() == TargetOpcode::G_FMUL)
+    std::swap(TrueReg, FalseReg);
+  MachineInstr *FalseInstr = MRI.getVRegDef(FalseReg);
+
+  Register ResultReg = MI.getOperand(0).getReg();
+  Builder.setInstrAndDebugLoc(MI);
+  Builder.buildIntrinsic(Intrinsic::spv_faceforward, ResultReg)
+      .addUse(TrueReg)      // N
+      .addUse(DotOperand1)  // I
+      .addUse(DotOperand2); // Ng
+
+  SPIRVGlobalRegistry *GR =
+      MI.getMF()->getSubtarget<SPIRVSubtarget>().getSPIRVGlobalRegistry();
+  auto RemoveAllUses = [&](Register Reg) {
+    SmallVector<MachineInstr *, 4> UsesToErase;
+    for (auto &UseMI : MRI.use_instructions(Reg))
+      UsesToErase.push_back(&UseMI);
+
+    // calling eraseFromParent to early invalidates the iterator.
+    for (auto *MIToErase : UsesToErase)
+      MIToErase->eraseFromParent();
+  };
+
+  RemoveAllUses(CondReg); // remove all uses of FCMP Result
+  GR->invalidateMachineInstr(CondInstr);
+  CondInstr->eraseFromParent(); // remove FCMP instruction
+  RemoveAllUses(DotReg);        // remove all uses of spv_fdot/G_FMUL Result
+  GR->invalidateMachineInstr(DotInstr);
+  DotInstr->eraseFromParent(); // remove spv_fdot/G_FMUL instruction
+  RemoveAllUses(FalseReg);
+  GR->invalidateMachineInstr(FalseInstr);
+  FalseInstr->eraseFromParent();
+}

diff  --git a/llvm/lib/Target/SPIRV/SPIRVCombinerHelper.h b/llvm/lib/Target/SPIRV/SPIRVCombinerHelper.h
index 0b39d3408aab6..3118cdc744b8f 100644
--- a/llvm/lib/Target/SPIRV/SPIRVCombinerHelper.h
+++ b/llvm/lib/Target/SPIRV/SPIRVCombinerHelper.h
@@ -31,6 +31,8 @@ class SPIRVCombinerHelper : public CombinerHelper {
 
   bool matchLengthToDistance(MachineInstr &MI) const;
   void applySPIRVDistance(MachineInstr &MI) const;
+  bool matchSelectToFaceForward(MachineInstr &MI) const;
+  void applySPIRVFaceForward(MachineInstr &MI) const;
 };
 
 } // end namespace llvm

diff  --git a/llvm/test/CodeGen/SPIRV/GlobalISel/InstCombine/prelegalizercombiner-select-to-faceforward.mir b/llvm/test/CodeGen/SPIRV/GlobalISel/InstCombine/prelegalizercombiner-select-to-faceforward.mir
new file mode 100644
index 0000000000000..fc89f58451809
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/GlobalISel/InstCombine/prelegalizercombiner-select-to-faceforward.mir
@@ -0,0 +1,199 @@
+# RUN: llc -verify-machineinstrs -O0 -mtriple spirv-vulkan1.3-unknown -run-pass=spirv-prelegalizer-combiner %s -o - | FileCheck %s
+# REQUIRES: asserts
+---
+name:            faceforward_instcombine_float
+tracksRegLiveness: true
+legalized: true
+body:             |
+  bb.1.entry:
+    ; CHECK-LABEL: name: faceforward_instcombine_float
+    ; CHECK-NOT: %9:_(s32) = G_FCONSTANT float 0.000000e+00
+    ; CHECK-NOT: %8:_(s32) = G_FMUL %1:fid, %2:fid
+    ; CHECK-NOT: %10:_(s1) = G_FCMP floatpred(olt), %8:_(s32), %9:_
+    ; CHECK-NOT: %11:_(s32) = G_FNEG %0:fid
+    ; CHECK-NOT: %12:id(s32) = G_SELECT %10:_(s1), %0:fid, %11:_
+    ; CHECK: %10:id(s32) = G_INTRINSIC intrinsic(@llvm.spv.faceforward), %2(s32), %3(s32), %4(s32)
+    ; CHECK: OpReturnValue %10(s32)
+    %3:type(s64) = OpTypeFloat 32
+    %5:type(s64) = OpTypeFunction %3:type(s64), %3:type(s64), %3:type(s64), %3:type(s64)
+    OpName %0:fid(s32), 97
+    OpName %1:fid(s32), 98
+    OpName %2:fid(s32), 99
+    %4:iid(s64) = OpFunction %3:type(s64), 0, %5:type(s64)
+    %0:fid(s32) = OpFunctionParameter %3:type(s64)
+    %1:fid(s32) = OpFunctionParameter %3:type(s64)
+    %2:fid(s32) = OpFunctionParameter %3:type(s64)
+    OpName %4:iid(s64), 1701011814, 2003988326, 1600418401, 1953721961, 1651339107, 1600482921, 1634692198, 116
+    %9:_(s32) = G_FCONSTANT float 0.000000e+00
+    %8:_(s32) = G_FMUL %1:fid, %2:fid
+    %10:_(s1) = G_FCMP floatpred(olt), %8:_(s32), %9:_
+    %11:_(s32) = G_FNEG %0:fid
+    %12:id(s32) = G_SELECT %10:_(s1), %0:fid, %11:_
+    OpReturnValue %12:id(s32)
+---
+name:            faceforward_instcombine_float4
+tracksRegLiveness: true
+legalized: true
+body:             |
+  bb.1.entry:
+    ; CHECK-LABEL: name: faceforward_instcombine_float4
+    ; CHECK-NOT: %10:_(s32) = G_FCONSTANT float 0.000000e+00
+    ; CHECK-NOT: %9:_(s32) = G_INTRINSIC intrinsic(@llvm.spv.fdot), %1:vfid(<4 x s32>), %2:vfid(<4 x s32>)
+    ; CHECK-NOT: %11:_(s1) = G_FCMP floatpred(olt), %9:_(s32), %10:_
+    ; CHECK-NOT: %12:_(<4 x s32>) = G_FNEG %0:vfid
+    ; CHECK-NOT: %13:id(<4 x s32>) = G_SELECT %11:_(s1), %0:vfid, %12:_
+    ; CHECK: %11:id(<4 x s32>) = G_INTRINSIC intrinsic(@llvm.spv.faceforward), %3(<4 x s32>), %4(<4 x s32>), %5(<4 x s32>)
+    ; CHECK: OpReturnValue %11(<4 x s32>)
+    %4:type(s64) = OpTypeVector %3:type(s64), 4
+    %6:type(s64) = OpTypeFunction %4:type(s64), %4:type(s64), %4:type(s64), %4:type(s64)
+    %3:type(s64) = OpTypeFloat 32
+    OpName %0:vfid(<4 x s32>), 97
+    OpName %1:vfid(<4 x s32>), 98
+    OpName %2:vfid(<4 x s32>), 99
+    %5:iid(s64) = OpFunction %4:type(s64), 0, %6:type(s64)
+    %0:vfid(<4 x s32>) = OpFunctionParameter %4:type(s64)
+    %1:vfid(<4 x s32>) = OpFunctionParameter %4:type(s64)
+    %2:vfid(<4 x s32>) = OpFunctionParameter %4:type(s64)
+    OpName %5:iid(s64), 1701011814, 2003988326, 1600418401, 1953721961, 1651339107, 1600482921, 1634692198, 13428
+    %10:_(s32) = G_FCONSTANT float 0.000000e+00
+    %9:_(s32) = G_INTRINSIC intrinsic(@llvm.spv.fdot), %1:vfid(<4 x s32>), %2:vfid(<4 x s32>)
+    %11:_(s1) = G_FCMP floatpred(olt), %9:_(s32), %10:_
+    %12:_(<4 x s32>) = G_FNEG %0:vfid
+    %13:id(<4 x s32>) = G_SELECT %11:_(s1), %0:vfid, %12:_
+    OpReturnValue %13:id(<4 x s32>)
+---
+name:            faceforward_instcombine_float_constants
+tracksRegLiveness: true
+legalized: true
+body:             |
+  bb.1.entry:
+    ; CHECK-LABEL: name: faceforward_instcombine_float_constants
+    ; CHECK-NOT: %9:_(s32) = G_FCONSTANT float 0.000000e+00
+    ; CHECK-NOT: %13:_(s32) = G_FCONSTANT float -1.000000e+00
+    ; CHECK-NOT: %8:_(s32) = G_FMUL %1:fid, %2:fid
+    ; CHECK-NOT: %10:_(s1) = G_FCMP floatpred(olt), %8:_(s32), %9:_
+    ; CHECK-NOT: %11:id(s32) = G_SELECT %10:_(s1), %12:_, %13:_
+    ; CHECK: %7:_(s32) = G_FCONSTANT float 1.000000e+00
+    ; CHECK: %11:id(s32) = G_INTRINSIC intrinsic(@llvm.spv.faceforward), %7(s32), %3(s32), %4(s32)
+    ; CHECK: OpReturnValue %11(s32)
+    %3:type(s64) = OpTypeFloat 32
+    %5:type(s64) = OpTypeFunction %3:type(s64), %3:type(s64), %3:type(s64), %3:type(s64)
+    OpName %0:fid(s32), 97
+    OpName %1:fid(s32), 98
+    OpName %2:fid(s32), 99
+    %4:iid(s64) = OpFunction %3:type(s64), 0, %5:type(s64)
+    %0:fid(s32) = OpFunctionParameter %3:type(s64)
+    %1:fid(s32) = OpFunctionParameter %3:type(s64)
+    %2:fid(s32) = OpFunctionParameter %3:type(s64)
+    OpName %4:iid(s64), 1701011814, 2003988326, 1600418401, 1953721961, 1651339107, 1600482921, 1634692198, 1868783476, 1635021678, 7566446
+    %9:_(s32) = G_FCONSTANT float 0.000000e+00
+    %12:_(s32) = G_FCONSTANT float 1.000000e+00
+    %13:_(s32) = G_FCONSTANT float -1.000000e+00
+    %8:_(s32) = G_FMUL %1:fid, %2:fid
+    %10:_(s1) = G_FCMP floatpred(olt), %8:_(s32), %9:_
+    %11:id(s32) = G_SELECT %10:_(s1), %12:_, %13:_
+    OpReturnValue %11:id(s32)
+---
+name:            faceforward_instcombine_float4_constants
+tracksRegLiveness: true
+legalized: true
+body:             |
+  bb.1.entry:
+    ; CHECK-LABEL: name: faceforward_instcombine_float4_constants
+    ; CHECK-NOT: %10:_(s32) = G_FCONSTANT float 0.000000e+00
+    ; CHECK-NOT: %16:_(s32) = G_FCONSTANT float -1.000000e+00
+    ; CHECK-NOT: %15:_(<4 x s32>) = G_BUILD_VECTOR %16:_(s32), %16:_(s32), %16:_(s32), %16:_(s32)
+    ; CHECK-NOT: %9:_(s32) = G_INTRINSIC intrinsic(@llvm.spv.fdot), %1:vfid(<4 x s32>), %2:vfid(<4 x s32>)
+    ; CHECK-NOT: %11:_(s1) = G_FCMP floatpred(olt), %9:_(s32), %10:_
+    ; CHECK-NOT: %12:id(<4 x s32>) = G_SELECT %11:_(s1), %13:_, %15:_
+    ; CHECK: %8:_(s32) = G_FCONSTANT float 1.000000e+00
+    ; CHECK: %9:_(<4 x s32>) = G_BUILD_VECTOR %8(s32), %8(s32), %8(s32), %8(s32)
+    ; CHECK: %14:id(<4 x s32>) = G_INTRINSIC intrinsic(@llvm.spv.faceforward), %9(<4 x s32>), %4(<4 x s32>), %5(<4 x s32>)
+    ; CHECK: OpReturnValue %14(<4 x s32>)
+    %4:type(s64) = OpTypeVector %3:type(s64), 4
+    %6:type(s64) = OpTypeFunction %4:type(s64), %4:type(s64), %4:type(s64), %4:type(s64)
+    %3:type(s64) = OpTypeFloat 32
+    OpName %0:vfid(<4 x s32>), 97
+    OpName %1:vfid(<4 x s32>), 98
+    OpName %2:vfid(<4 x s32>), 99
+    %5:iid(s64) = OpFunction %4:type(s64), 0, %6:type(s64)
+    %0:vfid(<4 x s32>) = OpFunctionParameter %4:type(s64)
+    %1:vfid(<4 x s32>) = OpFunctionParameter %4:type(s64)
+    %2:vfid(<4 x s32>) = OpFunctionParameter %4:type(s64)
+    OpName %5:iid(s64), 1701011814, 2003988326, 1600418401, 1953721961, 1651339107, 1600482921, 1634692198, 1667183732, 1953721967, 1937010273, 0
+    %10:_(s32) = G_FCONSTANT float 0.000000e+00
+    %14:_(s32) = G_FCONSTANT float 1.000000e+00
+    %13:_(<4 x s32>) = G_BUILD_VECTOR %14:_(s32), %14:_(s32), %14:_(s32), %14:_(s32)
+    %16:_(s32) = G_FCONSTANT float -1.000000e+00
+    %15:_(<4 x s32>) = G_BUILD_VECTOR %16:_(s32), %16:_(s32), %16:_(s32), %16:_(s32)
+    %9:_(s32) = G_INTRINSIC intrinsic(@llvm.spv.fdot), %1:vfid(<4 x s32>), %2:vfid(<4 x s32>)
+    %11:_(s1) = G_FCMP floatpred(olt), %9:_(s32), %10:_
+    %12:id(<4 x s32>) = G_SELECT %11:_(s1), %13:_, %15:_
+    OpReturnValue %12:id(<4 x s32>)
+---
+name:            faceforward_instcombine_float4_false_fmul
+tracksRegLiveness: true
+legalized: true
+body:             |
+  bb.1.entry:
+    ; CHECK-LABEL: name: faceforward_instcombine_float4_false_fmul
+    ; CHECK-NOT: %10:_(s32) = G_FCONSTANT float 0.000000e+00
+    ; CHECK-NOT: %13:_(s32) = G_FCONSTANT float -1.000000e+00
+    ; CHECK-NOT: %12:_(<4 x s32>) = G_BUILD_VECTOR %13:_(s32), %13:_(s32), %13:_(s32), %13:_(s32)
+    ; CHECK-NOT: %9:_(s32) = G_INTRINSIC intrinsic(@llvm.spv.fdot), %1:vfid(<4 x s32>), %2:vfid(<4 x s32>)
+    ; CHECK-NOT: %11:_(s1) = G_FCMP floatpred(olt), %9:_(s32), %10:_
+    ; CHECK-NOT: %14:_(<4 x s32>) = G_FMUL %0:vfid, %12:_
+    ; CHECK-NOT: %15:id(<4 x s32>) = G_SELECT %11:_(s1), %0:vfid, %14:_
+    ; CHECK: %13:id(<4 x s32>) = G_INTRINSIC intrinsic(@llvm.spv.faceforward), %3(<4 x s32>), %4(<4 x s32>), %5(<4 x s32>)
+    ; CHECK: OpReturnValue %13(<4 x s32>)
+    %4:type(s64) = OpTypeVector %3:type(s64), 4
+    %6:type(s64) = OpTypeFunction %4:type(s64), %4:type(s64), %4:type(s64), %4:type(s64)
+    %3:type(s64) = OpTypeFloat 32
+    OpName %0:vfid(<4 x s32>), 97
+    OpName %1:vfid(<4 x s32>), 98
+    OpName %2:vfid(<4 x s32>), 99
+    %5:iid(s64) = OpFunction %4:type(s64), 0, %6:type(s64)
+    %0:vfid(<4 x s32>) = OpFunctionParameter %4:type(s64)
+    %1:vfid(<4 x s32>) = OpFunctionParameter %4:type(s64)
+    %2:vfid(<4 x s32>) = OpFunctionParameter %4:type(s64)
+    OpName %5:iid(s64), 1701011814, 2003988326, 1600418401, 1953721961, 1651339107, 1600482921, 1634692198, 1717515380, 1702063201, 1970103903, 108
+    %10:_(s32) = G_FCONSTANT float 0.000000e+00
+    %13:_(s32) = G_FCONSTANT float -1.000000e+00
+    %12:_(<4 x s32>) = G_BUILD_VECTOR %13:_(s32), %13:_(s32), %13:_(s32), %13:_(s32)
+    %9:_(s32) = G_INTRINSIC intrinsic(@llvm.spv.fdot), %1:vfid(<4 x s32>), %2:vfid(<4 x s32>)
+    %11:_(s1) = G_FCMP floatpred(olt), %9:_(s32), %10:_
+    %14:_(<4 x s32>) = G_FMUL %0:vfid, %12:_
+    %15:id(<4 x s32>) = G_SELECT %11:_(s1), %0:vfid, %14:_
+    OpReturnValue %15:id(<4 x s32>)
+---
+name:            faceforward_instcombine_float4_ogt
+tracksRegLiveness: true
+legalized: true
+body:             |
+  bb.1.entry:
+    ; CHECK-LABEL: name: faceforward_instcombine_float4
+    ; CHECK-NOT: %10:_(s32) = G_FCONSTANT float 0.000000e+00
+    ; CHECK-NOT: %9:_(s32) = G_INTRINSIC intrinsic(@llvm.spv.fdot), %1:vfid(<4 x s32>), %2:vfid(<4 x s32>)
+    ; CHECK-NOT: %11:_(s1) = G_FCMP floatpred(ogt), %10:_(s32), %9:_
+    ; CHECK-NOT: %12:_(<4 x s32>) = G_FNEG %0:vfid
+    ; CHECK-NOT: %13:id(<4 x s32>) =  G_SELECT %11:_(s1), %12:_, %0:vfid
+    ; CHECK: %11:id(<4 x s32>) = G_INTRINSIC intrinsic(@llvm.spv.faceforward), %3(<4 x s32>), %4(<4 x s32>), %5(<4 x s32>)
+    ; CHECK: OpReturnValue %11(<4 x s32>)
+    %4:type(s64) = OpTypeVector %3:type(s64), 4
+    %6:type(s64) = OpTypeFunction %4:type(s64), %4:type(s64), %4:type(s64), %4:type(s64)
+    %3:type(s64) = OpTypeFloat 32
+    OpName %0:vfid(<4 x s32>), 97
+    OpName %1:vfid(<4 x s32>), 98
+    OpName %2:vfid(<4 x s32>), 99
+    %5:iid(s64) = OpFunction %4:type(s64), 0, %6:type(s64)
+    %0:vfid(<4 x s32>) = OpFunctionParameter %4:type(s64)
+    %1:vfid(<4 x s32>) = OpFunctionParameter %4:type(s64)
+    %2:vfid(<4 x s32>) = OpFunctionParameter %4:type(s64)
+    OpName %5:iid(s64), 1701011814, 2003988326, 1600418401, 1953721961, 1651339107, 1600482921, 1634692198, 1868510324, 29799
+    %10:_(s32) = G_FCONSTANT float 0.000000e+00
+    %9:_(s32) = G_INTRINSIC intrinsic(@llvm.spv.fdot), %1:vfid(<4 x s32>), %2:vfid(<4 x s32>)
+    %11:_(s1) = G_FCMP floatpred(ogt), %10:_(s32), %9:_
+    %12:_(<4 x s32>) = G_FNEG %0:vfid
+    %13:id(<4 x s32>) = G_SELECT %11:_(s1), %12:_, %0:vfid
+    OpReturnValue %13:id(<4 x s32>)
+

diff  --git a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/faceforward.ll b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/faceforward.ll
index df11694703f82..f7aa42a53de8a 100644
--- a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/faceforward.ll
+++ b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/faceforward.ll
@@ -1,62 +1,163 @@
-; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv-unknown-vulkan %s -o - | FileCheck %s
-; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-vulkan %s -o - -filetype=obj | spirv-val --target-env spv1.4 %}
-
-; FIXME(#136344): Change --target-env to vulkan1.3 and update this test accordingly once the issue is resolved.
-
-; Make sure SPIRV operation function calls for faceforward are lowered correctly.
-
-; CHECK-DAG: %[[#op_ext_glsl:]] = OpExtInstImport "GLSL.std.450"
-; CHECK-DAG: %[[#float_16:]] = OpTypeFloat 16
-; CHECK-DAG: %[[#vec4_float_16:]] = OpTypeVector %[[#float_16]] 4
-; CHECK-DAG: %[[#float_32:]] = OpTypeFloat 32
-; CHECK-DAG: %[[#vec4_float_32:]] = OpTypeVector %[[#float_32]] 4
-
-define noundef half @faceforward_half(half noundef %a, half noundef %b, half noundef %c) {
-entry:
-  ; CHECK: %[[#]] = OpFunction %[[#float_16]] None %[[#]]
-  ; CHECK: %[[#arg0:]] = OpFunctionParameter %[[#float_16]]
-  ; CHECK: %[[#arg1:]] = OpFunctionParameter %[[#float_16]]
-  ; CHECK: %[[#arg2:]] = OpFunctionParameter %[[#float_16]]
-  ; CHECK: %[[#]] = OpExtInst %[[#float_16]] %[[#op_ext_glsl]] FaceForward %[[#arg0]] %[[#arg1]] %[[#arg2]]
-  %spv.faceforward = call half @llvm.spv.faceforward.f16(half %a, half %b, half %c)
-  ret half %spv.faceforward
-}
-
-define noundef float @faceforward_float(float noundef %a, float noundef %b, float noundef %c) {
-entry:
-  ; CHECK: %[[#]] = OpFunction %[[#float_32]] None %[[#]]
-  ; CHECK: %[[#arg0:]] = OpFunctionParameter %[[#float_32]]
-  ; CHECK: %[[#arg1:]] = OpFunctionParameter %[[#float_32]]
-  ; CHECK: %[[#arg2:]] = OpFunctionParameter %[[#float_32]]
-  ; CHECK: %[[#]] = OpExtInst %[[#float_32]] %[[#op_ext_glsl]] FaceForward %[[#arg0]] %[[#arg1]] %[[#arg2]]
-  %spv.faceforward = call float @llvm.spv.faceforward.f32(float %a, float %b, float %c)
-  ret float %spv.faceforward
-}
-
-define noundef <4 x half> @faceforward_half4(<4 x half> noundef %a, <4 x half> noundef %b, <4 x half> noundef %c) {
-entry:
-  ; CHECK: %[[#]] = OpFunction %[[#vec4_float_16]] None %[[#]]
-  ; CHECK: %[[#arg0:]] = OpFunctionParameter %[[#vec4_float_16]]
-  ; CHECK: %[[#arg1:]] = OpFunctionParameter %[[#vec4_float_16]]
-  ; CHECK: %[[#arg2:]] = OpFunctionParameter %[[#vec4_float_16]]
-  ; CHECK: %[[#]] = OpExtInst %[[#vec4_float_16]] %[[#op_ext_glsl]] FaceForward %[[#arg0]] %[[#arg1]] %[[#arg2]]
-  %spv.faceforward = call <4 x half> @llvm.spv.faceforward.v4f16(<4 x half> %a, <4 x half> %b, <4 x half> %c)
-  ret <4 x half> %spv.faceforward
-}
-
-define noundef <4 x float> @faceforward_float4(<4 x float> noundef %a, <4 x float> noundef %b, <4 x float> noundef %c) {
-entry:
-  ; CHECK: %[[#]] = OpFunction %[[#vec4_float_32]] None %[[#]]
-  ; CHECK: %[[#arg0:]] = OpFunctionParameter %[[#vec4_float_32]]
-  ; CHECK: %[[#arg1:]] = OpFunctionParameter %[[#vec4_float_32]]
-  ; CHECK: %[[#arg2:]] = OpFunctionParameter %[[#vec4_float_32]]
-  ; CHECK: %[[#]] = OpExtInst %[[#vec4_float_32]] %[[#op_ext_glsl]] FaceForward %[[#arg0]] %[[#arg1]] %[[#arg2]]
-  %spv.faceforward = call <4 x float> @llvm.spv.faceforward.v4f32(<4 x float> %a, <4 x float> %b, <4 x float> %c)
-  ret <4 x float> %spv.faceforward
-}
-
-declare half @llvm.spv.faceforward.f16(half, half, half)
-declare float @llvm.spv.faceforward.f32(float, float, float)
-
-declare <4 x half> @llvm.spv.faceforward.v4f16(<4 x half>, <4 x half>, <4 x half>)
-declare <4 x float> @llvm.spv.faceforward.v4f32(<4 x float>, <4 x float>, <4 x float>)
+; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv-vulkan1.3-unknown %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-vulkan1.3-unknown %s -o - -filetype=obj | spirv-val --target-env vulkan1.3 %}
+
+; Make sure SPIRV operation function calls for faceforward are lowered correctly.
+
+; CHECK-DAG: %[[#op_ext_glsl:]] = OpExtInstImport "GLSL.std.450"
+; CHECK-DAG: %[[#float_16:]] = OpTypeFloat 16
+; CHECK-DAG: %[[#vec4_float_16:]] = OpTypeVector %[[#float_16]] 4
+; CHECK-DAG: %[[#float_32:]] = OpTypeFloat 32
+; CHECK-DAG: %[[#vec4_float_32:]] = OpTypeVector %[[#float_32]] 4
+
+define internal noundef half @faceforward_half(half noundef %a, half noundef %b, half noundef %c) {
+entry:
+  ; CHECK: %[[#]] = OpFunction %[[#float_16]] None %[[#]]
+  ; CHECK: %[[#arg0:]] = OpFunctionParameter %[[#float_16]]
+  ; CHECK: %[[#arg1:]] = OpFunctionParameter %[[#float_16]]
+  ; CHECK: %[[#arg2:]] = OpFunctionParameter %[[#float_16]]
+  ; CHECK: %[[#]] = OpExtInst %[[#float_16]] %[[#op_ext_glsl]] FaceForward %[[#arg0]] %[[#arg1]] %[[#arg2]]
+  %spv.faceforward = call half @llvm.spv.faceforward.f16(half %a, half %b, half %c)
+  ret half %spv.faceforward
+}
+
+define internal noundef float @faceforward_float(float noundef %a, float noundef %b, float noundef %c) {
+entry:
+  ; CHECK: %[[#]] = OpFunction %[[#float_32]] None %[[#]]
+  ; CHECK: %[[#arg0:]] = OpFunctionParameter %[[#float_32]]
+  ; CHECK: %[[#arg1:]] = OpFunctionParameter %[[#float_32]]
+  ; CHECK: %[[#arg2:]] = OpFunctionParameter %[[#float_32]]
+  ; CHECK: %[[#]] = OpExtInst %[[#float_32]] %[[#op_ext_glsl]] FaceForward %[[#arg0]] %[[#arg1]] %[[#arg2]]
+  %spv.faceforward = call float @llvm.spv.faceforward.f32(float %a, float %b, float %c)
+  ret float %spv.faceforward
+}
+
+define internal noundef <4 x half> @faceforward_half4(<4 x half> noundef %a, <4 x half> noundef %b, <4 x half> noundef %c) {
+entry:
+  ; CHECK: %[[#]] = OpFunction %[[#vec4_float_16]] None %[[#]]
+  ; CHECK: %[[#arg0:]] = OpFunctionParameter %[[#vec4_float_16]]
+  ; CHECK: %[[#arg1:]] = OpFunctionParameter %[[#vec4_float_16]]
+  ; CHECK: %[[#arg2:]] = OpFunctionParameter %[[#vec4_float_16]]
+  ; CHECK: %[[#]] = OpExtInst %[[#vec4_float_16]] %[[#op_ext_glsl]] FaceForward %[[#arg0]] %[[#arg1]] %[[#arg2]]
+  %spv.faceforward = call <4 x half> @llvm.spv.faceforward.v4f16(<4 x half> %a, <4 x half> %b, <4 x half> %c)
+  ret <4 x half> %spv.faceforward
+}
+
+define internal noundef <4 x float> @faceforward_float4(<4 x float> noundef %a, <4 x float> noundef %b, <4 x float> noundef %c) {
+entry:
+  ; CHECK: %[[#]] = OpFunction %[[#vec4_float_32]] None %[[#]]
+  ; CHECK: %[[#arg0:]] = OpFunctionParameter %[[#vec4_float_32]]
+  ; CHECK: %[[#arg1:]] = OpFunctionParameter %[[#vec4_float_32]]
+  ; CHECK: %[[#arg2:]] = OpFunctionParameter %[[#vec4_float_32]]
+  ; CHECK: %[[#]] = OpExtInst %[[#vec4_float_32]] %[[#op_ext_glsl]] FaceForward %[[#arg0]] %[[#arg1]] %[[#arg2]]
+  %spv.faceforward = call <4 x float> @llvm.spv.faceforward.v4f32(<4 x float> %a, <4 x float> %b, <4 x float> %c)
+  ret <4 x float> %spv.faceforward
+}
+
+define internal noundef float @faceforward_instcombine_float(float noundef %a, float noundef %b, float noundef %c) {
+entry:
+  ; CHECK: %[[#]] = OpFunction %[[#float_32]] None %[[#]]
+  ; CHECK: %[[#arg0:]] = OpFunctionParameter %[[#float_32]]
+  ; CHECK: %[[#arg1:]] = OpFunctionParameter %[[#float_32]]
+  ; CHECK: %[[#arg2:]] = OpFunctionParameter %[[#float_32]]
+  ; CHECK: %[[#]] = OpExtInst %[[#float_32]] %[[#op_ext_glsl]] FaceForward %[[#arg0]] %[[#arg1]] %[[#arg2]]
+  %fmul= fmul float %b, %c
+  %fcmp = fcmp olt float %fmul, 0.000000e+00
+  %fneg = fneg float %a
+  %select = select i1 %fcmp, float %a, float %fneg
+  ret float %select
+ }
+
+define internal noundef <4 x float> @faceforward_instcombine_float4(<4 x float> noundef %a, <4 x float> noundef %b, <4 x float> noundef %c) {
+entry:
+  ; CHECK: %[[#]] = OpFunction %[[#vec4_float_32]] None %[[#]]
+  ; CHECK: %[[#arg0:]] = OpFunctionParameter %[[#vec4_float_32]]
+  ; CHECK: %[[#arg1:]] = OpFunctionParameter %[[#vec4_float_32]]
+  ; CHECK: %[[#arg2:]] = OpFunctionParameter %[[#vec4_float_32]]
+  ; CHECK: %[[#]] = OpExtInst %[[#vec4_float_32]] %[[#op_ext_glsl]] FaceForward %[[#arg0]] %[[#arg1]] %[[#arg2]]
+  %spv.fdot = call float @llvm.spv.fdot.v4f32(<4 x float> %b, <4 x float> %c)
+  %fcmp = fcmp olt float %spv.fdot, 0.000000e+00
+  %fneg = fneg <4 x float> %a
+  %select = select i1 %fcmp, <4 x float> %fneg, <4 x float> %a
+  ret <4 x float> %select
+ }
+
+define internal noundef float @faceforward_instcombine_float_constants(float noundef %a, float noundef %b, float noundef %c) {
+entry:
+  ; CHECK: %[[#]] = OpFunction %[[#float_32]] None %[[#]]
+  ; CHECK: %[[#arg0:]] = OpFunctionParameter %[[#float_32]]
+  ; CHECK: %[[#arg1:]] = OpFunctionParameter %[[#float_32]]
+  ; CHECK: %[[#arg2:]] = OpFunctionParameter %[[#float_32]]
+  ; CHECK: %[[#]] = OpExtInst %[[#float_32]] %[[#op_ext_glsl]] FaceForward %[[#]] %[[#arg1]] %[[#arg2]]
+  %fmul = fmul float %b, %c
+  %fcmp = fcmp olt float %fmul, -0.000000e+00
+  %select = select i1 %fcmp, float 1.000000e+00, float -1.000000e+00
+  ret float %select
+}
+
+define internal noundef <4 x float> @faceforward_instcombine_float4_constants(<4 x float> noundef %a, <4 x float> noundef %b, <4 x float> noundef %c) {
+entry:
+  ; CHECK: %[[#]] = OpFunction %[[#vec4_float_32]] None %[[#]]
+  ; CHECK: %[[#arg0:]] = OpFunctionParameter %[[#vec4_float_32]]
+  ; CHECK: %[[#arg1:]] = OpFunctionParameter %[[#vec4_float_32]]
+  ; CHECK: %[[#arg2:]] = OpFunctionParameter %[[#vec4_float_32]]
+  ; CHECK: %[[#]] = OpExtInst %[[#vec4_float_32]] %[[#op_ext_glsl]] FaceForward %[[#]] %[[#arg1]] %[[#arg2]]
+  %spv.fdot = call float @llvm.spv.fdot.v4f32(<4 x float> %b, <4 x float> %c)
+  %fcmp = fcmp olt float %spv.fdot, 0.000000e+00
+  %select = select i1 %fcmp, <4 x float> <float 1.000000e+00, float 2.000000e+00, float 3.000000e+00, float 4.000000e+00>, <4 x float> <float -1.000000e+00, float -2.000000e+00, float -3.000000e+00, float -4.000000e+00>
+  ret <4 x float> %select
+}
+
+define internal noundef <4 x float> @faceforward_instcombine_float4_splat(<4 x float> noundef %a, <4 x float> noundef %b, <4 x float> noundef %c) {
+entry:
+  ; CHECK: %[[#]] = OpFunction %[[#vec4_float_32]] None %[[#]]
+  ; CHECK: %[[#arg0:]] = OpFunctionParameter %[[#vec4_float_32]]
+  ; CHECK: %[[#arg1:]] = OpFunctionParameter %[[#vec4_float_32]]
+  ; CHECK: %[[#arg2:]] = OpFunctionParameter %[[#vec4_float_32]]
+  ; CHECK: %[[#]] = OpExtInst %[[#vec4_float_32]] %[[#op_ext_glsl]] FaceForward %[[#]] %[[#arg1]] %[[#arg2]]
+  %spv.fdot = call float @llvm.spv.fdot.v4f32(<4 x float> %b, <4 x float> %c)
+  %fcmp = fcmp olt float %spv.fdot, 0.000000e+00
+  %select = select i1 %fcmp, <4 x float> splat (float 2.500000e+00), <4 x float> splat (float -2.500000e+00)
+  ret <4 x float> %select
+}
+
+define internal noundef <4 x float> @faceforward_instcombine_float4_false_fmul(<4 x float> noundef %a, <4 x float> noundef %b, <4 x float> noundef %c) {
+entry:
+  ; CHECK: %[[#]] = OpFunction %[[#vec4_float_32]] None %[[#]]
+  ; CHECK: %[[#arg0:]] = OpFunctionParameter %[[#vec4_float_32]]
+  ; CHECK: %[[#arg1:]] = OpFunctionParameter %[[#vec4_float_32]]
+  ; CHECK: %[[#arg2:]] = OpFunctionParameter %[[#vec4_float_32]]
+  ; CHECK: %[[#]] = OpExtInst %[[#vec4_float_32]] %[[#op_ext_glsl]] FaceForward %[[#]] %[[#arg1]] %[[#arg2]]
+  %spv.fdot = call float @llvm.spv.fdot.v4f32(<4 x float> %b, <4 x float> %c)
+  %fcmp = fcmp olt float %spv.fdot, 0.000000e+00
+  %fneg = fmul <4 x float> %a, <float -1.000000e+00, float -1.000000e+00, float -1.000000e+00, float -1.000000e+00>
+  %select = select i1 %fcmp, <4 x float> %a, <4 x float> %fneg
+  ret <4 x float> %select
+}
+
+define internal noundef <4 x float> @faceforward_instcombine_float4_ogt(<4 x float> noundef %a, <4 x float> noundef %b, <4 x float> noundef %c) {
+entry:
+  ; CHECK: %[[#]] = OpFunction %[[#vec4_float_32]] None %[[#]]
+  ; CHECK: %[[#arg0:]] = OpFunctionParameter %[[#vec4_float_32]]
+  ; CHECK: %[[#arg1:]] = OpFunctionParameter %[[#vec4_float_32]]
+  ; CHECK: %[[#arg2:]] = OpFunctionParameter %[[#vec4_float_32]]
+  ; CHECK: %[[#]] = OpExtInst %[[#vec4_float_32]] %[[#op_ext_glsl]] FaceForward %[[#]] %[[#arg1]] %[[#arg2]]
+  %spv.fdot = call float @llvm.spv.fdot.v4f32(<4 x float> %b, <4 x float> %c)
+  %fcmp = fcmp ogt float 0.000000e+00, %spv.fdot
+  %fneg = fneg <4 x float> %a
+  %select = select i1 %fcmp, <4 x float> %fneg, <4 x float> %a
+  ret <4 x float> %select
+}
+
+; The other functions are the test, but a entry point is required to have a valid SPIR-V module.
+define void @main() #1 {
+entry:
+  ret void
+}
+
+declare half @llvm.spv.faceforward.f16(half, half, half)
+declare float @llvm.spv.faceforward.f32(float, float, float)
+
+declare <4 x half> @llvm.spv.faceforward.v4f16(<4 x half>, <4 x half>, <4 x half>)
+declare <4 x float> @llvm.spv.faceforward.v4f32(<4 x float>, <4 x float>, <4 x float>)
+
+attributes #1 = { convergent noinline norecurse "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }

diff  --git a/llvm/test/CodeGen/SPIRV/opencl/faceforward-error.ll b/llvm/test/CodeGen/SPIRV/opencl/faceforward-error.ll
new file mode 100644
index 0000000000000..9334e0977347a
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/opencl/faceforward-error.ll
@@ -0,0 +1,13 @@
+; RUN: not llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown %s -o /dev/null 2>&1 | FileCheck %s
+; RUN: not llc -verify-machineinstrs -O0 -mtriple=spirv32-unknown-unknown %s -o /dev/null 2>&1 | FileCheck %s
+
+; CHECK: LLVM ERROR: %{{.*}} = G_INTRINSIC intrinsic(@llvm.spv.faceforward), %{{.*}}, %{{.*}}, %{{.*}} is only supported with the GLSL extended instruction set.
+
+define noundef <4 x float> @faceforward_float4(<4 x float> noundef %a, <4 x float> noundef %b, <4 x float> noundef %c) {
+entry:
+  %spv.faceforward = call <4 x float> @llvm.spv.faceforward.v4f32(<4 x float> %a, <4 x float> %b, <4 x float> %c)
+  ret <4 x float> %spv.faceforward
+}
+
+declare <4 x float> @llvm.spv.faceforward.v4f32(<4 x float>, <4 x float>, <4 x float>)
+

diff  --git a/llvm/test/CodeGen/SPIRV/opencl/faceforward.ll b/llvm/test/CodeGen/SPIRV/opencl/faceforward.ll
new file mode 100644
index 0000000000000..4da57f80058e1
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/opencl/faceforward.ll
@@ -0,0 +1,29 @@
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv32-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; Make sure we don't pattern match to faceforward in OpenCL.
+
+; CHECK-DAG: %[[#op_ext_glsl:]] = OpExtInstImport "OpenCL.std"
+
+; CHECK-DAG: %[[#float_32:]] = OpTypeFloat 32
+; CHECK-DAG: %[[#bool:]] = OpTypeBool
+; CHECK-DAG: %[[#zero:]] = OpConstantNull %[[#float_32]]
+
+define internal noundef float @faceforward_no_instcombine_float(float noundef %a, float noundef %b, float noundef %c) {
+entry:
+  ; CHECK: %[[#]] = OpFunction %[[#float_32]] None %[[#]]
+  ; CHECK: %[[#arg0:]] = OpFunctionParameter %[[#float_32]]
+  ; CHECK: %[[#arg1:]] = OpFunctionParameter %[[#float_32]]
+  ; CHECK: %[[#arg2:]] = OpFunctionParameter %[[#float_32]]
+  ; CHECK: %[[#fmul:]] = OpFMul %[[#float_32]] %[[#arg1]] %[[#arg2]]
+  ; CHECK: %[[#fcmp:]] = OpFOrdLessThan %[[#bool]] %[[#fmul]] %[[#zero]]
+  ; CHECK: %[[#fneg:]] = OpFNegate %[[#float_32]] %[[#arg0]]
+  ; CHECK: %[[#select:]] = OpSelect %[[#float_32]] %[[#fcmp]] %[[#arg0]] %[[#fneg]]
+  %fmul= fmul float %b, %c
+  %fcmp = fcmp olt float %fmul, 0.000000e+00
+  %fneg = fneg float %a
+  %select = select i1 %fcmp, float %a, float %fneg
+  ret float %select
+ } 


        


More information about the llvm-commits mailing list