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