Coverage Report

Created: 2025-10-28 07:27

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