Skip to content

Commit f352ce3

Browse files
[SPIR-V] Emit SPIR-V generator magic number and version (#87951)
This patch: - Adds SPIR-V backend's registered generator magic number to the emitted binary. The magic number consists of the generator ID (43) and LLVM major version. - Adds SPIR-V version to the binary. - Allows reading the expected (maximum supported) SPIR-V version from the target triple. - Uses VersionTuple for representing versions throughout the backend's codebase. - Registers v1.6 for spirv32 and spirv64 triple. See more: KhronosGroup/SPIRV-Headers@7d500c
1 parent 92631a4 commit f352ce3

16 files changed

+123
-88
lines changed

llvm/lib/MC/SPIRVObjectWriter.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,10 @@ class SPIRVObjectWriter : public MCObjectWriter {
4343

4444
void SPIRVObjectWriter::writeHeader(const MCAssembler &Asm) {
4545
constexpr uint32_t MagicNumber = 0x07230203;
46-
constexpr uint32_t GeneratorMagicNumber = 0;
46+
constexpr uint32_t GeneratorID = 43;
47+
constexpr uint32_t GeneratorMagicNumber =
48+
(GeneratorID << 16) | (LLVM_VERSION_MAJOR);
4749
constexpr uint32_t Schema = 0;
48-
49-
// Construct SPIR-V version and Bound
5050
const MCAssembler::VersionInfoType &VIT = Asm.getVersionInfo();
5151
uint32_t VersionNumber = 0 | (VIT.Major << 16) | (VIT.Minor << 8);
5252
uint32_t Bound = VIT.Update;

llvm/lib/Target/SPIRV/MCTargetDesc/SPIRVBaseInfo.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -88,28 +88,28 @@ getSymbolicOperandMnemonic(SPIRV::OperandCategory::OperandCategory Category,
8888
return Name;
8989
}
9090

91-
uint32_t
91+
VersionTuple
9292
getSymbolicOperandMinVersion(SPIRV::OperandCategory::OperandCategory Category,
9393
uint32_t Value) {
9494
const SPIRV::SymbolicOperand *Lookup =
9595
SPIRV::lookupSymbolicOperandByCategoryAndValue(Category, Value);
9696

9797
if (Lookup)
98-
return Lookup->MinVersion;
98+
return VersionTuple(Lookup->MinVersion / 10, Lookup->MinVersion % 10);
9999

100-
return 0;
100+
return VersionTuple(0);
101101
}
102102

103-
uint32_t
103+
VersionTuple
104104
getSymbolicOperandMaxVersion(SPIRV::OperandCategory::OperandCategory Category,
105105
uint32_t Value) {
106106
const SPIRV::SymbolicOperand *Lookup =
107107
SPIRV::lookupSymbolicOperandByCategoryAndValue(Category, Value);
108108

109109
if (Lookup)
110-
return Lookup->MaxVersion;
110+
return VersionTuple(Lookup->MaxVersion / 10, Lookup->MaxVersion % 10);
111111

112-
return 0;
112+
return VersionTuple();
113113
}
114114

115115
CapabilityList

llvm/lib/Target/SPIRV/MCTargetDesc/SPIRVBaseInfo.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
#include "llvm/ADT/SmallVector.h"
1919
#include "llvm/ADT/StringRef.h"
20+
#include "llvm/Support/VersionTuple.h"
2021
#include <string>
2122

2223
namespace llvm {
@@ -214,10 +215,10 @@ using ExtensionList = SmallVector<SPIRV::Extension::Extension, 8>;
214215
std::string
215216
getSymbolicOperandMnemonic(SPIRV::OperandCategory::OperandCategory Category,
216217
int32_t Value);
217-
uint32_t
218+
VersionTuple
218219
getSymbolicOperandMinVersion(SPIRV::OperandCategory::OperandCategory Category,
219220
uint32_t Value);
220-
uint32_t
221+
VersionTuple
221222
getSymbolicOperandMaxVersion(SPIRV::OperandCategory::OperandCategory Category,
222223
uint32_t Value);
223224
CapabilityList

llvm/lib/Target/SPIRV/SPIRV.td

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,6 @@ class Proc<string Name, list<SubtargetFeature> Features>
2020

2121
def : Proc<"generic", []>;
2222

23-
def SPIRV10 : SubtargetFeature<"spirv1.0", "SPIRVVersion", "10",
24-
"Use SPIR-V version 1.0">;
25-
def SPIRV11 : SubtargetFeature<"spirv1.1", "SPIRVVersion", "11",
26-
"Use SPIR-V version 1.1">;
27-
def SPIRV12 : SubtargetFeature<"spirv1.2", "SPIRVVersion", "12",
28-
"Use SPIR-V version 1.2">;
29-
def SPIRV13 : SubtargetFeature<"spirv1.3", "SPIRVVersion", "13",
30-
"Use SPIR-V version 1.3">;
31-
def SPIRV14 : SubtargetFeature<"spirv1.4", "SPIRVVersion", "14",
32-
"Use SPIR-V version 1.4">;
33-
def SPIRV15 : SubtargetFeature<"spirv1.5", "SPIRVVersion", "15",
34-
"Use SPIR-V version 1.5">;
35-
3623
def SPIRVInstPrinter : AsmWriter {
3724
string AsmWriterClassName = "InstPrinter";
3825
bit isMCAsmWriter = 1;

llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,9 @@ void SPIRVAsmPrinter::emitEndOfAsmFile(Module &M) {
108108
}
109109

110110
ST = static_cast<const SPIRVTargetMachine &>(TM).getSubtargetImpl();
111-
uint32_t DecSPIRVVersion = ST->getSPIRVVersion();
112-
uint32_t Major = DecSPIRVVersion / 10;
113-
uint32_t Minor = DecSPIRVVersion - Major * 10;
111+
VersionTuple SPIRVVersion = ST->getSPIRVVersion();
112+
uint32_t Major = SPIRVVersion.getMajor();
113+
uint32_t Minor = SPIRVVersion.getMinor().value_or(0);
114114
// Bound is an approximation that accounts for the maximum used register
115115
// number and number of generated OpLabels
116116
unsigned Bound = 2 * (ST->getBound() + 1) + NLabels;
@@ -321,8 +321,8 @@ void SPIRVAsmPrinter::outputEntryPoints() {
321321
// the Input and Output storage classes. Starting with version 1.4,
322322
// the interface's storage classes are all storage classes used in
323323
// declaring all global variables referenced by the entry point call tree.
324-
if (ST->getSPIRVVersion() >= 14 || SC == SPIRV::StorageClass::Input ||
325-
SC == SPIRV::StorageClass::Output) {
324+
if (ST->isAtLeastSPIRVVer(VersionTuple(1, 4)) ||
325+
SC == SPIRV::StorageClass::Input || SC == SPIRV::StorageClass::Output) {
326326
MachineFunction *MF = MI->getMF();
327327
Register Reg = MAI->getRegisterAlias(MF, MI->getOperand(0).getReg());
328328
InterfaceIDs.insert(Reg);

llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp

Lines changed: 30 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -76,18 +76,20 @@ getSymbolicOperandRequirements(SPIRV::OperandCategory::OperandCategory Category,
7676
SPIRV::RequirementHandler &Reqs) {
7777
static AvoidCapabilitiesSet
7878
AvoidCaps; // contains capabilities to avoid if there is another option
79-
unsigned ReqMinVer = getSymbolicOperandMinVersion(Category, i);
80-
unsigned ReqMaxVer = getSymbolicOperandMaxVersion(Category, i);
81-
unsigned TargetVer = ST.getSPIRVVersion();
82-
bool MinVerOK = !ReqMinVer || !TargetVer || TargetVer >= ReqMinVer;
83-
bool MaxVerOK = !ReqMaxVer || !TargetVer || TargetVer <= ReqMaxVer;
79+
80+
VersionTuple ReqMinVer = getSymbolicOperandMinVersion(Category, i);
81+
VersionTuple ReqMaxVer = getSymbolicOperandMaxVersion(Category, i);
82+
VersionTuple SPIRVVersion = ST.getSPIRVVersion();
83+
bool MinVerOK = SPIRVVersion.empty() || SPIRVVersion >= ReqMinVer;
84+
bool MaxVerOK =
85+
ReqMaxVer.empty() || SPIRVVersion.empty() || SPIRVVersion <= ReqMaxVer;
8486
CapabilityList ReqCaps = getSymbolicOperandCapabilities(Category, i);
8587
ExtensionList ReqExts = getSymbolicOperandExtensions(Category, i);
8688
if (ReqCaps.empty()) {
8789
if (ReqExts.empty()) {
8890
if (MinVerOK && MaxVerOK)
8991
return {true, {}, {}, ReqMinVer, ReqMaxVer};
90-
return {false, {}, {}, 0, 0};
92+
return {false, {}, {}, VersionTuple(), VersionTuple()};
9193
}
9294
} else if (MinVerOK && MaxVerOK) {
9395
if (ReqCaps.size() == 1) {
@@ -118,9 +120,13 @@ getSymbolicOperandRequirements(SPIRV::OperandCategory::OperandCategory Category,
118120
if (llvm::all_of(ReqExts, [&ST](const SPIRV::Extension::Extension &Ext) {
119121
return ST.canUseExtension(Ext);
120122
})) {
121-
return {true, {}, ReqExts, 0, 0}; // TODO: add versions to extensions.
123+
return {true,
124+
{},
125+
ReqExts,
126+
VersionTuple(),
127+
VersionTuple()}; // TODO: add versions to extensions.
122128
}
123-
return {false, {}, {}, 0, 0};
129+
return {false, {}, {}, VersionTuple(), VersionTuple()};
124130
}
125131

126132
void SPIRVModuleAnalysis::setBaseInfo(const Module &M) {
@@ -510,25 +516,25 @@ void SPIRV::RequirementHandler::addRequirements(
510516

511517
addExtensions(Req.Exts);
512518

513-
if (Req.MinVer) {
514-
if (MaxVersion && Req.MinVer > MaxVersion) {
519+
if (!Req.MinVer.empty()) {
520+
if (!MaxVersion.empty() && Req.MinVer > MaxVersion) {
515521
LLVM_DEBUG(dbgs() << "Conflicting version requirements: >= " << Req.MinVer
516522
<< " and <= " << MaxVersion << "\n");
517523
report_fatal_error("Adding SPIR-V requirements that can't be satisfied.");
518524
}
519525

520-
if (MinVersion == 0 || Req.MinVer > MinVersion)
526+
if (MinVersion.empty() || Req.MinVer > MinVersion)
521527
MinVersion = Req.MinVer;
522528
}
523529

524-
if (Req.MaxVer) {
525-
if (MinVersion && Req.MaxVer < MinVersion) {
530+
if (!Req.MaxVer.empty()) {
531+
if (!MinVersion.empty() && Req.MaxVer < MinVersion) {
526532
LLVM_DEBUG(dbgs() << "Conflicting version requirements: <= " << Req.MaxVer
527533
<< " and >= " << MinVersion << "\n");
528534
report_fatal_error("Adding SPIR-V requirements that can't be satisfied.");
529535
}
530536

531-
if (MaxVersion == 0 || Req.MaxVer < MaxVersion)
537+
if (MaxVersion.empty() || Req.MaxVer < MaxVersion)
532538
MaxVersion = Req.MaxVer;
533539
}
534540
}
@@ -539,22 +545,22 @@ void SPIRV::RequirementHandler::checkSatisfiable(
539545
bool IsSatisfiable = true;
540546
auto TargetVer = ST.getSPIRVVersion();
541547

542-
if (MaxVersion && TargetVer && MaxVersion < TargetVer) {
548+
if (!MaxVersion.empty() && !TargetVer.empty() && MaxVersion < TargetVer) {
543549
LLVM_DEBUG(
544550
dbgs() << "Target SPIR-V version too high for required features\n"
545551
<< "Required max version: " << MaxVersion << " target version "
546552
<< TargetVer << "\n");
547553
IsSatisfiable = false;
548554
}
549555

550-
if (MinVersion && TargetVer && MinVersion > TargetVer) {
556+
if (!MinVersion.empty() && !TargetVer.empty() && MinVersion > TargetVer) {
551557
LLVM_DEBUG(dbgs() << "Target SPIR-V version too low for required features\n"
552558
<< "Required min version: " << MinVersion
553559
<< " target version " << TargetVer << "\n");
554560
IsSatisfiable = false;
555561
}
556562

557-
if (MinVersion && MaxVersion && MinVersion > MaxVersion) {
563+
if (!MinVersion.empty() && !MaxVersion.empty() && MinVersion > MaxVersion) {
558564
LLVM_DEBUG(
559565
dbgs()
560566
<< "Version is too low for some features and too high for others.\n"
@@ -632,20 +638,21 @@ void RequirementHandler::initAvailableCapabilitiesForOpenCL(
632638
addAvailableCaps({Capability::ImageBasic, Capability::LiteralSampler,
633639
Capability::Image1D, Capability::SampledBuffer,
634640
Capability::ImageBuffer});
635-
if (ST.isAtLeastOpenCLVer(20))
641+
if (ST.isAtLeastOpenCLVer(VersionTuple(2, 0)))
636642
addAvailableCaps({Capability::ImageReadWrite});
637643
}
638-
if (ST.isAtLeastSPIRVVer(11) && ST.isAtLeastOpenCLVer(22))
644+
if (ST.isAtLeastSPIRVVer(VersionTuple(1, 1)) &&
645+
ST.isAtLeastOpenCLVer(VersionTuple(2, 2)))
639646
addAvailableCaps({Capability::SubgroupDispatch, Capability::PipeStorage});
640-
if (ST.isAtLeastSPIRVVer(13))
647+
if (ST.isAtLeastSPIRVVer(VersionTuple(1, 3)))
641648
addAvailableCaps({Capability::GroupNonUniform,
642649
Capability::GroupNonUniformVote,
643650
Capability::GroupNonUniformArithmetic,
644651
Capability::GroupNonUniformBallot,
645652
Capability::GroupNonUniformClustered,
646653
Capability::GroupNonUniformShuffle,
647654
Capability::GroupNonUniformShuffleRelative});
648-
if (ST.isAtLeastSPIRVVer(14))
655+
if (ST.isAtLeastSPIRVVer(VersionTuple(1, 4)))
649656
addAvailableCaps({Capability::DenormPreserve, Capability::DenormFlushToZero,
650657
Capability::SignedZeroInfNanPreserve,
651658
Capability::RoundingModeRTE,
@@ -1162,7 +1169,8 @@ static void collectReqs(const Module &M, SPIRV::ModuleAnalysisInfo &MAI,
11621169
auto Node = M.getNamedMetadata("spirv.ExecutionMode");
11631170
if (Node) {
11641171
// SPV_KHR_float_controls is not available until v1.4
1165-
bool RequireFloatControls = false, VerLower14 = !ST.isAtLeastSPIRVVer(14);
1172+
bool RequireFloatControls = false,
1173+
VerLower14 = !ST.isAtLeastSPIRVVer(VersionTuple(1, 4));
11661174
for (unsigned i = 0; i < Node->getNumOperands(); i++) {
11671175
MDNode *MDN = cast<MDNode>(Node->getOperand(i));
11681176
const MDOperand &MDOp = MDN->getOperand(1);

llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.h

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,13 @@ struct Requirements {
4545
const bool IsSatisfiable;
4646
const std::optional<Capability::Capability> Cap;
4747
const ExtensionList Exts;
48-
const unsigned MinVer; // 0 if no min version is required.
49-
const unsigned MaxVer; // 0 if no max version is required.
48+
const VersionTuple MinVer; // 0 if no min version is required.
49+
const VersionTuple MaxVer; // 0 if no max version is required.
5050

5151
Requirements(bool IsSatisfiable = false,
5252
std::optional<Capability::Capability> Cap = {},
53-
ExtensionList Exts = {}, unsigned MinVer = 0,
54-
unsigned MaxVer = 0)
53+
ExtensionList Exts = {}, VersionTuple MinVer = VersionTuple(),
54+
VersionTuple MaxVer = VersionTuple())
5555
: IsSatisfiable(IsSatisfiable), Cap(Cap), Exts(Exts), MinVer(MinVer),
5656
MaxVer(MaxVer) {}
5757
Requirements(Capability::Capability Cap) : Requirements(true, {Cap}) {}
@@ -69,8 +69,8 @@ struct RequirementHandler {
6969
DenseSet<unsigned> AvailableCaps;
7070

7171
SmallSet<Extension::Extension, 4> AllExtensions;
72-
unsigned MinVersion; // 0 if no min version is defined.
73-
unsigned MaxVersion; // 0 if no max version is defined.
72+
VersionTuple MinVersion; // 0 if no min version is defined.
73+
VersionTuple MaxVersion; // 0 if no max version is defined.
7474
// Add capabilities to AllCaps, recursing through their implicitly declared
7575
// capabilities too.
7676
void recursiveAddCapabilities(const CapabilityList &ToPrune);
@@ -79,17 +79,15 @@ struct RequirementHandler {
7979
void initAvailableCapabilitiesForVulkan(const SPIRVSubtarget &ST);
8080

8181
public:
82-
RequirementHandler() : MinVersion(0), MaxVersion(0) {}
82+
RequirementHandler() {}
8383
void clear() {
8484
MinimalCaps.clear();
8585
AllCaps.clear();
8686
AvailableCaps.clear();
8787
AllExtensions.clear();
88-
MinVersion = 0;
89-
MaxVersion = 0;
88+
MinVersion = VersionTuple();
89+
MaxVersion = VersionTuple();
9090
}
91-
unsigned getMinVersion() const { return MinVersion; }
92-
unsigned getMaxVersion() const { return MaxVersion; }
9391
const CapabilityList &getMinimalCapabilities() const { return MinimalCaps; }
9492
const SmallSet<Extension::Extension, 4> &getExtensions() const {
9593
return AllExtensions;

llvm/lib/Target/SPIRV/SPIRVSubtarget.cpp

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -39,18 +39,43 @@ static cl::opt<std::set<SPIRV::Extension::Extension>, false,
3939
cl::desc("Specify list of enabled SPIR-V extensions"));
4040

4141
// Compare version numbers, but allow 0 to mean unspecified.
42-
static bool isAtLeastVer(uint32_t Target, uint32_t VerToCompareTo) {
43-
return Target == 0 || Target >= VerToCompareTo;
42+
static bool isAtLeastVer(VersionTuple Target, VersionTuple VerToCompareTo) {
43+
return Target.empty() || Target >= VerToCompareTo;
4444
}
4545

4646
SPIRVSubtarget::SPIRVSubtarget(const Triple &TT, const std::string &CPU,
4747
const std::string &FS,
4848
const SPIRVTargetMachine &TM)
4949
: SPIRVGenSubtargetInfo(TT, CPU, /*TuneCPU=*/CPU, FS),
50-
PointerSize(TM.getPointerSizeInBits(/* AS= */ 0)), SPIRVVersion(0),
51-
OpenCLVersion(0), InstrInfo(),
50+
PointerSize(TM.getPointerSizeInBits(/* AS= */ 0)), InstrInfo(),
5251
FrameLowering(initSubtargetDependencies(CPU, FS)), TLInfo(TM, *this),
5352
TargetTriple(TT) {
53+
switch (TT.getSubArch()) {
54+
case Triple::SPIRVSubArch_v10:
55+
SPIRVVersion = VersionTuple(1, 0);
56+
break;
57+
case Triple::SPIRVSubArch_v11:
58+
SPIRVVersion = VersionTuple(1, 1);
59+
break;
60+
case Triple::SPIRVSubArch_v12:
61+
SPIRVVersion = VersionTuple(1, 2);
62+
break;
63+
case Triple::SPIRVSubArch_v13:
64+
SPIRVVersion = VersionTuple(1, 3);
65+
break;
66+
case Triple::SPIRVSubArch_v14:
67+
default:
68+
SPIRVVersion = VersionTuple(1, 4);
69+
break;
70+
case Triple::SPIRVSubArch_v15:
71+
SPIRVVersion = VersionTuple(1, 5);
72+
break;
73+
case Triple::SPIRVSubArch_v16:
74+
SPIRVVersion = VersionTuple(1, 6);
75+
break;
76+
}
77+
OpenCLVersion = VersionTuple(2, 2);
78+
5479
// The order of initialization is important.
5580
initAvailableExtensions();
5681
initAvailableExtInstSets();
@@ -66,10 +91,6 @@ SPIRVSubtarget::SPIRVSubtarget(const Triple &TT, const std::string &CPU,
6691
SPIRVSubtarget &SPIRVSubtarget::initSubtargetDependencies(StringRef CPU,
6792
StringRef FS) {
6893
ParseSubtargetFeatures(CPU, /*TuneCPU=*/CPU, FS);
69-
if (SPIRVVersion == 0)
70-
SPIRVVersion = 14;
71-
if (OpenCLVersion == 0)
72-
OpenCLVersion = 22;
7394
return *this;
7495
}
7596

@@ -82,11 +103,11 @@ bool SPIRVSubtarget::canUseExtInstSet(
82103
return AvailableExtInstSets.contains(E);
83104
}
84105

85-
bool SPIRVSubtarget::isAtLeastSPIRVVer(uint32_t VerToCompareTo) const {
106+
bool SPIRVSubtarget::isAtLeastSPIRVVer(VersionTuple VerToCompareTo) const {
86107
return isAtLeastVer(SPIRVVersion, VerToCompareTo);
87108
}
88109

89-
bool SPIRVSubtarget::isAtLeastOpenCLVer(uint32_t VerToCompareTo) const {
110+
bool SPIRVSubtarget::isAtLeastOpenCLVer(VersionTuple VerToCompareTo) const {
90111
if (!isOpenCLEnv())
91112
return false;
92113
return isAtLeastVer(OpenCLVersion, VerToCompareTo);
@@ -95,7 +116,7 @@ bool SPIRVSubtarget::isAtLeastOpenCLVer(uint32_t VerToCompareTo) const {
95116
// If the SPIR-V version is >= 1.4 we can call OpPtrEqual and OpPtrNotEqual.
96117
// In SPIR-V Translator compatibility mode this feature is not available.
97118
bool SPIRVSubtarget::canDirectlyComparePointers() const {
98-
return !SPVTranslatorCompat && isAtLeastVer(SPIRVVersion, 14);
119+
return !SPVTranslatorCompat && isAtLeastVer(SPIRVVersion, VersionTuple(1, 4));
99120
}
100121

101122
void SPIRVSubtarget::initAvailableExtensions() {

0 commit comments

Comments
 (0)