| /* |
| * Copyright 2015-2016 ARM Limited |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #ifndef SPIRV_COMMON_HPP |
| #define SPIRV_COMMON_HPP |
| |
| #include <sstream> |
| #include <stdio.h> |
| #include <string.h> |
| |
| namespace spirv_cross |
| { |
| class CompilerError : public std::runtime_error |
| { |
| public: |
| CompilerError(const std::string &str) |
| : std::runtime_error(str) |
| { |
| } |
| }; |
| |
| namespace inner |
| { |
| template <typename T> |
| void join_helper(std::ostringstream &stream, T &&t) |
| { |
| stream << std::forward<T>(t); |
| } |
| |
| template <typename T, typename... Ts> |
| void join_helper(std::ostringstream &stream, T &&t, Ts &&... ts) |
| { |
| stream << std::forward<T>(t); |
| join_helper(stream, std::forward<Ts>(ts)...); |
| } |
| } |
| |
| // Helper template to avoid lots of nasty string temporary munging. |
| template <typename... Ts> |
| std::string join(Ts &&... ts) |
| { |
| std::ostringstream stream; |
| inner::join_helper(stream, std::forward<Ts>(ts)...); |
| return stream.str(); |
| } |
| |
| inline std::string merge(const std::vector<std::string> &list) |
| { |
| std::string s; |
| for (auto &elem : list) |
| { |
| s += elem; |
| if (&elem != &list.back()) |
| s += ", "; |
| } |
| return s; |
| } |
| |
| template <typename T> |
| inline std::string convert_to_string(T &&t) |
| { |
| return std::to_string(std::forward<T>(t)); |
| } |
| |
| // Allow implementations to set a convenient standard precision |
| #ifndef SPIRV_CROSS_FLT_FMT |
| #define SPIRV_CROSS_FLT_FMT "%.32g" |
| #endif |
| |
| inline std::string convert_to_string(float t) |
| { |
| // std::to_string for floating point values is broken. |
| // Fallback to something more sane. |
| char buf[64]; |
| sprintf(buf, SPIRV_CROSS_FLT_FMT, t); |
| // Ensure that the literal is float. |
| if (!strchr(buf, '.') && !strchr(buf, 'e')) |
| strcat(buf, ".0"); |
| return buf; |
| } |
| |
| inline std::string convert_to_string(double t) |
| { |
| // std::to_string for floating point values is broken. |
| // Fallback to something more sane. |
| char buf[64]; |
| sprintf(buf, SPIRV_CROSS_FLT_FMT, t); |
| // Ensure that the literal is float. |
| if (!strchr(buf, '.') && !strchr(buf, 'e')) |
| strcat(buf, ".0"); |
| return buf; |
| } |
| |
| struct Instruction |
| { |
| Instruction(const std::vector<uint32_t> &spirv, uint32_t &index); |
| |
| uint16_t op; |
| uint16_t count; |
| uint32_t offset; |
| uint32_t length; |
| }; |
| |
| // Helper for Variant interface. |
| struct IVariant |
| { |
| virtual ~IVariant() = default; |
| uint32_t self = 0; |
| }; |
| |
| enum Types |
| { |
| TypeNone, |
| TypeType, |
| TypeVariable, |
| TypeConstant, |
| TypeFunction, |
| TypeFunctionPrototype, |
| TypePointer, |
| TypeBlock, |
| TypeExtension, |
| TypeExpression, |
| TypeUndef |
| }; |
| |
| struct SPIRUndef : IVariant |
| { |
| enum |
| { |
| type = TypeUndef |
| }; |
| SPIRUndef(uint32_t basetype_) |
| : basetype(basetype_) |
| { |
| } |
| uint32_t basetype; |
| }; |
| |
| struct SPIRType : IVariant |
| { |
| enum |
| { |
| type = TypeType |
| }; |
| |
| enum BaseType |
| { |
| Unknown, |
| Void, |
| Boolean, |
| Char, |
| Int, |
| UInt, |
| Int64, |
| UInt64, |
| AtomicCounter, |
| Float, |
| Double, |
| Struct, |
| Image, |
| SampledImage, |
| Sampler |
| }; |
| |
| // Scalar/vector/matrix support. |
| BaseType basetype = Unknown; |
| uint32_t width = 0; |
| uint32_t vecsize = 1; |
| uint32_t columns = 1; |
| |
| // Arrays, suport array of arrays by having a vector of array sizes. |
| std::vector<uint32_t> array; |
| |
| // Pointers |
| bool pointer = false; |
| spv::StorageClass storage = spv::StorageClassGeneric; |
| |
| std::vector<uint32_t> member_types; |
| |
| bool is_packed = false; // Tightly packed in memory (no alignment padding) |
| |
| struct Image |
| { |
| uint32_t type; |
| spv::Dim dim; |
| bool depth; |
| bool arrayed; |
| bool ms; |
| uint32_t sampled; |
| spv::ImageFormat format; |
| } image; |
| |
| // Structs can be declared multiple times if they are used as part of interface blocks. |
| // We want to detect this so that we only emit the struct definition once. |
| // Since we cannot rely on OpName to be equal, we need to figure out aliases. |
| uint32_t type_alias = 0; |
| |
| // Used in backends to avoid emitting members with conflicting names. |
| std::unordered_set<std::string> member_name_cache; |
| }; |
| |
| struct SPIRExtension : IVariant |
| { |
| enum |
| { |
| type = TypeExtension |
| }; |
| |
| enum Extension |
| { |
| GLSL |
| }; |
| |
| SPIRExtension(Extension ext_) |
| : ext(ext_) |
| { |
| } |
| |
| Extension ext; |
| }; |
| |
| // SPIREntryPoint is not a variant since its IDs are used to decorate OpFunction, |
| // so in order to avoid conflicts, we can't stick them in the ids array. |
| struct SPIREntryPoint |
| { |
| SPIREntryPoint(uint32_t self_, spv::ExecutionModel execution_model, std::string entry_name) |
| : self(self_) |
| , name(std::move(entry_name)) |
| , model(execution_model) |
| { |
| } |
| SPIREntryPoint() = default; |
| |
| uint32_t self = 0; |
| std::string name; |
| std::vector<uint32_t> interface_variables; |
| |
| uint64_t flags = 0; |
| struct |
| { |
| uint32_t x = 0, y = 0, z = 0; |
| } workgroup_size; |
| uint32_t invocations = 0; |
| uint32_t output_vertices = 0; |
| spv::ExecutionModel model = {}; |
| }; |
| |
| struct SPIRExpression : IVariant |
| { |
| enum |
| { |
| type = TypeExpression |
| }; |
| |
| // Only created by the backend target to avoid creating tons of temporaries. |
| SPIRExpression(std::string expr, uint32_t expression_type_, bool immutable_) |
| : expression(move(expr)) |
| , expression_type(expression_type_) |
| , immutable(immutable_) |
| { |
| } |
| |
| // If non-zero, prepend expression with to_expression(base_expression). |
| // Used in amortizing multiple calls to to_expression() |
| // where in certain cases that would quickly force a temporary when not needed. |
| uint32_t base_expression = 0; |
| |
| std::string expression; |
| uint32_t expression_type = 0; |
| |
| // If this expression is a forwarded load, |
| // allow us to reference the original variable. |
| uint32_t loaded_from = 0; |
| |
| // If this expression will never change, we can avoid lots of temporaries |
| // in high level source. |
| // An expression being immutable can be speculative, |
| // it is assumed that this is true almost always. |
| bool immutable = false; |
| |
| // If this expression has been used while invalidated. |
| bool used_while_invalidated = false; |
| |
| // A list of expressions which this expression depends on. |
| std::vector<uint32_t> expression_dependencies; |
| }; |
| |
| struct SPIRFunctionPrototype : IVariant |
| { |
| enum |
| { |
| type = TypeFunctionPrototype |
| }; |
| |
| SPIRFunctionPrototype(uint32_t return_type_) |
| : return_type(return_type_) |
| { |
| } |
| |
| uint32_t return_type; |
| std::vector<uint32_t> parameter_types; |
| }; |
| |
| struct SPIRBlock : IVariant |
| { |
| enum |
| { |
| type = TypeBlock |
| }; |
| |
| enum Terminator |
| { |
| Unknown, |
| Direct, // Emit next block directly without a particular condition. |
| |
| Select, // Block ends with an if/else block. |
| MultiSelect, // Block ends with switch statement. |
| Loop, // Block ends with a loop. |
| |
| Return, // Block ends with return. |
| Unreachable, // Noop |
| Kill // Discard |
| }; |
| |
| enum Merge |
| { |
| MergeNone, |
| MergeLoop, |
| MergeSelection |
| }; |
| |
| enum Method |
| { |
| MergeToSelectForLoop, |
| MergeToDirectForLoop |
| }; |
| |
| enum ContinueBlockType |
| { |
| ContinueNone, |
| |
| // Continue block is branchless and has at least one instruction. |
| ForLoop, |
| |
| // Noop continue block. |
| WhileLoop, |
| |
| // Continue block is conditional. |
| DoWhileLoop, |
| |
| // Highly unlikely that anything will use this, |
| // since it is really awkward/impossible to express in GLSL. |
| ComplexLoop |
| }; |
| |
| enum |
| { |
| NoDominator = 0xffffffffu |
| }; |
| |
| Terminator terminator = Unknown; |
| Merge merge = MergeNone; |
| uint32_t next_block = 0; |
| uint32_t merge_block = 0; |
| uint32_t continue_block = 0; |
| |
| uint32_t return_value = 0; // If 0, return nothing (void). |
| uint32_t condition = 0; |
| uint32_t true_block = 0; |
| uint32_t false_block = 0; |
| uint32_t default_block = 0; |
| |
| std::vector<Instruction> ops; |
| |
| struct Phi |
| { |
| uint32_t local_variable; // flush local variable ... |
| uint32_t parent; // If we're in from_block and want to branch into this block ... |
| uint32_t function_variable; // to this function-global "phi" variable first. |
| }; |
| |
| // Before entering this block flush out local variables to magical "phi" variables. |
| std::vector<Phi> phi_variables; |
| |
| // Declare these temporaries before beginning the block. |
| // Used for handling complex continue blocks which have side effects. |
| std::vector<std::pair<uint32_t, uint32_t>> declare_temporary; |
| |
| struct Case |
| { |
| uint32_t value; |
| uint32_t block; |
| }; |
| std::vector<Case> cases; |
| |
| // If we have tried to optimize code for this block but failed, |
| // keep track of this. |
| bool disable_block_optimization = false; |
| |
| // If the continue block is complex, fallback to "dumb" for loops. |
| bool complex_continue = false; |
| |
| // The dominating block which this block might be within. |
| // Used in continue; blocks to determine if we really need to write continue. |
| uint32_t loop_dominator = 0; |
| }; |
| |
| struct SPIRFunction : IVariant |
| { |
| enum |
| { |
| type = TypeFunction |
| }; |
| |
| SPIRFunction(uint32_t return_type_, uint32_t function_type_) |
| : return_type(return_type_) |
| , function_type(function_type_) |
| { |
| } |
| |
| struct Parameter |
| { |
| uint32_t type; |
| uint32_t id; |
| uint32_t read_count; |
| uint32_t write_count; |
| }; |
| |
| uint32_t return_type; |
| uint32_t function_type; |
| std::vector<Parameter> arguments; |
| std::vector<uint32_t> local_variables; |
| uint32_t entry_block = 0; |
| std::vector<uint32_t> blocks; |
| |
| void add_local_variable(uint32_t id) |
| { |
| local_variables.push_back(id); |
| } |
| |
| void add_parameter(uint32_t parameter_type, uint32_t id) |
| { |
| // Arguments are read-only until proven otherwise. |
| arguments.push_back({ parameter_type, id, 0u, 0u }); |
| } |
| |
| bool active = false; |
| bool flush_undeclared = true; |
| }; |
| |
| struct SPIRVariable : IVariant |
| { |
| enum |
| { |
| type = TypeVariable |
| }; |
| |
| SPIRVariable() = default; |
| SPIRVariable(uint32_t basetype_, spv::StorageClass storage_, uint32_t initializer_ = 0) |
| : basetype(basetype_) |
| , storage(storage_) |
| , initializer(initializer_) |
| { |
| } |
| |
| uint32_t basetype = 0; |
| spv::StorageClass storage = spv::StorageClassGeneric; |
| uint32_t decoration = 0; |
| uint32_t initializer = 0; |
| |
| std::vector<uint32_t> dereference_chain; |
| bool compat_builtin = false; |
| |
| // If a variable is shadowed, we only statically assign to it |
| // and never actually emit a statement for it. |
| // When we read the variable as an expression, just forward |
| // shadowed_id as the expression. |
| bool statically_assigned = false; |
| uint32_t static_expression = 0; |
| |
| // Temporaries which can remain forwarded as long as this variable is not modified. |
| std::vector<uint32_t> dependees; |
| bool forwardable = true; |
| |
| bool deferred_declaration = false; |
| bool phi_variable = false; |
| bool remapped_variable = false; |
| uint32_t remapped_components = 0; |
| |
| SPIRFunction::Parameter *parameter = nullptr; |
| }; |
| |
| struct SPIRConstant : IVariant |
| { |
| enum |
| { |
| type = TypeConstant |
| }; |
| |
| union Constant { |
| uint32_t u32; |
| int32_t i32; |
| float f32; |
| |
| uint64_t u64; |
| int64_t i64; |
| double f64; |
| }; |
| |
| struct ConstantVector |
| { |
| Constant r[4]; |
| uint32_t vecsize; |
| }; |
| |
| struct ConstantMatrix |
| { |
| ConstantVector c[4]; |
| uint32_t columns; |
| }; |
| |
| inline uint32_t scalar(uint32_t col = 0, uint32_t row = 0) const |
| { |
| return m.c[col].r[row].u32; |
| } |
| |
| inline float scalar_f32(uint32_t col = 0, uint32_t row = 0) const |
| { |
| return m.c[col].r[row].f32; |
| } |
| |
| inline int32_t scalar_i32(uint32_t col = 0, uint32_t row = 0) const |
| { |
| return m.c[col].r[row].i32; |
| } |
| |
| inline double scalar_f64(uint32_t col = 0, uint32_t row = 0) const |
| { |
| return m.c[col].r[row].f64; |
| } |
| |
| inline int64_t scalar_i64(uint32_t col = 0, uint32_t row = 0) const |
| { |
| return m.c[col].r[row].i64; |
| } |
| |
| inline uint64_t scalar_u64(uint32_t col = 0, uint32_t row = 0) const |
| { |
| return m.c[col].r[row].u64; |
| } |
| |
| inline const ConstantVector &vector() const |
| { |
| return m.c[0]; |
| } |
| inline uint32_t vector_size() const |
| { |
| return m.c[0].vecsize; |
| } |
| inline uint32_t columns() const |
| { |
| return m.columns; |
| } |
| |
| SPIRConstant(uint32_t constant_type_, const uint32_t *elements, uint32_t num_elements) |
| : constant_type(constant_type_) |
| { |
| subconstants.insert(end(subconstants), elements, elements + num_elements); |
| } |
| |
| SPIRConstant(uint32_t constant_type_, uint32_t v0) |
| : constant_type(constant_type_) |
| { |
| m.c[0].r[0].u32 = v0; |
| m.c[0].vecsize = 1; |
| m.columns = 1; |
| } |
| |
| SPIRConstant(uint32_t constant_type_, uint32_t v0, uint32_t v1) |
| : constant_type(constant_type_) |
| { |
| m.c[0].r[0].u32 = v0; |
| m.c[0].r[1].u32 = v1; |
| m.c[0].vecsize = 2; |
| m.columns = 1; |
| } |
| |
| SPIRConstant(uint32_t constant_type_, uint32_t v0, uint32_t v1, uint32_t v2) |
| : constant_type(constant_type_) |
| { |
| m.c[0].r[0].u32 = v0; |
| m.c[0].r[1].u32 = v1; |
| m.c[0].r[2].u32 = v2; |
| m.c[0].vecsize = 3; |
| m.columns = 1; |
| } |
| |
| SPIRConstant(uint32_t constant_type_, uint32_t v0, uint32_t v1, uint32_t v2, uint32_t v3) |
| : constant_type(constant_type_) |
| { |
| m.c[0].r[0].u32 = v0; |
| m.c[0].r[1].u32 = v1; |
| m.c[0].r[2].u32 = v2; |
| m.c[0].r[3].u32 = v3; |
| m.c[0].vecsize = 4; |
| m.columns = 1; |
| } |
| |
| SPIRConstant(uint32_t constant_type_, uint64_t v0) |
| : constant_type(constant_type_) |
| { |
| m.c[0].r[0].u64 = v0; |
| m.c[0].vecsize = 1; |
| m.columns = 1; |
| } |
| |
| SPIRConstant(uint32_t constant_type_, uint64_t v0, uint64_t v1) |
| : constant_type(constant_type_) |
| { |
| m.c[0].r[0].u64 = v0; |
| m.c[0].r[1].u64 = v1; |
| m.c[0].vecsize = 2; |
| m.columns = 1; |
| } |
| |
| SPIRConstant(uint32_t constant_type_, uint64_t v0, uint64_t v1, uint64_t v2) |
| : constant_type(constant_type_) |
| { |
| m.c[0].r[0].u64 = v0; |
| m.c[0].r[1].u64 = v1; |
| m.c[0].r[2].u64 = v2; |
| m.c[0].vecsize = 3; |
| m.columns = 1; |
| } |
| |
| SPIRConstant(uint32_t constant_type_, uint64_t v0, uint64_t v1, uint64_t v2, uint64_t v3) |
| : constant_type(constant_type_) |
| { |
| m.c[0].r[0].u64 = v0; |
| m.c[0].r[1].u64 = v1; |
| m.c[0].r[2].u64 = v2; |
| m.c[0].r[3].u64 = v3; |
| m.c[0].vecsize = 4; |
| m.columns = 1; |
| } |
| |
| SPIRConstant(uint32_t constant_type_, const ConstantVector &vec0) |
| : constant_type(constant_type_) |
| { |
| m.columns = 1; |
| m.c[0] = vec0; |
| } |
| |
| SPIRConstant(uint32_t constant_type_, const ConstantVector &vec0, const ConstantVector &vec1) |
| : constant_type(constant_type_) |
| { |
| m.columns = 2; |
| m.c[0] = vec0; |
| m.c[1] = vec1; |
| } |
| |
| SPIRConstant(uint32_t constant_type_, const ConstantVector &vec0, const ConstantVector &vec1, |
| const ConstantVector &vec2) |
| : constant_type(constant_type_) |
| { |
| m.columns = 3; |
| m.c[0] = vec0; |
| m.c[1] = vec1; |
| m.c[2] = vec2; |
| } |
| |
| SPIRConstant(uint32_t constant_type_, const ConstantVector &vec0, const ConstantVector &vec1, |
| const ConstantVector &vec2, const ConstantVector &vec3) |
| : constant_type(constant_type_) |
| { |
| m.columns = 4; |
| m.c[0] = vec0; |
| m.c[1] = vec1; |
| m.c[2] = vec2; |
| m.c[3] = vec3; |
| } |
| |
| uint32_t constant_type; |
| ConstantMatrix m; |
| bool specialization = false; // If the constant is a specialization constant. |
| |
| // For composites which are constant arrays, etc. |
| std::vector<uint32_t> subconstants; |
| }; |
| |
| class Variant |
| { |
| public: |
| // MSVC 2013 workaround, we shouldn't need these constructors. |
| Variant() = default; |
| Variant(Variant &&other) |
| { |
| *this = std::move(other); |
| } |
| Variant &operator=(Variant &&other) |
| { |
| if (this != &other) |
| { |
| holder = move(other.holder); |
| type = other.type; |
| other.type = TypeNone; |
| } |
| return *this; |
| } |
| |
| void set(std::unique_ptr<IVariant> val, uint32_t new_type) |
| { |
| holder = std::move(val); |
| if (type != TypeNone && type != new_type) |
| throw CompilerError("Overwriting a variant with new type."); |
| type = new_type; |
| } |
| |
| template <typename T> |
| T &get() |
| { |
| if (!holder) |
| throw CompilerError("nullptr"); |
| if (T::type != type) |
| throw CompilerError("Bad cast"); |
| return *static_cast<T *>(holder.get()); |
| } |
| |
| template <typename T> |
| const T &get() const |
| { |
| if (!holder) |
| throw CompilerError("nullptr"); |
| if (T::type != type) |
| throw CompilerError("Bad cast"); |
| return *static_cast<const T *>(holder.get()); |
| } |
| |
| uint32_t get_type() const |
| { |
| return type; |
| } |
| bool empty() const |
| { |
| return !holder; |
| } |
| void reset() |
| { |
| holder.reset(); |
| type = TypeNone; |
| } |
| |
| private: |
| std::unique_ptr<IVariant> holder; |
| uint32_t type = TypeNone; |
| }; |
| |
| template <typename T> |
| T &variant_get(Variant &var) |
| { |
| return var.get<T>(); |
| } |
| |
| template <typename T> |
| const T &variant_get(const Variant &var) |
| { |
| return var.get<T>(); |
| } |
| |
| template <typename T, typename... P> |
| T &variant_set(Variant &var, P &&... args) |
| { |
| auto uptr = std::unique_ptr<T>(new T(std::forward<P>(args)...)); |
| auto ptr = uptr.get(); |
| var.set(std::move(uptr), T::type); |
| return *ptr; |
| } |
| |
| struct Meta |
| { |
| struct Decoration |
| { |
| std::string alias; |
| uint64_t decoration_flags = 0; |
| spv::BuiltIn builtin_type; |
| uint32_t location = 0; |
| uint32_t set = 0; |
| uint32_t binding = 0; |
| uint32_t offset = 0; |
| uint32_t array_stride = 0; |
| uint32_t input_attachment = 0; |
| bool builtin = false; |
| bool per_instance = false; |
| }; |
| |
| Decoration decoration; |
| std::vector<Decoration> members; |
| uint32_t sampler = 0; |
| }; |
| } |
| |
| #endif |