Coverage Report

Created: 2026-02-14 06:54

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/spirv-cross/spirv_common.hpp
Line
Count
Source
1
/*
2
 * Copyright 2015-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
#ifndef SPIRV_CROSS_COMMON_HPP
25
#define SPIRV_CROSS_COMMON_HPP
26
27
#ifndef SPV_ENABLE_UTILITY_CODE
28
#define SPV_ENABLE_UTILITY_CODE
29
#endif
30
31
// Pragmatic hack to avoid symbol conflicts when including both hpp11 and hpp headers in same translation unit.
32
// This is an unfortunate SPIRV-Headers issue that we cannot easily deal with ourselves.
33
#ifdef SPIRV_CROSS_SPV_HEADER_NAMESPACE_OVERRIDE
34
#define spv SPIRV_CROSS_SPV_HEADER_NAMESPACE_OVERRIDE
35
#define SPIRV_CROSS_SPV_HEADER_NAMESPACE SPIRV_CROSS_SPV_HEADER_NAMESPACE_OVERRIDE
36
#else
37
#define SPIRV_CROSS_SPV_HEADER_NAMESPACE spv
38
#endif
39
40
#include "spirv.hpp"
41
#include "spirv_cross_containers.hpp"
42
#include "spirv_cross_error_handling.hpp"
43
#include <functional>
44
45
// A bit crude, but allows projects which embed SPIRV-Cross statically to
46
// effectively hide all the symbols from other projects.
47
// There is a case where we have:
48
// - Project A links against SPIRV-Cross statically.
49
// - Project A links against Project B statically.
50
// - Project B links against SPIRV-Cross statically (might be a different version).
51
// This leads to a conflict with extremely bizarre results.
52
// By overriding the namespace in one of the project builds, we can work around this.
53
// If SPIRV-Cross is embedded in dynamic libraries,
54
// prefer using -fvisibility=hidden on GCC/Clang instead.
55
#ifdef SPIRV_CROSS_NAMESPACE_OVERRIDE
56
#define SPIRV_CROSS_NAMESPACE SPIRV_CROSS_NAMESPACE_OVERRIDE
57
#else
58
#define SPIRV_CROSS_NAMESPACE spirv_cross
59
#endif
60
61
namespace SPIRV_CROSS_NAMESPACE
62
{
63
namespace inner
64
{
65
template <typename T>
66
void join_helper(StringStream<> &stream, T &&t)
67
0
{
68
0
  stream << std::forward<T>(t);
69
0
}
70
71
template <typename T, typename... Ts>
72
void join_helper(StringStream<> &stream, T &&t, Ts &&... ts)
73
0
{
74
0
  stream << std::forward<T>(t);
75
0
  join_helper(stream, std::forward<Ts>(ts)...);
76
0
}
Unexecuted instantiation: void spirv_cross::inner::join_helper<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, char const (&) [21], char const*, char const (&) [2]>(spirv_cross::StringStream<4096ul, 4096ul>&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, char const (&) [21], char const*&&, char const (&) [2])
Unexecuted instantiation: void spirv_cross::inner::join_helper<char const (&) [21], char const*, char const (&) [2]>(spirv_cross::StringStream<4096ul, 4096ul>&, char const (&) [21], char const*&&, char const (&) [2])
Unexecuted instantiation: void spirv_cross::inner::join_helper<char const*, char const (&) [2]>(spirv_cross::StringStream<4096ul, 4096ul>&, char const*&&, char const (&) [2])
77
} // namespace inner
78
79
class Bitset
80
{
81
public:
82
59.5M
  Bitset() = default;
83
  explicit inline Bitset(uint64_t lower_)
84
      : lower(lower_)
85
0
  {
86
0
  }
87
88
  inline bool get(uint32_t bit) const
89
86.8k
  {
90
86.8k
    if (bit < 64)
91
25.0k
      return (lower & (1ull << bit)) != 0;
92
61.7k
    else
93
61.7k
      return higher.count(bit) != 0;
94
86.8k
  }
95
96
  inline void set(uint32_t bit)
97
102k
  {
98
102k
    if (bit < 64)
99
38.6k
      lower |= 1ull << bit;
100
63.7k
    else
101
63.7k
      higher.insert(bit);
102
102k
  }
103
104
  inline void clear(uint32_t bit)
105
0
  {
106
0
    if (bit < 64)
107
0
      lower &= ~(1ull << bit);
108
0
    else
109
0
      higher.erase(bit);
110
0
  }
111
112
  inline uint64_t get_lower() const
113
0
  {
114
0
    return lower;
115
0
  }
116
117
  inline void reset()
118
0
  {
119
0
    lower = 0;
120
0
    higher.clear();
121
0
  }
122
123
  inline void merge_and(const Bitset &other)
124
0
  {
125
0
    lower &= other.lower;
126
0
    std::unordered_set<uint32_t> tmp_set;
127
0
    for (auto &v : higher)
128
0
      if (other.higher.count(v) != 0)
129
0
        tmp_set.insert(v);
130
0
    higher = std::move(tmp_set);
131
0
  }
132
133
  inline void merge_or(const Bitset &other)
134
0
  {
135
0
    lower |= other.lower;
136
0
    for (auto &v : other.higher)
137
0
      higher.insert(v);
138
0
  }
139
140
  inline bool operator==(const Bitset &other) const
141
0
  {
142
0
    if (lower != other.lower)
143
0
      return false;
144
0
145
0
    if (higher.size() != other.higher.size())
146
0
      return false;
147
0
148
0
    for (auto &v : higher)
149
0
      if (other.higher.count(v) == 0)
150
0
        return false;
151
0
152
0
    return true;
153
0
  }
154
155
  inline bool operator!=(const Bitset &other) const
156
0
  {
157
0
    return !(*this == other);
158
0
  }
159
160
  template <typename Op>
161
  void for_each_bit(const Op &op) const
162
25.3k
  {
163
    // TODO: Add ctz-based iteration.
164
1.64M
    for (uint32_t i = 0; i < 64; i++)
165
1.62M
    {
166
1.62M
      if (lower & (1ull << i))
167
25.0k
        op(i);
168
1.62M
    }
169
170
25.3k
    if (higher.empty())
171
995
      return;
172
173
    // Need to enforce an order here for reproducible results,
174
    // but hitting this path should happen extremely rarely, so having this slow path is fine.
175
24.3k
    SmallVector<uint32_t> bits;
176
24.3k
    bits.reserve(higher.size());
177
24.3k
    for (auto &v : higher)
178
61.7k
      bits.push_back(v);
179
24.3k
    std::sort(std::begin(bits), std::end(bits));
180
181
24.3k
    for (auto &v : bits)
182
61.7k
      op(v);
183
24.3k
  }
spirv_parser.cpp:void spirv_cross::Bitset::for_each_bit<spirv_cross::Parser::parse(spirv_cross::Instruction const&)::$_0>(spirv_cross::Parser::parse(spirv_cross::Instruction const&)::$_0 const&) const
Line
Count
Source
162
21.9k
  {
163
    // TODO: Add ctz-based iteration.
164
1.42M
    for (uint32_t i = 0; i < 64; i++)
165
1.40M
    {
166
1.40M
      if (lower & (1ull << i))
167
21.4k
        op(i);
168
1.40M
    }
169
170
21.9k
    if (higher.empty())
171
627
      return;
172
173
    // Need to enforce an order here for reproducible results,
174
    // but hitting this path should happen extremely rarely, so having this slow path is fine.
175
21.2k
    SmallVector<uint32_t> bits;
176
21.2k
    bits.reserve(higher.size());
177
21.2k
    for (auto &v : higher)
178
57.1k
      bits.push_back(v);
179
21.2k
    std::sort(std::begin(bits), std::end(bits));
180
181
21.2k
    for (auto &v : bits)
182
57.1k
      op(v);
183
21.2k
  }
spirv_parser.cpp:void spirv_cross::Bitset::for_each_bit<spirv_cross::Parser::parse(spirv_cross::Instruction const&)::$_1>(spirv_cross::Parser::parse(spirv_cross::Instruction const&)::$_1 const&) const
Line
Count
Source
162
3.47k
  {
163
    // TODO: Add ctz-based iteration.
164
225k
    for (uint32_t i = 0; i < 64; i++)
165
222k
    {
166
222k
      if (lower & (1ull << i))
167
3.62k
        op(i);
168
222k
    }
169
170
3.47k
    if (higher.empty())
171
368
      return;
172
173
    // Need to enforce an order here for reproducible results,
174
    // but hitting this path should happen extremely rarely, so having this slow path is fine.
175
3.10k
    SmallVector<uint32_t> bits;
176
3.10k
    bits.reserve(higher.size());
177
3.10k
    for (auto &v : higher)
178
4.63k
      bits.push_back(v);
179
3.10k
    std::sort(std::begin(bits), std::end(bits));
180
181
3.10k
    for (auto &v : bits)
182
4.63k
      op(v);
183
3.10k
  }
184
185
  inline bool empty() const
186
0
  {
187
0
    return lower == 0 && higher.empty();
188
0
  }
189
190
private:
191
  // The most common bits to set are all lower than 64,
192
  // so optimize for this case. Bits spilling outside 64 go into a slower data structure.
193
  // In almost all cases, higher data structure will not be used.
194
  uint64_t lower = 0;
195
  std::unordered_set<uint32_t> higher;
196
};
197
198
// Helper template to avoid lots of nasty string temporary munging.
199
template <typename... Ts>
200
std::string join(Ts &&... ts)
201
0
{
202
0
  StringStream<> stream;
203
0
  inner::join_helper(stream, std::forward<Ts>(ts)...);
204
0
  return stream.str();
205
0
}
206
207
inline std::string merge(const SmallVector<std::string> &list, const char *between = ", ")
208
0
{
209
0
  StringStream<> stream;
210
0
  for (auto &elem : list)
211
0
  {
212
0
    stream << elem;
213
0
    if (&elem != &list.back())
214
0
      stream << between;
215
0
  }
216
0
  return stream.str();
217
0
}
218
219
// Make sure we don't accidentally call this with float or doubles with SFINAE.
220
// Have to use the radix-aware overload.
221
template <typename T, typename std::enable_if<!std::is_floating_point<T>::value, int>::type = 0>
222
inline std::string convert_to_string(const T &t)
223
{
224
  return std::to_string(t);
225
}
226
227
static inline std::string convert_to_string(int32_t value)
228
0
{
229
0
  // INT_MIN is ... special on some backends. If we use a decimal literal, and negate it, we
230
0
  // could accidentally promote the literal to long first, then negate.
231
0
  // To workaround it, emit int(0x80000000) instead.
232
0
  if (value == (std::numeric_limits<int32_t>::min)())
233
0
    return "int(0x80000000)";
234
0
  else
235
0
    return std::to_string(value);
236
0
}
Unexecuted instantiation: parser_fuzzer.cpp:spirv_cross::convert_to_string(int)
Unexecuted instantiation: spirv_parser.cpp:spirv_cross::convert_to_string(int)
Unexecuted instantiation: spirv_cross_parsed_ir.cpp:spirv_cross::convert_to_string(int)
237
238
static inline std::string convert_to_string(int64_t value, const std::string &int64_type, bool long_long_literal_suffix)
239
0
{
240
0
  // INT64_MIN is ... special on some backends.
241
0
  // If we use a decimal literal, and negate it, we might overflow the representable numbers.
242
0
  // To workaround it, emit int(0x80000000) instead.
243
0
  if (value == (std::numeric_limits<int64_t>::min)())
244
0
    return join(int64_type, "(0x8000000000000000u", (long_long_literal_suffix ? "ll" : "l"), ")");
245
0
  else
246
0
    return std::to_string(value) + (long_long_literal_suffix ? "ll" : "l");
247
0
}
Unexecuted instantiation: parser_fuzzer.cpp:spirv_cross::convert_to_string(long, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, bool)
Unexecuted instantiation: spirv_parser.cpp:spirv_cross::convert_to_string(long, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, bool)
Unexecuted instantiation: spirv_cross_parsed_ir.cpp:spirv_cross::convert_to_string(long, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, bool)
248
249
// Allow implementations to set a convenient standard precision
250
#ifndef SPIRV_CROSS_FLT_FMT
251
#define SPIRV_CROSS_FLT_FMT "%.32g"
252
#endif
253
254
// Disable sprintf and strcat warnings.
255
// We cannot rely on snprintf and family existing because, ..., MSVC.
256
#if defined(__clang__) || defined(__GNUC__)
257
#pragma GCC diagnostic push
258
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
259
#elif defined(_MSC_VER)
260
#pragma warning(push)
261
#pragma warning(disable : 4996)
262
#endif
263
264
static inline void fixup_radix_point(char *str, char radix_point)
265
0
{
266
0
  // Setting locales is a very risky business in multi-threaded program,
267
0
  // so just fixup locales instead. We only need to care about the radix point.
268
0
  if (radix_point != '.')
269
0
  {
270
0
    while (*str != '\0')
271
0
    {
272
0
      if (*str == radix_point)
273
0
        *str = '.';
274
0
      str++;
275
0
    }
276
0
  }
277
0
}
Unexecuted instantiation: parser_fuzzer.cpp:spirv_cross::fixup_radix_point(char*, char)
Unexecuted instantiation: spirv_parser.cpp:spirv_cross::fixup_radix_point(char*, char)
Unexecuted instantiation: spirv_cross_parsed_ir.cpp:spirv_cross::fixup_radix_point(char*, char)
278
279
inline std::string convert_to_string(float t, char locale_radix_point)
280
0
{
281
0
  // std::to_string for floating point values is broken.
282
0
  // Fallback to something more sane.
283
0
  char buf[64];
284
0
  sprintf(buf, SPIRV_CROSS_FLT_FMT, t);
285
0
  fixup_radix_point(buf, locale_radix_point);
286
0
287
0
  // Ensure that the literal is float.
288
0
  if (!strchr(buf, '.') && !strchr(buf, 'e'))
289
0
    strcat(buf, ".0");
290
0
  return buf;
291
0
}
292
293
inline std::string convert_to_string(double t, char locale_radix_point)
294
0
{
295
0
  // std::to_string for floating point values is broken.
296
0
  // Fallback to something more sane.
297
0
  char buf[64];
298
0
  sprintf(buf, SPIRV_CROSS_FLT_FMT, t);
299
0
  fixup_radix_point(buf, locale_radix_point);
300
0
301
0
  // Ensure that the literal is float.
302
0
  if (!strchr(buf, '.') && !strchr(buf, 'e'))
303
0
    strcat(buf, ".0");
304
0
  return buf;
305
0
}
306
307
#if defined(__clang__) || defined(__GNUC__)
308
#pragma GCC diagnostic pop
309
#elif defined(_MSC_VER)
310
#pragma warning(pop)
311
#endif
312
313
class FloatFormatter
314
{
315
public:
316
  virtual ~FloatFormatter() = default;
317
  virtual std::string format_float(float value) = 0;
318
  virtual std::string format_double(double value) = 0;
319
};
320
321
template <typename T>
322
struct ValueSaver
323
{
324
  explicit ValueSaver(T &current_)
325
      : current(current_)
326
      , saved(current_)
327
  {
328
  }
329
330
  void release()
331
  {
332
    current = saved;
333
  }
334
335
  ~ValueSaver()
336
  {
337
    release();
338
  }
339
340
  T &current;
341
  T saved;
342
};
343
344
struct Instruction
345
{
346
  uint16_t op = 0;
347
  uint16_t count = 0;
348
  // If offset is 0 (not a valid offset into the instruction stream),
349
  // we have an instruction stream which is embedded in the object.
350
  uint32_t offset = 0;
351
  uint32_t length = 0;
352
353
  inline bool is_embedded() const
354
0
  {
355
0
    return offset == 0;
356
0
  }
357
};
358
359
struct EmbeddedInstruction : Instruction
360
{
361
  SmallVector<uint32_t> ops;
362
};
363
364
enum Types
365
{
366
  TypeNone,
367
  TypeType,
368
  TypeVariable,
369
  TypeConstant,
370
  TypeFunction,
371
  TypeFunctionPrototype,
372
  TypeBlock,
373
  TypeExtension,
374
  TypeExpression,
375
  TypeConstantOp,
376
  TypeCombinedImageSampler,
377
  TypeAccessChain,
378
  TypeUndef,
379
  TypeString,
380
  TypeDebugLocalVariable,
381
  TypeCount
382
};
383
384
template <Types type>
385
class TypedID;
386
387
template <>
388
class TypedID<TypeNone>
389
{
390
public:
391
1.90M
  TypedID() = default;
392
  TypedID(uint32_t id_)
393
689k
      : id(id_)
394
689k
  {
395
689k
  }
396
397
  template <Types U>
398
  TypedID(const TypedID<U> &other)
399
11.9k
  {
400
11.9k
    *this = other;
401
11.9k
  }
spirv_cross::TypedID<(spirv_cross::Types)0>::TypedID<(spirv_cross::Types)1>(spirv_cross::TypedID<(spirv_cross::Types)1> const&)
Line
Count
Source
399
11.9k
  {
400
11.9k
    *this = other;
401
11.9k
  }
Unexecuted instantiation: spirv_cross::TypedID<(spirv_cross::Types)0>::TypedID<(spirv_cross::Types)3>(spirv_cross::TypedID<(spirv_cross::Types)3> const&)
402
403
  template <Types U>
404
  TypedID &operator=(const TypedID<U> &other)
405
11.9k
  {
406
11.9k
    id = uint32_t(other);
407
11.9k
    return *this;
408
11.9k
  }
spirv_cross::TypedID<(spirv_cross::Types)0>& spirv_cross::TypedID<(spirv_cross::Types)0>::operator=<(spirv_cross::Types)1>(spirv_cross::TypedID<(spirv_cross::Types)1> const&)
Line
Count
Source
405
11.9k
  {
406
11.9k
    id = uint32_t(other);
407
11.9k
    return *this;
408
11.9k
  }
Unexecuted instantiation: spirv_cross::TypedID<(spirv_cross::Types)0>& spirv_cross::TypedID<(spirv_cross::Types)0>::operator=<(spirv_cross::Types)3>(spirv_cross::TypedID<(spirv_cross::Types)3> const&)
409
410
  // Implicit conversion to u32 is desired here.
411
  // As long as we block implicit conversion between TypedID<A> and TypedID<B> we're good.
412
  operator uint32_t() const
413
1.53M
  {
414
1.53M
    return id;
415
1.53M
  }
416
417
  template <Types U>
418
  operator TypedID<U>() const
419
0
  {
420
0
    return TypedID<U>(*this);
421
0
  }
422
423
private:
424
  uint32_t id = 0;
425
};
426
427
template <Types type>
428
class TypedID
429
{
430
public:
431
10.4k
  TypedID() = default;
spirv_cross::TypedID<(spirv_cross::Types)1>::TypedID()
Line
Count
Source
431
9.95k
  TypedID() = default;
spirv_cross::TypedID<(spirv_cross::Types)6>::TypedID()
Line
Count
Source
431
524
  TypedID() = default;
432
  TypedID(uint32_t id_)
433
239k
      : id(id_)
434
239k
  {
435
239k
  }
spirv_cross::TypedID<(spirv_cross::Types)1>::TypedID(unsigned int)
Line
Count
Source
433
118k
      : id(id_)
434
118k
  {
435
118k
  }
spirv_cross::TypedID<(spirv_cross::Types)6>::TypedID(unsigned int)
Line
Count
Source
433
93.1k
      : id(id_)
434
93.1k
  {
435
93.1k
  }
spirv_cross::TypedID<(spirv_cross::Types)2>::TypedID(unsigned int)
Line
Count
Source
433
14.0k
      : id(id_)
434
14.0k
  {
435
14.0k
  }
spirv_cross::TypedID<(spirv_cross::Types)4>::TypedID(unsigned int)
Line
Count
Source
433
8.65k
      : id(id_)
434
8.65k
  {
435
8.65k
  }
spirv_cross::TypedID<(spirv_cross::Types)3>::TypedID(unsigned int)
Line
Count
Source
433
5.25k
      : id(id_)
434
5.25k
  {
435
5.25k
  }
436
437
  explicit TypedID(const TypedID<TypeNone> &other)
438
2.65k
      : id(uint32_t(other))
439
2.65k
  {
440
2.65k
  }
spirv_cross::TypedID<(spirv_cross::Types)6>::TypedID(spirv_cross::TypedID<(spirv_cross::Types)0> const&)
Line
Count
Source
438
2.65k
      : id(uint32_t(other))
439
2.65k
  {
440
2.65k
  }
Unexecuted instantiation: spirv_cross::TypedID<(spirv_cross::Types)1>::TypedID(spirv_cross::TypedID<(spirv_cross::Types)0> const&)
441
442
  operator uint32_t() const
443
112k
  {
444
112k
    return id;
445
112k
  }
spirv_cross::TypedID<(spirv_cross::Types)4>::operator unsigned int() const
Line
Count
Source
443
19.5k
  {
444
19.5k
    return id;
445
19.5k
  }
spirv_cross::TypedID<(spirv_cross::Types)1>::operator unsigned int() const
Line
Count
Source
443
72.4k
  {
444
72.4k
    return id;
445
72.4k
  }
spirv_cross::TypedID<(spirv_cross::Types)6>::operator unsigned int() const
Line
Count
Source
443
20.5k
  {
444
20.5k
    return id;
445
20.5k
  }
Unexecuted instantiation: spirv_cross::TypedID<(spirv_cross::Types)3>::operator unsigned int() const
446
447
private:
448
  uint32_t id = 0;
449
};
450
451
using VariableID = TypedID<TypeVariable>;
452
using TypeID = TypedID<TypeType>;
453
using ConstantID = TypedID<TypeConstant>;
454
using FunctionID = TypedID<TypeFunction>;
455
using BlockID = TypedID<TypeBlock>;
456
using ID = TypedID<TypeNone>;
457
458
// Helper for Variant interface.
459
struct IVariant
460
{
461
95.0k
  virtual ~IVariant() = default;
462
  virtual IVariant *clone(ObjectPoolBase *pool) = 0;
463
  ID self = 0;
464
465
protected:
466
90.4k
  IVariant() = default;
467
4.54k
  IVariant(const IVariant&) = default;
468
2.54k
  IVariant &operator=(const IVariant&) = default;
469
};
470
471
#define SPIRV_CROSS_DECLARE_CLONE(T)                                \
472
  IVariant *clone(ObjectPoolBase *pool) override                  \
473
0
  {                                                               \
474
0
    return static_cast<ObjectPool<T> *>(pool)->allocate(*this); \
475
0
  }
Unexecuted instantiation: spirv_cross::SPIRUndef::clone(spirv_cross::ObjectPoolBase*)
Unexecuted instantiation: spirv_cross::SPIRString::clone(spirv_cross::ObjectPoolBase*)
Unexecuted instantiation: spirv_cross::SPIRDebugLocalVariable::clone(spirv_cross::ObjectPoolBase*)
Unexecuted instantiation: spirv_cross::SPIRCombinedImageSampler::clone(spirv_cross::ObjectPoolBase*)
Unexecuted instantiation: spirv_cross::SPIRConstantOp::clone(spirv_cross::ObjectPoolBase*)
Unexecuted instantiation: spirv_cross::SPIRType::clone(spirv_cross::ObjectPoolBase*)
Unexecuted instantiation: spirv_cross::SPIRExtension::clone(spirv_cross::ObjectPoolBase*)
Unexecuted instantiation: spirv_cross::SPIRExpression::clone(spirv_cross::ObjectPoolBase*)
Unexecuted instantiation: spirv_cross::SPIRFunctionPrototype::clone(spirv_cross::ObjectPoolBase*)
Unexecuted instantiation: spirv_cross::SPIRBlock::clone(spirv_cross::ObjectPoolBase*)
Unexecuted instantiation: spirv_cross::SPIRFunction::clone(spirv_cross::ObjectPoolBase*)
Unexecuted instantiation: spirv_cross::SPIRAccessChain::clone(spirv_cross::ObjectPoolBase*)
Unexecuted instantiation: spirv_cross::SPIRVariable::clone(spirv_cross::ObjectPoolBase*)
Unexecuted instantiation: spirv_cross::SPIRConstant::clone(spirv_cross::ObjectPoolBase*)
476
477
struct SPIRUndef : IVariant
478
{
479
  enum
480
  {
481
    type = TypeUndef
482
  };
483
484
  explicit SPIRUndef(TypeID basetype_)
485
70
      : basetype(basetype_)
486
70
  {
487
70
  }
488
  TypeID basetype;
489
490
  SPIRV_CROSS_DECLARE_CLONE(SPIRUndef)
491
};
492
493
struct SPIRString : IVariant
494
{
495
  enum
496
  {
497
    type = TypeString
498
  };
499
500
  explicit SPIRString(std::string str_)
501
667
      : str(std::move(str_))
502
667
  {
503
667
  }
504
505
  std::string str;
506
507
  SPIRV_CROSS_DECLARE_CLONE(SPIRString)
508
};
509
510
struct SPIRDebugLocalVariable : IVariant
511
{
512
  enum
513
  {
514
    type = TypeDebugLocalVariable
515
  };
516
517
  uint32_t name_id;
518
519
  SPIRV_CROSS_DECLARE_CLONE(SPIRDebugLocalVariable)
520
};
521
522
// This type is only used by backends which need to access the combined image and sampler IDs separately after
523
// the OpSampledImage opcode.
524
struct SPIRCombinedImageSampler : IVariant
525
{
526
  enum
527
  {
528
    type = TypeCombinedImageSampler
529
  };
530
  SPIRCombinedImageSampler(TypeID type_, VariableID image_, VariableID sampler_)
531
      : combined_type(type_)
532
      , image(image_)
533
      , sampler(sampler_)
534
0
  {
535
0
  }
536
  TypeID combined_type;
537
  VariableID image;
538
  VariableID sampler;
539
540
  SPIRV_CROSS_DECLARE_CLONE(SPIRCombinedImageSampler)
541
};
542
543
struct SPIRConstantOp : IVariant
544
{
545
  enum
546
  {
547
    type = TypeConstantOp
548
  };
549
550
  SPIRConstantOp(TypeID result_type, spv::Op op, const uint32_t *args, uint32_t length)
551
517
      : opcode(op)
552
517
      , basetype(result_type)
553
517
  {
554
517
    arguments.reserve(length);
555
1.06k
    for (uint32_t i = 0; i < length; i++)
556
549
      arguments.push_back(args[i]);
557
517
  }
558
559
  spv::Op opcode;
560
  SmallVector<uint32_t> arguments;
561
  TypeID basetype;
562
563
  SPIRV_CROSS_DECLARE_CLONE(SPIRConstantOp)
564
};
565
566
struct SPIRType : IVariant
567
{
568
  enum
569
  {
570
    type = TypeType
571
  };
572
573
  spv::Op op = spv::Op::OpNop;
574
9.95k
  explicit SPIRType(spv::Op op_) : op(op_) {}
575
576
  enum BaseType
577
  {
578
    Unknown,
579
    Void,
580
    Boolean,
581
    SByte,
582
    UByte,
583
    Short,
584
    UShort,
585
    Int,
586
    UInt,
587
    Int64,
588
    UInt64,
589
    AtomicCounter,
590
    Half,
591
    Float,
592
    Double,
593
    Struct,
594
    Image,
595
    SampledImage,
596
    Sampler,
597
    AccelerationStructure,
598
    RayQuery,
599
    CoopVecNV,
600
601
    // Keep internal types at the end.
602
    ControlPointArray,
603
    Interpolant,
604
    Char,
605
    // MSL specific type, that is used by 'object'(analog of 'task' from glsl) shader.
606
    MeshGridProperties,
607
    BFloat16,
608
    FloatE4M3,
609
    FloatE5M2,
610
611
    Tensor
612
  };
613
614
  // Scalar/vector/matrix support.
615
  BaseType basetype = Unknown;
616
  uint32_t width = 0;
617
  uint32_t vecsize = 1;
618
  uint32_t columns = 1;
619
620
  // Arrays, support array of arrays by having a vector of array sizes.
621
  SmallVector<uint32_t> array;
622
623
  // Array elements can be either specialization constants or specialization ops.
624
  // This array determines how to interpret the array size.
625
  // If an element is true, the element is a literal,
626
  // otherwise, it's an expression, which must be resolved on demand.
627
  // The actual size is not really known until runtime.
628
  SmallVector<bool> array_size_literal;
629
630
  // Pointers
631
  // Keep track of how many pointer layers we have.
632
  uint32_t pointer_depth = 0;
633
  bool pointer = false;
634
  bool forward_pointer = false;
635
636
  union
637
  {
638
    struct
639
    {
640
      uint32_t use_id;
641
      uint32_t rows_id;
642
      uint32_t columns_id;
643
      uint32_t scope_id;
644
    } cooperative;
645
646
    struct
647
    {
648
      uint32_t component_type_id;
649
      uint32_t component_count_id;
650
    } coopVecNV;
651
652
    struct
653
    {
654
      uint32_t type;
655
      uint32_t rank;
656
      uint32_t shape;
657
    } tensor;
658
  } ext;
659
660
  spv::StorageClass storage = spv::StorageClassGeneric;
661
662
  SmallVector<TypeID> member_types;
663
664
  // If member order has been rewritten to handle certain scenarios with Offset,
665
  // allow codegen to rewrite the index.
666
  SmallVector<uint32_t> member_type_index_redirection;
667
668
  struct ImageType
669
  {
670
    TypeID type;
671
    spv::Dim dim;
672
    bool depth;
673
    bool arrayed;
674
    bool ms;
675
    uint32_t sampled;
676
    spv::ImageFormat format;
677
    spv::AccessQualifier access;
678
  } image = {};
679
680
  // Structs can be declared multiple times if they are used as part of interface blocks.
681
  // We want to detect this so that we only emit the struct definition once.
682
  // Since we cannot rely on OpName to be equal, we need to figure out aliases.
683
  TypeID type_alias = 0;
684
685
  // Denotes the type which this type is based on.
686
  // Allows the backend to traverse how a complex type is built up during access chains.
687
  TypeID parent_type = 0;
688
689
  // Used in backends to avoid emitting members with conflicting names.
690
  std::unordered_set<std::string> member_name_cache;
691
692
  SPIRV_CROSS_DECLARE_CLONE(SPIRType)
693
};
694
695
struct SPIRExtension : IVariant
696
{
697
  enum
698
  {
699
    type = TypeExtension
700
  };
701
702
  enum Extension
703
  {
704
    Unsupported,
705
    GLSL,
706
    SPV_debug_info,
707
    SPV_AMD_shader_ballot,
708
    SPV_AMD_shader_explicit_vertex_parameter,
709
    SPV_AMD_shader_trinary_minmax,
710
    SPV_AMD_gcn_shader,
711
    NonSemanticDebugPrintf,
712
    NonSemanticShaderDebugInfo,
713
    NonSemanticGeneric
714
  };
715
716
  enum ShaderDebugInfoOps
717
  {
718
    DebugLine = 103,
719
    DebugSource = 35
720
  };
721
722
  explicit SPIRExtension(Extension ext_)
723
500
      : ext(ext_)
724
500
  {
725
500
  }
726
727
  Extension ext;
728
  SPIRV_CROSS_DECLARE_CLONE(SPIRExtension)
729
};
730
731
// SPIREntryPoint is not a variant since its IDs are used to decorate OpFunction,
732
// so in order to avoid conflicts, we can't stick them in the ids array.
733
struct SPIREntryPoint
734
{
735
  SPIREntryPoint(FunctionID self_, spv::ExecutionModel execution_model, const std::string &entry_name)
736
2.89k
      : self(self_)
737
2.89k
      , name(entry_name)
738
2.89k
      , orig_name(entry_name)
739
2.89k
      , model(execution_model)
740
2.89k
  {
741
2.89k
  }
742
85
  SPIREntryPoint() = default;
743
744
  FunctionID self = 0;
745
  std::string name;
746
  std::string orig_name;
747
  std::unordered_map<uint32_t, uint32_t> fp_fast_math_defaults;
748
  bool signed_zero_inf_nan_preserve_8 = false;
749
  bool signed_zero_inf_nan_preserve_16 = false;
750
  bool signed_zero_inf_nan_preserve_32 = false;
751
  bool signed_zero_inf_nan_preserve_64 = false;
752
  SmallVector<VariableID> interface_variables;
753
754
  Bitset flags;
755
  struct WorkgroupSize
756
  {
757
    uint32_t x = 0, y = 0, z = 0;
758
    uint32_t id_x = 0, id_y = 0, id_z = 0;
759
    uint32_t constant = 0; // Workgroup size can be expressed as a constant/spec-constant instead.
760
  } workgroup_size;
761
  uint32_t invocations = 0;
762
  uint32_t output_vertices = 0;
763
  uint32_t output_primitives = 0;
764
  spv::ExecutionModel model = spv::ExecutionModelMax;
765
  bool geometry_passthrough = false;
766
};
767
768
struct SPIRExpression : IVariant
769
{
770
  enum
771
  {
772
    type = TypeExpression
773
  };
774
775
  // Only created by the backend target to avoid creating tons of temporaries.
776
  SPIRExpression(std::string expr, TypeID expression_type_, bool immutable_)
777
      : expression(std::move(expr))
778
      , expression_type(expression_type_)
779
      , immutable(immutable_)
780
0
  {
781
0
  }
782
783
  // If non-zero, prepend expression with to_expression(base_expression).
784
  // Used in amortizing multiple calls to to_expression()
785
  // where in certain cases that would quickly force a temporary when not needed.
786
  ID base_expression = 0;
787
788
  std::string expression;
789
  TypeID expression_type = 0;
790
791
  // If this expression is a forwarded load,
792
  // allow us to reference the original variable.
793
  ID loaded_from = 0;
794
795
  // If this expression will never change, we can avoid lots of temporaries
796
  // in high level source.
797
  // An expression being immutable can be speculative,
798
  // it is assumed that this is true almost always.
799
  bool immutable = false;
800
801
  // Before use, this expression must be transposed.
802
  // This is needed for targets which don't support row_major layouts.
803
  bool need_transpose = false;
804
805
  // Whether or not this is an access chain expression.
806
  bool access_chain = false;
807
808
  // Whether or not gl_MeshVerticesEXT[].gl_Position (as a whole or .y) is referenced
809
  bool access_meshlet_position_y = false;
810
811
  // A list of expressions which this expression depends on.
812
  SmallVector<ID> expression_dependencies;
813
814
  // Similar as expression dependencies, but does not stop the tracking for force-temporary variables.
815
  // We need to know the full chain from store back to any SSA variable.
816
  SmallVector<ID> invariance_dependencies;
817
818
  // By reading this expression, we implicitly read these expressions as well.
819
  // Used by access chain Store and Load since we read multiple expressions in this case.
820
  SmallVector<ID> implied_read_expressions;
821
822
  // The expression was emitted at a certain scope. Lets us track when an expression read means multiple reads.
823
  uint32_t emitted_loop_level = 0;
824
825
  SPIRV_CROSS_DECLARE_CLONE(SPIRExpression)
826
};
827
828
struct SPIRFunctionPrototype : IVariant
829
{
830
  enum
831
  {
832
    type = TypeFunctionPrototype
833
  };
834
835
  explicit SPIRFunctionPrototype(TypeID return_type_)
836
1.21k
      : return_type(return_type_)
837
1.21k
  {
838
1.21k
  }
839
840
  TypeID return_type;
841
  SmallVector<uint32_t> parameter_types;
842
843
  SPIRV_CROSS_DECLARE_CLONE(SPIRFunctionPrototype)
844
};
845
846
struct SPIRBlock : IVariant
847
{
848
  enum
849
  {
850
    type = TypeBlock
851
  };
852
853
  enum Terminator
854
  {
855
    Unknown,
856
    Direct, // Emit next block directly without a particular condition.
857
858
    Select, // Block ends with an if/else block.
859
    MultiSelect, // Block ends with switch statement.
860
861
    Return, // Block ends with return.
862
    Unreachable, // Noop
863
    Kill, // Discard
864
    IgnoreIntersection, // Ray Tracing
865
    TerminateRay, // Ray Tracing
866
    EmitMeshTasks // Mesh shaders
867
  };
868
869
  enum Merge
870
  {
871
    MergeNone,
872
    MergeLoop,
873
    MergeSelection
874
  };
875
876
  enum Hints
877
  {
878
    HintNone,
879
    HintUnroll,
880
    HintDontUnroll,
881
    HintFlatten,
882
    HintDontFlatten
883
  };
884
885
  enum Method
886
  {
887
    MergeToSelectForLoop,
888
    MergeToDirectForLoop,
889
    MergeToSelectContinueForLoop
890
  };
891
892
  enum ContinueBlockType
893
  {
894
    ContinueNone,
895
896
    // Continue block is branchless and has at least one instruction.
897
    ForLoop,
898
899
    // Noop continue block.
900
    WhileLoop,
901
902
    // Continue block is conditional.
903
    DoWhileLoop,
904
905
    // Highly unlikely that anything will use this,
906
    // since it is really awkward/impossible to express in GLSL.
907
    ComplexLoop
908
  };
909
910
  enum : uint32_t
911
  {
912
    NoDominator = 0xffffffffu
913
  };
914
915
  Terminator terminator = Unknown;
916
  Merge merge = MergeNone;
917
  Hints hint = HintNone;
918
  BlockID next_block = 0;
919
  BlockID merge_block = 0;
920
  BlockID continue_block = 0;
921
922
  ID return_value = 0; // If 0, return nothing (void).
923
  ID condition = 0;
924
  BlockID true_block = 0;
925
  BlockID false_block = 0;
926
  BlockID default_block = 0;
927
928
  // If terminator is EmitMeshTasksEXT.
929
  struct
930
  {
931
    ID groups[3];
932
    ID payload;
933
  } mesh = {};
934
935
  SmallVector<Instruction> ops;
936
937
  struct Phi
938
  {
939
    ID local_variable; // flush local variable ...
940
    BlockID parent; // If we're in from_block and want to branch into this block ...
941
    VariableID function_variable; // to this function-global "phi" variable first.
942
  };
943
944
  // Before entering this block flush out local variables to magical "phi" variables.
945
  SmallVector<Phi> phi_variables;
946
947
  // Declare these temporaries before beginning the block.
948
  // Used for handling complex continue blocks which have side effects.
949
  SmallVector<std::pair<TypeID, ID>> declare_temporary;
950
951
  // Declare these temporaries, but only conditionally if this block turns out to be
952
  // a complex loop header.
953
  SmallVector<std::pair<TypeID, ID>> potential_declare_temporary;
954
955
  struct Case
956
  {
957
    uint64_t value;
958
    BlockID block;
959
  };
960
  SmallVector<Case> cases_32bit;
961
  SmallVector<Case> cases_64bit;
962
963
  // If we have tried to optimize code for this block but failed,
964
  // keep track of this.
965
  bool disable_block_optimization = false;
966
967
  // If the continue block is complex, fallback to "dumb" for loops.
968
  bool complex_continue = false;
969
970
  // Do we need a ladder variable to defer breaking out of a loop construct after a switch block?
971
  bool need_ladder_break = false;
972
973
  // If marked, we have explicitly handled Phi from this block, so skip any flushes related to that on a branch.
974
  // Used to handle an edge case with switch and case-label fallthrough where fall-through writes to Phi.
975
  BlockID ignore_phi_from_block = 0;
976
977
  // The dominating block which this block might be within.
978
  // Used in continue; blocks to determine if we really need to write continue.
979
  BlockID loop_dominator = 0;
980
981
  // All access to these variables are dominated by this block,
982
  // so before branching anywhere we need to make sure that we declare these variables.
983
  SmallVector<VariableID> dominated_variables;
984
  SmallVector<bool> rearm_dominated_variables;
985
986
  // These are variables which should be declared in a for loop header, if we
987
  // fail to use a classic for-loop,
988
  // we remove these variables, and fall back to regular variables outside the loop.
989
  SmallVector<VariableID> loop_variables;
990
991
  // Some expressions are control-flow dependent, i.e. any instruction which relies on derivatives or
992
  // sub-group-like operations.
993
  // Make sure that we only use these expressions in the original block.
994
  SmallVector<ID> invalidate_expressions;
995
996
  SPIRV_CROSS_DECLARE_CLONE(SPIRBlock)
997
};
998
999
struct SPIRFunction : IVariant
1000
{
1001
  enum
1002
  {
1003
    type = TypeFunction
1004
  };
1005
1006
  SPIRFunction(TypeID return_type_, TypeID function_type_)
1007
831
      : return_type(return_type_)
1008
831
      , function_type(function_type_)
1009
831
  {
1010
831
  }
1011
1012
  struct Parameter
1013
  {
1014
    TypeID type;
1015
    ID id;
1016
    uint32_t read_count;
1017
    uint32_t write_count;
1018
1019
    // Set to true if this parameter aliases a global variable,
1020
    // used mostly in Metal where global variables
1021
    // have to be passed down to functions as regular arguments.
1022
    // However, for this kind of variable, we should not care about
1023
    // read and write counts as access to the function arguments
1024
    // is not local to the function in question.
1025
    bool alias_global_variable;
1026
  };
1027
1028
  // When calling a function, and we're remapping separate image samplers,
1029
  // resolve these arguments into combined image samplers and pass them
1030
  // as additional arguments in this order.
1031
  // It gets more complicated as functions can pull in their own globals
1032
  // and combine them with parameters,
1033
  // so we need to distinguish if something is local parameter index
1034
  // or a global ID.
1035
  struct CombinedImageSamplerParameter
1036
  {
1037
    VariableID id;
1038
    VariableID image_id;
1039
    VariableID sampler_id;
1040
    bool global_image;
1041
    bool global_sampler;
1042
    bool depth;
1043
  };
1044
1045
  TypeID return_type;
1046
  TypeID function_type;
1047
  SmallVector<Parameter> arguments;
1048
1049
  // Can be used by backends to add magic arguments.
1050
  // Currently used by combined image/sampler implementation.
1051
1052
  SmallVector<Parameter> shadow_arguments;
1053
  SmallVector<VariableID> local_variables;
1054
  BlockID entry_block = 0;
1055
  SmallVector<BlockID> blocks;
1056
  SmallVector<CombinedImageSamplerParameter> combined_parameters;
1057
1058
  struct EntryLine
1059
  {
1060
    uint32_t file_id = 0;
1061
    uint32_t line_literal = 0;
1062
  };
1063
  EntryLine entry_line;
1064
1065
  void add_local_variable(VariableID id)
1066
2.93k
  {
1067
2.93k
    local_variables.push_back(id);
1068
2.93k
  }
1069
1070
  void add_parameter(TypeID parameter_type, ID id, bool alias_global_variable = false)
1071
321
  {
1072
    // Arguments are read-only until proven otherwise.
1073
321
    arguments.push_back({ parameter_type, id, 0u, 0u, alias_global_variable });
1074
321
  }
1075
1076
  // Hooks to be run when the function returns.
1077
  // Mostly used for lowering internal data structures onto flattened structures.
1078
  // Need to defer this, because they might rely on things which change during compilation.
1079
  // Intentionally not a small vector, this one is rare, and std::function can be large.
1080
  Vector<std::function<void()>> fixup_hooks_out;
1081
1082
  // Hooks to be run when the function begins.
1083
  // Mostly used for populating internal data structures from flattened structures.
1084
  // Need to defer this, because they might rely on things which change during compilation.
1085
  // Intentionally not a small vector, this one is rare, and std::function can be large.
1086
  Vector<std::function<void()>> fixup_hooks_in;
1087
1088
  // On function entry, make sure to copy a constant array into thread addr space to work around
1089
  // the case where we are passing a constant array by value to a function on backends which do not
1090
  // consider arrays value types.
1091
  SmallVector<ID> constant_arrays_needed_on_stack;
1092
1093
  // Does this function (or any function called by it), emit geometry?
1094
  bool emits_geometry = false;
1095
1096
  bool active = false;
1097
  bool flush_undeclared = true;
1098
  bool do_combined_parameters = true;
1099
1100
  SPIRV_CROSS_DECLARE_CLONE(SPIRFunction)
1101
};
1102
1103
struct SPIRAccessChain : IVariant
1104
{
1105
  enum
1106
  {
1107
    type = TypeAccessChain
1108
  };
1109
1110
  SPIRAccessChain(TypeID basetype_, spv::StorageClass storage_, std::string base_, std::string dynamic_index_,
1111
                  int32_t static_index_)
1112
      : basetype(basetype_)
1113
      , storage(storage_)
1114
      , base(std::move(base_))
1115
      , dynamic_index(std::move(dynamic_index_))
1116
      , static_index(static_index_)
1117
0
  {
1118
0
  }
1119
1120
  // The access chain represents an offset into a buffer.
1121
  // Some backends need more complicated handling of access chains to be able to use buffers, like HLSL
1122
  // which has no usable buffer type ala GLSL SSBOs.
1123
  // StructuredBuffer is too limited, so our only option is to deal with ByteAddressBuffer which works with raw addresses.
1124
1125
  TypeID basetype;
1126
  spv::StorageClass storage;
1127
  std::string base;
1128
  std::string dynamic_index;
1129
  int32_t static_index;
1130
1131
  VariableID loaded_from = 0;
1132
  uint32_t matrix_stride = 0;
1133
  uint32_t array_stride = 0;
1134
  bool row_major_matrix = false;
1135
  bool immutable = false;
1136
1137
  // By reading this expression, we implicitly read these expressions as well.
1138
  // Used by access chain Store and Load since we read multiple expressions in this case.
1139
  SmallVector<ID> implied_read_expressions;
1140
1141
  SPIRV_CROSS_DECLARE_CLONE(SPIRAccessChain)
1142
};
1143
1144
struct SPIRVariable : IVariant
1145
{
1146
  enum
1147
  {
1148
    type = TypeVariable
1149
  };
1150
1151
  SPIRVariable() = default;
1152
  SPIRVariable(TypeID basetype_, spv::StorageClass storage_, ID initializer_ = 0, VariableID basevariable_ = 0)
1153
3.97k
      : basetype(basetype_)
1154
3.97k
      , storage(storage_)
1155
3.97k
      , initializer(initializer_)
1156
3.97k
      , basevariable(basevariable_)
1157
3.97k
  {
1158
3.97k
  }
1159
1160
  TypeID basetype = 0;
1161
  spv::StorageClass storage = spv::StorageClassGeneric;
1162
  uint32_t decoration = 0;
1163
  ID initializer = 0;
1164
  VariableID basevariable = 0;
1165
1166
  SmallVector<uint32_t> dereference_chain;
1167
  bool compat_builtin = false;
1168
1169
  // If a variable is shadowed, we only statically assign to it
1170
  // and never actually emit a statement for it.
1171
  // When we read the variable as an expression, just forward
1172
  // shadowed_id as the expression.
1173
  bool statically_assigned = false;
1174
  ID static_expression = 0;
1175
1176
  // Temporaries which can remain forwarded as long as this variable is not modified.
1177
  SmallVector<ID> dependees;
1178
1179
  // ShaderDebugInfo local variables attached to this variable via DebugDeclare
1180
  SmallVector<ID> debug_local_variables;
1181
1182
  bool deferred_declaration = false;
1183
  bool phi_variable = false;
1184
1185
  // Used to deal with Phi variable flushes. See flush_phi().
1186
  bool allocate_temporary_copy = false;
1187
1188
  bool remapped_variable = false;
1189
  uint32_t remapped_components = 0;
1190
1191
  // The block which dominates all access to this variable.
1192
  BlockID dominator = 0;
1193
  // If true, this variable is a loop variable, when accessing the variable
1194
  // outside a loop,
1195
  // we should statically forward it.
1196
  bool loop_variable = false;
1197
  // Set to true while we're inside the for loop.
1198
  bool loop_variable_enable = false;
1199
1200
  // Used to find global LUTs
1201
  bool is_written_to = false;
1202
1203
  SPIRFunction::Parameter *parameter = nullptr;
1204
1205
  SPIRV_CROSS_DECLARE_CLONE(SPIRVariable)
1206
};
1207
1208
struct SPIRConstant : IVariant
1209
{
1210
  enum
1211
  {
1212
    type = TypeConstant
1213
  };
1214
1215
  union Constant
1216
  {
1217
    uint32_t u32;
1218
    int32_t i32;
1219
    float f32;
1220
1221
    uint64_t u64;
1222
    int64_t i64;
1223
    double f64;
1224
  };
1225
1226
  struct ConstantVector
1227
  {
1228
    Constant r[4];
1229
    // If != 0, this element is a specialization constant, and we should keep track of it as such.
1230
    ID id[4];
1231
    uint32_t vecsize = 1;
1232
1233
    ConstantVector()
1234
374k
    {
1235
374k
      memset(r, 0, sizeof(r));
1236
374k
    }
1237
  };
1238
1239
  struct ConstantMatrix
1240
  {
1241
    ConstantVector c[4];
1242
    // If != 0, this column is a specialization constant, and we should keep track of it as such.
1243
    ID id[4];
1244
    uint32_t columns = 1;
1245
  };
1246
1247
  static inline float f16_to_f32(uint16_t u16_value)
1248
0
  {
1249
0
    // Based on the GLM implementation.
1250
0
    int s = (u16_value >> 15) & 0x1;
1251
0
    int e = (u16_value >> 10) & 0x1f;
1252
0
    int m = (u16_value >> 0) & 0x3ff;
1253
0
1254
0
    union
1255
0
    {
1256
0
      float f32;
1257
0
      uint32_t u32;
1258
0
    } u;
1259
0
1260
0
    if (e == 0)
1261
0
    {
1262
0
      if (m == 0)
1263
0
      {
1264
0
        u.u32 = uint32_t(s) << 31;
1265
0
        return u.f32;
1266
0
      }
1267
0
      else
1268
0
      {
1269
0
        while ((m & 0x400) == 0)
1270
0
        {
1271
0
          m <<= 1;
1272
0
          e--;
1273
0
        }
1274
0
1275
0
        e++;
1276
0
        m &= ~0x400;
1277
0
      }
1278
0
    }
1279
0
    else if (e == 31)
1280
0
    {
1281
0
      if (m == 0)
1282
0
      {
1283
0
        u.u32 = (uint32_t(s) << 31) | 0x7f800000u;
1284
0
        return u.f32;
1285
0
      }
1286
0
      else
1287
0
      {
1288
0
        u.u32 = (uint32_t(s) << 31) | 0x7f800000u | (m << 13);
1289
0
        return u.f32;
1290
0
      }
1291
0
    }
1292
0
1293
0
    e += 127 - 15;
1294
0
    m <<= 13;
1295
0
    u.u32 = (uint32_t(s) << 31) | (e << 23) | m;
1296
0
    return u.f32;
1297
0
  }
1298
1299
  static inline float fe4m3_to_f32(uint8_t v)
1300
0
  {
1301
0
    if ((v & 0x7f) == 0x7f)
1302
0
    {
1303
0
      union
1304
0
      {
1305
0
        float f32;
1306
0
        uint32_t u32;
1307
0
      } u;
1308
0
1309
0
      u.u32 = (v & 0x80) ? 0xffffffffu : 0x7fffffffu;
1310
0
      return u.f32;
1311
0
    }
1312
0
    else
1313
0
    {
1314
0
      // Reuse the FP16 to FP32 code. Cute bit-hackery.
1315
0
      return f16_to_f32((int16_t(int8_t(v)) << 7) & (0xffff ^ 0x4000)) * 256.0f;
1316
0
    }
1317
0
  }
1318
1319
  inline uint32_t specialization_constant_id(uint32_t col, uint32_t row) const
1320
0
  {
1321
0
    return m.c[col].id[row];
1322
0
  }
1323
1324
  inline uint32_t specialization_constant_id(uint32_t col) const
1325
0
  {
1326
0
    return m.id[col];
1327
0
  }
1328
1329
  inline uint32_t scalar(uint32_t col = 0, uint32_t row = 0) const
1330
215
  {
1331
215
    return m.c[col].r[row].u32;
1332
215
  }
1333
1334
  inline int16_t scalar_i16(uint32_t col = 0, uint32_t row = 0) const
1335
0
  {
1336
0
    return int16_t(m.c[col].r[row].u32 & 0xffffu);
1337
0
  }
1338
1339
  inline uint16_t scalar_u16(uint32_t col = 0, uint32_t row = 0) const
1340
0
  {
1341
0
    return uint16_t(m.c[col].r[row].u32 & 0xffffu);
1342
0
  }
1343
1344
  inline int8_t scalar_i8(uint32_t col = 0, uint32_t row = 0) const
1345
0
  {
1346
0
    return int8_t(m.c[col].r[row].u32 & 0xffu);
1347
0
  }
1348
1349
  inline uint8_t scalar_u8(uint32_t col = 0, uint32_t row = 0) const
1350
0
  {
1351
0
    return uint8_t(m.c[col].r[row].u32 & 0xffu);
1352
0
  }
1353
1354
  inline float scalar_f16(uint32_t col = 0, uint32_t row = 0) const
1355
0
  {
1356
0
    return f16_to_f32(scalar_u16(col, row));
1357
0
  }
1358
1359
  inline float scalar_bf16(uint32_t col = 0, uint32_t row = 0) const
1360
0
  {
1361
0
    uint32_t v = scalar_u16(col, row) << 16;
1362
0
    float fp32;
1363
0
    memcpy(&fp32, &v, sizeof(float));
1364
0
    return fp32;
1365
0
  }
1366
1367
  inline float scalar_floate4m3(uint32_t col = 0, uint32_t row = 0) const
1368
0
  {
1369
0
    return fe4m3_to_f32(scalar_u8(col, row));
1370
0
  }
1371
1372
  inline float scalar_bf8(uint32_t col = 0, uint32_t row = 0) const
1373
0
  {
1374
0
    return f16_to_f32(uint16_t(scalar_u8(col, row) << 8));
1375
0
  }
1376
1377
  inline float scalar_f32(uint32_t col = 0, uint32_t row = 0) const
1378
0
  {
1379
0
    return m.c[col].r[row].f32;
1380
0
  }
1381
1382
  inline int32_t scalar_i32(uint32_t col = 0, uint32_t row = 0) const
1383
90
  {
1384
90
    return m.c[col].r[row].i32;
1385
90
  }
1386
1387
  inline double scalar_f64(uint32_t col = 0, uint32_t row = 0) const
1388
0
  {
1389
0
    return m.c[col].r[row].f64;
1390
0
  }
1391
1392
  inline int64_t scalar_i64(uint32_t col = 0, uint32_t row = 0) const
1393
0
  {
1394
0
    return m.c[col].r[row].i64;
1395
0
  }
1396
1397
  inline uint64_t scalar_u64(uint32_t col = 0, uint32_t row = 0) const
1398
0
  {
1399
0
    return m.c[col].r[row].u64;
1400
0
  }
1401
1402
  inline const ConstantVector &vector() const
1403
0
  {
1404
0
    return m.c[0];
1405
0
  }
1406
1407
  inline uint32_t vector_size() const
1408
0
  {
1409
0
    return m.c[0].vecsize;
1410
0
  }
1411
1412
  inline uint32_t columns() const
1413
0
  {
1414
0
    return m.columns;
1415
0
  }
1416
1417
  inline void make_null(const SPIRType &constant_type_)
1418
28.8k
  {
1419
28.8k
    m = {};
1420
28.8k
    m.columns = constant_type_.columns;
1421
28.8k
    for (auto &c : m.c)
1422
115k
      c.vecsize = constant_type_.vecsize;
1423
28.8k
  }
1424
1425
  inline bool constant_is_null() const
1426
0
  {
1427
0
    if (specialization)
1428
0
      return false;
1429
0
    if (!subconstants.empty())
1430
0
      return false;
1431
0
1432
0
    for (uint32_t col = 0; col < columns(); col++)
1433
0
      for (uint32_t row = 0; row < vector_size(); row++)
1434
0
        if (scalar_u64(col, row) != 0)
1435
0
          return false;
1436
0
1437
0
    return true;
1438
0
  }
1439
1440
  explicit SPIRConstant(uint32_t constant_type_)
1441
28.9k
      : constant_type(constant_type_)
1442
28.9k
  {
1443
28.9k
  }
1444
1445
1.54k
  SPIRConstant() = default;
1446
1447
  SPIRConstant(TypeID constant_type_, const uint32_t *elements, uint32_t num_elements, bool specialized, bool replicated_ = false)
1448
27.4k
      : constant_type(constant_type_)
1449
27.4k
      , specialization(specialized)
1450
27.4k
      , replicated(replicated_)
1451
27.4k
  {
1452
27.4k
    subconstants.reserve(num_elements);
1453
32.7k
    for (uint32_t i = 0; i < num_elements; i++)
1454
5.25k
      subconstants.push_back(elements[i]);
1455
27.4k
    specialization = specialized;
1456
27.4k
  }
1457
1458
  // Construct scalar (32-bit).
1459
  SPIRConstant(TypeID constant_type_, uint32_t v0, bool specialized)
1460
6.43k
      : constant_type(constant_type_)
1461
6.43k
      , specialization(specialized)
1462
6.43k
  {
1463
6.43k
    m.c[0].r[0].u32 = v0;
1464
6.43k
    m.c[0].vecsize = 1;
1465
6.43k
    m.columns = 1;
1466
6.43k
  }
1467
1468
  // Construct scalar (64-bit).
1469
  SPIRConstant(TypeID constant_type_, uint64_t v0, bool specialized)
1470
5
      : constant_type(constant_type_)
1471
5
      , specialization(specialized)
1472
5
  {
1473
5
    m.c[0].r[0].u64 = v0;
1474
5
    m.c[0].vecsize = 1;
1475
5
    m.columns = 1;
1476
5
  }
1477
1478
  // Construct vectors and matrices.
1479
  SPIRConstant(TypeID constant_type_, const SPIRConstant *const *vector_elements, uint32_t num_elements,
1480
               bool specialized)
1481
383
      : constant_type(constant_type_)
1482
383
      , specialization(specialized)
1483
383
  {
1484
383
    bool matrix = vector_elements[0]->m.c[0].vecsize > 1;
1485
1486
383
    if (matrix)
1487
1
    {
1488
1
      m.columns = num_elements;
1489
1490
5
      for (uint32_t i = 0; i < num_elements; i++)
1491
4
      {
1492
4
        m.c[i] = vector_elements[i]->m.c[0];
1493
4
        if (vector_elements[i]->specialization)
1494
1
          m.id[i] = vector_elements[i]->self;
1495
4
      }
1496
1
    }
1497
382
    else
1498
382
    {
1499
382
      m.c[0].vecsize = num_elements;
1500
382
      m.columns = 1;
1501
1502
1.72k
      for (uint32_t i = 0; i < num_elements; i++)
1503
1.33k
      {
1504
1.33k
        m.c[0].r[i] = vector_elements[i]->m.c[0].r[0];
1505
1.33k
        if (vector_elements[i]->specialization)
1506
5
          m.c[0].id[i] = vector_elements[i]->self;
1507
1.33k
      }
1508
382
    }
1509
383
  }
1510
1511
  TypeID constant_type = 0;
1512
  ConstantMatrix m;
1513
1514
  // If this constant is a specialization constant (i.e. created with OpSpecConstant*).
1515
  bool specialization = false;
1516
  // If this constant is used as an array length which creates specialization restrictions on some backends.
1517
  bool is_used_as_array_length = false;
1518
1519
  // If true, this is a LUT, and should always be declared in the outer scope.
1520
  bool is_used_as_lut = false;
1521
1522
  // If this is a null constant of array type with specialized length.
1523
  // May require special handling in initializer
1524
  bool is_null_array_specialized_length = false;
1525
1526
  // For composites which are constant arrays, etc.
1527
  SmallVector<ConstantID> subconstants;
1528
1529
  // Whether the subconstants are intended to be replicated (e.g. OpConstantCompositeReplicateEXT)
1530
  bool replicated = false;
1531
1532
  // Non-Vulkan GLSL, HLSL and sometimes MSL emits defines for each specialization constant,
1533
  // and uses them to initialize the constant. This allows the user
1534
  // to still be able to specialize the value by supplying corresponding
1535
  // preprocessor directives before compiling the shader.
1536
  std::string specialization_constant_macro_name;
1537
1538
  SPIRV_CROSS_DECLARE_CLONE(SPIRConstant)
1539
};
1540
1541
// Variants have a very specific allocation scheme.
1542
struct ObjectPoolGroup
1543
{
1544
  std::unique_ptr<ObjectPoolBase> pools[TypeCount];
1545
};
1546
1547
class Variant
1548
{
1549
public:
1550
  explicit Variant(ObjectPoolGroup *group_)
1551
106M
      : group(group_)
1552
106M
  {
1553
106M
  }
1554
1555
  ~Variant()
1556
106M
  {
1557
106M
    if (holder)
1558
45.0k
      group->pools[type]->deallocate_opaque(holder);
1559
106M
  }
1560
1561
  // Marking custom move constructor as noexcept is important.
1562
  Variant(Variant &&other) SPIRV_CROSS_NOEXCEPT
1563
41.0k
  {
1564
41.0k
    *this = std::move(other);
1565
41.0k
  }
1566
1567
  // We cannot copy from other variant without our own pool group.
1568
  // Have to explicitly copy.
1569
  Variant(const Variant &variant) = delete;
1570
1571
  // Marking custom move constructor as noexcept is important.
1572
  Variant &operator=(Variant &&other) SPIRV_CROSS_NOEXCEPT
1573
41.0k
  {
1574
41.0k
    if (this != &other)
1575
41.0k
    {
1576
41.0k
      if (holder)
1577
0
        group->pools[type]->deallocate_opaque(holder);
1578
41.0k
      holder = other.holder;
1579
41.0k
      group = other.group;
1580
41.0k
      type = other.type;
1581
41.0k
      allow_type_rewrite = other.allow_type_rewrite;
1582
1583
41.0k
      other.holder = nullptr;
1584
41.0k
      other.type = TypeNone;
1585
41.0k
    }
1586
41.0k
    return *this;
1587
41.0k
  }
1588
1589
  // This copy/clone should only be called in the Compiler constructor.
1590
  // If this is called inside ::compile(), we invalidate any references we took higher in the stack.
1591
  // This should never happen.
1592
  Variant &operator=(const Variant &other)
1593
0
  {
1594
//#define SPIRV_CROSS_COPY_CONSTRUCTOR_SANITIZE
1595
#ifdef SPIRV_CROSS_COPY_CONSTRUCTOR_SANITIZE
1596
    abort();
1597
#endif
1598
0
    if (this != &other)
1599
0
    {
1600
0
      if (holder)
1601
0
        group->pools[type]->deallocate_opaque(holder);
1602
1603
0
      if (other.holder)
1604
0
        holder = other.holder->clone(group->pools[other.type].get());
1605
0
      else
1606
0
        holder = nullptr;
1607
1608
0
      type = other.type;
1609
0
      allow_type_rewrite = other.allow_type_rewrite;
1610
0
    }
1611
0
    return *this;
1612
0
  }
1613
1614
  void set(IVariant *val, Types new_type)
1615
93.4k
  {
1616
93.4k
    if (holder)
1617
48.4k
      group->pools[type]->deallocate_opaque(holder);
1618
93.4k
    holder = nullptr;
1619
1620
93.4k
    if (!allow_type_rewrite && type != TypeNone && type != new_type)
1621
10
    {
1622
10
      if (val)
1623
10
        group->pools[new_type]->deallocate_opaque(val);
1624
10
      SPIRV_CROSS_THROW("Overwriting a variant with new type.");
1625
10
    }
1626
1627
93.4k
    holder = val;
1628
93.4k
    type = new_type;
1629
93.4k
    allow_type_rewrite = false;
1630
93.4k
  }
1631
1632
  template <typename T, typename... Ts>
1633
  T *allocate_and_set(Types new_type, Ts &&... ts)
1634
93.4k
  {
1635
93.4k
    T *val = static_cast<ObjectPool<T> &>(*group->pools[new_type]).allocate(std::forward<Ts>(ts)...);
1636
93.4k
    set(val, new_type);
1637
93.4k
    return val;
1638
93.4k
  }
spirv_cross::SPIRString* spirv_cross::Variant::allocate_and_set<spirv_cross::SPIRString, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(spirv_cross::Types, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&&)
Line
Count
Source
1634
627
  {
1635
627
    T *val = static_cast<ObjectPool<T> &>(*group->pools[new_type]).allocate(std::forward<Ts>(ts)...);
1636
627
    set(val, new_type);
1637
627
    return val;
1638
627
  }
spirv_cross::SPIRUndef* spirv_cross::Variant::allocate_and_set<spirv_cross::SPIRUndef, unsigned int&>(spirv_cross::Types, unsigned int&)
Line
Count
Source
1634
70
  {
1635
70
    T *val = static_cast<ObjectPool<T> &>(*group->pools[new_type]).allocate(std::forward<Ts>(ts)...);
1636
70
    set(val, new_type);
1637
70
    return val;
1638
70
  }
spirv_cross::SPIRExtension* spirv_cross::Variant::allocate_and_set<spirv_cross::SPIRExtension, spirv_cross::SPIRExtension::Extension&>(spirv_cross::Types, spirv_cross::SPIRExtension::Extension&)
Line
Count
Source
1634
500
  {
1635
500
    T *val = static_cast<ObjectPool<T> &>(*group->pools[new_type]).allocate(std::forward<Ts>(ts)...);
1636
500
    set(val, new_type);
1637
500
    return val;
1638
500
  }
spirv_cross::SPIRString* spirv_cross::Variant::allocate_and_set<spirv_cross::SPIRString, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&>(spirv_cross::Types, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&)
Line
Count
Source
1634
40
  {
1635
40
    T *val = static_cast<ObjectPool<T> &>(*group->pools[new_type]).allocate(std::forward<Ts>(ts)...);
1636
40
    set(val, new_type);
1637
40
    return val;
1638
40
  }
spirv_cross::SPIRDebugLocalVariable* spirv_cross::Variant::allocate_and_set<spirv_cross::SPIRDebugLocalVariable>(spirv_cross::Types)
Line
Count
Source
1634
42
  {
1635
42
    T *val = static_cast<ObjectPool<T> &>(*group->pools[new_type]).allocate(std::forward<Ts>(ts)...);
1636
42
    set(val, new_type);
1637
42
    return val;
1638
42
  }
spirv_cross::SPIRType* spirv_cross::Variant::allocate_and_set<spirv_cross::SPIRType, spv::Op&>(spirv_cross::Types, spv::Op&)
Line
Count
Source
1634
9.88k
  {
1635
9.88k
    T *val = static_cast<ObjectPool<T> &>(*group->pools[new_type]).allocate(std::forward<Ts>(ts)...);
1636
9.88k
    set(val, new_type);
1637
9.88k
    return val;
1638
9.88k
  }
spirv_cross::SPIRType* spirv_cross::Variant::allocate_and_set<spirv_cross::SPIRType, spirv_cross::SPIRType&>(spirv_cross::Types, spirv_cross::SPIRType&)
Line
Count
Source
1634
4.54k
  {
1635
4.54k
    T *val = static_cast<ObjectPool<T> &>(*group->pools[new_type]).allocate(std::forward<Ts>(ts)...);
1636
4.54k
    set(val, new_type);
1637
4.54k
    return val;
1638
4.54k
  }
spirv_cross::SPIRFunctionPrototype* spirv_cross::Variant::allocate_and_set<spirv_cross::SPIRFunctionPrototype, unsigned int&>(spirv_cross::Types, unsigned int&)
Line
Count
Source
1634
1.21k
  {
1635
1.21k
    T *val = static_cast<ObjectPool<T> &>(*group->pools[new_type]).allocate(std::forward<Ts>(ts)...);
1636
1.21k
    set(val, new_type);
1637
1.21k
    return val;
1638
1.21k
  }
spirv_cross::SPIRVariable* spirv_cross::Variant::allocate_and_set<spirv_cross::SPIRVariable, unsigned int&, spv::StorageClass&, unsigned int&>(spirv_cross::Types, unsigned int&, spv::StorageClass&, unsigned int&)
Line
Count
Source
1634
2.40k
  {
1635
2.40k
    T *val = static_cast<ObjectPool<T> &>(*group->pools[new_type]).allocate(std::forward<Ts>(ts)...);
1636
2.40k
    set(val, new_type);
1637
2.40k
    return val;
1638
2.40k
  }
spirv_cross::SPIRVariable* spirv_cross::Variant::allocate_and_set<spirv_cross::SPIRVariable, unsigned int&, spv::StorageClass>(spirv_cross::Types, unsigned int&, spv::StorageClass&&)
Line
Count
Source
1634
1.56k
  {
1635
1.56k
    T *val = static_cast<ObjectPool<T> &>(*group->pools[new_type]).allocate(std::forward<Ts>(ts)...);
1636
1.56k
    set(val, new_type);
1637
1.56k
    return val;
1638
1.56k
  }
Unexecuted instantiation: spirv_cross::SPIRConstant* spirv_cross::Variant::allocate_and_set<spirv_cross::SPIRConstant, unsigned int const&, unsigned int*, int, bool, bool>(spirv_cross::Types, unsigned int const&, unsigned int*&&, int&&, bool&&, bool&&)
spirv_cross::SPIRConstant* spirv_cross::Variant::allocate_and_set<spirv_cross::SPIRConstant, unsigned int const&, unsigned long, bool>(spirv_cross::Types, unsigned int const&, unsigned long&&, bool&&)
Line
Count
Source
1634
5
  {
1635
5
    T *val = static_cast<ObjectPool<T> &>(*group->pools[new_type]).allocate(std::forward<Ts>(ts)...);
1636
5
    set(val, new_type);
1637
5
    return val;
1638
5
  }
spirv_cross::SPIRConstant* spirv_cross::Variant::allocate_and_set<spirv_cross::SPIRConstant, unsigned int const&, unsigned int const&, bool>(spirv_cross::Types, unsigned int const&, unsigned int const&, bool&&)
Line
Count
Source
1634
3.52k
  {
1635
3.52k
    T *val = static_cast<ObjectPool<T> &>(*group->pools[new_type]).allocate(std::forward<Ts>(ts)...);
1636
3.52k
    set(val, new_type);
1637
3.52k
    return val;
1638
3.52k
  }
spirv_cross::SPIRConstant* spirv_cross::Variant::allocate_and_set<spirv_cross::SPIRConstant, unsigned int const&, unsigned int, bool>(spirv_cross::Types, unsigned int const&, unsigned int&&, bool&&)
Line
Count
Source
1634
2.90k
  {
1635
2.90k
    T *val = static_cast<ObjectPool<T> &>(*group->pools[new_type]).allocate(std::forward<Ts>(ts)...);
1636
2.90k
    set(val, new_type);
1637
2.90k
    return val;
1638
2.90k
  }
spirv_cross::SPIRConstant* spirv_cross::Variant::allocate_and_set<spirv_cross::SPIRConstant, unsigned int&, unsigned int const*, unsigned int, bool>(spirv_cross::Types, unsigned int&, unsigned int const*&&, unsigned int&&, bool&&)
Line
Count
Source
1634
215
  {
1635
215
    T *val = static_cast<ObjectPool<T> &>(*group->pools[new_type]).allocate(std::forward<Ts>(ts)...);
1636
215
    set(val, new_type);
1637
215
    return val;
1638
215
  }
spirv_cross::SPIRConstant* spirv_cross::Variant::allocate_and_set<spirv_cross::SPIRConstant, unsigned int&, spirv_cross::SPIRConstant const* (&) [4], unsigned int&, bool>(spirv_cross::Types, unsigned int&, spirv_cross::SPIRConstant const* (&) [4], unsigned int&, bool&&)
Line
Count
Source
1634
383
  {
1635
383
    T *val = static_cast<ObjectPool<T> &>(*group->pools[new_type]).allocate(std::forward<Ts>(ts)...);
1636
383
    set(val, new_type);
1637
383
    return val;
1638
383
  }
spirv_cross::SPIRFunction* spirv_cross::Variant::allocate_and_set<spirv_cross::SPIRFunction, unsigned int&, unsigned int&>(spirv_cross::Types, unsigned int&, unsigned int&)
Line
Count
Source
1634
831
  {
1635
831
    T *val = static_cast<ObjectPool<T> &>(*group->pools[new_type]).allocate(std::forward<Ts>(ts)...);
1636
831
    set(val, new_type);
1637
831
    return val;
1638
831
  }
spirv_cross::SPIRBlock* spirv_cross::Variant::allocate_and_set<spirv_cross::SPIRBlock>(spirv_cross::Types)
Line
Count
Source
1634
7.95k
  {
1635
7.95k
    T *val = static_cast<ObjectPool<T> &>(*group->pools[new_type]).allocate(std::forward<Ts>(ts)...);
1636
7.95k
    set(val, new_type);
1637
7.95k
    return val;
1638
7.95k
  }
spirv_cross::SPIRType* spirv_cross::Variant::allocate_and_set<spirv_cross::SPIRType, spv::Op>(spirv_cross::Types, spv::Op&&)
Line
Count
Source
1634
69
  {
1635
69
    T *val = static_cast<ObjectPool<T> &>(*group->pools[new_type]).allocate(std::forward<Ts>(ts)...);
1636
69
    set(val, new_type);
1637
69
    return val;
1638
69
  }
spirv_cross::SPIRConstant* spirv_cross::Variant::allocate_and_set<spirv_cross::SPIRConstant, unsigned int&>(spirv_cross::Types, unsigned int&)
Line
Count
Source
1634
28.9k
  {
1635
28.9k
    T *val = static_cast<ObjectPool<T> &>(*group->pools[new_type]).allocate(std::forward<Ts>(ts)...);
1636
28.9k
    set(val, new_type);
1637
28.9k
    return val;
1638
28.9k
  }
spirv_cross::SPIRConstantOp* spirv_cross::Variant::allocate_and_set<spirv_cross::SPIRConstantOp, unsigned int&, spv::Op&, unsigned int const*, unsigned int>(spirv_cross::Types, unsigned int&, spv::Op&, unsigned int const*&&, unsigned int&&)
Line
Count
Source
1634
517
  {
1635
517
    T *val = static_cast<ObjectPool<T> &>(*group->pools[new_type]).allocate(std::forward<Ts>(ts)...);
1636
517
    set(val, new_type);
1637
517
    return val;
1638
517
  }
spirv_cross::SPIRConstant* spirv_cross::Variant::allocate_and_set<spirv_cross::SPIRConstant, unsigned int&, unsigned int*, unsigned int, bool>(spirv_cross::Types, unsigned int&, unsigned int*&&, unsigned int&&, bool&&)
Line
Count
Source
1634
27.2k
  {
1635
27.2k
    T *val = static_cast<ObjectPool<T> &>(*group->pools[new_type]).allocate(std::forward<Ts>(ts)...);
1636
27.2k
    set(val, new_type);
1637
27.2k
    return val;
1638
27.2k
  }
1639
1640
  template <typename T>
1641
  T &get()
1642
87.9k
  {
1643
87.9k
    if (!holder)
1644
15
      SPIRV_CROSS_THROW("nullptr");
1645
87.8k
    if (static_cast<Types>(T::type) != type)
1646
4
      SPIRV_CROSS_THROW("Bad cast");
1647
87.8k
    return *static_cast<T *>(holder);
1648
87.8k
  }
spirv_cross::SPIRType& spirv_cross::Variant::get<spirv_cross::SPIRType>()
Line
Count
Source
1642
84.8k
  {
1643
84.8k
    if (!holder)
1644
5
      SPIRV_CROSS_THROW("nullptr");
1645
84.7k
    if (static_cast<Types>(T::type) != type)
1646
0
      SPIRV_CROSS_THROW("Bad cast");
1647
84.7k
    return *static_cast<T *>(holder);
1648
84.7k
  }
spirv_cross::SPIRExtension& spirv_cross::Variant::get<spirv_cross::SPIRExtension>()
Line
Count
Source
1642
1.12k
  {
1643
1.12k
    if (!holder)
1644
2
      SPIRV_CROSS_THROW("nullptr");
1645
1.12k
    if (static_cast<Types>(T::type) != type)
1646
0
      SPIRV_CROSS_THROW("Bad cast");
1647
1.12k
    return *static_cast<T *>(holder);
1648
1.12k
  }
spirv_cross::SPIRString& spirv_cross::Variant::get<spirv_cross::SPIRString>()
Line
Count
Source
1642
81
  {
1643
81
    if (!holder)
1644
2
      SPIRV_CROSS_THROW("nullptr");
1645
79
    if (static_cast<Types>(T::type) != type)
1646
1
      SPIRV_CROSS_THROW("Bad cast");
1647
78
    return *static_cast<T *>(holder);
1648
79
  }
spirv_cross::SPIRConstant& spirv_cross::Variant::get<spirv_cross::SPIRConstant>()
Line
Count
Source
1642
1.85k
  {
1643
1.85k
    if (!holder)
1644
4
      SPIRV_CROSS_THROW("nullptr");
1645
1.84k
    if (static_cast<Types>(T::type) != type)
1646
2
      SPIRV_CROSS_THROW("Bad cast");
1647
1.84k
    return *static_cast<T *>(holder);
1648
1.84k
  }
spirv_cross::SPIRDebugLocalVariable& spirv_cross::Variant::get<spirv_cross::SPIRDebugLocalVariable>()
Line
Count
Source
1642
16
  {
1643
16
    if (!holder)
1644
1
      SPIRV_CROSS_THROW("nullptr");
1645
15
    if (static_cast<Types>(T::type) != type)
1646
1
      SPIRV_CROSS_THROW("Bad cast");
1647
14
    return *static_cast<T *>(holder);
1648
15
  }
spirv_cross::SPIRVariable& spirv_cross::Variant::get<spirv_cross::SPIRVariable>()
Line
Count
Source
1642
14
  {
1643
14
    if (!holder)
1644
1
      SPIRV_CROSS_THROW("nullptr");
1645
13
    if (static_cast<Types>(T::type) != type)
1646
0
      SPIRV_CROSS_THROW("Bad cast");
1647
13
    return *static_cast<T *>(holder);
1648
13
  }
Unexecuted instantiation: spirv_cross::SPIRConstantOp& spirv_cross::Variant::get<spirv_cross::SPIRConstantOp>()
spirv_cross::SPIRUndef& spirv_cross::Variant::get<spirv_cross::SPIRUndef>()
Line
Count
Source
1642
16
  {
1643
16
    if (!holder)
1644
0
      SPIRV_CROSS_THROW("nullptr");
1645
16
    if (static_cast<Types>(T::type) != type)
1646
0
      SPIRV_CROSS_THROW("Bad cast");
1647
16
    return *static_cast<T *>(holder);
1648
16
  }
1649
1650
  template <typename T>
1651
  const T &get() const
1652
2.49k
  {
1653
2.49k
    if (!holder)
1654
4
      SPIRV_CROSS_THROW("nullptr");
1655
2.48k
    if (static_cast<Types>(T::type) != type)
1656
0
      SPIRV_CROSS_THROW("Bad cast");
1657
2.48k
    return *static_cast<const T *>(holder);
1658
2.48k
  }
1659
1660
  Types get_type() const
1661
125k
  {
1662
125k
    return type;
1663
125k
  }
1664
1665
  ID get_id() const
1666
0
  {
1667
0
    return holder ? holder->self : ID(0);
1668
0
  }
1669
1670
  bool empty() const
1671
186k
  {
1672
186k
    return !holder;
1673
186k
  }
1674
1675
  void reset()
1676
0
  {
1677
0
    if (holder)
1678
0
      group->pools[type]->deallocate_opaque(holder);
1679
0
    holder = nullptr;
1680
0
    type = TypeNone;
1681
0
  }
1682
1683
  void set_allow_type_rewrite()
1684
0
  {
1685
0
    allow_type_rewrite = true;
1686
0
  }
1687
1688
private:
1689
  ObjectPoolGroup *group = nullptr;
1690
  IVariant *holder = nullptr;
1691
  Types type = TypeNone;
1692
  bool allow_type_rewrite = false;
1693
};
1694
1695
template <typename T>
1696
T &variant_get(Variant &var)
1697
87.9k
{
1698
87.9k
  return var.get<T>();
1699
87.9k
}
spirv_cross::SPIRType& spirv_cross::variant_get<spirv_cross::SPIRType>(spirv_cross::Variant&)
Line
Count
Source
1697
84.8k
{
1698
84.8k
  return var.get<T>();
1699
84.8k
}
spirv_cross::SPIRExtension& spirv_cross::variant_get<spirv_cross::SPIRExtension>(spirv_cross::Variant&)
Line
Count
Source
1697
1.12k
{
1698
1.12k
  return var.get<T>();
1699
1.12k
}
spirv_cross::SPIRString& spirv_cross::variant_get<spirv_cross::SPIRString>(spirv_cross::Variant&)
Line
Count
Source
1697
81
{
1698
81
  return var.get<T>();
1699
81
}
spirv_cross::SPIRConstant& spirv_cross::variant_get<spirv_cross::SPIRConstant>(spirv_cross::Variant&)
Line
Count
Source
1697
1.85k
{
1698
1.85k
  return var.get<T>();
1699
1.85k
}
spirv_cross::SPIRDebugLocalVariable& spirv_cross::variant_get<spirv_cross::SPIRDebugLocalVariable>(spirv_cross::Variant&)
Line
Count
Source
1697
16
{
1698
16
  return var.get<T>();
1699
16
}
spirv_cross::SPIRVariable& spirv_cross::variant_get<spirv_cross::SPIRVariable>(spirv_cross::Variant&)
Line
Count
Source
1697
14
{
1698
14
  return var.get<T>();
1699
14
}
Unexecuted instantiation: spirv_cross::SPIRConstantOp& spirv_cross::variant_get<spirv_cross::SPIRConstantOp>(spirv_cross::Variant&)
spirv_cross::SPIRUndef& spirv_cross::variant_get<spirv_cross::SPIRUndef>(spirv_cross::Variant&)
Line
Count
Source
1697
16
{
1698
16
  return var.get<T>();
1699
16
}
1700
1701
template <typename T>
1702
const T &variant_get(const Variant &var)
1703
2.49k
{
1704
2.49k
  return var.get<T>();
1705
2.49k
}
1706
1707
template <typename T, typename... P>
1708
T &variant_set(Variant &var, P &&... args)
1709
93.4k
{
1710
93.4k
  auto *ptr = var.allocate_and_set<T>(static_cast<Types>(T::type), std::forward<P>(args)...);
1711
93.4k
  return *ptr;
1712
93.4k
}
spirv_cross::SPIRString& spirv_cross::variant_set<spirv_cross::SPIRString, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(spirv_cross::Variant&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&&)
Line
Count
Source
1709
627
{
1710
627
  auto *ptr = var.allocate_and_set<T>(static_cast<Types>(T::type), std::forward<P>(args)...);
1711
627
  return *ptr;
1712
627
}
spirv_cross::SPIRUndef& spirv_cross::variant_set<spirv_cross::SPIRUndef, unsigned int&>(spirv_cross::Variant&, unsigned int&)
Line
Count
Source
1709
70
{
1710
70
  auto *ptr = var.allocate_and_set<T>(static_cast<Types>(T::type), std::forward<P>(args)...);
1711
70
  return *ptr;
1712
70
}
spirv_cross::SPIRExtension& spirv_cross::variant_set<spirv_cross::SPIRExtension, spirv_cross::SPIRExtension::Extension&>(spirv_cross::Variant&, spirv_cross::SPIRExtension::Extension&)
Line
Count
Source
1709
500
{
1710
500
  auto *ptr = var.allocate_and_set<T>(static_cast<Types>(T::type), std::forward<P>(args)...);
1711
500
  return *ptr;
1712
500
}
spirv_cross::SPIRString& spirv_cross::variant_set<spirv_cross::SPIRString, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&>(spirv_cross::Variant&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&)
Line
Count
Source
1709
40
{
1710
40
  auto *ptr = var.allocate_and_set<T>(static_cast<Types>(T::type), std::forward<P>(args)...);
1711
40
  return *ptr;
1712
40
}
spirv_cross::SPIRDebugLocalVariable& spirv_cross::variant_set<spirv_cross::SPIRDebugLocalVariable>(spirv_cross::Variant&)
Line
Count
Source
1709
42
{
1710
42
  auto *ptr = var.allocate_and_set<T>(static_cast<Types>(T::type), std::forward<P>(args)...);
1711
42
  return *ptr;
1712
42
}
spirv_cross::SPIRType& spirv_cross::variant_set<spirv_cross::SPIRType, spv::Op&>(spirv_cross::Variant&, spv::Op&)
Line
Count
Source
1709
9.88k
{
1710
9.88k
  auto *ptr = var.allocate_and_set<T>(static_cast<Types>(T::type), std::forward<P>(args)...);
1711
9.88k
  return *ptr;
1712
9.88k
}
spirv_cross::SPIRType& spirv_cross::variant_set<spirv_cross::SPIRType, spirv_cross::SPIRType&>(spirv_cross::Variant&, spirv_cross::SPIRType&)
Line
Count
Source
1709
4.54k
{
1710
4.54k
  auto *ptr = var.allocate_and_set<T>(static_cast<Types>(T::type), std::forward<P>(args)...);
1711
4.54k
  return *ptr;
1712
4.54k
}
spirv_cross::SPIRFunctionPrototype& spirv_cross::variant_set<spirv_cross::SPIRFunctionPrototype, unsigned int&>(spirv_cross::Variant&, unsigned int&)
Line
Count
Source
1709
1.21k
{
1710
1.21k
  auto *ptr = var.allocate_and_set<T>(static_cast<Types>(T::type), std::forward<P>(args)...);
1711
1.21k
  return *ptr;
1712
1.21k
}
spirv_cross::SPIRVariable& spirv_cross::variant_set<spirv_cross::SPIRVariable, unsigned int&, spv::StorageClass&, unsigned int&>(spirv_cross::Variant&, unsigned int&, spv::StorageClass&, unsigned int&)
Line
Count
Source
1709
2.40k
{
1710
2.40k
  auto *ptr = var.allocate_and_set<T>(static_cast<Types>(T::type), std::forward<P>(args)...);
1711
2.40k
  return *ptr;
1712
2.40k
}
spirv_cross::SPIRVariable& spirv_cross::variant_set<spirv_cross::SPIRVariable, unsigned int&, spv::StorageClass>(spirv_cross::Variant&, unsigned int&, spv::StorageClass&&)
Line
Count
Source
1709
1.56k
{
1710
1.56k
  auto *ptr = var.allocate_and_set<T>(static_cast<Types>(T::type), std::forward<P>(args)...);
1711
1.56k
  return *ptr;
1712
1.56k
}
Unexecuted instantiation: spirv_cross::SPIRConstant& spirv_cross::variant_set<spirv_cross::SPIRConstant, unsigned int const&, unsigned int*, int, bool, bool>(spirv_cross::Variant&, unsigned int const&, unsigned int*&&, int&&, bool&&, bool&&)
spirv_cross::SPIRConstant& spirv_cross::variant_set<spirv_cross::SPIRConstant, unsigned int const&, unsigned long, bool>(spirv_cross::Variant&, unsigned int const&, unsigned long&&, bool&&)
Line
Count
Source
1709
5
{
1710
5
  auto *ptr = var.allocate_and_set<T>(static_cast<Types>(T::type), std::forward<P>(args)...);
1711
5
  return *ptr;
1712
5
}
spirv_cross::SPIRConstant& spirv_cross::variant_set<spirv_cross::SPIRConstant, unsigned int const&, unsigned int const&, bool>(spirv_cross::Variant&, unsigned int const&, unsigned int const&, bool&&)
Line
Count
Source
1709
3.52k
{
1710
3.52k
  auto *ptr = var.allocate_and_set<T>(static_cast<Types>(T::type), std::forward<P>(args)...);
1711
3.52k
  return *ptr;
1712
3.52k
}
spirv_cross::SPIRConstant& spirv_cross::variant_set<spirv_cross::SPIRConstant, unsigned int const&, unsigned int, bool>(spirv_cross::Variant&, unsigned int const&, unsigned int&&, bool&&)
Line
Count
Source
1709
2.90k
{
1710
2.90k
  auto *ptr = var.allocate_and_set<T>(static_cast<Types>(T::type), std::forward<P>(args)...);
1711
2.90k
  return *ptr;
1712
2.90k
}
spirv_cross::SPIRConstant& spirv_cross::variant_set<spirv_cross::SPIRConstant, unsigned int&, unsigned int const*, unsigned int, bool>(spirv_cross::Variant&, unsigned int&, unsigned int const*&&, unsigned int&&, bool&&)
Line
Count
Source
1709
215
{
1710
215
  auto *ptr = var.allocate_and_set<T>(static_cast<Types>(T::type), std::forward<P>(args)...);
1711
215
  return *ptr;
1712
215
}
spirv_cross::SPIRConstant& spirv_cross::variant_set<spirv_cross::SPIRConstant, unsigned int&, spirv_cross::SPIRConstant const* (&) [4], unsigned int&, bool>(spirv_cross::Variant&, unsigned int&, spirv_cross::SPIRConstant const* (&) [4], unsigned int&, bool&&)
Line
Count
Source
1709
383
{
1710
383
  auto *ptr = var.allocate_and_set<T>(static_cast<Types>(T::type), std::forward<P>(args)...);
1711
383
  return *ptr;
1712
383
}
spirv_cross::SPIRFunction& spirv_cross::variant_set<spirv_cross::SPIRFunction, unsigned int&, unsigned int&>(spirv_cross::Variant&, unsigned int&, unsigned int&)
Line
Count
Source
1709
831
{
1710
831
  auto *ptr = var.allocate_and_set<T>(static_cast<Types>(T::type), std::forward<P>(args)...);
1711
831
  return *ptr;
1712
831
}
spirv_cross::SPIRBlock& spirv_cross::variant_set<spirv_cross::SPIRBlock>(spirv_cross::Variant&)
Line
Count
Source
1709
7.95k
{
1710
7.95k
  auto *ptr = var.allocate_and_set<T>(static_cast<Types>(T::type), std::forward<P>(args)...);
1711
7.95k
  return *ptr;
1712
7.95k
}
spirv_cross::SPIRType& spirv_cross::variant_set<spirv_cross::SPIRType, spv::Op>(spirv_cross::Variant&, spv::Op&&)
Line
Count
Source
1709
69
{
1710
69
  auto *ptr = var.allocate_and_set<T>(static_cast<Types>(T::type), std::forward<P>(args)...);
1711
69
  return *ptr;
1712
69
}
spirv_cross::SPIRConstant& spirv_cross::variant_set<spirv_cross::SPIRConstant, unsigned int&>(spirv_cross::Variant&, unsigned int&)
Line
Count
Source
1709
28.9k
{
1710
28.9k
  auto *ptr = var.allocate_and_set<T>(static_cast<Types>(T::type), std::forward<P>(args)...);
1711
28.9k
  return *ptr;
1712
28.9k
}
spirv_cross::SPIRConstantOp& spirv_cross::variant_set<spirv_cross::SPIRConstantOp, unsigned int&, spv::Op&, unsigned int const*, unsigned int>(spirv_cross::Variant&, unsigned int&, spv::Op&, unsigned int const*&&, unsigned int&&)
Line
Count
Source
1709
517
{
1710
517
  auto *ptr = var.allocate_and_set<T>(static_cast<Types>(T::type), std::forward<P>(args)...);
1711
517
  return *ptr;
1712
517
}
spirv_cross::SPIRConstant& spirv_cross::variant_set<spirv_cross::SPIRConstant, unsigned int&, unsigned int*, unsigned int, bool>(spirv_cross::Variant&, unsigned int&, unsigned int*&&, unsigned int&&, bool&&)
Line
Count
Source
1709
27.2k
{
1710
27.2k
  auto *ptr = var.allocate_and_set<T>(static_cast<Types>(T::type), std::forward<P>(args)...);
1711
27.2k
  return *ptr;
1712
27.2k
}
1713
1714
struct AccessChainMeta
1715
{
1716
  uint32_t storage_physical_type = 0;
1717
  bool need_transpose = false;
1718
  bool storage_is_packed = false;
1719
  bool storage_is_invariant = false;
1720
  bool flattened_struct = false;
1721
  bool relaxed_precision = false;
1722
  bool access_meshlet_position_y = false;
1723
  bool chain_is_builtin = false;
1724
  spv::BuiltIn builtin = {};
1725
};
1726
1727
enum ExtendedDecorations
1728
{
1729
  // Marks if a buffer block is re-packed, i.e. member declaration might be subject to PhysicalTypeID remapping and padding.
1730
  SPIRVCrossDecorationBufferBlockRepacked = 0,
1731
1732
  // A type in a buffer block might be declared with a different physical type than the logical type.
1733
  // If this is not set, PhysicalTypeID == the SPIR-V type as declared.
1734
  SPIRVCrossDecorationPhysicalTypeID,
1735
1736
  // Marks if the physical type is to be declared with tight packing rules, i.e. packed_floatN on MSL and friends.
1737
  // If this is set, PhysicalTypeID might also be set. It can be set to same as logical type if all we're doing
1738
  // is converting float3 to packed_float3 for example.
1739
  // If this is marked on a struct, it means the struct itself must use only Packed types for all its members.
1740
  SPIRVCrossDecorationPhysicalTypePacked,
1741
1742
  // The padding in bytes before declaring this struct member.
1743
  // If used on a struct type, marks the target size of a struct.
1744
  SPIRVCrossDecorationPaddingTarget,
1745
1746
  SPIRVCrossDecorationInterfaceMemberIndex,
1747
  SPIRVCrossDecorationInterfaceOrigID,
1748
  SPIRVCrossDecorationResourceIndexPrimary,
1749
  // Used for decorations like resource indices for samplers when part of combined image samplers.
1750
  // A variable might need to hold two resource indices in this case.
1751
  SPIRVCrossDecorationResourceIndexSecondary,
1752
  // Used for resource indices for multiplanar images when part of combined image samplers.
1753
  SPIRVCrossDecorationResourceIndexTertiary,
1754
  SPIRVCrossDecorationResourceIndexQuaternary,
1755
1756
  // Marks a buffer block for using explicit offsets (GLSL/HLSL).
1757
  SPIRVCrossDecorationExplicitOffset,
1758
1759
  // Apply to a variable in the Input storage class; marks it as holding the base group passed to vkCmdDispatchBase(),
1760
  // or the base vertex and instance indices passed to vkCmdDrawIndexed().
1761
  // In MSL, this is used to adjust the WorkgroupId and GlobalInvocationId variables in compute shaders,
1762
  // and to hold the BaseVertex and BaseInstance variables in vertex shaders.
1763
  SPIRVCrossDecorationBuiltInDispatchBase,
1764
1765
  // Apply to a variable that is a function parameter; marks it as being a "dynamic"
1766
  // combined image-sampler. In MSL, this is used when a function parameter might hold
1767
  // either a regular combined image-sampler or one that has an attached sampler
1768
  // Y'CbCr conversion.
1769
  SPIRVCrossDecorationDynamicImageSampler,
1770
1771
  // Apply to a variable in the Input storage class; marks it as holding the size of the stage
1772
  // input grid.
1773
  // In MSL, this is used to hold the vertex and instance counts in a tessellation pipeline
1774
  // vertex shader.
1775
  SPIRVCrossDecorationBuiltInStageInputSize,
1776
1777
  // Apply to any access chain of a tessellation I/O variable; stores the type of the sub-object
1778
  // that was chained to, as recorded in the input variable itself. This is used in case the pointer
1779
  // is itself used as the base of an access chain, to calculate the original type of the sub-object
1780
  // chained to, in case a swizzle needs to be applied. This should not happen normally with valid
1781
  // SPIR-V, but the MSL backend can change the type of input variables, necessitating the
1782
  // addition of swizzles to keep the generated code compiling.
1783
  SPIRVCrossDecorationTessIOOriginalInputTypeID,
1784
1785
  // Apply to any access chain of an interface variable used with pull-model interpolation, where the variable is a
1786
  // vector but the resulting pointer is a scalar; stores the component index that is to be accessed by the chain.
1787
  // This is used when emitting calls to interpolation functions on the chain in MSL: in this case, the component
1788
  // must be applied to the result, since pull-model interpolants in MSL cannot be swizzled directly, but the
1789
  // results of interpolation can.
1790
  SPIRVCrossDecorationInterpolantComponentExpr,
1791
1792
  // Apply to any struct type that is used in the Workgroup storage class.
1793
  // This causes matrices in MSL prior to Metal 3.0 to be emitted using a special
1794
  // class that is convertible to the standard matrix type, to work around the
1795
  // lack of constructors in the 'threadgroup' address space.
1796
  SPIRVCrossDecorationWorkgroupStruct,
1797
1798
  SPIRVCrossDecorationOverlappingBinding,
1799
1800
  SPIRVCrossDecorationCount
1801
};
1802
1803
struct Meta
1804
{
1805
  struct Decoration
1806
  {
1807
    std::string alias;
1808
    std::string qualified_alias;
1809
    std::string user_semantic;
1810
    std::string user_type;
1811
    Bitset decoration_flags;
1812
    spv::BuiltIn builtin_type = spv::BuiltInMax;
1813
    uint32_t location = 0;
1814
    uint32_t component = 0;
1815
    uint32_t set = 0;
1816
    uint32_t binding = 0;
1817
    uint32_t offset = 0;
1818
    uint32_t xfb_buffer = 0;
1819
    uint32_t xfb_stride = 0;
1820
    uint32_t stream = 0;
1821
    uint32_t array_stride = 0;
1822
    uint32_t matrix_stride = 0;
1823
    uint32_t input_attachment = 0;
1824
    uint32_t spec_id = 0;
1825
    uint32_t index = 0;
1826
    spv::FPRoundingMode fp_rounding_mode = spv::FPRoundingModeMax;
1827
    spv::FPFastMathModeMask fp_fast_math_mode = spv::FPFastMathModeMaskNone;
1828
    bool builtin = false;
1829
    bool qualified_alias_explicit_override = false;
1830
1831
    struct Extended
1832
    {
1833
      Extended()
1834
29.7M
      {
1835
        // MSVC 2013 workaround to init like this.
1836
29.7M
        for (auto &v : values)
1837
536M
          v = 0;
1838
29.7M
      }
1839
1840
      Bitset flags;
1841
      uint32_t values[SPIRVCrossDecorationCount];
1842
    } extended;
1843
  };
1844
1845
  Decoration decoration;
1846
1847
  // Intentionally not a SmallVector. Decoration is large and somewhat rare.
1848
  Vector<Decoration> members;
1849
1850
  std::unordered_map<uint32_t, uint32_t> decoration_word_offset;
1851
1852
  // For SPV_GOOGLE_hlsl_functionality1.
1853
  bool hlsl_is_magic_counter_buffer = false;
1854
  // ID for the sibling counter buffer.
1855
  uint32_t hlsl_magic_counter_buffer = 0;
1856
};
1857
1858
// A user callback that remaps the type of any variable.
1859
// var_name is the declared name of the variable.
1860
// name_of_type is the textual name of the type which will be used in the code unless written to by the callback.
1861
using VariableTypeRemapCallback =
1862
    std::function<void(const SPIRType &type, const std::string &var_name, std::string &name_of_type)>;
1863
1864
class Hasher
1865
{
1866
public:
1867
  inline void u32(uint32_t value)
1868
0
  {
1869
0
    h = (h * 0x100000001b3ull) ^ value;
1870
0
  }
1871
1872
  inline uint64_t get() const
1873
0
  {
1874
0
    return h;
1875
0
  }
1876
1877
private:
1878
  uint64_t h = 0xcbf29ce484222325ull;
1879
};
1880
1881
static inline bool type_is_floating_point(const SPIRType &type)
1882
0
{
1883
0
  return type.basetype == SPIRType::Half || type.basetype == SPIRType::Float || type.basetype == SPIRType::Double ||
1884
0
         type.basetype == SPIRType::BFloat16 || type.basetype == SPIRType::FloatE5M2 || type.basetype == SPIRType::FloatE4M3;
1885
0
}
Unexecuted instantiation: parser_fuzzer.cpp:spirv_cross::type_is_floating_point(spirv_cross::SPIRType const&)
Unexecuted instantiation: spirv_parser.cpp:spirv_cross::type_is_floating_point(spirv_cross::SPIRType const&)
Unexecuted instantiation: spirv_cross_parsed_ir.cpp:spirv_cross::type_is_floating_point(spirv_cross::SPIRType const&)
1886
1887
static inline bool type_is_integral(const SPIRType &type)
1888
0
{
1889
0
  return type.basetype == SPIRType::SByte || type.basetype == SPIRType::UByte || type.basetype == SPIRType::Short ||
1890
0
         type.basetype == SPIRType::UShort || type.basetype == SPIRType::Int || type.basetype == SPIRType::UInt ||
1891
0
         type.basetype == SPIRType::Int64 || type.basetype == SPIRType::UInt64;
1892
0
}
Unexecuted instantiation: parser_fuzzer.cpp:spirv_cross::type_is_integral(spirv_cross::SPIRType const&)
Unexecuted instantiation: spirv_parser.cpp:spirv_cross::type_is_integral(spirv_cross::SPIRType const&)
Unexecuted instantiation: spirv_cross_parsed_ir.cpp:spirv_cross::type_is_integral(spirv_cross::SPIRType const&)
1893
1894
static inline SPIRType::BaseType to_signed_basetype(uint32_t width)
1895
229
{
1896
229
  switch (width)
1897
229
  {
1898
1
  case 8:
1899
1
    return SPIRType::SByte;
1900
1
  case 16:
1901
1
    return SPIRType::Short;
1902
222
  case 32:
1903
222
    return SPIRType::Int;
1904
5
  case 64:
1905
5
    return SPIRType::Int64;
1906
0
  default:
1907
0
    SPIRV_CROSS_THROW("Invalid bit width.");
1908
229
  }
1909
229
}
Unexecuted instantiation: parser_fuzzer.cpp:spirv_cross::to_signed_basetype(unsigned int)
spirv_parser.cpp:spirv_cross::to_signed_basetype(unsigned int)
Line
Count
Source
1895
229
{
1896
229
  switch (width)
1897
229
  {
1898
1
  case 8:
1899
1
    return SPIRType::SByte;
1900
1
  case 16:
1901
1
    return SPIRType::Short;
1902
222
  case 32:
1903
222
    return SPIRType::Int;
1904
5
  case 64:
1905
5
    return SPIRType::Int64;
1906
0
  default:
1907
0
    SPIRV_CROSS_THROW("Invalid bit width.");
1908
229
  }
1909
229
}
Unexecuted instantiation: spirv_cross_parsed_ir.cpp:spirv_cross::to_signed_basetype(unsigned int)
1910
1911
static inline SPIRType::BaseType to_unsigned_basetype(uint32_t width)
1912
321
{
1913
321
  switch (width)
1914
321
  {
1915
0
  case 8:
1916
0
    return SPIRType::UByte;
1917
0
  case 16:
1918
0
    return SPIRType::UShort;
1919
320
  case 32:
1920
320
    return SPIRType::UInt;
1921
0
  case 64:
1922
0
    return SPIRType::UInt64;
1923
1
  default:
1924
1
    SPIRV_CROSS_THROW("Invalid bit width.");
1925
321
  }
1926
321
}
Unexecuted instantiation: parser_fuzzer.cpp:spirv_cross::to_unsigned_basetype(unsigned int)
spirv_parser.cpp:spirv_cross::to_unsigned_basetype(unsigned int)
Line
Count
Source
1912
321
{
1913
321
  switch (width)
1914
321
  {
1915
0
  case 8:
1916
0
    return SPIRType::UByte;
1917
0
  case 16:
1918
0
    return SPIRType::UShort;
1919
320
  case 32:
1920
320
    return SPIRType::UInt;
1921
0
  case 64:
1922
0
    return SPIRType::UInt64;
1923
1
  default:
1924
1
    SPIRV_CROSS_THROW("Invalid bit width.");
1925
321
  }
1926
321
}
Unexecuted instantiation: spirv_cross_parsed_ir.cpp:spirv_cross::to_unsigned_basetype(unsigned int)
1927
1928
// Returns true if an arithmetic operation does not change behavior depending on signedness.
1929
static inline bool opcode_is_sign_invariant(spv::Op opcode)
1930
0
{
1931
0
  switch (opcode)
1932
0
  {
1933
0
  case spv::OpIEqual:
1934
0
  case spv::OpINotEqual:
1935
0
  case spv::OpISub:
1936
0
  case spv::OpIAdd:
1937
0
  case spv::OpIMul:
1938
0
  case spv::OpShiftLeftLogical:
1939
0
  case spv::OpBitwiseOr:
1940
0
  case spv::OpBitwiseXor:
1941
0
  case spv::OpBitwiseAnd:
1942
0
    return true;
1943
0
1944
0
  default:
1945
0
    return false;
1946
0
  }
1947
0
}
Unexecuted instantiation: parser_fuzzer.cpp:spirv_cross::opcode_is_sign_invariant(spv::Op)
Unexecuted instantiation: spirv_parser.cpp:spirv_cross::opcode_is_sign_invariant(spv::Op)
Unexecuted instantiation: spirv_cross_parsed_ir.cpp:spirv_cross::opcode_is_sign_invariant(spv::Op)
1948
1949
static inline bool opcode_can_promote_integer_implicitly(spv::Op opcode)
1950
0
{
1951
0
  switch (opcode)
1952
0
  {
1953
0
  case spv::OpSNegate:
1954
0
  case spv::OpNot:
1955
0
  case spv::OpBitwiseAnd:
1956
0
  case spv::OpBitwiseOr:
1957
0
  case spv::OpBitwiseXor:
1958
0
  case spv::OpShiftLeftLogical:
1959
0
  case spv::OpShiftRightLogical:
1960
0
  case spv::OpShiftRightArithmetic:
1961
0
  case spv::OpIAdd:
1962
0
  case spv::OpISub:
1963
0
  case spv::OpIMul:
1964
0
  case spv::OpSDiv:
1965
0
  case spv::OpUDiv:
1966
0
  case spv::OpSRem:
1967
0
  case spv::OpUMod:
1968
0
  case spv::OpSMod:
1969
0
    return true;
1970
0
1971
0
  default:
1972
0
    return false;
1973
0
  }
1974
0
}
Unexecuted instantiation: parser_fuzzer.cpp:spirv_cross::opcode_can_promote_integer_implicitly(spv::Op)
Unexecuted instantiation: spirv_parser.cpp:spirv_cross::opcode_can_promote_integer_implicitly(spv::Op)
Unexecuted instantiation: spirv_cross_parsed_ir.cpp:spirv_cross::opcode_can_promote_integer_implicitly(spv::Op)
1975
1976
struct SetBindingPair
1977
{
1978
  uint32_t desc_set;
1979
  uint32_t binding;
1980
1981
  inline bool operator==(const SetBindingPair &other) const
1982
0
  {
1983
0
    return desc_set == other.desc_set && binding == other.binding;
1984
0
  }
1985
1986
  inline bool operator<(const SetBindingPair &other) const
1987
0
  {
1988
0
    return desc_set < other.desc_set || (desc_set == other.desc_set && binding < other.binding);
1989
0
  }
1990
};
1991
1992
struct LocationComponentPair
1993
{
1994
  uint32_t location;
1995
  uint32_t component;
1996
1997
  inline bool operator==(const LocationComponentPair &other) const
1998
0
  {
1999
0
    return location == other.location && component == other.component;
2000
0
  }
2001
2002
  inline bool operator<(const LocationComponentPair &other) const
2003
0
  {
2004
0
    return location < other.location || (location == other.location && component < other.component);
2005
0
  }
2006
};
2007
2008
struct StageSetBinding
2009
{
2010
  spv::ExecutionModel model;
2011
  uint32_t desc_set;
2012
  uint32_t binding;
2013
2014
  inline bool operator==(const StageSetBinding &other) const
2015
0
  {
2016
0
    return model == other.model && desc_set == other.desc_set && binding == other.binding;
2017
0
  }
2018
};
2019
2020
struct InternalHasher
2021
{
2022
  inline size_t operator()(const SetBindingPair &value) const
2023
0
  {
2024
0
    // Quality of hash doesn't really matter here.
2025
0
    auto hash_set = std::hash<uint32_t>()(value.desc_set);
2026
0
    auto hash_binding = std::hash<uint32_t>()(value.binding);
2027
0
    return (hash_set * 0x10001b31) ^ hash_binding;
2028
0
  }
2029
2030
  inline size_t operator()(const LocationComponentPair &value) const
2031
0
  {
2032
0
    // Quality of hash doesn't really matter here.
2033
0
    auto hash_set = std::hash<uint32_t>()(value.location);
2034
0
    auto hash_binding = std::hash<uint32_t>()(value.component);
2035
0
    return (hash_set * 0x10001b31) ^ hash_binding;
2036
0
  }
2037
2038
  inline size_t operator()(const StageSetBinding &value) const
2039
0
  {
2040
0
    // Quality of hash doesn't really matter here.
2041
0
    auto hash_model = std::hash<uint32_t>()(value.model);
2042
0
    auto hash_set = std::hash<uint32_t>()(value.desc_set);
2043
0
    auto tmp_hash = (hash_model * 0x10001b31) ^ hash_set;
2044
0
    return (tmp_hash * 0x10001b31) ^ value.binding;
2045
0
  }
2046
};
2047
2048
// Special constant used in a {MSL,HLSL}ResourceBinding desc_set
2049
// element to indicate the bindings for the push constants.
2050
static const uint32_t ResourceBindingPushConstantDescriptorSet = ~(0u);
2051
2052
// Special constant used in a {MSL,HLSL}ResourceBinding binding
2053
// element to indicate the bindings for the push constants.
2054
static const uint32_t ResourceBindingPushConstantBinding = 0;
2055
} // namespace SPIRV_CROSS_NAMESPACE
2056
2057
namespace std
2058
{
2059
template <SPIRV_CROSS_NAMESPACE::Types type>
2060
struct hash<SPIRV_CROSS_NAMESPACE::TypedID<type>>
2061
{
2062
  size_t operator()(const SPIRV_CROSS_NAMESPACE::TypedID<type> &value) const
2063
405k
  {
2064
405k
    return std::hash<uint32_t>()(value);
2065
405k
  }
std::__1::hash<spirv_cross::TypedID<(spirv_cross::Types)0> >::operator()(spirv_cross::TypedID<(spirv_cross::Types)0> const&) const
Line
Count
Source
2063
396k
  {
2064
396k
    return std::hash<uint32_t>()(value);
2065
396k
  }
std::__1::hash<spirv_cross::TypedID<(spirv_cross::Types)4> >::operator()(spirv_cross::TypedID<(spirv_cross::Types)4> const&) const
Line
Count
Source
2063
7.76k
  {
2064
7.76k
    return std::hash<uint32_t>()(value);
2065
7.76k
  }
std::__1::hash<spirv_cross::TypedID<(spirv_cross::Types)6> >::operator()(spirv_cross::TypedID<(spirv_cross::Types)6> const&) const
Line
Count
Source
2063
1.32k
  {
2064
1.32k
    return std::hash<uint32_t>()(value);
2065
1.32k
  }
2066
};
2067
} // namespace std
2068
2069
#ifdef SPIRV_CROSS_SPV_HEADER_NAMESPACE_OVERRIDE
2070
#undef spv
2071
#endif
2072
#endif