[llvm] fa2a7a2 - [SPIR-V] Introduce SPIR-V global entities tracking and deduplication infra.

Aleksandr Bezzubikov via llvm-commits llvm-commits at lists.llvm.org
Thu Jul 7 08:15:26 PDT 2022


Author: Aleksandr Bezzubikov
Date: 2022-07-07T18:15:10+03:00
New Revision: fa2a7a25c9893b2cee2a4cf4fe92531979ef523e

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

LOG: [SPIR-V] Introduce SPIR-V global entities tracking and deduplication infra.

SPIR-V module typically contains some global entities that were not
global before made it to SPIR-V, e.g. types and constants are not usually
declared globally in LLVM. By design SPIR-V requires such stuff to be declared
once and in the module's global section. Since MIR is not able to represent
such things properly they were generated per-function, and then at the very end
of the backend's pipeline hoisted into some 'meta' function minding possible
duplicates.

New SPIRVDuplicatesTracker keeps mapping of the original LLVM entities such
as types, constant, global variables, etc to their MIR counterparts -
(MachineFunction, Register). Later SPIRVModuleAnalysis (apart from other
thing it's responsible for) performs topological sorting of the
tracker's entries to ensure proper ordering before the hoisting,
and actually performs the hoisting in a duplicates-free manner
by the tracker's nature.

Differential Revision: https://reviews.llvm.org/D128471

Added: 
    llvm/lib/Target/SPIRV/SPIRVDuplicatesTracker.cpp
    llvm/lib/Target/SPIRV/SPIRVDuplicatesTracker.h
    llvm/test/CodeGen/SPIRV/transcoding/RelationalOperators.ll
    llvm/test/CodeGen/SPIRV/transcoding/fcmp.ll

Modified: 
    llvm/lib/Target/SPIRV/CMakeLists.txt
    llvm/lib/Target/SPIRV/SPIRVCallLowering.cpp
    llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
    llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h
    llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
    llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
    llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.h

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Target/SPIRV/CMakeLists.txt b/llvm/lib/Target/SPIRV/CMakeLists.txt
index f19609efa3cf0..8e1154b29f545 100644
--- a/llvm/lib/Target/SPIRV/CMakeLists.txt
+++ b/llvm/lib/Target/SPIRV/CMakeLists.txt
@@ -15,6 +15,7 @@ add_public_tablegen_target(SPIRVCommonTableGen)
 add_llvm_target(SPIRVCodeGen
   SPIRVAsmPrinter.cpp
   SPIRVCallLowering.cpp
+  SPIRVDuplicatesTracker.cpp
   SPIRVEmitIntrinsics.cpp
   SPIRVGlobalRegistry.cpp
   SPIRVInstrInfo.cpp

diff  --git a/llvm/lib/Target/SPIRV/SPIRVCallLowering.cpp b/llvm/lib/Target/SPIRV/SPIRVCallLowering.cpp
index df07a126eeead..5b6b82aebf302 100644
--- a/llvm/lib/Target/SPIRV/SPIRVCallLowering.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVCallLowering.cpp
@@ -68,6 +68,7 @@ bool SPIRVCallLowering::lowerFormalArguments(MachineIRBuilder &MIRBuilder,
                                              ArrayRef<ArrayRef<Register>> VRegs,
                                              FunctionLoweringInfo &FLI) const {
   assert(GR && "Must initialize the SPIRV type registry before lowering args.");
+  GR->setCurrentFunc(MIRBuilder.getMF());
 
   // Assign types and names to all args, and store their types for later.
   SmallVector<Register, 4> ArgTypeVRegs;
@@ -114,6 +115,8 @@ bool SPIRVCallLowering::lowerFormalArguments(MachineIRBuilder &MIRBuilder,
   auto MRI = MIRBuilder.getMRI();
   Register FuncVReg = MRI->createGenericVirtualRegister(LLT::scalar(32));
   MRI->setRegClass(FuncVReg, &SPIRV::IDRegClass);
+  if (F.isDeclaration())
+    GR->add(&F, &MIRBuilder.getMF(), FuncVReg);
 
   auto *FTy = F.getFunctionType();
   auto FuncTy = GR->assignTypeToVReg(FTy, FuncVReg, MIRBuilder);
@@ -136,6 +139,8 @@ bool SPIRVCallLowering::lowerFormalArguments(MachineIRBuilder &MIRBuilder,
     MIRBuilder.buildInstr(SPIRV::OpFunctionParameter)
         .addDef(VRegs[i][0])
         .addUse(ArgTypeVRegs[i]);
+    if (F.isDeclaration())
+      GR->add(F.getArg(i), &MIRBuilder.getMF(), VRegs[i][0]);
   }
   // Name the function.
   if (F.hasName())
@@ -165,6 +170,7 @@ bool SPIRVCallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
   if (Info.OrigRet.Regs.size() > 1)
     return false;
 
+  GR->setCurrentFunc(MIRBuilder.getMF());
   Register ResVReg =
       Info.OrigRet.Regs.empty() ? Register(0) : Info.OrigRet.Regs[0];
   // Emit a regular OpFunctionCall. If it's an externally declared function,

diff  --git a/llvm/lib/Target/SPIRV/SPIRVDuplicatesTracker.cpp b/llvm/lib/Target/SPIRV/SPIRVDuplicatesTracker.cpp
new file mode 100644
index 0000000000000..57cd4bafd3516
--- /dev/null
+++ b/llvm/lib/Target/SPIRV/SPIRVDuplicatesTracker.cpp
@@ -0,0 +1,95 @@
+//===-- SPIRVDuplicatesTracker.cpp - SPIR-V Duplicates Tracker --*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// General infrastructure for keeping track of the values that according to
+// the SPIR-V binary layout should be global to the whole module.
+//
+//===----------------------------------------------------------------------===//
+
+#include "SPIRVDuplicatesTracker.h"
+
+using namespace llvm;
+
+template <typename T>
+void SPIRVGeneralDuplicatesTracker::prebuildReg2Entry(
+    SPIRVDuplicatesTracker<T> &DT, SPIRVReg2EntryTy &Reg2Entry) {
+  for (auto &TPair : DT.getAllUses()) {
+    for (auto &RegPair : TPair.second) {
+      const MachineFunction *MF = RegPair.first;
+      Register R = RegPair.second;
+      MachineInstr *MI = MF->getRegInfo().getUniqueVRegDef(R);
+      if (!MI)
+        continue;
+      Reg2Entry[&MI->getOperand(0)] = &TPair.second;
+    }
+  }
+}
+
+void SPIRVGeneralDuplicatesTracker::buildDepsGraph(
+    std::vector<SPIRV::DTSortableEntry *> &Graph,
+    MachineModuleInfo *MMI = nullptr) {
+  SPIRVReg2EntryTy Reg2Entry;
+  prebuildReg2Entry(TT, Reg2Entry);
+  prebuildReg2Entry(CT, Reg2Entry);
+  prebuildReg2Entry(GT, Reg2Entry);
+  prebuildReg2Entry(FT, Reg2Entry);
+  prebuildReg2Entry(AT, Reg2Entry);
+
+  for (auto &Op2E : Reg2Entry) {
+    SPIRV::DTSortableEntry *E = Op2E.second;
+    Graph.push_back(E);
+    for (auto &U : *E) {
+      const MachineRegisterInfo &MRI = U.first->getRegInfo();
+      MachineInstr *MI = MRI.getUniqueVRegDef(U.second);
+      if (!MI)
+        continue;
+      assert(MI && MI->getParent() && "No MachineInstr created yet");
+      for (auto i = MI->getNumDefs(); i < MI->getNumOperands(); i++) {
+        MachineOperand &Op = MI->getOperand(i);
+        if (!Op.isReg())
+          continue;
+        MachineOperand *RegOp = &MRI.getVRegDef(Op.getReg())->getOperand(0);
+        assert((MI->getOpcode() == SPIRV::OpVariable && i == 3) ||
+               Reg2Entry.count(RegOp));
+        if (Reg2Entry.count(RegOp))
+          E->addDep(Reg2Entry[RegOp]);
+      }
+
+      if (E->getIsFunc()) {
+        MachineInstr *Next = MI->getNextNode();
+        if (Next && (Next->getOpcode() == SPIRV::OpFunction ||
+                     Next->getOpcode() == SPIRV::OpFunctionParameter)) {
+          E->addDep(Reg2Entry[&Next->getOperand(0)]);
+        }
+      }
+    }
+  }
+
+  if (MMI) {
+    const Module *M = MMI->getModule();
+    for (auto F = M->begin(), E = M->end(); F != E; ++F) {
+      const MachineFunction *MF = MMI->getMachineFunction(*F);
+      if (!MF)
+        continue;
+      for (const MachineBasicBlock &MBB : *MF) {
+        for (const MachineInstr &CMI : MBB) {
+          MachineInstr &MI = const_cast<MachineInstr &>(CMI);
+          MI.dump();
+          if (MI.getNumExplicitDefs() > 0 &&
+              Reg2Entry.count(&MI.getOperand(0))) {
+            dbgs() << "\t[";
+            for (SPIRV::DTSortableEntry *D :
+                 Reg2Entry.lookup(&MI.getOperand(0))->getDeps())
+              dbgs() << Register::virtReg2Index(D->lookup(MF)) << ", ";
+            dbgs() << "]\n";
+          }
+        }
+      }
+    }
+  }
+}
\ No newline at end of file

diff  --git a/llvm/lib/Target/SPIRV/SPIRVDuplicatesTracker.h b/llvm/lib/Target/SPIRV/SPIRVDuplicatesTracker.h
new file mode 100644
index 0000000000000..58ae1f86ce42c
--- /dev/null
+++ b/llvm/lib/Target/SPIRV/SPIRVDuplicatesTracker.h
@@ -0,0 +1,174 @@
+//===-- SPIRVDuplicatesTracker.h - SPIR-V Duplicates Tracker ----*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// General infrastructure for keeping track of the values that according to
+// the SPIR-V binary layout should be global to the whole module.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIB_TARGET_SPIRV_SPIRVDUPLICATESTRACKER_H
+#define LLVM_LIB_TARGET_SPIRV_SPIRVDUPLICATESTRACKER_H
+
+#include "MCTargetDesc/SPIRVBaseInfo.h"
+#include "MCTargetDesc/SPIRVMCTargetDesc.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/MapVector.h"
+#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
+#include "llvm/CodeGen/MachineModuleInfo.h"
+
+#include <type_traits>
+
+namespace llvm {
+namespace SPIRV {
+// NOTE: using MapVector instead of DenseMap because it helps getting
+// everything ordered in a stable manner for a price of extra (NumKeys)*PtrSize
+// memory and expensive removals which do not happen anyway.
+class DTSortableEntry : public MapVector<const MachineFunction *, Register> {
+  SmallVector<DTSortableEntry *, 2> Deps;
+
+  struct FlagsTy {
+    unsigned IsFunc : 1;
+    unsigned IsGV : 1;
+    // NOTE: bit-field default init is a C++20 feature.
+    FlagsTy() : IsFunc(0), IsGV(0) {}
+  };
+  FlagsTy Flags;
+
+public:
+  // Common hoisting utility doesn't support function, because their hoisting
+  // require hoisting of params as well.
+  bool getIsFunc() const { return Flags.IsFunc; }
+  bool getIsGV() const { return Flags.IsGV; }
+  void setIsFunc(bool V) { Flags.IsFunc = V; }
+  void setIsGV(bool V) { Flags.IsGV = V; }
+
+  const SmallVector<DTSortableEntry *, 2> &getDeps() const { return Deps; }
+  void addDep(DTSortableEntry *E) { Deps.push_back(E); }
+};
+} // namespace SPIRV
+
+template <typename KeyTy> class SPIRVDuplicatesTrackerBase {
+public:
+  // NOTE: using MapVector instead of DenseMap helps getting everything ordered
+  // in a stable manner for a price of extra (NumKeys)*PtrSize memory and
+  // expensive removals which don't happen anyway.
+  using StorageTy = MapVector<KeyTy, SPIRV::DTSortableEntry>;
+
+private:
+  StorageTy Storage;
+
+public:
+  void add(KeyTy V, const MachineFunction *MF, Register R) {
+    if (find(V, MF).isValid())
+      return;
+
+    Storage[V][MF] = R;
+    if (std::is_same<Function,
+                     typename std::remove_const<
+                         typename std::remove_pointer<KeyTy>::type>::type>() ||
+        std::is_same<Argument,
+                     typename std::remove_const<
+                         typename std::remove_pointer<KeyTy>::type>::type>())
+      Storage[V].setIsFunc(true);
+    if (std::is_same<GlobalVariable,
+                     typename std::remove_const<
+                         typename std::remove_pointer<KeyTy>::type>::type>())
+      Storage[V].setIsGV(true);
+  }
+
+  Register find(KeyTy V, const MachineFunction *MF) const {
+    auto iter = Storage.find(V);
+    if (iter != Storage.end()) {
+      auto Map = iter->second;
+      auto iter2 = Map.find(MF);
+      if (iter2 != Map.end())
+        return iter2->second;
+    }
+    return Register();
+  }
+
+  const StorageTy &getAllUses() const { return Storage; }
+
+private:
+  StorageTy &getAllUses() { return Storage; }
+
+  // The friend class needs to have access to the internal storage
+  // to be able to build dependency graph, can't declare only one
+  // function a 'friend' due to the incomplete declaration at this point
+  // and mutual dependency problems.
+  friend class SPIRVGeneralDuplicatesTracker;
+};
+
+template <typename T>
+class SPIRVDuplicatesTracker : public SPIRVDuplicatesTrackerBase<const T *> {};
+
+class SPIRVGeneralDuplicatesTracker {
+  SPIRVDuplicatesTracker<Type> TT;
+  SPIRVDuplicatesTracker<Constant> CT;
+  SPIRVDuplicatesTracker<GlobalVariable> GT;
+  SPIRVDuplicatesTracker<Function> FT;
+  SPIRVDuplicatesTracker<Argument> AT;
+
+  // NOTE: using MOs instead of regs to get rid of MF dependency to be able
+  // to use flat data structure.
+  // NOTE: replacing DenseMap with MapVector doesn't affect overall correctness
+  // but makes LITs more stable, should prefer DenseMap still due to
+  // significant perf 
diff erence.
+  using SPIRVReg2EntryTy =
+      MapVector<MachineOperand *, SPIRV::DTSortableEntry *>;
+
+  template <typename T>
+  void prebuildReg2Entry(SPIRVDuplicatesTracker<T> &DT,
+                         SPIRVReg2EntryTy &Reg2Entry);
+
+public:
+  void buildDepsGraph(std::vector<SPIRV::DTSortableEntry *> &Graph,
+                      MachineModuleInfo *MMI);
+
+  void add(const Type *T, const MachineFunction *MF, Register R) {
+    TT.add(T, MF, R);
+  }
+
+  void add(const Constant *C, const MachineFunction *MF, Register R) {
+    CT.add(C, MF, R);
+  }
+
+  void add(const GlobalVariable *GV, const MachineFunction *MF, Register R) {
+    GT.add(GV, MF, R);
+  }
+
+  void add(const Function *F, const MachineFunction *MF, Register R) {
+    FT.add(F, MF, R);
+  }
+
+  void add(const Argument *Arg, const MachineFunction *MF, Register R) {
+    AT.add(Arg, MF, R);
+  }
+
+  Register find(const Type *T, const MachineFunction *MF) {
+    return TT.find(const_cast<Type *>(T), MF);
+  }
+
+  Register find(const Constant *C, const MachineFunction *MF) {
+    return CT.find(const_cast<Constant *>(C), MF);
+  }
+
+  Register find(const GlobalVariable *GV, const MachineFunction *MF) {
+    return GT.find(const_cast<GlobalVariable *>(GV), MF);
+  }
+
+  Register find(const Function *F, const MachineFunction *MF) {
+    return FT.find(const_cast<Function *>(F), MF);
+  }
+
+  Register find(const Argument *Arg, const MachineFunction *MF) {
+    return AT.find(const_cast<Argument *>(Arg), MF);
+  }
+};
+} // namespace llvm
+#endif
\ No newline at end of file

diff  --git a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
index 02a6905a1abcf..5f890c003cbcf 100644
--- a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
@@ -101,7 +101,6 @@ Register SPIRVGlobalRegistry::buildConstantInt(uint64_t Val,
                                                SPIRVType *SpvType,
                                                bool EmitIR) {
   auto &MF = MIRBuilder.getMF();
-  Register Res;
   const IntegerType *LLVMIntTy;
   if (SpvType)
     LLVMIntTy = cast<IntegerType>(getTypeForSPIRVType(SpvType));
@@ -110,15 +109,18 @@ Register SPIRVGlobalRegistry::buildConstantInt(uint64_t Val,
   // Find a constant in DT or build a new one.
   const auto ConstInt =
       ConstantInt::get(const_cast<IntegerType *>(LLVMIntTy), Val);
-  unsigned BitWidth = SpvType ? getScalarOrVectorBitWidth(SpvType) : 32;
-  Res = MF.getRegInfo().createGenericVirtualRegister(LLT::scalar(BitWidth));
-  assignTypeToVReg(LLVMIntTy, Res, MIRBuilder);
-  if (EmitIR)
-    MIRBuilder.buildConstant(Res, *ConstInt);
-  else
-    MIRBuilder.buildInstr(SPIRV::OpConstantI)
-        .addDef(Res)
-        .addImm(ConstInt->getSExtValue());
+  Register Res = DT.find(ConstInt, &MF);
+  if (!Res.isValid()) {
+    unsigned BitWidth = SpvType ? getScalarOrVectorBitWidth(SpvType) : 32;
+    Res = MF.getRegInfo().createGenericVirtualRegister(LLT::scalar(BitWidth));
+    assignTypeToVReg(LLVMIntTy, Res, MIRBuilder);
+    if (EmitIR)
+      MIRBuilder.buildConstant(Res, *ConstInt);
+    else
+      MIRBuilder.buildInstr(SPIRV::OpConstantI)
+          .addDef(Res)
+          .addImm(ConstInt->getSExtValue());
+  }
   return Res;
 }
 
@@ -126,7 +128,6 @@ Register SPIRVGlobalRegistry::buildConstantFP(APFloat Val,
                                               MachineIRBuilder &MIRBuilder,
                                               SPIRVType *SpvType) {
   auto &MF = MIRBuilder.getMF();
-  Register Res;
   const Type *LLVMFPTy;
   if (SpvType) {
     LLVMFPTy = getTypeForSPIRVType(SpvType);
@@ -136,10 +137,13 @@ Register SPIRVGlobalRegistry::buildConstantFP(APFloat Val,
   }
   // Find a constant in DT or build a new one.
   const auto ConstFP = ConstantFP::get(LLVMFPTy->getContext(), Val);
-  unsigned BitWidth = SpvType ? getScalarOrVectorBitWidth(SpvType) : 32;
-  Res = MF.getRegInfo().createGenericVirtualRegister(LLT::scalar(BitWidth));
-  assignTypeToVReg(LLVMFPTy, Res, MIRBuilder);
-  MIRBuilder.buildFConstant(Res, *ConstFP);
+  Register Res = DT.find(ConstFP, &MF);
+  if (!Res.isValid()) {
+    unsigned BitWidth = SpvType ? getScalarOrVectorBitWidth(SpvType) : 32;
+    Res = MF.getRegInfo().createGenericVirtualRegister(LLT::scalar(BitWidth));
+    assignTypeToVReg(LLVMFPTy, Res, MIRBuilder);
+    MIRBuilder.buildFConstant(Res, *ConstFP);
+  }
   return Res;
 }
 
@@ -184,6 +188,7 @@ Register SPIRVGlobalRegistry::buildGlobalVariable(
                                      *Subtarget.getRegBankInfo());
   }
   Reg = MIB->getOperand(0).getReg();
+  DT.add(GVar, &MIRBuilder.getMF(), Reg);
 
   // Set to Reg the same type as ResVReg has.
   auto MRI = MIRBuilder.getMRI();
@@ -318,10 +323,11 @@ SPIRVType *SPIRVGlobalRegistry::getSPIRVTypeForVReg(Register VReg) const {
 SPIRVType *SPIRVGlobalRegistry::getOrCreateSPIRVType(
     const Type *Type, MachineIRBuilder &MIRBuilder,
     SPIRV::AccessQualifier AccessQual, bool EmitIR) {
+  Register Reg = DT.find(Type, &MIRBuilder.getMF());
+  if (Reg.isValid())
+    return getSPIRVTypeForVReg(Reg);
   SPIRVType *SpirvType = createSPIRVType(Type, MIRBuilder, AccessQual, EmitIR);
-  VRegToTypeMap[&MIRBuilder.getMF()][getSPIRVTypeID(SpirvType)] = SpirvType;
-  SPIRVToLLVMType[SpirvType] = Type;
-  return SpirvType;
+  return restOfCreateSPIRVType(Type, SpirvType);
 }
 
 bool SPIRVGlobalRegistry::isScalarOfType(Register VReg,
@@ -387,17 +393,21 @@ SPIRVGlobalRegistry::getOrCreateSPIRVIntegerType(unsigned BitWidth,
       MIRBuilder);
 }
 
-SPIRVType *SPIRVGlobalRegistry::restOfCreateSPIRVType(Type *LLVMTy,
-                                                      MachineInstrBuilder MIB) {
-  SPIRVType *SpirvType = MIB;
+SPIRVType *SPIRVGlobalRegistry::restOfCreateSPIRVType(const Type *LLVMTy,
+                                                      SPIRVType *SpirvType) {
+  assert(CurMF == SpirvType->getMF());
   VRegToTypeMap[CurMF][getSPIRVTypeID(SpirvType)] = SpirvType;
   SPIRVToLLVMType[SpirvType] = LLVMTy;
+  DT.add(LLVMTy, CurMF, getSPIRVTypeID(SpirvType));
   return SpirvType;
 }
 
 SPIRVType *SPIRVGlobalRegistry::getOrCreateSPIRVIntegerType(
     unsigned BitWidth, MachineInstr &I, const SPIRVInstrInfo &TII) {
   Type *LLVMTy = IntegerType::get(CurMF->getFunction().getContext(), BitWidth);
+  Register Reg = DT.find(LLVMTy, CurMF);
+  if (Reg.isValid())
+    return getSPIRVTypeForVReg(Reg);
   MachineBasicBlock &BB = *I.getParent();
   auto MIB = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpTypeInt))
                  .addDef(createTypeVReg(CurMF->getRegInfo()))

diff  --git a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h
index 952ab4c13e294..13dcc20a3e0a9 100644
--- a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h
+++ b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h
@@ -17,6 +17,7 @@
 #define LLVM_LIB_TARGET_SPIRV_SPIRVTYPEMANAGER_H
 
 #include "MCTargetDesc/SPIRVBaseInfo.h"
+#include "SPIRVDuplicatesTracker.h"
 #include "SPIRVInstrInfo.h"
 #include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
 
@@ -30,7 +31,10 @@ class SPIRVGlobalRegistry {
   // where Reg = OpType...
   // while VRegToTypeMap tracks SPIR-V type assigned to other regs (i.e. not
   // type-declaring ones)
-  DenseMap<MachineFunction *, DenseMap<Register, SPIRVType *>> VRegToTypeMap;
+  DenseMap<const MachineFunction *, DenseMap<Register, SPIRVType *>>
+      VRegToTypeMap;
+
+  SPIRVGeneralDuplicatesTracker DT;
 
   DenseMap<SPIRVType *, const Type *> SPIRVToLLVMType;
 
@@ -48,6 +52,39 @@ class SPIRVGlobalRegistry {
 
   MachineFunction *CurMF;
 
+  void add(const Constant *C, MachineFunction *MF, Register R) {
+    DT.add(C, MF, R);
+  }
+
+  void add(const GlobalVariable *GV, MachineFunction *MF, Register R) {
+    DT.add(GV, MF, R);
+  }
+
+  void add(const Function *F, MachineFunction *MF, Register R) {
+    DT.add(F, MF, R);
+  }
+
+  void add(const Argument *Arg, MachineFunction *MF, Register R) {
+    DT.add(Arg, MF, R);
+  }
+
+  Register find(const Constant *C, MachineFunction *MF) {
+    return DT.find(C, MF);
+  }
+
+  Register find(const GlobalVariable *GV, MachineFunction *MF) {
+    return DT.find(GV, MF);
+  }
+
+  Register find(const Function *F, MachineFunction *MF) {
+    return DT.find(F, MF);
+  }
+
+  void buildDepsGraph(std::vector<SPIRV::DTSortableEntry *> &Graph,
+                      MachineModuleInfo *MMI = nullptr) {
+    DT.buildDepsGraph(Graph, MMI);
+  }
+
   // Get or create a SPIR-V type corresponding the given LLVM IR type,
   // and map it to the given VReg by creating an ASSIGN_TYPE instruction.
   SPIRVType *assignTypeToVReg(
@@ -136,7 +173,7 @@ class SPIRVGlobalRegistry {
   SPIRVType *getOpTypeFunction(SPIRVType *RetType,
                                const SmallVectorImpl<SPIRVType *> &ArgTypes,
                                MachineIRBuilder &MIRBuilder);
-  SPIRVType *restOfCreateSPIRVType(Type *LLVMTy, MachineInstrBuilder MIB);
+  SPIRVType *restOfCreateSPIRVType(const Type *LLVMTy, SPIRVType *SpirvType);
 
 public:
   Register buildConstantInt(uint64_t Val, MachineIRBuilder &MIRBuilder,

diff  --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index 9294a60506a8d..90b921a06f212 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -807,23 +807,29 @@ void SPIRVInstructionSelector::renderImm32(MachineInstrBuilder &MIB,
 Register
 SPIRVInstructionSelector::buildI32Constant(uint32_t Val, MachineInstr &I,
                                            const SPIRVType *ResType) const {
+  Type *LLVMTy = IntegerType::get(GR.CurMF->getFunction().getContext(), 32);
   const SPIRVType *SpvI32Ty =
       ResType ? ResType : GR.getOrCreateSPIRVIntegerType(32, I, TII);
-  Register NewReg;
-  NewReg = MRI->createGenericVirtualRegister(LLT::scalar(32));
-  MachineInstr *MI;
-  MachineBasicBlock &BB = *I.getParent();
-  if (Val == 0) {
-    MI = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpConstantNull))
-             .addDef(NewReg)
-             .addUse(GR.getSPIRVTypeID(SpvI32Ty));
-  } else {
-    MI = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpConstantI))
-             .addDef(NewReg)
-             .addUse(GR.getSPIRVTypeID(SpvI32Ty))
-             .addImm(APInt(32, Val).getZExtValue());
+  // Find a constant in DT or build a new one.
+  auto ConstInt = ConstantInt::get(LLVMTy, Val);
+  Register NewReg = GR.find(ConstInt, GR.CurMF);
+  if (!NewReg.isValid()) {
+    NewReg = MRI->createGenericVirtualRegister(LLT::scalar(32));
+    GR.add(ConstInt, GR.CurMF, NewReg);
+    MachineInstr *MI;
+    MachineBasicBlock &BB = *I.getParent();
+    if (Val == 0) {
+      MI = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpConstantNull))
+               .addDef(NewReg)
+               .addUse(GR.getSPIRVTypeID(SpvI32Ty));
+    } else {
+      MI = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpConstantI))
+               .addDef(NewReg)
+               .addUse(GR.getSPIRVTypeID(SpvI32Ty))
+               .addImm(APInt(32, Val).getZExtValue());
+    }
+    constrainSelectedInstRegOperands(*MI, TII, TRI, RBI);
   }
-  constrainSelectedInstRegOperands(*MI, TII, TRI, RBI);
   return NewReg;
 }
 

diff  --git a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
index fa78dd7942c6b..a39df52349352 100644
--- a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
@@ -28,6 +28,11 @@ using namespace llvm;
 
 #define DEBUG_TYPE "spirv-module-analysis"
 
+static cl::opt<bool>
+    SPVDumpDeps("spv-dump-deps",
+                cl::desc("Dump MIR with SPIR-V dependencies info"),
+                cl::Optional, cl::init(false));
+
 char llvm::SPIRVModuleAnalysis::ID = 0;
 
 namespace llvm {
@@ -113,6 +118,83 @@ static bool findSameInstrInMS(const MachineInstr &A,
   return false;
 }
 
+// Collect MI which defines the register in the given machine function.
+static void collectDefInstr(Register Reg, const MachineFunction *MF,
+                            SPIRV::ModuleAnalysisInfo *MAI,
+                            SPIRV::ModuleSectionType MSType,
+                            bool DoInsert = true) {
+  assert(MAI->hasRegisterAlias(MF, Reg) && "Cannot find register alias");
+  MachineInstr *MI = MF->getRegInfo().getUniqueVRegDef(Reg);
+  assert(MI && "There should be an instruction that defines the register");
+  MAI->setSkipEmission(MI);
+  if (DoInsert)
+    MAI->MS[MSType].push_back(MI);
+}
+
+void SPIRVModuleAnalysis::collectGlobalEntities(
+    const std::vector<SPIRV::DTSortableEntry *> &DepsGraph,
+    SPIRV::ModuleSectionType MSType,
+    std::function<bool(const SPIRV::DTSortableEntry *)> Pred,
+    bool UsePreOrder) {
+  DenseSet<const SPIRV::DTSortableEntry *> Visited;
+  for (const auto *E : DepsGraph) {
+    std::function<void(const SPIRV::DTSortableEntry *)> RecHoistUtil;
+    // NOTE: here we prefer recursive approach over iterative because
+    // we don't expect depchains long enough to cause SO.
+    RecHoistUtil = [MSType, UsePreOrder, &Visited, &Pred,
+                    &RecHoistUtil](const SPIRV::DTSortableEntry *E) {
+      if (Visited.count(E) || !Pred(E))
+        return;
+      Visited.insert(E);
+
+      // Traversing deps graph in post-order allows us to get rid of
+      // register aliases preprocessing.
+      // But pre-order is required for correct processing of function
+      // declaration and arguments processing.
+      if (!UsePreOrder)
+        for (auto *S : E->getDeps())
+          RecHoistUtil(S);
+
+      Register GlobalReg = Register::index2VirtReg(MAI.getNextID());
+      bool IsFirst = true;
+      for (auto &U : *E) {
+        const MachineFunction *MF = U.first;
+        Register Reg = U.second;
+        MAI.setRegisterAlias(MF, Reg, GlobalReg);
+        if (!MF->getRegInfo().getUniqueVRegDef(Reg))
+          continue;
+        collectDefInstr(Reg, MF, &MAI, MSType, IsFirst);
+        IsFirst = false;
+        if (E->getIsGV())
+          MAI.GlobalVarList.push_back(MF->getRegInfo().getUniqueVRegDef(Reg));
+      }
+
+      if (UsePreOrder)
+        for (auto *S : E->getDeps())
+          RecHoistUtil(S);
+    };
+    RecHoistUtil(E);
+  }
+}
+
+// The function initializes global register alias table for types, consts,
+// global vars and func decls and collects these instruction for output
+// at module level. Also it collects explicit OpExtension/OpCapability
+// instructions.
+void SPIRVModuleAnalysis::processDefInstrs(const Module &M) {
+  std::vector<SPIRV::DTSortableEntry *> DepsGraph;
+
+  GR->buildDepsGraph(DepsGraph, SPVDumpDeps ? MMI : nullptr);
+
+  collectGlobalEntities(
+      DepsGraph, SPIRV::MB_TypeConstVars,
+      [](const SPIRV::DTSortableEntry *E) { return !E->getIsFunc(); }, false);
+
+  collectGlobalEntities(
+      DepsGraph, SPIRV::MB_ExtFuncDecls,
+      [](const SPIRV::DTSortableEntry *E) { return E->getIsFunc(); }, true);
+}
+
 // Look for IDs declared with Import linkage, and map the imported name string
 // to the register defining that variable (which will usually be the result of
 // an OpFunction). This lets us call externally imported functions using
@@ -146,10 +228,9 @@ void SPIRVModuleAnalysis::collectFuncNames(MachineInstr &MI,
 // numbering has already occurred by this point. We can directly compare reg
 // arguments when detecting duplicates.
 static void collectOtherInstr(MachineInstr &MI, SPIRV::ModuleAnalysisInfo &MAI,
-                              SPIRV::ModuleSectionType MSType,
-                              bool IsConstOrType = false) {
+                              SPIRV::ModuleSectionType MSType) {
   MAI.setSkipEmission(&MI);
-  if (findSameInstrInMS(MI, MSType, MAI, IsConstOrType, IsConstOrType ? 1 : 0))
+  if (findSameInstrInMS(MI, MSType, MAI, false))
     return; // Found a duplicate, so don't add it.
   // No duplicates, so add it.
   MAI.MS[MSType].push_back(&MI);
@@ -163,18 +244,11 @@ void SPIRVModuleAnalysis::processOtherInstrs(const Module &M) {
       continue;
     MachineFunction *MF = MMI->getMachineFunction(*F);
     assert(MF);
-    unsigned FCounter = 0;
     for (MachineBasicBlock &MBB : *MF)
       for (MachineInstr &MI : MBB) {
-        if (MI.getOpcode() == SPIRV::OpFunction)
-          FCounter++;
         if (MAI.getSkipEmission(&MI))
           continue;
         const unsigned OpCode = MI.getOpcode();
-        const bool IsFuncOrParm =
-            OpCode == SPIRV::OpFunction || OpCode == SPIRV::OpFunctionParameter;
-        const bool IsConstOrType =
-            TII->isConstantInstr(MI) || TII->isTypeDeclInstr(MI);
         if (OpCode == SPIRV::OpName || OpCode == SPIRV::OpMemberName) {
           collectOtherInstr(MI, MAI, SPIRV::MB_DebugNames);
         } else if (OpCode == SPIRV::OpEntryPoint) {
@@ -182,12 +256,6 @@ void SPIRVModuleAnalysis::processOtherInstrs(const Module &M) {
         } else if (TII->isDecorationInstr(MI)) {
           collectOtherInstr(MI, MAI, SPIRV::MB_Annotations);
           collectFuncNames(MI, *F);
-        } else if (IsConstOrType || (FCounter > 1 && IsFuncOrParm)) {
-          // Now OpSpecConstant*s are not in DT,
-          // but they need to be collected anyway.
-          enum SPIRV::ModuleSectionType Type =
-              IsFuncOrParm ? SPIRV::MB_ExtFuncDecls : SPIRV::MB_TypeConstVars;
-          collectOtherInstr(MI, MAI, Type, IsConstOrType);
         } else if (OpCode == SPIRV::OpFunction) {
           collectFuncNames(MI, *F);
         }
@@ -239,6 +307,7 @@ bool SPIRVModuleAnalysis::runOnModule(Module &M) {
 
   // TODO: Process type/const/global var/func decl instructions, number their
   // destination registers from 0 to N, collect Extensions and Capabilities.
+  processDefInstrs(M);
 
   // Number rest of registers from N+1 onwards.
   numberRegistersGlobally(M);

diff  --git a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.h b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.h
index 1bef13d458c10..585868909d28a 100644
--- a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.h
+++ b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.h
@@ -15,6 +15,7 @@
 #define LLVM_LIB_TARGET_SPIRV_SPIRVMODULEANALYSIS_H
 
 #include "MCTargetDesc/SPIRVBaseInfo.h"
+#include "SPIRVDuplicatesTracker.h"
 #include "SPIRVSubtarget.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/SmallVector.h"
@@ -123,6 +124,11 @@ struct SPIRVModuleAnalysis : public ModulePass {
 private:
   void setBaseInfo(const Module &M);
   template <typename T> void collectTypesConstsVars();
+  void collectGlobalEntities(
+      const std::vector<SPIRV::DTSortableEntry *> &DepsGraph,
+      SPIRV::ModuleSectionType MSType,
+      std::function<bool(const SPIRV::DTSortableEntry *)> Pred,
+      bool UsePreOrder);
   void processDefInstrs(const Module &M);
   void collectFuncNames(MachineInstr &MI, const Function &F);
   void processOtherInstrs(const Module &M);

diff  --git a/llvm/test/CodeGen/SPIRV/transcoding/RelationalOperators.ll b/llvm/test/CodeGen/SPIRV/transcoding/RelationalOperators.ll
new file mode 100644
index 0000000000000..2512c1410f9fb
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/transcoding/RelationalOperators.ll
@@ -0,0 +1,298 @@
+; RUN: llc -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s --check-prefix=CHECK-SPIRV
+
+; ModuleID = 'RelationalOperators.cl'
+source_filename = "RelationalOperators.cl"
+target datalayout = "e-p:32:32-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024"
+target triple = "spirv32-unknown-unknown"
+
+; CHECK-SPIRV: %[[bool:[0-9]+]] = OpTypeBool
+; CHECK-SPIRV: %[[bool2:[0-9]+]] = OpTypeVector %[[bool]] 2
+
+; CHECK-SPIRV: OpFunction
+; CHECK-SPIRV-NEXT: %[[A:[0-9]+]] = OpFunctionParameter %{{[0-9]+}}
+; CHECK-SPIRV-NEXT: %[[B:[0-9]+]] = OpFunctionParameter %{{[0-9]+}}
+; CHECK-SPIRV: %{{[0-9]+}} = OpUGreaterThan %[[bool2]] %[[A]] %[[B]]
+; CHECK-SPIRV: OpFunctionEnd
+
+; kernel void testUGreaterThan(uint2 a, uint2 b, global int2 *res) {
+;   res[0] = a > b;
+; }
+
+; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn writeonly
+define dso_local spir_kernel void @testUGreaterThan(<2 x i32> noundef %a, <2 x i32> noundef %b, <2 x i32> addrspace(1)* nocapture noundef writeonly %res) local_unnamed_addr #0 !kernel_arg_addr_space !3 !kernel_arg_access_qual !4 !kernel_arg_type !5 !kernel_arg_base_type !6 !kernel_arg_type_qual !7 {
+entry:
+  %cmp = icmp ugt <2 x i32> %a, %b
+  %sext = sext <2 x i1> %cmp to <2 x i32>
+  store <2 x i32> %sext, <2 x i32> addrspace(1)* %res, align 8, !tbaa !8
+  ret void
+}
+
+; CHECK-SPIRV: OpFunction
+; CHECK-SPIRV-NEXT: %[[A:[0-9]+]] = OpFunctionParameter %{{[0-9]+}}
+; CHECK-SPIRV-NEXT: %[[B:[0-9]+]] = OpFunctionParameter %{{[0-9]+}}
+; CHECK-SPIRV: %{{[0-9]+}} = OpSGreaterThan %[[bool2]] %[[A]] %[[B]]
+; CHECK-SPIRV: OpFunctionEnd
+
+; kernel void testSGreaterThan(int2 a, int2 b, global int2 *res) {
+;   res[0] = a > b;
+; }
+
+; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn writeonly
+define dso_local spir_kernel void @testSGreaterThan(<2 x i32> noundef %a, <2 x i32> noundef %b, <2 x i32> addrspace(1)* nocapture noundef writeonly %res) local_unnamed_addr #0 !kernel_arg_addr_space !3 !kernel_arg_access_qual !4 !kernel_arg_type !11 !kernel_arg_base_type !12 !kernel_arg_type_qual !7 {
+entry:
+  %cmp = icmp sgt <2 x i32> %a, %b
+  %sext = sext <2 x i1> %cmp to <2 x i32>
+  store <2 x i32> %sext, <2 x i32> addrspace(1)* %res, align 8, !tbaa !8
+  ret void
+}
+
+; CHECK-SPIRV: OpFunction
+; CHECK-SPIRV-NEXT: %[[A:[0-9]+]] = OpFunctionParameter %{{[0-9]+}}
+; CHECK-SPIRV-NEXT: %[[B:[0-9]+]] = OpFunctionParameter %{{[0-9]+}}
+; CHECK-SPIRV: %{{[0-9]+}} = OpUGreaterThanEqual %[[bool2]] %[[A]] %[[B]]
+; CHECK-SPIRV: OpFunctionEnd
+
+; kernel void testUGreaterThanEqual(uint2 a, uint2 b, global int2 *res) {
+;   res[0] = a >= b;
+; }
+
+; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn writeonly
+define dso_local spir_kernel void @testUGreaterThanEqual(<2 x i32> noundef %a, <2 x i32> noundef %b, <2 x i32> addrspace(1)* nocapture noundef writeonly %res) local_unnamed_addr #0 !kernel_arg_addr_space !3 !kernel_arg_access_qual !4 !kernel_arg_type !5 !kernel_arg_base_type !6 !kernel_arg_type_qual !7 {
+entry:
+  %cmp = icmp uge <2 x i32> %a, %b
+  %sext = sext <2 x i1> %cmp to <2 x i32>
+  store <2 x i32> %sext, <2 x i32> addrspace(1)* %res, align 8, !tbaa !8
+  ret void
+}
+
+; CHECK-SPIRV: OpFunction
+; CHECK-SPIRV-NEXT: %[[A:[0-9]+]] = OpFunctionParameter %{{[0-9]+}}
+; CHECK-SPIRV-NEXT: %[[B:[0-9]+]] = OpFunctionParameter %{{[0-9]+}}
+; CHECK-SPIRV: %{{[0-9]+}} = OpSGreaterThanEqual %[[bool2]] %[[A]] %[[B]]
+; CHECK-SPIRV: OpFunctionEnd
+
+; kernel void testSGreaterThanEqual(int2 a, int2 b, global int2 *res) {
+;   res[0] = a >= b;
+; }
+
+; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn writeonly
+define dso_local spir_kernel void @testSGreaterThanEqual(<2 x i32> noundef %a, <2 x i32> noundef %b, <2 x i32> addrspace(1)* nocapture noundef writeonly %res) local_unnamed_addr #0 !kernel_arg_addr_space !3 !kernel_arg_access_qual !4 !kernel_arg_type !11 !kernel_arg_base_type !12 !kernel_arg_type_qual !7 {
+entry:
+  %cmp = icmp sge <2 x i32> %a, %b
+  %sext = sext <2 x i1> %cmp to <2 x i32>
+  store <2 x i32> %sext, <2 x i32> addrspace(1)* %res, align 8, !tbaa !8
+  ret void
+}
+
+; CHECK-SPIRV: OpFunction
+; CHECK-SPIRV-NEXT: %[[A:[0-9]+]] = OpFunctionParameter %{{[0-9]+}}
+; CHECK-SPIRV-NEXT: %[[B:[0-9]+]] = OpFunctionParameter %{{[0-9]+}}
+; CHECK-SPIRV: %{{[0-9]+}} = OpULessThan %[[bool2]] %[[A]] %[[B]]
+; CHECK-SPIRV: OpFunctionEnd
+
+; kernel void testULessThan(uint2 a, uint2 b, global int2 *res) {
+;   res[0] = a < b;
+; }
+
+; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn writeonly
+define dso_local spir_kernel void @testULessThan(<2 x i32> noundef %a, <2 x i32> noundef %b, <2 x i32> addrspace(1)* nocapture noundef writeonly %res) local_unnamed_addr #0 !kernel_arg_addr_space !3 !kernel_arg_access_qual !4 !kernel_arg_type !5 !kernel_arg_base_type !6 !kernel_arg_type_qual !7 {
+entry:
+  %cmp = icmp ult <2 x i32> %a, %b
+  %sext = sext <2 x i1> %cmp to <2 x i32>
+  store <2 x i32> %sext, <2 x i32> addrspace(1)* %res, align 8, !tbaa !8
+  ret void
+}
+
+; CHECK-SPIRV: OpFunction
+; CHECK-SPIRV-NEXT: %[[A:[0-9]+]] = OpFunctionParameter %{{[0-9]+}}
+; CHECK-SPIRV-NEXT: %[[B:[0-9]+]] = OpFunctionParameter %{{[0-9]+}}
+; CHECK-SPIRV: %{{[0-9]+}} = OpSLessThan %[[bool2]] %[[A]] %[[B]]
+; CHECK-SPIRV: OpFunctionEnd
+
+; kernel void testSLessThan(int2 a, int2 b, global int2 *res) {
+;   res[0] = a < b;
+; }
+
+; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn writeonly
+define dso_local spir_kernel void @testSLessThan(<2 x i32> noundef %a, <2 x i32> noundef %b, <2 x i32> addrspace(1)* nocapture noundef writeonly %res) local_unnamed_addr #0 !kernel_arg_addr_space !3 !kernel_arg_access_qual !4 !kernel_arg_type !11 !kernel_arg_base_type !12 !kernel_arg_type_qual !7 {
+entry:
+  %cmp = icmp slt <2 x i32> %a, %b
+  %sext = sext <2 x i1> %cmp to <2 x i32>
+  store <2 x i32> %sext, <2 x i32> addrspace(1)* %res, align 8, !tbaa !8
+  ret void
+}
+
+; CHECK-SPIRV: OpFunction
+; CHECK-SPIRV-NEXT: %[[A:[0-9]+]] = OpFunctionParameter %{{[0-9]+}}
+; CHECK-SPIRV-NEXT: %[[B:[0-9]+]] = OpFunctionParameter %{{[0-9]+}}
+; CHECK-SPIRV: %{{[0-9]+}} = OpULessThanEqual %[[bool2]] %[[A]] %[[B]]
+; CHECK-SPIRV: OpFunctionEnd
+
+; kernel void testULessThanEqual(uint2 a, uint2 b, global int2 *res) {
+;   res[0] = a <= b;
+; }
+
+; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn writeonly
+define dso_local spir_kernel void @testULessThanEqual(<2 x i32> noundef %a, <2 x i32> noundef %b, <2 x i32> addrspace(1)* nocapture noundef writeonly %res) local_unnamed_addr #0 !kernel_arg_addr_space !3 !kernel_arg_access_qual !4 !kernel_arg_type !5 !kernel_arg_base_type !6 !kernel_arg_type_qual !7 {
+entry:
+  %cmp = icmp ule <2 x i32> %a, %b
+  %sext = sext <2 x i1> %cmp to <2 x i32>
+  store <2 x i32> %sext, <2 x i32> addrspace(1)* %res, align 8, !tbaa !8
+  ret void
+}
+
+; CHECK-SPIRV: OpFunction
+; CHECK-SPIRV-NEXT: %[[A:[0-9]+]] = OpFunctionParameter %{{[0-9]+}}
+; CHECK-SPIRV-NEXT: %[[B:[0-9]+]] = OpFunctionParameter %{{[0-9]+}}
+; CHECK-SPIRV: %{{[0-9]+}} = OpSLessThanEqual %[[bool2]] %[[A]] %[[B]]
+; CHECK-SPIRV: OpFunctionEnd
+
+; kernel void testSLessThanEqual(int2 a, int2 b, global int2 *res) {
+;   res[0] = a <= b;
+; }
+
+; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn writeonly
+define dso_local spir_kernel void @testSLessThanEqual(<2 x i32> noundef %a, <2 x i32> noundef %b, <2 x i32> addrspace(1)* nocapture noundef writeonly %res) local_unnamed_addr #0 !kernel_arg_addr_space !3 !kernel_arg_access_qual !4 !kernel_arg_type !11 !kernel_arg_base_type !12 !kernel_arg_type_qual !7 {
+entry:
+  %cmp = icmp sle <2 x i32> %a, %b
+  %sext = sext <2 x i1> %cmp to <2 x i32>
+  store <2 x i32> %sext, <2 x i32> addrspace(1)* %res, align 8, !tbaa !8
+  ret void
+}
+
+; CHECK-SPIRV: OpFunction
+; CHECK-SPIRV-NEXT: %[[A:[0-9]+]] = OpFunctionParameter %{{[0-9]+}}
+; CHECK-SPIRV-NEXT: %[[B:[0-9]+]] = OpFunctionParameter %{{[0-9]+}}
+; CHECK-SPIRV: %{{[0-9]+}} = OpFOrdEqual %[[bool2]] %[[A]] %[[B]]
+; CHECK-SPIRV: OpFunctionEnd
+
+; kernel void testFOrdEqual(float2 a, float2 b, global int2 *res) {
+;   res[0] = a == b;
+; }
+
+; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn writeonly
+define dso_local spir_kernel void @testFOrdEqual(<2 x float> noundef %a, <2 x float> noundef %b, <2 x i32> addrspace(1)* nocapture noundef writeonly %res) local_unnamed_addr #0 !kernel_arg_addr_space !3 !kernel_arg_access_qual !4 !kernel_arg_type !13 !kernel_arg_base_type !14 !kernel_arg_type_qual !7 {
+entry:
+  %cmp = fcmp oeq <2 x float> %a, %b
+  %sext = sext <2 x i1> %cmp to <2 x i32>
+  store <2 x i32> %sext, <2 x i32> addrspace(1)* %res, align 8, !tbaa !8
+  ret void
+}
+
+; CHECK-SPIRV: OpFunction
+; CHECK-SPIRV-NEXT: %[[A:[0-9]+]] = OpFunctionParameter %{{[0-9]+}}
+; CHECK-SPIRV-NEXT: %[[B:[0-9]+]] = OpFunctionParameter %{{[0-9]+}}
+; CHECK-SPIRV: %{{[0-9]+}} = OpFUnordNotEqual %[[bool2]] %[[A]] %[[B]]
+; CHECK-SPIRV: OpFunctionEnd
+
+; kernel void testFUnordNotEqual(float2 a, float2 b, global int2 *res) {
+;   res[0] = a != b;
+; }
+
+; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn writeonly
+define dso_local spir_kernel void @testFUnordNotEqual(<2 x float> noundef %a, <2 x float> noundef %b, <2 x i32> addrspace(1)* nocapture noundef writeonly %res) local_unnamed_addr #0 !kernel_arg_addr_space !3 !kernel_arg_access_qual !4 !kernel_arg_type !13 !kernel_arg_base_type !14 !kernel_arg_type_qual !7 {
+entry:
+  %cmp = fcmp une <2 x float> %a, %b
+  %sext = sext <2 x i1> %cmp to <2 x i32>
+  store <2 x i32> %sext, <2 x i32> addrspace(1)* %res, align 8, !tbaa !8
+  ret void
+}
+
+; CHECK-SPIRV: OpFunction
+; CHECK-SPIRV-NEXT: %[[A:[0-9]+]] = OpFunctionParameter %{{[0-9]+}}
+; CHECK-SPIRV-NEXT: %[[B:[0-9]+]] = OpFunctionParameter %{{[0-9]+}}
+; CHECK-SPIRV: %{{[0-9]+}} = OpFOrdGreaterThan %[[bool2]] %[[A]] %[[B]]
+; CHECK-SPIRV: OpFunctionEnd
+
+; kernel void testFOrdGreaterThan(float2 a, float2 b, global int2 *res) {
+;   res[0] = a > b;
+; }
+
+; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn writeonly
+define dso_local spir_kernel void @testFOrdGreaterThan(<2 x float> noundef %a, <2 x float> noundef %b, <2 x i32> addrspace(1)* nocapture noundef writeonly %res) local_unnamed_addr #0 !kernel_arg_addr_space !3 !kernel_arg_access_qual !4 !kernel_arg_type !13 !kernel_arg_base_type !14 !kernel_arg_type_qual !7 {
+entry:
+  %cmp = fcmp ogt <2 x float> %a, %b
+  %sext = sext <2 x i1> %cmp to <2 x i32>
+  store <2 x i32> %sext, <2 x i32> addrspace(1)* %res, align 8, !tbaa !8
+  ret void
+}
+
+; CHECK-SPIRV: OpFunction
+; CHECK-SPIRV-NEXT: %[[A:[0-9]+]] = OpFunctionParameter %{{[0-9]+}}
+; CHECK-SPIRV-NEXT: %[[B:[0-9]+]] = OpFunctionParameter %{{[0-9]+}}
+; CHECK-SPIRV: %{{[0-9]+}} = OpFOrdGreaterThanEqual %[[bool2]] %[[A]] %[[B]]
+; CHECK-SPIRV: OpFunctionEnd
+
+; kernel void testFOrdGreaterThanEqual(float2 a, float2 b, global int2 *res) {
+;   res[0] = a >= b;
+; }
+
+; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn writeonly
+define dso_local spir_kernel void @testFOrdGreaterThanEqual(<2 x float> noundef %a, <2 x float> noundef %b, <2 x i32> addrspace(1)* nocapture noundef writeonly %res) local_unnamed_addr #0 !kernel_arg_addr_space !3 !kernel_arg_access_qual !4 !kernel_arg_type !13 !kernel_arg_base_type !14 !kernel_arg_type_qual !7 {
+entry:
+  %cmp = fcmp oge <2 x float> %a, %b
+  %sext = sext <2 x i1> %cmp to <2 x i32>
+  store <2 x i32> %sext, <2 x i32> addrspace(1)* %res, align 8, !tbaa !8
+  ret void
+}
+
+; CHECK-SPIRV: OpFunction
+; CHECK-SPIRV-NEXT: %[[A:[0-9]+]] = OpFunctionParameter %{{[0-9]+}}
+; CHECK-SPIRV-NEXT: %[[B:[0-9]+]] = OpFunctionParameter %{{[0-9]+}}
+; CHECK-SPIRV: %{{[0-9]+}} = OpFOrdLessThan %[[bool2]] %[[A]] %[[B]]
+; CHECK-SPIRV: OpFunctionEnd
+
+; kernel void testFOrdLessThan(float2 a, float2 b, global int2 *res) {
+;   res[0] = a < b;
+; }
+
+; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn writeonly
+define dso_local spir_kernel void @testFOrdLessThan(<2 x float> noundef %a, <2 x float> noundef %b, <2 x i32> addrspace(1)* nocapture noundef writeonly %res) local_unnamed_addr #0 !kernel_arg_addr_space !3 !kernel_arg_access_qual !4 !kernel_arg_type !13 !kernel_arg_base_type !14 !kernel_arg_type_qual !7 {
+entry:
+  %cmp = fcmp olt <2 x float> %a, %b
+  %sext = sext <2 x i1> %cmp to <2 x i32>
+  store <2 x i32> %sext, <2 x i32> addrspace(1)* %res, align 8, !tbaa !8
+  ret void
+}
+
+; CHECK-SPIRV: OpFunction
+; CHECK-SPIRV-NEXT: %[[A:[0-9]+]] = OpFunctionParameter %{{[0-9]+}}
+; CHECK-SPIRV-NEXT: %[[B:[0-9]+]] = OpFunctionParameter %{{[0-9]+}}
+; CHECK-SPIRV: %{{[0-9]+}} = OpFOrdLessThanEqual %[[bool2]] %[[A]] %[[B]]
+; CHECK-SPIRV: OpFunctionEnd
+
+; kernel void testFOrdLessThanEqual(float2 a, float2 b, global int2 *res) {
+;   res[0] = a <= b;
+; }
+
+; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn writeonly
+define dso_local spir_kernel void @testFOrdLessThanEqual(<2 x float> noundef %a, <2 x float> noundef %b, <2 x i32> addrspace(1)* nocapture noundef writeonly %res) local_unnamed_addr #0 !kernel_arg_addr_space !3 !kernel_arg_access_qual !4 !kernel_arg_type !13 !kernel_arg_base_type !14 !kernel_arg_type_qual !7 {
+entry:
+  %cmp = fcmp ole <2 x float> %a, %b
+  %sext = sext <2 x i1> %cmp to <2 x i32>
+  store <2 x i32> %sext, <2 x i32> addrspace(1)* %res, align 8, !tbaa !8
+  ret void
+}
+
+attributes #0 = { mustprogress nofree norecurse nosync nounwind willreturn writeonly "frame-pointer"="none" "min-legal-vector-width"="64" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "uniform-work-group-size"="false" }
+
+!llvm.module.flags = !{!0}
+!opencl.ocl.version = !{!1}
+!opencl.spir.version = !{!1}
+!llvm.ident = !{!2}
+
+!0 = !{i32 1, !"wchar_size", i32 4}
+!1 = !{i32 2, i32 0}
+!2 = !{!"clang version 14.0.0 (https://github.com/llvm/llvm-project.git 881b6a009fb6e2dd5fb924524cd6eacd14148a08)"}
+!3 = !{i32 0, i32 0, i32 1}
+!4 = !{!"none", !"none", !"none"}
+!5 = !{!"uint2", !"uint2", !"int2*"}
+!6 = !{!"uint __attribute__((ext_vector_type(2)))", !"uint __attribute__((ext_vector_type(2)))", !"int __attribute__((ext_vector_type(2)))*"}
+!7 = !{!"", !"", !""}
+!8 = !{!9, !9, i64 0}
+!9 = !{!"omnipotent char", !10, i64 0}
+!10 = !{!"Simple C/C++ TBAA"}
+!11 = !{!"int2", !"int2", !"int2*"}
+!12 = !{!"int __attribute__((ext_vector_type(2)))", !"int __attribute__((ext_vector_type(2)))", !"int __attribute__((ext_vector_type(2)))*"}
+!13 = !{!"float2", !"float2", !"int2*"}
+!14 = !{!"float __attribute__((ext_vector_type(2)))", !"float __attribute__((ext_vector_type(2)))", !"int __attribute__((ext_vector_type(2)))*"}

