• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2014-2020 The Khronos Group Inc.
2 //
3 // Permission is hereby granted, free of charge, to any person obtaining a copy
4 // of this software and/or associated documentation files (the "Materials"),
5 // to deal in the Materials without restriction, including without limitation
6 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 // and/or sell copies of the Materials, and to permit persons to whom the
8 // Materials are furnished to do so, subject to the following conditions:
9 //
10 // The above copyright notice and this permission notice shall be included in
11 // all copies or substantial portions of the Materials.
12 //
13 // MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS
14 // STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND
15 // HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/
16 //
17 // THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 // FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS
23 // IN THE MATERIALS.
24 
25 #include <assert.h>
26 #include <string.h>
27 #include <algorithm>
28 #include <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 LinkageTypeParams;
65 EnumValues DecorationParams;
66 EnumValues BuiltInParams;
67 EnumValues DimensionalityParams;
68 EnumValues FuncParamAttrParams;
69 EnumValues AccessQualifierParams;
70 EnumValues GroupOperationParams;
71 EnumValues LoopControlParams;
72 EnumValues SelectionControlParams;
73 EnumValues FunctionControlParams;
74 EnumValues MemorySemanticsParams;
75 EnumValues MemoryAccessParams;
76 EnumValues ScopeParams;
77 EnumValues KernelEnqueueFlagsParams;
78 EnumValues KernelProfilingInfoParams;
79 EnumValues CapabilityParams;
80 EnumValues RayFlagsParams;
81 EnumValues RayQueryIntersectionParams;
82 EnumValues RayQueryCommittedIntersectionTypeParams;
83 EnumValues RayQueryCandidateIntersectionTypeParams;
84 
ReadFile(const std::string & path)85 std::pair<bool, std::string> ReadFile(const std::string& path)
86 {
87     std::ifstream fstream(path, std::ios::in);
88     if (fstream) {
89         std::string contents;
90         fstream.seekg(0, std::ios::end);
91         contents.reserve((unsigned int)fstream.tellg());
92         fstream.seekg(0, std::ios::beg);
93         contents.assign((std::istreambuf_iterator<char>(fstream)),
94                         std::istreambuf_iterator<char>());
95         return std::make_pair(true, contents);
96     }
97     return std::make_pair(false, "");
98 }
99 
100 struct ClassOptionality {
101     OperandClass type;
102     bool optional;
103 };
104 
105 // Converts the |operandKind| and |quantifier| pair used to describe operands
106 // in the JSON grammar to OperandClass and optionality used in this repo.
ToOperandClassAndOptionality(const std::string & operandKind,const std::string & quantifier)107 ClassOptionality ToOperandClassAndOptionality(const std::string& operandKind, const std::string& quantifier)
108 {
109     assert(quantifier.empty() || quantifier == "?" || quantifier == "*");
110 
111     if (operandKind == "IdRef") {
112         if (quantifier.empty())
113             return {OperandId, false};
114         else if (quantifier == "?")
115             return {OperandId, true};
116         else
117             return {OperandVariableIds, false};
118     } else if (operandKind == "LiteralInteger") {
119         if (quantifier.empty())
120             return {OperandLiteralNumber, false};
121         if (quantifier == "?")
122             return {OperandOptionalLiteral, true};
123         else
124             return {OperandVariableLiterals, false};
125     } else if (operandKind == "LiteralString") {
126         if (quantifier.empty())
127             return {OperandLiteralString, false};
128         else if (quantifier == "?")
129             return {OperandLiteralString, true};
130         else {
131             return {OperandOptionalLiteralStrings, false};
132         }
133     } else if (operandKind == "PairLiteralIntegerIdRef") {
134         // Used by OpSwitch in the grammar
135         return {OperandVariableLiteralId, false};
136     } else if (operandKind == "PairIdRefLiteralInteger") {
137         // Used by OpGroupMemberDecorate in the grammar
138         return {OperandVariableIdLiteral, false};
139     } else if (operandKind == "PairIdRefIdRef") {
140         // Used by OpPhi in the grammar
141         return {OperandVariableIds, false};
142     } else {
143         OperandClass type = OperandNone;
144         if (operandKind == "IdMemorySemantics" || operandKind == "MemorySemantics") {
145             type = OperandMemorySemantics;
146         } else if (operandKind == "IdScope" || operandKind == "Scope") {
147             type = OperandScope;
148         } else if (operandKind == "LiteralExtInstInteger") {
149             type = OperandLiteralNumber;
150         } else if (operandKind == "LiteralSpecConstantOpInteger") {
151             type = OperandLiteralNumber;
152         } else if (operandKind == "LiteralContextDependentNumber") {
153             type = OperandAnySizeLiteralNumber;
154         } else if (operandKind == "SourceLanguage") {
155             type = OperandSource;
156         } else if (operandKind == "ExecutionModel") {
157             type = OperandExecutionModel;
158         } else if (operandKind == "AddressingModel") {
159             type = OperandAddressing;
160         } else if (operandKind == "MemoryModel") {
161             type = OperandMemory;
162         } else if (operandKind == "ExecutionMode") {
163             type = OperandExecutionMode;
164         } else if (operandKind == "StorageClass") {
165             type = OperandStorage;
166         } else if (operandKind == "Dim") {
167             type = OperandDimensionality;
168         } else if (operandKind == "SamplerAddressingMode") {
169             type = OperandSamplerAddressingMode;
170         } else if (operandKind == "SamplerFilterMode") {
171             type = OperandSamplerFilterMode;
172         } else if (operandKind == "ImageFormat") {
173             type = OperandSamplerImageFormat;
174         } else if (operandKind == "ImageChannelOrder") {
175             type = OperandImageChannelOrder;
176         } else if (operandKind == "ImageChannelDataType") {
177             type = OperandImageChannelDataType;
178         } else if (operandKind == "FPRoundingMode") {
179             type = OperandFPRoundingMode;
180         } else if (operandKind == "LinkageType") {
181             type = OperandLinkageType;
182         } else if (operandKind == "AccessQualifier") {
183             type = OperandAccessQualifier;
184         } else if (operandKind == "FunctionParameterAttribute") {
185             type = OperandFuncParamAttr;
186         } else if (operandKind == "Decoration") {
187             type = OperandDecoration;
188         } else if (operandKind == "BuiltIn") {
189             type = OperandBuiltIn;
190         } else if (operandKind == "GroupOperation") {
191             type = OperandGroupOperation;
192         } else if (operandKind == "KernelEnqueueFlags") {
193             type = OperandKernelEnqueueFlags;
194         } else if (operandKind == "KernelProfilingInfo") {
195             type = OperandKernelProfilingInfo;
196         } else if (operandKind == "Capability") {
197             type = OperandCapability;
198         } else if (operandKind == "ImageOperands") {
199             type = OperandImageOperands;
200         } else if (operandKind == "FPFastMathMode") {
201             type = OperandFPFastMath;
202         } else if (operandKind == "SelectionControl") {
203             type = OperandSelect;
204         } else if (operandKind == "LoopControl") {
205             type = OperandLoop;
206         } else if (operandKind == "FunctionControl") {
207             type = OperandFunction;
208         } else if (operandKind == "MemoryAccess") {
209             type = OperandMemoryOperands;
210         } else if (operandKind == "RayFlags") {
211             type = OperandRayFlags;
212         } else if (operandKind == "RayQueryIntersection") {
213             type = OperandRayQueryIntersection;
214         } else if (operandKind == "RayQueryCommittedIntersectionType") {
215             type = OperandRayQueryCommittedIntersectionType;
216         } else if (operandKind == "RayQueryCandidateIntersectionType") {
217             type = OperandRayQueryCandidateIntersectionType;
218         }
219 
220         if (type == OperandNone) {
221             std::cerr << "Unhandled operand kind found: " << operandKind << std::endl;
222             exit(1);
223         }
224         return {type, !quantifier.empty()};
225     }
226 }
227 
IsTypeOrResultId(const std::string & str,bool * isType,bool * isResult)228 bool IsTypeOrResultId(const std::string& str, bool* isType, bool* isResult)
229 {
230     if (str == "IdResultType")
231         return *isType = true;
232     if (str == "IdResult")
233         return *isResult = true;
234     return false;
235 }
236 
237 // Given a number string, returns the position of the only bits set in the number.
238 // So it requires the number is a power of two.
NumberStringToBit(const std::string & str)239 unsigned int NumberStringToBit(const std::string& str)
240 {
241     char* parseEnd;
242     unsigned int value = (unsigned int)std::strtol(str.c_str(), &parseEnd, 16);
243     assert(!(value & (value - 1)) && "input number is not a power of 2");
244     unsigned int bit = 0;
245     for (; value; value >>= 1) ++bit;
246     return bit;
247 }
248 
jsonToSpirv(const std::string & jsonPath,bool buildingHeaders)249 void jsonToSpirv(const std::string& jsonPath, bool buildingHeaders)
250 {
251     // only do this once.
252     static bool initialized = false;
253     if (initialized)
254         return;
255     initialized = true;
256 
257     // Read the JSON grammar file.
258     bool fileReadOk = false;
259     std::string content;
260     std::tie(fileReadOk, content) = ReadFile(jsonPath);
261     if (!fileReadOk) {
262         std::cerr << "Failed to read JSON grammar file: "
263                   << jsonPath << std::endl;
264         exit(1);
265     }
266 
267     // Decode the JSON grammar file.
268     Json::Reader reader;
269     Json::Value root;
270     if (!reader.parse(content, root)) {
271         std::cerr << "Failed to parse JSON grammar:\n"
272                   << reader.getFormattedErrorMessages();
273         exit(1);
274     }
275 
276     // Layouts for all instructions.
277 
278     // A lambda for returning capabilities from a JSON object as strings.
279     const auto getCaps = [](const Json::Value& object) {
280         EnumCaps result;
281         const auto& caps = object["capabilities"];
282         if (!caps.empty()) {
283             assert(caps.isArray());
284             for (const auto& cap : caps) {
285                 result.emplace_back(cap.asString());
286             }
287         }
288         return result;
289     };
290 
291     // A lambda for returning extensions from a JSON object as strings.
292     const auto getExts = [](const Json::Value& object) {
293         Extensions result;
294         const auto& exts = object["extensions"];
295         if (!exts.empty()) {
296             assert(exts.isArray());
297             for (const auto& ext : exts) {
298                 result.emplace_back(ext.asString());
299             }
300         }
301         return result;
302     };
303 
304     // set up the printing classes
305     std::unordered_set<std::string> tags;  // short-lived local for error checking below
306     const Json::Value printingClasses = root["instruction_printing_class"];
307     for (const auto& printingClass : printingClasses) {
308         if (printingClass["tag"].asString().size() > 0)
309             tags.insert(printingClass["tag"].asString()); // just for error checking
310         else
311             std::cerr << "Error: each instruction_printing_class requires a non-empty \"tag\"" << std::endl;
312         if (buildingHeaders || printingClass["tag"].asString() != "@exclude") {
313             InstructionPrintingClasses.push_back({printingClass["tag"].asString(),
314                                                   printingClass["heading"].asString()});
315         }
316     }
317 
318     // process the instructions
319     const Json::Value insts = root["instructions"];
320     for (const auto& inst : insts) {
321         const auto printingClass = inst["class"].asString();
322         if (printingClass.size() == 0) {
323             std::cerr << "Error: " << inst["opname"].asString()
324                       << " requires a non-empty printing \"class\" tag" << std::endl;
325         }
326         if (!buildingHeaders && printingClass == "@exclude")
327             continue;
328         if (tags.find(printingClass) == tags.end()) {
329             std::cerr << "Error: " << inst["opname"].asString()
330                       << " requires a \"class\" declared as a \"tag\" in \"instruction printing_class\""
331                       << std::endl;
332         }
333         const auto opcode = inst["opcode"].asUInt();
334         const std::string name = inst["opname"].asString();
335         EnumCaps caps = getCaps(inst);
336         std::string version = inst["version"].asString();
337         std::string lastVersion = inst["lastVersion"].asString();
338         Extensions exts = getExts(inst);
339         OperandParameters operands;
340         bool defResultId = false;
341         bool defTypeId = false;
342         for (const auto& operand : inst["operands"]) {
343             const std::string kind = operand["kind"].asString();
344             const std::string quantifier = operand.get("quantifier", "").asString();
345             const std::string doc = operand.get("name", "").asString();
346             if (!IsTypeOrResultId(kind, &defTypeId, &defResultId)) {
347                 const auto p = ToOperandClassAndOptionality(kind, quantifier);
348                 operands.push(p.type, doc, p.optional);
349             }
350         }
351         InstructionDesc.emplace_back(
352             std::move(EnumValue(opcode, name,
353                                 std::move(caps), std::move(version), std::move(lastVersion), std::move(exts),
354                                 std::move(operands))),
355              printingClass, defTypeId, defResultId);
356     }
357 
358     // Specific additional context-dependent operands
359 
360     // Populate dest with EnumValue objects constructed from source.
361     const auto populateEnumValues = [&getCaps,&getExts](EnumValues* dest, const Json::Value& source, bool bitEnum) {
362         // A lambda for determining the numeric value to be used for a given
363         // enumerant in JSON form, and whether that value is a 0 in a bitfield.
364         auto getValue = [&bitEnum](const Json::Value& enumerant) {
365             std::pair<unsigned, bool> result{0u,false};
366             if (!bitEnum) {
367                 result.first = enumerant["value"].asUInt();
368             } else {
369                 const unsigned int bit = NumberStringToBit(enumerant["value"].asString());
370                 if (bit == 0)
371                     result.second = true;
372                 else
373                     result.first = bit - 1;  // This is the *shift* amount.
374             }
375             return result;
376         };
377 
378         for (const auto& enumerant : source["enumerants"]) {
379             unsigned value;
380             bool skip_zero_in_bitfield;
381             std::tie(value, skip_zero_in_bitfield) = getValue(enumerant);
382             if (skip_zero_in_bitfield)
383                 continue;
384             EnumCaps caps(getCaps(enumerant));
385             std::string version = enumerant["version"].asString();
386             std::string lastVersion = enumerant["lastVersion"].asString();
387             Extensions exts(getExts(enumerant));
388             OperandParameters params;
389             const Json::Value& paramsJson = enumerant["parameters"];
390             if (!paramsJson.empty()) {  // This enumerant has parameters.
391                 assert(paramsJson.isArray());
392                 for (const auto& param : paramsJson) {
393                     const std::string kind = param["kind"].asString();
394                     const std::string doc = param.get("name", "").asString();
395                     const auto p = ToOperandClassAndOptionality(kind, ""); // All parameters are required!
396                     params.push(p.type, doc);
397                 }
398             }
399             dest->emplace_back(
400                 value, enumerant["enumerant"].asString(),
401                 std::move(caps), std::move(version), std::move(lastVersion), std::move(exts), std::move(params));
402         }
403     };
404 
405     const auto establishOperandClass = [&populateEnumValues](
406             const std::string& enumName, spv::OperandClass operandClass,
407             spv::EnumValues* enumValues, const Json::Value& operandEnum, const std::string& category) {
408         assert(category == "BitEnum" || category == "ValueEnum");
409         bool bitEnum = (category == "BitEnum");
410         populateEnumValues(enumValues, operandEnum, bitEnum);
411         OperandClassParams[operandClass].set(enumName, enumValues, bitEnum);
412     };
413 
414     const Json::Value operandEnums = root["operand_kinds"];
415     for (const auto& operandEnum : operandEnums) {
416         const std::string enumName = operandEnum["kind"].asString();
417         const std::string category = operandEnum["category"].asString();
418         if (enumName == "SourceLanguage") {
419             establishOperandClass(enumName, OperandSource, &SourceLanguageParams, operandEnum, category);
420         } else if (enumName == "Decoration") {
421             establishOperandClass(enumName, OperandDecoration, &DecorationParams, operandEnum, category);
422         } else if (enumName == "ExecutionMode") {
423             establishOperandClass(enumName, OperandExecutionMode, &ExecutionModeParams, operandEnum, category);
424         } else if (enumName == "Capability") {
425             establishOperandClass(enumName, OperandCapability, &CapabilityParams, operandEnum, category);
426         } else if (enumName == "AddressingModel") {
427             establishOperandClass(enumName, OperandAddressing, &AddressingParams, operandEnum, category);
428         } else if (enumName == "MemoryModel") {
429             establishOperandClass(enumName, OperandMemory, &MemoryParams, operandEnum, category);
430         } else if (enumName == "MemorySemantics") {
431             establishOperandClass(enumName, OperandMemorySemantics, &MemorySemanticsParams, operandEnum, category);
432         } else if (enumName == "ExecutionModel") {
433             establishOperandClass(enumName, OperandExecutionModel, &ExecutionModelParams, operandEnum, category);
434         } else if (enumName == "StorageClass") {
435             establishOperandClass(enumName, OperandStorage, &StorageParams, operandEnum, category);
436         } else if (enumName == "SamplerAddressingMode") {
437             establishOperandClass(enumName, OperandSamplerAddressingMode, &SamplerAddressingModeParams, operandEnum, category);
438         } else if (enumName == "SamplerFilterMode") {
439             establishOperandClass(enumName, OperandSamplerFilterMode, &SamplerFilterModeParams, operandEnum, category);
440         } else if (enumName == "ImageFormat") {
441             establishOperandClass(enumName, OperandSamplerImageFormat, &ImageFormatParams, operandEnum, category);
442         } else if (enumName == "ImageChannelOrder") {
443             establishOperandClass(enumName, OperandImageChannelOrder, &ImageChannelOrderParams, operandEnum, category);
444         } else if (enumName == "ImageChannelDataType") {
445             establishOperandClass(enumName, OperandImageChannelDataType, &ImageChannelDataTypeParams, operandEnum, category);
446         } else if (enumName == "ImageOperands") {
447             establishOperandClass(enumName, OperandImageOperands, &ImageOperandsParams, operandEnum, category);
448         } else if (enumName == "FPFastMathMode") {
449             establishOperandClass(enumName, OperandFPFastMath, &FPFastMathParams, operandEnum, category);
450         } else if (enumName == "FPRoundingMode") {
451             establishOperandClass(enumName, OperandFPRoundingMode, &FPRoundingModeParams, operandEnum, category);
452         } else if (enumName == "LinkageType") {
453             establishOperandClass(enumName, OperandLinkageType, &LinkageTypeParams, operandEnum, category);
454         } else if (enumName == "FunctionParameterAttribute") {
455             establishOperandClass(enumName, OperandFuncParamAttr, &FuncParamAttrParams, operandEnum, category);
456         } else if (enumName == "AccessQualifier") {
457             establishOperandClass(enumName, OperandAccessQualifier, &AccessQualifierParams, operandEnum, category);
458         } else if (enumName == "BuiltIn") {
459             establishOperandClass(enumName, OperandBuiltIn, &BuiltInParams, operandEnum, category);
460         } else if (enumName == "SelectionControl") {
461             establishOperandClass(enumName, OperandSelect, &SelectionControlParams, operandEnum, category);
462         } else if (enumName == "LoopControl") {
463             establishOperandClass(enumName, OperandLoop, &LoopControlParams, operandEnum, category);
464         } else if (enumName == "FunctionControl") {
465             establishOperandClass(enumName, OperandFunction, &FunctionControlParams, operandEnum, category);
466         } else if (enumName == "Dim") {
467             establishOperandClass(enumName, OperandDimensionality, &DimensionalityParams, operandEnum, category);
468         } else if (enumName == "MemoryAccess") {
469             establishOperandClass(enumName, OperandMemoryOperands, &MemoryAccessParams, operandEnum, category);
470         } else if (enumName == "Scope") {
471             establishOperandClass(enumName, OperandScope, &ScopeParams, operandEnum, category);
472         } else if (enumName == "GroupOperation") {
473             establishOperandClass(enumName, OperandGroupOperation, &GroupOperationParams, operandEnum, category);
474         } else if (enumName == "KernelEnqueueFlags") {
475             establishOperandClass(enumName, OperandKernelEnqueueFlags, &KernelEnqueueFlagsParams, operandEnum, category);
476         } else if (enumName == "KernelProfilingInfo") {
477             establishOperandClass(enumName, OperandKernelProfilingInfo, &KernelProfilingInfoParams, operandEnum, category);
478         } else if (enumName == "RayFlags") {
479             establishOperandClass(enumName, OperandRayFlags, &RayFlagsParams, operandEnum, category);
480         } else if (enumName == "RayQueryIntersection") {
481             establishOperandClass(enumName, OperandRayQueryIntersection, &RayQueryIntersectionParams, operandEnum, category);
482         } else if (enumName == "RayQueryCommittedIntersectionType") {
483             establishOperandClass(enumName, OperandRayQueryCommittedIntersectionType, &RayQueryCommittedIntersectionTypeParams, operandEnum, category);
484         } else if (enumName == "RayQueryCandidateIntersectionType") {
485             establishOperandClass(enumName, OperandRayQueryCandidateIntersectionType, &RayQueryCandidateIntersectionTypeParams, operandEnum, category);
486         }
487     }
488 }
489 
490 };  // end namespace spv
491