Update path-to-regexp to address CVE-2024-45296 (#5895)
[KhronosGroup/SPIRV-Tools.git] / source / name_mapper.cpp
blob7e5f0917403e0e0b892d8447cf010115d3222a50
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.
15 #include "source/name_mapper.h"
17 #include <algorithm>
18 #include <cassert>
19 #include <iterator>
20 #include <sstream>
21 #include <string>
22 #include <unordered_map>
23 #include <unordered_set>
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"
31 namespace spvtools {
33 NameMapper GetTrivialNameMapper() {
34 return [](uint32_t i) { return spvtools::to_string(i); };
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);
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;
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;
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;
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);
88 name_for_id_[id] = name;
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;
156 #undef GLCASE
157 #undef GLCASE2
158 #undef CASE
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.
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]);
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;
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;
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;
314 return SPV_SUCCESS;
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);
328 } // namespace spvtools