diff  --git a/llvm/test/CodeGen/SPIRV/transcoding/fcmp.ll b/llvm/test/CodeGen/SPIRV/transcoding/fcmp.ll
new file mode 100644
index 0000000000000..14dfd6fc250c6
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/transcoding/fcmp.ll
@@ -0,0 +1,296 @@
+; RUN: llc -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s --check-prefix=CHECK-SPIRV
+
+; CHECK-SPIRV: OpName %[[#r1:]] "r1"
+; CHECK-SPIRV: OpName %[[#r2:]] "r2"
+; CHECK-SPIRV: OpName %[[#r3:]] "r3"
+; CHECK-SPIRV: OpName %[[#r4:]] "r4"
+; CHECK-SPIRV: OpName %[[#r5:]] "r5"
+; CHECK-SPIRV: OpName %[[#r6:]] "r6"
+; CHECK-SPIRV: OpName %[[#r7:]] "r7"
+; CHECK-SPIRV: OpName %[[#r8:]] "r8"
+; CHECK-SPIRV: OpName %[[#r9:]] "r9"
+; CHECK-SPIRV: OpName %[[#r10:]] "r10"
+; CHECK-SPIRV: OpName %[[#r11:]] "r11"
+; CHECK-SPIRV: OpName %[[#r12:]] "r12"
+; CHECK-SPIRV: OpName %[[#r13:]] "r13"
+; CHECK-SPIRV: OpName %[[#r14:]] "r14"
+; CHECK-SPIRV: OpName %[[#r15:]] "r15"
+; CHECK-SPIRV: OpName %[[#r16:]] "r16"
+; CHECK-SPIRV: OpName %[[#r17:]] "r17"
+; CHECK-SPIRV: OpName %[[#r18:]] "r18"
+; CHECK-SPIRV: OpName %[[#r19:]] "r19"
+; CHECK-SPIRV: OpName %[[#r20:]] "r20"
+; CHECK-SPIRV: OpName %[[#r21:]] "r21"
+; CHECK-SPIRV: OpName %[[#r22:]] "r22"
+; CHECK-SPIRV: OpName %[[#r23:]] "r23"
+; CHECK-SPIRV: OpName %[[#r24:]] "r24"
+; CHECK-SPIRV: OpName %[[#r25:]] "r25"
+; CHECK-SPIRV: OpName %[[#r26:]] "r26"
+; CHECK-SPIRV: OpName %[[#r27:]] "r27"
+; CHECK-SPIRV: OpName %[[#r28:]] "r28"
+; CHECK-SPIRV: OpName %[[#r29:]] "r29"
+; CHECK-SPIRV: OpName %[[#r30:]] "r30"
+; CHECK-SPIRV: OpName %[[#r31:]] "r31"
+; CHECK-SPIRV: OpName %[[#r32:]] "r32"
+; CHECK-SPIRV: OpName %[[#r33:]] "r33"
+; CHECK-SPIRV: OpName %[[#r34:]] "r34"
+; CHECK-SPIRV: OpName %[[#r35:]] "r35"
+; CHECK-SPIRV: OpName %[[#r36:]] "r36"
+; CHECK-SPIRV: OpName %[[#r37:]] "r37"
+; CHECK-SPIRV: OpName %[[#r38:]] "r38"
+; CHECK-SPIRV: OpName %[[#r39:]] "r39"
+; CHECK-SPIRV: OpName %[[#r40:]] "r40"
+; CHECK-SPIRV: OpName %[[#r41:]] "r41"
+; CHECK-SPIRV: OpName %[[#r42:]] "r42"
+; CHECK-SPIRV: OpName %[[#r43:]] "r43"
+; CHECK-SPIRV: OpName %[[#r44:]] "r44"
+; CHECK-SPIRV: OpName %[[#r45:]] "r45"
+; CHECK-SPIRV: OpName %[[#r46:]] "r46"
+; CHECK-SPIRV: OpName %[[#r47:]] "r47"
+; CHECK-SPIRV: OpName %[[#r48:]] "r48"
+; CHECK-SPIRV: OpName %[[#r49:]] "r49"
+; CHECK-SPIRV: OpName %[[#r50:]] "r50"
+; CHECK-SPIRV: OpName %[[#r51:]] "r51"
+; CHECK-SPIRV: OpName %[[#r52:]] "r52"
+; CHECK-SPIRV: OpName %[[#r53:]] "r53"
+; CHECK-SPIRV: OpName %[[#r54:]] "r54"
+; CHECK-SPIRV: OpName %[[#r55:]] "r55"
+; CHECK-SPIRV: OpName %[[#r56:]] "r56"
+; CHECK-SPIRV: OpName %[[#r57:]] "r57"
+; CHECK-SPIRV: OpName %[[#r58:]] "r58"
+; CHECK-SPIRV: OpName %[[#r59:]] "r59"
+; CHECK-SPIRV: OpName %[[#r60:]] "r60"
+; CHECK-SPIRV: OpName %[[#r61:]] "r61"
+; CHECK-SPIRV: OpName %[[#r62:]] "r62"
+; CHECK-SPIRV: OpName %[[#r63:]] "r63"
+; CHECK-SPIRV: OpName %[[#r64:]] "r64"
+; CHECK-SPIRV: OpName %[[#r65:]] "r65"
+; CHECK-SPIRV: OpName %[[#r66:]] "r66"
+; CHECK-SPIRV: OpName %[[#r67:]] "r67"
+; CHECK-SPIRV: OpName %[[#r68:]] "r68"
+; CHECK-SPIRV: OpName %[[#r69:]] "r69"
+; CHECK-SPIRV: OpName %[[#r70:]] "r70"
+; CHECK-SPIRV: OpName %[[#r71:]] "r71"
+; CHECK-SPIRV: OpName %[[#r72:]] "r72"
+; CHECK-SPIRV: OpName %[[#r73:]] "r73"
+; CHECK-SPIRV: OpName %[[#r74:]] "r74"
+; CHECK-SPIRV: OpName %[[#r75:]] "r75"
+; CHECK-SPIRV: OpName %[[#r76:]] "r76"
+; CHECK-SPIRV: OpName %[[#r77:]] "r77"
+; CHECK-SPIRV: OpName %[[#r78:]] "r78"
+; CHECK-SPIRV: OpName %[[#r79:]] "r79"
+; CHECK-SPIRV: OpName %[[#r80:]] "r80"
+; CHECK-SPIRV: OpName %[[#r81:]] "r81"
+; CHECK-SPIRV: OpName %[[#r82:]] "r82"
+; CHECK-SPIRV: OpName %[[#r83:]] "r83"
+; CHECK-SPIRV: OpName %[[#r84:]] "r84"
+; CHECK-SPIRV: OpName %[[#r85:]] "r85"
+; CHECK-SPIRV: OpName %[[#r86:]] "r86"
+; CHECK-SPIRV: OpName %[[#r87:]] "r87"
+; CHECK-SPIRV: OpName %[[#r88:]] "r88"
+; CHECK-SPIRV: OpName %[[#r89:]] "r89"
+; CHECK-SPIRV: OpName %[[#r90:]] "r90"
+; CHECK-SPIRV-NOT: OpDecorate %{{.*}} FPFastMathMode
+; CHECK-SPIRV: %[[#bool:]] = OpTypeBool
+; CHECK-SPIRV: %[[#r1]] = OpFOrdEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r2]] = OpFOrdEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r3]] = OpFOrdEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r4]] = OpFOrdEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r5]] = OpFOrdEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r6]] = OpFOrdEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r7]] = OpFOrdEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r8]] = OpFOrdNotEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r9]] = OpFOrdNotEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r10]] = OpFOrdNotEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r11]] = OpFOrdNotEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r12]] = OpFOrdNotEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r13]] = OpFOrdNotEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r14]] = OpFOrdNotEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r15]] = OpFOrdLessThan %[[#bool]]
+; CHECK-SPIRV: %[[#r16]] = OpFOrdLessThan %[[#bool]]
+; CHECK-SPIRV: %[[#r17]] = OpFOrdLessThan %[[#bool]]
+; CHECK-SPIRV: %[[#r18]] = OpFOrdLessThan %[[#bool]]
+; CHECK-SPIRV: %[[#r19]] = OpFOrdLessThan %[[#bool]]
+; CHECK-SPIRV: %[[#r20]] = OpFOrdLessThan %[[#bool]]
+; CHECK-SPIRV: %[[#r21]] = OpFOrdLessThan %[[#bool]]
+; CHECK-SPIRV: %[[#r22]] = OpFOrdGreaterThan %[[#bool]]
+; CHECK-SPIRV: %[[#r23]] = OpFOrdGreaterThan %[[#bool]]
+; CHECK-SPIRV: %[[#r24]] = OpFOrdGreaterThan %[[#bool]]
+; CHECK-SPIRV: %[[#r25]] = OpFOrdGreaterThan %[[#bool]]
+; CHECK-SPIRV: %[[#r26]] = OpFOrdGreaterThan %[[#bool]]
+; CHECK-SPIRV: %[[#r27]] = OpFOrdGreaterThan %[[#bool]]
+; CHECK-SPIRV: %[[#r28]] = OpFOrdGreaterThan %[[#bool]]
+; CHECK-SPIRV: %[[#r29]] = OpFOrdLessThanEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r30]] = OpFOrdLessThanEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r31]] = OpFOrdLessThanEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r32]] = OpFOrdLessThanEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r33]] = OpFOrdLessThanEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r34]] = OpFOrdLessThanEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r35]] = OpFOrdLessThanEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r36]] = OpFOrdGreaterThanEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r37]] = OpFOrdGreaterThanEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r38]] = OpFOrdGreaterThanEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r39]] = OpFOrdGreaterThanEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r40]] = OpFOrdGreaterThanEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r41]] = OpFOrdGreaterThanEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r42]] = OpFOrdGreaterThanEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r43]] = OpOrdered %[[#bool]]
+; CHECK-SPIRV: %[[#r44]] = OpOrdered %[[#bool]]
+; CHECK-SPIRV: %[[#r45]] = OpOrdered %[[#bool]]
+; CHECK-SPIRV: %[[#r46]] = OpFUnordEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r47]] = OpFUnordEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r48]] = OpFUnordEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r49]] = OpFUnordEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r50]] = OpFUnordEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r51]] = OpFUnordEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r52]] = OpFUnordEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r53]] = OpFUnordNotEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r54]] = OpFUnordNotEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r55]] = OpFUnordNotEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r56]] = OpFUnordNotEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r57]] = OpFUnordNotEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r58]] = OpFUnordNotEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r59]] = OpFUnordNotEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r60]] = OpFUnordLessThan %[[#bool]]
+; CHECK-SPIRV: %[[#r61]] = OpFUnordLessThan %[[#bool]]
+; CHECK-SPIRV: %[[#r62]] = OpFUnordLessThan %[[#bool]]
+; CHECK-SPIRV: %[[#r63]] = OpFUnordLessThan %[[#bool]]
+; CHECK-SPIRV: %[[#r64]] = OpFUnordLessThan %[[#bool]]
+; CHECK-SPIRV: %[[#r65]] = OpFUnordLessThan %[[#bool]]
+; CHECK-SPIRV: %[[#r66]] = OpFUnordLessThan %[[#bool]]
+; CHECK-SPIRV: %[[#r67]] = OpFUnordGreaterThan %[[#bool]]
+; CHECK-SPIRV: %[[#r68]] = OpFUnordGreaterThan %[[#bool]]
+; CHECK-SPIRV: %[[#r69]] = OpFUnordGreaterThan %[[#bool]]
+; CHECK-SPIRV: %[[#r70]] = OpFUnordGreaterThan %[[#bool]]
+; CHECK-SPIRV: %[[#r71]] = OpFUnordGreaterThan %[[#bool]]
+; CHECK-SPIRV: %[[#r72]] = OpFUnordGreaterThan %[[#bool]]
+; CHECK-SPIRV: %[[#r73]] = OpFUnordGreaterThan %[[#bool]]
+; CHECK-SPIRV: %[[#r74]] = OpFUnordLessThanEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r75]] = OpFUnordLessThanEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r76]] = OpFUnordLessThanEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r77]] = OpFUnordLessThanEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r78]] = OpFUnordLessThanEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r79]] = OpFUnordLessThanEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r80]] = OpFUnordLessThanEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r81]] = OpFUnordGreaterThanEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r82]] = OpFUnordGreaterThanEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r83]] = OpFUnordGreaterThanEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r84]] = OpFUnordGreaterThanEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r85]] = OpFUnordGreaterThanEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r86]] = OpFUnordGreaterThanEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r87]] = OpFUnordGreaterThanEqual %[[#bool]]
+; CHECK-SPIRV: %[[#r88]] = OpUnordered %[[#bool]]
+; CHECK-SPIRV: %[[#r89]] = OpUnordered %[[#bool]]
+; CHECK-SPIRV: %[[#r90]] = OpUnordered %[[#bool]]
+
+target datalayout = "e-p:32:32-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024"
+target triple = "spirv32-unknown-unknown"
+
+; Function Attrs: nounwind
+define spir_kernel void @testFCmp(float %a, float %b) local_unnamed_addr #0 !kernel_arg_addr_space !2 !kernel_arg_access_qual !3 !kernel_arg_type !4 !kernel_arg_base_type !4 !kernel_arg_type_qual !5 {
+entry:
+  %r1 = fcmp oeq float %a, %b
+  %r2 = fcmp nnan oeq float %a, %b
+  %r3 = fcmp ninf oeq float %a, %b
+  %r4 = fcmp nsz oeq float %a, %b
+  %r5 = fcmp arcp oeq float %a, %b
+  %r6 = fcmp fast oeq float %a, %b
+  %r7 = fcmp nnan ninf oeq float %a, %b
+  %r8 = fcmp one float %a, %b
+  %r9 = fcmp nnan one float %a, %b
+  %r10 = fcmp ninf one float %a, %b
+  %r11 = fcmp nsz one float %a, %b
+  %r12 = fcmp arcp one float %a, %b
+  %r13 = fcmp fast one float %a, %b
+  %r14 = fcmp nnan ninf one float %a, %b
+  %r15 = fcmp olt float %a, %b
+  %r16 = fcmp nnan olt float %a, %b
+  %r17 = fcmp ninf olt float %a, %b
+  %r18 = fcmp nsz olt float %a, %b
+  %r19 = fcmp arcp olt float %a, %b
+  %r20 = fcmp fast olt float %a, %b
+  %r21 = fcmp nnan ninf olt float %a, %b
+  %r22 = fcmp ogt float %a, %b
+  %r23 = fcmp nnan ogt float %a, %b
+  %r24 = fcmp ninf ogt float %a, %b
+  %r25 = fcmp nsz ogt float %a, %b
+  %r26 = fcmp arcp ogt float %a, %b
+  %r27 = fcmp fast ogt float %a, %b
+  %r28 = fcmp nnan ninf ogt float %a, %b
+  %r29 = fcmp ole float %a, %b
+  %r30 = fcmp nnan ole float %a, %b
+  %r31 = fcmp ninf ole float %a, %b
+  %r32 = fcmp nsz ole float %a, %b
+  %r33 = fcmp arcp ole float %a, %b
+  %r34 = fcmp fast ole float %a, %b
+  %r35 = fcmp nnan ninf ole float %a, %b
+  %r36 = fcmp oge float %a, %b
+  %r37 = fcmp nnan oge float %a, %b
+  %r38 = fcmp ninf oge float %a, %b
+  %r39 = fcmp nsz oge float %a, %b
+  %r40 = fcmp arcp oge float %a, %b
+  %r41 = fcmp fast oge float %a, %b
+  %r42 = fcmp nnan ninf oge float %a, %b
+  %r43 = fcmp ord float %a, %b
+  %r44 = fcmp ninf ord float %a, %b
+  %r45 = fcmp nsz ord float %a, %b
+  %r46 = fcmp ueq float %a, %b
+  %r47 = fcmp nnan ueq float %a, %b
+  %r48 = fcmp ninf ueq float %a, %b
+  %r49 = fcmp nsz ueq float %a, %b
+  %r50 = fcmp arcp ueq float %a, %b
+  %r51 = fcmp fast ueq float %a, %b
+  %r52 = fcmp nnan ninf ueq float %a, %b
+  %r53 = fcmp une float %a, %b
+  %r54 = fcmp nnan une float %a, %b
+  %r55 = fcmp ninf une float %a, %b
+  %r56 = fcmp nsz une float %a, %b
+  %r57 = fcmp arcp une float %a, %b
+  %r58 = fcmp fast une float %a, %b
+  %r59 = fcmp nnan ninf une float %a, %b
+  %r60 = fcmp ult float %a, %b
+  %r61 = fcmp nnan ult float %a, %b
+  %r62 = fcmp ninf ult float %a, %b
+  %r63 = fcmp nsz ult float %a, %b
+  %r64 = fcmp arcp ult float %a, %b
+  %r65 = fcmp fast ult float %a, %b
+  %r66 = fcmp nnan ninf ult float %a, %b
+  %r67 = fcmp ugt float %a, %b
+  %r68 = fcmp nnan ugt float %a, %b
+  %r69 = fcmp ninf ugt float %a, %b
+  %r70 = fcmp nsz ugt float %a, %b
+  %r71 = fcmp arcp ugt float %a, %b
+  %r72 = fcmp fast ugt float %a, %b
+  %r73 = fcmp nnan ninf ugt float %a, %b
+  %r74 = fcmp ule float %a, %b
+  %r75 = fcmp nnan ule float %a, %b
+  %r76 = fcmp ninf ule float %a, %b
+  %r77 = fcmp nsz ule float %a, %b
+  %r78 = fcmp arcp ule float %a, %b
+  %r79 = fcmp fast ule float %a, %b
+  %r80 = fcmp nnan ninf ule float %a, %b
+  %r81 = fcmp uge float %a, %b
+  %r82 = fcmp nnan uge float %a, %b
+  %r83 = fcmp ninf uge float %a, %b
+  %r84 = fcmp nsz uge float %a, %b
+  %r85 = fcmp arcp uge float %a, %b
+  %r86 = fcmp fast uge float %a, %b
+  %r87 = fcmp nnan ninf uge float %a, %b
+  %r88 = fcmp uno float %a, %b
+  %r89 = fcmp ninf uno float %a, %b
+  %r90 = fcmp nsz uno float %a, %b
+  ret void
+}
+
+attributes #0 = { convergent nounwind writeonly "correctly-rounded-divide-sqrt-fp-math"="false" "denorms-are-zero"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "uniform-work-group-size"="false" "unsafe-fp-math"="false" "use-soft-float"="false" }
+
+!llvm.module.flags = !{!0}
+!opencl.ocl.version = !{!1}
+!opencl.spir.version = !{!1}
+
+!0 = !{i32 1, !"wchar_size", i32 4}
+!1 = !{i32 2, i32 0}
+!2 = !{i32 0, i32 0}
+!3 = !{!"none", !"none"}
+!4 = !{!"float", !"float"}
+!5 = !{!"", !""}


        


More information about the llvm-commits mailing list