Skip to content

Commit 9d1e9d5

Browse files
aratajewigcbot
authored andcommitted
Allow to define both experimental and non-experimental SPIRV extension support
Allow to define both experimental and non-experimental SPIRV extension support
1 parent 6b2bd17 commit 9d1e9d5

File tree

4 files changed

+304
-243
lines changed

4 files changed

+304
-243
lines changed

IGC/AdaptorOCL/Utils/IGCCSPIRVSupportTblGen/IGCCSPIRVPlatformSupport.h

Lines changed: 84 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ SPDX-License-Identifier: MIT
1313

1414
#include "llvm/ADT/StringRef.h"
1515
#include "llvm/ADT/SmallVector.h"
16+
#include "llvm/ADT/StringMap.h"
1617
#include "llvm/TableGen/Record.h"
1718
#include "llvm/TableGen/Error.h"
1819
#include "llvm/Support/SourceMgr.h"
@@ -45,17 +46,17 @@ enum class PlatformSupportKind {
4546
// Capability entry for an extension.
4647
struct CapabilityEntry {
4748
StringRef Name;
48-
const Record *Root; // original capability record
49-
const Record *EffectivePlatform; // resolved support (extension or capability)
49+
const Record *Root; // original capability record
50+
const Record *ProductionSupport = nullptr;
51+
const Record *ExperimentalSupport = nullptr;
5052
};
5153

5254
struct ExtensionEntry {
5355
StringRef Name;
5456
StringRef SpecURL;
55-
const Record *Root; // original extension record
56-
const Record *Platforms; // extension support record (raw)
57-
bool IsInheritFromCapabilitiesMode; // true if extSupport == InheritFromCapabilities (aggregated capability mode)
58-
bool IsExperimental; // true if extension is experimental, false if production
57+
const Record *Root; // original extension record (first encountered)
58+
const Record *ProductionSupport = nullptr;
59+
const Record *ExperimentalSupport = nullptr;
5960
SmallVector<CapabilityEntry, 8> Capabilities;
6061
};
6162

@@ -126,33 +127,91 @@ static void validateExtensionPlatformSupport(const Record *Ext) {
126127
}
127128

128129

130+
// Validate duplicate extension definitions policy: at most two per name,
131+
// and if two exist, exactly one must be Experimental and one Production.
132+
static void validateExtensionMultiplicityPolicy(const RecordKeeper &Records) {
133+
auto AllExtensions = Records.getAllDerivedDefinitions("SPIRVExtension");
134+
llvm::StringMap<SmallVector<const Record *, 4>> Groups;
135+
for (const Record *Ext : AllExtensions) {
136+
StringRef Name = Ext->getValueAsString("ExtName");
137+
Groups[Name].push_back(Ext);
138+
}
139+
140+
for (auto &KV : Groups) {
141+
const StringRef ExtName = KV.first();
142+
auto &Defs = KV.second;
143+
if (Defs.size() > 2) {
144+
// Report error on the third definition location as a representative.
145+
PrintFatalError(
146+
Defs[2]->getLoc(),
147+
"Extension '" + ExtName.str() +
148+
"' defined more than twice; only one production and one experimental definition are allowed.");
149+
}
150+
if (Defs.size() == 2) {
151+
bool e0 = Defs[0]->getValueAsBit("Experimental");
152+
bool e1 = Defs[1]->getValueAsBit("Experimental");
153+
if (e0 == e1) {
154+
// Both production or both experimental — invalid.
155+
PrintFatalError(Defs[1]->getLoc(), "Extension '" + ExtName.str() +
156+
"' has two definitions with the same Experimental flag; one must be "
157+
"marked experimental and the other production.");
158+
}
159+
}
160+
}
161+
}
162+
129163
static SPIRVExtensions collectSPIRVExtensionSupport(const RecordKeeper &Records) {
164+
// Enforce duplicate-definition policy before merging.
165+
validateExtensionMultiplicityPolicy(Records);
130166
SPIRVExtensions Result;
131167
auto AllExtensions = Records.getAllDerivedDefinitions("SPIRVExtension");
132168
Result.reserve(AllExtensions.size());
169+
llvm::StringMap<size_t> ExtIndex; // fast lookup of merged extension entries by name
170+
133171
for (const Record *Ext : AllExtensions) {
134172
validateExtensionPlatformSupport(Ext);
135-
ExtensionEntry Entry;
136-
Entry.Name = Ext->getValueAsString("ExtName");
137-
Entry.SpecURL = Ext->getValueAsString("ExtSpecURL");
138-
Entry.Root = Ext;
139-
Entry.Platforms = Ext->getValueAsDef("ExtSupport");
140-
Entry.IsInheritFromCapabilitiesMode = Entry.Platforms->getName() == "InheritFromCapabilities";
141-
Entry.IsExperimental = Ext->getValueAsBit("Experimental");
142-
auto Caps = Ext->getValueAsListOfDefs("ExtCapabilities");
143-
for (const Record *Cap : Caps) {
144-
CapabilityEntry CapEntry;
145-
CapEntry.Name = Cap->getValueAsString("Name");
146-
CapEntry.Root = Cap;
147-
const Record *CapSupport = Cap->getValueAsDef("Support");
148-
if (CapSupport->getName() == "InheritFromExtension" && Entry.Platforms->getName() != "InheritFromCapabilities") {
149-
CapEntry.EffectivePlatform = Entry.Platforms;
150-
} else {
151-
CapEntry.EffectivePlatform = CapSupport; // explicit capability support or InheritFromCapabilities mode
173+
174+
StringRef ExtName = Ext->getValueAsString("ExtName");
175+
const Record *ExtSupport = Ext->getValueAsDef("ExtSupport");
176+
bool IsExperimental = Ext->getValueAsBit("Experimental");
177+
178+
// Look up or create merged extension entry via map
179+
if (ExtIndex.find(ExtName) == ExtIndex.end()) {
180+
ExtIndex[ExtName] = Result.size();
181+
Result.push_back(ExtensionEntry{});
182+
Result.back().Name = ExtName;
183+
Result.back().SpecURL = Ext->getValueAsString("ExtSpecURL");
184+
Result.back().Root = Ext;
185+
}
186+
ExtensionEntry &Entry = Result[ExtIndex[ExtName]];
187+
188+
// Assign variant-specific platform support
189+
(IsExperimental ? Entry.ExperimentalSupport : Entry.ProductionSupport) = ExtSupport;
190+
191+
// Merge capabilities by name and set variant-specific effective platform
192+
bool ExtAggregateMode = (ExtSupport->getName() == "InheritFromCapabilities");
193+
for (const Record *Cap : Ext->getValueAsListOfDefs("ExtCapabilities")) {
194+
StringRef CapName = Cap->getValueAsString("Name");
195+
CapabilityEntry *CapEntry = nullptr;
196+
for (auto &CE : Entry.Capabilities) {
197+
if (CE.Name == CapName) {
198+
CapEntry = &CE;
199+
break;
200+
}
152201
}
153-
Entry.Capabilities.push_back(CapEntry);
202+
if (!CapEntry) {
203+
Entry.Capabilities.push_back(CapabilityEntry{});
204+
CapEntry = &Entry.Capabilities.back();
205+
CapEntry->Name = CapName;
206+
CapEntry->Root = Cap;
207+
}
208+
209+
const Record *CapSupport = Cap->getValueAsDef("Support");
210+
const Record *Effective =
211+
(CapSupport->getName() == "InheritFromExtension" && !ExtAggregateMode) ? ExtSupport : CapSupport;
212+
213+
(IsExperimental ? CapEntry->ExperimentalSupport : CapEntry->ProductionSupport) = Effective;
154214
}
155-
Result.push_back(std::move(Entry));
156215
}
157216
return Result;
158217
}

IGC/AdaptorOCL/Utils/IGCCSPIRVSupportTblGen/IGCCSPIRVSupportTblGen.cpp

Lines changed: 63 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -62,23 +62,30 @@ void SPIRVSupportDocsEmitter::emit(raw_ostream &OS) {
6262
OS << "This document lists all SPIR-V extensions supported by IGC and their platform requirements.\n\n";
6363
for (const auto &Ext : Extensions) {
6464
OS << "## " << Ext.Name << "\n\n";
65-
if (Ext.IsExperimental) {
66-
OS << "> **Note**: The support for this extension is experimental. It has been implemented but has not been "
67-
"thoroughly tested "
68-
"and should not be used in production environments.\n\n";
65+
bool HasProd = (Ext.ProductionSupport != nullptr);
66+
bool HasExp = (Ext.ExperimentalSupport != nullptr);
67+
OS << "**Specification**: " << Ext.SpecURL << "\n\n";
68+
// Print explicit extension platform support when not inheriting from capabilities
69+
if (HasProd && Ext.ProductionSupport->getName() != "InheritFromCapabilities") {
70+
OS << "> **Supported on**: " << formatPlatformSupport(Ext.ProductionSupport) << "\n\n";
6971
}
70-
OS << "**Specification:** " << Ext.SpecURL << "\n\n";
71-
if (!Ext.IsInheritFromCapabilitiesMode) {
72-
OS << "**Extension Platform Support:** " << formatPlatformSupport(Ext.Platforms) << "\n\n";
72+
if (HasExp && Ext.ExperimentalSupport->getName() != "InheritFromCapabilities") {
73+
OS << "> **Experimentally supported on**: " << formatPlatformSupport(Ext.ExperimentalSupport) << "\n\n";
7374
}
74-
OS << "**Capabilities:**\n\n";
75+
OS << "**Capabilities**:\n\n";
7576
if (Ext.Capabilities.empty()) {
7677
OS << "- No capabilities defined\n";
7778
} else {
7879
for (const auto &Cap : Ext.Capabilities) {
7980
OS << "- **" << Cap.Name << "**";
80-
if (shouldShowCapabilityPlatformSupport(Ext, Cap))
81-
OS << "\n - Platform Support: " << formatPlatformSupport(Cap.EffectivePlatform);
81+
if (shouldShowCapabilityPlatformSupport(Ext, Cap)) {
82+
if (Cap.ProductionSupport) {
83+
OS << "\n > **Supported On**: " << formatPlatformSupport(Cap.ProductionSupport);
84+
}
85+
if (Cap.ExperimentalSupport) {
86+
OS << "\n > **Experimentally supported on**: " << formatPlatformSupport(Cap.ExperimentalSupport);
87+
}
88+
}
8289
OS << "\n";
8390
}
8491
}
@@ -88,7 +95,11 @@ void SPIRVSupportDocsEmitter::emit(raw_ostream &OS) {
8895

8996
bool SPIRVSupportDocsEmitter::shouldShowCapabilityPlatformSupport(const ExtensionEntry &Ext,
9097
const CapabilityEntry &Cap) const {
91-
if (Ext.IsInheritFromCapabilitiesMode)
98+
// Show capability platform support if extension inherits from capabilities in any variant,
99+
// or if the capability specified explicit support (not InheritFromExtension).
100+
bool ExtInherit = (Ext.ProductionSupport && Ext.ProductionSupport->getName() == "InheritFromCapabilities") ||
101+
(Ext.ExperimentalSupport && Ext.ExperimentalSupport->getName() == "InheritFromCapabilities");
102+
if (ExtInherit)
92103
return true;
93104
const Record *OriginalCapSupport = Cap.Root->getValueAsDef("Support");
94105
return OriginalCapSupport->getName() != "InheritFromExtension";
@@ -281,7 +292,7 @@ class SPIRVSupportQueriesEmitter {
281292
void emitGetSupportedInfoFn(raw_ostream &OS);
282293
std::string buildPredicate(const Record *Support, StringRef platformVar);
283294
void emitSingleExtensionSupportIf(raw_ostream &OS, const ExtensionEntry &Ext);
284-
void emitSingleCapabilitySupportIf(raw_ostream &OS, StringRef CapName, const Record *Support);
295+
void emitSingleCapabilitySupportIf(raw_ostream &OS, const CapabilityEntry &Cap);
285296
std::string buildAutoExtensionPredicate(const ExtensionEntry &Ext);
286297

287298
public:
@@ -321,14 +332,15 @@ void SPIRVSupportQueriesEmitter::emitSPIRVExtensionStructures(raw_ostream &OS) {
321332
OS << " std::string Name;\n";
322333
OS << " std::string SpecURL;\n";
323334
OS << " std::vector<SPIRVCapability> Capabilities;\n";
324-
OS << " bool IsExperimental;\n";
325335
OS << "};\n\n";
326336
}
327337

328338
void SPIRVSupportQueriesEmitter::emitForwardDecls(raw_ostream &OS) {
329339
OS << "// Forward declarations\n";
330-
OS << "inline bool isExtensionSupported(const std::string& ExtensionName, PLATFORM Platform);\n";
331-
OS << "inline bool isCapabilitySupported(const std::string& CapabilityName, PLATFORM Platform);\n\n";
340+
OS << "inline bool isExtensionSupported(const std::string& ExtensionName, PLATFORM Platform, bool "
341+
"includeExperimental);\n";
342+
OS << "inline bool isCapabilitySupported(const std::string& CapabilityName, PLATFORM Platform, bool "
343+
"includeExperimental);\n\n";
332344
}
333345

334346
void SPIRVSupportQueriesEmitter::emitExtensionsVector(raw_ostream &OS) {
@@ -346,8 +358,7 @@ void SPIRVSupportQueriesEmitter::emitExtensionsVector(raw_ostream &OS) {
346358
OS << ",";
347359
OS << "\n";
348360
}
349-
OS << " },\n";
350-
OS << " " << (Ext.IsExperimental ? "true" : "false") << "\n";
361+
OS << " }\n";
351362
OS << " }" << (std::next(It) != Extensions.end() ? "," : "") << "\n";
352363
}
353364
OS << "};\n\n";
@@ -361,49 +372,64 @@ std::string SPIRVSupportQueriesEmitter::buildAutoExtensionPredicate(const Extens
361372
for (const auto &Cap : Ext.Capabilities) {
362373
if (!First)
363374
Expr += " || ";
364-
Expr += "isCapabilitySupported(\"" + Cap.Name.str() + "\", Platform)";
375+
Expr += "isCapabilitySupported(\"" + Cap.Name.str() + "\", Platform, includeExperimental)";
365376
First = false;
366377
}
367378
return Expr;
368379
}
369380

370381
void SPIRVSupportQueriesEmitter::emitSingleExtensionSupportIf(raw_ostream &OS, const ExtensionEntry &Ext) {
371382
OS << " if (ExtensionName == \"" << Ext.Name << "\") {\n";
372-
if (Ext.IsInheritFromCapabilitiesMode) {
373-
OS << " return " << buildAutoExtensionPredicate(Ext) << ";\n";
374-
} else {
375-
OS << " return " << buildPredicate(Ext.Platforms, "Platform") << ";\n";
383+
// Experimental variant (gated by includeExperimental)
384+
if (Ext.ExperimentalSupport) {
385+
OS << " if (includeExperimental) {\n";
386+
if (Ext.ExperimentalSupport->getName() == "InheritFromCapabilities") {
387+
OS << " if (" << buildAutoExtensionPredicate(Ext) << ") return true;\n";
388+
} else {
389+
OS << " if (" << buildPredicate(Ext.ExperimentalSupport, "Platform") << ") return true;\n";
390+
}
391+
OS << " }\n";
392+
}
393+
// Production variant
394+
if (Ext.ProductionSupport) {
395+
if (Ext.ProductionSupport->getName() == "InheritFromCapabilities") {
396+
OS << " if (" << buildAutoExtensionPredicate(Ext) << ") return true;\n";
397+
} else {
398+
OS << " if (" << buildPredicate(Ext.ProductionSupport, "Platform") << ") return true;\n";
399+
}
376400
}
377401
OS << " }\n";
378402
}
379403

380-
void SPIRVSupportQueriesEmitter::emitSingleCapabilitySupportIf(raw_ostream &OS, StringRef CapName,
381-
const Record *Support) {
382-
OS << " if (CapabilityName == \"" << CapName << "\") {\n";
383-
OS << " return " << buildPredicate(Support, "Platform") << ";\n";
404+
void SPIRVSupportQueriesEmitter::emitSingleCapabilitySupportIf(raw_ostream &OS, const CapabilityEntry &Cap) {
405+
OS << " if (CapabilityName == \"" << Cap.Name << "\") {\n";
406+
if (Cap.ExperimentalSupport) {
407+
OS << " if (includeExperimental) {\n";
408+
OS << " if (" << buildPredicate(Cap.ExperimentalSupport, "Platform") << ") return true;\n";
409+
OS << " }\n";
410+
}
411+
if (Cap.ProductionSupport) {
412+
OS << " if (" << buildPredicate(Cap.ProductionSupport, "Platform") << ") return true;\n";
413+
}
384414
OS << " }\n";
385415
}
386416

387417
void SPIRVSupportQueriesEmitter::emitExtensionSupportFn(raw_ostream &OS) {
388418
OS << "// Individual extension/capability query functions\n";
389-
OS << "inline bool isExtensionSupported(const std::string& ExtensionName, PLATFORM Platform) {\n";
419+
OS << "inline bool isExtensionSupported(const std::string& ExtensionName, PLATFORM Platform, bool "
420+
"includeExperimental) {\n";
390421
for (const auto &Ext : Extensions)
391422
emitSingleExtensionSupportIf(OS, Ext);
392423
OS << " return false;\n";
393424
OS << "}\n\n";
394425
}
395426

396427
void SPIRVSupportQueriesEmitter::emitCapabilitySupportFn(raw_ostream &OS) {
397-
OS << "inline bool isCapabilitySupported(const std::string& CapabilityName, PLATFORM Platform) {\n";
398-
SmallSet<StringRef, 32> Seen;
428+
OS << "inline bool isCapabilitySupported(const std::string& CapabilityName, PLATFORM Platform, bool "
429+
"includeExperimental) {\n";
399430
for (const auto &Ext : Extensions) {
400431
for (const auto &Cap : Ext.Capabilities) {
401-
if (!Seen.insert(Cap.Name).second) {
402-
PrintFatalError(Cap.Root->getLoc(), "Duplicate capability name '" + Cap.Name.str() +
403-
"' encountered in extension '" + Ext.Name.str() +
404-
"'. Capability names must be unique.");
405-
}
406-
emitSingleCapabilitySupportIf(OS, Cap.Name, Cap.EffectivePlatform);
432+
emitSingleCapabilitySupportIf(OS, Cap);
407433
}
408434
}
409435
OS << " return false;\n";
@@ -416,16 +442,12 @@ void SPIRVSupportQueriesEmitter::emitGetSupportedInfoFn(raw_ostream &OS) {
416442
"false) {\n";
417443
OS << " std::vector<SPIRVExtension> SupportedExtensions;\n";
418444
OS << " for (const auto& Ext : AllExtensions) {\n";
419-
OS << " if (!includeExperimental && Ext.IsExperimental) {\n";
420-
OS << " continue;\n";
421-
OS << " }\n";
422-
OS << " if (isExtensionSupported(Ext.Name, Platform)) {\n";
445+
OS << " if (isExtensionSupported(Ext.Name, Platform, includeExperimental)) {\n";
423446
OS << " SPIRVExtension SupportedExt;\n";
424447
OS << " SupportedExt.Name = Ext.Name;\n";
425448
OS << " SupportedExt.SpecURL = Ext.SpecURL;\n";
426-
OS << " SupportedExt.IsExperimental = Ext.IsExperimental;\n";
427449
OS << " for (const auto& Cap : Ext.Capabilities) {\n";
428-
OS << " if (isCapabilitySupported(Cap.Name, Platform)) {\n";
450+
OS << " if (isCapabilitySupported(Cap.Name, Platform, includeExperimental)) {\n";
429451
OS << " SupportedExt.Capabilities.push_back(Cap);\n";
430452
OS << " }\n";
431453
OS << " }\n";

IGC/AdaptorOCL/ocl_igc_interface/extensions/SPIRVExtensions_Common.td

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,26 @@ SPDX-License-Identifier: MIT
9696
// - If you want uniform platform support → use regular extension with explicit platform
9797
// - If you want per-capability platform support → use InheritFromCapabilities
9898
// - No mixing of the two approaches in a single extension
99+
//
100+
// 5. DUAL EXTENSION DEFINITIONS (OFFICIAL + EXPERIMENTAL):
101+
//
102+
// It is valid to describe the same SPIR-V extension name with two
103+
// TableGen records when some platforms support it experimentally and
104+
// others officially. Example pattern:
105+
//
106+
// def SPV_INTEL_example : Extension< // official support
107+
// "SPV_INTEL_example",
108+
// [ Capability<"ExampleCapabilityINTEL"> ],
109+
// "https://example.com/spec/SPV_INTEL_example.asciidoc",
110+
// SOME_OFFICIAL_PLATFORM_PREDICATE
111+
// >;
112+
//
113+
// def SPV_INTEL_example_experimental : ExperimentalExtension< // experimental
114+
// "SPV_INTEL_example", // same SPIR-V extension string
115+
// [ Capability<"ExampleCapabilityINTEL"> ],
116+
// "https://example.com/spec/SPV_INTEL_example.asciidoc",
117+
// SOME_EXPERIMENTAL_PLATFORM_PREDICATE
118+
// >;
99119

100120
class Platform<string ProductFamilyEnum> {
101121
string ProductFamily = ProductFamilyEnum;

0 commit comments

Comments
 (0)