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