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