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 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. 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 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. 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 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