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