#include "source/name_mapper.h"
#include <algorithm>
#include <cassert>
#include <iterator>
#include <sstream>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include "source/binary.h"
#include "source/latest_version_spirv_header.h"
#include "source/parsed_operand.h"
#include "spirv-tools/libspirv.h"
namespace spvtools {
namespace {
std::string to_string(uint32_t id) {
std::stringstream os;
os << id;
return os.str();
}
}
NameMapper GetTrivialNameMapper() { return to_string; }
FriendlyNameMapper::FriendlyNameMapper(const spv_const_context context,
const uint32_t* code,
const size_t wordCount)
: grammar_(AssemblyGrammar(context)) {
spv_diagnostic diag = nullptr;
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()) {
return to_string(id);
} else {
return iter->second;
}
}
std::string FriendlyNameMapper::Sanitize(const std::string& suggested_name) {
if (suggested_name.empty()) return "_";
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;
std::string base_name;
const uint32_t kMaxSize = 256;
if (suggested_name.size() > kMaxSize) {
base_name = to_string(id);
} else {
base_name = Sanitize(suggested_name);
}
std::string name = base_name;
auto inserted = used_names_.insert(name);
if (!inserted.second) {
const std::string base_name_prefix = base_name + "_";
for (uint32_t index = 0; !inserted.second; ++index) {
name = base_name_prefix + to_string(index);
inserted = used_names_.insert(name);
}
}
name_for_id_[id] = name;
}
void FriendlyNameMapper::SaveBuiltInName(uint32_t target_id,
uint32_t built_in) {
#define GLCASE(name) \
case spv::BuiltIn::name: \
SaveName(target_id, "gl_" #name); \
return;
#define GLCASE2(name, suggested) \
case spv::BuiltIn::name: \
SaveName(target_id, "gl_" #suggested); \
return;
#define CASE(name) \
case spv::BuiltIn::name: \
SaveName(target_id, #name); \
return;
switch (spv::BuiltIn(built_in)) {
GLCASE(Position)
GLCASE(PointSize)
GLCASE(ClipDistance)
GLCASE(CullDistance)
GLCASE2(VertexId, VertexID)
GLCASE2(InstanceId, InstanceID)
GLCASE2(PrimitiveId, PrimitiveID)
GLCASE2(InvocationId, InvocationID)
GLCASE(Layer)
GLCASE(ViewportIndex)
GLCASE(TessLevelOuter)
GLCASE(TessLevelInner)
GLCASE(TessCoord)
GLCASE(PatchVertices)
GLCASE(FragCoord)
GLCASE(PointCoord)
GLCASE(FrontFacing)
GLCASE2(SampleId, SampleID)
GLCASE(SamplePosition)
GLCASE(SampleMask)
GLCASE(FragDepth)
GLCASE(HelperInvocation)
GLCASE2(NumWorkgroups, NumWorkGroups)
GLCASE2(WorkgroupSize, WorkGroupSize)
GLCASE2(WorkgroupId, WorkGroupID)
GLCASE2(LocalInvocationId, LocalInvocationID)
GLCASE2(GlobalInvocationId, GlobalInvocationID)
GLCASE(LocalInvocationIndex)
CASE(WorkDim)
CASE(GlobalSize)
CASE(EnqueuedWorkgroupSize)
CASE(GlobalOffset)
CASE(GlobalLinearId)
CASE(SubgroupSize)
CASE(SubgroupMaxSize)
CASE(NumSubgroups)
CASE(NumEnqueuedSubgroups)
CASE(SubgroupId)
CASE(SubgroupLocalInvocationId)
GLCASE(VertexIndex)
GLCASE(InstanceIndex)
GLCASE(BaseInstance)
CASE(SubgroupEqMaskKHR)
CASE(SubgroupGeMaskKHR)
CASE(SubgroupGtMaskKHR)
CASE(SubgroupLeMaskKHR)
CASE(SubgroupLtMaskKHR)
default:
break;
}
#undef GLCASE
#undef GLCASE2
#undef CASE
}
spv_result_t FriendlyNameMapper::ParseInstruction(
const spv_parsed_instruction_t& inst) {
const auto result_id = inst.result_id;
switch (spv::Op(inst.opcode)) {
case spv::Op::OpName:
SaveName(inst.words[1], spvDecodeLiteralStringOperand(inst, 1));
break;
case spv::Op::OpDecorate:
if (spv::Decoration(inst.words[2]) == spv::Decoration::BuiltIn) {
assert(inst.num_words > 3);
SaveBuiltInName(inst.words[1], inst.words[3]);
}
break;
case spv::Op::OpTypeVoid:
SaveName(result_id, "void");
break;
case spv::Op::OpTypeBool:
SaveName(result_id, "bool");
break;
case spv::Op::OpTypeInt: {
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 spv::Op::OpTypeFloat: {
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 spv::Op::OpTypeVector:
SaveName(result_id, std::string("v") + to_string(inst.words[3]) +
NameForId(inst.words[2]));
break;
case spv::Op::OpTypeMatrix:
SaveName(result_id, std::string("mat") + to_string(inst.words[3]) +
NameForId(inst.words[2]));
break;
case spv::Op::OpTypeArray:
SaveName(result_id, std::string("_arr_") + NameForId(inst.words[2]) +
"_" + NameForId(inst.words[3]));
break;
case spv::Op::OpTypeRuntimeArray:
SaveName(result_id,
std::string("_runtimearr_") + NameForId(inst.words[2]));
break;
case spv::Op::OpTypePointer:
SaveName(result_id, std::string("_ptr_") +
NameForEnumOperand(SPV_OPERAND_TYPE_STORAGE_CLASS,
inst.words[2]) +
"_" + NameForId(inst.words[3]));
break;
case spv::Op::OpTypePipe:
SaveName(result_id,
std::string("Pipe") +
NameForEnumOperand(SPV_OPERAND_TYPE_ACCESS_QUALIFIER,
inst.words[2]));
break;
case spv::Op::OpTypeEvent:
SaveName(result_id, "Event");
break;
case spv::Op::OpTypeDeviceEvent:
SaveName(result_id, "DeviceEvent");
break;
case spv::Op::OpTypeReserveId:
SaveName(result_id, "ReserveId");
break;
case spv::Op::OpTypeQueue:
SaveName(result_id, "Queue");
break;
case spv::Op::OpTypeOpaque:
SaveName(result_id, std::string("Opaque_") +
Sanitize(spvDecodeLiteralStringOperand(inst, 1)));
break;
case spv::Op::OpTypePipeStorage:
SaveName(result_id, "PipeStorage");
break;
case spv::Op::OpTypeNamedBarrier:
SaveName(result_id, "NamedBarrier");
break;
case spv::Op::OpTypeStruct:
SaveName(result_id, std::string("_struct_") + to_string(result_id));
break;
case spv::Op::OpConstantTrue:
SaveName(result_id, "true");
break;
case spv::Op::OpConstantFalse:
SaveName(result_id, "false");
break;
case spv::Op::OpConstant: {
std::ostringstream value;
EmitNumericLiteral(&value, inst, inst.operands[2]);
auto value_str = value.str();
for (auto& c : value_str)
if (c == '-') c = 'n';
SaveName(result_id, NameForId(inst.type_id) + "_" + value_str);
} break;
default:
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 {
return std::string("StorageClass") + to_string(word);
}
}
}