[llvm] df87130 - [SPIRV] support capabilities and extensions

Ilia Diachkov via llvm-commits llvm-commits at lists.llvm.org
Fri Aug 12 12:51:11 PDT 2022


Author: Ilia Diachkov
Date: 2022-08-12T23:33:15+03:00
New Revision: df8713079bf59f4aa78c5c0b421c10a51bb984ba

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

LOG: [SPIRV] support capabilities and extensions

This patch supports SPIR-V capabilities and extensions. In addition,
it inserts decorations related to MIFlags and improves support of switches.
Five tests are included to demonstrate the improvement.

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

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/constant/local-float-point-constants.ll
    llvm/test/CodeGen/SPIRV/constant/local-integers-constants.ll
    llvm/test/CodeGen/SPIRV/extensions/no_wrap.ll
    llvm/test/CodeGen/SPIRV/transcoding/ReqdSubgroupSize.ll
    llvm/test/CodeGen/SPIRV/transcoding/fadd.ll

Modified: 
    llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp
    llvm/lib/Target/SPIRV/SPIRVInstrInfo.cpp
    llvm/lib/Target/SPIRV/SPIRVInstrInfo.h
    llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
    llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.h
    llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp
    llvm/lib/Target/SPIRV/SPIRVSubtarget.cpp
    llvm/lib/Target/SPIRV/SPIRVSubtarget.h

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp b/llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp
index 759891c52a0fd..4e5cd21e2ad85 100644
--- a/llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp
@@ -57,6 +57,7 @@ class SPIRVAsmPrinter : public AsmPrinter {
   void outputMCInst(MCInst &Inst);
   void outputInstruction(const MachineInstr *MI);
   void outputModuleSection(SPIRV::ModuleSectionType MSType);
+  void outputGlobalRequirements();
   void outputEntryPoints();
   void outputDebugSourceAndStrings(const Module &M);
   void outputOpExtInstImports(const Module &M);
@@ -318,6 +319,30 @@ void SPIRVAsmPrinter::outputEntryPoints() {
   }
 }
 
