Coverage Report

Created: 2026-11-12 06:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/spirv-cross/spirv_parser.cpp
Line
Count
Source
1
/*
2
 * Copyright 2018-2021 Arm Limited
3
 * SPDX-License-Identifier: Apache-2.0 OR MIT
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
18
/*
19
 * At your option, you may choose to accept this material under either:
20
 *  1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
21
 *  2. The MIT License, found at <http://opensource.org/licenses/MIT>.
22
 */
23
24
#include "spirv_parser.hpp"
25
#include "spirv_common.hpp"
26
#include "NonSemanticShaderDebugInfo100.h"
27
#include <assert.h>
28
29
using namespace std;
30
using namespace SPIRV_CROSS_SPV_HEADER_NAMESPACE;
31
32
namespace SPIRV_CROSS_NAMESPACE
33
{
34
Parser::Parser(vector<uint32_t> spirv)
35
438
{
36
438
  ir.spirv = std::move(spirv);
37
438
}
38
39
Parser::Parser(const uint32_t *spirv_data, size_t word_count)
40
0
{
41
0
  ir.spirv = vector<uint32_t>(spirv_data, spirv_data + word_count);
42
0
}
43
44
static bool decoration_is_string(Decoration decoration)
45
3.61k
{
46
3.61k
  switch (decoration)
47
3.61k
  {
48
0
  case DecorationUserSemantic:
49
0
    return true;
50
51
3.61k
  default:
52
3.61k
    return false;
53
3.61k
  }
54
3.61k
}
55
56
static inline uint32_t swap_endian(uint32_t v)
57
438
{
58
438
  return ((v >> 24) & 0x000000ffu) | ((v >> 8) & 0x0000ff00u) | ((v << 8) & 0x00ff0000u) | ((v << 24) & 0xff000000u);
59
438
}
60
61
static bool is_valid_spirv_version(uint32_t version)
62
438
{
63
438
  switch (version)
64
438
  {
65
  // Allow v99 since it tends to just work.
66
0
  case 99:
67
0
  case 0x10000: // SPIR-V 1.0
68
0
  case 0x10100: // SPIR-V 1.1
69
0
  case 0x10200: // SPIR-V 1.2
70
0
  case 0x10300: // SPIR-V 1.3
71
0
  case 0x10400: // SPIR-V 1.4
72
0
  case 0x10500: // SPIR-V 1.5
73
438
  case 0x10600: // SPIR-V 1.6
74
438
    return true;
75
76
0
  default:
77
0
    return false;
78
438
  }
79
438
}
80
81
void Parser::parse()
82
438
{
83
438
  auto &spirv = ir.spirv;
84
85
438
  auto len = spirv.size();
86
438
  if (len < 5)
87
0
    SPIRV_CROSS_THROW("SPIRV file too small.");
88
89
438
  auto s = spirv.data();
90
91
  // Endian-swap if we need to.
92
438
  if (s[0] == swap_endian(MagicNumber))
93
0
    transform(begin(spirv), end(spirv), begin(spirv), [](uint32_t c) { return swap_endian(c); });
94
95
438
  if (s[0] != MagicNumber || !is_valid_spirv_version(s[1]))
96
0
    SPIRV_CROSS_THROW("Invalid SPIRV format.");
97
98
438
  uint32_t bound = s[3];
99
100
438
  const uint32_t MaximumNumberOfIDs = 0x3fffff;
101
438
  if (bound > MaximumNumberOfIDs)
102
3
    SPIRV_CROSS_THROW("ID bound exceeds limit of 0x3fffff.\n");
103
104
435
  ir.set_id_bounds(bound);
105
106
435
  uint32_t offset = 5;
107
108
435
  SmallVector<Instruction> instructions;
109
895k
  while (offset < len)
110
895k
  {
111
895k
    Instruction instr = {};
112
895k
    instr.op = spirv[offset] & 0xffff;
113
895k
    instr.count = (spirv[offset] >> 16) & 0xffff;
114
115
895k
    if (instr.count == 0)
116
18
      SPIRV_CROSS_THROW("SPIR-V instructions cannot consume 0 words. Invalid SPIR-V file.");
117
118
895k
    instr.offset = offset + 1;
119
895k
    instr.length = instr.count - 1;
120
121
895k
    offset += instr.count;
122
123
895k
    if (offset > spirv.size())
124
11
      SPIRV_CROSS_THROW("SPIR-V instruction goes out of bounds.");
125
126
895k
    instructions.push_back(instr);
127
895k
  }
128
129
406
  for (auto &i : instructions)
130
791k
    parse(i);
131
132
406
  for (auto &fixup : forward_pointer_fixups)
133
4.25k
  {
134
4.25k
    auto &target = get<SPIRType>(fixup.first);
135
4.25k
    auto &source = get<SPIRType>(fixup.second);
136
4.25k
    target.member_types = source.member_types;
137
4.25k
    target.basetype = source.basetype;
138
4.25k
    target.self = source.self;
139
4.25k
  }
140
406
  forward_pointer_fixups.clear();
141
142
406
  for (auto &source : ir.sources)
143
120k
  {
144
1.83M
    auto cmp = [](const ParsedIR::Source::Marker &a, const ParsedIR::Source::Marker &b) {
145
1.83M
      return a.line < b.line;
146
1.83M
    };
147
148
120k
    std::sort(source.line_markers.begin(), source.line_markers.end(), cmp);
149
120k
  }
150
151
406
  if (current_function)
152
104
    SPIRV_CROSS_THROW("Function was not terminated.");
153
302
  if (current_block)
154
0
    SPIRV_CROSS_THROW("Block was not terminated.");
155
156
  // Now that all definitions are bound to a kind, we can filter the library
157
  // exports and populate the exported functions.
158
302
  for (uint32_t id : ir.library_exports)
159
0
  {
160
0
    if (ir.ids[id].get_type() == TypeFunction)
161
0
      ir.library_exported_functions.push_back(id);
162
0
  }
163
164
302
  if (ir.default_entry_point == 0)
165
138
  {
166
138
    if (ir.library_exported_functions.empty())
167
138
      SPIRV_CROSS_THROW("There is no entry point in the SPIR-V module.");
168
169
    // No OpEntryPoint, but the module exports functions. Treat as a library
170
    // module: designate the first exported function as the default entry
171
    // point so analyses keyed on default_entry_point can run.
172
0
    ir.is_library_module = true;
173
0
    ir.default_entry_point = ir.library_exported_functions.front();
174
0
    auto &name = ir.get_name(ir.default_entry_point);
175
0
    ir.entry_points.insert(std::make_pair(ir.default_entry_point,
176
0
                                          SPIREntryPoint(ir.default_entry_point, ExecutionModelGLCompute, name)));
177
0
  }
178
302
}
179
180
const uint32_t *Parser::stream(const Instruction &instr) const
181
791k
{
182
  // If we're not going to use any arguments, just return nullptr.
183
  // We want to avoid case where we return an out of range pointer
184
  // that trips debug assertions on some platforms.
185
791k
  if (!instr.length)
186
61.6k
    return nullptr;
187
188
729k
  if (instr.offset + instr.length > ir.spirv.size())
189
0
    SPIRV_CROSS_THROW("Compiler::stream() out of range.");
190
729k
  return &ir.spirv[instr.offset];
191
729k
}
192
193
static string extract_string(const std::vector<uint32_t> &spirv, uint32_t offset)
194
64.1k
{
195
64.1k
  return extract_string(spirv.data() + offset, spirv.size() - offset);
196
64.1k
}
197
198
void Parser::parse(const Instruction &instruction)
199
791k
{
200
791k
  auto *ops = stream(instruction);
201
791k
  auto op = static_cast<Op>(instruction.op);
202
791k
  uint32_t length = instruction.length;
203
204
  // HACK for glslang that might emit OpEmitMeshTasksEXT followed by return / branch.
205
  // Instead of failing hard, just ignore it.
206
791k
  if (ignore_trailing_block_opcodes)
207
0
  {
208
0
    ignore_trailing_block_opcodes = false;
209
0
    if (op == OpReturn || op == OpBranch || op == OpUnreachable)
210
0
      return;
211
0
  }
212
213
791k
  switch (op)
214
791k
  {
215
186
  case OpSourceExtension:
216
1.64k
  case OpNop:
217
1.64k
  case OpModuleProcessed:
218
1.64k
    break;
219
220
359
  case OpString:
221
359
  {
222
359
    set<SPIRString>(ops[0], extract_string(ir.spirv, instruction.offset + 1));
223
359
    break;
224
1.64k
  }
225
226
451
  case OpMemoryModel:
227
451
    ir.addressing_model = static_cast<AddressingModel>(ops[0]);
228
451
    ir.memory_model = static_cast<MemoryModel>(ops[1]);
229
451
    break;
230
231
121k
  case OpSource:
232
121k
  {
233
121k
    ir.sources.emplace_back();
234
121k
    auto &source = ir.sources.back();
235
121k
    source.lang = static_cast<SourceLanguage>(ops[0]);
236
237
121k
    switch (source.lang)
238
121k
    {
239
85.1k
    case SourceLanguageESSL:
240
85.1k
      source.es = true;
241
85.1k
      source.version = ops[1];
242
85.1k
      source.known = true;
243
85.1k
      source.hlsl = false;
244
85.1k
      break;
245
246
309
    case SourceLanguageGLSL:
247
309
      source.es = false;
248
309
      source.version = ops[1];
249
309
      source.known = true;
250
309
      source.hlsl = false;
251
309
      break;
252
253
255
    case SourceLanguageHLSL:
254
      // For purposes of cross-compiling, this is GLSL 450.
255
255
      source.es = false;
256
255
      source.version = 450;
257
255
      source.known = true;
258
255
      source.hlsl = true;
259
255
      break;
260
261
36.1k
    default:
262
36.1k
      source.known = false;
263
36.1k
      break;
264
121k
    }
265
266
121k
    if (length >= 3)
267
1.21k
      source.file_id = ops[2];
268
269
121k
    if (length >= 4)
270
1.21k
      source.source = extract_string(ir.spirv, instruction.offset + 3);
271
272
121k
    break;
273
121k
  }
274
275
233
  case OpSourceContinued:
276
233
    if (!ir.sources.empty())
277
230
      ir.sources.back().source += extract_string(ir.spirv, instruction.offset);
278
233
    break;
279
280
32
  case OpUndef:
281
32
  {
282
32
    uint32_t result_type = ops[0];
283
32
    uint32_t id = ops[1];
284
32
    set<SPIRUndef>(id, result_type);
285
32
    if (current_block)
286
1
      current_block->ops.push_back(instruction);
287
32
    break;
288
121k
  }
289
290
22.1k
  case OpCapability:
291
22.1k
  {
292
22.1k
    uint32_t cap = ops[0];
293
22.1k
    if (cap == CapabilityKernel)
294
0
      SPIRV_CROSS_THROW("Kernel capability not supported.");
295
296
22.1k
    ir.declared_capabilities.push_back(static_cast<Capability>(ops[0]));
297
22.1k
    break;
298
22.1k
  }
299
300
57.6k
  case OpExtension:
301
57.6k
  {
302
57.6k
    auto ext = extract_string(ir.spirv, instruction.offset);
303
57.6k
    ir.declared_extensions.push_back(std::move(ext));
304
57.6k
    break;
305
22.1k
  }
306
307
354
  case OpExtInstImport:
308
354
  {
309
354
    uint32_t id = ops[0];
310
311
354
    SPIRExtension::Extension spirv_ext = SPIRExtension::Unsupported;
312
313
354
    auto ext = extract_string(ir.spirv, instruction.offset + 1);
314
354
    if (ext == "GLSL.std.450")
315
187
      spirv_ext = SPIRExtension::GLSL;
316
167
    else if (ext == "DebugInfo")
317
0
      spirv_ext = SPIRExtension::SPV_debug_info;
318
167
    else if (ext == "SPV_AMD_shader_ballot")
319
0
      spirv_ext = SPIRExtension::SPV_AMD_shader_ballot;
320
167
    else if (ext == "SPV_AMD_shader_explicit_vertex_parameter")
321
0
      spirv_ext = SPIRExtension::SPV_AMD_shader_explicit_vertex_parameter;
322
167
    else if (ext == "SPV_AMD_shader_trinary_minmax")
323
0
      spirv_ext = SPIRExtension::SPV_AMD_shader_trinary_minmax;
324
167
    else if (ext == "SPV_AMD_gcn_shader")
325
0
      spirv_ext = SPIRExtension::SPV_AMD_gcn_shader;
326
167
    else if (ext == "NonSemantic.DebugPrintf")
327
0
      spirv_ext = SPIRExtension::NonSemanticDebugPrintf;
328
167
    else if (ext.find("NonSemantic.Shader.DebugInfo.") == 0)
329
8
      spirv_ext = SPIRExtension::NonSemanticShaderDebugInfo;
330
159
    else if (ext.find("NonSemantic.") == 0)
331
0
      spirv_ext = SPIRExtension::NonSemanticGeneric;
332
333
354
    set<SPIRExtension>(id, spirv_ext);
334
    // Other SPIR-V extensions which have ExtInstrs are currently not supported.
335
336
354
    break;
337
22.1k
  }
338
339
1.57k
  case OpExtInst:
340
1.57k
  case OpExtInstWithForwardRefsKHR:
341
1.57k
  {
342
    // The SPIR-V debug information extended instructions might come at global scope.
343
1.57k
    if (current_block)
344
903
    {
345
903
      current_block->ops.push_back(instruction);
346
903
      if (length >= 2)
347
903
      {
348
903
        const auto *type = maybe_get<SPIRType>(ops[0]);
349
903
        if (type)
350
611
          ir.load_type_width.insert({ ops[1], type->width });
351
903
      }
352
903
    }
353
354
1.57k
    if (op == OpExtInst && length > 4)
355
1.47k
    {
356
      // Don't want to deal with ForwardRefs here.
357
1.47k
      auto &ext = get<SPIRExtension>(ops[2]);
358
1.47k
      if (ext.ext == SPIRExtension::NonSemanticShaderDebugInfo)
359
736
      {
360
736
        const auto instr = ops[3];
361
736
        if (instr == NonSemanticShaderDebugInfo100DebugSource)
362
95
        {
363
95
          set<SPIRString>(ops[1], get<SPIRString>(ops[4]).str);
364
365
95
          ir.sources.emplace_back();
366
95
          auto &source = ir.sources.back();
367
95
          source.file_id = ops[4];
368
95
          source.define_id = ops[1];
369
95
          if (length >= 6)
370
95
            source.source = ir.get<SPIRString>(ops[5]).str;
371
95
        }
372
641
        else if (instr == NonSemanticShaderDebugInfo100DebugSourceContinued)
373
0
        {
374
0
          if (length < 5)
375
0
            SPIRV_CROSS_THROW("Invalid arguments for ShaderDebugInfo100DebugSourceContinued");
376
0
          if (!ir.sources.empty())
377
0
            ir.sources.back().source += ir.get<SPIRString>(ops[4]).str;
378
0
        }
379
641
        else if (instr == NonSemanticShaderDebugInfo100DebugLine)
380
120
        {
381
120
          if (length < 9)
382
0
            SPIRV_CROSS_THROW("Invalid arguments for ShaderDebugInfo100DebugLine");
383
120
          auto source_id = ops[4];
384
120
          auto line_start = ir.get<SPIRConstant>(ops[5]).scalar_i32();
385
120
          auto col_start = ir.get<SPIRConstant>(ops[7]).scalar_i32();
386
387
120
          for (auto &source : ir.sources)
388
360
          {
389
360
            if (source.define_id != source_id)
390
242
              continue;
391
392
118
            source.line_markers.emplace_back();
393
118
            auto &marker = source.line_markers.back();
394
118
            marker.line = line_start;
395
118
            marker.col = col_start;
396
118
            marker.offset = instruction.offset - 1;
397
118
            marker.function_id = current_function ? current_function->self : ID(0);
398
118
            marker.block_id = current_block ? current_block->self : ID(0);
399
118
            break;
400
360
          }
401
120
        }
402
521
        else if (instr == NonSemanticShaderDebugInfo100DebugLocalVariable)
403
148
        {
404
148
          if (length < 11)
405
0
            SPIRV_CROSS_THROW("Invalid arguments for ShaderDebugInfo100DebugLocalVariable");
406
148
          auto &lvar = set<SPIRDebugLocalVariable>(ops[1]);
407
148
          lvar.name_id = ops[4];
408
148
        }
409
373
        else if (instr == NonSemanticShaderDebugInfo100DebugDeclare)
410
43
        {
411
43
          if (length < 7)
412
0
            SPIRV_CROSS_THROW("Invalid arguments for ShaderDebugInfo100DebugDeclare");
413
43
          auto &lvar = get<SPIRDebugLocalVariable>(ops[4]);
414
43
          auto &var = get<SPIRVariable>(ops[5]);
415
43
          var.debug_local_variables.push_back(lvar.self);
416
43
        }
417
736
      }
418
1.47k
    }
419
1.57k
    break;
420
1.57k
  }
421
422
1.57k
  case OpEntryPoint:
423
304
  {
424
304
    auto itr =
425
304
        ir.entry_points.insert(make_pair(ops[1], SPIREntryPoint(ops[1], static_cast<ExecutionModel>(ops[0]),
426
304
                                                                extract_string(ir.spirv, instruction.offset + 2))));
427
304
    auto &e = itr.first->second;
428
429
    // Strings need nul-terminator and consume the whole word.
430
304
    uint32_t strlen_words = uint32_t((e.name.size() + 1 + 3) >> 2);
431
432
1.21k
    for (uint32_t i = strlen_words + 2; i < instruction.length; i++)
433
909
      e.interface_variables.push_back(ops[i]);
434
435
    // Set the name of the entry point in case OpName is not provided later.
436
304
    ir.set_name(ops[1], e.name);
437
438
    // If we don't have an entry, make the first one our "default".
439
304
    if (!ir.default_entry_point)
440
238
      ir.default_entry_point = ops[1];
441
304
    break;
442
1.57k
  }
443
444
1.34k
  case OpExecutionMode:
445
1.34k
  {
446
1.34k
    auto &execution = ir.entry_points[ops[0]];
447
1.34k
    auto mode = static_cast<ExecutionMode>(ops[1]);
448
1.34k
    execution.flags.set(mode);
449
450
1.34k
    switch (mode)
451
1.34k
    {
452
0
    case ExecutionModeInvocations:
453
0
      execution.invocations = ops[2];
454
0
      break;
455
456
147
    case ExecutionModeLocalSize:
457
147
      execution.workgroup_size.x = ops[2];
458
147
      execution.workgroup_size.y = ops[3];
459
147
      execution.workgroup_size.z = ops[4];
460
147
      break;
461
462
0
    case ExecutionModeOutputVertices:
463
0
      execution.output_vertices = ops[2];
464
0
      break;
465
466
0
    case ExecutionModeOutputPrimitivesEXT:
467
0
      execution.output_primitives = ops[2];
468
0
      break;
469
470
0
    case ExecutionModeSignedZeroInfNanPreserve:
471
0
      switch (ops[2])
472
0
      {
473
0
      case 8:
474
0
        execution.signed_zero_inf_nan_preserve_8 = true;
475
0
        break;
476
477
0
      case 16:
478
0
        execution.signed_zero_inf_nan_preserve_16 = true;
479
0
        break;
480
481
0
      case 32:
482
0
        execution.signed_zero_inf_nan_preserve_32 = true;
483
0
        break;
484
485
0
      case 64:
486
0
        execution.signed_zero_inf_nan_preserve_64 = true;
487
0
        break;
488
489
0
      default:
490
0
        SPIRV_CROSS_THROW("Invalid bit-width for SignedZeroInfNanPreserve.");
491
0
      }
492
0
      break;
493
494
1.19k
    default:
495
1.19k
      break;
496
1.34k
    }
497
1.34k
    break;
498
1.34k
  }
499
500
1.34k
  case OpExecutionModeId:
501
0
  {
502
0
    auto &execution = ir.entry_points[ops[0]];
503
0
    auto mode = static_cast<ExecutionMode>(ops[1]);
504
0
    execution.flags.set(mode);
505
506
0
    switch (mode)
507
0
    {
508
0
    case ExecutionModeLocalSizeId:
509
0
      execution.workgroup_size.id_x = ops[2];
510
0
      execution.workgroup_size.id_y = ops[3];
511
0
      execution.workgroup_size.id_z = ops[4];
512
0
      break;
513
514
0
    case ExecutionModeFPFastMathDefault:
515
0
      execution.fp_fast_math_defaults[ops[2]] = ops[3];
516
0
      break;
517
518
0
    default:
519
0
      break;
520
0
    }
521
0
    break;
522
0
  }
523
524
3.04k
  case OpName:
525
3.04k
  {
526
3.04k
    uint32_t id = ops[0];
527
3.04k
    ir.set_name(id, extract_string(ir.spirv, instruction.offset + 1));
528
3.04k
    break;
529
0
  }
530
531
962
  case OpMemberName:
532
962
  {
533
962
    uint32_t id = ops[0];
534
962
    uint32_t member = ops[1];
535
962
    ir.set_member_name(id, member, extract_string(ir.spirv, instruction.offset + 2));
536
962
    break;
537
0
  }
538
539
10
  case OpDecorationGroup:
540
10
  {
541
    // Noop, this simply means an ID should be a collector of decorations.
542
    // The meta array is already a flat array of decorations which will contain the relevant decorations.
543
10
    break;
544
0
  }
545
546
278k
  case OpGroupDecorate:
547
278k
  {
548
278k
    uint32_t group_id = ops[0];
549
278k
    auto &decorations = ir.meta[group_id].decoration;
550
278k
    auto &flags = decorations.decoration_flags;
551
552
    // Copies decorations from one ID to another. Only copy decorations which are set in the group,
553
    // i.e., we cannot just copy the meta structure directly.
554
278k
    for (uint32_t i = 1; i < length; i++)
555
4
    {
556
4
      uint32_t target = ops[i];
557
4
      flags.for_each_bit([&](uint32_t bit) {
558
4
        auto decoration = static_cast<Decoration>(bit);
559
560
4
        if (decoration_is_string(decoration))
561
0
        {
562
0
          ir.set_decoration_string(target, decoration, ir.get_decoration_string(group_id, decoration));
563
0
        }
564
4
        else
565
4
        {
566
4
          ir.meta[target].decoration_word_offset[decoration] =
567
4
              ir.meta[group_id].decoration_word_offset[decoration];
568
4
          ir.set_decoration(target, decoration, ir.get_decoration(group_id, decoration));
569
4
        }
570
4
      });
571
4
    }
572
278k
    break;
573
0
  }
574
575
12.7k
  case OpGroupMemberDecorate:
576
12.7k
  {
577
12.7k
    uint32_t group_id = ops[0];
578
12.7k
    auto &flags = ir.meta[group_id].decoration.decoration_flags;
579
580
    // Copies decorations from one ID to another. Only copy decorations which are set in the group,
581
    // i.e., we cannot just copy the meta structure directly.
582
13.1k
    for (uint32_t i = 1; i + 1 < length; i += 2)
583
404
    {
584
404
      uint32_t target = ops[i + 0];
585
404
      uint32_t index = ops[i + 1];
586
3.61k
      flags.for_each_bit([&](uint32_t bit) {
587
3.61k
        auto decoration = static_cast<Decoration>(bit);
588
589
3.61k
        if (decoration_is_string(decoration))
590
0
          ir.set_member_decoration_string(target, index, decoration,
591
0
                                          ir.get_decoration_string(group_id, decoration));
592
3.61k
        else
593
3.61k
          ir.set_member_decoration(target, index, decoration, ir.get_decoration(group_id, decoration));
594
3.61k
      });
595
404
    }
596
12.7k
    break;
597
0
  }
598
599
3.55k
  case OpDecorate:
600
3.55k
  case OpDecorateId:
601
3.55k
  {
602
    // OpDecorateId technically supports an array of arguments, but our only supported decorations are single uint,
603
    // so merge decorate and decorate-id here.
604
3.55k
    uint32_t id = ops[0];
605
606
3.55k
    auto decoration = static_cast<Decoration>(ops[1]);
607
3.55k
    if (length >= 3)
608
1.03k
    {
609
1.03k
      ir.meta[id].decoration_word_offset[decoration] = uint32_t(&ops[2] - ir.spirv.data());
610
1.03k
      ir.set_decoration(id, decoration, ops[2]);
611
1.03k
    }
612
2.51k
    else
613
2.51k
      ir.set_decoration(id, decoration);
614
615
    // Track exported functions so we can compile library modules that have no OpEntryPoint.
616
    // LinkageAttributes layout: literal-string (variable words) followed by LinkageType.
617
3.55k
    if (decoration == DecorationLinkageAttributes && length >= 4 &&
618
0
        static_cast<LinkageType>(ops[length - 1]) == LinkageTypeExport)
619
0
    {
620
0
      ir.library_exports.push_back(id);
621
622
      // If OpName was stripped (e.g. by spirv-opt --strip-debug), fall back
623
      // to the linkage name so the emitted function keeps its export name.
624
0
      if (ir.get_name(id).empty())
625
0
        ir.set_name(id, extract_string(ir.spirv, instruction.offset + 2));
626
0
    }
627
628
3.55k
    break;
629
3.55k
  }
630
631
0
  case OpDecorateStringGOOGLE:
632
0
  {
633
0
    uint32_t id = ops[0];
634
0
    auto decoration = static_cast<Decoration>(ops[1]);
635
0
    ir.set_decoration_string(id, decoration, extract_string(ir.spirv, instruction.offset + 2));
636
0
    break;
637
3.55k
  }
638
639
72
  case OpMemberDecorate:
640
72
  case OpMemberDecorateIdEXT:
641
72
  {
642
72
    uint32_t id = ops[0];
643
72
    uint32_t member = ops[1];
644
72
    auto decoration = static_cast<Decoration>(ops[2]);
645
72
    if (length >= 4)
646
66
      ir.set_member_decoration(id, member, decoration, ops[3]);
647
6
    else
648
6
      ir.set_member_decoration(id, member, decoration);
649
72
    break;
650
72
  }
651
652
0
  case OpMemberDecorateStringGOOGLE:
653
0
  {
654
0
    uint32_t id = ops[0];
655
0
    uint32_t member = ops[1];
656
0
    auto decoration = static_cast<Decoration>(ops[2]);
657
0
    ir.set_member_decoration_string(id, member, decoration, extract_string(ir.spirv, instruction.offset + 3));
658
0
    break;
659
72
  }
660
661
  // Build up basic types.
662
210
  case OpTypeVoid:
663
210
  {
664
210
    uint32_t id = ops[0];
665
210
    auto &type = set<SPIRType>(id, op);
666
210
    type.basetype = SPIRType::Void;
667
210
    break;
668
72
  }
669
670
61
  case OpTypeBool:
671
61
  {
672
61
    uint32_t id = ops[0];
673
61
    auto &type = set<SPIRType>(id, op);
674
61
    type.basetype = SPIRType::Boolean;
675
61
    type.width = 1;
676
61
    break;
677
72
  }
678
679
270
  case OpTypeFloat:
680
270
  {
681
270
    uint32_t id = ops[0];
682
270
    uint32_t width = ops[1];
683
270
    auto &type = set<SPIRType>(id, op);
684
685
270
    if (width != 16 && width != 8 && length > 2)
686
0
      SPIRV_CROSS_THROW("Unrecognized FP encoding mode for OpTypeFloat.");
687
688
270
    if (width == 64)
689
0
      type.basetype = SPIRType::Double;
690
270
    else if (width == 32)
691
267
      type.basetype = SPIRType::Float;
692
3
    else if (width == 16)
693
0
    {
694
0
      if (length > 2)
695
0
      {
696
0
        if (ops[2] == FPEncodingBFloat16KHR)
697
0
          type.basetype = SPIRType::BFloat16;
698
0
        else
699
0
          SPIRV_CROSS_THROW("Unrecognized encoding for OpTypeFloat 16.");
700
0
      }
701
0
      else
702
0
        type.basetype = SPIRType::Half;
703
0
    }
704
3
    else if (width == 8)
705
0
    {
706
0
      if (length < 3)
707
0
        SPIRV_CROSS_THROW("Missing encoding for OpTypeFloat 8.");
708
0
      else if (ops[2] == FPEncodingFloat8E4M3EXT)
709
0
        type.basetype = SPIRType::FloatE4M3;
710
0
      else if (ops[2] == FPEncodingFloat8E5M2EXT)
711
0
        type.basetype = SPIRType::FloatE5M2;
712
0
      else
713
0
        SPIRV_CROSS_THROW("Invalid encoding for OpTypeFloat 8.");
714
0
    }
715
3
    else
716
3
      SPIRV_CROSS_THROW("Unrecognized bit-width of floating point type.");
717
267
    type.width = width;
718
267
    break;
719
270
  }
720
721
516
  case OpTypeInt:
722
516
  {
723
516
    uint32_t id = ops[0];
724
516
    uint32_t width = ops[1];
725
516
    bool signedness = ops[2] != 0;
726
516
    auto &type = set<SPIRType>(id, op);
727
516
    type.basetype = signedness ? to_signed_basetype(width) : to_unsigned_basetype(width);
728
516
    type.width = width;
729
516
    break;
730
270
  }
731
732
  // Build composite types by "inheriting".
733
  // NOTE: The self member is also copied! For pointers and array modifiers this is a good thing
734
  // since we can refer to decorations on pointee classes which is needed for UBO/SSBO, I/O blocks in geometry/tess etc.
735
162
  case OpTypeVector:
736
162
  {
737
162
    uint32_t id = ops[0];
738
162
    uint32_t vecsize = ops[2];
739
740
162
    auto &base = get<SPIRType>(ops[1]);
741
162
    auto &vecbase = set<SPIRType>(id, base);
742
743
162
    vecbase.op = op;
744
162
    vecbase.vecsize = vecsize;
745
162
    vecbase.self = id;
746
162
    vecbase.parent_type = ops[1];
747
162
    break;
748
270
  }
749
750
124
  case OpTypeMatrix:
751
124
  {
752
124
    uint32_t id = ops[0];
753
124
    uint32_t colcount = ops[2];
754
755
124
    auto &base = get<SPIRType>(ops[1]);
756
124
    auto &matrixbase = set<SPIRType>(id, base);
757
758
124
    matrixbase.op = op;
759
124
    matrixbase.columns = colcount;
760
124
    matrixbase.self = id;
761
124
    matrixbase.parent_type = ops[1];
762
124
    break;
763
270
  }
764
765
0
  case OpTypeCooperativeMatrixKHR:
766
0
  {
767
0
    uint32_t id = ops[0];
768
0
    auto &base = get<SPIRType>(ops[1]);
769
0
    auto &matrixbase = set<SPIRType>(id, base);
770
771
0
    matrixbase.op = op;
772
0
    matrixbase.ext.cooperative.scope_id = ops[2];
773
0
    matrixbase.ext.cooperative.rows_id = ops[3];
774
0
    matrixbase.ext.cooperative.columns_id = ops[4];
775
0
    matrixbase.ext.cooperative.use_id = ops[5];
776
0
    matrixbase.self = id;
777
0
    matrixbase.parent_type = ops[1];
778
0
    break;
779
270
  }
780
781
0
  case OpTypeCooperativeVectorNV:
782
0
  {
783
0
    uint32_t id = ops[0];
784
0
    auto &type = set<SPIRType>(id, op);
785
786
0
    type.basetype = SPIRType::CoopVecNV;
787
0
    type.op = op;
788
0
    type.ext.coopVecNV.component_type_id = ops[1];
789
0
    type.ext.coopVecNV.component_count_id = ops[2];
790
0
    type.parent_type = ops[1];
791
792
    // CoopVec-Nv can be used with integer operations like SMax where
793
    // where spirv-opt does explicit checks on integer bitwidth
794
0
    auto component_type = get<SPIRType>(type.ext.coopVecNV.component_type_id);
795
0
    type.width = component_type.width;
796
0
    break;
797
270
  }
798
799
55
  case OpTypeArray:
800
55
  {
801
55
    uint32_t id = ops[0];
802
55
    uint32_t tid = ops[1];
803
55
    auto &base = get<SPIRType>(tid);
804
55
    auto &arraybase = set<SPIRType>(id, base);
805
806
55
    arraybase.op = op;
807
55
    arraybase.parent_type = tid;
808
809
55
    uint32_t cid = ops[2];
810
55
    ir.mark_used_as_array_length(cid);
811
55
    auto *c = maybe_get<SPIRConstant>(cid);
812
55
    bool literal = c && !c->specialization;
813
814
    // We're copying type information into Array types, so we'll need a fixup for any physical pointer
815
    // references.
816
55
    if (base.forward_pointer)
817
0
      forward_pointer_fixups.push_back({ id, tid });
818
819
55
    arraybase.array_size_literal.push_back(literal);
820
55
    arraybase.array.push_back(literal ? c->scalar() : cid);
821
822
    // .self resolves down to non-array/non-pointer type.
823
55
    arraybase.self = base.self;
824
55
    break;
825
270
  }
826
827
5.10k
  case OpTypeRuntimeArray:
828
5.10k
  {
829
5.10k
    uint32_t id = ops[0];
830
831
5.10k
    auto &base = get<SPIRType>(ops[1]);
832
5.10k
    auto &arraybase = set<SPIRType>(id, base);
833
834
    // We're copying type information into Array types, so we'll need a fixup for any physical pointer
835
    // references.
836
5.10k
    if (base.forward_pointer)
837
5.07k
      forward_pointer_fixups.push_back({ id, ops[1] });
838
839
5.10k
    arraybase.op = op;
840
5.10k
    arraybase.array.push_back(0);
841
5.10k
    arraybase.array_size_literal.push_back(true);
842
5.10k
    arraybase.parent_type = ops[1];
843
844
    // .self resolves down to non-array/non-pointer type.
845
5.10k
    arraybase.self = base.self;
846
5.10k
    break;
847
270
  }
848
849
132
  case OpTypeImage:
850
132
  {
851
132
    uint32_t id = ops[0];
852
132
    auto &type = set<SPIRType>(id, op);
853
132
    type.basetype = SPIRType::Image;
854
132
    type.image.type = ops[1];
855
132
    type.image.dim = static_cast<Dim>(ops[2]);
856
132
    type.image.depth = ops[3] == 1;
857
132
    type.image.arrayed = ops[4] != 0;
858
132
    type.image.ms = ops[5] != 0;
859
132
    type.image.sampled = ops[6];
860
132
    type.image.format = static_cast<ImageFormat>(ops[7]);
861
132
    type.image.access = (length >= 9) ? static_cast<AccessQualifier>(ops[8]) : AccessQualifierMax;
862
132
    break;
863
270
  }
864
865
1
  case OpTypeSampledImage:
866
1
  {
867
1
    uint32_t id = ops[0];
868
1
    uint32_t imagetype = ops[1];
869
1
    auto &type = set<SPIRType>(id, op);
870
1
    type = get<SPIRType>(imagetype);
871
1
    type.basetype = SPIRType::SampledImage;
872
1
    type.self = id;
873
1
    break;
874
270
  }
875
876
0
  case OpTypeSampler:
877
0
  {
878
0
    uint32_t id = ops[0];
879
0
    auto &type = set<SPIRType>(id, op);
880
0
    type.basetype = SPIRType::Sampler;
881
0
    break;
882
270
  }
883
884
0
  case OpTypeUntypedPointerKHR:
885
1.55k
  case OpTypePointer:
886
1.55k
  {
887
1.55k
    uint32_t id = ops[0];
888
889
    // Very rarely, we might receive a FunctionPrototype here.
890
    // We won't be able to compile it, but we shouldn't crash when parsing.
891
    // We should be able to reflect.
892
1.55k
    auto *base = op == OpTypePointer ? maybe_get<SPIRType>(ops[2]) : nullptr;
893
1.55k
    auto &ptrbase = set<SPIRType>(id, op);
894
895
1.55k
    if (base)
896
1.53k
    {
897
1.53k
      ptrbase = *base;
898
1.53k
      ptrbase.op = op;
899
1.53k
    }
900
901
1.55k
    ptrbase.pointer = true;
902
1.55k
    ptrbase.pointer_depth++;
903
1.55k
    ptrbase.storage = static_cast<StorageClass>(ops[1]);
904
905
1.55k
    if (ptrbase.storage == StorageClassAtomicCounter)
906
1
      ptrbase.basetype = SPIRType::AtomicCounter;
907
908
1.55k
    if (base && base->forward_pointer)
909
246
      forward_pointer_fixups.push_back({ id, ops[2] });
910
911
1.55k
    if (op == OpTypePointer)
912
1.55k
      ptrbase.parent_type = ops[2];
913
0
    else
914
0
      ptrbase.basetype = SPIRType::Void;
915
916
    // Do NOT set ptrbase.self!
917
1.55k
    break;
918
0
  }
919
920
7
  case OpTypeForwardPointer:
921
7
  {
922
7
    uint32_t id = ops[0];
923
7
    auto &ptrbase = set<SPIRType>(id, op);
924
7
    ptrbase.pointer = true;
925
7
    ptrbase.pointer_depth++;
926
7
    ptrbase.storage = static_cast<StorageClass>(ops[1]);
927
7
    ptrbase.forward_pointer = true;
928
929
7
    if (ptrbase.storage == StorageClassAtomicCounter)
930
0
      ptrbase.basetype = SPIRType::AtomicCounter;
931
932
7
    break;
933
0
  }
934
935
314
  case OpTypeStruct:
936
314
  {
937
314
    uint32_t id = ops[0];
938
314
    auto &type = set<SPIRType>(id, op);
939
314
    type.basetype = SPIRType::Struct;
940
684
    for (uint32_t i = 1; i < length; i++)
941
370
      type.member_types.push_back(ops[i]);
942
943
    // Check if we have seen this struct type before, with just different
944
    // decorations.
945
    //
946
    // Add workaround for issue #17 as well by looking at OpName for the struct
947
    // types, which we shouldn't normally do.
948
    // We should not normally have to consider type aliases like this to begin with
949
    // however ... glslang issues #304, #307 cover this.
950
951
    // For stripped names, never consider struct type aliasing.
952
    // We risk declaring the same struct multiple times, but type-punning is not allowed
953
    // so this is safe.
954
314
    bool consider_aliasing = !ir.get_name(type.self).empty();
955
314
    if (consider_aliasing)
956
195
    {
957
195
      for (auto &other : global_struct_cache)
958
267
      {
959
267
        if (ir.get_name(type.self) == ir.get_name(other) &&
960
122
            types_are_logically_equivalent(type, get<SPIRType>(other)))
961
121
        {
962
121
          type.type_alias = other;
963
121
          break;
964
121
        }
965
267
      }
966
967
195
      if (type.type_alias == TypeID(0))
968
74
        global_struct_cache.push_back(id);
969
195
    }
970
314
    break;
971
0
  }
972
973
462
  case OpTypeFunction:
974
462
  {
975
462
    uint32_t id = ops[0];
976
462
    uint32_t ret = ops[1];
977
978
462
    auto &func = set<SPIRFunctionPrototype>(id, ret);
979
833
    for (uint32_t i = 2; i < length; i++)
980
371
      func.parameter_types.push_back(ops[i]);
981
462
    break;
982
0
  }
983
984
0
  case OpTypeAccelerationStructureKHR:
985
0
  {
986
0
    uint32_t id = ops[0];
987
0
    auto &type = set<SPIRType>(id, op);
988
0
    type.basetype = SPIRType::AccelerationStructure;
989
0
    break;
990
0
  }
991
992
0
  case OpTypeRayQueryKHR:
993
0
  {
994
0
    uint32_t id = ops[0];
995
0
    auto &type = set<SPIRType>(id, op);
996
0
    type.basetype = SPIRType::RayQuery;
997
0
    break;
998
0
  }
999
1000
0
  case OpTypeTensorARM:
1001
0
  {
1002
0
    uint32_t id = ops[0];
1003
0
    auto &type = set<SPIRType>(id, op);
1004
0
    type.basetype = SPIRType::Tensor;
1005
0
    type.ext.tensor = {};
1006
0
    type.ext.tensor.type = ops[1];
1007
0
    if (length >= 3)
1008
0
      type.ext.tensor.rank = ops[2];
1009
0
    if (length >= 4)
1010
0
      type.ext.tensor.shape = ops[3];
1011
0
    break;
1012
0
  }
1013
1014
  // Variable declaration
1015
  // All variables are essentially pointers with a storage qualifier.
1016
3.60k
  case OpVariable:
1017
3.60k
  {
1018
3.60k
    uint32_t type = ops[0];
1019
3.60k
    uint32_t id = ops[1];
1020
3.60k
    auto storage = static_cast<StorageClass>(ops[2]);
1021
3.60k
    uint32_t initializer = length == 4 ? ops[3] : 0;
1022
1023
3.60k
    if (storage == StorageClassFunction)
1024
3.32k
    {
1025
3.32k
      if (!current_function)
1026
0
        SPIRV_CROSS_THROW("No function currently in scope");
1027
3.32k
      current_function->add_local_variable(id);
1028
3.32k
    }
1029
1030
3.60k
    set<SPIRVariable>(id, type, storage, initializer);
1031
3.60k
    break;
1032
3.60k
  }
1033
1034
0
  case OpUntypedVariableKHR:
1035
0
  {
1036
0
    uint32_t type = ops[0];
1037
0
    uint32_t id = ops[1];
1038
0
    auto storage = static_cast<StorageClass>(ops[2]);
1039
0
    uint32_t data_type = length >= 4 ? ops[3] : 0;
1040
0
    uint32_t initializer = length >= 5 ? ops[4] : 0;
1041
1042
0
    if (storage == StorageClassFunction)
1043
0
    {
1044
0
      if (!current_function)
1045
0
        SPIRV_CROSS_THROW("No function currently in scope");
1046
0
      current_function->add_local_variable(id);
1047
0
    }
1048
1049
0
    auto &v = set<SPIRVariable>(id, type, storage, initializer);
1050
0
    v.untyped = true;
1051
0
    v.untyped_alloca_type = data_type;
1052
0
    break;
1053
0
  }
1054
1055
  // OpPhi
1056
  // OpPhi is a fairly magical opcode.
1057
  // It selects temporary variables based on which parent block we *came from*.
1058
  // In high-level languages we can "de-SSA" by creating a function local, and flush out temporaries to this function-local
1059
  // variable to emulate SSA Phi.
1060
1.26k
  case OpPhi:
1061
1.26k
  {
1062
1.26k
    if (!current_function)
1063
0
      SPIRV_CROSS_THROW("No function currently in scope");
1064
1.26k
    if (!current_block)
1065
0
      SPIRV_CROSS_THROW("No block currently in scope");
1066
1067
1.26k
    uint32_t result_type = ops[0];
1068
1.26k
    uint32_t id = ops[1];
1069
1070
    // Instead of a temporary, create a new function-wide temporary with this ID instead.
1071
1.26k
    auto &var = set<SPIRVariable>(id, result_type, StorageClassFunction);
1072
1.26k
    var.phi_variable = true;
1073
1074
1.26k
    current_function->add_local_variable(id);
1075
1076
4.04k
    for (uint32_t i = 2; i + 2 <= length; i += 2)
1077
2.77k
      current_block->phi_variables.push_back({ ops[i], ops[i + 1], id });
1078
1.26k
    break;
1079
1.26k
  }
1080
1081
  // Constants
1082
21
  case OpSpecConstant:
1083
2.37k
  case OpConstant:
1084
2.39k
  case OpConstantCompositeReplicateEXT:
1085
2.39k
  case OpSpecConstantCompositeReplicateEXT:
1086
2.39k
  {
1087
2.39k
    uint32_t id = ops[1];
1088
2.39k
    auto &type = get<SPIRType>(ops[0]);
1089
2.39k
    if (op == OpConstantCompositeReplicateEXT || op == OpSpecConstantCompositeReplicateEXT)
1090
13
    {
1091
13
      auto subconstant = uint32_t(ops[2]);
1092
13
      set<SPIRConstant>(id, ops[0], &subconstant, 1, op == OpSpecConstantCompositeReplicateEXT, true);
1093
13
    }
1094
2.37k
    else
1095
2.37k
    {
1096
1097
2.37k
      if (type.width > 32)
1098
8
        set<SPIRConstant>(id, ops[0], ops[2] | (uint64_t(ops[3]) << 32), op == OpSpecConstant);
1099
2.37k
      else
1100
2.37k
        set<SPIRConstant>(id, ops[0], ops[2], op == OpSpecConstant);
1101
2.37k
    }
1102
2.39k
    break;
1103
2.39k
  }
1104
1105
1.17k
  case OpSpecConstantFalse:
1106
1.19k
  case OpConstantFalse:
1107
1.19k
  {
1108
1.19k
    uint32_t id = ops[1];
1109
1.19k
    set<SPIRConstant>(id, ops[0], uint32_t(0), op == OpSpecConstantFalse);
1110
1.19k
    break;
1111
1.17k
  }
1112
1113
476
  case OpSpecConstantTrue:
1114
488
  case OpConstantTrue:
1115
488
  {
1116
488
    uint32_t id = ops[1];
1117
488
    set<SPIRConstant>(id, ops[0], uint32_t(1), op == OpSpecConstantTrue);
1118
488
    break;
1119
476
  }
1120
1121
49.2k
  case OpConstantNull:
1122
49.2k
  {
1123
49.2k
    uint32_t id = ops[1];
1124
49.2k
    uint32_t type = ops[0];
1125
49.2k
    ir.make_constant_null(id, type, true);
1126
49.2k
    break;
1127
476
  }
1128
1129
604
  case OpSpecConstantComposite:
1130
896
  case OpConstantComposite:
1131
896
  {
1132
896
    uint32_t id = ops[1];
1133
896
    uint32_t type = ops[0];
1134
1135
896
    auto &ctype = get<SPIRType>(type);
1136
896
    uint32_t elements = length - 2;
1137
1138
    // We can have constants which are structs and arrays.
1139
    // In this case, our SPIRConstant will be a list of other SPIRConstant ids which we
1140
    // can refer to.
1141
896
    if (ctype.basetype == SPIRType::Struct || !ctype.array.empty() || elements > 4)
1142
42
    {
1143
42
      set<SPIRConstant>(id, type, ops + 2, length - 2, op == OpSpecConstantComposite);
1144
42
    }
1145
854
    else
1146
854
    {
1147
854
      SPIRConstant remapped_constant_ops[4];
1148
854
      const SPIRConstant *c[4];
1149
2.98k
      for (uint32_t i = 0; i < elements; i++)
1150
2.12k
      {
1151
        // Specialization constants operations can also be part of this.
1152
        // We do not know their value, so any attempt to query SPIRConstant later
1153
        // will fail. We can only propagate the ID of the expression and use to_expression on it.
1154
2.12k
        auto *constant_op = maybe_get<SPIRConstantOp>(ops[2 + i]);
1155
2.12k
        auto *undef_op = maybe_get<SPIRUndef>(ops[2 + i]);
1156
2.12k
        if (constant_op)
1157
0
        {
1158
0
          if (op == OpConstantComposite)
1159
0
            SPIRV_CROSS_THROW("Specialization constant operation used in OpConstantComposite.");
1160
1161
0
          remapped_constant_ops[i].make_null(get<SPIRType>(constant_op->basetype));
1162
0
          remapped_constant_ops[i].self = constant_op->self;
1163
0
          remapped_constant_ops[i].constant_type = constant_op->basetype;
1164
0
          remapped_constant_ops[i].specialization = true;
1165
0
          c[i] = &remapped_constant_ops[i];
1166
0
        }
1167
2.12k
        else if (undef_op)
1168
0
        {
1169
          // Undefined, just pick 0.
1170
0
          remapped_constant_ops[i].make_null(get<SPIRType>(undef_op->basetype));
1171
0
          remapped_constant_ops[i].constant_type = undef_op->basetype;
1172
0
          c[i] = &remapped_constant_ops[i];
1173
0
        }
1174
2.12k
        else
1175
2.12k
          c[i] = &get<SPIRConstant>(ops[2 + i]);
1176
2.12k
      }
1177
854
      set<SPIRConstant>(id, type, c, elements, op == OpSpecConstantComposite);
1178
854
    }
1179
896
    break;
1180
896
  }
1181
1182
896
  case OpConstantSizeOfEXT:
1183
0
  {
1184
0
    uint32_t id = ops[1];
1185
0
    uint32_t type = ops[0];
1186
0
    auto &c = set<SPIRConstant>(id, type);
1187
0
    c.size_of_type = ops[2];
1188
0
    break;
1189
896
  }
1190
1191
0
  case OpConstantDataKHR:
1192
0
  case OpSpecConstantDataKHR:
1193
0
  {
1194
0
    uint32_t id = ops[1];
1195
0
    uint32_t type = ops[0];
1196
0
    set<SPIRConstantData>(id, type, ops + 2, length - 2, op == OpSpecConstantDataKHR);
1197
0
    break;
1198
0
  }
1199
1200
0
  case OpTypeBufferEXT:
1201
0
  {
1202
0
    uint32_t type = ops[0];
1203
0
    auto &t = set<SPIRType>(type, OpTypeBufferEXT);
1204
0
    t.basetype = SPIRType::DescriptorHeapBuffer;
1205
0
    t.ext.descriptor_heap_buffer.storage = static_cast<StorageClass>(ops[1]);
1206
0
    break;
1207
0
  }
1208
1209
  // Functions
1210
1.37k
  case OpFunction:
1211
1.37k
  {
1212
1.37k
    uint32_t res = ops[0];
1213
1.37k
    uint32_t id = ops[1];
1214
1.37k
    uint32_t control = ops[2];
1215
1.37k
    uint32_t type = ops[3];
1216
1217
1.37k
    if (current_function)
1218
4
      SPIRV_CROSS_THROW("Must end a function before starting a new one!");
1219
1220
1.37k
    current_function = &set<SPIRFunction>(id, res, control, type);
1221
1.37k
    break;
1222
1.37k
  }
1223
1224
202
  case OpFunctionParameter:
1225
202
  {
1226
202
    uint32_t type = ops[0];
1227
202
    uint32_t id = ops[1];
1228
1229
202
    if (!current_function)
1230
2
      SPIRV_CROSS_THROW("Must be in a function!");
1231
1232
200
    current_function->add_parameter(type, id);
1233
200
    set<SPIRVariable>(id, type, StorageClassFunction);
1234
200
    break;
1235
202
  }
1236
1237
1.45k
  case OpFunctionEnd:
1238
1.45k
  {
1239
1.45k
    if (current_block)
1240
2
    {
1241
      // Very specific error message, but seems to come up quite often.
1242
2
      SPIRV_CROSS_THROW(
1243
2
          "Cannot end a function before ending the current block.\n"
1244
2
          "Likely cause: If this SPIR-V was created from glslang HLSL, make sure the entry point is valid.");
1245
2
    }
1246
1.45k
    current_function = nullptr;
1247
1.45k
    break;
1248
1.45k
  }
1249
1250
  // Blocks
1251
11.5k
  case OpLabel:
1252
11.5k
  {
1253
    // OpLabel always starts a block.
1254
11.5k
    if (!current_function)
1255
0
      SPIRV_CROSS_THROW("Blocks cannot exist outside functions!");
1256
1257
11.5k
    uint32_t id = ops[0];
1258
1259
11.5k
    current_function->blocks.push_back(id);
1260
11.5k
    if (!current_function->entry_block)
1261
1.37k
      current_function->entry_block = id;
1262
1263
11.5k
    if (current_block)
1264
1
      SPIRV_CROSS_THROW("Cannot start a block before ending the current block.");
1265
1266
11.5k
    current_block = &set<SPIRBlock>(id);
1267
11.5k
    break;
1268
11.5k
  }
1269
1270
  // Branch instructions end blocks.
1271
5.15k
  case OpBranch:
1272
5.15k
  {
1273
5.15k
    if (!current_block)
1274
0
      SPIRV_CROSS_THROW("Trying to end a non-existing block.");
1275
1276
5.15k
    uint32_t target = ops[0];
1277
5.15k
    current_block->terminator = SPIRBlock::Direct;
1278
5.15k
    current_block->next_block = target;
1279
5.15k
    current_block = nullptr;
1280
5.15k
    break;
1281
5.15k
  }
1282
1283
1.04k
  case OpBranchConditional:
1284
1.04k
  {
1285
1.04k
    if (!current_block)
1286
0
      SPIRV_CROSS_THROW("Trying to end a non-existing block.");
1287
1288
1.04k
    current_block->condition = ops[0];
1289
1.04k
    current_block->true_block = ops[1];
1290
1.04k
    current_block->false_block = ops[2];
1291
1292
1.04k
    current_block->terminator = SPIRBlock::Select;
1293
1294
1.04k
    if (current_block->true_block == current_block->false_block)
1295
64
    {
1296
      // Bogus conditional, translate to a direct branch.
1297
      // Avoids some ugly edge cases later when analyzing CFGs.
1298
1299
      // There are some super jank cases where the merge block is different from the true/false,
1300
      // and later branches can "break" out of the selection construct this way.
1301
      // This is complete nonsense, but CTS hits this case.
1302
      // In this scenario, we should see the selection construct as more of a Switch with one default case.
1303
      // The problem here is that this breaks any attempt to break out of outer switch statements,
1304
      // but it's theoretically solvable if this ever comes up using the ladder breaking system ...
1305
1306
64
      if (current_block->true_block != current_block->next_block &&
1307
64
          current_block->merge == SPIRBlock::MergeSelection)
1308
58
      {
1309
58
        uint32_t ids = ir.increase_bound_by(2);
1310
1311
58
        auto &type = set<SPIRType>(ids, OpTypeInt);
1312
58
        type.basetype = SPIRType::Int;
1313
58
        type.width = 32;
1314
58
        auto &c = set<SPIRConstant>(ids + 1, ids);
1315
1316
58
        current_block->condition = c.self;
1317
58
        current_block->default_block = current_block->true_block;
1318
58
        current_block->terminator = SPIRBlock::MultiSelect;
1319
58
        ir.block_meta[current_block->next_block] &= ~ParsedIR::BLOCK_META_SELECTION_MERGE_BIT;
1320
58
        ir.block_meta[current_block->next_block] |= ParsedIR::BLOCK_META_MULTISELECT_MERGE_BIT;
1321
58
      }
1322
6
      else
1323
6
      {
1324
        // Collapse loops if we have to.
1325
6
        bool collapsed_loop = current_block->true_block == current_block->merge_block &&
1326
0
                              current_block->merge == SPIRBlock::MergeLoop;
1327
1328
6
        if (collapsed_loop)
1329
0
        {
1330
0
          ir.block_meta[current_block->merge_block] &= ~ParsedIR::BLOCK_META_LOOP_MERGE_BIT;
1331
0
          ir.block_meta[current_block->continue_block] &= ~ParsedIR::BLOCK_META_CONTINUE_BIT;
1332
0
        }
1333
1334
6
        current_block->next_block = current_block->true_block;
1335
6
        current_block->condition = 0;
1336
6
        current_block->true_block = 0;
1337
6
        current_block->false_block = 0;
1338
6
        current_block->merge_block = 0;
1339
6
        current_block->merge = SPIRBlock::MergeNone;
1340
6
        current_block->terminator = SPIRBlock::Direct;
1341
6
      }
1342
64
    }
1343
1344
1.04k
    current_block = nullptr;
1345
1.04k
    break;
1346
1.04k
  }
1347
1348
2.56k
  case OpSwitch:
1349
2.56k
  {
1350
2.56k
    if (!current_block)
1351
0
      SPIRV_CROSS_THROW("Trying to end a non-existing block.");
1352
1353
2.56k
    current_block->terminator = SPIRBlock::MultiSelect;
1354
1355
2.56k
    current_block->condition = ops[0];
1356
2.56k
    current_block->default_block = ops[1];
1357
1358
2.56k
    uint32_t remaining_ops = length - 2;
1359
2.56k
    if ((remaining_ops % 2) == 0)
1360
2.56k
    {
1361
30.9k
      for (uint32_t i = 2; i + 2 <= length; i += 2)
1362
28.3k
        current_block->cases_32bit.push_back({ ops[i], ops[i + 1] });
1363
2.56k
    }
1364
1365
2.56k
    if ((remaining_ops % 3) == 0)
1366
2.55k
    {
1367
21.4k
      for (uint32_t i = 2; i + 3 <= length; i += 3)
1368
18.8k
      {
1369
18.8k
        uint64_t value = (static_cast<uint64_t>(ops[i + 1]) << 32) | ops[i];
1370
18.8k
        current_block->cases_64bit.push_back({ value, ops[i + 2] });
1371
18.8k
      }
1372
2.55k
    }
1373
1374
    // If we jump to next block, make it break instead since we're inside a switch case block at that point.
1375
2.56k
    ir.block_meta[current_block->next_block] |= ParsedIR::BLOCK_META_MULTISELECT_MERGE_BIT;
1376
1377
2.56k
    current_block = nullptr;
1378
2.56k
    break;
1379
2.56k
  }
1380
1381
68
  case OpKill:
1382
68
  case OpTerminateInvocation:
1383
68
  {
1384
68
    if (!current_block)
1385
0
      SPIRV_CROSS_THROW("Trying to end a non-existing block.");
1386
68
    current_block->terminator = SPIRBlock::Kill;
1387
68
    current_block = nullptr;
1388
68
    break;
1389
68
  }
1390
1391
0
  case OpTerminateRayKHR:
1392
    // NV variant is not a terminator.
1393
0
    if (!current_block)
1394
0
      SPIRV_CROSS_THROW("Trying to end a non-existing block.");
1395
0
    current_block->terminator = SPIRBlock::TerminateRay;
1396
0
    current_block = nullptr;
1397
0
    break;
1398
1399
0
  case OpIgnoreIntersectionKHR:
1400
    // NV variant is not a terminator.
1401
0
    if (!current_block)
1402
0
      SPIRV_CROSS_THROW("Trying to end a non-existing block.");
1403
0
    current_block->terminator = SPIRBlock::IgnoreIntersection;
1404
0
    current_block = nullptr;
1405
0
    break;
1406
1407
0
  case OpEmitMeshTasksEXT:
1408
0
    if (!current_block)
1409
0
      SPIRV_CROSS_THROW("Trying to end a non-existing block.");
1410
0
    current_block->terminator = SPIRBlock::EmitMeshTasks;
1411
0
    for (uint32_t i = 0; i < 3; i++)
1412
0
      current_block->mesh.groups[i] = ops[i];
1413
0
    current_block->mesh.payload = length >= 4 ? ops[3] : 0;
1414
0
    current_block = nullptr;
1415
    // Currently glslang is bugged and does not treat EmitMeshTasksEXT as a terminator.
1416
0
    ignore_trailing_block_opcodes = true;
1417
0
    break;
1418
1419
0
  case OpAbortKHR:
1420
0
    if (!current_block)
1421
0
      SPIRV_CROSS_THROW("Trying to end a non-existing block.");
1422
0
    current_block->terminator = SPIRBlock::ShaderAbort;
1423
0
    current_block->shader_abort.physical_type = ops[0];
1424
0
    current_block->shader_abort.payload = ops[1];
1425
0
    current_block = nullptr;
1426
0
    break;
1427
1428
1.13k
  case OpReturn:
1429
1.13k
  {
1430
1.13k
    if (!current_block)
1431
0
      SPIRV_CROSS_THROW("Trying to end a non-existing block.");
1432
1.13k
    current_block->terminator = SPIRBlock::Return;
1433
1.13k
    current_block = nullptr;
1434
1.13k
    break;
1435
1.13k
  }
1436
1437
154
  case OpReturnValue:
1438
154
  {
1439
154
    if (!current_block)
1440
0
      SPIRV_CROSS_THROW("Trying to end a non-existing block.");
1441
154
    current_block->terminator = SPIRBlock::Return;
1442
154
    current_block->return_value = ops[0];
1443
154
    current_block = nullptr;
1444
154
    break;
1445
154
  }
1446
1447
1.37k
  case OpUnreachable:
1448
1.37k
  {
1449
1.37k
    if (!current_block)
1450
0
      SPIRV_CROSS_THROW("Trying to end a non-existing block.");
1451
1.37k
    current_block->terminator = SPIRBlock::Unreachable;
1452
1.37k
    current_block = nullptr;
1453
1.37k
    break;
1454
1.37k
  }
1455
1456
2.21k
  case OpSelectionMerge:
1457
2.21k
  {
1458
2.21k
    if (!current_block)
1459
0
      SPIRV_CROSS_THROW("Trying to modify a non-existing block.");
1460
1461
2.21k
    current_block->next_block = ops[0];
1462
2.21k
    current_block->merge = SPIRBlock::MergeSelection;
1463
2.21k
    ir.block_meta[current_block->next_block] |= ParsedIR::BLOCK_META_SELECTION_MERGE_BIT;
1464
1465
2.21k
    if (length >= 2)
1466
2.21k
    {
1467
2.21k
      if (ops[1] & SelectionControlFlattenMask)
1468
71
        current_block->hint = SPIRBlock::HintFlatten;
1469
2.14k
      else if (ops[1] & SelectionControlDontFlattenMask)
1470
72
        current_block->hint = SPIRBlock::HintDontFlatten;
1471
2.21k
    }
1472
2.21k
    break;
1473
2.21k
  }
1474
1475
835
  case OpLoopMerge:
1476
835
  {
1477
835
    if (!current_block)
1478
0
      SPIRV_CROSS_THROW("Trying to modify a non-existing block.");
1479
1480
835
    current_block->merge_block = ops[0];
1481
835
    current_block->continue_block = ops[1];
1482
835
    current_block->merge = SPIRBlock::MergeLoop;
1483
1484
835
    ir.block_meta[current_block->self] |= ParsedIR::BLOCK_META_LOOP_HEADER_BIT;
1485
835
    ir.block_meta[current_block->merge_block] |= ParsedIR::BLOCK_META_LOOP_MERGE_BIT;
1486
1487
835
    ir.continue_block_to_loop_header[current_block->continue_block] = BlockID(current_block->self);
1488
1489
    // Don't add loop headers to continue blocks,
1490
    // which would make it impossible branch into the loop header since
1491
    // they are treated as continues.
1492
835
    if (current_block->continue_block != BlockID(current_block->self))
1493
832
      ir.block_meta[current_block->continue_block] |= ParsedIR::BLOCK_META_CONTINUE_BIT;
1494
1495
835
    if (length >= 3)
1496
835
    {
1497
835
      if (ops[2] & LoopControlUnrollMask)
1498
28
        current_block->hint = SPIRBlock::HintUnroll;
1499
807
      else if (ops[2] & LoopControlDontUnrollMask)
1500
3
        current_block->hint = SPIRBlock::HintDontUnroll;
1501
835
    }
1502
835
    break;
1503
835
  }
1504
1505
380
  case OpSpecConstantOp:
1506
380
  {
1507
380
    if (length < 3)
1508
1
      SPIRV_CROSS_THROW("OpSpecConstantOp not enough arguments.");
1509
1510
379
    uint32_t result_type = ops[0];
1511
379
    uint32_t id = ops[1];
1512
379
    auto spec_op = static_cast<Op>(ops[2]);
1513
1514
379
    set<SPIRConstantOp>(id, result_type, spec_op, ops + 3, length - 3);
1515
379
    break;
1516
380
  }
1517
1518
107k
  case OpLine:
1519
107k
  {
1520
    // OpLine might come at global scope, but we don't care about those since they will not be declared in any
1521
    // meaningful correct order.
1522
    // Ignore all OpLine directives which live outside a function.
1523
107k
    if (current_block)
1524
26.3k
      current_block->ops.push_back(instruction);
1525
1526
    // Line directives may arrive before first OpLabel.
1527
    // Treat this as the line of the function declaration,
1528
    // so warnings for arguments can propagate properly.
1529
107k
    if (current_function)
1530
26.3k
    {
1531
      // Store the first one we find and emit it before creating the function prototype.
1532
26.3k
      if (current_function->entry_line.file_id == 0)
1533
169
      {
1534
169
        current_function->entry_line.file_id = ops[0];
1535
169
        current_function->entry_line.line_literal = ops[1];
1536
169
      }
1537
26.3k
    }
1538
1539
107k
    uint32_t file = ops[0];
1540
107k
    uint32_t line = ops[1];
1541
1542
107k
    for (auto &source : ir.sources)
1543
75.8M
    {
1544
75.8M
      if (source.file_id == file)
1545
89.7k
      {
1546
89.7k
        source.line_markers.emplace_back();
1547
89.7k
        auto &marker = source.line_markers.back();
1548
89.7k
        marker.line = line;
1549
89.7k
        marker.offset = instruction.offset - 1;
1550
89.7k
        marker.function_id = current_function ? current_function->self : ID(0);
1551
89.7k
        marker.block_id = current_block ? current_block->self : ID(0);
1552
89.7k
        break;
1553
89.7k
      }
1554
75.8M
    }
1555
1556
107k
    break;
1557
380
  }
1558
1559
75.7M
  case OpNoLine:
1560
0
  {
1561
    // OpNoLine might come at global scope.
1562
0
    if (current_block)
1563
0
      current_block->ops.push_back(instruction);
1564
0
    break;
1565
380
  }
1566
1567
  // Actual opcodes.
1568
79.4k
  default:
1569
79.4k
  {
1570
79.4k
    if (length >= 2)
1571
79.4k
    {
1572
79.4k
      const auto *type = maybe_get<SPIRType>(ops[0]);
1573
79.4k
      if (type)
1574
35.9k
        ir.load_type_width.insert({ ops[1], type->width });
1575
79.4k
    }
1576
1577
79.4k
    if (!current_block)
1578
12
      SPIRV_CROSS_THROW("Currently no block to insert opcode.");
1579
1580
79.4k
    current_block->ops.push_back(instruction);
1581
79.4k
    break;
1582
79.4k
  }
1583
791k
  }
1584
791k
}
1585
1586
bool Parser::types_are_logically_equivalent(const SPIRType &a, const SPIRType &b) const
1587
122
{
1588
122
  unordered_set<uint64_t> visited;
1589
122
  return types_are_logically_equivalent(a, b, visited);
1590
122
}
1591
1592
bool Parser::types_are_logically_equivalent(const SPIRType &a, const SPIRType &b,
1593
                                            unordered_set<uint64_t> &visited) const
1594
244
{
1595
244
  if (a.basetype != b.basetype)
1596
0
    return false;
1597
244
  if (a.width != b.width)
1598
0
    return false;
1599
244
  if (a.vecsize != b.vecsize)
1600
1
    return false;
1601
243
  if (a.columns != b.columns)
1602
0
    return false;
1603
243
  if (a.array.size() != b.array.size())
1604
0
    return false;
1605
1606
243
  size_t array_count = a.array.size();
1607
243
  if (array_count && memcmp(a.array.data(), b.array.data(), array_count * sizeof(uint32_t)) != 0)
1608
0
    return false;
1609
1610
243
  if (a.basetype == SPIRType::Image || a.basetype == SPIRType::SampledImage)
1611
0
  {
1612
0
    if (memcmp(&a.image, &b.image, sizeof(SPIRType::Image)) != 0)
1613
0
      return false;
1614
0
  }
1615
1616
243
  if (a.member_types.size() != b.member_types.size())
1617
0
    return false;
1618
1619
  // Buffer references let a struct point back to itself. If we're already
1620
  // comparing this pair, assume it matches and let the other members decide.
1621
243
  if (!visited.insert((uint64_t(a.self) << 32) | uint64_t(b.self)).second)
1622
0
    return true;
1623
1624
243
  size_t member_types = a.member_types.size();
1625
364
  for (size_t i = 0; i < member_types; i++)
1626
122
  {
1627
122
    if (!types_are_logically_equivalent(get<SPIRType>(a.member_types[i]), get<SPIRType>(b.member_types[i]),
1628
122
                                        visited))
1629
1
      return false;
1630
122
  }
1631
1632
242
  return true;
1633
243
}
1634
1635
bool Parser::variable_storage_is_aliased(const SPIRVariable &v) const
1636
0
{
1637
0
  auto &type = get<SPIRType>(v.basetype);
1638
1639
0
  auto *type_meta = ir.find_meta(type.self);
1640
1641
0
  bool ssbo = v.storage == StorageClassStorageBuffer ||
1642
0
              (type_meta && type_meta->decoration.decoration_flags.get(DecorationBufferBlock));
1643
0
  bool image = type.basetype == SPIRType::Image;
1644
0
  bool counter = type.basetype == SPIRType::AtomicCounter;
1645
1646
0
  bool is_restrict;
1647
0
  if (ssbo)
1648
0
    is_restrict = ir.get_buffer_block_flags(v).get(DecorationRestrict);
1649
0
  else
1650
0
    is_restrict = ir.has_decoration(v.self, DecorationRestrict);
1651
1652
0
  return !is_restrict && (ssbo || image || counter);
1653
0
}
1654
} // namespace SPIRV_CROSS_NAMESPACE