1 // Copyright (c) 2016 Google Inc.
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
7 // http://www.apache.org/licenses/LICENSE-2.0
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"
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"
33 NameMapper
GetTrivialNameMapper() {
34 return [](uint32_t i
) { return spvtools::to_string(i
); };
37 FriendlyNameMapper::FriendlyNameMapper(const spv_const_context context
,
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.
59 std::string
FriendlyNameMapper::Sanitize(const std::string
& suggested_name
) {
60 if (suggested_name
.empty()) return "_";
61 // Otherwise, replace invalid characters by '_'.
64 "abcdefghijklmnopqrstuvwxyz"
65 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
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
;
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
,
93 #define GLCASE(name) \
94 case spv::BuiltIn::name: \
95 SaveName(target_id, "gl_" #name); \
97 #define GLCASE2(name, suggested) \
98 case spv::BuiltIn::name: \
99 SaveName(target_id, "gl_" #suggested); \
102 case spv::BuiltIn::name: \
103 SaveName(target_id, #name); \
105 switch (spv::BuiltIn(built_in
)) {
110 GLCASE2(VertexId
, VertexID
)
111 GLCASE2(InstanceId
, InstanceID
)
112 GLCASE2(PrimitiveId
, PrimitiveID
)
113 GLCASE2(InvocationId
, InvocationID
)
115 GLCASE(ViewportIndex
)
116 GLCASE(TessLevelOuter
)
117 GLCASE(TessLevelInner
)
119 GLCASE(PatchVertices
)
123 GLCASE2(SampleId
, SampleID
)
124 GLCASE(SamplePosition
)
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
)
136 CASE(EnqueuedWorkgroupSize
)
140 CASE(SubgroupMaxSize
)
142 CASE(NumEnqueuedSubgroups
)
144 CASE(SubgroupLocalInvocationId
)
146 GLCASE(InstanceIndex
)
148 CASE(SubgroupEqMaskKHR
)
149 CASE(SubgroupGeMaskKHR
)
150 CASE(SubgroupGtMaskKHR
)
151 CASE(SubgroupLeMaskKHR
)
152 CASE(SubgroupLtMaskKHR
)
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));
168 case spv::Op::OpDecorate
:
169 // Decorations come after OpName. So OpName will take precedence over
172 // In theory, we should also handle OpGroupDecorate. But that's unlikely
174 if (spv::Decoration(inst
.words
[2]) == spv::Decoration::BuiltIn
) {
175 assert(inst
.num_words
> 3);
176 SaveBuiltInName(inst
.words
[1], inst
.words
[3]);
179 case spv::Op::OpTypeVoid
:
180 SaveName(result_id
, "void");
182 case spv::Op::OpTypeBool
:
183 SaveName(result_id
, "bool");
185 case spv::Op::OpTypeInt
: {
186 std::string signedness
;
188 const auto bit_width
= inst
.words
[2];
203 root
= to_string(bit_width
);
207 if (0 == inst
.words
[3]) signedness
= "u";
208 SaveName(result_id
, signedness
+ root
);
210 case spv::Op::OpTypeFloat
: {
211 const auto bit_width
= inst
.words
[2];
212 // TODO: Handle optional fpencoding enum once actually used.
215 SaveName(result_id
, "half");
218 SaveName(result_id
, "float");
221 SaveName(result_id
, "double");
224 SaveName(result_id
, std::string("fp") + to_string(bit_width
));
228 case spv::Op::OpTypeVector
:
229 SaveName(result_id
, std::string("v") + to_string(inst
.words
[3]) +
230 NameForId(inst
.words
[2]));
232 case spv::Op::OpTypeMatrix
:
233 SaveName(result_id
, std::string("mat") + to_string(inst
.words
[3]) +
234 NameForId(inst
.words
[2]));
236 case spv::Op::OpTypeArray
:
237 SaveName(result_id
, std::string("_arr_") + NameForId(inst
.words
[2]) +
238 "_" + NameForId(inst
.words
[3]));
240 case spv::Op::OpTypeRuntimeArray
:
242 std::string("_runtimearr_") + NameForId(inst
.words
[2]));
244 case spv::Op::OpTypePointer
:
245 SaveName(result_id
, std::string("_ptr_") +
246 NameForEnumOperand(SPV_OPERAND_TYPE_STORAGE_CLASS
,
248 "_" + NameForId(inst
.words
[3]));
250 case spv::Op::OpTypeUntypedPointerKHR
:
251 SaveName(result_id
, std::string("_ptr_") +
252 NameForEnumOperand(SPV_OPERAND_TYPE_STORAGE_CLASS
,
255 case spv::Op::OpTypePipe
:
257 std::string("Pipe") +
258 NameForEnumOperand(SPV_OPERAND_TYPE_ACCESS_QUALIFIER
,
261 case spv::Op::OpTypeEvent
:
262 SaveName(result_id
, "Event");
264 case spv::Op::OpTypeDeviceEvent
:
265 SaveName(result_id
, "DeviceEvent");
267 case spv::Op::OpTypeReserveId
:
268 SaveName(result_id
, "ReserveId");
270 case spv::Op::OpTypeQueue
:
271 SaveName(result_id
, "Queue");
273 case spv::Op::OpTypeOpaque
:
274 SaveName(result_id
, std::string("Opaque_") +
275 Sanitize(spvDecodeLiteralStringOperand(inst
, 1)));
277 case spv::Op::OpTypePipeStorage
:
278 SaveName(result_id
, "PipeStorage");
280 case spv::Op::OpTypeNamedBarrier
:
281 SaveName(result_id
, "NamedBarrier");
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
));
288 case spv::Op::OpConstantTrue
:
289 SaveName(result_id
, "true");
291 case spv::Op::OpConstantFalse
:
292 SaveName(result_id
, "false");
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
300 for (auto& c
: value_str
)
301 if (c
== '-') c
= 'n';
302 SaveName(result_id
, NameForId(inst
.type_id
) + "_" + value_str
);
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
));
317 std::string
FriendlyNameMapper::NameForEnumOperand(spv_operand_type_t type
,
319 spv_operand_desc desc
= nullptr;
320 if (SPV_SUCCESS
== grammar_
.lookupOperand(type
, word
, &desc
)) {
323 // Invalid input. Just give something.
324 return std::string("StorageClass") + to_string(word
);
328 } // namespace spvtools