From: Mikko Rasa Date: Fri, 9 Apr 2021 14:31:47 +0000 (+0300) Subject: Add a SPIR-V backend to the GLSL compiler X-Git-Url: http://git.tdb.fi/?p=libs%2Fgl.git;a=commitdiff_plain;h=cc5483cc709fdf7b6966a3e69dabfcafebaaffa0 Add a SPIR-V backend to the GLSL compiler --- diff --git a/source/glsl/compiler.cpp b/source/glsl/compiler.cpp index b2c4f9ba..74eec856 100644 --- a/source/glsl/compiler.cpp +++ b/source/glsl/compiler.cpp @@ -12,6 +12,7 @@ #include "output.h" #include "resolve.h" #include "resources.h" +#include "spirv.h" #include "validate.h" #undef interface @@ -156,6 +157,15 @@ string Compiler::get_stage_glsl(Stage::Type stage_type) const throw key_error(Stage::get_stage_name(stage_type)); } +vector Compiler::get_combined_spirv() const +{ + if(!compiled) + throw invalid_operation("Compiler::get_combined_spirv"); + SpirVGenerator gen; + gen.apply(*module); + return gen.get_code(); +} + const map &Compiler::get_vertex_attributes() const { if(!compiled) @@ -392,6 +402,8 @@ void Compiler::finalize(Stage &stage, Mode mode) resolve(stage, RESOLVE_VARIABLES|RESOLVE_FUNCTIONS); PrecisionConverter().apply(stage); } + else if(mode==SPIRV) + StructOrganizer().apply(stage); // Collect bindings from all stages into the shared stage's maps module->shared.texture_bindings.insert(stage.texture_bindings.begin(), stage.texture_bindings.end()); diff --git a/source/glsl/compiler.h b/source/glsl/compiler.h index a1ab51f2..51b4f00a 100644 --- a/source/glsl/compiler.h +++ b/source/glsl/compiler.h @@ -17,7 +17,8 @@ public: enum Mode { MODULE, - PROGRAM + PROGRAM, + SPIRV }; private: @@ -88,6 +89,10 @@ public: GLSL suitable for OpenGL or an external GLSL compiler. */ std::string get_stage_glsl(Stage::Type) const; + /** Returns a combined SPIR-V binary for all shader stages. The result is + suitable for use with OpenGL or Vulkan. */ + std::vector get_combined_spirv() const; + /** Returns a map of vertex attribute locations. If the target GLSL version supports interface layouts, the map is empty (locations are included in the GLSL soucre). */ diff --git a/source/glsl/glsl_error.h b/source/glsl/glsl_error.h index 6654770c..0b4b4b02 100644 --- a/source/glsl/glsl_error.h +++ b/source/glsl/glsl_error.h @@ -38,6 +38,13 @@ public: virtual ~parse_error() throw() { } }; +class internal_error: public std::logic_error +{ +public: + internal_error(const std::string &w): logic_error(w) { } + virtual ~internal_error() throw() { } +}; + struct Diagnostic { enum Severity diff --git a/source/glsl/reflect.h b/source/glsl/reflect.h index c2d7aa04..8de91572 100644 --- a/source/glsl/reflect.h +++ b/source/glsl/reflect.h @@ -53,7 +53,7 @@ private: virtual void visit(VariableDeclaration &); }; -/** Determines the size and alignment of a variable, in bytes. */ +/** Determines the size and alignment of a variable or a type, in bytes. */ class MemoryRequirementsCalculator: private NodeVisitor { public: @@ -61,8 +61,9 @@ public: { unsigned size; unsigned alignment; + unsigned stride; - Result(unsigned s, unsigned a): size(s), alignment(a) { } + Result(unsigned s, unsigned a): size(s), alignment(a), stride(s+a-1-(s+a-1)%a) { } }; private: unsigned r_size; @@ -71,6 +72,7 @@ private: public: Result apply(VariableDeclaration &v) { v.visit(*this); return Result(r_size, r_alignment); } + Result apply(TypeDeclaration &t) { t.visit(*this); return Result(r_size, r_alignment); } private: virtual void visit(BasicTypeDeclaration &); diff --git a/source/glsl/spirv.cpp b/source/glsl/spirv.cpp new file mode 100644 index 00000000..799e31d6 --- /dev/null +++ b/source/glsl/spirv.cpp @@ -0,0 +1,1832 @@ +#include +#include +#include "reflect.h" +#include "spirv.h" + +using namespace std; + +namespace Msp { +namespace GL { +namespace SL { + +const SpirVGenerator::BuiltinFunctionInfo SpirVGenerator::builtin_functions[] = +{ + { "radians", "f", "GLSL.std.450", GLSL450_RADIANS, { 1 }, 0 }, + { "degrees", "f", "GLSL.std.450", GLSL450_DEGREES, { 1 }, 0 }, + { "sin", "f", "GLSL.std.450", GLSL450_SIN, { 1 }, 0 }, + { "cos", "f", "GLSL.std.450", GLSL450_COS, { 1 }, 0 }, + { "tan", "f", "GLSL.std.450", GLSL450_TAN, { 1 }, 0 }, + { "asin", "f", "GLSL.std.450", GLSL450_ASIN, { 1 }, 0 }, + { "acos", "f", "GLSL.std.450", GLSL450_ACOS, { 1 }, 0 }, + { "atan", "f", "GLSL.std.450", GLSL450_ATAN, { 1 }, 0 }, + { "atan", "ff", "GLSL.std.450", GLSL450_ATAN2, { 1, 2 }, 0 }, + { "sinh", "f", "GLSL.std.450", GLSL450_SINH, { 1 }, 0 }, + { "cosh", "f", "GLSL.std.450", GLSL450_COSH, { 1 }, 0 }, + { "tanh", "f", "GLSL.std.450", GLSL450_TANH, { 1 }, 0 }, + { "asinh", "f", "GLSL.std.450", GLSL450_ASINH, { 1 }, 0 }, + { "acosh", "f", "GLSL.std.450", GLSL450_ACOSH, { 1 }, 0 }, + { "atanh", "f", "GLSL.std.450", GLSL450_ATANH, { 1 }, 0 }, + { "pow", "ff", "GLSL.std.450", GLSL450_POW, { 1, 2 }, 0 }, + { "exp", "f", "GLSL.std.450", GLSL450_EXP, { 1 }, 0 }, + { "log", "f", "GLSL.std.450", GLSL450_LOG, { 1 }, 0 }, + { "exp2", "f", "GLSL.std.450", GLSL450_EXP2, { 1 }, 0 }, + { "log2", "f", "GLSL.std.450", GLSL450_LOG2, { 1 }, 0 }, + { "sqrt", "f", "GLSL.std.450", GLSL450_SQRT, { 1 }, 0 }, + { "inversesqrt", "f", "GLSL.std.450", GLSL450_INVERSE_SQRT, { 1 }, 0 }, + { "abs", "f", "GLSL.std.450", GLSL450_F_ABS, { 1 }, 0 }, + { "abs", "i", "GLSL.std.450", GLSL450_S_ABS, { 1 }, 0 }, + { "sign", "f", "GLSL.std.450", GLSL450_F_SIGN, { 1 }, 0 }, + { "sign", "i", "GLSL.std.450", GLSL450_S_SIGN, { 1 }, 0 }, + { "floor", "f", "GLSL.std.450", GLSL450_FLOOR, { 1 }, 0 }, + { "trunc", "f", "GLSL.std.450", GLSL450_TRUNC, { 1 }, 0 }, + { "round", "f", "GLSL.std.450", GLSL450_ROUND, { 1 }, 0 }, + { "roundEven", "f", "GLSL.std.450", GLSL450_ROUND_EVEN, { 1 }, 0 }, + { "ceil", "f", "GLSL.std.450", GLSL450_CEIL, { 1 }, 0 }, + { "fract", "f", "GLSL.std.450", GLSL450_FRACT, { 1 }, 0 }, + { "mod", "f", "", OP_F_MOD, { 1, 2 }, 0 }, + { "min", "ff", "GLSL.std.450", GLSL450_F_MIN, { 1, 2 }, 0 }, + { "min", "ii", "GLSL.std.450", GLSL450_S_MIN, { 1, 2 }, 0 }, + { "max", "ff", "GLSL.std.450", GLSL450_F_MAX, { 1, 2 }, 0 }, + { "max", "ii", "GLSL.std.450", GLSL450_S_MAX, { 1, 2 }, 0 }, + { "clamp", "fff", "GLSL.std.450", GLSL450_F_CLAMP, { 1, 2, 3 }, 0 }, + { "clamp", "iii", "GLSL.std.450", GLSL450_S_CLAMP, { 1, 2, 3 }, 0 }, + { "mix", "fff", "GLSL.std.450", GLSL450_F_MIX, { 1, 2, 3 }, 0 }, + { "mix", "ffb", "", OP_SELECT, { 3, 2, 1 }, 0 }, + { "mix", "iib", "", OP_SELECT, { 3, 2, 1 }, 0 }, + { "step", "ff", "GLSL.std.450", GLSL450_F_STEP, { 1, 2 }, 0 }, + { "smoothstep", "fff", "GLSL.std.450", GLSL450_F_SMOOTH_STEP, { 1, 2, 3 }, 0 }, + { "isnan", "f", "", OP_IS_NAN, { 1 }, 0 }, + { "isinf", "f", "", OP_IS_INF, { 1 }, 0 }, + { "fma", "fff", "GLSL.std.450", GLSL450_F_FMA, { 1, 2, 3 }, 0 }, + { "length", "f", "GLSL.std.450", GLSL450_LENGTH, { 1 }, 0 }, + { "distance", "ff", "GLSL.std.450", GLSL450_DISTANCE, { 1, 2 }, 0 }, + { "dot", "ff", "", OP_DOT, { 1, 2 }, 0 }, + { "cross", "ff", "GLSL.std.450", GLSL450_CROSS, { 1, 2 }, 0 }, + { "normalize", "f", "GLSL.std.450", GLSL450_NORMALIZE, { 1 }, 0 }, + { "faceforward", "fff", "GLSL.std.450", GLSL450_FACE_FORWARD, { 1, 2, 3 }, 0 }, + { "reflect", "ff", "GLSL.std.450", GLSL450_REFLECT, { 1, 2 }, 0 }, + { "refract", "fff", "GLSL.std.450", GLSL450_REFRACT, { 1, 2, 3 }, 0 }, + { "matrixCompMult", "ff", "", 0, { 0 }, &SpirVGenerator::visit_builtin_matrix_comp_mult }, + { "outerProduct", "ff", "", OP_OUTER_PRODUCT, { 1, 2 }, 0 }, + { "transpose", "f", "", OP_TRANSPOSE, { 1 }, 0 }, + { "determinant", "f", "GLSL.std.450", GLSL450_DETERMINANT, { 1 }, 0 }, + { "inverse", "f", "GLSL.std.450", GLSL450_MATRIX_INVERSE, { 1 }, 0 }, + { "lessThan", "ff", "", OP_F_ORD_LESS_THAN, { 1, 2 }, 0 }, + { "lessThan", "ii", "", OP_S_LESS_THAN, { 1, 2 }, 0 }, + { "lessThanEqual", "ff", "", OP_F_ORD_LESS_THAN_EQUAL, { 1, 2 }, 0 }, + { "lessThanEqual", "ii", "", OP_S_LESS_THAN_EQUAL, { 1, 2 }, 0 }, + { "greaterThan", "ff", "", OP_F_ORD_GREATER_THAN, { 1, 2 }, 0 }, + { "greaterThan", "ii", "", OP_S_GREATER_THAN, { 1, 2 }, 0 }, + { "greaterThanEqual", "ff", "", OP_F_ORD_GREATER_THAN_EQUAL, { 1, 2 }, 0 }, + { "greaterThanEqual", "ii", "", OP_S_GREATER_THAN_EQUAL, { 1, 2 }, 0 }, + { "equal", "ff", "", OP_F_ORD_EQUAL, { 1, 2 }, 0 }, + { "equal", "ii", "", OP_I_EQUAL, { 1, 2 }, 0 }, + { "notEqual", "ff", "", OP_F_ORD_NOT_EQUAL, { 1, 2 }, 0 }, + { "notEqual", "ii", "", OP_I_NOT_EQUAL, { 1, 2 }, 0 }, + { "any", "b", "", OP_ANY, { 1 }, 0 }, + { "all", "b", "", OP_ALL, { 1 }, 0 }, + { "not", "b", "", OP_LOGICAL_NOT, { 1 }, 0 }, + { "bitfieldExtract", "iii", "", OP_BIT_FIELD_S_EXTRACT, { 1, 2, 3 }, 0 }, + { "bitfieldInsert", "iiii", "", OP_BIT_FIELD_INSERT, { 1, 2, 3, 4 }, 0 }, + { "bitfieldReverse", "i", "", OP_BIT_REVERSE, { 1 }, 0 }, + { "bitCount", "i", "", OP_BIT_COUNT, { 1 }, 0 }, + { "findLSB", "i", "GLSL.std.450", GLSL450_FIND_I_LSB, { 1 }, 0 }, + { "findMSB", "i", "GLSL.std.450", GLSL450_FIND_S_MSB, { 1 }, 0 }, + { "textureSize", "", "", OP_IMAGE_QUERY_SIZE_LOD, { 1, 2 }, 0 }, + { "texture", "", "", 0, { }, &SpirVGenerator::visit_builtin_texture }, + { "textureLod", "", "", 0, { }, &SpirVGenerator::visit_builtin_texture }, + { "texelFetch", "", "", 0, { }, &SpirVGenerator::visit_builtin_texel_fetch }, + { "EmitVertex", "", "", OP_EMIT_VERTEX, { }, 0 }, + { "EndPrimitive", "", "", OP_END_PRIMITIVE, { }, 0 }, + { "dFdx", "f", "", OP_DP_DX, { 1 }, 0 }, + { "dFdy", "f", "", OP_DP_DY, { 1 }, 0 }, + { "dFdxFine", "f", "", OP_DP_DX_FINE, { 1 }, 0 }, + { "dFdyFine", "f", "", OP_DP_DY_FINE, { 1 }, 0 }, + { "dFdxCoarse", "f", "", OP_DP_DX_COARSE, { 1 }, 0 }, + { "dFdyCoarse", "f", "", OP_DP_DY_COARSE, { 1 }, 0 }, + { "fwidth", "f", "", OP_FWIDTH, { 1 }, 0 }, + { "fwidthFine", "f", "", OP_FWIDTH_FINE, { 1 }, 0 }, + { "fwidthCoarse", "f", "", OP_FWIDTH_COARSE, { 1 }, 0 }, + { "interpolateAtCentroid", "", "", 0, { }, &SpirVGenerator::visit_builtin_interpolate }, + { "interpolateAtSample", "", "", 0, { }, &SpirVGenerator::visit_builtin_interpolate }, + { "interpolateAtOffset", "", "", 0, { }, &SpirVGenerator::visit_builtin_interpolate }, + { "", "", "", 0, { }, 0 } +}; + +SpirVGenerator::SpirVGenerator(): + stage(0), + current_function(0), + writer(content), + next_id(1), + r_expression_result_id(0), + constant_expression(false), + spec_constant(false), + reachable(false), + composite_access(false), + r_composite_base_id(0), + r_composite_base(0), + assignment_source_id(0), + loop_merge_block_id(0), + loop_continue_target_id(0) +{ } + +void SpirVGenerator::apply(Module &module) +{ + use_capability(CAP_SHADER); + + for(list::iterator i=module.stages.begin(); i!=module.stages.end(); ++i) + { + stage = &*i; + interface_layouts.clear(); + i->content.visit(*this); + } + + writer.finalize(next_id); +} + +SpirVGenerator::StorageClass SpirVGenerator::get_interface_storage(const string &iface, bool block) +{ + if(iface=="in") + return STORAGE_INPUT; + else if(iface=="out") + return STORAGE_OUTPUT; + else if(iface=="uniform") + return (block ? STORAGE_UNIFORM : STORAGE_UNIFORM_CONSTANT); + else if(iface.empty()) + return STORAGE_PRIVATE; + else + throw invalid_argument("SpirVGenerator::get_interface_storage"); +} + +SpirVGenerator::BuiltinSemantic SpirVGenerator::get_builtin_semantic(const string &name) +{ + if(name=="gl_Position") + return BUILTIN_POSITION; + else if(name=="gl_PointSize") + return BUILTIN_POINT_SIZE; + else if(name=="gl_ClipDistance") + return BUILTIN_CLIP_DISTANCE; + else if(name=="gl_VertexID") + return BUILTIN_VERTEX_ID; + else if(name=="gl_InstanceID") + return BUILTIN_INSTANCE_ID; + else if(name=="gl_PrimitiveID" || name=="gl_PrimitiveIDIn") + return BUILTIN_PRIMITIVE_ID; + else if(name=="gl_InvocationID") + return BUILTIN_INVOCATION_ID; + else if(name=="gl_Layer") + return BUILTIN_LAYER; + else if(name=="gl_FragCoord") + return BUILTIN_FRAG_COORD; + else if(name=="gl_PointCoord") + return BUILTIN_POINT_COORD; + else if(name=="gl_FrontFacing") + return BUILTIN_FRONT_FACING; + else if(name=="gl_SampleId") + return BUILTIN_SAMPLE_ID; + else if(name=="gl_SamplePosition") + return BUILTIN_SAMPLE_POSITION; + else if(name=="gl_FragDepth") + return BUILTIN_FRAG_DEPTH; + else + throw invalid_argument("SpirVGenerator::get_builtin_semantic"); +} + +void SpirVGenerator::use_capability(Capability cap) +{ + if(used_capabilities.count(cap)) + return; + + used_capabilities.insert(cap); + writer.write_op(content.capabilities, OP_CAPABILITY, cap); +} + +SpirVGenerator::Id SpirVGenerator::import_extension(const string &name) +{ + Id &ext_id = imported_extension_ids[name]; + if(!ext_id) + { + ext_id = next_id++; + writer.begin_op(content.extensions, OP_EXT_INST_IMPORT); + writer.write(ext_id); + writer.write_string(name); + writer.end_op(OP_EXT_INST_IMPORT); + } + return ext_id; +} + +SpirVGenerator::Id SpirVGenerator::get_id(Node &node) const +{ + return get_item(declared_ids, &node).id; +} + +SpirVGenerator::Id SpirVGenerator::allocate_id(Node &node, Id type_id) +{ + Id id = next_id++; + insert_unique(declared_ids, &node, Declaration(id, type_id)); + return id; +} + +SpirVGenerator::Id SpirVGenerator::write_constant(Id type_id, Word value, bool spec) +{ + Id const_id = next_id++; + if(is_scalar_type(type_id, BasicTypeDeclaration::BOOL)) + { + Opcode opcode = (value ? (spec ? OP_SPEC_CONSTANT_TRUE : OP_CONSTANT_TRUE) : + (spec ? OP_SPEC_CONSTANT_FALSE : OP_CONSTANT_FALSE)); + writer.write_op(content.globals, opcode, type_id, const_id); + } + else + { + Opcode opcode = (spec ? OP_SPEC_CONSTANT : OP_CONSTANT); + writer.write_op(content.globals, opcode, type_id, const_id, value); + } + return const_id; +} + +SpirVGenerator::ConstantKey SpirVGenerator::get_constant_key(Id type_id, const Variant &value) +{ + if(value.check_type()) + return ConstantKey(type_id, value.value()); + else if(value.check_type()) + return ConstantKey(type_id, value.value()); + else if(value.check_type()) + return ConstantKey(type_id, value.value()); + else + throw invalid_argument("SpirVGenerator::get_constant_key"); +} + +SpirVGenerator::Id SpirVGenerator::get_constant_id(Id type_id, const Variant &value) +{ + ConstantKey key = get_constant_key(type_id, value); + Id &const_id = constant_ids[key]; + if(!const_id) + const_id = write_constant(type_id, key.int_value, false); + return const_id; +} + +SpirVGenerator::Id SpirVGenerator::get_vector_constant_id(Id type_id, unsigned size, Id scalar_id) +{ + Id &const_id = constant_ids[get_constant_key(type_id, static_cast(scalar_id))]; + if(!const_id) + { + const_id = next_id++; + writer.begin_op(content.globals, OP_CONSTANT_COMPOSITE, 3+size); + writer.write(type_id); + writer.write(const_id); + for(unsigned i=0; i1 ? get_standard_type_id(kind, 1) : 0); + Id &type_id = standard_type_ids[TypeKey(base_id, (size>1 ? size : static_cast(kind)))]; + if(!type_id) + { + type_id = next_id++; + if(size>1) + writer.write_op(content.globals, OP_TYPE_VECTOR, type_id, base_id, size); + else if(kind==BasicTypeDeclaration::VOID) + writer.write_op(content.globals, OP_TYPE_VOID, type_id); + else if(kind==BasicTypeDeclaration::BOOL) + writer.write_op(content.globals, OP_TYPE_BOOL, type_id); + else if(kind==BasicTypeDeclaration::INT) + writer.write_op(content.globals, OP_TYPE_INT, type_id, 32, 1); + else if(kind==BasicTypeDeclaration::FLOAT) + writer.write_op(content.globals, OP_TYPE_FLOAT, type_id, 32); + else + throw invalid_argument("SpirVGenerator::get_standard_type_id"); + } + return type_id; +} + +bool SpirVGenerator::is_scalar_type(Id type_id, BasicTypeDeclaration::Kind kind) const +{ + map::const_iterator i = standard_type_ids.find(TypeKey(0, kind)); + return (i!=standard_type_ids.end() && i->second==type_id); +} + +SpirVGenerator::Id SpirVGenerator::get_array_type_id(TypeDeclaration &base_type, Id size_id) +{ + Id base_type_id = get_id(base_type); + Id &array_type_id = array_type_ids[TypeKey(base_type_id, size_id)]; + if(!array_type_id) + { + array_type_id = next_id++; + if(size_id) + writer.write_op(content.globals, OP_TYPE_ARRAY, array_type_id, base_type_id, size_id); + else + writer.write_op(content.globals, OP_TYPE_RUNTIME_ARRAY, array_type_id, base_type_id); + + unsigned stride = MemoryRequirementsCalculator().apply(base_type).stride; + writer.write_op_decorate(array_type_id, DECO_ARRAY_STRIDE, stride); + } + + return array_type_id; +} + +SpirVGenerator::Id SpirVGenerator::get_pointer_type_id(Id type_id, StorageClass storage) +{ + Id &ptr_type_id = pointer_type_ids[TypeKey(type_id, storage)]; + if(!ptr_type_id) + { + ptr_type_id = next_id++; + writer.write_op(content.globals, OP_TYPE_POINTER, ptr_type_id, storage, type_id); + } + return ptr_type_id; +} + +SpirVGenerator::Id SpirVGenerator::get_variable_type_id(const VariableDeclaration &var) +{ + if(const BasicTypeDeclaration *basic = dynamic_cast(var.type_declaration)) + if(basic->kind==BasicTypeDeclaration::ARRAY) + { + Id size_id = 0; + if(var.array_size) + { + SetFlag set_const(constant_expression); + r_expression_result_id = 0; + var.array_size->visit(*this); + size_id = r_expression_result_id; + } + else + size_id = get_constant_id(get_standard_type_id(BasicTypeDeclaration::INT, 1), 1); + return get_array_type_id(*basic->base_type, size_id); + } + + return get_id(*var.type_declaration); +} + +SpirVGenerator::Id SpirVGenerator::get_load_id(VariableDeclaration &var) +{ + Id &load_result_id = variable_load_ids[&var]; + if(!load_result_id) + { + load_result_id = next_id++; + writer.write_op(content.function_body, OP_LOAD, get_variable_type_id(var), load_result_id, get_id(var)); + } + return load_result_id; +} + +void SpirVGenerator::prune_loads(Id min_id) +{ + for(map::iterator i=variable_load_ids.begin(); i!=variable_load_ids.end(); ) + { + if(i->second>=min_id) + variable_load_ids.erase(i++); + else + ++i; + } +} + +SpirVGenerator::Id SpirVGenerator::begin_expression(Opcode opcode, Id type_id, unsigned n_args) +{ + bool has_result = (opcode==OP_FUNCTION_CALL || !is_scalar_type(type_id, BasicTypeDeclaration::VOID)); + if(!constant_expression) + { + if(!current_function) + throw internal_error("non-constant expression outside a function"); + + writer.begin_op(content.function_body, opcode, (n_args ? 1+has_result*2+n_args : 0)); + } + else if(opcode==OP_COMPOSITE_CONSTRUCT) + writer.begin_op(content.function_body, OP_SPEC_CONSTANT_COMPOSITE, (n_args ? 1+has_result*2+n_args : 0)); + else + writer.begin_op(content.function_body, OP_SPEC_CONSTANT_OP, (n_args ? 2+has_result*2+n_args : 0)); + + Id result_id = next_id++; + if(has_result) + { + writer.write(type_id); + writer.write(result_id); + } + if(constant_expression && opcode!=OP_COMPOSITE_CONSTRUCT) + writer.write(opcode); + + return result_id; +} + +void SpirVGenerator::end_expression(Opcode opcode) +{ + if(constant_expression) + opcode = (opcode==OP_COMPOSITE_CONSTRUCT ? OP_SPEC_CONSTANT_COMPOSITE : OP_SPEC_CONSTANT_OP); + writer.end_op(opcode); +} + +SpirVGenerator::Id SpirVGenerator::write_expression(Opcode opcode, Id type_id, Id arg_id) +{ + Id result_id = begin_expression(opcode, type_id, 1); + writer.write(arg_id); + end_expression(opcode); + return result_id; +} + +SpirVGenerator::Id SpirVGenerator::write_expression(Opcode opcode, Id type_id, Id left_id, Id right_id) +{ + Id result_id = begin_expression(opcode, type_id, 2); + writer.write(left_id); + writer.write(right_id); + end_expression(opcode); + return result_id; +} + +void SpirVGenerator::write_deconstruct(Id elem_type_id, Id composite_id, Id *elem_ids, unsigned n_elems) +{ + for(unsigned i=0; i(*basic.base_type)); + else + throw invalid_argument("SpirVGenerator::get_element_type"); +} + +void SpirVGenerator::visit(Block &block) +{ + for(NodeList::iterator i=block.body.begin(); i!=block.body.end(); ++i) + (*i)->visit(*this); +} + +void SpirVGenerator::visit(Literal &literal) +{ + Id type_id = get_id(*literal.type); + if(spec_constant) + r_expression_result_id = write_constant(type_id, get_constant_key(type_id, literal.value).int_value, true); + else + r_expression_result_id = get_constant_id(type_id, literal.value); +} + +void SpirVGenerator::visit(VariableReference &var) +{ + if(constant_expression || var.declaration->constant) + { + if(!var.declaration->constant) + throw internal_error("reference to non-constant variable in constant context"); + + r_expression_result_id = get_id(*var.declaration); + return; + } + else if(!current_function) + throw internal_error("non-constant context outside a function"); + + if(composite_access) + { + r_composite_base = var.declaration; + r_expression_result_id = 0; + } + else if(assignment_source_id) + { + writer.write_op(content.function_body, OP_STORE, get_id(*var.declaration), assignment_source_id); + variable_load_ids[var.declaration] = assignment_source_id; + r_expression_result_id = assignment_source_id; + } + else + r_expression_result_id = get_load_id(*var.declaration); +} + +void SpirVGenerator::visit(InterfaceBlockReference &iface) +{ + if(!composite_access || !current_function) + throw internal_error("invalid interface block reference"); + + r_composite_base = iface.declaration; + r_expression_result_id = 0; +} + +void SpirVGenerator::generate_composite_access(TypeDeclaration &result_type) +{ + Opcode opcode; + Id result_type_id = get_id(result_type); + Id access_type_id = result_type_id; + if(r_composite_base) + { + if(constant_expression) + throw internal_error("composite access through pointer in constant context"); + + Id int32_type_id = get_standard_type_id(BasicTypeDeclaration::INT, 1); + for(vector::iterator i=r_composite_chain.begin(); i!=r_composite_chain.end(); ++i) + *i = (*i<0x400000 ? get_constant_id(int32_type_id, static_cast(*i)) : *i&0x3FFFFF); + + /* Find the storage class of the base and obtain appropriate pointer type + for the result. */ + const Declaration &base_decl = get_item(declared_ids, r_composite_base); + map::const_iterator i = pointer_type_ids.begin(); + for(; (i!=pointer_type_ids.end() && i->second!=base_decl.type_id); ++i) ; + if(i==pointer_type_ids.end()) + throw internal_error("could not find storage class"); + access_type_id = get_pointer_type_id(result_type_id, static_cast(i->first.detail)); + + opcode = OP_ACCESS_CHAIN; + } + else if(assignment_source_id) + throw internal_error("assignment to temporary composite"); + else + { + for(vector::iterator i=r_composite_chain.begin(); i!=r_composite_chain.end(); ++i) + for(map::iterator j=constant_ids.begin(); (*i>=0x400000 && j!=constant_ids.end()); ++j) + if(j->second==(*i&0x3FFFFF)) + *i = j->first.int_value; + + opcode = OP_COMPOSITE_EXTRACT; + } + + Id access_id = begin_expression(opcode, access_type_id, 1+r_composite_chain.size()); + writer.write(r_composite_base_id); + for(vector::const_iterator i=r_composite_chain.begin(); i!=r_composite_chain.end(); ++i) + writer.write(*i); + end_expression(opcode); + + if(r_composite_base) + { + if(assignment_source_id) + { + writer.write_op(content.function_body, OP_STORE, access_id, assignment_source_id); + r_expression_result_id = assignment_source_id; + } + else + r_expression_result_id = write_expression(OP_LOAD, result_type_id, access_id); + } + else + r_expression_result_id = access_id; +} + +void SpirVGenerator::visit_composite(Expression &base_expr, unsigned index, TypeDeclaration &type) +{ + if(!composite_access) + { + r_composite_base = 0; + r_composite_base_id = 0; + r_composite_chain.clear(); + } + + { + SetFlag set_composite(composite_access); + base_expr.visit(*this); + } + + if(!r_composite_base_id) + r_composite_base_id = (r_composite_base ? get_id(*r_composite_base) : r_expression_result_id); + + r_composite_chain.push_back(index); + if(!composite_access) + generate_composite_access(type); + else + r_expression_result_id = 0; +} + +void SpirVGenerator::visit_isolated(Expression &expr) +{ + SetForScope clear_assign(assignment_source_id, 0); + SetFlag clear_composite(composite_access, false); + SetForScope clear_base(r_composite_base, 0); + SetForScope clear_base_id(r_composite_base_id, 0); + vector saved_chain; + swap(saved_chain, r_composite_chain); + expr.visit(*this); + swap(saved_chain, r_composite_chain); +} + +void SpirVGenerator::visit(MemberAccess &memacc) +{ + visit_composite(*memacc.left, memacc.index, *memacc.type); +} + +void SpirVGenerator::visit(Swizzle &swizzle) +{ + if(swizzle.count==1) + visit_composite(*swizzle.left, swizzle.components[0], *swizzle.type); + else if(assignment_source_id) + { + const BasicTypeDeclaration &basic = dynamic_cast(*swizzle.left->type); + + unsigned mask = 0; + for(unsigned i=0; itype), 2+basic.size); + writer.write(r_expression_result_id); + writer.write(assignment_source_id); + for(unsigned i=0; i>i)&1)*basic.size); + end_expression(OP_VECTOR_SHUFFLE); + + SetForScope set_assign(assignment_source_id, combined_id); + swizzle.left->visit(*this); + + r_expression_result_id = combined_id; + } + else + { + swizzle.left->visit(*this); + Id left_id = r_expression_result_id; + + r_expression_result_id = begin_expression(OP_VECTOR_SHUFFLE, get_id(*swizzle.type), 2+swizzle.count); + writer.write(left_id); + writer.write(left_id); + for(unsigned i=0; ivisit(*this); + + char oper = unary.oper->token[0]; + char oper2 = unary.oper->token[1]; + if(oper=='+' && !oper2) + return; + + BasicTypeDeclaration &basic = dynamic_cast(*unary.expression->type); + BasicTypeDeclaration &elem = get_element_type(basic); + + if(constant_expression && elem.kind!=BasicTypeDeclaration::BOOL && elem.kind!=BasicTypeDeclaration::INT) + /* SPIR-V allows constant operations on floating-point values only for + OpenGL kernels. */ + throw internal_error("invalid operands for constant unary expression"); + + Id result_type_id = get_id(*unary.type); + Opcode opcode = OP_NOP; + + if(oper=='!') + opcode = OP_LOGICAL_NOT; + else if(oper=='~') + opcode = OP_NOT; + else if(oper=='-' && !oper2) + { + opcode = (elem.kind==BasicTypeDeclaration::INT ? OP_S_NEGATE : OP_F_NEGATE); + + if(basic.kind==BasicTypeDeclaration::MATRIX) + { + Id column_type_id = get_id(*basic.base_type); + unsigned n_columns = basic.size&0xFFFF; + Id column_ids[4]; + write_deconstruct(column_type_id, r_expression_result_id, column_ids, n_columns); + for(unsigned i=0; i set_assign(assignment_source_id, post_value_id); + unary.expression->visit(*this); + + r_expression_result_id = (unary.oper->type==Operator::POSTFIX ? r_expression_result_id : post_value_id); + return; + } + + if(opcode==OP_NOP) + throw internal_error("unknown unary operator"); + + r_expression_result_id = write_expression(opcode, result_type_id, r_expression_result_id); +} + +void SpirVGenerator::visit(BinaryExpression &binary) +{ + char oper = binary.oper->token[0]; + if(oper=='[') + { + visit_isolated(*binary.right); + return visit_composite(*binary.left, 0x400000|r_expression_result_id, *binary.type); + } + + if(assignment_source_id) + throw internal_error("invalid binary expression in assignment target"); + + BasicTypeDeclaration &basic_left = dynamic_cast(*binary.left->type); + BasicTypeDeclaration &basic_right = dynamic_cast(*binary.right->type); + // Expression resolver ensures that element types are the same + BasicTypeDeclaration &elem = get_element_type(basic_left); + + if(constant_expression && elem.kind!=BasicTypeDeclaration::BOOL && elem.kind!=BasicTypeDeclaration::INT) + /* SPIR-V allows constant operations on floating-point values only for + OpenGL kernels. */ + throw internal_error("invalid operands for constant binary expression"); + + binary.left->visit(*this); + Id left_id = r_expression_result_id; + binary.right->visit(*this); + Id right_id = r_expression_result_id; + + Id result_type_id = get_id(*binary.type); + Opcode opcode = OP_NOP; + bool swap_operands = false; + + char oper2 = binary.oper->token[1]; + if((oper=='<' || oper=='>') && oper2!=oper) + { + if(basic_left.kind==BasicTypeDeclaration::INT) + opcode = (oper=='<' ? (oper2=='=' ? OP_S_LESS_THAN_EQUAL : OP_S_LESS_THAN) : + (oper2=='=' ? OP_S_GREATER_THAN_EQUAL : OP_S_GREATER_THAN)); + else if(basic_left.kind==BasicTypeDeclaration::FLOAT) + opcode = (oper=='<' ? (oper2=='=' ? OP_F_ORD_LESS_THAN_EQUAL : OP_F_ORD_LESS_THAN) : + (oper2=='=' ? OP_F_ORD_GREATER_THAN_EQUAL : OP_F_ORD_GREATER_THAN)); + } + else if((oper=='=' || oper=='!') && oper2=='=') + { + if(elem.kind==BasicTypeDeclaration::BOOL) + opcode = (oper=='=' ? OP_LOGICAL_EQUAL : OP_LOGICAL_NOT_EQUAL); + else if(elem.kind==BasicTypeDeclaration::INT) + opcode = (oper=='=' ? OP_I_EQUAL : OP_I_NOT_EQUAL); + else if(elem.kind==BasicTypeDeclaration::FLOAT) + opcode = (oper=='=' ? OP_F_ORD_EQUAL : OP_F_ORD_NOT_EQUAL); + + if(opcode!=OP_NOP && basic_left.base_type) + { + /* The SPIR-V equality operations produce component-wise results, but + GLSL operators return a single boolean. Use the any/all operations to + combine the results. */ + Opcode combine_op = (oper=='!' ? OP_ANY : OP_ALL); + unsigned n_elems = basic_left.size&0xFFFF; + Id bool_vec_type_id = get_standard_type_id(BasicTypeDeclaration::BOOL, n_elems); + + Id compare_id = 0; + if(basic_left.kind==BasicTypeDeclaration::VECTOR) + compare_id = write_expression(opcode, bool_vec_type_id, left_id, right_id); + else if(basic_left.kind==BasicTypeDeclaration::MATRIX) + { + Id column_type_id = get_id(*basic_left.base_type); + Id column_ids[8]; + write_deconstruct(column_type_id, left_id, column_ids, n_elems); + write_deconstruct(column_type_id, right_id, column_ids+4, n_elems); + + Id column_bvec_type_id = get_standard_type_id(BasicTypeDeclaration::BOOL, basic_left.size>>16); + for(unsigned i=0; i' && oper2==oper && elem.kind==BasicTypeDeclaration::INT) + opcode = OP_SHIFT_RIGHT_ARITHMETIC; + else if(oper=='%' && elem.kind==BasicTypeDeclaration::INT) + opcode = OP_S_MOD; + else if(oper=='+' || oper=='-' || oper=='*' || oper=='/') + { + Opcode elem_op = OP_NOP; + if(elem.kind==BasicTypeDeclaration::INT) + elem_op = (oper=='+' ? OP_I_ADD : oper=='-' ? OP_I_SUB : oper=='*' ? OP_I_MUL : OP_S_DIV); + else if(elem.kind==BasicTypeDeclaration::FLOAT) + elem_op = (oper=='+' ? OP_F_ADD : oper=='-' ? OP_F_SUB : oper=='*' ? OP_F_MUL : OP_F_DIV); + + if(oper=='*' && (basic_left.base_type || basic_right.base_type) && elem.kind==BasicTypeDeclaration::FLOAT) + { + /* Multiplication between floating-point vectors and matrices has + dedicated operations. */ + if(basic_left.kind==BasicTypeDeclaration::MATRIX && basic_right.kind==BasicTypeDeclaration::MATRIX) + opcode = OP_MATRIX_TIMES_MATRIX; + else if(basic_left.kind==BasicTypeDeclaration::MATRIX || basic_right.kind==BasicTypeDeclaration::MATRIX) + { + if(basic_left.kind==BasicTypeDeclaration::VECTOR) + opcode = OP_VECTOR_TIMES_MATRIX; + else if(basic_right.kind==BasicTypeDeclaration::VECTOR) + opcode = OP_MATRIX_TIMES_VECTOR; + else + { + opcode = OP_MATRIX_TIMES_SCALAR; + swap_operands = (basic_right.kind==BasicTypeDeclaration::MATRIX); + } + } + else if(basic_left.kind==BasicTypeDeclaration::VECTOR && basic_right.kind==BasicTypeDeclaration::VECTOR) + opcode = elem_op; + else + { + opcode = OP_VECTOR_TIMES_SCALAR; + swap_operands = (basic_right.kind==BasicTypeDeclaration::VECTOR); + } + } + else if((basic_left.base_type!=0)!=(basic_right.base_type!=0)) + { + /* One operand is scalar and the other is a vector or a matrix. + Expand the scalar to a vector of appropriate size. */ + Id &scalar_id = (basic_left.base_type ? right_id : left_id); + BasicTypeDeclaration *vector_type = (basic_left.base_type ? &basic_left : &basic_right); + if(vector_type->kind==BasicTypeDeclaration::MATRIX) + vector_type = dynamic_cast(vector_type->base_type); + Id vector_type_id = get_id(*vector_type); + + Id expanded_id = begin_expression(OP_COMPOSITE_CONSTRUCT, vector_type_id, vector_type->size); + for(unsigned i=0; isize; ++i) + writer.write(scalar_id); + end_expression(OP_COMPOSITE_CONSTRUCT); + + scalar_id = expanded_id; + + if(basic_left.kind==BasicTypeDeclaration::MATRIX || basic_right.kind==BasicTypeDeclaration::MATRIX) + { + // Apply matrix operation column-wise. + Id matrix_id = (basic_left.base_type ? left_id : right_id); + + Id column_ids[4]; + unsigned n_columns = (basic_left.base_type ? basic_left.size : basic_right.size)&0xFFFF; + write_deconstruct(vector_type_id, matrix_id, column_ids, n_columns); + + for(unsigned i=0; itoken[0]!='=') + visit(static_cast(assign)); + else + assign.right->visit(*this); + + SetForScope set_assign(assignment_source_id, r_expression_result_id); + assign.left->visit(*this); +} + +void SpirVGenerator::visit(TernaryExpression &ternary) +{ + if(constant_expression) + { + ternary.condition->visit(*this); + Id condition_id = r_expression_result_id; + ternary.true_expr->visit(*this); + Id true_result_id = r_expression_result_id; + ternary.false_expr->visit(*this); + Id false_result_id = r_expression_result_id; + + r_expression_result_id = begin_expression(OP_SELECT, get_id(*ternary.type), 3); + writer.write(condition_id); + writer.write(true_result_id); + writer.write(false_result_id); + end_expression(OP_SELECT); + + return; + } + + ternary.condition->visit(*this); + Id condition_id = r_expression_result_id; + + Id true_label_id = next_id++; + Id false_label_id = next_id++; + Id merge_block_id = next_id++; + writer.write_op(content.function_body, OP_SELECTION_MERGE, merge_block_id, 0); // Selection control (none) + writer.write_op(content.function_body, OP_BRANCH_CONDITIONAL, condition_id, true_label_id, false_label_id); + + writer.write_op_label(true_label_id); + ternary.true_expr->visit(*this); + Id true_result_id = r_expression_result_id; + writer.write_op(content.function_body, OP_BRANCH, merge_block_id); + + writer.write_op_label(false_label_id); + ternary.false_expr->visit(*this); + Id false_result_id = r_expression_result_id; + + writer.write_op_label(merge_block_id); + r_expression_result_id = begin_expression(OP_PHI, get_id(*ternary.type), 4); + writer.write(true_result_id); + writer.write(true_label_id); + writer.write(false_result_id); + writer.write(false_label_id); + end_expression(OP_PHI); +} + +void SpirVGenerator::visit(FunctionCall &call) +{ + if(constant_expression) + throw internal_error("function call in constant expression"); + else if(assignment_source_id) + throw internal_error("assignment to function call"); + else if(composite_access) + return visit_isolated(call); + else if(call.constructor && call.arguments.size()==1 && call.arguments[0]->type==call.type) + return call.arguments[0]->visit(*this); + + vector argument_ids; + argument_ids.reserve(call.arguments.size()); + for(NodeArray::const_iterator i=call.arguments.begin(); i!=call.arguments.end(); ++i) + { + (*i)->visit(*this); + argument_ids.push_back(r_expression_result_id); + } + + Id result_type_id = get_id(*call.type); + + if(call.constructor) + visit_constructor(call, argument_ids); + else if(call.declaration->source==BUILTIN_SOURCE) + { + string arg_types; + for(NodeArray::const_iterator i=call.arguments.begin(); i!=call.arguments.end(); ++i) + if(BasicTypeDeclaration *basic_arg = dynamic_cast((*i)->type)) + { + BasicTypeDeclaration &elem_arg = get_element_type(*basic_arg); + switch(elem_arg.kind) + { + case BasicTypeDeclaration::BOOL: arg_types += 'b'; break; + case BasicTypeDeclaration::INT: arg_types += 'i'; break; + case BasicTypeDeclaration::FLOAT: arg_types += 'f'; break; + default: arg_types += '?'; + } + } + + const BuiltinFunctionInfo *builtin_info; + for(builtin_info=builtin_functions; builtin_info->function[0]; ++builtin_info) + if(builtin_info->function==call.name && (!builtin_info->arg_types[0] || builtin_info->arg_types==arg_types)) + break; + + if(builtin_info->opcode) + { + Opcode opcode; + if(builtin_info->extension[0]) + { + opcode = OP_EXT_INST; + Id ext_id = import_extension(builtin_info->extension); + + r_expression_result_id = begin_expression(opcode, result_type_id); + writer.write(ext_id); + writer.write(builtin_info->opcode); + } + else + { + opcode = static_cast(builtin_info->opcode); + r_expression_result_id = begin_expression(opcode, result_type_id); + } + + for(unsigned i=0; iarg_order[i] || builtin_info->arg_order[i]>argument_ids.size()) + throw internal_error("invalid builtin function info"); + writer.write(argument_ids[builtin_info->arg_order[i]-1]); + } + + end_expression(opcode); + } + else if(builtin_info->handler) + (this->*(builtin_info->handler))(call, argument_ids); + else + throw internal_error("unknown builtin function "+call.name); + } + else + { + r_expression_result_id = begin_expression(OP_FUNCTION_CALL, result_type_id, 1+call.arguments.size()); + writer.write(get_id(*call.declaration->definition)); + for(vector::const_iterator i=argument_ids.begin(); i!=argument_ids.end(); ++i) + writer.write(*i); + end_expression(OP_FUNCTION_CALL); + + // Any global variables the called function uses might have changed value + set dependencies = DependencyCollector().apply(*call.declaration->definition); + for(set::const_iterator i=dependencies.begin(); i!=dependencies.end(); ++i) + if(const VariableDeclaration *var = dynamic_cast(*i)) + variable_load_ids.erase(var); + } +} + +void SpirVGenerator::visit_constructor(FunctionCall &call, const vector &argument_ids) +{ + Id result_type_id = get_id(*call.type); + + BasicTypeDeclaration *basic = dynamic_cast(call.type); + if(!basic) + { + if(dynamic_cast(call.type)) + r_expression_result_id = write_construct(result_type_id, &argument_ids[0], argument_ids.size()); + else + throw internal_error("unconstructable type "+call.name); + return; + } + + BasicTypeDeclaration &elem = get_element_type(*basic); + BasicTypeDeclaration &basic_arg0 = dynamic_cast(*call.arguments[0]->type); + BasicTypeDeclaration &elem_arg0 = get_element_type(basic_arg0); + + if(basic->kind==BasicTypeDeclaration::MATRIX) + { + Id col_type_id = get_id(*basic->base_type); + unsigned n_columns = basic->size&0xFFFF; + unsigned n_rows = basic->size>>16; + + Id column_ids[4]; + if(call.arguments.size()==1) + { + // Construct diagonal matrix from a single scalar. + Id zero_id = get_constant_id(get_id(elem), 0.0f); + for(unsigned i=0; ikind==BasicTypeDeclaration::VECTOR && (call.arguments.size()>1 || basic_arg0.kind!=BasicTypeDeclaration::VECTOR)) + { + /* There's either a single scalar argument or multiple arguments + which make up the vector's components. */ + if(call.arguments.size()==1) + { + r_expression_result_id = begin_expression(OP_COMPOSITE_CONSTRUCT, result_type_id); + for(unsigned i=0; isize; ++i) + writer.write(argument_ids[0]); + end_expression(OP_COMPOSITE_CONSTRUCT); + } + else + r_expression_result_id = write_construct(result_type_id, &argument_ids[0], argument_ids.size()); + } + else if(elem.kind==BasicTypeDeclaration::BOOL) + { + // Conversion to boolean is implemented as comparing against zero. + Id number_type_id = get_id(elem_arg0); + Id zero_id = (elem_arg0.kind==BasicTypeDeclaration::FLOAT ? + get_constant_id(number_type_id, 0.0f) : get_constant_id(number_type_id, 0)); + if(basic_arg0.kind==BasicTypeDeclaration::VECTOR) + zero_id = get_vector_constant_id(get_id(basic_arg0), basic_arg0.size, zero_id); + + Opcode opcode = (elem_arg0.kind==BasicTypeDeclaration::FLOAT ? OP_F_ORD_NOT_EQUAL : OP_I_NOT_EQUAL); + r_expression_result_id = write_expression(opcode, result_type_id, argument_ids[0], zero_id); + } + else if(elem_arg0.kind==BasicTypeDeclaration::BOOL) + { + /* Conversion from boolean is implemented as selecting from zero + or one. */ + Id number_type_id = get_id(elem); + Id zero_id = (elem.kind==BasicTypeDeclaration::FLOAT ? + get_constant_id(number_type_id, 0.0f) : get_constant_id(number_type_id, 0)); + Id one_id = (elem.kind==BasicTypeDeclaration::FLOAT ? + get_constant_id(number_type_id, 1.0f) : get_constant_id(number_type_id, 1)); + if(basic->kind==BasicTypeDeclaration::VECTOR) + { + zero_id = get_vector_constant_id(get_id(*basic), basic->size, zero_id); + one_id = get_vector_constant_id(get_id(*basic), basic->size, one_id); + } + + r_expression_result_id = begin_expression(OP_SELECT, result_type_id, 3); + writer.write(argument_ids[0]); + writer.write(zero_id); + writer.write(one_id); + end_expression(OP_SELECT); + } + else + { + // Scalar or vector conversion between types of equal size. + Opcode opcode; + if(elem.kind==BasicTypeDeclaration::INT && elem_arg0.kind==BasicTypeDeclaration::FLOAT) + opcode = OP_CONVERT_F_TO_S; + else if(elem.kind==BasicTypeDeclaration::FLOAT && elem_arg0.kind==BasicTypeDeclaration::INT) + opcode = OP_CONVERT_S_TO_F; + else + throw internal_error("invalid conversion"); + + r_expression_result_id = write_expression(opcode, result_type_id, argument_ids[0]); + } +} + +void SpirVGenerator::visit_builtin_matrix_comp_mult(FunctionCall &call, const vector &argument_ids) +{ + if(argument_ids.size()!=2) + throw internal_error("invalid matrixCompMult call"); + + const BasicTypeDeclaration &basic_arg0 = dynamic_cast(*call.arguments[0]->type); + Id column_type_id = get_id(*basic_arg0.base_type); + Id column_ids[8]; + + unsigned n_columns = basic_arg0.size&0xFFFF; + write_deconstruct(column_type_id, argument_ids[0], column_ids, n_columns); + write_deconstruct(column_type_id, argument_ids[1], column_ids+4, n_columns); + + for(unsigned i=0; i &argument_ids) +{ + if(argument_ids.size()<2) + throw internal_error("invalid texture sampling call"); + + bool explicit_lod = (stage->type!=Stage::FRAGMENT || call.name=="textureLod"); + Id lod_id = (!explicit_lod ? 0 : call.name=="textureLod" ? argument_ids.back() : + get_constant_id(get_standard_type_id(BasicTypeDeclaration::FLOAT, 1), 0.0f)); + + const ImageTypeDeclaration &image = dynamic_cast(*call.arguments[0]->type); + + Opcode opcode; + Id result_type_id = get_id(*call.type); + Id dref_id = 0; + if(image.shadow) + { + if(argument_ids.size()==2) + { + const BasicTypeDeclaration &basic_arg1 = dynamic_cast(*call.arguments[1]->type); + dref_id = begin_expression(OP_COMPOSITE_EXTRACT, get_id(*basic_arg1.base_type), 2); + writer.write(argument_ids.back()); + writer.write(basic_arg1.size-1); + end_expression(OP_COMPOSITE_EXTRACT); + } + else + dref_id = argument_ids[2]; + + opcode = (explicit_lod ? OP_IMAGE_SAMPLE_DREF_EXPLICIT_LOD : OP_IMAGE_SAMPLE_DREF_IMPLICIT_LOD); + r_expression_result_id = begin_expression(opcode, result_type_id, 3+explicit_lod*2); + } + else + { + opcode = (explicit_lod ? OP_IMAGE_SAMPLE_EXPLICIT_LOD : OP_IMAGE_SAMPLE_IMPLICIT_LOD); + r_expression_result_id = begin_expression(opcode, result_type_id, 2+explicit_lod*2); + } + + for(unsigned i=0; i<2; ++i) + writer.write(argument_ids[i]); + if(dref_id) + writer.write(dref_id); + if(explicit_lod) + { + writer.write(2); // Lod + writer.write(lod_id); + } + + end_expression(opcode); +} + +void SpirVGenerator::visit_builtin_texel_fetch(FunctionCall &call, const vector &argument_ids) +{ + if(argument_ids.size()!=3) + throw internal_error("invalid texelFetch call"); + + r_expression_result_id = begin_expression(OP_IMAGE_FETCH, get_id(*call.type), 4); + for(unsigned i=0; i<2; ++i) + writer.write(argument_ids[i]); + writer.write(2); // Lod + writer.write(argument_ids.back()); + end_expression(OP_IMAGE_FETCH); +} + +void SpirVGenerator::visit_builtin_interpolate(FunctionCall &call, const vector &argument_ids) +{ + if(argument_ids.size()<1) + throw internal_error("invalid interpolate call"); + const VariableReference *var = dynamic_cast(call.arguments[0].get()); + if(!var || !var->declaration || var->declaration->interface!="in") + throw internal_error("invalid interpolate call"); + + SpirVGlslStd450Opcode opcode; + if(call.name=="interpolateAtCentroid") + opcode = GLSL450_INTERPOLATE_AT_CENTROID; + else if(call.name=="interpolateAtSample") + opcode = GLSL450_INTERPOLATE_AT_SAMPLE; + else if(call.name=="interpolateAtOffset") + opcode = GLSL450_INTERPOLATE_AT_OFFSET; + else + throw internal_error("invalid interpolate call"); + + use_capability(CAP_INTERPOLATION_FUNCTION); + + Id ext_id = import_extension("GLSL.std.450"); + r_expression_result_id = begin_expression(OP_EXT_INST, get_id(*call.type)); + writer.write(ext_id); + writer.write(opcode); + writer.write(get_id(*var->declaration)); + for(vector::const_iterator i=argument_ids.begin(); ++i!=argument_ids.end(); ) + writer.write(*i); + end_expression(OP_EXT_INST); +} + +void SpirVGenerator::visit(ExpressionStatement &expr) +{ + expr.expression->visit(*this); +} + +void SpirVGenerator::visit(InterfaceLayout &layout) +{ + interface_layouts.push_back(&layout); +} + +bool SpirVGenerator::check_duplicate_type(TypeDeclaration &type) +{ + for(map::const_iterator i=declared_ids.begin(); i!=declared_ids.end(); ++i) + if(TypeDeclaration *type2 = dynamic_cast(i->first)) + if(TypeComparer().apply(type, *type2)) + { + insert_unique(declared_ids, &type, i->second); + return true; + } + + return false; +} + +bool SpirVGenerator::check_standard_type(BasicTypeDeclaration &basic) +{ + const BasicTypeDeclaration *elem = (basic.kind==BasicTypeDeclaration::VECTOR ? + dynamic_cast(basic.base_type) : &basic); + if(!elem || elem->base_type) + return false; + if((elem->kind==BasicTypeDeclaration::INT || elem->kind==BasicTypeDeclaration::FLOAT) && elem->size!=32) + return false; + + Id standard_id = get_standard_type_id(elem->kind, (basic.kind==BasicTypeDeclaration::VECTOR ? basic.size : 1)); + insert_unique(declared_ids, &basic, Declaration(standard_id, 0)); + writer.write_op_name(standard_id, basic.name); + + return true; +} + +void SpirVGenerator::visit(BasicTypeDeclaration &basic) +{ + if(check_standard_type(basic)) + return; + if(check_duplicate_type(basic)) + return; + // Alias types shouldn't exist at this point and arrays are handled elsewhere + if(basic.kind==BasicTypeDeclaration::ALIAS || basic.kind==BasicTypeDeclaration::ARRAY) + return; + + Id type_id = allocate_id(basic, 0); + writer.write_op_name(type_id, basic.name); + + switch(basic.kind) + { + case BasicTypeDeclaration::INT: + writer.write_op(content.globals, OP_TYPE_INT, type_id, basic.size, 1); + break; + case BasicTypeDeclaration::FLOAT: + writer.write_op(content.globals, OP_TYPE_FLOAT, type_id, basic.size); + break; + case BasicTypeDeclaration::VECTOR: + writer.write_op(content.globals, OP_TYPE_VECTOR, type_id, get_id(*basic.base_type), basic.size); + break; + case BasicTypeDeclaration::MATRIX: + writer.write_op(content.globals, OP_TYPE_MATRIX, type_id, get_id(*basic.base_type), basic.size&0xFFFF); + break; + default: + throw internal_error("unknown basic type"); + } +} + +void SpirVGenerator::visit(ImageTypeDeclaration &image) +{ + if(check_duplicate_type(image)) + return; + + Id type_id = allocate_id(image, 0); + + Id image_id = (image.sampled ? next_id++ : type_id); + writer.begin_op(content.globals, OP_TYPE_IMAGE, 9); + writer.write(image_id); + writer.write(get_id(*image.base_type)); + writer.write(image.dimensions-1); + writer.write(image.shadow); + writer.write(image.array); + writer.write(false); // Multisample + writer.write(image.sampled ? 1 : 2); + writer.write(0); // Format (unknown) + writer.end_op(OP_TYPE_IMAGE); + + if(image.sampled) + { + writer.write_op_name(type_id, image.name); + writer.write_op(content.globals, OP_TYPE_SAMPLED_IMAGE, type_id, image_id); + } + + if(image.dimensions==ImageTypeDeclaration::ONE) + use_capability(image.sampled ? CAP_SAMPLED_1D : CAP_IMAGE_1D); + else if(image.dimensions==ImageTypeDeclaration::CUBE && image.array) + use_capability(image.sampled ? CAP_SAMPLED_CUBE_ARRAY : CAP_IMAGE_CUBE_ARRAY); +} + +void SpirVGenerator::visit(StructDeclaration &strct) +{ + if(check_duplicate_type(strct)) + return; + + Id type_id = allocate_id(strct, 0); + writer.write_op_name(type_id, strct.name); + + if(strct.interface_block) + writer.write_op_decorate(type_id, DECO_BLOCK); + + bool builtin = (strct.interface_block && !strct.interface_block->block_name.compare(0, 3, "gl_")); + vector member_type_ids; + member_type_ids.reserve(strct.members.body.size()); + for(NodeList::const_iterator i=strct.members.body.begin(); i!=strct.members.body.end(); ++i) + { + const VariableDeclaration *var = dynamic_cast(i->get()); + if(!var) + continue; + + unsigned index = member_type_ids.size(); + member_type_ids.push_back(get_variable_type_id(*var)); + + writer.write_op_member_name(type_id, index, var->name); + + if(builtin) + { + BuiltinSemantic semantic = get_builtin_semantic(var->name); + writer.write_op_member_decorate(type_id, index, DECO_BUILTIN, semantic); + } + else + { + if(var->layout) + { + const vector &qualifiers = var->layout->qualifiers; + for(vector::const_iterator j=qualifiers.begin(); j!=qualifiers.end(); ++j) + { + if(j->name=="offset") + writer.write_op_member_decorate(type_id, index, DECO_OFFSET, j->value); + else if(j->name=="column_major") + writer.write_op_member_decorate(type_id, index, DECO_COL_MAJOR); + else if(j->name=="row_major") + writer.write_op_member_decorate(type_id, index, DECO_ROW_MAJOR); + } + } + + const BasicTypeDeclaration *basic = dynamic_cast(var->type_declaration); + while(basic && basic->kind==BasicTypeDeclaration::ARRAY) + basic = dynamic_cast(basic->base_type); + if(basic && basic->kind==BasicTypeDeclaration::MATRIX) + { + unsigned stride = MemoryRequirementsCalculator().apply(*basic->base_type).stride; + writer.write_op_member_decorate(type_id, index, DECO_MATRIX_STRIDE, stride); + } + } + } + + writer.begin_op(content.globals, OP_TYPE_STRUCT); + writer.write(type_id); + for(vector::const_iterator i=member_type_ids.begin(); i!=member_type_ids.end(); ++i) + writer.write(*i); + writer.end_op(OP_TYPE_STRUCT); +} + +void SpirVGenerator::visit(VariableDeclaration &var) +{ + const vector *layout_ql = (var.layout ? &var.layout->qualifiers : 0); + + int spec_id = -1; + if(layout_ql) + { + for(vector::const_iterator i=layout_ql->begin(); (spec_id<0 && i!=layout_ql->end()); ++i) + if(i->name=="constant_id") + spec_id = i->value; + } + + Id type_id = get_variable_type_id(var); + Id var_id; + + if(var.constant) + { + if(!var.init_expression) + throw internal_error("const variable without initializer"); + + SetFlag set_const(constant_expression); + SetFlag set_spec(spec_constant, spec_id>=0); + r_expression_result_id = 0; + var.init_expression->visit(*this); + var_id = r_expression_result_id; + insert_unique(declared_ids, &var, Declaration(var_id, type_id)); + writer.write_op_decorate(var_id, DECO_SPEC_ID, spec_id); + + /* It's unclear what should be done if a specialization constant is + initialized with anything other than a literal. GLSL doesn't seem to + prohibit that but SPIR-V says OpSpecConstantOp can't be updated via + specialization. */ + } + else + { + StorageClass storage = (current_function ? STORAGE_FUNCTION : get_interface_storage(var.interface, false)); + Id ptr_type_id = get_pointer_type_id(type_id, storage); + if(var.interface=="uniform") + { + Id &uni_id = declared_uniform_ids["v"+var.name]; + if(uni_id) + { + insert_unique(declared_ids, &var, Declaration(uni_id, ptr_type_id)); + return; + } + + uni_id = var_id = allocate_id(var, ptr_type_id); + } + else + var_id = allocate_id(var, (var.constant ? type_id : ptr_type_id)); + + Id init_id = 0; + if(var.init_expression) + { + SetFlag set_const(constant_expression, !current_function); + r_expression_result_id = 0; + var.init_expression->visit(*this); + init_id = r_expression_result_id; + } + + vector &target = (current_function ? content.locals : content.globals); + writer.begin_op(target, OP_VARIABLE, 4+(init_id && !current_function)); + writer.write(ptr_type_id); + writer.write(var_id); + writer.write(storage); + if(init_id && !current_function) + writer.write(init_id); + writer.end_op(OP_VARIABLE); + + if(layout_ql) + { + for(vector::const_iterator i=layout_ql->begin(); i!=layout_ql->end(); ++i) + { + if(i->name=="location") + writer.write_op_decorate(var_id, DECO_LOCATION, i->value); + else if(i->name=="set") + writer.write_op_decorate(var_id, DECO_DESCRIPTOR_SET, i->value); + else if(i->name=="binding") + writer.write_op_decorate(var_id, DECO_BINDING, i->value); + } + } + + if(init_id && current_function) + writer.write_op(content.function_body, OP_STORE, var_id, init_id); + } + + writer.write_op_name(var_id, var.name); +} + +void SpirVGenerator::visit(InterfaceBlock &iface) +{ + StorageClass storage = get_interface_storage(iface.interface, true); + Id type_id; + if(iface.array) + type_id = get_array_type_id(*iface.struct_declaration, 0); + else + type_id = get_id(*iface.struct_declaration); + Id ptr_type_id = get_pointer_type_id(type_id, storage); + + Id block_id; + if(iface.interface=="uniform") + { + Id &uni_id = declared_uniform_ids["b"+iface.block_name]; + if(uni_id) + { + insert_unique(declared_ids, &iface, Declaration(uni_id, ptr_type_id)); + return; + } + + uni_id = block_id = allocate_id(iface, ptr_type_id); + } + else + block_id = allocate_id(iface, ptr_type_id); + writer.write_op_name(block_id, iface.instance_name); + + writer.write_op(content.globals, OP_VARIABLE, ptr_type_id, block_id, storage); + + if(iface.layout) + { + const vector &qualifiers = iface.layout->qualifiers; + for(vector::const_iterator i=qualifiers.begin(); i!=qualifiers.end(); ++i) + if(i->name=="binding") + writer.write_op_decorate(block_id, DECO_BINDING, i->value); + } +} + +void SpirVGenerator::visit_entry_point(FunctionDeclaration &func, Id func_id) +{ + writer.begin_op(content.entry_points, OP_ENTRY_POINT); + switch(stage->type) + { + case Stage::VERTEX: writer.write(0); break; + case Stage::GEOMETRY: writer.write(3); break; + case Stage::FRAGMENT: writer.write(4); break; + default: throw internal_error("unknown stage"); + } + writer.write(func_id); + writer.write_string(func.name); + + set dependencies = DependencyCollector().apply(func); + for(set::const_iterator i=dependencies.begin(); i!=dependencies.end(); ++i) + { + if(const VariableDeclaration *var = dynamic_cast(*i)) + { + if(!var->interface.empty()) + writer.write(get_id(**i)); + } + else if(dynamic_cast(*i)) + writer.write(get_id(**i)); + } + + writer.end_op(OP_ENTRY_POINT); + + if(stage->type==Stage::FRAGMENT) + writer.write_op(content.exec_modes, OP_EXECUTION_MODE, func_id, EXEC_ORIGIN_LOWER_LEFT); + else if(stage->type==Stage::GEOMETRY) + use_capability(CAP_GEOMETRY); + + for(vector::const_iterator i=interface_layouts.begin(); i!=interface_layouts.end(); ++i) + { + const vector &qualifiers = (*i)->layout.qualifiers; + for(vector::const_iterator j=qualifiers.begin(); j!=qualifiers.end(); ++j) + { + if(j->name=="point") + writer.write_op(content.exec_modes, OP_EXECUTION_MODE, func_id, + ((*i)->interface=="in" ? EXEC_INPUT_POINTS : EXEC_OUTPUT_POINTS)); + else if(j->name=="lines") + writer.write_op(content.exec_modes, OP_EXECUTION_MODE, func_id, EXEC_INPUT_LINES); + else if(j->name=="lines_adjacency") + writer.write_op(content.exec_modes, OP_EXECUTION_MODE, func_id, EXEC_INPUT_LINES_ADJACENCY); + else if(j->name=="triangles") + writer.write_op(content.exec_modes, OP_EXECUTION_MODE, func_id, EXEC_TRIANGLES); + else if(j->name=="triangles_adjacency") + writer.write_op(content.exec_modes, OP_EXECUTION_MODE, func_id, EXEC_INPUT_TRIANGLES_ADJACENCY); + else if(j->name=="line_strip") + writer.write_op(content.exec_modes, OP_EXECUTION_MODE, func_id, EXEC_OUTPUT_LINE_STRIP); + else if(j->name=="triangle_strip") + writer.write_op(content.exec_modes, OP_EXECUTION_MODE, func_id, EXEC_OUTPUT_TRIANGLE_STRIP); + else if(j->name=="max_vertices") + writer.write_op(content.exec_modes, OP_EXECUTION_MODE, func_id, EXEC_OUTPUT_VERTICES, j->value); + } + } +} + +void SpirVGenerator::visit(FunctionDeclaration &func) +{ + if(func.source==BUILTIN_SOURCE || func.definition!=&func) + return; + + Id return_type_id = get_id(*func.return_type_declaration); + vector param_type_ids; + param_type_ids.reserve(func.parameters.size()); + for(NodeArray::const_iterator i=func.parameters.begin(); i!=func.parameters.end(); ++i) + param_type_ids.push_back(get_variable_type_id(**i)); + + string sig_with_return = func.return_type+func.signature; + Id &type_id = function_type_ids[sig_with_return]; + if(!type_id) + { + type_id = next_id++; + writer.begin_op(content.globals, OP_TYPE_FUNCTION); + writer.write(type_id); + writer.write(return_type_id); + for(vector::const_iterator i=param_type_ids.begin(); i!=param_type_ids.end(); ++i) + writer.write(*i); + writer.end_op(OP_TYPE_FUNCTION); + + writer.write_op_name(type_id, sig_with_return); + } + + Id func_id = allocate_id(func, type_id); + writer.write_op_name(func_id, func.name+func.signature); + + if(func.name=="main") + visit_entry_point(func, func_id); + + writer.begin_op(content.functions, OP_FUNCTION, 5); + writer.write(return_type_id); + writer.write(func_id); + writer.write(0); // Function control flags (none) + writer.write(type_id); + writer.end_op(OP_FUNCTION); + + for(unsigned i=0; i set_func(current_function, &func); + func.body.visit(*this); + + if(writer.has_current_block()) + { + if(!reachable) + writer.write_op(content.function_body, OP_UNREACHABLE); + else + { + const BasicTypeDeclaration *basic_return = dynamic_cast(func.return_type_declaration); + if(basic_return && basic_return->kind==BasicTypeDeclaration::VOID) + writer.write_op(content.function_body, OP_RETURN); + else + throw internal_error("missing return in non-void function"); + } + } + writer.end_function_body(); + variable_load_ids.clear(); +} + +void SpirVGenerator::visit(Conditional &cond) +{ + cond.condition->visit(*this); + + Id true_label_id = next_id++; + Id merge_block_id = next_id++; + Id false_label_id = (cond.else_body.body.empty() ? merge_block_id : next_id++); + writer.write_op(content.function_body, OP_SELECTION_MERGE, merge_block_id, 0); // Selection control (none) + writer.write_op(content.function_body, OP_BRANCH_CONDITIONAL, r_expression_result_id, true_label_id, false_label_id); + + writer.write_op_label(true_label_id); + cond.body.visit(*this); + if(writer.has_current_block()) + writer.write_op(content.function_body, OP_BRANCH, merge_block_id); + + bool reachable_if_true = reachable; + + reachable = true; + if(!cond.else_body.body.empty()) + { + writer.write_op_label(false_label_id); + cond.else_body.visit(*this); + reachable |= reachable_if_true; + } + + writer.write_op_label(merge_block_id); + prune_loads(true_label_id); +} + +void SpirVGenerator::visit(Iteration &iter) +{ + if(iter.init_statement) + iter.init_statement->visit(*this); + + Id header_id = next_id++; + Id continue_id = next_id++; + Id merge_block_id = next_id++; + + SetForScope set_merge(loop_merge_block_id, merge_block_id); + SetForScope set_continue(loop_continue_target_id, continue_id); + + writer.write_op_label(header_id); + writer.write_op(content.function_body, OP_LOOP_MERGE, merge_block_id, continue_id, 0); // Loop control (none) + + Id body_id = next_id++; + if(iter.condition) + { + writer.write_op_label(next_id++); + iter.condition->visit(*this); + writer.write_op(content.function_body, OP_BRANCH_CONDITIONAL, r_expression_result_id, body_id, merge_block_id); + } + + writer.write_op_label(body_id); + iter.body.visit(*this); + + writer.write_op_label(continue_id); + if(iter.loop_expression) + iter.loop_expression->visit(*this); + writer.write_op(content.function_body, OP_BRANCH, header_id); + + writer.write_op_label(merge_block_id); + prune_loads(header_id); + reachable = true; +} + +void SpirVGenerator::visit(Return &ret) +{ + if(ret.expression) + { + ret.expression->visit(*this); + writer.write_op(content.function_body, OP_RETURN_VALUE, r_expression_result_id); + } + else + writer.write_op(content.function_body, OP_RETURN); + reachable = false; +} + +void SpirVGenerator::visit(Jump &jump) +{ + if(jump.keyword=="discard") + writer.write_op(content.function_body, OP_KILL); + else if(jump.keyword=="break") + writer.write_op(content.function_body, OP_BRANCH, loop_merge_block_id); + else if(jump.keyword=="continue") + writer.write_op(content.function_body, OP_BRANCH, loop_continue_target_id); + else + throw internal_error("unknown jump"); + reachable = false; +} + + +bool SpirVGenerator::TypeKey::operator<(const TypeKey &other) const +{ + if(type_id!=other.type_id) + return type_id +#include +#include +#include "spirvconstants.h" +#include "spirvwriter.h" +#include "visitor.h" + +namespace Msp { +namespace GL { +namespace SL { + +/** Creates SPIR-V binary from a module. */ +class SpirVGenerator: private NodeVisitor +{ +private: + typedef SpirVCapability Capability; + typedef SpirVStorageClass StorageClass; + typedef SpirVOpcode Opcode; + typedef SpirVDecoration Decoration; + typedef SpirVBuiltin BuiltinSemantic; + typedef SpirVContent::Word Word; + typedef SpirVWriter::Id Id; + + struct BuiltinFunctionInfo + { + char function[22]; + char arg_types[5]; + char extension[13]; + Word opcode; + UInt8 arg_order[4]; + void (SpirVGenerator::*handler)(FunctionCall &, const std::vector &); + }; + + struct Declaration + { + Id id; + Id type_id; + + Declaration(Id i, Id t): id(i), type_id(t) { } + }; + + struct TypeKey + { + Id type_id; + unsigned detail; + + TypeKey(Id i, unsigned d): type_id(i), detail(d) { } + + bool operator<(const TypeKey &) const; + }; + + struct ConstantKey + { + Id type_id; + union + { + int int_value; + float float_value; + }; + + ConstantKey(Id t, int i): type_id(t), int_value(i) { } + ConstantKey(Id t, float f): type_id(t), float_value(f) { } + + bool operator<(const ConstantKey &) const; + }; + + Stage *stage; + FunctionDeclaration *current_function; + std::vector interface_layouts; + SpirVContent content; + SpirVWriter writer; + std::set used_capabilities; + std::map imported_extension_ids; + std::map declared_ids; + std::map declared_uniform_ids; + std::map standard_type_ids; + std::map array_type_ids; + std::map pointer_type_ids; + std::map function_type_ids; + std::map constant_ids; + std::map variable_load_ids; + Id next_id; + Id r_expression_result_id; + bool constant_expression; + bool spec_constant; + bool reachable; + bool composite_access; + Id r_composite_base_id; + Node *r_composite_base; + std::vector r_composite_chain; + Id assignment_source_id; + Id loop_merge_block_id; + Id loop_continue_target_id; + + static const BuiltinFunctionInfo builtin_functions[]; + +public: + SpirVGenerator(); + + void apply(Module &); + const std::vector &get_code() const { return content.code; } + +private: + static StorageClass get_interface_storage(const std::string &, bool); + static SpirVBuiltin get_builtin_semantic(const std::string &); + void use_capability(Capability); + Id import_extension(const std::string &); + Id get_id(Node &) const; + Id allocate_id(Node &, Id); + Id write_constant(Id, Word, bool); + static ConstantKey get_constant_key(Id, const Variant &value); + Id get_constant_id(Id, const Variant &value); + Id get_vector_constant_id(Id, unsigned, Id); + Id get_standard_type_id(BasicTypeDeclaration::Kind, unsigned); + bool is_scalar_type(Id, BasicTypeDeclaration::Kind) const; + Id get_array_type_id(TypeDeclaration &, unsigned); + Id get_pointer_type_id(Id, StorageClass); + Id get_variable_type_id(const VariableDeclaration &); + Id get_load_id(VariableDeclaration &); + void prune_loads(Id); + Id begin_expression(Opcode, Id, unsigned = 0); + void end_expression(Opcode); + Id write_expression(Opcode, Id, Id); + Id write_expression(Opcode, Id, Id, Id); + void write_deconstruct(Id, Id, Id *, unsigned); + Id write_construct(Id, const Id *, unsigned); + static BasicTypeDeclaration &get_element_type(BasicTypeDeclaration &); + + virtual void visit(Block &); + virtual void visit(Literal &); + virtual void visit(VariableReference &); + virtual void visit(InterfaceBlockReference &); + void generate_composite_access(TypeDeclaration &); + void visit_composite(Expression &, unsigned, TypeDeclaration &); + void visit_isolated(Expression &); + virtual void visit(MemberAccess &); + virtual void visit(Swizzle &); + virtual void visit(UnaryExpression &); + virtual void visit(BinaryExpression &); + virtual void visit(Assignment &); + virtual void visit(TernaryExpression &); + virtual void visit(FunctionCall &); + void visit_constructor(FunctionCall &, const std::vector &); + void visit_builtin_matrix_comp_mult(FunctionCall &, const std::vector &); + void visit_builtin_texture(FunctionCall &, const std::vector &); + void visit_builtin_texel_fetch(FunctionCall &, const std::vector &); + void visit_builtin_interpolate(FunctionCall &, const std::vector &); + virtual void visit(ExpressionStatement &); + virtual void visit(InterfaceLayout &); + bool check_duplicate_type(TypeDeclaration &); + bool check_standard_type(BasicTypeDeclaration &); + virtual void visit(BasicTypeDeclaration &); + virtual void visit(ImageTypeDeclaration &); + virtual void visit(StructDeclaration &); + virtual void visit(VariableDeclaration &); + virtual void visit(InterfaceBlock &); + void visit_entry_point(FunctionDeclaration &, Id); + virtual void visit(FunctionDeclaration &); + virtual void visit(Conditional &); + virtual void visit(Iteration &); + virtual void visit(Return &); + virtual void visit(Jump &); +}; + +} // namespace SL +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/glsl/spirvconstants.h b/source/glsl/spirvconstants.h new file mode 100644 index 00000000..b981d191 --- /dev/null +++ b/source/glsl/spirvconstants.h @@ -0,0 +1,275 @@ +#ifndef MSP_GL_SL_SPIRVCONSTANTS_H_ +#define MSP_GL_SL_SPIRVCONSTANTS_H_ + +namespace Msp { +namespace GL { +namespace SL { + +enum SpirVConstants +{ + SPIRV_MAGIC = 0x07230203, + SPIRV_MAGIC_REVERSED = 0x03022307 +}; + +enum SpirVOpcode +{ + OP_NOP = 1, + OP_NAME = 5, + OP_MEMBER_NAME = 6, + OP_EXT_INST_IMPORT = 11, + OP_EXT_INST = 12, + OP_MEMORY_MODEL = 14, + OP_ENTRY_POINT = 15, + OP_EXECUTION_MODE = 16, + OP_CAPABILITY = 17, + OP_TYPE_VOID = 19, + OP_TYPE_BOOL = 20, + OP_TYPE_INT = 21, + OP_TYPE_FLOAT = 22, + OP_TYPE_VECTOR = 23, + OP_TYPE_MATRIX = 24, + OP_TYPE_IMAGE = 25, + OP_TYPE_SAMPLED_IMAGE = 27, + OP_TYPE_ARRAY = 28, + OP_TYPE_RUNTIME_ARRAY = 29, + OP_TYPE_STRUCT = 30, + OP_TYPE_POINTER = 32, + OP_TYPE_FUNCTION = 33, + OP_CONSTANT_TRUE = 41, + OP_CONSTANT_FALSE = 42, + OP_CONSTANT = 43, + OP_CONSTANT_COMPOSITE = 44, + OP_SPEC_CONSTANT_TRUE = 48, + OP_SPEC_CONSTANT_FALSE = 49, + OP_SPEC_CONSTANT = 50, + OP_SPEC_CONSTANT_COMPOSITE = 51, + OP_SPEC_CONSTANT_OP = 52, + OP_FUNCTION = 54, + OP_FUNCTION_PARAMETER = 55, + OP_FUNCTION_END = 56, + OP_FUNCTION_CALL = 57, + OP_VARIABLE = 59, + OP_LOAD = 61, + OP_STORE = 62, + OP_ACCESS_CHAIN = 65, + OP_DECORATE = 71, + OP_MEMBER_DECORATE = 72, + OP_VECTOR_SHUFFLE = 79, + OP_COMPOSITE_CONSTRUCT = 80, + OP_COMPOSITE_EXTRACT = 81, + OP_TRANSPOSE = 84, + OP_IMAGE_SAMPLE_IMPLICIT_LOD = 87, + OP_IMAGE_SAMPLE_EXPLICIT_LOD = 88, + OP_IMAGE_SAMPLE_DREF_IMPLICIT_LOD = 89, + OP_IMAGE_SAMPLE_DREF_EXPLICIT_LOD = 89, + OP_IMAGE_FETCH = 95, + OP_IMAGE_QUERY_SIZE_LOD = 103, + OP_CONVERT_F_TO_S = 110, + OP_CONVERT_S_TO_F = 111, + OP_S_NEGATE = 126, + OP_F_NEGATE = 127, + OP_I_ADD = 128, + OP_F_ADD = 129, + OP_I_SUB = 130, + OP_F_SUB = 131, + OP_I_MUL = 132, + OP_F_MUL = 133, + OP_S_DIV = 135, + OP_F_DIV = 136, + OP_S_MOD = 139, + OP_F_MOD = 141, + OP_VECTOR_TIMES_SCALAR = 142, + OP_MATRIX_TIMES_SCALAR = 143, + OP_VECTOR_TIMES_MATRIX = 144, + OP_MATRIX_TIMES_VECTOR = 145, + OP_MATRIX_TIMES_MATRIX = 146, + OP_OUTER_PRODUCT = 147, + OP_DOT = 148, + OP_ANY = 154, + OP_ALL = 155, + OP_IS_NAN = 156, + OP_IS_INF = 157, + OP_LOGICAL_EQUAL = 164, + OP_LOGICAL_NOT_EQUAL = 165, + OP_LOGICAL_OR = 166, + OP_LOGICAL_AND = 167, + OP_LOGICAL_NOT = 168, + OP_SELECT = 169, + OP_I_EQUAL = 170, + OP_I_NOT_EQUAL = 171, + OP_S_GREATER_THAN = 173, + OP_S_GREATER_THAN_EQUAL = 175, + OP_S_LESS_THAN = 177, + OP_S_LESS_THAN_EQUAL = 179, + OP_F_ORD_EQUAL = 180, + OP_F_ORD_NOT_EQUAL = 182, + OP_F_ORD_LESS_THAN = 184, + OP_F_ORD_GREATER_THAN = 186, + OP_F_ORD_LESS_THAN_EQUAL = 188, + OP_F_ORD_GREATER_THAN_EQUAL = 190, + OP_SHIFT_RIGHT_ARITHMETIC = 195, + OP_SHIFT_LEFT_LOGICAL = 196, + OP_BITWISE_OR = 197, + OP_BITWISE_XOR = 198, + OP_BITWISE_AND = 199, + OP_NOT = 200, + OP_BIT_FIELD_INSERT = 201, + OP_BIT_FIELD_S_EXTRACT = 202, + OP_BIT_REVERSE = 204, + OP_BIT_COUNT = 205, + OP_DP_DX = 207, + OP_DP_DY = 208, + OP_FWIDTH = 209, + OP_DP_DX_FINE = 210, + OP_DP_DY_FINE = 211, + OP_FWIDTH_FINE = 212, + OP_DP_DX_COARSE = 213, + OP_DP_DY_COARSE = 214, + OP_FWIDTH_COARSE = 215, + OP_EMIT_VERTEX = 218, + OP_END_PRIMITIVE = 219, + OP_PHI = 245, + OP_LOOP_MERGE = 246, + OP_SELECTION_MERGE = 247, + OP_LABEL = 248, + OP_BRANCH = 249, + OP_BRANCH_CONDITIONAL = 250, + OP_KILL = 252, + OP_RETURN = 253, + OP_RETURN_VALUE = 254, + OP_UNREACHABLE = 255 +}; + +enum SpirVCapability +{ + CAP_SHADER = 1, + CAP_GEOMETRY = 2, + CAP_IMAGE_CUBE_ARRAY = 34, + CAP_SAMPLED_1D = 43, + CAP_IMAGE_1D = 44, + CAP_SAMPLED_CUBE_ARRAY = 45, + CAP_INTERPOLATION_FUNCTION = 52 +}; + +enum SpirVExecutionMode +{ + EXEC_ORIGIN_LOWER_LEFT = 8, + EXEC_INPUT_POINTS = 19, + EXEC_INPUT_LINES = 20, + EXEC_INPUT_LINES_ADJACENCY = 21, + EXEC_TRIANGLES = 22, + EXEC_INPUT_TRIANGLES_ADJACENCY = 23, + EXEC_OUTPUT_VERTICES = 26, + EXEC_OUTPUT_POINTS = 27, + EXEC_OUTPUT_LINE_STRIP = 28, + EXEC_OUTPUT_TRIANGLE_STRIP = 29 +}; + +enum SpirVStorageClass +{ + STORAGE_UNIFORM_CONSTANT = 0, + STORAGE_INPUT = 1, + STORAGE_UNIFORM = 2, + STORAGE_OUTPUT = 3, + STORAGE_PRIVATE = 6, + STORAGE_FUNCTION = 7 +}; + +enum SpirVDecoration +{ + DECO_SPEC_ID = 1, + DECO_BLOCK = 2, + DECO_ROW_MAJOR = 4, + DECO_COL_MAJOR = 5, + DECO_ARRAY_STRIDE = 6, + DECO_MATRIX_STRIDE = 7, + DECO_BUILTIN = 11, + DECO_LOCATION = 30, + DECO_BINDING = 33, + DECO_DESCRIPTOR_SET = 34, + DECO_OFFSET = 35 +}; + +enum SpirVBuiltin +{ + BUILTIN_POSITION = 0, + BUILTIN_POINT_SIZE = 1, + BUILTIN_CLIP_DISTANCE = 3, + BUILTIN_VERTEX_ID = 5, + BUILTIN_INSTANCE_ID = 6, + BUILTIN_PRIMITIVE_ID = 7, + BUILTIN_INVOCATION_ID = 8, + BUILTIN_LAYER = 9, + BUILTIN_FRAG_COORD = 15, + BUILTIN_POINT_COORD = 16, + BUILTIN_FRONT_FACING = 17, + BUILTIN_SAMPLE_ID = 18, + BUILTIN_SAMPLE_POSITION = 19, + BUILTIN_FRAG_DEPTH = 22 +}; + +enum SpirVGlslStd450Opcode +{ + GLSL450_ROUND = 1, + GLSL450_ROUND_EVEN = 2, + GLSL450_TRUNC = 3, + GLSL450_F_ABS = 4, + GLSL450_S_ABS = 5, + GLSL450_F_SIGN = 6, + GLSL450_S_SIGN = 7, + GLSL450_FLOOR = 8, + GLSL450_CEIL = 9, + GLSL450_FRACT = 10, + GLSL450_RADIANS = 11, + GLSL450_DEGREES = 12, + GLSL450_SIN = 13, + GLSL450_COS = 14, + GLSL450_TAN = 15, + GLSL450_ASIN = 16, + GLSL450_ACOS = 17, + GLSL450_ATAN = 18, + GLSL450_SINH = 19, + GLSL450_COSH = 20, + GLSL450_TANH = 21, + GLSL450_ASINH = 22, + GLSL450_ACOSH = 23, + GLSL450_ATANH = 24, + GLSL450_ATAN2 = 25, + GLSL450_POW = 26, + GLSL450_EXP = 27, + GLSL450_LOG = 28, + GLSL450_EXP2 = 29, + GLSL450_LOG2 = 30, + GLSL450_SQRT = 31, + GLSL450_INVERSE_SQRT = 32, + GLSL450_DETERMINANT = 33, + GLSL450_MATRIX_INVERSE = 33, + GLSL450_F_MIN = 37, + GLSL450_S_MIN = 39, + GLSL450_F_MAX = 40, + GLSL450_S_MAX = 42, + GLSL450_F_CLAMP = 43, + GLSL450_S_CLAMP = 45, + GLSL450_F_MIX = 46, + GLSL450_F_STEP = 48, + GLSL450_F_SMOOTH_STEP = 49, + GLSL450_F_FMA = 50, + GLSL450_LENGTH = 66, + GLSL450_DISTANCE = 67, + GLSL450_CROSS = 68, + GLSL450_NORMALIZE = 69, + GLSL450_FACE_FORWARD = 70, + GLSL450_REFLECT = 71, + GLSL450_REFRACT = 72, + GLSL450_FIND_I_LSB = 73, + GLSL450_FIND_S_MSB = 74, + GLSL450_INTERPOLATE_AT_CENTROID = 76, + GLSL450_INTERPOLATE_AT_SAMPLE = 77, + GLSL450_INTERPOLATE_AT_OFFSET = 78 +}; + +} // namespace SL +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/glsl/spirvwriter.cpp b/source/glsl/spirvwriter.cpp new file mode 100644 index 00000000..77ddbdb9 --- /dev/null +++ b/source/glsl/spirvwriter.cpp @@ -0,0 +1,213 @@ +#include "glsl_error.h" +#include "spirvwriter.h" + +using namespace std; + +namespace Msp { +namespace GL { +namespace SL { + +SpirVWriter::SpirVWriter(SpirVContent &c): + content(c), + op_target(0), + op_head_pos(0), + current_block_id(0) +{ } + +void SpirVWriter::append(vector &target, const vector &source) +{ + target.insert(target.end(), source.begin(), source.end()); +} + +void SpirVWriter::write(Word word) +{ + if(!op_target) + throw logic_error("write without begin_op"); + op_target->push_back(word); +} + +void SpirVWriter::write_float(float value) +{ + union + { + float fv; + Word word; + }; + fv = value; + write(word); +} + +void SpirVWriter::write_string(const string &str) +{ + for(unsigned i=0; i<=str.size(); i+=4) + { + Word word = 0; + for(unsigned j=0; (j<4 && i+j(str[i+j])<<(j*8); + write(word); + } +} + +void SpirVWriter::begin_op(vector &target, Opcode opcode, unsigned size) +{ + if(op_target) + throw logic_error("begin_op without end_op"); + if(&target==&content.function_body && !current_block_id) + throw logic_error("no open block in function"); + + op_head_pos = target.size(); + op_target = ⌖ + write(opcode | (size<<16)); + + if(opcode==OP_BRANCH || opcode==OP_BRANCH_CONDITIONAL || opcode==OP_KILL || + opcode==OP_RETURN || opcode==OP_RETURN_VALUE || opcode==OP_UNREACHABLE) + current_block_id = 0; +} + +void SpirVWriter::end_op(Opcode opcode) +{ + if(!op_target) + throw logic_error("end_op without begin_op"); + Word &op_head = (*op_target)[op_head_pos]; + if(opcode!=(op_head&0xFFFF)) + throw logic_error("opcode mismatch"); + + unsigned words = op_target->size()-op_head_pos; + unsigned op_size = op_head>>16; + if(op_size) + { + if(words!=op_size) + throw logic_error("incorred number of words written"); + } + else + op_head |= (words<<16); + + op_target = 0; + op_head_pos = 0; +} + +void SpirVWriter::write_op(vector &target, Opcode opcode) +{ + begin_op(target, opcode, 1); + end_op(opcode); +} + +void SpirVWriter::write_op(vector &target, Opcode opcode, Word arg0) +{ + begin_op(target, opcode, 2); + write(arg0); + end_op(opcode); +} + +void SpirVWriter::write_op(vector &target, Opcode opcode, Word arg0, Word arg1) +{ + begin_op(target, opcode, 3); + write(arg0); + write(arg1); + end_op(opcode); +} + +void SpirVWriter::write_op(vector &target, Opcode opcode, Word arg0, Word arg1, Word arg2) +{ + begin_op(target, opcode, 4); + write(arg0); + write(arg1); + write(arg2); + end_op(opcode); +} + +void SpirVWriter::write_op_name(Id id, const string &name) +{ + begin_op(content.names, OP_NAME); + write(id); + write_string(name); + end_op(OP_NAME); +} + +void SpirVWriter::write_op_member_name(Id id, unsigned index, const string &name) +{ + begin_op(content.names, OP_MEMBER_NAME); + write(id); + write(index); + write_string(name); + end_op(OP_MEMBER_NAME); +} + +void SpirVWriter::write_op_decorate(Id id, Decoration decoration) +{ + write_op(content.decorations, OP_DECORATE, id, decoration); +} + +void SpirVWriter::write_op_decorate(Id id, Decoration decoration, Word value) +{ + write_op(content.decorations, OP_DECORATE, id, decoration, value); +} + +void SpirVWriter::write_op_member_decorate(Id id, unsigned index, Decoration decoration) +{ + write_op(content.decorations, OP_MEMBER_DECORATE, id, index, decoration); +} + +void SpirVWriter::write_op_member_decorate(Id id, unsigned index, Decoration decoration, Word value) +{ + begin_op(content.decorations, OP_MEMBER_DECORATE, 5); + write(id); + write(index); + write(decoration); + write(value); + end_op(OP_MEMBER_DECORATE); +} + +void SpirVWriter::write_op_label(Id label_id) +{ + if(current_block_id) + write_op(content.function_body, OP_BRANCH, label_id); + current_block_id = label_id; + write_op(content.function_body, OP_LABEL, label_id); +} + +void SpirVWriter::begin_function_body(Id first_block_id) +{ + if(!content.function_body.empty() || current_block_id) + throw internal_error("begin_function without end_function"); + + current_block_id = first_block_id; + write_op(content.functions, OP_LABEL, first_block_id); +} + +void SpirVWriter::end_function_body() +{ + if(content.function_body.empty()) + throw internal_error("end_function without begin_function"); + if(current_block_id) + throw internal_error("end_function with open block"); + + append(content.functions, content.locals); + append(content.functions, content.function_body); + write_op(content.functions, OP_FUNCTION_END); + + content.locals.clear(); + content.function_body.clear(); +} + +void SpirVWriter::finalize(Id id_bound) +{ + content.code.push_back(SPIRV_MAGIC); + content.code.push_back(0x00010500); + content.code.push_back(0); // Generator + content.code.push_back(id_bound); + content.code.push_back(0); // Reserved + append(content.code, content.capabilities); + append(content.code, content.extensions); + write_op(content.code, OP_MEMORY_MODEL, 0, 1); // Logical, GLSL450 + append(content.code, content.entry_points); + append(content.code, content.exec_modes); + append(content.code, content.names); + append(content.code, content.decorations); + append(content.code, content.globals); + append(content.code, content.functions); +} + +} // namespace SL +} // namespace GL +} // namespace Msp diff --git a/source/glsl/spirvwriter.h b/source/glsl/spirvwriter.h new file mode 100644 index 00000000..94f44e88 --- /dev/null +++ b/source/glsl/spirvwriter.h @@ -0,0 +1,74 @@ +#ifndef MSP_GL_SL_SPIRVWRITER_H_ +#define MSP_GL_SL_SPIRVWRITER_H_ + +#include +#include +#include +#include "spirvconstants.h" + +namespace Msp { +namespace GL { +namespace SL { + +struct SpirVContent +{ + typedef UInt32 Word; + + std::vector code; + std::vector capabilities; + std::vector extensions; + std::vector entry_points; + std::vector exec_modes; + std::vector names; + std::vector decorations; + std::vector globals; + std::vector functions; + std::vector locals; + std::vector function_body; +}; + +class SpirVWriter +{ +public: + typedef SpirVOpcode Opcode; + typedef SpirVDecoration Decoration; + typedef SpirVContent::Word Word; + typedef Word Id; + +private: + SpirVContent &content; + std::vector *op_target; + unsigned op_head_pos; + Id current_block_id; + +public: + SpirVWriter(SpirVContent &); + + void append(std::vector &, const std::vector &); + void write(Word); + void write_float(float); + void write_string(const std::string &); + void begin_op(std::vector &, Opcode, unsigned = 0); + void end_op(SpirVOpcode); + void write_op(std::vector &, Opcode); + void write_op(std::vector &, Opcode, Word); + void write_op(std::vector &, Opcode, Word, Word); + void write_op(std::vector &, Opcode, Word, Word, Word); + void write_op_name(Id, const std::string &); + void write_op_member_name(Id, unsigned, const std::string &); + void write_op_decorate(Id, Decoration); + void write_op_decorate(Id, Decoration, Word); + void write_op_member_decorate(Id, unsigned, Decoration); + void write_op_member_decorate(Id, unsigned, Decoration, Word); + void write_op_label(Id); + bool has_current_block() const { return current_block_id; } + void begin_function_body(Id); + void end_function_body(); + void finalize(Id); +}; + +} // namespace SL +} // namespace GL +} // namespace Msp + +#endif