diff options
Diffstat (limited to 'spir2common.hpp')
-rw-r--r-- | spir2common.hpp | 635 |
1 files changed, 635 insertions, 0 deletions
diff --git a/spir2common.hpp b/spir2common.hpp new file mode 100644 index 00000000..d2df785c --- /dev/null +++ b/spir2common.hpp @@ -0,0 +1,635 @@ +/* + * 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 SPIR2COMMON_HPP +#define SPIR2COMMON_HPP + +#include <sstream> +#include <stdio.h> + +namespace spir2cross +{ + 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)); + } + + 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, "%.32g", t); + 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, "%.32g", t); + 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, + Bool, + Int, + UInt, + AtomicCounter, + Float, + 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; + + struct Image + { + uint32_t type; + spv::Dim dim; + bool depth; + bool arrayed; + bool ms; + uint32_t sampled; + spv::ImageFormat format; + } image; + }; + + struct SPIRExtension : IVariant + { + enum { type = TypeExtension }; + + enum Extension + { + GLSL + }; + + SPIRExtension(Extension ext) + : ext(ext) {} + + Extension ext; + }; + + 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. + bool immutable = false; + + // If this expression has been used while invalidated. + bool used_while_invalidated = false; + + // A list of a variables for which this expression was invalidated by. + std::vector<uint32_t> invalidated_by; + }; + + 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 type, uint32_t id) + { + // Arguments are read-only until proven otherwise. + arguments.push_back({ 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; + + SPIRFunction::Parameter *parameter = nullptr; + }; + + struct SPIRConstant : IVariant + { + enum { type = TypeConstant }; + + union Constant + { + uint32_t u32; + int32_t i32; + float f32; + }; + + 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 int scalar_i32(uint32_t col = 0, uint32_t row = 0) const + { + return m.c[col].r[row].i32; + } + + 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, + 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 type) + { + holder = std::move(val); + if (this->type != TypeNone && this->type != type) + throw CompilerError("Overwriting a variant with new type."); + this->type = 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; + bool builtin = false; + }; + + Decoration decoration; + std::vector<Decoration> members; + }; +} + +#endif + |