• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
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 
39 class ParsedIR
40 {
41 private:
42 	// This must be destroyed after the "ids" vector.
43 	std::unique_ptr<ObjectPoolGroup> pool_group;
44 
45 public:
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 	// Declared capabilities and extensions in the SPIR-V module.
82 	// Not really used except for reflection at the moment.
83 	SmallVector<spv::Capability> declared_capabilities;
84 	SmallVector<std::string> declared_extensions;
85 
86 	// Meta data about blocks. The cross-compiler needs to query if a block is either of these types.
87 	// It is a bitset as there can be more than one tag per block.
88 	enum BlockMetaFlagBits
89 	{
90 		BLOCK_META_LOOP_HEADER_BIT = 1 << 0,
91 		BLOCK_META_CONTINUE_BIT = 1 << 1,
92 		BLOCK_META_LOOP_MERGE_BIT = 1 << 2,
93 		BLOCK_META_SELECTION_MERGE_BIT = 1 << 3,
94 		BLOCK_META_MULTISELECT_MERGE_BIT = 1 << 4
95 	};
96 	using BlockMetaFlags = uint8_t;
97 	SmallVector<BlockMetaFlags> block_meta;
98 	std::unordered_map<BlockID, BlockID> continue_block_to_loop_header;
99 
100 	// Normally, we'd stick SPIREntryPoint in ids array, but it conflicts with SPIRFunction.
101 	// Entry points can therefore be seen as some sort of meta structure.
102 	std::unordered_map<FunctionID, SPIREntryPoint> entry_points;
103 	FunctionID default_entry_point = 0;
104 
105 	struct Source
106 	{
107 		uint32_t version = 0;
108 		bool es = false;
109 		bool known = false;
110 		bool hlsl = false;
111 
112 		Source() = default;
113 	};
114 
115 	Source source;
116 
117 	spv::AddressingModel addressing_model = spv::AddressingModelMax;
118 	spv::MemoryModel memory_model = spv::MemoryModelMax;
119 
120 	// Decoration handling methods.
121 	// Can be useful for simple "raw" reflection.
122 	// However, most members are here because the Parser needs most of these,
123 	// and might as well just have the whole suite of decoration/name handling in one place.
124 	void set_name(ID id, const std::string &name);
125 	const std::string &get_name(ID id) const;
126 	void set_decoration(ID id, spv::Decoration decoration, uint32_t argument = 0);
127 	void set_decoration_string(ID id, spv::Decoration decoration, const std::string &argument);
128 	bool has_decoration(ID id, spv::Decoration decoration) const;
129 	uint32_t get_decoration(ID id, spv::Decoration decoration) const;
130 	const std::string &get_decoration_string(ID id, spv::Decoration decoration) const;
131 	const Bitset &get_decoration_bitset(ID id) const;
132 	void unset_decoration(ID id, spv::Decoration decoration);
133 
134 	// Decoration handling methods (for members of a struct).
135 	void set_member_name(TypeID id, uint32_t index, const std::string &name);
136 	const std::string &get_member_name(TypeID id, uint32_t index) const;
137 	void set_member_decoration(TypeID id, uint32_t index, spv::Decoration decoration, uint32_t argument = 0);
138 	void set_member_decoration_string(TypeID id, uint32_t index, spv::Decoration decoration,
139 	                                  const std::string &argument);
140 	uint32_t get_member_decoration(TypeID id, uint32_t index, spv::Decoration decoration) const;
141 	const std::string &get_member_decoration_string(TypeID id, uint32_t index, spv::Decoration decoration) const;
142 	bool has_member_decoration(TypeID id, uint32_t index, spv::Decoration decoration) const;
143 	const Bitset &get_member_decoration_bitset(TypeID id, uint32_t index) const;
144 	void unset_member_decoration(TypeID id, uint32_t index, spv::Decoration decoration);
145 
146 	void mark_used_as_array_length(ID id);
147 	uint32_t increase_bound_by(uint32_t count);
148 	Bitset get_buffer_block_flags(const SPIRVariable &var) const;
149 	Bitset get_buffer_block_type_flags(const SPIRType &type) const;
150 
151 	void add_typed_id(Types type, ID id);
152 	void remove_typed_id(Types type, ID id);
153 
154 	class LoopLock
155 	{
156 	public:
157 		explicit LoopLock(uint32_t *counter);
158 		LoopLock(const LoopLock &) = delete;
159 		void operator=(const LoopLock &) = delete;
160 		LoopLock(LoopLock &&other) SPIRV_CROSS_NOEXCEPT;
161 		LoopLock &operator=(LoopLock &&other) SPIRV_CROSS_NOEXCEPT;
162 		~LoopLock();
163 
164 	private:
165 		uint32_t *lock;
166 	};
167 
168 	// This must be held while iterating over a type ID array.
169 	// It is undefined if someone calls set<>() while we're iterating over a data structure, so we must
170 	// make sure that this case is avoided.
171 
172 	// If we have a hard lock, it is an error to call set<>(), and an exception is thrown.
173 	// If we have a soft lock, we silently ignore any additions to the typed arrays.
174 	// This should only be used for physical ID remapping where we need to create an ID, but we will never
175 	// care about iterating over them.
176 	LoopLock create_loop_hard_lock() const;
177 	LoopLock create_loop_soft_lock() const;
178 
179 	template <typename T, typename Op>
for_each_typed_id(const Op & op)180 	void for_each_typed_id(const Op &op)
181 	{
182 		auto loop_lock = create_loop_hard_lock();
183 		for (auto &id : ids_for_type[T::type])
184 		{
185 			if (ids[id].get_type() == static_cast<Types>(T::type))
186 				op(id, get<T>(id));
187 		}
188 	}
189 
190 	template <typename T, typename Op>
for_each_typed_id(const Op & op) const191 	void for_each_typed_id(const Op &op) const
192 	{
193 		auto loop_lock = create_loop_hard_lock();
194 		for (auto &id : ids_for_type[T::type])
195 		{
196 			if (ids[id].get_type() == static_cast<Types>(T::type))
197 				op(id, get<T>(id));
198 		}
199 	}
200 
201 	template <typename T>
reset_all_of_type()202 	void reset_all_of_type()
203 	{
204 		reset_all_of_type(static_cast<Types>(T::type));
205 	}
206 
207 	void reset_all_of_type(Types type);
208 
209 	Meta *find_meta(ID id);
210 	const Meta *find_meta(ID id) const;
211 
get_empty_string() const212 	const std::string &get_empty_string() const
213 	{
214 		return empty_string;
215 	}
216 
217 	void make_constant_null(uint32_t id, uint32_t type, bool add_to_typed_id_set);
218 
219 	void fixup_reserved_names();
220 
221 	static void sanitize_underscores(std::string &str);
222 	static void sanitize_identifier(std::string &str, bool member, bool allow_reserved_prefixes);
223 	static bool is_globally_reserved_identifier(std::string &str, bool allow_reserved_prefixes);
224 
225 	uint32_t get_spirv_version() const;
226 
227 private:
228 	template <typename T>
get(uint32_t id)229 	T &get(uint32_t id)
230 	{
231 		return variant_get<T>(ids[id]);
232 	}
233 
234 	template <typename T>
get(uint32_t id) const235 	const T &get(uint32_t id) const
236 	{
237 		return variant_get<T>(ids[id]);
238 	}
239 
240 	mutable uint32_t loop_iteration_depth_hard = 0;
241 	mutable uint32_t loop_iteration_depth_soft = 0;
242 	std::string empty_string;
243 	Bitset cleared_bitset;
244 
245 	std::unordered_set<uint32_t> meta_needing_name_fixup;
246 };
247 } // namespace SPIRV_CROSS_NAMESPACE
248 
249 #endif
250