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