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