Coverage Report

Created: 2026-02-26 07:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/shaderc/third_party/spirv-tools/source/table2.cpp
Line
Count
Source
1
// Copyright (c) 2025 Google LLC
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
// Compressed grammar tables.
16
17
#include "source/table2.h"
18
19
#include <algorithm>
20
#include <array>
21
#include <cstring>
22
23
#include "source/extensions.h"
24
#include "source/latest_version_spirv_header.h"
25
#include "source/spirv_constant.h"
26
#include "source/spirv_target_env.h"
27
#include "spirv-tools/libspirv.hpp"
28
29
namespace spvtools {
30
namespace {
31
32
// This is used in the source for the generated tables.
33
87.4k
constexpr inline IndexRange IR(uint32_t first, uint32_t count) {
34
87.4k
  return IndexRange{first, count};
35
87.4k
}
36
37
struct NameIndex {
38
  // Location of the null-terminated name in the global string table kStrings.
39
  IndexRange name;
40
  // Index of this name's entry in the corresponding by-value table.
41
  uint32_t index;
42
};
43
44
struct NameValue {
45
  // Location of the null-terminated name in the global string table kStrings.
46
  IndexRange name;
47
  // Enum value in the binary format.
48
  uint32_t value;
49
};
50
51
// The generated include file contains variables:
52
//
53
//   std::array<NameIndex,...> kOperandNames:
54
//      Operand names and index, ordered by (operand kind, name)
55
//      The index part is the named entry's index in kOperandsByValue array.
56
//      Aliases are included as their own entries.
57
//
58
//   std::array<OperandDesc, ...> kOperandsByValue:
59
//      Operand descriptions, ordered by (operand kind, operand enum value).
60
//
61
//   std::array<NameIndex,...> kInstructionNames:
62
//      Instruction names and index, ordered by (name, value)
63
//      The index part is the named entry's index in kInstructionDesc array.
64
//      Aliases are included as their own entries.
65
//
66
//   std::array<InstructionDesc, ...> kInstructionDesc
67
//      Instruction descriptions, ordered by opcode.
68
//
69
//   const char kStrings[]
70
//      Array of characters, referenced by IndexRanges elsewhere.
71
//      Each IndexRange denotes a string.
72
//
73
//   const IndexRange kAliasSpans[]
74
//      Array of IndexRanges, where each represents a string by referencing
75
//      the kStrings table.
76
//      This array contains all sequences of alias strings used in the grammar.
77
//      This table is referenced by an IndexRange elsewhere, i.e. by the
78
//      'aliases' field of an instruction or operand description.
79
//
80
//   const spv::Capability kCapabilitySpans[]
81
//      Array of capabilities, referenced by IndexRanges elsewhere.
82
//      Contains all sequences of capabilities used in the grammar.
83
//
84
//   const spvtools::Extension kExtensionSpans[] = {
85
//      Array of extensions, referenced by IndexRanges elsewhere.
86
//      Contains all sequences of extensions used in the grammar.
87
//
88
//   const spv_operand_type_t kOperandSpans[] = {
89
//      Array of operand types, referenced by IndexRanges elsewhere.
90
//      Contains all sequences of operand types used in the grammar.
91
92
// Maps an operand kind to NameValue entries for that kind.
93
// The result is an IndexRange into kOperandNames, and are sorted
94
// by string name within that span.
95
IndexRange OperandNameRangeForKind(spv_operand_type_t type);
96
97
// Maps an operand kind to possible operands for that kind.
98
// The result is an IndexRange into kOperandsByValue, and the operands
99
// are sorted by value within that span.
100
IndexRange OperandByValueRangeForKind(spv_operand_type_t type);
101
102
// Maps an extended instruction set kind to NameValue entries for that kind.
103
// The result is an IndexRange into kExtIntNames, and are sorted
104
// by string name within that span.
105
IndexRange ExtInstNameRangeForKind(spv_ext_inst_type_t type);
106
107
// Maps an extended instruction set kind to possible operands for that kind.
108
// The result is an IndexRange into kExtInstByValue, and the instructions
109
// are sorted by opcode value within that span.
110
IndexRange ExtInstByValueRangeForKind(spv_ext_inst_type_t type);
111
112
// Returns the name of an extension, as an index into kStrings
113
IndexRange ExtensionToIndexRange(Extension extension);
114
115
#include "core_tables_body.inc"
116
117
// Returns a pointer to the null-terminated C-style string in the global
118
// strings table, as referenced by 'ir'.  Assumes the given range is valid.
119
4.83k
const char* getChars(IndexRange ir) {
120
4.83k
  assert(ir.first() < sizeof(kStrings));
121
4.83k
  return ir.apply(kStrings).data();
122
4.83k
}
123
124
}  // anonymous namespace
125
126
43.5k
utils::Span<const spv_operand_type_t> OperandDesc::operands() const {
127
43.5k
  return operands_range.apply(kOperandSpans);
128
43.5k
}
129
6.27k
utils::Span<const char> OperandDesc::name() const {
130
6.27k
  return name_range.apply(kStrings);
131
6.27k
}
132
0
utils::Span<const IndexRange> OperandDesc::aliases() const {
133
0
  return name_range.apply(kAliasSpans);
134
0
}
135
10.7k
utils::Span<const spv::Capability> OperandDesc::capabilities() const {
136
10.7k
  return capabilities_range.apply(kCapabilitySpans);
137
10.7k
}
138
638
utils::Span<const spvtools::Extension> OperandDesc::extensions() const {
139
638
  return extensions_range.apply(kExtensionSpans);
140
638
}
141
142
186k
utils::Span<const spv_operand_type_t> InstructionDesc::operands() const {
143
186k
  return operands_range.apply(kOperandSpans);
144
186k
}
145
21.4k
utils::Span<const char> InstructionDesc::name() const {
146
21.4k
  return name_range.apply(kStrings);
147
21.4k
}
148
0
utils::Span<const IndexRange> InstructionDesc::aliases() const {
149
0
  return name_range.apply(kAliasSpans);
150
0
}
151
84.7k
utils::Span<const spv::Capability> InstructionDesc::capabilities() const {
152
84.7k
  return capabilities_range.apply(kCapabilitySpans);
153
84.7k
}
154
80.1k
utils::Span<const spvtools::Extension> InstructionDesc::extensions() const {
155
80.1k
  return extensions_range.apply(kExtensionSpans);
156
80.1k
}
157
158
786
utils::Span<const spv_operand_type_t> ExtInstDesc::operands() const {
159
786
  return operands_range.apply(kOperandSpans);
160
786
}
161
117
utils::Span<const char> ExtInstDesc::name() const {
162
117
  return name_range.apply(kStrings);
163
117
}
164
0
utils::Span<const spv::Capability> ExtInstDesc::capabilities() const {
165
0
  return capabilities_range.apply(kCapabilitySpans);
166
0
}
167
168
293k
spv_result_t LookupOpcode(spv::Op opcode, const InstructionDesc** desc) {
169
  // Metaphor: Look for the needle in the haystack.
170
293k
  const InstructionDesc needle(opcode);
171
293k
  auto where = std::lower_bound(
172
293k
      kInstructionDesc.begin(), kInstructionDesc.end(), needle,
173
2.89M
      [&](const InstructionDesc& lhs, const InstructionDesc& rhs) {
174
2.89M
        return uint32_t(lhs.opcode) < uint32_t(rhs.opcode);
175
2.89M
      });
176
293k
  if (where != kInstructionDesc.end() && where->opcode == opcode) {
177
293k
    *desc = &*where;
178
293k
    return SPV_SUCCESS;
179
293k
  }
180
0
  return SPV_ERROR_INVALID_LOOKUP;
181
293k
}
182
183
0
spv_result_t LookupOpcode(const char* name, const InstructionDesc** desc) {
184
  // The comparison function knows to use 'name' string to compare against
185
  // when the value is kSentinel.
186
0
  const auto kSentinel = uint32_t(-1);
187
0
  const NameIndex needle{{}, kSentinel};
188
0
  auto less = [&](const NameIndex& lhs, const NameIndex& rhs) {
189
0
    const char* lhs_chars = lhs.index == kSentinel ? name : getChars(lhs.name);
190
0
    const char* rhs_chars = rhs.index == kSentinel ? name : getChars(rhs.name);
191
0
    return std::strcmp(lhs_chars, rhs_chars) < 0;
192
0
  };
193
194
0
  auto where = std::lower_bound(kInstructionNames.begin(),
195
0
                                kInstructionNames.end(), needle, less);
196
0
  if (where != kInstructionNames.end() &&
197
0
      std::strcmp(getChars(where->name), name) == 0) {
198
0
    *desc = &kInstructionDesc[where->index];
199
0
    return SPV_SUCCESS;
200
0
  }
201
0
  return SPV_ERROR_INVALID_LOOKUP;
202
0
}
203
204
namespace {
205
template <typename KEY_TYPE>
206
spv_result_t LookupOpcodeForEnvInternal(spv_target_env env, KEY_TYPE key,
207
42.3k
                                        const InstructionDesc** desc) {
208
42.3k
  const InstructionDesc* desc_proxy;
209
42.3k
  auto status = LookupOpcode(key, &desc_proxy);
210
42.3k
  if (status != SPV_SUCCESS) {
211
0
    return status;
212
0
  }
213
42.3k
  const auto& entry = *desc_proxy;
214
42.3k
  const auto version = spvVersionForTargetEnv(env);
215
42.3k
  if ((version >= entry.minVersion && version <= entry.lastVersion) ||
216
1.87k
      entry.extensions_range.count() > 0 ||
217
42.3k
      entry.capabilities_range.count() > 0) {
218
42.3k
    *desc = desc_proxy;
219
42.3k
    return SPV_SUCCESS;
220
42.3k
  }
221
0
  return SPV_ERROR_INVALID_LOOKUP;
222
42.3k
}
Unexecuted instantiation: table2.cpp:spv_result_t spvtools::(anonymous namespace)::LookupOpcodeForEnvInternal<char const*>(spv_target_env, char const*, spvtools::InstructionDesc const**)
table2.cpp:spv_result_t spvtools::(anonymous namespace)::LookupOpcodeForEnvInternal<spv::Op>(spv_target_env, spv::Op, spvtools::InstructionDesc const**)
Line
Count
Source
207
42.3k
                                        const InstructionDesc** desc) {
208
42.3k
  const InstructionDesc* desc_proxy;
209
42.3k
  auto status = LookupOpcode(key, &desc_proxy);
210
42.3k
  if (status != SPV_SUCCESS) {
211
0
    return status;
212
0
  }
213
42.3k
  const auto& entry = *desc_proxy;
214
42.3k
  const auto version = spvVersionForTargetEnv(env);
215
42.3k
  if ((version >= entry.minVersion && version <= entry.lastVersion) ||
216
1.87k
      entry.extensions_range.count() > 0 ||
217
42.3k
      entry.capabilities_range.count() > 0) {
218
42.3k
    *desc = desc_proxy;
219
42.3k
    return SPV_SUCCESS;
220
42.3k
  }
221
0
  return SPV_ERROR_INVALID_LOOKUP;
222
42.3k
}
223
}  // namespace
224
225
spv_result_t LookupOpcodeForEnv(spv_target_env env, const char* name,
226
0
                                const InstructionDesc** desc) {
227
0
  return LookupOpcodeForEnvInternal(env, name, desc);
228
0
}
229
230
spv_result_t LookupOpcodeForEnv(spv_target_env env, spv::Op opcode,
231
42.3k
                                const InstructionDesc** desc) {
232
42.3k
  return LookupOpcodeForEnvInternal(env, opcode, desc);
233
42.3k
}
234
235
spv_result_t LookupOperand(spv_operand_type_t type, uint32_t value,
236
85.2k
                           const OperandDesc** desc) {
237
85.2k
  auto ir = OperandByValueRangeForKind(type);
238
85.2k
  if (ir.empty()) {
239
15.8k
    return SPV_ERROR_INVALID_LOOKUP;
240
15.8k
  }
241
242
69.4k
  auto span = ir.apply(kOperandsByValue.data());
243
244
  // Metaphor: Look for the needle in the haystack.
245
  // The operand value is the first member.
246
69.4k
  const OperandDesc needle{value};
247
69.4k
  auto where =
248
69.4k
      std::lower_bound(span.begin(), span.end(), needle,
249
416k
                       [&](const OperandDesc& lhs, const OperandDesc& rhs) {
250
416k
                         return lhs.value < rhs.value;
251
416k
                       });
252
69.4k
  if (where != span.end() && where->value == value) {
253
69.4k
    *desc = &*where;
254
69.4k
    return SPV_SUCCESS;
255
69.4k
  }
256
0
  return SPV_ERROR_INVALID_LOOKUP;
257
69.4k
}
258
259
spv_result_t LookupOperand(spv_operand_type_t type, const char* name,
260
0
                           size_t name_len, const OperandDesc** desc) {
261
0
  auto ir = OperandNameRangeForKind(type);
262
0
  if (ir.empty()) {
263
0
    return SPV_ERROR_INVALID_LOOKUP;
264
0
  }
265
266
0
  auto span = ir.apply(kOperandNames.data());
267
268
  // The comparison function knows to use (name, name_len) as the
269
  // string to compare against when the value is kSentinel.
270
0
  const auto kSentinel = uint32_t(-1);
271
0
  const NameIndex needle{{}, kSentinel};
272
  // The strings in the global string table are null-terminated, and the count
273
  // reflects that.  So always deduct 1 from its length.
274
0
  auto less = [&](const NameIndex& lhs, const NameIndex& rhs) {
275
0
    const char* lhs_chars = lhs.index == kSentinel ? name : getChars(lhs.name);
276
0
    const char* rhs_chars = rhs.index == kSentinel ? name : getChars(rhs.name);
277
0
    const auto content_cmp = std::strncmp(lhs_chars, rhs_chars, name_len);
278
0
    if (content_cmp != 0) {
279
0
      return content_cmp < 0;
280
0
    }
281
0
    const auto lhs_len =
282
0
        lhs.index == kSentinel ? name_len : lhs.name.count() - 1;
283
0
    const auto rhs_len =
284
0
        rhs.index == kSentinel ? name_len : rhs.name.count() - 1;
285
0
    return lhs_len < rhs_len;
286
0
  };
287
288
0
  auto where = std::lower_bound(span.begin(), span.end(), needle, less);
289
0
  if (where != span.end() && where->name.count() - 1 == name_len &&
290
0
      std::strncmp(getChars(where->name), name, name_len) == 0) {
291
0
    *desc = &kOperandsByValue[where->index];
292
0
    return SPV_SUCCESS;
293
0
  }
294
0
  return SPV_ERROR_INVALID_LOOKUP;
295
0
}
296
297
spv_result_t LookupExtInst(spv_ext_inst_type_t type, const char* name,
298
0
                           const ExtInstDesc** desc) {
299
0
  auto ir = ExtInstNameRangeForKind(type);
300
0
  if (ir.empty()) {
301
0
    return SPV_ERROR_INVALID_LOOKUP;
302
0
  }
303
304
0
  auto span = ir.apply(kExtInstNames.data());
305
306
  // The comparison function knows to use 'name' string to compare against
307
  // when the value is kSentinel.
308
0
  const auto kSentinel = uint32_t(-1);
309
0
  const NameIndex needle{{}, kSentinel};
310
0
  auto less = [&](const NameIndex& lhs, const NameIndex& rhs) {
311
0
    const char* lhs_chars = lhs.index == kSentinel ? name : getChars(lhs.name);
312
0
    const char* rhs_chars = rhs.index == kSentinel ? name : getChars(rhs.name);
313
0
    return std::strcmp(lhs_chars, rhs_chars) < 0;
314
0
  };
315
316
0
  auto where = std::lower_bound(span.begin(), span.end(), needle, less);
317
0
  if (where != span.end() && std::strcmp(getChars(where->name), name) == 0) {
318
0
    *desc = &kExtInstByValue[where->index];
319
0
    return SPV_SUCCESS;
320
0
  }
321
0
  return SPV_ERROR_INVALID_LOOKUP;
322
0
}
323
324
// Finds the extended instruction description by opcode value.
325
// On success, returns SPV_SUCCESS and updates *desc.
326
spv_result_t LookupExtInst(spv_ext_inst_type_t type, uint32_t value,
327
903
                           const ExtInstDesc** desc) {
328
903
  auto ir = ExtInstByValueRangeForKind(type);
329
903
  if (ir.empty()) {
330
0
    return SPV_ERROR_INVALID_LOOKUP;
331
0
  }
332
333
903
  auto span = ir.apply(kExtInstByValue.data());
334
335
  // Metaphor: Look for the needle in the haystack.
336
  // The operand value is the first member.
337
903
  const ExtInstDesc needle(value);
338
903
  auto where =
339
903
      std::lower_bound(span.begin(), span.end(), needle,
340
5.81k
                       [&](const ExtInstDesc& lhs, const ExtInstDesc& rhs) {
341
5.81k
                         return lhs.value < rhs.value;
342
5.81k
                       });
343
903
  if (where != span.end() && where->value == value) {
344
903
    *desc = &*where;
345
903
    return SPV_SUCCESS;
346
903
  }
347
0
  return SPV_ERROR_INVALID_LOOKUP;
348
903
}
349
350
1.30k
const char* ExtensionToString(Extension extension) {
351
1.30k
  return getChars(ExtensionToIndexRange(extension));
352
1.30k
}
353
354
408
bool GetExtensionFromString(const char* name, Extension* extension) {
355
  // The comparison function knows to use 'name' string to compare against
356
  // when the value is kSentinel.
357
408
  const auto kSentinel = uint32_t(-1);
358
408
  const NameValue needle{{}, kSentinel};
359
3.12k
  auto less = [&](const NameValue& lhs, const NameValue& rhs) {
360
3.12k
    const char* lhs_chars = lhs.value == kSentinel ? name : getChars(lhs.name);
361
3.12k
    const char* rhs_chars = rhs.value == kSentinel ? name : getChars(rhs.name);
362
3.12k
    return std::strcmp(lhs_chars, rhs_chars) < 0;
363
3.12k
  };
364
365
408
  auto where = std::lower_bound(kExtensionNames.begin(), kExtensionNames.end(),
366
408
                                needle, less);
367
408
  if (where != kExtensionNames.end() &&
368
408
      std::strcmp(getChars(where->name), name) == 0) {
369
408
    *extension = static_cast<Extension>(where->value);
370
408
    return true;
371
408
  }
372
0
  return false;
373
408
}
374
375
// This is dirty copy of the spirv.hpp11 function
376
// TODO - Use a generated version of this function
377
0
const char* StorageClassToString(spv::StorageClass value) {
378
0
  switch (value) {
379
0
    case spv::StorageClass::UniformConstant:
380
0
      return "UniformConstant";
381
0
    case spv::StorageClass::Input:
382
0
      return "Input";
383
0
    case spv::StorageClass::Uniform:
384
0
      return "Uniform";
385
0
    case spv::StorageClass::Output:
386
0
      return "Output";
387
0
    case spv::StorageClass::Workgroup:
388
0
      return "Workgroup";
389
0
    case spv::StorageClass::CrossWorkgroup:
390
0
      return "CrossWorkgroup";
391
0
    case spv::StorageClass::Private:
392
0
      return "Private";
393
0
    case spv::StorageClass::Function:
394
0
      return "Function";
395
0
    case spv::StorageClass::Generic:
396
0
      return "Generic";
397
0
    case spv::StorageClass::PushConstant:
398
0
      return "PushConstant";
399
0
    case spv::StorageClass::AtomicCounter:
400
0
      return "AtomicCounter";
401
0
    case spv::StorageClass::Image:
402
0
      return "Image";
403
0
    case spv::StorageClass::StorageBuffer:
404
0
      return "StorageBuffer";
405
0
    case spv::StorageClass::TileImageEXT:
406
0
      return "TileImageEXT";
407
0
    case spv::StorageClass::TileAttachmentQCOM:
408
0
      return "TileAttachmentQCOM";
409
0
    case spv::StorageClass::NodePayloadAMDX:
410
0
      return "NodePayloadAMDX";
411
0
    case spv::StorageClass::CallableDataKHR:
412
0
      return "CallableDataKHR";
413
0
    case spv::StorageClass::IncomingCallableDataKHR:
414
0
      return "IncomingCallableDataKHR";
415
0
    case spv::StorageClass::RayPayloadKHR:
416
0
      return "RayPayloadKHR";
417
0
    case spv::StorageClass::HitAttributeKHR:
418
0
      return "HitAttributeKHR";
419
0
    case spv::StorageClass::IncomingRayPayloadKHR:
420
0
      return "IncomingRayPayloadKHR";
421
0
    case spv::StorageClass::ShaderRecordBufferKHR:
422
0
      return "ShaderRecordBufferKHR";
423
0
    case spv::StorageClass::PhysicalStorageBuffer:
424
0
      return "PhysicalStorageBuffer";
425
0
    case spv::StorageClass::HitObjectAttributeNV:
426
0
      return "HitObjectAttributeNV";
427
0
    case spv::StorageClass::TaskPayloadWorkgroupEXT:
428
0
      return "TaskPayloadWorkgroupEXT";
429
0
    case spv::StorageClass::CodeSectionINTEL:
430
0
      return "CodeSectionINTEL";
431
0
    case spv::StorageClass::DeviceOnlyINTEL:
432
0
      return "DeviceOnlyINTEL";
433
0
    case spv::StorageClass::HostOnlyINTEL:
434
0
      return "HostOnlyINTEL";
435
0
    default:
436
0
      return "Unknown";
437
0
  }
438
0
}
439
440
}  // namespace spvtools