Coverage Report

Created: 2025-09-27 06:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/spirv-cross/spirv_cross_parsed_ir.hpp
Line
Count
Source
1
/*
2
 * Copyright 2018-2021 Arm Limited
3
 * SPDX-License-Identifier: Apache-2.0 OR MIT
4
 *
5
 * Licensed under the Apache License, Version 2.0 (the "License");
6
 * you may not use this file except in compliance with the License.
7
 * You may obtain a copy of the License at
8
 *
9
 *     http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 */
17
18
/*
19
 * At your option, you may choose to accept this material under either:
20
 *  1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
21
 *  2. The MIT License, found at <http://opensource.org/licenses/MIT>.
22
 */
23
24
#ifndef SPIRV_CROSS_PARSED_IR_HPP
25
#define SPIRV_CROSS_PARSED_IR_HPP
26
27
#include "spirv_common.hpp"
28
#include <stdint.h>
29
#include <unordered_map>
30
31
namespace SPIRV_CROSS_NAMESPACE
32
{
33
using namespace SPIRV_CROSS_SPV_HEADER_NAMESPACE;
34
35
// This data structure holds all information needed to perform cross-compilation and reflection.
36
// It is the output of the Parser, but any implementation could create this structure.
37
// It is intentionally very "open" and struct-like with some helper functions to deal with decorations.
38
// Parser is the reference implementation of how this data structure should be filled in.
39
40
class ParsedIR
41
{
42
private:
43
  // This must be destroyed after the "ids" vector.
44
  std::unique_ptr<ObjectPoolGroup> pool_group;
45
46
public:
47
  ParsedIR();
48
49
  // Due to custom allocations from object pools, we cannot use a default copy constructor.
50
  ParsedIR(const ParsedIR &other);
51
  ParsedIR &operator=(const ParsedIR &other);
52
53
  // Moves are unproblematic, but we need to implement it anyways, since MSVC 2013 does not understand
54
  // how to default-implement these.
55
  ParsedIR(ParsedIR &&other) SPIRV_CROSS_NOEXCEPT;
56
  ParsedIR &operator=(ParsedIR &&other) SPIRV_CROSS_NOEXCEPT;
57
58
  // Resizes ids, meta and block_meta.
59
  void set_id_bounds(uint32_t bounds);
60
61
  // The raw SPIR-V, instructions and opcodes refer to this by offset + count.
62
  std::vector<uint32_t> spirv;
63
64
  // Holds various data structures which inherit from IVariant.
65
  SmallVector<Variant> ids;
66
67
  // Various meta data for IDs, decorations, names, etc.
68
  std::unordered_map<ID, Meta> meta;
69
70
  // Holds all IDs which have a certain type.
71
  // This is needed so we can iterate through a specific kind of resource quickly,
72
  // and in-order of module declaration.
73
  SmallVector<ID> ids_for_type[TypeCount];
74
75
  // Special purpose lists which contain a union of types.
76
  // This is needed so we can declare specialization constants and structs in an interleaved fashion,
77
  // among other things.
78
  // Constants can be undef or of struct type, and struct array sizes can use specialization constants.
79
  SmallVector<ID> ids_for_constant_undef_or_type;
80
  SmallVector<ID> ids_for_constant_or_variable;
81
82
  // We need to keep track of the width the Ops that contains a type for the
83
  // OpSwitch instruction, since this one doesn't contains the type in the
84
  // instruction itself. And in some case we need to cast the condition to
85
  // wider types. We only need the width to do the branch fixup since the
86
  // type check itself can be done at runtime
87
  std::unordered_map<ID, uint32_t> load_type_width;
88
89
  // Declared capabilities and extensions in the SPIR-V module.
90
  // Not really used except for reflection at the moment.
91
  SmallVector<Capability> declared_capabilities;
92
  SmallVector<std::string> declared_extensions;
93
94
  // Meta data about blocks. The cross-compiler needs to query if a block is either of these types.
95
  // It is a bitset as there can be more than one tag per block.
96
  enum BlockMetaFlagBits
97
  {
98
    BLOCK_META_LOOP_HEADER_BIT = 1 << 0,
99
    BLOCK_META_CONTINUE_BIT = 1 << 1,
100
    BLOCK_META_LOOP_MERGE_BIT = 1 << 2,
101
    BLOCK_META_SELECTION_MERGE_BIT = 1 << 3,
102
    BLOCK_META_MULTISELECT_MERGE_BIT = 1 << 4
103
  };
104
  using BlockMetaFlags = uint8_t;
105
  SmallVector<BlockMetaFlags> block_meta;
106
  std::unordered_map<BlockID, BlockID> continue_block_to_loop_header;
107
108
  // Normally, we'd stick SPIREntryPoint in ids array, but it conflicts with SPIRFunction.
109
  // Entry points can therefore be seen as some sort of meta structure.
110
  std::unordered_map<FunctionID, SPIREntryPoint> entry_points;
111
  FunctionID default_entry_point = 0;
112
113
  struct Source
114
  {
115
    SourceLanguage lang = SourceLanguageUnknown;
116
    uint32_t version = 0;
117
    bool es = false;
118
    bool known = false;
119
    bool hlsl = false;
120
121
891
    Source() = default;
122
  };
123
124
  Source source;
125
126
  AddressingModel addressing_model = AddressingModelMax;
127
  MemoryModel memory_model = MemoryModelMax;
128
129
  // Decoration handling methods.
130
  // Can be useful for simple "raw" reflection.
131
  // However, most members are here because the Parser needs most of these,
132
  // and might as well just have the whole suite of decoration/name handling in one place.
133
  void set_name(ID id, const std::string &name);
134
  const std::string &get_name(ID id) const;
135
  void set_decoration(ID id, Decoration decoration, uint32_t argument = 0);
136
  void set_decoration_string(ID id, Decoration decoration, const std::string &argument);
137
  bool has_decoration(ID id, Decoration decoration) const;
138
  uint32_t get_decoration(ID id, Decoration decoration) const;
139
  const std::string &get_decoration_string(ID id, Decoration decoration) const;
140
  const Bitset &get_decoration_bitset(ID id) const;
141
  void unset_decoration(ID id, Decoration decoration);
142
143
  // Decoration handling methods (for members of a struct).
144
  void set_member_name(TypeID id, uint32_t index, const std::string &name);
145
  const std::string &get_member_name(TypeID id, uint32_t index) const;
146
  void set_member_decoration(TypeID id, uint32_t index, Decoration decoration, uint32_t argument = 0);
147
  void set_member_decoration_string(TypeID id, uint32_t index, Decoration decoration,
148
                                    const std::string &argument);
149
  uint32_t get_member_decoration(TypeID id, uint32_t index, Decoration decoration) const;
150
  const std::string &get_member_decoration_string(TypeID id, uint32_t index, Decoration decoration) const;
151
  bool has_member_decoration(TypeID id, uint32_t index, Decoration decoration) const;
152
  const Bitset &get_member_decoration_bitset(TypeID id, uint32_t index) const;
153
  void unset_member_decoration(TypeID id, uint32_t index, Decoration decoration);
154
155
  void mark_used_as_array_length(ID id);
156
  uint32_t increase_bound_by(uint32_t count);
157
  Bitset get_buffer_block_flags(const SPIRVariable &var) const;
158
  Bitset get_buffer_block_type_flags(const SPIRType &type) const;
159
160
  void add_typed_id(Types type, ID id);
161
  void remove_typed_id(Types type, ID id);
162
163
  class LoopLock
164
  {
165
  public:
166
    explicit LoopLock(uint32_t *counter);
167
    LoopLock(const LoopLock &) = delete;
168
    void operator=(const LoopLock &) = delete;
169
    LoopLock(LoopLock &&other) SPIRV_CROSS_NOEXCEPT;
170
    LoopLock &operator=(LoopLock &&other) SPIRV_CROSS_NOEXCEPT;
171
    ~LoopLock();
172
173
  private:
174
    uint32_t *lock = nullptr;
175
  };
176
177
  // This must be held while iterating over a type ID array.
178
  // It is undefined if someone calls set<>() while we're iterating over a data structure, so we must
179
  // make sure that this case is avoided.
180
181
  // If we have a hard lock, it is an error to call set<>(), and an exception is thrown.
182
  // If we have a soft lock, we silently ignore any additions to the typed arrays.
183
  // This should only be used for physical ID remapping where we need to create an ID, but we will never
184
  // care about iterating over them.
185
  LoopLock create_loop_hard_lock() const;
186
  LoopLock create_loop_soft_lock() const;
187
188
  template <typename T, typename Op>
189
  void for_each_typed_id(const Op &op)
190
  {
191
    auto loop_lock = create_loop_hard_lock();
192
    for (auto &id : ids_for_type[T::type])
193
    {
194
      if (ids[id].get_type() == static_cast<Types>(T::type))
195
        op(id, get<T>(id));
196
    }
197
  }
198
199
  template <typename T, typename Op>
200
  void for_each_typed_id(const Op &op) const
201
  {
202
    auto loop_lock = create_loop_hard_lock();
203
    for (auto &id : ids_for_type[T::type])
204
    {
205
      if (ids[id].get_type() == static_cast<Types>(T::type))
206
        op(id, get<T>(id));
207
    }
208
  }
209
210
  template <typename T>
211
  void reset_all_of_type()
212
  {
213
    reset_all_of_type(static_cast<Types>(T::type));
214
  }
215
216
  void reset_all_of_type(Types type);
217
218
  Meta *find_meta(ID id);
219
  const Meta *find_meta(ID id) const;
220
221
  const std::string &get_empty_string() const
222
0
  {
223
0
    return empty_string;
224
0
  }
225
226
  void make_constant_null(uint32_t id, uint32_t type, bool add_to_typed_id_set);
227
228
  void fixup_reserved_names();
229
230
  static void sanitize_underscores(std::string &str);
231
  static void sanitize_identifier(std::string &str, bool member, bool allow_reserved_prefixes);
232
  static bool is_globally_reserved_identifier(std::string &str, bool allow_reserved_prefixes);
233
234
  uint32_t get_spirv_version() const;
235
236
private:
237
  template <typename T>
238
  T &get(uint32_t id)
239
6.36k
  {
240
6.36k
    return variant_get<T>(ids[id]);
241
6.36k
  }
Unexecuted instantiation: spirv_cross::SPIRVariable& spirv_cross::ParsedIR::get<spirv_cross::SPIRVariable>(unsigned int)
spirv_cross::SPIRConstant& spirv_cross::ParsedIR::get<spirv_cross::SPIRConstant>(unsigned int)
Line
Count
Source
239
1.25k
  {
240
1.25k
    return variant_get<T>(ids[id]);
241
1.25k
  }
Unexecuted instantiation: spirv_cross::SPIRConstantOp& spirv_cross::ParsedIR::get<spirv_cross::SPIRConstantOp>(unsigned int)
spirv_cross::SPIRType& spirv_cross::ParsedIR::get<spirv_cross::SPIRType>(unsigned int)
Line
Count
Source
239
5.10k
  {
240
5.10k
    return variant_get<T>(ids[id]);
241
5.10k
  }
242
243
  template <typename T>
244
  const T &get(uint32_t id) const
245
0
  {
246
0
    return variant_get<T>(ids[id]);
247
0
  }
248
249
  mutable uint32_t loop_iteration_depth_hard = 0;
250
  mutable uint32_t loop_iteration_depth_soft = 0;
251
  std::string empty_string;
252
  Bitset cleared_bitset;
253
254
  std::unordered_set<uint32_t> meta_needing_name_fixup;
255
};
256
} // namespace SPIRV_CROSS_NAMESPACE
257
258
#endif