xref: /aosp_15_r20/external/swiftshader/third_party/SPIRV-Headers/tools/buildHeaders/jsonToSpirv.cpp (revision 03ce13f70fcc45d86ee91b7ee4cab1936a95046e)
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 
jsonToSpirv(const std::string & jsonPath,bool buildingHeaders)474 void jsonToSpirv(const std::string& jsonPath, bool buildingHeaders)
475 {
476     // only do this once.
477     static bool initialized = false;
478     if (initialized)
479         return;
480     initialized = true;
481 
482     size_t errorCount = 0;
483 
484     // Read the JSON grammar file.
485     bool fileReadOk = false;
486     std::string content;
487     std::tie(fileReadOk, content) = ReadFile(jsonPath);
488     if (!fileReadOk) {
489         std::cerr << "Failed to read JSON grammar file: "
490                   << jsonPath << std::endl;
491         exit(1);
492     }
493 
494     // Decode the JSON grammar file.
495     Json::Reader reader;
496     Json::Value root;
497     if (!reader.parse(content, root)) {
498         std::cerr << "Failed to parse JSON grammar:\n"
499                   << reader.getFormattedErrorMessages();
500         exit(1);
501     }
502 
503     // Layouts for all instructions.
504 
505     // A lambda for returning capabilities from a JSON object as strings.
506     const auto getCaps = [](const Json::Value& object) {
507         EnumCaps result;
508         const auto& caps = object["capabilities"];
509         if (!caps.empty()) {
510             assert(caps.isArray());
511             for (const auto& cap : caps) {
512                 result.emplace_back(cap.asString());
513             }
514         }
515         return result;
516     };
517 
518     // A lambda for returning extensions from a JSON object as strings.
519     const auto getExts = [](const Json::Value& object) {
520         Extensions result;
521         const auto& exts = object["extensions"];
522         if (!exts.empty()) {
523             assert(exts.isArray());
524             for (const auto& ext : exts) {
525                 result.emplace_back(ext.asString());
526             }
527         }
528         return result;
529     };
530 
531     // set up the printing classes
532     std::unordered_set<std::string> tags;  // short-lived local for error checking below
533     const Json::Value printingClasses = root["instruction_printing_class"];
534     for (const auto& printingClass : printingClasses) {
535         if (printingClass["tag"].asString().size() > 0)
536             tags.insert(printingClass["tag"].asString()); // just for error checking
537         else {
538             std::cerr << "Error: each instruction_printing_class requires a non-empty \"tag\"" << std::endl;
539             std::exit(1);
540         }
541         if (buildingHeaders || printingClass["tag"].asString() != "@exclude") {
542             InstructionPrintingClasses.push_back({printingClass["tag"].asString(),
543                                                   printingClass["heading"].asString()});
544         }
545     }
546 
547     // process the instructions
548     const Json::Value insts = root["instructions"];
549     unsigned maxOpcode = 0;
550     bool firstOpcode = true;
551     for (const auto& inst : insts) {
552         const auto printingClass = inst["class"].asString();
553         if (printingClass.size() == 0) {
554             std::cerr << "Error: " << inst["opname"].asString()
555                       << " requires a non-empty printing \"class\" tag" << std::endl;
556             std::exit(1);
557         }
558         if (!buildingHeaders && printingClass == "@exclude")
559             continue;
560         if (tags.find(printingClass) == tags.end()) {
561             std::cerr << "Error: " << inst["opname"].asString()
562                       << " requires a \"class\" declared as a \"tag\" in \"instruction printing_class\""
563                       << std::endl;
564             std::exit(1);
565         }
566         const auto opcode = inst["opcode"].asUInt();
567         const std::string name = inst["opname"].asString();
568         if (firstOpcode) {
569           maxOpcode = opcode;
570           firstOpcode = false;
571         } else {
572           if (maxOpcode > opcode) {
573             std::cerr << "Error: " << name
574                       << " is out of order. It follows the instruction with opcode " << maxOpcode
575                       << std::endl;
576             std::exit(1);
577           } else {
578             maxOpcode = opcode;
579           }
580         }
581         EnumCaps caps = getCaps(inst);
582         std::string version = inst["version"].asString();
583         std::string lastVersion = inst["lastVersion"].asString();
584         Extensions exts = getExts(inst);
585         OperandParameters operands;
586         bool defResultId = false;
587         bool defTypeId = false;
588         for (const auto& operand : inst["operands"]) {
589             const std::string kind = operand["kind"].asString();
590             const std::string quantifier = operand.get("quantifier", "").asString();
591             const std::string doc = operand.get("name", "").asString();
592             if (!IsTypeOrResultId(kind, &defTypeId, &defResultId)) {
593                 const auto p = ToOperandClassAndOptionality(kind, quantifier);
594                 operands.push(p.type, doc, p.optional);
595             }
596         }
597         InstructionDesc.emplace_back(
598             std::move(EnumValue(opcode, name,
599                                 std::move(caps), std::move(version), std::move(lastVersion), std::move(exts),
600                                 std::move(operands))),
601              printingClass, defTypeId, defResultId);
602         if (!InstructionDesc.back().IsValid(OperandOpcode, "instruction")) {
603           errorCount++;
604         }
605     }
606 
607     // Specific additional context-dependent operands
608 
609     // Populate dest with EnumValue objects constructed from source.
610     const auto populateEnumValues = [&getCaps,&getExts,&errorCount](EnumValues* dest, const Json::Value& source, bool bitEnum) {
611         // A lambda for determining the numeric value to be used for a given
612         // enumerant in JSON form, and whether that value is a 0 in a bitfield.
613         auto getValue = [&bitEnum](const Json::Value& enumerant) {
614             std::pair<unsigned, bool> result{0u,false};
615             if (!bitEnum) {
616                 result.first = enumerant["value"].asUInt();
617             } else {
618                 const unsigned int bit = NumberStringToBit(enumerant["value"].asString());
619                 if (bit == 0)
620                     result.second = true;
621                 else
622                     result.first = bit - 1;  // This is the *shift* amount.
623             }
624             return result;
625         };
626 
627         unsigned maxValue = 0;
628         bool firstValue = true;
629         for (const auto& enumerant : source["enumerants"]) {
630             unsigned value;
631             bool skip_zero_in_bitfield;
632             std::tie(value, skip_zero_in_bitfield) = getValue(enumerant);
633             if (skip_zero_in_bitfield)
634                 continue;
635             if (firstValue) {
636               maxValue = value;
637               firstValue = false;
638             } else {
639               if (maxValue > value) {
640                 std::cerr << "Error: " << source["kind"] << " enumerant " << enumerant["enumerant"]
641                           << " is out of order. It has value " <<  value
642                           << " but follows the enumerant with value " << maxValue << std::endl;
643                 std::exit(1);
644               } else {
645                 maxValue = value;
646               }
647             }
648             EnumCaps caps(getCaps(enumerant));
649             std::string version = enumerant["version"].asString();
650             std::string lastVersion = enumerant["lastVersion"].asString();
651             Extensions exts(getExts(enumerant));
652             OperandParameters params;
653             const Json::Value& paramsJson = enumerant["parameters"];
654             if (!paramsJson.empty()) {  // This enumerant has parameters.
655                 assert(paramsJson.isArray());
656                 for (const auto& param : paramsJson) {
657                     const std::string kind = param["kind"].asString();
658                     const std::string doc = param.get("name", "").asString();
659                     const auto p = ToOperandClassAndOptionality(kind, ""); // All parameters are required!
660                     params.push(p.type, doc);
661                 }
662             }
663             dest->emplace_back(
664                 value, enumerant["enumerant"].asString(),
665                 std::move(caps), std::move(version), std::move(lastVersion), std::move(exts), std::move(params));
666         }
667     };
668 
669     const auto establishOperandClass = [&populateEnumValues,&errorCount](
670             const std::string& enumName, spv::OperandClass operandClass,
671             spv::EnumValues* enumValues, const Json::Value& operandEnum, const std::string& category) {
672         assert(category == "BitEnum" || category == "ValueEnum");
673         bool bitEnum = (category == "BitEnum");
674         if (!operandEnum["version"].empty()) {
675           std::cerr << "Error: container for " << enumName << " operand_kind must not have a version field" << std::endl;
676           errorCount++;
677         }
678         populateEnumValues(enumValues, operandEnum, bitEnum);
679         const std::string errContext = "enum " + enumName;
680         for (const auto& e: *enumValues) {
681           if (!e.IsValid(operandClass, errContext)) {
682             errorCount++;
683           }
684         }
685         OperandClassParams[operandClass].set(enumName, enumValues, bitEnum);
686     };
687 
688     const Json::Value operandEnums = root["operand_kinds"];
689     for (const auto& operandEnum : operandEnums) {
690         const std::string enumName = operandEnum["kind"].asString();
691         const std::string category = operandEnum["category"].asString();
692         if (enumName == "SourceLanguage") {
693             establishOperandClass(enumName, OperandSource, &SourceLanguageParams, operandEnum, category);
694         } else if (enumName == "Decoration") {
695             establishOperandClass(enumName, OperandDecoration, &DecorationParams, operandEnum, category);
696         } else if (enumName == "ExecutionMode") {
697             establishOperandClass(enumName, OperandExecutionMode, &ExecutionModeParams, operandEnum, category);
698         } else if (enumName == "Capability") {
699             establishOperandClass(enumName, OperandCapability, &CapabilityParams, operandEnum, category);
700         } else if (enumName == "AddressingModel") {
701             establishOperandClass(enumName, OperandAddressing, &AddressingParams, operandEnum, category);
702         } else if (enumName == "MemoryModel") {
703             establishOperandClass(enumName, OperandMemory, &MemoryParams, operandEnum, category);
704         } else if (enumName == "MemorySemantics") {
705             establishOperandClass(enumName, OperandMemorySemantics, &MemorySemanticsParams, operandEnum, category);
706         } else if (enumName == "ExecutionModel") {
707             establishOperandClass(enumName, OperandExecutionModel, &ExecutionModelParams, operandEnum, category);
708         } else if (enumName == "StorageClass") {
709             establishOperandClass(enumName, OperandStorage, &StorageParams, operandEnum, category);
710         } else if (enumName == "SamplerAddressingMode") {
711             establishOperandClass(enumName, OperandSamplerAddressingMode, &SamplerAddressingModeParams, operandEnum, category);
712         } else if (enumName == "SamplerFilterMode") {
713             establishOperandClass(enumName, OperandSamplerFilterMode, &SamplerFilterModeParams, operandEnum, category);
714         } else if (enumName == "ImageFormat") {
715             establishOperandClass(enumName, OperandSamplerImageFormat, &ImageFormatParams, operandEnum, category);
716         } else if (enumName == "ImageChannelOrder") {
717             establishOperandClass(enumName, OperandImageChannelOrder, &ImageChannelOrderParams, operandEnum, category);
718         } else if (enumName == "ImageChannelDataType") {
719             establishOperandClass(enumName, OperandImageChannelDataType, &ImageChannelDataTypeParams, operandEnum, category);
720         } else if (enumName == "ImageOperands") {
721             establishOperandClass(enumName, OperandImageOperands, &ImageOperandsParams, operandEnum, category);
722         } else if (enumName == "FPFastMathMode") {
723             establishOperandClass(enumName, OperandFPFastMath, &FPFastMathParams, operandEnum, category);
724         } else if (enumName == "FPRoundingMode") {
725             establishOperandClass(enumName, OperandFPRoundingMode, &FPRoundingModeParams, operandEnum, category);
726         } else if (enumName == "FPDenormMode") {
727             establishOperandClass(enumName, OperandFPDenormMode, &FPDenormModeParams, operandEnum, category);
728         } else if (enumName == "FPOperationMode") {
729             establishOperandClass(enumName, OperandFPOperationMode, &FPOperationModeParams, operandEnum, category);
730         } else if (enumName == "QuantizationModes") {
731             establishOperandClass(enumName, OperandQuantizationModes, &QuantizationModesParams, operandEnum, category);
732         } else if (enumName == "OverflowModes") {
733             establishOperandClass(enumName, OperandOverflowModes, &OverflowModesParams, operandEnum, category);
734         } else if (enumName == "LinkageType") {
735             establishOperandClass(enumName, OperandLinkageType, &LinkageTypeParams, operandEnum, category);
736         } else if (enumName == "FunctionParameterAttribute") {
737             establishOperandClass(enumName, OperandFuncParamAttr, &FuncParamAttrParams, operandEnum, category);
738         } else if (enumName == "AccessQualifier") {
739             establishOperandClass(enumName, OperandAccessQualifier, &AccessQualifierParams, operandEnum, category);
740         } else if (enumName == "BuiltIn") {
741             establishOperandClass(enumName, OperandBuiltIn, &BuiltInParams, operandEnum, category);
742         } else if (enumName == "SelectionControl") {
743             establishOperandClass(enumName, OperandSelect, &SelectionControlParams, operandEnum, category);
744         } else if (enumName == "LoopControl") {
745             establishOperandClass(enumName, OperandLoop, &LoopControlParams, operandEnum, category);
746         } else if (enumName == "FunctionControl") {
747             establishOperandClass(enumName, OperandFunction, &FunctionControlParams, operandEnum, category);
748         } else if (enumName == "Dim") {
749             establishOperandClass(enumName, OperandDimensionality, &DimensionalityParams, operandEnum, category);
750         } else if (enumName == "MemoryAccess") {
751             establishOperandClass(enumName, OperandMemoryOperands, &MemoryAccessParams, operandEnum, category);
752         } else if (enumName == "Scope") {
753             establishOperandClass(enumName, OperandScope, &ScopeParams, operandEnum, category);
754         } else if (enumName == "GroupOperation") {
755             establishOperandClass(enumName, OperandGroupOperation, &GroupOperationParams, operandEnum, category);
756         } else if (enumName == "KernelEnqueueFlags") {
757             establishOperandClass(enumName, OperandKernelEnqueueFlags, &KernelEnqueueFlagsParams, operandEnum, category);
758         } else if (enumName == "KernelProfilingInfo") {
759             establishOperandClass(enumName, OperandKernelProfilingInfo, &KernelProfilingInfoParams, operandEnum, category);
760         } else if (enumName == "RayFlags") {
761             establishOperandClass(enumName, OperandRayFlags, &RayFlagsParams, operandEnum, category);
762         } else if (enumName == "RayQueryIntersection") {
763             establishOperandClass(enumName, OperandRayQueryIntersection, &RayQueryIntersectionParams, operandEnum, category);
764         } else if (enumName == "RayQueryCommittedIntersectionType") {
765             establishOperandClass(enumName, OperandRayQueryCommittedIntersectionType, &RayQueryCommittedIntersectionTypeParams, operandEnum, category);
766         } else if (enumName == "RayQueryCandidateIntersectionType") {
767             establishOperandClass(enumName, OperandRayQueryCandidateIntersectionType, &RayQueryCandidateIntersectionTypeParams, operandEnum, category);
768         } else if (enumName == "FragmentShadingRate") {
769             establishOperandClass(enumName, OperandFragmentShadingRate, &FragmentShadingRateParams, operandEnum, category);
770         } else if (enumName == "PackedVectorFormat") {
771             establishOperandClass(enumName, OperandPackedVectorFormat, &PackedVectorFormatParams, operandEnum, category);
772         } else if (enumName == "CooperativeMatrixOperands") {
773             establishOperandClass(enumName, OperandCooperativeMatrixOperands, &CooperativeMatrixOperandsParams, operandEnum, category);
774         } else if (enumName == "CooperativeMatrixLayout") {
775             establishOperandClass(enumName, OperandCooperativeMatrixLayout, &CooperativeMatrixLayoutParams, operandEnum, category);
776         } else if (enumName == "CooperativeMatrixUse") {
777             establishOperandClass(enumName, OperandCooperativeMatrixUse, &CooperativeMatrixUseParams, operandEnum, category);
778         } else if (enumName == "InitializationModeQualifier") {
779             establishOperandClass(enumName, OperandInitializationModeQualifier, &InitializationModeQualifierParams, operandEnum, category);
780         } else if (enumName == "HostAccessQualifier") {
781             establishOperandClass(enumName, OperandHostAccessQualifier, &HostAccessQualifierParams, operandEnum, category);
782         } else if (enumName == "LoadCacheControl") {
783             establishOperandClass(enumName, OperandLoadCacheControl, &LoadCacheControlParams, operandEnum, category);
784         } else if (enumName == "StoreCacheControl") {
785             establishOperandClass(enumName, OperandStoreCacheControl, &StoreCacheControlParams, operandEnum, category);
786         } else if (enumName == "NamedMaximumNumberOfRegisters") {
787             establishOperandClass(enumName, OperandNamedMaximumNumberOfRegisters, &NamedMaximumNumberOfRegistersParams, operandEnum, category);
788         } else if (enumName == "RawAccessChainOperands") {
789             establishOperandClass(enumName, OperandRawAccessChainOperands, &RawAccessChainOperandsParams, operandEnum, category);
790         }
791     }
792 
793     if (errorCount > 0) {
794       std::exit(1);
795     }
796 }
797 
798 };  // end namespace spv
799