| 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 undef or of struct type, and struct array sizes can use specialization constants. | 
| 78 | 	SmallVector<ID> ids_for_constant_undef_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 | 		 = 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> ; | 
| 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 = nullptr; | 
| 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(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 |  | 
| 234 | private: | 
| 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 |  |