1 // Copyright (c) 2014-2024 The Khronos Group Inc.
2 //
3 // Permission is hereby granted, free of charge, to any person obtaining a copy
4 // of this software and/or associated documentation files (the "Materials"),
5 // to deal in the Materials without restriction, including without limitation
6 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 // and/or sell copies of the Materials, and to permit persons to whom the
8 // Materials are furnished to do so, subject to the following conditions:
9 //
10 // The above copyright notice and this permission notice shall be included in
11 // all copies or substantial portions of the Materials.
12 //
13 // MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS
14 // STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND
15 // HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/
16 //
17 // THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 // FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS
23 // IN THE MATERIALS.
24
25 #include <assert.h>
26 #include <string.h>
27 #include <algorithm>
28 #include <cstdlib>
29 #include <iostream>
30 #include <unordered_map>
31 #include <unordered_set>
32 #include <utility>
33 #include <fstream>
34
35 #include "jsoncpp/dist/json/json.h"
36
37 #include "jsonToSpirv.h"
38
39 namespace {
40 // Returns true if the given string is a valid SPIR-V version.
validSpirvVersionString(const std::string s)41 bool validSpirvVersionString(const std::string s) {
42 return
43 s == "1.0" ||
44 s == "1.1" ||
45 s == "1.2" ||
46 s == "1.3" ||
47 s == "1.4" ||
48 s == "1.5" ||
49 s == "1.6";
50 }
51
52 // Returns true if the given string is a valid version
53 // specifier in the grammar file.
validSpirvVersionStringSpecifier(const std::string s)54 bool validSpirvVersionStringSpecifier(const std::string s) {
55 return s == "None" || validSpirvVersionString(s);
56 }
57 } // anonymous namespace
58
59 namespace spv {
60
IsLegacyDoublyEnabledInstruction(const std::string & instruction)61 bool IsLegacyDoublyEnabledInstruction(const std::string& instruction) {
62 static std::unordered_set<std::string> allowed = {
63 "OpSubgroupBallotKHR",
64 "OpSubgroupFirstInvocationKHR",
65 "OpSubgroupAllKHR",
66 "OpSubgroupAnyKHR",
67 "OpSubgroupAllEqualKHR",
68 "OpSubgroupReadInvocationKHR",
69 "OpTraceRayKHR",
70 "OpExecuteCallableKHR",
71 "OpConvertUToAccelerationStructureKHR",
72 "OpIgnoreIntersectionKHR",
73 "OpTerminateRayKHR",
74 "OpTypeRayQueryKHR",
75 "OpRayQueryInitializeKHR",
76 "OpRayQueryTerminateKHR",
77 "OpRayQueryGenerateIntersectionKHR",
78 "OpRayQueryConfirmIntersectionKHR",
79 "OpRayQueryProceedKHR",
80 "OpRayQueryGetIntersectionTypeKHR",
81 "OpGroupIAddNonUniformAMD",
82 "OpGroupFAddNonUniformAMD",
83 "OpGroupFMinNonUniformAMD",
84 "OpGroupUMinNonUniformAMD",
85 "OpGroupSMinNonUniformAMD",
86 "OpGroupFMaxNonUniformAMD",
87 "OpGroupUMaxNonUniformAMD",
88 "OpGroupSMaxNonUniformAMD",
89 "OpFragmentMaskFetchAMD",
90 "OpFragmentFetchAMD",
91 "OpImageSampleFootprintNV",
92 "OpGroupNonUniformPartitionNV",
93 "OpWritePackedPrimitiveIndices4x8NV",
94 "OpReportIntersectionNV",
95 "OpReportIntersectionKHR",
96 "OpIgnoreIntersectionNV",
97 "OpTerminateRayNV",
98 "OpTraceNV",
99 "OpTraceMotionNV",
100 "OpTraceRayMotionNV",
101 "OpTypeAccelerationStructureNV",
102 "OpTypeAccelerationStructureKHR",
103 "OpExecuteCallableNV",
104 "OpTypeCooperativeMatrixNV",
105 "OpCooperativeMatrixLoadNV",
106 "OpCooperativeMatrixStoreNV",
107 "OpCooperativeMatrixMulAddNV",
108 "OpCooperativeMatrixLengthNV",
109 "OpBeginInvocationInterlockEXT",
110 "OpEndInvocationInterlockEXT",
111 "OpIsHelperInvocationEXT",
112 "OpConstantFunctionPointerINTEL",
113 "OpFunctionPointerCallINTEL",
114 "OpAssumeTrueKHR",
115 "OpExpectKHR",
116 "OpLoopControlINTEL",
117 "OpAliasDomainDeclINTEL",
118 "OpAliasScopeDeclINTEL",
119 "OpAliasScopeListDeclINTEL",
120 "OpReadPipeBlockingINTEL",
121 "OpWritePipeBlockingINTEL",
122 "OpFPGARegINTEL",
123 "OpRayQueryGetRayTMinKHR",
124 "OpRayQueryGetRayFlagsKHR",
125 "OpRayQueryGetIntersectionTKHR",
126 "OpRayQueryGetIntersectionInstanceCustomIndexKHR",
127 "OpRayQueryGetIntersectionInstanceIdKHR",
128 "OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR",
129 "OpRayQueryGetIntersectionGeometryIndexKHR",
130 "OpRayQueryGetIntersectionPrimitiveIndexKHR",
131 "OpRayQueryGetIntersectionBarycentricsKHR",
132 "OpRayQueryGetIntersectionFrontFaceKHR",
133 "OpRayQueryGetIntersectionCandidateAABBOpaqueKHR",
134 "OpRayQueryGetIntersectionObjectRayDirectionKHR",
135 "OpRayQueryGetIntersectionObjectRayOriginKHR",
136 "OpRayQueryGetWorldRayDirectionKHR",
137 "OpRayQueryGetWorldRayOriginKHR",
138 "OpRayQueryGetIntersectionObjectToWorldKHR",
139 "OpRayQueryGetIntersectionWorldToObjectKHR",
140 "OpAtomicFAddEXT",
141 };
142 return allowed.count(instruction) != 0;
143 }
144
IsValid(OperandClass oc,const std::string & context) const145 bool EnumValue::IsValid(OperandClass oc, const std::string& context) const
146 {
147 bool result = true;
148 if (firstVersion.empty()) {
149 std::cerr << "Error: " << context << " " << name << " \"version\" must be set, probably to \"None\"" << std::endl;
150 result = false;
151 } else if (!validSpirvVersionStringSpecifier(firstVersion)) {
152 std::cerr << "Error: " << context << " " << name << " \"version\" is invalid: " << firstVersion << std::endl;
153 result = false;
154 }
155 if (!lastVersion.empty() && !validSpirvVersionString(lastVersion)) {
156 std::cerr << "Error: " << context << " " << name << " \"lastVersion\" is invalid: " << lastVersion << std::endl;
157 result = false;
158 }
159
160 // When a feature is introduced by an extension, the firstVersion is set to
161 // "None". There are three cases:
162 // - A new capability should be guarded/enabled by the extension
163 // - A new instruction should be:
164 // - Guarded/enabled by a new capability.
165 // - Not enabled by *both* a capability and an extension.
166 // There are many existing instructions that are already like this,
167 // and we grandparent them as allowed.
168 // - Other enums fall into two cases:
169 // 1. The enum is part of a new operand kind introduced by the extension.
170 // In this case we rely on transitivity: The use of the operand occurs
171 // in a new instruction that itself is guarded; or as the operand of
172 // another operand that itself is (recursively) guarded.
173 // 2. The enum is a new case in an existing operand kind. This case
174 // should be guarded by a capability. However, we do not check this
175 // here. Checking it requires more context than we have here.
176 if (oc == OperandOpcode) {
177 const bool instruction_unusable =
178 (firstVersion == "None") && extensions.empty() && capabilities.empty();
179 if (instruction_unusable) {
180 std::cerr << "Error: " << context << " " << name << " is not usable: "
181 << "its version is set to \"None\", and it is not enabled by a "
182 << "capability or extension. Guard it with a capability."
183 << std::endl;
184 result = false;
185 }
186 // Complain if an instruction is not in any core version and also enabled by
187 // both an extension and a capability.
188 // It's important to check the "not in any core version" case, because,
189 // for example, OpTerminateInvocation is in SPIR-V 1.6 *and* enabled by an
190 // extension, and guarded by the Shader capability.
191 const bool instruction_doubly_enabled = (firstVersion == "None") &&
192 !extensions.empty() &&
193 !capabilities.empty();
194 if (instruction_doubly_enabled && !IsLegacyDoublyEnabledInstruction(name)) {
195 std::cerr << "Error: " << context << " " << name << " is doubly-enabled: "
196 << "it is enabled by both a capability and an extension. "
197 << "Guard it with a capability only." << std::endl;
198 result = false;
199 }
200 }
201 if (oc == OperandCapability) {
202 // If capability X lists capabilities Y and Z, then Y and Z are *enabled*
203 // when X is enabled. They are not *guards* on X's use.
204 // Only versions and extensions can guard a capability.
205 const bool capability_unusable =
206 (firstVersion == "None") && extensions.empty();
207 if (capability_unusable) {
208 std::cerr << "Error: " << context << " " << name << " is not usable: "
209 << "its version is set to \"None\", and it is not enabled by "
210 << "an extension. Guard it with an extension." << std::endl;
211 result = false;
212 }
213 }
214
215 return result;
216 }
217
218 // The set of objects that hold all the instruction/operand
219 // parameterization information.
220 InstructionValues InstructionDesc;
221
222 // The ordered list (in printing order) of printing classes
223 // (specification subsections).
224 PrintingClasses InstructionPrintingClasses;
225
226 // Note: There is no entry for OperandOpcode. Use InstructionDesc instead.
227 EnumDefinition OperandClassParams[OperandOpcode];
228 EnumValues SourceLanguageParams;
229 EnumValues ExecutionModelParams;
230 EnumValues AddressingParams;
231 EnumValues MemoryParams;
232 EnumValues ExecutionModeParams;
233 EnumValues StorageParams;
234 EnumValues SamplerAddressingModeParams;
235 EnumValues SamplerFilterModeParams;
236 EnumValues ImageFormatParams;
237 EnumValues ImageChannelOrderParams;
238 EnumValues ImageChannelDataTypeParams;
239 EnumValues ImageOperandsParams;
240 EnumValues FPFastMathParams;
241 EnumValues FPRoundingModeParams;
242 EnumValues FPDenormModeParams;
243 EnumValues FPOperationModeParams;
244 EnumValues QuantizationModesParams;
245 EnumValues OverflowModesParams;
246 EnumValues LinkageTypeParams;
247 EnumValues DecorationParams;
248 EnumValues BuiltInParams;
249 EnumValues DimensionalityParams;
250 EnumValues FuncParamAttrParams;
251 EnumValues AccessQualifierParams;
252 EnumValues GroupOperationParams;
253 EnumValues LoopControlParams;
254 EnumValues SelectionControlParams;
255 EnumValues FunctionControlParams;
256 EnumValues MemorySemanticsParams;
257 EnumValues MemoryAccessParams;
258 EnumValues ScopeParams;
259 EnumValues KernelEnqueueFlagsParams;
260 EnumValues KernelProfilingInfoParams;
261 EnumValues CapabilityParams;
262 EnumValues RayFlagsParams;
263 EnumValues RayQueryIntersectionParams;
264 EnumValues RayQueryCommittedIntersectionTypeParams;
265 EnumValues RayQueryCandidateIntersectionTypeParams;
266 EnumValues FragmentShadingRateParams;
267 EnumValues PackedVectorFormatParams;
268 EnumValues CooperativeMatrixOperandsParams;
269 EnumValues CooperativeMatrixLayoutParams;
270 EnumValues CooperativeMatrixUseParams;
271 EnumValues InitializationModeQualifierParams;
272 EnumValues HostAccessQualifierParams;
273 EnumValues LoadCacheControlParams;
274 EnumValues StoreCacheControlParams;
275 EnumValues NamedMaximumNumberOfRegistersParams;
276 EnumValues RawAccessChainOperandsParams;
277
ReadFile(const std::string & path)278 std::pair<bool, std::string> ReadFile(const std::string& path)
279 {
280 std::ifstream fstream(path, std::ios::in);
281 if (fstream) {
282 std::string contents;
283 fstream.seekg(0, std::ios::end);
284 contents.reserve((unsigned int)fstream.tellg());
285 fstream.seekg(0, std::ios::beg);
286 contents.assign((std::istreambuf_iterator<char>(fstream)),
287 std::istreambuf_iterator<char>());
288 return std::make_pair(true, contents);
289 }
290 return std::make_pair(false, "");
291 }
292
293 struct ClassOptionality {
294 OperandClass type;
295 bool optional;
296 };
297
298 // Converts the |operandKind| and |quantifier| pair used to describe operands
299 // in the JSON grammar to OperandClass and optionality used in this repo.
ToOperandClassAndOptionality(const std::string & operandKind,const std::string & quantifier)300 ClassOptionality ToOperandClassAndOptionality(const std::string& operandKind, const std::string& quantifier)
301 {
302 assert(quantifier.empty() || quantifier == "?" || quantifier == "*");
303
304 if (operandKind == "IdRef") {
305 if (quantifier.empty())
306 return {OperandId, false};
307 else if (quantifier == "?")
308 return {OperandId, true};
309 else
310 return {OperandVariableIds, false};
311 } else if (operandKind == "LiteralInteger") {
312 if (quantifier.empty())
313 return {OperandLiteralNumber, false};
314 if (quantifier == "?")
315 return {OperandOptionalLiteral, true};
316 else
317 return {OperandVariableLiterals, false};
318 } else if (operandKind == "LiteralString") {
319 if (quantifier.empty())
320 return {OperandLiteralString, false};
321 else if (quantifier == "?")
322 return {OperandLiteralString, true};
323 else {
324 return {OperandOptionalLiteralStrings, false};
325 }
326 } else if (operandKind == "PairLiteralIntegerIdRef") {
327 // Used by OpSwitch in the grammar
328 return {OperandVariableLiteralId, false};
329 } else if (operandKind == "PairIdRefLiteralInteger") {
330 // Used by OpGroupMemberDecorate in the grammar
331 return {OperandVariableIdLiteral, false};
332 } else if (operandKind == "PairIdRefIdRef") {
333 // Used by OpPhi in the grammar
334 return {OperandVariableIds, false};
335 } else {
336 OperandClass type = OperandNone;
337 if (operandKind == "IdMemorySemantics" || operandKind == "MemorySemantics") {
338 type = OperandMemorySemantics;
339 } else if (operandKind == "IdScope" || operandKind == "Scope") {
340 type = OperandScope;
341 } else if (operandKind == "LiteralExtInstInteger") {
342 type = OperandLiteralNumber;
343 } else if (operandKind == "LiteralSpecConstantOpInteger") {
344 type = OperandLiteralNumber;
345 } else if (operandKind == "LiteralContextDependentNumber") {
346 type = OperandAnySizeLiteralNumber;
347 } else if (operandKind == "LiteralFloat") {
348 type = OperandLiteralNumber;
349 } else if (operandKind == "SourceLanguage") {
350 type = OperandSource;
351 } else if (operandKind == "ExecutionModel") {
352 type = OperandExecutionModel;
353 } else if (operandKind == "AddressingModel") {
354 type = OperandAddressing;
355 } else if (operandKind == "MemoryModel") {
356 type = OperandMemory;
357 } else if (operandKind == "ExecutionMode") {
358 type = OperandExecutionMode;
359 } else if (operandKind == "StorageClass") {
360 type = OperandStorage;
361 } else if (operandKind == "Dim") {
362 type = OperandDimensionality;
363 } else if (operandKind == "SamplerAddressingMode") {
364 type = OperandSamplerAddressingMode;
365 } else if (operandKind == "SamplerFilterMode") {
366 type = OperandSamplerFilterMode;
367 } else if (operandKind == "ImageFormat") {
368 type = OperandSamplerImageFormat;
369 } else if (operandKind == "ImageChannelOrder") {
370 type = OperandImageChannelOrder;
371 } else if (operandKind == "ImageChannelDataType") {
372 type = OperandImageChannelDataType;
373 } else if (operandKind == "FPRoundingMode") {
374 type = OperandFPRoundingMode;
375 } else if (operandKind == "FPDenormMode") {
376 type = OperandFPDenormMode;
377 } else if (operandKind == "FPOperationMode") {
378 type = OperandFPOperationMode;
379 } else if (operandKind == "QuantizationModes") {
380 type = OperandQuantizationModes;
381 } else if (operandKind == "OverflowModes") {
382 type = OperandOverflowModes;
383 } else if (operandKind == "LinkageType") {
384 type = OperandLinkageType;
385 } else if (operandKind == "AccessQualifier") {
386 type = OperandAccessQualifier;
387 } else if (operandKind == "FunctionParameterAttribute") {
388 type = OperandFuncParamAttr;
389 } else if (operandKind == "Decoration") {
390 type = OperandDecoration;
391 } else if (operandKind == "BuiltIn") {
392 type = OperandBuiltIn;
393 } else if (operandKind == "GroupOperation") {
394 type = OperandGroupOperation;
395 } else if (operandKind == "KernelEnqueueFlags") {
396 type = OperandKernelEnqueueFlags;
397 } else if (operandKind == "KernelProfilingInfo") {
398 type = OperandKernelProfilingInfo;
399 } else if (operandKind == "Capability") {
400 type = OperandCapability;
401 } else if (operandKind == "ImageOperands") {
402 type = OperandImageOperands;
403 } else if (operandKind == "FPFastMathMode") {
404 type = OperandFPFastMath;
405 } else if (operandKind == "SelectionControl") {
406 type = OperandSelect;
407 } else if (operandKind == "LoopControl") {
408 type = OperandLoop;
409 } else if (operandKind == "FunctionControl") {
410 type = OperandFunction;
411 } else if (operandKind == "MemoryAccess") {
412 type = OperandMemoryOperands;
413 } else if (operandKind == "RayFlags") {
414 type = OperandRayFlags;
415 } else if (operandKind == "RayQueryIntersection") {
416 type = OperandRayQueryIntersection;
417 } else if (operandKind == "RayQueryCommittedIntersectionType") {
418 type = OperandRayQueryCommittedIntersectionType;
419 } else if (operandKind == "RayQueryCandidateIntersectionType") {
420 type = OperandRayQueryCandidateIntersectionType;
421 } else if (operandKind == "FragmentShadingRate") {
422 type = OperandFragmentShadingRate;
423 } else if (operandKind == "PackedVectorFormat") {
424 type = OperandPackedVectorFormat;
425 } else if (operandKind == "CooperativeMatrixOperands") {
426 type = OperandCooperativeMatrixOperands;
427 } else if (operandKind == "CooperativeMatrixLayout") {
428 type = OperandCooperativeMatrixLayout;
429 } else if (operandKind == "CooperativeMatrixUse") {
430 type = OperandCooperativeMatrixUse;
431 } else if (operandKind == "InitializationModeQualifier") {
432 type = OperandInitializationModeQualifier;
433 } else if (operandKind == "HostAccessQualifier") {
434 type = OperandHostAccessQualifier;
435 } else if (operandKind == "LoadCacheControl") {
436 type = OperandLoadCacheControl;
437 } else if (operandKind == "StoreCacheControl") {
438 type = OperandStoreCacheControl;
439 } else if (operandKind == "NamedMaximumNumberOfRegisters") {
440 type = OperandNamedMaximumNumberOfRegisters;
441 } else if (operandKind == "RawAccessChainOperands") {
442 type = OperandRawAccessChainOperands;
443 }
444
445 if (type == OperandNone) {
446 std::cerr << "Unhandled operand kind found: " << operandKind << std::endl;
447 exit(1);
448 }
449 return {type, !quantifier.empty()};
450 }
451 }
452
IsTypeOrResultId(const std::string & str,bool * isType,bool * isResult)453 bool IsTypeOrResultId(const std::string& str, bool* isType, bool* isResult)
454 {
455 if (str == "IdResultType")
456 return *isType = true;
457 if (str == "IdResult")
458 return *isResult = true;
459 return false;
460 }
461
462 // Given a number string, returns the position of the only bits set in the number.
463 // So it requires the number is a power of two.
NumberStringToBit(const std::string & str)464 unsigned int NumberStringToBit(const std::string& str)
465 {
466 char* parseEnd;
467 unsigned int value = (unsigned int)std::strtol(str.c_str(), &parseEnd, 16);
468 assert(!(value & (value - 1)) && "input number is not a power of 2");
469 unsigned int bit = 0;
470 for (; value; value >>= 1) ++bit;
471 return bit;
472 }
473
474 // Given two pairs (name and in core) compares if the order is correct for naming
475 // conventions. The conventions are:
476 // * Core
477 // * KHR
478 // * EXT
479 // * Vendor (no preference between vendors)
480 //
481 // Returns true if the order is valid.
SuffixComparison(const std::string & prev,bool prevCore,const std::string & cur,bool curCore)482 bool SuffixComparison(const std::string& prev, bool prevCore,
483 const std::string& cur, bool curCore)
484 {
485 // Duplicate entry
486 if (prev == cur) return false;
487
488 if (prevCore) return true;
489 if (curCore) return false;
490
491 // Both are suffixed names.
492 const bool prevKHR = prev.substr(prev.size() - 3) == "KHR";
493 const bool prevEXT = prev.substr(prev.size() - 3) == "EXT";
494 const bool curKHR = cur.substr(cur.size() - 3) == "KHR";
495 const bool curEXT = cur.substr(cur.size() - 3) == "EXT";
496
497 if (prevKHR) return true;
498 if (curKHR) return false;
499 if (prevEXT) return true;
500 if (curEXT) return false;
501
502 return true;
503 }
504
jsonToSpirv(const std::string & jsonPath,bool buildingHeaders)505 void jsonToSpirv(const std::string& jsonPath, bool buildingHeaders)
506 {
507 // only do this once.
508 static bool initialized = false;
509 if (initialized)
510 return;
511 initialized = true;
512
513 size_t errorCount = 0;
514
515 // Read the JSON grammar file.
516 bool fileReadOk = false;
517 std::string content;
518 std::tie(fileReadOk, content) = ReadFile(jsonPath);
519 if (!fileReadOk) {
520 std::cerr << "Failed to read JSON grammar file: "
521 << jsonPath << std::endl;
522 exit(1);
523 }
524
525 // Decode the JSON grammar file.
526 Json::Reader reader;
527 Json::Value root;
528 if (!reader.parse(content, root)) {
529 std::cerr << "Failed to parse JSON grammar:\n"
530 << reader.getFormattedErrorMessages();
531 exit(1);
532 }
533
534 // Layouts for all instructions.
535
536 // A lambda for returning capabilities from a JSON object as strings.
537 const auto getCaps = [](const Json::Value& object) {
538 EnumCaps result;
539 const auto& caps = object["capabilities"];
540 if (!caps.empty()) {
541 assert(caps.isArray());
542 for (const auto& cap : caps) {
543 result.emplace_back(cap.asString());
544 }
545 }
546 return result;
547 };
548
549 // A lambda for returning extensions from a JSON object as strings.
550 const auto getExts = [](const Json::Value& object) {
551 Extensions result;
552 const auto& exts = object["extensions"];
553 if (!exts.empty()) {
554 assert(exts.isArray());
555 for (const auto& ext : exts) {
556 result.emplace_back(ext.asString());
557 }
558 }
559 return result;
560 };
561
562 // set up the printing classes
563 std::unordered_set<std::string> tags; // short-lived local for error checking below
564 const Json::Value printingClasses = root["instruction_printing_class"];
565 for (const auto& printingClass : printingClasses) {
566 if (printingClass["tag"].asString().size() > 0)
567 tags.insert(printingClass["tag"].asString()); // just for error checking
568 else {
569 std::cerr << "Error: each instruction_printing_class requires a non-empty \"tag\"" << std::endl;
570 std::exit(1);
571 }
572 if (buildingHeaders || printingClass["tag"].asString() != "@exclude") {
573 InstructionPrintingClasses.push_back({printingClass["tag"].asString(),
574 printingClass["heading"].asString()});
575 }
576 }
577
578 // process the instructions
579 const Json::Value insts = root["instructions"];
580 unsigned maxOpcode = 0;
581 std::string maxName = "";
582 bool maxCore = false;
583 bool firstOpcode = true;
584 for (const auto& inst : insts) {
585 const auto printingClass = inst["class"].asString();
586 if (printingClass.size() == 0) {
587 std::cerr << "Error: " << inst["opname"].asString()
588 << " requires a non-empty printing \"class\" tag" << std::endl;
589 std::exit(1);
590 }
591 if (!buildingHeaders && printingClass == "@exclude")
592 continue;
593 if (tags.find(printingClass) == tags.end()) {
594 std::cerr << "Error: " << inst["opname"].asString()
595 << " requires a \"class\" declared as a \"tag\" in \"instruction printing_class\""
596 << std::endl;
597 std::exit(1);
598 }
599 const auto opcode = inst["opcode"].asUInt();
600 const std::string name = inst["opname"].asString();
601 std::string version = inst["version"].asString();
602 if (firstOpcode) {
603 maxOpcode = opcode;
604 maxName = name;
605 maxCore = version != "None";
606 firstOpcode = false;
607 } else {
608 if (maxOpcode > opcode) {
609 std::cerr << "Error: " << name
610 << " is out of order. It follows the instruction with opcode " << maxOpcode
611 << std::endl;
612 std::exit(1);
613 } else if (maxOpcode == opcode &&
614 !SuffixComparison(maxName, maxCore, name,
615 version != "None")) {
616 std::cerr << "Error: " << name
617 << " is out of order. It follows alias " << maxName
618 << std::endl;
619 std::exit(1);
620 } else {
621 maxOpcode = opcode;
622 maxName = name;
623 maxCore = version != "None";
624 }
625 }
626 EnumCaps caps = getCaps(inst);
627 std::string lastVersion = inst["lastVersion"].asString();
628 Extensions exts = getExts(inst);
629 OperandParameters operands;
630 bool defResultId = false;
631 bool defTypeId = false;
632 for (const auto& operand : inst["operands"]) {
633 const std::string kind = operand["kind"].asString();
634 const std::string quantifier = operand.get("quantifier", "").asString();
635 const std::string doc = operand.get("name", "").asString();
636 if (!IsTypeOrResultId(kind, &defTypeId, &defResultId)) {
637 const auto p = ToOperandClassAndOptionality(kind, quantifier);
638 operands.push(p.type, doc, p.optional);
639 }
640 }
641 InstructionDesc.emplace_back(
642 std::move(EnumValue(opcode, name,
643 std::move(caps), std::move(version), std::move(lastVersion), std::move(exts),
644 std::move(operands))),
645 printingClass, defTypeId, defResultId);
646 if (!InstructionDesc.back().IsValid(OperandOpcode, "instruction")) {
647 errorCount++;
648 }
649 }
650
651 // Specific additional context-dependent operands
652
653 // Populate dest with EnumValue objects constructed from source.
654 const auto populateEnumValues = [&getCaps,&getExts,&errorCount](EnumValues* dest, const Json::Value& source, bool bitEnum) {
655 // A lambda for determining the numeric value to be used for a given
656 // enumerant in JSON form, and whether that value is a 0 in a bitfield.
657 auto getValue = [&bitEnum](const Json::Value& enumerant) {
658 std::pair<unsigned, bool> result{0u,false};
659 if (!bitEnum) {
660 result.first = enumerant["value"].asUInt();
661 } else {
662 const unsigned int bit = NumberStringToBit(enumerant["value"].asString());
663 if (bit == 0)
664 result.second = true;
665 else
666 result.first = bit - 1; // This is the *shift* amount.
667 }
668 return result;
669 };
670
671 unsigned maxValue = 0;
672 std::string maxName = "";
673 bool maxCore = false;
674 bool firstValue = true;
675 for (const auto& enumerant : source["enumerants"]) {
676 unsigned value;
677 bool skip_zero_in_bitfield;
678 std::tie(value, skip_zero_in_bitfield) = getValue(enumerant);
679 std::string name = enumerant["enumerant"].asString();
680 std::string version = enumerant["version"].asString();
681 if (skip_zero_in_bitfield)
682 continue;
683 if (firstValue) {
684 maxValue = value;
685 maxName = name;
686 maxCore = version != "None";
687 firstValue = false;
688 } else {
689 if (maxValue > value) {
690 std::cerr << "Error: " << source["kind"] << " enumerant " << name
691 << " is out of order. It has value " << value
692 << " but follows the enumerant with value " << maxValue << std::endl;
693 std::exit(1);
694 } else if (maxValue == value &&
695 !SuffixComparison(maxName, maxCore, name,
696 version != "None")) {
697 std::cerr << "Error: " << source["kind"] << " enumerant " << name
698 << " is out of order. It follows alias " << maxName << std::endl;
699 std::exit(1);
700 } else {
701 maxValue = value;
702 maxName = name;
703 maxCore = version != "None";
704 }
705 }
706 EnumCaps caps(getCaps(enumerant));
707 std::string lastVersion = enumerant["lastVersion"].asString();
708 Extensions exts(getExts(enumerant));
709 OperandParameters params;
710 const Json::Value& paramsJson = enumerant["parameters"];
711 if (!paramsJson.empty()) { // This enumerant has parameters.
712 assert(paramsJson.isArray());
713 for (const auto& param : paramsJson) {
714 const std::string kind = param["kind"].asString();
715 const std::string doc = param.get("name", "").asString();
716 const auto p = ToOperandClassAndOptionality(kind, ""); // All parameters are required!
717 params.push(p.type, doc);
718 }
719 }
720 dest->emplace_back(
721 value, enumerant["enumerant"].asString(),
722 std::move(caps), std::move(version), std::move(lastVersion), std::move(exts), std::move(params));
723 }
724 };
725
726 const auto establishOperandClass = [&populateEnumValues,&errorCount](
727 const std::string& enumName, spv::OperandClass operandClass,
728 spv::EnumValues* enumValues, const Json::Value& operandEnum, const std::string& category) {
729 assert(category == "BitEnum" || category == "ValueEnum");
730 bool bitEnum = (category == "BitEnum");
731 if (!operandEnum["version"].empty()) {
732 std::cerr << "Error: container for " << enumName << " operand_kind must not have a version field" << std::endl;
733 errorCount++;
734 }
735 populateEnumValues(enumValues, operandEnum, bitEnum);
736 const std::string errContext = "enum " + enumName;
737 for (const auto& e: *enumValues) {
738 if (!e.IsValid(operandClass, errContext)) {
739 errorCount++;
740 }
741 }
742 OperandClassParams[operandClass].set(enumName, enumValues, bitEnum);
743 };
744
745 const Json::Value operandEnums = root["operand_kinds"];
746 for (const auto& operandEnum : operandEnums) {
747 const std::string enumName = operandEnum["kind"].asString();
748 const std::string category = operandEnum["category"].asString();
749 if (enumName == "SourceLanguage") {
750 establishOperandClass(enumName, OperandSource, &SourceLanguageParams, operandEnum, category);
751 } else if (enumName == "Decoration") {
752 establishOperandClass(enumName, OperandDecoration, &DecorationParams, operandEnum, category);
753 } else if (enumName == "ExecutionMode") {
754 establishOperandClass(enumName, OperandExecutionMode, &ExecutionModeParams, operandEnum, category);
755 } else if (enumName == "Capability") {
756 establishOperandClass(enumName, OperandCapability, &CapabilityParams, operandEnum, category);
757 } else if (enumName == "AddressingModel") {
758 establishOperandClass(enumName, OperandAddressing, &AddressingParams, operandEnum, category);
759 } else if (enumName == "MemoryModel") {
760 establishOperandClass(enumName, OperandMemory, &MemoryParams, operandEnum, category);
761 } else if (enumName == "MemorySemantics") {
762 establishOperandClass(enumName, OperandMemorySemantics, &MemorySemanticsParams, operandEnum, category);
763 } else if (enumName == "ExecutionModel") {
764 establishOperandClass(enumName, OperandExecutionModel, &ExecutionModelParams, operandEnum, category);
765 } else if (enumName == "StorageClass") {
766 establishOperandClass(enumName, OperandStorage, &StorageParams, operandEnum, category);
767 } else if (enumName == "SamplerAddressingMode") {
768 establishOperandClass(enumName, OperandSamplerAddressingMode, &SamplerAddressingModeParams, operandEnum, category);
769 } else if (enumName == "SamplerFilterMode") {
770 establishOperandClass(enumName, OperandSamplerFilterMode, &SamplerFilterModeParams, operandEnum, category);
771 } else if (enumName == "ImageFormat") {
772 establishOperandClass(enumName, OperandSamplerImageFormat, &ImageFormatParams, operandEnum, category);
773 } else if (enumName == "ImageChannelOrder") {
774 establishOperandClass(enumName, OperandImageChannelOrder, &ImageChannelOrderParams, operandEnum, category);
775 } else if (enumName == "ImageChannelDataType") {
776 establishOperandClass(enumName, OperandImageChannelDataType, &ImageChannelDataTypeParams, operandEnum, category);
777 } else if (enumName == "ImageOperands") {
778 establishOperandClass(enumName, OperandImageOperands, &ImageOperandsParams, operandEnum, category);
779 } else if (enumName == "FPFastMathMode") {
780 establishOperandClass(enumName, OperandFPFastMath, &FPFastMathParams, operandEnum, category);
781 } else if (enumName == "FPRoundingMode") {
782 establishOperandClass(enumName, OperandFPRoundingMode, &FPRoundingModeParams, operandEnum, category);
783 } else if (enumName == "FPDenormMode") {
784 establishOperandClass(enumName, OperandFPDenormMode, &FPDenormModeParams, operandEnum, category);
785 } else if (enumName == "FPOperationMode") {
786 establishOperandClass(enumName, OperandFPOperationMode, &FPOperationModeParams, operandEnum, category);
787 } else if (enumName == "QuantizationModes") {
788 establishOperandClass(enumName, OperandQuantizationModes, &QuantizationModesParams, operandEnum, category);
789 } else if (enumName == "OverflowModes") {
790 establishOperandClass(enumName, OperandOverflowModes, &OverflowModesParams, operandEnum, category);
791 } else if (enumName == "LinkageType") {
792 establishOperandClass(enumName, OperandLinkageType, &LinkageTypeParams, operandEnum, category);
793 } else if (enumName == "FunctionParameterAttribute") {
794 establishOperandClass(enumName, OperandFuncParamAttr, &FuncParamAttrParams, operandEnum, category);
795 } else if (enumName == "AccessQualifier") {
796 establishOperandClass(enumName, OperandAccessQualifier, &AccessQualifierParams, operandEnum, category);
797 } else if (enumName == "BuiltIn") {
798 establishOperandClass(enumName, OperandBuiltIn, &BuiltInParams, operandEnum, category);
799 } else if (enumName == "SelectionControl") {
800 establishOperandClass(enumName, OperandSelect, &SelectionControlParams, operandEnum, category);
801 } else if (enumName == "LoopControl") {
802 establishOperandClass(enumName, OperandLoop, &LoopControlParams, operandEnum, category);
803 } else if (enumName == "FunctionControl") {
804 establishOperandClass(enumName, OperandFunction, &FunctionControlParams, operandEnum, category);
805 } else if (enumName == "Dim") {
806 establishOperandClass(enumName, OperandDimensionality, &DimensionalityParams, operandEnum, category);
807 } else if (enumName == "MemoryAccess") {
808 establishOperandClass(enumName, OperandMemoryOperands, &MemoryAccessParams, operandEnum, category);
809 } else if (enumName == "Scope") {
810 establishOperandClass(enumName, OperandScope, &ScopeParams, operandEnum, category);
811 } else if (enumName == "GroupOperation") {
812 establishOperandClass(enumName, OperandGroupOperation, &GroupOperationParams, operandEnum, category);
813 } else if (enumName == "KernelEnqueueFlags") {
814 establishOperandClass(enumName, OperandKernelEnqueueFlags, &KernelEnqueueFlagsParams, operandEnum, category);
815 } else if (enumName == "KernelProfilingInfo") {
816 establishOperandClass(enumName, OperandKernelProfilingInfo, &KernelProfilingInfoParams, operandEnum, category);
817 } else if (enumName == "RayFlags") {
818 establishOperandClass(enumName, OperandRayFlags, &RayFlagsParams, operandEnum, category);
819 } else if (enumName == "RayQueryIntersection") {
820 establishOperandClass(enumName, OperandRayQueryIntersection, &RayQueryIntersectionParams, operandEnum, category);
821 } else if (enumName == "RayQueryCommittedIntersectionType") {
822 establishOperandClass(enumName, OperandRayQueryCommittedIntersectionType, &RayQueryCommittedIntersectionTypeParams, operandEnum, category);
823 } else if (enumName == "RayQueryCandidateIntersectionType") {
824 establishOperandClass(enumName, OperandRayQueryCandidateIntersectionType, &RayQueryCandidateIntersectionTypeParams, operandEnum, category);
825 } else if (enumName == "FragmentShadingRate") {
826 establishOperandClass(enumName, OperandFragmentShadingRate, &FragmentShadingRateParams, operandEnum, category);
827 } else if (enumName == "PackedVectorFormat") {
828 establishOperandClass(enumName, OperandPackedVectorFormat, &PackedVectorFormatParams, operandEnum, category);
829 } else if (enumName == "CooperativeMatrixOperands") {
830 establishOperandClass(enumName, OperandCooperativeMatrixOperands, &CooperativeMatrixOperandsParams, operandEnum, category);
831 } else if (enumName == "CooperativeMatrixLayout") {
832 establishOperandClass(enumName, OperandCooperativeMatrixLayout, &CooperativeMatrixLayoutParams, operandEnum, category);
833 } else if (enumName == "CooperativeMatrixUse") {
834 establishOperandClass(enumName, OperandCooperativeMatrixUse, &CooperativeMatrixUseParams, operandEnum, category);
835 } else if (enumName == "InitializationModeQualifier") {
836 establishOperandClass(enumName, OperandInitializationModeQualifier, &InitializationModeQualifierParams, operandEnum, category);
837 } else if (enumName == "HostAccessQualifier") {
838 establishOperandClass(enumName, OperandHostAccessQualifier, &HostAccessQualifierParams, operandEnum, category);
839 } else if (enumName == "LoadCacheControl") {
840 establishOperandClass(enumName, OperandLoadCacheControl, &LoadCacheControlParams, operandEnum, category);
841 } else if (enumName == "StoreCacheControl") {
842 establishOperandClass(enumName, OperandStoreCacheControl, &StoreCacheControlParams, operandEnum, category);
843 } else if (enumName == "NamedMaximumNumberOfRegisters") {
844 establishOperandClass(enumName, OperandNamedMaximumNumberOfRegisters, &NamedMaximumNumberOfRegistersParams, operandEnum, category);
845 } else if (enumName == "RawAccessChainOperands") {
846 establishOperandClass(enumName, OperandRawAccessChainOperands, &RawAccessChainOperandsParams, operandEnum, category);
847 }
848 }
849
850 if (errorCount > 0) {
851 std::exit(1);
852 }
853 }
854
855 }; // end namespace spv
856