• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
to_string(uint32_t id)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 
GetTrivialNameMapper()44 NameMapper GetTrivialNameMapper() { return to_string; }
45 
FriendlyNameMapper(const spv_const_context context,const uint32_t * code,const size_t wordCount)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 
NameForId(uint32_t id)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 
Sanitize(const std::string & suggested_name)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 
SaveName(uint32_t id,const std::string & suggested_name)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 
SaveBuiltInName(uint32_t target_id,uint32_t built_in)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     GLCASE(BaseInstance)
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 
ParseInstruction(const spv_parsed_instruction_t & inst)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 
NameForEnumOperand(spv_operand_type_t type,uint32_t word)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.
328     return std::string("StorageClass") + to_string(word);
329   }
330 }
331 
332 }  // namespace spvtools
333