[llvm] 698c800 - [SPIRV] support builtin types and ExtInsts selection

Ilia Diachkov via llvm-commits llvm-commits at lists.llvm.org
Thu Sep 1 06:01:44 PDT 2022


Author: Ilia Diachkov
Date: 2022-09-01T16:44:54+03:00
New Revision: 698c800142d84d32471f36c7e0d33e68db07e06f

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

LOG: [SPIRV] support builtin types and ExtInsts selection

The patch adds the support of OpenCL and SPIR-V built-in types. It also
implements ExtInst selection and adds spv_unreachable and spv_alloca
intrinsics which improve the generation of the corresponding SPIR-V code.
Five LIT tests are included to demonstrate the improvement.

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

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/test/CodeGen/SPIRV/instructions/intrinsics.ll
    llvm/test/CodeGen/SPIRV/instructions/unreachable.ll
    llvm/test/CodeGen/SPIRV/llvm-intrinsics/fp-intrinsics.ll
    llvm/test/CodeGen/SPIRV/llvm-intrinsics/maxnum.ll
    llvm/test/CodeGen/SPIRV/opencl/image.ll

Modified: 
    llvm/include/llvm/IR/IntrinsicsSPIRV.td
    llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
    llvm/lib/Target/SPIRV/SPIRVBuiltins.h
    llvm/lib/Target/SPIRV/SPIRVBuiltins.td
    llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
    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/SPIRVSymbolicOperands.td

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/IR/IntrinsicsSPIRV.td b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
index 2cdd75f82962c..bf90aa0c04022 100644
--- a/llvm/include/llvm/IR/IntrinsicsSPIRV.td
+++ b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
@@ -29,4 +29,6 @@ let TargetPrefix = "spv" in {
   def int_spv_bitcast : Intrinsic<[llvm_any_ty], [llvm_any_ty]>;
   def int_spv_switch : Intrinsic<[], [llvm_any_ty, llvm_vararg_ty]>;
   def int_spv_cmpxchg : Intrinsic<[llvm_i32_ty], [llvm_any_ty, llvm_vararg_ty]>;
+  def int_spv_unreachable : Intrinsic<[], []>;
+  def int_spv_alloca : Intrinsic<[llvm_any_ty], []>;
 }

diff  --git a/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp b/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
index 892b3a015f534..5655662bd766d 100644
--- a/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
@@ -1611,5 +1611,236 @@ Optional<bool> lowerBuiltin(const StringRef DemangledCall,
   }
   return false;
 }
+
+struct DemangledType {
+  StringRef Name;
+  uint32_t Opcode;
+};
+
+#define GET_DemangledTypes_DECL
+#define GET_DemangledTypes_IMPL
+
+struct ImageType {
+  StringRef Name;
+  StringRef SampledType;
+  AccessQualifier::AccessQualifier Qualifier;
+  Dim::Dim Dimensionality;
+  bool Arrayed;
+  bool Depth;
+  bool Multisampled;
+  bool Sampled;
+  ImageFormat::ImageFormat Format;
+};
+
+struct PipeType {
+  StringRef Name;
+  AccessQualifier::AccessQualifier Qualifier;
+};
+
+using namespace AccessQualifier;
+using namespace Dim;
+using namespace ImageFormat;
+#define GET_ImageTypes_DECL
+#define GET_ImageTypes_IMPL
+#define GET_PipeTypes_DECL
+#define GET_PipeTypes_IMPL
+#include "SPIRVGenTables.inc"
+} // namespace SPIRV
+
+//===----------------------------------------------------------------------===//
+// Misc functions for parsing builtin types and looking up implementation
+// details in TableGenerated tables.
+//===----------------------------------------------------------------------===//
+
+static const SPIRV::DemangledType *findBuiltinType(StringRef Name) {
+  if (Name.startswith("opencl."))
+    return SPIRV::lookupBuiltinType(Name);
+  if (Name.startswith("spirv.")) {
+    // Some SPIR-V builtin types have a complex list of parameters as part of
+    // their name (e.g. spirv.Image._void_1_0_0_0_0_0_0). Those parameters often
+    // are numeric literals which cannot be easily represented by TableGen
+    // records and should be parsed instead.
+    unsigned BaseTypeNameLength =
+        Name.contains('_') ? Name.find('_') - 1 : Name.size();
+    return SPIRV::lookupBuiltinType(Name.substr(0, BaseTypeNameLength).str());
+  }
+  return nullptr;
+}
+
+static std::unique_ptr<const SPIRV::ImageType>
+lookupOrParseBuiltinImageType(StringRef Name) {
+  if (Name.startswith("opencl.")) {
+    // Lookup OpenCL builtin image type lowering details in TableGen records.
+    const SPIRV::ImageType *Record = SPIRV::lookupImageType(Name);
+    return std::unique_ptr<SPIRV::ImageType>(new SPIRV::ImageType(*Record));
+  }
+  if (Name.startswith("spirv.")) {
+    // Parse the literals of SPIR-V image builtin parameters. The name should
+    // have the following format:
+    // spirv.Image._Type_Dim_Depth_Arrayed_MS_Sampled_ImageFormat_AccessQualifier
+    // e.g. %spirv.Image._void_1_0_0_0_0_0_0
+    StringRef TypeParametersString = Name.substr(strlen("spirv.Image."));
+    SmallVector<StringRef> TypeParameters;
+    SplitString(TypeParametersString, TypeParameters, "_");
+    assert(TypeParameters.size() == 8 &&
+           "Wrong number of literals in SPIR-V builtin image type");
+
+    StringRef SampledType = TypeParameters[0];
+    unsigned Dim, Depth, Arrayed, Multisampled, Sampled, Format, AccessQual;
+    bool AreParameterLiteralsValid =
+        !(TypeParameters[1].getAsInteger(10, Dim) ||
+          TypeParameters[2].getAsInteger(10, Depth) ||
+          TypeParameters[3].getAsInteger(10, Arrayed) ||
+          TypeParameters[4].getAsInteger(10, Multisampled) ||
+          TypeParameters[5].getAsInteger(10, Sampled) ||
+          TypeParameters[6].getAsInteger(10, Format) ||
+          TypeParameters[7].getAsInteger(10, AccessQual));
+    assert(AreParameterLiteralsValid &&
+           "Invalid format of SPIR-V image type parameter literals.");
+
+    return std::unique_ptr<SPIRV::ImageType>(new SPIRV::ImageType{
+        Name, SampledType, SPIRV::AccessQualifier::AccessQualifier(AccessQual),
+        SPIRV::Dim::Dim(Dim), static_cast<bool>(Arrayed),
+        static_cast<bool>(Depth), static_cast<bool>(Multisampled),
+        static_cast<bool>(Sampled), SPIRV::ImageFormat::ImageFormat(Format)});
+  }
+  llvm_unreachable("Unknown builtin image type name/literal");
+}
+
+static std::unique_ptr<const SPIRV::PipeType>
+lookupOrParseBuiltinPipeType(StringRef Name) {
+  if (Name.startswith("opencl.")) {
+    // Lookup OpenCL builtin pipe type lowering details in TableGen records.
+    const SPIRV::PipeType *Record = SPIRV::lookupPipeType(Name);
+    return std::unique_ptr<SPIRV::PipeType>(new SPIRV::PipeType(*Record));
+  }
+  if (Name.startswith("spirv.")) {
+    // Parse the access qualifier literal in the name of the SPIR-V pipe type.
+    // The name should have the following format:
+    // spirv.Pipe._AccessQualifier
+    // e.g. %spirv.Pipe._1
+    if (Name.endswith("_0"))
+      return std::unique_ptr<SPIRV::PipeType>(
+          new SPIRV::PipeType{Name, SPIRV::AccessQualifier::ReadOnly});
+    if (Name.endswith("_1"))
+      return std::unique_ptr<SPIRV::PipeType>(
+          new SPIRV::PipeType{Name, SPIRV::AccessQualifier::WriteOnly});
+    if (Name.endswith("_2"))
+      return std::unique_ptr<SPIRV::PipeType>(
+          new SPIRV::PipeType{Name, SPIRV::AccessQualifier::ReadWrite});
+    llvm_unreachable("Unknown pipe type access qualifier literal");
+  }
+  llvm_unreachable("Unknown builtin pipe type name/literal");
+}
+
+//===----------------------------------------------------------------------===//
+// Implementation functions for builtin types.
+//===----------------------------------------------------------------------===//
+
+SPIRVType *getNonParametrizedType(const StructType *OpaqueType,
+                                  const SPIRV::DemangledType *TypeRecord,
+                                  MachineIRBuilder &MIRBuilder,
+                                  SPIRVGlobalRegistry *GR) {
+  unsigned Opcode = TypeRecord->Opcode;
+  // Create or get an existing type from GlobalRegistry.
+  return GR->getOrCreateOpTypeByOpcode(OpaqueType, MIRBuilder, Opcode);
+}
+
+SPIRVType *getSamplerType(MachineIRBuilder &MIRBuilder,
+                          SPIRVGlobalRegistry *GR) {
+  // Create or get an existing type from GlobalRegistry.
+  return GR->getOrCreateOpTypeSampler(MIRBuilder);
+}
+
+SPIRVType *getPipeType(const StructType *OpaqueType,
+                       MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR) {
+  // Lookup pipe type lowering details in TableGen records or parse the
+  // name/literal for details.
+  std::unique_ptr<const SPIRV::PipeType> Record =
+      lookupOrParseBuiltinPipeType(OpaqueType->getName());
+  // Create or get an existing type from GlobalRegistry.
+  return GR->getOrCreateOpTypePipe(MIRBuilder, Record.get()->Qualifier);
+}
+
+SPIRVType *getImageType(const StructType *OpaqueType,
+                        SPIRV::AccessQualifier::AccessQualifier AccessQual,
+                        MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR) {
+  // Lookup image type lowering details in TableGen records or parse the
+  // name/literal for details.
+  std::unique_ptr<const SPIRV::ImageType> Record =
+      lookupOrParseBuiltinImageType(OpaqueType->getName());
+
+  SPIRVType *SampledType =
+      GR->getOrCreateSPIRVTypeByName(Record.get()->SampledType, MIRBuilder);
+  return GR->getOrCreateOpTypeImage(
+      MIRBuilder, SampledType, Record.get()->Dimensionality,
+      Record.get()->Depth, Record.get()->Arrayed, Record.get()->Multisampled,
+      Record.get()->Sampled, Record.get()->Format,
+      AccessQual == SPIRV::AccessQualifier::WriteOnly
+          ? SPIRV::AccessQualifier::WriteOnly
+          : Record.get()->Qualifier);
+}
+
+SPIRVType *getSampledImageType(const StructType *OpaqueType,
+                               MachineIRBuilder &MIRBuilder,
+                               SPIRVGlobalRegistry *GR) {
+  StringRef TypeParametersString =
+      OpaqueType->getName().substr(strlen("spirv.SampledImage."));
+  LLVMContext &Context = MIRBuilder.getMF().getFunction().getContext();
+  Type *ImageOpaqueType = StructType::getTypeByName(
+      Context, "spirv.Image." + TypeParametersString.str());
+  SPIRVType *TargetImageType =
+      GR->getOrCreateSPIRVType(ImageOpaqueType, MIRBuilder);
+  return GR->getOrCreateOpTypeSampledImage(TargetImageType, MIRBuilder);
+}
+
+namespace SPIRV {
+SPIRVType *lowerBuiltinType(const StructType *OpaqueType,
+                            AccessQualifier::AccessQualifier AccessQual,
+                            MachineIRBuilder &MIRBuilder,
+                            SPIRVGlobalRegistry *GR) {
+  assert(OpaqueType->hasName() &&
+         "Structs representing builtin types must have a parsable name");
+  unsigned NumStartingVRegs = MIRBuilder.getMRI()->getNumVirtRegs();
+
+  const StringRef Name = OpaqueType->getName();
+  LLVM_DEBUG(dbgs() << "Lowering builtin type: " << Name << "\n");
+
+  // Lookup the demangled builtin type in the TableGen records.
+  const SPIRV::DemangledType *TypeRecord = findBuiltinType(Name);
+  if (!TypeRecord)
+    report_fatal_error("Missing TableGen record for builtin type: " + Name);
+
+  // "Lower" the BuiltinType into TargetType. The following get<...>Type methods
+  // use the implementation details from TableGen records to either create a new
+  // OpType<...> machine instruction or get an existing equivalent SPIRVType
+  // from GlobalRegistry.
+  SPIRVType *TargetType;
+  switch (TypeRecord->Opcode) {
+  case SPIRV::OpTypeImage:
+    TargetType = getImageType(OpaqueType, AccessQual, MIRBuilder, GR);
+    break;
+  case SPIRV::OpTypePipe:
+    TargetType = getPipeType(OpaqueType, MIRBuilder, GR);
+    break;
+  case SPIRV::OpTypeSampler:
+    TargetType = getSamplerType(MIRBuilder, GR);
+    break;
+  case SPIRV::OpTypeSampledImage:
+    TargetType = getSampledImageType(OpaqueType, MIRBuilder, GR);
+    break;
+  default:
+    TargetType = getNonParametrizedType(OpaqueType, TypeRecord, MIRBuilder, GR);
+    break;
+  }
+
+  // Emit OpName instruction if a new OpType<...> instruction was added
+  // (equivalent type was not found in GlobalRegistry).
+  if (NumStartingVRegs < MIRBuilder.getMRI()->getNumVirtRegs())
+    buildOpName(GR->getSPIRVTypeID(TargetType), OpaqueType->getName(),
+                MIRBuilder);
+
+  return TargetType;
+}
 } // namespace SPIRV
 } // namespace llvm

