Coverage Report

Created: 2026-02-11 06:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/spirv-cross/spirv_cross_parsed_ir.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_cross_parsed_ir.hpp"
25
#include <algorithm>
26
#include <assert.h>
27
28
using namespace std;
29
using namespace SPIRV_CROSS_SPV_HEADER_NAMESPACE;
30
31
namespace SPIRV_CROSS_NAMESPACE
32
{
33
ParsedIR::ParsedIR()
34
516
{
35
  // If we move ParsedIR, we need to make sure the pointer stays fixed since the child Variant objects consume a pointer to this group,
36
  // so need an extra pointer here.
37
516
  pool_group.reset(new ObjectPoolGroup);
38
39
516
  pool_group->pools[TypeType].reset(new ObjectPool<SPIRType>);
40
516
  pool_group->pools[TypeVariable].reset(new ObjectPool<SPIRVariable>);
41
516
  pool_group->pools[TypeConstant].reset(new ObjectPool<SPIRConstant>);
42
516
  pool_group->pools[TypeFunction].reset(new ObjectPool<SPIRFunction>);
43
516
  pool_group->pools[TypeFunctionPrototype].reset(new ObjectPool<SPIRFunctionPrototype>);
44
516
  pool_group->pools[TypeBlock].reset(new ObjectPool<SPIRBlock>);
45
516
  pool_group->pools[TypeExtension].reset(new ObjectPool<SPIRExtension>);
46
516
  pool_group->pools[TypeExpression].reset(new ObjectPool<SPIRExpression>);
47
516
  pool_group->pools[TypeConstantOp].reset(new ObjectPool<SPIRConstantOp>);
48
516
  pool_group->pools[TypeCombinedImageSampler].reset(new ObjectPool<SPIRCombinedImageSampler>);
49
516
  pool_group->pools[TypeAccessChain].reset(new ObjectPool<SPIRAccessChain>);
50
516
  pool_group->pools[TypeUndef].reset(new ObjectPool<SPIRUndef>);
51
516
  pool_group->pools[TypeString].reset(new ObjectPool<SPIRString>);
52
516
  pool_group->pools[TypeDebugLocalVariable].reset(new ObjectPool<SPIRDebugLocalVariable>);
53
516
}
54
55
// Should have been default-implemented, but need this on MSVC 2013.
56
ParsedIR::ParsedIR(ParsedIR &&other) SPIRV_CROSS_NOEXCEPT
57
0
{
58
0
  *this = std::move(other);
59
0
}
60
61
ParsedIR &ParsedIR::operator=(ParsedIR &&other) SPIRV_CROSS_NOEXCEPT
62
0
{
63
0
  if (this != &other)
64
0
  {
65
0
    pool_group = std::move(other.pool_group);
66
0
    spirv = std::move(other.spirv);
67
0
    meta = std::move(other.meta);
68
0
    for (int i = 0; i < TypeCount; i++)
69
0
      ids_for_type[i] = std::move(other.ids_for_type[i]);
70
0
    ids_for_constant_undef_or_type = std::move(other.ids_for_constant_undef_or_type);
71
0
    ids_for_constant_or_variable = std::move(other.ids_for_constant_or_variable);
72
0
    declared_capabilities = std::move(other.declared_capabilities);
73
0
    declared_extensions = std::move(other.declared_extensions);
74
0
    block_meta = std::move(other.block_meta);
75
0
    continue_block_to_loop_header = std::move(other.continue_block_to_loop_header);
76
0
    entry_points = std::move(other.entry_points);
77
0
    ids = std::move(other.ids);
78
0
    addressing_model = other.addressing_model;
79
0
    memory_model = other.memory_model;
80
81
0
    default_entry_point = other.default_entry_point;
82
0
    sources = std::move(other.sources);
83
0
    loop_iteration_depth_hard = other.loop_iteration_depth_hard;
84
0
    loop_iteration_depth_soft = other.loop_iteration_depth_soft;
85
86
0
    meta_needing_name_fixup = std::move(other.meta_needing_name_fixup);
87
0
    load_type_width = std::move(other.load_type_width);
88
0
  }
89
0
  return *this;
90
0
}
91
92
ParsedIR::ParsedIR(const ParsedIR &other)
93
0
    : ParsedIR()
94
0
{
95
0
  *this = other;
96
0
}
97
98
ParsedIR &ParsedIR::operator=(const ParsedIR &other)
99
0
{
100
0
  if (this != &other)
101
0
  {
102
0
    spirv = other.spirv;
103
0
    meta = other.meta;
104
0
    for (int i = 0; i < TypeCount; i++)
105
0
      ids_for_type[i] = other.ids_for_type[i];
106
0
    ids_for_constant_undef_or_type = other.ids_for_constant_undef_or_type;
107
0
    ids_for_constant_or_variable = other.ids_for_constant_or_variable;
108
0
    declared_capabilities = other.declared_capabilities;
109
0
    declared_extensions = other.declared_extensions;
110
0
    block_meta = other.block_meta;
111
0
    continue_block_to_loop_header = other.continue_block_to_loop_header;
112
0
    entry_points = other.entry_points;
113
0
    default_entry_point = other.default_entry_point;
114
0
    sources = other.sources;
115
0
    loop_iteration_depth_hard = other.loop_iteration_depth_hard;
116
0
    loop_iteration_depth_soft = other.loop_iteration_depth_soft;
117
0
    addressing_model = other.addressing_model;
118
0
    memory_model = other.memory_model;
119
120
121
0
    meta_needing_name_fixup = other.meta_needing_name_fixup;
122
0
    load_type_width = other.load_type_width;
123
124
    // Very deliberate copying of IDs. There is no default copy constructor, nor a simple default constructor.
125
    // Construct object first so we have the correct allocator set-up, then we can copy object into our new pool group.
126
0
    ids.clear();
127
0
    ids.reserve(other.ids.size());
128
0
    for (size_t i = 0; i < other.ids.size(); i++)
129
0
    {
130
0
      ids.emplace_back(pool_group.get());
131
0
      ids.back() = other.ids[i];
132
0
    }
133
0
  }
134
0
  return *this;
135
0
}
136
137
void ParsedIR::set_id_bounds(uint32_t bounds)
138
511
{
139
511
  ids.reserve(bounds);
140
93.7M
  while (ids.size() < bounds)
141
93.7M
    ids.emplace_back(pool_group.get());
142
143
511
  block_meta.resize(bounds);
144
511
}
145
146
// Roll our own versions of these functions to avoid potential locale shenanigans.
147
static bool is_alpha(char c)
148
33.8k
{
149
33.8k
  return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
150
33.8k
}
151
152
static bool is_numeric(char c)
153
24.1k
{
154
24.1k
  return c >= '0' && c <= '9';
155
24.1k
}
156
157
static bool is_alphanumeric(char c)
158
33.8k
{
159
33.8k
  return is_alpha(c) || is_numeric(c);
160
33.8k
}
161
162
static bool is_valid_identifier(const string &name)
163
11.6k
{
164
11.6k
  if (name.empty())
165
163
    return true;
166
167
11.4k
  if (is_numeric(name[0]))
168
6
    return false;
169
170
11.4k
  for (auto c : name)
171
33.8k
    if (!is_alphanumeric(c) && c != '_')
172
7.19k
      return false;
173
174
4.27k
  bool saw_underscore = false;
175
  // Two underscores in a row is not a valid identifier either.
176
  // Technically reserved, but it's easier to treat it as invalid.
177
4.27k
  for (auto c : name)
178
22.9k
  {
179
22.9k
    bool is_underscore = c == '_';
180
22.9k
    if (is_underscore && saw_underscore)
181
394
      return false;
182
22.5k
    saw_underscore = is_underscore;
183
22.5k
  }
184
185
3.87k
  return true;
186
4.27k
}
187
188
static bool is_reserved_prefix(const string &name)
189
4.04k
{
190
  // Generic reserved identifiers used by the implementation.
191
4.04k
  return name.compare(0, 3, "gl_", 3) == 0 ||
192
         // Ignore this case for now, might rewrite internal code to always use spv prefix.
193
         //name.compare(0, 11, "SPIRV_Cross", 11) == 0 ||
194
3.93k
         name.compare(0, 3, "spv", 3) == 0;
195
4.04k
}
196
197
static bool is_reserved_identifier(const string &name, bool member, bool allow_reserved_prefixes)
198
4.04k
{
199
4.04k
  if (!allow_reserved_prefixes && is_reserved_prefix(name))
200
115
    return true;
201
202
3.92k
  if (member)
203
1.09k
  {
204
    // Reserved member identifiers come in one form:
205
    // _m[0-9]+$.
206
1.09k
    if (name.size() < 3)
207
728
      return false;
208
209
371
    if (name.compare(0, 2, "_m", 2) != 0)
210
153
      return false;
211
212
218
    size_t index = 2;
213
218
    while (index < name.size() && is_numeric(name[index]))
214
0
      index++;
215
216
218
    return index == name.size();
217
371
  }
218
2.82k
  else
219
2.82k
  {
220
    // Reserved non-member identifiers come in two forms:
221
    // _[0-9]+$, used for temporaries which map directly to a SPIR-V ID.
222
    // _[0-9]+_, used for auxillary temporaries which derived from a SPIR-V ID.
223
2.82k
    if (name.size() < 2)
224
762
      return false;
225
226
2.06k
    if (name[0] != '_' || !is_numeric(name[1]))
227
1.75k
      return false;
228
229
311
    size_t index = 2;
230
936
    while (index < name.size() && is_numeric(name[index]))
231
625
      index++;
232
233
311
    return index == name.size() || (index < name.size() && name[index] == '_');
234
2.06k
  }
235
3.92k
}
236
237
bool ParsedIR::is_globally_reserved_identifier(std::string &str, bool allow_reserved_prefixes)
238
0
{
239
0
  return is_reserved_identifier(str, false, allow_reserved_prefixes);
240
0
}
241
242
uint32_t ParsedIR::get_spirv_version() const
243
0
{
244
0
  return spirv[1];
245
0
}
246
247
static string make_unreserved_identifier(const string &name)
248
0
{
249
0
  if (is_reserved_prefix(name))
250
0
    return "_RESERVED_IDENTIFIER_FIXUP_" + name;
251
0
  else
252
0
    return "_RESERVED_IDENTIFIER_FIXUP" + name;
253
0
}
254
255
void ParsedIR::sanitize_underscores(std::string &str)
256
0
{
257
  // Compact adjacent underscores to make it valid.
258
0
  auto dst = str.begin();
259
0
  auto src = dst;
260
0
  bool saw_underscore = false;
261
0
  while (src != str.end())
262
0
  {
263
0
    bool is_underscore = *src == '_';
264
0
    if (saw_underscore && is_underscore)
265
0
    {
266
0
      src++;
267
0
    }
268
0
    else
269
0
    {
270
0
      if (dst != src)
271
0
        *dst = *src;
272
0
      dst++;
273
0
      src++;
274
0
      saw_underscore = is_underscore;
275
0
    }
276
0
  }
277
0
  str.erase(dst, str.end());
278
0
}
279
280
static string ensure_valid_identifier(const string &name)
281
0
{
282
  // Functions in glslangValidator are mangled with name(<mangled> stuff.
283
  // Normally, we would never see '(' in any legal identifiers, so just strip them out.
284
0
  auto str = name.substr(0, name.find('('));
285
286
0
  if (str.empty())
287
0
    return str;
288
289
0
  if (is_numeric(str[0]))
290
0
    str[0] = '_';
291
292
0
  for (auto &c : str)
293
0
    if (!is_alphanumeric(c) && c != '_')
294
0
      c = '_';
295
296
0
  ParsedIR::sanitize_underscores(str);
297
0
  return str;
298
0
}
299
300
const string &ParsedIR::get_name(ID id) const
301
10.4k
{
302
10.4k
  auto *m = find_meta(id);
303
10.4k
  if (m)
304
6.38k
    return m->decoration.alias;
305
4.09k
  else
306
4.09k
    return empty_string;
307
10.4k
}
308
309
const string &ParsedIR::get_member_name(TypeID id, uint32_t index) const
310
0
{
311
0
  auto *m = find_meta(id);
312
0
  if (m)
313
0
  {
314
0
    if (index >= m->members.size())
315
0
      return empty_string;
316
0
    return m->members[index].alias;
317
0
  }
318
0
  else
319
0
    return empty_string;
320
0
}
321
322
void ParsedIR::sanitize_identifier(std::string &name, bool member, bool allow_reserved_prefixes)
323
0
{
324
0
  if (!is_valid_identifier(name))
325
0
    name = ensure_valid_identifier(name);
326
0
  if (is_reserved_identifier(name, member, allow_reserved_prefixes))
327
0
    name = make_unreserved_identifier(name);
328
0
}
329
330
void ParsedIR::fixup_reserved_names()
331
0
{
332
0
  for (uint32_t id : meta_needing_name_fixup)
333
0
  {
334
    // Don't rename remapped variables like 'gl_LastFragDepthARM'.
335
0
    if (ids[id].get_type() == TypeVariable && get<SPIRVariable>(id).remapped_variable)
336
0
      continue;
337
338
0
    auto &m = meta[id];
339
0
    sanitize_identifier(m.decoration.alias, false, false);
340
0
    for (auto &memb : m.members)
341
0
      sanitize_identifier(memb.alias, true, false);
342
0
  }
343
0
  meta_needing_name_fixup.clear();
344
0
}
345
346
void ParsedIR::set_name(ID id, const string &name)
347
10.1k
{
348
10.1k
  auto &m = meta[id];
349
10.1k
  m.decoration.alias = name;
350
10.1k
  if (!is_valid_identifier(name) || is_reserved_identifier(name, false, false))
351
7.33k
    meta_needing_name_fixup.insert(id);
352
10.1k
}
353
354
void ParsedIR::set_member_name(TypeID id, uint32_t index, const string &name)
355
1.53k
{
356
1.53k
  auto &m = meta[id];
357
1.53k
  m.members.resize(max(m.members.size(), size_t(index) + 1));
358
1.53k
  m.members[index].alias = name;
359
1.53k
  if (!is_valid_identifier(name) || is_reserved_identifier(name, true, false))
360
435
    meta_needing_name_fixup.insert(id);
361
1.53k
}
362
363
void ParsedIR::set_decoration_string(ID id, Decoration decoration, const string &argument)
364
144
{
365
144
  auto &dec = meta[id].decoration;
366
144
  dec.decoration_flags.set(decoration);
367
368
144
  switch (decoration)
369
144
  {
370
144
  case DecorationUserSemantic:
371
144
    dec.user_semantic = argument;
372
144
    break;
373
374
0
  case DecorationUserTypeGOOGLE:
375
0
    dec.user_type = argument;
376
0
    break;
377
378
0
  default:
379
0
    break;
380
144
  }
381
144
}
382
383
void ParsedIR::set_decoration(ID id, Decoration decoration, uint32_t argument)
384
85.6k
{
385
85.6k
  auto &dec = meta[id].decoration;
386
85.6k
  dec.decoration_flags.set(decoration);
387
388
85.6k
  switch (decoration)
389
85.6k
  {
390
690
  case DecorationBuiltIn:
391
690
    dec.builtin = true;
392
690
    dec.builtin_type = static_cast<BuiltIn>(argument);
393
690
    break;
394
395
569
  case DecorationLocation:
396
569
    dec.location = argument;
397
569
    break;
398
399
2.52k
  case DecorationComponent:
400
2.52k
    dec.component = argument;
401
2.52k
    break;
402
403
4
  case DecorationOffset:
404
4
    dec.offset = argument;
405
4
    break;
406
407
131
  case DecorationXfbBuffer:
408
131
    dec.xfb_buffer = argument;
409
131
    break;
410
411
0
  case DecorationXfbStride:
412
0
    dec.xfb_stride = argument;
413
0
    break;
414
415
0
  case DecorationStream:
416
0
    dec.stream = argument;
417
0
    break;
418
419
941
  case DecorationArrayStride:
420
941
    dec.array_stride = argument;
421
941
    break;
422
423
18.8k
  case DecorationMatrixStride:
424
18.8k
    dec.matrix_stride = argument;
425
18.8k
    break;
426
427
1.41k
  case DecorationBinding:
428
1.41k
    dec.binding = argument;
429
1.41k
    break;
430
431
420
  case DecorationDescriptorSet:
432
420
    dec.set = argument;
433
420
    break;
434
435
0
  case DecorationInputAttachmentIndex:
436
0
    dec.input_attachment = argument;
437
0
    break;
438
439
15
  case DecorationSpecId:
440
15
    dec.spec_id = argument;
441
15
    break;
442
443
1.05k
  case DecorationIndex:
444
1.05k
    dec.index = argument;
445
1.05k
    break;
446
447
0
  case DecorationHlslCounterBufferGOOGLE:
448
0
    meta[id].hlsl_magic_counter_buffer = argument;
449
0
    meta[argument].hlsl_is_magic_counter_buffer = true;
450
0
    break;
451
452
0
  case DecorationFPRoundingMode:
453
0
    dec.fp_rounding_mode = static_cast<FPRoundingMode>(argument);
454
0
    break;
455
456
39
  case DecorationFPFastMathMode:
457
39
    dec.fp_fast_math_mode = static_cast<FPFastMathModeMask>(argument);
458
39
    break;
459
460
59.0k
  default:
461
59.0k
    break;
462
85.6k
  }
463
85.6k
}
464
465
void ParsedIR::set_member_decoration(TypeID id, uint32_t index, Decoration decoration, uint32_t argument)
466
8.98k
{
467
8.98k
  auto &m = meta[id];
468
8.98k
  m.members.resize(max(m.members.size(), size_t(index) + 1));
469
8.98k
  auto &dec = m.members[index];
470
8.98k
  dec.decoration_flags.set(decoration);
471
472
8.98k
  switch (decoration)
473
8.98k
  {
474
2.81k
  case DecorationBuiltIn:
475
2.81k
    dec.builtin = true;
476
2.81k
    dec.builtin_type = static_cast<BuiltIn>(argument);
477
2.81k
    break;
478
479
0
  case DecorationLocation:
480
0
    dec.location = argument;
481
0
    break;
482
483
18
  case DecorationComponent:
484
18
    dec.component = argument;
485
18
    break;
486
487
61
  case DecorationBinding:
488
61
    dec.binding = argument;
489
61
    break;
490
491
348
  case DecorationOffset:
492
348
    dec.offset = argument;
493
348
    break;
494
495
223
  case DecorationXfbBuffer:
496
223
    dec.xfb_buffer = argument;
497
223
    break;
498
499
0
  case DecorationXfbStride:
500
0
    dec.xfb_stride = argument;
501
0
    break;
502
503
0
  case DecorationStream:
504
0
    dec.stream = argument;
505
0
    break;
506
507
344
  case DecorationSpecId:
508
344
    dec.spec_id = argument;
509
344
    break;
510
511
3
  case DecorationMatrixStride:
512
3
    dec.matrix_stride = argument;
513
3
    break;
514
515
65
  case DecorationIndex:
516
65
    dec.index = argument;
517
65
    break;
518
519
5.10k
  default:
520
5.10k
    break;
521
8.98k
  }
522
8.98k
}
523
524
// Recursively marks any constants referenced by the specified constant instruction as being used
525
// as an array length. The id must be a constant instruction (SPIRConstant or SPIRConstantOp).
526
void ParsedIR::mark_used_as_array_length(ID id)
527
3.50k
{
528
3.50k
  switch (ids[id].get_type())
529
3.50k
  {
530
186
  case TypeConstant:
531
186
  {
532
186
    auto &c = get<SPIRConstant>(id);
533
186
    c.is_used_as_array_length = true;
534
535
    // Mark composite dependencies as well.
536
186
    for (auto &sub_id: c.m.id)
537
744
      if (sub_id)
538
0
        mark_used_as_array_length(sub_id);
539
540
372
    for (uint32_t col = 0; col < c.m.columns; col++)
541
186
    {
542
186
      for (auto &sub_id : c.m.c[col].id)
543
744
        if (sub_id)
544
0
          mark_used_as_array_length(sub_id);
545
186
    }
546
547
186
    for (auto &sub_id : c.subconstants)
548
0
      if (sub_id)
549
0
        mark_used_as_array_length(sub_id);
550
186
    break;
551
0
  }
552
553
0
  case TypeConstantOp:
554
0
  {
555
0
    auto &cop = get<SPIRConstantOp>(id);
556
0
    if (cop.opcode == OpCompositeExtract)
557
0
      mark_used_as_array_length(cop.arguments[0]);
558
0
    else if (cop.opcode == OpCompositeInsert)
559
0
    {
560
0
      mark_used_as_array_length(cop.arguments[0]);
561
0
      mark_used_as_array_length(cop.arguments[1]);
562
0
    }
563
0
    else
564
0
      for (uint32_t arg_id : cop.arguments)
565
0
        mark_used_as_array_length(arg_id);
566
0
    break;
567
0
  }
568
569
3.31k
  case TypeUndef:
570
3.31k
    break;
571
572
0
  default:
573
0
    assert(0);
574
3.50k
  }
575
3.50k
}
576
577
Bitset ParsedIR::get_buffer_block_type_flags(const SPIRType &type) const
578
0
{
579
0
  if (type.member_types.empty())
580
0
    return {};
581
582
0
  Bitset all_members_flags = get_member_decoration_bitset(type.self, 0);
583
0
  for (uint32_t i = 1; i < uint32_t(type.member_types.size()); i++)
584
0
    all_members_flags.merge_and(get_member_decoration_bitset(type.self, i));
585
0
  return all_members_flags;
586
0
}
587
588
Bitset ParsedIR::get_buffer_block_flags(const SPIRVariable &var) const
589
0
{
590
0
  auto &type = get<SPIRType>(var.basetype);
591
0
  if (type.basetype != SPIRType::Struct)
592
0
    SPIRV_CROSS_THROW("Cannot get buffer block flags for non-buffer variable.");
593
594
  // Some flags like non-writable, non-readable are actually found
595
  // as member decorations. If all members have a decoration set, propagate
596
  // the decoration up as a regular variable decoration.
597
0
  Bitset base_flags;
598
0
  auto *m = find_meta(var.self);
599
0
  if (m)
600
0
    base_flags = m->decoration.decoration_flags;
601
602
0
  if (type.member_types.empty())
603
0
    return base_flags;
604
605
0
  auto all_members_flags = get_buffer_block_type_flags(type);
606
0
  base_flags.merge_or(all_members_flags);
607
0
  return base_flags;
608
0
}
609
610
const Bitset &ParsedIR::get_member_decoration_bitset(TypeID id, uint32_t index) const
611
0
{
612
0
  auto *m = find_meta(id);
613
0
  if (m)
614
0
  {
615
0
    if (index >= m->members.size())
616
0
      return cleared_bitset;
617
0
    return m->members[index].decoration_flags;
618
0
  }
619
0
  else
620
0
    return cleared_bitset;
621
0
}
622
623
bool ParsedIR::has_decoration(ID id, Decoration decoration) const
624
0
{
625
0
  return get_decoration_bitset(id).get(decoration);
626
0
}
627
628
uint32_t ParsedIR::get_decoration(ID id, Decoration decoration) const
629
82.6k
{
630
82.6k
  auto *m = find_meta(id);
631
82.6k
  if (!m)
632
0
    return 0;
633
634
82.6k
  auto &dec = m->decoration;
635
82.6k
  if (!dec.decoration_flags.get(decoration))
636
0
    return 0;
637
638
82.6k
  switch (decoration)
639
82.6k
  {
640
2.81k
  case DecorationBuiltIn:
641
2.81k
    return dec.builtin_type;
642
0
  case DecorationLocation:
643
0
    return dec.location;
644
18
  case DecorationComponent:
645
18
    return dec.component;
646
0
  case DecorationOffset:
647
0
    return dec.offset;
648
351
  case DecorationXfbBuffer:
649
351
    return dec.xfb_buffer;
650
0
  case DecorationXfbStride:
651
0
    return dec.xfb_stride;
652
0
  case DecorationStream:
653
0
    return dec.stream;
654
61
  case DecorationBinding:
655
61
    return dec.binding;
656
464
  case DecorationDescriptorSet:
657
464
    return dec.set;
658
0
  case DecorationInputAttachmentIndex:
659
0
    return dec.input_attachment;
660
344
  case DecorationSpecId:
661
344
    return dec.spec_id;
662
985
  case DecorationArrayStride:
663
985
    return dec.array_stride;
664
18.4k
  case DecorationMatrixStride:
665
18.4k
    return dec.matrix_stride;
666
1.11k
  case DecorationIndex:
667
1.11k
    return dec.index;
668
0
  case DecorationFPRoundingMode:
669
0
    return dec.fp_rounding_mode;
670
78
  case DecorationFPFastMathMode:
671
78
    return dec.fp_fast_math_mode;
672
58.0k
  default:
673
58.0k
    return 1;
674
82.6k
  }
675
82.6k
}
676
677
const string &ParsedIR::get_decoration_string(ID id, Decoration decoration) const
678
294
{
679
294
  auto *m = find_meta(id);
680
294
  if (!m)
681
0
    return empty_string;
682
683
294
  auto &dec = m->decoration;
684
685
294
  if (!dec.decoration_flags.get(decoration))
686
0
    return empty_string;
687
688
294
  switch (decoration)
689
294
  {
690
294
  case DecorationUserSemantic:
691
294
    return dec.user_semantic;
692
693
0
  case DecorationUserTypeGOOGLE:
694
0
    return dec.user_type;
695
696
0
  default:
697
0
    return empty_string;
698
294
  }
699
294
}
700
701
void ParsedIR::unset_decoration(ID id, Decoration decoration)
702
0
{
703
0
  auto &dec = meta[id].decoration;
704
0
  dec.decoration_flags.clear(decoration);
705
0
  switch (decoration)
706
0
  {
707
0
  case DecorationBuiltIn:
708
0
    dec.builtin = false;
709
0
    break;
710
711
0
  case DecorationLocation:
712
0
    dec.location = 0;
713
0
    break;
714
715
0
  case DecorationComponent:
716
0
    dec.component = 0;
717
0
    break;
718
719
0
  case DecorationOffset:
720
0
    dec.offset = 0;
721
0
    break;
722
723
0
  case DecorationXfbBuffer:
724
0
    dec.xfb_buffer = 0;
725
0
    break;
726
727
0
  case DecorationXfbStride:
728
0
    dec.xfb_stride = 0;
729
0
    break;
730
731
0
  case DecorationStream:
732
0
    dec.stream = 0;
733
0
    break;
734
735
0
  case DecorationBinding:
736
0
    dec.binding = 0;
737
0
    break;
738
739
0
  case DecorationDescriptorSet:
740
0
    dec.set = 0;
741
0
    break;
742
743
0
  case DecorationInputAttachmentIndex:
744
0
    dec.input_attachment = 0;
745
0
    break;
746
747
0
  case DecorationSpecId:
748
0
    dec.spec_id = 0;
749
0
    break;
750
751
0
  case DecorationUserSemantic:
752
0
    dec.user_semantic.clear();
753
0
    break;
754
755
0
  case DecorationFPRoundingMode:
756
0
    dec.fp_rounding_mode = FPRoundingModeMax;
757
0
    break;
758
759
0
  case DecorationFPFastMathMode:
760
0
    dec.fp_fast_math_mode = FPFastMathModeMaskNone;
761
0
    break;
762
763
0
  case DecorationHlslCounterBufferGOOGLE:
764
0
  {
765
0
    auto &counter = meta[id].hlsl_magic_counter_buffer;
766
0
    if (counter)
767
0
    {
768
0
      meta[counter].hlsl_is_magic_counter_buffer = false;
769
0
      counter = 0;
770
0
    }
771
0
    break;
772
0
  }
773
774
0
  default:
775
0
    break;
776
0
  }
777
0
}
778
779
bool ParsedIR::has_member_decoration(TypeID id, uint32_t index, Decoration decoration) const
780
0
{
781
0
  return get_member_decoration_bitset(id, index).get(decoration);
782
0
}
783
784
uint32_t ParsedIR::get_member_decoration(TypeID id, uint32_t index, Decoration decoration) const
785
0
{
786
0
  auto *m = find_meta(id);
787
0
  if (!m)
788
0
    return 0;
789
790
0
  if (index >= m->members.size())
791
0
    return 0;
792
793
0
  auto &dec = m->members[index];
794
0
  if (!dec.decoration_flags.get(decoration))
795
0
    return 0;
796
797
0
  switch (decoration)
798
0
  {
799
0
  case DecorationBuiltIn:
800
0
    return dec.builtin_type;
801
0
  case DecorationLocation:
802
0
    return dec.location;
803
0
  case DecorationComponent:
804
0
    return dec.component;
805
0
  case DecorationBinding:
806
0
    return dec.binding;
807
0
  case DecorationOffset:
808
0
    return dec.offset;
809
0
  case DecorationXfbBuffer:
810
0
    return dec.xfb_buffer;
811
0
  case DecorationXfbStride:
812
0
    return dec.xfb_stride;
813
0
  case DecorationStream:
814
0
    return dec.stream;
815
0
  case DecorationSpecId:
816
0
    return dec.spec_id;
817
0
  case DecorationMatrixStride:
818
0
    return dec.matrix_stride;
819
0
  case DecorationIndex:
820
0
    return dec.index;
821
0
  default:
822
0
    return 1;
823
0
  }
824
0
}
825
826
const Bitset &ParsedIR::get_decoration_bitset(ID id) const
827
0
{
828
0
  auto *m = find_meta(id);
829
0
  if (m)
830
0
  {
831
0
    auto &dec = m->decoration;
832
0
    return dec.decoration_flags;
833
0
  }
834
0
  else
835
0
    return cleared_bitset;
836
0
}
837
838
void ParsedIR::set_member_decoration_string(TypeID id, uint32_t index, Decoration decoration, const string &argument)
839
301
{
840
301
  auto &m = meta[id];
841
301
  m.members.resize(max(m.members.size(), size_t(index) + 1));
842
301
  auto &dec = meta[id].members[index];
843
301
  dec.decoration_flags.set(decoration);
844
845
301
  switch (decoration)
846
301
  {
847
150
  case DecorationUserSemantic:
848
150
    dec.user_semantic = argument;
849
150
    break;
850
851
151
  default:
852
151
    break;
853
301
  }
854
301
}
855
856
const string &ParsedIR::get_member_decoration_string(TypeID id, uint32_t index, Decoration decoration) const
857
0
{
858
0
  auto *m = find_meta(id);
859
0
  if (m)
860
0
  {
861
0
    if (!has_member_decoration(id, index, decoration))
862
0
      return empty_string;
863
864
0
    auto &dec = m->members[index];
865
866
0
    switch (decoration)
867
0
    {
868
0
    case DecorationUserSemantic:
869
0
      return dec.user_semantic;
870
871
0
    default:
872
0
      return empty_string;
873
0
    }
874
0
  }
875
0
  else
876
0
    return empty_string;
877
0
}
878
879
void ParsedIR::unset_member_decoration(TypeID id, uint32_t index, Decoration decoration)
880
0
{
881
0
  auto &m = meta[id];
882
0
  if (index >= m.members.size())
883
0
    return;
884
885
0
  auto &dec = m.members[index];
886
887
0
  dec.decoration_flags.clear(decoration);
888
0
  switch (decoration)
889
0
  {
890
0
  case DecorationBuiltIn:
891
0
    dec.builtin = false;
892
0
    break;
893
894
0
  case DecorationLocation:
895
0
    dec.location = 0;
896
0
    break;
897
898
0
  case DecorationComponent:
899
0
    dec.component = 0;
900
0
    break;
901
902
0
  case DecorationOffset:
903
0
    dec.offset = 0;
904
0
    break;
905
906
0
  case DecorationXfbBuffer:
907
0
    dec.xfb_buffer = 0;
908
0
    break;
909
910
0
  case DecorationXfbStride:
911
0
    dec.xfb_stride = 0;
912
0
    break;
913
914
0
  case DecorationStream:
915
0
    dec.stream = 0;
916
0
    break;
917
918
0
  case DecorationSpecId:
919
0
    dec.spec_id = 0;
920
0
    break;
921
922
0
  case DecorationUserSemantic:
923
0
    dec.user_semantic.clear();
924
0
    break;
925
926
0
  default:
927
0
    break;
928
0
  }
929
0
}
930
931
uint32_t ParsedIR::increase_bound_by(uint32_t incr_amount)
932
59.1k
{
933
59.1k
  auto curr_bound = ids.size();
934
59.1k
  auto new_bound = curr_bound + incr_amount;
935
936
59.1k
  ids.reserve(ids.size() + incr_amount);
937
119k
  for (uint32_t i = 0; i < incr_amount; i++)
938
60.4k
    ids.emplace_back(pool_group.get());
939
940
59.1k
  block_meta.resize(new_bound);
941
59.1k
  return uint32_t(curr_bound);
942
59.1k
}
943
944
void ParsedIR::remove_typed_id(Types type, ID id)
945
9
{
946
9
  auto &type_ids = ids_for_type[type];
947
9
  type_ids.erase(remove(begin(type_ids), end(type_ids), id), end(type_ids));
948
9
}
949
950
void ParsedIR::reset_all_of_type(Types type)
951
0
{
952
0
  for (auto &id : ids_for_type[type])
953
0
    if (ids[id].get_type() == type)
954
0
      ids[id].reset();
955
956
0
  ids_for_type[type].clear();
957
0
}
958
959
void ParsedIR::add_typed_id(Types type, ID id)
960
165k
{
961
165k
  assert(id < ids.size());
962
963
165k
  if (loop_iteration_depth_hard != 0)
964
0
    SPIRV_CROSS_THROW("Cannot add typed ID while looping over it.");
965
966
165k
  if (loop_iteration_depth_soft != 0)
967
0
  {
968
0
    if (!ids[id].empty())
969
0
      SPIRV_CROSS_THROW("Cannot override IDs when loop is soft locked.");
970
0
    return;
971
0
  }
972
973
165k
  if (ids[id].empty() || ids[id].get_type() != type)
974
76.3k
  {
975
76.3k
    switch (type)
976
76.3k
    {
977
64.1k
    case TypeConstant:
978
64.1k
      ids_for_constant_or_variable.push_back(id);
979
64.1k
      ids_for_constant_undef_or_type.push_back(id);
980
64.1k
      break;
981
982
2.65k
    case TypeVariable:
983
2.65k
      ids_for_constant_or_variable.push_back(id);
984
2.65k
      break;
985
986
3.63k
    case TypeType:
987
3.64k
    case TypeConstantOp:
988
3.69k
    case TypeUndef:
989
3.69k
      ids_for_constant_undef_or_type.push_back(id);
990
3.69k
      break;
991
992
5.87k
    default:
993
5.87k
      break;
994
76.3k
    }
995
76.3k
  }
996
997
165k
  if (ids[id].empty())
998
76.3k
  {
999
76.3k
    ids_for_type[type].push_back(id);
1000
76.3k
  }
1001
89.3k
  else if (ids[id].get_type() != type)
1002
9
  {
1003
9
    remove_typed_id(ids[id].get_type(), id);
1004
9
    ids_for_type[type].push_back(id);
1005
9
  }
1006
165k
}
1007
1008
const Meta *ParsedIR::find_meta(ID id) const
1009
93.4k
{
1010
93.4k
  auto itr = meta.find(id);
1011
93.4k
  if (itr != end(meta))
1012
89.3k
    return &itr->second;
1013
4.09k
  else
1014
4.09k
    return nullptr;
1015
93.4k
}
1016
1017
Meta *ParsedIR::find_meta(ID id)
1018
0
{
1019
0
  auto itr = meta.find(id);
1020
0
  if (itr != end(meta))
1021
0
    return &itr->second;
1022
0
  else
1023
0
    return nullptr;
1024
0
}
1025
1026
ParsedIR::LoopLock ParsedIR::create_loop_hard_lock() const
1027
0
{
1028
0
  return ParsedIR::LoopLock(&loop_iteration_depth_hard);
1029
0
}
1030
1031
ParsedIR::LoopLock ParsedIR::create_loop_soft_lock() const
1032
0
{
1033
0
  return ParsedIR::LoopLock(&loop_iteration_depth_soft);
1034
0
}
1035
1036
ParsedIR::LoopLock::~LoopLock()
1037
0
{
1038
0
  if (lock)
1039
0
    (*lock)--;
1040
0
}
1041
1042
ParsedIR::LoopLock::LoopLock(uint32_t *lock_)
1043
0
    : lock(lock_)
1044
0
{
1045
0
  if (lock)
1046
0
    (*lock)++;
1047
0
}
1048
1049
ParsedIR::LoopLock::LoopLock(LoopLock &&other) SPIRV_CROSS_NOEXCEPT
1050
0
{
1051
0
  *this = std::move(other);
1052
0
}
1053
1054
ParsedIR::LoopLock &ParsedIR::LoopLock::operator=(LoopLock &&other) SPIRV_CROSS_NOEXCEPT
1055
0
{
1056
0
  if (lock)
1057
0
    (*lock)--;
1058
0
  lock = other.lock;
1059
0
  other.lock = nullptr;
1060
0
  return *this;
1061
0
}
1062
1063
void ParsedIR::make_constant_null(uint32_t id, uint32_t type, bool add_to_typed_id_set)
1064
119k
{
1065
119k
  assert(id < ids.size());
1066
1067
119k
  auto &constant_type = get<SPIRType>(type);
1068
1069
119k
  if (constant_type.pointer)
1070
13
  {
1071
13
    if (add_to_typed_id_set)
1072
13
      add_typed_id(TypeConstant, id);
1073
13
    auto &constant = variant_set<SPIRConstant>(ids[id], type);
1074
13
    constant.self = id;
1075
13
    constant.make_null(constant_type);
1076
13
  }
1077
119k
  else if (!constant_type.array.empty())
1078
57.9k
  {
1079
57.9k
    assert(constant_type.parent_type);
1080
57.9k
    uint32_t parent_id = increase_bound_by(1);
1081
57.9k
    make_constant_null(parent_id, constant_type.parent_type, add_to_typed_id_set);
1082
1083
    // The array size of OpConstantNull can be either literal or specialization constant.
1084
    // In the latter case, we cannot take the value as-is, as it can be changed to anything.
1085
    // Rather, we assume it to be *one* for the sake of initializer.
1086
57.9k
    bool is_literal_array_size = constant_type.array_size_literal.back();
1087
57.9k
    uint32_t count = is_literal_array_size ? constant_type.array.back() : 1;
1088
1089
57.9k
    SmallVector<uint32_t> elements(count);
1090
144M
    for (uint32_t i = 0; i < count; i++)
1091
144M
      elements[i] = parent_id;
1092
1093
57.9k
    if (add_to_typed_id_set)
1094
57.9k
      add_typed_id(TypeConstant, id);
1095
57.9k
    auto& constant = variant_set<SPIRConstant>(ids[id], type, elements.data(), uint32_t(elements.size()), false);
1096
57.9k
    constant.self = id;
1097
57.9k
    constant.is_null_array_specialized_length = !is_literal_array_size;
1098
57.9k
  }
1099
61.4k
  else if (!constant_type.member_types.empty())
1100
1.03k
  {
1101
1.03k
    uint32_t member_ids = increase_bound_by(uint32_t(constant_type.member_types.size()));
1102
1.03k
    SmallVector<uint32_t> elements(constant_type.member_types.size());
1103
3.33k
    for (uint32_t i = 0; i < constant_type.member_types.size(); i++)
1104
2.30k
    {
1105
2.30k
      make_constant_null(member_ids + i, constant_type.member_types[i], add_to_typed_id_set);
1106
2.30k
      elements[i] = member_ids + i;
1107
2.30k
    }
1108
1109
1.03k
    if (add_to_typed_id_set)
1110
1.03k
      add_typed_id(TypeConstant, id);
1111
1.03k
    variant_set<SPIRConstant>(ids[id], type, elements.data(), uint32_t(elements.size()), false).self = id;
1112
1.03k
  }
1113
60.3k
  else
1114
60.3k
  {
1115
60.3k
    if (add_to_typed_id_set)
1116
60.3k
      add_typed_id(TypeConstant, id);
1117
60.3k
    auto &constant = variant_set<SPIRConstant>(ids[id], type);
1118
60.3k
    constant.self = id;
1119
60.3k
    constant.make_null(constant_type);
1120
60.3k
  }
1121
119k
}
1122
1123
} // namespace SPIRV_CROSS_NAMESPACE