Home | History | Annotate | Download | only in source
      1 // Copyright (c) 2016 Google Inc.
      2 //
      3 // Licensed under the Apache License, Version 2.0 (the "License");
      4 // you may not use this file except in compliance with the License.
      5 // You may obtain a copy of the License at
      6 //
      7 //     http://www.apache.org/licenses/LICENSE-2.0
      8 //
      9 // Unless required by applicable law or agreed to in writing, software
     10 // distributed under the License is distributed on an "AS IS" BASIS,
     11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 // See the License for the specific language governing permissions and
     13 // limitations under the License.
     14 
     15 #include "source/name_mapper.h"
     16 
     17 #include <algorithm>
     18 #include <cassert>
     19 #include <iterator>
     20 #include <sstream>
     21 #include <string>
     22 #include <unordered_map>
     23 #include <unordered_set>
     24 
     25 #include "spirv-tools/libspirv.h"
     26 
     27 #include "source/latest_version_spirv_header.h"
     28 #include "source/parsed_operand.h"
     29 
     30 namespace spvtools {
     31 namespace {
     32 
     33 // Converts a uint32_t to its string decimal representation.
     34 std::string to_string(uint32_t id) {
     35   // Use stringstream, since some versions of Android compilers lack
     36   // std::to_string.
     37   std::stringstream os;
     38   os << id;
     39   return os.str();
     40 }
     41 
     42 }  // anonymous namespace
     43 
     44 NameMapper GetTrivialNameMapper() { return to_string; }
     45 
     46 FriendlyNameMapper::FriendlyNameMapper(const spv_const_context context,
     47                                        const uint32_t* code,
     48                                        const size_t wordCount)
     49     : grammar_(AssemblyGrammar(context)) {
     50   spv_diagnostic diag = nullptr;
     51   // We don't care if the parse fails.
     52   spvBinaryParse(context, this, code, wordCount, nullptr,
     53                  ParseInstructionForwarder, &diag);
     54   spvDiagnosticDestroy(diag);
     55 }
     56 
     57 std::string FriendlyNameMapper::NameForId(uint32_t id) {
     58   auto iter = name_for_id_.find(id);
     59   if (iter == name_for_id_.end()) {
     60     // It must have been an invalid module, so just return a trivial mapping.
     61     // We don't care about uniqueness.
     62     return to_string(id);
     63   } else {
     64     return iter->second;
     65   }
     66 }
     67 
     68 std::string FriendlyNameMapper::Sanitize(const std::string& suggested_name) {
     69   if (suggested_name.empty()) return "_";
     70   // Otherwise, replace invalid characters by '_'.
     71   std::string result;
     72   std::string valid =
     73       "abcdefghijklmnopqrstuvwxyz"
     74       "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
     75       "_0123456789";
     76   std::transform(suggested_name.begin(), suggested_name.end(),
     77                  std::back_inserter(result), [&valid](const char c) {
     78                    return (std::string::npos == valid.find(c)) ? '_' : c;
     79                  });
     80   return result;
     81 }
     82 
     83 void FriendlyNameMapper::SaveName(uint32_t id,
     84                                   const std::string& suggested_name) {
     85   if (name_for_id_.find(id) != name_for_id_.end()) return;
     86 
     87   const std::string sanitized_suggested_name = Sanitize(suggested_name);
     88   std::string name = sanitized_suggested_name;
     89   auto inserted = used_names_.insert(name);
     90   if (!inserted.second) {
     91     const std::string base_name = sanitized_suggested_name + "_";
     92     for (uint32_t index = 0; !inserted.second; ++index) {
     93       name = base_name + to_string(index);
     94       inserted = used_names_.insert(name);
     95     }
     96   }
     97   name_for_id_[id] = name;
     98 }
     99 
    100 void FriendlyNameMapper::SaveBuiltInName(uint32_t target_id,
    101                                          uint32_t built_in) {
    102 #define GLCASE(name)                  \
    103   case SpvBuiltIn##name:              \
    104     SaveName(target_id, "gl_" #name); \
    105     return;
    106 #define GLCASE2(name, suggested)           \
    107   case SpvBuiltIn##name:                   \
    108     SaveName(target_id, "gl_" #suggested); \
    109     return;
    110 #define CASE(name)              \
    111   case SpvBuiltIn##name:        \
    112     SaveName(target_id, #name); \
    113     return;
    114   switch (built_in) {
    115     GLCASE(Position)
    116     GLCASE(PointSize)
    117     GLCASE(ClipDistance)
    118     GLCASE(CullDistance)
    119     GLCASE2(VertexId, VertexID)
    120     GLCASE2(InstanceId, InstanceID)
    121     GLCASE2(PrimitiveId, PrimitiveID)
    122     GLCASE2(InvocationId, InvocationID)
    123     GLCASE(Layer)
    124     GLCASE(ViewportIndex)
    125     GLCASE(TessLevelOuter)
    126     GLCASE(TessLevelInner)
    127     GLCASE(TessCoord)
    128     GLCASE(PatchVertices)
    129     GLCASE(FragCoord)
    130     GLCASE(PointCoord)
    131     GLCASE(FrontFacing)
    132     GLCASE2(SampleId, SampleID)
    133     GLCASE(SamplePosition)
    134     GLCASE(SampleMask)
    135     GLCASE(FragDepth)
    136     GLCASE(HelperInvocation)
    137     GLCASE2(NumWorkgroups, NumWorkGroups)
    138     GLCASE2(WorkgroupSize, WorkGroupSize)
    139     GLCASE2(WorkgroupId, WorkGroupID)
    140     GLCASE2(LocalInvocationId, LocalInvocationID)
    141     GLCASE2(GlobalInvocationId, GlobalInvocationID)
    142     GLCASE(LocalInvocationIndex)
    143     CASE(WorkDim)
    144     CASE(GlobalSize)
    145     CASE(EnqueuedWorkgroupSize)
    146     CASE(GlobalOffset)
    147     CASE(GlobalLinearId)
    148     CASE(SubgroupSize)
    149     CASE(SubgroupMaxSize)
    150     CASE(NumSubgroups)
    151     CASE(NumEnqueuedSubgroups)
    152     CASE(SubgroupId)
    153     CASE(SubgroupLocalInvocationId)
    154     GLCASE(VertexIndex)
    155     GLCASE(InstanceIndex)
    156     CASE(SubgroupEqMaskKHR)
    157     CASE(SubgroupGeMaskKHR)
    158     CASE(SubgroupGtMaskKHR)
    159     CASE(SubgroupLeMaskKHR)
    160     CASE(SubgroupLtMaskKHR)
    161     default:
    162       break;
    163   }
    164 #undef GLCASE
    165 #undef GLCASE2
    166 #undef CASE
    167 }
    168 
    169 spv_result_t FriendlyNameMapper::ParseInstruction(
    170     const spv_parsed_instruction_t& inst) {
    171   const auto result_id = inst.result_id;
    172   switch (inst.opcode) {
    173     case SpvOpName:
    174       SaveName(inst.words[1], reinterpret_cast<const char*>(inst.words + 2));
    175       break;
    176     case SpvOpDecorate:
    177       // Decorations come after OpName.  So OpName will take precedence over
    178       // decorations.
    179       //
    180       // In theory, we should also handle OpGroupDecorate.  But that's unlikely
    181       // to occur.
    182       if (inst.words[2] == SpvDecorationBuiltIn) {
    183         assert(inst.num_words > 3);
    184         SaveBuiltInName(inst.words[1], inst.words[3]);
    185       }
    186       break;
    187     case SpvOpTypeVoid:
    188       SaveName(result_id, "void");
    189       break;
    190     case SpvOpTypeBool:
    191       SaveName(result_id, "bool");
    192       break;
    193     case SpvOpTypeInt: {
    194       std::string signedness;
    195       std::string root;
    196       const auto bit_width = inst.words[2];
    197       switch (bit_width) {
    198         case 8:
    199           root = "char";
    200           break;
    201         case 16:
    202           root = "short";
    203           break;
    204         case 32:
    205           root = "int";
    206           break;
    207         case 64:
    208           root = "long";
    209           break;
    210         default:
    211           root = to_string(bit_width);
    212           signedness = "i";
    213           break;
    214       }
    215       if (0 == inst.words[3]) signedness = "u";
    216       SaveName(result_id, signedness + root);
    217     } break;
    218     case SpvOpTypeFloat: {
    219       const auto bit_width = inst.words[2];
    220       switch (bit_width) {
    221         case 16:
    222           SaveName(result_id, "half");
    223           break;
    224         case 32:
    225           SaveName(result_id, "float");
    226           break;
    227         case 64:
    228           SaveName(result_id, "double");
    229           break;
    230         default:
    231           SaveName(result_id, std::string("fp") + to_string(bit_width));
    232           break;
    233       }
    234     } break;
    235     case SpvOpTypeVector:
    236       SaveName(result_id, std::string("v") + to_string(inst.words[3]) +
    237                               NameForId(inst.words[2]));
    238       break;
    239     case SpvOpTypeMatrix:
    240       SaveName(result_id, std::string("mat") + to_string(inst.words[3]) +
    241                               NameForId(inst.words[2]));
    242       break;
    243     case SpvOpTypeArray:
    244       SaveName(result_id, std::string("_arr_") + NameForId(inst.words[2]) +
    245                               "_" + NameForId(inst.words[3]));
    246       break;
    247     case SpvOpTypeRuntimeArray:
    248       SaveName(result_id,
    249                std::string("_runtimearr_") + NameForId(inst.words[2]));
    250       break;
    251     case SpvOpTypePointer:
    252       SaveName(result_id, std::string("_ptr_") +
    253                               NameForEnumOperand(SPV_OPERAND_TYPE_STORAGE_CLASS,
    254                                                  inst.words[2]) +
    255                               "_" + NameForId(inst.words[3]));
    256       break;
    257     case SpvOpTypePipe:
    258       SaveName(result_id,
    259                std::string("Pipe") +
    260                    NameForEnumOperand(SPV_OPERAND_TYPE_ACCESS_QUALIFIER,
    261                                       inst.words[2]));
    262       break;
    263     case SpvOpTypeEvent:
    264       SaveName(result_id, "Event");
    265       break;
    266     case SpvOpTypeDeviceEvent:
    267       SaveName(result_id, "DeviceEvent");
    268       break;
    269     case SpvOpTypeReserveId:
    270       SaveName(result_id, "ReserveId");
    271       break;
    272     case SpvOpTypeQueue:
    273       SaveName(result_id, "Queue");
    274       break;
    275     case SpvOpTypeOpaque:
    276       SaveName(result_id,
    277                std::string("Opaque_") +
    278                    Sanitize(reinterpret_cast<const char*>(inst.words + 2)));
    279       break;
    280     case SpvOpTypePipeStorage:
    281       SaveName(result_id, "PipeStorage");
    282       break;
    283     case SpvOpTypeNamedBarrier:
    284       SaveName(result_id, "NamedBarrier");
    285       break;
    286     case SpvOpTypeStruct:
    287       // Structs are mapped rather simplisitically. Just indicate that they
    288       // are a struct and then give the raw Id number.
    289       SaveName(result_id, std::string("_struct_") + to_string(result_id));
    290       break;
    291     case SpvOpConstantTrue:
    292       SaveName(result_id, "true");
    293       break;
    294     case SpvOpConstantFalse:
    295       SaveName(result_id, "false");
    296       break;
    297     case SpvOpConstant: {
    298       std::ostringstream value;
    299       EmitNumericLiteral(&value, inst, inst.operands[2]);
    300       auto value_str = value.str();
    301       // Use 'n' to signify negative. Other invalid characters will be mapped
    302       // to underscore.
    303       for (auto& c : value_str)
    304         if (c == '-') c = 'n';
    305       SaveName(result_id, NameForId(inst.type_id) + "_" + value_str);
    306     } break;
    307     default:
    308       // If this instruction otherwise defines an Id, then save a mapping for
    309       // it.  This is needed to ensure uniqueness in there is an OpName with
    310       // string something like "1" that might collide with this result_id.
    311       // We should only do this if a name hasn't already been registered by some
    312       // previous forward reference.
    313       if (result_id && name_for_id_.find(result_id) == name_for_id_.end())
    314         SaveName(result_id, to_string(result_id));
    315       break;
    316   }
    317   return SPV_SUCCESS;
    318 }
    319 
    320 std::string FriendlyNameMapper::NameForEnumOperand(spv_operand_type_t type,
    321                                                    uint32_t word) {
    322   spv_operand_desc desc = nullptr;
    323   if (SPV_SUCCESS == grammar_.lookupOperand(type, word, &desc)) {
    324     return desc->name;
    325   } else {
    326     // Invalid input.  Just give something sane.
    327     return std::string("StorageClass") + to_string(word);
    328   }
    329 }
    330 
    331 }  // namespace spvtools
    332