diff  --git a/llvm/lib/Target/SPIRV/SPIRVBuiltins.h b/llvm/lib/Target/SPIRV/SPIRVBuiltins.h
index 8b105f7a81cfd..2f622e7443307 100644
--- a/llvm/lib/Target/SPIRV/SPIRVBuiltins.h
+++ b/llvm/lib/Target/SPIRV/SPIRVBuiltins.h
@@ -28,14 +28,27 @@ namespace SPIRV {
 /// \p DemangledCall is the skeleton of the lowered builtin function call.
 /// \p Set is the external instruction set containing the given builtin.
 /// \p OrigRet is the single original virtual return register if defined,
-/// Register(0) otherwise. \p OrigRetTy is the type of the \p OrigRet. \p Args
-/// are the arguments of the lowered builtin call.
+/// Register(0) otherwise.
+/// \p OrigRetTy is the type of the \p OrigRet.
+/// \p Args are the arguments of the lowered builtin call.
 Optional<bool> lowerBuiltin(const StringRef DemangledCall,
                             InstructionSet::InstructionSet Set,
                             MachineIRBuilder &MIRBuilder,
                             const Register OrigRet, const Type *OrigRetTy,
                             const SmallVectorImpl<Register> &Args,
                             SPIRVGlobalRegistry *GR);
+/// Handles the translation of the provided special opaque/builtin type \p Type
+/// to SPIR-V type. Generates the corresponding machine instructions for the
+/// target type or gets the already existing OpType<...> register from the
+/// global registry \p GR.
+///
+/// \return A machine instruction representing the OpType<...> SPIR-V type.
+///
+/// \p Type is the special opaque/builtin type to be lowered.
+SPIRVType *lowerBuiltinType(const StructType *Type,
+                            AccessQualifier::AccessQualifier AccessQual,
+                            MachineIRBuilder &MIRBuilder,
+                            SPIRVGlobalRegistry *GR);
 } // namespace SPIRV
 } // namespace llvm
 #endif // LLVM_LIB_TARGET_SPIRV_SPIRVBUILTINS_H

diff  --git a/llvm/lib/Target/SPIRV/SPIRVBuiltins.td b/llvm/lib/Target/SPIRV/SPIRVBuiltins.td
index 763ae7d361503..da63ef53a53be 100644
--- a/llvm/lib/Target/SPIRV/SPIRVBuiltins.td
+++ b/llvm/lib/Target/SPIRV/SPIRVBuiltins.td
@@ -1104,22 +1104,36 @@ def DemangledTypes : GenericTable {
 }
 
 // Function to lookup builtin types by their demangled name.
-def lookupType : SearchIndex {
+def lookupBuiltinType : SearchIndex {
   let Table = DemangledTypes;
   let Key = ["Name"];
 }
 
-// OpenCL builtin types:
 def : DemangledType<"opencl.reserve_id_t", OpTypeReserveId>;
 def : DemangledType<"opencl.event_t", OpTypeEvent>;
 def : DemangledType<"opencl.queue_t", OpTypeQueue>;
 def : DemangledType<"opencl.sampler_t", OpTypeSampler>;
 def : DemangledType<"opencl.clk_event_t", OpTypeDeviceEvent>;
-def : DemangledType<"opencl.clk_event_t", OpTypeDeviceEvent>;
+
+def : DemangledType<"spirv.ReserveId", OpTypeReserveId>;
+def : DemangledType<"spirv.PipeStorage", OpTypePipeStorage>;
+def : DemangledType<"spirv.Queue", OpTypeQueue>;
+def : DemangledType<"spirv.Event", OpTypeEvent>;
+def : DemangledType<"spirv.Sampler", OpTypeSampler>;
+def : DemangledType<"spirv.DeviceEvent", OpTypeDeviceEvent>;
+
+// Some SPIR-V builtin types (e.g. spirv.Image) have a complex list of
+// parameters as part of their name. Some of those parameters should be treated
+// as numeric literals and therefore they cannot be represented in TableGen and
+// should be parsed instead.
+def : DemangledType<"spirv.Image", OpTypeImage>;
+def : DemangledType<"spirv.SampledImage", OpTypeSampledImage>;
+def : DemangledType<"spirv.Pipe", OpTypePipe>;
 
 // Class definining lowering details for various variants of image type indentifiers.
 class ImageType<string name> {
   string Name = name;
+  string Type = "void";
   AccessQualifier Qualifier = !cond(!not(!eq(!find(name, "_ro_t"), -1)) : ReadOnly,
                                   !not(!eq(!find(name, "_wo_t"), -1)) : WriteOnly,
                                   !not(!eq(!find(name, "_rw_t"), -1)) : ReadWrite,
@@ -1130,14 +1144,19 @@ class ImageType<string name> {
                                   !not(!eq(!find(name, "image3"), -1)) : DIM_3D);
   bit Arrayed = !not(!eq(!find(name, "array"), -1));
   bit Depth = !not(!eq(!find(name, "depth"), -1));
+  bit Multisampled = false;
+  bit Sampled = false;
+  ImageFormat Format = Unknown;
 }
 
 // Table gathering all the image type records.
 def ImageTypes : GenericTable {
   let FilterClass = "ImageType";
-  let Fields = ["Name", "Qualifier", "Dimensionality", "Arrayed", "Depth"];
+  let Fields = ["Name", "Type", "Qualifier", "Dimensionality", "Arrayed",
+                "Depth", "Multisampled", "Sampled", "Format"];
   string TypeOf_Qualifier = "AccessQualifier";
   string TypeOf_Dimensionality = "Dim";
+  string TypeOf_Format = "ImageFormat";
 }
 
 // Function to lookup builtin image types by their demangled name.

diff  --git a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
index 0075f547b6d67..7b7455a53325d 100644
--- a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
@@ -88,6 +88,7 @@ class SPIRVEmitIntrinsics
   Instruction *visitStoreInst(StoreInst &I);
   Instruction *visitAllocaInst(AllocaInst &I);
   Instruction *visitAtomicCmpXchgInst(AtomicCmpXchgInst &I);
+  Instruction *visitUnreachableInst(UnreachableInst &I);
   bool runOnFunction(Function &F) override;
 };
 } // namespace
