Coverage Report

Created: 2026-05-16 07:08

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