• 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 "source/binary.h"
26 #include "source/latest_version_spirv_header.h"
27 #include "source/parsed_operand.h"
28 #include "spirv-tools/libspirv.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 spv::BuiltIn::name:            \
104     SaveName(target_id, "gl_" #name); \
105     return;
106 #define GLCASE2(name, suggested)           \
107   case spv::BuiltIn::name:                 \
108     SaveName(target_id, "gl_" #suggested); \
109     return;
110 #define CASE(name)              \
111   case spv::BuiltIn::name:      \
112     SaveName(target_id, #name); \
113     return;
114   switch (spv::BuiltIn(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 (spv::Op(inst.opcode)) {
174     case spv::Op::OpName:
175       SaveName(inst.words[1], spvDecodeLiteralStringOperand(inst, 1));
176       break;
177     case spv::Op::OpDecorate:
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 (spv::Decoration(inst.words[2]) == spv::Decoration::BuiltIn) {
184         assert(inst.num_words > 3);
185         SaveBuiltInName(inst.words[1], inst.words[3]);
186       }
187       break;
188     case spv::Op::OpTypeVoid:
189       SaveName(result_id, "void");
190       break;
191     case spv::Op::OpTypeBool:
192       SaveName(result_id, "bool");
193       break;
194     case spv::Op::OpTypeInt: {
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 spv::Op::OpTypeFloat: {
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 spv::Op::OpTypeVector:
237       SaveName(result_id, std::string("v") + to_string(inst.words[3]) +
238                               NameForId(inst.words[2]));
239       break;
240     case spv::Op::OpTypeMatrix:
241       SaveName(result_id, std::string("mat") + to_string(inst.words[3]) +
242                               NameForId(inst.words[2]));
243       break;
244     case spv::Op::OpTypeArray:
245       SaveName(result_id, std::string("_arr_") + NameForId(inst.words[2]) +
246                               "_" + NameForId(inst.words[3]));
247       break;
248     case spv::Op::OpTypeRuntimeArray:
249       SaveName(result_id,
250                std::string("_runtimearr_") + NameForId(inst.words[2]));
251       break;
252     case spv::Op::OpTypePointer:
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 spv::Op::OpTypePipe:
259       SaveName(result_id,
260                std::string("Pipe") +
261                    NameForEnumOperand(SPV_OPERAND_TYPE_ACCESS_QUALIFIER,
262                                       inst.words[2]));
263       break;
264     case spv::Op::OpTypeEvent:
265       SaveName(result_id, "Event");
266       break;
267     case spv::Op::OpTypeDeviceEvent:
268       SaveName(result_id, "DeviceEvent");
269       break;
270     case spv::Op::OpTypeReserveId:
271       SaveName(result_id, "ReserveId");
272       break;
273     case spv::Op::OpTypeQueue:
274       SaveName(result_id, "Queue");
275       break;
276     case spv::Op::OpTypeOpaque:
277       SaveName(result_id, std::string("Opaque_") +
278                               Sanitize(spvDecodeLiteralStringOperand(inst, 1)));
279       break;
280     case spv::Op::OpTypePipeStorage:
281       SaveName(result_id, "PipeStorage");
282       break;
283     case spv::Op::OpTypeNamedBarrier:
284       SaveName(result_id, "NamedBarrier");
285       break;
286     case spv::Op::OpTypeStruct:
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 spv::Op::OpConstantTrue:
292       SaveName(result_id, "true");
293       break;
294     case spv::Op::OpConstantFalse:
295       SaveName(result_id, "false");
296       break;
297     case spv::Op::OpConstant: {
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 
NameForEnumOperand(spv_operand_type_t type,uint32_t word)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.
327     return std::string("StorageClass") + to_string(word);
328   }
329 }
330 
331 }  // namespace spvtools
332