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