1 // Copyright (c) 2014-2018 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 assert(0 && "this case should not exist");
123 return {OperandNone, false};
124 }
125 } else if (operandKind == "PairLiteralIntegerIdRef") {
126 // Used by OpSwitch in the grammar
127 return {OperandVariableLiteralId, false};
128 } else if (operandKind == "PairIdRefLiteralInteger") {
129 // Used by OpGroupMemberDecorate in the grammar
130 return {OperandVariableIdLiteral, false};
131 } else if (operandKind == "PairIdRefIdRef") {
132 // Used by OpPhi in the grammar
133 return {OperandVariableIds, false};
134 } else {
135 OperandClass type = OperandNone;
136 if (operandKind == "IdMemorySemantics" || operandKind == "MemorySemantics") {
137 type = OperandMemorySemantics;
138 } else if (operandKind == "IdScope" || operandKind == "Scope") {
139 type = OperandScope;
140 } else if (operandKind == "LiteralExtInstInteger") {
141 type = OperandLiteralNumber;
142 } else if (operandKind == "LiteralSpecConstantOpInteger") {
143 type = OperandLiteralNumber;
144 } else if (operandKind == "LiteralContextDependentNumber") {
145 type = OperandVariableLiterals;
146 } else if (operandKind == "SourceLanguage") {
147 type = OperandSource;
148 } else if (operandKind == "ExecutionModel") {
149 type = OperandExecutionModel;
150 } else if (operandKind == "AddressingModel") {
151 type = OperandAddressing;
152 } else if (operandKind == "MemoryModel") {
153 type = OperandMemory;
154 } else if (operandKind == "ExecutionMode") {
155 type = OperandExecutionMode;
156 } else if (operandKind == "StorageClass") {
157 type = OperandStorage;
158 } else if (operandKind == "Dim") {
159 type = OperandDimensionality;
160 } else if (operandKind == "SamplerAddressingMode") {
161 type = OperandSamplerAddressingMode;
162 } else if (operandKind == "SamplerFilterMode") {
163 type = OperandSamplerFilterMode;
164 } else if (operandKind == "ImageFormat") {
165 type = OperandSamplerImageFormat;
166 } else if (operandKind == "ImageChannelOrder") {
167 type = OperandImageChannelOrder;
168 } else if (operandKind == "ImageChannelDataType") {
169 type = OperandImageChannelDataType;
170 } else if (operandKind == "FPRoundingMode") {
171 type = OperandFPRoundingMode;
172 } else if (operandKind == "LinkageType") {
173 type = OperandLinkageType;
174 } else if (operandKind == "AccessQualifier") {
175 type = OperandAccessQualifier;
176 } else if (operandKind == "FunctionParameterAttribute") {
177 type = OperandFuncParamAttr;
178 } else if (operandKind == "Decoration") {
179 type = OperandDecoration;
180 } else if (operandKind == "BuiltIn") {
181 type = OperandBuiltIn;
182 } else if (operandKind == "GroupOperation") {
183 type = OperandGroupOperation;
184 } else if (operandKind == "KernelEnqueueFlags") {
185 type = OperandKernelEnqueueFlags;
186 } else if (operandKind == "KernelProfilingInfo") {
187 type = OperandKernelProfilingInfo;
188 } else if (operandKind == "Capability") {
189 type = OperandCapability;
190 } else if (operandKind == "ImageOperands") {
191 type = OperandImageOperands;
192 } else if (operandKind == "FPFastMathMode") {
193 type = OperandFPFastMath;
194 } else if (operandKind == "SelectionControl") {
195 type = OperandSelect;
196 } else if (operandKind == "LoopControl") {
197 type = OperandLoop;
198 } else if (operandKind == "FunctionControl") {
199 type = OperandFunction;
200 } else if (operandKind == "MemoryAccess") {
201 type = OperandMemoryAccess;
202 }
203
204 if (type == OperandNone) {
205 std::cerr << "Unhandled operand kind found: " << operandKind << std::endl;
206 exit(1);
207 }
208 return {type, !quantifier.empty()};
209 }
210 }
211
IsTypeOrResultId(const std::string & str,bool * isType,bool * isResult)212 bool IsTypeOrResultId(const std::string& str, bool* isType, bool* isResult)
213 {
214 if (str == "IdResultType")
215 return *isType = true;
216 if (str == "IdResult")
217 return *isResult = true;
218 return false;
219 }
220
221 // Given a number string, returns the position of the only bits set in the number.
222 // So it requires the number is a power of two.
NumberStringToBit(const std::string & str)223 unsigned int NumberStringToBit(const std::string& str)
224 {
225 char* parseEnd;
226 unsigned int value = (unsigned int)std::strtol(str.c_str(), &parseEnd, 16);
227 assert(!(value & (value - 1)) && "input number is not a power of 2");
228 unsigned int bit = 0;
229 for (; value; value >>= 1) ++bit;
230 return bit;
231 }
232
jsonToSpirv(const std::string & jsonPath)233 void jsonToSpirv(const std::string& jsonPath)
234 {
235 // only do this once.
236 static bool initialized = false;
237 if (initialized)
238 return;
239 initialized = true;
240
241 // Read the JSON grammar file.
242 bool fileReadOk = false;
243 std::string content;
244 std::tie(fileReadOk, content) = ReadFile(jsonPath);
245 if (!fileReadOk) {
246 std::cerr << "Failed to read JSON grammar file: "
247 << jsonPath << std::endl;
248 exit(1);
249 }
250
251 // Decode the JSON grammar file.
252 Json::Reader reader;
253 Json::Value root;
254 if (!reader.parse(content, root)) {
255 std::cerr << "Failed to parse JSON grammar:\n"
256 << reader.getFormattedErrorMessages();
257 exit(1);
258 }
259
260 // Layouts for all instructions.
261
262 // A lambda for returning capabilities from a JSON object as strings.
263 const auto getCaps = [](const Json::Value& object) {
264 EnumCaps result;
265 const auto& caps = object["capabilities"];
266 if (!caps.empty()) {
267 assert(caps.isArray());
268 for (const auto& cap : caps) {
269 result.emplace_back(cap.asString());
270 }
271 }
272 return result;
273 };
274
275 // A lambda for returning extensions from a JSON object as strings.
276 const auto getExts = [](const Json::Value& object) {
277 Extensions result;
278 const auto& exts = object["extensions"];
279 if (!exts.empty()) {
280 assert(exts.isArray());
281 for (const auto& ext : exts) {
282 result.emplace_back(ext.asString());
283 }
284 }
285 return result;
286 };
287
288 const Json::Value insts = root["instructions"];
289 for (const auto& inst : insts) {
290 const unsigned int opcode = inst["opcode"].asUInt();
291 const std::string name = inst["opname"].asString();
292 EnumCaps caps = getCaps(inst);
293 std::string version = inst["version"].asString();
294 Extensions exts = getExts(inst);
295 OperandParameters operands;
296 bool defResultId = false;
297 bool defTypeId = false;
298 for (const auto& operand : inst["operands"]) {
299 const std::string kind = operand["kind"].asString();
300 const std::string quantifier = operand.get("quantifier", "").asString();
301 const std::string doc = operand.get("name", "").asString();
302 if (!IsTypeOrResultId(kind, &defTypeId, &defResultId)) {
303 const auto p = ToOperandClassAndOptionality(kind, quantifier);
304 operands.push(p.type, doc, p.optional);
305 }
306 }
307 InstructionDesc.emplace_back(
308 std::move(EnumValue(opcode, name,
309 std::move(caps), std::move(version), std::move(exts),
310 std::move(operands))),
311 defTypeId, defResultId);
312 }
313
314 // Specific additional context-dependent operands
315
316 // Populate dest with EnumValue objects constructed from source.
317 const auto populateEnumValues = [&getCaps,&getExts](EnumValues* dest, const Json::Value& source, bool bitEnum) {
318 // A lambda for determining the numeric value to be used for a given
319 // enumerant in JSON form, and whether that value is a 0 in a bitfield.
320 auto getValue = [&bitEnum](const Json::Value& enumerant) {
321 std::pair<unsigned, bool> result{0u,false};
322 if (!bitEnum) {
323 result.first = enumerant["value"].asUInt();
324 } else {
325 const unsigned int bit = NumberStringToBit(enumerant["value"].asString());
326 if (bit == 0)
327 result.second = true;
328 else
329 result.first = bit - 1; // This is the *shift* amount.
330 }
331 return result;
332 };
333
334 for (const auto& enumerant : source["enumerants"]) {
335 unsigned value;
336 bool skip_zero_in_bitfield;
337 std::tie(value, skip_zero_in_bitfield) = getValue(enumerant);
338 if (skip_zero_in_bitfield)
339 continue;
340 EnumCaps caps(getCaps(enumerant));
341 std::string version = enumerant["version"].asString();
342 Extensions exts(getExts(enumerant));
343 OperandParameters params;
344 const Json::Value& paramsJson = enumerant["parameters"];
345 if (!paramsJson.empty()) { // This enumerant has parameters.
346 assert(paramsJson.isArray());
347 for (const auto& param : paramsJson) {
348 const std::string kind = param["kind"].asString();
349 const std::string doc = param.get("name", "").asString();
350 const auto p = ToOperandClassAndOptionality(kind, ""); // All parameters are required!
351 params.push(p.type, doc);
352 }
353 }
354 dest->emplace_back(
355 value, enumerant["enumerant"].asString(),
356 std::move(caps), std::move(version), std::move(exts), std::move(params));
357 }
358 };
359
360 const auto establishOperandClass = [&populateEnumValues](
361 const std::string& enumName, spv::OperandClass operandClass,
362 spv::EnumValues* enumValues, const Json::Value& operandEnum, const std::string& category) {
363 assert(category == "BitEnum" || category == "ValueEnum");
364 bool bitEnum = (category == "BitEnum");
365 populateEnumValues(enumValues, operandEnum, bitEnum);
366 OperandClassParams[operandClass].set(enumName, enumValues, bitEnum);
367 };
368
369 const Json::Value operandEnums = root["operand_kinds"];
370 for (const auto& operandEnum : operandEnums) {
371 const std::string enumName = operandEnum["kind"].asString();
372 const std::string category = operandEnum["category"].asString();
373 if (enumName == "SourceLanguage") {
374 establishOperandClass(enumName, OperandSource, &SourceLanguageParams, operandEnum, category);
375 } else if (enumName == "Decoration") {
376 establishOperandClass(enumName, OperandDecoration, &DecorationParams, operandEnum, category);
377 } else if (enumName == "ExecutionMode") {
378 establishOperandClass(enumName, OperandExecutionMode, &ExecutionModeParams, operandEnum, category);
379 } else if (enumName == "Capability") {
380 establishOperandClass(enumName, OperandCapability, &CapabilityParams, operandEnum, category);
381 } else if (enumName == "AddressingModel") {
382 establishOperandClass(enumName, OperandAddressing, &AddressingParams, operandEnum, category);
383 } else if (enumName == "MemoryModel") {
384 establishOperandClass(enumName, OperandMemory, &MemoryParams, operandEnum, category);
385 } else if (enumName == "MemorySemantics") {
386 establishOperandClass(enumName, OperandMemorySemantics, &MemorySemanticsParams, operandEnum, category);
387 } else if (enumName == "ExecutionModel") {
388 establishOperandClass(enumName, OperandExecutionModel, &ExecutionModelParams, operandEnum, category);
389 } else if (enumName == "StorageClass") {
390 establishOperandClass(enumName, OperandStorage, &StorageParams, operandEnum, category);
391 } else if (enumName == "SamplerAddressingMode") {
392 establishOperandClass(enumName, OperandSamplerAddressingMode, &SamplerAddressingModeParams, operandEnum, category);
393 } else if (enumName == "SamplerFilterMode") {
394 establishOperandClass(enumName, OperandSamplerFilterMode, &SamplerFilterModeParams, operandEnum, category);
395 } else if (enumName == "ImageFormat") {
396 establishOperandClass(enumName, OperandSamplerImageFormat, &ImageFormatParams, operandEnum, category);
397 } else if (enumName == "ImageChannelOrder") {
398 establishOperandClass(enumName, OperandImageChannelOrder, &ImageChannelOrderParams, operandEnum, category);
399 } else if (enumName == "ImageChannelDataType") {
400 establishOperandClass(enumName, OperandImageChannelDataType, &ImageChannelDataTypeParams, operandEnum, category);
401 } else if (enumName == "ImageOperands") {
402 establishOperandClass(enumName, OperandImageOperands, &ImageOperandsParams, operandEnum, category);
403 } else if (enumName == "FPFastMathMode") {
404 establishOperandClass(enumName, OperandFPFastMath, &FPFastMathParams, operandEnum, category);
405 } else if (enumName == "FPRoundingMode") {
406 establishOperandClass(enumName, OperandFPRoundingMode, &FPRoundingModeParams, operandEnum, category);
407 } else if (enumName == "LinkageType") {
408 establishOperandClass(enumName, OperandLinkageType, &LinkageTypeParams, operandEnum, category);
409 } else if (enumName == "FunctionParameterAttribute") {
410 establishOperandClass(enumName, OperandFuncParamAttr, &FuncParamAttrParams, operandEnum, category);
411 } else if (enumName == "AccessQualifier") {
412 establishOperandClass(enumName, OperandAccessQualifier, &AccessQualifierParams, operandEnum, category);
413 } else if (enumName == "BuiltIn") {
414 establishOperandClass(enumName, OperandBuiltIn, &BuiltInParams, operandEnum, category);
415 } else if (enumName == "SelectionControl") {
416 establishOperandClass(enumName, OperandSelect, &SelectionControlParams, operandEnum, category);
417 } else if (enumName == "LoopControl") {
418 establishOperandClass(enumName, OperandLoop, &LoopControlParams, operandEnum, category);
419 } else if (enumName == "FunctionControl") {
420 establishOperandClass(enumName, OperandFunction, &FunctionControlParams, operandEnum, category);
421 } else if (enumName == "Dim") {
422 establishOperandClass(enumName, OperandDimensionality, &DimensionalityParams, operandEnum, category);
423 } else if (enumName == "MemoryAccess") {
424 establishOperandClass(enumName, OperandMemoryAccess, &MemoryAccessParams, operandEnum, category);
425 } else if (enumName == "Scope") {
426 establishOperandClass(enumName, OperandScope, &ScopeParams, operandEnum, category);
427 } else if (enumName == "GroupOperation") {
428 establishOperandClass(enumName, OperandGroupOperation, &GroupOperationParams, operandEnum, category);
429 } else if (enumName == "KernelEnqueueFlags") {
430 establishOperandClass(enumName, OperandKernelEnqueueFlags, &KernelEnqueueFlagsParams, operandEnum, category);
431 } else if (enumName == "KernelProfilingInfo") {
432 establishOperandClass(enumName, OperandKernelProfilingInfo, &KernelProfilingInfoParams, operandEnum, category);
433 }
434 }
435 }
436
437 }; // end namespace spv
438