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 <iostream>
29 #include <unordered_map>
30 #include <unordered_set>
31 #include <utility>
32 #include <fstream>
33 
34 #include "jsoncpp/dist/json/json.h"
35 
36 #include "jsonToSpirv.h"
37 
38 namespace spv {
39 
40 // The set of objects that hold all the instruction/operand
41 // parameterization information.
42 InstructionValues InstructionDesc;
43 
44 // The ordered list (in printing order) of printing classes
45 // (specification subsections).
46 PrintingClasses InstructionPrintingClasses;
47 
48 // Note: There is no entry for OperandOpcode. Use InstructionDesc instead.
49 EnumDefinition OperandClassParams[OperandOpcode];
50 EnumValues SourceLanguageParams;
51 EnumValues ExecutionModelParams;
52 EnumValues AddressingParams;
53 EnumValues MemoryParams;
54 EnumValues ExecutionModeParams;
55 EnumValues StorageParams;
56 EnumValues SamplerAddressingModeParams;
57 EnumValues SamplerFilterModeParams;
58 EnumValues ImageFormatParams;
59 EnumValues ImageChannelOrderParams;
60 EnumValues ImageChannelDataTypeParams;
61 EnumValues ImageOperandsParams;
62 EnumValues FPFastMathParams;
63 EnumValues FPRoundingModeParams;
64 EnumValues FPDenormModeParams;
65 EnumValues FPOperationModeParams;
66 EnumValues LinkageTypeParams;
67 EnumValues DecorationParams;
68 EnumValues BuiltInParams;
69 EnumValues DimensionalityParams;
70 EnumValues FuncParamAttrParams;
71 EnumValues AccessQualifierParams;
72 EnumValues GroupOperationParams;
73 EnumValues LoopControlParams;
74 EnumValues SelectionControlParams;
75 EnumValues FunctionControlParams;
76 EnumValues MemorySemanticsParams;
77 EnumValues MemoryAccessParams;
78 EnumValues ScopeParams;
79 EnumValues KernelEnqueueFlagsParams;
80 EnumValues KernelProfilingInfoParams;
81 EnumValues CapabilityParams;
82 EnumValues RayFlagsParams;
83 EnumValues RayQueryIntersectionParams;
84 EnumValues RayQueryCommittedIntersectionTypeParams;
85 EnumValues RayQueryCandidateIntersectionTypeParams;
86 EnumValues FragmentShadingRateParams;
87 
ReadFile(const std::string & path)88 std::pair<bool, std::string> ReadFile(const std::string& path)
89 {
90     std::ifstream fstream(path, std::ios::in);
91     if (fstream) {
92         std::string contents;
93         fstream.seekg(0, std::ios::end);
94         contents.reserve((unsigned int)fstream.tellg());
95         fstream.seekg(0, std::ios::beg);
96         contents.assign((std::istreambuf_iterator<char>(fstream)),
97                         std::istreambuf_iterator<char>());
98         return std::make_pair(true, contents);
99     }
100     return std::make_pair(false, "");
101 }
102 
103 struct ClassOptionality {
104     OperandClass type;
105     bool optional;
106 };
107 
108 // Converts the |operandKind| and |quantifier| pair used to describe operands
109 // in the JSON grammar to OperandClass and optionality used in this repo.
ToOperandClassAndOptionality(const std::string & operandKind,const std::string & quantifier)110 ClassOptionality ToOperandClassAndOptionality(const std::string& operandKind, const std::string& quantifier)
111 {
112     assert(quantifier.empty() || quantifier == "?" || quantifier == "*");
113 
114     if (operandKind == "IdRef") {
115         if (quantifier.empty())
116             return {OperandId, false};
117         else if (quantifier == "?")
118             return {OperandId, true};
119         else
120             return {OperandVariableIds, false};
121     } else if (operandKind == "LiteralInteger") {
122         if (quantifier.empty())
123             return {OperandLiteralNumber, false};
124         if (quantifier == "?")
125             return {OperandOptionalLiteral, true};
126         else
127             return {OperandVariableLiterals, false};
128     } else if (operandKind == "LiteralString") {
129         if (quantifier.empty())
130             return {OperandLiteralString, false};
131         else if (quantifier == "?")
132             return {OperandLiteralString, true};
133         else {
134             return {OperandOptionalLiteralStrings, false};
135         }
136     } else if (operandKind == "PairLiteralIntegerIdRef") {
137         // Used by OpSwitch in the grammar
138         return {OperandVariableLiteralId, false};
139     } else if (operandKind == "PairIdRefLiteralInteger") {
140         // Used by OpGroupMemberDecorate in the grammar
141         return {OperandVariableIdLiteral, false};
142     } else if (operandKind == "PairIdRefIdRef") {
143         // Used by OpPhi in the grammar
144         return {OperandVariableIds, false};
145     } else {
146         OperandClass type = OperandNone;
147         if (operandKind == "IdMemorySemantics" || operandKind == "MemorySemantics") {
148             type = OperandMemorySemantics;
149         } else if (operandKind == "IdScope" || operandKind == "Scope") {
150             type = OperandScope;
151         } else if (operandKind == "LiteralExtInstInteger") {
152             type = OperandLiteralNumber;
153         } else if (operandKind == "LiteralSpecConstantOpInteger") {
154             type = OperandLiteralNumber;
155         } else if (operandKind == "LiteralContextDependentNumber") {
156             type = OperandAnySizeLiteralNumber;
157         } else if (operandKind == "SourceLanguage") {
158             type = OperandSource;
159         } else if (operandKind == "ExecutionModel") {
160             type = OperandExecutionModel;
161         } else if (operandKind == "AddressingModel") {
162             type = OperandAddressing;
163         } else if (operandKind == "MemoryModel") {
164             type = OperandMemory;
165         } else if (operandKind == "ExecutionMode") {
166             type = OperandExecutionMode;
167         } else if (operandKind == "StorageClass") {
168             type = OperandStorage;
169         } else if (operandKind == "Dim") {
170             type = OperandDimensionality;
171         } else if (operandKind == "SamplerAddressingMode") {
172             type = OperandSamplerAddressingMode;
173         } else if (operandKind == "SamplerFilterMode") {
174             type = OperandSamplerFilterMode;
175         } else if (operandKind == "ImageFormat") {
176             type = OperandSamplerImageFormat;
177         } else if (operandKind == "ImageChannelOrder") {
178             type = OperandImageChannelOrder;
179         } else if (operandKind == "ImageChannelDataType") {
180             type = OperandImageChannelDataType;
181         } else if (operandKind == "FPRoundingMode") {
182             type = OperandFPRoundingMode;
183         } else if (operandKind == "FPDenormMode") {
184             type = OperandFPDenormMode;
185         } else if (operandKind == "FPOperationMode") {
186             type = OperandFPOperationMode;
187         } else if (operandKind == "LinkageType") {
188             type = OperandLinkageType;
189         } else if (operandKind == "AccessQualifier") {
190             type = OperandAccessQualifier;
191         } else if (operandKind == "FunctionParameterAttribute") {
192             type = OperandFuncParamAttr;
193         } else if (operandKind == "Decoration") {
194             type = OperandDecoration;
195         } else if (operandKind == "BuiltIn") {
196             type = OperandBuiltIn;
197         } else if (operandKind == "GroupOperation") {
198             type = OperandGroupOperation;
199         } else if (operandKind == "KernelEnqueueFlags") {
200             type = OperandKernelEnqueueFlags;
201         } else if (operandKind == "KernelProfilingInfo") {
202             type = OperandKernelProfilingInfo;
203         } else if (operandKind == "Capability") {
204             type = OperandCapability;
205         } else if (operandKind == "ImageOperands") {
206             type = OperandImageOperands;
207         } else if (operandKind == "FPFastMathMode") {
208             type = OperandFPFastMath;
209         } else if (operandKind == "SelectionControl") {
210             type = OperandSelect;
211         } else if (operandKind == "LoopControl") {
212             type = OperandLoop;
213         } else if (operandKind == "FunctionControl") {
214             type = OperandFunction;
215         } else if (operandKind == "MemoryAccess") {
216             type = OperandMemoryOperands;
217         } else if (operandKind == "RayFlags") {
218             type = OperandRayFlags;
219         } else if (operandKind == "RayQueryIntersection") {
220             type = OperandRayQueryIntersection;
221         } else if (operandKind == "RayQueryCommittedIntersectionType") {
222             type = OperandRayQueryCommittedIntersectionType;
223         } else if (operandKind == "RayQueryCandidateIntersectionType") {
224             type = OperandRayQueryCandidateIntersectionType;
225         } else if (operandKind == "FragmentShadingRate") {
226             type = OperandFragmentShadingRate;
227         }
228 
229         if (type == OperandNone) {
230             std::cerr << "Unhandled operand kind found: " << operandKind << std::endl;
231             exit(1);
232         }
233         return {type, !quantifier.empty()};
234     }
235 }
236 
IsTypeOrResultId(const std::string & str,bool * isType,bool * isResult)237 bool IsTypeOrResultId(const std::string& str, bool* isType, bool* isResult)
238 {
239     if (str == "IdResultType")
240         return *isType = true;
241     if (str == "IdResult")
242         return *isResult = true;
243     return false;
244 }
245 
246 // Given a number string, returns the position of the only bits set in the number.
247 // So it requires the number is a power of two.
NumberStringToBit(const std::string & str)248 unsigned int NumberStringToBit(const std::string& str)
249 {
250     char* parseEnd;
251     unsigned int value = (unsigned int)std::strtol(str.c_str(), &parseEnd, 16);
252     assert(!(value & (value - 1)) && "input number is not a power of 2");
253     unsigned int bit = 0;
254     for (; value; value >>= 1) ++bit;
255     return bit;
256 }
257 
jsonToSpirv(const std::string & jsonPath,bool buildingHeaders)258 void jsonToSpirv(const std::string& jsonPath, bool buildingHeaders)
259 {
260     // only do this once.
261     static bool initialized = false;
262     if (initialized)
263         return;
264     initialized = true;
265 
266     // Read the JSON grammar file.
267     bool fileReadOk = false;
268     std::string content;
269     std::tie(fileReadOk, content) = ReadFile(jsonPath);
270     if (!fileReadOk) {
271         std::cerr << "Failed to read JSON grammar file: "
272                   << jsonPath << std::endl;
273         exit(1);
274     }
275 
276     // Decode the JSON grammar file.
277     Json::Reader reader;
278     Json::Value root;
279     if (!reader.parse(content, root)) {
280         std::cerr << "Failed to parse JSON grammar:\n"
281                   << reader.getFormattedErrorMessages();
282         exit(1);
283     }
284 
285     // Layouts for all instructions.
286 
287     // A lambda for returning capabilities from a JSON object as strings.
288     const auto getCaps = [](const Json::Value& object) {
289         EnumCaps result;
290         const auto& caps = object["capabilities"];
291         if (!caps.empty()) {
292             assert(caps.isArray());
293             for (const auto& cap : caps) {
294                 result.emplace_back(cap.asString());
295             }
296         }
297         return result;
298     };
299 
300     // A lambda for returning extensions from a JSON object as strings.
301     const auto getExts = [](const Json::Value& object) {
302         Extensions result;
303         const auto& exts = object["extensions"];
304         if (!exts.empty()) {
305             assert(exts.isArray());
306             for (const auto& ext : exts) {
307                 result.emplace_back(ext.asString());
308             }
309         }
310         return result;
311     };
312 
313     // set up the printing classes
314     std::unordered_set<std::string> tags;  // short-lived local for error checking below
315     const Json::Value printingClasses = root["instruction_printing_class"];
316     for (const auto& printingClass : printingClasses) {
317         if (printingClass["tag"].asString().size() > 0)
318             tags.insert(printingClass["tag"].asString()); // just for error checking
319         else
320             std::cerr << "Error: each instruction_printing_class requires a non-empty \"tag\"" << std::endl;
321         if (buildingHeaders || printingClass["tag"].asString() != "@exclude") {
322             InstructionPrintingClasses.push_back({printingClass["tag"].asString(),
323                                                   printingClass["heading"].asString()});
324         }
325     }
326 
327     // process the instructions
328     const Json::Value insts = root["instructions"];
329     for (const auto& inst : insts) {
330         const auto printingClass = inst["class"].asString();
331         if (printingClass.size() == 0) {
332             std::cerr << "Error: " << inst["opname"].asString()
333                       << " requires a non-empty printing \"class\" tag" << std::endl;
334         }
335         if (!buildingHeaders && printingClass == "@exclude")
336             continue;
337         if (tags.find(printingClass) == tags.end()) {
338             std::cerr << "Error: " << inst["opname"].asString()
339                       << " requires a \"class\" declared as a \"tag\" in \"instruction printing_class\""
340                       << std::endl;
341         }
342         const auto opcode = inst["opcode"].asUInt();
343         const std::string name = inst["opname"].asString();
344         EnumCaps caps = getCaps(inst);
345         std::string version = inst["version"].asString();
346         std::string lastVersion = inst["lastVersion"].asString();
347         Extensions exts = getExts(inst);
348         OperandParameters operands;
349         bool defResultId = false;
350         bool defTypeId = false;
351         for (const auto& operand : inst["operands"]) {
352             const std::string kind = operand["kind"].asString();
353             const std::string quantifier = operand.get("quantifier", "").asString();
354             const std::string doc = operand.get("name", "").asString();
355             if (!IsTypeOrResultId(kind, &defTypeId, &defResultId)) {
356                 const auto p = ToOperandClassAndOptionality(kind, quantifier);
357                 operands.push(p.type, doc, p.optional);
358             }
359         }
360         InstructionDesc.emplace_back(
361             std::move(EnumValue(opcode, name,
362                                 std::move(caps), std::move(version), std::move(lastVersion), std::move(exts),
363                                 std::move(operands))),
364              printingClass, defTypeId, defResultId);
365     }
366 
367     // Specific additional context-dependent operands
368 
369     // Populate dest with EnumValue objects constructed from source.
370     const auto populateEnumValues = [&getCaps,&getExts](EnumValues* dest, const Json::Value& source, bool bitEnum) {
371         // A lambda for determining the numeric value to be used for a given
372         // enumerant in JSON form, and whether that value is a 0 in a bitfield.
373         auto getValue = [&bitEnum](const Json::Value& enumerant) {
374             std::pair<unsigned, bool> result{0u,false};
375             if (!bitEnum) {
376                 result.first = enumerant["value"].asUInt();
377             } else {
378                 const unsigned int bit = NumberStringToBit(enumerant["value"].asString());
379                 if (bit == 0)
380                     result.second = true;
381                 else
382                     result.first = bit - 1;  // This is the *shift* amount.
383             }
384             return result;
385         };
386 
387         for (const auto& enumerant : source["enumerants"]) {
388             unsigned value;
389             bool skip_zero_in_bitfield;
390             std::tie(value, skip_zero_in_bitfield) = getValue(enumerant);
391             if (skip_zero_in_bitfield)
392                 continue;
393             EnumCaps caps(getCaps(enumerant));
394             std::string version = enumerant["version"].asString();
395             std::string lastVersion = enumerant["lastVersion"].asString();
396             Extensions exts(getExts(enumerant));
397             OperandParameters params;
398             const Json::Value& paramsJson = enumerant["parameters"];
399             if (!paramsJson.empty()) {  // This enumerant has parameters.
400                 assert(paramsJson.isArray());
401                 for (const auto& param : paramsJson) {
402                     const std::string kind = param["kind"].asString();
403                     const std::string doc = param.get("name", "").asString();
404                     const auto p = ToOperandClassAndOptionality(kind, ""); // All parameters are required!
405                     params.push(p.type, doc);
406                 }
407             }
408             dest->emplace_back(
409                 value, enumerant["enumerant"].asString(),
410                 std::move(caps), std::move(version), std::move(lastVersion), std::move(exts), std::move(params));
411         }
412     };
413 
414     const auto establishOperandClass = [&populateEnumValues](
415             const std::string& enumName, spv::OperandClass operandClass,
416             spv::EnumValues* enumValues, const Json::Value& operandEnum, const std::string& category) {
417         assert(category == "BitEnum" || category == "ValueEnum");
418         bool bitEnum = (category == "BitEnum");
419         populateEnumValues(enumValues, operandEnum, bitEnum);
420         OperandClassParams[operandClass].set(enumName, enumValues, bitEnum);
421     };
422 
423     const Json::Value operandEnums = root["operand_kinds"];
424     for (const auto& operandEnum : operandEnums) {
425         const std::string enumName = operandEnum["kind"].asString();
426         const std::string category = operandEnum["category"].asString();
427         if (enumName == "SourceLanguage") {
428             establishOperandClass(enumName, OperandSource, &SourceLanguageParams, operandEnum, category);
429         } else if (enumName == "Decoration") {
430             establishOperandClass(enumName, OperandDecoration, &DecorationParams, operandEnum, category);
431         } else if (enumName == "ExecutionMode") {
432             establishOperandClass(enumName, OperandExecutionMode, &ExecutionModeParams, operandEnum, category);
433         } else if (enumName == "Capability") {
434             establishOperandClass(enumName, OperandCapability, &CapabilityParams, operandEnum, category);
435         } else if (enumName == "AddressingModel") {
436             establishOperandClass(enumName, OperandAddressing, &AddressingParams, operandEnum, category);
437         } else if (enumName == "MemoryModel") {
438             establishOperandClass(enumName, OperandMemory, &MemoryParams, operandEnum, category);
439         } else if (enumName == "MemorySemantics") {
440             establishOperandClass(enumName, OperandMemorySemantics, &MemorySemanticsParams, operandEnum, category);
441         } else if (enumName == "ExecutionModel") {
442             establishOperandClass(enumName, OperandExecutionModel, &ExecutionModelParams, operandEnum, category);
443         } else if (enumName == "StorageClass") {
444             establishOperandClass(enumName, OperandStorage, &StorageParams, operandEnum, category);
445         } else if (enumName == "SamplerAddressingMode") {
446             establishOperandClass(enumName, OperandSamplerAddressingMode, &SamplerAddressingModeParams, operandEnum, category);
447         } else if (enumName == "SamplerFilterMode") {
448             establishOperandClass(enumName, OperandSamplerFilterMode, &SamplerFilterModeParams, operandEnum, category);
449         } else if (enumName == "ImageFormat") {
450             establishOperandClass(enumName, OperandSamplerImageFormat, &ImageFormatParams, operandEnum, category);
451         } else if (enumName == "ImageChannelOrder") {
452             establishOperandClass(enumName, OperandImageChannelOrder, &ImageChannelOrderParams, operandEnum, category);
453         } else if (enumName == "ImageChannelDataType") {
454             establishOperandClass(enumName, OperandImageChannelDataType, &ImageChannelDataTypeParams, operandEnum, category);
455         } else if (enumName == "ImageOperands") {
456             establishOperandClass(enumName, OperandImageOperands, &ImageOperandsParams, operandEnum, category);
457         } else if (enumName == "FPFastMathMode") {
458             establishOperandClass(enumName, OperandFPFastMath, &FPFastMathParams, operandEnum, category);
459         } else if (enumName == "FPRoundingMode") {
460             establishOperandClass(enumName, OperandFPRoundingMode, &FPRoundingModeParams, operandEnum, category);
461         } else if (enumName == "FPDenormMode") {
462             establishOperandClass(enumName, OperandFPDenormMode, &FPDenormModeParams, operandEnum, category);
463         } else if (enumName == "FPOperationMode") {
464             establishOperandClass(enumName, OperandFPOperationMode, &FPOperationModeParams, operandEnum, category);
465         } else if (enumName == "LinkageType") {
466             establishOperandClass(enumName, OperandLinkageType, &LinkageTypeParams, operandEnum, category);
467         } else if (enumName == "FunctionParameterAttribute") {
468             establishOperandClass(enumName, OperandFuncParamAttr, &FuncParamAttrParams, operandEnum, category);
469         } else if (enumName == "AccessQualifier") {
470             establishOperandClass(enumName, OperandAccessQualifier, &AccessQualifierParams, operandEnum, category);
471         } else if (enumName == "BuiltIn") {
472             establishOperandClass(enumName, OperandBuiltIn, &BuiltInParams, operandEnum, category);
473         } else if (enumName == "SelectionControl") {
474             establishOperandClass(enumName, OperandSelect, &SelectionControlParams, operandEnum, category);
475         } else if (enumName == "LoopControl") {
476             establishOperandClass(enumName, OperandLoop, &LoopControlParams, operandEnum, category);
477         } else if (enumName == "FunctionControl") {
478             establishOperandClass(enumName, OperandFunction, &FunctionControlParams, operandEnum, category);
479         } else if (enumName == "Dim") {
480             establishOperandClass(enumName, OperandDimensionality, &DimensionalityParams, operandEnum, category);
481         } else if (enumName == "MemoryAccess") {
482             establishOperandClass(enumName, OperandMemoryOperands, &MemoryAccessParams, operandEnum, category);
483         } else if (enumName == "Scope") {
484             establishOperandClass(enumName, OperandScope, &ScopeParams, operandEnum, category);
485         } else if (enumName == "GroupOperation") {
486             establishOperandClass(enumName, OperandGroupOperation, &GroupOperationParams, operandEnum, category);
487         } else if (enumName == "KernelEnqueueFlags") {
488             establishOperandClass(enumName, OperandKernelEnqueueFlags, &KernelEnqueueFlagsParams, operandEnum, category);
489         } else if (enumName == "KernelProfilingInfo") {
490             establishOperandClass(enumName, OperandKernelProfilingInfo, &KernelProfilingInfoParams, operandEnum, category);
491         } else if (enumName == "RayFlags") {
492             establishOperandClass(enumName, OperandRayFlags, &RayFlagsParams, operandEnum, category);
493         } else if (enumName == "RayQueryIntersection") {
494             establishOperandClass(enumName, OperandRayQueryIntersection, &RayQueryIntersectionParams, operandEnum, category);
495         } else if (enumName == "RayQueryCommittedIntersectionType") {
496             establishOperandClass(enumName, OperandRayQueryCommittedIntersectionType, &RayQueryCommittedIntersectionTypeParams, operandEnum, category);
497         } else if (enumName == "RayQueryCandidateIntersectionType") {
498             establishOperandClass(enumName, OperandRayQueryCandidateIntersectionType, &RayQueryCandidateIntersectionTypeParams, operandEnum, category);
499         } else if (enumName == "FragmentShadingRate") {
500             establishOperandClass(enumName, OperandFragmentShadingRate, &FragmentShadingRateParams, operandEnum, category);
501         }
502     }
503 }
504 
505 };  // end namespace spv
506