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