1 /* 2 * Copyright 2015-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_HPP 25 #define SPIRV_CROSS_HPP 26 27 #include "spirv.hpp" 28 #include "spirv_cfg.hpp" 29 #include "spirv_cross_parsed_ir.hpp" 30 31 namespace SPIRV_CROSS_NAMESPACE 32 { 33 struct Resource 34 { 35 // Resources are identified with their SPIR-V ID. 36 // This is the ID of the OpVariable. 37 ID id; 38 39 // The type ID of the variable which includes arrays and all type modifications. 40 // This type ID is not suitable for parsing OpMemberDecoration of a struct and other decorations in general 41 // since these modifications typically happen on the base_type_id. 42 TypeID type_id; 43 44 // The base type of the declared resource. 45 // This type is the base type which ignores pointers and arrays of the type_id. 46 // This is mostly useful to parse decorations of the underlying type. 47 // base_type_id can also be obtained with get_type(get_type(type_id).self). 48 TypeID base_type_id; 49 50 // The declared name (OpName) of the resource. 51 // For Buffer blocks, the name actually reflects the externally 52 // visible Block name. 53 // 54 // This name can be retrieved again by using either 55 // get_name(id) or get_name(base_type_id) depending if it's a buffer block or not. 56 // 57 // This name can be an empty string in which case get_fallback_name(id) can be 58 // used which obtains a suitable fallback identifier for an ID. 59 std::string name; 60 }; 61 62 struct BuiltInResource 63 { 64 // This is mostly here to support reflection of builtins such as Position/PointSize/CullDistance/ClipDistance. 65 // This needs to be different from Resource since we can collect builtins from blocks. 66 // A builtin present here does not necessarily mean it's considered an active builtin, 67 // since variable ID "activeness" is only tracked on OpVariable level, not Block members. 68 // For that, update_active_builtins() -> has_active_builtin() can be used to further refine the reflection. 69 spv::BuiltIn builtin; 70 71 // This is the actual value type of the builtin. 72 // Typically float4, float, array<float, N> for the gl_PerVertex builtins. 73 // If the builtin is a control point, the control point array type will be stripped away here as appropriate. 74 TypeID value_type_id; 75 76 // This refers to the base resource which contains the builtin. 77 // If resource is a Block, it can hold multiple builtins, or it might not be a block. 78 // For advanced reflection scenarios, all information in builtin/value_type_id can be deduced, 79 // it's just more convenient this way. 80 Resource resource; 81 }; 82 83 struct ShaderResources 84 { 85 SmallVector<Resource> uniform_buffers; 86 SmallVector<Resource> storage_buffers; 87 SmallVector<Resource> stage_inputs; 88 SmallVector<Resource> stage_outputs; 89 SmallVector<Resource> subpass_inputs; 90 SmallVector<Resource> storage_images; 91 SmallVector<Resource> sampled_images; 92 SmallVector<Resource> atomic_counters; 93 SmallVector<Resource> acceleration_structures; 94 95 // There can only be one push constant block, 96 // but keep the vector in case this restriction is lifted in the future. 97 SmallVector<Resource> push_constant_buffers; 98 99 // For Vulkan GLSL and HLSL source, 100 // these correspond to separate texture2D and samplers respectively. 101 SmallVector<Resource> separate_images; 102 SmallVector<Resource> separate_samplers; 103 104 SmallVector<BuiltInResource> builtin_inputs; 105 SmallVector<BuiltInResource> builtin_outputs; 106 }; 107 108 struct CombinedImageSampler 109 { 110 // The ID of the sampler2D variable. 111 VariableID combined_id; 112 // The ID of the texture2D variable. 113 VariableID image_id; 114 // The ID of the sampler variable. 115 VariableID sampler_id; 116 }; 117 118 struct SpecializationConstant 119 { 120 // The ID of the specialization constant. 121 ConstantID id; 122 // The constant ID of the constant, used in Vulkan during pipeline creation. 123 uint32_t constant_id; 124 }; 125 126 struct BufferRange 127 { 128 unsigned index; 129 size_t offset; 130 size_t range; 131 }; 132 133 enum BufferPackingStandard 134 { 135 BufferPackingStd140, 136 BufferPackingStd430, 137 BufferPackingStd140EnhancedLayout, 138 BufferPackingStd430EnhancedLayout, 139 BufferPackingHLSLCbuffer, 140 BufferPackingHLSLCbufferPackOffset, 141 BufferPackingScalar, 142 BufferPackingScalarEnhancedLayout 143 }; 144 145 struct EntryPoint 146 { 147 std::string name; 148 spv::ExecutionModel execution_model; 149 }; 150 151 class Compiler 152 { 153 public: 154 friend class CFG; 155 friend class DominatorBuilder; 156 157 // The constructor takes a buffer of SPIR-V words and parses it. 158 // It will create its own parser, parse the SPIR-V and move the parsed IR 159 // as if you had called the constructors taking ParsedIR directly. 160 explicit Compiler(std::vector<uint32_t> ir); 161 Compiler(const uint32_t *ir, size_t word_count); 162 163 // This is more modular. We can also consume a ParsedIR structure directly, either as a move, or copy. 164 // With copy, we can reuse the same parsed IR for multiple Compiler instances. 165 explicit Compiler(const ParsedIR &ir); 166 explicit Compiler(ParsedIR &&ir); 167 168 virtual ~Compiler() = default; 169 170 // After parsing, API users can modify the SPIR-V via reflection and call this 171 // to disassemble the SPIR-V into the desired langauage. 172 // Sub-classes actually implement this. 173 virtual std::string compile(); 174 175 // Gets the identifier (OpName) of an ID. If not defined, an empty string will be returned. 176 const std::string &get_name(ID id) const; 177 178 // Applies a decoration to an ID. Effectively injects OpDecorate. 179 void set_decoration(ID id, spv::Decoration decoration, uint32_t argument = 0); 180 void set_decoration_string(ID id, spv::Decoration decoration, const std::string &argument); 181 182 // Overrides the identifier OpName of an ID. 183 // Identifiers beginning with underscores or identifiers which contain double underscores 184 // are reserved by the implementation. 185 void set_name(ID id, const std::string &name); 186 187 // Gets a bitmask for the decorations which are applied to ID. 188 // I.e. (1ull << spv::DecorationFoo) | (1ull << spv::DecorationBar) 189 const Bitset &get_decoration_bitset(ID id) const; 190 191 // Returns whether the decoration has been applied to the ID. 192 bool has_decoration(ID id, spv::Decoration decoration) const; 193 194 // Gets the value for decorations which take arguments. 195 // If the decoration is a boolean (i.e. spv::DecorationNonWritable), 196 // 1 will be returned. 197 // If decoration doesn't exist or decoration is not recognized, 198 // 0 will be returned. 199 uint32_t get_decoration(ID id, spv::Decoration decoration) const; 200 const std::string &get_decoration_string(ID id, spv::Decoration decoration) const; 201 202 // Removes the decoration for an ID. 203 void unset_decoration(ID id, spv::Decoration decoration); 204 205 // Gets the SPIR-V type associated with ID. 206 // Mostly used with Resource::type_id and Resource::base_type_id to parse the underlying type of a resource. 207 const SPIRType &get_type(TypeID id) const; 208 209 // Gets the SPIR-V type of a variable. 210 const SPIRType &get_type_from_variable(VariableID id) const; 211 212 // Gets the underlying storage class for an OpVariable. 213 spv::StorageClass get_storage_class(VariableID id) const; 214 215 // If get_name() is an empty string, get the fallback name which will be used 216 // instead in the disassembled source. 217 virtual const std::string get_fallback_name(ID id) const; 218 219 // If get_name() of a Block struct is an empty string, get the fallback name. 220 // This needs to be per-variable as multiple variables can use the same block type. 221 virtual const std::string get_block_fallback_name(VariableID id) const; 222 223 // Given an OpTypeStruct in ID, obtain the identifier for member number "index". 224 // This may be an empty string. 225 const std::string &get_member_name(TypeID id, uint32_t index) const; 226 227 // Given an OpTypeStruct in ID, obtain the OpMemberDecoration for member number "index". 228 uint32_t get_member_decoration(TypeID id, uint32_t index, spv::Decoration decoration) const; 229 const std::string &get_member_decoration_string(TypeID id, uint32_t index, spv::Decoration decoration) const; 230 231 // Sets the member identifier for OpTypeStruct ID, member number "index". 232 void set_member_name(TypeID id, uint32_t index, const std::string &name); 233 234 // Returns the qualified member identifier for OpTypeStruct ID, member number "index", 235 // or an empty string if no qualified alias exists 236 const std::string &get_member_qualified_name(TypeID type_id, uint32_t index) const; 237 238 // Gets the decoration mask for a member of a struct, similar to get_decoration_mask. 239 const Bitset &get_member_decoration_bitset(TypeID id, uint32_t index) const; 240 241 // Returns whether the decoration has been applied to a member of a struct. 242 bool has_member_decoration(TypeID id, uint32_t index, spv::Decoration decoration) const; 243 244 // Similar to set_decoration, but for struct members. 245 void set_member_decoration(TypeID id, uint32_t index, spv::Decoration decoration, uint32_t argument = 0); 246 void set_member_decoration_string(TypeID id, uint32_t index, spv::Decoration decoration, 247 const std::string &argument); 248 249 // Unsets a member decoration, similar to unset_decoration. 250 void unset_member_decoration(TypeID id, uint32_t index, spv::Decoration decoration); 251 252 // Gets the fallback name for a member, similar to get_fallback_name. get_fallback_member_name(uint32_t index) const253 virtual const std::string get_fallback_member_name(uint32_t index) const 254 { 255 return join("_", index); 256 } 257 258 // Returns a vector of which members of a struct are potentially in use by a 259 // SPIR-V shader. The granularity of this analysis is per-member of a struct. 260 // This can be used for Buffer (UBO), BufferBlock/StorageBuffer (SSBO) and PushConstant blocks. 261 // ID is the Resource::id obtained from get_shader_resources(). 262 SmallVector<BufferRange> get_active_buffer_ranges(VariableID id) const; 263 264 // Returns the effective size of a buffer block. 265 size_t get_declared_struct_size(const SPIRType &struct_type) const; 266 267 // Returns the effective size of a buffer block, with a given array size 268 // for a runtime array. 269 // SSBOs are typically declared as runtime arrays. get_declared_struct_size() will return 0 for the size. 270 // This is not very helpful for applications which might need to know the array stride of its last member. 271 // This can be done through the API, but it is not very intuitive how to accomplish this, so here we provide a helper function 272 // to query the size of the buffer, assuming that the last member has a certain size. 273 // If the buffer does not contain a runtime array, array_size is ignored, and the function will behave as 274 // get_declared_struct_size(). 275 // To get the array stride of the last member, something like: 276 // get_declared_struct_size_runtime_array(type, 1) - get_declared_struct_size_runtime_array(type, 0) will work. 277 size_t get_declared_struct_size_runtime_array(const SPIRType &struct_type, size_t array_size) const; 278 279 // Returns the effective size of a buffer block struct member. 280 size_t get_declared_struct_member_size(const SPIRType &struct_type, uint32_t index) const; 281 282 // Returns a set of all global variables which are statically accessed 283 // by the control flow graph from the current entry point. 284 // Only variables which change the interface for a shader are returned, that is, 285 // variables with storage class of Input, Output, Uniform, UniformConstant, PushConstant and AtomicCounter 286 // storage classes are returned. 287 // 288 // To use the returned set as the filter for which variables are used during compilation, 289 // this set can be moved to set_enabled_interface_variables(). 290 std::unordered_set<VariableID> get_active_interface_variables() const; 291 292 // Sets the interface variables which are used during compilation. 293 // By default, all variables are used. 294 // Once set, compile() will only consider the set in active_variables. 295 void set_enabled_interface_variables(std::unordered_set<VariableID> active_variables); 296 297 // Query shader resources, use ids with reflection interface to modify or query binding points, etc. 298 ShaderResources get_shader_resources() const; 299 300 // Query shader resources, but only return the variables which are part of active_variables. 301 // E.g.: get_shader_resources(get_active_variables()) to only return the variables which are statically 302 // accessed. 303 ShaderResources get_shader_resources(const std::unordered_set<VariableID> &active_variables) const; 304 305 // Remapped variables are considered built-in variables and a backend will 306 // not emit a declaration for this variable. 307 // This is mostly useful for making use of builtins which are dependent on extensions. 308 void set_remapped_variable_state(VariableID id, bool remap_enable); 309 bool get_remapped_variable_state(VariableID id) const; 310 311 // For subpassInput variables which are remapped to plain variables, 312 // the number of components in the remapped 313 // variable must be specified as the backing type of subpass inputs are opaque. 314 void set_subpass_input_remapped_components(VariableID id, uint32_t components); 315 uint32_t get_subpass_input_remapped_components(VariableID id) const; 316 317 // All operations work on the current entry point. 318 // Entry points can be swapped out with set_entry_point(). 319 // Entry points should be set right after the constructor completes as some reflection functions traverse the graph from the entry point. 320 // Resource reflection also depends on the entry point. 321 // By default, the current entry point is set to the first OpEntryPoint which appears in the SPIR-V module. 322 323 // Some shader languages restrict the names that can be given to entry points, and the 324 // corresponding backend will automatically rename an entry point name, during the call 325 // to compile() if it is illegal. For example, the common entry point name main() is 326 // illegal in MSL, and is renamed to an alternate name by the MSL backend. 327 // Given the original entry point name contained in the SPIR-V, this function returns 328 // the name, as updated by the backend during the call to compile(). If the name is not 329 // illegal, and has not been renamed, or if this function is called before compile(), 330 // this function will simply return the same name. 331 332 // New variants of entry point query and reflection. 333 // Names for entry points in the SPIR-V module may alias if they belong to different execution models. 334 // To disambiguate, we must pass along with the entry point names the execution model. 335 SmallVector<EntryPoint> get_entry_points_and_stages() const; 336 void set_entry_point(const std::string &entry, spv::ExecutionModel execution_model); 337 338 // Renames an entry point from old_name to new_name. 339 // If old_name is currently selected as the current entry point, it will continue to be the current entry point, 340 // albeit with a new name. 341 // get_entry_points() is essentially invalidated at this point. 342 void rename_entry_point(const std::string &old_name, const std::string &new_name, 343 spv::ExecutionModel execution_model); 344 const SPIREntryPoint &get_entry_point(const std::string &name, spv::ExecutionModel execution_model) const; 345 SPIREntryPoint &get_entry_point(const std::string &name, spv::ExecutionModel execution_model); 346 const std::string &get_cleansed_entry_point_name(const std::string &name, 347 spv::ExecutionModel execution_model) const; 348 349 // Traverses all reachable opcodes and sets active_builtins to a bitmask of all builtin variables which are accessed in the shader. 350 void update_active_builtins(); 351 bool has_active_builtin(spv::BuiltIn builtin, spv::StorageClass storage) const; 352 353 // Query and modify OpExecutionMode. 354 const Bitset &get_execution_mode_bitset() const; 355 356 void unset_execution_mode(spv::ExecutionMode mode); 357 void set_execution_mode(spv::ExecutionMode mode, uint32_t arg0 = 0, uint32_t arg1 = 0, uint32_t arg2 = 0); 358 359 // Gets argument for an execution mode (LocalSize, Invocations, OutputVertices). 360 // For LocalSize, the index argument is used to select the dimension (X = 0, Y = 1, Z = 2). 361 // For execution modes which do not have arguments, 0 is returned. 362 uint32_t get_execution_mode_argument(spv::ExecutionMode mode, uint32_t index = 0) const; 363 spv::ExecutionModel get_execution_model() const; 364 365 bool is_tessellation_shader() const; 366 367 // In SPIR-V, the compute work group size can be represented by a constant vector, in which case 368 // the LocalSize execution mode is ignored. 369 // 370 // This constant vector can be a constant vector, specialization constant vector, or partly specialized constant vector. 371 // To modify and query work group dimensions which are specialization constants, SPIRConstant values must be modified 372 // directly via get_constant() rather than using LocalSize directly. This function will return which constants should be modified. 373 // 374 // To modify dimensions which are *not* specialization constants, set_execution_mode should be used directly. 375 // Arguments to set_execution_mode which are specialization constants are effectively ignored during compilation. 376 // NOTE: This is somewhat different from how SPIR-V works. In SPIR-V, the constant vector will completely replace LocalSize, 377 // while in this interface, LocalSize is only ignored for specialization constants. 378 // 379 // The specialization constant will be written to x, y and z arguments. 380 // If the component is not a specialization constant, a zeroed out struct will be written. 381 // The return value is the constant ID of the builtin WorkGroupSize, but this is not expected to be useful 382 // for most use cases. 383 uint32_t get_work_group_size_specialization_constants(SpecializationConstant &x, SpecializationConstant &y, 384 SpecializationConstant &z) const; 385 386 // Analyzes all OpImageFetch (texelFetch) opcodes and checks if there are instances where 387 // said instruction is used without a combined image sampler. 388 // GLSL targets do not support the use of texelFetch without a sampler. 389 // To workaround this, we must inject a dummy sampler which can be used to form a sampler2D at the call-site of 390 // texelFetch as necessary. 391 // 392 // This must be called before build_combined_image_samplers(). 393 // build_combined_image_samplers() may refer to the ID returned by this method if the returned ID is non-zero. 394 // The return value will be the ID of a sampler object if a dummy sampler is necessary, or 0 if no sampler object 395 // is required. 396 // 397 // If the returned ID is non-zero, it can be decorated with set/bindings as desired before calling compile(). 398 // Calling this function also invalidates get_active_interface_variables(), so this should be called 399 // before that function. 400 VariableID build_dummy_sampler_for_combined_images(); 401 402 // Analyzes all separate image and samplers used from the currently selected entry point, 403 // and re-routes them all to a combined image sampler instead. 404 // This is required to "support" separate image samplers in targets which do not natively support 405 // this feature, like GLSL/ESSL. 406 // 407 // This must be called before compile() if such remapping is desired. 408 // This call will add new sampled images to the SPIR-V, 409 // so it will appear in reflection if get_shader_resources() is called after build_combined_image_samplers. 410 // 411 // If any image/sampler remapping was found, no separate image/samplers will appear in the decompiled output, 412 // but will still appear in reflection. 413 // 414 // The resulting samplers will be void of any decorations like name, descriptor sets and binding points, 415 // so this can be added before compile() if desired. 416 // 417 // Combined image samplers originating from this set are always considered active variables. 418 // Arrays of separate samplers are not supported, but arrays of separate images are supported. 419 // Array of images + sampler -> Array of combined image samplers. 420 void build_combined_image_samplers(); 421 422 // Gets a remapping for the combined image samplers. get_combined_image_samplers() const423 const SmallVector<CombinedImageSampler> &get_combined_image_samplers() const 424 { 425 return combined_image_samplers; 426 } 427 428 // Set a new variable type remap callback. 429 // The type remapping is designed to allow global interface variable to assume more special types. 430 // A typical example here is to remap sampler2D into samplerExternalOES, which currently isn't supported 431 // directly by SPIR-V. 432 // 433 // In compile() while emitting code, 434 // for every variable that is declared, including function parameters, the callback will be called 435 // and the API user has a chance to change the textual representation of the type used to declare the variable. 436 // The API user can detect special patterns in names to guide the remapping. set_variable_type_remap_callback(VariableTypeRemapCallback cb)437 void set_variable_type_remap_callback(VariableTypeRemapCallback cb) 438 { 439 variable_remap_callback = std::move(cb); 440 } 441 442 // API for querying which specialization constants exist. 443 // To modify a specialization constant before compile(), use get_constant(constant.id), 444 // then update constants directly in the SPIRConstant data structure. 445 // For composite types, the subconstants can be iterated over and modified. 446 // constant_type is the SPIRType for the specialization constant, 447 // which can be queried to determine which fields in the unions should be poked at. 448 SmallVector<SpecializationConstant> get_specialization_constants() const; 449 SPIRConstant &get_constant(ConstantID id); 450 const SPIRConstant &get_constant(ConstantID id) const; 451 get_current_id_bound() const452 uint32_t get_current_id_bound() const 453 { 454 return uint32_t(ir.ids.size()); 455 } 456 457 // API for querying buffer objects. 458 // The type passed in here should be the base type of a resource, i.e. 459 // get_type(resource.base_type_id) 460 // as decorations are set in the basic Block type. 461 // The type passed in here must have these decorations set, or an exception is raised. 462 // Only UBOs and SSBOs or sub-structs which are part of these buffer types will have these decorations set. 463 uint32_t type_struct_member_offset(const SPIRType &type, uint32_t index) const; 464 uint32_t type_struct_member_array_stride(const SPIRType &type, uint32_t index) const; 465 uint32_t type_struct_member_matrix_stride(const SPIRType &type, uint32_t index) const; 466 467 // Gets the offset in SPIR-V words (uint32_t) for a decoration which was originally declared in the SPIR-V binary. 468 // The offset will point to one or more uint32_t literals which can be modified in-place before using the SPIR-V binary. 469 // Note that adding or removing decorations using the reflection API will not change the behavior of this function. 470 // If the decoration was declared, sets the word_offset to an offset into the provided SPIR-V binary buffer and returns true, 471 // otherwise, returns false. 472 // If the decoration does not have any value attached to it (e.g. DecorationRelaxedPrecision), this function will also return false. 473 bool get_binary_offset_for_decoration(VariableID id, spv::Decoration decoration, uint32_t &word_offset) const; 474 475 // HLSL counter buffer reflection interface. 476 // Append/Consume/Increment/Decrement in HLSL is implemented as two "neighbor" buffer objects where 477 // one buffer implements the storage, and a single buffer containing just a lone "int" implements the counter. 478 // To SPIR-V these will be exposed as two separate buffers, but glslang HLSL frontend emits a special indentifier 479 // which lets us link the two buffers together. 480 481 // Queries if a variable ID is a counter buffer which "belongs" to a regular buffer object. 482 483 // If SPV_GOOGLE_hlsl_functionality1 is used, this can be used even with a stripped SPIR-V module. 484 // Otherwise, this query is purely based on OpName identifiers as found in the SPIR-V module, and will 485 // only return true if OpSource was reported HLSL. 486 // To rely on this functionality, ensure that the SPIR-V module is not stripped. 487 488 bool buffer_is_hlsl_counter_buffer(VariableID id) const; 489 490 // Queries if a buffer object has a neighbor "counter" buffer. 491 // If so, the ID of that counter buffer will be returned in counter_id. 492 // If SPV_GOOGLE_hlsl_functionality1 is used, this can be used even with a stripped SPIR-V module. 493 // Otherwise, this query is purely based on OpName identifiers as found in the SPIR-V module, and will 494 // only return true if OpSource was reported HLSL. 495 // To rely on this functionality, ensure that the SPIR-V module is not stripped. 496 bool buffer_get_hlsl_counter_buffer(VariableID id, uint32_t &counter_id) const; 497 498 // Gets the list of all SPIR-V Capabilities which were declared in the SPIR-V module. 499 const SmallVector<spv::Capability> &get_declared_capabilities() const; 500 501 // Gets the list of all SPIR-V extensions which were declared in the SPIR-V module. 502 const SmallVector<std::string> &get_declared_extensions() const; 503 504 // When declaring buffer blocks in GLSL, the name declared in the GLSL source 505 // might not be the same as the name declared in the SPIR-V module due to naming conflicts. 506 // In this case, SPIRV-Cross needs to find a fallback-name, and it might only 507 // be possible to know this name after compiling to GLSL. 508 // This is particularly important for HLSL input and UAVs which tends to reuse the same block type 509 // for multiple distinct blocks. For these cases it is not possible to modify the name of the type itself 510 // because it might be unique. Instead, you can use this interface to check after compilation which 511 // name was actually used if your input SPIR-V tends to have this problem. 512 // For other names like remapped names for variables, etc, it's generally enough to query the name of the variables 513 // after compiling, block names are an exception to this rule. 514 // ID is the name of a variable as returned by Resource::id, and must be a variable with a Block-like type. 515 // 516 // This also applies to HLSL cbuffers. 517 std::string get_remapped_declared_block_name(VariableID id) const; 518 519 // For buffer block variables, get the decorations for that variable. 520 // Sometimes, decorations for buffer blocks are found in member decorations instead 521 // of direct decorations on the variable itself. 522 // The most common use here is to check if a buffer is readonly or writeonly. 523 Bitset get_buffer_block_flags(VariableID id) const; 524 525 // Returns whether the position output is invariant is_position_invariant() const526 bool is_position_invariant() const 527 { 528 return position_invariant; 529 } 530 531 protected: stream(const Instruction & instr) const532 const uint32_t *stream(const Instruction &instr) const 533 { 534 // If we're not going to use any arguments, just return nullptr. 535 // We want to avoid case where we return an out of range pointer 536 // that trips debug assertions on some platforms. 537 if (!instr.length) 538 return nullptr; 539 540 if (instr.is_embedded()) 541 { 542 auto &embedded = static_cast<const EmbeddedInstruction &>(instr); 543 assert(embedded.ops.size() == instr.length); 544 return embedded.ops.data(); 545 } 546 else 547 { 548 if (instr.offset + instr.length > ir.spirv.size()) 549 SPIRV_CROSS_THROW("Compiler::stream() out of range."); 550 return &ir.spirv[instr.offset]; 551 } 552 } 553 554 ParsedIR ir; 555 // Marks variables which have global scope and variables which can alias with other variables 556 // (SSBO, image load store, etc) 557 SmallVector<uint32_t> global_variables; 558 SmallVector<uint32_t> aliased_variables; 559 560 SPIRFunction *current_function = nullptr; 561 SPIRBlock *current_block = nullptr; 562 uint32_t current_loop_level = 0; 563 std::unordered_set<VariableID> active_interface_variables; 564 bool check_active_interface_variables = false; 565 566 void add_loop_level(); 567 set_initializers(SPIRExpression & e)568 void set_initializers(SPIRExpression &e) 569 { 570 e.emitted_loop_level = current_loop_level; 571 } 572 573 template <typename T> set_initializers(const T &)574 void set_initializers(const T &) 575 { 576 } 577 578 // If our IDs are out of range here as part of opcodes, throw instead of 579 // undefined behavior. 580 template <typename T, typename... P> set(uint32_t id,P &&...args)581 T &set(uint32_t id, P &&... args) 582 { 583 ir.add_typed_id(static_cast<Types>(T::type), id); 584 auto &var = variant_set<T>(ir.ids[id], std::forward<P>(args)...); 585 var.self = id; 586 set_initializers(var); 587 return var; 588 } 589 590 template <typename T> get(uint32_t id)591 T &get(uint32_t id) 592 { 593 return variant_get<T>(ir.ids[id]); 594 } 595 596 template <typename T> maybe_get(uint32_t id)597 T *maybe_get(uint32_t id) 598 { 599 if (id >= ir.ids.size()) 600 return nullptr; 601 else if (ir.ids[id].get_type() == static_cast<Types>(T::type)) 602 return &get<T>(id); 603 else 604 return nullptr; 605 } 606 607 template <typename T> get(uint32_t id) const608 const T &get(uint32_t id) const 609 { 610 return variant_get<T>(ir.ids[id]); 611 } 612 613 template <typename T> maybe_get(uint32_t id) const614 const T *maybe_get(uint32_t id) const 615 { 616 if (id >= ir.ids.size()) 617 return nullptr; 618 else if (ir.ids[id].get_type() == static_cast<Types>(T::type)) 619 return &get<T>(id); 620 else 621 return nullptr; 622 } 623 624 // Gets the id of SPIR-V type underlying the given type_id, which might be a pointer. 625 uint32_t get_pointee_type_id(uint32_t type_id) const; 626 627 // Gets the SPIR-V type underlying the given type, which might be a pointer. 628 const SPIRType &get_pointee_type(const SPIRType &type) const; 629 630 // Gets the SPIR-V type underlying the given type_id, which might be a pointer. 631 const SPIRType &get_pointee_type(uint32_t type_id) const; 632 633 // Gets the ID of the SPIR-V type underlying a variable. 634 uint32_t get_variable_data_type_id(const SPIRVariable &var) const; 635 636 // Gets the SPIR-V type underlying a variable. 637 SPIRType &get_variable_data_type(const SPIRVariable &var); 638 639 // Gets the SPIR-V type underlying a variable. 640 const SPIRType &get_variable_data_type(const SPIRVariable &var) const; 641 642 // Gets the SPIR-V element type underlying an array variable. 643 SPIRType &get_variable_element_type(const SPIRVariable &var); 644 645 // Gets the SPIR-V element type underlying an array variable. 646 const SPIRType &get_variable_element_type(const SPIRVariable &var) const; 647 648 // Sets the qualified member identifier for OpTypeStruct ID, member number "index". 649 void set_member_qualified_name(uint32_t type_id, uint32_t index, const std::string &name); 650 void set_qualified_name(uint32_t id, const std::string &name); 651 652 // Returns if the given type refers to a sampled image. 653 bool is_sampled_image_type(const SPIRType &type); 654 655 const SPIREntryPoint &get_entry_point() const; 656 SPIREntryPoint &get_entry_point(); 657 static bool is_tessellation_shader(spv::ExecutionModel model); 658 659 virtual std::string to_name(uint32_t id, bool allow_alias = true) const; 660 bool is_builtin_variable(const SPIRVariable &var) const; 661 bool is_builtin_type(const SPIRType &type) const; 662 bool is_hidden_variable(const SPIRVariable &var, bool include_builtins = false) const; 663 bool is_immutable(uint32_t id) const; 664 bool is_member_builtin(const SPIRType &type, uint32_t index, spv::BuiltIn *builtin) const; 665 bool is_scalar(const SPIRType &type) const; 666 bool is_vector(const SPIRType &type) const; 667 bool is_matrix(const SPIRType &type) const; 668 bool is_array(const SPIRType &type) const; 669 uint32_t expression_type_id(uint32_t id) const; 670 const SPIRType &expression_type(uint32_t id) const; 671 bool expression_is_lvalue(uint32_t id) const; 672 bool variable_storage_is_aliased(const SPIRVariable &var); 673 SPIRVariable *maybe_get_backing_variable(uint32_t chain); 674 675 void register_read(uint32_t expr, uint32_t chain, bool forwarded); 676 void register_write(uint32_t chain); 677 is_continue(uint32_t next) const678 inline bool is_continue(uint32_t next) const 679 { 680 return (ir.block_meta[next] & ParsedIR::BLOCK_META_CONTINUE_BIT) != 0; 681 } 682 is_single_block_loop(uint32_t next) const683 inline bool is_single_block_loop(uint32_t next) const 684 { 685 auto &block = get<SPIRBlock>(next); 686 return block.merge == SPIRBlock::MergeLoop && block.continue_block == ID(next); 687 } 688 is_break(uint32_t next) const689 inline bool is_break(uint32_t next) const 690 { 691 return (ir.block_meta[next] & 692 (ParsedIR::BLOCK_META_LOOP_MERGE_BIT | ParsedIR::BLOCK_META_MULTISELECT_MERGE_BIT)) != 0; 693 } 694 is_loop_break(uint32_t next) const695 inline bool is_loop_break(uint32_t next) const 696 { 697 return (ir.block_meta[next] & ParsedIR::BLOCK_META_LOOP_MERGE_BIT) != 0; 698 } 699 is_conditional(uint32_t next) const700 inline bool is_conditional(uint32_t next) const 701 { 702 return (ir.block_meta[next] & 703 (ParsedIR::BLOCK_META_SELECTION_MERGE_BIT | ParsedIR::BLOCK_META_MULTISELECT_MERGE_BIT)) != 0; 704 } 705 706 // Dependency tracking for temporaries read from variables. 707 void flush_dependees(SPIRVariable &var); 708 void flush_all_active_variables(); 709 void flush_control_dependent_expressions(uint32_t block); 710 void flush_all_atomic_capable_variables(); 711 void flush_all_aliased_variables(); 712 void register_global_read_dependencies(const SPIRBlock &func, uint32_t id); 713 void register_global_read_dependencies(const SPIRFunction &func, uint32_t id); 714 std::unordered_set<uint32_t> invalid_expressions; 715 716 void update_name_cache(std::unordered_set<std::string> &cache, std::string &name); 717 718 // A variant which takes two sets of names. The secondary is only used to verify there are no collisions, 719 // but the set is not updated when we have found a new name. 720 // Used primarily when adding block interface names. 721 void update_name_cache(std::unordered_set<std::string> &cache_primary, 722 const std::unordered_set<std::string> &cache_secondary, std::string &name); 723 724 bool function_is_pure(const SPIRFunction &func); 725 bool block_is_pure(const SPIRBlock &block); 726 727 bool execution_is_branchless(const SPIRBlock &from, const SPIRBlock &to) const; 728 bool execution_is_direct_branch(const SPIRBlock &from, const SPIRBlock &to) const; 729 bool execution_is_noop(const SPIRBlock &from, const SPIRBlock &to) const; 730 SPIRBlock::ContinueBlockType continue_block_type(const SPIRBlock &continue_block) const; 731 732 void force_recompile(); 733 void clear_force_recompile(); 734 bool is_forcing_recompilation() const; 735 bool is_force_recompile = false; 736 737 bool block_is_loop_candidate(const SPIRBlock &block, SPIRBlock::Method method) const; 738 739 bool types_are_logically_equivalent(const SPIRType &a, const SPIRType &b) const; 740 void inherit_expression_dependencies(uint32_t dst, uint32_t source); 741 void add_implied_read_expression(SPIRExpression &e, uint32_t source); 742 void add_implied_read_expression(SPIRAccessChain &e, uint32_t source); 743 744 // For proper multiple entry point support, allow querying if an Input or Output 745 // variable is part of that entry points interface. 746 bool interface_variable_exists_in_entry_point(uint32_t id) const; 747 748 SmallVector<CombinedImageSampler> combined_image_samplers; 749 remap_variable_type_name(const SPIRType & type,const std::string & var_name,std::string & type_name) const750 void remap_variable_type_name(const SPIRType &type, const std::string &var_name, std::string &type_name) const 751 { 752 if (variable_remap_callback) 753 variable_remap_callback(type, var_name, type_name); 754 } 755 756 void set_ir(const ParsedIR &parsed); 757 void set_ir(ParsedIR &&parsed); 758 void parse_fixup(); 759 760 // Used internally to implement various traversals for queries. 761 struct OpcodeHandler 762 { 763 virtual ~OpcodeHandler() = default; 764 765 // Return true if traversal should continue. 766 // If false, traversal will end immediately. 767 virtual bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) = 0; handle_terminatorSPIRV_CROSS_NAMESPACE::Compiler::OpcodeHandler768 virtual bool handle_terminator(const SPIRBlock &) 769 { 770 return true; 771 } 772 follow_function_callSPIRV_CROSS_NAMESPACE::Compiler::OpcodeHandler773 virtual bool follow_function_call(const SPIRFunction &) 774 { 775 return true; 776 } 777 set_current_blockSPIRV_CROSS_NAMESPACE::Compiler::OpcodeHandler778 virtual void set_current_block(const SPIRBlock &) 779 { 780 } 781 782 // Called after returning from a function or when entering a block, 783 // can be called multiple times per block, 784 // while set_current_block is only called on block entry. rearm_current_blockSPIRV_CROSS_NAMESPACE::Compiler::OpcodeHandler785 virtual void rearm_current_block(const SPIRBlock &) 786 { 787 } 788 begin_function_scopeSPIRV_CROSS_NAMESPACE::Compiler::OpcodeHandler789 virtual bool begin_function_scope(const uint32_t *, uint32_t) 790 { 791 return true; 792 } 793 end_function_scopeSPIRV_CROSS_NAMESPACE::Compiler::OpcodeHandler794 virtual bool end_function_scope(const uint32_t *, uint32_t) 795 { 796 return true; 797 } 798 }; 799 800 struct BufferAccessHandler : OpcodeHandler 801 { BufferAccessHandlerSPIRV_CROSS_NAMESPACE::Compiler::BufferAccessHandler802 BufferAccessHandler(const Compiler &compiler_, SmallVector<BufferRange> &ranges_, uint32_t id_) 803 : compiler(compiler_) 804 , ranges(ranges_) 805 , id(id_) 806 { 807 } 808 809 bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) override; 810 811 const Compiler &compiler; 812 SmallVector<BufferRange> &ranges; 813 uint32_t id; 814 815 std::unordered_set<uint32_t> seen; 816 }; 817 818 struct InterfaceVariableAccessHandler : OpcodeHandler 819 { InterfaceVariableAccessHandlerSPIRV_CROSS_NAMESPACE::Compiler::InterfaceVariableAccessHandler820 InterfaceVariableAccessHandler(const Compiler &compiler_, std::unordered_set<VariableID> &variables_) 821 : compiler(compiler_) 822 , variables(variables_) 823 { 824 } 825 826 bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) override; 827 828 const Compiler &compiler; 829 std::unordered_set<VariableID> &variables; 830 }; 831 832 struct CombinedImageSamplerHandler : OpcodeHandler 833 { CombinedImageSamplerHandlerSPIRV_CROSS_NAMESPACE::Compiler::CombinedImageSamplerHandler834 CombinedImageSamplerHandler(Compiler &compiler_) 835 : compiler(compiler_) 836 { 837 } 838 bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) override; 839 bool begin_function_scope(const uint32_t *args, uint32_t length) override; 840 bool end_function_scope(const uint32_t *args, uint32_t length) override; 841 842 Compiler &compiler; 843 844 // Each function in the call stack needs its own remapping for parameters so we can deduce which global variable each texture/sampler the parameter is statically bound to. 845 std::stack<std::unordered_map<uint32_t, uint32_t>> parameter_remapping; 846 std::stack<SPIRFunction *> functions; 847 848 uint32_t remap_parameter(uint32_t id); 849 void push_remap_parameters(const SPIRFunction &func, const uint32_t *args, uint32_t length); 850 void pop_remap_parameters(); 851 void register_combined_image_sampler(SPIRFunction &caller, VariableID combined_id, VariableID texture_id, 852 VariableID sampler_id, bool depth); 853 }; 854 855 struct DummySamplerForCombinedImageHandler : OpcodeHandler 856 { DummySamplerForCombinedImageHandlerSPIRV_CROSS_NAMESPACE::Compiler::DummySamplerForCombinedImageHandler857 DummySamplerForCombinedImageHandler(Compiler &compiler_) 858 : compiler(compiler_) 859 { 860 } 861 bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) override; 862 863 Compiler &compiler; 864 bool need_dummy_sampler = false; 865 }; 866 867 struct ActiveBuiltinHandler : OpcodeHandler 868 { ActiveBuiltinHandlerSPIRV_CROSS_NAMESPACE::Compiler::ActiveBuiltinHandler869 ActiveBuiltinHandler(Compiler &compiler_) 870 : compiler(compiler_) 871 { 872 } 873 874 bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) override; 875 Compiler &compiler; 876 877 void handle_builtin(const SPIRType &type, spv::BuiltIn builtin, const Bitset &decoration_flags); 878 void add_if_builtin(uint32_t id); 879 void add_if_builtin_or_block(uint32_t id); 880 void add_if_builtin(uint32_t id, bool allow_blocks); 881 }; 882 883 bool traverse_all_reachable_opcodes(const SPIRBlock &block, OpcodeHandler &handler) const; 884 bool traverse_all_reachable_opcodes(const SPIRFunction &block, OpcodeHandler &handler) const; 885 // This must be an ordered data structure so we always pick the same type aliases. 886 SmallVector<uint32_t> global_struct_cache; 887 888 ShaderResources get_shader_resources(const std::unordered_set<VariableID> *active_variables) const; 889 890 VariableTypeRemapCallback variable_remap_callback; 891 892 bool get_common_basic_type(const SPIRType &type, SPIRType::BaseType &base_type); 893 894 std::unordered_set<uint32_t> forced_temporaries; 895 std::unordered_set<uint32_t> forwarded_temporaries; 896 std::unordered_set<uint32_t> suppressed_usage_tracking; 897 std::unordered_set<uint32_t> hoisted_temporaries; 898 std::unordered_set<uint32_t> forced_invariant_temporaries; 899 900 Bitset active_input_builtins; 901 Bitset active_output_builtins; 902 uint32_t clip_distance_count = 0; 903 uint32_t cull_distance_count = 0; 904 bool position_invariant = false; 905 906 void analyze_parameter_preservation( 907 SPIRFunction &entry, const CFG &cfg, 908 const std::unordered_map<uint32_t, std::unordered_set<uint32_t>> &variable_to_blocks, 909 const std::unordered_map<uint32_t, std::unordered_set<uint32_t>> &complete_write_blocks); 910 911 // If a variable ID or parameter ID is found in this set, a sampler is actually a shadow/comparison sampler. 912 // SPIR-V does not support this distinction, so we must keep track of this information outside the type system. 913 // There might be unrelated IDs found in this set which do not correspond to actual variables. 914 // This set should only be queried for the existence of samplers which are already known to be variables or parameter IDs. 915 // Similar is implemented for images, as well as if subpass inputs are needed. 916 std::unordered_set<uint32_t> comparison_ids; 917 bool need_subpass_input = false; 918 919 // In certain backends, we will need to use a dummy sampler to be able to emit code. 920 // GLSL does not support texelFetch on texture2D objects, but SPIR-V does, 921 // so we need to workaround by having the application inject a dummy sampler. 922 uint32_t dummy_sampler_id = 0; 923 924 void analyze_image_and_sampler_usage(); 925 926 struct CombinedImageSamplerDrefHandler : OpcodeHandler 927 { CombinedImageSamplerDrefHandlerSPIRV_CROSS_NAMESPACE::Compiler::CombinedImageSamplerDrefHandler928 CombinedImageSamplerDrefHandler(Compiler &compiler_) 929 : compiler(compiler_) 930 { 931 } 932 bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) override; 933 934 Compiler &compiler; 935 std::unordered_set<uint32_t> dref_combined_samplers; 936 }; 937 938 struct CombinedImageSamplerUsageHandler : OpcodeHandler 939 { CombinedImageSamplerUsageHandlerSPIRV_CROSS_NAMESPACE::Compiler::CombinedImageSamplerUsageHandler940 CombinedImageSamplerUsageHandler(Compiler &compiler_, 941 const std::unordered_set<uint32_t> &dref_combined_samplers_) 942 : compiler(compiler_) 943 , dref_combined_samplers(dref_combined_samplers_) 944 { 945 } 946 947 bool begin_function_scope(const uint32_t *args, uint32_t length) override; 948 bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) override; 949 Compiler &compiler; 950 const std::unordered_set<uint32_t> &dref_combined_samplers; 951 952 std::unordered_map<uint32_t, std::unordered_set<uint32_t>> dependency_hierarchy; 953 std::unordered_set<uint32_t> comparison_ids; 954 955 void add_hierarchy_to_comparison_ids(uint32_t ids); 956 bool need_subpass_input = false; 957 void add_dependency(uint32_t dst, uint32_t src); 958 }; 959 960 void build_function_control_flow_graphs_and_analyze(); 961 std::unordered_map<uint32_t, std::unique_ptr<CFG>> function_cfgs; 962 const CFG &get_cfg_for_current_function() const; 963 const CFG &get_cfg_for_function(uint32_t id) const; 964 965 struct CFGBuilder : OpcodeHandler 966 { 967 explicit CFGBuilder(Compiler &compiler_); 968 969 bool follow_function_call(const SPIRFunction &func) override; 970 bool handle(spv::Op op, const uint32_t *args, uint32_t length) override; 971 Compiler &compiler; 972 std::unordered_map<uint32_t, std::unique_ptr<CFG>> function_cfgs; 973 }; 974 975 struct AnalyzeVariableScopeAccessHandler : OpcodeHandler 976 { 977 AnalyzeVariableScopeAccessHandler(Compiler &compiler_, SPIRFunction &entry_); 978 979 bool follow_function_call(const SPIRFunction &) override; 980 void set_current_block(const SPIRBlock &block) override; 981 982 void notify_variable_access(uint32_t id, uint32_t block); 983 bool id_is_phi_variable(uint32_t id) const; 984 bool id_is_potential_temporary(uint32_t id) const; 985 bool handle(spv::Op op, const uint32_t *args, uint32_t length) override; 986 bool handle_terminator(const SPIRBlock &block) override; 987 988 Compiler &compiler; 989 SPIRFunction &entry; 990 std::unordered_map<uint32_t, std::unordered_set<uint32_t>> accessed_variables_to_block; 991 std::unordered_map<uint32_t, std::unordered_set<uint32_t>> accessed_temporaries_to_block; 992 std::unordered_map<uint32_t, uint32_t> result_id_to_type; 993 std::unordered_map<uint32_t, std::unordered_set<uint32_t>> complete_write_variables_to_block; 994 std::unordered_map<uint32_t, std::unordered_set<uint32_t>> partial_write_variables_to_block; 995 std::unordered_set<uint32_t> access_chain_expressions; 996 // Access chains used in multiple blocks mean hoisting all the variables used to construct the access chain as not all backends can use pointers. 997 std::unordered_map<uint32_t, std::unordered_set<uint32_t>> access_chain_children; 998 const SPIRBlock *current_block = nullptr; 999 }; 1000 1001 struct StaticExpressionAccessHandler : OpcodeHandler 1002 { 1003 StaticExpressionAccessHandler(Compiler &compiler_, uint32_t variable_id_); 1004 bool follow_function_call(const SPIRFunction &) override; 1005 bool handle(spv::Op op, const uint32_t *args, uint32_t length) override; 1006 1007 Compiler &compiler; 1008 uint32_t variable_id; 1009 uint32_t static_expression = 0; 1010 uint32_t write_count = 0; 1011 }; 1012 1013 struct PhysicalStorageBufferPointerHandler : OpcodeHandler 1014 { 1015 explicit PhysicalStorageBufferPointerHandler(Compiler &compiler_); 1016 bool handle(spv::Op op, const uint32_t *args, uint32_t length) override; 1017 Compiler &compiler; 1018 std::unordered_set<uint32_t> types; 1019 }; 1020 void analyze_non_block_pointer_types(); 1021 SmallVector<uint32_t> physical_storage_non_block_pointer_types; 1022 1023 void analyze_variable_scope(SPIRFunction &function, AnalyzeVariableScopeAccessHandler &handler); 1024 void find_function_local_luts(SPIRFunction &function, const AnalyzeVariableScopeAccessHandler &handler, 1025 bool single_function); 1026 bool may_read_undefined_variable_in_block(const SPIRBlock &block, uint32_t var); 1027 1028 // Finds all resources that are written to from inside the critical section, if present. 1029 // The critical section is delimited by OpBeginInvocationInterlockEXT and 1030 // OpEndInvocationInterlockEXT instructions. In MSL and HLSL, any resources written 1031 // while inside the critical section must be placed in a raster order group. 1032 struct InterlockedResourceAccessHandler : OpcodeHandler 1033 { InterlockedResourceAccessHandlerSPIRV_CROSS_NAMESPACE::Compiler::InterlockedResourceAccessHandler1034 InterlockedResourceAccessHandler(Compiler &compiler_, uint32_t entry_point_id) 1035 : compiler(compiler_) 1036 { 1037 call_stack.push_back(entry_point_id); 1038 } 1039 1040 bool handle(spv::Op op, const uint32_t *args, uint32_t length) override; 1041 bool begin_function_scope(const uint32_t *args, uint32_t length) override; 1042 bool end_function_scope(const uint32_t *args, uint32_t length) override; 1043 1044 Compiler &compiler; 1045 bool in_crit_sec = false; 1046 1047 uint32_t interlock_function_id = 0; 1048 bool split_function_case = false; 1049 bool control_flow_interlock = false; 1050 bool use_critical_section = false; 1051 bool call_stack_is_interlocked = false; 1052 SmallVector<uint32_t> call_stack; 1053 1054 void access_potential_resource(uint32_t id); 1055 }; 1056 1057 struct InterlockedResourceAccessPrepassHandler : OpcodeHandler 1058 { InterlockedResourceAccessPrepassHandlerSPIRV_CROSS_NAMESPACE::Compiler::InterlockedResourceAccessPrepassHandler1059 InterlockedResourceAccessPrepassHandler(Compiler &compiler_, uint32_t entry_point_id) 1060 : compiler(compiler_) 1061 { 1062 call_stack.push_back(entry_point_id); 1063 } 1064 1065 void rearm_current_block(const SPIRBlock &block) override; 1066 bool handle(spv::Op op, const uint32_t *args, uint32_t length) override; 1067 bool begin_function_scope(const uint32_t *args, uint32_t length) override; 1068 bool end_function_scope(const uint32_t *args, uint32_t length) override; 1069 1070 Compiler &compiler; 1071 uint32_t interlock_function_id = 0; 1072 uint32_t current_block_id = 0; 1073 bool split_function_case = false; 1074 bool control_flow_interlock = false; 1075 SmallVector<uint32_t> call_stack; 1076 }; 1077 1078 void analyze_interlocked_resource_usage(); 1079 // The set of all resources written while inside the critical section, if present. 1080 std::unordered_set<uint32_t> interlocked_resources; 1081 bool interlocked_is_complex = false; 1082 1083 void make_constant_null(uint32_t id, uint32_t type); 1084 1085 std::unordered_map<uint32_t, std::string> declared_block_names; 1086 1087 bool instruction_to_result_type(uint32_t &result_type, uint32_t &result_id, spv::Op op, const uint32_t *args, 1088 uint32_t length); 1089 1090 Bitset combined_decoration_for_member(const SPIRType &type, uint32_t index) const; 1091 static bool is_desktop_only_format(spv::ImageFormat format); 1092 1093 bool image_is_comparison(const SPIRType &type, uint32_t id) const; 1094 1095 void set_extended_decoration(uint32_t id, ExtendedDecorations decoration, uint32_t value = 0); 1096 uint32_t get_extended_decoration(uint32_t id, ExtendedDecorations decoration) const; 1097 bool has_extended_decoration(uint32_t id, ExtendedDecorations decoration) const; 1098 void unset_extended_decoration(uint32_t id, ExtendedDecorations decoration); 1099 1100 void set_extended_member_decoration(uint32_t type, uint32_t index, ExtendedDecorations decoration, 1101 uint32_t value = 0); 1102 uint32_t get_extended_member_decoration(uint32_t type, uint32_t index, ExtendedDecorations decoration) const; 1103 bool has_extended_member_decoration(uint32_t type, uint32_t index, ExtendedDecorations decoration) const; 1104 void unset_extended_member_decoration(uint32_t type, uint32_t index, ExtendedDecorations decoration); 1105 1106 bool type_is_array_of_pointers(const SPIRType &type) const; 1107 bool type_is_top_level_physical_pointer(const SPIRType &type) const; 1108 bool type_is_block_like(const SPIRType &type) const; 1109 bool type_is_opaque_value(const SPIRType &type) const; 1110 1111 bool reflection_ssbo_instance_name_is_significant() const; 1112 std::string get_remapped_declared_block_name(uint32_t id, bool fallback_prefer_instance_name) const; 1113 1114 bool flush_phi_required(BlockID from, BlockID to) const; 1115 1116 uint32_t evaluate_spec_constant_u32(const SPIRConstantOp &spec) const; 1117 uint32_t evaluate_constant_u32(uint32_t id) const; 1118 1119 bool is_vertex_like_shader() const; 1120 1121 private: 1122 // Used only to implement the old deprecated get_entry_point() interface. 1123 const SPIREntryPoint &get_first_entry_point(const std::string &name) const; 1124 SPIREntryPoint &get_first_entry_point(const std::string &name); 1125 }; 1126 } // namespace SPIRV_CROSS_NAMESPACE 1127 1128 #endif 1129