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