[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