[llvm] 0098f2a - [SPIRV] Add SPIR-V specific intrinsics, two passes and tests

Ilia Diachkov via llvm-commits llvm-commits at lists.llvm.org
Thu May 5 16:23:19 PDT 2022


Author: Ilia Diachkov
Date: 2022-05-06T03:02:00+03:00
New Revision: 0098f2aebb43218f0040f0b739897b0bfe0e259e

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

LOG: [SPIRV] Add SPIR-V specific intrinsics, two passes and tests

The patch adds SPIR-V specific intrinsics required to keep information
critical to SPIR-V consistency (types, constants, etc.) during translation
from IR to MIR.

Two related passes (SPIRVEmitIntrinsics and SPIRVPreLegalizer) and several
LIT tests (passed with this change) have also been added.

It also fixes the issue with opaque pointers in SPIRVGlobalRegistry.cpp
and the mismatch of the data layout between the SPIR-V backend and clang
(Issue #55122).

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

Co-authored-by: Aleksandr Bezzubikov <zuban32s at gmail.com>
Co-authored-by: Michal Paszkowski <michal.paszkowski at outlook.com>
Co-authored-by: Andrey Tretyakov <andrey1.tretyakov at intel.com>
Co-authored-by: Konrad Trifunovic <konrad.trifunovic at intel.com>

Added: 
    llvm/include/llvm/IR/IntrinsicsSPIRV.td
    llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
    llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp
    llvm/test/CodeGen/SPIRV/branching/if-merging.ll
    llvm/test/CodeGen/SPIRV/function/alloca-load-store.ll
    llvm/test/CodeGen/SPIRV/instructions/atomic.ll
    llvm/test/CodeGen/SPIRV/instructions/atomic_acqrel.ll
    llvm/test/CodeGen/SPIRV/instructions/atomic_seq.ll
    llvm/test/CodeGen/SPIRV/instructions/fcmp.ll
    llvm/test/CodeGen/SPIRV/instructions/float-casts.ll
    llvm/test/CodeGen/SPIRV/instructions/icmp.ll
    llvm/test/CodeGen/SPIRV/instructions/integer-casts.ll
    llvm/test/CodeGen/SPIRV/instructions/ptrcmp.ll

Modified: 
    llvm/include/llvm/IR/CMakeLists.txt
    llvm/include/llvm/IR/Intrinsics.td
    llvm/lib/Target/SPIRV/CMakeLists.txt
    llvm/lib/Target/SPIRV/SPIRV.h
    llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
    llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h
    llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
    llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
    llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp
    llvm/lib/Target/SPIRV/SPIRVUtils.cpp
    llvm/lib/Target/SPIRV/SPIRVUtils.h
    llvm/test/CodeGen/SPIRV/function/trivial-function-with-attributes.ll

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/IR/CMakeLists.txt b/llvm/include/llvm/IR/CMakeLists.txt
index 0498fc269b634..872c400f2bf10 100644
--- a/llvm/include/llvm/IR/CMakeLists.txt
+++ b/llvm/include/llvm/IR/CMakeLists.txt
@@ -14,6 +14,7 @@ tablegen(LLVM IntrinsicsNVPTX.h -gen-intrinsic-enums -intrinsic-prefix=nvvm)
 tablegen(LLVM IntrinsicsPowerPC.h -gen-intrinsic-enums -intrinsic-prefix=ppc)
 tablegen(LLVM IntrinsicsR600.h -gen-intrinsic-enums -intrinsic-prefix=r600)
 tablegen(LLVM IntrinsicsRISCV.h -gen-intrinsic-enums -intrinsic-prefix=riscv)
+tablegen(LLVM IntrinsicsSPIRV.h -gen-intrinsic-enums -intrinsic-prefix=spv)
 tablegen(LLVM IntrinsicsS390.h -gen-intrinsic-enums -intrinsic-prefix=s390)
 tablegen(LLVM IntrinsicsWebAssembly.h -gen-intrinsic-enums -intrinsic-prefix=wasm)
 tablegen(LLVM IntrinsicsX86.h -gen-intrinsic-enums -intrinsic-prefix=x86)

diff  --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td
index 72ff1444b28dc..76ff4818fc539 100644
--- a/llvm/include/llvm/IR/Intrinsics.td
+++ b/llvm/include/llvm/IR/Intrinsics.td
@@ -2046,4 +2046,5 @@ include "llvm/IR/IntrinsicsBPF.td"
 include "llvm/IR/IntrinsicsSystemZ.td"
 include "llvm/IR/IntrinsicsWebAssembly.td"
 include "llvm/IR/IntrinsicsRISCV.td"
+include "llvm/IR/IntrinsicsSPIRV.td"
 include "llvm/IR/IntrinsicsVE.td"

diff  --git a/llvm/include/llvm/IR/IntrinsicsSPIRV.td b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
new file mode 100644
index 0000000000000..14c628595d30d
--- /dev/null
+++ b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
@@ -0,0 +1,31 @@
+//===- IntrinsicsSPIRV.td - Defines SPIRV intrinsics -------*- tablegen -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines all of the SPIRV-specific intrinsics.
+//
+//===----------------------------------------------------------------------===//
+
+let TargetPrefix = "spv" in {
+  def int_spv_assign_type : Intrinsic<[], [llvm_any_ty, llvm_metadata_ty]>;
+  def int_spv_assign_name : Intrinsic<[], [llvm_any_ty, llvm_vararg_ty]>;
+
+  def int_spv_track_constant : Intrinsic<[llvm_any_ty], [llvm_any_ty, llvm_metadata_ty]>;
+  def int_spv_init_global : Intrinsic<[], [llvm_any_ty, llvm_any_ty]>;
+  def int_spv_unref_global : Intrinsic<[], [llvm_any_ty]>;
+
+  def int_spv_gep : Intrinsic<[llvm_anyptr_ty], [llvm_i1_ty, llvm_any_ty, llvm_vararg_ty], [ImmArg<ArgIndex<0>>]>;
+  def int_spv_load : Intrinsic<[llvm_i32_ty], [llvm_anyptr_ty, llvm_i16_ty, llvm_i8_ty], [ImmArg<ArgIndex<1>>, ImmArg<ArgIndex<2>>]>;
+  def int_spv_store : Intrinsic<[], [llvm_i32_ty, llvm_anyptr_ty, llvm_i16_ty, llvm_i8_ty], [ImmArg<ArgIndex<2>>, ImmArg<ArgIndex<3>>]>;
+  def int_spv_extractv : Intrinsic<[llvm_any_ty], [llvm_i32_ty, llvm_vararg_ty]>;
+  def int_spv_insertv : Intrinsic<[llvm_i32_ty], [llvm_i32_ty, llvm_any_ty, llvm_vararg_ty]>;
+  def int_spv_extractelt : Intrinsic<[llvm_any_ty], [llvm_any_ty, llvm_anyint_ty]>;
+  def int_spv_insertelt : Intrinsic<[llvm_any_ty], [llvm_any_ty, llvm_any_ty, llvm_anyint_ty]>;
+  def int_spv_const_composite : Intrinsic<[llvm_i32_ty], [llvm_vararg_ty]>;
+  def int_spv_bitcast : Intrinsic<[llvm_any_ty], [llvm_any_ty]>;
+  def int_spv_switch : Intrinsic<[], [llvm_any_ty, llvm_vararg_ty]>;
+}

diff  --git a/llvm/lib/Target/SPIRV/CMakeLists.txt b/llvm/lib/Target/SPIRV/CMakeLists.txt
index 0c52fed54eb5b..f19609efa3cf0 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
+  SPIRVEmitIntrinsics.cpp
   SPIRVGlobalRegistry.cpp
   SPIRVInstrInfo.cpp
   SPIRVInstructionSelector.cpp
@@ -22,6 +23,7 @@ add_llvm_target(SPIRVCodeGen
   SPIRVLegalizerInfo.cpp
   SPIRVMCInstLower.cpp
   SPIRVModuleAnalysis.cpp
+  SPIRVPreLegalizer.cpp
   SPIRVRegisterBankInfo.cpp
   SPIRVRegisterInfo.cpp
   SPIRVSubtarget.cpp

diff  --git a/llvm/lib/Target/SPIRV/SPIRV.h b/llvm/lib/Target/SPIRV/SPIRV.h
index c5740025e6c8b..8da54a5d6e61b 100644
--- a/llvm/lib/Target/SPIRV/SPIRV.h
+++ b/llvm/lib/Target/SPIRV/SPIRV.h
@@ -19,12 +19,16 @@ class SPIRVSubtarget;
 class InstructionSelector;
 class RegisterBankInfo;
 
+FunctionPass *createSPIRVPreLegalizerPass();
+FunctionPass *createSPIRVEmitIntrinsicsPass(SPIRVTargetMachine *TM);
 InstructionSelector *
 createSPIRVInstructionSelector(const SPIRVTargetMachine &TM,
                                const SPIRVSubtarget &Subtarget,
                                const RegisterBankInfo &RBI);
 
 void initializeSPIRVModuleAnalysisPass(PassRegistry &);
+void initializeSPIRVPreLegalizerPass(PassRegistry &);
+void initializeSPIRVEmitIntrinsicsPass(PassRegistry &);
 } // namespace llvm
 
 #endif // LLVM_LIB_TARGET_SPIRV_SPIRV_H

diff  --git a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
new file mode 100644
index 0000000000000..4c5bad4f5683e
--- /dev/null
+++ b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
@@ -0,0 +1,433 @@
+//===-- SPIRVEmitIntrinsics.cpp - emit SPIRV intrinsics ---------*- 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
+//
+//===----------------------------------------------------------------------===//
+//
+// The pass emits SPIRV intrinsics keeping essential high-level information for
+// the translation of LLVM IR to SPIR-V.
+//
+//===----------------------------------------------------------------------===//
+
+#include "SPIRV.h"
+#include "SPIRVTargetMachine.h"
+#include "SPIRVUtils.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/InstIterator.h"
+#include "llvm/IR/InstVisitor.h"
+#include "llvm/IR/IntrinsicsSPIRV.h"
+
+#include <queue>
+
+// This pass performs the following transformation on LLVM IR level required
+// for the following translation to SPIR-V:
+// - replaces direct usages of aggregate constants with target-specific
+//   intrinsics;
+// - replaces aggregates-related instructions (extract/insert, ld/st, etc)
+//   with a target-specific intrinsics;
+// - emits intrinsics for the global variable initializers since IRTranslator
+//   doesn't handle them and it's not very convenient to translate them
+//   ourselves;
+// - emits intrinsics to keep track of the string names assigned to the values;
+// - emits intrinsics to keep track of constants (this is necessary to have an
+//   LLVM IR constant after the IRTranslation is completed) for their further
+//   deduplication;
+// - emits intrinsics to keep track of original LLVM types of the values
+//   to be able to emit proper SPIR-V types eventually.
+//
+// TODO: consider removing spv.track.constant in favor of spv.assign.type.
+
+using namespace llvm;
+
+namespace llvm {
+void initializeSPIRVEmitIntrinsicsPass(PassRegistry &);
+} // namespace llvm
+
+namespace {
+class SPIRVEmitIntrinsics
+    : public FunctionPass,
+      public InstVisitor<SPIRVEmitIntrinsics, Instruction *> {
+  SPIRVTargetMachine *TM = nullptr;
+  IRBuilder<> *IRB = nullptr;
+  Function *F = nullptr;
+  bool TrackConstants = true;
+  DenseMap<Instruction *, Constant *> AggrConsts;
+  DenseSet<Instruction *> AggrStores;
+  void preprocessCompositeConstants();
+  CallInst *buildIntrWithMD(Intrinsic::ID IntrID, ArrayRef<Type *> Types,
+                            Value *Arg, Value *Arg2) {
+    ConstantAsMetadata *CM = ValueAsMetadata::getConstant(Arg);
+    MDTuple *TyMD = MDNode::get(F->getContext(), CM);
+    MetadataAsValue *VMD = MetadataAsValue::get(F->getContext(), TyMD);
+    return IRB->CreateIntrinsic(IntrID, {Types}, {Arg2, VMD});
+  }
+  void replaceMemInstrUses(Instruction *Old, Instruction *New);
+  void processInstrAfterVisit(Instruction *I);
+  void insertAssignTypeIntrs(Instruction *I);
+  void processGlobalValue(GlobalVariable &GV);
+
+public:
+  static char ID;
+  SPIRVEmitIntrinsics() : FunctionPass(ID) {
+    initializeSPIRVEmitIntrinsicsPass(*PassRegistry::getPassRegistry());
+  }
+  SPIRVEmitIntrinsics(SPIRVTargetMachine *_TM) : FunctionPass(ID), TM(_TM) {
+    initializeSPIRVEmitIntrinsicsPass(*PassRegistry::getPassRegistry());
+  }
+  Instruction *visitInstruction(Instruction &I) { return &I; }
+  Instruction *visitSwitchInst(SwitchInst &I);
+  Instruction *visitGetElementPtrInst(GetElementPtrInst &I);
+  Instruction *visitBitCastInst(BitCastInst &I);
+  Instruction *visitInsertElementInst(InsertElementInst &I);
+  Instruction *visitExtractElementInst(ExtractElementInst &I);
+  Instruction *visitInsertValueInst(InsertValueInst &I);
+  Instruction *visitExtractValueInst(ExtractValueInst &I);
+  Instruction *visitLoadInst(LoadInst &I);
+  Instruction *visitStoreInst(StoreInst &I);
+  Instruction *visitAllocaInst(AllocaInst &I);
+  bool runOnFunction(Function &F) override;
+};
+} // namespace
+
+char SPIRVEmitIntrinsics::ID = 0;
+
+INITIALIZE_PASS(SPIRVEmitIntrinsics, "emit-intrinsics", "SPIRV emit intrinsics",
+                false, false)
+
+static inline bool isAssignTypeInstr(const Instruction *I) {
+  return isa<IntrinsicInst>(I) &&
+         cast<IntrinsicInst>(I)->getIntrinsicID() == Intrinsic::spv_assign_type;
+}
+
+static bool isMemInstrToReplace(Instruction *I) {
+  return isa<StoreInst>(I) || isa<LoadInst>(I) || isa<InsertValueInst>(I) ||
+         isa<ExtractValueInst>(I);
+}
+
+static bool isAggrToReplace(const Value *V) {
+  return isa<ConstantAggregate>(V) || isa<ConstantDataArray>(V) ||
+         (isa<ConstantAggregateZero>(V) && !V->getType()->isVectorTy());
+}
+
+static void setInsertPointSkippingPhis(IRBuilder<> &B, Instruction *I) {
+  if (isa<PHINode>(I))
+    B.SetInsertPoint(I->getParent(), I->getParent()->getFirstInsertionPt());
+  else
+    B.SetInsertPoint(I);
+}
+
+static bool requireAssignType(Instruction *I) {
+  IntrinsicInst *Intr = dyn_cast<IntrinsicInst>(I);
+  if (Intr) {
+    switch (Intr->getIntrinsicID()) {
+    case Intrinsic::invariant_start:
+    case Intrinsic::invariant_end:
+      return false;
+    }
+  }
+  return true;
+}
+
+void SPIRVEmitIntrinsics::replaceMemInstrUses(Instruction *Old,
+                                              Instruction *New) {
+  while (!Old->user_empty()) {
+    auto *U = Old->user_back();
+    if (isMemInstrToReplace(U) || isa<ReturnInst>(U)) {
+      U->replaceUsesOfWith(Old, New);
+    } else if (isAssignTypeInstr(U)) {
+      IRB->SetInsertPoint(U);
+      SmallVector<Value *, 2> Args = {New, U->getOperand(1)};
+      IRB->CreateIntrinsic(Intrinsic::spv_assign_type, {New->getType()}, Args);
+      U->eraseFromParent();
+    } else {
+      llvm_unreachable("illegal aggregate intrinsic user");
+    }
+  }
+  Old->eraseFromParent();
+}
+
+void SPIRVEmitIntrinsics::preprocessCompositeConstants() {
+  std::queue<Instruction *> Worklist;
+  for (auto &I : instructions(F))
+    Worklist.push(&I);
+
+  while (!Worklist.empty()) {
+    auto *I = Worklist.front();
+    assert(I);
+    bool KeepInst = false;
+    for (const auto &Op : I->operands()) {
+      auto BuildCompositeIntrinsic = [&KeepInst, &Worklist, &I, &Op,
+                                      this](Constant *AggrC,
+                                            ArrayRef<Value *> Args) {
+        IRB->SetInsertPoint(I);
+        auto *CCI =
+            IRB->CreateIntrinsic(Intrinsic::spv_const_composite, {}, {Args});
+        Worklist.push(CCI);
+        I->replaceUsesOfWith(Op, CCI);
+        KeepInst = true;
+        AggrConsts[CCI] = AggrC;
+      };
+
+      if (auto *AggrC = dyn_cast<ConstantAggregate>(Op)) {
+        SmallVector<Value *> Args(AggrC->op_begin(), AggrC->op_end());
+        BuildCompositeIntrinsic(AggrC, Args);
+      } else if (auto *AggrC = dyn_cast<ConstantDataArray>(Op)) {
+        SmallVector<Value *> Args;
+        for (unsigned i = 0; i < AggrC->getNumElements(); ++i)
+          Args.push_back(AggrC->getElementAsConstant(i));
+        BuildCompositeIntrinsic(AggrC, Args);
+      } else if (isa<ConstantAggregateZero>(Op) &&
+                 !Op->getType()->isVectorTy()) {
+        auto *AggrC = cast<ConstantAggregateZero>(Op);
+        SmallVector<Value *> Args(AggrC->op_begin(), AggrC->op_end());
+        BuildCompositeIntrinsic(AggrC, Args);
+      }
+    }
+    if (!KeepInst)
+      Worklist.pop();
+  }
+}
+
+Instruction *SPIRVEmitIntrinsics::visitSwitchInst(SwitchInst &I) {
+  SmallVector<Value *, 4> Args;
+  for (auto &Op : I.operands())
+    if (Op.get()->getType()->isSized())
+      Args.push_back(Op);
+  IRB->CreateIntrinsic(Intrinsic::spv_switch, {I.getOperand(0)->getType()},
+                       {Args});
+  return &I;
+}
+
+Instruction *SPIRVEmitIntrinsics::visitGetElementPtrInst(GetElementPtrInst &I) {
+  SmallVector<Type *, 2> Types = {I.getType(), I.getOperand(0)->getType()};
+  SmallVector<Value *, 4> Args;
+  Args.push_back(IRB->getInt1(I.isInBounds()));
+  for (auto &Op : I.operands())
+    Args.push_back(Op);
+  auto *NewI = IRB->CreateIntrinsic(Intrinsic::spv_gep, {Types}, {Args});
+  I.replaceAllUsesWith(NewI);
+  I.eraseFromParent();
+  return NewI;
+}
+
+Instruction *SPIRVEmitIntrinsics::visitBitCastInst(BitCastInst &I) {
+  SmallVector<Type *, 2> Types = {I.getType(), I.getOperand(0)->getType()};
+  SmallVector<Value *> Args(I.op_begin(), I.op_end());
+  auto *NewI = IRB->CreateIntrinsic(Intrinsic::spv_bitcast, {Types}, {Args});
+  std::string InstName = I.hasName() ? I.getName().str() : "";
+  I.replaceAllUsesWith(NewI);
+  I.eraseFromParent();
+  NewI->setName(InstName);
+  return NewI;
+}
+
+Instruction *SPIRVEmitIntrinsics::visitInsertElementInst(InsertElementInst &I) {
+  SmallVector<Type *, 4> Types = {I.getType(), I.getOperand(0)->getType(),
+                                  I.getOperand(1)->getType(),
+                                  I.getOperand(2)->getType()};
+  SmallVector<Value *> Args(I.op_begin(), I.op_end());
+  auto *NewI = IRB->CreateIntrinsic(Intrinsic::spv_insertelt, {Types}, {Args});
+  std::string InstName = I.hasName() ? I.getName().str() : "";
+  I.replaceAllUsesWith(NewI);
+  I.eraseFromParent();
+  NewI->setName(InstName);
+  return NewI;
+}
+
+Instruction *
+SPIRVEmitIntrinsics::visitExtractElementInst(ExtractElementInst &I) {
+  SmallVector<Type *, 3> Types = {I.getType(), I.getVectorOperandType(),
+                                  I.getIndexOperand()->getType()};
+  SmallVector<Value *, 2> Args = {I.getVectorOperand(), I.getIndexOperand()};
+  auto *NewI = IRB->CreateIntrinsic(Intrinsic::spv_extractelt, {Types}, {Args});
+  std::string InstName = I.hasName() ? I.getName().str() : "";
+  I.replaceAllUsesWith(NewI);
+  I.eraseFromParent();
+  NewI->setName(InstName);
+  return NewI;
+}
+
+Instruction *SPIRVEmitIntrinsics::visitInsertValueInst(InsertValueInst &I) {
+  SmallVector<Type *, 1> Types = {I.getInsertedValueOperand()->getType()};
+  SmallVector<Value *> Args;
+  for (auto &Op : I.operands())
+    if (isa<UndefValue>(Op))
+      Args.push_back(UndefValue::get(IRB->getInt32Ty()));
+    else
+      Args.push_back(Op);
+  for (auto &Op : I.indices())
+    Args.push_back(IRB->getInt32(Op));
+  Instruction *NewI =
+      IRB->CreateIntrinsic(Intrinsic::spv_insertv, {Types}, {Args});
+  replaceMemInstrUses(&I, NewI);
+  return NewI;
+}
+
+Instruction *SPIRVEmitIntrinsics::visitExtractValueInst(ExtractValueInst &I) {
+  SmallVector<Value *> Args;
+  for (auto &Op : I.operands())
+    Args.push_back(Op);
+  for (auto &Op : I.indices())
+    Args.push_back(IRB->getInt32(Op));
+  auto *NewI =
+      IRB->CreateIntrinsic(Intrinsic::spv_extractv, {I.getType()}, {Args});
+  I.replaceAllUsesWith(NewI);
+  I.eraseFromParent();
+  return NewI;
+}
+
+Instruction *SPIRVEmitIntrinsics::visitLoadInst(LoadInst &I) {
+  if (!I.getType()->isAggregateType())
+    return &I;
+  TrackConstants = false;
+  const auto *TLI = TM->getSubtargetImpl()->getTargetLowering();
+  MachineMemOperand::Flags Flags =
+      TLI->getLoadMemOperandFlags(I, F->getParent()->getDataLayout());
+  auto *NewI =
+      IRB->CreateIntrinsic(Intrinsic::spv_load, {I.getOperand(0)->getType()},
+                           {I.getPointerOperand(), IRB->getInt16(Flags),
+                            IRB->getInt8(I.getAlignment())});
+  replaceMemInstrUses(&I, NewI);
+  return NewI;
+}
+
+Instruction *SPIRVEmitIntrinsics::visitStoreInst(StoreInst &I) {
+  if (!AggrStores.contains(&I))
+    return &I;
+  TrackConstants = false;
+  const auto *TLI = TM->getSubtargetImpl()->getTargetLowering();
+  MachineMemOperand::Flags Flags =
+      TLI->getStoreMemOperandFlags(I, F->getParent()->getDataLayout());
+  auto *PtrOp = I.getPointerOperand();
+  auto *NewI =
+      IRB->CreateIntrinsic(Intrinsic::spv_store, {PtrOp->getType()},
+                           {I.getValueOperand(), PtrOp, IRB->getInt16(Flags),
+                            IRB->getInt8(I.getAlignment())});
+  I.eraseFromParent();
+  return NewI;
+}
+
+Instruction *SPIRVEmitIntrinsics::visitAllocaInst(AllocaInst &I) {
+  TrackConstants = false;
+  return &I;
+}
+
+void SPIRVEmitIntrinsics::processGlobalValue(GlobalVariable &GV) {
+  // Skip special artifical variable llvm.global.annotations.
+  if (GV.getName() == "llvm.global.annotations")
+    return;
+  if (GV.hasInitializer() && !isa<UndefValue>(GV.getInitializer())) {
+    Constant *Init = GV.getInitializer();
+    Type *Ty = isAggrToReplace(Init) ? IRB->getInt32Ty() : Init->getType();
+    Constant *Const = isAggrToReplace(Init) ? IRB->getInt32(1) : Init;
+    auto *InitInst = IRB->CreateIntrinsic(Intrinsic::spv_init_global,
+                                          {GV.getType(), Ty}, {&GV, Const});
+    InitInst->setArgOperand(1, Init);
+  }
+  if ((!GV.hasInitializer() || isa<UndefValue>(GV.getInitializer())) &&
+      GV.getNumUses() == 0)
+    IRB->CreateIntrinsic(Intrinsic::spv_unref_global, GV.getType(), &GV);
+}
+
+void SPIRVEmitIntrinsics::insertAssignTypeIntrs(Instruction *I) {
+  Type *Ty = I->getType();
+  if (!Ty->isVoidTy() && requireAssignType(I)) {
+    setInsertPointSkippingPhis(*IRB, I->getNextNode());
+    Type *TypeToAssign = Ty;
+    if (auto *II = dyn_cast<IntrinsicInst>(I)) {
+      if (II->getIntrinsicID() == Intrinsic::spv_const_composite) {
+        auto t = AggrConsts.find(II);
+        assert(t != AggrConsts.end());
+        TypeToAssign = t->second->getType();
+      }
+    }
+    Constant *Const = Constant::getNullValue(TypeToAssign);
+    buildIntrWithMD(Intrinsic::spv_assign_type, {Ty}, Const, I);
+  }
+  for (const auto &Op : I->operands()) {
+    if (isa<ConstantPointerNull>(Op) || isa<UndefValue>(Op) ||
+        // Check GetElementPtrConstantExpr case.
+        (isa<ConstantExpr>(Op) && isa<GEPOperator>(Op))) {
+      IRB->SetInsertPoint(I);
+      buildIntrWithMD(Intrinsic::spv_assign_type, {Op->getType()}, Op, Op);
+    }
+  }
+  // StoreInst's operand type can be changed in the next stage so we need to
+  // store it in the set.
+  if (isa<StoreInst>(I) &&
+      cast<StoreInst>(I)->getValueOperand()->getType()->isAggregateType())
+    AggrStores.insert(I);
+}
+
+void SPIRVEmitIntrinsics::processInstrAfterVisit(Instruction *I) {
+  auto *II = dyn_cast<IntrinsicInst>(I);
+  if (II && II->getIntrinsicID() == Intrinsic::spv_const_composite &&
+      TrackConstants) {
+    IRB->SetInsertPoint(I->getNextNode());
+    Type *Ty = IRB->getInt32Ty();
+    auto t = AggrConsts.find(I);
+    assert(t != AggrConsts.end());
+    auto *NewOp =
+        buildIntrWithMD(Intrinsic::spv_track_constant, {Ty, Ty}, t->second, I);
+    I->replaceAllUsesWith(NewOp);
+    NewOp->setArgOperand(0, I);
+  }
+  for (const auto &Op : I->operands()) {
+    if ((isa<ConstantAggregateZero>(Op) && Op->getType()->isVectorTy()) ||
+        isa<PHINode>(I) || isa<SwitchInst>(I))
+      TrackConstants = false;
+    if (isa<ConstantData>(Op) && TrackConstants) {
+      unsigned OpNo = Op.getOperandNo();
+      if (II && ((II->getIntrinsicID() == Intrinsic::spv_gep && OpNo == 0) ||
+                 (II->paramHasAttr(OpNo, Attribute::ImmArg))))
+        continue;
+      IRB->SetInsertPoint(I);
+      auto *NewOp = buildIntrWithMD(Intrinsic::spv_track_constant,
+                                    {Op->getType(), Op->getType()}, Op, Op);
+      I->setOperand(OpNo, NewOp);
+    }
+  }
+  if (I->hasName()) {
+    setInsertPointSkippingPhis(*IRB, I->getNextNode());
+    std::vector<Value *> Args = {I};
+    addStringImm(I->getName(), *IRB, Args);
+    IRB->CreateIntrinsic(Intrinsic::spv_assign_name, {I->getType()}, Args);
+  }
+}
+
+bool SPIRVEmitIntrinsics::runOnFunction(Function &Func) {
+  if (Func.isDeclaration())
+    return false;
+  F = &Func;
+  IRB = new IRBuilder<>(Func.getContext());
+  AggrConsts.clear();
+  AggrStores.clear();
+
+  IRB->SetInsertPoint(&Func.getEntryBlock().front());
+
+  for (auto &GV : Func.getParent()->globals())
+    processGlobalValue(GV);
+
+  preprocessCompositeConstants();
+  SmallVector<Instruction *> Worklist;
+  for (auto &I : instructions(Func))
+    Worklist.push_back(&I);
+
+  for (auto &I : Worklist)
+    insertAssignTypeIntrs(I);
+
+  for (auto *I : Worklist) {
+    TrackConstants = true;
+    if (!I->getType()->isVoidTy() || isa<StoreInst>(I))
+      IRB->SetInsertPoint(I->getNextNode());
+    I = visit(*I);
+    processInstrAfterVisit(I);
+  }
+  return true;
+}
+
+FunctionPass *llvm::createSPIRVEmitIntrinsicsPass(SPIRVTargetMachine *TM) {
+  return new SPIRVEmitIntrinsics(TM);
+}

diff  --git a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
index f1d9386b731c8..02a6905a1abcf 100644
--- a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
@@ -30,14 +30,14 @@ SPIRVType *SPIRVGlobalRegistry::assignTypeToVReg(
 
   SPIRVType *SpirvType =
       getOrCreateSPIRVType(Type, MIRBuilder, AccessQual, EmitIR);
-  assignSPIRVTypeToVReg(SpirvType, VReg, MIRBuilder);
+  assignSPIRVTypeToVReg(SpirvType, VReg, MIRBuilder.getMF());
   return SpirvType;
 }
 
 void SPIRVGlobalRegistry::assignSPIRVTypeToVReg(SPIRVType *SpirvType,
                                                 Register VReg,
-                                                MachineIRBuilder &MIRBuilder) {
-  VRegToTypeMap[&MIRBuilder.getMF()][VReg] = SpirvType;
+                                                MachineFunction &MF) {
+  VRegToTypeMap[&MF][VReg] = SpirvType;
 }
 
 static Register createTypeVReg(MachineIRBuilder &MIRBuilder) {
@@ -191,7 +191,7 @@ Register SPIRVGlobalRegistry::buildGlobalVariable(
   if (Reg != ResVReg) {
     LLT RegLLTy = LLT::pointer(MRI->getType(ResVReg).getAddressSpace(), 32);
     MRI->setType(Reg, RegLLTy);
-    assignSPIRVTypeToVReg(BaseType, Reg, MIRBuilder);
+    assignSPIRVTypeToVReg(BaseType, Reg, MIRBuilder.getMF());
   }
 
   // If it's a global variable with name, output OpName for it.
@@ -283,16 +283,23 @@ SPIRVType *SPIRVGlobalRegistry::createSPIRVType(const Type *Ty,
     return getOpTypeFunction(RetTy, ParamTypes, MIRBuilder);
   }
   if (auto PType = dyn_cast<PointerType>(Ty)) {
-    Type *ElemType = PType->getPointerElementType();
-
-    // Some OpenCL and SPIRV builtins like image2d_t are passed in as pointers,
-    // but should be treated as custom types like OpTypeImage.
-    assert(!isa<StructType>(ElemType) && "Unsupported StructType pointer");
-
-    // Otherwise, treat it as a regular pointer type.
+    SPIRVType *SpvElementType;
+    // At the moment, all opaque pointers correspond to i8 element type.
+    // TODO: change the implementation once opaque pointers are supported
+    // in the SPIR-V specification.
+    if (PType->isOpaque()) {
+      SpvElementType = getOrCreateSPIRVIntegerType(8, MIRBuilder);
+    } else {
+      Type *ElemType = PType->getNonOpaquePointerElementType();
+      // TODO: support OpenCL and SPIRV builtins like image2d_t that are passed
+      // as pointers, but should be treated as custom types like OpTypeImage.
+      assert(!isa<StructType>(ElemType) && "Unsupported StructType pointer");
+
+      // Otherwise, treat it as a regular pointer type.
+      SpvElementType = getOrCreateSPIRVType(
+          ElemType, MIRBuilder, SPIRV::AccessQualifier::ReadWrite, EmitIR);
+    }
     auto SC = addressSpaceToStorageClass(PType->getAddressSpace());
-    SPIRVType *SpvElementType = getOrCreateSPIRVType(
-        ElemType, MIRBuilder, SPIRV::AccessQualifier::ReadWrite, EmitIR);
     return getOpTypePointer(SC, SpvElementType, MIRBuilder);
   }
   llvm_unreachable("Unable to convert LLVM type to SPIRVType");

diff  --git a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h
index b6727a6dd73a1..952ab4c13e294 100644
--- a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h
+++ b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h
@@ -58,7 +58,7 @@ class SPIRVGlobalRegistry {
   // In cases where the SPIR-V type is already known, this function can be
   // used to map it to the given VReg via an ASSIGN_TYPE instruction.
   void assignSPIRVTypeToVReg(SPIRVType *Type, Register VReg,
-                             MachineIRBuilder &MIRBuilder);
+                             MachineFunction &MF);
 
   // Either generate a new OpTypeXXX instruction or return an existing one
   // corresponding to the given LLVM IR type.

diff  --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index 367fca0a06874..9294a60506a8d 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -24,6 +24,7 @@
 #include "llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h"
 #include "llvm/CodeGen/MachineInstrBuilder.h"
 #include "llvm/CodeGen/MachineRegisterInfo.h"
+#include "llvm/IR/IntrinsicsSPIRV.h"
 #include "llvm/Support/Debug.h"
 
 #define DEBUG_TYPE "spirv-isel"
@@ -139,6 +140,16 @@ class SPIRVInstructionSelector : public InstructionSelector {
                      MachineInstr &I) const;
   bool selectIntrinsic(Register ResVReg, const SPIRVType *ResType,
                        MachineInstr &I) const;
+  bool selectExtractVal(Register ResVReg, const SPIRVType *ResType,
+                        MachineInstr &I) const;
+  bool selectInsertVal(Register ResVReg, const SPIRVType *ResType,
+                       MachineInstr &I) const;
+  bool selectExtractElt(Register ResVReg, const SPIRVType *ResType,
+                        MachineInstr &I) const;
+  bool selectInsertElt(Register ResVReg, const SPIRVType *ResType,
+                       MachineInstr &I) const;
+  bool selectGEP(Register ResVReg, const SPIRVType *ResType,
+                 MachineInstr &I) const;
 
   bool selectFrameIndex(Register ResVReg, const SPIRVType *ResType,
                         MachineInstr &I) const;
@@ -968,10 +979,179 @@ bool SPIRVInstructionSelector::selectOpUndef(Register ResVReg,
       .constrainAllUses(TII, TRI, RBI);
 }
 
+static bool isImm(const MachineOperand &MO, MachineRegisterInfo *MRI) {
+  assert(MO.isReg());
+  const SPIRVType *TypeInst = MRI->getVRegDef(MO.getReg());
+  if (TypeInst->getOpcode() != SPIRV::ASSIGN_TYPE)
+    return false;
+  assert(TypeInst->getOperand(1).isReg());
+  MachineInstr *ImmInst = MRI->getVRegDef(TypeInst->getOperand(1).getReg());
+  return ImmInst->getOpcode() == TargetOpcode::G_CONSTANT;
+}
+
+static int64_t foldImm(const MachineOperand &MO, MachineRegisterInfo *MRI) {
+  const SPIRVType *TypeInst = MRI->getVRegDef(MO.getReg());
+  MachineInstr *ImmInst = MRI->getVRegDef(TypeInst->getOperand(1).getReg());
+  assert(ImmInst->getOpcode() == TargetOpcode::G_CONSTANT);
+  return ImmInst->getOperand(1).getCImm()->getZExtValue();
+}
+
+bool SPIRVInstructionSelector::selectInsertVal(Register ResVReg,
+                                               const SPIRVType *ResType,
+                                               MachineInstr &I) const {
+  MachineBasicBlock &BB = *I.getParent();
+  return BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpCompositeInsert))
+      .addDef(ResVReg)
+      .addUse(GR.getSPIRVTypeID(ResType))
+      // object to insert
+      .addUse(I.getOperand(3).getReg())
+      // composite to insert into
+      .addUse(I.getOperand(2).getReg())
+      // TODO: support arbitrary number of indices
+      .addImm(foldImm(I.getOperand(4), MRI))
+      .constrainAllUses(TII, TRI, RBI);
+}
+
+bool SPIRVInstructionSelector::selectExtractVal(Register ResVReg,
+                                                const SPIRVType *ResType,
+                                                MachineInstr &I) const {
+  MachineBasicBlock &BB = *I.getParent();
+  return BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpCompositeExtract))
+      .addDef(ResVReg)
+      .addUse(GR.getSPIRVTypeID(ResType))
+      .addUse(I.getOperand(2).getReg())
+      // TODO: support arbitrary number of indices
+      .addImm(foldImm(I.getOperand(3), MRI))
+      .constrainAllUses(TII, TRI, RBI);
+}
+
+bool SPIRVInstructionSelector::selectInsertElt(Register ResVReg,
+                                               const SPIRVType *ResType,
+                                               MachineInstr &I) const {
+  if (isImm(I.getOperand(4), MRI))
+    return selectInsertVal(ResVReg, ResType, I);
+  MachineBasicBlock &BB = *I.getParent();
+  return BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpVectorInsertDynamic))
+      .addDef(ResVReg)
+      .addUse(GR.getSPIRVTypeID(ResType))
+      .addUse(I.getOperand(2).getReg())
+      .addUse(I.getOperand(3).getReg())
+      .addUse(I.getOperand(4).getReg())
+      .constrainAllUses(TII, TRI, RBI);
+}
+
+bool SPIRVInstructionSelector::selectExtractElt(Register ResVReg,
+                                                const SPIRVType *ResType,
+                                                MachineInstr &I) const {
+  if (isImm(I.getOperand(3), MRI))
+    return selectExtractVal(ResVReg, ResType, I);
+  MachineBasicBlock &BB = *I.getParent();
+  return BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpVectorExtractDynamic))
+      .addDef(ResVReg)
+      .addUse(GR.getSPIRVTypeID(ResType))
+      .addUse(I.getOperand(2).getReg())
+      .addUse(I.getOperand(3).getReg())
+      .constrainAllUses(TII, TRI, RBI);
+}
+
+bool SPIRVInstructionSelector::selectGEP(Register ResVReg,
+                                         const SPIRVType *ResType,
+                                         MachineInstr &I) const {
+  // In general we should also support OpAccessChain instrs here (i.e. not
+  // PtrAccessChain) but SPIRV-LLVM Translator doesn't emit them at all and so
+  // do we to stay compliant with its test and more importantly consumers.
+  unsigned Opcode = I.getOperand(2).getImm() ? SPIRV::OpInBoundsPtrAccessChain
+                                             : SPIRV::OpPtrAccessChain;
+  auto Res = BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(Opcode))
+                 .addDef(ResVReg)
+                 .addUse(GR.getSPIRVTypeID(ResType))
+                 // Object to get a pointer to.
+                 .addUse(I.getOperand(3).getReg());
+  // Adding indices.
+  for (unsigned i = 4; i < I.getNumExplicitOperands(); ++i)
+    Res.addUse(I.getOperand(i).getReg());
+  return Res.constrainAllUses(TII, TRI, RBI);
+}
+
 bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
                                                const SPIRVType *ResType,
                                                MachineInstr &I) const {
-  llvm_unreachable("Intrinsic selection not implemented");
+  MachineBasicBlock &BB = *I.getParent();
+  switch (I.getIntrinsicID()) {
+  case Intrinsic::spv_load:
+    return selectLoad(ResVReg, ResType, I);
+    break;
+  case Intrinsic::spv_store:
+    return selectStore(I);
+    break;
+  case Intrinsic::spv_extractv:
+    return selectExtractVal(ResVReg, ResType, I);
+    break;
+  case Intrinsic::spv_insertv:
+    return selectInsertVal(ResVReg, ResType, I);
+    break;
+  case Intrinsic::spv_extractelt:
+    return selectExtractElt(ResVReg, ResType, I);
+    break;
+  case Intrinsic::spv_insertelt:
+    return selectInsertElt(ResVReg, ResType, I);
+    break;
+  case Intrinsic::spv_gep:
+    return selectGEP(ResVReg, ResType, I);
+    break;
+  case Intrinsic::spv_unref_global:
+  case Intrinsic::spv_init_global: {
+    MachineInstr *MI = MRI->getVRegDef(I.getOperand(1).getReg());
+    MachineInstr *Init = I.getNumExplicitOperands() > 2
+                             ? MRI->getVRegDef(I.getOperand(2).getReg())
+                             : nullptr;
+    assert(MI);
+    return selectGlobalValue(MI->getOperand(0).getReg(), *MI, Init);
+  } break;
+  case Intrinsic::spv_const_composite: {
+    // If no values are attached, the composite is null constant.
+    bool IsNull = I.getNumExplicitDefs() + 1 == I.getNumExplicitOperands();
+    unsigned Opcode =
+        IsNull ? SPIRV::OpConstantNull : SPIRV::OpConstantComposite;
+    auto MIB = BuildMI(BB, I, I.getDebugLoc(), TII.get(Opcode))
+                   .addDef(ResVReg)
+                   .addUse(GR.getSPIRVTypeID(ResType));
+    // skip type MD node we already used when generated assign.type for this
+    if (!IsNull) {
+      for (unsigned i = I.getNumExplicitDefs() + 1;
+           i < I.getNumExplicitOperands(); ++i) {
+        MIB.addUse(I.getOperand(i).getReg());
+      }
+    }
+    return MIB.constrainAllUses(TII, TRI, RBI);
+  } break;
+  case Intrinsic::spv_assign_name: {
+    auto MIB = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpName));
+    MIB.addUse(I.getOperand(I.getNumExplicitDefs() + 1).getReg());
+    for (unsigned i = I.getNumExplicitDefs() + 2;
+         i < I.getNumExplicitOperands(); ++i) {
+      MIB.addImm(I.getOperand(i).getImm());
+    }
+    return MIB.constrainAllUses(TII, TRI, RBI);
+  } break;
+  case Intrinsic::spv_switch: {
+    auto MIB = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpSwitch));
+    for (unsigned i = 1; i < I.getNumExplicitOperands(); ++i) {
+      if (I.getOperand(i).isReg())
+        MIB.addReg(I.getOperand(i).getReg());
+      else if (I.getOperand(i).isCImm())
+        addNumImm(I.getOperand(i).getCImm()->getValue(), MIB);
+      else if (I.getOperand(i).isMBB())
+        MIB.addMBB(I.getOperand(i).getMBB());
+      else
+        llvm_unreachable("Unexpected OpSwitch operand");
+    }
+    return MIB.constrainAllUses(TII, TRI, RBI);
+  } break;
+  default:
+    llvm_unreachable("Intrinsic selection not implemented");
+  }
+  return true;
 }
 
 bool SPIRVInstructionSelector::selectFrameIndex(Register ResVReg,

diff  --git a/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp b/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
index fff277e9ad251..87f9e9545dd39 100644
--- a/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
@@ -264,7 +264,7 @@ static Register convertPtrToInt(Register Reg, LLT ConvTy, SPIRVType *SpirvType,
                                 MachineRegisterInfo &MRI,
                                 SPIRVGlobalRegistry *GR) {
   Register ConvReg = MRI.createGenericVirtualRegister(ConvTy);
-  GR->assignSPIRVTypeToVReg(SpirvType, ConvReg, Helper.MIRBuilder);
+  GR->assignSPIRVTypeToVReg(SpirvType, ConvReg, Helper.MIRBuilder.getMF());
   Helper.MIRBuilder.buildInstr(TargetOpcode::G_PTRTOINT)
       .addDef(ConvReg)
       .addUse(Reg);

diff  --git a/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp b/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp
new file mode 100644
index 0000000000000..687f84046650d
--- /dev/null
+++ b/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp
@@ -0,0 +1,440 @@
+//===-- SPIRVPreLegalizer.cpp - prepare IR for legalization -----*- 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
+//
+//===----------------------------------------------------------------------===//
+//
+// The pass prepares IR for legalization: it assigns SPIR-V types to registers
+// and removes intrinsics which holded these types during IR translation.
+// Also it processes constants and registers them in GR to avoid duplication.
+//
+//===----------------------------------------------------------------------===//
+
+#include "SPIRV.h"
+#include "SPIRVGlobalRegistry.h"
+#include "SPIRVSubtarget.h"
+#include "SPIRVUtils.h"
+#include "llvm/ADT/PostOrderIterator.h"
+#include "llvm/Analysis/OptimizationRemarkEmitter.h"
+#include "llvm/IR/Attributes.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/DebugInfoMetadata.h"
+#include "llvm/IR/IntrinsicsSPIRV.h"
+#include "llvm/Target/TargetIntrinsicInfo.h"
+
+#define DEBUG_TYPE "spirv-prelegalizer"
+
+using namespace llvm;
+
+namespace {
+class SPIRVPreLegalizer : public MachineFunctionPass {
+public:
+  static char ID;
+  SPIRVPreLegalizer() : MachineFunctionPass(ID) {
+    initializeSPIRVPreLegalizerPass(*PassRegistry::getPassRegistry());
+  }
+  bool runOnMachineFunction(MachineFunction &MF) override;
+};
+} // namespace
+
+static bool isSpvIntrinsic(MachineInstr &MI, Intrinsic::ID IntrinsicID) {
+  if (MI.getOpcode() == TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS &&
+      MI.getIntrinsicID() == IntrinsicID)
+    return true;
+  return false;
+}
+
+static void foldConstantsIntoIntrinsics(MachineFunction &MF) {
+  SmallVector<MachineInstr *, 10> ToErase;
+  MachineRegisterInfo &MRI = MF.getRegInfo();
+  const unsigned AssignNameOperandShift = 2;
+  for (MachineBasicBlock &MBB : MF) {
+    for (MachineInstr &MI : MBB) {
+      if (!isSpvIntrinsic(MI, Intrinsic::spv_assign_name))
+        continue;
+      unsigned NumOp = MI.getNumExplicitDefs() + AssignNameOperandShift;
+      while (MI.getOperand(NumOp).isReg()) {
+        MachineOperand &MOp = MI.getOperand(NumOp);
+        MachineInstr *ConstMI = MRI.getVRegDef(MOp.getReg());
+        assert(ConstMI->getOpcode() == TargetOpcode::G_CONSTANT);
+        MI.removeOperand(NumOp);
+        MI.addOperand(MachineOperand::CreateImm(
+            ConstMI->getOperand(1).getCImm()->getZExtValue()));
+        if (MRI.use_empty(ConstMI->getOperand(0).getReg()))
+          ToErase.push_back(ConstMI);
+      }
+    }
+  }
+  for (MachineInstr *MI : ToErase)
+    MI->eraseFromParent();
+}
+
+static void insertBitcasts(MachineFunction &MF, SPIRVGlobalRegistry *GR,
+                           MachineIRBuilder MIB) {
+  SmallVector<MachineInstr *, 10> ToErase;
+  for (MachineBasicBlock &MBB : MF) {
+    for (MachineInstr &MI : MBB) {
+      if (!isSpvIntrinsic(MI, Intrinsic::spv_bitcast))
+        continue;
+      assert(MI.getOperand(2).isReg());
+      MIB.setInsertPt(*MI.getParent(), MI);
+      MIB.buildBitcast(MI.getOperand(0).getReg(), MI.getOperand(2).getReg());
+      ToErase.push_back(&MI);
+    }
+  }
+  for (MachineInstr *MI : ToErase)
+    MI->eraseFromParent();
+}
+
+// Translating GV, IRTranslator sometimes generates following IR:
+//   %1 = G_GLOBAL_VALUE
+//   %2 = COPY %1
+//   %3 = G_ADDRSPACE_CAST %2
+// New registers have no SPIRVType and no register class info.
+//
+// Set SPIRVType for GV, propagate it from GV to other instructions,
+// also set register classes.
+static SPIRVType *propagateSPIRVType(MachineInstr *MI, SPIRVGlobalRegistry *GR,
+                                     MachineRegisterInfo &MRI,
+                                     MachineIRBuilder &MIB) {
+  SPIRVType *SpirvTy = nullptr;
+  assert(MI && "Machine instr is expected");
+  if (MI->getOperand(0).isReg()) {
+    Register Reg = MI->getOperand(0).getReg();
+    SpirvTy = GR->getSPIRVTypeForVReg(Reg);
+    if (!SpirvTy) {
+      switch (MI->getOpcode()) {
+      case TargetOpcode::G_CONSTANT: {
+        MIB.setInsertPt(*MI->getParent(), MI);
+        Type *Ty = MI->getOperand(1).getCImm()->getType();
+        SpirvTy = GR->getOrCreateSPIRVType(Ty, MIB);
+        break;
+      }
+      case TargetOpcode::G_GLOBAL_VALUE: {
+        MIB.setInsertPt(*MI->getParent(), MI);
+        Type *Ty = MI->getOperand(1).getGlobal()->getType();
+        SpirvTy = GR->getOrCreateSPIRVType(Ty, MIB);
+        break;
+      }
+      case TargetOpcode::G_TRUNC:
+      case TargetOpcode::G_ADDRSPACE_CAST:
+      case TargetOpcode::COPY: {
+        MachineOperand &Op = MI->getOperand(1);
+        MachineInstr *Def = Op.isReg() ? MRI.getVRegDef(Op.getReg()) : nullptr;
+        if (Def)
+          SpirvTy = propagateSPIRVType(Def, GR, MRI, MIB);
+        break;
+      }
+      default:
+        break;
+      }
+      if (SpirvTy)
+        GR->assignSPIRVTypeToVReg(SpirvTy, Reg, MIB.getMF());
+      if (!MRI.getRegClassOrNull(Reg))
+        MRI.setRegClass(Reg, &SPIRV::IDRegClass);
+    }
+  }
+  return SpirvTy;
+}
+
+// Insert ASSIGN_TYPE instuction between Reg and its definition, set NewReg as
+// a dst of the definition, assign SPIRVType to both registers. If SpirvTy is
+// provided, use it as SPIRVType in ASSIGN_TYPE, otherwise create it from Ty.
+// TODO: maybe move to SPIRVUtils.
+static Register insertAssignInstr(Register Reg, Type *Ty, SPIRVType *SpirvTy,
+                                  SPIRVGlobalRegistry *GR,
+                                  MachineIRBuilder &MIB,
+                                  MachineRegisterInfo &MRI) {
+  MachineInstr *Def = MRI.getVRegDef(Reg);
+  assert((Ty || SpirvTy) && "Either LLVM or SPIRV type is expected.");
+  MIB.setInsertPt(*Def->getParent(),
+                  (Def->getNextNode() ? Def->getNextNode()->getIterator()
+                                      : Def->getParent()->end()));
+  Register NewReg = MRI.createGenericVirtualRegister(MRI.getType(Reg));
+  if (auto *RC = MRI.getRegClassOrNull(Reg))
+    MRI.setRegClass(NewReg, RC);
+  SpirvTy = SpirvTy ? SpirvTy : GR->getOrCreateSPIRVType(Ty, MIB);
+  GR->assignSPIRVTypeToVReg(SpirvTy, Reg, MIB.getMF());
+  // This is to make it convenient for Legalizer to get the SPIRVType
+  // when processing the actual MI (i.e. not pseudo one).
+  GR->assignSPIRVTypeToVReg(SpirvTy, NewReg, MIB.getMF());
+  MIB.buildInstr(SPIRV::ASSIGN_TYPE)
+      .addDef(Reg)
+      .addUse(NewReg)
+      .addUse(GR->getSPIRVTypeID(SpirvTy));
+  Def->getOperand(0).setReg(NewReg);
+  MRI.setRegClass(Reg, &SPIRV::ANYIDRegClass);
+  return NewReg;
+}
+
+static void generateAssignInstrs(MachineFunction &MF, SPIRVGlobalRegistry *GR,
+                                 MachineIRBuilder MIB) {
+  MachineRegisterInfo &MRI = MF.getRegInfo();
+  SmallVector<MachineInstr *, 10> ToErase;
+
+  for (MachineBasicBlock *MBB : post_order(&MF)) {
+    if (MBB->empty())
+      continue;
+
+    bool ReachedBegin = false;
+    for (auto MII = std::prev(MBB->end()), Begin = MBB->begin();
+         !ReachedBegin;) {
+      MachineInstr &MI = *MII;
+
+      if (isSpvIntrinsic(MI, Intrinsic::spv_assign_type)) {
+        Register Reg = MI.getOperand(1).getReg();
+        Type *Ty = getMDOperandAsType(MI.getOperand(2).getMetadata(), 0);
+        MachineInstr *Def = MRI.getVRegDef(Reg);
+        assert(Def && "Expecting an instruction that defines the register");
+        // G_GLOBAL_VALUE already has type info.
+        if (Def->getOpcode() != TargetOpcode::G_GLOBAL_VALUE)
+          insertAssignInstr(Reg, Ty, nullptr, GR, MIB, MF.getRegInfo());
+        ToErase.push_back(&MI);
+      } else if (MI.getOpcode() == TargetOpcode::G_CONSTANT ||
+                 MI.getOpcode() == TargetOpcode::G_FCONSTANT ||
+                 MI.getOpcode() == TargetOpcode::G_BUILD_VECTOR) {
+        // %rc = G_CONSTANT ty Val
+        // ===>
+        // %cty = OpType* ty
+        // %rctmp = G_CONSTANT ty Val
+        // %rc = ASSIGN_TYPE %rctmp, %cty
+        Register Reg = MI.getOperand(0).getReg();
+        if (MRI.hasOneUse(Reg)) {
+          MachineInstr &UseMI = *MRI.use_instr_begin(Reg);
+          if (isSpvIntrinsic(UseMI, Intrinsic::spv_assign_type) ||
+              isSpvIntrinsic(UseMI, Intrinsic::spv_assign_name))
+            continue;
+        }
+        Type *Ty = nullptr;
+        if (MI.getOpcode() == TargetOpcode::G_CONSTANT)
+          Ty = MI.getOperand(1).getCImm()->getType();
+        else if (MI.getOpcode() == TargetOpcode::G_FCONSTANT)
+          Ty = MI.getOperand(1).getFPImm()->getType();
+        else {
+          assert(MI.getOpcode() == TargetOpcode::G_BUILD_VECTOR);
+          Type *ElemTy = nullptr;
+          MachineInstr *ElemMI = MRI.getVRegDef(MI.getOperand(1).getReg());
+          assert(ElemMI);
+
+          if (ElemMI->getOpcode() == TargetOpcode::G_CONSTANT)
+            ElemTy = ElemMI->getOperand(1).getCImm()->getType();
+          else if (ElemMI->getOpcode() == TargetOpcode::G_FCONSTANT)
+            ElemTy = ElemMI->getOperand(1).getFPImm()->getType();
+          else
+            llvm_unreachable("Unexpected opcode");
+          unsigned NumElts =
+              MI.getNumExplicitOperands() - MI.getNumExplicitDefs();
+          Ty = VectorType::get(ElemTy, NumElts, false);
+        }
+        insertAssignInstr(Reg, Ty, nullptr, GR, MIB, MRI);
+      } else if (MI.getOpcode() == TargetOpcode::G_TRUNC ||
+                 MI.getOpcode() == TargetOpcode::G_GLOBAL_VALUE ||
+                 MI.getOpcode() == TargetOpcode::COPY ||
+                 MI.getOpcode() == TargetOpcode::G_ADDRSPACE_CAST) {
+        propagateSPIRVType(&MI, GR, MRI, MIB);
+      }
+
+      if (MII == Begin)
+        ReachedBegin = true;
+      else
+        --MII;
+    }
+  }
+  for (MachineInstr *MI : ToErase)
+    MI->eraseFromParent();
+}
+
+static std::pair<Register, unsigned>
+createNewIdReg(Register ValReg, unsigned Opcode, MachineRegisterInfo &MRI,
+               const SPIRVGlobalRegistry &GR) {
+  LLT NewT = LLT::scalar(32);
+  SPIRVType *SpvType = GR.getSPIRVTypeForVReg(ValReg);
+  assert(SpvType && "VReg is expected to have SPIRV type");
+  bool IsFloat = SpvType->getOpcode() == SPIRV::OpTypeFloat;
+  bool IsVectorFloat =
+      SpvType->getOpcode() == SPIRV::OpTypeVector &&
+      GR.getSPIRVTypeForVReg(SpvType->getOperand(1).getReg())->getOpcode() ==
+          SPIRV::OpTypeFloat;
+  IsFloat |= IsVectorFloat;
+  auto GetIdOp = IsFloat ? SPIRV::GET_fID : SPIRV::GET_ID;
+  auto DstClass = IsFloat ? &SPIRV::fIDRegClass : &SPIRV::IDRegClass;
+  if (MRI.getType(ValReg).isPointer()) {
+    NewT = LLT::pointer(0, 32);
+    GetIdOp = SPIRV::GET_pID;
+    DstClass = &SPIRV::pIDRegClass;
+  } else if (MRI.getType(ValReg).isVector()) {
+    NewT = LLT::fixed_vector(2, NewT);
+    GetIdOp = IsFloat ? SPIRV::GET_vfID : SPIRV::GET_vID;
+    DstClass = IsFloat ? &SPIRV::vfIDRegClass : &SPIRV::vIDRegClass;
+  }
+  Register IdReg = MRI.createGenericVirtualRegister(NewT);
+  MRI.setRegClass(IdReg, DstClass);
+  return {IdReg, GetIdOp};
+}
+
+static void processInstr(MachineInstr &MI, MachineIRBuilder &MIB,
+                         MachineRegisterInfo &MRI, SPIRVGlobalRegistry *GR) {
+  unsigned Opc = MI.getOpcode();
+  assert(MI.getNumDefs() > 0 && MRI.hasOneUse(MI.getOperand(0).getReg()));
+  MachineInstr &AssignTypeInst =
+      *(MRI.use_instr_begin(MI.getOperand(0).getReg()));
+  auto NewReg = createNewIdReg(MI.getOperand(0).getReg(), Opc, MRI, *GR).first;
+  AssignTypeInst.getOperand(1).setReg(NewReg);
+  MI.getOperand(0).setReg(NewReg);
+  MIB.setInsertPt(*MI.getParent(),
+                  (MI.getNextNode() ? MI.getNextNode()->getIterator()
+                                    : MI.getParent()->end()));
+  for (auto &Op : MI.operands()) {
+    if (!Op.isReg() || Op.isDef())
+      continue;
+    auto IdOpInfo = createNewIdReg(Op.getReg(), Opc, MRI, *GR);
+    MIB.buildInstr(IdOpInfo.second).addDef(IdOpInfo.first).addUse(Op.getReg());
+    Op.setReg(IdOpInfo.first);
+  }
+}
+
+// Defined in SPIRVLegalizerInfo.cpp.
+extern bool isTypeFoldingSupported(unsigned Opcode);
+
+static void processInstrsWithTypeFolding(MachineFunction &MF,
+                                         SPIRVGlobalRegistry *GR,
+                                         MachineIRBuilder MIB) {
+  MachineRegisterInfo &MRI = MF.getRegInfo();
+  for (MachineBasicBlock &MBB : MF) {
+    for (MachineInstr &MI : MBB) {
+      if (isTypeFoldingSupported(MI.getOpcode()))
+        processInstr(MI, MIB, MRI, GR);
+    }
+  }
+}
+
+static void processSwitches(MachineFunction &MF, SPIRVGlobalRegistry *GR,
+                            MachineIRBuilder MIB) {
+  DenseMap<Register, SmallDenseMap<uint64_t, MachineBasicBlock *>>
+      SwitchRegToMBB;
+  DenseMap<Register, MachineBasicBlock *> DefaultMBBs;
+  DenseSet<Register> SwitchRegs;
+  MachineRegisterInfo &MRI = MF.getRegInfo();
+  // Before IRTranslator pass, spv_switch calls are inserted before each
+  // switch instruction. IRTranslator lowers switches to ICMP+CBr+Br triples.
+  // A switch with two cases may be translated to this MIR sequesnce:
+  //   intrinsic(@llvm.spv.switch), %CmpReg, %Const0, %Const1
+  //   %Dst0 = G_ICMP intpred(eq), %CmpReg, %Const0
+  //   G_BRCOND %Dst0, %bb.2
+  //   G_BR %bb.5
+  // bb.5.entry:
+  //   %Dst1 = G_ICMP intpred(eq), %CmpReg, %Const1
+  //   G_BRCOND %Dst1, %bb.3
+  //   G_BR %bb.4
+  // bb.2.sw.bb:
+  //   ...
+  // bb.3.sw.bb1:
+  //   ...
+  // bb.4.sw.epilog:
+  //   ...
+  // Walk MIs and collect information about destination MBBs to update
+  // spv_switch call. We assume that all spv_switch precede corresponding ICMPs.
+  for (MachineBasicBlock &MBB : MF) {
+    for (MachineInstr &MI : MBB) {
+      if (isSpvIntrinsic(MI, Intrinsic::spv_switch)) {
+        assert(MI.getOperand(1).isReg());
+        Register Reg = MI.getOperand(1).getReg();
+        SwitchRegs.insert(Reg);
+        // Set the first successor as default MBB to support empty switches.
+        DefaultMBBs[Reg] = *MBB.succ_begin();
+      }
+      // Process only ICMPs that relate to spv_switches.
+      if (MI.getOpcode() == TargetOpcode::G_ICMP && MI.getOperand(2).isReg() &&
+          SwitchRegs.contains(MI.getOperand(2).getReg())) {
+        assert(MI.getOperand(0).isReg() && MI.getOperand(1).isPredicate() &&
+               MI.getOperand(3).isReg());
+        Register Dst = MI.getOperand(0).getReg();
+        // Set type info for destination register of switch's ICMP instruction.
+        if (GR->getSPIRVTypeForVReg(Dst) == nullptr) {
+          MIB.setInsertPt(*MI.getParent(), MI);
+          Type *LLVMTy = IntegerType::get(MF.getFunction().getContext(), 1);
+          SPIRVType *SpirvTy = GR->getOrCreateSPIRVType(LLVMTy, MIB);
+          MRI.setRegClass(Dst, &SPIRV::IDRegClass);
+          GR->assignSPIRVTypeToVReg(SpirvTy, Dst, MIB.getMF());
+        }
+        Register CmpReg = MI.getOperand(2).getReg();
+        MachineOperand &PredOp = MI.getOperand(1);
+        const auto CC = static_cast<CmpInst::Predicate>(PredOp.getPredicate());
+        assert(CC == CmpInst::ICMP_EQ && MRI.hasOneUse(Dst) &&
+               MRI.hasOneDef(CmpReg));
+        uint64_t Val = getIConstVal(MI.getOperand(3).getReg(), &MRI);
+        MachineInstr *CBr = MRI.use_begin(Dst)->getParent();
+        assert(CBr->getOpcode() == SPIRV::G_BRCOND &&
+               CBr->getOperand(1).isMBB());
+        SwitchRegToMBB[CmpReg][Val] = CBr->getOperand(1).getMBB();
+        // The next MI is always BR to either the next case or the default.
+        MachineInstr *NextMI = CBr->getNextNode();
+        assert(NextMI->getOpcode() == SPIRV::G_BR &&
+               NextMI->getOperand(0).isMBB());
+        MachineBasicBlock *NextMBB = NextMI->getOperand(0).getMBB();
+        assert(NextMBB != nullptr);
+        // The default MBB is not started by ICMP with switch's cmp register.
+        if (NextMBB->front().getOpcode() != SPIRV::G_ICMP ||
+            (NextMBB->front().getOperand(2).isReg() &&
+             NextMBB->front().getOperand(2).getReg() != CmpReg))
+          DefaultMBBs[CmpReg] = NextMBB;
+      }
+    }
+  }
+  // Modify spv_switch's operands by collected values. For the example above,
+  // the result will be like this:
+  //   intrinsic(@llvm.spv.switch), %CmpReg, %bb.4, i32 0, %bb.2, i32 1, %bb.3
+  // Note that ICMP+CBr+Br sequences are not removed, but ModuleAnalysis marks
+  // them as skipped and AsmPrinter does not output them.
+  for (MachineBasicBlock &MBB : MF) {
+    for (MachineInstr &MI : MBB) {
+      if (!isSpvIntrinsic(MI, Intrinsic::spv_switch))
+        continue;
+      assert(MI.getOperand(1).isReg());
+      Register Reg = MI.getOperand(1).getReg();
+      unsigned NumOp = MI.getNumExplicitOperands();
+      SmallVector<const ConstantInt *, 3> Vals;
+      SmallVector<MachineBasicBlock *, 3> MBBs;
+      for (unsigned i = 2; i < NumOp; i++) {
+        Register CReg = MI.getOperand(i).getReg();
+        uint64_t Val = getIConstVal(CReg, &MRI);
+        MachineInstr *ConstInstr = getDefInstrMaybeConstant(CReg, &MRI);
+        Vals.push_back(ConstInstr->getOperand(1).getCImm());
+        MBBs.push_back(SwitchRegToMBB[Reg][Val]);
+      }
+      for (unsigned i = MI.getNumExplicitOperands() - 1; i > 1; i--)
+        MI.removeOperand(i);
+      MI.addOperand(MachineOperand::CreateMBB(DefaultMBBs[Reg]));
+      for (unsigned i = 0; i < Vals.size(); i++) {
+        MI.addOperand(MachineOperand::CreateCImm(Vals[i]));
+        MI.addOperand(MachineOperand::CreateMBB(MBBs[i]));
+      }
+    }
+  }
+}
+
+bool SPIRVPreLegalizer::runOnMachineFunction(MachineFunction &MF) {
+  // Initialize the type registry.
+  const SPIRVSubtarget &ST = MF.getSubtarget<SPIRVSubtarget>();
+  SPIRVGlobalRegistry *GR = ST.getSPIRVGlobalRegistry();
+  GR->setCurrentFunc(MF);
+  MachineIRBuilder MIB(MF);
+  foldConstantsIntoIntrinsics(MF);
+  insertBitcasts(MF, GR, MIB);
+  generateAssignInstrs(MF, GR, MIB);
+  processInstrsWithTypeFolding(MF, GR, MIB);
+  processSwitches(MF, GR, MIB);
+
+  return true;
+}
+
+INITIALIZE_PASS(SPIRVPreLegalizer, DEBUG_TYPE, "SPIRV pre legalizer", false,
+                false)
+
+char SPIRVPreLegalizer::ID = 0;
+
+FunctionPass *llvm::createSPIRVPreLegalizerPass() {
+  return new SPIRVPreLegalizer();
+}

diff  --git a/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp b/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp
index e705de56c5bfd..f7c88a5c6d4a4 100644
--- a/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp
@@ -44,14 +44,12 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeSPIRVTarget() {
 }
 
 static std::string computeDataLayout(const Triple &TT) {
-  std::string DataLayout = "e-m:e";
-
   const auto Arch = TT.getArch();
   if (Arch == Triple::spirv32)
-    DataLayout += "-p:32:32";
-  else if (Arch == Triple::spirv64)
-    DataLayout += "-p:64:64";
-  return DataLayout;
+    return "e-p:32:32-i64:64-v16:16-v24:32-v32:32-v48:64-"
+           "v96:128-v192:256-v256:256-v512:512-v1024:1024";
+  return "e-i64:64-v16:16-v24:32-v32:32-v48:64-"
+         "v96:128-v192:256-v256:256-v512:512-v1024:1024";
 }
 
 static Reloc::Model getEffectiveRelocModel(Optional<Reloc::Model> RM) {
@@ -95,6 +93,7 @@ class SPIRVPassConfig : public TargetPassConfig {
   void addISelPrepare() override;
 
   bool addIRTranslator() override;
+  void addPreLegalizeMachineIR() override;
   bool addLegalizeMachineIR() override;
   bool addRegBankSelect() override;
   bool addGlobalInstructionSelect() override;
@@ -143,13 +142,20 @@ TargetPassConfig *SPIRVTargetMachine::createPassConfig(PassManagerBase &PM) {
 
 void SPIRVPassConfig::addIRPasses() { TargetPassConfig::addIRPasses(); }
 
-void SPIRVPassConfig::addISelPrepare() { TargetPassConfig::addISelPrepare(); }
+void SPIRVPassConfig::addISelPrepare() {
+  addPass(createSPIRVEmitIntrinsicsPass(&getTM<SPIRVTargetMachine>()));
+  TargetPassConfig::addISelPrepare();
+}
 
 bool SPIRVPassConfig::addIRTranslator() {
   addPass(new IRTranslator(getOptLevel()));
   return false;
 }
 
+void SPIRVPassConfig::addPreLegalizeMachineIR() {
+  addPass(createSPIRVPreLegalizerPass());
+}
+
 // Use a default legalizer.
 bool SPIRVPassConfig::addLegalizeMachineIR() {
   addPass(new Legalizer());

diff  --git a/llvm/lib/Target/SPIRV/SPIRVUtils.cpp b/llvm/lib/Target/SPIRV/SPIRVUtils.cpp
index aa1933fc4f02e..b92dc12735f86 100644
--- a/llvm/lib/Target/SPIRV/SPIRVUtils.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVUtils.cpp
@@ -18,6 +18,7 @@
 #include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
 #include "llvm/CodeGen/MachineInstr.h"
 #include "llvm/CodeGen/MachineInstrBuilder.h"
+#include "llvm/IR/IntrinsicsSPIRV.h"
 
 using namespace llvm;
 
@@ -180,3 +181,27 @@ SPIRV::MemorySemantics getMemSemanticsForStorageClass(SPIRV::StorageClass SC) {
     return SPIRV::MemorySemantics::None;
   }
 }
+
+MachineInstr *getDefInstrMaybeConstant(Register &ConstReg,
+                                       const MachineRegisterInfo *MRI) {
+  MachineInstr *ConstInstr = MRI->getVRegDef(ConstReg);
+  if (ConstInstr->getOpcode() == TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS &&
+      ConstInstr->getIntrinsicID() == Intrinsic::spv_track_constant) {
+    ConstReg = ConstInstr->getOperand(2).getReg();
+    ConstInstr = MRI->getVRegDef(ConstReg);
+  } else if (ConstInstr->getOpcode() == SPIRV::ASSIGN_TYPE) {
+    ConstReg = ConstInstr->getOperand(1).getReg();
+    ConstInstr = MRI->getVRegDef(ConstReg);
+  }
+  return ConstInstr;
+}
+
+uint64_t getIConstVal(Register ConstReg, const MachineRegisterInfo *MRI) {
+  const MachineInstr *MI = getDefInstrMaybeConstant(ConstReg, MRI);
+  assert(MI && MI->getOpcode() == TargetOpcode::G_CONSTANT);
+  return MI->getOperand(1).getCImm()->getValue().getZExtValue();
+}
+
+Type *getMDOperandAsType(const MDNode *N, unsigned I) {
+  return cast<ValueAsMetadata>(N->getOperand(I))->getType();
+}

diff  --git a/llvm/lib/Target/SPIRV/SPIRVUtils.h b/llvm/lib/Target/SPIRV/SPIRVUtils.h
index 3cb1db7795751..ffa82c9c1fe40 100644
--- a/llvm/lib/Target/SPIRV/SPIRVUtils.h
+++ b/llvm/lib/Target/SPIRV/SPIRVUtils.h
@@ -66,4 +66,18 @@ llvm::SPIRV::StorageClass addressSpaceToStorageClass(unsigned AddrSpace);
 
 llvm::SPIRV::MemorySemantics
 getMemSemanticsForStorageClass(llvm::SPIRV::StorageClass SC);
+
+// Find def instruction for the given ConstReg, walking through
+// spv_track_constant and ASSIGN_TYPE instructions. Updates ConstReg by def
+// of OpConstant instruction.
+llvm::MachineInstr *
+getDefInstrMaybeConstant(llvm::Register &ConstReg,
+                         const llvm::MachineRegisterInfo *MRI);
+
+// Get constant integer value of the given ConstReg.
+uint64_t getIConstVal(llvm::Register ConstReg,
+                      const llvm::MachineRegisterInfo *MRI);
+
+// Get type of i-th operand of the metadata node.
+llvm::Type *getMDOperandAsType(const llvm::MDNode *N, unsigned I);
 #endif // LLVM_LIB_TARGET_SPIRV_SPIRVUTILS_H

diff  --git a/llvm/test/CodeGen/SPIRV/branching/if-merging.ll b/llvm/test/CodeGen/SPIRV/branching/if-merging.ll
new file mode 100644
index 0000000000000..e112982ee0eaa
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/branching/if-merging.ll
@@ -0,0 +1,53 @@
+; RUN: llc -O0 %s -o - | FileCheck %s
+
+; NOTE: This does not check for structured control-flow operations.
+
+target triple = "spirv32-unknown-unknown"
+
+; CHECK-DAG: OpName [[FOO:%.+]] "foo"
+; CHECK-DAG: OpName [[BAR:%.+]] "bar"
+
+; CHECK-DAG: [[I32:%.+]] = OpTypeInt 32
+; CHECK-DAG: [[BOOL:%.+]] = OpTypeBool
+
+declare i32 @foo()
+declare i32 @bar()
+
+define i32 @test_if(i32 %a, i32 %b) {
+entry:
+  %cond = icmp eq i32 %a, %b
+  br i1 %cond, label %true_label, label %false_label
+
+true_label:
+  %v1 = call i32 @foo()
+  br label %merge_label
+
+false_label:
+  %v2 = call i32 @bar()
+  br label %merge_label
+
+merge_label:
+  %v = phi i32 [%v1, %true_label], [%v2, %false_label]
+  ret i32 %v
+}
+
+; CHECK: OpFunction
+; CHECK: [[A:%.+]] = OpFunctionParameter [[I32]]
+; CHECK: [[B:%.+]] = OpFunctionParameter [[I32]]
+
+; CHECK: [[ENTRY:%.+]] = OpLabel
+; CHECK: [[COND:%.+]] = OpIEqual [[BOOL]] [[A]] [[B]]
+; CHECK: OpBranchConditional [[COND]] [[TRUE_LABEL:%.+]] [[FALSE_LABEL:%.+]]
+
+; CHECK: [[TRUE_LABEL]] = OpLabel
+; CHECK: [[V1:%.+]] = OpFunctionCall [[I32]] [[FOO]]
+; CHECK: OpBranch [[MERGE_LABEL:%.+]]
+
+; CHECK: [[FALSE_LABEL]] = OpLabel
+; CHECK: [[V2:%.+]] = OpFunctionCall [[I32]] [[BAR]]
+; CHECK: OpBranch [[MERGE_LABEL]]
+
+; CHECK: [[MERGE_LABEL]] = OpLabel
+; CHECK-NEXT: [[V:%.+]] = OpPhi [[I32]] [[V1]] [[TRUE_LABEL]] [[V2]] [[FALSE_LABEL]]
+; CHECK: OpReturnValue [[V]]
+; CHECK-NEXT: OpFunctionEnd

diff  --git a/llvm/test/CodeGen/SPIRV/function/alloca-load-store.ll b/llvm/test/CodeGen/SPIRV/function/alloca-load-store.ll
new file mode 100644
index 0000000000000..3fb83016a4664
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/function/alloca-load-store.ll
@@ -0,0 +1,63 @@
+; RUN: llc -O0 %s -o - | FileCheck %s
+
+target triple = "spirv32-unknown-unknown"
+
+; CHECK-DAG: OpName [[BAR:%.+]] "bar"
+; CHECK-DAG: OpName [[FOO:%.+]] "foo"
+; CHECK-DAG: OpName [[GOO:%.+]] "goo"
+
+; CHECK: [[INT:%.+]] = OpTypeInt 32
+; CHECK-DAG: [[STACK_PTR:%.+]] = OpTypePointer Function [[INT]]
+; CHECK-DAG: [[GLOBAL_PTR:%.+]] = OpTypePointer CrossWorkgroup [[INT]]
+; CHECK-DAG: [[FN1:%.+]] = OpTypeFunction [[INT]] [[INT]]
+; CHECK-DAG: [[FN2:%.+]] = OpTypeFunction [[INT]] [[INT]] [[GLOBAL_PTR]]
+
+define i32 @bar(i32 %a) {
+  %p = alloca i32
+  store i32 %a, i32* %p
+  %b = load i32, i32* %p
+  ret i32 %b
+}
+
+; CHECK: [[BAR]] = OpFunction [[INT]] None [[FN1]]
+; CHECK: [[A:%.+]] = OpFunctionParameter [[INT]]
+; CHECK: OpLabel
+; CHECK: [[P:%.+]] = OpVariable [[STACK_PTR]] Function
+; CHECK: OpStore [[P]] [[A]]
+; CHECK: [[B:%.+]] = OpLoad [[INT]] [[P]]
+; CHECK: OpReturnValue [[B]]
+; CHECK: OpFunctionEnd
+
+
+define i32 @foo(i32 %a) {
+  %p = alloca i32
+  store volatile i32 %a, i32* %p
+  %b = load volatile i32, i32* %p
+  ret i32 %b
+}
+
+; CHECK: [[FOO]] = OpFunction [[INT]] None [[FN1]]
+; CHECK: [[A:%.+]] = OpFunctionParameter [[INT]]
+; CHECK: OpLabel
+; CHECK: [[P:%.+]] = OpVariable [[STACK_PTR]] Function
+; CHECK: OpStore [[P]] [[A]] Volatile
+; CHECK: [[B:%.+]] = OpLoad [[INT]] [[P]] Volatile
+; CHECK: OpReturnValue [[B]]
+; CHECK: OpFunctionEnd
+
+
+; Test load and store in global address space.
+define i32 @goo(i32 %a, i32 addrspace(1)* %p) {
+  store i32 %a, i32 addrspace(1)* %p
+  %b = load i32, i32 addrspace(1)* %p
+  ret i32 %b
+}
+
+; CHECK: [[GOO]] = OpFunction [[INT]] None [[FN2]]
+; CHECK: [[A:%.+]] = OpFunctionParameter [[INT]]
+; CHECK: [[P:%.+]] = OpFunctionParameter [[GLOBAL_PTR]]
+; CHECK: OpLabel
+; CHECK: OpStore [[P]] [[A]]
+; CHECK: [[B:%.+]] = OpLoad [[INT]] [[P]]
+; CHECK: OpReturnValue [[B]]
+; CHECK: OpFunctionEnd

diff  --git a/llvm/test/CodeGen/SPIRV/function/trivial-function-with-attributes.ll b/llvm/test/CodeGen/SPIRV/function/trivial-function-with-attributes.ll
index df61f2c6ebab6..66b273dea38ba 100644
--- a/llvm/test/CodeGen/SPIRV/function/trivial-function-with-attributes.ll
+++ b/llvm/test/CodeGen/SPIRV/function/trivial-function-with-attributes.ll
@@ -13,8 +13,6 @@ target triple = "spirv32-unknown-unknown"
 ; CHECK-DAG: OpName [[FN6:%.+]] "fn6"
 ; CHECK-DAG: OpName [[FN7:%.+]] "fn7"
 
-; CHECK-NOT: DAG-FENCE
-
 ; Types:
 ; CHECK: [[VOID:%.+]] = OpTypeVoid
 ; CHECK: [[FN:%.+]] = OpTypeFunction [[VOID]]

diff  --git a/llvm/test/CodeGen/SPIRV/instructions/atomic.ll b/llvm/test/CodeGen/SPIRV/instructions/atomic.ll
new file mode 100644
index 0000000000000..90603b4939e8c
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/instructions/atomic.ll
@@ -0,0 +1,127 @@
+; RUN: llc %s -o - | FileCheck %s
+
+target triple = "spirv32-unknown-unknown"
+
+; CHECK-DAG: OpName [[ADD:%.*]] "test_add"
+; CHECK-DAG: OpName [[SUB:%.*]] "test_sub"
+; CHECK-DAG: OpName [[MIN:%.*]] "test_min"
+; CHECK-DAG: OpName [[MAX:%.*]] "test_max"
+; CHECK-DAG: OpName [[UMIN:%.*]] "test_umin"
+; CHECK-DAG: OpName [[UMAX:%.*]] "test_umax"
+; CHECK-DAG: OpName [[AND:%.*]] "test_and"
+; CHECK-DAG: OpName [[OR:%.*]] "test_or"
+; CHECK-DAG: OpName [[XOR:%.*]] "test_xor"
+
+; CHECK-DAG: [[I32Ty:%.*]] = OpTypeInt 32 0
+; Device scope is encoded with constant 1
+; CHECK-DAG: [[SCOPE:%.*]] = OpConstant [[I32Ty]] 1
+; "monotonic" maps to the relaxed memory semantics, encoded with constant 0
+; CHECK-DAG: [[RELAXED:%.*]] = OpConstantNull [[I32Ty]]
+
+; CHECK: [[ADD]] = OpFunction [[I32Ty]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpAtomicIAdd [[I32Ty]] [[A]] [[SCOPE]] [[RELAXED]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i32 @test_add(i32* %ptr, i32 %val) {
+  %r = atomicrmw add i32* %ptr, i32 %val monotonic
+  ret i32 %r
+}
+
+; CHECK: [[SUB]] = OpFunction [[I32Ty]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpAtomicISub [[I32Ty]] [[A]] [[SCOPE]] [[RELAXED]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i32 @test_sub(i32* %ptr, i32 %val) {
+  %r = atomicrmw sub i32* %ptr, i32 %val monotonic
+  ret i32 %r
+}
+
+; CHECK: [[MIN]] = OpFunction [[I32Ty]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpAtomicSMin [[I32Ty]] [[A]] [[SCOPE]] [[RELAXED]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i32 @test_min(i32* %ptr, i32 %val) {
+  %r = atomicrmw min i32* %ptr, i32 %val monotonic
+  ret i32 %r
+}
+
+; CHECK: [[MAX]] = OpFunction [[I32Ty]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpAtomicSMax [[I32Ty]] [[A]] [[SCOPE]] [[RELAXED]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i32 @test_max(i32* %ptr, i32 %val) {
+  %r = atomicrmw max i32* %ptr, i32 %val monotonic
+  ret i32 %r
+}
+
+; CHECK: [[UMIN]] = OpFunction [[I32Ty]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpAtomicUMin [[I32Ty]] [[A]] [[SCOPE]] [[RELAXED]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i32 @test_umin(i32* %ptr, i32 %val) {
+  %r = atomicrmw umin i32* %ptr, i32 %val monotonic
+  ret i32 %r
+}
+
+; CHECK: [[UMAX]] = OpFunction [[I32Ty]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpAtomicUMax [[I32Ty]] [[A]] [[SCOPE]] [[RELAXED]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i32 @test_umax(i32* %ptr, i32 %val) {
+  %r = atomicrmw umax i32* %ptr, i32 %val monotonic
+  ret i32 %r
+}
+
+; CHECK: [[AND]] = OpFunction [[I32Ty]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpAtomicAnd [[I32Ty]] [[A]] [[SCOPE]] [[RELAXED]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i32 @test_and(i32* %ptr, i32 %val) {
+  %r = atomicrmw and i32* %ptr, i32 %val monotonic
+  ret i32 %r
+}
+
+; CHECK: [[OR]] = OpFunction [[I32Ty]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpAtomicOr [[I32Ty]] [[A]] [[SCOPE]] [[RELAXED]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i32 @test_or(i32* %ptr, i32 %val) {
+  %r = atomicrmw or i32* %ptr, i32 %val monotonic
+  ret i32 %r
+}
+
+; CHECK: [[XOR]] = OpFunction [[I32Ty]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpAtomicXor [[I32Ty]] [[A]] [[SCOPE]] [[RELAXED]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i32 @test_xor(i32* %ptr, i32 %val) {
+  %r = atomicrmw xor i32* %ptr, i32 %val monotonic
+  ret i32 %r
+}

diff  --git a/llvm/test/CodeGen/SPIRV/instructions/atomic_acqrel.ll b/llvm/test/CodeGen/SPIRV/instructions/atomic_acqrel.ll
new file mode 100644
index 0000000000000..e7581172843b0
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/instructions/atomic_acqrel.ll
@@ -0,0 +1,127 @@
+; RUN: llc %s -o - | FileCheck %s
+
+target triple = "spirv32-unknown-unknown"
+
+; CHECK-DAG: OpName [[ADD:%.*]] "test_add"
+; CHECK-DAG: OpName [[SUB:%.*]] "test_sub"
+; CHECK-DAG: OpName [[MIN:%.*]] "test_min"
+; CHECK-DAG: OpName [[MAX:%.*]] "test_max"
+; CHECK-DAG: OpName [[UMIN:%.*]] "test_umin"
+; CHECK-DAG: OpName [[UMAX:%.*]] "test_umax"
+; CHECK-DAG: OpName [[AND:%.*]] "test_and"
+; CHECK-DAG: OpName [[OR:%.*]] "test_or"
+; CHECK-DAG: OpName [[XOR:%.*]] "test_xor"
+
+; CHECK-DAG: [[I32Ty:%.*]] = OpTypeInt 32 0
+; Device scope is encoded with constant 1
+; CHECK-DAG: [[SCOPE:%.*]] = OpConstant [[I32Ty]] 1
+; "acq_rel" maps to the constant 8
+; CHECK-DAG: [[ACQREL:%.*]] = OpConstant [[I32Ty]] 8
+
+; CHECK: [[ADD]] = OpFunction [[I32Ty]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpAtomicIAdd [[I32Ty]] [[A]] [[SCOPE]] [[ACQREL]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i32 @test_add(i32* %ptr, i32 %val) {
+  %r = atomicrmw add i32* %ptr, i32 %val acq_rel
+  ret i32 %r
+}
+
+; CHECK: [[SUB]] = OpFunction [[I32Ty]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpAtomicISub [[I32Ty]] [[A]] [[SCOPE]] [[ACQREL]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i32 @test_sub(i32* %ptr, i32 %val) {
+  %r = atomicrmw sub i32* %ptr, i32 %val acq_rel
+  ret i32 %r
+}
+
+; CHECK: [[MIN]] = OpFunction [[I32Ty]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpAtomicSMin [[I32Ty]] [[A]] [[SCOPE]] [[ACQREL]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i32 @test_min(i32* %ptr, i32 %val) {
+  %r = atomicrmw min i32* %ptr, i32 %val acq_rel
+  ret i32 %r
+}
+
+; CHECK: [[MAX]] = OpFunction [[I32Ty]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpAtomicSMax [[I32Ty]] [[A]] [[SCOPE]] [[ACQREL]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i32 @test_max(i32* %ptr, i32 %val) {
+  %r = atomicrmw max i32* %ptr, i32 %val acq_rel
+  ret i32 %r
+}
+
+; CHECK: [[UMIN]] = OpFunction [[I32Ty]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpAtomicUMin [[I32Ty]] [[A]] [[SCOPE]] [[ACQREL]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i32 @test_umin(i32* %ptr, i32 %val) {
+  %r = atomicrmw umin i32* %ptr, i32 %val acq_rel
+  ret i32 %r
+}
+
+; CHECK: [[UMAX]] = OpFunction [[I32Ty]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpAtomicUMax [[I32Ty]] [[A]] [[SCOPE]] [[ACQREL]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i32 @test_umax(i32* %ptr, i32 %val) {
+  %r = atomicrmw umax i32* %ptr, i32 %val acq_rel
+  ret i32 %r
+}
+
+; CHECK: [[AND]] = OpFunction [[I32Ty]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpAtomicAnd [[I32Ty]] [[A]] [[SCOPE]] [[ACQREL]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i32 @test_and(i32* %ptr, i32 %val) {
+  %r = atomicrmw and i32* %ptr, i32 %val acq_rel
+  ret i32 %r
+}
+
+; CHECK: [[OR]] = OpFunction [[I32Ty]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpAtomicOr [[I32Ty]] [[A]] [[SCOPE]] [[ACQREL]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i32 @test_or(i32* %ptr, i32 %val) {
+  %r = atomicrmw or i32* %ptr, i32 %val acq_rel
+  ret i32 %r
+}
+
+; CHECK: [[XOR]] = OpFunction [[I32Ty]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpAtomicXor [[I32Ty]] [[A]] [[SCOPE]] [[ACQREL]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i32 @test_xor(i32* %ptr, i32 %val) {
+  %r = atomicrmw xor i32* %ptr, i32 %val acq_rel
+  ret i32 %r
+}

diff  --git a/llvm/test/CodeGen/SPIRV/instructions/atomic_seq.ll b/llvm/test/CodeGen/SPIRV/instructions/atomic_seq.ll
new file mode 100644
index 0000000000000..7ad22d8d53c15
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/instructions/atomic_seq.ll
@@ -0,0 +1,127 @@
+; RUN: llc %s -o - | FileCheck %s
+
+target triple = "spirv32-unknown-unknown"
+
+; CHECK-DAG: OpName [[ADD:%.*]] "test_add"
+; CHECK-DAG: OpName [[SUB:%.*]] "test_sub"
+; CHECK-DAG: OpName [[MIN:%.*]] "test_min"
+; CHECK-DAG: OpName [[MAX:%.*]] "test_max"
+; CHECK-DAG: OpName [[UMIN:%.*]] "test_umin"
+; CHECK-DAG: OpName [[UMAX:%.*]] "test_umax"
+; CHECK-DAG: OpName [[AND:%.*]] "test_and"
+; CHECK-DAG: OpName [[OR:%.*]] "test_or"
+; CHECK-DAG: OpName [[XOR:%.*]] "test_xor"
+
+; CHECK-DAG: [[I32Ty:%.*]] = OpTypeInt 32 0
+; Device scope is encoded with constant 1
+; CHECK-DAG: [[SCOPE:%.*]] = OpConstant [[I32Ty]] 1
+; "sequentially consistent" maps to constant 16
+; CHECK-DAG: [[SEQ:%.*]] = OpConstant [[I32Ty]] 16
+
+; CHECK: [[ADD]] = OpFunction [[I32Ty]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpAtomicIAdd [[I32Ty]] [[A]] [[SCOPE]] [[SEQ]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i32 @test_add(i32* %ptr, i32 %val) {
+  %r = atomicrmw add i32* %ptr, i32 %val seq_cst
+  ret i32 %r
+}
+
+; CHECK: [[SUB]] = OpFunction [[I32Ty]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpAtomicISub [[I32Ty]] [[A]] [[SCOPE]] [[SEQ]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i32 @test_sub(i32* %ptr, i32 %val) {
+  %r = atomicrmw sub i32* %ptr, i32 %val seq_cst
+  ret i32 %r
+}
+
+; CHECK: [[MIN]] = OpFunction [[I32Ty]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpAtomicSMin [[I32Ty]] [[A]] [[SCOPE]] [[SEQ]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i32 @test_min(i32* %ptr, i32 %val) {
+  %r = atomicrmw min i32* %ptr, i32 %val seq_cst
+  ret i32 %r
+}
+
+; CHECK: [[MAX]] = OpFunction [[I32Ty]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpAtomicSMax [[I32Ty]] [[A]] [[SCOPE]] [[SEQ]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i32 @test_max(i32* %ptr, i32 %val) {
+  %r = atomicrmw max i32* %ptr, i32 %val seq_cst
+  ret i32 %r
+}
+
+; CHECK: [[UMIN]] = OpFunction [[I32Ty]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpAtomicUMin [[I32Ty]] [[A]] [[SCOPE]] [[SEQ]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i32 @test_umin(i32* %ptr, i32 %val) {
+  %r = atomicrmw umin i32* %ptr, i32 %val seq_cst
+  ret i32 %r
+}
+
+; CHECK: [[UMAX]] = OpFunction [[I32Ty]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpAtomicUMax [[I32Ty]] [[A]] [[SCOPE]] [[SEQ]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i32 @test_umax(i32* %ptr, i32 %val) {
+  %r = atomicrmw umax i32* %ptr, i32 %val seq_cst
+  ret i32 %r
+}
+
+; CHECK: [[AND]] = OpFunction [[I32Ty]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpAtomicAnd [[I32Ty]] [[A]] [[SCOPE]] [[SEQ]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i32 @test_and(i32* %ptr, i32 %val) {
+  %r = atomicrmw and i32* %ptr, i32 %val seq_cst
+  ret i32 %r
+}
+
+; CHECK: [[OR]] = OpFunction [[I32Ty]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpAtomicOr [[I32Ty]] [[A]] [[SCOPE]] [[SEQ]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i32 @test_or(i32* %ptr, i32 %val) {
+  %r = atomicrmw or i32* %ptr, i32 %val seq_cst
+  ret i32 %r
+}
+
+; CHECK: [[XOR]] = OpFunction [[I32Ty]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpAtomicXor [[I32Ty]] [[A]] [[SCOPE]] [[SEQ]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i32 @test_xor(i32* %ptr, i32 %val) {
+  %r = atomicrmw xor i32* %ptr, i32 %val seq_cst
+  ret i32 %r
+}

diff  --git a/llvm/test/CodeGen/SPIRV/instructions/fcmp.ll b/llvm/test/CodeGen/SPIRV/instructions/fcmp.ll
new file mode 100644
index 0000000000000..73ec5ca9c5ed2
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/instructions/fcmp.ll
@@ -0,0 +1,369 @@
+; RUN: llc %s -o - | FileCheck %s
+
+target triple = "spirv32-unknown-unknown"
+
+; CHECK-DAG: OpName [[UEQ:%.*]] "test_ueq"
+; CHECK-DAG: OpName [[OEQ:%.*]] "test_oeq"
+; CHECK-DAG: OpName [[UNE:%.*]] "test_une"
+; CHECK-DAG: OpName [[ONE:%.*]] "test_one"
+; CHECK-DAG: OpName [[ULT:%.*]] "test_ult"
+; CHECK-DAG: OpName [[OLT:%.*]] "test_olt"
+; CHECK-DAG: OpName [[ULE:%.*]] "test_ule"
+; CHECK-DAG: OpName [[OLE:%.*]] "test_ole"
+; CHECK-DAG: OpName [[UGT:%.*]] "test_ugt"
+; CHECK-DAG: OpName [[OGT:%.*]] "test_ogt"
+; CHECK-DAG: OpName [[UGE:%.*]] "test_uge"
+; CHECK-DAG: OpName [[OGE:%.*]] "test_oge"
+; CHECK-DAG: OpName [[UNO:%.*]] "test_uno"
+; CHECK-DAG: OpName [[ORD:%.*]] "test_ord"
+
+; CHECK-DAG: OpName [[v3UEQ:%.*]] "test_v3_ueq"
+; CHECK-DAG: OpName [[v3OEQ:%.*]] "test_v3_oeq"
+; CHECK-DAG: OpName [[v3UNE:%.*]] "test_v3_une"
+; CHECK-DAG: OpName [[v3ONE:%.*]] "test_v3_one"
+; CHECK-DAG: OpName [[v3ULT:%.*]] "test_v3_ult"
+; CHECK-DAG: OpName [[v3OLT:%.*]] "test_v3_olt"
+; CHECK-DAG: OpName [[v3ULE:%.*]] "test_v3_ule"
+; CHECK-DAG: OpName [[v3OLE:%.*]] "test_v3_ole"
+; CHECK-DAG: OpName [[v3UGT:%.*]] "test_v3_ugt"
+; CHECK-DAG: OpName [[v3OGT:%.*]] "test_v3_ogt"
+; CHECK-DAG: OpName [[v3UGE:%.*]] "test_v3_uge"
+; CHECK-DAG: OpName [[v3OGE:%.*]] "test_v3_oge"
+; CHECK-DAG: OpName [[v3UNO:%.*]] "test_v3_uno"
+; CHECK-DAG: OpName [[v3ORD:%.*]] "test_v3_ord"
+
+; CHECK: [[UEQ]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpFUnordEqual {{%.+}} [[A]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i1 @test_ueq(float %a, float %b) {
+  %r = fcmp ueq float %a, %b
+  ret i1 %r
+}
+
+; CHECK: [[OEQ]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpFOrdEqual {{%.+}} [[A]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i1 @test_oeq(float %a, float %b) {
+  %r = fcmp oeq float %a, %b
+  ret i1 %r
+}
+
+; CHECK: [[UNE]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpFUnordNotEqual {{%.+}} [[A]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i1 @test_une(float %a, float %b) {
+  %r = fcmp une float %a, %b
+  ret i1 %r
+}
+
+; CHECK: [[ONE]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpFOrdNotEqual {{%.+}} [[A]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i1 @test_one(float %a, float %b) {
+  %r = fcmp one float %a, %b
+  ret i1 %r
+}
+
+; CHECK: [[ULT]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpFUnordLessThan {{%.+}} [[A]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i1 @test_ult(float %a, float %b) {
+  %r = fcmp ult float %a, %b
+  ret i1 %r
+}
+
+; CHECK: [[OLT]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpFOrdLessThan {{%.+}} [[A]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i1 @test_olt(float %a, float %b) {
+  %r = fcmp olt float %a, %b
+  ret i1 %r
+}
+
+; CHECK: [[ULE]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpFUnordLessThanEqual {{%.+}} [[A]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i1 @test_ule(float %a, float %b) {
+  %r = fcmp ule float %a, %b
+  ret i1 %r
+}
+
+; CHECK: [[OLE]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpFOrdLessThanEqual {{%.+}} [[A]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i1 @test_ole(float %a, float %b) {
+  %r = fcmp ole float %a, %b
+  ret i1 %r
+}
+
+; CHECK: [[UGT]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpFUnordGreaterThan {{%.+}} [[A]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i1 @test_ugt(float %a, float %b) {
+  %r = fcmp ugt float %a, %b
+  ret i1 %r
+}
+
+; CHECK: [[OGT]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpFOrdGreaterThan {{%.+}} [[A]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i1 @test_ogt(float %a, float %b) {
+  %r = fcmp ogt float %a, %b
+  ret i1 %r
+}
+
+; CHECK: [[UGE]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpFUnordGreaterThanEqual {{%.+}} [[A]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i1 @test_uge(float %a, float %b) {
+  %r = fcmp uge float %a, %b
+  ret i1 %r
+}
+
+; CHECK: [[OGE]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpFOrdGreaterThanEqual {{%.+}} [[A]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i1 @test_oge(float %a, float %b) {
+  %r = fcmp oge float %a, %b
+  ret i1 %r
+}
+
+; CHECK: [[ORD]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpOrdered {{%.+}} [[A]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i1 @test_ord(float %a, float %b) {
+  %r = fcmp ord float %a, %b
+  ret i1 %r
+}
+
+; CHECK: [[UNO]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpUnordered {{%.+}} [[A]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i1 @test_uno(float %a, float %b) {
+  %r = fcmp uno float %a, %b
+  ret i1 %r
+}
+
+; CHECK: [[v3UEQ]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpFUnordEqual {{%.+}} [[A]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define <3 x i1> @test_v3_ueq(<3 x float> %a, <3 x float> %b) {
+  %r = fcmp ueq <3 x float> %a, %b
+  ret <3 x i1> %r
+}
+
+; CHECK: [[v3OEQ]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpFOrdEqual {{%.+}} [[A]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define <3 x i1> @test_v3_oeq(<3 x float> %a, <3 x float> %b) {
+  %r = fcmp oeq <3 x float> %a, %b
+  ret <3 x i1> %r
+}
+
+; CHECK: [[v3UNE]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpFUnordNotEqual {{%.+}} [[A]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define <3 x i1> @test_v3_une(<3 x float> %a, <3 x float> %b) {
+  %r = fcmp une <3 x float> %a, %b
+  ret <3 x i1> %r
+}
+
+; CHECK: [[v3ONE]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpFOrdNotEqual {{%.+}} [[A]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define <3 x i1> @test_v3_one(<3 x float> %a, <3 x float> %b) {
+  %r = fcmp one <3 x float> %a, %b
+  ret <3 x i1> %r
+}
+
+; CHECK: [[v3ULT]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpFUnordLessThan {{%.+}} [[A]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define <3 x i1> @test_v3_ult(<3 x float> %a, <3 x float> %b) {
+  %r = fcmp ult <3 x float> %a, %b
+  ret <3 x i1> %r
+}
+
+; CHECK: [[v3OLT]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpFOrdLessThan {{%.+}} [[A]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define <3 x i1> @test_v3_olt(<3 x float> %a, <3 x float> %b) {
+  %r = fcmp olt <3 x float> %a, %b
+  ret <3 x i1> %r
+}
+
+; CHECK: [[v3ULE]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpFUnordLessThanEqual {{%.+}} [[A]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define <3 x i1> @test_v3_ule(<3 x float> %a, <3 x float> %b) {
+  %r = fcmp ule <3 x float> %a, %b
+  ret <3 x i1> %r
+}
+
+; CHECK: [[v3OLE]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpFOrdLessThanEqual {{%.+}} [[A]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define <3 x i1> @test_v3_ole(<3 x float> %a, <3 x float> %b) {
+  %r = fcmp ole <3 x float> %a, %b
+  ret <3 x i1> %r
+}
+
+; CHECK: [[v3UGT]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpFUnordGreaterThan {{%.+}} [[A]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define <3 x i1> @test_v3_ugt(<3 x float> %a, <3 x float> %b) {
+  %r = fcmp ugt <3 x float> %a, %b
+  ret <3 x i1> %r
+}
+
+; CHECK: [[v3OGT]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpFOrdGreaterThan {{%.+}} [[A]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define <3 x i1> @test_v3_ogt(<3 x float> %a, <3 x float> %b) {
+  %r = fcmp ogt <3 x float> %a, %b
+  ret <3 x i1> %r
+}
+
+; CHECK: [[v3UGE]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpFUnordGreaterThanEqual {{%.+}} [[A]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define <3 x i1> @test_v3_uge(<3 x float> %a, <3 x float> %b) {
+  %r = fcmp uge <3 x float> %a, %b
+  ret <3 x i1> %r
+}
+
+; CHECK: [[v3OGE]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpFOrdGreaterThanEqual {{%.+}} [[A]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define <3 x i1> @test_v3_oge(<3 x float> %a, <3 x float> %b) {
+  %r = fcmp oge <3 x float> %a, %b
+  ret <3 x i1> %r
+}
+
+; CHECK: [[v3ORD]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpOrdered {{%.+}} [[A]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define <3 x i1> @test_v3_ord(<3 x float> %a, <3 x float> %b) {
+  %r = fcmp ord <3 x float> %a, %b
+  ret <3 x i1> %r
+}
+
+; CHECK: [[v3UNO]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpUnordered {{%.+}} [[A]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define <3 x i1> @test_v3_uno(<3 x float> %a, <3 x float> %b) {
+  %r = fcmp uno <3 x float> %a, %b
+  ret <3 x i1> %r
+}

diff  --git a/llvm/test/CodeGen/SPIRV/instructions/float-casts.ll b/llvm/test/CodeGen/SPIRV/instructions/float-casts.ll
new file mode 100644
index 0000000000000..bdf8284b988ae
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/instructions/float-casts.ll
@@ -0,0 +1,213 @@
+; RUN: llc %s -o - | FileCheck %s
+
+target triple = "spirv32-unknown-unknown"
+
+; CHECK-DAG: OpName [[TRUNC32_16:%.*]] "f32tof16"
+; CHECK-DAG: OpName [[EXT16_32:%.*]] "f16tof32"
+
+; CHECK-DAG: OpName [[TRUNC32_16v3:%.*]] "f32tof16v3"
+; CHECK-DAG: OpName [[EXT16_32v3:%.*]] "f16tof32v3"
+
+; CHECK-DAG: OpName [[F32toS32:%.*]] "f32tos32"
+; CHECK-DAG: OpName [[F32toS16:%.*]] "f32tos16"
+; CHECK-DAG: OpName [[F32toS8:%.*]] "f32tos8"
+; CHECK-DAG: OpName [[F16toS32:%.*]] "f16tos32"
+; CHECK-DAG: OpName [[F16toS16:%.*]] "f16tos16"
+; CHECK-DAG: OpName [[F16toS8:%.*]] "f16tos8"
+
+; CHECK-DAG: OpName [[F32toU32v2:%.*]] "f32tou32v2"
+; CHECK-DAG: OpName [[F32toU16v2:%.*]] "f32tou16v2"
+; CHECK-DAG: OpName [[F32toU8v2:%.*]] "f32tou8v2"
+; CHECK-DAG: OpName [[F16toU32v2:%.*]] "f16tou32v2"
+; CHECK-DAG: OpName [[F16toU16v2:%.*]] "f16tou16v2"
+; CHECK-DAG: OpName [[F16toU8v2:%.*]] "f16tou8v2"
+
+; CHECK-DAG: [[F32:%.*]] = OpTypeFloat 32
+; CHECK-DAG: [[F16:%.*]] = OpTypeFloat 16
+; CHECK-DAG: [[F32v2:%.*]] = OpTypeVector [[F32]] 2
+; CHECK-DAG: [[F16v2:%.*]] = OpTypeVector [[F16]] 2
+; CHECK-DAG: [[F32v3:%.*]] = OpTypeVector [[F32]] 3
+; CHECK-DAG: [[F16v3:%.*]] = OpTypeVector [[F16]] 3
+; CHECK-DAG: [[U32:%.*]] = OpTypeInt 32 0
+; CHECK-DAG: [[U16:%.*]] = OpTypeInt 16 0
+; CHECK-DAG: [[U8:%.*]] = OpTypeInt 8 0
+; CHECK-DAG: [[U32v2:%.*]] = OpTypeVector [[U32]] 2
+; CHECK-DAG: [[U16v2:%.*]] = OpTypeVector [[U16]] 2
+; CHECK-DAG: [[U8v2:%.*]] = OpTypeVector [[U8]] 2
+
+
+; CHECK: [[TRUNC32_16]] = OpFunction [[F16]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[F32]]
+; CHECK: OpLabel
+; CHECK: [[R:%.*]] = OpFConvert [[F16]] [[A]]
+; CHECK: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define half @f32tof16(float %a) {
+    %r = fptrunc float %a to half
+    ret half %r
+}
+
+; CHECK: [[EXT16_32]] = OpFunction [[F32]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[F16]]
+; CHECK: OpLabel
+; CHECK: [[R:%.*]] = OpFConvert [[F32]] [[A]]
+; CHECK: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define float @f16tof32(half %a) {
+  %r = fpext half %a to float
+  ret float %r
+}
+
+; CHECK: [[TRUNC32_16v3]] = OpFunction [[F16v3]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[F32v3]]
+; CHECK: OpLabel
+; CHECK: [[R:%.*]] = OpFConvert [[F16v3]] [[A]]
+; CHECK: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define <3 x half> @f32tof16v3(<3 x float> %a) {
+    %r = fptrunc <3 x float> %a to <3 x half>
+    ret <3 x half> %r
+}
+
+; CHECK: [[EXT16_32v3]] = OpFunction [[F32v3]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[F16v3]]
+; CHECK: OpLabel
+; CHECK: [[R:%.*]] = OpFConvert [[F32v3]] [[A]]
+; CHECK: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define <3 x float> @f16tof32v3(<3 x half> %a) {
+  %r = fpext <3 x half> %a to <3 x float>
+  ret <3 x float> %r
+}
+
+; CHECK: [[F32toS32]] = OpFunction [[U32]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[F32]]
+; CHECK: OpLabel
+; CHECK: [[R:%.*]] = OpConvertFToS [[U32]] [[A]]
+; CHECK: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i32 @f32tos32(float %a) {
+  %r = fptosi float %a to i32
+  ret i32 %r
+}
+
+; CHECK: [[F32toS16]] = OpFunction [[U16]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[F32]]
+; CHECK: OpLabel
+; CHECK: [[R:%.*]] = OpConvertFToS [[U16]] [[A]]
+; CHECK: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i16 @f32tos16(float %a) {
+  %r = fptosi float %a to i16
+  ret i16 %r
+}
+
+; CHECK: [[F32toS8]] = OpFunction [[U8]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[F32]]
+; CHECK: OpLabel
+; CHECK: [[R:%.*]] = OpConvertFToS [[U8]] [[A]]
+; CHECK: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i8 @f32tos8(float %a) {
+  %r = fptosi float %a to i8
+  ret i8 %r
+}
+
+; CHECK: [[F16toS32]] = OpFunction [[U32]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[F16]]
+; CHECK: OpLabel
+; CHECK: [[R:%.*]] = OpConvertFToS [[U32]] [[A]]
+; CHECK: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i32 @f16tos32(half %a) {
+  %r = fptosi half %a to i32
+  ret i32 %r
+}
+
+; CHECK: [[F16toS16]] = OpFunction [[U16]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[F16]]
+; CHECK: OpLabel
+; CHECK: [[R:%.*]] = OpConvertFToS [[U16]] [[A]]
+; CHECK: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i16 @f16tos16(half %a) {
+  %r = fptosi half %a to i16
+  ret i16 %r
+}
+
+; CHECK: [[F16toS8]] = OpFunction [[U8]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[F16]]
+; CHECK: OpLabel
+; CHECK: [[R:%.*]] = OpConvertFToS [[U8]] [[A]]
+; CHECK: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i8 @f16tos8(half %a) {
+  %r = fptosi half %a to i8
+  ret i8 %r
+}
+
+; CHECK: [[F32toU32v2]] = OpFunction [[U32v2]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[F32v2]]
+; CHECK: OpLabel
+; CHECK: [[R:%.*]] = OpConvertFToU [[U32v2]] [[A]]
+; CHECK: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define <2 x i32> @f32tou32v2(<2 x float> %a) {
+  %r = fptoui <2 x float> %a to <2 x i32>
+  ret <2 x i32> %r
+}
+
+; CHECK: [[F32toU16v2]] = OpFunction [[U16v2]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[F32v2]]
+; CHECK: OpLabel
+; CHECK: [[R:%.*]] = OpConvertFToU [[U16v2]] [[A]]
+; CHECK: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define <2 x i16> @f32tou16v2(<2 x float> %a) {
+  %r = fptoui <2 x float> %a to <2 x i16>
+  ret <2 x i16> %r
+}
+
+; CHECK: [[F32toU8v2]] = OpFunction [[U8v2]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[F32v2]]
+; CHECK: OpLabel
+; CHECK: [[R:%.*]] = OpConvertFToU [[U8v2]] [[A]]
+; CHECK: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define <2 x i8> @f32tou8v2(<2 x float> %a) {
+  %r = fptoui <2 x float> %a to <2 x i8>
+  ret <2 x i8> %r
+}
+
+; CHECK: [[F16toU32v2]] = OpFunction [[U32v2]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[F16v2]]
+; CHECK: OpLabel
+; CHECK: [[R:%.*]] = OpConvertFToU [[U32v2]] [[A]]
+; CHECK: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define <2 x i32> @f16tou32v2(<2 x half> %a) {
+  %r = fptoui <2 x half> %a to <2 x i32>
+  ret <2 x i32> %r
+}
+
+; CHECK: [[F16toU16v2]] = OpFunction [[U16v2]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[F16v2]]
+; CHECK: OpLabel
+; CHECK: [[R:%.*]] = OpConvertFToU [[U16v2]] [[A]]
+; CHECK: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define <2 x i16> @f16tou16v2(<2 x half> %a) {
+  %r = fptoui <2 x half> %a to <2 x i16>
+  ret <2 x i16> %r
+}
+
+; CHECK: [[F16toU8v2]] = OpFunction [[U8v2]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[F16v2]]
+; CHECK: OpLabel
+; CHECK: [[R:%.*]] = OpConvertFToU [[U8v2]] [[A]]
+; CHECK: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define <2 x i8> @f16tou8v2(<2 x half> %a) {
+  %r = fptoui <2 x half> %a to <2 x i8>
+  ret <2 x i8> %r
+}

diff  --git a/llvm/test/CodeGen/SPIRV/instructions/icmp.ll b/llvm/test/CodeGen/SPIRV/instructions/icmp.ll
new file mode 100644
index 0000000000000..74f95e5888980
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/instructions/icmp.ll
@@ -0,0 +1,265 @@
+; RUN: llc %s -o - | FileCheck %s
+
+target triple = "spirv32-unknown-unknown"
+
+; CHECK-DAG: OpName [[EQ:%.*]] "test_eq"
+; CHECK-DAG: OpName [[NE:%.*]] "test_ne"
+; CHECK-DAG: OpName [[ULT:%.*]] "test_ult"
+; CHECK-DAG: OpName [[SLT:%.*]] "test_slt"
+; CHECK-DAG: OpName [[ULE:%.*]] "test_ule"
+; CHECK-DAG: OpName [[SLE:%.*]] "test_sle"
+; CHECK-DAG: OpName [[UGT:%.*]] "test_ugt"
+; CHECK-DAG: OpName [[SGT:%.*]] "test_sgt"
+; CHECK-DAG: OpName [[UGE:%.*]] "test_uge"
+; CHECK-DAG: OpName [[SGE:%.*]] "test_sge"
+
+; CHECK-DAG: OpName [[v3EQ:%.*]] "test_v3_eq"
+; CHECK-DAG: OpName [[v3NE:%.*]] "test_v3_ne"
+; CHECK-DAG: OpName [[v3ULT:%.*]] "test_v3_ult"
+; CHECK-DAG: OpName [[v3SLT:%.*]] "test_v3_slt"
+; CHECK-DAG: OpName [[v3ULE:%.*]] "test_v3_ule"
+; CHECK-DAG: OpName [[v3SLE:%.*]] "test_v3_sle"
+; CHECK-DAG: OpName [[v3UGT:%.*]] "test_v3_ugt"
+; CHECK-DAG: OpName [[v3SGT:%.*]] "test_v3_sgt"
+; CHECK-DAG: OpName [[v3UGE:%.*]] "test_v3_uge"
+; CHECK-DAG: OpName [[v3SGE:%.*]] "test_v3_sge"
+
+; CHECK: [[EQ]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpIEqual {{%.+}} [[A]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i1 @test_eq(i32 %a, i32 %b) {
+  %r = icmp eq i32 %a, %b
+  ret i1 %r
+}
+
+; CHECK: [[NE]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpINotEqual {{%.+}} [[A]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i1 @test_ne(i32 %a, i32 %b) {
+  %r = icmp ne i32 %a, %b
+  ret i1 %r
+}
+
+; CHECK: [[SLT]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpSLessThan {{%.+}} [[A]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i1 @test_slt(i32 %a, i32 %b) {
+  %r = icmp slt i32 %a, %b
+  ret i1 %r
+}
+
+; CHECK: [[ULT]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpULessThan {{%.+}} [[A]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i1 @test_ult(i32 %a, i32 %b) {
+  %r = icmp ult i32 %a, %b
+  ret i1 %r
+}
+
+; CHECK: [[ULE]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpULessThanEqual {{%.+}} [[A]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i1 @test_ule(i32 %a, i32 %b) {
+  %r = icmp ule i32 %a, %b
+  ret i1 %r
+}
+
+; CHECK: [[SLE]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpSLessThanEqual {{%.+}} [[A]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i1 @test_sle(i32 %a, i32 %b) {
+  %r = icmp sle i32 %a, %b
+  ret i1 %r
+}
+
+; CHECK: [[UGT]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpUGreaterThan {{%.+}} [[A]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i1 @test_ugt(i32 %a, i32 %b) {
+  %r = icmp ugt i32 %a, %b
+  ret i1 %r
+}
+
+; CHECK: [[SGT]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpSGreaterThan {{%.+}} [[A]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i1 @test_sgt(i32 %a, i32 %b) {
+  %r = icmp sgt i32 %a, %b
+  ret i1 %r
+}
+
+; CHECK: [[UGE]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpUGreaterThanEqual {{%.+}} [[A]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i1 @test_uge(i32 %a, i32 %b) {
+  %r = icmp uge i32 %a, %b
+  ret i1 %r
+}
+
+; CHECK: [[SGE]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpSGreaterThanEqual {{%.+}} [[A]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i1 @test_sge(i32 %a, i32 %b) {
+  %r = icmp sge i32 %a, %b
+  ret i1 %r
+}
+
+; CHECK: [[v3EQ]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpIEqual {{%.+}} [[A]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define <3 x i1> @test_v3_eq(<3 x i32> %a, <3 x i32> %b) {
+  %r = icmp eq <3 x i32> %a, %b
+  ret <3 x i1> %r
+}
+
+; CHECK: [[v3NE]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpINotEqual {{%.+}} [[A]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define <3 x i1> @test_v3_ne(<3 x i32> %a, <3 x i32> %b) {
+  %r = icmp ne <3 x i32> %a, %b
+  ret <3 x i1> %r
+}
+
+; CHECK: [[v3SLT]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpSLessThan {{%.+}} [[A]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define <3 x i1> @test_v3_slt(<3 x i32> %a, <3 x i32> %b) {
+  %r = icmp slt <3 x i32> %a, %b
+  ret <3 x i1> %r
+}
+
+; CHECK: [[v3ULT]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpULessThan {{%.+}} [[A]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define <3 x i1> @test_v3_ult(<3 x i32> %a, <3 x i32> %b) {
+  %r = icmp ult <3 x i32> %a, %b
+  ret <3 x i1> %r
+}
+
+; CHECK: [[v3ULE]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpULessThanEqual {{%.+}} [[A]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define <3 x i1> @test_v3_ule(<3 x i32> %a, <3 x i32> %b) {
+  %r = icmp ule <3 x i32> %a, %b
+  ret <3 x i1> %r
+}
+
+; CHECK: [[v3SLE]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpSLessThanEqual {{%.+}} [[A]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define <3 x i1> @test_v3_sle(<3 x i32> %a, <3 x i32> %b) {
+  %r = icmp sle <3 x i32> %a, %b
+  ret <3 x i1> %r
+}
+
+; CHECK: [[v3UGT]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpUGreaterThan {{%.+}} [[A]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define <3 x i1> @test_v3_ugt(<3 x i32> %a, <3 x i32> %b) {
+  %r = icmp ugt <3 x i32> %a, %b
+  ret <3 x i1> %r
+}
+
+; CHECK: [[v3SGT]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpSGreaterThan {{%.+}} [[A]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define <3 x i1> @test_v3_sgt(<3 x i32> %a, <3 x i32> %b) {
+  %r = icmp sgt <3 x i32> %a, %b
+  ret <3 x i1> %r
+}
+
+; CHECK: [[v3UGE]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpUGreaterThanEqual {{%.+}} [[A]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define <3 x i1> @test_v3_uge(<3 x i32> %a, <3 x i32> %b) {
+  %r = icmp uge <3 x i32> %a, %b
+  ret <3 x i1> %r
+}
+
+; CHECK: [[v3SGE]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpSGreaterThanEqual {{%.+}} [[A]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define <3 x i1> @test_v3_sge(<3 x i32> %a, <3 x i32> %b) {
+  %r = icmp sge <3 x i32> %a, %b
+  ret <3 x i1> %r
+}

diff  --git a/llvm/test/CodeGen/SPIRV/instructions/integer-casts.ll b/llvm/test/CodeGen/SPIRV/instructions/integer-casts.ll
new file mode 100644
index 0000000000000..bbc2e202d7e58
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/instructions/integer-casts.ll
@@ -0,0 +1,231 @@
+; RUN: llc %s -o - | FileCheck %s
+
+target triple = "spirv32-unknown-unknown"
+
+; CHECK-DAG: OpName [[TRUNC32_16:%.*]] "i32toi16"
+; CHECK-DAG: OpName [[TRUNC32_8:%.*]] "i32toi8"
+; CHECK-DAG: OpName [[TRUNC16_8:%.*]] "i16toi8"
+; CHECK-DAG: OpName [[SEXT8_32:%.*]] "s8tos32"
+; CHECK-DAG: OpName [[SEXT8_16:%.*]] "s8tos16"
+; CHECK-DAG: OpName [[SEXT16_32:%.*]] "s16tos32"
+; CHECK-DAG: OpName [[ZEXT8_32:%.*]] "u8tou32"
+; CHECK-DAG: OpName [[ZEXT8_16:%.*]] "u8tou16"
+; CHECK-DAG: OpName [[ZEXT16_32:%.*]] "u16tou32"
+
+; CHECK-DAG: OpName [[TRUNC32_16v4:%.*]] "i32toi16v4"
+; CHECK-DAG: OpName [[TRUNC32_8v4:%.*]] "i32toi8v4"
+; CHECK-DAG: OpName [[TRUNC16_8v4:%.*]] "i16toi8v4"
+; CHECK-DAG: OpName [[SEXT8_32v4:%.*]] "s8tos32v4"
+; CHECK-DAG: OpName [[SEXT8_16v4:%.*]] "s8tos16v4"
+; CHECK-DAG: OpName [[SEXT16_32v4:%.*]] "s16tos32v4"
+; CHECK-DAG: OpName [[ZEXT8_32v4:%.*]] "u8tou32v4"
+; CHECK-DAG: OpName [[ZEXT8_16v4:%.*]] "u8tou16v4"
+; CHECK-DAG: OpName [[ZEXT16_32v4:%.*]] "u16tou32v4"
+
+; CHECK-DAG: [[U32:%.*]] = OpTypeInt 32 0
+; CHECK-DAG: [[U16:%.*]] = OpTypeInt 16 0
+; CHECK-DAG: [[U8:%.*]] = OpTypeInt 8 0
+; CHECK-DAG: [[U32v4:%.*]] = OpTypeVector [[U32]] 4
+; CHECK-DAG: [[U16v4:%.*]] = OpTypeVector [[U16]] 4
+; CHECK-DAG: [[U8v4:%.*]] = OpTypeVector [[U8]] 4
+
+
+; CHECK: [[TRUNC32_16]] = OpFunction [[U16]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[U32]]
+; CHECK: OpLabel
+; CHECK: [[R:%.*]] = OpUConvert [[U16]] [[A]]
+; CHECK: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i16 @i32toi16(i32 %a) {
+    %r = trunc i32 %a to i16
+    ret i16 %r
+}
+
+; CHECK: [[TRUNC32_8]] = OpFunction [[U8]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[U32]]
+; CHECK: OpLabel
+; CHECK: [[R:%.*]] = OpUConvert [[U8]] [[A]]
+; CHECK: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i8 @i32toi8(i32 %a) {
+    %r = trunc i32 %a to i8
+    ret i8 %r
+}
+
+; CHECK: [[TRUNC16_8]] = OpFunction [[U8]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[U16]]
+; CHECK: OpLabel
+; CHECK: [[R:%.*]] = OpUConvert [[U8]] [[A]]
+; CHECK: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i8 @i16toi8(i16 %a) {
+    %r = trunc i16 %a to i8
+    ret i8 %r
+}
+
+
+; CHECK: [[SEXT8_32]] = OpFunction [[U32]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[U8]]
+; CHECK: OpLabel
+; CHECK: [[R:%.*]] = OpSConvert [[U32]] [[A]]
+; CHECK: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i32 @s8tos32(i8 %a) {
+  %r = sext i8 %a to i32
+  ret i32 %r
+}
+
+; CHECK: [[SEXT8_16]] = OpFunction [[U16]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[U8]]
+; CHECK: OpLabel
+; CHECK: [[R:%.*]] = OpSConvert [[U16]] [[A]]
+; CHECK: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i16 @s8tos16(i8 %a) {
+  %r = sext i8 %a to i16
+  ret i16 %r
+}
+
+; CHECK: [[SEXT16_32]] = OpFunction [[U32]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[U16]]
+; CHECK: OpLabel
+; CHECK: [[R:%.*]] = OpSConvert [[U32]] [[A]]
+; CHECK: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i32 @s16tos32(i16 %a) {
+  %r = sext i16 %a to i32
+  ret i32 %r
+}
+
+; CHECK: [[ZEXT8_32]] = OpFunction [[U32]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[U8]]
+; CHECK: OpLabel
+; CHECK: [[R:%.*]] = OpUConvert [[U32]] [[A]]
+; CHECK: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i32 @u8tou32(i8 %a) {
+  %r = zext i8 %a to i32
+  ret i32 %r
+}
+
+; CHECK: [[ZEXT8_16]] = OpFunction [[U16]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[U8]]
+; CHECK: OpLabel
+; CHECK: [[R:%.*]] = OpUConvert [[U16]] [[A]]
+; CHECK: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i16 @u8tou16(i8 %a) {
+  %r = zext i8 %a to i16
+  ret i16 %r
+}
+
+; CHECK: [[ZEXT16_32]] = OpFunction [[U32]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[U16]]
+; CHECK: OpLabel
+; CHECK: [[R:%.*]] = OpUConvert [[U32]] [[A]]
+; CHECK: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i32 @u16tou32(i16 %a) {
+  %r = zext i16 %a to i32
+  ret i32 %r
+}
+
+; CHECK: [[TRUNC32_16v4]] = OpFunction [[U16v4]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[U32v4]]
+; CHECK: OpLabel
+; CHECK: [[R:%.*]] = OpUConvert [[U16v4]] [[A]]
+; CHECK: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define <4 x i16> @i32toi16v4(<4 x i32> %a) {
+    %r = trunc <4 x i32> %a to <4 x i16>
+    ret <4 x i16> %r
+}
+
+; CHECK: [[TRUNC32_8v4]] = OpFunction [[U8v4]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[U32v4]]
+; CHECK: OpLabel
+; CHECK: [[R:%.*]] = OpUConvert [[U8v4]] [[A]]
+; CHECK: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define <4 x i8> @i32toi8v4(<4 x i32> %a) {
+    %r = trunc <4 x i32> %a to <4 x i8>
+    ret <4 x i8> %r
+}
+
+; CHECK: [[TRUNC16_8v4]] = OpFunction [[U8v4]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[U16v4]]
+; CHECK: OpLabel
+; CHECK: [[R:%.*]] = OpUConvert [[U8v4]] [[A]]
+; CHECK: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define <4 x i8> @i16toi8v4(<4 x i16> %a) {
+    %r = trunc <4 x i16> %a to <4 x i8>
+    ret <4 x i8> %r
+}
+
+
+; CHECK: [[SEXT8_32v4]] = OpFunction [[U32v4]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[U8v4]]
+; CHECK: OpLabel
+; CHECK: [[R:%.*]] = OpSConvert [[U32v4]] [[A]]
+; CHECK: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define <4 x i32>  @s8tos32v4(<4 x i8> %a) {
+  %r = sext <4 x i8> %a to <4 x i32>
+  ret <4 x i32>  %r
+}
+
+; CHECK: [[SEXT8_16v4]] = OpFunction [[U16v4]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[U8v4]]
+; CHECK: OpLabel
+; CHECK: [[R:%.*]] = OpSConvert [[U16v4]] [[A]]
+; CHECK: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define <4 x i16> @s8tos16v4(<4 x i8> %a) {
+  %r = sext <4 x i8> %a to <4 x i16>
+  ret <4 x i16> %r
+}
+
+; CHECK: [[SEXT16_32v4]] = OpFunction [[U32v4]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[U16v4]]
+; CHECK: OpLabel
+; CHECK: [[R:%.*]] = OpSConvert [[U32v4]] [[A]]
+; CHECK: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define <4 x i32>  @s16tos32v4(<4 x i16> %a) {
+  %r = sext <4 x i16> %a to <4 x i32>
+  ret <4 x i32>  %r
+}
+
+; CHECK: [[ZEXT8_32v4]] = OpFunction [[U32v4]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[U8v4]]
+; CHECK: OpLabel
+; CHECK: [[R:%.*]] = OpUConvert [[U32v4]] [[A]]
+; CHECK: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define <4 x i32>  @u8tou32v4(<4 x i8> %a) {
+  %r = zext <4 x i8> %a to <4 x i32>
+  ret <4 x i32>  %r
+}
+
+; CHECK: [[ZEXT8_16v4]] = OpFunction [[U16v4]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[U8v4]]
+; CHECK: OpLabel
+; CHECK: [[R:%.*]] = OpUConvert [[U16v4]] [[A]]
+; CHECK: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define <4 x i16> @u8tou16v4(<4 x i8> %a) {
+  %r = zext <4 x i8> %a to <4 x i16>
+  ret <4 x i16> %r
+}
+
+; CHECK: [[ZEXT16_32v4]] = OpFunction [[U32v4]]
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter [[U16v4]]
+; CHECK: OpLabel
+; CHECK: [[R:%.*]] = OpUConvert [[U32v4]] [[A]]
+; CHECK: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define <4 x i32>  @u16tou32v4(<4 x i16> %a) {
+  %r = zext <4 x i16> %a to <4 x i32>
+  ret <4 x i32>  %r
+}

diff  --git a/llvm/test/CodeGen/SPIRV/instructions/ptrcmp.ll b/llvm/test/CodeGen/SPIRV/instructions/ptrcmp.ll
new file mode 100644
index 0000000000000..55da2110335a0
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/instructions/ptrcmp.ll
@@ -0,0 +1,151 @@
+; RUN: llc %s -o - | FileCheck %s
+
+target triple = "spirv32-unknown-unknown"
+
+; CHECK-DAG: OpName [[EQ:%.*]] "test_eq"
+; CHECK-DAG: OpName [[NE:%.*]] "test_ne"
+; CHECK-DAG: OpName [[ULT:%.*]] "test_ult"
+; CHECK-DAG: OpName [[SLT:%.*]] "test_slt"
+; CHECK-DAG: OpName [[ULE:%.*]] "test_ule"
+; CHECK-DAG: OpName [[SLE:%.*]] "test_sle"
+; CHECK-DAG: OpName [[UGT:%.*]] "test_ugt"
+; CHECK-DAG: OpName [[SGT:%.*]] "test_sgt"
+; CHECK-DAG: OpName [[UGE:%.*]] "test_uge"
+; CHECK-DAG: OpName [[SGE:%.*]] "test_sge"
+
+; FIXME: Translator uses OpIEqual/OpINotEqual for test_eq/test_ne cases
+; CHECK: [[EQ]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpPtrEqual {{%.+}} [[A]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i1 @test_eq(i16* %a, i16* %b) {
+  %r = icmp eq i16* %a, %b
+  ret i1 %r
+}
+
+; CHECK: [[NE]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[R:%.*]] = OpPtrNotEqual {{%.+}} [[A]] [[B]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i1 @test_ne(i16* %a, i16* %b) {
+  %r = icmp ne i16* %a, %b
+  ret i1 %r
+}
+
+; CHECK: [[SLT]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[AI:%.*]] = OpConvertPtrToU {{%.+}} [[A]]
+; CHECK-NEXT: [[BI:%.*]] = OpConvertPtrToU {{%.+}} [[B]]
+; CHECK: [[R:%.*]] = OpSLessThan {{%.+}} [[AI]] [[BI]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i1 @test_slt(i16* %a, i16* %b) {
+  %r = icmp slt i16* %a, %b
+  ret i1 %r
+}
+
+; CHECK: [[ULT]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[AI:%.*]] = OpConvertPtrToU {{%.+}} [[A]]
+; CHECK-NEXT: [[BI:%.*]] = OpConvertPtrToU {{%.+}} [[B]]
+; CHECK: [[R:%.*]] = OpULessThan {{%.+}} [[AI]] [[BI]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i1 @test_ult(i16* %a, i16* %b) {
+  %r = icmp ult i16* %a, %b
+  ret i1 %r
+}
+
+; CHECK: [[ULE]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[AI:%.*]] = OpConvertPtrToU {{%.+}} [[A]]
+; CHECK-NEXT: [[BI:%.*]] = OpConvertPtrToU {{%.+}} [[B]]
+; CHECK: [[R:%.*]] = OpULessThanEqual {{%.+}} [[AI]] [[BI]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i1 @test_ule(i16* %a, i16* %b) {
+  %r = icmp ule i16* %a, %b
+  ret i1 %r
+}
+
+; CHECK: [[SLE]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[AI:%.*]] = OpConvertPtrToU {{%.+}} [[A]]
+; CHECK-NEXT: [[BI:%.*]] = OpConvertPtrToU {{%.+}} [[B]]
+; CHECK: [[R:%.*]] = OpSLessThanEqual {{%.+}} [[AI]] [[BI]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i1 @test_sle(i16* %a, i16* %b) {
+  %r = icmp sle i16* %a, %b
+  ret i1 %r
+}
+
+; CHECK: [[UGT]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[AI:%.*]] = OpConvertPtrToU {{%.+}} [[A]]
+; CHECK-NEXT: [[BI:%.*]] = OpConvertPtrToU {{%.+}} [[B]]
+; CHECK: [[R:%.*]] = OpUGreaterThan {{%.+}} [[AI]] [[BI]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i1 @test_ugt(i16* %a, i16* %b) {
+  %r = icmp ugt i16* %a, %b
+  ret i1 %r
+}
+
+; CHECK: [[SGT]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[AI:%.*]] = OpConvertPtrToU {{%.+}} [[A]]
+; CHECK-NEXT: [[BI:%.*]] = OpConvertPtrToU {{%.+}} [[B]]
+; CHECK: [[R:%.*]] = OpSGreaterThan {{%.+}} [[AI]] [[BI]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i1 @test_sgt(i16* %a, i16* %b) {
+  %r = icmp sgt i16* %a, %b
+  ret i1 %r
+}
+
+; CHECK: [[UGE]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[AI:%.*]] = OpConvertPtrToU {{%.+}} [[A]]
+; CHECK-NEXT: [[BI:%.*]] = OpConvertPtrToU {{%.+}} [[B]]
+; CHECK: [[R:%.*]] = OpUGreaterThanEqual {{%.+}} [[AI]] [[BI]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i1 @test_uge(i16* %a, i16* %b) {
+  %r = icmp uge i16* %a, %b
+  ret i1 %r
+}
+
+; CHECK: [[SGE]] = OpFunction
+; CHECK-NEXT: [[A:%.*]] = OpFunctionParameter
+; CHECK-NEXT: [[B:%.*]] = OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: [[AI:%.*]] = OpConvertPtrToU {{%.+}} [[A]]
+; CHECK-NEXT: [[BI:%.*]] = OpConvertPtrToU {{%.+}} [[B]]
+; CHECK: [[R:%.*]] = OpSGreaterThanEqual {{%.+}} [[AI]] [[BI]]
+; CHECK-NEXT: OpReturnValue [[R]]
+; CHECK-NEXT: OpFunctionEnd
+define i1 @test_sge(i16* %a, i16* %b) {
+  %r = icmp sge i16* %a, %b
+  ret i1 %r
+}


        


More information about the llvm-commits mailing list