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