1 | // Copyright (c) 2014-2024 The Khronos Group Inc.
|
---|
2 | //
|
---|
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
|
---|
4 | // of this software and/or associated documentation files (the "Materials"),
|
---|
5 | // to deal in the Materials without restriction, including without limitation
|
---|
6 | // the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
---|
7 | // and/or sell copies of the Materials, and to permit persons to whom the
|
---|
8 | // Materials are furnished to do so, subject to the following conditions:
|
---|
9 | //
|
---|
10 | // The above copyright notice and this permission notice shall be included in
|
---|
11 | // all copies or substantial portions of the Materials.
|
---|
12 | //
|
---|
13 | // MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS
|
---|
14 | // STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND
|
---|
15 | // HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/
|
---|
16 | //
|
---|
17 | // THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
---|
18 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
---|
19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
---|
20 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
---|
21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
---|
22 | // FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS
|
---|
23 | // IN THE MATERIALS.
|
---|
24 |
|
---|
25 | #include <assert.h>
|
---|
26 | #include <string.h>
|
---|
27 | #include <algorithm>
|
---|
28 | #include <cstdlib>
|
---|
29 | #include <iostream>
|
---|
30 | #include <unordered_map>
|
---|
31 | #include <unordered_set>
|
---|
32 | #include <utility>
|
---|
33 | #include <fstream>
|
---|
34 |
|
---|
35 | #include "jsoncpp/dist/json/json.h"
|
---|
36 |
|
---|
37 | #include "jsonToSpirv.h"
|
---|
38 |
|
---|
39 | namespace {
|
---|
40 | // Returns true if the given string is a valid SPIR-V version.
|
---|
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.
|
---|
54 | bool validSpirvVersionStringSpecifier(const std::string s) {
|
---|
55 | return s == "None" || validSpirvVersionString(s);
|
---|
56 | }
|
---|
57 | } // anonymous namespace
|
---|
58 |
|
---|
59 | namespace spv {
|
---|
60 |
|
---|
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 |
|
---|
145 | bool EnumValue::IsValid(OperandClass oc, const std::string& context) const
|
---|
146 | {
|
---|
147 | bool result = true;
|
---|
148 | if (firstVersion.empty()) {
|
---|
149 | std::cerr << "Error: " << context << " " << name << " \"version\" must be set, probably to \"None\"" << std::endl;
|
---|
150 | result = false;
|
---|
151 | } else if (!validSpirvVersionStringSpecifier(firstVersion)) {
|
---|
152 | std::cerr << "Error: " << context << " " << name << " \"version\" is invalid: " << firstVersion << std::endl;
|
---|
153 | result = false;
|
---|
154 | }
|
---|
155 | if (!lastVersion.empty() && !validSpirvVersionString(lastVersion)) {
|
---|
156 | std::cerr << "Error: " << context << " " << name << " \"lastVersion\" is invalid: " << lastVersion << std::endl;
|
---|
157 | result = false;
|
---|
158 | }
|
---|
159 |
|
---|
160 | // When a feature is introduced by an extension, the firstVersion is set to
|
---|
161 | // "None". There are three cases:
|
---|
162 | // - A new capability should be guarded/enabled by the extension
|
---|
163 | // - A new instruction should be:
|
---|
164 | // - Guarded/enabled by a new capability.
|
---|
165 | // - Not enabled by *both* a capability and an extension.
|
---|
166 | // There are many existing instructions that are already like this,
|
---|
167 | // and we grandparent them as allowed.
|
---|
168 | // - Other enums fall into two cases:
|
---|
169 | // 1. The enum is part of a new operand kind introduced by the extension.
|
---|
170 | // In this case we rely on transitivity: The use of the operand occurs
|
---|
171 | // in a new instruction that itself is guarded; or as the operand of
|
---|
172 | // another operand that itself is (recursively) guarded.
|
---|
173 | // 2. The enum is a new case in an existing operand kind. This case
|
---|
174 | // should be guarded by a capability. However, we do not check this
|
---|
175 | // here. Checking it requires more context than we have here.
|
---|
176 | if (oc == OperandOpcode) {
|
---|
177 | const bool instruction_unusable =
|
---|
178 | (firstVersion == "None") && extensions.empty() && capabilities.empty();
|
---|
179 | if (instruction_unusable) {
|
---|
180 | std::cerr << "Error: " << context << " " << name << " is not usable: "
|
---|
181 | << "its version is set to \"None\", and it is not enabled by a "
|
---|
182 | << "capability or extension. Guard it with a capability."
|
---|
183 | << std::endl;
|
---|
184 | result = false;
|
---|
185 | }
|
---|
186 | // Complain if an instruction is not in any core version and also enabled by
|
---|
187 | // both an extension and a capability.
|
---|
188 | // It's important to check the "not in any core version" case, because,
|
---|
189 | // for example, OpTerminateInvocation is in SPIR-V 1.6 *and* enabled by an
|
---|
190 | // extension, and guarded by the Shader capability.
|
---|
191 | const bool instruction_doubly_enabled = (firstVersion == "None") &&
|
---|
192 | !extensions.empty() &&
|
---|
193 | !capabilities.empty();
|
---|
194 | if (instruction_doubly_enabled && !IsLegacyDoublyEnabledInstruction(name)) {
|
---|
195 | std::cerr << "Error: " << context << " " << name << " is doubly-enabled: "
|
---|
196 | << "it is enabled by both a capability and an extension. "
|
---|
197 | << "Guard it with a capability only." << std::endl;
|
---|
198 | result = false;
|
---|
199 | }
|
---|
200 | }
|
---|
201 | if (oc == OperandCapability) {
|
---|
202 | // If capability X lists capabilities Y and Z, then Y and Z are *enabled*
|
---|
203 | // when X is enabled. They are not *guards* on X's use.
|
---|
204 | // Only versions and extensions can guard a capability.
|
---|
205 | const bool capability_unusable =
|
---|
206 | (firstVersion == "None") && extensions.empty();
|
---|
207 | if (capability_unusable) {
|
---|
208 | std::cerr << "Error: " << context << " " << name << " is not usable: "
|
---|
209 | << "its version is set to \"None\", and it is not enabled by "
|
---|
210 | << "an extension. Guard it with an extension." << std::endl;
|
---|
211 | result = false;
|
---|
212 | }
|
---|
213 | }
|
---|
214 |
|
---|
215 | return result;
|
---|
216 | }
|
---|
217 |
|
---|
218 | // The set of objects that hold all the instruction/operand
|
---|
219 | // parameterization information.
|
---|
220 | InstructionValues InstructionDesc;
|
---|
221 |
|
---|
222 | // The ordered list (in printing order) of printing classes
|
---|
223 | // (specification subsections).
|
---|
224 | PrintingClasses InstructionPrintingClasses;
|
---|
225 |
|
---|
226 | // Note: There is no entry for OperandOpcode. Use InstructionDesc instead.
|
---|
227 | EnumDefinition OperandClassParams[OperandOpcode];
|
---|
228 | EnumValues SourceLanguageParams;
|
---|
229 | EnumValues ExecutionModelParams;
|
---|
230 | EnumValues AddressingParams;
|
---|
231 | EnumValues MemoryParams;
|
---|
232 | EnumValues ExecutionModeParams;
|
---|
233 | EnumValues StorageParams;
|
---|
234 | EnumValues SamplerAddressingModeParams;
|
---|
235 | EnumValues SamplerFilterModeParams;
|
---|
236 | EnumValues ImageFormatParams;
|
---|
237 | EnumValues ImageChannelOrderParams;
|
---|
238 | EnumValues ImageChannelDataTypeParams;
|
---|
239 | EnumValues ImageOperandsParams;
|
---|
240 | EnumValues FPFastMathParams;
|
---|
241 | EnumValues FPRoundingModeParams;
|
---|
242 | EnumValues FPDenormModeParams;
|
---|
243 | EnumValues FPOperationModeParams;
|
---|
244 | EnumValues QuantizationModesParams;
|
---|
245 | EnumValues OverflowModesParams;
|
---|
246 | EnumValues LinkageTypeParams;
|
---|
247 | EnumValues DecorationParams;
|
---|
248 | EnumValues BuiltInParams;
|
---|
249 | EnumValues DimensionalityParams;
|
---|
250 | EnumValues FuncParamAttrParams;
|
---|
251 | EnumValues AccessQualifierParams;
|
---|
252 | EnumValues GroupOperationParams;
|
---|
253 | EnumValues LoopControlParams;
|
---|
254 | EnumValues SelectionControlParams;
|
---|
255 | EnumValues FunctionControlParams;
|
---|
256 | EnumValues MemorySemanticsParams;
|
---|
257 | EnumValues MemoryAccessParams;
|
---|
258 | EnumValues ScopeParams;
|
---|
259 | EnumValues KernelEnqueueFlagsParams;
|
---|
260 | EnumValues KernelProfilingInfoParams;
|
---|
261 | EnumValues CapabilityParams;
|
---|
262 | EnumValues RayFlagsParams;
|
---|
263 | EnumValues RayQueryIntersectionParams;
|
---|
264 | EnumValues RayQueryCommittedIntersectionTypeParams;
|
---|
265 | EnumValues RayQueryCandidateIntersectionTypeParams;
|
---|
266 | EnumValues FragmentShadingRateParams;
|
---|
267 | EnumValues PackedVectorFormatParams;
|
---|
268 | EnumValues CooperativeMatrixOperandsParams;
|
---|
269 | EnumValues CooperativeMatrixLayoutParams;
|
---|
270 | EnumValues CooperativeMatrixUseParams;
|
---|
271 | EnumValues InitializationModeQualifierParams;
|
---|
272 | EnumValues HostAccessQualifierParams;
|
---|
273 | EnumValues LoadCacheControlParams;
|
---|
274 | EnumValues StoreCacheControlParams;
|
---|
275 | EnumValues NamedMaximumNumberOfRegistersParams;
|
---|
276 | EnumValues RawAccessChainOperandsParams;
|
---|
277 |
|
---|
278 | std::pair<bool, std::string> ReadFile(const std::string& path)
|
---|
279 | {
|
---|
280 | std::ifstream fstream(path, std::ios::in);
|
---|
281 | if (fstream) {
|
---|
282 | std::string contents;
|
---|
283 | fstream.seekg(0, std::ios::end);
|
---|
284 | contents.reserve((unsigned int)fstream.tellg());
|
---|
285 | fstream.seekg(0, std::ios::beg);
|
---|
286 | contents.assign((std::istreambuf_iterator<char>(fstream)),
|
---|
287 | std::istreambuf_iterator<char>());
|
---|
288 | return std::make_pair(true, contents);
|
---|
289 | }
|
---|
290 | return std::make_pair(false, "");
|
---|
291 | }
|
---|
292 |
|
---|
293 | struct ClassOptionality {
|
---|
294 | OperandClass type;
|
---|
295 | bool optional;
|
---|
296 | };
|
---|
297 |
|
---|
298 | // Converts the |operandKind| and |quantifier| pair used to describe operands
|
---|
299 | // in the JSON grammar to OperandClass and optionality used in this repo.
|
---|
300 | ClassOptionality ToOperandClassAndOptionality(const std::string& operandKind, const std::string& quantifier)
|
---|
301 | {
|
---|
302 | assert(quantifier.empty() || quantifier == "?" || quantifier == "*");
|
---|
303 |
|
---|
304 | if (operandKind == "IdRef") {
|
---|
305 | if (quantifier.empty())
|
---|
306 | return {OperandId, false};
|
---|
307 | else if (quantifier == "?")
|
---|
308 | return {OperandId, true};
|
---|
309 | else
|
---|
310 | return {OperandVariableIds, false};
|
---|
311 | } else if (operandKind == "LiteralInteger") {
|
---|
312 | if (quantifier.empty())
|
---|
313 | return {OperandLiteralNumber, false};
|
---|
314 | if (quantifier == "?")
|
---|
315 | return {OperandOptionalLiteral, true};
|
---|
316 | else
|
---|
317 | return {OperandVariableLiterals, false};
|
---|
318 | } else if (operandKind == "LiteralString") {
|
---|
319 | if (quantifier.empty())
|
---|
320 | return {OperandLiteralString, false};
|
---|
321 | else if (quantifier == "?")
|
---|
322 | return {OperandLiteralString, true};
|
---|
323 | else {
|
---|
324 | return {OperandOptionalLiteralStrings, false};
|
---|
325 | }
|
---|
326 | } else if (operandKind == "PairLiteralIntegerIdRef") {
|
---|
327 | // Used by OpSwitch in the grammar
|
---|
328 | return {OperandVariableLiteralId, false};
|
---|
329 | } else if (operandKind == "PairIdRefLiteralInteger") {
|
---|
330 | // Used by OpGroupMemberDecorate in the grammar
|
---|
331 | return {OperandVariableIdLiteral, false};
|
---|
332 | } else if (operandKind == "PairIdRefIdRef") {
|
---|
333 | // Used by OpPhi in the grammar
|
---|
334 | return {OperandVariableIds, false};
|
---|
335 | } else {
|
---|
336 | OperandClass type = OperandNone;
|
---|
337 | if (operandKind == "IdMemorySemantics" || operandKind == "MemorySemantics") {
|
---|
338 | type = OperandMemorySemantics;
|
---|
339 | } else if (operandKind == "IdScope" || operandKind == "Scope") {
|
---|
340 | type = OperandScope;
|
---|
341 | } else if (operandKind == "LiteralExtInstInteger") {
|
---|
342 | type = OperandLiteralNumber;
|
---|
343 | } else if (operandKind == "LiteralSpecConstantOpInteger") {
|
---|
344 | type = OperandLiteralNumber;
|
---|
345 | } else if (operandKind == "LiteralContextDependentNumber") {
|
---|
346 | type = OperandAnySizeLiteralNumber;
|
---|
347 | } else if (operandKind == "LiteralFloat") {
|
---|
348 | type = OperandLiteralNumber;
|
---|
349 | } else if (operandKind == "SourceLanguage") {
|
---|
350 | type = OperandSource;
|
---|
351 | } else if (operandKind == "ExecutionModel") {
|
---|
352 | type = OperandExecutionModel;
|
---|
353 | } else if (operandKind == "AddressingModel") {
|
---|
354 | type = OperandAddressing;
|
---|
355 | } else if (operandKind == "MemoryModel") {
|
---|
356 | type = OperandMemory;
|
---|
357 | } else if (operandKind == "ExecutionMode") {
|
---|
358 | type = OperandExecutionMode;
|
---|
359 | } else if (operandKind == "StorageClass") {
|
---|
360 | type = OperandStorage;
|
---|
361 | } else if (operandKind == "Dim") {
|
---|
362 | type = OperandDimensionality;
|
---|
363 | } else if (operandKind == "SamplerAddressingMode") {
|
---|
364 | type = OperandSamplerAddressingMode;
|
---|
365 | } else if (operandKind == "SamplerFilterMode") {
|
---|
366 | type = OperandSamplerFilterMode;
|
---|
367 | } else if (operandKind == "ImageFormat") {
|
---|
368 | type = OperandSamplerImageFormat;
|
---|
369 | } else if (operandKind == "ImageChannelOrder") {
|
---|
370 | type = OperandImageChannelOrder;
|
---|
371 | } else if (operandKind == "ImageChannelDataType") {
|
---|
372 | type = OperandImageChannelDataType;
|
---|
373 | } else if (operandKind == "FPRoundingMode") {
|
---|
374 | type = OperandFPRoundingMode;
|
---|
375 | } else if (operandKind == "FPDenormMode") {
|
---|
376 | type = OperandFPDenormMode;
|
---|
377 | } else if (operandKind == "FPOperationMode") {
|
---|
378 | type = OperandFPOperationMode;
|
---|
379 | } else if (operandKind == "QuantizationModes") {
|
---|
380 | type = OperandQuantizationModes;
|
---|
381 | } else if (operandKind == "OverflowModes") {
|
---|
382 | type = OperandOverflowModes;
|
---|
383 | } else if (operandKind == "LinkageType") {
|
---|
384 | type = OperandLinkageType;
|
---|
385 | } else if (operandKind == "AccessQualifier") {
|
---|
386 | type = OperandAccessQualifier;
|
---|
387 | } else if (operandKind == "FunctionParameterAttribute") {
|
---|
388 | type = OperandFuncParamAttr;
|
---|
389 | } else if (operandKind == "Decoration") {
|
---|
390 | type = OperandDecoration;
|
---|
391 | } else if (operandKind == "BuiltIn") {
|
---|
392 | type = OperandBuiltIn;
|
---|
393 | } else if (operandKind == "GroupOperation") {
|
---|
394 | type = OperandGroupOperation;
|
---|
395 | } else if (operandKind == "KernelEnqueueFlags") {
|
---|
396 | type = OperandKernelEnqueueFlags;
|
---|
397 | } else if (operandKind == "KernelProfilingInfo") {
|
---|
398 | type = OperandKernelProfilingInfo;
|
---|
399 | } else if (operandKind == "Capability") {
|
---|
400 | type = OperandCapability;
|
---|
401 | } else if (operandKind == "ImageOperands") {
|
---|
402 | type = OperandImageOperands;
|
---|
403 | } else if (operandKind == "FPFastMathMode") {
|
---|
404 | type = OperandFPFastMath;
|
---|
405 | } else if (operandKind == "SelectionControl") {
|
---|
406 | type = OperandSelect;
|
---|
407 | } else if (operandKind == "LoopControl") {
|
---|
408 | type = OperandLoop;
|
---|
409 | } else if (operandKind == "FunctionControl") {
|
---|
410 | type = OperandFunction;
|
---|
411 | } else if (operandKind == "MemoryAccess") {
|
---|
412 | type = OperandMemoryOperands;
|
---|
413 | } else if (operandKind == "RayFlags") {
|
---|
414 | type = OperandRayFlags;
|
---|
415 | } else if (operandKind == "RayQueryIntersection") {
|
---|
416 | type = OperandRayQueryIntersection;
|
---|
417 | } else if (operandKind == "RayQueryCommittedIntersectionType") {
|
---|
418 | type = OperandRayQueryCommittedIntersectionType;
|
---|
419 | } else if (operandKind == "RayQueryCandidateIntersectionType") {
|
---|
420 | type = OperandRayQueryCandidateIntersectionType;
|
---|
421 | } else if (operandKind == "FragmentShadingRate") {
|
---|
422 | type = OperandFragmentShadingRate;
|
---|
423 | } else if (operandKind == "PackedVectorFormat") {
|
---|
424 | type = OperandPackedVectorFormat;
|
---|
425 | } else if (operandKind == "CooperativeMatrixOperands") {
|
---|
426 | type = OperandCooperativeMatrixOperands;
|
---|
427 | } else if (operandKind == "CooperativeMatrixLayout") {
|
---|
428 | type = OperandCooperativeMatrixLayout;
|
---|
429 | } else if (operandKind == "CooperativeMatrixUse") {
|
---|
430 | type = OperandCooperativeMatrixUse;
|
---|
431 | } else if (operandKind == "InitializationModeQualifier") {
|
---|
432 | type = OperandInitializationModeQualifier;
|
---|
433 | } else if (operandKind == "HostAccessQualifier") {
|
---|
434 | type = OperandHostAccessQualifier;
|
---|
435 | } else if (operandKind == "LoadCacheControl") {
|
---|
436 | type = OperandLoadCacheControl;
|
---|
437 | } else if (operandKind == "StoreCacheControl") {
|
---|
438 | type = OperandStoreCacheControl;
|
---|
439 | } else if (operandKind == "NamedMaximumNumberOfRegisters") {
|
---|
440 | type = OperandNamedMaximumNumberOfRegisters;
|
---|
441 | } else if (operandKind == "RawAccessChainOperands") {
|
---|
442 | type = OperandRawAccessChainOperands;
|
---|
443 | }
|
---|
444 |
|
---|
445 | if (type == OperandNone) {
|
---|
446 | std::cerr << "Unhandled operand kind found: " << operandKind << std::endl;
|
---|
447 | exit(1);
|
---|
448 | }
|
---|
449 | return {type, !quantifier.empty()};
|
---|
450 | }
|
---|
451 | }
|
---|
452 |
|
---|
453 | bool IsTypeOrResultId(const std::string& str, bool* isType, bool* isResult)
|
---|
454 | {
|
---|
455 | if (str == "IdResultType")
|
---|
456 | return *isType = true;
|
---|
457 | if (str == "IdResult")
|
---|
458 | return *isResult = true;
|
---|
459 | return false;
|
---|
460 | }
|
---|
461 |
|
---|
462 | // Given a number string, returns the position of the only bits set in the number.
|
---|
463 | // So it requires the number is a power of two.
|
---|
464 | unsigned int NumberStringToBit(const std::string& str)
|
---|
465 | {
|
---|
466 | char* parseEnd;
|
---|
467 | unsigned int value = (unsigned int)std::strtol(str.c_str(), &parseEnd, 16);
|
---|
468 | assert(!(value & (value - 1)) && "input number is not a power of 2");
|
---|
469 | unsigned int bit = 0;
|
---|
470 | for (; value; value >>= 1) ++bit;
|
---|
471 | return bit;
|
---|
472 | }
|
---|
473 |
|
---|
474 | void jsonToSpirv(const std::string& jsonPath, bool buildingHeaders)
|
---|
475 | {
|
---|
476 | // only do this once.
|
---|
477 | static bool initialized = false;
|
---|
478 | if (initialized)
|
---|
479 | return;
|
---|
480 | initialized = true;
|
---|
481 |
|
---|
482 | size_t errorCount = 0;
|
---|
483 |
|
---|
484 | // Read the JSON grammar file.
|
---|
485 | bool fileReadOk = false;
|
---|
486 | std::string content;
|
---|
487 | std::tie(fileReadOk, content) = ReadFile(jsonPath);
|
---|
488 | if (!fileReadOk) {
|
---|
489 | std::cerr << "Failed to read JSON grammar file: "
|
---|
490 | << jsonPath << std::endl;
|
---|
491 | exit(1);
|
---|
492 | }
|
---|
493 |
|
---|
494 | // Decode the JSON grammar file.
|
---|
495 | Json::Reader reader;
|
---|
496 | Json::Value root;
|
---|
497 | if (!reader.parse(content, root)) {
|
---|
498 | std::cerr << "Failed to parse JSON grammar:\n"
|
---|
499 | << reader.getFormattedErrorMessages();
|
---|
500 | exit(1);
|
---|
501 | }
|
---|
502 |
|
---|
503 | // Layouts for all instructions.
|
---|
504 |
|
---|
505 | // A lambda for returning capabilities from a JSON object as strings.
|
---|
506 | const auto getCaps = [](const Json::Value& object) {
|
---|
507 | EnumCaps result;
|
---|
508 | const auto& caps = object["capabilities"];
|
---|
509 | if (!caps.empty()) {
|
---|
510 | assert(caps.isArray());
|
---|
511 | for (const auto& cap : caps) {
|
---|
512 | result.emplace_back(cap.asString());
|
---|
513 | }
|
---|
514 | }
|
---|
515 | return result;
|
---|
516 | };
|
---|
517 |
|
---|
518 | // A lambda for returning extensions from a JSON object as strings.
|
---|
519 | const auto getExts = [](const Json::Value& object) {
|
---|
520 | Extensions result;
|
---|
521 | const auto& exts = object["extensions"];
|
---|
522 | if (!exts.empty()) {
|
---|
523 | assert(exts.isArray());
|
---|
524 | for (const auto& ext : exts) {
|
---|
525 | result.emplace_back(ext.asString());
|
---|
526 | }
|
---|
527 | }
|
---|
528 | return result;
|
---|
529 | };
|
---|
530 |
|
---|
531 | // set up the printing classes
|
---|
532 | std::unordered_set<std::string> tags; // short-lived local for error checking below
|
---|
533 | const Json::Value printingClasses = root["instruction_printing_class"];
|
---|
534 | for (const auto& printingClass : printingClasses) {
|
---|
535 | if (printingClass["tag"].asString().size() > 0)
|
---|
536 | tags.insert(printingClass["tag"].asString()); // just for error checking
|
---|
537 | else {
|
---|
538 | std::cerr << "Error: each instruction_printing_class requires a non-empty \"tag\"" << std::endl;
|
---|
539 | std::exit(1);
|
---|
540 | }
|
---|
541 | if (buildingHeaders || printingClass["tag"].asString() != "@exclude") {
|
---|
542 | InstructionPrintingClasses.push_back({printingClass["tag"].asString(),
|
---|
543 | printingClass["heading"].asString()});
|
---|
544 | }
|
---|
545 | }
|
---|
546 |
|
---|
547 | // process the instructions
|
---|
548 | const Json::Value insts = root["instructions"];
|
---|
549 | unsigned maxOpcode = 0;
|
---|
550 | bool firstOpcode = true;
|
---|
551 | for (const auto& inst : insts) {
|
---|
552 | const auto printingClass = inst["class"].asString();
|
---|
553 | if (printingClass.size() == 0) {
|
---|
554 | std::cerr << "Error: " << inst["opname"].asString()
|
---|
555 | << " requires a non-empty printing \"class\" tag" << std::endl;
|
---|
556 | std::exit(1);
|
---|
557 | }
|
---|
558 | if (!buildingHeaders && printingClass == "@exclude")
|
---|
559 | continue;
|
---|
560 | if (tags.find(printingClass) == tags.end()) {
|
---|
561 | std::cerr << "Error: " << inst["opname"].asString()
|
---|
562 | << " requires a \"class\" declared as a \"tag\" in \"instruction printing_class\""
|
---|
563 | << std::endl;
|
---|
564 | std::exit(1);
|
---|
565 | }
|
---|
566 | const auto opcode = inst["opcode"].asUInt();
|
---|
567 | const std::string name = inst["opname"].asString();
|
---|
568 | if (firstOpcode) {
|
---|
569 | maxOpcode = opcode;
|
---|
570 | firstOpcode = false;
|
---|
571 | } else {
|
---|
572 | if (maxOpcode > opcode) {
|
---|
573 | std::cerr << "Error: " << name
|
---|
574 | << " is out of order. It follows the instruction with opcode " << maxOpcode
|
---|
575 | << std::endl;
|
---|
576 | std::exit(1);
|
---|
577 | } else {
|
---|
578 | maxOpcode = opcode;
|
---|
579 | }
|
---|
580 | }
|
---|
581 | EnumCaps caps = getCaps(inst);
|
---|
582 | std::string version = inst["version"].asString();
|
---|
583 | std::string lastVersion = inst["lastVersion"].asString();
|
---|
584 | Extensions exts = getExts(inst);
|
---|
585 | OperandParameters operands;
|
---|
586 | bool defResultId = false;
|
---|
587 | bool defTypeId = false;
|
---|
588 | for (const auto& operand : inst["operands"]) {
|
---|
589 | const std::string kind = operand["kind"].asString();
|
---|
590 | const std::string quantifier = operand.get("quantifier", "").asString();
|
---|
591 | const std::string doc = operand.get("name", "").asString();
|
---|
592 | if (!IsTypeOrResultId(kind, &defTypeId, &defResultId)) {
|
---|
593 | const auto p = ToOperandClassAndOptionality(kind, quantifier);
|
---|
594 | operands.push(p.type, doc, p.optional);
|
---|
595 | }
|
---|
596 | }
|
---|
597 | InstructionDesc.emplace_back(
|
---|
598 | std::move(EnumValue(opcode, name,
|
---|
599 | std::move(caps), std::move(version), std::move(lastVersion), std::move(exts),
|
---|
600 | std::move(operands))),
|
---|
601 | printingClass, defTypeId, defResultId);
|
---|
602 | if (!InstructionDesc.back().IsValid(OperandOpcode, "instruction")) {
|
---|
603 | errorCount++;
|
---|
604 | }
|
---|
605 | }
|
---|
606 |
|
---|
607 | // Specific additional context-dependent operands
|
---|
608 |
|
---|
609 | // Populate dest with EnumValue objects constructed from source.
|
---|
610 | const auto populateEnumValues = [&getCaps,&getExts,&errorCount](EnumValues* dest, const Json::Value& source, bool bitEnum) {
|
---|
611 | // A lambda for determining the numeric value to be used for a given
|
---|
612 | // enumerant in JSON form, and whether that value is a 0 in a bitfield.
|
---|
613 | auto getValue = [&bitEnum](const Json::Value& enumerant) {
|
---|
614 | std::pair<unsigned, bool> result{0u,false};
|
---|
615 | if (!bitEnum) {
|
---|
616 | result.first = enumerant["value"].asUInt();
|
---|
617 | } else {
|
---|
618 | const unsigned int bit = NumberStringToBit(enumerant["value"].asString());
|
---|
619 | if (bit == 0)
|
---|
620 | result.second = true;
|
---|
621 | else
|
---|
622 | result.first = bit - 1; // This is the *shift* amount.
|
---|
623 | }
|
---|
624 | return result;
|
---|
625 | };
|
---|
626 |
|
---|
627 | unsigned maxValue = 0;
|
---|
628 | bool firstValue = true;
|
---|
629 | for (const auto& enumerant : source["enumerants"]) {
|
---|
630 | unsigned value;
|
---|
631 | bool skip_zero_in_bitfield;
|
---|
632 | std::tie(value, skip_zero_in_bitfield) = getValue(enumerant);
|
---|
633 | if (skip_zero_in_bitfield)
|
---|
634 | continue;
|
---|
635 | if (firstValue) {
|
---|
636 | maxValue = value;
|
---|
637 | firstValue = false;
|
---|
638 | } else {
|
---|
639 | if (maxValue > value) {
|
---|
640 | std::cerr << "Error: " << source["kind"] << " enumerant " << enumerant["enumerant"]
|
---|
641 | << " is out of order. It has value " << value
|
---|
642 | << " but follows the enumerant with value " << maxValue << std::endl;
|
---|
643 | std::exit(1);
|
---|
644 | } else {
|
---|
645 | maxValue = value;
|
---|
646 | }
|
---|
647 | }
|
---|
648 | EnumCaps caps(getCaps(enumerant));
|
---|
649 | std::string version = enumerant["version"].asString();
|
---|
650 | std::string lastVersion = enumerant["lastVersion"].asString();
|
---|
651 | Extensions exts(getExts(enumerant));
|
---|
652 | OperandParameters params;
|
---|
653 | const Json::Value& paramsJson = enumerant["parameters"];
|
---|
654 | if (!paramsJson.empty()) { // This enumerant has parameters.
|
---|
655 | assert(paramsJson.isArray());
|
---|
656 | for (const auto& param : paramsJson) {
|
---|
657 | const std::string kind = param["kind"].asString();
|
---|
658 | const std::string doc = param.get("name", "").asString();
|
---|
659 | const auto p = ToOperandClassAndOptionality(kind, ""); // All parameters are required!
|
---|
660 | params.push(p.type, doc);
|
---|
661 | }
|
---|
662 | }
|
---|
663 | dest->emplace_back(
|
---|
664 | value, enumerant["enumerant"].asString(),
|
---|
665 | std::move(caps), std::move(version), std::move(lastVersion), std::move(exts), std::move(params));
|
---|
666 | }
|
---|
667 | };
|
---|
668 |
|
---|
669 | const auto establishOperandClass = [&populateEnumValues,&errorCount](
|
---|
670 | const std::string& enumName, spv::OperandClass operandClass,
|
---|
671 | spv::EnumValues* enumValues, const Json::Value& operandEnum, const std::string& category) {
|
---|
672 | assert(category == "BitEnum" || category == "ValueEnum");
|
---|
673 | bool bitEnum = (category == "BitEnum");
|
---|
674 | if (!operandEnum["version"].empty()) {
|
---|
675 | std::cerr << "Error: container for " << enumName << " operand_kind must not have a version field" << std::endl;
|
---|
676 | errorCount++;
|
---|
677 | }
|
---|
678 | populateEnumValues(enumValues, operandEnum, bitEnum);
|
---|
679 | const std::string errContext = "enum " + enumName;
|
---|
680 | for (const auto& e: *enumValues) {
|
---|
681 | if (!e.IsValid(operandClass, errContext)) {
|
---|
682 | errorCount++;
|
---|
683 | }
|
---|
684 | }
|
---|
685 | OperandClassParams[operandClass].set(enumName, enumValues, bitEnum);
|
---|
686 | };
|
---|
687 |
|
---|
688 | const Json::Value operandEnums = root["operand_kinds"];
|
---|
689 | for (const auto& operandEnum : operandEnums) {
|
---|
690 | const std::string enumName = operandEnum["kind"].asString();
|
---|
691 | const std::string category = operandEnum["category"].asString();
|
---|
692 | if (enumName == "SourceLanguage") {
|
---|
693 | establishOperandClass(enumName, OperandSource, &SourceLanguageParams, operandEnum, category);
|
---|
694 | } else if (enumName == "Decoration") {
|
---|
695 | establishOperandClass(enumName, OperandDecoration, &DecorationParams, operandEnum, category);
|
---|
696 | } else if (enumName == "ExecutionMode") {
|
---|
697 | establishOperandClass(enumName, OperandExecutionMode, &ExecutionModeParams, operandEnum, category);
|
---|
698 | } else if (enumName == "Capability") {
|
---|
699 | establishOperandClass(enumName, OperandCapability, &CapabilityParams, operandEnum, category);
|
---|
700 | } else if (enumName == "AddressingModel") {
|
---|
701 | establishOperandClass(enumName, OperandAddressing, &AddressingParams, operandEnum, category);
|
---|
702 | } else if (enumName == "MemoryModel") {
|
---|
703 | establishOperandClass(enumName, OperandMemory, &MemoryParams, operandEnum, category);
|
---|
704 | } else if (enumName == "MemorySemantics") {
|
---|
705 | establishOperandClass(enumName, OperandMemorySemantics, &MemorySemanticsParams, operandEnum, category);
|
---|
706 | } else if (enumName == "ExecutionModel") {
|
---|
707 | establishOperandClass(enumName, OperandExecutionModel, &ExecutionModelParams, operandEnum, category);
|
---|
708 | } else if (enumName == "StorageClass") {
|
---|
709 | establishOperandClass(enumName, OperandStorage, &StorageParams, operandEnum, category);
|
---|
710 | } else if (enumName == "SamplerAddressingMode") {
|
---|
711 | establishOperandClass(enumName, OperandSamplerAddressingMode, &SamplerAddressingModeParams, operandEnum, category);
|
---|
712 | } else if (enumName == "SamplerFilterMode") {
|
---|
713 | establishOperandClass(enumName, OperandSamplerFilterMode, &SamplerFilterModeParams, operandEnum, category);
|
---|
714 | } else if (enumName == "ImageFormat") {
|
---|
715 | establishOperandClass(enumName, OperandSamplerImageFormat, &ImageFormatParams, operandEnum, category);
|
---|
716 | } else if (enumName == "ImageChannelOrder") {
|
---|
717 | establishOperandClass(enumName, OperandImageChannelOrder, &ImageChannelOrderParams, operandEnum, category);
|
---|
718 | } else if (enumName == "ImageChannelDataType") {
|
---|
719 | establishOperandClass(enumName, OperandImageChannelDataType, &ImageChannelDataTypeParams, operandEnum, category);
|
---|
720 | } else if (enumName == "ImageOperands") {
|
---|
721 | establishOperandClass(enumName, OperandImageOperands, &ImageOperandsParams, operandEnum, category);
|
---|
722 | } else if (enumName == "FPFastMathMode") {
|
---|
723 | establishOperandClass(enumName, OperandFPFastMath, &FPFastMathParams, operandEnum, category);
|
---|
724 | } else if (enumName == "FPRoundingMode") {
|
---|
725 | establishOperandClass(enumName, OperandFPRoundingMode, &FPRoundingModeParams, operandEnum, category);
|
---|
726 | } else if (enumName == "FPDenormMode") {
|
---|
727 | establishOperandClass(enumName, OperandFPDenormMode, &FPDenormModeParams, operandEnum, category);
|
---|
728 | } else if (enumName == "FPOperationMode") {
|
---|
729 | establishOperandClass(enumName, OperandFPOperationMode, &FPOperationModeParams, operandEnum, category);
|
---|
730 | } else if (enumName == "QuantizationModes") {
|
---|
731 | establishOperandClass(enumName, OperandQuantizationModes, &QuantizationModesParams, operandEnum, category);
|
---|
732 | } else if (enumName == "OverflowModes") {
|
---|
733 | establishOperandClass(enumName, OperandOverflowModes, &OverflowModesParams, operandEnum, category);
|
---|
734 | } else if (enumName == "LinkageType") {
|
---|
735 | establishOperandClass(enumName, OperandLinkageType, &LinkageTypeParams, operandEnum, category);
|
---|
736 | } else if (enumName == "FunctionParameterAttribute") {
|
---|
737 | establishOperandClass(enumName, OperandFuncParamAttr, &FuncParamAttrParams, operandEnum, category);
|
---|
738 | } else if (enumName == "AccessQualifier") {
|
---|
739 | establishOperandClass(enumName, OperandAccessQualifier, &AccessQualifierParams, operandEnum, category);
|
---|
740 | } else if (enumName == "BuiltIn") {
|
---|
741 | establishOperandClass(enumName, OperandBuiltIn, &BuiltInParams, operandEnum, category);
|
---|
742 | } else if (enumName == "SelectionControl") {
|
---|
743 | establishOperandClass(enumName, OperandSelect, &SelectionControlParams, operandEnum, category);
|
---|
744 | } else if (enumName == "LoopControl") {
|
---|
745 | establishOperandClass(enumName, OperandLoop, &LoopControlParams, operandEnum, category);
|
---|
746 | } else if (enumName == "FunctionControl") {
|
---|
747 | establishOperandClass(enumName, OperandFunction, &FunctionControlParams, operandEnum, category);
|
---|
748 | } else if (enumName == "Dim") {
|
---|
749 | establishOperandClass(enumName, OperandDimensionality, &DimensionalityParams, operandEnum, category);
|
---|
750 | } else if (enumName == "MemoryAccess") {
|
---|
751 | establishOperandClass(enumName, OperandMemoryOperands, &MemoryAccessParams, operandEnum, category);
|
---|
752 | } else if (enumName == "Scope") {
|
---|
753 | establishOperandClass(enumName, OperandScope, &ScopeParams, operandEnum, category);
|
---|
754 | } else if (enumName == "GroupOperation") {
|
---|
755 | establishOperandClass(enumName, OperandGroupOperation, &GroupOperationParams, operandEnum, category);
|
---|
756 | } else if (enumName == "KernelEnqueueFlags") {
|
---|
757 | establishOperandClass(enumName, OperandKernelEnqueueFlags, &KernelEnqueueFlagsParams, operandEnum, category);
|
---|
758 | } else if (enumName == "KernelProfilingInfo") {
|
---|
759 | establishOperandClass(enumName, OperandKernelProfilingInfo, &KernelProfilingInfoParams, operandEnum, category);
|
---|
760 | } else if (enumName == "RayFlags") {
|
---|
761 | establishOperandClass(enumName, OperandRayFlags, &RayFlagsParams, operandEnum, category);
|
---|
762 | } else if (enumName == "RayQueryIntersection") {
|
---|
763 | establishOperandClass(enumName, OperandRayQueryIntersection, &RayQueryIntersectionParams, operandEnum, category);
|
---|
764 | } else if (enumName == "RayQueryCommittedIntersectionType") {
|
---|
765 | establishOperandClass(enumName, OperandRayQueryCommittedIntersectionType, &RayQueryCommittedIntersectionTypeParams, operandEnum, category);
|
---|
766 | } else if (enumName == "RayQueryCandidateIntersectionType") {
|
---|
767 | establishOperandClass(enumName, OperandRayQueryCandidateIntersectionType, &RayQueryCandidateIntersectionTypeParams, operandEnum, category);
|
---|
768 | } else if (enumName == "FragmentShadingRate") {
|
---|
769 | establishOperandClass(enumName, OperandFragmentShadingRate, &FragmentShadingRateParams, operandEnum, category);
|
---|
770 | } else if (enumName == "PackedVectorFormat") {
|
---|
771 | establishOperandClass(enumName, OperandPackedVectorFormat, &PackedVectorFormatParams, operandEnum, category);
|
---|
772 | } else if (enumName == "CooperativeMatrixOperands") {
|
---|
773 | establishOperandClass(enumName, OperandCooperativeMatrixOperands, &CooperativeMatrixOperandsParams, operandEnum, category);
|
---|
774 | } else if (enumName == "CooperativeMatrixLayout") {
|
---|
775 | establishOperandClass(enumName, OperandCooperativeMatrixLayout, &CooperativeMatrixLayoutParams, operandEnum, category);
|
---|
776 | } else if (enumName == "CooperativeMatrixUse") {
|
---|
777 | establishOperandClass(enumName, OperandCooperativeMatrixUse, &CooperativeMatrixUseParams, operandEnum, category);
|
---|
778 | } else if (enumName == "InitializationModeQualifier") {
|
---|
779 | establishOperandClass(enumName, OperandInitializationModeQualifier, &InitializationModeQualifierParams, operandEnum, category);
|
---|
780 | } else if (enumName == "HostAccessQualifier") {
|
---|
781 | establishOperandClass(enumName, OperandHostAccessQualifier, &HostAccessQualifierParams, operandEnum, category);
|
---|
782 | } else if (enumName == "LoadCacheControl") {
|
---|
783 | establishOperandClass(enumName, OperandLoadCacheControl, &LoadCacheControlParams, operandEnum, category);
|
---|
784 | } else if (enumName == "StoreCacheControl") {
|
---|
785 | establishOperandClass(enumName, OperandStoreCacheControl, &StoreCacheControlParams, operandEnum, category);
|
---|
786 | } else if (enumName == "NamedMaximumNumberOfRegisters") {
|
---|
787 | establishOperandClass(enumName, OperandNamedMaximumNumberOfRegisters, &NamedMaximumNumberOfRegistersParams, operandEnum, category);
|
---|
788 | } else if (enumName == "RawAccessChainOperands") {
|
---|
789 | establishOperandClass(enumName, OperandRawAccessChainOperands, &RawAccessChainOperandsParams, operandEnum, category);
|
---|
790 | }
|
---|
791 | }
|
---|
792 |
|
---|
793 | if (errorCount > 0) {
|
---|
794 | std::exit(1);
|
---|
795 | }
|
---|
796 | }
|
---|
797 |
|
---|
798 | }; // end namespace spv
|
---|