• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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