@@ -313,7 +314,13 @@ Instruction *SPIRVEmitIntrinsics::visitStoreInst(StoreInst &I) {
 
 Instruction *SPIRVEmitIntrinsics::visitAllocaInst(AllocaInst &I) {
   TrackConstants = false;
-  return &I;
+  Type *PtrTy = I.getType();
+  auto *NewI = IRB->CreateIntrinsic(Intrinsic::spv_alloca, {PtrTy}, {});
+  std::string InstName = I.hasName() ? I.getName().str() : "";
+  I.replaceAllUsesWith(NewI);
+  I.eraseFromParent();
+  NewI->setName(InstName);
+  return NewI;
 }
 
 Instruction *SPIRVEmitIntrinsics::visitAtomicCmpXchgInst(AtomicCmpXchgInst &I) {
@@ -332,6 +339,12 @@ Instruction *SPIRVEmitIntrinsics::visitAtomicCmpXchgInst(AtomicCmpXchgInst &I) {
   return NewI;
 }
 
+Instruction *SPIRVEmitIntrinsics::visitUnreachableInst(UnreachableInst &I) {
+  IRB->SetInsertPoint(&I);
+  IRB->CreateIntrinsic(Intrinsic::spv_unreachable, {}, {});
+  return &I;
+}
+
 void SPIRVEmitIntrinsics::processGlobalValue(GlobalVariable &GV) {
   // Skip special artifical variable llvm.global.annotations.
   if (GV.getName() == "llvm.global.annotations")
@@ -368,7 +381,7 @@ void SPIRVEmitIntrinsics::insertAssignTypeIntrs(Instruction *I) {
     if (isa<ConstantPointerNull>(Op) || isa<UndefValue>(Op) ||
         // Check GetElementPtrConstantExpr case.
         (isa<ConstantExpr>(Op) && isa<GEPOperator>(Op))) {
-      IRB->SetInsertPoint(I);
+      setInsertPointSkippingPhis(*IRB, I);
       if (isa<UndefValue>(Op) && Op->getType()->isAggregateType())
         buildIntrWithMD(Intrinsic::spv_assign_type, {IRB->getInt32Ty()}, Op,
                         UndefValue::get(IRB->getInt32Ty()));

diff  --git a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
index 03f8fa6f61b48..3131794eed2d0 100644
--- a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
@@ -16,6 +16,7 @@
 
 #include "SPIRVGlobalRegistry.h"
 #include "SPIRV.h"
+#include "SPIRVBuiltins.h"
 #include "SPIRVSubtarget.h"
 #include "SPIRVTargetMachine.h"
 #include "SPIRVUtils.h"
@@ -479,10 +480,10 @@ Register SPIRVGlobalRegistry::buildGlobalVariable(
   if (IsConst)
     buildOpDecorate(Reg, MIRBuilder, SPIRV::Decoration::Constant, {});
 
-  if (GVar && GVar->getAlign().valueOrOne().value() != 1)
-    buildOpDecorate(
-        Reg, MIRBuilder, SPIRV::Decoration::Alignment,
-        {static_cast<uint32_t>(GVar->getAlign().valueOrOne().value())});
+  if (GVar && GVar->getAlign().valueOrOne().value() != 1) {
+    unsigned Alignment = (unsigned)GVar->getAlign().valueOrOne().value();
+    buildOpDecorate(Reg, MIRBuilder, SPIRV::Decoration::Alignment, {Alignment});
+  }
 
   if (HasLinkageTy)
     buildOpDecorate(Reg, MIRBuilder, SPIRV::Decoration::LinkageAttributes,
@@ -563,6 +564,20 @@ static bool isSpecialType(const Type *Ty) {
   return false;
 }
 
+SPIRVType *SPIRVGlobalRegistry::getOrCreateSpecialType(
+    const Type *Ty, MachineIRBuilder &MIRBuilder,
+    SPIRV::AccessQualifier::AccessQualifier AccQual) {
+  // Some OpenCL and SPIRV builtins like image2d_t are passed in as
+  // pointers, but should be treated as custom types like OpTypeImage.
+  if (auto PType = dyn_cast<PointerType>(Ty)) {
+    assert(!PType->isOpaque());
+    Ty = PType->getNonOpaquePointerElementType();
+  }
+  auto SType = cast<StructType>(Ty);
+  assert(isOpenCLBuiltinType(SType) || isSPIRVBuiltinType(SType));
+  return SPIRV::lowerBuiltinType(SType, AccQual, MIRBuilder, this);
+}
+
 SPIRVType *SPIRVGlobalRegistry::getOpTypePointer(
     SPIRV::StorageClass::StorageClass SC, SPIRVType *ElemType,
     MachineIRBuilder &MIRBuilder, Register Reg) {
@@ -624,7 +639,8 @@ Register SPIRVGlobalRegistry::getSPIRVTypeID(const SPIRVType *SpirvType) const {
 SPIRVType *SPIRVGlobalRegistry::createSPIRVType(
     const Type *Ty, MachineIRBuilder &MIRBuilder,
     SPIRV::AccessQualifier::AccessQualifier AccQual, bool EmitIR) {
-  assert(!isSpecialType(Ty));
+  if (isSpecialType(Ty))
+    return getOrCreateSpecialType(Ty, MIRBuilder, AccQual);
   auto &TypeToSPIRVTypeMap = DT.getTypes()->getAllUses();
   auto t = TypeToSPIRVTypeMap.find(Ty);
   if (t != TypeToSPIRVTypeMap.end()) {
@@ -729,7 +745,7 @@ SPIRVType *SPIRVGlobalRegistry::getOrCreateSPIRVType(
     const Type *Ty, MachineIRBuilder &MIRBuilder,
     SPIRV::AccessQualifier::AccessQualifier AccessQual, bool EmitIR) {
   Register Reg = DT.find(Ty, &MIRBuilder.getMF());
-  if (Reg.isValid())
+  if (Reg.isValid() && !isSpecialType(Ty))
     return getSPIRVTypeForVReg(Reg);
   TypesInProcessing.clear();
   SPIRVType *STy = restOfCreateSPIRVType(Ty, MIRBuilder, AccessQual, EmitIR);
@@ -804,6 +820,53 @@ SPIRVGlobalRegistry::getPointerStorageClass(Register VReg) const {
       Type->getOperand(1).getImm());
 }
 
+SPIRVType *SPIRVGlobalRegistry::getOrCreateOpTypeImage(
+    MachineIRBuilder &MIRBuilder, SPIRVType *SampledType, SPIRV::Dim::Dim Dim,
+    uint32_t Depth, uint32_t Arrayed, uint32_t Multisampled, uint32_t Sampled,
+    SPIRV::ImageFormat::ImageFormat ImageFormat,
+    SPIRV::AccessQualifier::AccessQualifier AccessQual) {
+  SPIRV::ImageTypeDescriptor TD(SPIRVToLLVMType.lookup(SampledType), Dim, Depth,
+                                Arrayed, Multisampled, Sampled, ImageFormat,
+                                AccessQual);
+  if (auto *Res = checkSpecialInstr(TD, MIRBuilder))
+    return Res;
+  Register ResVReg = createTypeVReg(MIRBuilder);
+  DT.add(TD, &MIRBuilder.getMF(), ResVReg);
+  return MIRBuilder.buildInstr(SPIRV::OpTypeImage)
+      .addDef(ResVReg)
+      .addUse(getSPIRVTypeID(SampledType))
+      .addImm(Dim)
+      .addImm(Depth)        // Depth (whether or not it is a Depth image).
+      .addImm(Arrayed)      // Arrayed.
+      .addImm(Multisampled) // Multisampled (0 = only single-sample).
+      .addImm(Sampled)      // Sampled (0 = usage known at runtime).
+      .addImm(ImageFormat)
+      .addImm(AccessQual);
+}
+
+SPIRVType *
+SPIRVGlobalRegistry::getOrCreateOpTypeSampler(MachineIRBuilder &MIRBuilder) {
+  SPIRV::SamplerTypeDescriptor TD;
+  if (auto *Res = checkSpecialInstr(TD, MIRBuilder))
+    return Res;
+  Register ResVReg = createTypeVReg(MIRBuilder);
+  DT.add(TD, &MIRBuilder.getMF(), ResVReg);
+  return MIRBuilder.buildInstr(SPIRV::OpTypeSampler).addDef(ResVReg);
+}
+
+SPIRVType *SPIRVGlobalRegistry::getOrCreateOpTypePipe(
+    MachineIRBuilder &MIRBuilder,
+    SPIRV::AccessQualifier::AccessQualifier AccessQual) {
+  SPIRV::PipeTypeDescriptor TD(AccessQual);
+  if (auto *Res = checkSpecialInstr(TD, MIRBuilder))
+    return Res;
+  Register ResVReg = createTypeVReg(MIRBuilder);
+  DT.add(TD, &MIRBuilder.getMF(), ResVReg);
+  return MIRBuilder.buildInstr(SPIRV::OpTypePipe)
+      .addDef(ResVReg)
+      .addImm(AccessQual);
+}
+
 SPIRVType *SPIRVGlobalRegistry::getOrCreateOpTypeSampledImage(
     SPIRVType *ImageType, MachineIRBuilder &MIRBuilder) {
   SPIRV::SampledImageTypeDescriptor TD(
@@ -813,11 +876,20 @@ SPIRVType *SPIRVGlobalRegistry::getOrCreateOpTypeSampledImage(
   if (auto *Res = checkSpecialInstr(TD, MIRBuilder))
     return Res;
   Register ResVReg = createTypeVReg(MIRBuilder);
-  auto MIB = MIRBuilder.buildInstr(SPIRV::OpTypeSampledImage)
-                 .addDef(ResVReg)
-                 .addUse(getSPIRVTypeID(ImageType));
   DT.add(TD, &MIRBuilder.getMF(), ResVReg);
-  return MIB;
+  return MIRBuilder.buildInstr(SPIRV::OpTypeSampledImage)
+      .addDef(ResVReg)
+      .addUse(getSPIRVTypeID(ImageType));
+}
+
+SPIRVType *SPIRVGlobalRegistry::getOrCreateOpTypeByOpcode(
+    const Type *Ty, MachineIRBuilder &MIRBuilder, unsigned Opcode) {
+  Register ResVReg = DT.find(Ty, &MIRBuilder.getMF());
+  if (ResVReg.isValid())
+    return MIRBuilder.getMF().getRegInfo().getUniqueVRegDef(ResVReg);
+  ResVReg = createTypeVReg(MIRBuilder);
+  DT.add(Ty, &MIRBuilder.getMF(), ResVReg);
+  return MIRBuilder.buildInstr(Opcode).addDef(ResVReg);
 }
 
 const MachineInstr *
@@ -942,6 +1014,24 @@ SPIRVType *SPIRVGlobalRegistry::getOrCreateSPIRVVectorType(
   return finishCreatingSPIRVType(LLVMTy, MIB);
 }
 
+SPIRVType *SPIRVGlobalRegistry::getOrCreateSPIRVArrayType(
+    SPIRVType *BaseType, unsigned NumElements, MachineInstr &I,
+    const SPIRVInstrInfo &TII) {
+  Type *LLVMTy = ArrayType::get(
+      const_cast<Type *>(getTypeForSPIRVType(BaseType)), NumElements);
+  Register Reg = DT.find(LLVMTy, CurMF);
+  if (Reg.isValid())
+    return getSPIRVTypeForVReg(Reg);
+  MachineBasicBlock &BB = *I.getParent();
+  SPIRVType *SpirvType = getOrCreateSPIRVIntegerType(32, I, TII);
+  Register Len = getOrCreateConstInt(NumElements, I, SpirvType, TII);
+  auto MIB = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpTypeArray))
+                 .addDef(createTypeVReg(CurMF->getRegInfo()))
+                 .addUse(getSPIRVTypeID(BaseType))
+                 .addUse(Len);
+  return finishCreatingSPIRVType(LLVMTy, MIB);
+}
+
 SPIRVType *SPIRVGlobalRegistry::getOrCreateSPIRVPointerType(
     SPIRVType *BaseType, MachineIRBuilder &MIRBuilder,
     SPIRV::StorageClass::StorageClass SClass) {

diff  --git a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h
index 5c19b2735d52c..667802a84ee47 100644
--- a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h
+++ b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h
@@ -208,6 +208,11 @@ class SPIRVGlobalRegistry {
   SPIRVType *getOpTypeFunction(SPIRVType *RetType,
                                const SmallVectorImpl<SPIRVType *> &ArgTypes,
                                MachineIRBuilder &MIRBuilder);
+
+  SPIRVType *
+  getOrCreateSpecialType(const Type *Ty, MachineIRBuilder &MIRBuilder,
+                         SPIRV::AccessQualifier::AccessQualifier AccQual);
+
   std::tuple<Register, ConstantInt *, bool> getOrCreateConstIntReg(
       uint64_t Val, SPIRVType *SpvType, MachineIRBuilder *MIRBuilder,
       MachineInstr *I = nullptr, const SPIRVInstrInfo *TII = nullptr);
@@ -240,7 +245,6 @@ class SPIRVGlobalRegistry {
                                     SPIRVType *SpvType, bool EmitIR = true);
   Register getOrCreateConsIntArray(uint64_t Val, MachineIRBuilder &MIRBuilder,
                                    SPIRVType *SpvType, bool EmitIR = true);
-
   Register buildConstantSampler(Register Res, unsigned AddrMode, unsigned Param,
                                 unsigned FilerMode,
                                 MachineIRBuilder &MIRBuilder,
@@ -270,19 +274,39 @@ class SPIRVGlobalRegistry {
   SPIRVType *getOrCreateSPIRVVectorType(SPIRVType *BaseType,
                                         unsigned NumElements, MachineInstr &I,
                                         const SPIRVInstrInfo &TII);
+  SPIRVType *getOrCreateSPIRVArrayType(SPIRVType *BaseType,
+                                       unsigned NumElements, MachineInstr &I,
+                                       const SPIRVInstrInfo &TII);
+
   SPIRVType *getOrCreateSPIRVPointerType(
       SPIRVType *BaseType, MachineIRBuilder &MIRBuilder,
       SPIRV::StorageClass::StorageClass SClass = SPIRV::StorageClass::Function);
   SPIRVType *getOrCreateSPIRVPointerType(
       SPIRVType *BaseType, MachineInstr &I, const SPIRVInstrInfo &TII,
       SPIRV::StorageClass::StorageClass SClass = SPIRV::StorageClass::Function);
+
+  SPIRVType *
+  getOrCreateOpTypeImage(MachineIRBuilder &MIRBuilder, SPIRVType *SampledType,
+                         SPIRV::Dim::Dim Dim, uint32_t Depth, uint32_t Arrayed,
+                         uint32_t Multisampled, uint32_t Sampled,
+                         SPIRV::ImageFormat::ImageFormat ImageFormat,
+                         SPIRV::AccessQualifier::AccessQualifier AccQual);
+
+  SPIRVType *getOrCreateOpTypeSampler(MachineIRBuilder &MIRBuilder);
+
   SPIRVType *getOrCreateOpTypeSampledImage(SPIRVType *ImageType,
                                            MachineIRBuilder &MIRBuilder);
 
+  SPIRVType *
+  getOrCreateOpTypePipe(MachineIRBuilder &MIRBuilder,
+                        SPIRV::AccessQualifier::AccessQualifier AccQual);
   SPIRVType *getOrCreateOpTypeFunctionWithArgs(
       const Type *Ty, SPIRVType *RetType,
       const SmallVectorImpl<SPIRVType *> &ArgTypes,
       MachineIRBuilder &MIRBuilder);
+  SPIRVType *getOrCreateOpTypeByOpcode(const Type *Ty,
+                                       MachineIRBuilder &MIRBuilder,
+                                       unsigned Opcode);
 };
 } // end namespace llvm
 #endif // LLLVM_LIB_TARGET_SPIRV_SPIRVTYPEMANAGER_H

diff  --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index 07e1158677862..5ebec6b8fa137 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -30,6 +30,11 @@
 #define DEBUG_TYPE "spirv-isel"
 
 using namespace llvm;
+namespace CL = SPIRV::OpenCLExtInst;
+namespace GL = SPIRV::GLSLExtInst;
+
+using ExtInstList =
+    std::vector<std::pair<SPIRV::InstructionSet::InstructionSet, uint32_t>>;
 
 namespace {
 
@@ -132,9 +137,8 @@ class SPIRVInstructionSelector : public InstructionSelector {
   bool selectTrunc(Register ResVReg, const SPIRVType *ResType,
                    MachineInstr &I) const;
 
-  bool selectIntToBool(Register IntReg, Register ResVReg,
-                       const SPIRVType *intTy, const SPIRVType *boolTy,
-                       MachineInstr &I) const;
+  bool selectIntToBool(Register IntReg, Register ResVReg, MachineInstr &I,
+                       const SPIRVType *intTy, const SPIRVType *boolTy) const;
 
   bool selectOpUndef(Register ResVReg, const SPIRVType *ResType,
                      MachineInstr &I) const;
@@ -160,6 +164,14 @@ class SPIRVInstructionSelector : public InstructionSelector {
   bool selectPhi(Register ResVReg, const SPIRVType *ResType,
                  MachineInstr &I) const;
 
+  bool selectExtInst(Register ResVReg, const SPIRVType *ResType,
+                     MachineInstr &I, CL::OpenCLExtInst CLInst) const;
+  bool selectExtInst(Register ResVReg, const SPIRVType *ResType,
+                     MachineInstr &I, CL::OpenCLExtInst CLInst,
+                     GL::GLSLExtInst GLInst) const;
+  bool selectExtInst(Register ResVReg, const SPIRVType *ResType,
+                     MachineInstr &I, const ExtInstList &ExtInsts) const;
+
   Register buildI32Constant(uint32_t Val, MachineInstr &I,
                             const SPIRVType *ResType = nullptr) const;
 
@@ -283,6 +295,7 @@ bool SPIRVInstructionSelector::spvSelect(Register ResVReg,
   }
   case TargetOpcode::G_MEMMOVE:
   case TargetOpcode::G_MEMCPY:
+  case TargetOpcode::G_MEMSET:
     return selectMemOperation(ResVReg, I);
 
   case TargetOpcode::G_ICMP:
@@ -318,6 +331,85 @@ bool SPIRVInstructionSelector::spvSelect(Register ResVReg,
 
   case TargetOpcode::G_CTPOP:
     return selectUnOp(ResVReg, ResType, I, SPIRV::OpBitCount);
+  case TargetOpcode::G_SMIN:
+    return selectExtInst(ResVReg, ResType, I, CL::s_min, GL::SMin);
+  case TargetOpcode::G_UMIN:
+    return selectExtInst(ResVReg, ResType, I, CL::u_min, GL::UMin);
+
+  case TargetOpcode::G_SMAX:
+    return selectExtInst(ResVReg, ResType, I, CL::s_max, GL::SMax);
+  case TargetOpcode::G_UMAX:
+    return selectExtInst(ResVReg, ResType, I, CL::u_max, GL::UMax);
+
+  case TargetOpcode::G_FMA:
+    return selectExtInst(ResVReg, ResType, I, CL::fma, GL::Fma);
+
+  case TargetOpcode::G_FPOW:
+    return selectExtInst(ResVReg, ResType, I, CL::pow, GL::Pow);
+  case TargetOpcode::G_FPOWI:
+    return selectExtInst(ResVReg, ResType, I, CL::pown);
+
+  case TargetOpcode::G_FEXP:
+    return selectExtInst(ResVReg, ResType, I, CL::exp, GL::Exp);
+  case TargetOpcode::G_FEXP2:
+    return selectExtInst(ResVReg, ResType, I, CL::exp2, GL::Exp2);
+
+  case TargetOpcode::G_FLOG:
+    return selectExtInst(ResVReg, ResType, I, CL::log, GL::Log);
+  case TargetOpcode::G_FLOG2:
+    return selectExtInst(ResVReg, ResType, I, CL::log2, GL::Log2);
+  case TargetOpcode::G_FLOG10:
+    return selectExtInst(ResVReg, ResType, I, CL::log10);
+
+  case TargetOpcode::G_FABS:
+    return selectExtInst(ResVReg, ResType, I, CL::fabs, GL::FAbs);
+  case TargetOpcode::G_ABS:
+    return selectExtInst(ResVReg, ResType, I, CL::s_abs, GL::SAbs);
+
+  case TargetOpcode::G_FMINNUM:
+  case TargetOpcode::G_FMINIMUM:
+    return selectExtInst(ResVReg, ResType, I, CL::fmin, GL::FMin);
+  case TargetOpcode::G_FMAXNUM:
+  case TargetOpcode::G_FMAXIMUM:
+    return selectExtInst(ResVReg, ResType, I, CL::fmax, GL::FMax);
+
+  case TargetOpcode::G_FCOPYSIGN:
+    return selectExtInst(ResVReg, ResType, I, CL::copysign);
+
+  case TargetOpcode::G_FCEIL:
+    return selectExtInst(ResVReg, ResType, I, CL::ceil, GL::Ceil);
+  case TargetOpcode::G_FFLOOR:
+    return selectExtInst(ResVReg, ResType, I, CL::floor, GL::Floor);
+
+  case TargetOpcode::G_FCOS:
+    return selectExtInst(ResVReg, ResType, I, CL::cos, GL::Cos);
+  case TargetOpcode::G_FSIN:
+    return selectExtInst(ResVReg, ResType, I, CL::sin, GL::Sin);
+
+  case TargetOpcode::G_FSQRT:
+    return selectExtInst(ResVReg, ResType, I, CL::sqrt, GL::Sqrt);
+
+  case TargetOpcode::G_CTTZ:
+  case TargetOpcode::G_CTTZ_ZERO_UNDEF:
+    return selectExtInst(ResVReg, ResType, I, CL::ctz);
+  case TargetOpcode::G_CTLZ:
+  case TargetOpcode::G_CTLZ_ZERO_UNDEF:
+    return selectExtInst(ResVReg, ResType, I, CL::clz);
+
+  case TargetOpcode::G_INTRINSIC_ROUND:
+    return selectExtInst(ResVReg, ResType, I, CL::round, GL::Round);
+  case TargetOpcode::G_INTRINSIC_ROUNDEVEN:
+    return selectExtInst(ResVReg, ResType, I, CL::rint, GL::RoundEven);
+  case TargetOpcode::G_INTRINSIC_TRUNC:
+    return selectExtInst(ResVReg, ResType, I, CL::trunc, GL::Trunc);
+  case TargetOpcode::G_FRINT:
+  case TargetOpcode::G_FNEARBYINT:
+    return selectExtInst(ResVReg, ResType, I, CL::rint, GL::RoundEven);
+
+  case TargetOpcode::G_SMULH:
+    return selectExtInst(ResVReg, ResType, I, CL::s_mul_hi);
+  case TargetOpcode::G_UMULH:
+    return selectExtInst(ResVReg, ResType, I, CL::u_mul_hi);
 
   case TargetOpcode::G_SEXT:
     return selectExt(ResVReg, ResType, I, true);
@@ -394,6 +486,48 @@ bool SPIRVInstructionSelector::spvSelect(Register ResVReg,
   }
 }
 
+bool SPIRVInstructionSelector::selectExtInst(Register ResVReg,
+                                             const SPIRVType *ResType,
+                                             MachineInstr &I,
+                                             CL::OpenCLExtInst CLInst) const {
+  return selectExtInst(ResVReg, ResType, I,
+                       {{SPIRV::InstructionSet::OpenCL_std, CLInst}});
+}
+
+bool SPIRVInstructionSelector::selectExtInst(Register ResVReg,
+                                             const SPIRVType *ResType,
+                                             MachineInstr &I,
+                                             CL::OpenCLExtInst CLInst,
+                                             GL::GLSLExtInst GLInst) const {
+  ExtInstList ExtInsts = {{SPIRV::InstructionSet::OpenCL_std, CLInst},
+                          {SPIRV::InstructionSet::GLSL_std_450, GLInst}};
+  return selectExtInst(ResVReg, ResType, I, ExtInsts);
+}
+
+bool SPIRVInstructionSelector::selectExtInst(Register ResVReg,
+                                             const SPIRVType *ResType,
+                                             MachineInstr &I,
+                                             const ExtInstList &Insts) const {
+
+  for (const auto &Ex : Insts) {
+    SPIRV::InstructionSet::InstructionSet Set = Ex.first;
+    uint32_t Opcode = Ex.second;
+    if (STI.canUseExtInstSet(Set)) {
+      MachineBasicBlock &BB = *I.getParent();
+      auto MIB = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpExtInst))
+                     .addDef(ResVReg)
+                     .addUse(GR.getSPIRVTypeID(ResType))
+                     .addImm(static_cast<uint32_t>(Set))
+                     .addImm(Opcode);
+      const unsigned NumOps = I.getNumOperands();
+      for (unsigned i = 1; i < NumOps; ++i)
+        MIB.add(I.getOperand(i));
+      return MIB.constrainAllUses(TII, TRI, RBI);
+    }
+  }
+  return false;
+}
+
 bool SPIRVInstructionSelector::selectUnOpWithSrc(Register ResVReg,
                                                  const SPIRVType *ResType,
                                                  MachineInstr &I,
@@ -493,9 +627,39 @@ bool SPIRVInstructionSelector::selectStore(MachineInstr &I) const {
 bool SPIRVInstructionSelector::selectMemOperation(Register ResVReg,
                                                   MachineInstr &I) const {
   MachineBasicBlock &BB = *I.getParent();
+  Register SrcReg = I.getOperand(1).getReg();
+  if (I.getOpcode() == TargetOpcode::G_MEMSET) {
+    assert(I.getOperand(1).isReg() && I.getOperand(2).isReg());
+    unsigned Val = getIConstVal(I.getOperand(1).getReg(), MRI);
+    unsigned Num = getIConstVal(I.getOperand(2).getReg(), MRI);
+    SPIRVType *ValTy = GR.getOrCreateSPIRVIntegerType(8, I, TII);
+    SPIRVType *ArrTy = GR.getOrCreateSPIRVArrayType(ValTy, Num, I, TII);
+    Register Const = GR.getOrCreateConsIntArray(Val, I, ArrTy, TII);
+    SPIRVType *VarTy = GR.getOrCreateSPIRVPointerType(
+        ArrTy, I, TII, SPIRV::StorageClass::UniformConstant);
+    // TODO: check if we have such GV, add init, use buildGlobalVariable.
+    Type *LLVMArrTy = ArrayType::get(
+        IntegerType::get(GR.CurMF->getFunction().getContext(), 8), Num);
+    GlobalVariable *GV =
+        new GlobalVariable(LLVMArrTy, true, GlobalValue::InternalLinkage);
+    Register VarReg = MRI->createGenericVirtualRegister(LLT::scalar(32));
+    GR.add(GV, GR.CurMF, VarReg);
+
+    buildOpDecorate(VarReg, I, TII, SPIRV::Decoration::Constant, {});
+    BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(SPIRV::OpVariable))
+        .addDef(VarReg)
+        .addUse(GR.getSPIRVTypeID(VarTy))
+        .addImm(SPIRV::StorageClass::UniformConstant)
+        .addUse(Const)
+        .constrainAllUses(TII, TRI, RBI);
+    SPIRVType *SourceTy = GR.getOrCreateSPIRVPointerType(
+        ValTy, I, TII, SPIRV::StorageClass::UniformConstant);
+    SrcReg = MRI->createGenericVirtualRegister(LLT::scalar(32));
+    selectUnOpWithSrc(SrcReg, SourceTy, I, VarReg, SPIRV::OpBitcast);
+  }
   auto MIB = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpCopyMemorySized))
                  .addUse(I.getOperand(0).getReg())
-                 .addUse(I.getOperand(1).getReg())
+                 .addUse(SrcReg)
                  .addUse(I.getOperand(2).getReg());
   if (I.getNumMemOperands())
     addMemoryOperands(*I.memoperands_begin(), MIB);
@@ -974,9 +1138,9 @@ bool SPIRVInstructionSelector::selectExt(Register ResVReg,
 
 bool SPIRVInstructionSelector::selectIntToBool(Register IntReg,
                                                Register ResVReg,
+                                               MachineInstr &I,
                                                const SPIRVType *IntTy,
-                                               const SPIRVType *BoolTy,
-                                               MachineInstr &I) const {
+                                               const SPIRVType *BoolTy) const {
   // To truncate to a bool, we use OpBitwiseAnd 1 and OpINotEqual to zero.
   Register BitIntReg = MRI->createVirtualRegister(&SPIRV::IDRegClass);
   bool IsVectorTy = IntTy->getOpcode() == SPIRV::OpTypeVector;
@@ -1004,7 +1168,7 @@ bool SPIRVInstructionSelector::selectTrunc(Register ResVReg,
   if (GR.isScalarOrVectorOfType(ResVReg, SPIRV::OpTypeBool)) {
     Register IntReg = I.getOperand(1).getReg();
     const SPIRVType *ArgType = GR.getSPIRVTypeForVReg(IntReg);
-    return selectIntToBool(IntReg, ResVReg, ArgType, ResType, I);
+    return selectIntToBool(IntReg, ResVReg, I, ArgType, ResType);
   }
   bool IsSigned = GR.isScalarOrVectorSigned(ResType);
   unsigned Opcode = IsSigned ? SPIRV::OpSConvert : SPIRV::OpUConvert;
@@ -1223,6 +1387,12 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
   case Intrinsic::spv_cmpxchg:
     return selectAtomicCmpXchg(ResVReg, ResType, I);
     break;
+  case Intrinsic::spv_unreachable:
+    BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpUnreachable));
+    break;
+  case Intrinsic::spv_alloca:
+    return selectFrameIndex(ResVReg, ResType, I);
+    break;
   default:
     llvm_unreachable("Intrinsic selection not implemented");
   }

diff  --git a/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp b/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
index 87f9e9545dd39..3d12077cd2c03 100644
--- a/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
@@ -255,6 +255,18 @@ SPIRVLegalizerInfo::SPIRVLegalizerInfo(const SPIRVSubtarget &ST) {
   getActionDefinitionsBuilder(G_FPOWI).legalForCartesianProduct(
       allFloatScalarsAndVectors, allIntScalarsAndVectors);
 
+  if (ST.canUseExtInstSet(SPIRV::InstructionSet::OpenCL_std)) {
+    getActionDefinitionsBuilder(G_FLOG10).legalFor(allFloatScalarsAndVectors);
+
+    getActionDefinitionsBuilder(
+        {G_CTTZ, G_CTTZ_ZERO_UNDEF, G_CTLZ, G_CTLZ_ZERO_UNDEF})
+        .legalForCartesianProduct(allIntScalarsAndVectors,
+                                  allIntScalarsAndVectors);
+
+    // Struct return types become a single scalar, so cannot easily legalize.
+    getActionDefinitionsBuilder({G_SMULH, G_UMULH}).alwaysLegal();
+  }
+
   getLegacyLegalizerInfo().computeTables();
   verify(*ST.getInstrInfo());
 }

diff  --git a/llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td b/llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td
index 347193505a739..1b36c10df15b1 100644
--- a/llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td
+++ b/llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td
@@ -734,7 +734,7 @@ class ImageFormat<string name, bits<32> value> {
 }
 
 multiclass ImageFormatOperand<bits<32> value, list<Capability> reqCapabilities> {
-  def : ImageFormat<NAME, value>;
+  def NAME : ImageFormat<NAME, value>;
   defm : SymbolicOperandWithRequirements<ImageFormatOperand, value, NAME, 0, 0, [], reqCapabilities>;
 }
 

diff  --git a/llvm/test/CodeGen/SPIRV/instructions/intrinsics.ll b/llvm/test/CodeGen/SPIRV/instructions/intrinsics.ll
new file mode 100644
index 0000000000000..04dadc58599c0
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/instructions/intrinsics.ll
@@ -0,0 +1,397 @@
+; RUN: llc %s -mtriple=spirv32-unknown-unknown -o - | FileCheck %s
+
+declare float @llvm.fabs.f32(float)
+declare float @llvm.rint.f32(float)
+declare float @llvm.nearbyint.f32(float)
+declare float @llvm.floor.f32(float)
+declare float @llvm.ceil.f32(float)
+declare float @llvm.round.f32(float)
+declare float @llvm.trunc.f32(float)
+declare float @llvm.sqrt.f32(float)
+declare float @llvm.sin.f32(float)
+declare float @llvm.cos.f32(float)
+declare float @llvm.exp2.f32(float)
+declare float @llvm.log.f32(float)
+declare float @llvm.log10.f32(float)
+declare float @llvm.log2.f32(float)
+declare float @llvm.minnum.f32(float, float)
+declare float @llvm.maxnum.f32(float, float)
+declare <2 x half> @llvm.fabs.v2f16(<2 x half>)
+declare <2 x half> @llvm.rint.v2f16(<2 x half>)
+declare <2 x half> @llvm.nearbyint.v2f16(<2 x half>)
+declare <2 x half> @llvm.floor.v2f16(<2 x half>)
+declare <2 x half> @llvm.ceil.v2f16(<2 x half>)
+declare <2 x half> @llvm.round.v2f16(<2 x half>)
+declare <2 x half> @llvm.trunc.v2f16(<2 x half>)
+declare <2 x half> @llvm.sqrt.v2f16(<2 x half>)
+declare <2 x half> @llvm.sin.v2f16(<2 x half>)
+declare <2 x half> @llvm.cos.v2f16(<2 x half>)
+declare <2 x half> @llvm.exp2.v2f16(<2 x half>)
+declare <2 x half> @llvm.log.v2f16(<2 x half>)
+declare <2 x half> @llvm.log10.v2f16(<2 x half>)
+declare <2 x half> @llvm.log2.v2f16(<2 x half>)
+
+; CHECK-DAG: OpName %[[#SCALAR_FABS:]] "scalar_fabs"
+; CHECK-DAG: OpName %[[#SCALAR_RINT:]] "scalar_rint"
+; CHECK-DAG: OpName %[[#SCALAR_NEARBYINT:]] "scalar_nearbyint"
+; CHECK-DAG: OpName %[[#SCALAR_FLOOR:]] "scalar_floor"
+; CHECK-DAG: OpName %[[#SCALAR_CEIL:]] "scalar_ceil"
+; CHECK-DAG: OpName %[[#SCALAR_ROUND:]] "scalar_round"
+; CHECK-DAG: OpName %[[#SCALAR_TRUNC:]] "scalar_trunc"
+; CHECK-DAG: OpName %[[#SCALAR_SQRT:]] "scalar_sqrt"
+; CHECK-DAG: OpName %[[#SCALAR_SIN:]] "scalar_sin"
+; CHECK-DAG: OpName %[[#SCALAR_COS:]] "scalar_cos"
+; CHECK-DAG: OpName %[[#SCALAR_EXP2:]] "scalar_exp2"
+; CHECK-DAG: OpName %[[#SCALAR_LOG:]] "scalar_log"
+; CHECK-DAG: OpName %[[#SCALAR_LOG10:]] "scalar_log10"
+; CHECK-DAG: OpName %[[#SCALAR_LOG2:]] "scalar_log2"
+; CHECK-DAG: OpName %[[#SCALAR_MINNUM:]] "scalar_minnum"
+; CHECK-DAG: OpName %[[#SCALAR_MAXNUM:]] "scalar_maxnum"
+; CHECK-DAG: OpName %[[#VECTOR_FABS:]] "vector_fabs"
+; CHECK-DAG: OpName %[[#VECTOR_RINT:]] "vector_rint"
+; CHECK-DAG: OpName %[[#VECTOR_NEARBYINT:]] "vector_nearbyint"
+; CHECK-DAG: OpName %[[#VECTOR_FLOOR:]] "vector_floor"
+; CHECK-DAG: OpName %[[#VECTOR_CEIL:]] "vector_ceil"
+; CHECK-DAG: OpName %[[#VECTOR_ROUND:]] "vector_round"
+; CHECK-DAG: OpName %[[#VECTOR_TRUNC:]] "vector_trunc"
+; CHECK-DAG: OpName %[[#VECTOR_SQRT:]] "vector_sqrt"
+; CHECK-DAG: OpName %[[#VECTOR_SIN:]] "vector_sin"
+; CHECK-DAG: OpName %[[#VECTOR_COS:]] "vector_cos"
+; CHECK-DAG: OpName %[[#VECTOR_EXP2:]] "vector_exp2"
+; CHECK-DAG: OpName %[[#VECTOR_LOG:]] "vector_log"
+; CHECK-DAG: OpName %[[#VECTOR_LOG10:]] "vector_log10"
+; CHECK-DAG: OpName %[[#VECTOR_LOG2:]] "vector_log2"
+
+; CHECK-DAG: %[[#CLEXT:]] = OpExtInstImport "OpenCL.std"
+
+; CHECK: %[[#SCALAR_FABS]] = OpFunction
+; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
+; CHECK: OpLabel
+; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] fabs %[[#A]]
+; CHECK: OpReturnValue %[[#R]]
+; CHECK-NEXT: OpFunctionEnd
+define float @scalar_fabs(float %a) {
+    %r = call float @llvm.fabs.f32(float %a)
+    ret float %r
+}
+
+; CHECK: %[[#SCALAR_RINT]] = OpFunction
+; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
+; CHECK: OpLabel
+; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] rint %[[#A]]
+; CHECK: OpReturnValue %[[#R]]
+; CHECK-NEXT: OpFunctionEnd
+define float @scalar_rint(float %a) {
+    %r = call float @llvm.rint.f32(float %a)
+    ret float %r
+}
+
+; CHECK: %[[#SCALAR_NEARBYINT]] = OpFunction
+; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
+; CHECK: OpLabel
+; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] rint %[[#A]]
+; CHECK: OpReturnValue %[[#R]]
+; CHECK-NEXT: OpFunctionEnd
+define float @scalar_nearbyint(float %a) {
+    %r = call float @llvm.nearbyint.f32(float %a)
+    ret float %r
+}
+
+; CHECK: %[[#SCALAR_FLOOR]] = OpFunction
+; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
+; CHECK: OpLabel
+; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] floor %[[#A]]
+; CHECK: OpReturnValue %[[#R]]
+; CHECK-NEXT: OpFunctionEnd
+define float @scalar_floor(float %a) {
+    %r = call float @llvm.floor.f32(float %a)
+    ret float %r
+}
+
+; CHECK: %[[#SCALAR_CEIL]] = OpFunction
+; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
+; CHECK: OpLabel
+; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] ceil %[[#A]]
+; CHECK: OpReturnValue %[[#R]]
+; CHECK-NEXT: OpFunctionEnd
+define float @scalar_ceil(float %a) {
+    %r = call float @llvm.ceil.f32(float %a)
+    ret float %r
+}
+
+; CHECK: %[[#SCALAR_ROUND]] = OpFunction
+; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
+; CHECK: OpLabel
+; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] round %[[#A]]
+; CHECK: OpReturnValue %[[#R]]
+; CHECK-NEXT: OpFunctionEnd
+define float @scalar_round(float %a) {
+    %r = call float @llvm.round.f32(float %a)
+    ret float %r
+}
+
+; CHECK: %[[#SCALAR_TRUNC]] = OpFunction
+; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
+; CHECK: OpLabel
+; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] trunc %[[#A]]
+; CHECK: OpReturnValue %[[#R]]
+; CHECK-NEXT: OpFunctionEnd
+define float @scalar_trunc(float %a) {
+    %r = call float @llvm.trunc.f32(float %a)
+    ret float %r
+}
+
+; CHECK: %[[#SCALAR_SQRT]] = OpFunction
+; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
+; CHECK: OpLabel
+; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] sqrt %[[#A]]
+; CHECK: OpReturnValue %[[#R]]
+; CHECK-NEXT: OpFunctionEnd
+define float @scalar_sqrt(float %a) {
+    %r = call float @llvm.sqrt.f32(float %a)
+    ret float %r
+}
+
+; CHECK: %[[#SCALAR_SIN]] = OpFunction
+; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
+; CHECK: OpLabel
+; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] sin %[[#A]]
+; CHECK: OpReturnValue %[[#R]]
+; CHECK-NEXT: OpFunctionEnd
+define float @scalar_sin(float %a) {
+    %r = call float @llvm.sin.f32(float %a)
+    ret float %r
+}
+
+; CHECK: %[[#SCALAR_COS]] = OpFunction
+; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
+; CHECK: OpLabel
+; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] cos %[[#A]]
+; CHECK: OpReturnValue %[[#R]]
+; CHECK-NEXT: OpFunctionEnd
+define float @scalar_cos(float %a) {
+    %r = call float @llvm.cos.f32(float %a)
+    ret float %r
+}
+
+; CHECK: %[[#SCALAR_EXP2]] = OpFunction
+; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
+; CHECK: OpLabel
+; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] exp2 %[[#A]]
+; CHECK: OpReturnValue %[[#R]]
+; CHECK-NEXT: OpFunctionEnd
+define float @scalar_exp2(float %a) {
+    %r = call float @llvm.exp2.f32(float %a)
+    ret float %r
+}
+
+; CHECK: %[[#SCALAR_LOG]] = OpFunction
+; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
+; CHECK: OpLabel
+; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] log %[[#A]]
+; CHECK: OpReturnValue %[[#R]]
+; CHECK-NEXT: OpFunctionEnd
+define float @scalar_log(float %a) {
+    %r = call float @llvm.log.f32(float %a)
+    ret float %r
+}
+
+; CHECK: %[[#SCALAR_LOG10]] = OpFunction
+; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
+; CHECK: OpLabel
+; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] log10 %[[#A]]
+; CHECK: OpReturnValue %[[#R]]
+; CHECK-NEXT: OpFunctionEnd
+define float @scalar_log10(float %a) {
+    %r = call float @llvm.log10.f32(float %a)
+    ret float %r
+}
+
+; CHECK: %[[#SCALAR_LOG2]] = OpFunction
+; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
+; CHECK: OpLabel
+; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] log2 %[[#A]]
+; CHECK: OpReturnValue %[[#R]]
+; CHECK-NEXT: OpFunctionEnd
+define float @scalar_log2(float %a) {
+    %r = call float @llvm.log2.f32(float %a)
+    ret float %r
+}
+
+; CHECK: %[[#VECTOR_FABS]] = OpFunction
+; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
+; CHECK: OpLabel
+; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] fabs %[[#A]]
+; CHECK: OpReturnValue %[[#R]]
+; CHECK-NEXT: OpFunctionEnd
+define <2 x half> @vector_fabs(<2 x half> %a) {
+    %r = call <2 x half> @llvm.fabs.v2f16(<2 x half> %a)
+    ret <2 x half> %r
+}
+
+; CHECK: %[[#VECTOR_RINT]] = OpFunction
+; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
+; CHECK: OpLabel
+; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] rint %[[#A]]
+; CHECK: OpReturnValue %[[#R]]
+; CHECK-NEXT: OpFunctionEnd
+define <2 x half> @vector_rint(<2 x half> %a) {
+    %r = call <2 x half> @llvm.rint.v2f16(<2 x half> %a)
+    ret <2 x half> %r
+}
+
+; CHECK: %[[#VECTOR_NEARBYINT]] = OpFunction
+; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
+; CHECK: OpLabel
+; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] rint %[[#A]]
+; CHECK: OpReturnValue %[[#R]]
+; CHECK-NEXT: OpFunctionEnd
+define <2 x half> @vector_nearbyint(<2 x half> %a) {
+    %r = call <2 x half> @llvm.nearbyint.v2f16(<2 x half> %a)
+    ret <2 x half> %r
+}
+
+; CHECK: %[[#VECTOR_FLOOR]] = OpFunction
+; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
+; CHECK: OpLabel
+; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] floor %[[#A]]
+; CHECK: OpReturnValue %[[#R]]
+; CHECK-NEXT: OpFunctionEnd
+define <2 x half> @vector_floor(<2 x half> %a) {
+    %r = call <2 x half> @llvm.floor.v2f16(<2 x half> %a)
+    ret <2 x half> %r
+}
+
+; CHECK: %[[#VECTOR_CEIL]] = OpFunction
+; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
+; CHECK: OpLabel
+; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] ceil %[[#A]]
+; CHECK: OpReturnValue %[[#R]]
+; CHECK-NEXT: OpFunctionEnd
+define <2 x half> @vector_ceil(<2 x half> %a) {
+    %r = call <2 x half> @llvm.ceil.v2f16(<2 x half> %a)
+    ret <2 x half> %r
+}
+
+; CHECK: %[[#VECTOR_ROUND]] = OpFunction
+; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
+; CHECK: OpLabel
+; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] round %[[#A]]
+; CHECK: OpReturnValue %[[#R]]
+; CHECK-NEXT: OpFunctionEnd
+define <2 x half> @vector_round(<2 x half> %a) {
+    %r = call <2 x half> @llvm.round.v2f16(<2 x half> %a)
+    ret <2 x half> %r
+}
+
+; CHECK: %[[#VECTOR_TRUNC]] = OpFunction
+; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
+; CHECK: OpLabel
+; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] trunc %[[#A]]
+; CHECK: OpReturnValue %[[#R]]
+; CHECK-NEXT: OpFunctionEnd
+define <2 x half> @vector_trunc(<2 x half> %a) {
+    %r = call <2 x half> @llvm.trunc.v2f16(<2 x half> %a)
+    ret <2 x half> %r
+}
+
+; CHECK: %[[#VECTOR_SQRT]] = OpFunction
+; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
+; CHECK: OpLabel
+; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] sqrt %[[#A]]
+; CHECK: OpReturnValue %[[#R]]
+; CHECK-NEXT: OpFunctionEnd
+define <2 x half> @vector_sqrt(<2 x half> %a) {
+    %r = call <2 x half> @llvm.sqrt.v2f16(<2 x half> %a)
+    ret <2 x half> %r
+}
+
+; CHECK: %[[#VECTOR_SIN]] = OpFunction
+; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
+; CHECK: OpLabel
+; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] sin %[[#A]]
+; CHECK: OpReturnValue %[[#R]]
+; CHECK-NEXT: OpFunctionEnd
+define <2 x half> @vector_sin(<2 x half> %a) {
+    %r = call <2 x half> @llvm.sin.v2f16(<2 x half> %a)
+    ret <2 x half> %r
+}
+
+; CHECK: %[[#VECTOR_COS]] = OpFunction
+; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
+; CHECK: OpLabel
+; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] cos %[[#A]]
+; CHECK: OpReturnValue %[[#R]]
+; CHECK-NEXT: OpFunctionEnd
+define <2 x half> @vector_cos(<2 x half> %a) {
+    %r = call <2 x half> @llvm.cos.v2f16(<2 x half> %a)
+    ret <2 x half> %r
+}
+
+; CHECK: %[[#VECTOR_EXP2]] = OpFunction
+; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
+; CHECK: OpLabel
+; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] exp2 %[[#A]]
+; CHECK: OpReturnValue %[[#R]]
+; CHECK-NEXT: OpFunctionEnd
+define <2 x half> @vector_exp2(<2 x half> %a) {
+    %r = call <2 x half> @llvm.exp2.v2f16(<2 x half> %a)
+    ret <2 x half> %r
+}
+
+; CHECK: %[[#VECTOR_LOG]] = OpFunction
+; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
+; CHECK: OpLabel
+; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] log %[[#A]]
+; CHECK: OpReturnValue %[[#R]]
+; CHECK-NEXT: OpFunctionEnd
+define <2 x half> @vector_log(<2 x half> %a) {
+    %r = call <2 x half> @llvm.log.v2f16(<2 x half> %a)
+    ret <2 x half> %r
+}
+
+; CHECK: %[[#VECTOR_LOG10]] = OpFunction
+; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
+; CHECK: OpLabel
+; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] log10 %[[#A]]
+; CHECK: OpReturnValue %[[#R]]
+; CHECK-NEXT: OpFunctionEnd
+define <2 x half> @vector_log10(<2 x half> %a) {
+    %r = call <2 x half> @llvm.log10.v2f16(<2 x half> %a)
+    ret <2 x half> %r
+}
+
+; CHECK: %[[#VECTOR_LOG2]] = OpFunction
+; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
+; CHECK: OpLabel
+; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] log2 %[[#A]]
+; CHECK: OpReturnValue %[[#R]]
+; CHECK-NEXT: OpFunctionEnd
+define <2 x half> @vector_log2(<2 x half> %a) {
+    %r = call <2 x half> @llvm.log2.v2f16(<2 x half> %a)
+    ret <2 x half> %r
+}
+
+; CHECK: %[[#SCALAR_MINNUM]] = OpFunction
+; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
+; CHECK-NEXT: %[[#B:]] = OpFunctionParameter
+; CHECK: OpLabel
+; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] fmin %[[#A]] %[[#B]]
+; CHECK: OpReturnValue %[[#R]]
+; CHECK-NEXT: OpFunctionEnd
+define float @scalar_minnum(float %A, float %B) {
+  %r = call float @llvm.minnum.f32(float %A, float %B)
+  ret float %r
+}
+
+; CHECK: %[[#SCALAR_MAXNUM]] = OpFunction
+; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
+; CHECK-NEXT: %[[#B:]] = OpFunctionParameter
+; CHECK: OpLabel
+; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] fmax %[[#A]] %[[#B]]
+; CHECK: OpReturnValue %[[#R]]
+; CHECK-NEXT: OpFunctionEnd
+define float @scalar_maxnum(float %A, float %B) {
+  %r = call float @llvm.maxnum.f32(float %A, float %B)
+  ret float %r
+}

diff  --git a/llvm/test/CodeGen/SPIRV/instructions/unreachable.ll b/llvm/test/CodeGen/SPIRV/instructions/unreachable.ll
new file mode 100644
index 0000000000000..0a4538c6de280
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/instructions/unreachable.ll
@@ -0,0 +1,6 @@
+; RUN: llc -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s
+
+; CHECK: OpUnreachable
+define void @test_unreachable() {
+  unreachable
+}

diff  --git a/llvm/test/CodeGen/SPIRV/llvm-intrinsics/fp-intrinsics.ll b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/fp-intrinsics.ll
new file mode 100644
index 0000000000000..8f14e8cf272ba
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/fp-intrinsics.ll
@@ -0,0 +1,339 @@
+; RUN: llc -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s
+
+; CHECK: %[[#extinst_id:]] = OpExtInstImport "OpenCL.std"
+
+; CHECK: %[[#var0:]] = OpTypeFloat 16
+; CHECK: %[[#var1:]] = OpTypeFloat 32
+; CHECK: %[[#var2:]] = OpTypeFloat 64
+; CHECK: %[[#var3:]] = OpTypeVector %[[#var1]] 4
+
+; CHECK: OpFunction
+; CHECK: %[[#]] = OpExtInst %[[#var0]] %[[#extinst_id]] fabs
+; CHECK: OpFunctionEnd
+
+define spir_func half @TestFabs16(half %x) local_unnamed_addr {
+entry:
+  %t = tail call half @llvm.fabs.f16(half %x)
+  ret half %t
+}
+
+; CHECK: OpFunction
+; CHECK: %[[#]] = OpExtInst %[[#var1]] %[[#extinst_id]] fabs
+; CHECK: OpFunctionEnd
+
+define spir_func float @TestFabs32(float %x) local_unnamed_addr {
+entry:
+  %t = tail call float @llvm.fabs.f32(float %x)
+  ret float %t
+}
+
+; CHECK: OpFunction
+; CHECK: %[[#]] = OpExtInst %[[#var2]] %[[#extinst_id]] fabs
+; CHECK: OpFunctionEnd
+
+define spir_func double @TestFabs64(double %x) local_unnamed_addr {
+entry:
+  %t = tail call double @llvm.fabs.f64(double %x)
+  ret double %t
+}
+
+; CHECK: OpFunction
+; CHECK: %[[#]] = OpExtInst %[[#var3]] %[[#extinst_id]] fabs
+; CHECK: OpFunctionEnd
+
+define spir_func <4 x float> @TestFabsVec(<4 x float> %x) local_unnamed_addr {
+entry:
+  %t = tail call <4 x float> @llvm.fabs.v4f32(<4 x float> %x)
+  ret <4 x float> %t
+}
+
+declare half @llvm.fabs.f16(half)
+declare float @llvm.fabs.f32(float)
+declare double @llvm.fabs.f64(double)
+declare <4 x float> @llvm.fabs.v4f32(<4 x float>)
+
+;; We checked several types with fabs, but the type check works the same for
+;; all intrinsics being translated, so for the rest we'll just test one type.
+
+; CHECK: OpFunction
+; CHECK: %[[#]] = OpExtInst %[[#var1]] %[[#extinst_id]] ceil
+; CHECK: OpFunctionEnd
+
+define spir_func float @TestCeil(float %x) local_unnamed_addr {
+entry:
+  %t = tail call float @llvm.ceil.f32(float %x)
+  ret float %t
+}
+
+declare float @llvm.ceil.f32(float)
+
+; CHECK: OpFunction
+; CHECK: %[[#x:]] = OpFunctionParameter %[[#]]
+; CHECK: %[[#n:]] = OpFunctionParameter %[[#]]
+; CHECK: %[[#]] = OpExtInst %[[#var1]] %[[#extinst_id]] pown %[[#x]] %[[#n]]
+; CHECK: OpFunctionEnd
+
+define spir_func float @TestPowi(float %x, i32 %n) local_unnamed_addr {
+entry:
+  %t = tail call float @llvm.powi.f32(float %x, i32 %n)
+  ret float %t
+}
+
+declare float @llvm.powi.f32(float, i32)
+
+; CHECK: OpFunction
+; CHECK: %[[#]] = OpExtInst %[[#var1]] %[[#extinst_id]] sin
+; CHECK: OpFunctionEnd
+
+define spir_func float @TestSin(float %x) local_unnamed_addr {
+entry:
+  %t = tail call float @llvm.sin.f32(float %x)
+  ret float %t
+}
+
+declare float @llvm.sin.f32(float)
+
+; CHECK: OpFunction
+; CHECK: %[[#]] = OpExtInst %[[#var1]] %[[#extinst_id]] cos
+; CHECK: OpFunctionEnd
+
+define spir_func float @TestCos(float %x) local_unnamed_addr {
+entry:
+  %t = tail call float @llvm.cos.f32(float %x)
+  ret float %t
+}
+
+declare float @llvm.cos.f32(float)
+
+; CHECK: OpFunction
+; CHECK: %[[#x:]] = OpFunctionParameter %[[#]]
+; CHECK: %[[#y:]] = OpFunctionParameter %[[#]]
+; CHECK: %[[#]] = OpExtInst %[[#var1]] %[[#extinst_id]] pow %[[#x]] %[[#y]]
+; CHECK: OpFunctionEnd
+
+define spir_func float @TestPow(float %x, float %y) local_unnamed_addr {
+entry:
+  %t = tail call float @llvm.pow.f32(float %x, float %y)
+  ret float %t
+}
+
+declare float @llvm.pow.f32(float, float)
+
+; CHECK: OpFunction
+; CHECK: %[[#]] = OpExtInst %[[#var1]] %[[#extinst_id]] exp
+; CHECK: OpFunctionEnd
+
+define spir_func float @TestExp(float %x) local_unnamed_addr {
+entry:
+  %t = tail call float @llvm.exp.f32(float %x)
+  ret float %t
+}
+
+declare float @llvm.exp.f32(float)
+
+; CHECK: OpFunction
+; CHECK: %[[#]] = OpExtInst %[[#var1]] %[[#extinst_id]] exp2
+; CHECK: OpFunctionEnd
+
+define spir_func float @TestExp2(float %x) local_unnamed_addr {
+entry:
+  %t = tail call float @llvm.exp2.f32(float %x)
+  ret float %t
+}
+
+declare float @llvm.exp2.f32(float)
+
+; CHECK: OpFunction
+; CHECK: %[[#]] = OpExtInst %[[#var1]] %[[#extinst_id]] log
+; CHECK: OpFunctionEnd
+
+define spir_func float @TestLog(float %x) local_unnamed_addr {
+entry:
+  %t = tail call float @llvm.log.f32(float %x)
+  ret float %t
+}
+
+declare float @llvm.log.f32(float)
+
+; CHECK: OpFunction
+; CHECK: %[[#]] = OpExtInst %[[#var1]] %[[#extinst_id]] log10
+; CHECK: OpFunctionEnd
+
+define spir_func float @TestLog10(float %x) local_unnamed_addr {
+entry:
+  %t = tail call float @llvm.log10.f32(float %x)
+  ret float %t
+}
+
+declare float @llvm.log10.f32(float)
+
+; CHECK: OpFunction
+; CHECK: %[[#]] = OpExtInst %[[#var1]] %[[#extinst_id]] log2
+; CHECK: OpFunctionEnd
+
+define spir_func float @TestLog2(float %x) local_unnamed_addr {
+entry:
+  %t = tail call float @llvm.log2.f32(float %x)
+  ret float %t
+}
+
+declare float @llvm.log2.f32(float)
+
+; CHECK: OpFunction
+; CHECK: %[[#x:]] = OpFunctionParameter %[[#]]
+; CHECK: %[[#y:]] = OpFunctionParameter %[[#]]
+; CHECK: %[[#res:]] = OpExtInst %[[#]] %[[#]] fmin %[[#x]] %[[#y]]
+; CHECK: OpReturnValue %[[#res]]
+
+define spir_func float @TestMinNum(float %x, float %y) {
+entry:
+  %t = call float @llvm.minnum.f32(float %x, float %y)
+  ret float %t
+}
+
+declare float @llvm.minnum.f32(float, float)
+
+; CHECK: OpFunction
+; CHECK: %[[#x:]] = OpFunctionParameter %[[#]]
+; CHECK: %[[#y:]] = OpFunctionParameter %[[#]]
+; CHECK: %[[#res:]] = OpExtInst %[[#]] %[[#]] fmax %[[#x]] %[[#y]]
+; CHECK: OpReturnValue %[[#res]]
+
+define spir_func float @TestMaxNum(float %x, float %y) {
+entry:
+  %t = call float @llvm.maxnum.f32(float %x, float %y)
+  ret float %t
+}
+
+declare float @llvm.maxnum.f32(float, float)
+
+; CHECK: OpFunction
+; CHECK: %[[#x:]] = OpFunctionParameter %[[#]]
+; CHECK: %[[#y:]] = OpFunctionParameter %[[#]]
+; CHECK: %[[#res:]] = OpExtInst %[[#]] %[[#]] fmin %[[#x]] %[[#y]]
+; CHECK: OpReturnValue %[[#res]]
+
+define spir_func float @TestMinimum(float %x, float %y) {
+entry:
+  %t = call float @llvm.minimum.f32(float %x, float %y)
+  ret float %t
+}
+
+declare float @llvm.minimum.f32(float, float)
+
+; CHECK: OpFunction
+; CHECK: %[[#x:]] = OpFunctionParameter %[[#]]
+; CHECK: %[[#y:]] = OpFunctionParameter %[[#]]
+; CHECK: %[[#res:]] = OpExtInst %[[#]] %[[#]] fmax %[[#x]] %[[#y]]
+; CHECK: OpReturnValue %[[#res]]
+
+define spir_func float @TestMaximum(float %x, float %y) {
+entry:
+  %t = call float @llvm.maximum.f32(float %x, float %y)
+  ret float %t
+}
+
+declare float @llvm.maximum.f32(float, float)
+
+; CHECK: OpFunction
+; CHECK: %[[#x:]] = OpFunctionParameter %[[#]]
+; CHECK: %[[#y:]] = OpFunctionParameter %[[#]]
+; CHECK: %[[#]] = OpExtInst %[[#var1]] %[[#extinst_id]] copysign %[[#x]] %[[#y]]
+; CHECK: OpFunctionEnd
+
+define spir_func float @TestCopysign(float %x, float %y) local_unnamed_addr {
+entry:
+  %t = tail call float @llvm.copysign.f32(float %x, float %y)
+  ret float %t
+}
+
+declare float @llvm.copysign.f32(float, float)
+
+; CHECK: OpFunction
+; CHECK: %[[#]] = OpExtInst %[[#var1]] %[[#extinst_id]] floor
+; CHECK: OpFunctionEnd
+
+define spir_func float @TestFloor(float %x) local_unnamed_addr {
+entry:
+  %t = tail call float @llvm.floor.f32(float %x)
+  ret float %t
+}
+
+declare float @llvm.floor.f32(float)
+
+; CHECK: OpFunction
+; CHECK: %[[#]] = OpExtInst %[[#var1]] %[[#extinst_id]] trunc
+; CHECK: OpFunctionEnd
+
+define spir_func float @TestTrunc(float %x) local_unnamed_addr {
+entry:
+  %t = tail call float @llvm.trunc.f32(float %x)
+  ret float %t
+}
+
+declare float @llvm.trunc.f32(float)
+
+; CHECK: OpFunction
+; CHECK: %[[#]] = OpExtInst %[[#var1]] %[[#extinst_id]] rint
+; CHECK: OpFunctionEnd
+
+define spir_func float @TestRint(float %x) local_unnamed_addr {
+entry:
+  %t = tail call float @llvm.rint.f32(float %x)
+  ret float %t
+}
+
+declare float @llvm.rint.f32(float)
+
+;; It is intentional that nearbyint translates to rint.
+; CHECK: OpFunction
+; CHECK: %[[#]] = OpExtInst %[[#var1]] %[[#extinst_id]] rint
+; CHECK: OpFunctionEnd
+
+define spir_func float @TestNearbyint(float %x) local_unnamed_addr {
+entry:
+  %t = tail call float @llvm.nearbyint.f32(float %x)
+  ret float %t
+}
+
+declare float @llvm.nearbyint.f32(float)
+
+; CHECK: OpFunction
+; CHECK: %[[#]] = OpExtInst %[[#var1]] %[[#extinst_id]] round
+; CHECK: OpFunctionEnd
+
+define spir_func float @TestRound(float %x) local_unnamed_addr {
+entry:
+  %t = tail call float @llvm.round.f32(float %x)
+  ret float %t
+}
+
+declare float @llvm.round.f32(float)
+
+;; It is intentional that roundeven translates to rint.
+; CHECK: OpFunction
+; CHECK: %[[#]] = OpExtInst %[[#var1]] %[[#extinst_id]] rint
+; CHECK: OpFunctionEnd
+
+define spir_func float @TestRoundEven(float %x) local_unnamed_addr {
+entry:
+  %t = tail call float @llvm.roundeven.f32(float %x)
+  ret float %t
+}
+
+declare float @llvm.roundeven.f32(float)
+
+; CHECK: OpFunction
+; CHECK: %[[#x:]] = OpFunctionParameter %[[#]]
+; CHECK: %[[#y:]] = OpFunctionParameter %[[#]]
+; CHECK: %[[#z:]] = OpFunctionParameter %[[#]]
+; CHECK: %[[#]] = OpExtInst %[[#var1]] %[[#extinst_id]] fma %[[#x]] %[[#y]] %[[#z]]
+; CHECK: OpFunctionEnd
+
+define spir_func float @TestFma(float %x, float %y, float %z) {
+entry:
+  %t = tail call float @llvm.fma.f32(float %x, float %y, float %z)
+  ret float %t
+}
+
+declare float @llvm.fma.f32(float, float, float)

diff  --git a/llvm/test/CodeGen/SPIRV/llvm-intrinsics/maxnum.ll b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/maxnum.ll
new file mode 100644
index 0000000000000..7b8c05d9c98ed
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/maxnum.ll
@@ -0,0 +1,15 @@
+; RUN: llc -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s
+
+define spir_func float @Test(float %x, float %y) {
+entry:
+  %0 = call float @llvm.maxnum.f32(float %x, float %y)
+  ret float %0
+}
+
+; CHECK: OpFunction
+; CHECK: %[[#x:]] = OpFunctionParameter %[[#]]
+; CHECK: %[[#y:]] = OpFunctionParameter %[[#]]
+; CHECK: %[[#res:]] = OpExtInst %[[#]] %[[#]] fmax %[[#x]] %[[#y]]
+; CHECK: OpReturnValue %[[#res]]
+
+declare float @llvm.maxnum.f32(float, float)

diff  --git a/llvm/test/CodeGen/SPIRV/opencl/image.ll b/llvm/test/CodeGen/SPIRV/opencl/image.ll
new file mode 100644
index 0000000000000..43fcda71e8300
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/opencl/image.ll
@@ -0,0 +1,54 @@
+; RUN: llc -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s
+
+;; FIXME: Write tests to ensure invalid usage of image are rejected, such as:
+;;  - invalid AS (only global is allowed);
+;;  - used in struct, union, array, pointer or return types;
+;;  - used with invalid CV-qualifiers (const or volatile in C99).
+;; FIXME: Write further tests to cover _array, _buffer, _depth, ... types.
+
+%opencl.image1d_ro_t = type opaque ;; read_only image1d_t
+%opencl.image2d_wo_t = type opaque ;; write_only image2d_t
+%opencl.image3d_rw_t = type opaque ;; read_write image3d_t
+
+define void @foo(
+  %opencl.image1d_ro_t addrspace(1)* %a,
+  %opencl.image2d_wo_t addrspace(1)* %b,
+  %opencl.image3d_rw_t addrspace(1)* %c,
+  i32 addrspace(1)* %d
+) {
+  %pixel = call <4 x i32> @_Z11read_imagei14ocl_image1d_roi(%opencl.image1d_ro_t addrspace(1)* %a, i32 0)
+  call void @_Z12write_imagei14ocl_image2d_woDv2_iDv4_i(%opencl.image2d_wo_t addrspace(1)* %b, <2 x i32> zeroinitializer, <4 x i32> %pixel)
+  %size = call i32 @_Z15get_image_width14ocl_image3d_rw(%opencl.image3d_rw_t addrspace(1)* %c)
+  store i32 %size, i32 addrspace(1)* %d
+  ret void
+}
+
+declare <4 x i32> @_Z11read_imagei14ocl_image1d_roi(%opencl.image1d_ro_t addrspace(1)*, i32)
+
+declare void @_Z12write_imagei14ocl_image2d_woDv2_iDv4_i(%opencl.image2d_wo_t addrspace(1)*, <2 x i32>, <4 x i32>)
+
+declare i32 @_Z15get_image_width14ocl_image3d_rw(%opencl.image3d_rw_t addrspace(1)*)
+
+
+;; Capabilities:
+; CHECK-DAG: OpCapability ImageReadWrite
+; CHECK-NOT: DAG-FENCE
+
+;; Types, Constants and Variables:
+;; FIXME: The values should be double checked here.
+; CHECK-DAG: %[[#IMG_1D:]] = OpTypeImage %[[#VOID:]] 1D 0 0 0 0 Unknown ReadOnly
+; CHECK-DAG: %[[#IMG_2D:]] = OpTypeImage %[[#VOID]] 2D 0 0 0 0 Unknown WriteOnly
+; CHECK-DAG: %[[#IMG_3D:]] = OpTypeImage %[[#VOID]] 3D 0 0 0 0 Unknown ReadWrite
+; CHECK-DAG: %[[#PTR:]] = OpTypePointer CrossWorkgroup %[[#I32:]]
+; CHECK-DAG: %[[#FN:]] = OpTypeFunction %[[#VOID]] %[[#IMG_1D]] %[[#IMG_2D]] %[[#IMG_3D]] %[[#PTR]]
+
+;; Functions:
+; CHECK: OpFunction %[[#VOID]] None %[[#FN]]
+; CHECK: %[[#A:]] = OpFunctionParameter %[[#IMG_1D]]
+; CHECK: %[[#B:]] = OpFunctionParameter %[[#IMG_2D]]
+; CHECK: %[[#C:]] = OpFunctionParameter %[[#IMG_3D]]
+; CHECK: %[[#D:]] = OpFunctionParameter %[[#PTR]]
+; CHECK: %[[#PIXEL:]] = OpImageRead %[[#VEC:]] %[[#A]] %[[#]]
+; CHECK: OpImageWrite %[[#B]] %[[#]] %[[#PIXEL]]
+;; FIXME: It is unclear which of OpImageQuerySize and OpImageQuerySizeLod should be used.
+; CHECK: %[[#SIZE:]] = OpImageQuerySize{{(Lod)?}} %[[#]] %[[#C]]


        


More information about the llvm-commits mailing list