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