// Copyright (c) 2016 Google Inc. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and/or associated documentation files (the // "Materials"), to deal in the Materials without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Materials, and to // permit persons to whom the Materials are furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Materials. // // MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS // KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS // SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT // https://www.khronos.org/registry/ // // THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE // MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. #include "name_mapper.h" #include #include #include #include #include #include #include "spirv-tools/libspirv.h" #include "spirv/1.1/spirv.h" namespace { // Converts a uint32_t to its string decimal representation. std::string to_string(uint32_t id) { // Use stringstream, since some versions of Android compilers lack // std::to_string. std::stringstream os; os << id; return os.str(); } } // anonymous namespace namespace libspirv { NameMapper GetTrivialNameMapper() { return to_string; } FriendlyNameMapper::FriendlyNameMapper(const spv_const_context context, const uint32_t* code, const size_t wordCount) : grammar_(libspirv::AssemblyGrammar(context)) { spv_diagnostic diag = nullptr; // We don't care if the parse fails. spvBinaryParse(context, this, code, wordCount, nullptr, ParseInstructionForwarder, &diag); spvDiagnosticDestroy(diag); } std::string FriendlyNameMapper::NameForId(uint32_t id) { auto iter = name_for_id_.find(id); if (iter == name_for_id_.end()) { // It must have been an invalid module, so just return a trivial mapping. // We don't care about uniqueness. return to_string(id); } else { return iter->second; } } std::string FriendlyNameMapper::Sanitize(const std::string& suggested_name) { if (suggested_name.empty()) return "_"; // Otherwise, replace invalid characters by '_'. std::string result; std::string valid = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "_0123456789"; std::transform(suggested_name.begin(), suggested_name.end(), std::back_inserter(result), [&valid](const char c) { return (std::string::npos == valid.find(c)) ? '_' : c; }); return result; } void FriendlyNameMapper::SaveName(uint32_t id, const std::string& suggested_name) { if (name_for_id_.find(id) != name_for_id_.end()) return; const std::string sanitized_suggested_name = Sanitize(suggested_name); std::string name = sanitized_suggested_name; auto inserted = used_names_.insert(name); if (!inserted.second) { const std::string base_name = sanitized_suggested_name + "_"; for (uint32_t index = 0; !inserted.second; ++index) { name = base_name + to_string(index); inserted = used_names_.insert(name); } } name_for_id_[id] = name; } spv_result_t FriendlyNameMapper::ParseInstruction( const spv_parsed_instruction_t& inst) { const auto result_id = inst.result_id; switch (inst.opcode) { case SpvOpName: SaveName(inst.words[1], reinterpret_cast(inst.words + 2)); break; case SpvOpTypeVoid: SaveName(result_id, "void"); break; case SpvOpTypeBool: SaveName(result_id, "bool"); break; case SpvOpTypeInt: { std::string signedness; std::string root; const auto bit_width = inst.words[2]; switch (bit_width) { case 8: root = "char"; break; case 16: root = "short"; break; case 32: root = "int"; break; case 64: root = "long"; break; default: root = to_string(bit_width); signedness = "i"; break; } if (0 == inst.words[3]) signedness = "u"; SaveName(result_id, signedness + root); } break; case SpvOpTypeFloat: { const auto bit_width = inst.words[2]; switch (bit_width) { case 16: SaveName(result_id, "half"); break; case 32: SaveName(result_id, "float"); break; case 64: SaveName(result_id, "double"); break; default: SaveName(result_id, std::string("fp") + to_string(bit_width)); break; } } break; case SpvOpTypeVector: SaveName(result_id, std::string("v") + to_string(inst.words[3]) + NameForId(inst.words[2])); break; case SpvOpTypeMatrix: SaveName(result_id, std::string("mat") + to_string(inst.words[3]) + NameForId(inst.words[2])); break; case SpvOpTypeArray: SaveName(result_id, std::string("_arr_") + NameForId(inst.words[2]) + "_" + NameForId(inst.words[3])); break; case SpvOpTypeRuntimeArray: SaveName(result_id, std::string("_runtimearr_") + NameForId(inst.words[2])); break; case SpvOpTypePointer: SaveName(result_id, std::string("_ptr_") + NameForEnumOperand(SPV_OPERAND_TYPE_STORAGE_CLASS, inst.words[2]) + "_" + NameForId(inst.words[3])); break; case SpvOpTypePipe: SaveName(result_id, std::string("Pipe") + NameForEnumOperand(SPV_OPERAND_TYPE_ACCESS_QUALIFIER, inst.words[2])); break; case SpvOpTypeEvent: SaveName(result_id, "Event"); break; case SpvOpTypeDeviceEvent: SaveName(result_id, "DeviceEvent"); break; case SpvOpTypeReserveId: SaveName(result_id, "ReserveId"); break; case SpvOpTypeQueue: SaveName(result_id, "Queue"); break; case SpvOpTypeOpaque: SaveName(result_id, std::string("Opaque_") + Sanitize(reinterpret_cast(inst.words + 2))); break; case SpvOpTypePipeStorage: SaveName(result_id, "PipeStorage"); break; case SpvOpTypeNamedBarrier: SaveName(result_id, "NamedBarrier"); break; case SpvOpTypeStruct: // Structs are mapped rather simplisitically. Just indicate that they // are a struct and then give the raw Id number. SaveName(result_id, std::string("_struct_") + to_string(result_id)); break; default: // If this instruction otherwise defines an Id, then save a mapping for // it. This is needed to ensure uniqueness in there is an OpName with // string something like "1" that might collide with this result_id. // We should only do this if a name hasn't already been registered by some // previous forward reference. if (result_id && name_for_id_.find(result_id) == name_for_id_.end()) SaveName(result_id, to_string(result_id)); break; } return SPV_SUCCESS; } std::string FriendlyNameMapper::NameForEnumOperand(spv_operand_type_t type, uint32_t word) { spv_operand_desc desc = nullptr; if (SPV_SUCCESS == grammar_.lookupOperand(type, word, &desc)) { return desc->name; } else { // Invalid input. Just give something sane. return std::string("StorageClass") + to_string(word); } } } // namespace libspirv