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