blob: 0dac99a23165ea3b1a57a1aa4496bfa64aa8e946 [file] [log] [blame]
// 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 <algorithm>
#include <iterator>
#include <sstream>
#include <string>
#include <unordered_map>
#include <unordered_set>
#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<const char*>(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<const char*>(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