• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2014-2020 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 
ReadFile(const std::string & path)276 std::pair<bool, std::string> ReadFile(const std::string& path)
277 {
278     std::ifstream fstream(path, std::ios::in);
279     if (fstream) {
280         std::string contents;
281         fstream.seekg(0, std::ios::end);
282         contents.reserve((unsigned int)fstream.tellg());
283         fstream.seekg(0, std::ios::beg);
284         contents.assign((std::istreambuf_iterator<char>(fstream)),
285                         std::istreambuf_iterator<char>());
286         return std::make_pair(true, contents);
287     }
288     return std::make_pair(false, "");
289 }
290 
291 struct ClassOptionality {
292     OperandClass type;
293     bool optional;
294 };
295 
296 // Converts the |operandKind| and |quantifier| pair used to describe operands
297 // in the JSON grammar to OperandClass and optionality used in this repo.
ToOperandClassAndOptionality(const std::string & operandKind,const std::string & quantifier)298 ClassOptionality ToOperandClassAndOptionality(const std::string& operandKind, const std::string& quantifier)
299 {
300     assert(quantifier.empty() || quantifier == "?" || quantifier == "*");
301 
302     if (operandKind == "IdRef") {
303         if (quantifier.empty())
304             return {OperandId, false};
305         else if (quantifier == "?")
306             return {OperandId, true};
307         else
308             return {OperandVariableIds, false};
309     } else if (operandKind == "LiteralInteger") {
310         if (quantifier.empty())
311             return {OperandLiteralNumber, false};
312         if (quantifier == "?")
313             return {OperandOptionalLiteral, true};
314         else
315             return {OperandVariableLiterals, false};
316     } else if (operandKind == "LiteralString") {
317         if (quantifier.empty())
318             return {OperandLiteralString, false};
319         else if (quantifier == "?")
320             return {OperandLiteralString, true};
321         else {
322             return {OperandOptionalLiteralStrings, false};
323         }
324     } else if (operandKind == "PairLiteralIntegerIdRef") {
325         // Used by OpSwitch in the grammar
326         return {OperandVariableLiteralId, false};
327     } else if (operandKind == "PairIdRefLiteralInteger") {
328         // Used by OpGroupMemberDecorate in the grammar
329         return {OperandVariableIdLiteral, false};
330     } else if (operandKind == "PairIdRefIdRef") {
331         // Used by OpPhi in the grammar
332         return {OperandVariableIds, false};
333     } else {
334         OperandClass type = OperandNone;
335         if (operandKind == "IdMemorySemantics" || operandKind == "MemorySemantics") {
336             type = OperandMemorySemantics;
337         } else if (operandKind == "IdScope" || operandKind == "Scope") {
338             type = OperandScope;
339         } else if (operandKind == "LiteralExtInstInteger") {
340             type = OperandLiteralNumber;
341         } else if (operandKind == "LiteralSpecConstantOpInteger") {
342             type = OperandLiteralNumber;
343         } else if (operandKind == "LiteralContextDependentNumber") {
344             type = OperandAnySizeLiteralNumber;
345         } else if (operandKind == "LiteralFloat") {
346             type = OperandLiteralNumber;
347         } else if (operandKind == "SourceLanguage") {
348             type = OperandSource;
349         } else if (operandKind == "ExecutionModel") {
350             type = OperandExecutionModel;
351         } else if (operandKind == "AddressingModel") {
352             type = OperandAddressing;
353         } else if (operandKind == "MemoryModel") {
354             type = OperandMemory;
355         } else if (operandKind == "ExecutionMode") {
356             type = OperandExecutionMode;
357         } else if (operandKind == "StorageClass") {
358             type = OperandStorage;
359         } else if (operandKind == "Dim") {
360             type = OperandDimensionality;
361         } else if (operandKind == "SamplerAddressingMode") {
362             type = OperandSamplerAddressingMode;
363         } else if (operandKind == "SamplerFilterMode") {
364             type = OperandSamplerFilterMode;
365         } else if (operandKind == "ImageFormat") {
366             type = OperandSamplerImageFormat;
367         } else if (operandKind == "ImageChannelOrder") {
368             type = OperandImageChannelOrder;
369         } else if (operandKind == "ImageChannelDataType") {
370             type = OperandImageChannelDataType;
371         } else if (operandKind == "FPRoundingMode") {
372             type = OperandFPRoundingMode;
373         } else if (operandKind == "FPDenormMode") {
374             type = OperandFPDenormMode;
375         } else if (operandKind == "FPOperationMode") {
376             type = OperandFPOperationMode;
377         } else if (operandKind == "QuantizationModes") {
378             type = OperandQuantizationModes;
379         } else if (operandKind == "OverflowModes") {
380             type = OperandOverflowModes;
381         } else if (operandKind == "LinkageType") {
382             type = OperandLinkageType;
383         } else if (operandKind == "AccessQualifier") {
384             type = OperandAccessQualifier;
385         } else if (operandKind == "FunctionParameterAttribute") {
386             type = OperandFuncParamAttr;
387         } else if (operandKind == "Decoration") {
388             type = OperandDecoration;
389         } else if (operandKind == "BuiltIn") {
390             type = OperandBuiltIn;
391         } else if (operandKind == "GroupOperation") {
392             type = OperandGroupOperation;
393         } else if (operandKind == "KernelEnqueueFlags") {
394             type = OperandKernelEnqueueFlags;
395         } else if (operandKind == "KernelProfilingInfo") {
396             type = OperandKernelProfilingInfo;
397         } else if (operandKind == "Capability") {
398             type = OperandCapability;
399         } else if (operandKind == "ImageOperands") {
400             type = OperandImageOperands;
401         } else if (operandKind == "FPFastMathMode") {
402             type = OperandFPFastMath;
403         } else if (operandKind == "SelectionControl") {
404             type = OperandSelect;
405         } else if (operandKind == "LoopControl") {
406             type = OperandLoop;
407         } else if (operandKind == "FunctionControl") {
408             type = OperandFunction;
409         } else if (operandKind == "MemoryAccess") {
410             type = OperandMemoryOperands;
411         } else if (operandKind == "RayFlags") {
412             type = OperandRayFlags;
413         } else if (operandKind == "RayQueryIntersection") {
414             type = OperandRayQueryIntersection;
415         } else if (operandKind == "RayQueryCommittedIntersectionType") {
416             type = OperandRayQueryCommittedIntersectionType;
417         } else if (operandKind == "RayQueryCandidateIntersectionType") {
418             type = OperandRayQueryCandidateIntersectionType;
419         } else if (operandKind == "FragmentShadingRate") {
420             type = OperandFragmentShadingRate;
421         } else if (operandKind == "PackedVectorFormat") {
422             type = OperandPackedVectorFormat;
423         } else if (operandKind == "CooperativeMatrixOperands") {
424             type = OperandCooperativeMatrixOperands;
425         } else if (operandKind == "CooperativeMatrixLayout") {
426             type = OperandCooperativeMatrixLayout;
427         } else if (operandKind == "CooperativeMatrixUse") {
428             type = OperandCooperativeMatrixUse;
429         } else if (operandKind == "InitializationModeQualifier") {
430             type = OperandInitializationModeQualifier;
431         } else if (operandKind == "HostAccessQualifier") {
432             type = OperandHostAccessQualifier;
433         } else if (operandKind == "LoadCacheControl") {
434             type = OperandLoadCacheControl;
435         } else if (operandKind == "StoreCacheControl") {
436             type = OperandStoreCacheControl;
437         }
438 
439         if (type == OperandNone) {
440             std::cerr << "Unhandled operand kind found: " << operandKind << std::endl;
441             exit(1);
442         }
443         return {type, !quantifier.empty()};
444     }
445 }
446 
IsTypeOrResultId(const std::string & str,bool * isType,bool * isResult)447 bool IsTypeOrResultId(const std::string& str, bool* isType, bool* isResult)
448 {
449     if (str == "IdResultType")
450         return *isType = true;
451     if (str == "IdResult")
452         return *isResult = true;
453     return false;
454 }
455 
456 // Given a number string, returns the position of the only bits set in the number.
457 // So it requires the number is a power of two.
NumberStringToBit(const std::string & str)458 unsigned int NumberStringToBit(const std::string& str)
459 {
460     char* parseEnd;
461     unsigned int value = (unsigned int)std::strtol(str.c_str(), &parseEnd, 16);
462     assert(!(value & (value - 1)) && "input number is not a power of 2");
463     unsigned int bit = 0;
464     for (; value; value >>= 1) ++bit;
465     return bit;
466 }
467 
jsonToSpirv(const std::string & jsonPath,bool buildingHeaders)468 void jsonToSpirv(const std::string& jsonPath, bool buildingHeaders)
469 {
470     // only do this once.
471     static bool initialized = false;
472     if (initialized)
473         return;
474     initialized = true;
475 
476     size_t errorCount = 0;
477 
478     // Read the JSON grammar file.
479     bool fileReadOk = false;
480     std::string content;
481     std::tie(fileReadOk, content) = ReadFile(jsonPath);
482     if (!fileReadOk) {
483         std::cerr << "Failed to read JSON grammar file: "
484                   << jsonPath << std::endl;
485         exit(1);
486     }
487 
488     // Decode the JSON grammar file.
489     Json::Reader reader;
490     Json::Value root;
491     if (!reader.parse(content, root)) {
492         std::cerr << "Failed to parse JSON grammar:\n"
493                   << reader.getFormattedErrorMessages();
494         exit(1);
495     }
496 
497     // Layouts for all instructions.
498 
499     // A lambda for returning capabilities from a JSON object as strings.
500     const auto getCaps = [](const Json::Value& object) {
501         EnumCaps result;
502         const auto& caps = object["capabilities"];
503         if (!caps.empty()) {
504             assert(caps.isArray());
505             for (const auto& cap : caps) {
506                 result.emplace_back(cap.asString());
507             }
508         }
509         return result;
510     };
511 
512     // A lambda for returning extensions from a JSON object as strings.
513     const auto getExts = [](const Json::Value& object) {
514         Extensions result;
515         const auto& exts = object["extensions"];
516         if (!exts.empty()) {
517             assert(exts.isArray());
518             for (const auto& ext : exts) {
519                 result.emplace_back(ext.asString());
520             }
521         }
522         return result;
523     };
524 
525     // set up the printing classes
526     std::unordered_set<std::string> tags;  // short-lived local for error checking below
527     const Json::Value printingClasses = root["instruction_printing_class"];
528     for (const auto& printingClass : printingClasses) {
529         if (printingClass["tag"].asString().size() > 0)
530             tags.insert(printingClass["tag"].asString()); // just for error checking
531         else {
532             std::cerr << "Error: each instruction_printing_class requires a non-empty \"tag\"" << std::endl;
533             std::exit(1);
534         }
535         if (buildingHeaders || printingClass["tag"].asString() != "@exclude") {
536             InstructionPrintingClasses.push_back({printingClass["tag"].asString(),
537                                                   printingClass["heading"].asString()});
538         }
539     }
540 
541     // process the instructions
542     const Json::Value insts = root["instructions"];
543     unsigned maxOpcode = 0;
544     bool firstOpcode = true;
545     for (const auto& inst : insts) {
546         const auto printingClass = inst["class"].asString();
547         if (printingClass.size() == 0) {
548             std::cerr << "Error: " << inst["opname"].asString()
549                       << " requires a non-empty printing \"class\" tag" << std::endl;
550             std::exit(1);
551         }
552         if (!buildingHeaders && printingClass == "@exclude")
553             continue;
554         if (tags.find(printingClass) == tags.end()) {
555             std::cerr << "Error: " << inst["opname"].asString()
556                       << " requires a \"class\" declared as a \"tag\" in \"instruction printing_class\""
557                       << std::endl;
558             std::exit(1);
559         }
560         const auto opcode = inst["opcode"].asUInt();
561         const std::string name = inst["opname"].asString();
562         if (firstOpcode) {
563           maxOpcode = opcode;
564           firstOpcode = false;
565         } else {
566           if (maxOpcode > opcode) {
567             std::cerr << "Error: " << name
568                       << " is out of order. It follows the instruction with opcode " << maxOpcode
569                       << std::endl;
570             std::exit(1);
571           } else {
572             maxOpcode = opcode;
573           }
574         }
575         EnumCaps caps = getCaps(inst);
576         std::string version = inst["version"].asString();
577         std::string lastVersion = inst["lastVersion"].asString();
578         Extensions exts = getExts(inst);
579         OperandParameters operands;
580         bool defResultId = false;
581         bool defTypeId = false;
582         for (const auto& operand : inst["operands"]) {
583             const std::string kind = operand["kind"].asString();
584             const std::string quantifier = operand.get("quantifier", "").asString();
585             const std::string doc = operand.get("name", "").asString();
586             if (!IsTypeOrResultId(kind, &defTypeId, &defResultId)) {
587                 const auto p = ToOperandClassAndOptionality(kind, quantifier);
588                 operands.push(p.type, doc, p.optional);
589             }
590         }
591         InstructionDesc.emplace_back(
592             std::move(EnumValue(opcode, name,
593                                 std::move(caps), std::move(version), std::move(lastVersion), std::move(exts),
594                                 std::move(operands))),
595              printingClass, defTypeId, defResultId);
596         if (!InstructionDesc.back().IsValid(OperandOpcode, "instruction")) {
597           errorCount++;
598         }
599     }
600 
601     // Specific additional context-dependent operands
602 
603     // Populate dest with EnumValue objects constructed from source.
604     const auto populateEnumValues = [&getCaps,&getExts,&errorCount](EnumValues* dest, const Json::Value& source, bool bitEnum) {
605         // A lambda for determining the numeric value to be used for a given
606         // enumerant in JSON form, and whether that value is a 0 in a bitfield.
607         auto getValue = [&bitEnum](const Json::Value& enumerant) {
608             std::pair<unsigned, bool> result{0u,false};
609             if (!bitEnum) {
610                 result.first = enumerant["value"].asUInt();
611             } else {
612                 const unsigned int bit = NumberStringToBit(enumerant["value"].asString());
613                 if (bit == 0)
614                     result.second = true;
615                 else
616                     result.first = bit - 1;  // This is the *shift* amount.
617             }
618             return result;
619         };
620 
621         unsigned maxValue = 0;
622         bool firstValue = true;
623         for (const auto& enumerant : source["enumerants"]) {
624             unsigned value;
625             bool skip_zero_in_bitfield;
626             std::tie(value, skip_zero_in_bitfield) = getValue(enumerant);
627             if (skip_zero_in_bitfield)
628                 continue;
629             if (firstValue) {
630               maxValue = value;
631               firstValue = false;
632             } else {
633               if (maxValue > value) {
634                 std::cerr << "Error: " << source["kind"] << " enumerant " << enumerant["enumerant"]
635                           << " is out of order. It has value " <<  value
636                           << " but follows the enumerant with value " << maxValue << std::endl;
637                 std::exit(1);
638               } else {
639                 maxValue = value;
640               }
641             }
642             EnumCaps caps(getCaps(enumerant));
643             std::string version = enumerant["version"].asString();
644             std::string lastVersion = enumerant["lastVersion"].asString();
645             Extensions exts(getExts(enumerant));
646             OperandParameters params;
647             const Json::Value& paramsJson = enumerant["parameters"];
648             if (!paramsJson.empty()) {  // This enumerant has parameters.
649                 assert(paramsJson.isArray());
650                 for (const auto& param : paramsJson) {
651                     const std::string kind = param["kind"].asString();
652                     const std::string doc = param.get("name", "").asString();
653                     const auto p = ToOperandClassAndOptionality(kind, ""); // All parameters are required!
654                     params.push(p.type, doc);
655                 }
656             }
657             dest->emplace_back(
658                 value, enumerant["enumerant"].asString(),
659                 std::move(caps), std::move(version), std::move(lastVersion), std::move(exts), std::move(params));
660         }
661     };
662 
663     const auto establishOperandClass = [&populateEnumValues,&errorCount](
664             const std::string& enumName, spv::OperandClass operandClass,
665             spv::EnumValues* enumValues, const Json::Value& operandEnum, const std::string& category) {
666         assert(category == "BitEnum" || category == "ValueEnum");
667         bool bitEnum = (category == "BitEnum");
668         if (!operandEnum["version"].empty()) {
669           std::cerr << "Error: container for " << enumName << " operand_kind must not have a version field" << std::endl;
670           errorCount++;
671         }
672         populateEnumValues(enumValues, operandEnum, bitEnum);
673         const std::string errContext = "enum " + enumName;
674         for (const auto& e: *enumValues) {
675           if (!e.IsValid(operandClass, errContext)) {
676             errorCount++;
677           }
678         }
679         OperandClassParams[operandClass].set(enumName, enumValues, bitEnum);
680     };
681 
682     const Json::Value operandEnums = root["operand_kinds"];
683     for (const auto& operandEnum : operandEnums) {
684         const std::string enumName = operandEnum["kind"].asString();
685         const std::string category = operandEnum["category"].asString();
686         if (enumName == "SourceLanguage") {
687             establishOperandClass(enumName, OperandSource, &SourceLanguageParams, operandEnum, category);
688         } else if (enumName == "Decoration") {
689             establishOperandClass(enumName, OperandDecoration, &DecorationParams, operandEnum, category);
690         } else if (enumName == "ExecutionMode") {
691             establishOperandClass(enumName, OperandExecutionMode, &ExecutionModeParams, operandEnum, category);
692         } else if (enumName == "Capability") {
693             establishOperandClass(enumName, OperandCapability, &CapabilityParams, operandEnum, category);
694         } else if (enumName == "AddressingModel") {
695             establishOperandClass(enumName, OperandAddressing, &AddressingParams, operandEnum, category);
696         } else if (enumName == "MemoryModel") {
697             establishOperandClass(enumName, OperandMemory, &MemoryParams, operandEnum, category);
698         } else if (enumName == "MemorySemantics") {
699             establishOperandClass(enumName, OperandMemorySemantics, &MemorySemanticsParams, operandEnum, category);
700         } else if (enumName == "ExecutionModel") {
701             establishOperandClass(enumName, OperandExecutionModel, &ExecutionModelParams, operandEnum, category);
702         } else if (enumName == "StorageClass") {
703             establishOperandClass(enumName, OperandStorage, &StorageParams, operandEnum, category);
704         } else if (enumName == "SamplerAddressingMode") {
705             establishOperandClass(enumName, OperandSamplerAddressingMode, &SamplerAddressingModeParams, operandEnum, category);
706         } else if (enumName == "SamplerFilterMode") {
707             establishOperandClass(enumName, OperandSamplerFilterMode, &SamplerFilterModeParams, operandEnum, category);
708         } else if (enumName == "ImageFormat") {
709             establishOperandClass(enumName, OperandSamplerImageFormat, &ImageFormatParams, operandEnum, category);
710         } else if (enumName == "ImageChannelOrder") {
711             establishOperandClass(enumName, OperandImageChannelOrder, &ImageChannelOrderParams, operandEnum, category);
712         } else if (enumName == "ImageChannelDataType") {
713             establishOperandClass(enumName, OperandImageChannelDataType, &ImageChannelDataTypeParams, operandEnum, category);
714         } else if (enumName == "ImageOperands") {
715             establishOperandClass(enumName, OperandImageOperands, &ImageOperandsParams, operandEnum, category);
716         } else if (enumName == "FPFastMathMode") {
717             establishOperandClass(enumName, OperandFPFastMath, &FPFastMathParams, operandEnum, category);
718         } else if (enumName == "FPRoundingMode") {
719             establishOperandClass(enumName, OperandFPRoundingMode, &FPRoundingModeParams, operandEnum, category);
720         } else if (enumName == "FPDenormMode") {
721             establishOperandClass(enumName, OperandFPDenormMode, &FPDenormModeParams, operandEnum, category);
722         } else if (enumName == "FPOperationMode") {
723             establishOperandClass(enumName, OperandFPOperationMode, &FPOperationModeParams, operandEnum, category);
724         } else if (enumName == "QuantizationModes") {
725             establishOperandClass(enumName, OperandQuantizationModes, &QuantizationModesParams, operandEnum, category);
726         } else if (enumName == "OverflowModes") {
727             establishOperandClass(enumName, OperandOverflowModes, &OverflowModesParams, operandEnum, category);
728         } else if (enumName == "LinkageType") {
729             establishOperandClass(enumName, OperandLinkageType, &LinkageTypeParams, operandEnum, category);
730         } else if (enumName == "FunctionParameterAttribute") {
731             establishOperandClass(enumName, OperandFuncParamAttr, &FuncParamAttrParams, operandEnum, category);
732         } else if (enumName == "AccessQualifier") {
733             establishOperandClass(enumName, OperandAccessQualifier, &AccessQualifierParams, operandEnum, category);
734         } else if (enumName == "BuiltIn") {
735             establishOperandClass(enumName, OperandBuiltIn, &BuiltInParams, operandEnum, category);
736         } else if (enumName == "SelectionControl") {
737             establishOperandClass(enumName, OperandSelect, &SelectionControlParams, operandEnum, category);
738         } else if (enumName == "LoopControl") {
739             establishOperandClass(enumName, OperandLoop, &LoopControlParams, operandEnum, category);
740         } else if (enumName == "FunctionControl") {
741             establishOperandClass(enumName, OperandFunction, &FunctionControlParams, operandEnum, category);
742         } else if (enumName == "Dim") {
743             establishOperandClass(enumName, OperandDimensionality, &DimensionalityParams, operandEnum, category);
744         } else if (enumName == "MemoryAccess") {
745             establishOperandClass(enumName, OperandMemoryOperands, &MemoryAccessParams, operandEnum, category);
746         } else if (enumName == "Scope") {
747             establishOperandClass(enumName, OperandScope, &ScopeParams, operandEnum, category);
748         } else if (enumName == "GroupOperation") {
749             establishOperandClass(enumName, OperandGroupOperation, &GroupOperationParams, operandEnum, category);
750         } else if (enumName == "KernelEnqueueFlags") {
751             establishOperandClass(enumName, OperandKernelEnqueueFlags, &KernelEnqueueFlagsParams, operandEnum, category);
752         } else if (enumName == "KernelProfilingInfo") {
753             establishOperandClass(enumName, OperandKernelProfilingInfo, &KernelProfilingInfoParams, operandEnum, category);
754         } else if (enumName == "RayFlags") {
755             establishOperandClass(enumName, OperandRayFlags, &RayFlagsParams, operandEnum, category);
756         } else if (enumName == "RayQueryIntersection") {
757             establishOperandClass(enumName, OperandRayQueryIntersection, &RayQueryIntersectionParams, operandEnum, category);
758         } else if (enumName == "RayQueryCommittedIntersectionType") {
759             establishOperandClass(enumName, OperandRayQueryCommittedIntersectionType, &RayQueryCommittedIntersectionTypeParams, operandEnum, category);
760         } else if (enumName == "RayQueryCandidateIntersectionType") {
761             establishOperandClass(enumName, OperandRayQueryCandidateIntersectionType, &RayQueryCandidateIntersectionTypeParams, operandEnum, category);
762         } else if (enumName == "FragmentShadingRate") {
763             establishOperandClass(enumName, OperandFragmentShadingRate, &FragmentShadingRateParams, operandEnum, category);
764         } else if (enumName == "PackedVectorFormat") {
765             establishOperandClass(enumName, OperandPackedVectorFormat, &PackedVectorFormatParams, operandEnum, category);
766         } else if (enumName == "CooperativeMatrixOperands") {
767             establishOperandClass(enumName, OperandCooperativeMatrixOperands, &CooperativeMatrixOperandsParams, operandEnum, category);
768         } else if (enumName == "CooperativeMatrixLayout") {
769             establishOperandClass(enumName, OperandCooperativeMatrixLayout, &CooperativeMatrixLayoutParams, operandEnum, category);
770         } else if (enumName == "CooperativeMatrixUse") {
771             establishOperandClass(enumName, OperandCooperativeMatrixUse, &CooperativeMatrixUseParams, operandEnum, category);
772         } else if (enumName == "InitializationModeQualifier") {
773             establishOperandClass(enumName, OperandInitializationModeQualifier, &InitializationModeQualifierParams, operandEnum, category);
774         } else if (enumName == "HostAccessQualifier") {
775             establishOperandClass(enumName, OperandHostAccessQualifier, &HostAccessQualifierParams, operandEnum, category);
776         } else if (enumName == "LoadCacheControl") {
777             establishOperandClass(enumName, OperandLoadCacheControl, &LoadCacheControlParams, operandEnum, category);
778         } else if (enumName == "StoreCacheControl") {
779             establishOperandClass(enumName, OperandStoreCacheControl, &StoreCacheControlParams, operandEnum, category);
780         }
781     }
782 
783     if (errorCount > 0) {
784       std::exit(1);
785     }
786 }
787 
788 };  // end namespace spv
789