Coverage Report

Created: 2026-07-16 07:02

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