+// Create global OpCapability instructions for the required capabilities.
+void SPIRVAsmPrinter::outputGlobalRequirements() {
+  // Abort here if not all requirements can be satisfied.
+  MAI->Reqs.checkSatisfiable(*ST);
+
+  for (const auto &Cap : MAI->Reqs.getMinimalCapabilities()) {
+    MCInst Inst;
+    Inst.setOpcode(SPIRV::OpCapability);
+    Inst.addOperand(MCOperand::createImm(Cap));
+    outputMCInst(Inst);
+  }
+
+  // Generate the final OpExtensions with strings instead of enums.
+  for (const auto &Ext : MAI->Reqs.getExtensions()) {
+    MCInst Inst;
+    Inst.setOpcode(SPIRV::OpExtension);
+    addStringImm(getSymbolicOperandMnemonic(
+                     SPIRV::OperandCategory::ExtensionOperand, Ext),
+                 Inst);
+    outputMCInst(Inst);
+  }
+  // TODO add a pseudo instr for version number.
+}
+
 void SPIRVAsmPrinter::outputExtFuncDecls() {
   // Insert OpFunctionEnd after each declaration.
   SmallVectorImpl<MachineInstr *>::iterator
@@ -467,8 +492,8 @@ void SPIRVAsmPrinter::outputModuleSections() {
   MAI = &SPIRVModuleAnalysis::MAI;
   assert(ST && TII && MAI && M && "Module analysis is required");
   // Output instructions according to the Logical Layout of a Module:
-  // TODO: 1,2. All OpCapability instructions, then optional OpExtension
-  // instructions.
+  // 1,2. All OpCapability instructions, then optional OpExtension instructions.
+  outputGlobalRequirements();
   // 3. Optional OpExtInstImport instructions.
   outputOpExtInstImports(*M);
   // 4. The single required OpMemoryModel instruction.

diff  --git a/llvm/lib/Target/SPIRV/SPIRVInstrInfo.cpp b/llvm/lib/Target/SPIRV/SPIRVInstrInfo.cpp
index 66d8b17b42963..42317453a2370 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstrInfo.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstrInfo.cpp
@@ -91,6 +91,56 @@ bool SPIRVInstrInfo::isHeaderInstr(const MachineInstr &MI) const {
   }
 }
 
+bool SPIRVInstrInfo::canUseFastMathFlags(const MachineInstr &MI) const {
+  switch (MI.getOpcode()) {
+  case SPIRV::OpFAddS:
+  case SPIRV::OpFSubS:
+  case SPIRV::OpFMulS:
+  case SPIRV::OpFDivS:
+  case SPIRV::OpFRemS:
+  case SPIRV::OpFAddV:
+  case SPIRV::OpFSubV:
+  case SPIRV::OpFMulV:
+  case SPIRV::OpFDivV:
+  case SPIRV::OpFRemV:
+  case SPIRV::OpFMod:
+    return true;
+  default:
+    return false;
+  }
+}
+
+bool SPIRVInstrInfo::canUseNSW(const MachineInstr &MI) const {
+  switch (MI.getOpcode()) {
+  case SPIRV::OpIAddS:
+  case SPIRV::OpIAddV:
+  case SPIRV::OpISubS:
+  case SPIRV::OpISubV:
+  case SPIRV::OpIMulS:
+  case SPIRV::OpIMulV:
+  case SPIRV::OpShiftLeftLogicalS:
+  case SPIRV::OpShiftLeftLogicalV:
+  case SPIRV::OpSNegate:
+    return true;
+  default:
+    return false;
+  }
+}
+
+bool SPIRVInstrInfo::canUseNUW(const MachineInstr &MI) const {
+  switch (MI.getOpcode()) {
+  case SPIRV::OpIAddS:
+  case SPIRV::OpIAddV:
+  case SPIRV::OpISubS:
+  case SPIRV::OpISubV:
+  case SPIRV::OpIMulS:
+  case SPIRV::OpIMulV:
+    return true;
+  default:
+    return false;
+  }
+}
+
 // Analyze the branching code at the end of MBB, returning
 // true if it cannot be understood (e.g. it's a switch dispatch or isn't
 // implemented for a target).  Upon success, this returns false and returns

diff  --git a/llvm/lib/Target/SPIRV/SPIRVInstrInfo.h b/llvm/lib/Target/SPIRV/SPIRVInstrInfo.h
index 334351c8eeaed..c01e30e109bd5 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstrInfo.h
+++ b/llvm/lib/Target/SPIRV/SPIRVInstrInfo.h
@@ -32,6 +32,9 @@ class SPIRVInstrInfo : public SPIRVGenInstrInfo {
   bool isConstantInstr(const MachineInstr &MI) const;
   bool isTypeDeclInstr(const MachineInstr &MI) const;
   bool isDecorationInstr(const MachineInstr &MI) const;
+  bool canUseFastMathFlags(const MachineInstr &MI) const;
+  bool canUseNSW(const MachineInstr &MI) const;
+  bool canUseNUW(const MachineInstr &MI) const;
 
   bool analyzeBranch(MachineBasicBlock &MBB, MachineBasicBlock *&TBB,
                      MachineBasicBlock *&FBB,

diff  --git a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
index 45a327f7d78e1..7e7183ef396f7 100644
--- a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
@@ -16,7 +16,6 @@
 
 #include "SPIRVModuleAnalysis.h"
 #include "SPIRV.h"
-#include "SPIRVGlobalRegistry.h"
 #include "SPIRVSubtarget.h"
 #include "SPIRVTargetMachine.h"
 #include "SPIRVUtils.h"
@@ -52,6 +51,41 @@ static unsigned getMetadataUInt(MDNode *MdNode, unsigned OpIndex,
   return DefaultVal;
 }
 
+static SPIRV::Requirements
+getSymbolicOperandRequirements(SPIRV::OperandCategory::OperandCategory Category,
+                               unsigned i, const SPIRVSubtarget &ST,
+                               SPIRV::RequirementHandler &Reqs) {
+  unsigned ReqMinVer = getSymbolicOperandMinVersion(Category, i);
+  unsigned ReqMaxVer = getSymbolicOperandMaxVersion(Category, i);
+  unsigned TargetVer = ST.getSPIRVVersion();
+  bool MinVerOK = !ReqMinVer || !TargetVer || TargetVer >= ReqMinVer;
+  bool MaxVerOK = !ReqMaxVer || !TargetVer || TargetVer <= ReqMaxVer;
+  CapabilityList ReqCaps = getSymbolicOperandCapabilities(Category, i);
+  ExtensionList ReqExts = getSymbolicOperandExtensions(Category, i);
+  if (ReqCaps.empty()) {
+    if (ReqExts.empty()) {
+      if (MinVerOK && MaxVerOK)
+        return {true, {}, {}, ReqMinVer, ReqMaxVer};
+      return {false, {}, {}, 0, 0};
+    }
+  } else if (MinVerOK && MaxVerOK) {
+    for (auto Cap : ReqCaps) { // Only need 1 of the capabilities to work.
+      if (Reqs.isCapabilityAvailable(Cap))
+        return {true, {Cap}, {}, ReqMinVer, ReqMaxVer};
+    }
+  }
+  // If there are no capabilities, or we can't satisfy the version or
+  // capability requirements, use the list of extensions (if the subtarget
+  // can handle them all).
+  if (std::all_of(ReqExts.begin(), ReqExts.end(),
+                  [&ST](const SPIRV::Extension::Extension &Ext) {
+                    return ST.canUseExtension(Ext);
+                  })) {
+    return {true, {}, ReqExts, 0, 0}; // TODO: add versions to extensions.
+  }
+  return {false, {}, {}, 0, 0};
+}
+
 void SPIRVModuleAnalysis::setBaseInfo(const Module &M) {
   MAI.MaxID = 0;
   for (int i = 0; i < SPIRV::NUM_MODULE_SECTIONS; i++)
@@ -61,6 +95,8 @@ void SPIRVModuleAnalysis::setBaseInfo(const Module &M) {
   MAI.FuncNameMap.clear();
   MAI.GlobalVarList.clear();
   MAI.ExtInstSetMap.clear();
+  MAI.Reqs.clear();
+  MAI.Reqs.initAvailableCapabilities(*ST);
 
   // TODO: determine memory model and source language from the configuratoin.
   if (auto MemModel = M.getNamedMetadata("spirv.MemoryModel")) {
@@ -103,6 +139,15 @@ void SPIRVModuleAnalysis::setBaseInfo(const Module &M) {
     }
   }
 
+  // Update required capabilities for this memory model, addressing model and
+  // source language.
+  MAI.Reqs.getAndAddRequirements(SPIRV::OperandCategory::MemoryModelOperand,
+                                 MAI.Mem, *ST);
+  MAI.Reqs.getAndAddRequirements(SPIRV::OperandCategory::SourceLanguageOperand,
+                                 MAI.SrcLang, *ST);
+  MAI.Reqs.getAndAddRequirements(SPIRV::OperandCategory::AddressingModelOperand,
+                                 MAI.Addr, *ST);
+
   // TODO: check if it's required by default.
   MAI.ExtInstSetMap[static_cast<unsigned>(SPIRV::InstructionSet::OpenCL_std)] =
       Register::index2VirtReg(MAI.getNextID());
@@ -180,6 +225,27 @@ void SPIRVModuleAnalysis::processDefInstrs(const Module &M) {
       DepsGraph, SPIRV::MB_TypeConstVars,
       [](const SPIRV::DTSortableEntry *E) { return !E->getIsFunc(); });
 
+  for (auto F = M.begin(), E = M.end(); F != E; ++F) {
+    MachineFunction *MF = MMI->getMachineFunction(*F);
+    if (!MF)
+      continue;
+    // Iterate through and collect OpExtension/OpCapability instructions.
+    for (MachineBasicBlock &MBB : *MF) {
+      for (MachineInstr &MI : MBB) {
+        if (MI.getOpcode() == SPIRV::OpExtension) {
+          // Here, OpExtension just has a single enum operand, not a string.
+          auto Ext = SPIRV::Extension::Extension(MI.getOperand(0).getImm());
+          MAI.Reqs.addExtension(Ext);
+          MAI.setSkipEmission(&MI);
+        } else if (MI.getOpcode() == SPIRV::OpCapability) {
+          auto Cap = SPIRV::Capability::Capability(MI.getOperand(0).getImm());
+          MAI.Reqs.addCapability(Cap);
+          MAI.setSkipEmission(&MI);
+        }
+      }
+    }
+  }
+
   collectGlobalEntities(
       DepsGraph, SPIRV::MB_ExtFuncDecls,
       [](const SPIRV::DTSortableEntry *E) { return E->getIsFunc(); }, true);
@@ -339,7 +405,14 @@ static void processSwitches(const Module &M, SPIRV::ModuleAnalysisInfo &MAI,
           assert(MI.getOperand(0).isReg());
           SwitchRegs.insert(MI.getOperand(0).getReg());
         }
-        if (MI.getOpcode() != SPIRV::OpIEqual || !MI.getOperand(2).isReg() ||
+        if (MI.getOpcode() == SPIRV::OpISubS &&
+            SwitchRegs.contains(MI.getOperand(2).getReg())) {
+          SwitchRegs.insert(MI.getOperand(0).getReg());
+          MAI.setSkipEmission(&MI);
+        }
+        if ((MI.getOpcode() != SPIRV::OpIEqual &&
+             MI.getOpcode() != SPIRV::OpULessThanEqual) ||
+            !MI.getOperand(2).isReg() ||
             !SwitchRegs.contains(MI.getOperand(2).getReg()))
           continue;
         Register CmpReg = MI.getOperand(0).getReg();
@@ -355,6 +428,576 @@ static void processSwitches(const Module &M, SPIRV::ModuleAnalysisInfo &MAI,
   }
 }
 
+// RequirementHandler implementations.
+void SPIRV::RequirementHandler::getAndAddRequirements(
+    SPIRV::OperandCategory::OperandCategory Category, uint32_t i,
+    const SPIRVSubtarget &ST) {
+  addRequirements(getSymbolicOperandRequirements(Category, i, ST, *this));
+}
+
+void SPIRV::RequirementHandler::pruneCapabilities(
+    const CapabilityList &ToPrune) {
+  for (const auto &Cap : ToPrune) {
+    AllCaps.insert(Cap);
+    auto FoundIndex = std::find(MinimalCaps.begin(), MinimalCaps.end(), Cap);
+    if (FoundIndex != MinimalCaps.end())
+      MinimalCaps.erase(FoundIndex);
+    CapabilityList ImplicitDecls =
+        getSymbolicOperandCapabilities(OperandCategory::CapabilityOperand, Cap);
+    pruneCapabilities(ImplicitDecls);
+  }
+}
+
+void SPIRV::RequirementHandler::addCapabilities(const CapabilityList &ToAdd) {
+  for (const auto &Cap : ToAdd) {
+    bool IsNewlyInserted = AllCaps.insert(Cap).second;
+    if (!IsNewlyInserted) // Don't re-add if it's already been declared.
+      continue;
+    CapabilityList ImplicitDecls =
+        getSymbolicOperandCapabilities(OperandCategory::CapabilityOperand, Cap);
+    pruneCapabilities(ImplicitDecls);
+    MinimalCaps.push_back(Cap);
+  }
+}
+
+void SPIRV::RequirementHandler::addRequirements(
+    const SPIRV::Requirements &Req) {
+  if (!Req.IsSatisfiable)
+    report_fatal_error("Adding SPIR-V requirements this target can't satisfy.");
+
+  if (Req.Cap.hasValue())
+    addCapabilities({Req.Cap.getValue()});
+
+  addExtensions(Req.Exts);
+
+  if (Req.MinVer) {
+    if (MaxVersion && Req.MinVer > MaxVersion) {
+      LLVM_DEBUG(dbgs() << "Conflicting version requirements: >= " << Req.MinVer
+                        << " and <= " << MaxVersion << "\n");
+      report_fatal_error("Adding SPIR-V requirements that can't be satisfied.");
+    }
+
+    if (MinVersion == 0 || Req.MinVer > MinVersion)
+      MinVersion = Req.MinVer;
+  }
+
+  if (Req.MaxVer) {
+    if (MinVersion && Req.MaxVer < MinVersion) {
+      LLVM_DEBUG(dbgs() << "Conflicting version requirements: <= " << Req.MaxVer
+                        << " and >= " << MinVersion << "\n");
+      report_fatal_error("Adding SPIR-V requirements that can't be satisfied.");
+    }
+
+    if (MaxVersion == 0 || Req.MaxVer < MaxVersion)
+      MaxVersion = Req.MaxVer;
+  }
+}
+
+void SPIRV::RequirementHandler::checkSatisfiable(
+    const SPIRVSubtarget &ST) const {
+  // Report as many errors as possible before aborting the compilation.
+  bool IsSatisfiable = true;
+  auto TargetVer = ST.getSPIRVVersion();
+
+  if (MaxVersion && TargetVer && MaxVersion < TargetVer) {
+    LLVM_DEBUG(
+        dbgs() << "Target SPIR-V version too high for required features\n"
+               << "Required max version: " << MaxVersion << " target version "
+               << TargetVer << "\n");
+    IsSatisfiable = false;
+  }
+
+  if (MinVersion && TargetVer && MinVersion > TargetVer) {
+    LLVM_DEBUG(dbgs() << "Target SPIR-V version too low for required features\n"
+                      << "Required min version: " << MinVersion
+                      << " target version " << TargetVer << "\n");
+    IsSatisfiable = false;
+  }
+
+  if (MinVersion && MaxVersion && MinVersion > MaxVersion) {
+    LLVM_DEBUG(
+        dbgs()
+        << "Version is too low for some features and too high for others.\n"
+        << "Required SPIR-V min version: " << MinVersion
+        << " required SPIR-V max version " << MaxVersion << "\n");
+    IsSatisfiable = false;
+  }
+
+  for (auto Cap : MinimalCaps) {
+    if (AvailableCaps.contains(Cap))
+      continue;
+    LLVM_DEBUG(dbgs() << "Capability not supported: "
+                      << getSymbolicOperandMnemonic(
+                             OperandCategory::CapabilityOperand, Cap)
+                      << "\n");
+    IsSatisfiable = false;
+  }
+
+  for (auto Ext : AllExtensions) {
+    if (ST.canUseExtension(Ext))
+      continue;
+    LLVM_DEBUG(dbgs() << "Extension not suported: "
+                      << getSymbolicOperandMnemonic(
+                             OperandCategory::ExtensionOperand, Ext)
+                      << "\n");
+    IsSatisfiable = false;
+  }
+
+  if (!IsSatisfiable)
+    report_fatal_error("Unable to meet SPIR-V requirements for this target.");
+}
+
+// Add the given capabilities and all their implicitly defined capabilities too.
+void SPIRV::RequirementHandler::addAvailableCaps(const CapabilityList &ToAdd) {
+  for (const auto Cap : ToAdd)
+    if (AvailableCaps.insert(Cap).second)
+      addAvailableCaps(getSymbolicOperandCapabilities(
+          SPIRV::OperandCategory::CapabilityOperand, Cap));
+}
+
+namespace llvm {
+namespace SPIRV {
+void RequirementHandler::initAvailableCapabilities(const SPIRVSubtarget &ST) {
+  // TODO: Implemented for other targets other then OpenCL.
+  if (!ST.isOpenCLEnv())
+    return;
+  // Add the min requirements for 
diff erent OpenCL and SPIR-V versions.
+  addAvailableCaps({Capability::Addresses, Capability::Float16Buffer,
+                    Capability::Int16, Capability::Int8, Capability::Kernel,
+                    Capability::Linkage, Capability::Vector16,
+                    Capability::Groups, Capability::GenericPointer,
+                    Capability::Shader});
+  if (ST.hasOpenCLFullProfile())
+    addAvailableCaps({Capability::Int64, Capability::Int64Atomics});
+  if (ST.hasOpenCLImageSupport()) {
+    addAvailableCaps({Capability::ImageBasic, Capability::LiteralSampler,
+                      Capability::Image1D, Capability::SampledBuffer,
+                      Capability::ImageBuffer});
+    if (ST.isAtLeastOpenCLVer(20))
+      addAvailableCaps({Capability::ImageReadWrite});
+  }
+  if (ST.isAtLeastSPIRVVer(11) && ST.isAtLeastOpenCLVer(22))
+    addAvailableCaps({Capability::SubgroupDispatch, Capability::PipeStorage});
+  if (ST.isAtLeastSPIRVVer(13))
+    addAvailableCaps({Capability::GroupNonUniform,
+                      Capability::GroupNonUniformVote,
+                      Capability::GroupNonUniformArithmetic,
+                      Capability::GroupNonUniformBallot,
+                      Capability::GroupNonUniformClustered,
+                      Capability::GroupNonUniformShuffle,
+                      Capability::GroupNonUniformShuffleRelative});
+  if (ST.isAtLeastSPIRVVer(14))
+    addAvailableCaps({Capability::DenormPreserve, Capability::DenormFlushToZero,
+                      Capability::SignedZeroInfNanPreserve,
+                      Capability::RoundingModeRTE,
+                      Capability::RoundingModeRTZ});
+  // TODO: verify if this needs some checks.
+  addAvailableCaps({Capability::Float16, Capability::Float64});
+
+  // TODO: add OpenCL extensions.
+}
+} // namespace SPIRV
+} // namespace llvm
+
+// Add the required capabilities from a decoration instruction (including
+// BuiltIns).
+static void addOpDecorateReqs(const MachineInstr &MI, unsigned DecIndex,
+                              SPIRV::RequirementHandler &Reqs,
+                              const SPIRVSubtarget &ST) {
+  int64_t DecOp = MI.getOperand(DecIndex).getImm();
+  auto Dec = static_cast<SPIRV::Decoration::Decoration>(DecOp);
+  Reqs.addRequirements(getSymbolicOperandRequirements(
+      SPIRV::OperandCategory::DecorationOperand, Dec, ST, Reqs));
+
+  if (Dec == SPIRV::Decoration::BuiltIn) {
+    int64_t BuiltInOp = MI.getOperand(DecIndex + 1).getImm();
+    auto BuiltIn = static_cast<SPIRV::BuiltIn::BuiltIn>(BuiltInOp);
+    Reqs.addRequirements(getSymbolicOperandRequirements(
+        SPIRV::OperandCategory::BuiltInOperand, BuiltIn, ST, Reqs));
+  }
+}
+
+// Add requirements for image handling.
+static void addOpTypeImageReqs(const MachineInstr &MI,
+                               SPIRV::RequirementHandler &Reqs,
+                               const SPIRVSubtarget &ST) {
+  assert(MI.getNumOperands() >= 8 && "Insufficient operands for OpTypeImage");
+  // The operand indices used here are based on the OpTypeImage layout, which
+  // the MachineInstr follows as well.
+  int64_t ImgFormatOp = MI.getOperand(7).getImm();
+  auto ImgFormat = static_cast<SPIRV::ImageFormat::ImageFormat>(ImgFormatOp);
+  Reqs.getAndAddRequirements(SPIRV::OperandCategory::ImageFormatOperand,
+                             ImgFormat, ST);
+
+  bool IsArrayed = MI.getOperand(4).getImm() == 1;
+  bool IsMultisampled = MI.getOperand(5).getImm() == 1;
+  bool NoSampler = MI.getOperand(6).getImm() == 2;
+  // Add dimension requirements.
+  assert(MI.getOperand(2).isImm());
+  switch (MI.getOperand(2).getImm()) {
+  case SPIRV::Dim::DIM_1D:
+    Reqs.addRequirements(NoSampler ? SPIRV::Capability::Image1D
+                                   : SPIRV::Capability::Sampled1D);
+    break;
+  case SPIRV::Dim::DIM_2D:
+    if (IsMultisampled && NoSampler)
+      Reqs.addRequirements(SPIRV::Capability::ImageMSArray);
+    break;
+  case SPIRV::Dim::DIM_Cube:
+    Reqs.addRequirements(SPIRV::Capability::Shader);
+    if (IsArrayed)
+      Reqs.addRequirements(NoSampler ? SPIRV::Capability::ImageCubeArray
+                                     : SPIRV::Capability::SampledCubeArray);
+    break;
+  case SPIRV::Dim::DIM_Rect:
+    Reqs.addRequirements(NoSampler ? SPIRV::Capability::ImageRect
+                                   : SPIRV::Capability::SampledRect);
+    break;
+  case SPIRV::Dim::DIM_Buffer:
+    Reqs.addRequirements(NoSampler ? SPIRV::Capability::ImageBuffer
+                                   : SPIRV::Capability::SampledBuffer);
+    break;
+  case SPIRV::Dim::DIM_SubpassData:
+    Reqs.addRequirements(SPIRV::Capability::InputAttachment);
+    break;
+  }
+
+  // Has optional access qualifier.
+  // TODO: check if it's OpenCL's kernel.
+  if (MI.getNumOperands() > 8 &&
+      MI.getOperand(8).getImm() == SPIRV::AccessQualifier::ReadWrite)
+    Reqs.addRequirements(SPIRV::Capability::ImageReadWrite);
+  else
+    Reqs.addRequirements(SPIRV::Capability::ImageBasic);
+}
+
+void addInstrRequirements(const MachineInstr &MI,
+                          SPIRV::RequirementHandler &Reqs,
+                          const SPIRVSubtarget &ST) {
+  switch (MI.getOpcode()) {
+  case SPIRV::OpMemoryModel: {
+    int64_t Addr = MI.getOperand(0).getImm();
+    Reqs.getAndAddRequirements(SPIRV::OperandCategory::AddressingModelOperand,
+                               Addr, ST);
+    int64_t Mem = MI.getOperand(1).getImm();
+    Reqs.getAndAddRequirements(SPIRV::OperandCategory::MemoryModelOperand, Mem,
+                               ST);
+    break;
+  }
+  case SPIRV::OpEntryPoint: {
+    int64_t Exe = MI.getOperand(0).getImm();
+    Reqs.getAndAddRequirements(SPIRV::OperandCategory::ExecutionModelOperand,
+                               Exe, ST);
+    break;
+  }
+  case SPIRV::OpExecutionMode:
+  case SPIRV::OpExecutionModeId: {
+    int64_t Exe = MI.getOperand(1).getImm();
+    Reqs.getAndAddRequirements(SPIRV::OperandCategory::ExecutionModeOperand,
+                               Exe, ST);
+    break;
+  }
+  case SPIRV::OpTypeMatrix:
+    Reqs.addCapability(SPIRV::Capability::Matrix);
+    break;
+  case SPIRV::OpTypeInt: {
+    unsigned BitWidth = MI.getOperand(1).getImm();
+    if (BitWidth == 64)
+      Reqs.addCapability(SPIRV::Capability::Int64);
+    else if (BitWidth == 16)
+      Reqs.addCapability(SPIRV::Capability::Int16);
+    else if (BitWidth == 8)
+      Reqs.addCapability(SPIRV::Capability::Int8);
+    break;
+  }
+  case SPIRV::OpTypeFloat: {
+    unsigned BitWidth = MI.getOperand(1).getImm();
+    if (BitWidth == 64)
+      Reqs.addCapability(SPIRV::Capability::Float64);
+    else if (BitWidth == 16)
+      Reqs.addCapability(SPIRV::Capability::Float16);
+    break;
+  }
+  case SPIRV::OpTypeVector: {
+    unsigned NumComponents = MI.getOperand(2).getImm();
+    if (NumComponents == 8 || NumComponents == 16)
+      Reqs.addCapability(SPIRV::Capability::Vector16);
+    break;
+  }
+  case SPIRV::OpTypePointer: {
+    auto SC = MI.getOperand(1).getImm();
+    Reqs.getAndAddRequirements(SPIRV::OperandCategory::StorageClassOperand, SC,
+                               ST);
+    // If it's a type of pointer to float16, add Float16Buffer capability.
+    assert(MI.getOperand(2).isReg());
+    const MachineRegisterInfo &MRI = MI.getMF()->getRegInfo();
+    SPIRVType *TypeDef = MRI.getVRegDef(MI.getOperand(2).getReg());
+    if (TypeDef->getOpcode() == SPIRV::OpTypeFloat &&
+        TypeDef->getOperand(1).getImm() == 16)
+      Reqs.addCapability(SPIRV::Capability::Float16Buffer);
+    break;
+  }
+  case SPIRV::OpBitReverse:
+  case SPIRV::OpTypeRuntimeArray:
+    Reqs.addCapability(SPIRV::Capability::Shader);
+    break;
+  case SPIRV::OpTypeOpaque:
+  case SPIRV::OpTypeEvent:
+    Reqs.addCapability(SPIRV::Capability::Kernel);
+    break;
+  case SPIRV::OpTypePipe:
+  case SPIRV::OpTypeReserveId:
+    Reqs.addCapability(SPIRV::Capability::Pipes);
+    break;
+  case SPIRV::OpTypeDeviceEvent:
+  case SPIRV::OpTypeQueue:
+    Reqs.addCapability(SPIRV::Capability::DeviceEnqueue);
+    break;
+  case SPIRV::OpDecorate:
+  case SPIRV::OpDecorateId:
+  case SPIRV::OpDecorateString:
+    addOpDecorateReqs(MI, 1, Reqs, ST);
+    break;
+  case SPIRV::OpMemberDecorate:
+  case SPIRV::OpMemberDecorateString:
+    addOpDecorateReqs(MI, 2, Reqs, ST);
+    break;
+  case SPIRV::OpInBoundsPtrAccessChain:
+    Reqs.addCapability(SPIRV::Capability::Addresses);
+    break;
+  case SPIRV::OpConstantSampler:
+    Reqs.addCapability(SPIRV::Capability::LiteralSampler);
+    break;
+  case SPIRV::OpTypeImage:
+    addOpTypeImageReqs(MI, Reqs, ST);
+    break;
+  case SPIRV::OpTypeSampler:
+    Reqs.addCapability(SPIRV::Capability::ImageBasic);
+    break;
+  case SPIRV::OpTypeForwardPointer:
+    // TODO: check if it's OpenCL's kernel.
+    Reqs.addCapability(SPIRV::Capability::Addresses);
+    break;
+  case SPIRV::OpAtomicFlagTestAndSet:
+  case SPIRV::OpAtomicLoad:
+  case SPIRV::OpAtomicStore:
+  case SPIRV::OpAtomicExchange:
+  case SPIRV::OpAtomicCompareExchange:
+  case SPIRV::OpAtomicIIncrement:
+  case SPIRV::OpAtomicIDecrement:
+  case SPIRV::OpAtomicIAdd:
+  case SPIRV::OpAtomicISub:
+  case SPIRV::OpAtomicUMin:
+  case SPIRV::OpAtomicUMax:
+  case SPIRV::OpAtomicSMin:
+  case SPIRV::OpAtomicSMax:
+  case SPIRV::OpAtomicAnd:
+  case SPIRV::OpAtomicOr:
+  case SPIRV::OpAtomicXor: {
+    const MachineRegisterInfo &MRI = MI.getMF()->getRegInfo();
+    const MachineInstr *InstrPtr = &MI;
+    if (MI.getOpcode() == SPIRV::OpAtomicStore) {
+      assert(MI.getOperand(3).isReg());
+      InstrPtr = MRI.getVRegDef(MI.getOperand(3).getReg());
+      assert(InstrPtr && "Unexpected type instruction for OpAtomicStore");
+    }
+    assert(InstrPtr->getOperand(1).isReg() && "Unexpected operand in atomic");
+    Register TypeReg = InstrPtr->getOperand(1).getReg();
+    SPIRVType *TypeDef = MRI.getVRegDef(TypeReg);
+    if (TypeDef->getOpcode() == SPIRV::OpTypeInt) {
+      unsigned BitWidth = TypeDef->getOperand(1).getImm();
+      if (BitWidth == 64)
+        Reqs.addCapability(SPIRV::Capability::Int64Atomics);
+    }
+    break;
+  }
+  case SPIRV::OpGroupNonUniformIAdd:
+  case SPIRV::OpGroupNonUniformFAdd:
+  case SPIRV::OpGroupNonUniformIMul:
+  case SPIRV::OpGroupNonUniformFMul:
+  case SPIRV::OpGroupNonUniformSMin:
+  case SPIRV::OpGroupNonUniformUMin:
+  case SPIRV::OpGroupNonUniformFMin:
+  case SPIRV::OpGroupNonUniformSMax:
+  case SPIRV::OpGroupNonUniformUMax:
+  case SPIRV::OpGroupNonUniformFMax:
+  case SPIRV::OpGroupNonUniformBitwiseAnd:
+  case SPIRV::OpGroupNonUniformBitwiseOr:
+  case SPIRV::OpGroupNonUniformBitwiseXor:
+  case SPIRV::OpGroupNonUniformLogicalAnd:
+  case SPIRV::OpGroupNonUniformLogicalOr:
+  case SPIRV::OpGroupNonUniformLogicalXor: {
+    assert(MI.getOperand(3).isImm());
+    int64_t GroupOp = MI.getOperand(3).getImm();
+    switch (GroupOp) {
+    case SPIRV::GroupOperation::Reduce:
+    case SPIRV::GroupOperation::InclusiveScan:
+    case SPIRV::GroupOperation::ExclusiveScan:
+      Reqs.addCapability(SPIRV::Capability::Kernel);
+      Reqs.addCapability(SPIRV::Capability::GroupNonUniformArithmetic);
+      Reqs.addCapability(SPIRV::Capability::GroupNonUniformBallot);
+      break;
+    case SPIRV::GroupOperation::ClusteredReduce:
+      Reqs.addCapability(SPIRV::Capability::GroupNonUniformClustered);
+      break;
+    case SPIRV::GroupOperation::PartitionedReduceNV:
+    case SPIRV::GroupOperation::PartitionedInclusiveScanNV:
+    case SPIRV::GroupOperation::PartitionedExclusiveScanNV:
+      Reqs.addCapability(SPIRV::Capability::GroupNonUniformPartitionedNV);
+      break;
+    }
+    break;
+  }
+  case SPIRV::OpGroupNonUniformShuffle:
+  case SPIRV::OpGroupNonUniformShuffleXor:
+    Reqs.addCapability(SPIRV::Capability::GroupNonUniformShuffle);
+    break;
+  case SPIRV::OpGroupNonUniformShuffleUp:
+  case SPIRV::OpGroupNonUniformShuffleDown:
+    Reqs.addCapability(SPIRV::Capability::GroupNonUniformShuffleRelative);
+    break;
+  case SPIRV::OpGroupAll:
+  case SPIRV::OpGroupAny:
+  case SPIRV::OpGroupBroadcast:
+  case SPIRV::OpGroupIAdd:
+  case SPIRV::OpGroupFAdd:
+  case SPIRV::OpGroupFMin:
+  case SPIRV::OpGroupUMin:
+  case SPIRV::OpGroupSMin:
+  case SPIRV::OpGroupFMax:
+  case SPIRV::OpGroupUMax:
+  case SPIRV::OpGroupSMax:
+    Reqs.addCapability(SPIRV::Capability::Groups);
+    break;
+  case SPIRV::OpGroupNonUniformElect:
+    Reqs.addCapability(SPIRV::Capability::GroupNonUniform);
+    break;
+  case SPIRV::OpGroupNonUniformAll:
+  case SPIRV::OpGroupNonUniformAny:
+  case SPIRV::OpGroupNonUniformAllEqual:
+    Reqs.addCapability(SPIRV::Capability::GroupNonUniformVote);
+    break;
+  case SPIRV::OpGroupNonUniformBroadcast:
+  case SPIRV::OpGroupNonUniformBroadcastFirst:
+  case SPIRV::OpGroupNonUniformBallot:
+  case SPIRV::OpGroupNonUniformInverseBallot:
+  case SPIRV::OpGroupNonUniformBallotBitExtract:
+  case SPIRV::OpGroupNonUniformBallotBitCount:
+  case SPIRV::OpGroupNonUniformBallotFindLSB:
+  case SPIRV::OpGroupNonUniformBallotFindMSB:
+    Reqs.addCapability(SPIRV::Capability::GroupNonUniformBallot);
+    break;
+  default:
+    break;
+  }
+}
+
+static void collectReqs(const Module &M, SPIRV::ModuleAnalysisInfo &MAI,
+                        MachineModuleInfo *MMI, const SPIRVSubtarget &ST) {
+  // Collect requirements for existing instructions.
+  for (auto F = M.begin(), E = M.end(); F != E; ++F) {
+    MachineFunction *MF = MMI->getMachineFunction(*F);
+    if (!MF)
+      continue;
+    for (const MachineBasicBlock &MBB : *MF)
+      for (const MachineInstr &MI : MBB)
+        addInstrRequirements(MI, MAI.Reqs, ST);
+  }
+  // Collect requirements for OpExecutionMode instructions.
+  auto Node = M.getNamedMetadata("spirv.ExecutionMode");
+  if (Node) {
+    for (unsigned i = 0; i < Node->getNumOperands(); i++) {
+      MDNode *MDN = cast<MDNode>(Node->getOperand(i));
+      const MDOperand &MDOp = MDN->getOperand(1);
+      if (auto *CMeta = dyn_cast<ConstantAsMetadata>(MDOp)) {
+        Constant *C = CMeta->getValue();
+        if (ConstantInt *Const = dyn_cast<ConstantInt>(C)) {
+          auto EM = Const->getZExtValue();
+          MAI.Reqs.getAndAddRequirements(
+              SPIRV::OperandCategory::ExecutionModeOperand, EM, ST);
+        }
+      }
+    }
+  }
+  for (auto FI = M.begin(), E = M.end(); FI != E; ++FI) {
+    const Function &F = *FI;
+    if (F.isDeclaration())
+      continue;
+    if (F.getMetadata("reqd_work_group_size"))
+      MAI.Reqs.getAndAddRequirements(
+          SPIRV::OperandCategory::ExecutionModeOperand,
+          SPIRV::ExecutionMode::LocalSize, ST);
+    if (F.getMetadata("work_group_size_hint"))
+      MAI.Reqs.getAndAddRequirements(
+          SPIRV::OperandCategory::ExecutionModeOperand,
+          SPIRV::ExecutionMode::LocalSizeHint, ST);
+    if (F.getMetadata("intel_reqd_sub_group_size"))
+      MAI.Reqs.getAndAddRequirements(
+          SPIRV::OperandCategory::ExecutionModeOperand,
+          SPIRV::ExecutionMode::SubgroupSize, ST);
+    if (F.getMetadata("vec_type_hint"))
+      MAI.Reqs.getAndAddRequirements(
+          SPIRV::OperandCategory::ExecutionModeOperand,
+          SPIRV::ExecutionMode::VecTypeHint, ST);
+  }
+}
+
+static unsigned getFastMathFlags(const MachineInstr &I) {
+  unsigned Flags = SPIRV::FPFastMathMode::None;
+  if (I.getFlag(MachineInstr::MIFlag::FmNoNans))
+    Flags |= SPIRV::FPFastMathMode::NotNaN;
+  if (I.getFlag(MachineInstr::MIFlag::FmNoInfs))
+    Flags |= SPIRV::FPFastMathMode::NotInf;
+  if (I.getFlag(MachineInstr::MIFlag::FmNsz))
+    Flags |= SPIRV::FPFastMathMode::NSZ;
+  if (I.getFlag(MachineInstr::MIFlag::FmArcp))
+    Flags |= SPIRV::FPFastMathMode::AllowRecip;
+  if (I.getFlag(MachineInstr::MIFlag::FmReassoc))
+    Flags |= SPIRV::FPFastMathMode::Fast;
+  return Flags;
+}
+
+static void handleMIFlagDecoration(MachineInstr &I, const SPIRVSubtarget &ST,
+                                   const SPIRVInstrInfo &TII,
+                                   SPIRV::RequirementHandler &Reqs) {
+  if (I.getFlag(MachineInstr::MIFlag::NoSWrap) && TII.canUseNSW(I) &&
+      getSymbolicOperandRequirements(SPIRV::OperandCategory::DecorationOperand,
+                                     SPIRV::Decoration::NoSignedWrap, ST, Reqs)
+          .IsSatisfiable) {
+    buildOpDecorate(I.getOperand(0).getReg(), I, TII,
+                    SPIRV::Decoration::NoSignedWrap, {});
+  }
+  if (I.getFlag(MachineInstr::MIFlag::NoUWrap) && TII.canUseNUW(I) &&
+      getSymbolicOperandRequirements(SPIRV::OperandCategory::DecorationOperand,
+                                     SPIRV::Decoration::NoUnsignedWrap, ST,
+                                     Reqs)
+          .IsSatisfiable) {
+    buildOpDecorate(I.getOperand(0).getReg(), I, TII,
+                    SPIRV::Decoration::NoUnsignedWrap, {});
+  }
+  if (!TII.canUseFastMathFlags(I))
+    return;
+  unsigned FMFlags = getFastMathFlags(I);
+  if (FMFlags == SPIRV::FPFastMathMode::None)
+    return;
+  Register DstReg = I.getOperand(0).getReg();
+  buildOpDecorate(DstReg, I, TII, SPIRV::Decoration::FPFastMathMode, {FMFlags});
+}
+
+// Walk all functions and add decorations related to MI flags.
+static void addDecorations(const Module &M, const SPIRVInstrInfo &TII,
+                           MachineModuleInfo *MMI, const SPIRVSubtarget &ST,
+                           SPIRV::ModuleAnalysisInfo &MAI) {
+  for (auto F = M.begin(), E = M.end(); F != E; ++F) {
+    MachineFunction *MF = MMI->getMachineFunction(*F);
+    if (!MF)
+      continue;
+    for (auto &MBB : *MF)
+      for (auto &MI : MBB)
+        handleMIFlagDecoration(MI, ST, TII, MAI.Reqs);
+  }
+}
+
 struct SPIRV::ModuleAnalysisInfo SPIRVModuleAnalysis::MAI;
 
 void SPIRVModuleAnalysis::getAnalysisUsage(AnalysisUsage &AU) const {
@@ -373,6 +1016,10 @@ bool SPIRVModuleAnalysis::runOnModule(Module &M) {
 
   setBaseInfo(M);
 
+  addDecorations(M, *TII, MMI, *ST, MAI);
+
+  collectReqs(M, MAI, MMI, *ST);
+
   processSwitches(M, MAI, MMI);
 
   // Process type/const/global var/func decl instructions, number their
@@ -385,5 +1032,9 @@ bool SPIRVModuleAnalysis::runOnModule(Module &M) {
   // Collect OpName, OpEntryPoint, OpDecorate etc, process other instructions.
   processOtherInstrs(M);
 
+  // If there are no entry points, we need the Linkage capability.
+  if (MAI.MS[SPIRV::MB_EntryPoints].empty())
+    MAI.Reqs.addCapability(SPIRV::Capability::Linkage);
+
   return false;
 }

diff  --git a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.h b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.h
index c19f2c2a9ce7f..be7c1a4446450 100644
--- a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.h
+++ b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.h
@@ -15,13 +15,15 @@
 #define LLVM_LIB_TARGET_SPIRV_SPIRVMODULEANALYSIS_H
 
 #include "MCTargetDesc/SPIRVBaseInfo.h"
-#include "SPIRVDuplicatesTracker.h"
-#include "SPIRVSubtarget.h"
+#include "SPIRVGlobalRegistry.h"
 #include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/SmallSet.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringMap.h"
 
 namespace llvm {
+class SPIRVSubtarget;
 class MachineFunction;
 class MachineModuleInfo;
 
@@ -39,6 +41,77 @@ enum ModuleSectionType {
   NUM_MODULE_SECTIONS      // Total number of sections requiring basic blocks.
 };
 
+struct Requirements {
+  const bool IsSatisfiable;
+  const Optional<Capability::Capability> Cap;
+  const ExtensionList Exts;
+  const unsigned MinVer; // 0 if no min version is required.
+  const unsigned MaxVer; // 0 if no max version is required.
+
+  Requirements(bool IsSatisfiable = false,
+               Optional<Capability::Capability> Cap = {},
+               ExtensionList Exts = {}, unsigned MinVer = 0,
+               unsigned MaxVer = 0)
+      : IsSatisfiable(IsSatisfiable), Cap(Cap), Exts(Exts), MinVer(MinVer),
+        MaxVer(MaxVer) {}
+  Requirements(Capability::Capability Cap) : Requirements(true, {Cap}) {}
+};
+
+struct RequirementHandler {
+private:
+  CapabilityList MinimalCaps;
+  SmallSet<Capability::Capability, 8> AllCaps;
+  SmallSet<Extension::Extension, 4> AllExtensions;
+  unsigned MinVersion; // 0 if no min version is defined.
+  unsigned MaxVersion; // 0 if no max version is defined.
+  DenseSet<unsigned> AvailableCaps;
+  // Remove a list of capabilities from dedupedCaps and add them to AllCaps,
+  // recursing through their implicitly declared capabilities too.
+  void pruneCapabilities(const CapabilityList &ToPrune);
+
+public:
+  RequirementHandler() : MinVersion(0), MaxVersion(0) {}
+  void clear() {
+    MinimalCaps.clear();
+    AllCaps.clear();
+    AvailableCaps.clear();
+    AllExtensions.clear();
+    MinVersion = 0;
+    MaxVersion = 0;
+  }
+  unsigned getMinVersion() const { return MinVersion; }
+  unsigned getMaxVersion() const { return MaxVersion; }
+  const CapabilityList &getMinimalCapabilities() const { return MinimalCaps; }
+  const SmallSet<Extension::Extension, 4> &getExtensions() const {
+    return AllExtensions;
+  }
+  // Add a list of capabilities, ensuring AllCaps captures all the implicitly
+  // declared capabilities, and MinimalCaps has the minimal set of required
+  // capabilities (so all implicitly declared ones are removed).
+  void addCapabilities(const CapabilityList &ToAdd);
+  void addCapability(Capability::Capability ToAdd) { addCapabilities({ToAdd}); }
+  void addExtensions(const ExtensionList &ToAdd) {
+    AllExtensions.insert(ToAdd.begin(), ToAdd.end());
+  }
+  void addExtension(Extension::Extension ToAdd) { AllExtensions.insert(ToAdd); }
+  // Add the given requirements to the lists. If constraints conflict, or these
+  // requirements cannot be satisfied, then abort the compilation.
+  void addRequirements(const Requirements &Req);
+  // Get requirement and add it to the list.
+  void getAndAddRequirements(SPIRV::OperandCategory::OperandCategory Category,
+                             uint32_t i, const SPIRVSubtarget &ST);
+  // Check if all the requirements can be satisfied for the given subtarget, and
+  // if not abort compilation.
+  void checkSatisfiable(const SPIRVSubtarget &ST) const;
+  void initAvailableCapabilities(const SPIRVSubtarget &ST);
+  // Add the given capabilities to available and all their implicitly defined
+  // capabilities too.
+  void addAvailableCaps(const CapabilityList &ToAdd);
+  bool isCapabilityAvailable(Capability::Capability Cap) const {
+    return AvailableCaps.contains(Cap);
+  }
+};
+
 using InstrList = SmallVector<MachineInstr *>;
 // Maps a local register to the corresponding global alias.
 using LocalToGlobalRegTable = std::map<Register, Register>;
@@ -48,9 +121,10 @@ using RegisterAliasMapTy =
 // The struct contains results of the module analysis and methods
 // to access them.
 struct ModuleAnalysisInfo {
-  SPIRV::MemoryModel::MemoryModel Mem;
-  SPIRV::AddressingModel::AddressingModel Addr;
-  SPIRV::SourceLanguage::SourceLanguage SrcLang;
+  RequirementHandler Reqs;
+  MemoryModel::MemoryModel Mem;
+  AddressingModel::AddressingModel Addr;
+  SourceLanguage::SourceLanguage SrcLang;
   unsigned SrcLangVersion;
   StringSet<> SrcExt;
   // Maps ExtInstSet to corresponding ID register.

diff  --git a/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp b/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp
index e620226dcc7af..1350cd5094610 100644
--- a/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp
@@ -13,7 +13,6 @@
 //===----------------------------------------------------------------------===//
 
 #include "SPIRV.h"
-#include "SPIRVGlobalRegistry.h"
 #include "SPIRVSubtarget.h"
 #include "SPIRVUtils.h"
 #include "llvm/ADT/PostOrderIterator.h"
@@ -208,10 +207,14 @@ static Register insertAssignInstr(Register Reg, Type *Ty, SPIRVType *SpirvTy,
   // 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());
+  // Copy MIFlags from Def to ASSIGN_TYPE instruction. It's required to keep
+  // the flags after instruction selection.
+  const uint16_t Flags = Def->getFlags();
   MIB.buildInstr(SPIRV::ASSIGN_TYPE)
       .addDef(Reg)
       .addUse(NewReg)
-      .addUse(GR->getSPIRVTypeID(SpirvTy));
+      .addUse(GR->getSPIRVTypeID(SpirvTy))
+      .setMIFlags(Flags);
   Def->getOperand(0).setReg(NewReg);
   MRI.setRegClass(Reg, &SPIRV::ANYIDRegClass);
   return NewReg;
@@ -409,6 +412,15 @@ static void processSwitches(MachineFunction &MF, SPIRVGlobalRegistry *GR,
         // Set the first successor as default MBB to support empty switches.
         DefaultMBBs[Reg] = *MBB.succ_begin();
       }
+      // Process G_SUB coming from switch range-compare lowering.
+      if (MI.getOpcode() == TargetOpcode::G_SUB && MI.getOperand(1).isReg() &&
+          SwitchRegs.contains(MI.getOperand(1).getReg())) {
+        assert(MI.getOperand(0).isReg() && MI.getOperand(1).isReg());
+        Register Dst = MI.getOperand(0).getReg();
+        SwitchRegs.insert(Dst);
+        SPIRVType *Ty = GR->getSPIRVTypeForVReg(MI.getOperand(1).getReg());
+        insertAssignInstr(Dst, nullptr, Ty, GR, MIB, MRI);
+      }
       // Process only ICMPs that relate to spv_switches.
       if (MI.getOpcode() == TargetOpcode::G_ICMP && MI.getOperand(2).isReg() &&
           SwitchRegs.contains(MI.getOperand(2).getReg())) {
@@ -426,8 +438,8 @@ static void processSwitches(MachineFunction &MF, SPIRVGlobalRegistry *GR,
         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));
+        assert((CC == CmpInst::ICMP_EQ || CC == CmpInst::ICMP_ULE) &&
+               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 &&
@@ -465,6 +477,8 @@ static void processSwitches(MachineFunction &MF, SPIRVGlobalRegistry *GR,
         Register CReg = MI.getOperand(i).getReg();
         uint64_t Val = getIConstVal(CReg, &MRI);
         MachineInstr *ConstInstr = getDefInstrMaybeConstant(CReg, &MRI);
+        if (!SwitchRegToMBB[Reg][Val])
+          continue;
         Vals.push_back(ConstInstr->getOperand(1).getCImm());
         MBBs.push_back(SwitchRegToMBB[Reg][Val]);
       }
@@ -489,8 +503,8 @@ bool SPIRVPreLegalizer::runOnMachineFunction(MachineFunction &MF) {
   foldConstantsIntoIntrinsics(MF);
   insertBitcasts(MF, GR, MIB);
   generateAssignInstrs(MF, GR, MIB);
-  processInstrsWithTypeFolding(MF, GR, MIB);
   processSwitches(MF, GR, MIB);
+  processInstrsWithTypeFolding(MF, GR, MIB);
 
   return true;
 }

diff  --git a/llvm/lib/Target/SPIRV/SPIRVSubtarget.cpp b/llvm/lib/Target/SPIRV/SPIRVSubtarget.cpp
index 00549c7b57685..a39ada9c82493 100644
--- a/llvm/lib/Target/SPIRV/SPIRVSubtarget.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVSubtarget.cpp
@@ -43,8 +43,10 @@ SPIRVSubtarget::SPIRVSubtarget(const Triple &TT, const std::string &CPU,
                                const std::string &FS,
                                const SPIRVTargetMachine &TM)
     : SPIRVGenSubtargetInfo(TT, CPU, /*TuneCPU=*/CPU, FS),
-      PointerSize(computePointerSize(TT)), SPIRVVersion(0), InstrInfo(),
-      FrameLowering(initSubtargetDependencies(CPU, FS)), TLInfo(TM, *this) {
+      PointerSize(computePointerSize(TT)), SPIRVVersion(0), OpenCLVersion(0),
+      InstrInfo(), FrameLowering(initSubtargetDependencies(CPU, FS)),
+      TLInfo(TM, *this) {
+  initAvailableExtensions();
   GR = std::make_unique<SPIRVGlobalRegistry>(PointerSize);
   CallLoweringInfo = std::make_unique<SPIRVCallLowering>(TLInfo, GR.get());
   Legalizer = std::make_unique<SPIRVLegalizerInfo>(*this);
@@ -58,10 +60,34 @@ SPIRVSubtarget &SPIRVSubtarget::initSubtargetDependencies(StringRef CPU,
   ParseSubtargetFeatures(CPU, /*TuneCPU=*/CPU, FS);
   if (SPIRVVersion == 0)
     SPIRVVersion = 14;
+  if (OpenCLVersion == 0)
+    OpenCLVersion = 22;
   return *this;
 }
 
+bool SPIRVSubtarget::canUseExtension(SPIRV::Extension::Extension E) const {
+  return AvailableExtensions.contains(E);
+}
+
+bool SPIRVSubtarget::isAtLeastSPIRVVer(uint32_t VerToCompareTo) const {
+  return isAtLeastVer(SPIRVVersion, VerToCompareTo);
+}
+
+bool SPIRVSubtarget::isAtLeastOpenCLVer(uint32_t VerToCompareTo) const {
+  return isAtLeastVer(OpenCLVersion, VerToCompareTo);
+}
+
 // If the SPIR-V version is >= 1.4 we can call OpPtrEqual and OpPtrNotEqual.
 bool SPIRVSubtarget::canDirectlyComparePointers() const {
   return isAtLeastVer(SPIRVVersion, 14);
 }
+
+// TODO: use command line args for this rather than defaults.
+void SPIRVSubtarget::initAvailableExtensions() {
+  AvailableExtensions.clear();
+  if (!isOpenCLEnv())
+    return;
+  // A default extension for testing.
+  AvailableExtensions.insert(
+      SPIRV::Extension::SPV_KHR_no_integer_wrap_decoration);
+}

diff  --git a/llvm/lib/Target/SPIRV/SPIRVSubtarget.h b/llvm/lib/Target/SPIRV/SPIRVSubtarget.h
index a6332cfefa8e3..28d11b9d26f64 100644
--- a/llvm/lib/Target/SPIRV/SPIRVSubtarget.h
+++ b/llvm/lib/Target/SPIRV/SPIRVSubtarget.h
@@ -30,14 +30,15 @@
 
 namespace llvm {
 class StringRef;
-class SPIRVGlobalRegistry;
 class SPIRVTargetMachine;
 
 class SPIRVSubtarget : public SPIRVGenSubtargetInfo {
 private:
   const unsigned PointerSize;
   uint32_t SPIRVVersion;
+  uint32_t OpenCLVersion;
 
+  SmallSet<SPIRV::Extension::Extension, 4> AvailableExtensions;
   std::unique_ptr<SPIRVGlobalRegistry> GR;
 
   SPIRVInstrInfo InstrInfo;
@@ -50,6 +51,10 @@ class SPIRVSubtarget : public SPIRVGenSubtargetInfo {
   std::unique_ptr<LegalizerInfo> Legalizer;
   std::unique_ptr<InstructionSelector> InstSelector;
 
+  // TODO: Initialise the available extensions based on
+  // the environment settings.
+  void initAvailableExtensions();
+
 public:
   // This constructor initializes the data members to match that
   // of the specified triple.
@@ -62,7 +67,18 @@ class SPIRVSubtarget : public SPIRVGenSubtargetInfo {
   void ParseSubtargetFeatures(StringRef CPU, StringRef TuneCPU, StringRef FS);
   unsigned getPointerSize() const { return PointerSize; }
   bool canDirectlyComparePointers() const;
+  // TODO: this environment is not implemented in Triple, we need to decide
+  // how to standartize its support. For now, let's assume that we always
+  // operate with OpenCL.
+  bool isOpenCLEnv() const { return true; }
   uint32_t getSPIRVVersion() const { return SPIRVVersion; };
+  bool isAtLeastSPIRVVer(uint32_t VerToCompareTo) const;
+  bool isAtLeastOpenCLVer(uint32_t VerToCompareTo) const;
+  // TODO: implement command line args or other ways to determine this.
+  bool hasOpenCLFullProfile() const { return true; }
+  bool hasOpenCLImageSupport() const { return true; }
+  bool canUseExtension(SPIRV::Extension::Extension E) const;
+
   SPIRVGlobalRegistry *getSPIRVGlobalRegistry() const { return GR.get(); }
 
   const CallLowering *getCallLowering() const override {

diff  --git a/llvm/test/CodeGen/SPIRV/constant/local-float-point-constants.ll b/llvm/test/CodeGen/SPIRV/constant/local-float-point-constants.ll
new file mode 100644
index 0000000000000..3a11ceb0d97cb
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/constant/local-float-point-constants.ll
@@ -0,0 +1,49 @@
+; RUN: llc -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s
+
+define half @getConstantFP16() {
+  ret half 0x3ff1340000000000 ; 0x3c4d represented as double.
+}
+
+define float @getConstantFP32() {
+  ret float 0x3fd27c8be0000000 ; 0x3e93e45f represented as double
+}
+
+define double @getConstantFP64() {
+  ret double 0x4f2de42b8c68f3f1
+}
+
+; Capabilities
+; CHECK-DAG: OpCapability Float16
+; CHECK-DAG: OpCapability Float64
+
+; CHECK-NOT: DAG-FENCE
+
+; Names:
+; CHECK-DAG: OpName %[[#GET_FP16:]] "getConstantFP16"
+; CHECK-DAG: OpName %[[#GET_FP32:]] "getConstantFP32"
+; CHECK-DAG: OpName %[[#GET_FP64:]] "getConstantFP64"
+
+; CHECK-NOT: DAG-FENCE
+
+; Types and Constants:
+; NOTE: These tests don't actually check the values of the constants because
+;       their representation isn't defined for textual output.
+; TODO: Test constant representation using binary output.
+; CHECK-DAG: %[[#FP16:]] = OpTypeFloat 16
+; CHECK-DAG: %[[#FP32:]] = OpTypeFloat 32
+; CHECK-DAG: %[[#FP64:]] = OpTypeFloat 64
+; CHECK-DAG: %[[#CST_FP16:]] = OpConstant %[[#FP16]]
+; CHECK-DAG: %[[#CST_FP32:]] = OpConstant %[[#FP32]]
+; CHECK-DAG: %[[#CST_FP64:]] = OpConstant %[[#FP64]]
+
+; CHECK: %[[#GET_FP16]] = OpFunction %[[#FP16]]
+; CHECK: OpReturnValue %[[#CST_FP16]]
+; CHECK: OpFunctionEnd
+
+; CHECK: %[[#GET_FP32]] = OpFunction %[[#FP32]]
+; CHECK: OpReturnValue %[[#CST_FP32]]
+; CHECK: OpFunctionEnd
+
+; CHECK: %[[#GET_FP64]] = OpFunction %[[#FP64]]
+; CHECK: OpReturnValue %[[#CST_FP64]]
+; CHECK: OpFunctionEnd

diff  --git a/llvm/test/CodeGen/SPIRV/constant/local-integers-constants.ll b/llvm/test/CodeGen/SPIRV/constant/local-integers-constants.ll
new file mode 100644
index 0000000000000..cc14327bb3174
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/constant/local-integers-constants.ll
@@ -0,0 +1,56 @@
+; RUN: llc -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s
+
+define i16 @getConstantI16() {
+  ret i16 -58
+}
+
+define i32 @getConstantI32() {
+  ret i32 42
+}
+
+define i64 @getConstantI64() {
+  ret i64 123456789
+}
+
+define i64 @getLargeConstantI64() {
+  ret i64 34359738368
+}
+
+; Capabilities:
+; CHECK-DAG: OpCapability Int16
+; CHECK-DAG: OpCapability Int64
+
+; CHECK-NOT: DAG-FENCE
+
+; Names:
+; CHECK-DAG: OpName %[[#GET_I16:]] "getConstantI16"
+; CHECK-DAG: OpName %[[#GET_I32:]] "getConstantI32"
+; CHECK-DAG: OpName %[[#GET_I64:]] "getConstantI64"
+; CHECK-DAG: OpName %[[#GET_LARGE_I64:]] "getLargeConstantI64"
+
+; CHECK-NOT: DAG-FENCE
+
+; Types and Constants:
+; CHECK-DAG: %[[#I16:]] = OpTypeInt 16 0
+; CHECK-DAG: %[[#I32:]] = OpTypeInt 32 0
+; CHECK-DAG: %[[#I64:]] = OpTypeInt 64 0
+; CHECK-DAG: %[[#CST_I16:]] = OpConstant %[[#I16]] 65478
+; CHECK-DAG: %[[#CST_I32:]] = OpConstant %[[#I32]] 42
+; CHECK-DAG: %[[#CST_I64:]] = OpConstant %[[#I64]] 123456789 0
+; CHECK-DAG: %[[#CST_LARGE_I64:]] = OpConstant %[[#I64]] 0 8
+
+; CHECK: %[[#GET_I16]] = OpFunction %[[#I16]]
+; CHECK: OpReturnValue %[[#CST_I16]]
+; CHECK: OpFunctionEnd
+
+; CHECK: %[[#GET_I32]] = OpFunction %[[#I32]]
+; CHECK: OpReturnValue %[[#CST_I32]]
+; CHECK: OpFunctionEnd
+
+; CHECK: %[[#GET_I64]] = OpFunction %[[#I64]]
+; CHECK: OpReturnValue %[[#CST_I64]]
+; CHECK: OpFunctionEnd
+
+; CHECK: %[[#GET_LARGE_I64]] = OpFunction %[[#I64]]
+; CHECK: OpReturnValue %[[#CST_LARGE_I64]]
+; CHECK: OpFunctionEnd

diff  --git a/llvm/test/CodeGen/SPIRV/extensions/no_wrap.ll b/llvm/test/CodeGen/SPIRV/extensions/no_wrap.ll
new file mode 100644
index 0000000000000..e59d2e34e7bca
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/extensions/no_wrap.ll
@@ -0,0 +1,43 @@
+; RUN: llc -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s
+
+; CHECK-DAG: OpExtension "SPV_KHR_no_integer_wrap_decoration"
+
+; CHECK-NOT: DAG-FENCE
+
+; CHECK-DAG: OpName %[[#NO_WRAP_TEST:]] "no_wrap_test"
+; CHECK-DAG: OpName %[[#A:]] "a"
+; CHECK-DAG: OpName %[[#B:]] "b"
+; CHECK-DAG: OpName %[[#C:]] "c"
+; CHECK-DAG: OpName %[[#D:]] "d"
+; CHECK-DAG: OpName %[[#E:]] "e"
+
+; CHECK-NOT: DAG-FENCE
+
+; CHECK-DAG: OpDecorate %[[#C]] NoUnsignedWrap
+; CHECK-DAG: OpDecorate %[[#D]] NoSignedWrap
+; CHECK-DAG: OpDecorate %[[#E]] NoUnsignedWrap
+; CHECK-DAG: OpDecorate %[[#E]] NoSignedWrap
+
+; CHECK-NOT: DAG-FENCE
+
+; CHECK-DAG: %[[#I32:]] = OpTypeInt 32
+; CHECK-DAG: %[[#FN:]] = OpTypeFunction %[[#I32]] %[[#I32]] %[[#I32]]
+
+; CHECK-NOT: DAG-FENCE
+
+define i32 @no_wrap_test(i32 %a, i32 %b) {
+    %c = mul nuw i32 %a, %b
+    %d = mul nsw i32 %a, %b
+    %e = add nuw nsw i32 %c, %d
+    ret i32 %e
+}
+
+; CHECK: %[[#NO_WRAP_TEST]] = OpFunction %[[#I32]] None %[[#FN]]
+; CHECK-NEXT: %[[#A]] = OpFunctionParameter %[[#I32]]
+; CHECK-NEXT: %[[#B]] = OpFunctionParameter %[[#I32]]
+; CHECK: OpLabel
+; CHECK: %[[#C]] = OpIMul %[[#I32]] %[[#A]] %[[#B]]
+; CHECK: %[[#D]] = OpIMul %[[#I32]] %[[#A]] %[[#B]]
+; CHECK: %[[#E]] = OpIAdd %[[#I32]] %[[#C]] %[[#D]]
+; CHECK: OpReturnValue %[[#E]]
+; CHECK-NEXT: OpFunctionEnd

diff  --git a/llvm/test/CodeGen/SPIRV/transcoding/ReqdSubgroupSize.ll b/llvm/test/CodeGen/SPIRV/transcoding/ReqdSubgroupSize.ll
new file mode 100644
index 0000000000000..04486b79705b6
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/transcoding/ReqdSubgroupSize.ll
@@ -0,0 +1,17 @@
+; Check translation of intel_reqd_sub_group_size metadata to SubgroupSize
+; execution mode and back. The IR is producded from the following OpenCL C code:
+; kernel __attribute__((intel_reqd_sub_group_size(8)))
+; void foo() {}
+
+; RUN: llc -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s --check-prefix=CHECK-SPIRV
+
+; CHECK-SPIRV: OpCapability SubgroupDispatch
+; CHECK-SPIRV: OpEntryPoint Kernel %[[#kernel:]] "foo"
+; CHECK-SPIRV: OpExecutionMode %[[#kernel]] SubgroupSize 8
+
+define spir_kernel void @foo() !intel_reqd_sub_group_size !0 {
+entry:
+  ret void
+}
+
+!0 = !{i32 8}

diff  --git a/llvm/test/CodeGen/SPIRV/transcoding/fadd.ll b/llvm/test/CodeGen/SPIRV/transcoding/fadd.ll
new file mode 100644
index 0000000000000..0cb9d99eaefb9
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/transcoding/fadd.ll
@@ -0,0 +1,36 @@
+; RUN: llc -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s --check-prefix=CHECK-SPIRV
+
+; CHECK-SPIRV: OpName %[[#r1:]] "r1"
+; CHECK-SPIRV: OpName %[[#r2:]] "r2"
+; CHECK-SPIRV: OpName %[[#r3:]] "r3"
+; CHECK-SPIRV: OpName %[[#r4:]] "r4"
+; CHECK-SPIRV: OpName %[[#r5:]] "r5"
+; CHECK-SPIRV: OpName %[[#r6:]] "r6"
+; CHECK-SPIRV: OpName %[[#r7:]] "r7"
+; CHECK-SPIRV-NOT: OpDecorate %[[#r1]] FPFastMathMode
+; CHECK-SPIRV-DAG: OpDecorate %[[#r2]] FPFastMathMode NotNaN
+; CHECK-SPIRV-DAG: OpDecorate %[[#r3]] FPFastMathMode NotInf
+; CHECK-SPIRV-DAG: OpDecorate %[[#r4]] FPFastMathMode NSZ
+; CHECK-SPIRV-DAG: OpDecorate %[[#r5]] FPFastMathMode AllowRecip
+; CHECK-SPIRV-DAG: OpDecorate %[[#r6]] FPFastMathMode NotNaN|NotInf|NSZ|AllowRecip|Fast
+; CHECK-SPIRV-DAG: OpDecorate %[[#r7]] FPFastMathMode NotNaN|NotInf
+; CHECK-SPIRV: %[[#float:]] = OpTypeFloat 32
+; CHECK-SPIRV: %[[#r1]] = OpFAdd %[[#float]]
+; CHECK-SPIRV: %[[#r2]] = OpFAdd %[[#float]]
+; CHECK-SPIRV: %[[#r3]] = OpFAdd %[[#float]]
+; CHECK-SPIRV: %[[#r4]] = OpFAdd %[[#float]]
+; CHECK-SPIRV: %[[#r5]] = OpFAdd %[[#float]]
+; CHECK-SPIRV: %[[#r6]] = OpFAdd %[[#float]]
+; CHECK-SPIRV: %[[#r7]] = OpFAdd %[[#float]]
+
+define spir_kernel void @testFAdd(float %a, float %b) {
+entry:
+  %r1 = fadd float %a, %b
+  %r2 = fadd nnan float %a, %b
+  %r3 = fadd ninf float %a, %b
+  %r4 = fadd nsz float %a, %b
+  %r5 = fadd arcp float %a, %b
+  %r6 = fadd fast float %a, %b
+  %r7 = fadd nnan ninf float %a, %b
+  ret void
+}


        


More information about the llvm-commits mailing list