2 * Copyright 2015-2021 Arm Limited
3 * SPDX-License-Identifier: Apache-2.0 OR MIT
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
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>.
24 #ifndef SPIRV_CROSS_COMMON_HPP
25 #define SPIRV_CROSS_COMMON_HPP
27 #ifndef SPV_ENABLE_UTILITY_CODE
28 #define SPV_ENABLE_UTILITY_CODE
32 #include "spirv_cross_containers.hpp"
33 #include "spirv_cross_error_handling.hpp"
36 // A bit crude, but allows projects which embed SPIRV-Cross statically to
37 // effectively hide all the symbols from other projects.
38 // There is a case where we have:
39 // - Project A links against SPIRV-Cross statically.
40 // - Project A links against Project B statically.
41 // - Project B links against SPIRV-Cross statically (might be a different version).
42 // This leads to a conflict with extremely bizarre results.
43 // By overriding the namespace in one of the project builds, we can work around this.
44 // If SPIRV-Cross is embedded in dynamic libraries,
45 // prefer using -fvisibility=hidden on GCC/Clang instead.
46 #ifdef SPIRV_CROSS_NAMESPACE_OVERRIDE
47 #define SPIRV_CROSS_NAMESPACE SPIRV_CROSS_NAMESPACE_OVERRIDE
49 #define SPIRV_CROSS_NAMESPACE spirv_cross
52 namespace SPIRV_CROSS_NAMESPACE
57 void join_helper(StringStream
<> &stream
, T
&&t
)
59 stream
<< std::forward
<T
>(t
);
62 template <typename T
, typename
... Ts
>
63 void join_helper(StringStream
<> &stream
, T
&&t
, Ts
&&... ts
)
65 stream
<< std::forward
<T
>(t
);
66 join_helper(stream
, std::forward
<Ts
>(ts
)...);
74 explicit inline Bitset(uint64_t lower_
)
79 inline bool get(uint32_t bit
) const
82 return (lower
& (1ull << bit
)) != 0;
84 return higher
.count(bit
) != 0;
87 inline void set(uint32_t bit
)
95 inline void clear(uint32_t bit
)
98 lower
&= ~(1ull << bit
);
103 inline uint64_t get_lower() const
114 inline void merge_and(const Bitset
&other
)
116 lower
&= other
.lower
;
117 std::unordered_set
<uint32_t> tmp_set
;
118 for (auto &v
: higher
)
119 if (other
.higher
.count(v
) != 0)
121 higher
= std::move(tmp_set
);
124 inline void merge_or(const Bitset
&other
)
126 lower
|= other
.lower
;
127 for (auto &v
: other
.higher
)
131 inline bool operator==(const Bitset
&other
) const
133 if (lower
!= other
.lower
)
136 if (higher
.size() != other
.higher
.size())
139 for (auto &v
: higher
)
140 if (other
.higher
.count(v
) == 0)
146 inline bool operator!=(const Bitset
&other
) const
148 return !(*this == other
);
151 template <typename Op
>
152 void for_each_bit(const Op
&op
) const
154 // TODO: Add ctz-based iteration.
155 for (uint32_t i
= 0; i
< 64; i
++)
157 if (lower
& (1ull << i
))
164 // Need to enforce an order here for reproducible results,
165 // but hitting this path should happen extremely rarely, so having this slow path is fine.
166 SmallVector
<uint32_t> bits
;
167 bits
.reserve(higher
.size());
168 for (auto &v
: higher
)
170 std::sort(std::begin(bits
), std::end(bits
));
176 inline bool empty() const
178 return lower
== 0 && higher
.empty();
182 // The most common bits to set are all lower than 64,
183 // so optimize for this case. Bits spilling outside 64 go into a slower data structure.
184 // In almost all cases, higher data structure will not be used.
186 std::unordered_set
<uint32_t> higher
;
189 // Helper template to avoid lots of nasty string temporary munging.
190 template <typename
... Ts
>
191 std::string
join(Ts
&&... ts
)
193 StringStream
<> stream
;
194 inner::join_helper(stream
, std::forward
<Ts
>(ts
)...);
198 inline std::string
merge(const SmallVector
<std::string
> &list
, const char *between
= ", ")
200 StringStream
<> stream
;
201 for (auto &elem
: list
)
204 if (&elem
!= &list
.back())
210 // Make sure we don't accidentally call this with float or doubles with SFINAE.
211 // Have to use the radix-aware overload.
212 template <typename T
, typename
std::enable_if
<!std::is_floating_point
<T
>::value
, int>::type
= 0>
213 inline std::string
convert_to_string(const T
&t
)
215 return std::to_string(t
);
218 static inline std::string
convert_to_string(int32_t value
)
220 // INT_MIN is ... special on some backends. If we use a decimal literal, and negate it, we
221 // could accidentally promote the literal to long first, then negate.
222 // To workaround it, emit int(0x80000000) instead.
223 if (value
== (std::numeric_limits
<int32_t>::min
)())
224 return "int(0x80000000)";
226 return std::to_string(value
);
229 static inline std::string
convert_to_string(int64_t value
, const std::string
&int64_type
, bool long_long_literal_suffix
)
231 // INT64_MIN is ... special on some backends.
232 // If we use a decimal literal, and negate it, we might overflow the representable numbers.
233 // To workaround it, emit int(0x80000000) instead.
234 if (value
== (std::numeric_limits
<int64_t>::min
)())
235 return join(int64_type
, "(0x8000000000000000u", (long_long_literal_suffix
? "ll" : "l"), ")");
237 return std::to_string(value
) + (long_long_literal_suffix
? "ll" : "l");
240 // Allow implementations to set a convenient standard precision
241 #ifndef SPIRV_CROSS_FLT_FMT
242 #define SPIRV_CROSS_FLT_FMT "%.32g"
245 // Disable sprintf and strcat warnings.
246 // We cannot rely on snprintf and family existing because, ..., MSVC.
247 #if defined(__clang__) || defined(__GNUC__)
248 #pragma GCC diagnostic push
249 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
250 #elif defined(_MSC_VER)
251 #pragma warning(push)
252 #pragma warning(disable : 4996)
255 static inline void fixup_radix_point(char *str
, char radix_point
)
257 // Setting locales is a very risky business in multi-threaded program,
258 // so just fixup locales instead. We only need to care about the radix point.
259 if (radix_point
!= '.')
263 if (*str
== radix_point
)
270 inline std::string
convert_to_string(float t
, char locale_radix_point
)
272 // std::to_string for floating point values is broken.
273 // Fallback to something more sane.
275 sprintf(buf
, SPIRV_CROSS_FLT_FMT
, t
);
276 fixup_radix_point(buf
, locale_radix_point
);
278 // Ensure that the literal is float.
279 if (!strchr(buf
, '.') && !strchr(buf
, 'e'))
284 inline std::string
convert_to_string(double t
, char locale_radix_point
)
286 // std::to_string for floating point values is broken.
287 // Fallback to something more sane.
289 sprintf(buf
, SPIRV_CROSS_FLT_FMT
, t
);
290 fixup_radix_point(buf
, locale_radix_point
);
292 // Ensure that the literal is float.
293 if (!strchr(buf
, '.') && !strchr(buf
, 'e'))
298 #if defined(__clang__) || defined(__GNUC__)
299 #pragma GCC diagnostic pop
300 #elif defined(_MSC_VER)
307 virtual ~FloatFormatter() = default;
308 virtual std::string
format_float(float value
) = 0;
309 virtual std::string
format_double(double value
) = 0;
312 template <typename T
>
315 explicit ValueSaver(T
¤t_
)
339 // If offset is 0 (not a valid offset into the instruction stream),
340 // we have an instruction stream which is embedded in the object.
344 inline bool is_embedded() const
350 struct EmbeddedInstruction
: Instruction
352 SmallVector
<uint32_t> ops
;
362 TypeFunctionPrototype
,
367 TypeCombinedImageSampler
,
374 template <Types type
>
378 class TypedID
<TypeNone
>
382 TypedID(uint32_t id_
)
388 TypedID(const TypedID
<U
> &other
)
394 TypedID
&operator=(const TypedID
<U
> &other
)
396 id
= uint32_t(other
);
400 // Implicit conversion to u32 is desired here.
401 // As long as we block implicit conversion between TypedID<A> and TypedID<B> we're good.
402 operator uint32_t() const
408 operator TypedID
<U
>() const
410 return TypedID
<U
>(*this);
417 template <Types type
>
422 TypedID(uint32_t id_
)
427 explicit TypedID(const TypedID
<TypeNone
> &other
)
428 : id(uint32_t(other
))
432 operator uint32_t() const
441 using VariableID
= TypedID
<TypeVariable
>;
442 using TypeID
= TypedID
<TypeType
>;
443 using ConstantID
= TypedID
<TypeConstant
>;
444 using FunctionID
= TypedID
<TypeFunction
>;
445 using BlockID
= TypedID
<TypeBlock
>;
446 using ID
= TypedID
<TypeNone
>;
448 // Helper for Variant interface.
451 virtual ~IVariant() = default;
452 virtual IVariant
*clone(ObjectPoolBase
*pool
) = 0;
456 IVariant() = default;
457 IVariant(const IVariant
&) = default;
458 IVariant
&operator=(const IVariant
&) = default;
461 #define SPIRV_CROSS_DECLARE_CLONE(T) \
462 IVariant *clone(ObjectPoolBase *pool) override \
464 return static_cast<ObjectPool<T> *>(pool)->allocate(*this); \
467 struct SPIRUndef
: IVariant
474 explicit SPIRUndef(TypeID basetype_
)
475 : basetype(basetype_
)
480 SPIRV_CROSS_DECLARE_CLONE(SPIRUndef
)
483 struct SPIRString
: IVariant
490 explicit SPIRString(std::string str_
)
491 : str(std::move(str_
))
497 SPIRV_CROSS_DECLARE_CLONE(SPIRString
)
500 // This type is only used by backends which need to access the combined image and sampler IDs separately after
501 // the OpSampledImage opcode.
502 struct SPIRCombinedImageSampler
: IVariant
506 type
= TypeCombinedImageSampler
508 SPIRCombinedImageSampler(TypeID type_
, VariableID image_
, VariableID sampler_
)
509 : combined_type(type_
)
514 TypeID combined_type
;
518 SPIRV_CROSS_DECLARE_CLONE(SPIRCombinedImageSampler
)
521 struct SPIRConstantOp
: IVariant
525 type
= TypeConstantOp
528 SPIRConstantOp(TypeID result_type
, spv::Op op
, const uint32_t *args
, uint32_t length
)
530 , basetype(result_type
)
532 arguments
.reserve(length
);
533 for (uint32_t i
= 0; i
< length
; i
++)
534 arguments
.push_back(args
[i
]);
538 SmallVector
<uint32_t> arguments
;
541 SPIRV_CROSS_DECLARE_CLONE(SPIRConstantOp
)
544 struct SPIRType
: IVariant
551 spv::Op op
= spv::Op::OpNop
;
552 explicit SPIRType(spv::Op op_
) : op(op_
) {}
575 AccelerationStructure
,
578 // Keep internal types at the end.
582 // MSL specific type, that is used by 'object'(analog of 'task' from glsl) shader.
586 // Scalar/vector/matrix support.
587 BaseType basetype
= Unknown
;
589 uint32_t vecsize
= 1;
590 uint32_t columns
= 1;
592 // Arrays, support array of arrays by having a vector of array sizes.
593 SmallVector
<uint32_t> array
;
595 // Array elements can be either specialization constants or specialization ops.
596 // This array determines how to interpret the array size.
597 // If an element is true, the element is a literal,
598 // otherwise, it's an expression, which must be resolved on demand.
599 // The actual size is not really known until runtime.
600 SmallVector
<bool> array_size_literal
;
603 // Keep track of how many pointer layers we have.
604 uint32_t pointer_depth
= 0;
605 bool pointer
= false;
606 bool forward_pointer
= false;
608 spv::StorageClass storage
= spv::StorageClassGeneric
;
610 SmallVector
<TypeID
> member_types
;
612 // If member order has been rewritten to handle certain scenarios with Offset,
613 // allow codegen to rewrite the index.
614 SmallVector
<uint32_t> member_type_index_redirection
;
624 spv::ImageFormat format
;
625 spv::AccessQualifier access
;
628 // Structs can be declared multiple times if they are used as part of interface blocks.
629 // We want to detect this so that we only emit the struct definition once.
630 // Since we cannot rely on OpName to be equal, we need to figure out aliases.
631 TypeID type_alias
= 0;
633 // Denotes the type which this type is based on.
634 // Allows the backend to traverse how a complex type is built up during access chains.
635 TypeID parent_type
= 0;
637 // Used in backends to avoid emitting members with conflicting names.
638 std::unordered_set
<std::string
> member_name_cache
;
640 SPIRV_CROSS_DECLARE_CLONE(SPIRType
)
643 struct SPIRExtension
: IVariant
655 SPV_AMD_shader_ballot
,
656 SPV_AMD_shader_explicit_vertex_parameter
,
657 SPV_AMD_shader_trinary_minmax
,
659 NonSemanticDebugPrintf
,
660 NonSemanticShaderDebugInfo
,
664 explicit SPIRExtension(Extension ext_
)
670 SPIRV_CROSS_DECLARE_CLONE(SPIRExtension
)
673 // SPIREntryPoint is not a variant since its IDs are used to decorate OpFunction,
674 // so in order to avoid conflicts, we can't stick them in the ids array.
675 struct SPIREntryPoint
677 SPIREntryPoint(FunctionID self_
, spv::ExecutionModel execution_model
, const std::string
&entry_name
)
680 , orig_name(entry_name
)
681 , model(execution_model
)
684 SPIREntryPoint() = default;
688 std::string orig_name
;
689 SmallVector
<VariableID
> interface_variables
;
694 uint32_t x
= 0, y
= 0, z
= 0;
695 uint32_t id_x
= 0, id_y
= 0, id_z
= 0;
696 uint32_t constant
= 0; // Workgroup size can be expressed as a constant/spec-constant instead.
698 uint32_t invocations
= 0;
699 uint32_t output_vertices
= 0;
700 uint32_t output_primitives
= 0;
701 spv::ExecutionModel model
= spv::ExecutionModelMax
;
702 bool geometry_passthrough
= false;
705 struct SPIRExpression
: IVariant
709 type
= TypeExpression
712 // Only created by the backend target to avoid creating tons of temporaries.
713 SPIRExpression(std::string expr
, TypeID expression_type_
, bool immutable_
)
714 : expression(std::move(expr
))
715 , expression_type(expression_type_
)
716 , immutable(immutable_
)
720 // If non-zero, prepend expression with to_expression(base_expression).
721 // Used in amortizing multiple calls to to_expression()
722 // where in certain cases that would quickly force a temporary when not needed.
723 ID base_expression
= 0;
725 std::string expression
;
726 TypeID expression_type
= 0;
728 // If this expression is a forwarded load,
729 // allow us to reference the original variable.
732 // If this expression will never change, we can avoid lots of temporaries
733 // in high level source.
734 // An expression being immutable can be speculative,
735 // it is assumed that this is true almost always.
736 bool immutable
= false;
738 // Before use, this expression must be transposed.
739 // This is needed for targets which don't support row_major layouts.
740 bool need_transpose
= false;
742 // Whether or not this is an access chain expression.
743 bool access_chain
= false;
745 // Whether or not gl_MeshVerticesEXT[].gl_Position (as a whole or .y) is referenced
746 bool access_meshlet_position_y
= false;
748 // A list of expressions which this expression depends on.
749 SmallVector
<ID
> expression_dependencies
;
751 // Similar as expression dependencies, but does not stop the tracking for force-temporary variables.
752 // We need to know the full chain from store back to any SSA variable.
753 SmallVector
<ID
> invariance_dependencies
;
755 // By reading this expression, we implicitly read these expressions as well.
756 // Used by access chain Store and Load since we read multiple expressions in this case.
757 SmallVector
<ID
> implied_read_expressions
;
759 // The expression was emitted at a certain scope. Lets us track when an expression read means multiple reads.
760 uint32_t emitted_loop_level
= 0;
762 SPIRV_CROSS_DECLARE_CLONE(SPIRExpression
)
765 struct SPIRFunctionPrototype
: IVariant
769 type
= TypeFunctionPrototype
772 explicit SPIRFunctionPrototype(TypeID return_type_
)
773 : return_type(return_type_
)
778 SmallVector
<uint32_t> parameter_types
;
780 SPIRV_CROSS_DECLARE_CLONE(SPIRFunctionPrototype
)
783 struct SPIRBlock
: IVariant
793 Direct
, // Emit next block directly without a particular condition.
795 Select
, // Block ends with an if/else block.
796 MultiSelect
, // Block ends with switch statement.
798 Return
, // Block ends with return.
801 IgnoreIntersection
, // Ray Tracing
802 TerminateRay
, // Ray Tracing
803 EmitMeshTasks
// Mesh shaders
824 MergeToSelectForLoop
,
825 MergeToDirectForLoop
,
826 MergeToSelectContinueForLoop
829 enum ContinueBlockType
833 // Continue block is branchless and has at least one instruction.
836 // Noop continue block.
839 // Continue block is conditional.
842 // Highly unlikely that anything will use this,
843 // since it is really awkward/impossible to express in GLSL.
849 NoDominator
= 0xffffffffu
852 Terminator terminator
= Unknown
;
853 Merge merge
= MergeNone
;
854 Hints hint
= HintNone
;
855 BlockID next_block
= 0;
856 BlockID merge_block
= 0;
857 BlockID continue_block
= 0;
859 ID return_value
= 0; // If 0, return nothing (void).
861 BlockID true_block
= 0;
862 BlockID false_block
= 0;
863 BlockID default_block
= 0;
865 // If terminator is EmitMeshTasksEXT.
872 SmallVector
<Instruction
> ops
;
876 ID local_variable
; // flush local variable ...
877 BlockID parent
; // If we're in from_block and want to branch into this block ...
878 VariableID function_variable
; // to this function-global "phi" variable first.
881 // Before entering this block flush out local variables to magical "phi" variables.
882 SmallVector
<Phi
> phi_variables
;
884 // Declare these temporaries before beginning the block.
885 // Used for handling complex continue blocks which have side effects.
886 SmallVector
<std::pair
<TypeID
, ID
>> declare_temporary
;
888 // Declare these temporaries, but only conditionally if this block turns out to be
889 // a complex loop header.
890 SmallVector
<std::pair
<TypeID
, ID
>> potential_declare_temporary
;
897 SmallVector
<Case
> cases_32bit
;
898 SmallVector
<Case
> cases_64bit
;
900 // If we have tried to optimize code for this block but failed,
901 // keep track of this.
902 bool disable_block_optimization
= false;
904 // If the continue block is complex, fallback to "dumb" for loops.
905 bool complex_continue
= false;
907 // Do we need a ladder variable to defer breaking out of a loop construct after a switch block?
908 bool need_ladder_break
= false;
910 // If marked, we have explicitly handled Phi from this block, so skip any flushes related to that on a branch.
911 // Used to handle an edge case with switch and case-label fallthrough where fall-through writes to Phi.
912 BlockID ignore_phi_from_block
= 0;
914 // The dominating block which this block might be within.
915 // Used in continue; blocks to determine if we really need to write continue.
916 BlockID loop_dominator
= 0;
918 // All access to these variables are dominated by this block,
919 // so before branching anywhere we need to make sure that we declare these variables.
920 SmallVector
<VariableID
> dominated_variables
;
922 // These are variables which should be declared in a for loop header, if we
923 // fail to use a classic for-loop,
924 // we remove these variables, and fall back to regular variables outside the loop.
925 SmallVector
<VariableID
> loop_variables
;
927 // Some expressions are control-flow dependent, i.e. any instruction which relies on derivatives or
928 // sub-group-like operations.
929 // Make sure that we only use these expressions in the original block.
930 SmallVector
<ID
> invalidate_expressions
;
932 SPIRV_CROSS_DECLARE_CLONE(SPIRBlock
)
935 struct SPIRFunction
: IVariant
942 SPIRFunction(TypeID return_type_
, TypeID function_type_
)
943 : return_type(return_type_
)
944 , function_type(function_type_
)
953 uint32_t write_count
;
955 // Set to true if this parameter aliases a global variable,
956 // used mostly in Metal where global variables
957 // have to be passed down to functions as regular arguments.
958 // However, for this kind of variable, we should not care about
959 // read and write counts as access to the function arguments
960 // is not local to the function in question.
961 bool alias_global_variable
;
964 // When calling a function, and we're remapping separate image samplers,
965 // resolve these arguments into combined image samplers and pass them
966 // as additional arguments in this order.
967 // It gets more complicated as functions can pull in their own globals
968 // and combine them with parameters,
969 // so we need to distinguish if something is local parameter index
971 struct CombinedImageSamplerParameter
975 VariableID sampler_id
;
982 TypeID function_type
;
983 SmallVector
<Parameter
> arguments
;
985 // Can be used by backends to add magic arguments.
986 // Currently used by combined image/sampler implementation.
988 SmallVector
<Parameter
> shadow_arguments
;
989 SmallVector
<VariableID
> local_variables
;
990 BlockID entry_block
= 0;
991 SmallVector
<BlockID
> blocks
;
992 SmallVector
<CombinedImageSamplerParameter
> combined_parameters
;
996 uint32_t file_id
= 0;
997 uint32_t line_literal
= 0;
999 EntryLine entry_line
;
1001 void add_local_variable(VariableID id
)
1003 local_variables
.push_back(id
);
1006 void add_parameter(TypeID parameter_type
, ID id
, bool alias_global_variable
= false)
1008 // Arguments are read-only until proven otherwise.
1009 arguments
.push_back({ parameter_type
, id
, 0u, 0u, alias_global_variable
});
1012 // Hooks to be run when the function returns.
1013 // Mostly used for lowering internal data structures onto flattened structures.
1014 // Need to defer this, because they might rely on things which change during compilation.
1015 // Intentionally not a small vector, this one is rare, and std::function can be large.
1016 Vector
<std::function
<void()>> fixup_hooks_out
;
1018 // Hooks to be run when the function begins.
1019 // Mostly used for populating internal data structures from flattened structures.
1020 // Need to defer this, because they might rely on things which change during compilation.
1021 // Intentionally not a small vector, this one is rare, and std::function can be large.
1022 Vector
<std::function
<void()>> fixup_hooks_in
;
1024 // On function entry, make sure to copy a constant array into thread addr space to work around
1025 // the case where we are passing a constant array by value to a function on backends which do not
1026 // consider arrays value types.
1027 SmallVector
<ID
> constant_arrays_needed_on_stack
;
1029 bool active
= false;
1030 bool flush_undeclared
= true;
1031 bool do_combined_parameters
= true;
1033 SPIRV_CROSS_DECLARE_CLONE(SPIRFunction
)
1036 struct SPIRAccessChain
: IVariant
1040 type
= TypeAccessChain
1043 SPIRAccessChain(TypeID basetype_
, spv::StorageClass storage_
, std::string base_
, std::string dynamic_index_
,
1044 int32_t static_index_
)
1045 : basetype(basetype_
)
1047 , base(std::move(base_
))
1048 , dynamic_index(std::move(dynamic_index_
))
1049 , static_index(static_index_
)
1053 // The access chain represents an offset into a buffer.
1054 // Some backends need more complicated handling of access chains to be able to use buffers, like HLSL
1055 // which has no usable buffer type ala GLSL SSBOs.
1056 // StructuredBuffer is too limited, so our only option is to deal with ByteAddressBuffer which works with raw addresses.
1059 spv::StorageClass storage
;
1061 std::string dynamic_index
;
1062 int32_t static_index
;
1064 VariableID loaded_from
= 0;
1065 uint32_t matrix_stride
= 0;
1066 uint32_t array_stride
= 0;
1067 bool row_major_matrix
= false;
1068 bool immutable
= false;
1070 // By reading this expression, we implicitly read these expressions as well.
1071 // Used by access chain Store and Load since we read multiple expressions in this case.
1072 SmallVector
<ID
> implied_read_expressions
;
1074 SPIRV_CROSS_DECLARE_CLONE(SPIRAccessChain
)
1077 struct SPIRVariable
: IVariant
1084 SPIRVariable() = default;
1085 SPIRVariable(TypeID basetype_
, spv::StorageClass storage_
, ID initializer_
= 0, VariableID basevariable_
= 0)
1086 : basetype(basetype_
)
1088 , initializer(initializer_
)
1089 , basevariable(basevariable_
)
1093 TypeID basetype
= 0;
1094 spv::StorageClass storage
= spv::StorageClassGeneric
;
1095 uint32_t decoration
= 0;
1097 VariableID basevariable
= 0;
1099 SmallVector
<uint32_t> dereference_chain
;
1100 bool compat_builtin
= false;
1102 // If a variable is shadowed, we only statically assign to it
1103 // and never actually emit a statement for it.
1104 // When we read the variable as an expression, just forward
1105 // shadowed_id as the expression.
1106 bool statically_assigned
= false;
1107 ID static_expression
= 0;
1109 // Temporaries which can remain forwarded as long as this variable is not modified.
1110 SmallVector
<ID
> dependees
;
1112 bool deferred_declaration
= false;
1113 bool phi_variable
= false;
1115 // Used to deal with Phi variable flushes. See flush_phi().
1116 bool allocate_temporary_copy
= false;
1118 bool remapped_variable
= false;
1119 uint32_t remapped_components
= 0;
1121 // The block which dominates all access to this variable.
1122 BlockID dominator
= 0;
1123 // If true, this variable is a loop variable, when accessing the variable
1125 // we should statically forward it.
1126 bool loop_variable
= false;
1127 // Set to true while we're inside the for loop.
1128 bool loop_variable_enable
= false;
1130 // Used to find global LUTs
1131 bool is_written_to
= false;
1133 SPIRFunction::Parameter
*parameter
= nullptr;
1135 SPIRV_CROSS_DECLARE_CLONE(SPIRVariable
)
1138 struct SPIRConstant
: IVariant
1156 struct ConstantVector
1159 // If != 0, this element is a specialization constant, and we should keep track of it as such.
1161 uint32_t vecsize
= 1;
1165 memset(r
, 0, sizeof(r
));
1169 struct ConstantMatrix
1171 ConstantVector c
[4];
1172 // If != 0, this column is a specialization constant, and we should keep track of it as such.
1174 uint32_t columns
= 1;
1177 static inline float f16_to_f32(uint16_t u16_value
)
1179 // Based on the GLM implementation.
1180 int s
= (u16_value
>> 15) & 0x1;
1181 int e
= (u16_value
>> 10) & 0x1f;
1182 int m
= (u16_value
>> 0) & 0x3ff;
1194 u
.u32
= uint32_t(s
) << 31;
1199 while ((m
& 0x400) == 0)
1213 u
.u32
= (uint32_t(s
) << 31) | 0x7f800000u
;
1218 u
.u32
= (uint32_t(s
) << 31) | 0x7f800000u
| (m
<< 13);
1225 u
.u32
= (uint32_t(s
) << 31) | (e
<< 23) | m
;
1229 inline uint32_t specialization_constant_id(uint32_t col
, uint32_t row
) const
1231 return m
.c
[col
].id
[row
];
1234 inline uint32_t specialization_constant_id(uint32_t col
) const
1239 inline uint32_t scalar(uint32_t col
= 0, uint32_t row
= 0) const
1241 return m
.c
[col
].r
[row
].u32
;
1244 inline int16_t scalar_i16(uint32_t col
= 0, uint32_t row
= 0) const
1246 return int16_t(m
.c
[col
].r
[row
].u32
& 0xffffu
);
1249 inline uint16_t scalar_u16(uint32_t col
= 0, uint32_t row
= 0) const
1251 return uint16_t(m
.c
[col
].r
[row
].u32
& 0xffffu
);
1254 inline int8_t scalar_i8(uint32_t col
= 0, uint32_t row
= 0) const
1256 return int8_t(m
.c
[col
].r
[row
].u32
& 0xffu
);
1259 inline uint8_t scalar_u8(uint32_t col
= 0, uint32_t row
= 0) const
1261 return uint8_t(m
.c
[col
].r
[row
].u32
& 0xffu
);
1264 inline float scalar_f16(uint32_t col
= 0, uint32_t row
= 0) const
1266 return f16_to_f32(scalar_u16(col
, row
));
1269 inline float scalar_f32(uint32_t col
= 0, uint32_t row
= 0) const
1271 return m
.c
[col
].r
[row
].f32
;
1274 inline int32_t scalar_i32(uint32_t col
= 0, uint32_t row
= 0) const
1276 return m
.c
[col
].r
[row
].i32
;
1279 inline double scalar_f64(uint32_t col
= 0, uint32_t row
= 0) const
1281 return m
.c
[col
].r
[row
].f64
;
1284 inline int64_t scalar_i64(uint32_t col
= 0, uint32_t row
= 0) const
1286 return m
.c
[col
].r
[row
].i64
;
1289 inline uint64_t scalar_u64(uint32_t col
= 0, uint32_t row
= 0) const
1291 return m
.c
[col
].r
[row
].u64
;
1294 inline const ConstantVector
&vector() const
1299 inline uint32_t vector_size() const
1301 return m
.c
[0].vecsize
;
1304 inline uint32_t columns() const
1309 inline void make_null(const SPIRType
&constant_type_
)
1312 m
.columns
= constant_type_
.columns
;
1314 c
.vecsize
= constant_type_
.vecsize
;
1317 inline bool constant_is_null() const
1321 if (!subconstants
.empty())
1324 for (uint32_t col
= 0; col
< columns(); col
++)
1325 for (uint32_t row
= 0; row
< vector_size(); row
++)
1326 if (scalar_u64(col
, row
) != 0)
1332 explicit SPIRConstant(uint32_t constant_type_
)
1333 : constant_type(constant_type_
)
1337 SPIRConstant() = default;
1339 SPIRConstant(TypeID constant_type_
, const uint32_t *elements
, uint32_t num_elements
, bool specialized
)
1340 : constant_type(constant_type_
)
1341 , specialization(specialized
)
1343 subconstants
.reserve(num_elements
);
1344 for (uint32_t i
= 0; i
< num_elements
; i
++)
1345 subconstants
.push_back(elements
[i
]);
1346 specialization
= specialized
;
1349 // Construct scalar (32-bit).
1350 SPIRConstant(TypeID constant_type_
, uint32_t v0
, bool specialized
)
1351 : constant_type(constant_type_
)
1352 , specialization(specialized
)
1354 m
.c
[0].r
[0].u32
= v0
;
1359 // Construct scalar (64-bit).
1360 SPIRConstant(TypeID constant_type_
, uint64_t v0
, bool specialized
)
1361 : constant_type(constant_type_
)
1362 , specialization(specialized
)
1364 m
.c
[0].r
[0].u64
= v0
;
1369 // Construct vectors and matrices.
1370 SPIRConstant(TypeID constant_type_
, const SPIRConstant
*const *vector_elements
, uint32_t num_elements
,
1372 : constant_type(constant_type_
)
1373 , specialization(specialized
)
1375 bool matrix
= vector_elements
[0]->m
.c
[0].vecsize
> 1;
1379 m
.columns
= num_elements
;
1381 for (uint32_t i
= 0; i
< num_elements
; i
++)
1383 m
.c
[i
] = vector_elements
[i
]->m
.c
[0];
1384 if (vector_elements
[i
]->specialization
)
1385 m
.id
[i
] = vector_elements
[i
]->self
;
1390 m
.c
[0].vecsize
= num_elements
;
1393 for (uint32_t i
= 0; i
< num_elements
; i
++)
1395 m
.c
[0].r
[i
] = vector_elements
[i
]->m
.c
[0].r
[0];
1396 if (vector_elements
[i
]->specialization
)
1397 m
.c
[0].id
[i
] = vector_elements
[i
]->self
;
1402 TypeID constant_type
= 0;
1405 // If this constant is a specialization constant (i.e. created with OpSpecConstant*).
1406 bool specialization
= false;
1407 // If this constant is used as an array length which creates specialization restrictions on some backends.
1408 bool is_used_as_array_length
= false;
1410 // If true, this is a LUT, and should always be declared in the outer scope.
1411 bool is_used_as_lut
= false;
1413 // For composites which are constant arrays, etc.
1414 SmallVector
<ConstantID
> subconstants
;
1416 // Non-Vulkan GLSL, HLSL and sometimes MSL emits defines for each specialization constant,
1417 // and uses them to initialize the constant. This allows the user
1418 // to still be able to specialize the value by supplying corresponding
1419 // preprocessor directives before compiling the shader.
1420 std::string specialization_constant_macro_name
;
1422 SPIRV_CROSS_DECLARE_CLONE(SPIRConstant
)
1425 // Variants have a very specific allocation scheme.
1426 struct ObjectPoolGroup
1428 std::unique_ptr
<ObjectPoolBase
> pools
[TypeCount
];
1434 explicit Variant(ObjectPoolGroup
*group_
)
1442 group
->pools
[type
]->deallocate_opaque(holder
);
1445 // Marking custom move constructor as noexcept is important.
1446 Variant(Variant
&&other
) SPIRV_CROSS_NOEXCEPT
1448 *this = std::move(other
);
1451 // We cannot copy from other variant without our own pool group.
1452 // Have to explicitly copy.
1453 Variant(const Variant
&variant
) = delete;
1455 // Marking custom move constructor as noexcept is important.
1456 Variant
&operator=(Variant
&&other
) SPIRV_CROSS_NOEXCEPT
1461 group
->pools
[type
]->deallocate_opaque(holder
);
1462 holder
= other
.holder
;
1463 group
= other
.group
;
1465 allow_type_rewrite
= other
.allow_type_rewrite
;
1467 other
.holder
= nullptr;
1468 other
.type
= TypeNone
;
1473 // This copy/clone should only be called in the Compiler constructor.
1474 // If this is called inside ::compile(), we invalidate any references we took higher in the stack.
1475 // This should never happen.
1476 Variant
&operator=(const Variant
&other
)
1478 //#define SPIRV_CROSS_COPY_CONSTRUCTOR_SANITIZE
1479 #ifdef SPIRV_CROSS_COPY_CONSTRUCTOR_SANITIZE
1485 group
->pools
[type
]->deallocate_opaque(holder
);
1488 holder
= other
.holder
->clone(group
->pools
[other
.type
].get());
1493 allow_type_rewrite
= other
.allow_type_rewrite
;
1498 void set(IVariant
*val
, Types new_type
)
1501 group
->pools
[type
]->deallocate_opaque(holder
);
1504 if (!allow_type_rewrite
&& type
!= TypeNone
&& type
!= new_type
)
1507 group
->pools
[new_type
]->deallocate_opaque(val
);
1508 SPIRV_CROSS_THROW("Overwriting a variant with new type.");
1513 allow_type_rewrite
= false;
1516 template <typename T
, typename
... Ts
>
1517 T
*allocate_and_set(Types new_type
, Ts
&&... ts
)
1519 T
*val
= static_cast<ObjectPool
<T
> &>(*group
->pools
[new_type
]).allocate(std::forward
<Ts
>(ts
)...);
1524 template <typename T
>
1528 SPIRV_CROSS_THROW("nullptr");
1529 if (static_cast<Types
>(T::type
) != type
)
1530 SPIRV_CROSS_THROW("Bad cast");
1531 return *static_cast<T
*>(holder
);
1534 template <typename T
>
1535 const T
&get() const
1538 SPIRV_CROSS_THROW("nullptr");
1539 if (static_cast<Types
>(T::type
) != type
)
1540 SPIRV_CROSS_THROW("Bad cast");
1541 return *static_cast<const T
*>(holder
);
1544 Types
get_type() const
1551 return holder
? holder
->self
: ID(0);
1562 group
->pools
[type
]->deallocate_opaque(holder
);
1567 void set_allow_type_rewrite()
1569 allow_type_rewrite
= true;
1573 ObjectPoolGroup
*group
= nullptr;
1574 IVariant
*holder
= nullptr;
1575 Types type
= TypeNone
;
1576 bool allow_type_rewrite
= false;
1579 template <typename T
>
1580 T
&variant_get(Variant
&var
)
1582 return var
.get
<T
>();
1585 template <typename T
>
1586 const T
&variant_get(const Variant
&var
)
1588 return var
.get
<T
>();
1591 template <typename T
, typename
... P
>
1592 T
&variant_set(Variant
&var
, P
&&... args
)
1594 auto *ptr
= var
.allocate_and_set
<T
>(static_cast<Types
>(T::type
), std::forward
<P
>(args
)...);
1598 struct AccessChainMeta
1600 uint32_t storage_physical_type
= 0;
1601 bool need_transpose
= false;
1602 bool storage_is_packed
= false;
1603 bool storage_is_invariant
= false;
1604 bool flattened_struct
= false;
1605 bool relaxed_precision
= false;
1606 bool access_meshlet_position_y
= false;
1607 bool chain_is_builtin
= false;
1608 spv::BuiltIn builtin
= {};
1611 enum ExtendedDecorations
1613 // Marks if a buffer block is re-packed, i.e. member declaration might be subject to PhysicalTypeID remapping and padding.
1614 SPIRVCrossDecorationBufferBlockRepacked
= 0,
1616 // A type in a buffer block might be declared with a different physical type than the logical type.
1617 // If this is not set, PhysicalTypeID == the SPIR-V type as declared.
1618 SPIRVCrossDecorationPhysicalTypeID
,
1620 // Marks if the physical type is to be declared with tight packing rules, i.e. packed_floatN on MSL and friends.
1621 // If this is set, PhysicalTypeID might also be set. It can be set to same as logical type if all we're doing
1622 // is converting float3 to packed_float3 for example.
1623 // If this is marked on a struct, it means the struct itself must use only Packed types for all its members.
1624 SPIRVCrossDecorationPhysicalTypePacked
,
1626 // The padding in bytes before declaring this struct member.
1627 // If used on a struct type, marks the target size of a struct.
1628 SPIRVCrossDecorationPaddingTarget
,
1630 SPIRVCrossDecorationInterfaceMemberIndex
,
1631 SPIRVCrossDecorationInterfaceOrigID
,
1632 SPIRVCrossDecorationResourceIndexPrimary
,
1633 // Used for decorations like resource indices for samplers when part of combined image samplers.
1634 // A variable might need to hold two resource indices in this case.
1635 SPIRVCrossDecorationResourceIndexSecondary
,
1636 // Used for resource indices for multiplanar images when part of combined image samplers.
1637 SPIRVCrossDecorationResourceIndexTertiary
,
1638 SPIRVCrossDecorationResourceIndexQuaternary
,
1640 // Marks a buffer block for using explicit offsets (GLSL/HLSL).
1641 SPIRVCrossDecorationExplicitOffset
,
1643 // Apply to a variable in the Input storage class; marks it as holding the base group passed to vkCmdDispatchBase(),
1644 // or the base vertex and instance indices passed to vkCmdDrawIndexed().
1645 // In MSL, this is used to adjust the WorkgroupId and GlobalInvocationId variables in compute shaders,
1646 // and to hold the BaseVertex and BaseInstance variables in vertex shaders.
1647 SPIRVCrossDecorationBuiltInDispatchBase
,
1649 // Apply to a variable that is a function parameter; marks it as being a "dynamic"
1650 // combined image-sampler. In MSL, this is used when a function parameter might hold
1651 // either a regular combined image-sampler or one that has an attached sampler
1652 // Y'CbCr conversion.
1653 SPIRVCrossDecorationDynamicImageSampler
,
1655 // Apply to a variable in the Input storage class; marks it as holding the size of the stage
1657 // In MSL, this is used to hold the vertex and instance counts in a tessellation pipeline
1659 SPIRVCrossDecorationBuiltInStageInputSize
,
1661 // Apply to any access chain of a tessellation I/O variable; stores the type of the sub-object
1662 // that was chained to, as recorded in the input variable itself. This is used in case the pointer
1663 // is itself used as the base of an access chain, to calculate the original type of the sub-object
1664 // chained to, in case a swizzle needs to be applied. This should not happen normally with valid
1665 // SPIR-V, but the MSL backend can change the type of input variables, necessitating the
1666 // addition of swizzles to keep the generated code compiling.
1667 SPIRVCrossDecorationTessIOOriginalInputTypeID
,
1669 // Apply to any access chain of an interface variable used with pull-model interpolation, where the variable is a
1670 // vector but the resulting pointer is a scalar; stores the component index that is to be accessed by the chain.
1671 // This is used when emitting calls to interpolation functions on the chain in MSL: in this case, the component
1672 // must be applied to the result, since pull-model interpolants in MSL cannot be swizzled directly, but the
1673 // results of interpolation can.
1674 SPIRVCrossDecorationInterpolantComponentExpr
,
1676 // Apply to any struct type that is used in the Workgroup storage class.
1677 // This causes matrices in MSL prior to Metal 3.0 to be emitted using a special
1678 // class that is convertible to the standard matrix type, to work around the
1679 // lack of constructors in the 'threadgroup' address space.
1680 SPIRVCrossDecorationWorkgroupStruct
,
1682 SPIRVCrossDecorationOverlappingBinding
,
1684 SPIRVCrossDecorationCount
1692 std::string qualified_alias
;
1693 std::string hlsl_semantic
;
1694 std::string user_type
;
1695 Bitset decoration_flags
;
1696 spv::BuiltIn builtin_type
= spv::BuiltInMax
;
1697 uint32_t location
= 0;
1698 uint32_t component
= 0;
1700 uint32_t binding
= 0;
1701 uint32_t offset
= 0;
1702 uint32_t xfb_buffer
= 0;
1703 uint32_t xfb_stride
= 0;
1704 uint32_t stream
= 0;
1705 uint32_t array_stride
= 0;
1706 uint32_t matrix_stride
= 0;
1707 uint32_t input_attachment
= 0;
1708 uint32_t spec_id
= 0;
1710 spv::FPRoundingMode fp_rounding_mode
= spv::FPRoundingModeMax
;
1711 bool builtin
= false;
1712 bool qualified_alias_explicit_override
= false;
1718 // MSVC 2013 workaround to init like this.
1719 for (auto &v
: values
)
1724 uint32_t values
[SPIRVCrossDecorationCount
];
1728 Decoration decoration
;
1730 // Intentionally not a SmallVector. Decoration is large and somewhat rare.
1731 Vector
<Decoration
> members
;
1733 std::unordered_map
<uint32_t, uint32_t> decoration_word_offset
;
1735 // For SPV_GOOGLE_hlsl_functionality1.
1736 bool hlsl_is_magic_counter_buffer
= false;
1737 // ID for the sibling counter buffer.
1738 uint32_t hlsl_magic_counter_buffer
= 0;
1741 // A user callback that remaps the type of any variable.
1742 // var_name is the declared name of the variable.
1743 // name_of_type is the textual name of the type which will be used in the code unless written to by the callback.
1744 using VariableTypeRemapCallback
=
1745 std::function
<void(const SPIRType
&type
, const std::string
&var_name
, std::string
&name_of_type
)>;
1750 inline void u32(uint32_t value
)
1752 h
= (h
* 0x100000001b3ull
) ^ value
;
1755 inline uint64_t get() const
1761 uint64_t h
= 0xcbf29ce484222325ull
;
1764 static inline bool type_is_floating_point(const SPIRType
&type
)
1766 return type
.basetype
== SPIRType::Half
|| type
.basetype
== SPIRType::Float
|| type
.basetype
== SPIRType::Double
;
1769 static inline bool type_is_integral(const SPIRType
&type
)
1771 return type
.basetype
== SPIRType::SByte
|| type
.basetype
== SPIRType::UByte
|| type
.basetype
== SPIRType::Short
||
1772 type
.basetype
== SPIRType::UShort
|| type
.basetype
== SPIRType::Int
|| type
.basetype
== SPIRType::UInt
||
1773 type
.basetype
== SPIRType::Int64
|| type
.basetype
== SPIRType::UInt64
;
1776 static inline SPIRType::BaseType
to_signed_basetype(uint32_t width
)
1781 return SPIRType::SByte
;
1783 return SPIRType::Short
;
1785 return SPIRType::Int
;
1787 return SPIRType::Int64
;
1789 SPIRV_CROSS_THROW("Invalid bit width.");
1793 static inline SPIRType::BaseType
to_unsigned_basetype(uint32_t width
)
1798 return SPIRType::UByte
;
1800 return SPIRType::UShort
;
1802 return SPIRType::UInt
;
1804 return SPIRType::UInt64
;
1806 SPIRV_CROSS_THROW("Invalid bit width.");
1810 // Returns true if an arithmetic operation does not change behavior depending on signedness.
1811 static inline bool opcode_is_sign_invariant(spv::Op opcode
)
1816 case spv::OpINotEqual
:
1820 case spv::OpShiftLeftLogical
:
1821 case spv::OpBitwiseOr
:
1822 case spv::OpBitwiseXor
:
1823 case spv::OpBitwiseAnd
:
1831 static inline bool opcode_can_promote_integer_implicitly(spv::Op opcode
)
1835 case spv::OpSNegate
:
1837 case spv::OpBitwiseAnd
:
1838 case spv::OpBitwiseOr
:
1839 case spv::OpBitwiseXor
:
1840 case spv::OpShiftLeftLogical
:
1841 case spv::OpShiftRightLogical
:
1842 case spv::OpShiftRightArithmetic
:
1858 struct SetBindingPair
1863 inline bool operator==(const SetBindingPair
&other
) const
1865 return desc_set
== other
.desc_set
&& binding
== other
.binding
;
1868 inline bool operator<(const SetBindingPair
&other
) const
1870 return desc_set
< other
.desc_set
|| (desc_set
== other
.desc_set
&& binding
< other
.binding
);
1874 struct LocationComponentPair
1879 inline bool operator==(const LocationComponentPair
&other
) const
1881 return location
== other
.location
&& component
== other
.component
;
1884 inline bool operator<(const LocationComponentPair
&other
) const
1886 return location
< other
.location
|| (location
== other
.location
&& component
< other
.component
);
1890 struct StageSetBinding
1892 spv::ExecutionModel model
;
1896 inline bool operator==(const StageSetBinding
&other
) const
1898 return model
== other
.model
&& desc_set
== other
.desc_set
&& binding
== other
.binding
;
1902 struct InternalHasher
1904 inline size_t operator()(const SetBindingPair
&value
) const
1906 // Quality of hash doesn't really matter here.
1907 auto hash_set
= std::hash
<uint32_t>()(value
.desc_set
);
1908 auto hash_binding
= std::hash
<uint32_t>()(value
.binding
);
1909 return (hash_set
* 0x10001b31) ^ hash_binding
;
1912 inline size_t operator()(const LocationComponentPair
&value
) const
1914 // Quality of hash doesn't really matter here.
1915 auto hash_set
= std::hash
<uint32_t>()(value
.location
);
1916 auto hash_binding
= std::hash
<uint32_t>()(value
.component
);
1917 return (hash_set
* 0x10001b31) ^ hash_binding
;
1920 inline size_t operator()(const StageSetBinding
&value
) const
1922 // Quality of hash doesn't really matter here.
1923 auto hash_model
= std::hash
<uint32_t>()(value
.model
);
1924 auto hash_set
= std::hash
<uint32_t>()(value
.desc_set
);
1925 auto tmp_hash
= (hash_model
* 0x10001b31) ^ hash_set
;
1926 return (tmp_hash
* 0x10001b31) ^ value
.binding
;
1930 // Special constant used in a {MSL,HLSL}ResourceBinding desc_set
1931 // element to indicate the bindings for the push constants.
1932 static const uint32_t ResourceBindingPushConstantDescriptorSet
= ~(0u);
1934 // Special constant used in a {MSL,HLSL}ResourceBinding binding
1935 // element to indicate the bindings for the push constants.
1936 static const uint32_t ResourceBindingPushConstantBinding
= 0;
1937 } // namespace SPIRV_CROSS_NAMESPACE
1941 template <SPIRV_CROSS_NAMESPACE::Types type
>
1942 struct hash
<SPIRV_CROSS_NAMESPACE::TypedID
<type
>>
1944 size_t operator()(const SPIRV_CROSS_NAMESPACE::TypedID
<type
> &value
) const
1946 return std::hash
<uint32_t>()(value
);