From f3f1a7fe42f8cd22655ebff3c64b342f8974dfd3 Mon Sep 17 00:00:00 2001 From: DrChat Date: Sun, 18 Feb 2018 20:13:38 -0600 Subject: [PATCH] [SPIR-V] Update glslang WIP new vertex fetching --- src/xenia/gpu/premake5.lua | 4 +- src/xenia/gpu/spirv_shader_translator.cc | 111 +- src/xenia/gpu/spirv_shader_translator.h | 3 +- third_party/glslang-spirv/GLSL.ext.AMD.h | 110 + third_party/glslang-spirv/GLSL.ext.EXT.h | 39 + third_party/glslang-spirv/GLSL.ext.KHR.h | 48 + third_party/glslang-spirv/GLSL.ext.NV.h | 54 + third_party/glslang-spirv/GlslangToSpv.cpp | 3348 ++++++++++++++--- third_party/glslang-spirv/GlslangToSpv.h | 66 +- third_party/glslang-spirv/InReadableOrder.cpp | 59 +- third_party/glslang-spirv/Include/BaseTypes.h | 387 ++ third_party/glslang-spirv/Include/Common.h | 274 ++ .../glslang-spirv/Include/ConstantUnion.h | 625 +++ third_party/glslang-spirv/Include/InfoSink.h | 144 + .../glslang-spirv/Include/InitializeGlobals.h | 44 + third_party/glslang-spirv/Include/PoolAlloc.h | 317 ++ .../glslang-spirv/Include/ResourceLimits.h | 140 + third_party/glslang-spirv/Include/ShHandle.h | 176 + third_party/glslang-spirv/Include/Types.h | 1924 ++++++++++ third_party/glslang-spirv/Include/arrays.h | 329 ++ .../glslang-spirv/Include/intermediate.h | 1486 ++++++++ third_party/glslang-spirv/Include/revision.h | 6 + .../glslang-spirv/Include/revision.template | 13 + third_party/glslang-spirv/Logger.cpp | 68 + third_party/glslang-spirv/Logger.h | 74 + third_party/glslang-spirv/SPVRemapper.cpp | 486 ++- third_party/glslang-spirv/SPVRemapper.h | 100 +- third_party/glslang-spirv/SpvBuilder.cpp | 708 +++- third_party/glslang-spirv/SpvBuilder.h | 169 +- third_party/glslang-spirv/bitutils.h | 81 + third_party/glslang-spirv/disassemble.cpp | 193 +- third_party/glslang-spirv/disassemble.h | 38 +- third_party/glslang-spirv/doc.cpp | 243 +- third_party/glslang-spirv/doc.h | 45 +- third_party/glslang-spirv/hex_float.h | 1078 ++++++ third_party/glslang-spirv/spirv.hpp | 155 +- third_party/glslang-spirv/spvIR.h | 159 +- 37 files changed, 11978 insertions(+), 1326 deletions(-) create mode 100644 third_party/glslang-spirv/GLSL.ext.AMD.h create mode 100644 third_party/glslang-spirv/GLSL.ext.EXT.h create mode 100644 third_party/glslang-spirv/GLSL.ext.KHR.h create mode 100644 third_party/glslang-spirv/GLSL.ext.NV.h create mode 100644 third_party/glslang-spirv/Include/BaseTypes.h create mode 100644 third_party/glslang-spirv/Include/Common.h create mode 100644 third_party/glslang-spirv/Include/ConstantUnion.h create mode 100644 third_party/glslang-spirv/Include/InfoSink.h create mode 100644 third_party/glslang-spirv/Include/InitializeGlobals.h create mode 100644 third_party/glslang-spirv/Include/PoolAlloc.h create mode 100644 third_party/glslang-spirv/Include/ResourceLimits.h create mode 100644 third_party/glslang-spirv/Include/ShHandle.h create mode 100644 third_party/glslang-spirv/Include/Types.h create mode 100644 third_party/glslang-spirv/Include/arrays.h create mode 100644 third_party/glslang-spirv/Include/intermediate.h create mode 100644 third_party/glslang-spirv/Include/revision.h create mode 100644 third_party/glslang-spirv/Include/revision.template create mode 100644 third_party/glslang-spirv/Logger.cpp create mode 100644 third_party/glslang-spirv/Logger.h create mode 100644 third_party/glslang-spirv/bitutils.h create mode 100644 third_party/glslang-spirv/hex_float.h diff --git a/src/xenia/gpu/premake5.lua b/src/xenia/gpu/premake5.lua index 1c7870edc..d0ce85820 100644 --- a/src/xenia/gpu/premake5.lua +++ b/src/xenia/gpu/premake5.lua @@ -22,8 +22,8 @@ project("xenia-gpu") project_root.."/third_party/gflags/src", }) local_platform_files() - local_platform_files("spirv") - local_platform_files("spirv/passes") + -- local_platform_files("spirv") + -- local_platform_files("spirv/passes") group("src") project("xenia-gpu-shader-compiler") diff --git a/src/xenia/gpu/spirv_shader_translator.cc b/src/xenia/gpu/spirv_shader_translator.cc index f646f8920..24f4f3981 100644 --- a/src/xenia/gpu/spirv_shader_translator.cc +++ b/src/xenia/gpu/spirv_shader_translator.cc @@ -16,8 +16,6 @@ #include #include "xenia/base/logging.h" -#include "xenia/gpu/spirv/passes/control_flow_analysis_pass.h" -#include "xenia/gpu/spirv/passes/control_flow_simplification_pass.h" DEFINE_bool(spv_validate, false, "Validate SPIR-V shaders after generation"); DEFINE_bool(spv_disasm, false, "Disassemble SPIR-V shaders after generation"); @@ -33,16 +31,12 @@ using spv::GLSLstd450; using spv::Id; using spv::Op; -SpirvShaderTranslator::SpirvShaderTranslator() { - compiler_.AddPass(std::make_unique()); - compiler_.AddPass(std::make_unique()); -} - +SpirvShaderTranslator::SpirvShaderTranslator() {} SpirvShaderTranslator::~SpirvShaderTranslator() = default; void SpirvShaderTranslator::StartTranslation() { // Create a new builder. - builder_ = std::make_unique(0xFFFFFFFF); + builder_ = std::make_unique(SPV_VERSION, 0xFFFFFFFF, nullptr); auto& b = *builder_; // Import required modules. @@ -225,69 +219,14 @@ void SpirvShaderTranslator::StartTranslation() { Id interpolators_type = b.makeArrayType( vec4_float_type_, b.makeUintConstant(kMaxInterpolators), 0); if (is_vertex_shader()) { - // Vertex inputs/outputs. - for (const auto& binding : vertex_bindings()) { - for (const auto& attrib : binding.attributes) { - Id attrib_type = 0; - bool is_signed = attrib.fetch_instr.attributes.is_signed; - bool is_integer = attrib.fetch_instr.attributes.is_integer; - switch (attrib.fetch_instr.attributes.data_format) { - case VertexFormat::k_32: - case VertexFormat::k_32_FLOAT: - attrib_type = float_type_; - break; - case VertexFormat::k_16_16: - case VertexFormat::k_32_32: - if (is_integer) { - attrib_type = is_signed ? vec2_int_type_ : vec2_uint_type_; - break; - } - // Intentionally fall through to float type. - case VertexFormat::k_16_16_FLOAT: - case VertexFormat::k_32_32_FLOAT: - attrib_type = vec2_float_type_; - break; - case VertexFormat::k_32_32_32_FLOAT: - attrib_type = vec3_float_type_; - break; - case VertexFormat::k_2_10_10_10: - attrib_type = vec4_float_type_; - break; - case VertexFormat::k_8_8_8_8: - case VertexFormat::k_16_16_16_16: - case VertexFormat::k_32_32_32_32: - if (is_integer) { - attrib_type = is_signed ? vec4_int_type_ : vec4_uint_type_; - break; - } - // Intentionally fall through to float type. - case VertexFormat::k_16_16_16_16_FLOAT: - case VertexFormat::k_32_32_32_32_FLOAT: - attrib_type = vec4_float_type_; - break; - case VertexFormat::k_10_11_11: - case VertexFormat::k_11_11_10: - // Manually converted. - attrib_type = is_signed ? int_type_ : uint_type_; - break; - default: - assert_always(); - } - - auto attrib_var = b.createVariable( - spv::StorageClass::StorageClassInput, attrib_type, - xe::format_string("vf%d_%d", binding.fetch_constant, - attrib.fetch_instr.attributes.offset) - .c_str()); - b.addDecoration(attrib_var, spv::Decoration::DecorationLocation, - attrib.attrib_index); - - interface_ids_.push_back(attrib_var); - vertex_binding_map_[binding.fetch_constant] - [attrib.fetch_instr.attributes.offset] = attrib_var; - } - } + // Vertex inputs/outputs + // Inputs: 32 SSBOs on DS 2 binding 0 + Id vtx_t = b.makeRuntimeArray(uint_type_); + Id vtx_a_t = b.makeArrayType(vtx_t, b.makeUintConstant(32), 0); + vtx_ = b.createVariable(spv::StorageClass::StorageClassUniform, vtx_a_t, + "vertex_bindings"); + // Outputs interpolators_ = b.createVariable(spv::StorageClass::StorageClassOutput, interpolators_type, "interpolators"); b.addDecoration(interpolators_, spv::Decoration::DecorationLocation, 0); @@ -421,7 +360,7 @@ void SpirvShaderTranslator::StartTranslation() { auto cond = b.createBinOp(spv::Op::OpINotEqual, bool_type_, ps_param_gen_idx, b.makeUintConstant(-1)); - spv::Builder::If ifb(cond, b); + spv::Builder::If ifb(cond, 0, b); // FYI: We do this instead of r[ps_param_gen_idx] because that causes // nvidia to move all registers into local memory (slow!) @@ -458,7 +397,7 @@ void SpirvShaderTranslator::StartTranslation() { // While loop header block b.setBuildPoint(loop_head_block_); b.createLoopMerge(loop_exit_block_, loop_cont_block_, - spv::LoopControlMask::LoopControlDontUnrollMask); + spv::LoopControlMask::LoopControlDontUnrollMask, 0); b.createBranch(block); // Condition block @@ -481,7 +420,8 @@ std::vector SpirvShaderTranslator::CompleteTranslation() { exec_skip_block_ = nullptr; // main() entry point. - auto mainFn = b.makeMain(); + auto mainFn = + b.makeFunctionEntry(spv::NoPrecision, b.makeVoidType(), "main", {}, {}); if (is_vertex_shader()) { auto entry = b.addEntryPoint(spv::ExecutionModel::ExecutionModelVertex, mainFn, "main"); @@ -570,12 +510,12 @@ std::vector SpirvShaderTranslator::CompleteTranslation() { auto cond = b.createBinOp(spv::Op::OpFOrdEqual, bool_type_, alpha_test_enabled, b.makeFloatConstant(1.f)); - spv::Builder::If alpha_if(cond, b); + spv::Builder::If alpha_if(cond, 0, b); std::vector switch_segments; - b.makeSwitch(alpha_test_func, 8, std::vector({0, 1, 2, 3, 4, 5, 6, 7}), - std::vector({0, 1, 2, 3, 4, 5, 6, 7}), 7, - switch_segments); + b.makeSwitch( + alpha_test_func, 0, 8, std::vector({0, 1, 2, 3, 4, 5, 6, 7}), + std::vector({0, 1, 2, 3, 4, 5, 6, 7}), 7, switch_segments); const static spv::Op alpha_op_map[] = { spv::Op::OpNop, @@ -597,7 +537,7 @@ std::vector SpirvShaderTranslator::CompleteTranslation() { b.nextSwitchSegment(switch_segments, i); auto cond = b.createBinOp(alpha_op_map[i], bool_type_, oC0_alpha, alpha_test_ref); - spv::Builder::If discard_if(cond, b); + spv::Builder::If discard_if(cond, 0, b); b.makeDiscard(); discard_if.makeEndIf(); b.addSwitchBreak(); @@ -1475,7 +1415,8 @@ void SpirvShaderTranslator::ProcessTextureFetchInstruction( std::memset(¶ms, 0, sizeof(params)); params.sampler = image; params.lod = b.makeIntConstant(0); - size = b.createTextureQueryCall(spv::Op::OpImageQuerySizeLod, params); + size = b.createTextureQueryCall(spv::Op::OpImageQuerySizeLod, params, + false); if (instr.dimension == TextureDimension::k1D) { size = b.createUnaryOp(spv::Op::OpConvertSToF, float_type_, size); @@ -1563,8 +1504,8 @@ void SpirvShaderTranslator::ProcessTextureFetchInstruction( std::memset(¶ms, 0, sizeof(params)); params.sampler = image; params.lod = b.makeIntConstant(0); - auto size = - b.createTextureQueryCall(spv::Op::OpImageQuerySizeLod, params); + auto size = b.createTextureQueryCall(spv::Op::OpImageQuerySizeLod, + params, true); size = b.createUnaryOp(spv::Op::OpConvertUToF, vec2_float_type_, size); @@ -1624,7 +1565,7 @@ spv::Function* SpirvShaderTranslator::CreateCubeFunction() { spv::Block* function_block = nullptr; auto function = b.makeFunctionEntry(spv::NoPrecision, vec4_float_type_, "cube", {vec4_float_type_}, - {spv::NoPrecision}, &function_block); + {{spv::NoPrecision}}, &function_block); auto src = function->getParamId(0); auto face_id = b.createVariable(spv::StorageClass::StorageClassFunction, float_type_, "face_id"); @@ -1685,7 +1626,7 @@ spv::Function* SpirvShaderTranslator::CreateCubeFunction() { auto x_gt_z = b.createBinOp(spv::Op::OpFOrdGreaterThan, bool_type_, abs_src_x, abs_src_z); auto c1 = b.createBinOp(spv::Op::OpLogicalAnd, bool_type_, x_gt_y, x_gt_z); - spv::Builder::If if1(c1, b); + spv::Builder::If if1(c1, 0, b); // sc = abs(src).y b.createStore(abs_src_y, sc); @@ -1720,7 +1661,7 @@ spv::Function* SpirvShaderTranslator::CreateCubeFunction() { auto y_gt_z = b.createBinOp(spv::Op::OpFOrdGreaterThan, bool_type_, abs_src_y, abs_src_z); auto c1 = b.createBinOp(spv::Op::OpLogicalAnd, bool_type_, y_gt_x, y_gt_z); - spv::Builder::If if1(c1, b); + spv::Builder::If if1(c1, 0, b); // tc = -abs(src).x b.createStore(neg_src_x, tc); @@ -1755,7 +1696,7 @@ spv::Function* SpirvShaderTranslator::CreateCubeFunction() { auto z_gt_y = b.createBinOp(spv::Op::OpFOrdGreaterThan, bool_type_, abs_src_z, abs_src_y); auto c1 = b.createBinOp(spv::Op::OpLogicalAnd, bool_type_, z_gt_x, z_gt_y); - spv::Builder::If if1(c1, b); + spv::Builder::If if1(c1, 0, b); // tc = -abs(src).x b.createStore(neg_src_x, tc); diff --git a/src/xenia/gpu/spirv_shader_translator.h b/src/xenia/gpu/spirv_shader_translator.h index bafbe7101..224c2c207 100644 --- a/src/xenia/gpu/spirv_shader_translator.h +++ b/src/xenia/gpu/spirv_shader_translator.h @@ -17,7 +17,6 @@ #include "third_party/glslang-spirv/SpvBuilder.h" #include "third_party/spirv/GLSL.std.450.hpp11" #include "xenia/gpu/shader_translator.h" -#include "xenia/gpu/spirv/compiler.h" #include "xenia/ui/spirv/spirv_disassembler.h" #include "xenia/ui/spirv/spirv_validator.h" @@ -112,7 +111,6 @@ class SpirvShaderTranslator : public ShaderTranslator { xe::ui::spirv::SpirvDisassembler disassembler_; xe::ui::spirv::SpirvValidator validator_; - xe::gpu::spirv::Compiler compiler_; // True if there's an open predicated block bool open_predicated_block_ = false; @@ -159,6 +157,7 @@ class SpirvShaderTranslator : public ShaderTranslator { spv::Id frag_outputs_ = 0, frag_depth_ = 0; spv::Id samplers_ = 0; spv::Id tex_[3] = {0}; // Images {2D, 3D, Cube} + spv::Id vtx_ = 0; // Vertex buffer array (32 runtime arrays) // SPIR-V IDs that are part of the in/out interface. std::vector interface_ids_; diff --git a/third_party/glslang-spirv/GLSL.ext.AMD.h b/third_party/glslang-spirv/GLSL.ext.AMD.h new file mode 100644 index 000000000..d4f57efdc --- /dev/null +++ b/third_party/glslang-spirv/GLSL.ext.AMD.h @@ -0,0 +1,110 @@ +/* +** Copyright (c) 2014-2016 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and/or associated documentation files (the "Materials"), +** to deal in the Materials without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Materials, and to permit persons to whom the +** Materials are furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Materials. +** +** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +** IN THE MATERIALS. +*/ + +#ifndef GLSLextAMD_H +#define GLSLextAMD_H + +enum BuiltIn; +enum Capability; +enum Decoration; +enum Op; + +static const int GLSLextAMDVersion = 100; +static const int GLSLextAMDRevision = 6; + +// SPV_AMD_shader_ballot +static const char* const E_SPV_AMD_shader_ballot = "SPV_AMD_shader_ballot"; + +enum ShaderBallotAMD { + ShaderBallotBadAMD = 0, // Don't use + + SwizzleInvocationsAMD = 1, + SwizzleInvocationsMaskedAMD = 2, + WriteInvocationAMD = 3, + MbcntAMD = 4, + + ShaderBallotCountAMD +}; + +// SPV_AMD_shader_trinary_minmax +static const char* const E_SPV_AMD_shader_trinary_minmax = "SPV_AMD_shader_trinary_minmax"; + +enum ShaderTrinaryMinMaxAMD { + ShaderTrinaryMinMaxBadAMD = 0, // Don't use + + FMin3AMD = 1, + UMin3AMD = 2, + SMin3AMD = 3, + FMax3AMD = 4, + UMax3AMD = 5, + SMax3AMD = 6, + FMid3AMD = 7, + UMid3AMD = 8, + SMid3AMD = 9, + + ShaderTrinaryMinMaxCountAMD +}; + +// SPV_AMD_shader_explicit_vertex_parameter +static const char* const E_SPV_AMD_shader_explicit_vertex_parameter = "SPV_AMD_shader_explicit_vertex_parameter"; + +enum ShaderExplicitVertexParameterAMD { + ShaderExplicitVertexParameterBadAMD = 0, // Don't use + + InterpolateAtVertexAMD = 1, + + ShaderExplicitVertexParameterCountAMD +}; + +// SPV_AMD_gcn_shader +static const char* const E_SPV_AMD_gcn_shader = "SPV_AMD_gcn_shader"; + +enum GcnShaderAMD { + GcnShaderBadAMD = 0, // Don't use + + CubeFaceIndexAMD = 1, + CubeFaceCoordAMD = 2, + TimeAMD = 3, + + GcnShaderCountAMD +}; + +// SPV_AMD_gpu_shader_half_float +static const char* const E_SPV_AMD_gpu_shader_half_float = "SPV_AMD_gpu_shader_half_float"; + +// SPV_AMD_texture_gather_bias_lod +static const char* const E_SPV_AMD_texture_gather_bias_lod = "SPV_AMD_texture_gather_bias_lod"; + +// SPV_AMD_gpu_shader_int16 +static const char* const E_SPV_AMD_gpu_shader_int16 = "SPV_AMD_gpu_shader_int16"; + +// SPV_AMD_shader_image_load_store_lod +static const char* const E_SPV_AMD_shader_image_load_store_lod = "SPV_AMD_shader_image_load_store_lod"; + +// SPV_AMD_shader_fragment_mask +static const char* const E_SPV_AMD_shader_fragment_mask = "SPV_AMD_shader_fragment_mask"; + +#endif // #ifndef GLSLextAMD_H diff --git a/third_party/glslang-spirv/GLSL.ext.EXT.h b/third_party/glslang-spirv/GLSL.ext.EXT.h new file mode 100644 index 000000000..e879714d0 --- /dev/null +++ b/third_party/glslang-spirv/GLSL.ext.EXT.h @@ -0,0 +1,39 @@ +/* +** Copyright (c) 2014-2016 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and/or associated documentation files (the "Materials"), +** to deal in the Materials without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Materials, and to permit persons to whom the +** Materials are furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Materials. +** +** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +** IN THE MATERIALS. +*/ + +#ifndef GLSLextEXT_H +#define GLSLextEXT_H + +enum BuiltIn; +enum Op; +enum Capability; + +static const int GLSLextEXTVersion = 100; +static const int GLSLextEXTRevision = 1; + +static const char* const E_SPV_EXT_fragment_fully_covered = "SPV_EXT_fragment_fully_covered"; + +#endif // #ifndef GLSLextEXT_H diff --git a/third_party/glslang-spirv/GLSL.ext.KHR.h b/third_party/glslang-spirv/GLSL.ext.KHR.h new file mode 100644 index 000000000..2eb10ae62 --- /dev/null +++ b/third_party/glslang-spirv/GLSL.ext.KHR.h @@ -0,0 +1,48 @@ +/* +** Copyright (c) 2014-2016 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and/or associated documentation files (the "Materials"), +** to deal in the Materials without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Materials, and to permit persons to whom the +** Materials are furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Materials. +** +** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +** IN THE MATERIALS. +*/ + +#ifndef GLSLextKHR_H +#define GLSLextKHR_H + +enum BuiltIn; +enum Op; +enum Capability; + +static const int GLSLextKHRVersion = 100; +static const int GLSLextKHRRevision = 2; + +static const char* const E_SPV_KHR_shader_ballot = "SPV_KHR_shader_ballot"; +static const char* const E_SPV_KHR_subgroup_vote = "SPV_KHR_subgroup_vote"; +static const char* const E_SPV_KHR_device_group = "SPV_KHR_device_group"; +static const char* const E_SPV_KHR_multiview = "SPV_KHR_multiview"; +static const char* const E_SPV_KHR_shader_draw_parameters = "SPV_KHR_shader_draw_parameters"; +static const char* const E_SPV_KHR_16bit_storage = "SPV_KHR_16bit_storage"; +static const char* const E_SPV_KHR_storage_buffer_storage_class = "SPV_KHR_storage_buffer_storage_class"; +static const char* const E_SPV_KHR_post_depth_coverage = "SPV_KHR_post_depth_coverage"; +static const char* const E_SPV_EXT_shader_stencil_export = "SPV_EXT_shader_stencil_export"; +static const char* const E_SPV_EXT_shader_viewport_index_layer = "SPV_EXT_shader_viewport_index_layer"; + +#endif // #ifndef GLSLextKHR_H diff --git a/third_party/glslang-spirv/GLSL.ext.NV.h b/third_party/glslang-spirv/GLSL.ext.NV.h new file mode 100644 index 000000000..c01858be4 --- /dev/null +++ b/third_party/glslang-spirv/GLSL.ext.NV.h @@ -0,0 +1,54 @@ +/* +** Copyright (c) 2014-2017 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and/or associated documentation files (the "Materials"), +** to deal in the Materials without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Materials, and to permit persons to whom the +** Materials are furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Materials. +** +** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +** IN THE MATERIALS. +*/ + +#ifndef GLSLextNV_H +#define GLSLextNV_H + +enum BuiltIn; +enum Decoration; +enum Op; +enum Capability; + +static const int GLSLextNVVersion = 100; +static const int GLSLextNVRevision = 5; + +//SPV_NV_sample_mask_override_coverage +const char* const E_SPV_NV_sample_mask_override_coverage = "SPV_NV_sample_mask_override_coverage"; + +//SPV_NV_geometry_shader_passthrough +const char* const E_SPV_NV_geometry_shader_passthrough = "SPV_NV_geometry_shader_passthrough"; + +//SPV_NV_viewport_array2 +const char* const E_SPV_NV_viewport_array2 = "SPV_NV_viewport_array2"; +const char* const E_ARB_shader_viewport_layer_array = "SPV_ARB_shader_viewport_layer_array"; + +//SPV_NV_stereo_view_rendering +const char* const E_SPV_NV_stereo_view_rendering = "SPV_NV_stereo_view_rendering"; + +//SPV_NVX_multiview_per_view_attributes +const char* const E_SPV_NVX_multiview_per_view_attributes = "SPV_NVX_multiview_per_view_attributes"; + +#endif // #ifndef GLSLextNV_H \ No newline at end of file diff --git a/third_party/glslang-spirv/GlslangToSpv.cpp b/third_party/glslang-spirv/GlslangToSpv.cpp index 864902ccb..6e9fb38bb 100644 --- a/third_party/glslang-spirv/GlslangToSpv.cpp +++ b/third_party/glslang-spirv/GlslangToSpv.cpp @@ -1,12 +1,12 @@ // -//Copyright (C) 2014-2015 LunarG, Inc. -//Copyright (C) 2015-2016 Google, Inc. +// Copyright (C) 2014-2016 LunarG, Inc. +// Copyright (C) 2015-2016 Google, Inc. // -//All rights reserved. +// All rights reserved. // -//Redistribution and use in source and binary forms, with or without -//modification, are permitted provided that the following conditions -//are met: +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: // // Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. @@ -20,21 +20,19 @@ // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // -//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -//"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -//LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -//FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -//COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -//INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -//BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -//LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -//CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -//ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -//POSSIBILITY OF SUCH DAMAGE. +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. -// -// Author: John Kessenich, LunarG // // Visit the nodes in the glslang intermediate tree representation to // translate them to SPIR-V. @@ -44,27 +42,63 @@ #include "GlslangToSpv.h" #include "SpvBuilder.h" namespace spv { - #include "GLSL.std.450.h" + #include "GLSL.std.450.h" + #include "GLSL.ext.KHR.h" + #include "GLSL.ext.EXT.h" +#ifdef AMD_EXTENSIONS + #include "GLSL.ext.AMD.h" +#endif +#ifdef NV_EXTENSIONS + #include "GLSL.ext.NV.h" +#endif } +#ifdef ENABLE_OPT + #include "spirv-tools/optimizer.hpp" + #include "message.h" + #include "SPVRemapper.h" +#endif + +#ifdef ENABLE_OPT +using namespace spvtools; +#endif + // Glslang includes #include "../glslang/MachineIndependent/localintermediate.h" #include "../glslang/MachineIndependent/SymbolTable.h" #include "../glslang/Include/Common.h" +#include "../glslang/Include/revision.h" -#include -#include -#include -#include -#include #include +#include +#include +#include +#include +#include +#include namespace { -// For low-order part of the generator's magic number. Bump up -// when there is a change in the style (e.g., if SSA form changes, -// or a different instruction sequence to do something gets used). -const int GeneratorVersion = 1; +namespace { +class SpecConstantOpModeGuard { +public: + SpecConstantOpModeGuard(spv::Builder* builder) + : builder_(builder) { + previous_flag_ = builder->isInSpecConstCodeGenMode(); + } + ~SpecConstantOpModeGuard() { + previous_flag_ ? builder_->setToSpecConstCodeGenMode() + : builder_->setToNormalCodeGenMode(); + } + void turnOnSpecConstantOpMode() { + builder_->setToSpecConstCodeGenMode(); + } + +private: + spv::Builder* builder_; + bool previous_flag_; +}; +} // // The main holder of information for translating glslang to SPIR-V. @@ -73,8 +107,9 @@ const int GeneratorVersion = 1; // class TGlslangToSpvTraverser : public glslang::TIntermTraverser { public: - TGlslangToSpvTraverser(const glslang::TIntermediate*); - virtual ~TGlslangToSpvTraverser(); + TGlslangToSpvTraverser(unsigned int spvVersion, const glslang::TIntermediate*, spv::SpvBuildLogger* logger, + glslang::SpvOptions& options); + virtual ~TGlslangToSpvTraverser() { } bool visitAggregate(glslang::TVisit, glslang::TIntermAggregate*); bool visitBinary(glslang::TVisit, glslang::TIntermBinary*); @@ -86,24 +121,43 @@ public: bool visitLoop(glslang::TVisit, glslang::TIntermLoop*); bool visitBranch(glslang::TVisit visit, glslang::TIntermBranch*); + void finishSpv(); void dumpSpv(std::vector& out); protected: spv::Decoration TranslateInterpolationDecoration(const glslang::TQualifier& qualifier); - spv::BuiltIn TranslateBuiltInDecoration(glslang::TBuiltInVariable); + spv::Decoration TranslateAuxiliaryStorageDecoration(const glslang::TQualifier& qualifier); + spv::BuiltIn TranslateBuiltInDecoration(glslang::TBuiltInVariable, bool memberDeclaration); spv::ImageFormat TranslateImageFormat(const glslang::TType& type); + spv::SelectionControlMask TranslateSelectionControl(const glslang::TIntermSelection&) const; + spv::SelectionControlMask TranslateSwitchControl(const glslang::TIntermSwitch&) const; + spv::LoopControlMask TranslateLoopControl(const glslang::TIntermLoop&, unsigned int& dependencyLength) const; + spv::StorageClass TranslateStorageClass(const glslang::TType&); spv::Id createSpvVariable(const glslang::TIntermSymbol*); spv::Id getSampledType(const glslang::TSampler&); + spv::Id getInvertedSwizzleType(const glslang::TIntermTyped&); + spv::Id createInvertedSwizzle(spv::Decoration precision, const glslang::TIntermTyped&, spv::Id parentResult); + void convertSwizzle(const glslang::TIntermAggregate&, std::vector& swizzle); spv::Id convertGlslangToSpvType(const glslang::TType& type); spv::Id convertGlslangToSpvType(const glslang::TType& type, glslang::TLayoutPacking, const glslang::TQualifier&); + bool filterMember(const glslang::TType& member); + spv::Id convertGlslangStructToSpvType(const glslang::TType&, const glslang::TTypeList* glslangStruct, + glslang::TLayoutPacking, const glslang::TQualifier&); + void decorateStructType(const glslang::TType&, const glslang::TTypeList* glslangStruct, glslang::TLayoutPacking, + const glslang::TQualifier&, spv::Id); spv::Id makeArraySizeId(const glslang::TArraySizes&, int dim); spv::Id accessChainLoad(const glslang::TType& type); + void accessChainStore(const glslang::TType& type, spv::Id rvalue); + void multiTypeStore(const glslang::TType&, spv::Id rValue); glslang::TLayoutPacking getExplicitLayout(const glslang::TType& type) const; int getArrayStride(const glslang::TType& arrayType, glslang::TLayoutPacking, glslang::TLayoutMatrix); int getMatrixStride(const glslang::TType& matrixType, glslang::TLayoutPacking, glslang::TLayoutMatrix); void updateMemberOffset(const glslang::TType& structType, const glslang::TType& memberType, int& currentOffset, int& nextOffset, glslang::TLayoutPacking, glslang::TLayoutMatrix); + void declareUseOfStructMember(const glslang::TTypeList& members, int glslangMember); - bool isShaderEntrypoint(const glslang::TIntermAggregate* node); + bool isShaderEntryPoint(const glslang::TIntermAggregate* node); + bool writableParam(glslang::TStorageQualifier); + bool originalParam(glslang::TStorageQualifier, const glslang::TType&, bool implicitThisParam); void makeFunctions(const glslang::TIntermSequence&); void makeGlobalInitializers(const glslang::TIntermSequence&); void visitFunctions(const glslang::TIntermSequence&); @@ -113,41 +167,51 @@ protected: spv::Id createImageTextureFunctionCall(glslang::TIntermOperator* node); spv::Id handleUserFunctionCall(const glslang::TIntermAggregate*); - spv::Id createBinaryOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId, spv::Id left, spv::Id right, glslang::TBasicType typeProxy, bool reduceComparison = true); - spv::Id createBinaryMatrixOperation(spv::Op, spv::Decoration precision, spv::Id typeId, spv::Id left, spv::Id right); - spv::Id createUnaryOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId, spv::Id operand,glslang::TBasicType typeProxy); - spv::Id createUnaryMatrixOperation(spv::Op, spv::Decoration precision, spv::Id typeId, spv::Id operand,glslang::TBasicType typeProxy); - spv::Id createConversion(glslang::TOperator op, spv::Decoration precision, spv::Id destTypeId, spv::Id operand); + spv::Id createBinaryOperation(glslang::TOperator op, spv::Decoration precision, spv::Decoration noContraction, spv::Id typeId, spv::Id left, spv::Id right, glslang::TBasicType typeProxy, bool reduceComparison = true); + spv::Id createBinaryMatrixOperation(spv::Op, spv::Decoration precision, spv::Decoration noContraction, spv::Id typeId, spv::Id left, spv::Id right); + spv::Id createUnaryOperation(glslang::TOperator op, spv::Decoration precision, spv::Decoration noContraction, spv::Id typeId, spv::Id operand,glslang::TBasicType typeProxy); + spv::Id createUnaryMatrixOperation(spv::Op op, spv::Decoration precision, spv::Decoration noContraction, spv::Id typeId, spv::Id operand,glslang::TBasicType typeProxy); + spv::Id createConversion(glslang::TOperator op, spv::Decoration precision, spv::Decoration noContraction, spv::Id destTypeId, spv::Id operand, glslang::TBasicType typeProxy); spv::Id makeSmearedConstant(spv::Id constant, int vectorSize); spv::Id createAtomicOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId, std::vector& operands, glslang::TBasicType typeProxy); + spv::Id createInvocationsOperation(glslang::TOperator op, spv::Id typeId, std::vector& operands, glslang::TBasicType typeProxy); + spv::Id CreateInvocationsVectorOperation(spv::Op op, spv::GroupOperation groupOperation, spv::Id typeId, std::vector& operands); spv::Id createMiscOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId, std::vector& operands, glslang::TBasicType typeProxy); - spv::Id createNoArgOperation(glslang::TOperator op); + spv::Id createNoArgOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId); spv::Id getSymbolId(const glslang::TIntermSymbol* node); void addDecoration(spv::Id id, spv::Decoration dec); void addDecoration(spv::Id id, spv::Decoration dec, unsigned value); void addMemberDecoration(spv::Id id, int member, spv::Decoration dec); void addMemberDecoration(spv::Id id, int member, spv::Decoration dec, unsigned value); - spv::Id createSpvSpecConstant(const glslang::TIntermTyped&); - spv::Id createSpvConstant(const glslang::TType& type, const glslang::TConstUnionArray&, int& nextConst, bool specConstant); + spv::Id createSpvConstant(const glslang::TIntermTyped&); + spv::Id createSpvConstantFromConstUnionArray(const glslang::TType& type, const glslang::TConstUnionArray&, int& nextConst, bool specConstant); bool isTrivialLeaf(const glslang::TIntermTyped* node); bool isTrivial(const glslang::TIntermTyped* node); spv::Id createShortCircuit(glslang::TOperator, glslang::TIntermTyped& left, glslang::TIntermTyped& right); +#ifdef AMD_EXTENSIONS + spv::Id getExtBuiltins(const char* name); +#endif + glslang::SpvOptions& options; spv::Function* shaderEntry; + spv::Function* currentFunction; spv::Instruction* entryPoint; int sequenceDepth; + spv::SpvBuildLogger* logger; + // There is a 1:1 mapping between a spv builder and a module; this is thread safe spv::Builder builder; - bool inMain; - bool mainTerminated; + bool inEntryPoint; + bool entryPointTerminated; bool linkageOnly; // true when visiting the set of objects in the AST present only for establishing interface, whether or not they were statically used std::set iOSet; // all input/output variables from either static use or declaration of interface const glslang::TIntermediate* glslangIntermediate; spv::Id stdBuiltins; + std::unordered_map extBuiltinMap; std::unordered_map symbolValues; - std::unordered_set constReadOnlyParameters; // set of formal function parameters that have glslang qualifier constReadOnly, so we know they are not local function "const" that are write-once + std::unordered_set rValueParameters; // set of formal function parameters passed as rValues, rather than a pointer std::unordered_map functionMap; std::unordered_map structMap[glslang::ElpCount][glslang::ElmCount]; std::unordered_map > memberRemapper; // for mapping glslang block indices to spv indices (e.g., due to hidden members) @@ -159,15 +223,22 @@ protected: // // Translate glslang profile to SPIR-V source language. -spv::SourceLanguage TranslateSourceLanguage(EProfile profile) +spv::SourceLanguage TranslateSourceLanguage(glslang::EShSource source, EProfile profile) { - switch (profile) { - case ENoProfile: - case ECoreProfile: - case ECompatibilityProfile: - return spv::SourceLanguageGLSL; - case EEsProfile: - return spv::SourceLanguageESSL; + switch (source) { + case glslang::EShSourceGlsl: + switch (profile) { + case ENoProfile: + case ECoreProfile: + case ECompatibilityProfile: + return spv::SourceLanguageGLSL; + case EEsProfile: + return spv::SourceLanguageESSL; + default: + return spv::SourceLanguageUnknown; + } + case glslang::EShSourceHlsl: + return spv::SourceLanguageHLSL; default: return spv::SourceLanguageUnknown; } @@ -189,36 +260,6 @@ spv::ExecutionModel TranslateExecutionModel(EShLanguage stage) } } -// Translate glslang type to SPIR-V storage class. -spv::StorageClass TranslateStorageClass(const glslang::TType& type) -{ - if (type.getQualifier().isPipeInput()) - return spv::StorageClassInput; - else if (type.getQualifier().isPipeOutput()) - return spv::StorageClassOutput; - else if (type.getQualifier().isUniformOrBuffer()) { - if (type.getQualifier().layoutPushConstant) - return spv::StorageClassPushConstant; - if (type.getBasicType() == glslang::EbtBlock) - return spv::StorageClassUniform; - else if (type.getBasicType() == glslang::EbtAtomicUint) - return spv::StorageClassAtomicCounter; - else - return spv::StorageClassUniformConstant; - // TODO: how are we distuingishing between default and non-default non-writable uniforms? Do default uniforms even exist? - } else { - switch (type.getQualifier().storage) { - case glslang::EvqShared: return spv::StorageClassWorkgroup; break; - case glslang::EvqGlobal: return spv::StorageClassPrivate; - case glslang::EvqConstReadOnly: return spv::StorageClassFunction; - case glslang::EvqTemporary: return spv::StorageClassFunction; - default: - assert(0); - return spv::StorageClassFunction; - } - } -} - // Translate glslang sampler type to SPIR-V dimensionality. spv::Dim TranslateDimensionality(const glslang::TSampler& sampler) { @@ -236,10 +277,10 @@ spv::Dim TranslateDimensionality(const glslang::TSampler& sampler) } } -// Translate glslang type to SPIR-V precision decorations. -spv::Decoration TranslatePrecisionDecoration(const glslang::TType& type) +// Translate glslang precision to SPIR-V precision decorations. +spv::Decoration TranslatePrecisionDecoration(glslang::TPrecisionQualifier glslangPrecision) { - switch (type.getQualifier().precision) { + switch (glslangPrecision) { case glslang::EpqLow: return spv::DecorationRelaxedPrecision; case glslang::EpqMedium: return spv::DecorationRelaxedPrecision; default: @@ -247,13 +288,19 @@ spv::Decoration TranslatePrecisionDecoration(const glslang::TType& type) } } +// Translate glslang type to SPIR-V precision decorations. +spv::Decoration TranslatePrecisionDecoration(const glslang::TType& type) +{ + return TranslatePrecisionDecoration(type.getQualifier().precision); +} + // Translate glslang type to SPIR-V block decorations. -spv::Decoration TranslateBlockDecoration(const glslang::TType& type) +spv::Decoration TranslateBlockDecoration(const glslang::TType& type, bool useStorageBuffer) { if (type.getBasicType() == glslang::EbtBlock) { switch (type.getQualifier().storage) { case glslang::EvqUniform: return spv::DecorationBlock; - case glslang::EvqBuffer: return spv::DecorationBufferBlock; + case glslang::EvqBuffer: return useStorageBuffer ? spv::DecorationBlock : spv::DecorationBufferBlock; case glslang::EvqVaryingIn: return spv::DecorationBlock; case glslang::EvqVaryingOut: return spv::DecorationBlock; default: @@ -262,7 +309,22 @@ spv::Decoration TranslateBlockDecoration(const glslang::TType& type) } } - return (spv::Decoration)spv::BadValue; + return spv::DecorationMax; +} + +// Translate glslang type to SPIR-V memory decorations. +void TranslateMemoryDecoration(const glslang::TQualifier& qualifier, std::vector& memory) +{ + if (qualifier.coherent) + memory.push_back(spv::DecorationCoherent); + if (qualifier.volatil) + memory.push_back(spv::DecorationVolatile); + if (qualifier.restrict) + memory.push_back(spv::DecorationRestrict); + if (qualifier.readonly) + memory.push_back(spv::DecorationNonWritable); + if (qualifier.writeonly) + memory.push_back(spv::DecorationNonReadable); } // Translate glslang type to SPIR-V layout decorations. @@ -276,12 +338,12 @@ spv::Decoration TranslateLayoutDecoration(const glslang::TType& type, glslang::T return spv::DecorationColMajor; default: // opaque layouts don't need a majorness - return (spv::Decoration)spv::BadValue; + return spv::DecorationMax; } } else { switch (type.getBasicType()) { default: - return (spv::Decoration)spv::BadValue; + return spv::DecorationMax; break; case glslang::EbtBlock: switch (type.getQualifier().storage) { @@ -291,42 +353,56 @@ spv::Decoration TranslateLayoutDecoration(const glslang::TType& type, glslang::T case glslang::ElpShared: return spv::DecorationGLSLShared; case glslang::ElpPacked: return spv::DecorationGLSLPacked; default: - return (spv::Decoration)spv::BadValue; + return spv::DecorationMax; } case glslang::EvqVaryingIn: case glslang::EvqVaryingOut: assert(type.getQualifier().layoutPacking == glslang::ElpNone); - return (spv::Decoration)spv::BadValue; + return spv::DecorationMax; default: assert(0); - return (spv::Decoration)spv::BadValue; + return spv::DecorationMax; } } } } // Translate glslang type to SPIR-V interpolation decorations. -// Returns spv::Decoration(spv::BadValue) when no decoration +// Returns spv::DecorationMax when no decoration // should be applied. spv::Decoration TGlslangToSpvTraverser::TranslateInterpolationDecoration(const glslang::TQualifier& qualifier) { - if (qualifier.smooth) { + if (qualifier.smooth) // Smooth decoration doesn't exist in SPIR-V 1.0 - return (spv::Decoration)spv::BadValue; - } - if (qualifier.nopersp) + return spv::DecorationMax; + else if (qualifier.nopersp) return spv::DecorationNoPerspective; - else if (qualifier.patch) - return spv::DecorationPatch; else if (qualifier.flat) return spv::DecorationFlat; +#ifdef AMD_EXTENSIONS + else if (qualifier.explicitInterp) { + builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter); + return spv::DecorationExplicitInterpAMD; + } +#endif + else + return spv::DecorationMax; +} + +// Translate glslang type to SPIR-V auxiliary storage decorations. +// Returns spv::DecorationMax when no decoration +// should be applied. +spv::Decoration TGlslangToSpvTraverser::TranslateAuxiliaryStorageDecoration(const glslang::TQualifier& qualifier) +{ + if (qualifier.patch) + return spv::DecorationPatch; else if (qualifier.centroid) return spv::DecorationCentroid; else if (qualifier.sample) { builder.addCapability(spv::CapabilitySampleRateShading); return spv::DecorationSample; } else - return (spv::Decoration)spv::BadValue; + return spv::DecorationMax; } // If glslang type is invariant, return SPIR-V invariant decoration. @@ -335,35 +411,68 @@ spv::Decoration TranslateInvariantDecoration(const glslang::TQualifier& qualifie if (qualifier.invariant) return spv::DecorationInvariant; else - return (spv::Decoration)spv::BadValue; + return spv::DecorationMax; } -// Translate glslang built-in variable to SPIR-V built in decoration. -spv::BuiltIn TGlslangToSpvTraverser::TranslateBuiltInDecoration(glslang::TBuiltInVariable builtIn) +// If glslang type is noContraction, return SPIR-V NoContraction decoration. +spv::Decoration TranslateNoContractionDecoration(const glslang::TQualifier& qualifier) +{ + if (qualifier.noContraction) + return spv::DecorationNoContraction; + else + return spv::DecorationMax; +} + +// Translate a glslang built-in variable to a SPIR-V built in decoration. Also generate +// associated capabilities when required. For some built-in variables, a capability +// is generated only when using the variable in an executable instruction, but not when +// just declaring a struct member variable with it. This is true for PointSize, +// ClipDistance, and CullDistance. +spv::BuiltIn TGlslangToSpvTraverser::TranslateBuiltInDecoration(glslang::TBuiltInVariable builtIn, bool memberDeclaration) { switch (builtIn) { case glslang::EbvPointSize: - switch (glslangIntermediate->getStage()) { - case EShLangGeometry: - builder.addCapability(spv::CapabilityGeometryPointSize); - break; - case EShLangTessControl: - case EShLangTessEvaluation: - builder.addCapability(spv::CapabilityTessellationPointSize); - break; + // Defer adding the capability until the built-in is actually used. + if (! memberDeclaration) { + switch (glslangIntermediate->getStage()) { + case EShLangGeometry: + builder.addCapability(spv::CapabilityGeometryPointSize); + break; + case EShLangTessControl: + case EShLangTessEvaluation: + builder.addCapability(spv::CapabilityTessellationPointSize); + break; + default: + break; + } } return spv::BuiltInPointSize; + // These *Distance capabilities logically belong here, but if the member is declared and + // then never used, consumers of SPIR-V prefer the capability not be declared. + // They are now generated when used, rather than here when declared. + // Potentially, the specification should be more clear what the minimum + // use needed is to trigger the capability. + // case glslang::EbvClipDistance: - builder.addCapability(spv::CapabilityClipDistance); + if (!memberDeclaration) + builder.addCapability(spv::CapabilityClipDistance); return spv::BuiltInClipDistance; case glslang::EbvCullDistance: - builder.addCapability(spv::CapabilityCullDistance); + if (!memberDeclaration) + builder.addCapability(spv::CapabilityCullDistance); return spv::BuiltInCullDistance; case glslang::EbvViewportIndex: - // TODO: builder.addCapability(spv::CapabilityMultiViewport); + builder.addCapability(spv::CapabilityMultiViewport); + if (glslangIntermediate->getStage() == EShLangVertex || + glslangIntermediate->getStage() == EShLangTessControl || + glslangIntermediate->getStage() == EShLangTessEvaluation) { + + builder.addExtension(spv::E_SPV_EXT_shader_viewport_index_layer); + builder.addCapability(spv::CapabilityShaderViewportIndexLayerEXT); + } return spv::BuiltInViewportIndex; case glslang::EbvSampleId: @@ -375,23 +484,51 @@ spv::BuiltIn TGlslangToSpvTraverser::TranslateBuiltInDecoration(glslang::TBuiltI return spv::BuiltInSamplePosition; case glslang::EbvSampleMask: - builder.addCapability(spv::CapabilitySampleRateShading); return spv::BuiltInSampleMask; + case glslang::EbvLayer: + builder.addCapability(spv::CapabilityGeometry); + if (glslangIntermediate->getStage() == EShLangVertex || + glslangIntermediate->getStage() == EShLangTessControl || + glslangIntermediate->getStage() == EShLangTessEvaluation) { + + builder.addExtension(spv::E_SPV_EXT_shader_viewport_index_layer); + builder.addCapability(spv::CapabilityShaderViewportIndexLayerEXT); + } + return spv::BuiltInLayer; + case glslang::EbvPosition: return spv::BuiltInPosition; case glslang::EbvVertexId: return spv::BuiltInVertexId; case glslang::EbvInstanceId: return spv::BuiltInInstanceId; case glslang::EbvVertexIndex: return spv::BuiltInVertexIndex; case glslang::EbvInstanceIndex: return spv::BuiltInInstanceIndex; + case glslang::EbvBaseVertex: + builder.addExtension(spv::E_SPV_KHR_shader_draw_parameters); + builder.addCapability(spv::CapabilityDrawParameters); + return spv::BuiltInBaseVertex; + case glslang::EbvBaseInstance: + builder.addExtension(spv::E_SPV_KHR_shader_draw_parameters); + builder.addCapability(spv::CapabilityDrawParameters); + return spv::BuiltInBaseInstance; + case glslang::EbvDrawId: - // TODO: Add SPIR-V builtin ID. - spv::MissingFunctionality("Draw parameters"); - return (spv::BuiltIn)spv::BadValue; - case glslang::EbvPrimitiveId: return spv::BuiltInPrimitiveId; + builder.addExtension(spv::E_SPV_KHR_shader_draw_parameters); + builder.addCapability(spv::CapabilityDrawParameters); + return spv::BuiltInDrawIndex; + + case glslang::EbvPrimitiveId: + if (glslangIntermediate->getStage() == EShLangFragment) + builder.addCapability(spv::CapabilityGeometry); + return spv::BuiltInPrimitiveId; + + case glslang::EbvFragStencilRef: + builder.addExtension(spv::E_SPV_EXT_shader_stencil_export); + builder.addCapability(spv::CapabilityStencilExportEXT); + return spv::BuiltInFragStencilRefEXT; + case glslang::EbvInvocationId: return spv::BuiltInInvocationId; - case glslang::EbvLayer: return spv::BuiltInLayer; case glslang::EbvTessLevelInner: return spv::BuiltInTessLevelInner; case glslang::EbvTessLevelOuter: return spv::BuiltInTessLevelOuter; case glslang::EbvTessCoord: return spv::BuiltInTessCoord; @@ -407,7 +544,120 @@ spv::BuiltIn TGlslangToSpvTraverser::TranslateBuiltInDecoration(glslang::TBuiltI case glslang::EbvLocalInvocationId: return spv::BuiltInLocalInvocationId; case glslang::EbvLocalInvocationIndex: return spv::BuiltInLocalInvocationIndex; case glslang::EbvGlobalInvocationId: return spv::BuiltInGlobalInvocationId; - default: return (spv::BuiltIn)spv::BadValue; + + case glslang::EbvSubGroupSize: + builder.addExtension(spv::E_SPV_KHR_shader_ballot); + builder.addCapability(spv::CapabilitySubgroupBallotKHR); + return spv::BuiltInSubgroupSize; + + case glslang::EbvSubGroupInvocation: + builder.addExtension(spv::E_SPV_KHR_shader_ballot); + builder.addCapability(spv::CapabilitySubgroupBallotKHR); + return spv::BuiltInSubgroupLocalInvocationId; + + case glslang::EbvSubGroupEqMask: + builder.addExtension(spv::E_SPV_KHR_shader_ballot); + builder.addCapability(spv::CapabilitySubgroupBallotKHR); + return spv::BuiltInSubgroupEqMaskKHR; + + case glslang::EbvSubGroupGeMask: + builder.addExtension(spv::E_SPV_KHR_shader_ballot); + builder.addCapability(spv::CapabilitySubgroupBallotKHR); + return spv::BuiltInSubgroupGeMaskKHR; + + case glslang::EbvSubGroupGtMask: + builder.addExtension(spv::E_SPV_KHR_shader_ballot); + builder.addCapability(spv::CapabilitySubgroupBallotKHR); + return spv::BuiltInSubgroupGtMaskKHR; + + case glslang::EbvSubGroupLeMask: + builder.addExtension(spv::E_SPV_KHR_shader_ballot); + builder.addCapability(spv::CapabilitySubgroupBallotKHR); + return spv::BuiltInSubgroupLeMaskKHR; + + case glslang::EbvSubGroupLtMask: + builder.addExtension(spv::E_SPV_KHR_shader_ballot); + builder.addCapability(spv::CapabilitySubgroupBallotKHR); + return spv::BuiltInSubgroupLtMaskKHR; + +#ifdef AMD_EXTENSIONS + case glslang::EbvBaryCoordNoPersp: + builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter); + return spv::BuiltInBaryCoordNoPerspAMD; + + case glslang::EbvBaryCoordNoPerspCentroid: + builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter); + return spv::BuiltInBaryCoordNoPerspCentroidAMD; + + case glslang::EbvBaryCoordNoPerspSample: + builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter); + return spv::BuiltInBaryCoordNoPerspSampleAMD; + + case glslang::EbvBaryCoordSmooth: + builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter); + return spv::BuiltInBaryCoordSmoothAMD; + + case glslang::EbvBaryCoordSmoothCentroid: + builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter); + return spv::BuiltInBaryCoordSmoothCentroidAMD; + + case glslang::EbvBaryCoordSmoothSample: + builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter); + return spv::BuiltInBaryCoordSmoothSampleAMD; + + case glslang::EbvBaryCoordPullModel: + builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter); + return spv::BuiltInBaryCoordPullModelAMD; +#endif + + case glslang::EbvDeviceIndex: + builder.addExtension(spv::E_SPV_KHR_device_group); + builder.addCapability(spv::CapabilityDeviceGroup); + return spv::BuiltInDeviceIndex; + + case glslang::EbvViewIndex: + builder.addExtension(spv::E_SPV_KHR_multiview); + builder.addCapability(spv::CapabilityMultiView); + return spv::BuiltInViewIndex; + +#ifdef NV_EXTENSIONS + case glslang::EbvViewportMaskNV: + if (!memberDeclaration) { + builder.addExtension(spv::E_SPV_NV_viewport_array2); + builder.addCapability(spv::CapabilityShaderViewportMaskNV); + } + return spv::BuiltInViewportMaskNV; + case glslang::EbvSecondaryPositionNV: + if (!memberDeclaration) { + builder.addExtension(spv::E_SPV_NV_stereo_view_rendering); + builder.addCapability(spv::CapabilityShaderStereoViewNV); + } + return spv::BuiltInSecondaryPositionNV; + case glslang::EbvSecondaryViewportMaskNV: + if (!memberDeclaration) { + builder.addExtension(spv::E_SPV_NV_stereo_view_rendering); + builder.addCapability(spv::CapabilityShaderStereoViewNV); + } + return spv::BuiltInSecondaryViewportMaskNV; + case glslang::EbvPositionPerViewNV: + if (!memberDeclaration) { + builder.addExtension(spv::E_SPV_NVX_multiview_per_view_attributes); + builder.addCapability(spv::CapabilityPerViewAttributesNV); + } + return spv::BuiltInPositionPerViewNV; + case glslang::EbvViewportMaskPerViewNV: + if (!memberDeclaration) { + builder.addExtension(spv::E_SPV_NVX_multiview_per_view_attributes); + builder.addCapability(spv::CapabilityPerViewAttributesNV); + } + return spv::BuiltInViewportMaskPerViewNV; + case glslang::EbvFragFullyCoveredNV: + builder.addExtension(spv::E_SPV_EXT_fragment_fully_covered); + builder.addCapability(spv::CapabilityFragmentFullyCoveredEXT); + return spv::BuiltInFullyCoveredEXT; +#endif + default: + return spv::BuiltInMax; } } @@ -495,17 +745,97 @@ spv::ImageFormat TGlslangToSpvTraverser::TranslateImageFormat(const glslang::TTy case glslang::ElfRg8ui: return spv::ImageFormatRg8ui; case glslang::ElfR16ui: return spv::ImageFormatR16ui; case glslang::ElfR8ui: return spv::ImageFormatR8ui; - default: return (spv::ImageFormat)spv::BadValue; + default: return spv::ImageFormatMax; } } -// Return whether or not the given type is something that should be tied to a +spv::SelectionControlMask TGlslangToSpvTraverser::TranslateSelectionControl(const glslang::TIntermSelection& selectionNode) const +{ + if (selectionNode.getFlatten()) + return spv::SelectionControlFlattenMask; + if (selectionNode.getDontFlatten()) + return spv::SelectionControlDontFlattenMask; + return spv::SelectionControlMaskNone; +} + +spv::SelectionControlMask TGlslangToSpvTraverser::TranslateSwitchControl(const glslang::TIntermSwitch& switchNode) const +{ + if (switchNode.getFlatten()) + return spv::SelectionControlFlattenMask; + if (switchNode.getDontFlatten()) + return spv::SelectionControlDontFlattenMask; + return spv::SelectionControlMaskNone; +} + +// return a non-0 dependency if the dependency argument must be set +spv::LoopControlMask TGlslangToSpvTraverser::TranslateLoopControl(const glslang::TIntermLoop& loopNode, + unsigned int& dependencyLength) const +{ + spv::LoopControlMask control = spv::LoopControlMaskNone; + + if (loopNode.getDontUnroll()) + control = control | spv::LoopControlDontUnrollMask; + if (loopNode.getUnroll()) + control = control | spv::LoopControlUnrollMask; + if (loopNode.getLoopDependency() == glslang::TIntermLoop::dependencyInfinite) + control = control | spv::LoopControlDependencyInfiniteMask; + else if (loopNode.getLoopDependency() > 0) { + control = control | spv::LoopControlDependencyLengthMask; + dependencyLength = loopNode.getLoopDependency(); + } + + return control; +} + +// Translate glslang type to SPIR-V storage class. +spv::StorageClass TGlslangToSpvTraverser::TranslateStorageClass(const glslang::TType& type) +{ + if (type.getQualifier().isPipeInput()) + return spv::StorageClassInput; + if (type.getQualifier().isPipeOutput()) + return spv::StorageClassOutput; + + if (glslangIntermediate->getSource() != glslang::EShSourceHlsl || + type.getQualifier().storage == glslang::EvqUniform) { + if (type.getBasicType() == glslang::EbtAtomicUint) + return spv::StorageClassAtomicCounter; + if (type.containsOpaque()) + return spv::StorageClassUniformConstant; + } + + if (glslangIntermediate->usingStorageBuffer() && type.getQualifier().storage == glslang::EvqBuffer) { + builder.addExtension(spv::E_SPV_KHR_storage_buffer_storage_class); + return spv::StorageClassStorageBuffer; + } + + if (type.getQualifier().isUniformOrBuffer()) { + if (type.getQualifier().layoutPushConstant) + return spv::StorageClassPushConstant; + if (type.getBasicType() == glslang::EbtBlock) + return spv::StorageClassUniform; + return spv::StorageClassUniformConstant; + } + + switch (type.getQualifier().storage) { + case glslang::EvqShared: return spv::StorageClassWorkgroup; + case glslang::EvqGlobal: return spv::StorageClassPrivate; + case glslang::EvqConstReadOnly: return spv::StorageClassFunction; + case glslang::EvqTemporary: return spv::StorageClassFunction; + default: + assert(0); + break; + } + + return spv::StorageClassFunction; +} + +// Return whether or not the given type is something that should be tied to a // descriptor set. bool IsDescriptorResource(const glslang::TType& type) { - // uniform and buffer blocks are included + // uniform and buffer blocks are included, unless it is a push_constant if (type.getBasicType() == glslang::EbtBlock) - return type.getQualifier().isUniformOrBuffer(); + return type.getQualifier().isUniformOrBuffer() && ! type.getQualifier().layoutPushConstant; // non block... // basically samplerXXX/subpass/sampler/texture are all included @@ -527,6 +857,10 @@ void InheritQualifiers(glslang::TQualifier& child, const glslang::TQualifier& pa child.invariant = true; if (parent.nopersp) child.nopersp = true; +#ifdef AMD_EXTENSIONS + if (parent.explicitInterp) + child.explicitInterp = true; +#endif if (parent.flat) child.flat = true; if (parent.centroid) @@ -535,37 +869,77 @@ void InheritQualifiers(glslang::TQualifier& child, const glslang::TQualifier& pa child.patch = true; if (parent.sample) child.sample = true; - - child.layoutLocation = parent.layoutLocation; + if (parent.coherent) + child.coherent = true; + if (parent.volatil) + child.volatil = true; + if (parent.restrict) + child.restrict = true; + if (parent.readonly) + child.readonly = true; + if (parent.writeonly) + child.writeonly = true; } -bool HasNonLayoutQualifiers(const glslang::TQualifier& qualifier) +bool HasNonLayoutQualifiers(const glslang::TType& type, const glslang::TQualifier& qualifier) { // This should list qualifiers that simultaneous satisfy: - // - struct members can inherit from a struct declaration - // - effect decorations on the struct members (note smooth does not, and expecting something like volatile to effect the whole object) + // - struct members might inherit from a struct declaration + // (note that non-block structs don't explicitly inherit, + // only implicitly, meaning no decoration involved) + // - affect decorations on the struct members + // (note smooth does not, and expecting something like volatile + // to effect the whole object) // - are not part of the offset/st430/etc or row/column-major layout - return qualifier.invariant || qualifier.nopersp || qualifier.flat || qualifier.centroid || qualifier.patch || qualifier.sample || qualifier.hasLocation(); + return qualifier.invariant || (qualifier.hasLocation() && type.getBasicType() == glslang::EbtBlock); } // // Implement the TGlslangToSpvTraverser class. // -TGlslangToSpvTraverser::TGlslangToSpvTraverser(const glslang::TIntermediate* glslangIntermediate) - : TIntermTraverser(true, false, true), shaderEntry(0), sequenceDepth(0), - builder((glslang::GetKhronosToolId() << 16) | GeneratorVersion), - inMain(false), mainTerminated(false), linkageOnly(false), +TGlslangToSpvTraverser::TGlslangToSpvTraverser(unsigned int spvVersion, const glslang::TIntermediate* glslangIntermediate, + spv::SpvBuildLogger* buildLogger, glslang::SpvOptions& options) + : TIntermTraverser(true, false, true), + options(options), + shaderEntry(nullptr), currentFunction(nullptr), + sequenceDepth(0), logger(buildLogger), + builder(spvVersion, (glslang::GetKhronosToolId() << 16) | glslang::GetSpirvGeneratorVersion(), logger), + inEntryPoint(false), entryPointTerminated(false), linkageOnly(false), glslangIntermediate(glslangIntermediate) { spv::ExecutionModel executionModel = TranslateExecutionModel(glslangIntermediate->getStage()); builder.clearAccessChain(); - builder.setSource(TranslateSourceLanguage(glslangIntermediate->getProfile()), glslangIntermediate->getVersion()); + builder.setSource(TranslateSourceLanguage(glslangIntermediate->getSource(), glslangIntermediate->getProfile()), + glslangIntermediate->getVersion()); + + if (options.generateDebugInfo) { + builder.setEmitOpLines(); + builder.setSourceFile(glslangIntermediate->getSourceFile()); + + // Set the source shader's text. If for SPV version 1.0, include + // a preamble in comments stating the OpModuleProcessed instructions. + // Otherwise, emit those as actual instructions. + std::string text; + const std::vector& processes = glslangIntermediate->getProcesses(); + for (int p = 0; p < (int)processes.size(); ++p) { + if (glslangIntermediate->getSpv().spv < 0x00010100) { + text.append("// OpModuleProcessed "); + text.append(processes[p]); + text.append("\n"); + } else + builder.addModuleProcessed(processes[p]); + } + if (glslangIntermediate->getSpv().spv < 0x00010100 && (int)processes.size() > 0) + text.append("#line 1\n"); + text.append(glslangIntermediate->getSourceText()); + builder.setSourceText(text); + } stdBuiltins = builder.import("GLSL.std.450"); builder.setMemoryModel(spv::AddressingModelLogical, spv::MemoryModelGLSL450); - shaderEntry = builder.makeMain(); - entryPoint = builder.addEntryPoint(executionModel, shaderEntry, "main"); + shaderEntry = builder.makeEntryPoint(glslangIntermediate->getEntryPointName().c_str()); + entryPoint = builder.addEntryPoint(executionModel, shaderEntry, glslangIntermediate->getEntryPointName().c_str()); // Add the source extensions const auto& sourceExtensions = glslangIntermediate->getRequestedExtensions(); @@ -585,37 +959,43 @@ TGlslangToSpvTraverser::TGlslangToSpvTraverser(const glslang::TIntermediate* gls builder.addCapability(spv::CapabilityShader); break; + case EShLangTessEvaluation: case EShLangTessControl: builder.addCapability(spv::CapabilityTessellation); - builder.addExecutionMode(shaderEntry, spv::ExecutionModeOutputVertices, glslangIntermediate->getVertices()); - break; - case EShLangTessEvaluation: - builder.addCapability(spv::CapabilityTessellation); - switch (glslangIntermediate->getInputPrimitive()) { + glslang::TLayoutGeometry primitive; + + if (glslangIntermediate->getStage() == EShLangTessControl) { + builder.addExecutionMode(shaderEntry, spv::ExecutionModeOutputVertices, glslangIntermediate->getVertices()); + primitive = glslangIntermediate->getOutputPrimitive(); + } else { + primitive = glslangIntermediate->getInputPrimitive(); + } + + switch (primitive) { case glslang::ElgTriangles: mode = spv::ExecutionModeTriangles; break; case glslang::ElgQuads: mode = spv::ExecutionModeQuads; break; case glslang::ElgIsolines: mode = spv::ExecutionModeIsolines; break; - default: mode = spv::BadValue; break; + default: mode = spv::ExecutionModeMax; break; } - if (mode != spv::BadValue) + if (mode != spv::ExecutionModeMax) builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode); switch (glslangIntermediate->getVertexSpacing()) { case glslang::EvsEqual: mode = spv::ExecutionModeSpacingEqual; break; case glslang::EvsFractionalEven: mode = spv::ExecutionModeSpacingFractionalEven; break; case glslang::EvsFractionalOdd: mode = spv::ExecutionModeSpacingFractionalOdd; break; - default: mode = spv::BadValue; break; + default: mode = spv::ExecutionModeMax; break; } - if (mode != spv::BadValue) + if (mode != spv::ExecutionModeMax) builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode); switch (glslangIntermediate->getVertexOrder()) { case glslang::EvoCw: mode = spv::ExecutionModeVertexOrderCw; break; case glslang::EvoCcw: mode = spv::ExecutionModeVertexOrderCcw; break; - default: mode = spv::BadValue; break; + default: mode = spv::ExecutionModeMax; break; } - if (mode != spv::BadValue) + if (mode != spv::ExecutionModeMax) builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode); if (glslangIntermediate->getPointMode()) @@ -630,9 +1010,9 @@ TGlslangToSpvTraverser::TGlslangToSpvTraverser(const glslang::TIntermediate* gls case glslang::ElgLinesAdjacency: mode = spv::ExecutionModeInputLinesAdjacency; break; case glslang::ElgTriangles: mode = spv::ExecutionModeTriangles; break; case glslang::ElgTrianglesAdjacency: mode = spv::ExecutionModeInputTrianglesAdjacency; break; - default: mode = spv::BadValue; break; + default: mode = spv::ExecutionModeMax; break; } - if (mode != spv::BadValue) + if (mode != spv::ExecutionModeMax) builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode); builder.addExecutionMode(shaderEntry, spv::ExecutionModeInvocations, glslangIntermediate->getInvocations()); @@ -641,9 +1021,9 @@ TGlslangToSpvTraverser::TGlslangToSpvTraverser(const glslang::TIntermediate* gls case glslang::ElgPoints: mode = spv::ExecutionModeOutputPoints; break; case glslang::ElgLineStrip: mode = spv::ExecutionModeOutputLineStrip; break; case glslang::ElgTriangleStrip: mode = spv::ExecutionModeOutputTriangleStrip; break; - default: mode = spv::BadValue; break; + default: mode = spv::ExecutionModeMax; break; } - if (mode != spv::BadValue) + if (mode != spv::ExecutionModeMax) builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode); builder.addExecutionMode(shaderEntry, spv::ExecutionModeOutputVertices, glslangIntermediate->getVertices()); break; @@ -661,12 +1041,18 @@ TGlslangToSpvTraverser::TGlslangToSpvTraverser(const glslang::TIntermediate* gls if (glslangIntermediate->getEarlyFragmentTests()) builder.addExecutionMode(shaderEntry, spv::ExecutionModeEarlyFragmentTests); + if (glslangIntermediate->getPostDepthCoverage()) { + builder.addCapability(spv::CapabilitySampleMaskPostDepthCoverage); + builder.addExecutionMode(shaderEntry, spv::ExecutionModePostDepthCoverage); + builder.addExtension(spv::E_SPV_KHR_post_depth_coverage); + } + switch(glslangIntermediate->getDepth()) { case glslang::EldGreater: mode = spv::ExecutionModeDepthGreater; break; case glslang::EldLess: mode = spv::ExecutionModeDepthLess; break; - default: mode = spv::BadValue; break; + default: mode = spv::ExecutionModeMax; break; } - if (mode != spv::BadValue) + if (mode != spv::ExecutionModeMax) builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode); if (glslangIntermediate->getDepth() != glslang::EldUnchanged && glslangIntermediate->isDepthReplacing()) @@ -683,26 +1069,27 @@ TGlslangToSpvTraverser::TGlslangToSpvTraverser(const glslang::TIntermediate* gls default: break; } - } -// Finish everything and dump -void TGlslangToSpvTraverser::dumpSpv(std::vector& out) +// Finish creating SPV, after the traversal is complete. +void TGlslangToSpvTraverser::finishSpv() { - // finish off the entry-point SPV instruction by adding the Input/Output - for (auto it : iOSet) - entryPoint->addIdOperand(it); - - builder.dump(out); -} - -TGlslangToSpvTraverser::~TGlslangToSpvTraverser() -{ - if (! mainTerminated) { - spv::Block* lastMainBlock = shaderEntry->getLastBlock(); - builder.setBuildPoint(lastMainBlock); + if (! entryPointTerminated) { + builder.setBuildPoint(shaderEntry->getLastBlock()); builder.leaveFunction(); } + + // finish off the entry-point SPV instruction by adding the Input/Output + for (auto it = iOSet.cbegin(); it != iOSet.cend(); ++it) + entryPoint->addIdOperand(*it); + + builder.eliminateDeadDecorations(); +} + +// Write the SPV into 'out'. +void TGlslangToSpvTraverser::dumpSpv(std::vector& out) +{ + builder.dump(out); } // @@ -714,7 +1101,7 @@ TGlslangToSpvTraverser::~TGlslangToSpvTraverser() // // -// Symbols can turn into +// Symbols can turn into // - uniform/input reads // - output writes // - complex lvalue base setups: foo.bar[3].... , where we see foo and start up an access chain @@ -722,6 +1109,10 @@ TGlslangToSpvTraverser::~TGlslangToSpvTraverser() // void TGlslangToSpvTraverser::visitSymbol(glslang::TIntermSymbol* symbol) { + SpecConstantOpModeGuard spec_constant_op_mode_setter(&builder); + if (symbol->getType().getQualifier().isSpecConstant()) + spec_constant_op_mode_setter.turnOnSpecConstantOpMode(); + // getSymbolId() will set up all the IO decorations on the first call. // Formal function parameters were mapped during makeFunctions(). spv::Id id = getSymbolId(symbol); @@ -729,8 +1120,10 @@ void TGlslangToSpvTraverser::visitSymbol(glslang::TIntermSymbol* symbol) // Include all "static use" and "linkage only" interface variables on the OpEntryPoint instruction if (builder.isPointer(id)) { spv::StorageClass sc = builder.getStorageClass(id); - if (sc == spv::StorageClassInput || sc == spv::StorageClassOutput) - iOSet.insert(id); + if (sc == spv::StorageClassInput || sc == spv::StorageClassOutput) { + if (!symbol->getType().isStruct() || symbol->getType().getStruct()->size() > 0) + iOSet.insert(id); + } } // Only process non-linkage-only nodes for generating actual static uses @@ -744,13 +1137,12 @@ void TGlslangToSpvTraverser::visitSymbol(glslang::TIntermSymbol* symbol) // For now, we consider all user variables as being in memory, so they are pointers, // except for - // A) "const in" arguments to a function, which are an intermediate object. + // A) R-Value arguments to a function, which are an intermediate object. // See comments in handleUserFunctionCall(). - // B) Specialization constants (normal constant don't even come in as a variable), + // B) Specialization constants (normal constants don't even come in as a variable), // These are also pure R-values. glslang::TQualifier qualifier = symbol->getQualifier(); - if ((qualifier.storage == glslang::EvqConstReadOnly && constReadOnlyParameters.find(symbol->getId()) != constReadOnlyParameters.end()) || - qualifier.isSpecConstant()) + if (qualifier.isSpecConstant() || rValueParameters.find(symbol->getId()) != rValueParameters.end()) builder.setAccessChainRValue(id); else builder.setAccessChainLValue(id); @@ -759,6 +1151,12 @@ void TGlslangToSpvTraverser::visitSymbol(glslang::TIntermSymbol* symbol) bool TGlslangToSpvTraverser::visitBinary(glslang::TVisit /* visit */, glslang::TIntermBinary* node) { + builder.setLine(node->getLoc().line); + + SpecConstantOpModeGuard spec_constant_op_mode_setter(&builder); + if (node->getType().getQualifier().isSpecConstant()) + spec_constant_op_mode_setter.turnOnSpecConstantOpMode(); + // First, handle special cases switch (node->getOp()) { case glslang::EOpAssign: @@ -797,7 +1195,8 @@ bool TGlslangToSpvTraverser::visitBinary(glslang::TVisit /* visit */, glslang::T spv::Id leftRValue = accessChainLoad(node->getLeft()->getType()); // do the operation - rValue = createBinaryOperation(node->getOp(), TranslatePrecisionDecoration(node->getType()), + rValue = createBinaryOperation(node->getOp(), TranslatePrecisionDecoration(node->getOperationPrecision()), + TranslateNoContractionDecoration(node->getType().getQualifier()), convertGlslangToSpvType(node->getType()), leftRValue, rValue, node->getType().getBasicType()); @@ -807,7 +1206,7 @@ bool TGlslangToSpvTraverser::visitBinary(glslang::TVisit /* visit */, glslang::T // store the result builder.setAccessChain(lValue); - builder.accessChainStore(rValue); + multiTypeStore(node->getType(), rValue); // assignments are expressions having an rValue after they are evaluated... builder.clearAccessChain(); @@ -822,26 +1221,34 @@ bool TGlslangToSpvTraverser::visitBinary(glslang::TVisit /* visit */, glslang::T // Add the next element in the chain - int index = node->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst(); - if (node->getLeft()->getBasicType() == glslang::EbtBlock && node->getOp() == glslang::EOpIndexDirectStruct) { - // This may be, e.g., an anonymous block-member selection, which generally need - // index remapping due to hidden members in anonymous blocks. - std::vector& remapper = memberRemapper[node->getLeft()->getType().getStruct()]; - assert(remapper.size() > 0); - index = remapper[index]; - } - + const int glslangIndex = node->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst(); if (! node->getLeft()->getType().isArray() && node->getLeft()->getType().isVector() && node->getOp() == glslang::EOpIndexDirect) { // This is essentially a hard-coded vector swizzle of size 1, // so short circuit the access-chain stuff with a swizzle. std::vector swizzle; - swizzle.push_back(node->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst()); + swizzle.push_back(glslangIndex); builder.accessChainPushSwizzle(swizzle, convertGlslangToSpvType(node->getLeft()->getType())); } else { + int spvIndex = glslangIndex; + if (node->getLeft()->getBasicType() == glslang::EbtBlock && + node->getOp() == glslang::EOpIndexDirectStruct) + { + // This may be, e.g., an anonymous block-member selection, which generally need + // index remapping due to hidden members in anonymous blocks. + std::vector& remapper = memberRemapper[node->getLeft()->getType().getStruct()]; + assert(remapper.size() > 0); + spvIndex = remapper[glslangIndex]; + } + // normal case for indexing array or structure or block - builder.accessChainPush(builder.makeIntConstant(index)); + builder.accessChainPush(builder.makeIntConstant(spvIndex)); + + // Add capabilities here for accessing PointSize and clip/cull distance. + // We have deferred generation of associated capabilities until now. + if (node->getLeft()->getType().isStruct() && ! node->getLeft()->getType().isArray()) + declareUseOfStructMember(*(node->getLeft()->getType().getStruct()), glslangIndex); } } return false; @@ -876,13 +1283,14 @@ bool TGlslangToSpvTraverser::visitBinary(glslang::TVisit /* visit */, glslang::T case glslang::EOpVectorSwizzle: { node->getLeft()->traverse(this); - glslang::TIntermSequence& swizzleSequence = node->getRight()->getAsAggregate()->getSequence(); std::vector swizzle; - for (int i = 0; i < (int)swizzleSequence.size(); ++i) - swizzle.push_back(swizzleSequence[i]->getAsConstantUnion()->getConstArray()[0].getIConst()); + convertSwizzle(*node->getRight()->getAsAggregate(), swizzle); builder.accessChainPushSwizzle(swizzle, convertGlslangToSpvType(node->getLeft()->getType())); } return false; + case glslang::EOpMatrixSwizzle: + logger->missingFunctionality("matrix swizzle"); + return true; case glslang::EOpLogicalOr: case glslang::EOpLogicalAnd: { @@ -915,13 +1323,14 @@ bool TGlslangToSpvTraverser::visitBinary(glslang::TVisit /* visit */, glslang::T spv::Id right = accessChainLoad(node->getRight()->getType()); // get result - spv::Id result = createBinaryOperation(node->getOp(), TranslatePrecisionDecoration(node->getType()), + spv::Id result = createBinaryOperation(node->getOp(), TranslatePrecisionDecoration(node->getOperationPrecision()), + TranslateNoContractionDecoration(node->getType().getQualifier()), convertGlslangToSpvType(node->getType()), left, right, node->getLeft()->getType().getBasicType()); builder.clearAccessChain(); if (! result) { - spv::MissingFunctionality("unknown glslang binary operation"); + logger->missingFunctionality("unknown glslang binary operation"); return true; // pick up a child as the place-holder result } else { builder.setAccessChainRValue(result); @@ -931,6 +1340,12 @@ bool TGlslangToSpvTraverser::visitBinary(glslang::TVisit /* visit */, glslang::T bool TGlslangToSpvTraverser::visitUnary(glslang::TVisit /* visit */, glslang::TIntermUnary* node) { + builder.setLine(node->getLoc().line); + + SpecConstantOpModeGuard spec_constant_op_mode_setter(&builder); + if (node->getType().getQualifier().isSpecConstant()) + spec_constant_op_mode_setter.turnOnSpecConstantOpMode(); + spv::Id result = spv::NoResult; // try texturing first @@ -964,8 +1379,18 @@ bool TGlslangToSpvTraverser::visitUnary(glslang::TVisit /* visit */, glslang::TI // Start by evaluating the operand + // Does it need a swizzle inversion? If so, evaluation is inverted; + // operate first on the swizzle base, then apply the swizzle. + spv::Id invertedType = spv::NoType; + auto resultType = [&invertedType, &node, this](){ return invertedType != spv::NoType ? invertedType : convertGlslangToSpvType(node->getType()); }; + if (node->getOp() == glslang::EOpInterpolateAtCentroid) + invertedType = getInvertedSwizzleType(*node->getOperand()); + builder.clearAccessChain(); - node->getOperand()->traverse(this); + if (invertedType != spv::NoType) + node->getOperand()->getAsBinaryNode()->getLeft()->traverse(this); + else + node->getOperand()->traverse(this); spv::Id operand = spv::NoResult; @@ -977,17 +1402,21 @@ bool TGlslangToSpvTraverser::visitUnary(glslang::TVisit /* visit */, glslang::TI else operand = accessChainLoad(node->getOperand()->getType()); - spv::Decoration precision = TranslatePrecisionDecoration(node->getType()); + spv::Decoration precision = TranslatePrecisionDecoration(node->getOperationPrecision()); + spv::Decoration noContraction = TranslateNoContractionDecoration(node->getType().getQualifier()); // it could be a conversion if (! result) - result = createConversion(node->getOp(), precision, convertGlslangToSpvType(node->getType()), operand); + result = createConversion(node->getOp(), precision, noContraction, resultType(), operand, node->getOperand()->getBasicType()); // if not, then possibly an operation if (! result) - result = createUnaryOperation(node->getOp(), precision, convertGlslangToSpvType(node->getType()), operand, node->getOperand()->getBasicType()); + result = createUnaryOperation(node->getOp(), precision, noContraction, resultType(), operand, node->getOperand()->getBasicType()); if (result) { + if (invertedType) + result = createInvertedSwizzle(precision, *node->getOperand(), result); + builder.clearAccessChain(); builder.setAccessChainRValue(result); @@ -1002,9 +1431,23 @@ bool TGlslangToSpvTraverser::visitUnary(glslang::TVisit /* visit */, glslang::TI case glslang::EOpPreDecrement: { // we need the integer value "1" or the floating point "1.0" to add/subtract - spv::Id one = node->getBasicType() == glslang::EbtFloat ? - builder.makeFloatConstant(1.0F) : - builder.makeIntConstant(1); + spv::Id one = 0; + if (node->getBasicType() == glslang::EbtFloat) + one = builder.makeFloatConstant(1.0F); + else if (node->getBasicType() == glslang::EbtDouble) + one = builder.makeDoubleConstant(1.0); +#ifdef AMD_EXTENSIONS + else if (node->getBasicType() == glslang::EbtFloat16) + one = builder.makeFloat16Constant(1.0F); +#endif + else if (node->getBasicType() == glslang::EbtInt64 || node->getBasicType() == glslang::EbtUint64) + one = builder.makeInt64Constant(1); +#ifdef AMD_EXTENSIONS + else if (node->getBasicType() == glslang::EbtInt16 || node->getBasicType() == glslang::EbtUint16) + one = builder.makeInt16Constant(1); +#endif + else + one = builder.makeIntConstant(1); glslang::TOperator op; if (node->getOp() == glslang::EOpPreIncrement || node->getOp() == glslang::EOpPostIncrement) @@ -1012,9 +1455,10 @@ bool TGlslangToSpvTraverser::visitUnary(glslang::TVisit /* visit */, glslang::TI else op = glslang::EOpSub; - spv::Id result = createBinaryOperation(op, TranslatePrecisionDecoration(node->getType()), - convertGlslangToSpvType(node->getType()), operand, one, - node->getType().getBasicType()); + spv::Id result = createBinaryOperation(op, precision, + TranslateNoContractionDecoration(node->getType().getQualifier()), + convertGlslangToSpvType(node->getType()), operand, one, + node->getType().getBasicType()); assert(result != spv::NoResult); // The result of operation is always stored, but conditionally the @@ -1038,14 +1482,20 @@ bool TGlslangToSpvTraverser::visitUnary(glslang::TVisit /* visit */, glslang::TI return false; default: - spv::MissingFunctionality("unknown glslang unary"); + logger->missingFunctionality("unknown glslang unary"); return true; // pick up operand as placeholder result } } bool TGlslangToSpvTraverser::visitAggregate(glslang::TVisit visit, glslang::TIntermAggregate* node) { + SpecConstantOpModeGuard spec_constant_op_mode_setter(&builder); + if (node->getType().getQualifier().isSpecConstant()) + spec_constant_op_mode_setter.turnOnSpecConstantOpMode(); + spv::Id result = spv::NoResult; + spv::Id invertedType = spv::NoType; // to use to override the natural type of the node + auto resultType = [&invertedType, &node, this](){ return invertedType != spv::NoType ? invertedType : convertGlslangToSpvType(node->getType()); }; // try texturing result = createImageTextureFunctionCall(node); @@ -1054,7 +1504,11 @@ bool TGlslangToSpvTraverser::visitAggregate(glslang::TVisit visit, glslang::TInt builder.setAccessChainRValue(result); return false; +#ifdef AMD_EXTENSIONS + } else if (node->getOp() == glslang::EOpImageStore || node->getOp() == glslang::EOpImageStoreLod) { +#else } else if (node->getOp() == glslang::EOpImageStore) { +#endif // "imageStore" is a special case, which has no result return false; } @@ -1067,7 +1521,7 @@ bool TGlslangToSpvTraverser::visitAggregate(glslang::TVisit visit, glslang::TInt assert(node->getOp()); - spv::Decoration precision = TranslatePrecisionDecoration(node->getType()); + spv::Decoration precision = TranslatePrecisionDecoration(node->getOperationPrecision()); switch (node->getOp()) { case glslang::EOpSequence: @@ -1083,11 +1537,11 @@ bool TGlslangToSpvTraverser::visitAggregate(glslang::TVisit visit, glslang::TInt // In all cases, still let the traverser visit the children for us. makeFunctions(node->getAsAggregate()->getSequence()); - // Also, we want all globals initializers to go into the entry of main(), before + // Also, we want all globals initializers to go into the beginning of the entry point, before // anything else gets there, so visit out of order, doing them all now. makeGlobalInitializers(node->getAsAggregate()->getSequence()); - // Initializers are done, don't want to visit again, but functions link objects need to be processed, + // Initializers are done, don't want to visit again, but functions and link objects need to be processed, // so do them manually. visitFunctions(node->getAsAggregate()->getSequence()); @@ -1117,17 +1571,18 @@ bool TGlslangToSpvTraverser::visitAggregate(glslang::TVisit visit, glslang::TInt } case glslang::EOpFunction: if (visit == glslang::EvPreVisit) { - if (isShaderEntrypoint(node)) { - inMain = true; + if (isShaderEntryPoint(node)) { + inEntryPoint = true; builder.setBuildPoint(shaderEntry->getLastBlock()); + currentFunction = shaderEntry; } else { handleFunctionEntry(node); } } else { - if (inMain) - mainTerminated = true; + if (inEntryPoint) + entryPointTerminated = true; builder.leaveFunction(); - inMain = false; + inEntryPoint = false; } return true; @@ -1138,14 +1593,15 @@ bool TGlslangToSpvTraverser::visitAggregate(glslang::TVisit visit, glslang::TInt return false; case glslang::EOpFunctionCall: { + builder.setLine(node->getLoc().line); if (node->isUserDefined()) result = handleUserFunctionCall(node); - //assert(result); // this can happen for bad shaders because the call graph completeness checking is not yet done + // assert(result); // this can happen for bad shaders because the call graph completeness checking is not yet done if (result) { builder.clearAccessChain(); builder.setAccessChainRValue(result); } else - spv::MissingFunctionality("missing user function; linker needs to catch that"); + logger->missingFunctionality("missing user function; linker needs to catch that"); return false; } @@ -1167,6 +1623,44 @@ bool TGlslangToSpvTraverser::visitAggregate(glslang::TVisit visit, glslang::TInt case glslang::EOpConstructDMat4x2: case glslang::EOpConstructDMat4x3: case glslang::EOpConstructDMat4x4: + case glslang::EOpConstructIMat2x2: + case glslang::EOpConstructIMat2x3: + case glslang::EOpConstructIMat2x4: + case glslang::EOpConstructIMat3x2: + case glslang::EOpConstructIMat3x3: + case glslang::EOpConstructIMat3x4: + case glslang::EOpConstructIMat4x2: + case glslang::EOpConstructIMat4x3: + case glslang::EOpConstructIMat4x4: + case glslang::EOpConstructUMat2x2: + case glslang::EOpConstructUMat2x3: + case glslang::EOpConstructUMat2x4: + case glslang::EOpConstructUMat3x2: + case glslang::EOpConstructUMat3x3: + case glslang::EOpConstructUMat3x4: + case glslang::EOpConstructUMat4x2: + case glslang::EOpConstructUMat4x3: + case glslang::EOpConstructUMat4x4: + case glslang::EOpConstructBMat2x2: + case glslang::EOpConstructBMat2x3: + case glslang::EOpConstructBMat2x4: + case glslang::EOpConstructBMat3x2: + case glslang::EOpConstructBMat3x3: + case glslang::EOpConstructBMat3x4: + case glslang::EOpConstructBMat4x2: + case glslang::EOpConstructBMat4x3: + case glslang::EOpConstructBMat4x4: +#ifdef AMD_EXTENSIONS + case glslang::EOpConstructF16Mat2x2: + case glslang::EOpConstructF16Mat2x3: + case glslang::EOpConstructF16Mat2x4: + case glslang::EOpConstructF16Mat3x2: + case glslang::EOpConstructF16Mat3x3: + case glslang::EOpConstructF16Mat3x4: + case glslang::EOpConstructF16Mat4x2: + case glslang::EOpConstructF16Mat4x3: + case glslang::EOpConstructF16Mat4x4: +#endif isMatrix = true; // fall through case glslang::EOpConstructFloat: @@ -1177,6 +1671,12 @@ bool TGlslangToSpvTraverser::visitAggregate(glslang::TVisit visit, glslang::TInt case glslang::EOpConstructDVec2: case glslang::EOpConstructDVec3: case glslang::EOpConstructDVec4: +#ifdef AMD_EXTENSIONS + case glslang::EOpConstructFloat16: + case glslang::EOpConstructF16Vec2: + case glslang::EOpConstructF16Vec3: + case glslang::EOpConstructF16Vec4: +#endif case glslang::EOpConstructBool: case glslang::EOpConstructBVec2: case glslang::EOpConstructBVec3: @@ -1189,24 +1689,42 @@ bool TGlslangToSpvTraverser::visitAggregate(glslang::TVisit visit, glslang::TInt case glslang::EOpConstructUVec2: case glslang::EOpConstructUVec3: case glslang::EOpConstructUVec4: + case glslang::EOpConstructInt64: + case glslang::EOpConstructI64Vec2: + case glslang::EOpConstructI64Vec3: + case glslang::EOpConstructI64Vec4: + case glslang::EOpConstructUint64: + case glslang::EOpConstructU64Vec2: + case glslang::EOpConstructU64Vec3: + case glslang::EOpConstructU64Vec4: +#ifdef AMD_EXTENSIONS + case glslang::EOpConstructInt16: + case glslang::EOpConstructI16Vec2: + case glslang::EOpConstructI16Vec3: + case glslang::EOpConstructI16Vec4: + case glslang::EOpConstructUint16: + case glslang::EOpConstructU16Vec2: + case glslang::EOpConstructU16Vec3: + case glslang::EOpConstructU16Vec4: +#endif case glslang::EOpConstructStruct: case glslang::EOpConstructTextureSampler: { + builder.setLine(node->getLoc().line); std::vector arguments; translateArguments(*node, arguments); - spv::Id resultTypeId = convertGlslangToSpvType(node->getType()); spv::Id constructed; if (node->getOp() == glslang::EOpConstructTextureSampler) - constructed = builder.createOp(spv::OpSampledImage, resultTypeId, arguments); + constructed = builder.createOp(spv::OpSampledImage, resultType(), arguments); else if (node->getOp() == glslang::EOpConstructStruct || node->getType().isArray()) { std::vector constituents; for (int c = 0; c < (int)arguments.size(); ++c) constituents.push_back(arguments[c]); - constructed = builder.createCompositeConstruct(resultTypeId, constituents); + constructed = builder.createCompositeConstruct(resultType(), constituents); } else if (isMatrix) - constructed = builder.createMatrixConstructor(precision, arguments, resultTypeId); + constructed = builder.createMatrixConstructor(precision, arguments, resultType()); else - constructed = builder.createConstructor(precision, arguments, resultTypeId); + constructed = builder.createConstructor(precision, arguments, resultType()); builder.clearAccessChain(); builder.setAccessChainRValue(constructed); @@ -1235,7 +1753,7 @@ bool TGlslangToSpvTraverser::visitAggregate(glslang::TVisit visit, glslang::TInt break; } case glslang::EOpMul: - // compontent-wise matrix multiply + // component-wise matrix multiply binOp = glslang::EOpMul; break; case glslang::EOpOuterProduct: @@ -1244,9 +1762,9 @@ bool TGlslangToSpvTraverser::visitAggregate(glslang::TVisit visit, glslang::TInt break; case glslang::EOpDot: { - // for scalar dot product, use multiply + // for scalar dot product, use multiply glslang::TIntermSequence& glslangOperands = node->getSequence(); - if (! glslangOperands[0]->getAsTyped()->isVector()) + if (glslangOperands[0]->getAsTyped()->getVectorSize() == 1) binOp = glslang::EOpMul; break; } @@ -1264,6 +1782,11 @@ bool TGlslangToSpvTraverser::visitAggregate(glslang::TVisit visit, glslang::TInt case glslang::EOpMemoryBarrierImage: case glslang::EOpMemoryBarrierShared: case glslang::EOpGroupMemoryBarrier: + case glslang::EOpDeviceMemoryBarrier: + case glslang::EOpAllMemoryBarrierWithGroupSync: + case glslang::EOpDeviceMemoryBarrierWithGroupSync: + case glslang::EOpWorkgroupMemoryBarrier: + case glslang::EOpWorkgroupMemoryBarrierWithGroupSync: noReturnValue = true; // These all have 0 operands and will naturally finish up in the code below for 0 operands break; @@ -1279,6 +1802,20 @@ bool TGlslangToSpvTraverser::visitAggregate(glslang::TVisit visit, glslang::TInt atomic = true; break; + case glslang::EOpAtomicCounterAdd: + case glslang::EOpAtomicCounterSubtract: + case glslang::EOpAtomicCounterMin: + case glslang::EOpAtomicCounterMax: + case glslang::EOpAtomicCounterAnd: + case glslang::EOpAtomicCounterOr: + case glslang::EOpAtomicCounterXor: + case glslang::EOpAtomicCounterExchange: + case glslang::EOpAtomicCounterCompSwap: + builder.addExtension("SPV_KHR_shader_atomic_counter_ops"); + builder.addCapability(spv::CapabilityAtomicStorageOps); + atomic = true; + break; + default: break; } @@ -1299,8 +1836,9 @@ bool TGlslangToSpvTraverser::visitAggregate(glslang::TVisit visit, glslang::TInt right->traverse(this); spv::Id rightId = accessChainLoad(right->getType()); - result = createBinaryOperation(binOp, precision, - convertGlslangToSpvType(node->getType()), leftId, rightId, + builder.setLine(node->getLoc().line); + result = createBinaryOperation(binOp, precision, TranslateNoContractionDecoration(node->getType().getQualifier()), + resultType(), leftId, rightId, left->getType().getBasicType(), reduceComparison); // code above should only make binOp that exists in createBinaryOperation @@ -1317,9 +1855,6 @@ bool TGlslangToSpvTraverser::visitAggregate(glslang::TVisit visit, glslang::TInt glslang::TIntermSequence& glslangOperands = node->getSequence(); std::vector operands; for (int arg = 0; arg < (int)glslangOperands.size(); ++arg) { - builder.clearAccessChain(); - glslangOperands[arg]->traverse(this); - // special case l-value operands; there are just a few bool lvalue = false; switch (node->getOp()) { @@ -1330,8 +1865,18 @@ bool TGlslangToSpvTraverser::visitAggregate(glslang::TVisit visit, glslang::TInt break; case glslang::EOpInterpolateAtSample: case glslang::EOpInterpolateAtOffset: - if (arg == 0) +#ifdef AMD_EXTENSIONS + case glslang::EOpInterpolateAtVertex: +#endif + if (arg == 0) { lvalue = true; + + // Does it need a swizzle inversion? If so, evaluation is inverted; + // operate first on the swizzle base, then apply the swizzle. + if (glslangOperands[0]->getAsOperator() && + glslangOperands[0]->getAsOperator()->getOp() == glslang::EOpVectorSwizzle) + invertedType = convertGlslangToSpvType(glslangOperands[0]->getAsBinaryNode()->getLeft()->getType()); + } break; case glslang::EOpAtomicAdd: case glslang::EOpAtomicMin: @@ -1341,6 +1886,15 @@ bool TGlslangToSpvTraverser::visitAggregate(glslang::TVisit visit, glslang::TInt case glslang::EOpAtomicXor: case glslang::EOpAtomicExchange: case glslang::EOpAtomicCompSwap: + case glslang::EOpAtomicCounterAdd: + case glslang::EOpAtomicCounterSubtract: + case glslang::EOpAtomicCounterMin: + case glslang::EOpAtomicCounterMax: + case glslang::EOpAtomicCounterAnd: + case glslang::EOpAtomicCounterOr: + case glslang::EOpAtomicCounterXor: + case glslang::EOpAtomicCounterExchange: + case glslang::EOpAtomicCounterCompSwap: if (arg == 0) lvalue = true; break; @@ -1357,35 +1911,49 @@ bool TGlslangToSpvTraverser::visitAggregate(glslang::TVisit visit, glslang::TInt default: break; } + builder.clearAccessChain(); + if (invertedType != spv::NoType && arg == 0) + glslangOperands[0]->getAsBinaryNode()->getLeft()->traverse(this); + else + glslangOperands[arg]->traverse(this); if (lvalue) operands.push_back(builder.accessChainGetLValue()); - else + else { + builder.setLine(node->getLoc().line); operands.push_back(accessChainLoad(glslangOperands[arg]->getAsTyped()->getType())); + } } + builder.setLine(node->getLoc().line); if (atomic) { // Handle all atomics - result = createAtomicOperation(node->getOp(), precision, convertGlslangToSpvType(node->getType()), operands, node->getBasicType()); + result = createAtomicOperation(node->getOp(), precision, resultType(), operands, node->getBasicType()); } else { // Pass through to generic operations. switch (glslangOperands.size()) { case 0: - result = createNoArgOperation(node->getOp()); + result = createNoArgOperation(node->getOp(), precision, resultType()); break; case 1: - result = createUnaryOperation(node->getOp(), precision, convertGlslangToSpvType(node->getType()), operands.front(), glslangOperands[0]->getAsTyped()->getBasicType()); + result = createUnaryOperation( + node->getOp(), precision, + TranslateNoContractionDecoration(node->getType().getQualifier()), + resultType(), operands.front(), + glslangOperands[0]->getAsTyped()->getBasicType()); break; default: - result = createMiscOperation(node->getOp(), precision, convertGlslangToSpvType(node->getType()), operands, node->getBasicType()); + result = createMiscOperation(node->getOp(), precision, resultType(), operands, node->getBasicType()); break; } + if (invertedType) + result = createInvertedSwizzle(precision, *glslangOperands[0]->getAsBinaryNode(), result); } if (noReturnValue) return false; if (! result) { - spv::MissingFunctionality("unknown glslang aggregate"); + logger->missingFunctionality("unknown glslang aggregate"); return true; // pick up a child as a placeholder operand } else { builder.clearAccessChain(); @@ -1394,42 +1962,111 @@ bool TGlslangToSpvTraverser::visitAggregate(glslang::TVisit visit, glslang::TInt } } +// This path handles both if-then-else and ?: +// The if-then-else has a node type of void, while +// ?: has either a void or a non-void node type +// +// Leaving the result, when not void: +// GLSL only has r-values as the result of a :?, but +// if we have an l-value, that can be more efficient if it will +// become the base of a complex r-value expression, because the +// next layer copies r-values into memory to use the access-chain mechanism bool TGlslangToSpvTraverser::visitSelection(glslang::TVisit /* visit */, glslang::TIntermSelection* node) { - // This path handles both if-then-else and ?: - // The if-then-else has a node type of void, while - // ?: has a non-void node type - spv::Id result = 0; - if (node->getBasicType() != glslang::EbtVoid) { - // don't handle this as just on-the-fly temporaries, because there will be two names - // and better to leave SSA to later passes - result = builder.createVariable(spv::StorageClassFunction, convertGlslangToSpvType(node->getType())); + // See if it simple and safe to generate OpSelect instead of using control flow. + // Crucially, side effects must be avoided, and there are performance trade-offs. + // Return true if good idea (and safe) for OpSelect, false otherwise. + const auto selectPolicy = [&]() -> bool { + if ((!node->getType().isScalar() && !node->getType().isVector()) || + node->getBasicType() == glslang::EbtVoid) + return false; + + if (node->getTrueBlock() == nullptr || + node->getFalseBlock() == nullptr) + return false; + + assert(node->getType() == node->getTrueBlock() ->getAsTyped()->getType() && + node->getType() == node->getFalseBlock()->getAsTyped()->getType()); + + // return true if a single operand to ? : is okay for OpSelect + const auto operandOkay = [](glslang::TIntermTyped* node) { + return node->getAsSymbolNode() || node->getType().getQualifier().isConstant(); + }; + + return operandOkay(node->getTrueBlock() ->getAsTyped()) && + operandOkay(node->getFalseBlock()->getAsTyped()); + }; + + // Emit OpSelect for this selection. + const auto handleAsOpSelect = [&]() { + node->getCondition()->traverse(this); + spv::Id condition = accessChainLoad(node->getCondition()->getType()); + node->getTrueBlock()->traverse(this); + spv::Id trueValue = accessChainLoad(node->getTrueBlock()->getAsTyped()->getType()); + node->getFalseBlock()->traverse(this); + spv::Id falseValue = accessChainLoad(node->getTrueBlock()->getAsTyped()->getType()); + + builder.setLine(node->getLoc().line); + + // smear condition to vector, if necessary (AST is always scalar) + if (builder.isVector(trueValue)) + condition = builder.smearScalar(spv::NoPrecision, condition, + builder.makeVectorType(builder.makeBoolType(), + builder.getNumComponents(trueValue))); + + spv::Id select = builder.createTriOp(spv::OpSelect, + convertGlslangToSpvType(node->getType()), condition, + trueValue, falseValue); + builder.clearAccessChain(); + builder.setAccessChainRValue(select); + }; + + // Try for OpSelect + + if (selectPolicy()) { + SpecConstantOpModeGuard spec_constant_op_mode_setter(&builder); + if (node->getType().getQualifier().isSpecConstant()) + spec_constant_op_mode_setter.turnOnSpecConstantOpMode(); + + handleAsOpSelect(); + return false; } + // Instead, emit control flow... + // Don't handle results as temporaries, because there will be two names + // and better to leave SSA to later passes. + spv::Id result = (node->getBasicType() == glslang::EbtVoid) + ? spv::NoResult + : builder.createVariable(spv::StorageClassFunction, convertGlslangToSpvType(node->getType())); + // emit the condition before doing anything with selection node->getCondition()->traverse(this); - // make an "if" based on the value created by the condition - spv::Builder::If ifBuilder(accessChainLoad(node->getCondition()->getType()), builder); + // Selection control: + const spv::SelectionControlMask control = TranslateSelectionControl(*node); - if (node->getTrueBlock()) { - // emit the "then" statement + // make an "if" based on the value created by the condition + spv::Builder::If ifBuilder(accessChainLoad(node->getCondition()->getType()), control, builder); + + // emit the "then" statement + if (node->getTrueBlock() != nullptr) { node->getTrueBlock()->traverse(this); - if (result) - builder.createStore(accessChainLoad(node->getTrueBlock()->getAsTyped()->getType()), result); + if (result != spv::NoResult) + builder.createStore(accessChainLoad(node->getTrueBlock()->getAsTyped()->getType()), result); } - if (node->getFalseBlock()) { + if (node->getFalseBlock() != nullptr) { ifBuilder.makeBeginElse(); // emit the "else" statement node->getFalseBlock()->traverse(this); - if (result) + if (result != spv::NoResult) builder.createStore(accessChainLoad(node->getFalseBlock()->getAsTyped()->getType()), result); } + // finish off the control flow ifBuilder.makeEndIf(); - if (result) { + if (result != spv::NoResult) { // GLSL only has r-values as the result of a :?, but // if we have an l-value, that can be more efficient if it will // become the base of a complex r-value expression, because the @@ -1447,6 +2084,9 @@ bool TGlslangToSpvTraverser::visitSwitch(glslang::TVisit /* visit */, glslang::T node->getCondition()->traverse(this); spv::Id selector = accessChainLoad(node->getCondition()->getAsTyped()->getType()); + // Selection control: + const spv::SelectionControlMask control = TranslateSwitchControl(*node); + // browse the children to sort out code segments int defaultSegment = -1; std::vector codeSegments; @@ -1464,7 +2104,7 @@ bool TGlslangToSpvTraverser::visitSwitch(glslang::TVisit /* visit */, glslang::T codeSegments.push_back(child); } - // handle the case where the last code segment is missing, due to no code + // handle the case where the last code segment is missing, due to no code // statements between the last case and the end of the switch statement if ((caseValues.size() && (int)codeSegments.size() == valueIndexToSegment[caseValues.size() - 1]) || (int)codeSegments.size() == defaultSegment) @@ -1472,7 +2112,7 @@ bool TGlslangToSpvTraverser::visitSwitch(glslang::TVisit /* visit */, glslang::T // make the switch statement std::vector segmentBlocks; // returned, as the blocks allocated in the call - builder.makeSwitch(selector, (int)codeSegments.size(), caseValues, valueIndexToSegment, defaultSegment, segmentBlocks); + builder.makeSwitch(selector, control, (int)codeSegments.size(), caseValues, valueIndexToSegment, defaultSegment, segmentBlocks); // emit all the code in the segments breakForLoop.push(false); @@ -1493,7 +2133,7 @@ bool TGlslangToSpvTraverser::visitSwitch(glslang::TVisit /* visit */, glslang::T void TGlslangToSpvTraverser::visitConstantUnion(glslang::TIntermConstantUnion* node) { int nextConst = 0; - spv::Id constant = createSpvConstant(node->getType(), node->getConstArray(), nextConst, false); + spv::Id constant = createSpvConstantFromConstUnionArray(node->getType(), node->getConstArray(), nextConst, false); builder.clearAccessChain(); builder.setAccessChainRValue(constant); @@ -1503,22 +2143,27 @@ bool TGlslangToSpvTraverser::visitLoop(glslang::TVisit /* visit */, glslang::TIn { auto blocks = builder.makeNewLoop(); builder.createBranch(&blocks.head); + + // Loop control: + unsigned int dependencyLength = glslang::TIntermLoop::dependencyInfinite; + const spv::LoopControlMask control = TranslateLoopControl(*node, dependencyLength); + // Spec requires back edges to target header blocks, and every header block // must dominate its merge block. Make a header block first to ensure these // conditions are met. By definition, it will contain OpLoopMerge, followed // by a block-ending branch. But we don't want to put any other body/test // instructions in it, since the body/test may have arbitrary instructions, // including merges of its own. + builder.setLine(node->getLoc().line); builder.setBuildPoint(&blocks.head); - builder.createLoopMerge(&blocks.merge, &blocks.continue_target, spv::LoopControlMaskNone); + builder.createLoopMerge(&blocks.merge, &blocks.continue_target, control, dependencyLength); if (node->testFirst() && node->getTest()) { spv::Block& test = builder.makeNewBlock(); builder.createBranch(&test); builder.setBuildPoint(&test); node->getTest()->traverse(this); - spv::Id condition = - accessChainLoad(node->getTest()->getType()); + spv::Id condition = accessChainLoad(node->getTest()->getType()); builder.createConditionalBranch(condition, &blocks.body, &blocks.merge); builder.setBuildPoint(&blocks.body); @@ -1533,6 +2178,7 @@ bool TGlslangToSpvTraverser::visitLoop(glslang::TVisit /* visit */, glslang::TIn node->getTerminal()->traverse(this); builder.createBranch(&blocks.head); } else { + builder.setLine(node->getLoc().line); builder.createBranch(&blocks.body); breakForLoop.push(true); @@ -1567,6 +2213,8 @@ bool TGlslangToSpvTraverser::visitBranch(glslang::TVisit /* visit */, glslang::T if (node->getExpression()) node->getExpression()->traverse(this); + builder.setLine(node->getLoc().line); + switch (node->getFlowOp()) { case glslang::EOpKill: builder.makeDiscard(); @@ -1581,9 +2229,18 @@ bool TGlslangToSpvTraverser::visitBranch(glslang::TVisit /* visit */, glslang::T builder.createLoopContinue(); break; case glslang::EOpReturn: - if (node->getExpression()) - builder.makeReturn(false, accessChainLoad(node->getExpression()->getType())); - else + if (node->getExpression()) { + const glslang::TType& glslangReturnType = node->getExpression()->getType(); + spv::Id returnId = accessChainLoad(glslangReturnType); + if (builder.getTypeId(returnId) != currentFunction->getReturnType()) { + builder.clearAccessChain(); + spv::Id copyId = builder.createVariable(spv::StorageClassFunction, currentFunction->getReturnType()); + builder.setAccessChainLValue(copyId); + multiTypeStore(glslangReturnType, returnId); + returnId = builder.createLoad(copyId); + } + builder.makeReturn(false, returnId); + } else builder.makeReturn(false); builder.clearAccessChain(); @@ -1599,17 +2256,37 @@ bool TGlslangToSpvTraverser::visitBranch(glslang::TVisit /* visit */, glslang::T spv::Id TGlslangToSpvTraverser::createSpvVariable(const glslang::TIntermSymbol* node) { - // First, steer off constants, which are not SPIR-V variables, but + // First, steer off constants, which are not SPIR-V variables, but // can still have a mapping to a SPIR-V Id. // This includes specialization constants. - if (node->getQualifier().storage == glslang::EvqConst) { - return createSpvSpecConstant(*node); + if (node->getQualifier().isConstant()) { + return createSpvConstant(*node); } // Now, handle actual variables spv::StorageClass storageClass = TranslateStorageClass(node->getType()); spv::Id spvType = convertGlslangToSpvType(node->getType()); +#ifdef AMD_EXTENSIONS + const bool contains16BitType = node->getType().containsBasicType(glslang::EbtFloat16) || + node->getType().containsBasicType(glslang::EbtInt16) || + node->getType().containsBasicType(glslang::EbtUint16); + if (contains16BitType) { + if (storageClass == spv::StorageClassInput || storageClass == spv::StorageClassOutput) { + builder.addExtension(spv::E_SPV_KHR_16bit_storage); + builder.addCapability(spv::CapabilityStorageInputOutput16); + } else if (storageClass == spv::StorageClassPushConstant) { + builder.addExtension(spv::E_SPV_KHR_16bit_storage); + builder.addCapability(spv::CapabilityStoragePushConstant16); + } else if (storageClass == spv::StorageClassUniform) { + builder.addExtension(spv::E_SPV_KHR_16bit_storage); + builder.addCapability(spv::CapabilityStorageUniform16); + if (node->getType().getQualifier().storage == glslang::EvqBuffer) + builder.addCapability(spv::CapabilityStorageUniformBufferBlock16); + } + } +#endif + const char* name = node->getName().c_str(); if (glslang::IsAnonymous(name)) name = ""; @@ -1630,6 +2307,35 @@ spv::Id TGlslangToSpvTraverser::getSampledType(const glslang::TSampler& sampler) } } +// If node is a swizzle operation, return the type that should be used if +// the swizzle base is first consumed by another operation, before the swizzle +// is applied. +spv::Id TGlslangToSpvTraverser::getInvertedSwizzleType(const glslang::TIntermTyped& node) +{ + if (node.getAsOperator() && + node.getAsOperator()->getOp() == glslang::EOpVectorSwizzle) + return convertGlslangToSpvType(node.getAsBinaryNode()->getLeft()->getType()); + else + return spv::NoType; +} + +// When inverting a swizzle with a parent op, this function +// will apply the swizzle operation to a completed parent operation. +spv::Id TGlslangToSpvTraverser::createInvertedSwizzle(spv::Decoration precision, const glslang::TIntermTyped& node, spv::Id parentResult) +{ + std::vector swizzle; + convertSwizzle(*node.getAsBinaryNode()->getRight()->getAsAggregate(), swizzle); + return builder.createRvalueSwizzle(precision, convertGlslangToSpvType(node.getType()), parentResult, swizzle); +} + +// Convert a glslang AST swizzle node to a swizzle vector for building SPIR-V. +void TGlslangToSpvTraverser::convertSwizzle(const glslang::TIntermAggregate& node, std::vector& swizzle) +{ + const glslang::TIntermSequence& swizzleSequence = node.getSequence(); + for (int i = 0; i < (int)swizzleSequence.size(); ++i) + swizzle.push_back(swizzleSequence[i]->getAsConstantUnion()->getConstArray()[0].getIConst()); +} + // Convert from a glslang type to an SPV type, by calling into a // recursive version of this function. This establishes the inherited // layout state rooted from the top-level type. @@ -1640,6 +2346,7 @@ spv::Id TGlslangToSpvTraverser::convertGlslangToSpvType(const glslang::TType& ty // Do full recursive conversion of an arbitrary glslang type to a SPIR-V Id. // explicitLayout can be kept the same throughout the hierarchical recursive walk. +// Mutually recursive with convertGlslangStructToSpvType(). spv::Id TGlslangToSpvTraverser::convertGlslangToSpvType(const glslang::TType& type, glslang::TLayoutPacking explicitLayout, const glslang::TQualifier& qualifier) { spv::Id spvType = spv::NoResult; @@ -1655,6 +2362,12 @@ spv::Id TGlslangToSpvTraverser::convertGlslangToSpvType(const glslang::TType& ty case glslang::EbtDouble: spvType = builder.makeFloatType(64); break; +#ifdef AMD_EXTENSIONS + case glslang::EbtFloat16: + builder.addExtension(spv::E_SPV_AMD_gpu_shader_half_float); + spvType = builder.makeFloatType(16); + break; +#endif case glslang::EbtBool: // "transparent" bool doesn't exist in SPIR-V. The GLSL convention is // a 32-bit int where non-0 means true. @@ -1669,8 +2382,24 @@ spv::Id TGlslangToSpvTraverser::convertGlslangToSpvType(const glslang::TType& ty case glslang::EbtUint: spvType = builder.makeUintType(32); break; + case glslang::EbtInt64: + spvType = builder.makeIntType(64); + break; + case glslang::EbtUint64: + spvType = builder.makeUintType(64); + break; +#ifdef AMD_EXTENSIONS + case glslang::EbtInt16: + builder.addExtension(spv::E_SPV_AMD_gpu_shader_int16); + spvType = builder.makeIntType(16); + break; + case glslang::EbtUint16: + builder.addExtension(spv::E_SPV_AMD_gpu_shader_int16); + spvType = builder.makeUintType(16); + break; +#endif case glslang::EbtAtomicUint: - spv::TbdFunctionality("Is atomic_uint an opaque handle in the uniform storage class, or an addresses in the atomic storage class?"); + builder.addCapability(spv::CapabilityAtomicStorage); spvType = builder.makeUintType(32); break; case glslang::EbtSampler: @@ -1694,109 +2423,19 @@ spv::Id TGlslangToSpvTraverser::convertGlslangToSpvType(const glslang::TType& ty case glslang::EbtBlock: { // If we've seen this struct type, return it - const glslang::TTypeList* glslangStruct = type.getStruct(); - std::vector structFields; + const glslang::TTypeList* glslangMembers = type.getStruct(); // Try to share structs for different layouts, but not yet for other // kinds of qualification (primarily not yet including interpolant qualification). - if (! HasNonLayoutQualifiers(qualifier)) - spvType = structMap[explicitLayout][qualifier.layoutMatrix][glslangStruct]; + if (! HasNonLayoutQualifiers(type, qualifier)) + spvType = structMap[explicitLayout][qualifier.layoutMatrix][glslangMembers]; if (spvType != spv::NoResult) break; // else, we haven't seen it... - - // Create a vector of struct types for SPIR-V to consume - int memberDelta = 0; // how much the member's index changes from glslang to SPIR-V, normally 0, except sometimes for blocks if (type.getBasicType() == glslang::EbtBlock) - memberRemapper[glslangStruct].resize(glslangStruct->size()); - int locationOffset = 0; // for use across struct members, when they are called recursively - for (int i = 0; i < (int)glslangStruct->size(); i++) { - glslang::TType& glslangType = *(*glslangStruct)[i].type; - if (glslangType.hiddenMember()) { - ++memberDelta; - if (type.getBasicType() == glslang::EbtBlock) - memberRemapper[glslangStruct][i] = -1; - } else { - if (type.getBasicType() == glslang::EbtBlock) - memberRemapper[glslangStruct][i] = i - memberDelta; - // modify just this child's view of the qualifier - glslang::TQualifier subQualifier = glslangType.getQualifier(); - InheritQualifiers(subQualifier, qualifier); - if (qualifier.hasLocation()) { - subQualifier.layoutLocation += locationOffset; - locationOffset += glslangIntermediate->computeTypeLocationSize(glslangType); - } - structFields.push_back(convertGlslangToSpvType(glslangType, explicitLayout, subQualifier)); - } - } - - // Make the SPIR-V type - spvType = builder.makeStructType(structFields, type.getTypeName().c_str()); - if (! HasNonLayoutQualifiers(qualifier)) - structMap[explicitLayout][qualifier.layoutMatrix][glslangStruct] = spvType; - - // Name and decorate the non-hidden members - int offset = -1; - locationOffset = 0; // for use within the members of this struct, right now - for (int i = 0; i < (int)glslangStruct->size(); i++) { - glslang::TType& glslangType = *(*glslangStruct)[i].type; - int member = i; - if (type.getBasicType() == glslang::EbtBlock) - member = memberRemapper[glslangStruct][i]; - - // modify just this child's view of the qualifier - glslang::TQualifier subQualifier = glslangType.getQualifier(); - InheritQualifiers(subQualifier, qualifier); - - // using -1 above to indicate a hidden member - if (member >= 0) { - builder.addMemberName(spvType, member, glslangType.getFieldName().c_str()); - addMemberDecoration(spvType, member, TranslateLayoutDecoration(glslangType, subQualifier.layoutMatrix)); - addMemberDecoration(spvType, member, TranslatePrecisionDecoration(glslangType)); - addMemberDecoration(spvType, member, TranslateInterpolationDecoration(subQualifier)); - addMemberDecoration(spvType, member, TranslateInvariantDecoration(subQualifier)); - if (qualifier.hasLocation()) { - builder.addMemberDecoration(spvType, member, spv::DecorationLocation, qualifier.layoutLocation + locationOffset); - locationOffset += glslangIntermediate->computeTypeLocationSize(glslangType); - } - if (glslangType.getQualifier().hasComponent()) - builder.addMemberDecoration(spvType, member, spv::DecorationComponent, glslangType.getQualifier().layoutComponent); - if (glslangType.getQualifier().hasXfbOffset()) - builder.addMemberDecoration(spvType, member, spv::DecorationOffset, glslangType.getQualifier().layoutXfbOffset); - else if (explicitLayout != glslang::ElpNone) { - // figure out what to do with offset, which is accumulating - int nextOffset; - updateMemberOffset(type, glslangType, offset, nextOffset, explicitLayout, subQualifier.layoutMatrix); - if (offset >= 0) - builder.addMemberDecoration(spvType, member, spv::DecorationOffset, offset); - offset = nextOffset; - } - - if (glslangType.isMatrix() && explicitLayout != glslang::ElpNone) - builder.addMemberDecoration(spvType, member, spv::DecorationMatrixStride, getMatrixStride(glslangType, explicitLayout, subQualifier.layoutMatrix)); - - // built-in variable decorations - spv::BuiltIn builtIn = TranslateBuiltInDecoration(glslangType.getQualifier().builtIn); - if (builtIn != spv::BadValue) - addMemberDecoration(spvType, member, spv::DecorationBuiltIn, (int)builtIn); - } - } - - // Decorate the structure - addDecoration(spvType, TranslateLayoutDecoration(type, qualifier.layoutMatrix)); - addDecoration(spvType, TranslateBlockDecoration(type)); - if (type.getQualifier().hasStream()) { - builder.addCapability(spv::CapabilityGeometryStreams); - builder.addDecoration(spvType, spv::DecorationStream, type.getQualifier().layoutStream); - } - if (glslangIntermediate->getXfbMode()) { - builder.addCapability(spv::CapabilityTransformFeedback); - if (type.getQualifier().hasXfbStride()) - builder.addDecoration(spvType, spv::DecorationXfbStride, type.getQualifier().layoutXfbStride); - if (type.getQualifier().hasXfbBuffer()) - builder.addDecoration(spvType, spv::DecorationXfbBuffer, type.getQualifier().layoutXfbBuffer); - } + memberRemapper[glslangMembers].resize(glslangMembers->size()); + spvType = convertGlslangStructToSpvType(type, glslangMembers, explicitLayout, qualifier); } break; default: @@ -1859,6 +2498,191 @@ spv::Id TGlslangToSpvTraverser::convertGlslangToSpvType(const glslang::TType& ty return spvType; } +// TODO: this functionality should exist at a higher level, in creating the AST +// +// Identify interface members that don't have their required extension turned on. +// +bool TGlslangToSpvTraverser::filterMember(const glslang::TType& member) +{ + auto& extensions = glslangIntermediate->getRequestedExtensions(); + + if (member.getFieldName() == "gl_ViewportMask" && + extensions.find("GL_NV_viewport_array2") == extensions.end()) + return true; + if (member.getFieldName() == "gl_SecondaryViewportMaskNV" && + extensions.find("GL_NV_stereo_view_rendering") == extensions.end()) + return true; + if (member.getFieldName() == "gl_SecondaryPositionNV" && + extensions.find("GL_NV_stereo_view_rendering") == extensions.end()) + return true; + if (member.getFieldName() == "gl_PositionPerViewNV" && + extensions.find("GL_NVX_multiview_per_view_attributes") == extensions.end()) + return true; + if (member.getFieldName() == "gl_ViewportMaskPerViewNV" && + extensions.find("GL_NVX_multiview_per_view_attributes") == extensions.end()) + return true; + + return false; +}; + +// Do full recursive conversion of a glslang structure (or block) type to a SPIR-V Id. +// explicitLayout can be kept the same throughout the hierarchical recursive walk. +// Mutually recursive with convertGlslangToSpvType(). +spv::Id TGlslangToSpvTraverser::convertGlslangStructToSpvType(const glslang::TType& type, + const glslang::TTypeList* glslangMembers, + glslang::TLayoutPacking explicitLayout, + const glslang::TQualifier& qualifier) +{ + // Create a vector of struct types for SPIR-V to consume + std::vector spvMembers; + int memberDelta = 0; // how much the member's index changes from glslang to SPIR-V, normally 0, except sometimes for blocks + for (int i = 0; i < (int)glslangMembers->size(); i++) { + glslang::TType& glslangMember = *(*glslangMembers)[i].type; + if (glslangMember.hiddenMember()) { + ++memberDelta; + if (type.getBasicType() == glslang::EbtBlock) + memberRemapper[glslangMembers][i] = -1; + } else { + if (type.getBasicType() == glslang::EbtBlock) { + memberRemapper[glslangMembers][i] = i - memberDelta; + if (filterMember(glslangMember)) + continue; + } + // modify just this child's view of the qualifier + glslang::TQualifier memberQualifier = glslangMember.getQualifier(); + InheritQualifiers(memberQualifier, qualifier); + + // manually inherit location + if (! memberQualifier.hasLocation() && qualifier.hasLocation()) + memberQualifier.layoutLocation = qualifier.layoutLocation; + + // recurse + spvMembers.push_back(convertGlslangToSpvType(glslangMember, explicitLayout, memberQualifier)); + } + } + + // Make the SPIR-V type + spv::Id spvType = builder.makeStructType(spvMembers, type.getTypeName().c_str()); + if (! HasNonLayoutQualifiers(type, qualifier)) + structMap[explicitLayout][qualifier.layoutMatrix][glslangMembers] = spvType; + + // Decorate it + decorateStructType(type, glslangMembers, explicitLayout, qualifier, spvType); + + return spvType; +} + +void TGlslangToSpvTraverser::decorateStructType(const glslang::TType& type, + const glslang::TTypeList* glslangMembers, + glslang::TLayoutPacking explicitLayout, + const glslang::TQualifier& qualifier, + spv::Id spvType) +{ + // Name and decorate the non-hidden members + int offset = -1; + int locationOffset = 0; // for use within the members of this struct + for (int i = 0; i < (int)glslangMembers->size(); i++) { + glslang::TType& glslangMember = *(*glslangMembers)[i].type; + int member = i; + if (type.getBasicType() == glslang::EbtBlock) { + member = memberRemapper[glslangMembers][i]; + if (filterMember(glslangMember)) + continue; + } + + // modify just this child's view of the qualifier + glslang::TQualifier memberQualifier = glslangMember.getQualifier(); + InheritQualifiers(memberQualifier, qualifier); + + // using -1 above to indicate a hidden member + if (member >= 0) { + builder.addMemberName(spvType, member, glslangMember.getFieldName().c_str()); + addMemberDecoration(spvType, member, TranslateLayoutDecoration(glslangMember, memberQualifier.layoutMatrix)); + addMemberDecoration(spvType, member, TranslatePrecisionDecoration(glslangMember)); + // Add interpolation and auxiliary storage decorations only to top-level members of Input and Output storage classes + if (type.getQualifier().storage == glslang::EvqVaryingIn || + type.getQualifier().storage == glslang::EvqVaryingOut) { + if (type.getBasicType() == glslang::EbtBlock || + glslangIntermediate->getSource() == glslang::EShSourceHlsl) { + addMemberDecoration(spvType, member, TranslateInterpolationDecoration(memberQualifier)); + addMemberDecoration(spvType, member, TranslateAuxiliaryStorageDecoration(memberQualifier)); + } + } + addMemberDecoration(spvType, member, TranslateInvariantDecoration(memberQualifier)); + + if (type.getBasicType() == glslang::EbtBlock && + qualifier.storage == glslang::EvqBuffer) { + // Add memory decorations only to top-level members of shader storage block + std::vector memory; + TranslateMemoryDecoration(memberQualifier, memory); + for (unsigned int i = 0; i < memory.size(); ++i) + addMemberDecoration(spvType, member, memory[i]); + } + + // Location assignment was already completed correctly by the front end, + // just track whether a member needs to be decorated. + // Ignore member locations if the container is an array, as that's + // ill-specified and decisions have been made to not allow this. + if (! type.isArray() && memberQualifier.hasLocation()) + builder.addMemberDecoration(spvType, member, spv::DecorationLocation, memberQualifier.layoutLocation); + + if (qualifier.hasLocation()) // track for upcoming inheritance + locationOffset += glslangIntermediate->computeTypeLocationSize(glslangMember); + + // component, XFB, others + if (glslangMember.getQualifier().hasComponent()) + builder.addMemberDecoration(spvType, member, spv::DecorationComponent, glslangMember.getQualifier().layoutComponent); + if (glslangMember.getQualifier().hasXfbOffset()) + builder.addMemberDecoration(spvType, member, spv::DecorationOffset, glslangMember.getQualifier().layoutXfbOffset); + else if (explicitLayout != glslang::ElpNone) { + // figure out what to do with offset, which is accumulating + int nextOffset; + updateMemberOffset(type, glslangMember, offset, nextOffset, explicitLayout, memberQualifier.layoutMatrix); + if (offset >= 0) + builder.addMemberDecoration(spvType, member, spv::DecorationOffset, offset); + offset = nextOffset; + } + + if (glslangMember.isMatrix() && explicitLayout != glslang::ElpNone) + builder.addMemberDecoration(spvType, member, spv::DecorationMatrixStride, getMatrixStride(glslangMember, explicitLayout, memberQualifier.layoutMatrix)); + + // built-in variable decorations + spv::BuiltIn builtIn = TranslateBuiltInDecoration(glslangMember.getQualifier().builtIn, true); + if (builtIn != spv::BuiltInMax) + addMemberDecoration(spvType, member, spv::DecorationBuiltIn, (int)builtIn); + +#ifdef NV_EXTENSIONS + if (builtIn == spv::BuiltInLayer) { + // SPV_NV_viewport_array2 extension + if (glslangMember.getQualifier().layoutViewportRelative){ + addMemberDecoration(spvType, member, (spv::Decoration)spv::DecorationViewportRelativeNV); + builder.addCapability(spv::CapabilityShaderViewportMaskNV); + builder.addExtension(spv::E_SPV_NV_viewport_array2); + } + if (glslangMember.getQualifier().layoutSecondaryViewportRelativeOffset != -2048){ + addMemberDecoration(spvType, member, (spv::Decoration)spv::DecorationSecondaryViewportRelativeNV, glslangMember.getQualifier().layoutSecondaryViewportRelativeOffset); + builder.addCapability(spv::CapabilityShaderStereoViewNV); + builder.addExtension(spv::E_SPV_NV_stereo_view_rendering); + } + } + if (glslangMember.getQualifier().layoutPassthrough) { + addMemberDecoration(spvType, member, (spv::Decoration)spv::DecorationPassthroughNV); + builder.addCapability(spv::CapabilityGeometryShaderPassthroughNV); + builder.addExtension(spv::E_SPV_NV_geometry_shader_passthrough); + } +#endif + } + } + + // Decorate the structure + addDecoration(spvType, TranslateLayoutDecoration(type, qualifier.layoutMatrix)); + addDecoration(spvType, TranslateBlockDecoration(type, glslangIntermediate->usingStorageBuffer())); + if (type.getQualifier().hasStream() && glslangIntermediate->isMultiStream()) { + builder.addCapability(spv::CapabilityGeometryStreams); + builder.addDecoration(spvType, spv::DecorationStream, type.getQualifier().layoutStream); + } +} + // Turn the expression forming the array size into an id. // This is not quite trivial, because of specialization constants. // Sometimes, a raw constant is turned into an Id, and sometimes @@ -1872,7 +2696,7 @@ spv::Id TGlslangToSpvTraverser::makeArraySizeId(const glslang::TArraySizes& arra specNode->traverse(this); return accessChainLoad(specNode->getAsTyped()->getType()); } - + // Otherwise, need a compile-time (front end) size, get it: int size = arraySizes.getDimSize(dim); assert(size > 0); @@ -1890,12 +2714,133 @@ spv::Id TGlslangToSpvTraverser::accessChainLoad(const glslang::TType& type) spv::Id loadedId = builder.accessChainLoad(TranslatePrecisionDecoration(type), nominalTypeId); // Need to convert to abstract types when necessary - if (builder.isScalarType(nominalTypeId) && type.getBasicType() == glslang::EbtBool && nominalTypeId != builder.makeBoolType()) - loadedId = builder.createBinOp(spv::OpINotEqual, builder.makeBoolType(), loadedId, builder.makeUintConstant(0)); + if (type.getBasicType() == glslang::EbtBool) { + if (builder.isScalarType(nominalTypeId)) { + // Conversion for bool + spv::Id boolType = builder.makeBoolType(); + if (nominalTypeId != boolType) + loadedId = builder.createBinOp(spv::OpINotEqual, boolType, loadedId, builder.makeUintConstant(0)); + } else if (builder.isVectorType(nominalTypeId)) { + // Conversion for bvec + int vecSize = builder.getNumTypeComponents(nominalTypeId); + spv::Id bvecType = builder.makeVectorType(builder.makeBoolType(), vecSize); + if (nominalTypeId != bvecType) + loadedId = builder.createBinOp(spv::OpINotEqual, bvecType, loadedId, makeSmearedConstant(builder.makeUintConstant(0), vecSize)); + } + } return loadedId; } +// Wrap the builder's accessChainStore to: +// - do conversion of concrete to abstract type +// +// Implicitly uses the existing builder.accessChain as the storage target. +void TGlslangToSpvTraverser::accessChainStore(const glslang::TType& type, spv::Id rvalue) +{ + // Need to convert to abstract types when necessary + if (type.getBasicType() == glslang::EbtBool) { + spv::Id nominalTypeId = builder.accessChainGetInferredType(); + + if (builder.isScalarType(nominalTypeId)) { + // Conversion for bool + spv::Id boolType = builder.makeBoolType(); + if (nominalTypeId != boolType) { + // keep these outside arguments, for determinant order-of-evaluation + spv::Id one = builder.makeUintConstant(1); + spv::Id zero = builder.makeUintConstant(0); + rvalue = builder.createTriOp(spv::OpSelect, nominalTypeId, rvalue, one, zero); + } else if (builder.getTypeId(rvalue) != boolType) + rvalue = builder.createBinOp(spv::OpINotEqual, boolType, rvalue, builder.makeUintConstant(0)); + } else if (builder.isVectorType(nominalTypeId)) { + // Conversion for bvec + int vecSize = builder.getNumTypeComponents(nominalTypeId); + spv::Id bvecType = builder.makeVectorType(builder.makeBoolType(), vecSize); + if (nominalTypeId != bvecType) { + // keep these outside arguments, for determinant order-of-evaluation + spv::Id one = makeSmearedConstant(builder.makeUintConstant(1), vecSize); + spv::Id zero = makeSmearedConstant(builder.makeUintConstant(0), vecSize); + rvalue = builder.createTriOp(spv::OpSelect, nominalTypeId, rvalue, one, zero); + } else if (builder.getTypeId(rvalue) != bvecType) + rvalue = builder.createBinOp(spv::OpINotEqual, bvecType, rvalue, + makeSmearedConstant(builder.makeUintConstant(0), vecSize)); + } + } + + builder.accessChainStore(rvalue); +} + +// For storing when types match at the glslang level, but not might match at the +// SPIR-V level. +// +// This especially happens when a single glslang type expands to multiple +// SPIR-V types, like a struct that is used in a member-undecorated way as well +// as in a member-decorated way. +// +// NOTE: This function can handle any store request; if it's not special it +// simplifies to a simple OpStore. +// +// Implicitly uses the existing builder.accessChain as the storage target. +void TGlslangToSpvTraverser::multiTypeStore(const glslang::TType& type, spv::Id rValue) +{ + // we only do the complex path here if it's an aggregate + if (! type.isStruct() && ! type.isArray()) { + accessChainStore(type, rValue); + return; + } + + // and, it has to be a case of type aliasing + spv::Id rType = builder.getTypeId(rValue); + spv::Id lValue = builder.accessChainGetLValue(); + spv::Id lType = builder.getContainedTypeId(builder.getTypeId(lValue)); + if (lType == rType) { + accessChainStore(type, rValue); + return; + } + + // Recursively (as needed) copy an aggregate type to a different aggregate type, + // where the two types were the same type in GLSL. This requires member + // by member copy, recursively. + + // If an array, copy element by element. + if (type.isArray()) { + glslang::TType glslangElementType(type, 0); + spv::Id elementRType = builder.getContainedTypeId(rType); + for (int index = 0; index < type.getOuterArraySize(); ++index) { + // get the source member + spv::Id elementRValue = builder.createCompositeExtract(rValue, elementRType, index); + + // set up the target storage + builder.clearAccessChain(); + builder.setAccessChainLValue(lValue); + builder.accessChainPush(builder.makeIntConstant(index)); + + // store the member + multiTypeStore(glslangElementType, elementRValue); + } + } else { + assert(type.isStruct()); + + // loop over structure members + const glslang::TTypeList& members = *type.getStruct(); + for (int m = 0; m < (int)members.size(); ++m) { + const glslang::TType& glslangMemberType = *members[m].type; + + // get the source member + spv::Id memberRType = builder.getContainedTypeId(rType, m); + spv::Id memberRValue = builder.createCompositeExtract(rValue, memberRType, m); + + // set up the target storage + builder.clearAccessChain(); + builder.setAccessChainLValue(lValue); + builder.accessChainPush(builder.makeIntConstant(m)); + + // store the member + multiTypeStore(glslangMemberType, memberRValue); + } + } +} + // Decide whether or not this type should be // decorated with offsets and strides, and if so // whether std140 or std430 rules should be applied. @@ -1951,7 +2896,7 @@ int TGlslangToSpvTraverser::getMatrixStride(const glslang::TType& matrixType, gl // 'currentOffset' should be passed in already initialized, ready to modify, and reflecting // the migration of data from nextOffset -> currentOffset. It should be -1 on the first call. // -1 means a non-forced member offset (no decoration needed). -void TGlslangToSpvTraverser::updateMemberOffset(const glslang::TType& /*structType*/, const glslang::TType& memberType, int& currentOffset, int& nextOffset, +void TGlslangToSpvTraverser::updateMemberOffset(const glslang::TType& structType, const glslang::TType& memberType, int& currentOffset, int& nextOffset, glslang::TLayoutPacking explicitLayout, glslang::TLayoutMatrix matrixLayout) { // this will get a positive value when deemed necessary @@ -1976,64 +2921,149 @@ void TGlslangToSpvTraverser::updateMemberOffset(const glslang::TType& /*structTy // Getting this far means we need explicit offsets if (currentOffset < 0) currentOffset = 0; - + // Now, currentOffset is valid (either 0, or from a previous nextOffset), // but possibly not yet correctly aligned. int memberSize; int dummyStride; int memberAlignment = glslangIntermediate->getBaseAlignment(memberType, memberSize, dummyStride, explicitLayout == glslang::ElpStd140, matrixLayout == glslang::ElmRowMajor); + + // Adjust alignment for HLSL rules + // TODO: make this consistent in early phases of code: + // adjusting this late means inconsistencies with earlier code, which for reflection is an issue + // Until reflection is brought in sync with these adjustments, don't apply to $Global, + // which is the most likely to rely on reflection, and least likely to rely implicit layouts + if (glslangIntermediate->usingHlslOFfsets() && + ! memberType.isArray() && memberType.isVector() && structType.getTypeName().compare("$Global") != 0) { + int dummySize; + int componentAlignment = glslangIntermediate->getBaseAlignmentScalar(memberType, dummySize); + if (componentAlignment <= 4) + memberAlignment = componentAlignment; + } + + // Bump up to member alignment glslang::RoundToPow2(currentOffset, memberAlignment); + + // Bump up to vec4 if there is a bad straddle + if (glslangIntermediate->improperStraddle(memberType, memberSize, currentOffset)) + glslang::RoundToPow2(currentOffset, 16); + nextOffset = currentOffset + memberSize; } -bool TGlslangToSpvTraverser::isShaderEntrypoint(const glslang::TIntermAggregate* node) +void TGlslangToSpvTraverser::declareUseOfStructMember(const glslang::TTypeList& members, int glslangMember) { - return node->getName() == "main("; + const glslang::TBuiltInVariable glslangBuiltIn = members[glslangMember].type->getQualifier().builtIn; + switch (glslangBuiltIn) + { + case glslang::EbvClipDistance: + case glslang::EbvCullDistance: + case glslang::EbvPointSize: +#ifdef NV_EXTENSIONS + case glslang::EbvViewportMaskNV: + case glslang::EbvSecondaryPositionNV: + case glslang::EbvSecondaryViewportMaskNV: + case glslang::EbvPositionPerViewNV: + case glslang::EbvViewportMaskPerViewNV: +#endif + // Generate the associated capability. Delegate to TranslateBuiltInDecoration. + // Alternately, we could just call this for any glslang built-in, since the + // capability already guards against duplicates. + TranslateBuiltInDecoration(glslangBuiltIn, false); + break; + default: + // Capabilities were already generated when the struct was declared. + break; + } +} + +bool TGlslangToSpvTraverser::isShaderEntryPoint(const glslang::TIntermAggregate* node) +{ + return node->getName().compare(glslangIntermediate->getEntryPointMangledName().c_str()) == 0; +} + +// Does parameter need a place to keep writes, separate from the original? +// Assumes called after originalParam(), which filters out block/buffer/opaque-based +// qualifiers such that we should have only in/out/inout/constreadonly here. +bool TGlslangToSpvTraverser::writableParam(glslang::TStorageQualifier qualifier) +{ + assert(qualifier == glslang::EvqIn || + qualifier == glslang::EvqOut || + qualifier == glslang::EvqInOut || + qualifier == glslang::EvqConstReadOnly); + return qualifier != glslang::EvqConstReadOnly; +} + +// Is parameter pass-by-original? +bool TGlslangToSpvTraverser::originalParam(glslang::TStorageQualifier qualifier, const glslang::TType& paramType, + bool implicitThisParam) +{ + if (implicitThisParam) // implicit this + return true; + if (glslangIntermediate->getSource() == glslang::EShSourceHlsl) + return paramType.getBasicType() == glslang::EbtBlock; + return paramType.containsOpaque() || // sampler, etc. + (paramType.getBasicType() == glslang::EbtBlock && qualifier == glslang::EvqBuffer); // SSBO } // Make all the functions, skeletally, without actually visiting their bodies. void TGlslangToSpvTraverser::makeFunctions(const glslang::TIntermSequence& glslFunctions) { + const auto getParamDecorations = [](std::vector& decorations, const glslang::TType& type) { + spv::Decoration paramPrecision = TranslatePrecisionDecoration(type); + if (paramPrecision != spv::NoPrecision) + decorations.push_back(paramPrecision); + TranslateMemoryDecoration(type.getQualifier(), decorations); + }; + for (int f = 0; f < (int)glslFunctions.size(); ++f) { glslang::TIntermAggregate* glslFunction = glslFunctions[f]->getAsAggregate(); - if (! glslFunction || glslFunction->getOp() != glslang::EOpFunction || isShaderEntrypoint(glslFunction)) + if (! glslFunction || glslFunction->getOp() != glslang::EOpFunction || isShaderEntryPoint(glslFunction)) continue; // We're on a user function. Set up the basic interface for the function now, - // so that it's available to call. - // Translating the body will happen later. + // so that it's available to call. Translating the body will happen later. // - // Typically (except for a "const in" parameter), an address will be passed to the + // Typically (except for a "const in" parameter), an address will be passed to the // function. What it is an address of varies: // - // - "in" parameters not marked as "const" can be written to without modifying the argument, - // so that write needs to be to a copy, hence the address of a copy works. + // - "in" parameters not marked as "const" can be written to without modifying the calling + // argument so that write needs to be to a copy, hence the address of a copy works. // // - "const in" parameters can just be the r-value, as no writes need occur. // - // - "out" and "inout" arguments can't be done as direct pointers, because GLSL has - // copy-in/copy-out semantics. They can be handled though with a pointer to a copy. + // - "out" and "inout" arguments can't be done as pointers to the calling argument, because + // GLSL has copy-in/copy-out semantics. They can be handled though with a pointer to a copy. std::vector paramTypes; - std::vector paramPrecisions; + std::vector> paramDecorations; // list of decorations per parameter glslang::TIntermSequence& parameters = glslFunction->getSequence()[0]->getAsAggregate()->getSequence(); + bool implicitThis = (int)parameters.size() > 0 && parameters[0]->getAsSymbolNode()->getName() == + glslangIntermediate->implicitThisName; + + paramDecorations.resize(parameters.size()); for (int p = 0; p < (int)parameters.size(); ++p) { const glslang::TType& paramType = parameters[p]->getAsTyped()->getType(); spv::Id typeId = convertGlslangToSpvType(paramType); - if (paramType.getQualifier().storage != glslang::EvqConstReadOnly) + if (originalParam(paramType.getQualifier().storage, paramType, implicitThis && p == 0)) + typeId = builder.makePointer(TranslateStorageClass(paramType), typeId); + else if (writableParam(paramType.getQualifier().storage)) typeId = builder.makePointer(spv::StorageClassFunction, typeId); else - constReadOnlyParameters.insert(parameters[p]->getAsSymbolNode()->getId()); - paramPrecisions.push_back(TranslatePrecisionDecoration(paramType)); + rValueParameters.insert(parameters[p]->getAsSymbolNode()->getId()); + getParamDecorations(paramDecorations[p], paramType); paramTypes.push_back(typeId); } spv::Block* functionBlock; spv::Function *function = builder.makeFunctionEntry(TranslatePrecisionDecoration(glslFunction->getType()), convertGlslangToSpvType(glslFunction->getType()), - glslFunction->getName().c_str(), paramTypes, paramPrecisions, &functionBlock); + glslFunction->getName().c_str(), paramTypes, + paramDecorations, &functionBlock); + if (implicitThis) + function->setImplicitThis(); // Track function to emit/call later functionMap[glslFunction->getName().c_str()] = function; @@ -2056,7 +3086,7 @@ void TGlslangToSpvTraverser::makeGlobalInitializers(const glslang::TIntermSequen if (initializer && initializer->getOp() != glslang::EOpFunction && initializer->getOp() != glslang::EOpLinkerObjects) { // We're on a top-level node that's not a function. Treat as an initializer, whose - // code goes into the beginning of main. + // code goes into the beginning of the entry point. initializer->traverse(this); } } @@ -2067,17 +3097,17 @@ void TGlslangToSpvTraverser::visitFunctions(const glslang::TIntermSequence& glsl { for (int f = 0; f < (int)glslFunctions.size(); ++f) { glslang::TIntermAggregate* node = glslFunctions[f]->getAsAggregate(); - if (node && (node->getOp() == glslang::EOpFunction || node->getOp() == glslang ::EOpLinkerObjects)) + if (node && (node->getOp() == glslang::EOpFunction || node->getOp() == glslang::EOpLinkerObjects)) node->traverse(this); } } void TGlslangToSpvTraverser::handleFunctionEntry(const glslang::TIntermAggregate* node) { - // SPIR-V functions should already be in the functionMap from the prepass + // SPIR-V functions should already be in the functionMap from the prepass // that called makeFunctions(). - spv::Function* function = functionMap[node->getName().c_str()]; - spv::Block* functionBlock = function->getEntryBlock(); + currentFunction = functionMap[node->getName().c_str()]; + spv::Block* functionBlock = currentFunction->getEntryBlock(); builder.setBuildPoint(functionBlock); } @@ -2087,7 +3117,7 @@ void TGlslangToSpvTraverser::translateArguments(const glslang::TIntermAggregate& glslang::TSampler sampler = {}; bool cubeCompare = false; - if (node.isTexture()) { + if (node.isTexture() || node.isImage()) { sampler = glslangArguments[0]->getAsTyped()->getType().getSampler(); cubeCompare = sampler.dim == glslang::EsdCube && sampler.arrayed && sampler.shadow; } @@ -2110,6 +3140,10 @@ void TGlslangToSpvTraverser::translateArguments(const glslang::TIntermAggregate& if (i == 0) lvalue = true; break; + case glslang::EOpSparseImageLoad: + if ((sampler.ms && i == 3) || (! sampler.ms && i == 2)) + lvalue = true; + break; case glslang::EOpSparseTexture: if ((cubeCompare && i == 3) || (! cubeCompare && i == 2)) lvalue = true; @@ -2146,7 +3180,7 @@ void TGlslangToSpvTraverser::translateArguments(const glslang::TIntermAggregate& if (i == 6) lvalue = true; break; - case glslang::EOpSparseTextureGather: + case glslang::EOpSparseTextureGather: if ((sampler.shadow && i == 3) || (! sampler.shadow && i == 2)) lvalue = true; break; @@ -2155,6 +3189,21 @@ void TGlslangToSpvTraverser::translateArguments(const glslang::TIntermAggregate& if ((sampler.shadow && i == 4) || (! sampler.shadow && i == 3)) lvalue = true; break; +#ifdef AMD_EXTENSIONS + case glslang::EOpSparseTextureGatherLod: + if (i == 3) + lvalue = true; + break; + case glslang::EOpSparseTextureGatherLodOffset: + case glslang::EOpSparseTextureGatherLodOffsets: + if (i == 4) + lvalue = true; + break; + case glslang::EOpSparseImageLoadLod: + if (i == 3) + lvalue = true; + break; +#endif default: break; } @@ -2175,9 +3224,12 @@ void TGlslangToSpvTraverser::translateArguments(glslang::TIntermUnary& node, std spv::Id TGlslangToSpvTraverser::createImageTextureFunctionCall(glslang::TIntermOperator* node) { - if (! node->isImage() && ! node->isTexture()) { + if (! node->isImage() && ! node->isTexture()) return spv::NoResult; - } + + builder.setLine(node->getLoc().line); + + auto resultType = [&node,this]{ return convertGlslangToSpvType(node->getType()); }; // Process a GLSL texturing op (will be SPV image) const glslang::TSampler sampler = node->getAsAggregate() ? node->getAsAggregate()->getSequence()[0]->getAsTyped()->getType().getSampler() @@ -2187,7 +3239,7 @@ spv::Id TGlslangToSpvTraverser::createImageTextureFunctionCall(glslang::TIntermO translateArguments(*node->getAsAggregate(), arguments); else translateArguments(*node->getAsUnaryNode(), arguments); - spv::Decoration precision = TranslatePrecisionDecoration(node->getType()); + spv::Decoration precision = TranslatePrecisionDecoration(node->getOperationPrecision()); spv::Builder::TextureParameters params = { }; params.sampler = arguments[0]; @@ -2195,27 +3247,30 @@ spv::Id TGlslangToSpvTraverser::createImageTextureFunctionCall(glslang::TIntermO glslang::TCrackedTextureOp cracked; node->crackTexture(sampler, cracked); + const bool isUnsignedResult = node->getType().getBasicType() == glslang::EbtUint; + // Check for queries if (cracked.query) { - // a sampled image needs to have the image extracted first - if (builder.isSampledImage(params.sampler)) + // OpImageQueryLod works on a sampled image, for other queries the image has to be extracted first + if (node->getOp() != glslang::EOpTextureQueryLod && builder.isSampledImage(params.sampler)) params.sampler = builder.createUnaryOp(spv::OpImage, builder.getImageType(params.sampler), params.sampler); + switch (node->getOp()) { case glslang::EOpImageQuerySize: case glslang::EOpTextureQuerySize: if (arguments.size() > 1) { params.lod = arguments[1]; - return builder.createTextureQueryCall(spv::OpImageQuerySizeLod, params); + return builder.createTextureQueryCall(spv::OpImageQuerySizeLod, params, isUnsignedResult); } else - return builder.createTextureQueryCall(spv::OpImageQuerySize, params); + return builder.createTextureQueryCall(spv::OpImageQuerySize, params, isUnsignedResult); case glslang::EOpImageQuerySamples: case glslang::EOpTextureQuerySamples: - return builder.createTextureQueryCall(spv::OpImageQuerySamples, params); + return builder.createTextureQueryCall(spv::OpImageQuerySamples, params, isUnsignedResult); case glslang::EOpTextureQueryLod: params.coords = arguments[1]; - return builder.createTextureQueryCall(spv::OpImageQueryLod, params); + return builder.createTextureQueryCall(spv::OpImageQueryLod, params, isUnsignedResult); case glslang::EOpTextureQueryLevels: - return builder.createTextureQueryCall(spv::OpImageQueryLevels, params); + return builder.createTextureQueryCall(spv::OpImageQueryLevels, params, isUnsignedResult); case glslang::EOpSparseTexelsResident: return builder.createUnaryOp(spv::OpImageSparseTexelsResident, builder.makeBoolType(), arguments[0]); default: @@ -2244,32 +3299,92 @@ spv::Id TGlslangToSpvTraverser::createImageTextureFunctionCall(glslang::TIntermO operands.push_back(spv::ImageOperandsSampleMask); operands.push_back(*(opIt++)); } - return builder.createOp(spv::OpImageRead, convertGlslangToSpvType(node->getType()), operands); + spv::Id result = builder.createOp(spv::OpImageRead, resultType(), operands); + builder.setPrecision(result, precision); + return result; } operands.push_back(*(opIt++)); +#ifdef AMD_EXTENSIONS + if (node->getOp() == glslang::EOpImageLoad || node->getOp() == glslang::EOpImageLoadLod) { +#else if (node->getOp() == glslang::EOpImageLoad) { +#endif if (sampler.ms) { operands.push_back(spv::ImageOperandsSampleMask); operands.push_back(*opIt); +#ifdef AMD_EXTENSIONS + } else if (cracked.lod) { + builder.addExtension(spv::E_SPV_AMD_shader_image_load_store_lod); + builder.addCapability(spv::CapabilityImageReadWriteLodAMD); + + operands.push_back(spv::ImageOperandsLodMask); + operands.push_back(*opIt); +#endif } - return builder.createOp(spv::OpImageRead, convertGlslangToSpvType(node->getType()), operands); if (builder.getImageTypeFormat(builder.getImageType(operands.front())) == spv::ImageFormatUnknown) builder.addCapability(spv::CapabilityStorageImageReadWithoutFormat); + + spv::Id result = builder.createOp(spv::OpImageRead, resultType(), operands); + builder.setPrecision(result, precision); + return result; +#ifdef AMD_EXTENSIONS + } else if (node->getOp() == glslang::EOpImageStore || node->getOp() == glslang::EOpImageStoreLod) { +#else } else if (node->getOp() == glslang::EOpImageStore) { +#endif if (sampler.ms) { operands.push_back(*(opIt + 1)); operands.push_back(spv::ImageOperandsSampleMask); operands.push_back(*opIt); +#ifdef AMD_EXTENSIONS + } else if (cracked.lod) { + builder.addExtension(spv::E_SPV_AMD_shader_image_load_store_lod); + builder.addCapability(spv::CapabilityImageReadWriteLodAMD); + + operands.push_back(*(opIt + 1)); + operands.push_back(spv::ImageOperandsLodMask); + operands.push_back(*opIt); +#endif } else operands.push_back(*opIt); builder.createNoResultOp(spv::OpImageWrite, operands); if (builder.getImageTypeFormat(builder.getImageType(operands.front())) == spv::ImageFormatUnknown) builder.addCapability(spv::CapabilityStorageImageWriteWithoutFormat); return spv::NoResult; - } else if (node->isSparseImage()) { - spv::MissingFunctionality("sparse image functions"); - return spv::NoResult; +#ifdef AMD_EXTENSIONS + } else if (node->getOp() == glslang::EOpSparseImageLoad || node->getOp() == glslang::EOpSparseImageLoadLod) { +#else + } else if (node->getOp() == glslang::EOpSparseImageLoad) { +#endif + builder.addCapability(spv::CapabilitySparseResidency); + if (builder.getImageTypeFormat(builder.getImageType(operands.front())) == spv::ImageFormatUnknown) + builder.addCapability(spv::CapabilityStorageImageReadWithoutFormat); + + if (sampler.ms) { + operands.push_back(spv::ImageOperandsSampleMask); + operands.push_back(*opIt++); +#ifdef AMD_EXTENSIONS + } else if (cracked.lod) { + builder.addExtension(spv::E_SPV_AMD_shader_image_load_store_lod); + builder.addCapability(spv::CapabilityImageReadWriteLodAMD); + + operands.push_back(spv::ImageOperandsLodMask); + operands.push_back(*opIt++); +#endif + } + + // Create the return type that was a special structure + spv::Id texelOut = *opIt; + spv::Id typeId0 = resultType(); + spv::Id typeId1 = builder.getDerefTypeId(texelOut); + spv::Id resultTypeId = builder.makeStructResultType(typeId0, typeId1); + + spv::Id resultId = builder.createOp(spv::OpImageSparseRead, resultTypeId, operands); + + // Decode the return type + builder.createStore(builder.createCompositeExtract(resultId, typeId1, 1), texelOut); + return builder.createCompositeExtract(resultId, typeId0, 0); } else { // Process image atomic operations @@ -2277,7 +3392,7 @@ spv::Id TGlslangToSpvTraverser::createImageTextureFunctionCall(glslang::TIntermO // as the first source operand, is required by SPIR-V atomic operations. operands.push_back(sampler.ms ? *(opIt++) : builder.makeUintConstant(0)); // For non-MS, the value should be 0 - spv::Id resultTypeId = builder.makePointer(spv::StorageClassImage, convertGlslangToSpvType(node->getType())); + spv::Id resultTypeId = builder.makePointer(spv::StorageClassImage, resultType()); spv::Id pointer = builder.createOp(spv::OpImageTexelPointer, resultTypeId, operands); std::vector operands; @@ -2285,20 +3400,71 @@ spv::Id TGlslangToSpvTraverser::createImageTextureFunctionCall(glslang::TIntermO for (; opIt != arguments.end(); ++opIt) operands.push_back(*opIt); - return createAtomicOperation(node->getOp(), precision, convertGlslangToSpvType(node->getType()), operands, node->getBasicType()); + return createAtomicOperation(node->getOp(), precision, resultType(), operands, node->getBasicType()); } } +#ifdef AMD_EXTENSIONS + // Check for fragment mask functions other than queries + if (cracked.fragMask) { + assert(sampler.ms); + + auto opIt = arguments.begin(); + std::vector operands; + + // Extract the image if necessary + if (builder.isSampledImage(params.sampler)) + params.sampler = builder.createUnaryOp(spv::OpImage, builder.getImageType(params.sampler), params.sampler); + + operands.push_back(params.sampler); + ++opIt; + + if (sampler.isSubpass()) { + // add on the (0,0) coordinate + spv::Id zero = builder.makeIntConstant(0); + std::vector comps; + comps.push_back(zero); + comps.push_back(zero); + operands.push_back(builder.makeCompositeConstant(builder.makeVectorType(builder.makeIntType(32), 2), comps)); + } + + for (; opIt != arguments.end(); ++opIt) + operands.push_back(*opIt); + + spv::Op fragMaskOp = spv::OpNop; + if (node->getOp() == glslang::EOpFragmentMaskFetch) + fragMaskOp = spv::OpFragmentMaskFetchAMD; + else if (node->getOp() == glslang::EOpFragmentFetch) + fragMaskOp = spv::OpFragmentFetchAMD; + + builder.addExtension(spv::E_SPV_AMD_shader_fragment_mask); + builder.addCapability(spv::CapabilityFragmentMaskAMD); + return builder.createOp(fragMaskOp, resultType(), operands); + } +#endif + // Check for texture functions other than queries bool sparse = node->isSparseTexture(); bool cubeCompare = sampler.dim == glslang::EsdCube && sampler.arrayed && sampler.shadow; // check for bias argument bool bias = false; +#ifdef AMD_EXTENSIONS + if (! cracked.lod && ! cracked.grad && ! cracked.fetch && ! cubeCompare) { +#else if (! cracked.lod && ! cracked.gather && ! cracked.grad && ! cracked.fetch && ! cubeCompare) { +#endif int nonBiasArgCount = 2; +#ifdef AMD_EXTENSIONS + if (cracked.gather) + ++nonBiasArgCount; // comp argument should be present when bias argument is present +#endif if (cracked.offset) ++nonBiasArgCount; +#ifdef AMD_EXTENSIONS + else if (cracked.offsets) + ++nonBiasArgCount; +#endif if (cracked.grad) nonBiasArgCount += 2; if (cracked.lodClamp) @@ -2310,6 +3476,24 @@ spv::Id TGlslangToSpvTraverser::createImageTextureFunctionCall(glslang::TIntermO bias = true; } + // See if the sampler param should really be just the SPV image part + if (cracked.fetch) { + // a fetch needs to have the image extracted first + if (builder.isSampledImage(params.sampler)) + params.sampler = builder.createUnaryOp(spv::OpImage, builder.getImageType(params.sampler), params.sampler); + } + +#ifdef AMD_EXTENSIONS + if (cracked.gather) { + const auto& sourceExtensions = glslangIntermediate->getRequestedExtensions(); + if (bias || cracked.lod || + sourceExtensions.find(glslang::E_GL_AMD_texture_gather_bias_lod) != sourceExtensions.end()) { + builder.addExtension(spv::E_SPV_AMD_texture_gather_bias_lod); + builder.addCapability(spv::CapabilityImageGatherBiasLodAMD); + } + } +#endif + // set the rest of the arguments params.coords = arguments[1]; @@ -2325,30 +3509,38 @@ spv::Id TGlslangToSpvTraverser::createImageTextureFunctionCall(glslang::TIntermO ++extraArgs; } else if (sampler.shadow) { std::vector indexes; - int comp; + int dRefComp; if (cracked.proj) - comp = 2; // "The resulting 3rd component of P in the shadow forms is used as Dref" + dRefComp = 2; // "The resulting 3rd component of P in the shadow forms is used as Dref" else - comp = builder.getNumComponents(params.coords) - 1; - indexes.push_back(comp); + dRefComp = builder.getNumComponents(params.coords) - 1; + indexes.push_back(dRefComp); params.Dref = builder.createCompositeExtract(params.coords, builder.getScalarTypeId(builder.getTypeId(params.coords)), indexes); } + + // lod if (cracked.lod) { - params.lod = arguments[2]; + params.lod = arguments[2 + extraArgs]; ++extraArgs; } else if (glslangIntermediate->getStage() != EShLangFragment) { // we need to invent the default lod for an explicit lod instruction for a non-fragment stage noImplicitLod = true; } + + // multisample if (sampler.ms) { - params.sample = arguments[2]; // For MS, "sample" should be specified + params.sample = arguments[2 + extraArgs]; // For MS, "sample" should be specified ++extraArgs; } + + // gradient if (cracked.grad) { params.gradX = arguments[2 + extraArgs]; params.gradY = arguments[3 + extraArgs]; extraArgs += 2; } + + // offset and offsets if (cracked.offset) { params.offset = arguments[2 + extraArgs]; ++extraArgs; @@ -2356,29 +3548,60 @@ spv::Id TGlslangToSpvTraverser::createImageTextureFunctionCall(glslang::TIntermO params.offsets = arguments[2 + extraArgs]; ++extraArgs; } + + // lod clamp if (cracked.lodClamp) { params.lodClamp = arguments[2 + extraArgs]; ++extraArgs; } + + // sparse if (sparse) { params.texelOut = arguments[2 + extraArgs]; ++extraArgs; } + + // gather component + if (cracked.gather && ! sampler.shadow) { + // default component is 0, if missing, otherwise an argument + if (2 + extraArgs < (int)arguments.size()) { + params.component = arguments[2 + extraArgs]; + ++extraArgs; + } else + params.component = builder.makeIntConstant(0); + } + + // bias if (bias) { params.bias = arguments[2 + extraArgs]; ++extraArgs; } - if (cracked.gather && ! sampler.shadow) { - // default component is 0, if missing, otherwise an argument - if (2 + extraArgs < (int)arguments.size()) { - params.comp = arguments[2 + extraArgs]; - ++extraArgs; - } else { - params.comp = builder.makeIntConstant(0); + + // projective component (might not to move) + // GLSL: "The texture coordinates consumed from P, not including the last component of P, + // are divided by the last component of P." + // SPIR-V: "... (u [, v] [, w], q)... It may be a vector larger than needed, but all + // unused components will appear after all used components." + if (cracked.proj) { + int projSourceComp = builder.getNumComponents(params.coords) - 1; + int projTargetComp; + switch (sampler.dim) { + case glslang::Esd1D: projTargetComp = 1; break; + case glslang::Esd2D: projTargetComp = 2; break; + case glslang::EsdRect: projTargetComp = 2; break; + default: projTargetComp = projSourceComp; break; + } + // copy the projective coordinate if we have to + if (projTargetComp != projSourceComp) { + spv::Id projComp = builder.createCompositeExtract(params.coords, + builder.getScalarTypeId(builder.getTypeId(params.coords)), + projSourceComp); + params.coords = builder.createCompositeInsert(projComp, params.coords, + builder.getTypeId(params.coords), projTargetComp); } } - return builder.createTextureCall(precision, convertGlslangToSpvType(node->getType()), sparse, cracked.fetch, cracked.proj, cracked.gather, noImplicitLod, params); + return builder.createTextureCall(precision, resultType(), sparse, cracked.fetch, cracked.proj, cracked.gather, noImplicitLod, params); } spv::Id TGlslangToSpvTraverser::handleUserFunctionCall(const glslang::TIntermAggregate* node) @@ -2404,12 +3627,14 @@ spv::Id TGlslangToSpvTraverser::handleUserFunctionCall(const glslang::TIntermAgg std::vector rValues; std::vector argTypes; for (int a = 0; a < (int)glslangArgs.size(); ++a) { + const glslang::TType& paramType = glslangArgs[a]->getAsTyped()->getType(); // build l-value builder.clearAccessChain(); glslangArgs[a]->traverse(this); - argTypes.push_back(&glslangArgs[a]->getAsTyped()->getType()); - // keep outputs as l-values, evaluate input-only as r-values - if (qualifiers[a] != glslang::EvqConstReadOnly) { + argTypes.push_back(¶mType); + // keep outputs and pass-by-originals as l-values, evaluate others as r-values + if (originalParam(qualifiers[a], paramType, function->hasImplicitThis() && a == 0) || + writableParam(qualifiers[a])) { // save l-value lValues.push_back(builder.getAccessChain()); } else { @@ -2426,16 +3651,22 @@ spv::Id TGlslangToSpvTraverser::handleUserFunctionCall(const glslang::TIntermAgg int rValueCount = 0; std::vector spvArgs; for (int a = 0; a < (int)glslangArgs.size(); ++a) { + const glslang::TType& paramType = glslangArgs[a]->getAsTyped()->getType(); spv::Id arg; - if (qualifiers[a] != glslang::EvqConstReadOnly) { + if (originalParam(qualifiers[a], paramType, function->hasImplicitThis() && a == 0)) { + builder.setAccessChain(lValues[lValueCount]); + arg = builder.accessChainGetLValue(); + ++lValueCount; + } else if (writableParam(qualifiers[a])) { // need space to hold the copy - const glslang::TType& paramType = glslangArgs[a]->getAsTyped()->getType(); arg = builder.createVariable(spv::StorageClassFunction, convertGlslangToSpvType(paramType), "param"); if (qualifiers[a] == glslang::EvqIn || qualifiers[a] == glslang::EvqInOut) { // need to copy the input into output space builder.setAccessChain(lValues[lValueCount]); spv::Id copy = accessChainLoad(*argTypes[a]); - builder.createStore(copy, arg); + builder.clearAccessChain(); + builder.setAccessChainLValue(arg); + multiTypeStore(paramType, copy); } ++lValueCount; } else { @@ -2452,11 +3683,14 @@ spv::Id TGlslangToSpvTraverser::handleUserFunctionCall(const glslang::TIntermAgg // 4. Copy back out an "out" arguments. lValueCount = 0; for (int a = 0; a < (int)glslangArgs.size(); ++a) { - if (qualifiers[a] != glslang::EvqConstReadOnly) { + const glslang::TType& paramType = glslangArgs[a]->getAsTyped()->getType(); + if (originalParam(qualifiers[a], paramType, function->hasImplicitThis() && a == 0)) + ++lValueCount; + else if (writableParam(qualifiers[a])) { if (qualifiers[a] == glslang::EvqOut || qualifiers[a] == glslang::EvqInOut) { spv::Id copy = builder.createLoad(spvArgs[a]); builder.setAccessChain(lValues[lValueCount]); - builder.accessChainStore(copy); + multiTypeStore(paramType, copy); } ++lValueCount; } @@ -2466,12 +3700,19 @@ spv::Id TGlslangToSpvTraverser::handleUserFunctionCall(const glslang::TIntermAgg } // Translate AST operation to SPV operation, already having SPV-based operands/types. -spv::Id TGlslangToSpvTraverser::createBinaryOperation(glslang::TOperator op, spv::Decoration precision, +spv::Id TGlslangToSpvTraverser::createBinaryOperation(glslang::TOperator op, spv::Decoration precision, + spv::Decoration noContraction, spv::Id typeId, spv::Id left, spv::Id right, glslang::TBasicType typeProxy, bool reduceComparison) { - bool isUnsigned = typeProxy == glslang::EbtUint; +#ifdef AMD_EXTENSIONS + bool isUnsigned = typeProxy == glslang::EbtUint || typeProxy == glslang::EbtUint64 || typeProxy == glslang::EbtUint16; + bool isFloat = typeProxy == glslang::EbtFloat || typeProxy == glslang::EbtDouble || typeProxy == glslang::EbtFloat16; +#else + bool isUnsigned = typeProxy == glslang::EbtUint || typeProxy == glslang::EbtUint64; bool isFloat = typeProxy == glslang::EbtFloat || typeProxy == glslang::EbtDouble; +#endif + bool isBool = typeProxy == glslang::EbtBool; spv::Op binOp = spv::OpNop; bool needMatchingVectors = true; // for non-matrix ops, would a scalar need to smear to match a vector? @@ -2501,7 +3742,7 @@ spv::Id TGlslangToSpvTraverser::createBinaryOperation(glslang::TOperator op, spv break; case glslang::EOpVectorTimesScalar: case glslang::EOpVectorTimesScalarAssign: - if (isFloat) { + if (isFloat && (builder.isVector(left) || builder.isVector(right))) { if (builder.isVector(right)) std::swap(left, right); assert(builder.isScalar(right)); @@ -2602,13 +3843,15 @@ spv::Id TGlslangToSpvTraverser::createBinaryOperation(glslang::TOperator op, spv if (binOp != spv::OpNop) { assert(comparison == false); if (builder.isMatrix(left) || builder.isMatrix(right)) - return createBinaryMatrixOperation(binOp, precision, typeId, left, right); + return createBinaryMatrixOperation(binOp, precision, noContraction, typeId, left, right); // No matrix involved; make both operands be the same number of components, if needed if (needMatchingVectors) builder.promoteScalar(precision, left, right); - return builder.setPrecision(builder.createBinOp(binOp, typeId, left, right), precision); + spv::Id result = builder.createBinOp(binOp, typeId, left, right); + addDecoration(result, noContraction); + return builder.setPrecision(result, precision); } if (! comparison) @@ -2616,11 +3859,9 @@ spv::Id TGlslangToSpvTraverser::createBinaryOperation(glslang::TOperator op, spv // Handle comparison instructions - if (reduceComparison && (builder.isVector(left) || builder.isMatrix(left) || builder.isAggregate(left))) { - assert(op == glslang::EOpEqual || op == glslang::EOpNotEqual); - + if (reduceComparison && (op == glslang::EOpEqual || op == glslang::EOpNotEqual) + && (builder.isVector(left) || builder.isMatrix(left) || builder.isAggregate(left))) return builder.createCompositeCompare(precision, left, right, op == glslang::EOpEqual); - } switch (op) { case glslang::EOpLessThan: @@ -2659,6 +3900,8 @@ spv::Id TGlslangToSpvTraverser::createBinaryOperation(glslang::TOperator op, spv case glslang::EOpVectorEqual: if (isFloat) binOp = spv::OpFOrdEqual; + else if (isBool) + binOp = spv::OpLogicalEqual; else binOp = spv::OpIEqual; break; @@ -2666,6 +3909,8 @@ spv::Id TGlslangToSpvTraverser::createBinaryOperation(glslang::TOperator op, spv case glslang::EOpVectorNotEqual: if (isFloat) binOp = spv::OpFOrdNotEqual; + else if (isBool) + binOp = spv::OpLogicalNotEqual; else binOp = spv::OpINotEqual; break; @@ -2673,8 +3918,11 @@ spv::Id TGlslangToSpvTraverser::createBinaryOperation(glslang::TOperator op, spv break; } - if (binOp != spv::OpNop) - return builder.setPrecision(builder.createBinOp(binOp, typeId, left, right), precision); + if (binOp != spv::OpNop) { + spv::Id result = builder.createBinOp(binOp, typeId, left, right); + addDecoration(result, noContraction); + return builder.setPrecision(result, precision); + } return 0; } @@ -2693,7 +3941,7 @@ spv::Id TGlslangToSpvTraverser::createBinaryOperation(glslang::TOperator op, spv // matrix op scalar op in {+, -, /} // scalar op matrix op in {+, -, /} // -spv::Id TGlslangToSpvTraverser::createBinaryMatrixOperation(spv::Op op, spv::Decoration precision, spv::Id typeId, spv::Id left, spv::Id right) +spv::Id TGlslangToSpvTraverser::createBinaryMatrixOperation(spv::Op op, spv::Decoration precision, spv::Decoration noContraction, spv::Id typeId, spv::Id left, spv::Id right) { bool firstClass = true; @@ -2729,10 +3977,13 @@ spv::Id TGlslangToSpvTraverser::createBinaryMatrixOperation(spv::Op op, spv::Dec break; } - if (firstClass) - return builder.setPrecision(builder.createBinOp(op, typeId, left, right), precision); + if (firstClass) { + spv::Id result = builder.createBinOp(op, typeId, left, right); + addDecoration(result, noContraction); + return builder.setPrecision(result, precision); + } - // Handle component-wise +, -, *, and / for all combinations of type. + // Handle component-wise +, -, *, %, and / for all combinations of type. // The result type of all of them is the same type as the (a) matrix operand. // The algorithm is to: // - break the matrix(es) into vectors @@ -2743,6 +3994,7 @@ spv::Id TGlslangToSpvTraverser::createBinaryMatrixOperation(spv::Op op, spv::Dec case spv::OpFAdd: case spv::OpFSub: case spv::OpFDiv: + case spv::OpFMod: case spv::OpFMul: { // one time set up... @@ -2765,8 +4017,9 @@ spv::Id TGlslangToSpvTraverser::createBinaryMatrixOperation(spv::Op op, spv::Dec indexes.push_back(c); spv::Id leftVec = leftMat ? builder.createCompositeExtract( left, vecType, indexes) : smearVec; spv::Id rightVec = rightMat ? builder.createCompositeExtract(right, vecType, indexes) : smearVec; - results.push_back(builder.createBinOp(op, vecType, leftVec, rightVec)); - builder.setPrecision(results.back(), precision); + spv::Id result = builder.createBinOp(op, vecType, leftVec, rightVec); + addDecoration(result, noContraction); + results.push_back(builder.setPrecision(result, precision)); } // put the pieces together @@ -2778,19 +4031,25 @@ spv::Id TGlslangToSpvTraverser::createBinaryMatrixOperation(spv::Op op, spv::Dec } } -spv::Id TGlslangToSpvTraverser::createUnaryOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId, spv::Id operand, glslang::TBasicType typeProxy) +spv::Id TGlslangToSpvTraverser::createUnaryOperation(glslang::TOperator op, spv::Decoration precision, spv::Decoration noContraction, spv::Id typeId, spv::Id operand, glslang::TBasicType typeProxy) { spv::Op unaryOp = spv::OpNop; + int extBuiltins = -1; int libCall = -1; - bool isUnsigned = typeProxy == glslang::EbtUint; +#ifdef AMD_EXTENSIONS + bool isUnsigned = typeProxy == glslang::EbtUint || typeProxy == glslang::EbtUint64 || typeProxy == glslang::EbtUint16; + bool isFloat = typeProxy == glslang::EbtFloat || typeProxy == glslang::EbtDouble || typeProxy == glslang::EbtFloat16; +#else + bool isUnsigned = typeProxy == glslang::EbtUint || typeProxy == glslang::EbtUint64; bool isFloat = typeProxy == glslang::EbtFloat || typeProxy == glslang::EbtDouble; +#endif switch (op) { case glslang::EOpNegative: if (isFloat) { unaryOp = spv::OpFNegate; if (builder.isMatrixType(typeId)) - return createUnaryMatrixOperation(unaryOp, precision, typeId, operand, typeProxy); + return createUnaryMatrixOperation(unaryOp, precision, noContraction, typeId, operand, typeProxy); } else unaryOp = spv::OpSNegate; break; @@ -2908,11 +4167,24 @@ spv::Id TGlslangToSpvTraverser::createUnaryOperation(glslang::TOperator op, spv: case glslang::EOpIsInf: unaryOp = spv::OpIsInf; break; + case glslang::EOpIsFinite: + unaryOp = spv::OpIsFinite; + break; case glslang::EOpFloatBitsToInt: case glslang::EOpFloatBitsToUint: case glslang::EOpIntBitsToFloat: case glslang::EOpUintBitsToFloat: + case glslang::EOpDoubleBitsToInt64: + case glslang::EOpDoubleBitsToUint64: + case glslang::EOpInt64BitsToDouble: + case glslang::EOpUint64BitsToDouble: +#ifdef AMD_EXTENSIONS + case glslang::EOpFloat16BitsToInt16: + case glslang::EOpFloat16BitsToUint16: + case glslang::EOpInt16BitsToFloat16: + case glslang::EOpUint16BitsToFloat16: +#endif unaryOp = spv::OpBitcast; break; @@ -2953,6 +4225,28 @@ spv::Id TGlslangToSpvTraverser::createUnaryOperation(glslang::TOperator op, spv: libCall = spv::GLSLstd450UnpackDouble2x32; break; + case glslang::EOpPackInt2x32: + case glslang::EOpUnpackInt2x32: + case glslang::EOpPackUint2x32: + case glslang::EOpUnpackUint2x32: + unaryOp = spv::OpBitcast; + break; + +#ifdef AMD_EXTENSIONS + case glslang::EOpPackInt2x16: + case glslang::EOpUnpackInt2x16: + case glslang::EOpPackUint2x16: + case glslang::EOpUnpackUint2x16: + case glslang::EOpPackInt4x16: + case glslang::EOpUnpackInt4x16: + case glslang::EOpPackUint4x16: + case glslang::EOpUnpackUint4x16: + case glslang::EOpPackFloat2x16: + case glslang::EOpUnpackFloat2x16: + unaryOp = spv::OpBitcast; + break; +#endif + case glslang::EOpDPdx: unaryOp = spv::OpDPdx; break; @@ -3036,6 +4330,54 @@ spv::Id TGlslangToSpvTraverser::createUnaryOperation(glslang::TOperator op, spv: libCall = spv::GLSLstd450FindSMsb; break; + case glslang::EOpBallot: + case glslang::EOpReadFirstInvocation: + case glslang::EOpAnyInvocation: + case glslang::EOpAllInvocations: + case glslang::EOpAllInvocationsEqual: +#ifdef AMD_EXTENSIONS + case glslang::EOpMinInvocations: + case glslang::EOpMaxInvocations: + case glslang::EOpAddInvocations: + case glslang::EOpMinInvocationsNonUniform: + case glslang::EOpMaxInvocationsNonUniform: + case glslang::EOpAddInvocationsNonUniform: + case glslang::EOpMinInvocationsInclusiveScan: + case glslang::EOpMaxInvocationsInclusiveScan: + case glslang::EOpAddInvocationsInclusiveScan: + case glslang::EOpMinInvocationsInclusiveScanNonUniform: + case glslang::EOpMaxInvocationsInclusiveScanNonUniform: + case glslang::EOpAddInvocationsInclusiveScanNonUniform: + case glslang::EOpMinInvocationsExclusiveScan: + case glslang::EOpMaxInvocationsExclusiveScan: + case glslang::EOpAddInvocationsExclusiveScan: + case glslang::EOpMinInvocationsExclusiveScanNonUniform: + case glslang::EOpMaxInvocationsExclusiveScanNonUniform: + case glslang::EOpAddInvocationsExclusiveScanNonUniform: +#endif + { + std::vector operands; + operands.push_back(operand); + return createInvocationsOperation(op, typeId, operands, typeProxy); + } + +#ifdef AMD_EXTENSIONS + case glslang::EOpMbcnt: + extBuiltins = getExtBuiltins(spv::E_SPV_AMD_shader_ballot); + libCall = spv::MbcntAMD; + break; + + case glslang::EOpCubeFaceIndex: + extBuiltins = getExtBuiltins(spv::E_SPV_AMD_gcn_shader); + libCall = spv::CubeFaceIndexAMD; + break; + + case glslang::EOpCubeFaceCoord: + extBuiltins = getExtBuiltins(spv::E_SPV_AMD_gcn_shader); + libCall = spv::CubeFaceCoordAMD; + break; +#endif + default: return 0; } @@ -3044,15 +4386,17 @@ spv::Id TGlslangToSpvTraverser::createUnaryOperation(glslang::TOperator op, spv: if (libCall >= 0) { std::vector args; args.push_back(operand); - id = builder.createBuiltinCall(typeId, stdBuiltins, libCall, args); - } else + id = builder.createBuiltinCall(typeId, extBuiltins >= 0 ? extBuiltins : stdBuiltins, libCall, args); + } else { id = builder.createUnaryOp(unaryOp, typeId, operand); + } + addDecoration(id, noContraction); return builder.setPrecision(id, precision); } // Create a unary operation on a matrix -spv::Id TGlslangToSpvTraverser::createUnaryMatrixOperation(spv::Op op, spv::Decoration precision, spv::Id typeId, spv::Id operand, glslang::TBasicType /* typeProxy */) +spv::Id TGlslangToSpvTraverser::createUnaryMatrixOperation(spv::Op op, spv::Decoration precision, spv::Decoration noContraction, spv::Id typeId, spv::Id operand, glslang::TBasicType /* typeProxy */) { // Handle unary operations vector by vector. // The result type is the same type as the original type. @@ -3064,35 +4408,50 @@ spv::Id TGlslangToSpvTraverser::createUnaryMatrixOperation(spv::Op op, spv::Deco // get the types sorted out int numCols = builder.getNumColumns(operand); int numRows = builder.getNumRows(operand); - spv::Id scalarType = builder.getScalarTypeId(typeId); - spv::Id vecType = builder.makeVectorType(scalarType, numRows); + spv::Id srcVecType = builder.makeVectorType(builder.getScalarTypeId(builder.getTypeId(operand)), numRows); + spv::Id destVecType = builder.makeVectorType(builder.getScalarTypeId(typeId), numRows); std::vector results; // do each vector op for (int c = 0; c < numCols; ++c) { std::vector indexes; indexes.push_back(c); - spv::Id vec = builder.createCompositeExtract(operand, vecType, indexes); - results.push_back(builder.createUnaryOp(op, vecType, vec)); - builder.setPrecision(results.back(), precision); + spv::Id srcVec = builder.createCompositeExtract(operand, srcVecType, indexes); + spv::Id destVec = builder.createUnaryOp(op, destVecType, srcVec); + addDecoration(destVec, noContraction); + results.push_back(builder.setPrecision(destVec, precision)); } // put the pieces together return builder.setPrecision(builder.createCompositeConstruct(typeId, results), precision); } -spv::Id TGlslangToSpvTraverser::createConversion(glslang::TOperator op, spv::Decoration precision, spv::Id destType, spv::Id operand) +spv::Id TGlslangToSpvTraverser::createConversion(glslang::TOperator op, spv::Decoration precision, spv::Decoration noContraction, spv::Id destType, spv::Id operand, glslang::TBasicType typeProxy) { spv::Op convOp = spv::OpNop; spv::Id zero = 0; spv::Id one = 0; + spv::Id type = 0; int vectorSize = builder.isVectorType(destType) ? builder.getNumTypeComponents(destType) : 0; switch (op) { case glslang::EOpConvIntToBool: case glslang::EOpConvUintToBool: - zero = builder.makeUintConstant(0); + case glslang::EOpConvInt64ToBool: + case glslang::EOpConvUint64ToBool: +#ifdef AMD_EXTENSIONS + case glslang::EOpConvInt16ToBool: + case glslang::EOpConvUint16ToBool: +#endif + if (op == glslang::EOpConvInt64ToBool || op == glslang::EOpConvUint64ToBool) + zero = builder.makeUint64Constant(0); +#ifdef AMD_EXTENSIONS + else if (op == glslang::EOpConvInt16ToBool || op == glslang::EOpConvUint16ToBool) + zero = builder.makeUint16Constant(0); +#endif + else + zero = builder.makeUintConstant(0); zero = makeSmearedConstant(zero, vectorSize); return builder.createBinOp(spv::OpINotEqual, destType, operand, zero); @@ -3106,56 +4465,296 @@ spv::Id TGlslangToSpvTraverser::createConversion(glslang::TOperator op, spv::Dec zero = makeSmearedConstant(zero, vectorSize); return builder.createBinOp(spv::OpFOrdNotEqual, destType, operand, zero); +#ifdef AMD_EXTENSIONS + case glslang::EOpConvFloat16ToBool: + zero = builder.makeFloat16Constant(0.0F); + zero = makeSmearedConstant(zero, vectorSize); + return builder.createBinOp(spv::OpFOrdNotEqual, destType, operand, zero); +#endif + case glslang::EOpConvBoolToFloat: convOp = spv::OpSelect; - zero = builder.makeFloatConstant(0.0); - one = builder.makeFloatConstant(1.0); + zero = builder.makeFloatConstant(0.0F); + one = builder.makeFloatConstant(1.0F); break; + case glslang::EOpConvBoolToDouble: convOp = spv::OpSelect; zero = builder.makeDoubleConstant(0.0); one = builder.makeDoubleConstant(1.0); break; + +#ifdef AMD_EXTENSIONS + case glslang::EOpConvBoolToFloat16: + convOp = spv::OpSelect; + zero = builder.makeFloat16Constant(0.0F); + one = builder.makeFloat16Constant(1.0F); + break; +#endif + case glslang::EOpConvBoolToInt: - zero = builder.makeIntConstant(0); - one = builder.makeIntConstant(1); + case glslang::EOpConvBoolToInt64: +#ifdef AMD_EXTENSIONS + case glslang::EOpConvBoolToInt16: +#endif + if (op == glslang::EOpConvBoolToInt64) + zero = builder.makeInt64Constant(0); +#ifdef AMD_EXTENSIONS + else if (op == glslang::EOpConvBoolToInt16) + zero = builder.makeInt16Constant(0); +#endif + else + zero = builder.makeIntConstant(0); + + if (op == glslang::EOpConvBoolToInt64) + one = builder.makeInt64Constant(1); +#ifdef AMD_EXTENSIONS + else if (op == glslang::EOpConvBoolToInt16) + one = builder.makeInt16Constant(1); +#endif + else + one = builder.makeIntConstant(1); + convOp = spv::OpSelect; break; + case glslang::EOpConvBoolToUint: - zero = builder.makeUintConstant(0); - one = builder.makeUintConstant(1); + case glslang::EOpConvBoolToUint64: +#ifdef AMD_EXTENSIONS + case glslang::EOpConvBoolToUint16: +#endif + if (op == glslang::EOpConvBoolToUint64) + zero = builder.makeUint64Constant(0); +#ifdef AMD_EXTENSIONS + else if (op == glslang::EOpConvBoolToUint16) + zero = builder.makeUint16Constant(0); +#endif + else + zero = builder.makeUintConstant(0); + + if (op == glslang::EOpConvBoolToUint64) + one = builder.makeUint64Constant(1); +#ifdef AMD_EXTENSIONS + else if (op == glslang::EOpConvBoolToUint16) + one = builder.makeUint16Constant(1); +#endif + else + one = builder.makeUintConstant(1); + convOp = spv::OpSelect; break; case glslang::EOpConvIntToFloat: case glslang::EOpConvIntToDouble: + case glslang::EOpConvInt64ToFloat: + case glslang::EOpConvInt64ToDouble: +#ifdef AMD_EXTENSIONS + case glslang::EOpConvInt16ToFloat: + case glslang::EOpConvInt16ToDouble: + case glslang::EOpConvInt16ToFloat16: + case glslang::EOpConvIntToFloat16: + case glslang::EOpConvInt64ToFloat16: +#endif convOp = spv::OpConvertSToF; break; case glslang::EOpConvUintToFloat: case glslang::EOpConvUintToDouble: + case glslang::EOpConvUint64ToFloat: + case glslang::EOpConvUint64ToDouble: +#ifdef AMD_EXTENSIONS + case glslang::EOpConvUint16ToFloat: + case glslang::EOpConvUint16ToDouble: + case glslang::EOpConvUint16ToFloat16: + case glslang::EOpConvUintToFloat16: + case glslang::EOpConvUint64ToFloat16: +#endif convOp = spv::OpConvertUToF; break; case glslang::EOpConvDoubleToFloat: case glslang::EOpConvFloatToDouble: +#ifdef AMD_EXTENSIONS + case glslang::EOpConvDoubleToFloat16: + case glslang::EOpConvFloat16ToDouble: + case glslang::EOpConvFloatToFloat16: + case glslang::EOpConvFloat16ToFloat: +#endif convOp = spv::OpFConvert; + if (builder.isMatrixType(destType)) + return createUnaryMatrixOperation(convOp, precision, noContraction, destType, operand, typeProxy); break; case glslang::EOpConvFloatToInt: case glslang::EOpConvDoubleToInt: + case glslang::EOpConvFloatToInt64: + case glslang::EOpConvDoubleToInt64: +#ifdef AMD_EXTENSIONS + case glslang::EOpConvFloatToInt16: + case glslang::EOpConvDoubleToInt16: + case glslang::EOpConvFloat16ToInt16: + case glslang::EOpConvFloat16ToInt: + case glslang::EOpConvFloat16ToInt64: +#endif convOp = spv::OpConvertFToS; break; case glslang::EOpConvUintToInt: case glslang::EOpConvIntToUint: + case glslang::EOpConvUint64ToInt64: + case glslang::EOpConvInt64ToUint64: +#ifdef AMD_EXTENSIONS + case glslang::EOpConvUint16ToInt16: + case glslang::EOpConvInt16ToUint16: +#endif + if (builder.isInSpecConstCodeGenMode()) { + // Build zero scalar or vector for OpIAdd. + if (op == glslang::EOpConvUint64ToInt64 || op == glslang::EOpConvInt64ToUint64) + zero = builder.makeUint64Constant(0); +#ifdef AMD_EXTENSIONS + else if (op == glslang::EOpConvUint16ToInt16 || op == glslang::EOpConvInt16ToUint16) + zero = builder.makeUint16Constant(0); +#endif + else + zero = builder.makeUintConstant(0); + + zero = makeSmearedConstant(zero, vectorSize); + // Use OpIAdd, instead of OpBitcast to do the conversion when + // generating for OpSpecConstantOp instruction. + return builder.createBinOp(spv::OpIAdd, destType, operand, zero); + } + // For normal run-time conversion instruction, use OpBitcast. convOp = spv::OpBitcast; break; case glslang::EOpConvFloatToUint: case glslang::EOpConvDoubleToUint: + case glslang::EOpConvFloatToUint64: + case glslang::EOpConvDoubleToUint64: +#ifdef AMD_EXTENSIONS + case glslang::EOpConvFloatToUint16: + case glslang::EOpConvDoubleToUint16: + case glslang::EOpConvFloat16ToUint16: + case glslang::EOpConvFloat16ToUint: + case glslang::EOpConvFloat16ToUint64: +#endif convOp = spv::OpConvertFToU; break; + + case glslang::EOpConvIntToInt64: + case glslang::EOpConvInt64ToInt: +#ifdef AMD_EXTENSIONS + case glslang::EOpConvIntToInt16: + case glslang::EOpConvInt16ToInt: + case glslang::EOpConvInt64ToInt16: + case glslang::EOpConvInt16ToInt64: +#endif + convOp = spv::OpSConvert; + break; + + case glslang::EOpConvUintToUint64: + case glslang::EOpConvUint64ToUint: +#ifdef AMD_EXTENSIONS + case glslang::EOpConvUintToUint16: + case glslang::EOpConvUint16ToUint: + case glslang::EOpConvUint64ToUint16: + case glslang::EOpConvUint16ToUint64: +#endif + convOp = spv::OpUConvert; + break; + + case glslang::EOpConvIntToUint64: + case glslang::EOpConvInt64ToUint: + case glslang::EOpConvUint64ToInt: + case glslang::EOpConvUintToInt64: +#ifdef AMD_EXTENSIONS + case glslang::EOpConvInt16ToUint: + case glslang::EOpConvUintToInt16: + case glslang::EOpConvInt16ToUint64: + case glslang::EOpConvUint64ToInt16: + case glslang::EOpConvUint16ToInt: + case glslang::EOpConvIntToUint16: + case glslang::EOpConvUint16ToInt64: + case glslang::EOpConvInt64ToUint16: +#endif + // OpSConvert/OpUConvert + OpBitCast + switch (op) { + case glslang::EOpConvIntToUint64: +#ifdef AMD_EXTENSIONS + case glslang::EOpConvInt16ToUint64: +#endif + convOp = spv::OpSConvert; + type = builder.makeIntType(64); + break; + case glslang::EOpConvInt64ToUint: +#ifdef AMD_EXTENSIONS + case glslang::EOpConvInt16ToUint: +#endif + convOp = spv::OpSConvert; + type = builder.makeIntType(32); + break; + case glslang::EOpConvUint64ToInt: +#ifdef AMD_EXTENSIONS + case glslang::EOpConvUint16ToInt: +#endif + convOp = spv::OpUConvert; + type = builder.makeUintType(32); + break; + case glslang::EOpConvUintToInt64: +#ifdef AMD_EXTENSIONS + case glslang::EOpConvUint16ToInt64: +#endif + convOp = spv::OpUConvert; + type = builder.makeUintType(64); + break; +#ifdef AMD_EXTENSIONS + case glslang::EOpConvUintToInt16: + case glslang::EOpConvUint64ToInt16: + convOp = spv::OpUConvert; + type = builder.makeUintType(16); + break; + case glslang::EOpConvIntToUint16: + case glslang::EOpConvInt64ToUint16: + convOp = spv::OpSConvert; + type = builder.makeIntType(16); + break; +#endif + default: + assert(0); + break; + } + + if (vectorSize > 0) + type = builder.makeVectorType(type, vectorSize); + + operand = builder.createUnaryOp(convOp, type, operand); + + if (builder.isInSpecConstCodeGenMode()) { + // Build zero scalar or vector for OpIAdd. +#ifdef AMD_EXTENSIONS + if (op == glslang::EOpConvIntToUint64 || op == glslang::EOpConvUintToInt64 || + op == glslang::EOpConvInt16ToUint64 || op == glslang::EOpConvUint16ToInt64) + zero = builder.makeUint64Constant(0); + else if (op == glslang::EOpConvIntToUint16 || op == glslang::EOpConvUintToInt16 || + op == glslang::EOpConvInt64ToUint16 || op == glslang::EOpConvUint64ToInt16) + zero = builder.makeUint16Constant(0); + else + zero = builder.makeUintConstant(0); +#else + if (op == glslang::EOpConvIntToUint64 || op == glslang::EOpConvUintToInt64) + zero = builder.makeUint64Constant(0); + else + zero = builder.makeUintConstant(0); +#endif + + zero = makeSmearedConstant(zero, vectorSize); + // Use OpIAdd, instead of OpBitcast to do the conversion when + // generating for OpSpecConstantOp instruction. + return builder.createBinOp(spv::OpIAdd, destType, operand, zero); + } + // For normal run-time conversion instruction, use OpBitcast. + convOp = spv::OpBitcast; + break; default: break; } @@ -3194,34 +4793,45 @@ spv::Id TGlslangToSpvTraverser::createAtomicOperation(glslang::TOperator op, spv switch (op) { case glslang::EOpAtomicAdd: case glslang::EOpImageAtomicAdd: + case glslang::EOpAtomicCounterAdd: opCode = spv::OpAtomicIAdd; break; + case glslang::EOpAtomicCounterSubtract: + opCode = spv::OpAtomicISub; + break; case glslang::EOpAtomicMin: case glslang::EOpImageAtomicMin: - opCode = typeProxy == glslang::EbtUint ? spv::OpAtomicUMin : spv::OpAtomicSMin; + case glslang::EOpAtomicCounterMin: + opCode = (typeProxy == glslang::EbtUint || typeProxy == glslang::EbtUint64) ? spv::OpAtomicUMin : spv::OpAtomicSMin; break; case glslang::EOpAtomicMax: case glslang::EOpImageAtomicMax: - opCode = typeProxy == glslang::EbtUint ? spv::OpAtomicUMax : spv::OpAtomicSMax; + case glslang::EOpAtomicCounterMax: + opCode = (typeProxy == glslang::EbtUint || typeProxy == glslang::EbtUint64) ? spv::OpAtomicUMax : spv::OpAtomicSMax; break; case glslang::EOpAtomicAnd: case glslang::EOpImageAtomicAnd: + case glslang::EOpAtomicCounterAnd: opCode = spv::OpAtomicAnd; break; case glslang::EOpAtomicOr: case glslang::EOpImageAtomicOr: + case glslang::EOpAtomicCounterOr: opCode = spv::OpAtomicOr; break; case glslang::EOpAtomicXor: case glslang::EOpImageAtomicXor: + case glslang::EOpAtomicCounterXor: opCode = spv::OpAtomicXor; break; case glslang::EOpAtomicExchange: case glslang::EOpImageAtomicExchange: + case glslang::EOpAtomicCounterExchange: opCode = spv::OpAtomicExchange; break; case glslang::EOpAtomicCompSwap: case glslang::EOpImageAtomicCompSwap: + case glslang::EOpAtomicCounterCompSwap: opCode = spv::OpAtomicCompareExchange; break; case glslang::EOpAtomicCounterIncrement: @@ -3238,11 +4848,15 @@ spv::Id TGlslangToSpvTraverser::createAtomicOperation(glslang::TOperator op, spv break; } + if (typeProxy == glslang::EbtInt64 || typeProxy == glslang::EbtUint64) + builder.addCapability(spv::CapabilityInt64Atomics); + // Sort out the operands // - mapping from glslang -> SPV // - there are extra SPV operands with no glslang source // - compare-exchange swaps the value and comparator // - compare-exchange has an extra memory semantics + // - EOpAtomicCounterDecrement needs a post decrement std::vector spvAtomicOperands; // hold the spv operands auto opIt = operands.begin(); // walk the glslang operands spvAtomicOperands.push_back(*(opIt++)); @@ -3261,20 +4875,303 @@ spv::Id TGlslangToSpvTraverser::createAtomicOperation(glslang::TOperator op, spv for (; opIt != operands.end(); ++opIt) spvAtomicOperands.push_back(*opIt); - return builder.createOp(opCode, typeId, spvAtomicOperands); + spv::Id resultId = builder.createOp(opCode, typeId, spvAtomicOperands); + + // GLSL and HLSL atomic-counter decrement return post-decrement value, + // while SPIR-V returns pre-decrement value. Translate between these semantics. + if (op == glslang::EOpAtomicCounterDecrement) + resultId = builder.createBinOp(spv::OpISub, typeId, resultId, builder.makeIntConstant(1)); + + return resultId; +} + +// Create group invocation operations. +spv::Id TGlslangToSpvTraverser::createInvocationsOperation(glslang::TOperator op, spv::Id typeId, std::vector& operands, glslang::TBasicType typeProxy) +{ +#ifdef AMD_EXTENSIONS + bool isUnsigned = typeProxy == glslang::EbtUint || typeProxy == glslang::EbtUint64; + bool isFloat = typeProxy == glslang::EbtFloat || typeProxy == glslang::EbtDouble || typeProxy == glslang::EbtFloat16; +#endif + + spv::Op opCode = spv::OpNop; + std::vector spvGroupOperands; + spv::GroupOperation groupOperation = spv::GroupOperationMax; + + if (op == glslang::EOpBallot || op == glslang::EOpReadFirstInvocation || + op == glslang::EOpReadInvocation) { + builder.addExtension(spv::E_SPV_KHR_shader_ballot); + builder.addCapability(spv::CapabilitySubgroupBallotKHR); + } else if (op == glslang::EOpAnyInvocation || + op == glslang::EOpAllInvocations || + op == glslang::EOpAllInvocationsEqual) { + builder.addExtension(spv::E_SPV_KHR_subgroup_vote); + builder.addCapability(spv::CapabilitySubgroupVoteKHR); + } else { + builder.addCapability(spv::CapabilityGroups); +#ifdef AMD_EXTENSIONS + if (op == glslang::EOpMinInvocationsNonUniform || + op == glslang::EOpMaxInvocationsNonUniform || + op == glslang::EOpAddInvocationsNonUniform || + op == glslang::EOpMinInvocationsInclusiveScanNonUniform || + op == glslang::EOpMaxInvocationsInclusiveScanNonUniform || + op == glslang::EOpAddInvocationsInclusiveScanNonUniform || + op == glslang::EOpMinInvocationsExclusiveScanNonUniform || + op == glslang::EOpMaxInvocationsExclusiveScanNonUniform || + op == glslang::EOpAddInvocationsExclusiveScanNonUniform) + builder.addExtension(spv::E_SPV_AMD_shader_ballot); +#endif + + spvGroupOperands.push_back(builder.makeUintConstant(spv::ScopeSubgroup)); +#ifdef AMD_EXTENSIONS + switch (op) { + case glslang::EOpMinInvocations: + case glslang::EOpMaxInvocations: + case glslang::EOpAddInvocations: + case glslang::EOpMinInvocationsNonUniform: + case glslang::EOpMaxInvocationsNonUniform: + case glslang::EOpAddInvocationsNonUniform: + groupOperation = spv::GroupOperationReduce; + spvGroupOperands.push_back(groupOperation); + break; + case glslang::EOpMinInvocationsInclusiveScan: + case glslang::EOpMaxInvocationsInclusiveScan: + case glslang::EOpAddInvocationsInclusiveScan: + case glslang::EOpMinInvocationsInclusiveScanNonUniform: + case glslang::EOpMaxInvocationsInclusiveScanNonUniform: + case glslang::EOpAddInvocationsInclusiveScanNonUniform: + groupOperation = spv::GroupOperationInclusiveScan; + spvGroupOperands.push_back(groupOperation); + break; + case glslang::EOpMinInvocationsExclusiveScan: + case glslang::EOpMaxInvocationsExclusiveScan: + case glslang::EOpAddInvocationsExclusiveScan: + case glslang::EOpMinInvocationsExclusiveScanNonUniform: + case glslang::EOpMaxInvocationsExclusiveScanNonUniform: + case glslang::EOpAddInvocationsExclusiveScanNonUniform: + groupOperation = spv::GroupOperationExclusiveScan; + spvGroupOperands.push_back(groupOperation); + break; + default: + break; + } +#endif + } + + for (auto opIt = operands.begin(); opIt != operands.end(); ++opIt) + spvGroupOperands.push_back(*opIt); + + switch (op) { + case glslang::EOpAnyInvocation: + opCode = spv::OpSubgroupAnyKHR; + break; + case glslang::EOpAllInvocations: + opCode = spv::OpSubgroupAllKHR; + break; + case glslang::EOpAllInvocationsEqual: + opCode = spv::OpSubgroupAllEqualKHR; + break; + case glslang::EOpReadInvocation: + opCode = spv::OpSubgroupReadInvocationKHR; + if (builder.isVectorType(typeId)) + return CreateInvocationsVectorOperation(opCode, groupOperation, typeId, operands); + break; + case glslang::EOpReadFirstInvocation: + opCode = spv::OpSubgroupFirstInvocationKHR; + break; + case glslang::EOpBallot: + { + // NOTE: According to the spec, the result type of "OpSubgroupBallotKHR" must be a 4 component vector of 32 + // bit integer types. The GLSL built-in function "ballotARB()" assumes the maximum number of invocations in + // a subgroup is 64. Thus, we have to convert uvec4.xy to uint64_t as follow: + // + // result = Bitcast(SubgroupBallotKHR(Predicate).xy) + // + spv::Id uintType = builder.makeUintType(32); + spv::Id uvec4Type = builder.makeVectorType(uintType, 4); + spv::Id result = builder.createOp(spv::OpSubgroupBallotKHR, uvec4Type, spvGroupOperands); + + std::vector components; + components.push_back(builder.createCompositeExtract(result, uintType, 0)); + components.push_back(builder.createCompositeExtract(result, uintType, 1)); + + spv::Id uvec2Type = builder.makeVectorType(uintType, 2); + return builder.createUnaryOp(spv::OpBitcast, typeId, + builder.createCompositeConstruct(uvec2Type, components)); + } + +#ifdef AMD_EXTENSIONS + case glslang::EOpMinInvocations: + case glslang::EOpMaxInvocations: + case glslang::EOpAddInvocations: + case glslang::EOpMinInvocationsInclusiveScan: + case glslang::EOpMaxInvocationsInclusiveScan: + case glslang::EOpAddInvocationsInclusiveScan: + case glslang::EOpMinInvocationsExclusiveScan: + case glslang::EOpMaxInvocationsExclusiveScan: + case glslang::EOpAddInvocationsExclusiveScan: + if (op == glslang::EOpMinInvocations || + op == glslang::EOpMinInvocationsInclusiveScan || + op == glslang::EOpMinInvocationsExclusiveScan) { + if (isFloat) + opCode = spv::OpGroupFMin; + else { + if (isUnsigned) + opCode = spv::OpGroupUMin; + else + opCode = spv::OpGroupSMin; + } + } else if (op == glslang::EOpMaxInvocations || + op == glslang::EOpMaxInvocationsInclusiveScan || + op == glslang::EOpMaxInvocationsExclusiveScan) { + if (isFloat) + opCode = spv::OpGroupFMax; + else { + if (isUnsigned) + opCode = spv::OpGroupUMax; + else + opCode = spv::OpGroupSMax; + } + } else { + if (isFloat) + opCode = spv::OpGroupFAdd; + else + opCode = spv::OpGroupIAdd; + } + + if (builder.isVectorType(typeId)) + return CreateInvocationsVectorOperation(opCode, groupOperation, typeId, operands); + + break; + case glslang::EOpMinInvocationsNonUniform: + case glslang::EOpMaxInvocationsNonUniform: + case glslang::EOpAddInvocationsNonUniform: + case glslang::EOpMinInvocationsInclusiveScanNonUniform: + case glslang::EOpMaxInvocationsInclusiveScanNonUniform: + case glslang::EOpAddInvocationsInclusiveScanNonUniform: + case glslang::EOpMinInvocationsExclusiveScanNonUniform: + case glslang::EOpMaxInvocationsExclusiveScanNonUniform: + case glslang::EOpAddInvocationsExclusiveScanNonUniform: + if (op == glslang::EOpMinInvocationsNonUniform || + op == glslang::EOpMinInvocationsInclusiveScanNonUniform || + op == glslang::EOpMinInvocationsExclusiveScanNonUniform) { + if (isFloat) + opCode = spv::OpGroupFMinNonUniformAMD; + else { + if (isUnsigned) + opCode = spv::OpGroupUMinNonUniformAMD; + else + opCode = spv::OpGroupSMinNonUniformAMD; + } + } + else if (op == glslang::EOpMaxInvocationsNonUniform || + op == glslang::EOpMaxInvocationsInclusiveScanNonUniform || + op == glslang::EOpMaxInvocationsExclusiveScanNonUniform) { + if (isFloat) + opCode = spv::OpGroupFMaxNonUniformAMD; + else { + if (isUnsigned) + opCode = spv::OpGroupUMaxNonUniformAMD; + else + opCode = spv::OpGroupSMaxNonUniformAMD; + } + } + else { + if (isFloat) + opCode = spv::OpGroupFAddNonUniformAMD; + else + opCode = spv::OpGroupIAddNonUniformAMD; + } + + if (builder.isVectorType(typeId)) + return CreateInvocationsVectorOperation(opCode, groupOperation, typeId, operands); + + break; +#endif + default: + logger->missingFunctionality("invocation operation"); + return spv::NoResult; + } + + assert(opCode != spv::OpNop); + return builder.createOp(opCode, typeId, spvGroupOperands); +} + +// Create group invocation operations on a vector +spv::Id TGlslangToSpvTraverser::CreateInvocationsVectorOperation(spv::Op op, spv::GroupOperation groupOperation, spv::Id typeId, std::vector& operands) +{ +#ifdef AMD_EXTENSIONS + assert(op == spv::OpGroupFMin || op == spv::OpGroupUMin || op == spv::OpGroupSMin || + op == spv::OpGroupFMax || op == spv::OpGroupUMax || op == spv::OpGroupSMax || + op == spv::OpGroupFAdd || op == spv::OpGroupIAdd || op == spv::OpGroupBroadcast || + op == spv::OpSubgroupReadInvocationKHR || + op == spv::OpGroupFMinNonUniformAMD || op == spv::OpGroupUMinNonUniformAMD || op == spv::OpGroupSMinNonUniformAMD || + op == spv::OpGroupFMaxNonUniformAMD || op == spv::OpGroupUMaxNonUniformAMD || op == spv::OpGroupSMaxNonUniformAMD || + op == spv::OpGroupFAddNonUniformAMD || op == spv::OpGroupIAddNonUniformAMD); +#else + assert(op == spv::OpGroupFMin || op == spv::OpGroupUMin || op == spv::OpGroupSMin || + op == spv::OpGroupFMax || op == spv::OpGroupUMax || op == spv::OpGroupSMax || + op == spv::OpGroupFAdd || op == spv::OpGroupIAdd || op == spv::OpGroupBroadcast || + op == spv::OpSubgroupReadInvocationKHR); +#endif + + // Handle group invocation operations scalar by scalar. + // The result type is the same type as the original type. + // The algorithm is to: + // - break the vector into scalars + // - apply the operation to each scalar + // - make a vector out the scalar results + + // get the types sorted out + int numComponents = builder.getNumComponents(operands[0]); + spv::Id scalarType = builder.getScalarTypeId(builder.getTypeId(operands[0])); + std::vector results; + + // do each scalar op + for (int comp = 0; comp < numComponents; ++comp) { + std::vector indexes; + indexes.push_back(comp); + spv::Id scalar = builder.createCompositeExtract(operands[0], scalarType, indexes); + std::vector spvGroupOperands; + if (op == spv::OpSubgroupReadInvocationKHR) { + spvGroupOperands.push_back(scalar); + spvGroupOperands.push_back(operands[1]); + } else if (op == spv::OpGroupBroadcast) { + spvGroupOperands.push_back(builder.makeUintConstant(spv::ScopeSubgroup)); + spvGroupOperands.push_back(scalar); + spvGroupOperands.push_back(operands[1]); + } else { + spvGroupOperands.push_back(builder.makeUintConstant(spv::ScopeSubgroup)); + spvGroupOperands.push_back(groupOperation); + spvGroupOperands.push_back(scalar); + } + + results.push_back(builder.createOp(op, scalarType, spvGroupOperands)); + } + + // put the pieces together + return builder.createCompositeConstruct(typeId, results); } spv::Id TGlslangToSpvTraverser::createMiscOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId, std::vector& operands, glslang::TBasicType typeProxy) { - bool isUnsigned = typeProxy == glslang::EbtUint; +#ifdef AMD_EXTENSIONS + bool isUnsigned = typeProxy == glslang::EbtUint || typeProxy == glslang::EbtUint64 || typeProxy == glslang::EbtUint16; + bool isFloat = typeProxy == glslang::EbtFloat || typeProxy == glslang::EbtDouble || typeProxy == glslang::EbtFloat16; +#else + bool isUnsigned = typeProxy == glslang::EbtUint || typeProxy == glslang::EbtUint64; bool isFloat = typeProxy == glslang::EbtFloat || typeProxy == glslang::EbtDouble; +#endif spv::Op opCode = spv::OpNop; + int extBuiltins = -1; int libCall = -1; size_t consumedOperands = operands.size(); spv::Id typeId0 = 0; if (consumedOperands > 0) typeId0 = builder.getTypeId(operands[0]); + spv::Id typeId1 = 0; + if (consumedOperands > 1) + typeId1 = builder.getTypeId(operands[1]); spv::Id frexpIntType = 0; switch (op) { @@ -3320,11 +5217,12 @@ spv::Id TGlslangToSpvTraverser::createMiscOperation(glslang::TOperator op, spv:: builder.promoteScalar(precision, operands.front(), operands[2]); break; case glslang::EOpMix: - if (isFloat) + if (! builder.isBoolType(builder.getScalarTypeId(builder.getTypeId(operands.back())))) { + assert(isFloat); libCall = spv::GLSLstd450FMix; - else { + } else { opCode = spv::OpSelect; - spv::MissingFunctionality("translating integer mix to OpSelect"); + std::swap(operands.front(), operands.back()); } builder.promoteScalar(precision, operands.front(), operands.back()); break; @@ -3395,18 +5293,84 @@ spv::Id TGlslangToSpvTraverser::createMiscOperation(glslang::TOperator op, spv:: libCall = spv::GLSLstd450Fma; break; case glslang::EOpFrexp: - libCall = spv::GLSLstd450FrexpStruct; - if (builder.getNumComponents(operands[0]) == 1) - frexpIntType = builder.makeIntegerType(32, true); - else - frexpIntType = builder.makeVectorType(builder.makeIntegerType(32, true), builder.getNumComponents(operands[0])); - typeId = builder.makeStructResultType(typeId0, frexpIntType); - consumedOperands = 1; + { + libCall = spv::GLSLstd450FrexpStruct; + assert(builder.isPointerType(typeId1)); + typeId1 = builder.getContainedTypeId(typeId1); +#ifdef AMD_EXTENSIONS + int width = builder.getScalarTypeWidth(typeId1); +#else + int width = 32; +#endif + if (builder.getNumComponents(operands[0]) == 1) + frexpIntType = builder.makeIntegerType(width, true); + else + frexpIntType = builder.makeVectorType(builder.makeIntegerType(width, true), builder.getNumComponents(operands[0])); + typeId = builder.makeStructResultType(typeId0, frexpIntType); + consumedOperands = 1; + } break; case glslang::EOpLdexp: libCall = spv::GLSLstd450Ldexp; break; + case glslang::EOpReadInvocation: + return createInvocationsOperation(op, typeId, operands, typeProxy); + +#ifdef AMD_EXTENSIONS + case glslang::EOpSwizzleInvocations: + extBuiltins = getExtBuiltins(spv::E_SPV_AMD_shader_ballot); + libCall = spv::SwizzleInvocationsAMD; + break; + case glslang::EOpSwizzleInvocationsMasked: + extBuiltins = getExtBuiltins(spv::E_SPV_AMD_shader_ballot); + libCall = spv::SwizzleInvocationsMaskedAMD; + break; + case glslang::EOpWriteInvocation: + extBuiltins = getExtBuiltins(spv::E_SPV_AMD_shader_ballot); + libCall = spv::WriteInvocationAMD; + break; + + case glslang::EOpMin3: + extBuiltins = getExtBuiltins(spv::E_SPV_AMD_shader_trinary_minmax); + if (isFloat) + libCall = spv::FMin3AMD; + else { + if (isUnsigned) + libCall = spv::UMin3AMD; + else + libCall = spv::SMin3AMD; + } + break; + case glslang::EOpMax3: + extBuiltins = getExtBuiltins(spv::E_SPV_AMD_shader_trinary_minmax); + if (isFloat) + libCall = spv::FMax3AMD; + else { + if (isUnsigned) + libCall = spv::UMax3AMD; + else + libCall = spv::SMax3AMD; + } + break; + case glslang::EOpMid3: + extBuiltins = getExtBuiltins(spv::E_SPV_AMD_shader_trinary_minmax); + if (isFloat) + libCall = spv::FMid3AMD; + else { + if (isUnsigned) + libCall = spv::UMid3AMD; + else + libCall = spv::SMid3AMD; + } + break; + + case glslang::EOpInterpolateAtVertex: + extBuiltins = getExtBuiltins(spv::E_SPV_AMD_shader_explicit_vertex_parameter); + libCall = spv::InterpolateAtVertexAMD; + break; +#endif + default: return 0; } @@ -3417,7 +5381,7 @@ spv::Id TGlslangToSpvTraverser::createMiscOperation(glslang::TOperator op, spv:: // Construct the call arguments, without modifying the original operands vector. // We might need the remaining arguments, e.g. in the EOpFrexp case. std::vector callArguments(operands.begin(), operands.begin() + consumedOperands); - id = builder.createBuiltinCall(typeId, stdBuiltins, libCall, callArguments); + id = builder.createBuiltinCall(typeId, extBuiltins >= 0 ? extBuiltins : stdBuiltins, libCall, callArguments); } else { switch (consumedOperands) { case 0: @@ -3452,9 +5416,18 @@ spv::Id TGlslangToSpvTraverser::createMiscOperation(glslang::TOperator op, spv:: builder.createStore(builder.createCompositeExtract(id, typeId0, 1), operands[2]); break; case glslang::EOpFrexp: - assert(operands.size() == 2); - builder.createStore(builder.createCompositeExtract(id, frexpIntType, 1), operands[1]); - id = builder.createCompositeExtract(id, typeId0, 0); + { + assert(operands.size() == 2); + if (builder.isFloatType(builder.getScalarTypeId(typeId1))) { + // "exp" is floating-point type (from HLSL intrinsic) + spv::Id member1 = builder.createCompositeExtract(id, frexpIntType, 1); + member1 = builder.createUnaryOp(spv::OpConvertSToF, typeId1, member1); + builder.createStore(member1, operands[1]); + } else + // "exp" is integer type (from GLSL built-in function) + builder.createStore(builder.createCompositeExtract(id, frexpIntType, 1), operands[1]); + id = builder.createCompositeExtract(id, typeId0, 0); + } break; default: break; @@ -3463,8 +5436,8 @@ spv::Id TGlslangToSpvTraverser::createMiscOperation(glslang::TOperator op, spv:: return builder.setPrecision(id, precision); } -// Intrinsics with no arguments, no return value, and no precision. -spv::Id TGlslangToSpvTraverser::createNoArgOperation(glslang::TOperator op) +// Intrinsics with no arguments (or no return value, and no precision). +spv::Id TGlslangToSpvTraverser::createNoArgOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId) { // TODO: get the barrier operands correct @@ -3476,29 +5449,76 @@ spv::Id TGlslangToSpvTraverser::createNoArgOperation(glslang::TOperator op) builder.createNoResultOp(spv::OpEndPrimitive); return 0; case glslang::EOpBarrier: - builder.createMemoryBarrier(spv::ScopeDevice, spv::MemorySemanticsAllMemory); - builder.createControlBarrier(spv::ScopeDevice, spv::ScopeDevice, spv::MemorySemanticsMaskNone); + if (glslangIntermediate->getStage() == EShLangTessControl) { + builder.createControlBarrier(spv::ScopeWorkgroup, spv::ScopeInvocation, spv::MemorySemanticsMaskNone); + // TODO: prefer the following, when available: + // builder.createControlBarrier(spv::ScopePatch, spv::ScopePatch, + // spv::MemorySemanticsPatchMask | + // spv::MemorySemanticsAcquireReleaseMask); + } else { + builder.createControlBarrier(spv::ScopeWorkgroup, spv::ScopeWorkgroup, + spv::MemorySemanticsWorkgroupMemoryMask | + spv::MemorySemanticsAcquireReleaseMask); + } return 0; case glslang::EOpMemoryBarrier: - builder.createMemoryBarrier(spv::ScopeDevice, spv::MemorySemanticsAllMemory); + builder.createMemoryBarrier(spv::ScopeDevice, spv::MemorySemanticsAllMemory | + spv::MemorySemanticsAcquireReleaseMask); return 0; case glslang::EOpMemoryBarrierAtomicCounter: - builder.createMemoryBarrier(spv::ScopeDevice, spv::MemorySemanticsAtomicCounterMemoryMask); + builder.createMemoryBarrier(spv::ScopeDevice, spv::MemorySemanticsAtomicCounterMemoryMask | + spv::MemorySemanticsAcquireReleaseMask); return 0; case glslang::EOpMemoryBarrierBuffer: - builder.createMemoryBarrier(spv::ScopeDevice, spv::MemorySemanticsUniformMemoryMask); + builder.createMemoryBarrier(spv::ScopeDevice, spv::MemorySemanticsUniformMemoryMask | + spv::MemorySemanticsAcquireReleaseMask); return 0; case glslang::EOpMemoryBarrierImage: - builder.createMemoryBarrier(spv::ScopeDevice, spv::MemorySemanticsImageMemoryMask); + builder.createMemoryBarrier(spv::ScopeDevice, spv::MemorySemanticsImageMemoryMask | + spv::MemorySemanticsAcquireReleaseMask); return 0; case glslang::EOpMemoryBarrierShared: - builder.createMemoryBarrier(spv::ScopeDevice, spv::MemorySemanticsWorkgroupMemoryMask); + builder.createMemoryBarrier(spv::ScopeDevice, spv::MemorySemanticsWorkgroupMemoryMask | + spv::MemorySemanticsAcquireReleaseMask); return 0; case glslang::EOpGroupMemoryBarrier: - builder.createMemoryBarrier(spv::ScopeDevice, spv::MemorySemanticsCrossWorkgroupMemoryMask); + builder.createMemoryBarrier(spv::ScopeWorkgroup, spv::MemorySemanticsAllMemory | + spv::MemorySemanticsAcquireReleaseMask); return 0; + case glslang::EOpAllMemoryBarrierWithGroupSync: + builder.createControlBarrier(spv::ScopeWorkgroup, spv::ScopeDevice, + spv::MemorySemanticsAllMemory | + spv::MemorySemanticsAcquireReleaseMask); + return 0; + case glslang::EOpDeviceMemoryBarrier: + builder.createMemoryBarrier(spv::ScopeDevice, spv::MemorySemanticsUniformMemoryMask | + spv::MemorySemanticsImageMemoryMask | + spv::MemorySemanticsAcquireReleaseMask); + return 0; + case glslang::EOpDeviceMemoryBarrierWithGroupSync: + builder.createControlBarrier(spv::ScopeWorkgroup, spv::ScopeDevice, spv::MemorySemanticsUniformMemoryMask | + spv::MemorySemanticsImageMemoryMask | + spv::MemorySemanticsAcquireReleaseMask); + return 0; + case glslang::EOpWorkgroupMemoryBarrier: + builder.createMemoryBarrier(spv::ScopeWorkgroup, spv::MemorySemanticsWorkgroupMemoryMask | + spv::MemorySemanticsAcquireReleaseMask); + return 0; + case glslang::EOpWorkgroupMemoryBarrierWithGroupSync: + builder.createControlBarrier(spv::ScopeWorkgroup, spv::ScopeWorkgroup, + spv::MemorySemanticsWorkgroupMemoryMask | + spv::MemorySemanticsAcquireReleaseMask); + return 0; +#ifdef AMD_EXTENSIONS + case glslang::EOpTime: + { + std::vector args; // Dummy arguments + spv::Id id = builder.createBuiltinCall(typeId, getExtBuiltins(spv::E_SPV_AMD_gcn_shader), spv::TimeAMD, args); + return builder.setPrecision(id, precision); + } +#endif default: - spv::MissingFunctionality("unknown operation with no arguments"); + logger->missingFunctionality("unknown operation with no arguments"); return 0; } } @@ -3516,30 +5536,25 @@ spv::Id TGlslangToSpvTraverser::getSymbolId(const glslang::TIntermSymbol* symbol id = createSpvVariable(symbol); symbolValues[symbol->getId()] = id; - if (! symbol->getType().isStruct()) { + if (symbol->getBasicType() != glslang::EbtBlock) { addDecoration(id, TranslatePrecisionDecoration(symbol->getType())); addDecoration(id, TranslateInterpolationDecoration(symbol->getType().getQualifier())); + addDecoration(id, TranslateAuxiliaryStorageDecoration(symbol->getType().getQualifier())); if (symbol->getType().getQualifier().hasSpecConstantId()) addDecoration(id, spv::DecorationSpecId, symbol->getType().getQualifier().layoutSpecConstantId); - if (symbol->getQualifier().hasLocation()) - builder.addDecoration(id, spv::DecorationLocation, symbol->getQualifier().layoutLocation); if (symbol->getQualifier().hasIndex()) builder.addDecoration(id, spv::DecorationIndex, symbol->getQualifier().layoutIndex); if (symbol->getQualifier().hasComponent()) builder.addDecoration(id, spv::DecorationComponent, symbol->getQualifier().layoutComponent); - if (glslangIntermediate->getXfbMode()) { - builder.addCapability(spv::CapabilityTransformFeedback); - if (symbol->getQualifier().hasXfbStride()) - builder.addDecoration(id, spv::DecorationXfbStride, symbol->getQualifier().layoutXfbStride); - if (symbol->getQualifier().hasXfbBuffer()) - builder.addDecoration(id, spv::DecorationXfbBuffer, symbol->getQualifier().layoutXfbBuffer); - if (symbol->getQualifier().hasXfbOffset()) - builder.addDecoration(id, spv::DecorationOffset, symbol->getQualifier().layoutXfbOffset); - } + // atomic counters use this: + if (symbol->getQualifier().hasOffset()) + builder.addDecoration(id, spv::DecorationOffset, symbol->getQualifier().layoutOffset); } + if (symbol->getQualifier().hasLocation()) + builder.addDecoration(id, spv::DecorationLocation, symbol->getQualifier().layoutLocation); addDecoration(id, TranslateInvariantDecoration(symbol->getType().getQualifier())); - if (symbol->getQualifier().hasStream()) { + if (symbol->getQualifier().hasStream() && glslangIntermediate->isMultiStream()) { builder.addCapability(spv::CapabilityGeometryStreams); builder.addDecoration(id, spv::DecorationStream, symbol->getQualifier().layoutStream); } @@ -3557,43 +5572,90 @@ spv::Id TGlslangToSpvTraverser::getSymbolId(const glslang::TIntermSymbol* symbol builder.addCapability(spv::CapabilityTransformFeedback); if (symbol->getQualifier().hasXfbStride()) builder.addDecoration(id, spv::DecorationXfbStride, symbol->getQualifier().layoutXfbStride); - if (symbol->getQualifier().hasXfbBuffer()) + if (symbol->getQualifier().hasXfbBuffer()) { builder.addDecoration(id, spv::DecorationXfbBuffer, symbol->getQualifier().layoutXfbBuffer); + unsigned stride = glslangIntermediate->getXfbStride(symbol->getQualifier().layoutXfbBuffer); + if (stride != glslang::TQualifier::layoutXfbStrideEnd) + builder.addDecoration(id, spv::DecorationXfbStride, stride); + } + if (symbol->getQualifier().hasXfbOffset()) + builder.addDecoration(id, spv::DecorationOffset, symbol->getQualifier().layoutXfbOffset); + } + + if (symbol->getType().isImage()) { + std::vector memory; + TranslateMemoryDecoration(symbol->getType().getQualifier(), memory); + for (unsigned int i = 0; i < memory.size(); ++i) + addDecoration(id, memory[i]); } // built-in variable decorations - spv::BuiltIn builtIn = TranslateBuiltInDecoration(symbol->getQualifier().builtIn); - if (builtIn != spv::BadValue) + spv::BuiltIn builtIn = TranslateBuiltInDecoration(symbol->getQualifier().builtIn, false); + if (builtIn != spv::BuiltInMax) addDecoration(id, spv::DecorationBuiltIn, (int)builtIn); +#ifdef NV_EXTENSIONS + if (builtIn == spv::BuiltInSampleMask) { + spv::Decoration decoration; + // GL_NV_sample_mask_override_coverage extension + if (glslangIntermediate->getLayoutOverrideCoverage()) + decoration = (spv::Decoration)spv::DecorationOverrideCoverageNV; + else + decoration = (spv::Decoration)spv::DecorationMax; + addDecoration(id, decoration); + if (decoration != spv::DecorationMax) { + builder.addExtension(spv::E_SPV_NV_sample_mask_override_coverage); + } + } + else if (builtIn == spv::BuiltInLayer) { + // SPV_NV_viewport_array2 extension + if (symbol->getQualifier().layoutViewportRelative) { + addDecoration(id, (spv::Decoration)spv::DecorationViewportRelativeNV); + builder.addCapability(spv::CapabilityShaderViewportMaskNV); + builder.addExtension(spv::E_SPV_NV_viewport_array2); + } + if (symbol->getQualifier().layoutSecondaryViewportRelativeOffset != -2048) { + addDecoration(id, (spv::Decoration)spv::DecorationSecondaryViewportRelativeNV, symbol->getQualifier().layoutSecondaryViewportRelativeOffset); + builder.addCapability(spv::CapabilityShaderStereoViewNV); + builder.addExtension(spv::E_SPV_NV_stereo_view_rendering); + } + } + + if (symbol->getQualifier().layoutPassthrough) { + addDecoration(id, spv::DecorationPassthroughNV); + builder.addCapability(spv::CapabilityGeometryShaderPassthroughNV); + builder.addExtension(spv::E_SPV_NV_geometry_shader_passthrough); + } +#endif + return id; } // If 'dec' is valid, add no-operand decoration to an object void TGlslangToSpvTraverser::addDecoration(spv::Id id, spv::Decoration dec) { - if (dec != spv::BadValue) + if (dec != spv::DecorationMax) builder.addDecoration(id, dec); } // If 'dec' is valid, add a one-operand decoration to an object void TGlslangToSpvTraverser::addDecoration(spv::Id id, spv::Decoration dec, unsigned value) { - if (dec != spv::BadValue) + if (dec != spv::DecorationMax) builder.addDecoration(id, dec, value); } // If 'dec' is valid, add a no-operand decoration to a struct member void TGlslangToSpvTraverser::addMemberDecoration(spv::Id id, int member, spv::Decoration dec) { - if (dec != spv::BadValue) + if (dec != spv::DecorationMax) builder.addMemberDecoration(id, (unsigned)member, dec); } // If 'dec' is valid, add a one-operand decoration to a struct member void TGlslangToSpvTraverser::addMemberDecoration(spv::Id id, int member, spv::Decoration dec, unsigned value) { - if (dec != spv::BadValue) + if (dec != spv::DecorationMax) builder.addMemberDecoration(id, (unsigned)member, dec, value); } @@ -3607,42 +5669,56 @@ void TGlslangToSpvTraverser::addMemberDecoration(spv::Id id, int member, spv::De // recursively walks. So, this function walks the "top" of the tree: // - emit specialization constant-building instructions for specConstant // - when running into a non-spec-constant, switch to createSpvConstant() -spv::Id TGlslangToSpvTraverser::createSpvSpecConstant(const glslang::TIntermTyped& node) +spv::Id TGlslangToSpvTraverser::createSpvConstant(const glslang::TIntermTyped& node) { - assert(node.getQualifier().storage == glslang::EvqConst); + assert(node.getQualifier().isConstant()); + // Handle front-end constants first (non-specialization constants). if (! node.getQualifier().specConstant) { // hand off to the non-spec-constant path assert(node.getAsConstantUnion() != nullptr || node.getAsSymbolNode() != nullptr); int nextConst = 0; - return createSpvConstant(node.getType(), node.getAsConstantUnion() ? node.getAsConstantUnion()->getConstArray() : node.getAsSymbolNode()->getConstArray(), + return createSpvConstantFromConstUnionArray(node.getType(), node.getAsConstantUnion() ? node.getAsConstantUnion()->getConstArray() : node.getAsSymbolNode()->getConstArray(), nextConst, false); } // We now know we have a specialization constant to build - if (node.getAsSymbolNode() && node.getQualifier().hasSpecConstantId()) { - // this is a direct literal assigned to a layout(constant_id=) declaration - int nextConst = 0; - return createSpvConstant(node.getType(), node.getAsConstantUnion() ? node.getAsConstantUnion()->getConstArray() : node.getAsSymbolNode()->getConstArray(), - nextConst, true); - } else { - // gl_WorkgroupSize is a special case until the front-end handles hierarchical specialization constants, - // even then, it's specialization ids are handled by special case syntax in GLSL: layout(local_size_x = ... - if (node.getType().getQualifier().builtIn == glslang::EbvWorkGroupSize) { - std::vector dimConstId; - for (int dim = 0; dim < 3; ++dim) { - bool specConst = (glslangIntermediate->getLocalSizeSpecId(dim) != glslang::TQualifier::layoutNotSet); - dimConstId.push_back(builder.makeUintConstant(glslangIntermediate->getLocalSize(dim), specConst)); - if (specConst) - addDecoration(dimConstId.back(), spv::DecorationSpecId, glslangIntermediate->getLocalSizeSpecId(dim)); - } - return builder.makeCompositeConstant(builder.makeVectorType(builder.makeUintType(32), 3), dimConstId, true); - } else { - spv::MissingFunctionality("specialization-constant expression trees"); - return spv::NoResult; + // gl_WorkGroupSize is a special case until the front-end handles hierarchical specialization constants, + // even then, it's specialization ids are handled by special case syntax in GLSL: layout(local_size_x = ... + if (node.getType().getQualifier().builtIn == glslang::EbvWorkGroupSize) { + std::vector dimConstId; + for (int dim = 0; dim < 3; ++dim) { + bool specConst = (glslangIntermediate->getLocalSizeSpecId(dim) != glslang::TQualifier::layoutNotSet); + dimConstId.push_back(builder.makeUintConstant(glslangIntermediate->getLocalSize(dim), specConst)); + if (specConst) + addDecoration(dimConstId.back(), spv::DecorationSpecId, glslangIntermediate->getLocalSizeSpecId(dim)); + } + return builder.makeCompositeConstant(builder.makeVectorType(builder.makeUintType(32), 3), dimConstId, true); + } + + // An AST node labelled as specialization constant should be a symbol node. + // Its initializer should either be a sub tree with constant nodes, or a constant union array. + if (auto* sn = node.getAsSymbolNode()) { + if (auto* sub_tree = sn->getConstSubtree()) { + // Traverse the constant constructor sub tree like generating normal run-time instructions. + // During the AST traversal, if the node is marked as 'specConstant', SpecConstantOpModeGuard + // will set the builder into spec constant op instruction generating mode. + sub_tree->traverse(this); + return accessChainLoad(sub_tree->getType()); + } else if (auto* const_union_array = &sn->getConstArray()){ + int nextConst = 0; + spv::Id id = createSpvConstantFromConstUnionArray(sn->getType(), *const_union_array, nextConst, true); + builder.addName(id, sn->getName().c_str()); + return id; } } + + // Neither a front-end constant node, nor a specialization constant node with constant union array or + // constant sub tree as initializer. + logger->missingFunctionality("Neither a front-end constant nor a spec constant."); + exit(1); + return spv::NoResult; } // Use 'consts' as the flattened glslang source of scalar constants to recursively @@ -3651,7 +5727,7 @@ spv::Id TGlslangToSpvTraverser::createSpvSpecConstant(const glslang::TIntermType // If there are not enough elements present in 'consts', 0 will be substituted; // an empty 'consts' can be used to create a fully zeroed SPIR-V constant. // -spv::Id TGlslangToSpvTraverser::createSpvConstant(const glslang::TType& glslangType, const glslang::TConstUnionArray& consts, int& nextConst, bool specConstant) +spv::Id TGlslangToSpvTraverser::createSpvConstantFromConstUnionArray(const glslang::TType& glslangType, const glslang::TConstUnionArray& consts, int& nextConst, bool specConstant) { // vector of constants for SPIR-V std::vector spvConsts; @@ -3662,16 +5738,16 @@ spv::Id TGlslangToSpvTraverser::createSpvConstant(const glslang::TType& glslangT if (glslangType.isArray()) { glslang::TType elementType(glslangType, 0); for (int i = 0; i < glslangType.getOuterArraySize(); ++i) - spvConsts.push_back(createSpvConstant(elementType, consts, nextConst, false)); + spvConsts.push_back(createSpvConstantFromConstUnionArray(elementType, consts, nextConst, false)); } else if (glslangType.isMatrix()) { glslang::TType vectorType(glslangType, 0); for (int col = 0; col < glslangType.getMatrixCols(); ++col) - spvConsts.push_back(createSpvConstant(vectorType, consts, nextConst, false)); + spvConsts.push_back(createSpvConstantFromConstUnionArray(vectorType, consts, nextConst, false)); } else if (glslangType.getStruct()) { glslang::TVector::const_iterator iter; for (iter = glslangType.getStruct()->begin(); iter != glslangType.getStruct()->end(); ++iter) - spvConsts.push_back(createSpvConstant(*iter->type, consts, nextConst, false)); - } else if (glslangType.isVector()) { + spvConsts.push_back(createSpvConstantFromConstUnionArray(*iter->type, consts, nextConst, false)); + } else if (glslangType.getVectorSize() > 1) { for (unsigned int i = 0; i < (unsigned int)glslangType.getVectorSize(); ++i) { bool zero = nextConst >= consts.size(); switch (glslangType.getBasicType()) { @@ -3681,12 +5757,31 @@ spv::Id TGlslangToSpvTraverser::createSpvConstant(const glslang::TType& glslangT case glslang::EbtUint: spvConsts.push_back(builder.makeUintConstant(zero ? 0 : consts[nextConst].getUConst())); break; + case glslang::EbtInt64: + spvConsts.push_back(builder.makeInt64Constant(zero ? 0 : consts[nextConst].getI64Const())); + break; + case glslang::EbtUint64: + spvConsts.push_back(builder.makeUint64Constant(zero ? 0 : consts[nextConst].getU64Const())); + break; +#ifdef AMD_EXTENSIONS + case glslang::EbtInt16: + spvConsts.push_back(builder.makeInt16Constant(zero ? 0 : (short)consts[nextConst].getIConst())); + break; + case glslang::EbtUint16: + spvConsts.push_back(builder.makeUint16Constant(zero ? 0 : (unsigned short)consts[nextConst].getUConst())); + break; +#endif case glslang::EbtFloat: spvConsts.push_back(builder.makeFloatConstant(zero ? 0.0F : (float)consts[nextConst].getDConst())); break; case glslang::EbtDouble: spvConsts.push_back(builder.makeDoubleConstant(zero ? 0.0 : consts[nextConst].getDConst())); break; +#ifdef AMD_EXTENSIONS + case glslang::EbtFloat16: + spvConsts.push_back(builder.makeFloat16Constant(zero ? 0.0F : (float)consts[nextConst].getDConst())); + break; +#endif case glslang::EbtBool: spvConsts.push_back(builder.makeBoolConstant(zero ? false : consts[nextConst].getBConst())); break; @@ -3707,12 +5802,31 @@ spv::Id TGlslangToSpvTraverser::createSpvConstant(const glslang::TType& glslangT case glslang::EbtUint: scalar = builder.makeUintConstant(zero ? 0 : consts[nextConst].getUConst(), specConstant); break; + case glslang::EbtInt64: + scalar = builder.makeInt64Constant(zero ? 0 : consts[nextConst].getI64Const(), specConstant); + break; + case glslang::EbtUint64: + scalar = builder.makeUint64Constant(zero ? 0 : consts[nextConst].getU64Const(), specConstant); + break; +#ifdef AMD_EXTENSIONS + case glslang::EbtInt16: + scalar = builder.makeInt16Constant(zero ? 0 : (short)consts[nextConst].getIConst(), specConstant); + break; + case glslang::EbtUint16: + scalar = builder.makeUint16Constant(zero ? 0 : (unsigned short)consts[nextConst].getUConst(), specConstant); + break; +#endif case glslang::EbtFloat: scalar = builder.makeFloatConstant(zero ? 0.0F : (float)consts[nextConst].getDConst(), specConstant); break; case glslang::EbtDouble: scalar = builder.makeDoubleConstant(zero ? 0.0 : consts[nextConst].getDConst(), specConstant); break; +#ifdef AMD_EXTENSIONS + case glslang::EbtFloat16: + scalar = builder.makeFloat16Constant(zero ? 0.0F : (float)consts[nextConst].getDConst(), specConstant); + break; +#endif case glslang::EbtBool: scalar = builder.makeBoolConstant(zero ? false : consts[nextConst].getBConst(), specConstant); break; @@ -3756,16 +5870,21 @@ bool TGlslangToSpvTraverser::isTrivialLeaf(const glslang::TIntermTyped* node) default: return false; } -} +} // A node is trivial if it is a single operation with no side effects. -// Error on the side of saying non-trivial. +// HLSL (and/or vectors) are always trivial, as it does not short circuit. +// Otherwise, error on the side of saying non-trivial. // Return true if trivial. bool TGlslangToSpvTraverser::isTrivial(const glslang::TIntermTyped* node) { if (node == nullptr) return false; + // count non scalars as trivial, as well as anything coming from HLSL + if (! node->getType().isScalarOrVec1() || glslangIntermediate->getSource() == glslang::EShSourceHlsl) + return true; + // symbols and constants are trivial if (isTrivialLeaf(node)) return true; @@ -3818,7 +5937,7 @@ spv::Id TGlslangToSpvTraverser::createShortCircuit(glslang::TOperator op, glslan // emit left operand builder.clearAccessChain(); left.traverse(this); - spv::Id leftId = builder.accessChainLoad(spv::NoPrecision, boolTypeId); + spv::Id leftId = accessChainLoad(left.getType()); // Operands to accumulate OpPhi operands std::vector phiOperands; @@ -3836,12 +5955,12 @@ spv::Id TGlslangToSpvTraverser::createShortCircuit(glslang::TOperator op, glslan leftId = builder.createUnaryOp(spv::OpLogicalNot, boolTypeId, leftId); // make an "if" based on the left value - spv::Builder::If ifBuilder(leftId, builder); + spv::Builder::If ifBuilder(leftId, spv::SelectionControlMaskNone, builder); // emit right operand as the "then" part of the "if" builder.clearAccessChain(); right.traverse(this); - spv::Id rightId = builder.accessChainLoad(spv::NoPrecision, boolTypeId); + spv::Id rightId = accessChainLoad(right.getType()); // accumulate left operand's phi information phiOperands.push_back(rightId); @@ -3854,6 +5973,22 @@ spv::Id TGlslangToSpvTraverser::createShortCircuit(glslang::TOperator op, glslan return builder.createOp(spv::OpPhi, boolTypeId, phiOperands); } +#ifdef AMD_EXTENSIONS +// Return type Id of the imported set of extended instructions corresponds to the name. +// Import this set if it has not been imported yet. +spv::Id TGlslangToSpvTraverser::getExtBuiltins(const char* name) +{ + if (extBuiltinMap.find(name) != extBuiltinMap.end()) + return extBuiltinMap[name]; + else { + builder.addExtension(name); + spv::Id extBuiltins = builder.import(name); + extBuiltinMap[name] = extBuiltins; + return extBuiltins; + } +} +#endif + }; // end anonymous namespace namespace glslang { @@ -3866,11 +6001,24 @@ void GetSpirvVersion(std::string& version) version = buf; } +// For low-order part of the generator's magic number. Bump up +// when there is a change in the style (e.g., if SSA form changes, +// or a different instruction sequence to do something gets used). +int GetSpirvGeneratorVersion() +{ + // return 1; // start + // return 2; // EOpAtomicCounterDecrement gets a post decrement, to map between GLSL -> SPIR-V + // return 3; // change/correct barrier-instruction operands, to match memory model group decisions + return 4; // some deeper access chains: for dynamic vector component, and local Boolean component +} + // Write SPIR-V out to a binary file -void OutputSpv(const std::vector& spirv, const char* baseName) +void OutputSpvBin(const std::vector& spirv, const char* baseName) { std::ofstream out; out.open(baseName, std::ios::binary | std::ios::out); + if (out.fail()) + printf("ERROR: Failed to open file: %s\n", baseName); for (int i = 0; i < (int)spirv.size(); ++i) { unsigned int word = spirv[i]; out.write((const char*)&word, 4); @@ -3878,24 +6026,120 @@ void OutputSpv(const std::vector& spirv, const char* baseName) out.close(); } +// Write SPIR-V out to a text file with 32-bit hexadecimal words +void OutputSpvHex(const std::vector& spirv, const char* baseName, const char* varName) +{ + std::ofstream out; + out.open(baseName, std::ios::binary | std::ios::out); + if (out.fail()) + printf("ERROR: Failed to open file: %s\n", baseName); + out << "\t// " GLSLANG_REVISION " " GLSLANG_DATE << std::endl; + if (varName != nullptr) { + out << "\t #pragma once" << std::endl; + out << "const uint32_t " << varName << "[] = {" << std::endl; + } + const int WORDS_PER_LINE = 8; + for (int i = 0; i < (int)spirv.size(); i += WORDS_PER_LINE) { + out << "\t"; + for (int j = 0; j < WORDS_PER_LINE && i + j < (int)spirv.size(); ++j) { + const unsigned int word = spirv[i + j]; + out << "0x" << std::hex << std::setw(8) << std::setfill('0') << word; + if (i + j + 1 < (int)spirv.size()) { + out << ","; + } + } + out << std::endl; + } + if (varName != nullptr) { + out << "};"; + } + out.close(); +} + +#ifdef ENABLE_OPT +void errHandler(const std::string& str) { + std::cerr << str << std::endl; +} +#endif + // // Set up the glslang traversal // -void GlslangToSpv(const glslang::TIntermediate& intermediate, std::vector& spirv) +void GlslangToSpv(const glslang::TIntermediate& intermediate, std::vector& spirv, SpvOptions* options) +{ + spv::SpvBuildLogger logger; + GlslangToSpv(intermediate, spirv, &logger, options); +} + +void GlslangToSpv(const glslang::TIntermediate& intermediate, std::vector& spirv, + spv::SpvBuildLogger* logger, SpvOptions* options) { TIntermNode* root = intermediate.getTreeRoot(); if (root == 0) return; + glslang::SpvOptions defaultOptions; + if (options == nullptr) + options = &defaultOptions; + glslang::GetThreadPoolAllocator().push(); - TGlslangToSpvTraverser it(&intermediate); - + TGlslangToSpvTraverser it(intermediate.getSpv().spv, &intermediate, logger, *options); root->traverse(&it); - + it.finishSpv(); it.dumpSpv(spirv); +#ifdef ENABLE_OPT + // If from HLSL, run spirv-opt to "legalize" the SPIR-V for Vulkan + // eg. forward and remove memory writes of opaque types. + if ((intermediate.getSource() == EShSourceHlsl || + options->optimizeSize) && + !options->disableOptimizer) { + spv_target_env target_env = SPV_ENV_UNIVERSAL_1_2; + + spvtools::Optimizer optimizer(target_env); + optimizer.SetMessageConsumer([](spv_message_level_t level, + const char* source, + const spv_position_t& position, + const char* message) { + std::cerr << StringifyMessage(level, source, position, message) + << std::endl; + }); + + optimizer.RegisterPass(CreateInlineExhaustivePass()); + optimizer.RegisterPass(CreateEliminateDeadFunctionsPass()); + optimizer.RegisterPass(CreateScalarReplacementPass()); + optimizer.RegisterPass(CreateLocalAccessChainConvertPass()); + optimizer.RegisterPass(CreateLocalSingleBlockLoadStoreElimPass()); + optimizer.RegisterPass(CreateLocalSingleStoreElimPass()); + optimizer.RegisterPass(CreateInsertExtractElimPass()); + optimizer.RegisterPass(CreateDeadInsertElimPass()); + optimizer.RegisterPass(CreateAggressiveDCEPass()); + optimizer.RegisterPass(CreateDeadBranchElimPass()); + optimizer.RegisterPass(CreateCFGCleanupPass()); + optimizer.RegisterPass(CreateBlockMergePass()); + optimizer.RegisterPass(CreateLocalMultiStoreElimPass()); + optimizer.RegisterPass(CreateInsertExtractElimPass()); + optimizer.RegisterPass(CreateDeadInsertElimPass()); + if (options->optimizeSize) { + optimizer.RegisterPass(CreateRedundancyEliminationPass()); + // TODO(greg-lunarg): Add this when AMD driver issues are resolved + // optimizer.RegisterPass(CreateCommonUniformElimPass()); + } + optimizer.RegisterPass(CreateAggressiveDCEPass()); + + if (!optimizer.Run(spirv.data(), spirv.size(), &spirv)) + return; + + // Remove dead module-level objects: functions, types, vars + // TODO(greg-lunarg): Switch to spirv-opt versions when available + spv::spirvbin_t Remapper(0); + Remapper.registerErrorHandler(errHandler); + Remapper.remap(spirv, spv::spirvbin_t::DCE_ALL); + } +#endif + glslang::GetThreadPoolAllocator().pop(); } diff --git a/third_party/glslang-spirv/GlslangToSpv.h b/third_party/glslang-spirv/GlslangToSpv.h index d8a18893c..3a4371375 100644 --- a/third_party/glslang-spirv/GlslangToSpv.h +++ b/third_party/glslang-spirv/GlslangToSpv.h @@ -1,11 +1,11 @@ // -//Copyright (C) 2014 LunarG, Inc. +// Copyright (C) 2014 LunarG, Inc. // -//All rights reserved. +// All rights reserved. // -//Redistribution and use in source and binary forms, with or without -//modification, are permitted provided that the following conditions -//are met: +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: // // Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. @@ -19,25 +19,49 @@ // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // -//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -//"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -//LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -//FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -//COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -//INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -//BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -//LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -//CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -//ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -//POSSIBILITY OF SUCH DAMAGE. +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#if _MSC_VER >= 1900 + #pragma warning(disable : 4464) // relative include path contains '..' +#endif #include "../glslang/Include/intermediate.h" +#include +#include + +#include "Logger.h" + namespace glslang { -void GetSpirvVersion(std::string&); -void GlslangToSpv(const glslang::TIntermediate& intermediate, std::vector& spirv); -void OutputSpv(const std::vector& spirv, const char* baseName); - +struct SpvOptions { + SpvOptions() : generateDebugInfo(false), disableOptimizer(true), + optimizeSize(false) { } + bool generateDebugInfo; + bool disableOptimizer; + bool optimizeSize; }; + +void GetSpirvVersion(std::string&); +int GetSpirvGeneratorVersion(); +void GlslangToSpv(const glslang::TIntermediate& intermediate, std::vector& spirv, + SpvOptions* options = nullptr); +void GlslangToSpv(const glslang::TIntermediate& intermediate, std::vector& spirv, + spv::SpvBuildLogger* logger, SpvOptions* options = nullptr); +void OutputSpvBin(const std::vector& spirv, const char* baseName); +void OutputSpvHex(const std::vector& spirv, const char* baseName, const char* varName); + +} diff --git a/third_party/glslang-spirv/InReadableOrder.cpp b/third_party/glslang-spirv/InReadableOrder.cpp index 9180dc8c7..52b29613a 100644 --- a/third_party/glslang-spirv/InReadableOrder.cpp +++ b/third_party/glslang-spirv/InReadableOrder.cpp @@ -1,11 +1,11 @@ // -//Copyright (C) 2016 Google, Inc. +// Copyright (C) 2016 Google, Inc. // -//All rights reserved. +// All rights reserved. // -//Redistribution and use in source and binary forms, with or without -//modification, are permitted provided that the following conditions -//are met: +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: // // Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. @@ -19,22 +19,18 @@ // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // -//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -//"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -//LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -//FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -//COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -//INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -//BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -//LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -//CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -//ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -//POSSIBILITY OF SUCH DAMAGE. - -// -// Author: Dejan Mircevski, Google -// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. // The SPIR-V spec requires code blocks to appear in an order satisfying the // dominator-tree direction (ie, dominator before the dominated). This is, @@ -55,7 +51,7 @@ #include "spvIR.h" #include -#include +#include using spv::Block; using spv::Id; @@ -73,32 +69,33 @@ public: void visit(Block* block) { assert(block); - if (visited_[block] || delayed_[block]) + if (visited_.count(block) || delayed_.count(block)) return; callback_(block); - visited_[block] = true; + visited_.insert(block); Block* mergeBlock = nullptr; Block* continueBlock = nullptr; auto mergeInst = block->getMergeInstruction(); if (mergeInst) { Id mergeId = mergeInst->getIdOperand(0); mergeBlock = block->getParent().getParent().getInstruction(mergeId)->getBlock(); - delayed_[mergeBlock] = true; + delayed_.insert(mergeBlock); if (mergeInst->getOpCode() == spv::OpLoopMerge) { Id continueId = mergeInst->getIdOperand(1); continueBlock = block->getParent().getParent().getInstruction(continueId)->getBlock(); - delayed_[continueBlock] = true; + delayed_.insert(continueBlock); } } - for (const auto succ : block->getSuccessors()) - visit(succ); + const auto successors = block->getSuccessors(); + for (auto it = successors.cbegin(); it != successors.cend(); ++it) + visit(*it); if (continueBlock) { - delayed_[continueBlock] = false; + delayed_.erase(continueBlock); visit(continueBlock); } if (mergeBlock) { - delayed_[mergeBlock] = false; + delayed_.erase(mergeBlock); visit(mergeBlock); } } @@ -106,7 +103,7 @@ public: private: std::function callback_; // Whether a block has already been visited or is being delayed. - std::unordered_map visited_, delayed_; + std::unordered_set visited_, delayed_; }; } diff --git a/third_party/glslang-spirv/Include/BaseTypes.h b/third_party/glslang-spirv/Include/BaseTypes.h new file mode 100644 index 000000000..050c2c4b9 --- /dev/null +++ b/third_party/glslang-spirv/Include/BaseTypes.h @@ -0,0 +1,387 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2013 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef _BASICTYPES_INCLUDED_ +#define _BASICTYPES_INCLUDED_ + +namespace glslang { + +// +// Basic type. Arrays, vectors, sampler details, etc., are orthogonal to this. +// +enum TBasicType { + EbtVoid, + EbtFloat, + EbtDouble, +#ifdef AMD_EXTENSIONS + EbtFloat16, +#endif + EbtInt, + EbtUint, + EbtInt64, + EbtUint64, +#ifdef AMD_EXTENSIONS + EbtInt16, + EbtUint16, +#endif + EbtBool, + EbtAtomicUint, + EbtSampler, + EbtStruct, + EbtBlock, + + // HLSL types that live only temporarily. + EbtString, + + EbtNumTypes +}; + +// +// Storage qualifiers. Should align with different kinds of storage or +// resource or GLSL storage qualifier. Expansion is deprecated. +// +// N.B.: You probably DON'T want to add anything here, but rather just add it +// to the built-in variables. See the comment above TBuiltInVariable. +// +// A new built-in variable will normally be an existing qualifier, like 'in', 'out', etc. +// DO NOT follow the design pattern of, say EvqInstanceId, etc. +// +enum TStorageQualifier { + EvqTemporary, // For temporaries (within a function), read/write + EvqGlobal, // For globals read/write + EvqConst, // User-defined constant values, will be semantically constant and constant folded + EvqVaryingIn, // pipeline input, read only, also supercategory for all built-ins not included in this enum (see TBuiltInVariable) + EvqVaryingOut, // pipeline output, read/write, also supercategory for all built-ins not included in this enum (see TBuiltInVariable) + EvqUniform, // read only, shared with app + EvqBuffer, // read/write, shared with app + EvqShared, // compute shader's read/write 'shared' qualifier + + // parameters + EvqIn, // also, for 'in' in the grammar before we know if it's a pipeline input or an 'in' parameter + EvqOut, // also, for 'out' in the grammar before we know if it's a pipeline output or an 'out' parameter + EvqInOut, + EvqConstReadOnly, // input; also other read-only types having neither a constant value nor constant-value semantics + + // built-ins read by vertex shader + EvqVertexId, + EvqInstanceId, + + // built-ins written by vertex shader + EvqPosition, + EvqPointSize, + EvqClipVertex, + + // built-ins read by fragment shader + EvqFace, + EvqFragCoord, + EvqPointCoord, + + // built-ins written by fragment shader + EvqFragColor, + EvqFragDepth, + + // end of list + EvqLast +}; + +// +// Subcategories of the TStorageQualifier, simply to give a direct mapping +// between built-in variable names and an numerical value (the enum). +// +// For backward compatibility, there is some redundancy between the +// TStorageQualifier and these. Existing members should both be maintained accurately. +// However, any new built-in variable (and any existing non-redundant one) +// must follow the pattern that the specific built-in is here, and only its +// general qualifier is in TStorageQualifier. +// +// Something like gl_Position, which is sometimes 'in' and sometimes 'out' +// shows up as two different built-in variables in a single stage, but +// only has a single enum in TBuiltInVariable, so both the +// TStorageQualifier and the TBuitinVariable are needed to distinguish +// between them. +// +enum TBuiltInVariable { + EbvNone, + EbvNumWorkGroups, + EbvWorkGroupSize, + EbvWorkGroupId, + EbvLocalInvocationId, + EbvGlobalInvocationId, + EbvLocalInvocationIndex, + EbvSubGroupSize, + EbvSubGroupInvocation, + EbvSubGroupEqMask, + EbvSubGroupGeMask, + EbvSubGroupGtMask, + EbvSubGroupLeMask, + EbvSubGroupLtMask, + EbvVertexId, + EbvInstanceId, + EbvVertexIndex, + EbvInstanceIndex, + EbvBaseVertex, + EbvBaseInstance, + EbvDrawId, + EbvPosition, + EbvPointSize, + EbvClipVertex, + EbvClipDistance, + EbvCullDistance, + EbvNormal, + EbvVertex, + EbvMultiTexCoord0, + EbvMultiTexCoord1, + EbvMultiTexCoord2, + EbvMultiTexCoord3, + EbvMultiTexCoord4, + EbvMultiTexCoord5, + EbvMultiTexCoord6, + EbvMultiTexCoord7, + EbvFrontColor, + EbvBackColor, + EbvFrontSecondaryColor, + EbvBackSecondaryColor, + EbvTexCoord, + EbvFogFragCoord, + EbvInvocationId, + EbvPrimitiveId, + EbvLayer, + EbvViewportIndex, + EbvPatchVertices, + EbvTessLevelOuter, + EbvTessLevelInner, + EbvBoundingBox, + EbvTessCoord, + EbvColor, + EbvSecondaryColor, + EbvFace, + EbvFragCoord, + EbvPointCoord, + EbvFragColor, + EbvFragData, + EbvFragDepth, + EbvFragStencilRef, + EbvSampleId, + EbvSamplePosition, + EbvSampleMask, + EbvHelperInvocation, + +#ifdef AMD_EXTENSIONS + EbvBaryCoordNoPersp, + EbvBaryCoordNoPerspCentroid, + EbvBaryCoordNoPerspSample, + EbvBaryCoordSmooth, + EbvBaryCoordSmoothCentroid, + EbvBaryCoordSmoothSample, + EbvBaryCoordPullModel, +#endif + + EbvViewIndex, + EbvDeviceIndex, + +#ifdef NV_EXTENSIONS + EbvViewportMaskNV, + EbvSecondaryPositionNV, + EbvSecondaryViewportMaskNV, + EbvPositionPerViewNV, + EbvViewportMaskPerViewNV, + EbvFragFullyCoveredNV, +#endif + + // HLSL built-ins that live only temporarily, until they get remapped + // to one of the above. + EbvFragDepthGreater, + EbvFragDepthLesser, + EbvGsOutputStream, + EbvOutputPatch, + EbvInputPatch, + + // structbuffer types + EbvAppendConsume, // no need to differentiate append and consume + EbvRWStructuredBuffer, + EbvStructuredBuffer, + EbvByteAddressBuffer, + EbvRWByteAddressBuffer, + + EbvLast +}; + +// These will show up in error messages +__inline const char* GetStorageQualifierString(TStorageQualifier q) +{ + switch (q) { + case EvqTemporary: return "temp"; break; + case EvqGlobal: return "global"; break; + case EvqConst: return "const"; break; + case EvqConstReadOnly: return "const (read only)"; break; + case EvqVaryingIn: return "in"; break; + case EvqVaryingOut: return "out"; break; + case EvqUniform: return "uniform"; break; + case EvqBuffer: return "buffer"; break; + case EvqShared: return "shared"; break; + case EvqIn: return "in"; break; + case EvqOut: return "out"; break; + case EvqInOut: return "inout"; break; + case EvqVertexId: return "gl_VertexId"; break; + case EvqInstanceId: return "gl_InstanceId"; break; + case EvqPosition: return "gl_Position"; break; + case EvqPointSize: return "gl_PointSize"; break; + case EvqClipVertex: return "gl_ClipVertex"; break; + case EvqFace: return "gl_FrontFacing"; break; + case EvqFragCoord: return "gl_FragCoord"; break; + case EvqPointCoord: return "gl_PointCoord"; break; + case EvqFragColor: return "fragColor"; break; + case EvqFragDepth: return "gl_FragDepth"; break; + default: return "unknown qualifier"; + } +} + +__inline const char* GetBuiltInVariableString(TBuiltInVariable v) +{ + switch (v) { + case EbvNone: return ""; + case EbvNumWorkGroups: return "NumWorkGroups"; + case EbvWorkGroupSize: return "WorkGroupSize"; + case EbvWorkGroupId: return "WorkGroupID"; + case EbvLocalInvocationId: return "LocalInvocationID"; + case EbvGlobalInvocationId: return "GlobalInvocationID"; + case EbvLocalInvocationIndex: return "LocalInvocationIndex"; + case EbvSubGroupSize: return "SubGroupSize"; + case EbvSubGroupInvocation: return "SubGroupInvocation"; + case EbvSubGroupEqMask: return "SubGroupEqMask"; + case EbvSubGroupGeMask: return "SubGroupGeMask"; + case EbvSubGroupGtMask: return "SubGroupGtMask"; + case EbvSubGroupLeMask: return "SubGroupLeMask"; + case EbvSubGroupLtMask: return "SubGroupLtMask"; + case EbvVertexId: return "VertexId"; + case EbvInstanceId: return "InstanceId"; + case EbvVertexIndex: return "VertexIndex"; + case EbvInstanceIndex: return "InstanceIndex"; + case EbvBaseVertex: return "BaseVertex"; + case EbvBaseInstance: return "BaseInstance"; + case EbvDrawId: return "DrawId"; + case EbvPosition: return "Position"; + case EbvPointSize: return "PointSize"; + case EbvClipVertex: return "ClipVertex"; + case EbvClipDistance: return "ClipDistance"; + case EbvCullDistance: return "CullDistance"; + case EbvNormal: return "Normal"; + case EbvVertex: return "Vertex"; + case EbvMultiTexCoord0: return "MultiTexCoord0"; + case EbvMultiTexCoord1: return "MultiTexCoord1"; + case EbvMultiTexCoord2: return "MultiTexCoord2"; + case EbvMultiTexCoord3: return "MultiTexCoord3"; + case EbvMultiTexCoord4: return "MultiTexCoord4"; + case EbvMultiTexCoord5: return "MultiTexCoord5"; + case EbvMultiTexCoord6: return "MultiTexCoord6"; + case EbvMultiTexCoord7: return "MultiTexCoord7"; + case EbvFrontColor: return "FrontColor"; + case EbvBackColor: return "BackColor"; + case EbvFrontSecondaryColor: return "FrontSecondaryColor"; + case EbvBackSecondaryColor: return "BackSecondaryColor"; + case EbvTexCoord: return "TexCoord"; + case EbvFogFragCoord: return "FogFragCoord"; + case EbvInvocationId: return "InvocationID"; + case EbvPrimitiveId: return "PrimitiveID"; + case EbvLayer: return "Layer"; + case EbvViewportIndex: return "ViewportIndex"; + case EbvPatchVertices: return "PatchVertices"; + case EbvTessLevelOuter: return "TessLevelOuter"; + case EbvTessLevelInner: return "TessLevelInner"; + case EbvBoundingBox: return "BoundingBox"; + case EbvTessCoord: return "TessCoord"; + case EbvColor: return "Color"; + case EbvSecondaryColor: return "SecondaryColor"; + case EbvFace: return "Face"; + case EbvFragCoord: return "FragCoord"; + case EbvPointCoord: return "PointCoord"; + case EbvFragColor: return "FragColor"; + case EbvFragData: return "FragData"; + case EbvFragDepth: return "FragDepth"; + case EbvFragStencilRef: return "FragStencilRef"; + case EbvSampleId: return "SampleId"; + case EbvSamplePosition: return "SamplePosition"; + case EbvSampleMask: return "SampleMaskIn"; + case EbvHelperInvocation: return "HelperInvocation"; + +#ifdef AMD_EXTENSIONS + case EbvBaryCoordNoPersp: return "BaryCoordNoPersp"; + case EbvBaryCoordNoPerspCentroid: return "BaryCoordNoPerspCentroid"; + case EbvBaryCoordNoPerspSample: return "BaryCoordNoPerspSample"; + case EbvBaryCoordSmooth: return "BaryCoordSmooth"; + case EbvBaryCoordSmoothCentroid: return "BaryCoordSmoothCentroid"; + case EbvBaryCoordSmoothSample: return "BaryCoordSmoothSample"; + case EbvBaryCoordPullModel: return "BaryCoordPullModel"; +#endif + + case EbvViewIndex: return "ViewIndex"; + case EbvDeviceIndex: return "DeviceIndex"; + +#ifdef NV_EXTENSIONS + case EbvViewportMaskNV: return "ViewportMaskNV"; + case EbvSecondaryPositionNV: return "SecondaryPositionNV"; + case EbvSecondaryViewportMaskNV: return "SecondaryViewportMaskNV"; + case EbvPositionPerViewNV: return "PositionPerViewNV"; + case EbvViewportMaskPerViewNV: return "ViewportMaskPerViewNV"; + case EbvFragFullyCoveredNV: return "FragFullyCoveredNV"; +#endif + default: return "unknown built-in variable"; + } +} + +// In this enum, order matters; users can assume higher precision is a bigger value +// and EpqNone is 0. +enum TPrecisionQualifier { + EpqNone = 0, + EpqLow, + EpqMedium, + EpqHigh +}; + +__inline const char* GetPrecisionQualifierString(TPrecisionQualifier p) +{ + switch(p) { + case EpqNone: return ""; break; + case EpqLow: return "lowp"; break; + case EpqMedium: return "mediump"; break; + case EpqHigh: return "highp"; break; + default: return "unknown precision qualifier"; + } +} + +} // end namespace glslang + +#endif // _BASICTYPES_INCLUDED_ diff --git a/third_party/glslang-spirv/Include/Common.h b/third_party/glslang-spirv/Include/Common.h new file mode 100644 index 000000000..041701774 --- /dev/null +++ b/third_party/glslang-spirv/Include/Common.h @@ -0,0 +1,274 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2013 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef _COMMON_INCLUDED_ +#define _COMMON_INCLUDED_ + +#if (defined(_MSC_VER) && _MSC_VER < 1900 /*vs2015*/) || defined MINGW_HAS_SECURE_API + #include + #define snprintf sprintf_s + #define safe_vsprintf(buf,max,format,args) vsnprintf_s((buf), (max), (max), (format), (args)) +#elif defined (solaris) + #define safe_vsprintf(buf,max,format,args) vsnprintf((buf), (max), (format), (args)) + #include + #define UINT_PTR uintptr_t +#else + #define safe_vsprintf(buf,max,format,args) vsnprintf((buf), (max), (format), (args)) + #include + #define UINT_PTR uintptr_t +#endif + +#if defined(__ANDROID__) || _MSC_VER < 1700 +#include +namespace std { +template +std::string to_string(const T& val) { + std::ostringstream os; + os << val; + return os.str(); +} +} +#endif + +#if defined(_MSC_VER) && _MSC_VER < 1800 +inline long long int strtoll (const char* str, char** endptr, int base) +{ + return _strtoi64(str, endptr, base); +} +inline unsigned long long int strtoull (const char* str, char** endptr, int base) +{ + return _strtoui64(str, endptr, base); +} +inline long long int atoll (const char* str) +{ + return strtoll(str, NULL, 10); +} +#endif + +#if defined(_MSC_VER) +#define strdup _strdup +#endif + +/* windows only pragma */ +#ifdef _MSC_VER + #pragma warning(disable : 4786) // Don't warn about too long identifiers + #pragma warning(disable : 4514) // unused inline method + #pragma warning(disable : 4201) // nameless union +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "PoolAlloc.h" + +// +// Put POOL_ALLOCATOR_NEW_DELETE in base classes to make them use this scheme. +// +#define POOL_ALLOCATOR_NEW_DELETE(A) \ + void* operator new(size_t s) { return (A).allocate(s); } \ + void* operator new(size_t, void *_Where) { return (_Where); } \ + void operator delete(void*) { } \ + void operator delete(void *, void *) { } \ + void* operator new[](size_t s) { return (A).allocate(s); } \ + void* operator new[](size_t, void *_Where) { return (_Where); } \ + void operator delete[](void*) { } \ + void operator delete[](void *, void *) { } + +namespace glslang { + + // + // Pool version of string. + // + typedef pool_allocator TStringAllocator; + typedef std::basic_string , TStringAllocator> TString; + +} // end namespace glslang + +// Repackage the std::hash for use by unordered map/set with a TString key. +namespace std { + + template<> struct hash { + std::size_t operator()(const glslang::TString& s) const + { + const unsigned _FNV_offset_basis = 2166136261U; + const unsigned _FNV_prime = 16777619U; + unsigned _Val = _FNV_offset_basis; + size_t _Count = s.size(); + const char* _First = s.c_str(); + for (size_t _Next = 0; _Next < _Count; ++_Next) + { + _Val ^= (unsigned)_First[_Next]; + _Val *= _FNV_prime; + } + + return _Val; + } + }; +} + +namespace glslang { + +inline TString* NewPoolTString(const char* s) +{ + void* memory = GetThreadPoolAllocator().allocate(sizeof(TString)); + return new(memory) TString(s); +} + +template inline T* NewPoolObject(T*) +{ + return new(GetThreadPoolAllocator().allocate(sizeof(T))) T; +} + +template inline T* NewPoolObject(T, int instances) +{ + return new(GetThreadPoolAllocator().allocate(instances * sizeof(T))) T[instances]; +} + +// +// Pool allocator versions of vectors, lists, and maps +// +template class TVector : public std::vector > { +public: + POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) + + typedef typename std::vector >::size_type size_type; + TVector() : std::vector >() {} + TVector(const pool_allocator& a) : std::vector >(a) {} + TVector(size_type i) : std::vector >(i) {} + TVector(size_type i, const T& val) : std::vector >(i, val) {} +}; + +template class TList : public std::list > { +}; + +template > +class TMap : public std::map > > { +}; + +template , class PRED = std::equal_to > +class TUnorderedMap : public std::unordered_map > > { +}; + +// +// Persistent string memory. Should only be used for strings that survive +// across compiles/links. +// +typedef std::basic_string TPersistString; + +// +// templatized min and max functions. +// +template T Min(const T a, const T b) { return a < b ? a : b; } +template T Max(const T a, const T b) { return a > b ? a : b; } + +// +// Create a TString object from an integer. +// +#if defined _MSC_VER || defined MINGW_HAS_SECURE_API +inline const TString String(const int i, const int base = 10) +{ + char text[16]; // 32 bit ints are at most 10 digits in base 10 + _itoa_s(i, text, sizeof(text), base); + return text; +} +#else +inline const TString String(const int i, const int /*base*/ = 10) +{ + char text[16]; // 32 bit ints are at most 10 digits in base 10 + + // we assume base 10 for all cases + snprintf(text, sizeof(text), "%d", i); + + return text; +} +#endif + +struct TSourceLoc { + void init() { name = nullptr; string = 0; line = 0; column = 0; } + void init(int stringNum) { init(); string = stringNum; } + // Returns the name if it exists. Otherwise, returns the string number. + std::string getStringNameOrNum(bool quoteStringName = true) const + { + if (name != nullptr) + return quoteStringName ? ("\"" + std::string(name) + "\"") : name; + return std::to_string((long long)string); + } + const char* name; // descriptive name for this string + int string; + int line; + int column; +}; + +class TPragmaTable : public TMap { +public: + POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) +}; + +const int MaxTokenLength = 1024; + +template bool IsPow2(T powerOf2) +{ + if (powerOf2 <= 0) + return false; + + return (powerOf2 & (powerOf2 - 1)) == 0; +} + +// Round number up to a multiple of the given powerOf2, which is not +// a power, just a number that must be a power of 2. +template void RoundToPow2(T& number, int powerOf2) +{ + assert(IsPow2(powerOf2)); + number = (number + powerOf2 - 1) & ~(powerOf2 - 1); +} + +template bool IsMultipleOfPow2(T number, int powerOf2) +{ + assert(IsPow2(powerOf2)); + return ! (number & (powerOf2 - 1)); +} + +} // end namespace glslang + +#endif // _COMMON_INCLUDED_ diff --git a/third_party/glslang-spirv/Include/ConstantUnion.h b/third_party/glslang-spirv/Include/ConstantUnion.h new file mode 100644 index 000000000..58c6094ea --- /dev/null +++ b/third_party/glslang-spirv/Include/ConstantUnion.h @@ -0,0 +1,625 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2013 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef _CONSTANT_UNION_INCLUDED_ +#define _CONSTANT_UNION_INCLUDED_ + +#include "../Include/Common.h" +#include "../Include/BaseTypes.h" + +namespace glslang { + +class TConstUnion { +public: + POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) + + TConstUnion() : iConst(0), type(EbtInt) { } + + void setIConst(int i) + { + iConst = i; + type = EbtInt; + } + + void setUConst(unsigned int u) + { + uConst = u; + type = EbtUint; + } + + void setI64Const(long long i64) + { + i64Const = i64; + type = EbtInt64; + } + + void setU64Const(unsigned long long u64) + { + u64Const = u64; + type = EbtUint64; + } + + void setDConst(double d) + { + dConst = d; + type = EbtDouble; + } + + void setBConst(bool b) + { + bConst = b; + type = EbtBool; + } + + void setSConst(const TString* s) + { + sConst = s; + type = EbtString; + } + + int getIConst() const { return iConst; } + unsigned int getUConst() const { return uConst; } + long long getI64Const() const { return i64Const; } + unsigned long long getU64Const() const { return u64Const; } + double getDConst() const { return dConst; } + bool getBConst() const { return bConst; } + const TString* getSConst() const { return sConst; } + + bool operator==(const int i) const + { + if (i == iConst) + return true; + + return false; + } + + bool operator==(const unsigned int u) const + { + if (u == uConst) + return true; + + return false; + } + + bool operator==(const long long i64) const + { + if (i64 == i64Const) + return true; + + return false; + } + + bool operator==(const unsigned long long u64) const + { + if (u64 == u64Const) + return true; + + return false; + } + + bool operator==(const double d) const + { + if (d == dConst) + return true; + + return false; + } + + bool operator==(const bool b) const + { + if (b == bConst) + return true; + + return false; + } + + bool operator==(const TConstUnion& constant) const + { + if (constant.type != type) + return false; + + switch (type) { + case EbtInt: + if (constant.iConst == iConst) + return true; + + break; + case EbtUint: + if (constant.uConst == uConst) + return true; + + break; + case EbtInt64: + if (constant.i64Const == i64Const) + return true; + + break; + case EbtUint64: + if (constant.u64Const == u64Const) + return true; + + break; + case EbtDouble: + if (constant.dConst == dConst) + return true; + + break; + case EbtBool: + if (constant.bConst == bConst) + return true; + + break; + default: + assert(false && "Default missing"); + } + + return false; + } + + bool operator!=(const int i) const + { + return !operator==(i); + } + + bool operator!=(const unsigned int u) const + { + return !operator==(u); + } + + bool operator!=(const long long i) const + { + return !operator==(i); + } + + bool operator!=(const unsigned long long u) const + { + return !operator==(u); + } + + bool operator!=(const float f) const + { + return !operator==(f); + } + + bool operator!=(const bool b) const + { + return !operator==(b); + } + + bool operator!=(const TConstUnion& constant) const + { + return !operator==(constant); + } + + bool operator>(const TConstUnion& constant) const + { + assert(type == constant.type); + switch (type) { + case EbtInt: + if (iConst > constant.iConst) + return true; + + return false; + case EbtUint: + if (uConst > constant.uConst) + return true; + + return false; + case EbtInt64: + if (i64Const > constant.i64Const) + return true; + + return false; + case EbtUint64: + if (u64Const > constant.u64Const) + return true; + + return false; + case EbtDouble: + if (dConst > constant.dConst) + return true; + + return false; + default: + assert(false && "Default missing"); + return false; + } + } + + bool operator<(const TConstUnion& constant) const + { + assert(type == constant.type); + switch (type) { + case EbtInt: + if (iConst < constant.iConst) + return true; + + return false; + case EbtUint: + if (uConst < constant.uConst) + return true; + + return false; + case EbtInt64: + if (i64Const < constant.i64Const) + return true; + + return false; + case EbtUint64: + if (u64Const < constant.u64Const) + return true; + + return false; + case EbtDouble: + if (dConst < constant.dConst) + return true; + + return false; + default: + assert(false && "Default missing"); + return false; + } + } + + TConstUnion operator+(const TConstUnion& constant) const + { + TConstUnion returnValue; + assert(type == constant.type); + switch (type) { + case EbtInt: returnValue.setIConst(iConst + constant.iConst); break; + case EbtInt64: returnValue.setI64Const(i64Const + constant.i64Const); break; + case EbtUint: returnValue.setUConst(uConst + constant.uConst); break; + case EbtUint64: returnValue.setU64Const(u64Const + constant.u64Const); break; + case EbtDouble: returnValue.setDConst(dConst + constant.dConst); break; + default: assert(false && "Default missing"); + } + + return returnValue; + } + + TConstUnion operator-(const TConstUnion& constant) const + { + TConstUnion returnValue; + assert(type == constant.type); + switch (type) { + case EbtInt: returnValue.setIConst(iConst - constant.iConst); break; + case EbtInt64: returnValue.setI64Const(i64Const - constant.i64Const); break; + case EbtUint: returnValue.setUConst(uConst - constant.uConst); break; + case EbtUint64: returnValue.setU64Const(u64Const - constant.u64Const); break; + case EbtDouble: returnValue.setDConst(dConst - constant.dConst); break; + default: assert(false && "Default missing"); + } + + return returnValue; + } + + TConstUnion operator*(const TConstUnion& constant) const + { + TConstUnion returnValue; + assert(type == constant.type); + switch (type) { + case EbtInt: returnValue.setIConst(iConst * constant.iConst); break; + case EbtInt64: returnValue.setI64Const(i64Const * constant.i64Const); break; + case EbtUint: returnValue.setUConst(uConst * constant.uConst); break; + case EbtUint64: returnValue.setU64Const(u64Const * constant.u64Const); break; + case EbtDouble: returnValue.setDConst(dConst * constant.dConst); break; + default: assert(false && "Default missing"); + } + + return returnValue; + } + + TConstUnion operator%(const TConstUnion& constant) const + { + TConstUnion returnValue; + assert(type == constant.type); + switch (type) { + case EbtInt: returnValue.setIConst(iConst % constant.iConst); break; + case EbtInt64: returnValue.setI64Const(i64Const % constant.i64Const); break; + case EbtUint: returnValue.setUConst(uConst % constant.uConst); break; + case EbtUint64: returnValue.setU64Const(u64Const % constant.u64Const); break; + default: assert(false && "Default missing"); + } + + return returnValue; + } + + TConstUnion operator>>(const TConstUnion& constant) const + { + TConstUnion returnValue; + switch (type) { + case EbtInt: + switch (constant.type) { + case EbtInt: returnValue.setIConst(iConst >> constant.iConst); break; + case EbtUint: returnValue.setIConst(iConst >> constant.uConst); break; + case EbtInt64: returnValue.setIConst(iConst >> constant.i64Const); break; + case EbtUint64: returnValue.setIConst(iConst >> constant.u64Const); break; + default: assert(false && "Default missing"); + } + break; + case EbtUint: + switch (constant.type) { + case EbtInt: returnValue.setUConst(uConst >> constant.iConst); break; + case EbtUint: returnValue.setUConst(uConst >> constant.uConst); break; + case EbtInt64: returnValue.setUConst(uConst >> constant.i64Const); break; + case EbtUint64: returnValue.setUConst(uConst >> constant.u64Const); break; + default: assert(false && "Default missing"); + } + break; + case EbtInt64: + switch (constant.type) { + case EbtInt: returnValue.setI64Const(i64Const >> constant.iConst); break; + case EbtUint: returnValue.setI64Const(i64Const >> constant.uConst); break; + case EbtInt64: returnValue.setI64Const(i64Const >> constant.i64Const); break; + case EbtUint64: returnValue.setI64Const(i64Const >> constant.u64Const); break; + default: assert(false && "Default missing"); + } + break; + case EbtUint64: + switch (constant.type) { + case EbtInt: returnValue.setU64Const(u64Const >> constant.iConst); break; + case EbtUint: returnValue.setU64Const(u64Const >> constant.uConst); break; + case EbtInt64: returnValue.setU64Const(u64Const >> constant.i64Const); break; + case EbtUint64: returnValue.setU64Const(u64Const >> constant.u64Const); break; + default: assert(false && "Default missing"); + } + break; + default: assert(false && "Default missing"); + } + + return returnValue; + } + + TConstUnion operator<<(const TConstUnion& constant) const + { + TConstUnion returnValue; + switch (type) { + case EbtInt: + switch (constant.type) { + case EbtInt: returnValue.setIConst(iConst << constant.iConst); break; + case EbtUint: returnValue.setIConst(iConst << constant.uConst); break; + case EbtInt64: returnValue.setIConst(iConst << constant.i64Const); break; + case EbtUint64: returnValue.setIConst(iConst << constant.u64Const); break; + default: assert(false && "Default missing"); + } + break; + case EbtUint: + switch (constant.type) { + case EbtInt: returnValue.setUConst(uConst << constant.iConst); break; + case EbtUint: returnValue.setUConst(uConst << constant.uConst); break; + case EbtInt64: returnValue.setUConst(uConst << constant.i64Const); break; + case EbtUint64: returnValue.setUConst(uConst << constant.u64Const); break; + default: assert(false && "Default missing"); + } + break; + case EbtInt64: + switch (constant.type) { + case EbtInt: returnValue.setI64Const(i64Const << constant.iConst); break; + case EbtUint: returnValue.setI64Const(i64Const << constant.uConst); break; + case EbtInt64: returnValue.setI64Const(i64Const << constant.i64Const); break; + case EbtUint64: returnValue.setI64Const(i64Const << constant.u64Const); break; + default: assert(false && "Default missing"); + } + break; + case EbtUint64: + switch (constant.type) { + case EbtInt: returnValue.setU64Const(u64Const << constant.iConst); break; + case EbtUint: returnValue.setU64Const(u64Const << constant.uConst); break; + case EbtInt64: returnValue.setU64Const(u64Const << constant.i64Const); break; + case EbtUint64: returnValue.setU64Const(u64Const << constant.u64Const); break; + default: assert(false && "Default missing"); + } + break; + default: assert(false && "Default missing"); + } + + return returnValue; + } + + TConstUnion operator&(const TConstUnion& constant) const + { + TConstUnion returnValue; + assert(type == constant.type); + switch (type) { + case EbtInt: returnValue.setIConst(iConst & constant.iConst); break; + case EbtUint: returnValue.setUConst(uConst & constant.uConst); break; + case EbtInt64: returnValue.setI64Const(i64Const & constant.i64Const); break; + case EbtUint64: returnValue.setU64Const(u64Const & constant.u64Const); break; + default: assert(false && "Default missing"); + } + + return returnValue; + } + + TConstUnion operator|(const TConstUnion& constant) const + { + TConstUnion returnValue; + assert(type == constant.type); + switch (type) { + case EbtInt: returnValue.setIConst(iConst | constant.iConst); break; + case EbtUint: returnValue.setUConst(uConst | constant.uConst); break; + case EbtInt64: returnValue.setI64Const(i64Const | constant.i64Const); break; + case EbtUint64: returnValue.setU64Const(u64Const | constant.u64Const); break; + default: assert(false && "Default missing"); + } + + return returnValue; + } + + TConstUnion operator^(const TConstUnion& constant) const + { + TConstUnion returnValue; + assert(type == constant.type); + switch (type) { + case EbtInt: returnValue.setIConst(iConst ^ constant.iConst); break; + case EbtUint: returnValue.setUConst(uConst ^ constant.uConst); break; + case EbtInt64: returnValue.setI64Const(i64Const ^ constant.i64Const); break; + case EbtUint64: returnValue.setU64Const(u64Const ^ constant.u64Const); break; + default: assert(false && "Default missing"); + } + + return returnValue; + } + + TConstUnion operator~() const + { + TConstUnion returnValue; + switch (type) { + case EbtInt: returnValue.setIConst(~iConst); break; + case EbtUint: returnValue.setUConst(~uConst); break; + case EbtInt64: returnValue.setI64Const(~i64Const); break; + case EbtUint64: returnValue.setU64Const(~u64Const); break; + default: assert(false && "Default missing"); + } + + return returnValue; + } + + TConstUnion operator&&(const TConstUnion& constant) const + { + TConstUnion returnValue; + assert(type == constant.type); + switch (type) { + case EbtBool: returnValue.setBConst(bConst && constant.bConst); break; + default: assert(false && "Default missing"); + } + + return returnValue; + } + + TConstUnion operator||(const TConstUnion& constant) const + { + TConstUnion returnValue; + assert(type == constant.type); + switch (type) { + case EbtBool: returnValue.setBConst(bConst || constant.bConst); break; + default: assert(false && "Default missing"); + } + + return returnValue; + } + + TBasicType getType() const { return type; } + +private: + union { + int iConst; // used for ivec, scalar ints + unsigned int uConst; // used for uvec, scalar uints + long long i64Const; // used for i64vec, scalar int64s + unsigned long long u64Const; // used for u64vec, scalar uint64s + bool bConst; // used for bvec, scalar bools + double dConst; // used for vec, dvec, mat, dmat, scalar floats and doubles + const TString* sConst; // string constant + }; + + TBasicType type; +}; + +// Encapsulate having a pointer to an array of TConstUnion, +// which only needs to be allocated if its size is going to be +// bigger than 0. +// +// One convenience is being able to use [] to go inside the array, instead +// of C++ assuming it as an array of pointers to vectors. +// +// General usage is that the size is known up front, and it is +// created once with the proper size. +// +class TConstUnionArray { +public: + POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) + + TConstUnionArray() : unionArray(nullptr) { } + virtual ~TConstUnionArray() { } + + explicit TConstUnionArray(int size) + { + if (size == 0) + unionArray = nullptr; + else + unionArray = new TConstUnionVector(size); + } + TConstUnionArray(const TConstUnionArray& a) : unionArray(a.unionArray) { } + TConstUnionArray(const TConstUnionArray& a, int start, int size) + { + unionArray = new TConstUnionVector(size); + for (int i = 0; i < size; ++i) + (*unionArray)[i] = a[start + i]; + } + + // Use this constructor for a smear operation + TConstUnionArray(int size, const TConstUnion& val) + { + unionArray = new TConstUnionVector(size, val); + } + + int size() const { return unionArray ? (int)unionArray->size() : 0; } + TConstUnion& operator[](size_t index) { return (*unionArray)[index]; } + const TConstUnion& operator[](size_t index) const { return (*unionArray)[index]; } + bool operator==(const TConstUnionArray& rhs) const + { + // this includes the case that both are unallocated + if (unionArray == rhs.unionArray) + return true; + + if (! unionArray || ! rhs.unionArray) + return false; + + return *unionArray == *rhs.unionArray; + } + bool operator!=(const TConstUnionArray& rhs) const { return ! operator==(rhs); } + + double dot(const TConstUnionArray& rhs) + { + assert(rhs.unionArray->size() == unionArray->size()); + double sum = 0.0; + + for (size_t comp = 0; comp < unionArray->size(); ++comp) + sum += (*this)[comp].getDConst() * rhs[comp].getDConst(); + + return sum; + } + + bool empty() const { return unionArray == nullptr; } + +protected: + typedef TVector TConstUnionVector; + TConstUnionVector* unionArray; +}; + +} // end namespace glslang + +#endif // _CONSTANT_UNION_INCLUDED_ diff --git a/third_party/glslang-spirv/Include/InfoSink.h b/third_party/glslang-spirv/Include/InfoSink.h new file mode 100644 index 000000000..dceb603cf --- /dev/null +++ b/third_party/glslang-spirv/Include/InfoSink.h @@ -0,0 +1,144 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef _INFOSINK_INCLUDED_ +#define _INFOSINK_INCLUDED_ + +#include "../Include/Common.h" +#include + +namespace glslang { + +// +// TPrefixType is used to centralize how info log messages start. +// See below. +// +enum TPrefixType { + EPrefixNone, + EPrefixWarning, + EPrefixError, + EPrefixInternalError, + EPrefixUnimplemented, + EPrefixNote +}; + +enum TOutputStream { + ENull = 0, + EDebugger = 0x01, + EStdOut = 0x02, + EString = 0x04, +}; +// +// Encapsulate info logs for all objects that have them. +// +// The methods are a general set of tools for getting a variety of +// messages and types inserted into the log. +// +class TInfoSinkBase { +public: + TInfoSinkBase() : outputStream(4) {} + void erase() { sink.erase(); } + TInfoSinkBase& operator<<(const TPersistString& t) { append(t); return *this; } + TInfoSinkBase& operator<<(char c) { append(1, c); return *this; } + TInfoSinkBase& operator<<(const char* s) { append(s); return *this; } + TInfoSinkBase& operator<<(int n) { append(String(n)); return *this; } + TInfoSinkBase& operator<<(unsigned int n) { append(String(n)); return *this; } + TInfoSinkBase& operator<<(float n) { const int size = 40; char buf[size]; + snprintf(buf, size, (fabs(n) > 1e-8 && fabs(n) < 1e8) || n == 0.0f ? "%f" : "%g", n); + append(buf); + return *this; } + TInfoSinkBase& operator+(const TPersistString& t) { append(t); return *this; } + TInfoSinkBase& operator+(const TString& t) { append(t); return *this; } + TInfoSinkBase& operator<<(const TString& t) { append(t); return *this; } + TInfoSinkBase& operator+(const char* s) { append(s); return *this; } + const char* c_str() const { return sink.c_str(); } + void prefix(TPrefixType message) { + switch(message) { + case EPrefixNone: break; + case EPrefixWarning: append("WARNING: "); break; + case EPrefixError: append("ERROR: "); break; + case EPrefixInternalError: append("INTERNAL ERROR: "); break; + case EPrefixUnimplemented: append("UNIMPLEMENTED: "); break; + case EPrefixNote: append("NOTE: "); break; + default: append("UNKNOWN ERROR: "); break; + } + } + void location(const TSourceLoc& loc) { + const int maxSize = 24; + char locText[maxSize]; + snprintf(locText, maxSize, ":%d", loc.line); + append(loc.getStringNameOrNum(false).c_str()); + append(locText); + append(": "); + } + void message(TPrefixType message, const char* s) { + prefix(message); + append(s); + append("\n"); + } + void message(TPrefixType message, const char* s, const TSourceLoc& loc) { + prefix(message); + location(loc); + append(s); + append("\n"); + } + + void setOutputStream(int output = 4) + { + outputStream = output; + } + +protected: + void append(const char* s); + + void append(int count, char c); + void append(const TPersistString& t); + void append(const TString& t); + + void checkMem(size_t growth) { if (sink.capacity() < sink.size() + growth + 2) + sink.reserve(sink.capacity() + sink.capacity() / 2); } + void appendToStream(const char* s); + TPersistString sink; + int outputStream; +}; + +} // end namespace glslang + +class TInfoSink { +public: + glslang::TInfoSinkBase info; + glslang::TInfoSinkBase debug; +}; + +#endif // _INFOSINK_INCLUDED_ diff --git a/third_party/glslang-spirv/Include/InitializeGlobals.h b/third_party/glslang-spirv/Include/InitializeGlobals.h new file mode 100644 index 000000000..95d0a40e9 --- /dev/null +++ b/third_party/glslang-spirv/Include/InitializeGlobals.h @@ -0,0 +1,44 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef __INITIALIZE_GLOBALS_INCLUDED_ +#define __INITIALIZE_GLOBALS_INCLUDED_ + +namespace glslang { + +bool InitializePoolIndex(); + +} // end namespace glslang + +#endif // __INITIALIZE_GLOBALS_INCLUDED_ diff --git a/third_party/glslang-spirv/Include/PoolAlloc.h b/third_party/glslang-spirv/Include/PoolAlloc.h new file mode 100644 index 000000000..0e237a6a2 --- /dev/null +++ b/third_party/glslang-spirv/Include/PoolAlloc.h @@ -0,0 +1,317 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2013 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef _POOLALLOC_INCLUDED_ +#define _POOLALLOC_INCLUDED_ + +#ifdef _DEBUG +# define GUARD_BLOCKS // define to enable guard block sanity checking +#endif + +// +// This header defines an allocator that can be used to efficiently +// allocate a large number of small requests for heap memory, with the +// intention that they are not individually deallocated, but rather +// collectively deallocated at one time. +// +// This simultaneously +// +// * Makes each individual allocation much more efficient; the +// typical allocation is trivial. +// * Completely avoids the cost of doing individual deallocation. +// * Saves the trouble of tracking down and plugging a large class of leaks. +// +// Individual classes can use this allocator by supplying their own +// new and delete methods. +// +// STL containers can use this allocator by using the pool_allocator +// class as the allocator (second) template argument. +// + +#include +#include +#include + +namespace glslang { + +// If we are using guard blocks, we must track each individual +// allocation. If we aren't using guard blocks, these +// never get instantiated, so won't have any impact. +// + +class TAllocation { +public: + TAllocation(size_t size, unsigned char* mem, TAllocation* prev = 0) : + size(size), mem(mem), prevAlloc(prev) { + // Allocations are bracketed: + // [allocationHeader][initialGuardBlock][userData][finalGuardBlock] + // This would be cleaner with if (guardBlockSize)..., but that + // makes the compiler print warnings about 0 length memsets, + // even with the if() protecting them. +# ifdef GUARD_BLOCKS + memset(preGuard(), guardBlockBeginVal, guardBlockSize); + memset(data(), userDataFill, size); + memset(postGuard(), guardBlockEndVal, guardBlockSize); +# endif + } + + void check() const { + checkGuardBlock(preGuard(), guardBlockBeginVal, "before"); + checkGuardBlock(postGuard(), guardBlockEndVal, "after"); + } + + void checkAllocList() const; + + // Return total size needed to accommodate user buffer of 'size', + // plus our tracking data. + inline static size_t allocationSize(size_t size) { + return size + 2 * guardBlockSize + headerSize(); + } + + // Offset from surrounding buffer to get to user data buffer. + inline static unsigned char* offsetAllocation(unsigned char* m) { + return m + guardBlockSize + headerSize(); + } + +private: + void checkGuardBlock(unsigned char* blockMem, unsigned char val, const char* locText) const; + + // Find offsets to pre and post guard blocks, and user data buffer + unsigned char* preGuard() const { return mem + headerSize(); } + unsigned char* data() const { return preGuard() + guardBlockSize; } + unsigned char* postGuard() const { return data() + size; } + + size_t size; // size of the user data area + unsigned char* mem; // beginning of our allocation (pts to header) + TAllocation* prevAlloc; // prior allocation in the chain + + const static unsigned char guardBlockBeginVal; + const static unsigned char guardBlockEndVal; + const static unsigned char userDataFill; + + const static size_t guardBlockSize; +# ifdef GUARD_BLOCKS + inline static size_t headerSize() { return sizeof(TAllocation); } +# else + inline static size_t headerSize() { return 0; } +# endif +}; + +// +// There are several stacks. One is to track the pushing and popping +// of the user, and not yet implemented. The others are simply a +// repositories of free pages or used pages. +// +// Page stacks are linked together with a simple header at the beginning +// of each allocation obtained from the underlying OS. Multi-page allocations +// are returned to the OS. Individual page allocations are kept for future +// re-use. +// +// The "page size" used is not, nor must it match, the underlying OS +// page size. But, having it be about that size or equal to a set of +// pages is likely most optimal. +// +class TPoolAllocator { +public: + TPoolAllocator(int growthIncrement = 8*1024, int allocationAlignment = 16); + + // + // Don't call the destructor just to free up the memory, call pop() + // + ~TPoolAllocator(); + + // + // Call push() to establish a new place to pop memory too. Does not + // have to be called to get things started. + // + void push(); + + // + // Call pop() to free all memory allocated since the last call to push(), + // or if no last call to push, frees all memory since first allocation. + // + void pop(); + + // + // Call popAll() to free all memory allocated. + // + void popAll(); + + // + // Call allocate() to actually acquire memory. Returns 0 if no memory + // available, otherwise a properly aligned pointer to 'numBytes' of memory. + // + void* allocate(size_t numBytes); + + // + // There is no deallocate. The point of this class is that + // deallocation can be skipped by the user of it, as the model + // of use is to simultaneously deallocate everything at once + // by calling pop(), and to not have to solve memory leak problems. + // + +protected: + friend struct tHeader; + + struct tHeader { + tHeader(tHeader* nextPage, size_t pageCount) : +#ifdef GUARD_BLOCKS + lastAllocation(0), +#endif + nextPage(nextPage), pageCount(pageCount) { } + + ~tHeader() { +#ifdef GUARD_BLOCKS + if (lastAllocation) + lastAllocation->checkAllocList(); +#endif + } + +#ifdef GUARD_BLOCKS + TAllocation* lastAllocation; +#endif + tHeader* nextPage; + size_t pageCount; + }; + + struct tAllocState { + size_t offset; + tHeader* page; + }; + typedef std::vector tAllocStack; + + // Track allocations if and only if we're using guard blocks +#ifndef GUARD_BLOCKS + void* initializeAllocation(tHeader*, unsigned char* memory, size_t) { +#else + void* initializeAllocation(tHeader* block, unsigned char* memory, size_t numBytes) { + new(memory) TAllocation(numBytes, memory, block->lastAllocation); + block->lastAllocation = reinterpret_cast(memory); +#endif + + // This is optimized entirely away if GUARD_BLOCKS is not defined. + return TAllocation::offsetAllocation(memory); + } + + size_t pageSize; // granularity of allocation from the OS + size_t alignment; // all returned allocations will be aligned at + // this granularity, which will be a power of 2 + size_t alignmentMask; + size_t headerSkip; // amount of memory to skip to make room for the + // header (basically, size of header, rounded + // up to make it aligned + size_t currentPageOffset; // next offset in top of inUseList to allocate from + tHeader* freeList; // list of popped memory + tHeader* inUseList; // list of all memory currently being used + tAllocStack stack; // stack of where to allocate from, to partition pool + + int numCalls; // just an interesting statistic + size_t totalBytes; // just an interesting statistic +private: + TPoolAllocator& operator=(const TPoolAllocator&); // don't allow assignment operator + TPoolAllocator(const TPoolAllocator&); // don't allow default copy constructor +}; + +// +// There could potentially be many pools with pops happening at +// different times. But a simple use is to have a global pop +// with everyone using the same global allocator. +// +extern TPoolAllocator& GetThreadPoolAllocator(); +void SetThreadPoolAllocator(TPoolAllocator* poolAllocator); + +// +// This STL compatible allocator is intended to be used as the allocator +// parameter to templatized STL containers, like vector and map. +// +// It will use the pools for allocation, and not +// do any deallocation, but will still do destruction. +// +template +class pool_allocator { +public: + typedef size_t size_type; + typedef ptrdiff_t difference_type; + typedef T *pointer; + typedef const T *const_pointer; + typedef T& reference; + typedef const T& const_reference; + typedef T value_type; + template + struct rebind { + typedef pool_allocator other; + }; + pointer address(reference x) const { return &x; } + const_pointer address(const_reference x) const { return &x; } + + pool_allocator() : allocator(GetThreadPoolAllocator()) { } + pool_allocator(TPoolAllocator& a) : allocator(a) { } + pool_allocator(const pool_allocator& p) : allocator(p.allocator) { } + + template + pool_allocator(const pool_allocator& p) : allocator(p.getAllocator()) { } + + pointer allocate(size_type n) { + return reinterpret_cast(getAllocator().allocate(n * sizeof(T))); } + pointer allocate(size_type n, const void*) { + return reinterpret_cast(getAllocator().allocate(n * sizeof(T))); } + + void deallocate(void*, size_type) { } + void deallocate(pointer, size_type) { } + + pointer _Charalloc(size_t n) { + return reinterpret_cast(getAllocator().allocate(n)); } + + void construct(pointer p, const T& val) { new ((void *)p) T(val); } + void destroy(pointer p) { p->T::~T(); } + + bool operator==(const pool_allocator& rhs) const { return &getAllocator() == &rhs.getAllocator(); } + bool operator!=(const pool_allocator& rhs) const { return &getAllocator() != &rhs.getAllocator(); } + + size_type max_size() const { return static_cast(-1) / sizeof(T); } + size_type max_size(int size) const { return static_cast(-1) / size; } + + void setAllocator(TPoolAllocator* a) { allocator = *a; } + TPoolAllocator& getAllocator() const { return allocator; } + +protected: + pool_allocator& operator=(const pool_allocator&) { return *this; } + TPoolAllocator& allocator; +}; + +} // end namespace glslang + +#endif // _POOLALLOC_INCLUDED_ diff --git a/third_party/glslang-spirv/Include/ResourceLimits.h b/third_party/glslang-spirv/Include/ResourceLimits.h new file mode 100644 index 000000000..0d07b8c84 --- /dev/null +++ b/third_party/glslang-spirv/Include/ResourceLimits.h @@ -0,0 +1,140 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2013 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef _RESOURCE_LIMITS_INCLUDED_ +#define _RESOURCE_LIMITS_INCLUDED_ + +struct TLimits { + bool nonInductiveForLoops; + bool whileLoops; + bool doWhileLoops; + bool generalUniformIndexing; + bool generalAttributeMatrixVectorIndexing; + bool generalVaryingIndexing; + bool generalSamplerIndexing; + bool generalVariableIndexing; + bool generalConstantMatrixVectorIndexing; +}; + +struct TBuiltInResource { + int maxLights; + int maxClipPlanes; + int maxTextureUnits; + int maxTextureCoords; + int maxVertexAttribs; + int maxVertexUniformComponents; + int maxVaryingFloats; + int maxVertexTextureImageUnits; + int maxCombinedTextureImageUnits; + int maxTextureImageUnits; + int maxFragmentUniformComponents; + int maxDrawBuffers; + int maxVertexUniformVectors; + int maxVaryingVectors; + int maxFragmentUniformVectors; + int maxVertexOutputVectors; + int maxFragmentInputVectors; + int minProgramTexelOffset; + int maxProgramTexelOffset; + int maxClipDistances; + int maxComputeWorkGroupCountX; + int maxComputeWorkGroupCountY; + int maxComputeWorkGroupCountZ; + int maxComputeWorkGroupSizeX; + int maxComputeWorkGroupSizeY; + int maxComputeWorkGroupSizeZ; + int maxComputeUniformComponents; + int maxComputeTextureImageUnits; + int maxComputeImageUniforms; + int maxComputeAtomicCounters; + int maxComputeAtomicCounterBuffers; + int maxVaryingComponents; + int maxVertexOutputComponents; + int maxGeometryInputComponents; + int maxGeometryOutputComponents; + int maxFragmentInputComponents; + int maxImageUnits; + int maxCombinedImageUnitsAndFragmentOutputs; + int maxCombinedShaderOutputResources; + int maxImageSamples; + int maxVertexImageUniforms; + int maxTessControlImageUniforms; + int maxTessEvaluationImageUniforms; + int maxGeometryImageUniforms; + int maxFragmentImageUniforms; + int maxCombinedImageUniforms; + int maxGeometryTextureImageUnits; + int maxGeometryOutputVertices; + int maxGeometryTotalOutputComponents; + int maxGeometryUniformComponents; + int maxGeometryVaryingComponents; + int maxTessControlInputComponents; + int maxTessControlOutputComponents; + int maxTessControlTextureImageUnits; + int maxTessControlUniformComponents; + int maxTessControlTotalOutputComponents; + int maxTessEvaluationInputComponents; + int maxTessEvaluationOutputComponents; + int maxTessEvaluationTextureImageUnits; + int maxTessEvaluationUniformComponents; + int maxTessPatchComponents; + int maxPatchVertices; + int maxTessGenLevel; + int maxViewports; + int maxVertexAtomicCounters; + int maxTessControlAtomicCounters; + int maxTessEvaluationAtomicCounters; + int maxGeometryAtomicCounters; + int maxFragmentAtomicCounters; + int maxCombinedAtomicCounters; + int maxAtomicCounterBindings; + int maxVertexAtomicCounterBuffers; + int maxTessControlAtomicCounterBuffers; + int maxTessEvaluationAtomicCounterBuffers; + int maxGeometryAtomicCounterBuffers; + int maxFragmentAtomicCounterBuffers; + int maxCombinedAtomicCounterBuffers; + int maxAtomicCounterBufferSize; + int maxTransformFeedbackBuffers; + int maxTransformFeedbackInterleavedComponents; + int maxCullDistances; + int maxCombinedClipAndCullDistances; + int maxSamples; + + TLimits limits; +}; + +#endif // _RESOURCE_LIMITS_INCLUDED_ diff --git a/third_party/glslang-spirv/Include/ShHandle.h b/third_party/glslang-spirv/Include/ShHandle.h new file mode 100644 index 000000000..df07bd8ed --- /dev/null +++ b/third_party/glslang-spirv/Include/ShHandle.h @@ -0,0 +1,176 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef _SHHANDLE_INCLUDED_ +#define _SHHANDLE_INCLUDED_ + +// +// Machine independent part of the compiler private objects +// sent as ShHandle to the driver. +// +// This should not be included by driver code. +// + +#define SH_EXPORTING +#include "../Public/ShaderLang.h" +#include "../MachineIndependent/Versions.h" +#include "InfoSink.h" + +class TCompiler; +class TLinker; +class TUniformMap; + +// +// The base class used to back handles returned to the driver. +// +class TShHandleBase { +public: + TShHandleBase() { pool = new glslang::TPoolAllocator; } + virtual ~TShHandleBase() { delete pool; } + virtual TCompiler* getAsCompiler() { return 0; } + virtual TLinker* getAsLinker() { return 0; } + virtual TUniformMap* getAsUniformMap() { return 0; } + virtual glslang::TPoolAllocator* getPool() const { return pool; } +private: + glslang::TPoolAllocator* pool; +}; + +// +// The base class for the machine dependent linker to derive from +// for managing where uniforms live. +// +class TUniformMap : public TShHandleBase { +public: + TUniformMap() { } + virtual ~TUniformMap() { } + virtual TUniformMap* getAsUniformMap() { return this; } + virtual int getLocation(const char* name) = 0; + virtual TInfoSink& getInfoSink() { return infoSink; } + TInfoSink infoSink; +}; + +class TIntermNode; + +// +// The base class for the machine dependent compiler to derive from +// for managing object code from the compile. +// +class TCompiler : public TShHandleBase { +public: + TCompiler(EShLanguage l, TInfoSink& sink) : infoSink(sink) , language(l), haveValidObjectCode(false) { } + virtual ~TCompiler() { } + EShLanguage getLanguage() { return language; } + virtual TInfoSink& getInfoSink() { return infoSink; } + + virtual bool compile(TIntermNode* root, int version = 0, EProfile profile = ENoProfile) = 0; + + virtual TCompiler* getAsCompiler() { return this; } + virtual bool linkable() { return haveValidObjectCode; } + + TInfoSink& infoSink; +protected: + TCompiler& operator=(TCompiler&); + + EShLanguage language; + bool haveValidObjectCode; +}; + +// +// Link operations are based on a list of compile results... +// +typedef glslang::TVector TCompilerList; +typedef glslang::TVector THandleList; + +// +// The base class for the machine dependent linker to derive from +// to manage the resulting executable. +// + +class TLinker : public TShHandleBase { +public: + TLinker(EShExecutable e, TInfoSink& iSink) : + infoSink(iSink), + executable(e), + haveReturnableObjectCode(false), + appAttributeBindings(0), + fixedAttributeBindings(0), + excludedAttributes(0), + excludedCount(0), + uniformBindings(0) { } + virtual TLinker* getAsLinker() { return this; } + virtual ~TLinker() { } + virtual bool link(TCompilerList&, TUniformMap*) = 0; + virtual bool link(THandleList&) { return false; } + virtual void setAppAttributeBindings(const ShBindingTable* t) { appAttributeBindings = t; } + virtual void setFixedAttributeBindings(const ShBindingTable* t) { fixedAttributeBindings = t; } + virtual void getAttributeBindings(ShBindingTable const **t) const = 0; + virtual void setExcludedAttributes(const int* attributes, int count) { excludedAttributes = attributes; excludedCount = count; } + virtual ShBindingTable* getUniformBindings() const { return uniformBindings; } + virtual const void* getObjectCode() const { return 0; } // a real compiler would be returning object code here + virtual TInfoSink& getInfoSink() { return infoSink; } + TInfoSink& infoSink; +protected: + TLinker& operator=(TLinker&); + EShExecutable executable; + bool haveReturnableObjectCode; // true when objectCode is acceptable to send to driver + + const ShBindingTable* appAttributeBindings; + const ShBindingTable* fixedAttributeBindings; + const int* excludedAttributes; + int excludedCount; + ShBindingTable* uniformBindings; // created by the linker +}; + +// +// This is the interface between the machine independent code +// and the machine dependent code. +// +// The machine dependent code should derive from the classes +// above. Then Construct*() and Delete*() will create and +// destroy the machine dependent objects, which contain the +// above machine independent information. +// +TCompiler* ConstructCompiler(EShLanguage, int); + +TShHandleBase* ConstructLinker(EShExecutable, int); +TShHandleBase* ConstructBindings(); +void DeleteLinker(TShHandleBase*); +void DeleteBindingList(TShHandleBase* bindingList); + +TUniformMap* ConstructUniformMap(); +void DeleteCompiler(TCompiler*); + +void DeleteUniformMap(TUniformMap*); + +#endif // _SHHANDLE_INCLUDED_ diff --git a/third_party/glslang-spirv/Include/Types.h b/third_party/glslang-spirv/Include/Types.h new file mode 100644 index 000000000..cc847b5e7 --- /dev/null +++ b/third_party/glslang-spirv/Include/Types.h @@ -0,0 +1,1924 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2016 LunarG, Inc. +// Copyright (C) 2015-2016 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef _TYPES_INCLUDED +#define _TYPES_INCLUDED + +#include "../Include/Common.h" +#include "../Include/BaseTypes.h" +#include "../Public/ShaderLang.h" +#include "arrays.h" + +#include + +namespace glslang { + +const int GlslangMaxTypeLength = 200; // TODO: need to print block/struct one member per line, so this can stay bounded + +const char* const AnonymousPrefix = "anon@"; // for something like a block whose members can be directly accessed +inline bool IsAnonymous(const TString& name) +{ + return name.compare(0, 5, AnonymousPrefix) == 0; +} + +// +// Details within a sampler type +// +enum TSamplerDim { + EsdNone, + Esd1D, + Esd2D, + Esd3D, + EsdCube, + EsdRect, + EsdBuffer, + EsdSubpass, // goes only with non-sampled image (image is true) + EsdNumDims +}; + +struct TSampler { // misnomer now; includes images, textures without sampler, and textures with sampler + TBasicType type : 8; // type returned by sampler + TSamplerDim dim : 8; + bool arrayed : 1; + bool shadow : 1; + bool ms : 1; + bool image : 1; // image, combined should be false + bool combined : 1; // true means texture is combined with a sampler, false means texture with no sampler + bool sampler : 1; // true means a pure sampler, other fields should be clear() + bool external : 1; // GL_OES_EGL_image_external + unsigned int vectorSize : 3; // vector return type size. + + // Some languages support structures as sample results. Storing the whole structure in the + // TSampler is too large, so there is an index to a separate table. + static const unsigned structReturnIndexBits = 4; // number of index bits to use. + static const unsigned structReturnSlots = (1< TTypeList; + +typedef TVector TIdentifierList; + +// +// Following are a series of helper enums for managing layouts and qualifiers, +// used for TPublicType, TType, others. +// + +enum TLayoutPacking { + ElpNone, + ElpShared, // default, but different than saying nothing + ElpStd140, + ElpStd430, + ElpPacked, + ElpCount // If expanding, see bitfield width below +}; + +enum TLayoutMatrix { + ElmNone, + ElmRowMajor, + ElmColumnMajor, // default, but different than saying nothing + ElmCount // If expanding, see bitfield width below +}; + +// Union of geometry shader and tessellation shader geometry types. +// They don't go into TType, but rather have current state per shader or +// active parser type (TPublicType). +enum TLayoutGeometry { + ElgNone, + ElgPoints, + ElgLines, + ElgLinesAdjacency, + ElgLineStrip, + ElgTriangles, + ElgTrianglesAdjacency, + ElgTriangleStrip, + ElgQuads, + ElgIsolines, +}; + +enum TVertexSpacing { + EvsNone, + EvsEqual, + EvsFractionalEven, + EvsFractionalOdd +}; + +enum TVertexOrder { + EvoNone, + EvoCw, + EvoCcw +}; + +// Note: order matters, as type of format is done by comparison. +enum TLayoutFormat { + ElfNone, + + // Float image + ElfRgba32f, + ElfRgba16f, + ElfR32f, + ElfRgba8, + ElfRgba8Snorm, + + ElfEsFloatGuard, // to help with comparisons + + ElfRg32f, + ElfRg16f, + ElfR11fG11fB10f, + ElfR16f, + ElfRgba16, + ElfRgb10A2, + ElfRg16, + ElfRg8, + ElfR16, + ElfR8, + ElfRgba16Snorm, + ElfRg16Snorm, + ElfRg8Snorm, + ElfR16Snorm, + ElfR8Snorm, + + ElfFloatGuard, // to help with comparisons + + // Int image + ElfRgba32i, + ElfRgba16i, + ElfRgba8i, + ElfR32i, + + ElfEsIntGuard, // to help with comparisons + + ElfRg32i, + ElfRg16i, + ElfRg8i, + ElfR16i, + ElfR8i, + + ElfIntGuard, // to help with comparisons + + // Uint image + ElfRgba32ui, + ElfRgba16ui, + ElfRgba8ui, + ElfR32ui, + + ElfEsUintGuard, // to help with comparisons + + ElfRg32ui, + ElfRg16ui, + ElfRgb10a2ui, + ElfRg8ui, + ElfR16ui, + ElfR8ui, + + ElfCount +}; + +enum TLayoutDepth { + EldNone, + EldAny, + EldGreater, + EldLess, + EldUnchanged, + + EldCount +}; + +enum TBlendEquationShift { + // No 'EBlendNone': + // These are used as bit-shift amounts. A mask of such shifts will have type 'int', + // and in that space, 0 means no bits set, or none. In this enum, 0 means (1 << 0), a bit is set. + EBlendMultiply, + EBlendScreen, + EBlendOverlay, + EBlendDarken, + EBlendLighten, + EBlendColordodge, + EBlendColorburn, + EBlendHardlight, + EBlendSoftlight, + EBlendDifference, + EBlendExclusion, + EBlendHslHue, + EBlendHslSaturation, + EBlendHslColor, + EBlendHslLuminosity, + EBlendAllEquations, + + EBlendCount +}; + +class TQualifier { +public: + static const int layoutNotSet = -1; + + void clear() + { + precision = EpqNone; + invariant = false; + noContraction = false; + makeTemporary(); + declaredBuiltIn = EbvNone; + } + + // drop qualifiers that don't belong in a temporary variable + void makeTemporary() + { + semanticName = nullptr; + storage = EvqTemporary; + builtIn = EbvNone; + clearInterstage(); + clearMemory(); + specConstant = false; + clearLayout(); + } + + void clearInterstage() + { + clearInterpolation(); + patch = false; + sample = false; + } + + void clearInterpolation() + { + centroid = false; + smooth = false; + flat = false; + nopersp = false; +#ifdef AMD_EXTENSIONS + explicitInterp = false; +#endif + } + + void clearMemory() + { + coherent = false; + volatil = false; + restrict = false; + readonly = false; + writeonly = false; + } + + // Drop just the storage qualification, which perhaps should + // never be done, as it is fundamentally inconsistent, but need to + // explore what downstream consumers need. + // E.g., in a deference, it is an inconsistency between: + // A) partially dereferenced resource is still in the storage class it started in + // B) partially dereferenced resource is a new temporary object + // If A, then nothing should change, if B, then everything should change, but this is half way. + void makePartialTemporary() + { + storage = EvqTemporary; + specConstant = false; + } + + const char* semanticName; + TStorageQualifier storage : 6; + TBuiltInVariable builtIn : 8; + TBuiltInVariable declaredBuiltIn : 8; + TPrecisionQualifier precision : 3; + bool invariant : 1; // require canonical treatment for cross-shader invariance + bool noContraction: 1; // prevent contraction and reassociation, e.g., for 'precise' keyword, and expressions it affects + bool centroid : 1; + bool smooth : 1; + bool flat : 1; + bool nopersp : 1; +#ifdef AMD_EXTENSIONS + bool explicitInterp : 1; +#endif + bool patch : 1; + bool sample : 1; + bool coherent : 1; + bool volatil : 1; + bool restrict : 1; + bool readonly : 1; + bool writeonly : 1; + bool specConstant : 1; // having a constant_id is not sufficient: expressions have no id, but are still specConstant + + bool isMemory() const + { + return coherent || volatil || restrict || readonly || writeonly; + } + bool isInterpolation() const + { +#ifdef AMD_EXTENSIONS + return flat || smooth || nopersp || explicitInterp; +#else + return flat || smooth || nopersp; +#endif + } + bool isAuxiliary() const + { + return centroid || patch || sample; + } + + bool isPipeInput() const + { + switch (storage) { + case EvqVaryingIn: + case EvqFragCoord: + case EvqPointCoord: + case EvqFace: + case EvqVertexId: + case EvqInstanceId: + return true; + default: + return false; + } + } + + bool isPipeOutput() const + { + switch (storage) { + case EvqPosition: + case EvqPointSize: + case EvqClipVertex: + case EvqVaryingOut: + case EvqFragColor: + case EvqFragDepth: + return true; + default: + return false; + } + } + + bool isParamInput() const + { + switch (storage) { + case EvqIn: + case EvqInOut: + case EvqConstReadOnly: + return true; + default: + return false; + } + } + + bool isParamOutput() const + { + switch (storage) { + case EvqOut: + case EvqInOut: + return true; + default: + return false; + } + } + + bool isUniformOrBuffer() const + { + switch (storage) { + case EvqUniform: + case EvqBuffer: + return true; + default: + return false; + } + } + + bool isIo() const + { + switch (storage) { + case EvqUniform: + case EvqBuffer: + case EvqVaryingIn: + case EvqFragCoord: + case EvqPointCoord: + case EvqFace: + case EvqVertexId: + case EvqInstanceId: + case EvqPosition: + case EvqPointSize: + case EvqClipVertex: + case EvqVaryingOut: + case EvqFragColor: + case EvqFragDepth: + return true; + default: + return false; + } + } + + // True if this type of IO is supposed to be arrayed with extra level for per-vertex data + bool isArrayedIo(EShLanguage language) const + { + switch (language) { + case EShLangGeometry: + return isPipeInput(); + case EShLangTessControl: + return ! patch && (isPipeInput() || isPipeOutput()); + case EShLangTessEvaluation: + return ! patch && isPipeInput(); + default: + return false; + } + } + + // Implementing an embedded layout-qualifier class here, since C++ can't have a real class bitfield + void clearLayout() // all layout + { + clearUniformLayout(); + + layoutPushConstant = false; +#ifdef NV_EXTENSIONS + layoutPassthrough = false; + layoutViewportRelative = false; + // -2048 as the default value indicating layoutSecondaryViewportRelative is not set + layoutSecondaryViewportRelativeOffset = -2048; +#endif + + clearInterstageLayout(); + + layoutSpecConstantId = layoutSpecConstantIdEnd; + + layoutFormat = ElfNone; + } + void clearInterstageLayout() + { + layoutLocation = layoutLocationEnd; + layoutComponent = layoutComponentEnd; + layoutIndex = layoutIndexEnd; + clearStreamLayout(); + clearXfbLayout(); + } + void clearStreamLayout() + { + layoutStream = layoutStreamEnd; + } + void clearXfbLayout() + { + layoutXfbBuffer = layoutXfbBufferEnd; + layoutXfbStride = layoutXfbStrideEnd; + layoutXfbOffset = layoutXfbOffsetEnd; + } + + bool hasNonXfbLayout() const + { + return hasUniformLayout() || + hasAnyLocation() || + hasStream() || + hasFormat() || + layoutPushConstant; + } + bool hasLayout() const + { + return hasNonXfbLayout() || + hasXfb(); + } + TLayoutMatrix layoutMatrix : 3; + TLayoutPacking layoutPacking : 4; + int layoutOffset; + int layoutAlign; + + unsigned int layoutLocation :12; + static const unsigned int layoutLocationEnd = 0xFFF; + + unsigned int layoutComponent : 3; + static const unsigned int layoutComponentEnd = 4; + + unsigned int layoutSet : 7; + static const unsigned int layoutSetEnd = 0x3F; + + unsigned int layoutBinding : 16; + static const unsigned int layoutBindingEnd = 0xFFFF; + + unsigned int layoutIndex : 8; + static const unsigned int layoutIndexEnd = 0xFF; + + unsigned int layoutStream : 8; + static const unsigned int layoutStreamEnd = 0xFF; + + unsigned int layoutXfbBuffer : 4; + static const unsigned int layoutXfbBufferEnd = 0xF; + + unsigned int layoutXfbStride : 10; + static const unsigned int layoutXfbStrideEnd = 0x3FF; + + unsigned int layoutXfbOffset : 10; + static const unsigned int layoutXfbOffsetEnd = 0x3FF; + + unsigned int layoutAttachment : 8; // for input_attachment_index + static const unsigned int layoutAttachmentEnd = 0XFF; + + unsigned int layoutSpecConstantId : 11; + static const unsigned int layoutSpecConstantIdEnd = 0x7FF; + + TLayoutFormat layoutFormat : 8; + + bool layoutPushConstant; + +#ifdef NV_EXTENSIONS + bool layoutPassthrough; + bool layoutViewportRelative; + int layoutSecondaryViewportRelativeOffset; +#endif + + bool hasUniformLayout() const + { + return hasMatrix() || + hasPacking() || + hasOffset() || + hasBinding() || + hasSet() || + hasAlign(); + } + void clearUniformLayout() // only uniform specific + { + layoutMatrix = ElmNone; + layoutPacking = ElpNone; + layoutOffset = layoutNotSet; + layoutAlign = layoutNotSet; + + layoutSet = layoutSetEnd; + layoutBinding = layoutBindingEnd; + layoutAttachment = layoutAttachmentEnd; + } + + bool hasMatrix() const + { + return layoutMatrix != ElmNone; + } + bool hasPacking() const + { + return layoutPacking != ElpNone; + } + bool hasOffset() const + { + return layoutOffset != layoutNotSet; + } + bool hasAlign() const + { + return layoutAlign != layoutNotSet; + } + bool hasAnyLocation() const + { + return hasLocation() || + hasComponent() || + hasIndex(); + } + bool hasLocation() const + { + return layoutLocation != layoutLocationEnd; + } + bool hasComponent() const + { + return layoutComponent != layoutComponentEnd; + } + bool hasIndex() const + { + return layoutIndex != layoutIndexEnd; + } + bool hasSet() const + { + return layoutSet != layoutSetEnd; + } + bool hasBinding() const + { + return layoutBinding != layoutBindingEnd; + } + bool hasStream() const + { + return layoutStream != layoutStreamEnd; + } + bool hasFormat() const + { + return layoutFormat != ElfNone; + } + bool hasXfb() const + { + return hasXfbBuffer() || + hasXfbStride() || + hasXfbOffset(); + } + bool hasXfbBuffer() const + { + return layoutXfbBuffer != layoutXfbBufferEnd; + } + bool hasXfbStride() const + { + return layoutXfbStride != layoutXfbStrideEnd; + } + bool hasXfbOffset() const + { + return layoutXfbOffset != layoutXfbOffsetEnd; + } + bool hasAttachment() const + { + return layoutAttachment != layoutAttachmentEnd; + } + bool hasSpecConstantId() const + { + // Not the same thing as being a specialization constant, this + // is just whether or not it was declared with an ID. + return layoutSpecConstantId != layoutSpecConstantIdEnd; + } + bool isSpecConstant() const + { + // True if type is a specialization constant, whether or not it + // had a specialization-constant ID, and false if it is not a + // true front-end constant. + return specConstant; + } + bool isFrontEndConstant() const + { + // True if the front-end knows the final constant value. + // This allows front-end constant folding. + return storage == EvqConst && ! specConstant; + } + bool isConstant() const + { + // True if is either kind of constant; specialization or regular. + return isFrontEndConstant() || isSpecConstant(); + } + void makeSpecConstant() + { + storage = EvqConst; + specConstant = true; + } + static const char* getLayoutPackingString(TLayoutPacking packing) + { + switch (packing) { + case ElpPacked: return "packed"; + case ElpShared: return "shared"; + case ElpStd140: return "std140"; + case ElpStd430: return "std430"; + default: return "none"; + } + } + static const char* getLayoutMatrixString(TLayoutMatrix m) + { + switch (m) { + case ElmColumnMajor: return "column_major"; + case ElmRowMajor: return "row_major"; + default: return "none"; + } + } + static const char* getLayoutFormatString(TLayoutFormat f) + { + switch (f) { + case ElfRgba32f: return "rgba32f"; + case ElfRgba16f: return "rgba16f"; + case ElfRg32f: return "rg32f"; + case ElfRg16f: return "rg16f"; + case ElfR11fG11fB10f: return "r11f_g11f_b10f"; + case ElfR32f: return "r32f"; + case ElfR16f: return "r16f"; + case ElfRgba16: return "rgba16"; + case ElfRgb10A2: return "rgb10_a2"; + case ElfRgba8: return "rgba8"; + case ElfRg16: return "rg16"; + case ElfRg8: return "rg8"; + case ElfR16: return "r16"; + case ElfR8: return "r8"; + case ElfRgba16Snorm: return "rgba16_snorm"; + case ElfRgba8Snorm: return "rgba8_snorm"; + case ElfRg16Snorm: return "rg16_snorm"; + case ElfRg8Snorm: return "rg8_snorm"; + case ElfR16Snorm: return "r16_snorm"; + case ElfR8Snorm: return "r8_snorm"; + + case ElfRgba32i: return "rgba32i"; + case ElfRgba16i: return "rgba16i"; + case ElfRgba8i: return "rgba8i"; + case ElfRg32i: return "rg32i"; + case ElfRg16i: return "rg16i"; + case ElfRg8i: return "rg8i"; + case ElfR32i: return "r32i"; + case ElfR16i: return "r16i"; + case ElfR8i: return "r8i"; + + case ElfRgba32ui: return "rgba32ui"; + case ElfRgba16ui: return "rgba16ui"; + case ElfRgba8ui: return "rgba8ui"; + case ElfRg32ui: return "rg32ui"; + case ElfRg16ui: return "rg16ui"; + case ElfRgb10a2ui: return "rgb10_a2ui"; + case ElfRg8ui: return "rg8ui"; + case ElfR32ui: return "r32ui"; + case ElfR16ui: return "r16ui"; + case ElfR8ui: return "r8ui"; + default: return "none"; + } + } + static const char* getLayoutDepthString(TLayoutDepth d) + { + switch (d) { + case EldAny: return "depth_any"; + case EldGreater: return "depth_greater"; + case EldLess: return "depth_less"; + case EldUnchanged: return "depth_unchanged"; + default: return "none"; + } + } + static const char* getBlendEquationString(TBlendEquationShift e) + { + switch (e) { + case EBlendMultiply: return "blend_support_multiply"; + case EBlendScreen: return "blend_support_screen"; + case EBlendOverlay: return "blend_support_overlay"; + case EBlendDarken: return "blend_support_darken"; + case EBlendLighten: return "blend_support_lighten"; + case EBlendColordodge: return "blend_support_colordodge"; + case EBlendColorburn: return "blend_support_colorburn"; + case EBlendHardlight: return "blend_support_hardlight"; + case EBlendSoftlight: return "blend_support_softlight"; + case EBlendDifference: return "blend_support_difference"; + case EBlendExclusion: return "blend_support_exclusion"; + case EBlendHslHue: return "blend_support_hsl_hue"; + case EBlendHslSaturation: return "blend_support_hsl_saturation"; + case EBlendHslColor: return "blend_support_hsl_color"; + case EBlendHslLuminosity: return "blend_support_hsl_luminosity"; + case EBlendAllEquations: return "blend_support_all_equations"; + default: return "unknown"; + } + } + static const char* getGeometryString(TLayoutGeometry geometry) + { + switch (geometry) { + case ElgPoints: return "points"; + case ElgLines: return "lines"; + case ElgLinesAdjacency: return "lines_adjacency"; + case ElgLineStrip: return "line_strip"; + case ElgTriangles: return "triangles"; + case ElgTrianglesAdjacency: return "triangles_adjacency"; + case ElgTriangleStrip: return "triangle_strip"; + case ElgQuads: return "quads"; + case ElgIsolines: return "isolines"; + default: return "none"; + } + } + static const char* getVertexSpacingString(TVertexSpacing spacing) + { + switch (spacing) { + case EvsEqual: return "equal_spacing"; + case EvsFractionalEven: return "fractional_even_spacing"; + case EvsFractionalOdd: return "fractional_odd_spacing"; + default: return "none"; + } + } + static const char* getVertexOrderString(TVertexOrder order) + { + switch (order) { + case EvoCw: return "cw"; + case EvoCcw: return "ccw"; + default: return "none"; + } + } + static int mapGeometryToSize(TLayoutGeometry geometry) + { + switch (geometry) { + case ElgPoints: return 1; + case ElgLines: return 2; + case ElgLinesAdjacency: return 4; + case ElgTriangles: return 3; + case ElgTrianglesAdjacency: return 6; + default: return 0; + } + } +}; + +// Qualifiers that don't need to be keep per object. They have shader scope, not object scope. +// So, they will not be part of TType, TQualifier, etc. +struct TShaderQualifiers { + TLayoutGeometry geometry; // geometry/tessellation shader in/out primitives + bool pixelCenterInteger; // fragment shader + bool originUpperLeft; // fragment shader + int invocations; + int vertices; // both for tessellation "vertices" and geometry "max_vertices" + TVertexSpacing spacing; + TVertexOrder order; + bool pointMode; + int localSize[3]; // compute shader + int localSizeSpecId[3]; // compute shader specialization id for gl_WorkGroupSize + bool earlyFragmentTests; // fragment input + bool postDepthCoverage; // fragment input + TLayoutDepth layoutDepth; + bool blendEquation; // true if any blend equation was specified + int numViews; // multiview extenstions + +#ifdef NV_EXTENSIONS + bool layoutOverrideCoverage; // true if layout override_coverage set +#endif + + void init() + { + geometry = ElgNone; + originUpperLeft = false; + pixelCenterInteger = false; + invocations = TQualifier::layoutNotSet; + vertices = TQualifier::layoutNotSet; + spacing = EvsNone; + order = EvoNone; + pointMode = false; + localSize[0] = 1; + localSize[1] = 1; + localSize[2] = 1; + localSizeSpecId[0] = TQualifier::layoutNotSet; + localSizeSpecId[1] = TQualifier::layoutNotSet; + localSizeSpecId[2] = TQualifier::layoutNotSet; + earlyFragmentTests = false; + postDepthCoverage = false; + layoutDepth = EldNone; + blendEquation = false; + numViews = TQualifier::layoutNotSet; +#ifdef NV_EXTENSIONS + layoutOverrideCoverage = false; +#endif + } + + // Merge in characteristics from the 'src' qualifier. They can override when + // set, but never erase when not set. + void merge(const TShaderQualifiers& src) + { + if (src.geometry != ElgNone) + geometry = src.geometry; + if (src.pixelCenterInteger) + pixelCenterInteger = src.pixelCenterInteger; + if (src.originUpperLeft) + originUpperLeft = src.originUpperLeft; + if (src.invocations != TQualifier::layoutNotSet) + invocations = src.invocations; + if (src.vertices != TQualifier::layoutNotSet) + vertices = src.vertices; + if (src.spacing != EvsNone) + spacing = src.spacing; + if (src.order != EvoNone) + order = src.order; + if (src.pointMode) + pointMode = true; + for (int i = 0; i < 3; ++i) { + if (src.localSize[i] > 1) + localSize[i] = src.localSize[i]; + } + for (int i = 0; i < 3; ++i) { + if (src.localSizeSpecId[i] != TQualifier::layoutNotSet) + localSizeSpecId[i] = src.localSizeSpecId[i]; + } + if (src.earlyFragmentTests) + earlyFragmentTests = true; + if (src.postDepthCoverage) + postDepthCoverage = true; + if (src.layoutDepth) + layoutDepth = src.layoutDepth; + if (src.blendEquation) + blendEquation = src.blendEquation; + if (src.numViews != TQualifier::layoutNotSet) + numViews = src.numViews; +#ifdef NV_EXTENSIONS + if (src.layoutOverrideCoverage) + layoutOverrideCoverage = src.layoutOverrideCoverage; +#endif + } +}; + +// +// TPublicType is just temporarily used while parsing and not quite the same +// information kept per node in TType. Due to the bison stack, it can't have +// types that it thinks have non-trivial constructors. It should +// just be used while recognizing the grammar, not anything else. +// Once enough is known about the situation, the proper information +// moved into a TType, or the parse context, etc. +// +class TPublicType { +public: + TBasicType basicType; + TSampler sampler; + TQualifier qualifier; + TShaderQualifiers shaderQualifiers; + int vectorSize : 4; + int matrixCols : 4; + int matrixRows : 4; + TArraySizes* arraySizes; + const TType* userDef; + TSourceLoc loc; + + void initType(const TSourceLoc& l) + { + basicType = EbtVoid; + vectorSize = 1; + matrixRows = 0; + matrixCols = 0; + arraySizes = nullptr; + userDef = nullptr; + loc = l; + } + + void initQualifiers(bool global = false) + { + qualifier.clear(); + if (global) + qualifier.storage = EvqGlobal; + } + + void init(const TSourceLoc& l, bool global = false) + { + initType(l); + sampler.clear(); + initQualifiers(global); + shaderQualifiers.init(); + } + + void setVector(int s) + { + matrixRows = 0; + matrixCols = 0; + vectorSize = s; + } + + void setMatrix(int c, int r) + { + matrixRows = r; + matrixCols = c; + vectorSize = 0; + } + + bool isScalar() const + { + return matrixCols == 0 && vectorSize == 1 && arraySizes == nullptr && userDef == nullptr; + } + + // "Image" is a superset of "Subpass" + bool isImage() const { return basicType == EbtSampler && sampler.isImage(); } + bool isSubpass() const { return basicType == EbtSampler && sampler.isSubpass(); } +}; + +// +// Base class for things that have a type. +// +class TType { +public: + POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) + + // for "empty" type (no args) or simple scalar/vector/matrix + explicit TType(TBasicType t = EbtVoid, TStorageQualifier q = EvqTemporary, int vs = 1, int mc = 0, int mr = 0, + bool isVector = false) : + basicType(t), vectorSize(vs), matrixCols(mc), matrixRows(mr), vector1(isVector && vs == 1), + arraySizes(nullptr), structure(nullptr), fieldName(nullptr), typeName(nullptr) + { + sampler.clear(); + qualifier.clear(); + qualifier.storage = q; + assert(!(isMatrix() && vectorSize != 0)); // prevent vectorSize != 0 on matrices + } + // for explicit precision qualifier + TType(TBasicType t, TStorageQualifier q, TPrecisionQualifier p, int vs = 1, int mc = 0, int mr = 0, + bool isVector = false) : + basicType(t), vectorSize(vs), matrixCols(mc), matrixRows(mr), vector1(isVector && vs == 1), + arraySizes(nullptr), structure(nullptr), fieldName(nullptr), typeName(nullptr) + { + sampler.clear(); + qualifier.clear(); + qualifier.storage = q; + qualifier.precision = p; + assert(p >= EpqNone && p <= EpqHigh); + assert(!(isMatrix() && vectorSize != 0)); // prevent vectorSize != 0 on matrices + } + // for turning a TPublicType into a TType, using a shallow copy + explicit TType(const TPublicType& p) : + basicType(p.basicType), + vectorSize(p.vectorSize), matrixCols(p.matrixCols), matrixRows(p.matrixRows), vector1(false), + arraySizes(p.arraySizes), structure(nullptr), fieldName(nullptr), typeName(nullptr) + { + if (basicType == EbtSampler) + sampler = p.sampler; + else + sampler.clear(); + qualifier = p.qualifier; + if (p.userDef) { + structure = p.userDef->getWritableStruct(); // public type is short-lived; there are no sharing issues + typeName = NewPoolTString(p.userDef->getTypeName().c_str()); + } + } + // for construction of sampler types + TType(const TSampler& sampler, TStorageQualifier q = EvqUniform, TArraySizes* as = nullptr) : + basicType(EbtSampler), vectorSize(1), matrixCols(0), matrixRows(0), vector1(false), + arraySizes(as), structure(nullptr), fieldName(nullptr), typeName(nullptr), + sampler(sampler) + { + qualifier.clear(); + qualifier.storage = q; + } + // to efficiently make a dereferenced type + // without ever duplicating the outer structure that will be thrown away + // and using only shallow copy + TType(const TType& type, int derefIndex, bool rowMajor = false) + { + if (type.isArray()) { + shallowCopy(type); + if (type.getArraySizes()->getNumDims() == 1) { + arraySizes = nullptr; + } else { + // want our own copy of the array, so we can edit it + arraySizes = new TArraySizes; + arraySizes->copyDereferenced(*type.arraySizes); + } + } else if (type.basicType == EbtStruct || type.basicType == EbtBlock) { + // do a structure dereference + const TTypeList& memberList = *type.getStruct(); + shallowCopy(*memberList[derefIndex].type); + return; + } else { + // do a vector/matrix dereference + shallowCopy(type); + if (matrixCols > 0) { + // dereference from matrix to vector + if (rowMajor) + vectorSize = matrixCols; + else + vectorSize = matrixRows; + matrixCols = 0; + matrixRows = 0; + if (vectorSize == 1) + vector1 = true; + } else if (isVector()) { + // dereference from vector to scalar + vectorSize = 1; + vector1 = false; + } + } + } + // for making structures, ... + TType(TTypeList* userDef, const TString& n) : + basicType(EbtStruct), vectorSize(1), matrixCols(0), matrixRows(0), vector1(false), + arraySizes(nullptr), structure(userDef), fieldName(nullptr) + { + sampler.clear(); + qualifier.clear(); + typeName = NewPoolTString(n.c_str()); + } + // For interface blocks + TType(TTypeList* userDef, const TString& n, const TQualifier& q) : + basicType(EbtBlock), vectorSize(1), matrixCols(0), matrixRows(0), vector1(false), + qualifier(q), arraySizes(nullptr), structure(userDef), fieldName(nullptr) + { + sampler.clear(); + typeName = NewPoolTString(n.c_str()); + } + virtual ~TType() {} + + // Not for use across pool pops; it will cause multiple instances of TType to point to the same information. + // This only works if that information (like a structure's list of types) does not change and + // the instances are sharing the same pool. + void shallowCopy(const TType& copyOf) + { + basicType = copyOf.basicType; + sampler = copyOf.sampler; + qualifier = copyOf.qualifier; + vectorSize = copyOf.vectorSize; + matrixCols = copyOf.matrixCols; + matrixRows = copyOf.matrixRows; + vector1 = copyOf.vector1; + arraySizes = copyOf.arraySizes; // copying the pointer only, not the contents + structure = copyOf.structure; + fieldName = copyOf.fieldName; + typeName = copyOf.typeName; + } + + // Make complete copy of the whole type graph rooted at 'copyOf'. + void deepCopy(const TType& copyOf) + { + TMap copied; // to enable copying a type graph as a graph, not a tree + deepCopy(copyOf, copied); + } + + // Recursively make temporary + void makeTemporary() + { + getQualifier().makeTemporary(); + + if (isStruct()) + for (unsigned int i = 0; i < structure->size(); ++i) + (*structure)[i].type->makeTemporary(); + } + + TType* clone() const + { + TType *newType = new TType(); + newType->deepCopy(*this); + + return newType; + } + + void makeVector() { vector1 = true; } + + // Merge type from parent, where a parentType is at the beginning of a declaration, + // establishing some characteristics for all subsequent names, while this type + // is on the individual names. + void mergeType(const TPublicType& parentType) + { + // arrayness is currently the only child aspect that has to be preserved + basicType = parentType.basicType; + vectorSize = parentType.vectorSize; + matrixCols = parentType.matrixCols; + matrixRows = parentType.matrixRows; + vector1 = false; // TPublicType is only GLSL which so far has no vec1 + qualifier = parentType.qualifier; + sampler = parentType.sampler; + if (parentType.arraySizes) + newArraySizes(*parentType.arraySizes); + if (parentType.userDef) { + structure = parentType.userDef->getWritableStruct(); + setTypeName(parentType.userDef->getTypeName()); + } + } + + virtual void hideMember() { basicType = EbtVoid; vectorSize = 1; } + virtual bool hiddenMember() const { return basicType == EbtVoid; } + + virtual void setTypeName(const TString& n) { typeName = NewPoolTString(n.c_str()); } + virtual void setFieldName(const TString& n) { fieldName = NewPoolTString(n.c_str()); } + virtual const TString& getTypeName() const + { + assert(typeName); + return *typeName; + } + + virtual const TString& getFieldName() const + { + assert(fieldName); + return *fieldName; + } + + virtual TBasicType getBasicType() const { return basicType; } + virtual const TSampler& getSampler() const { return sampler; } + virtual TSampler& getSampler() { return sampler; } + + virtual TQualifier& getQualifier() { return qualifier; } + virtual const TQualifier& getQualifier() const { return qualifier; } + + virtual int getVectorSize() const { return vectorSize; } // returns 1 for either scalar or vector of size 1, valid for both + virtual int getMatrixCols() const { return matrixCols; } + virtual int getMatrixRows() const { return matrixRows; } + virtual int getOuterArraySize() const { return arraySizes->getOuterSize(); } + virtual TIntermTyped* getOuterArrayNode() const { return arraySizes->getOuterNode(); } + virtual int getCumulativeArraySize() const { return arraySizes->getCumulativeSize(); } + virtual bool isArrayOfArrays() const { return arraySizes != nullptr && arraySizes->getNumDims() > 1; } + virtual int getImplicitArraySize() const { return arraySizes->getImplicitSize(); } + virtual const TArraySizes* getArraySizes() const { return arraySizes; } + virtual TArraySizes& getArraySizes() { assert(arraySizes != nullptr); return *arraySizes; } + + virtual bool isScalar() const { return ! isVector() && ! isMatrix() && ! isStruct() && ! isArray(); } + virtual bool isScalarOrVec1() const { return isScalar() || vector1; } + virtual bool isVector() const { return vectorSize > 1 || vector1; } + virtual bool isMatrix() const { return matrixCols ? true : false; } + virtual bool isArray() const { return arraySizes != nullptr; } + virtual bool isExplicitlySizedArray() const { return isArray() && getOuterArraySize() != UnsizedArraySize; } + virtual bool isImplicitlySizedArray() const { return isArray() && getOuterArraySize() == UnsizedArraySize && qualifier.storage != EvqBuffer; } + virtual bool isRuntimeSizedArray() const { return isArray() && getOuterArraySize() == UnsizedArraySize && qualifier.storage == EvqBuffer; } + virtual bool isStruct() const { return structure != nullptr; } +#ifdef AMD_EXTENSIONS + virtual bool isFloatingDomain() const { return basicType == EbtFloat || basicType == EbtDouble || basicType == EbtFloat16; } +#else + virtual bool isFloatingDomain() const { return basicType == EbtFloat || basicType == EbtDouble; } +#endif + virtual bool isIntegerDomain() const + { + switch (basicType) { + case EbtInt: + case EbtUint: + case EbtInt64: + case EbtUint64: +#ifdef AMD_EXTENSIONS + case EbtInt16: + case EbtUint16: +#endif + case EbtAtomicUint: + return true; + default: + break; + } + return false; + } + virtual bool isOpaque() const { return basicType == EbtSampler || basicType == EbtAtomicUint; } + virtual bool isBuiltIn() const { return getQualifier().builtIn != EbvNone; } + + // "Image" is a superset of "Subpass" + virtual bool isImage() const { return basicType == EbtSampler && getSampler().isImage(); } + virtual bool isSubpass() const { return basicType == EbtSampler && getSampler().isSubpass(); } + + // return true if this type contains any subtype which satisfies the given predicate. + template + bool contains(P predicate) const + { + if (predicate(this)) + return true; + + const auto hasa = [predicate](const TTypeLoc& tl) { return tl.type->contains(predicate); }; + + return structure && std::any_of(structure->begin(), structure->end(), hasa); + } + + // Recursively checks if the type contains the given basic type + virtual bool containsBasicType(TBasicType checkType) const + { + return contains([checkType](const TType* t) { return t->basicType == checkType; } ); + } + + // Recursively check the structure for any arrays, needed for some error checks + virtual bool containsArray() const + { + return contains([](const TType* t) { return t->isArray(); } ); + } + + // Check the structure for any structures, needed for some error checks + virtual bool containsStructure() const + { + return contains([this](const TType* t) { return t != this && t->isStruct(); } ); + } + + // Recursively check the structure for any implicitly-sized arrays, needed for triggering a copyUp(). + virtual bool containsImplicitlySizedArray() const + { + return contains([](const TType* t) { return t->isImplicitlySizedArray(); } ); + } + + virtual bool containsOpaque() const + { + return contains([](const TType* t) { return t->isOpaque(); } ); + } + + // Recursively checks if the type contains a built-in variable + virtual bool containsBuiltIn() const + { + return contains([](const TType* t) { return t->isBuiltIn(); } ); + } + + virtual bool containsNonOpaque() const + { + const auto nonOpaque = [](const TType* t) { + switch (t->basicType) { + case EbtVoid: + case EbtFloat: + case EbtDouble: +#ifdef AMD_EXTENSIONS + case EbtFloat16: +#endif + case EbtInt: + case EbtUint: + case EbtInt64: + case EbtUint64: +#ifdef AMD_EXTENSIONS + case EbtInt16: + case EbtUint16: +#endif + case EbtBool: + return true; + default: + return false; + } + }; + + return contains(nonOpaque); + } + + virtual bool containsSpecializationSize() const + { + return contains([](const TType* t) { return t->isArray() && t->arraySizes->isOuterSpecialization(); } ); + } + + // Array editing methods. Array descriptors can be shared across + // type instances. This allows all uses of the same array + // to be updated at once. E.g., all nodes can be explicitly sized + // by tracking and correcting one implicit size. Or, all nodes + // can get the explicit size on a redeclaration that gives size. + // + // N.B.: Don't share with the shared symbol tables (symbols are + // marked as isReadOnly(). Such symbols with arrays that will be + // edited need to copyUp() on first use, so that + // A) the edits don't effect the shared symbol table, and + // B) the edits are shared across all users. + void updateArraySizes(const TType& type) + { + // For when we may already be sharing existing array descriptors, + // keeping the pointers the same, just updating the contents. + assert(arraySizes != nullptr); + assert(type.arraySizes != nullptr); + *arraySizes = *type.arraySizes; + } + void newArraySizes(const TArraySizes& s) + { + // For setting a fresh new set of array sizes, not yet worrying about sharing. + arraySizes = new TArraySizes; + *arraySizes = s; + } + void clearArraySizes() + { + arraySizes = 0; + } + void addArrayOuterSizes(const TArraySizes& s) + { + if (arraySizes == nullptr) + newArraySizes(s); + else + arraySizes->addOuterSizes(s); + } + void changeOuterArraySize(int s) { arraySizes->changeOuterSize(s); } + void setImplicitArraySize(int s) { arraySizes->setImplicitSize(s); } + + // Recursively make the implicit array size the explicit array size, through the type tree. + void adoptImplicitArraySizes() + { + if (isImplicitlySizedArray()) + changeOuterArraySize(getImplicitArraySize()); + if (isStruct()) { + for (int i = 0; i < (int)structure->size(); ++i) + (*structure)[i].type->adoptImplicitArraySizes(); + } + } + + const char* getBasicString() const + { + return TType::getBasicString(basicType); + } + + static const char* getBasicString(TBasicType t) + { + switch (t) { + case EbtVoid: return "void"; + case EbtFloat: return "float"; + case EbtDouble: return "double"; +#ifdef AMD_EXTENSIONS + case EbtFloat16: return "float16_t"; +#endif + case EbtInt: return "int"; + case EbtUint: return "uint"; + case EbtInt64: return "int64_t"; + case EbtUint64: return "uint64_t"; +#ifdef AMD_EXTENSIONS + case EbtInt16: return "int16_t"; + case EbtUint16: return "uint16_t"; +#endif + case EbtBool: return "bool"; + case EbtAtomicUint: return "atomic_uint"; + case EbtSampler: return "sampler/image"; + case EbtStruct: return "structure"; + case EbtBlock: return "block"; + default: return "unknown type"; + } + } + + TString getCompleteString() const + { + TString typeString; + + const auto appendStr = [&](const char* s) { typeString.append(s); }; + const auto appendUint = [&](unsigned int u) { typeString.append(std::to_string(u).c_str()); }; + const auto appendInt = [&](int i) { typeString.append(std::to_string(i).c_str()); }; + + if (qualifier.hasLayout()) { + // To reduce noise, skip this if the only layout is an xfb_buffer + // with no triggering xfb_offset. + TQualifier noXfbBuffer = qualifier; + noXfbBuffer.layoutXfbBuffer = TQualifier::layoutXfbBufferEnd; + if (noXfbBuffer.hasLayout()) { + appendStr("layout("); + if (qualifier.hasAnyLocation()) { + appendStr(" location="); + appendUint(qualifier.layoutLocation); + if (qualifier.hasComponent()) { + appendStr(" component="); + appendUint(qualifier.layoutComponent); + } + if (qualifier.hasIndex()) { + appendStr(" index="); + appendUint(qualifier.layoutIndex); + } + } + if (qualifier.hasSet()) { + appendStr(" set="); + appendUint(qualifier.layoutSet); + } + if (qualifier.hasBinding()) { + appendStr(" binding="); + appendUint(qualifier.layoutBinding); + } + if (qualifier.hasStream()) { + appendStr(" stream="); + appendUint(qualifier.layoutStream); + } + if (qualifier.hasMatrix()) { + appendStr(" "); + appendStr(TQualifier::getLayoutMatrixString(qualifier.layoutMatrix)); + } + if (qualifier.hasPacking()) { + appendStr(" "); + appendStr(TQualifier::getLayoutPackingString(qualifier.layoutPacking)); + } + if (qualifier.hasOffset()) { + appendStr(" offset="); + appendInt(qualifier.layoutOffset); + } + if (qualifier.hasAlign()) { + appendStr(" align="); + appendInt(qualifier.layoutAlign); + } + if (qualifier.hasFormat()) { + appendStr(" "); + appendStr(TQualifier::getLayoutFormatString(qualifier.layoutFormat)); + } + if (qualifier.hasXfbBuffer() && qualifier.hasXfbOffset()) { + appendStr(" xfb_buffer="); + appendUint(qualifier.layoutXfbBuffer); + } + if (qualifier.hasXfbOffset()) { + appendStr(" xfb_offset="); + appendUint(qualifier.layoutXfbOffset); + } + if (qualifier.hasXfbStride()) { + appendStr(" xfb_stride="); + appendUint(qualifier.layoutXfbStride); + } + if (qualifier.hasAttachment()) { + appendStr(" input_attachment_index="); + appendUint(qualifier.layoutAttachment); + } + if (qualifier.hasSpecConstantId()) { + appendStr(" constant_id="); + appendUint(qualifier.layoutSpecConstantId); + } + if (qualifier.layoutPushConstant) + appendStr(" push_constant"); + +#ifdef NV_EXTENSIONS + if (qualifier.layoutPassthrough) + appendStr(" passthrough"); + if (qualifier.layoutViewportRelative) + appendStr(" layoutViewportRelative"); + if (qualifier.layoutSecondaryViewportRelativeOffset != -2048) { + appendStr(" layoutSecondaryViewportRelativeOffset="); + appendInt(qualifier.layoutSecondaryViewportRelativeOffset); + } +#endif + + appendStr(")"); + } + } + + if (qualifier.invariant) + appendStr(" invariant"); + if (qualifier.noContraction) + appendStr(" noContraction"); + if (qualifier.centroid) + appendStr(" centroid"); + if (qualifier.smooth) + appendStr(" smooth"); + if (qualifier.flat) + appendStr(" flat"); + if (qualifier.nopersp) + appendStr(" noperspective"); +#ifdef AMD_EXTENSIONS + if (qualifier.explicitInterp) + appendStr(" __explicitInterpAMD"); +#endif + if (qualifier.patch) + appendStr(" patch"); + if (qualifier.sample) + appendStr(" sample"); + if (qualifier.coherent) + appendStr(" coherent"); + if (qualifier.volatil) + appendStr(" volatile"); + if (qualifier.restrict) + appendStr(" restrict"); + if (qualifier.readonly) + appendStr(" readonly"); + if (qualifier.writeonly) + appendStr(" writeonly"); + if (qualifier.specConstant) + appendStr(" specialization-constant"); + appendStr(" "); + appendStr(getStorageQualifierString()); + if (isArray()) { + for(int i = 0; i < (int)arraySizes->getNumDims(); ++i) { + int size = arraySizes->getDimSize(i); + if (size == 0) + appendStr(" implicitly-sized array of"); + else { + appendStr(" "); + appendInt(arraySizes->getDimSize(i)); + appendStr("-element array of"); + } + } + } + if (qualifier.precision != EpqNone) { + appendStr(" "); + appendStr(getPrecisionQualifierString()); + } + if (isMatrix()) { + appendStr(" "); + appendInt(matrixCols); + appendStr("X"); + appendInt(matrixRows); + appendStr(" matrix of"); + } else if (isVector()) { + appendStr(" "); + appendInt(vectorSize); + appendStr("-component vector of"); + } + + appendStr(" "); + typeString.append(getBasicTypeString()); + + if (qualifier.builtIn != EbvNone) { + appendStr(" "); + appendStr(getBuiltInVariableString()); + } + + // Add struct/block members + if (structure) { + appendStr("{"); + for (size_t i = 0; i < structure->size(); ++i) { + if (! (*structure)[i].type->hiddenMember()) { + typeString.append((*structure)[i].type->getCompleteString()); + typeString.append(" "); + typeString.append((*structure)[i].type->getFieldName()); + if (i < structure->size() - 1) + appendStr(", "); + } + } + appendStr("}"); + } + + return typeString; + } + + TString getBasicTypeString() const + { + if (basicType == EbtSampler) + return sampler.getString(); + else + return getBasicString(); + } + + const char* getStorageQualifierString() const { return GetStorageQualifierString(qualifier.storage); } + const char* getBuiltInVariableString() const { return GetBuiltInVariableString(qualifier.builtIn); } + const char* getPrecisionQualifierString() const { return GetPrecisionQualifierString(qualifier.precision); } + const TTypeList* getStruct() const { return structure; } + void setStruct(TTypeList* s) { structure = s; } + TTypeList* getWritableStruct() const { return structure; } // This should only be used when known to not be sharing with other threads + + int computeNumComponents() const + { + int components = 0; + + if (getBasicType() == EbtStruct || getBasicType() == EbtBlock) { + for (TTypeList::const_iterator tl = getStruct()->begin(); tl != getStruct()->end(); tl++) + components += ((*tl).type)->computeNumComponents(); + } else if (matrixCols) + components = matrixCols * matrixRows; + else + components = vectorSize; + + if (arraySizes != nullptr) { + components *= arraySizes->getCumulativeSize(); + } + + return components; + } + + // append this type's mangled name to the passed in 'name' + void appendMangledName(TString& name) const + { + buildMangledName(name); + name += ';' ; + } + + // Do two structure types match? They could be declared independently, + // in different places, but still might satisfy the definition of matching. + // From the spec: + // + // "Structures must have the same name, sequence of type names, and + // type definitions, and member names to be considered the same type. + // This rule applies recursively for nested or embedded types." + // + bool sameStructType(const TType& right) const + { + // Most commonly, they are both nullptr, or the same pointer to the same actual structure + if (structure == right.structure) + return true; + + // Both being nullptr was caught above, now they both have to be structures of the same number of elements + if (structure == nullptr || right.structure == nullptr || + structure->size() != right.structure->size()) + return false; + + // Structure names have to match + if (*typeName != *right.typeName) + return false; + + // Compare the names and types of all the members, which have to match + for (unsigned int i = 0; i < structure->size(); ++i) { + if ((*structure)[i].type->getFieldName() != (*right.structure)[i].type->getFieldName()) + return false; + + if (*(*structure)[i].type != *(*right.structure)[i].type) + return false; + } + + return true; + } + + // See if two types match, in all aspects except arrayness + bool sameElementType(const TType& right) const + { + return basicType == right.basicType && sameElementShape(right); + } + + // See if two type's arrayness match + bool sameArrayness(const TType& right) const + { + return ((arraySizes == nullptr && right.arraySizes == nullptr) || + (arraySizes != nullptr && right.arraySizes != nullptr && *arraySizes == *right.arraySizes)); + } + + // See if two type's arrayness match in everything except their outer dimension + bool sameInnerArrayness(const TType& right) const + { + assert(arraySizes != nullptr && right.arraySizes != nullptr); + return arraySizes->sameInnerArrayness(*right.arraySizes); + } + + // See if two type's elements match in all ways except basic type + bool sameElementShape(const TType& right) const + { + return sampler == right.sampler && + vectorSize == right.vectorSize && + matrixCols == right.matrixCols && + matrixRows == right.matrixRows && + vector1 == right.vector1 && + sameStructType(right); + } + + // See if two types match in all ways (just the actual type, not qualification) + bool operator==(const TType& right) const + { + return sameElementType(right) && sameArrayness(right); + } + + bool operator!=(const TType& right) const + { + return ! operator==(right); + } + +protected: + // Require consumer to pick between deep copy and shallow copy. + TType(const TType& type); + TType& operator=(const TType& type); + + // Recursively copy a type graph, while preserving the graph-like + // quality. That is, don't make more than one copy of a structure that + // gets reused multiple times in the type graph. + void deepCopy(const TType& copyOf, TMap& copiedMap) + { + shallowCopy(copyOf); + + if (copyOf.arraySizes) { + arraySizes = new TArraySizes; + *arraySizes = *copyOf.arraySizes; + } + + if (copyOf.structure) { + auto prevCopy = copiedMap.find(copyOf.structure); + if (prevCopy != copiedMap.end()) + structure = prevCopy->second; + else { + structure = new TTypeList; + copiedMap[copyOf.structure] = structure; + for (unsigned int i = 0; i < copyOf.structure->size(); ++i) { + TTypeLoc typeLoc; + typeLoc.loc = (*copyOf.structure)[i].loc; + typeLoc.type = new TType(); + typeLoc.type->deepCopy(*(*copyOf.structure)[i].type, copiedMap); + structure->push_back(typeLoc); + } + } + } + + if (copyOf.fieldName) + fieldName = NewPoolTString(copyOf.fieldName->c_str()); + if (copyOf.typeName) + typeName = NewPoolTString(copyOf.typeName->c_str()); + } + + + void buildMangledName(TString&) const; + + TBasicType basicType : 8; + int vectorSize : 4; // 1 means either scalar or 1-component vector; see vector1 to disambiguate. + int matrixCols : 4; + int matrixRows : 4; + bool vector1 : 1; // Backward-compatible tracking of a 1-component vector distinguished from a scalar. + // GLSL 4.5 never has a 1-component vector; so this will always be false until such + // functionality is added. + // HLSL does have a 1-component vectors, so this will be true to disambiguate + // from a scalar. + TQualifier qualifier; + + TArraySizes* arraySizes; // nullptr unless an array; can be shared across types + TTypeList* structure; // nullptr unless this is a struct; can be shared across types + TString *fieldName; // for structure field names + TString *typeName; // for structure type name + TSampler sampler; +}; + +} // end namespace glslang + +#endif // _TYPES_INCLUDED_ diff --git a/third_party/glslang-spirv/Include/arrays.h b/third_party/glslang-spirv/Include/arrays.h new file mode 100644 index 000000000..1660a99f9 --- /dev/null +++ b/third_party/glslang-spirv/Include/arrays.h @@ -0,0 +1,329 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2013 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// Implement types for tracking GLSL arrays, arrays of arrays, etc. +// + +#ifndef _ARRAYS_INCLUDED +#define _ARRAYS_INCLUDED + +namespace glslang { + +// This is used to mean there is no size yet (unsized), it is waiting to get a size from somewhere else. +const int UnsizedArraySize = 0; + +class TIntermTyped; +extern bool SameSpecializationConstants(TIntermTyped*, TIntermTyped*); + +// Specialization constants need both a nominal size and a node that defines +// the specialization constant being used. Array types are the same when their +// size and specialization constant nodes are the same. +struct TArraySize { + unsigned int size; + TIntermTyped* node; // nullptr means no specialization constant node + bool operator==(const TArraySize& rhs) const + { + if (size != rhs.size) + return false; + if (node == nullptr || rhs.node == nullptr) + return node == rhs.node; + + return SameSpecializationConstants(node, rhs.node); + } +}; + +// +// TSmallArrayVector is used as the container for the set of sizes in TArraySizes. +// It has generic-container semantics, while TArraySizes has array-of-array semantics. +// That is, TSmallArrayVector should be more focused on mechanism and TArraySizes on policy. +// +struct TSmallArrayVector { + // + // TODO: memory: TSmallArrayVector is intended to be smaller. + // Almost all arrays could be handled by two sizes each fitting + // in 16 bits, needing a real vector only in the cases where there + // are more than 3 sizes or a size needing more than 16 bits. + // + POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) + + TSmallArrayVector() : sizes(nullptr) { } + virtual ~TSmallArrayVector() { dealloc(); } + + // For breaking into two non-shared copies, independently modifiable. + TSmallArrayVector& operator=(const TSmallArrayVector& from) + { + if (from.sizes == nullptr) + sizes = nullptr; + else { + alloc(); + *sizes = *from.sizes; + } + + return *this; + } + + int size() const + { + if (sizes == nullptr) + return 0; + return (int)sizes->size(); + } + + unsigned int frontSize() const + { + assert(sizes != nullptr && sizes->size() > 0); + return sizes->front().size; + } + + TIntermTyped* frontNode() const + { + assert(sizes != nullptr && sizes->size() > 0); + return sizes->front().node; + } + + void changeFront(unsigned int s) + { + assert(sizes != nullptr); + // this should only happen for implicitly sized arrays, not specialization constants + assert(sizes->front().node == nullptr); + sizes->front().size = s; + } + + void push_back(unsigned int e, TIntermTyped* n) + { + alloc(); + TArraySize pair = { e, n }; + sizes->push_back(pair); + } + + void push_front(const TSmallArrayVector& newDims) + { + alloc(); + sizes->insert(sizes->begin(), newDims.sizes->begin(), newDims.sizes->end()); + } + + void pop_front() + { + assert(sizes != nullptr && sizes->size() > 0); + if (sizes->size() == 1) + dealloc(); + else + sizes->erase(sizes->begin()); + } + + // 'this' should currently not be holding anything, and copyNonFront + // will make it hold a copy of all but the first element of rhs. + // (This would be useful for making a type that is dereferenced by + // one dimension.) + void copyNonFront(const TSmallArrayVector& rhs) + { + assert(sizes == nullptr); + if (rhs.size() > 1) { + alloc(); + sizes->insert(sizes->begin(), rhs.sizes->begin() + 1, rhs.sizes->end()); + } + } + + unsigned int getDimSize(int i) const + { + assert(sizes != nullptr && (int)sizes->size() > i); + return (*sizes)[i].size; + } + + void setDimSize(int i, unsigned int size) const + { + assert(sizes != nullptr && (int)sizes->size() > i); + assert((*sizes)[i].node == nullptr); + (*sizes)[i].size = size; + } + + TIntermTyped* getDimNode(int i) const + { + assert(sizes != nullptr && (int)sizes->size() > i); + return (*sizes)[i].node; + } + + bool operator==(const TSmallArrayVector& rhs) const + { + if (sizes == nullptr && rhs.sizes == nullptr) + return true; + if (sizes == nullptr || rhs.sizes == nullptr) + return false; + return *sizes == *rhs.sizes; + } + bool operator!=(const TSmallArrayVector& rhs) const { return ! operator==(rhs); } + +protected: + TSmallArrayVector(const TSmallArrayVector&); + + void alloc() + { + if (sizes == nullptr) + sizes = new TVector; + } + void dealloc() + { + delete sizes; + sizes = nullptr; + } + + TVector* sizes; // will either hold such a pointer, or in the future, hold the two array sizes +}; + +// +// Represent an array, or array of arrays, to arbitrary depth. This is not +// done through a hierarchy of types in a type tree, rather all contiguous arrayness +// in the type hierarchy is localized into this single cumulative object. +// +// The arrayness in TTtype is a pointer, so that it can be non-allocated and zero +// for the vast majority of types that are non-array types. +// +// Order Policy: these are all identical: +// - left to right order within a contiguous set of ...[..][..][..]... in the source language +// - index order 0, 1, 2, ... within the 'sizes' member below +// - outer-most to inner-most +// +struct TArraySizes { + POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) + + TArraySizes() : implicitArraySize(1) { } + + // For breaking into two non-shared copies, independently modifiable. + TArraySizes& operator=(const TArraySizes& from) + { + implicitArraySize = from.implicitArraySize; + sizes = from.sizes; + + return *this; + } + + // translate from array-of-array semantics to container semantics + int getNumDims() const { return sizes.size(); } + int getDimSize(int dim) const { return sizes.getDimSize(dim); } + TIntermTyped* getDimNode(int dim) const { return sizes.getDimNode(dim); } + void setDimSize(int dim, int size) { sizes.setDimSize(dim, size); } + int getOuterSize() const { return sizes.frontSize(); } + TIntermTyped* getOuterNode() const { return sizes.frontNode(); } + int getCumulativeSize() const + { + int size = 1; + for (int d = 0; d < sizes.size(); ++d) { + // this only makes sense in paths that have a known array size + assert(sizes.getDimSize(d) != UnsizedArraySize); + size *= sizes.getDimSize(d); + } + return size; + } + void addInnerSize() { addInnerSize((unsigned)UnsizedArraySize); } + void addInnerSize(int s) { addInnerSize((unsigned)s, nullptr); } + void addInnerSize(int s, TIntermTyped* n) { sizes.push_back((unsigned)s, n); } + void addInnerSize(TArraySize pair) { sizes.push_back(pair.size, pair.node); } + void changeOuterSize(int s) { sizes.changeFront((unsigned)s); } + int getImplicitSize() const { return (int)implicitArraySize; } + void setImplicitSize(int s) { implicitArraySize = s; } + bool isInnerImplicit() const + { + for (int d = 1; d < sizes.size(); ++d) { + if (sizes.getDimSize(d) == (unsigned)UnsizedArraySize) + return true; + } + + return false; + } + bool clearInnerImplicit() + { + for (int d = 1; d < sizes.size(); ++d) { + if (sizes.getDimSize(d) == (unsigned)UnsizedArraySize) + setDimSize(d, 1); + } + + return false; + } + bool isInnerSpecialization() const + { + for (int d = 1; d < sizes.size(); ++d) { + if (sizes.getDimNode(d) != nullptr) + return true; + } + + return false; + } + bool isOuterSpecialization() + { + return sizes.getDimNode(0) != nullptr; + } + + bool isImplicit() const { return getOuterSize() == UnsizedArraySize || isInnerImplicit(); } + void addOuterSizes(const TArraySizes& s) { sizes.push_front(s.sizes); } + void dereference() { sizes.pop_front(); } + void copyDereferenced(const TArraySizes& rhs) + { + assert(sizes.size() == 0); + if (rhs.sizes.size() > 1) + sizes.copyNonFront(rhs.sizes); + } + + bool sameInnerArrayness(const TArraySizes& rhs) const + { + if (sizes.size() != rhs.sizes.size()) + return false; + + for (int d = 1; d < sizes.size(); ++d) { + if (sizes.getDimSize(d) != rhs.sizes.getDimSize(d) || + sizes.getDimNode(d) != rhs.sizes.getDimNode(d)) + return false; + } + + return true; + } + + bool operator==(const TArraySizes& rhs) { return sizes == rhs.sizes; } + bool operator!=(const TArraySizes& rhs) { return sizes != rhs.sizes; } + +protected: + TSmallArrayVector sizes; + + TArraySizes(const TArraySizes&); + + // for tracking maximum referenced index, before an explicit size is given + // applies only to the outer-most dimension + int implicitArraySize; +}; + +} // end namespace glslang + +#endif // _ARRAYS_INCLUDED_ diff --git a/third_party/glslang-spirv/Include/intermediate.h b/third_party/glslang-spirv/Include/intermediate.h new file mode 100644 index 000000000..51ac45c37 --- /dev/null +++ b/third_party/glslang-spirv/Include/intermediate.h @@ -0,0 +1,1486 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2016 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// Definition of the in-memory high-level intermediate representation +// of shaders. This is a tree that parser creates. +// +// Nodes in the tree are defined as a hierarchy of classes derived from +// TIntermNode. Each is a node in a tree. There is no preset branching factor; +// each node can have it's own type of list of children. +// + +#ifndef __INTERMEDIATE_H +#define __INTERMEDIATE_H + +#if _MSC_VER >= 1900 + #pragma warning(disable : 4464) // relative include path contains '..' + #pragma warning(disable : 5026) // 'glslang::TIntermUnary': move constructor was implicitly defined as deleted +#endif + +#include "../Include/Common.h" +#include "../Include/Types.h" +#include "../Include/ConstantUnion.h" + +namespace glslang { + +class TIntermediate; + +// +// Operators used by the high-level (parse tree) representation. +// +enum TOperator { + EOpNull, // if in a node, should only mean a node is still being built + EOpSequence, // denotes a list of statements, or parameters, etc. + EOpLinkerObjects, // for aggregate node of objects the linker may need, if not reference by the rest of the AST + EOpFunctionCall, + EOpFunction, // For function definition + EOpParameters, // an aggregate listing the parameters to a function + + // + // Unary operators + // + + EOpNegative, + EOpLogicalNot, + EOpVectorLogicalNot, + EOpBitwiseNot, + + EOpPostIncrement, + EOpPostDecrement, + EOpPreIncrement, + EOpPreDecrement, + + EOpConvIntToBool, + EOpConvUintToBool, + EOpConvFloatToBool, + EOpConvDoubleToBool, + EOpConvInt64ToBool, + EOpConvUint64ToBool, + EOpConvBoolToFloat, + EOpConvIntToFloat, + EOpConvUintToFloat, + EOpConvDoubleToFloat, + EOpConvInt64ToFloat, + EOpConvUint64ToFloat, + EOpConvUintToInt, + EOpConvFloatToInt, + EOpConvBoolToInt, + EOpConvDoubleToInt, + EOpConvInt64ToInt, + EOpConvUint64ToInt, + EOpConvIntToUint, + EOpConvFloatToUint, + EOpConvBoolToUint, + EOpConvDoubleToUint, + EOpConvInt64ToUint, + EOpConvUint64ToUint, + EOpConvIntToDouble, + EOpConvUintToDouble, + EOpConvFloatToDouble, + EOpConvBoolToDouble, + EOpConvInt64ToDouble, + EOpConvUint64ToDouble, + EOpConvBoolToInt64, + EOpConvIntToInt64, + EOpConvUintToInt64, + EOpConvFloatToInt64, + EOpConvDoubleToInt64, + EOpConvUint64ToInt64, + EOpConvBoolToUint64, + EOpConvIntToUint64, + EOpConvUintToUint64, + EOpConvFloatToUint64, + EOpConvDoubleToUint64, + EOpConvInt64ToUint64, +#ifdef AMD_EXTENSIONS + EOpConvBoolToFloat16, + EOpConvIntToFloat16, + EOpConvUintToFloat16, + EOpConvFloatToFloat16, + EOpConvDoubleToFloat16, + EOpConvInt64ToFloat16, + EOpConvUint64ToFloat16, + EOpConvFloat16ToBool, + EOpConvFloat16ToInt, + EOpConvFloat16ToUint, + EOpConvFloat16ToFloat, + EOpConvFloat16ToDouble, + EOpConvFloat16ToInt64, + EOpConvFloat16ToUint64, + + EOpConvBoolToInt16, + EOpConvIntToInt16, + EOpConvUintToInt16, + EOpConvFloatToInt16, + EOpConvDoubleToInt16, + EOpConvFloat16ToInt16, + EOpConvInt64ToInt16, + EOpConvUint64ToInt16, + EOpConvUint16ToInt16, + EOpConvInt16ToBool, + EOpConvInt16ToInt, + EOpConvInt16ToUint, + EOpConvInt16ToFloat, + EOpConvInt16ToDouble, + EOpConvInt16ToFloat16, + EOpConvInt16ToInt64, + EOpConvInt16ToUint64, + + EOpConvBoolToUint16, + EOpConvIntToUint16, + EOpConvUintToUint16, + EOpConvFloatToUint16, + EOpConvDoubleToUint16, + EOpConvFloat16ToUint16, + EOpConvInt64ToUint16, + EOpConvUint64ToUint16, + EOpConvInt16ToUint16, + EOpConvUint16ToBool, + EOpConvUint16ToInt, + EOpConvUint16ToUint, + EOpConvUint16ToFloat, + EOpConvUint16ToDouble, + EOpConvUint16ToFloat16, + EOpConvUint16ToInt64, + EOpConvUint16ToUint64, +#endif + + // + // binary operations + // + + EOpAdd, + EOpSub, + EOpMul, + EOpDiv, + EOpMod, + EOpRightShift, + EOpLeftShift, + EOpAnd, + EOpInclusiveOr, + EOpExclusiveOr, + EOpEqual, + EOpNotEqual, + EOpVectorEqual, + EOpVectorNotEqual, + EOpLessThan, + EOpGreaterThan, + EOpLessThanEqual, + EOpGreaterThanEqual, + EOpComma, + + EOpVectorTimesScalar, + EOpVectorTimesMatrix, + EOpMatrixTimesVector, + EOpMatrixTimesScalar, + + EOpLogicalOr, + EOpLogicalXor, + EOpLogicalAnd, + + EOpIndexDirect, + EOpIndexIndirect, + EOpIndexDirectStruct, + + EOpVectorSwizzle, + + EOpMethod, + EOpScoping, + + // + // Built-in functions mapped to operators + // + + EOpRadians, + EOpDegrees, + EOpSin, + EOpCos, + EOpTan, + EOpAsin, + EOpAcos, + EOpAtan, + EOpSinh, + EOpCosh, + EOpTanh, + EOpAsinh, + EOpAcosh, + EOpAtanh, + + EOpPow, + EOpExp, + EOpLog, + EOpExp2, + EOpLog2, + EOpSqrt, + EOpInverseSqrt, + + EOpAbs, + EOpSign, + EOpFloor, + EOpTrunc, + EOpRound, + EOpRoundEven, + EOpCeil, + EOpFract, + EOpModf, + EOpMin, + EOpMax, + EOpClamp, + EOpMix, + EOpStep, + EOpSmoothStep, + + EOpIsNan, + EOpIsInf, + + EOpFma, + + EOpFrexp, + EOpLdexp, + + EOpFloatBitsToInt, + EOpFloatBitsToUint, + EOpIntBitsToFloat, + EOpUintBitsToFloat, + EOpDoubleBitsToInt64, + EOpDoubleBitsToUint64, + EOpInt64BitsToDouble, + EOpUint64BitsToDouble, +#ifdef AMD_EXTENSIONS + EOpFloat16BitsToInt16, + EOpFloat16BitsToUint16, + EOpInt16BitsToFloat16, + EOpUint16BitsToFloat16, +#endif + EOpPackSnorm2x16, + EOpUnpackSnorm2x16, + EOpPackUnorm2x16, + EOpUnpackUnorm2x16, + EOpPackSnorm4x8, + EOpUnpackSnorm4x8, + EOpPackUnorm4x8, + EOpUnpackUnorm4x8, + EOpPackHalf2x16, + EOpUnpackHalf2x16, + EOpPackDouble2x32, + EOpUnpackDouble2x32, + EOpPackInt2x32, + EOpUnpackInt2x32, + EOpPackUint2x32, + EOpUnpackUint2x32, +#ifdef AMD_EXTENSIONS + EOpPackFloat2x16, + EOpUnpackFloat2x16, + EOpPackInt2x16, + EOpUnpackInt2x16, + EOpPackUint2x16, + EOpUnpackUint2x16, + EOpPackInt4x16, + EOpUnpackInt4x16, + EOpPackUint4x16, + EOpUnpackUint4x16, +#endif + + EOpLength, + EOpDistance, + EOpDot, + EOpCross, + EOpNormalize, + EOpFaceForward, + EOpReflect, + EOpRefract, + +#ifdef AMD_EXTENSIONS + EOpMin3, + EOpMax3, + EOpMid3, +#endif + + EOpDPdx, // Fragment only + EOpDPdy, // Fragment only + EOpFwidth, // Fragment only + EOpDPdxFine, // Fragment only + EOpDPdyFine, // Fragment only + EOpFwidthFine, // Fragment only + EOpDPdxCoarse, // Fragment only + EOpDPdyCoarse, // Fragment only + EOpFwidthCoarse, // Fragment only + + EOpInterpolateAtCentroid, // Fragment only + EOpInterpolateAtSample, // Fragment only + EOpInterpolateAtOffset, // Fragment only + +#ifdef AMD_EXTENSIONS + EOpInterpolateAtVertex, +#endif + + EOpMatrixTimesMatrix, + EOpOuterProduct, + EOpDeterminant, + EOpMatrixInverse, + EOpTranspose, + + EOpFtransform, + + EOpNoise, + + EOpEmitVertex, // geometry only + EOpEndPrimitive, // geometry only + EOpEmitStreamVertex, // geometry only + EOpEndStreamPrimitive, // geometry only + + EOpBarrier, + EOpMemoryBarrier, + EOpMemoryBarrierAtomicCounter, + EOpMemoryBarrierBuffer, + EOpMemoryBarrierImage, + EOpMemoryBarrierShared, // compute only + EOpGroupMemoryBarrier, // compute only + + EOpBallot, + EOpReadInvocation, + EOpReadFirstInvocation, + + EOpAnyInvocation, + EOpAllInvocations, + EOpAllInvocationsEqual, + +#ifdef AMD_EXTENSIONS + EOpMinInvocations, + EOpMaxInvocations, + EOpAddInvocations, + EOpMinInvocationsNonUniform, + EOpMaxInvocationsNonUniform, + EOpAddInvocationsNonUniform, + EOpMinInvocationsInclusiveScan, + EOpMaxInvocationsInclusiveScan, + EOpAddInvocationsInclusiveScan, + EOpMinInvocationsInclusiveScanNonUniform, + EOpMaxInvocationsInclusiveScanNonUniform, + EOpAddInvocationsInclusiveScanNonUniform, + EOpMinInvocationsExclusiveScan, + EOpMaxInvocationsExclusiveScan, + EOpAddInvocationsExclusiveScan, + EOpMinInvocationsExclusiveScanNonUniform, + EOpMaxInvocationsExclusiveScanNonUniform, + EOpAddInvocationsExclusiveScanNonUniform, + EOpSwizzleInvocations, + EOpSwizzleInvocationsMasked, + EOpWriteInvocation, + EOpMbcnt, + + EOpCubeFaceIndex, + EOpCubeFaceCoord, + EOpTime, +#endif + + EOpAtomicAdd, + EOpAtomicMin, + EOpAtomicMax, + EOpAtomicAnd, + EOpAtomicOr, + EOpAtomicXor, + EOpAtomicExchange, + EOpAtomicCompSwap, + + EOpAtomicCounterIncrement, // results in pre-increment value + EOpAtomicCounterDecrement, // results in post-decrement value + EOpAtomicCounter, + EOpAtomicCounterAdd, + EOpAtomicCounterSubtract, + EOpAtomicCounterMin, + EOpAtomicCounterMax, + EOpAtomicCounterAnd, + EOpAtomicCounterOr, + EOpAtomicCounterXor, + EOpAtomicCounterExchange, + EOpAtomicCounterCompSwap, + + EOpAny, + EOpAll, + + // + // Branch + // + + EOpKill, // Fragment only + EOpReturn, + EOpBreak, + EOpContinue, + EOpCase, + EOpDefault, + + // + // Constructors + // + + EOpConstructGuardStart, + EOpConstructInt, // these first scalar forms also identify what implicit conversion is needed + EOpConstructUint, + EOpConstructInt64, + EOpConstructUint64, +#ifdef AMD_EXTENSIONS + EOpConstructInt16, + EOpConstructUint16, +#endif + EOpConstructBool, + EOpConstructFloat, + EOpConstructDouble, +#ifdef AMD_EXTENSIONS + EOpConstructFloat16, +#endif + EOpConstructVec2, + EOpConstructVec3, + EOpConstructVec4, + EOpConstructDVec2, + EOpConstructDVec3, + EOpConstructDVec4, +#ifdef AMD_EXTENSIONS + EOpConstructF16Vec2, + EOpConstructF16Vec3, + EOpConstructF16Vec4, +#endif + EOpConstructBVec2, + EOpConstructBVec3, + EOpConstructBVec4, + EOpConstructIVec2, + EOpConstructIVec3, + EOpConstructIVec4, + EOpConstructUVec2, + EOpConstructUVec3, + EOpConstructUVec4, + EOpConstructI64Vec2, + EOpConstructI64Vec3, + EOpConstructI64Vec4, + EOpConstructU64Vec2, + EOpConstructU64Vec3, + EOpConstructU64Vec4, +#ifdef AMD_EXTENSIONS + EOpConstructI16Vec2, + EOpConstructI16Vec3, + EOpConstructI16Vec4, + EOpConstructU16Vec2, + EOpConstructU16Vec3, + EOpConstructU16Vec4, +#endif + EOpConstructMat2x2, + EOpConstructMat2x3, + EOpConstructMat2x4, + EOpConstructMat3x2, + EOpConstructMat3x3, + EOpConstructMat3x4, + EOpConstructMat4x2, + EOpConstructMat4x3, + EOpConstructMat4x4, + EOpConstructDMat2x2, + EOpConstructDMat2x3, + EOpConstructDMat2x4, + EOpConstructDMat3x2, + EOpConstructDMat3x3, + EOpConstructDMat3x4, + EOpConstructDMat4x2, + EOpConstructDMat4x3, + EOpConstructDMat4x4, + EOpConstructIMat2x2, + EOpConstructIMat2x3, + EOpConstructIMat2x4, + EOpConstructIMat3x2, + EOpConstructIMat3x3, + EOpConstructIMat3x4, + EOpConstructIMat4x2, + EOpConstructIMat4x3, + EOpConstructIMat4x4, + EOpConstructUMat2x2, + EOpConstructUMat2x3, + EOpConstructUMat2x4, + EOpConstructUMat3x2, + EOpConstructUMat3x3, + EOpConstructUMat3x4, + EOpConstructUMat4x2, + EOpConstructUMat4x3, + EOpConstructUMat4x4, + EOpConstructBMat2x2, + EOpConstructBMat2x3, + EOpConstructBMat2x4, + EOpConstructBMat3x2, + EOpConstructBMat3x3, + EOpConstructBMat3x4, + EOpConstructBMat4x2, + EOpConstructBMat4x3, + EOpConstructBMat4x4, +#ifdef AMD_EXTENSIONS + EOpConstructF16Mat2x2, + EOpConstructF16Mat2x3, + EOpConstructF16Mat2x4, + EOpConstructF16Mat3x2, + EOpConstructF16Mat3x3, + EOpConstructF16Mat3x4, + EOpConstructF16Mat4x2, + EOpConstructF16Mat4x3, + EOpConstructF16Mat4x4, +#endif + EOpConstructStruct, + EOpConstructTextureSampler, + EOpConstructGuardEnd, + + // + // moves + // + + EOpAssign, + EOpAddAssign, + EOpSubAssign, + EOpMulAssign, + EOpVectorTimesMatrixAssign, + EOpVectorTimesScalarAssign, + EOpMatrixTimesScalarAssign, + EOpMatrixTimesMatrixAssign, + EOpDivAssign, + EOpModAssign, + EOpAndAssign, + EOpInclusiveOrAssign, + EOpExclusiveOrAssign, + EOpLeftShiftAssign, + EOpRightShiftAssign, + + // + // Array operators + // + + EOpArrayLength, // "Array" distinguishes from length(v) built-in function, but it applies to vectors and matrices as well. + + // + // Image operations + // + + EOpImageGuardBegin, + + EOpImageQuerySize, + EOpImageQuerySamples, + EOpImageLoad, + EOpImageStore, +#ifdef AMD_EXTENSIONS + EOpImageLoadLod, + EOpImageStoreLod, +#endif + EOpImageAtomicAdd, + EOpImageAtomicMin, + EOpImageAtomicMax, + EOpImageAtomicAnd, + EOpImageAtomicOr, + EOpImageAtomicXor, + EOpImageAtomicExchange, + EOpImageAtomicCompSwap, + + EOpSubpassLoad, + EOpSubpassLoadMS, + EOpSparseImageLoad, +#ifdef AMD_EXTENSIONS + EOpSparseImageLoadLod, +#endif + + EOpImageGuardEnd, + + // + // Texture operations + // + + EOpTextureGuardBegin, + + EOpTextureQuerySize, + EOpTextureQueryLod, + EOpTextureQueryLevels, + EOpTextureQuerySamples, + + EOpSamplingGuardBegin, + + EOpTexture, + EOpTextureProj, + EOpTextureLod, + EOpTextureOffset, + EOpTextureFetch, + EOpTextureFetchOffset, + EOpTextureProjOffset, + EOpTextureLodOffset, + EOpTextureProjLod, + EOpTextureProjLodOffset, + EOpTextureGrad, + EOpTextureGradOffset, + EOpTextureProjGrad, + EOpTextureProjGradOffset, + EOpTextureGather, + EOpTextureGatherOffset, + EOpTextureGatherOffsets, + EOpTextureClamp, + EOpTextureOffsetClamp, + EOpTextureGradClamp, + EOpTextureGradOffsetClamp, +#ifdef AMD_EXTENSIONS + EOpTextureGatherLod, + EOpTextureGatherLodOffset, + EOpTextureGatherLodOffsets, + EOpFragmentMaskFetch, + EOpFragmentFetch, +#endif + + EOpSparseTextureGuardBegin, + + EOpSparseTexture, + EOpSparseTextureLod, + EOpSparseTextureOffset, + EOpSparseTextureFetch, + EOpSparseTextureFetchOffset, + EOpSparseTextureLodOffset, + EOpSparseTextureGrad, + EOpSparseTextureGradOffset, + EOpSparseTextureGather, + EOpSparseTextureGatherOffset, + EOpSparseTextureGatherOffsets, + EOpSparseTexelsResident, + EOpSparseTextureClamp, + EOpSparseTextureOffsetClamp, + EOpSparseTextureGradClamp, + EOpSparseTextureGradOffsetClamp, +#ifdef AMD_EXTENSIONS + EOpSparseTextureGatherLod, + EOpSparseTextureGatherLodOffset, + EOpSparseTextureGatherLodOffsets, +#endif + + EOpSparseTextureGuardEnd, + EOpSamplingGuardEnd, + EOpTextureGuardEnd, + + // + // Integer operations + // + + EOpAddCarry, + EOpSubBorrow, + EOpUMulExtended, + EOpIMulExtended, + EOpBitfieldExtract, + EOpBitfieldInsert, + EOpBitFieldReverse, + EOpBitCount, + EOpFindLSB, + EOpFindMSB, + + // + // HLSL operations + // + + EOpClip, // discard if input value < 0 + EOpIsFinite, + EOpLog10, // base 10 log + EOpRcp, // 1/x + EOpSaturate, // clamp from 0 to 1 + EOpSinCos, // sin and cos in out parameters + EOpGenMul, // mul(x,y) on any of mat/vec/scalars + EOpDst, // x = 1, y=src0.y * src1.y, z=src0.z, w=src1.w + EOpInterlockedAdd, // atomic ops, but uses [optional] out arg instead of return + EOpInterlockedAnd, // ... + EOpInterlockedCompareExchange, // ... + EOpInterlockedCompareStore, // ... + EOpInterlockedExchange, // ... + EOpInterlockedMax, // ... + EOpInterlockedMin, // ... + EOpInterlockedOr, // ... + EOpInterlockedXor, // ... + EOpAllMemoryBarrierWithGroupSync, // memory barriers without non-hlsl AST equivalents + EOpDeviceMemoryBarrier, // ... + EOpDeviceMemoryBarrierWithGroupSync, // ... + EOpWorkgroupMemoryBarrier, // ... + EOpWorkgroupMemoryBarrierWithGroupSync, // ... + EOpEvaluateAttributeSnapped, // InterpolateAtOffset with int position on 16x16 grid + EOpF32tof16, // HLSL conversion: half of a PackHalf2x16 + EOpF16tof32, // HLSL conversion: half of an UnpackHalf2x16 + EOpLit, // HLSL lighting coefficient vector + EOpTextureBias, // HLSL texture bias: will be lowered to EOpTexture + EOpAsDouble, // slightly different from EOpUint64BitsToDouble + EOpD3DCOLORtoUBYTE4, // convert and swizzle 4-component color to UBYTE4 range + + EOpMethodSample, // Texture object methods. These are translated to existing + EOpMethodSampleBias, // AST methods, and exist to represent HLSL semantics until that + EOpMethodSampleCmp, // translation is performed. See HlslParseContext::decomposeSampleMethods(). + EOpMethodSampleCmpLevelZero, // ... + EOpMethodSampleGrad, // ... + EOpMethodSampleLevel, // ... + EOpMethodLoad, // ... + EOpMethodGetDimensions, // ... + EOpMethodGetSamplePosition, // ... + EOpMethodGather, // ... + EOpMethodCalculateLevelOfDetail, // ... + EOpMethodCalculateLevelOfDetailUnclamped, // ... + + // Load already defined above for textures + EOpMethodLoad2, // Structure buffer object methods. These are translated to existing + EOpMethodLoad3, // AST methods, and exist to represent HLSL semantics until that + EOpMethodLoad4, // translation is performed. See HlslParseContext::decomposeSampleMethods(). + EOpMethodStore, // ... + EOpMethodStore2, // ... + EOpMethodStore3, // ... + EOpMethodStore4, // ... + EOpMethodIncrementCounter, // ... + EOpMethodDecrementCounter, // ... + // EOpMethodAppend is defined for geo shaders below + EOpMethodConsume, + + // SM5 texture methods + EOpMethodGatherRed, // These are covered under the above EOpMethodSample comment about + EOpMethodGatherGreen, // translation to existing AST opcodes. They exist temporarily + EOpMethodGatherBlue, // because HLSL arguments are slightly different. + EOpMethodGatherAlpha, // ... + EOpMethodGatherCmp, // ... + EOpMethodGatherCmpRed, // ... + EOpMethodGatherCmpGreen, // ... + EOpMethodGatherCmpBlue, // ... + EOpMethodGatherCmpAlpha, // ... + + // geometry methods + EOpMethodAppend, // Geometry shader methods + EOpMethodRestartStrip, // ... + + // matrix + EOpMatrixSwizzle, // select multiple matrix components (non-column) +}; + +class TIntermTraverser; +class TIntermOperator; +class TIntermAggregate; +class TIntermUnary; +class TIntermBinary; +class TIntermConstantUnion; +class TIntermSelection; +class TIntermSwitch; +class TIntermBranch; +class TIntermTyped; +class TIntermMethod; +class TIntermSymbol; +class TIntermLoop; + +} // end namespace glslang + +// +// Base class for the tree nodes +// +// (Put outside the glslang namespace, as it's used as part of the external interface.) +// +class TIntermNode { +public: + POOL_ALLOCATOR_NEW_DELETE(glslang::GetThreadPoolAllocator()) + + TIntermNode() { loc.init(); } + virtual const glslang::TSourceLoc& getLoc() const { return loc; } + virtual void setLoc(const glslang::TSourceLoc& l) { loc = l; } + virtual void traverse(glslang::TIntermTraverser*) = 0; + virtual glslang::TIntermTyped* getAsTyped() { return 0; } + virtual glslang::TIntermOperator* getAsOperator() { return 0; } + virtual glslang::TIntermConstantUnion* getAsConstantUnion() { return 0; } + virtual glslang::TIntermAggregate* getAsAggregate() { return 0; } + virtual glslang::TIntermUnary* getAsUnaryNode() { return 0; } + virtual glslang::TIntermBinary* getAsBinaryNode() { return 0; } + virtual glslang::TIntermSelection* getAsSelectionNode() { return 0; } + virtual glslang::TIntermSwitch* getAsSwitchNode() { return 0; } + virtual glslang::TIntermMethod* getAsMethodNode() { return 0; } + virtual glslang::TIntermSymbol* getAsSymbolNode() { return 0; } + virtual glslang::TIntermBranch* getAsBranchNode() { return 0; } + virtual glslang::TIntermLoop* getAsLoopNode() { return 0; } + + virtual const glslang::TIntermTyped* getAsTyped() const { return 0; } + virtual const glslang::TIntermOperator* getAsOperator() const { return 0; } + virtual const glslang::TIntermConstantUnion* getAsConstantUnion() const { return 0; } + virtual const glslang::TIntermAggregate* getAsAggregate() const { return 0; } + virtual const glslang::TIntermUnary* getAsUnaryNode() const { return 0; } + virtual const glslang::TIntermBinary* getAsBinaryNode() const { return 0; } + virtual const glslang::TIntermSelection* getAsSelectionNode() const { return 0; } + virtual const glslang::TIntermSwitch* getAsSwitchNode() const { return 0; } + virtual const glslang::TIntermMethod* getAsMethodNode() const { return 0; } + virtual const glslang::TIntermSymbol* getAsSymbolNode() const { return 0; } + virtual const glslang::TIntermBranch* getAsBranchNode() const { return 0; } + virtual const glslang::TIntermLoop* getAsLoopNode() const { return 0; } + virtual ~TIntermNode() { } + +protected: + TIntermNode(const TIntermNode&); + TIntermNode& operator=(const TIntermNode&); + glslang::TSourceLoc loc; +}; + +namespace glslang { + +// +// This is just to help yacc. +// +struct TIntermNodePair { + TIntermNode* node1; + TIntermNode* node2; +}; + +// +// Intermediate class for nodes that have a type. +// +class TIntermTyped : public TIntermNode { +public: + TIntermTyped(const TType& t) { type.shallowCopy(t); } + TIntermTyped(TBasicType basicType) { TType bt(basicType); type.shallowCopy(bt); } + virtual TIntermTyped* getAsTyped() { return this; } + virtual const TIntermTyped* getAsTyped() const { return this; } + virtual void setType(const TType& t) { type.shallowCopy(t); } + virtual const TType& getType() const { return type; } + virtual TType& getWritableType() { return type; } + + virtual TBasicType getBasicType() const { return type.getBasicType(); } + virtual TQualifier& getQualifier() { return type.getQualifier(); } + virtual const TQualifier& getQualifier() const { return type.getQualifier(); } + virtual void propagatePrecision(TPrecisionQualifier); + virtual int getVectorSize() const { return type.getVectorSize(); } + virtual int getMatrixCols() const { return type.getMatrixCols(); } + virtual int getMatrixRows() const { return type.getMatrixRows(); } + virtual bool isMatrix() const { return type.isMatrix(); } + virtual bool isArray() const { return type.isArray(); } + virtual bool isVector() const { return type.isVector(); } + virtual bool isScalar() const { return type.isScalar(); } + virtual bool isStruct() const { return type.isStruct(); } + virtual bool isFloatingDomain() const { return type.isFloatingDomain(); } + virtual bool isIntegerDomain() const { return type.isIntegerDomain(); } + TString getCompleteString() const { return type.getCompleteString(); } + +protected: + TIntermTyped& operator=(const TIntermTyped&); + TType type; +}; + +// +// Handle for, do-while, and while loops. +// +class TIntermLoop : public TIntermNode { +public: + TIntermLoop(TIntermNode* aBody, TIntermTyped* aTest, TIntermTyped* aTerminal, bool testFirst) : + body(aBody), + test(aTest), + terminal(aTerminal), + first(testFirst), + unroll(false), + dontUnroll(false), + dependency(0) + { } + + virtual TIntermLoop* getAsLoopNode() { return this; } + virtual const TIntermLoop* getAsLoopNode() const { return this; } + virtual void traverse(TIntermTraverser*); + TIntermNode* getBody() const { return body; } + TIntermTyped* getTest() const { return test; } + TIntermTyped* getTerminal() const { return terminal; } + bool testFirst() const { return first; } + + void setUnroll() { unroll = true; } + void setDontUnroll() { dontUnroll = true; } + bool getUnroll() const { return unroll; } + bool getDontUnroll() const { return dontUnroll; } + + static const unsigned int dependencyInfinite = 0xFFFFFFFF; + void setLoopDependency(int d) { dependency = d; } + int getLoopDependency() const { return dependency; } + +protected: + TIntermNode* body; // code to loop over + TIntermTyped* test; // exit condition associated with loop, could be 0 for 'for' loops + TIntermTyped* terminal; // exists for for-loops + bool first; // true for while and for, not for do-while + bool unroll; // true if unroll requested + bool dontUnroll; // true if request to not unroll + unsigned int dependency; // loop dependency hint; 0 means not set or unknown +}; + +// +// Handle case, break, continue, return, and kill. +// +class TIntermBranch : public TIntermNode { +public: + TIntermBranch(TOperator op, TIntermTyped* e) : + flowOp(op), + expression(e) { } + virtual TIntermBranch* getAsBranchNode() { return this; } + virtual const TIntermBranch* getAsBranchNode() const { return this; } + virtual void traverse(TIntermTraverser*); + TOperator getFlowOp() const { return flowOp; } + TIntermTyped* getExpression() const { return expression; } +protected: + TOperator flowOp; + TIntermTyped* expression; +}; + +// +// Represent method names before seeing their calling signature +// or resolving them to operations. Just an expression as the base object +// and a textural name. +// +class TIntermMethod : public TIntermTyped { +public: + TIntermMethod(TIntermTyped* o, const TType& t, const TString& m) : TIntermTyped(t), object(o), method(m) { } + virtual TIntermMethod* getAsMethodNode() { return this; } + virtual const TIntermMethod* getAsMethodNode() const { return this; } + virtual const TString& getMethodName() const { return method; } + virtual TIntermTyped* getObject() const { return object; } + virtual void traverse(TIntermTraverser*); +protected: + TIntermTyped* object; + TString method; +}; + +// +// Nodes that correspond to symbols or constants in the source code. +// +class TIntermSymbol : public TIntermTyped { +public: + // if symbol is initialized as symbol(sym), the memory comes from the pool allocator of sym. If sym comes from + // per process threadPoolAllocator, then it causes increased memory usage per compile + // it is essential to use "symbol = sym" to assign to symbol + TIntermSymbol(int i, const TString& n, const TType& t) + : TIntermTyped(t), id(i), +#ifdef ENABLE_HLSL + flattenSubset(-1), +#endif + constSubtree(nullptr) + { name = n; } + virtual int getId() const { return id; } + virtual const TString& getName() const { return name; } + virtual void traverse(TIntermTraverser*); + virtual TIntermSymbol* getAsSymbolNode() { return this; } + virtual const TIntermSymbol* getAsSymbolNode() const { return this; } + void setConstArray(const TConstUnionArray& c) { constArray = c; } + const TConstUnionArray& getConstArray() const { return constArray; } + void setConstSubtree(TIntermTyped* subtree) { constSubtree = subtree; } + TIntermTyped* getConstSubtree() const { return constSubtree; } +#ifdef ENABLE_HLSL + void setFlattenSubset(int subset) { flattenSubset = subset; } + int getFlattenSubset() const { return flattenSubset; } // -1 means full object +#endif + + // This is meant for cases where a node has already been constructed, and + // later on, it becomes necessary to switch to a different symbol. + virtual void switchId(int newId) { id = newId; } + +protected: + int id; // the unique id of the symbol this node represents +#ifdef ENABLE_HLSL + int flattenSubset; // how deeply the flattened object rooted at id has been dereferenced +#endif + TString name; // the name of the symbol this node represents + TConstUnionArray constArray; // if the symbol is a front-end compile-time constant, this is its value + TIntermTyped* constSubtree; +}; + +class TIntermConstantUnion : public TIntermTyped { +public: + TIntermConstantUnion(const TConstUnionArray& ua, const TType& t) : TIntermTyped(t), constArray(ua), literal(false) { } + const TConstUnionArray& getConstArray() const { return constArray; } + virtual TIntermConstantUnion* getAsConstantUnion() { return this; } + virtual const TIntermConstantUnion* getAsConstantUnion() const { return this; } + virtual void traverse(TIntermTraverser*); + virtual TIntermTyped* fold(TOperator, const TIntermTyped*) const; + virtual TIntermTyped* fold(TOperator, const TType&) const; + void setLiteral() { literal = true; } + void setExpression() { literal = false; } + bool isLiteral() const { return literal; } + +protected: + TIntermConstantUnion& operator=(const TIntermConstantUnion&); + + const TConstUnionArray constArray; + bool literal; // true if node represents a literal in the source code +}; + +// Represent the independent aspects of a texturing TOperator +struct TCrackedTextureOp { + bool query; + bool proj; + bool lod; + bool fetch; + bool offset; + bool offsets; + bool gather; + bool grad; + bool subpass; + bool lodClamp; +#ifdef AMD_EXTENSIONS + bool fragMask; +#endif +}; + +// +// Intermediate class for node types that hold operators. +// +class TIntermOperator : public TIntermTyped { +public: + virtual TIntermOperator* getAsOperator() { return this; } + virtual const TIntermOperator* getAsOperator() const { return this; } + TOperator getOp() const { return op; } + void setOp(TOperator newOp) { op = newOp; } + bool modifiesState() const; + bool isConstructor() const; + bool isTexture() const { return op > EOpTextureGuardBegin && op < EOpTextureGuardEnd; } + bool isSampling() const { return op > EOpSamplingGuardBegin && op < EOpSamplingGuardEnd; } + bool isImage() const { return op > EOpImageGuardBegin && op < EOpImageGuardEnd; } + bool isSparseTexture() const { return op > EOpSparseTextureGuardBegin && op < EOpSparseTextureGuardEnd; } + bool isSparseImage() const { return op == EOpSparseImageLoad; } + + void setOperationPrecision(TPrecisionQualifier p) { operationPrecision = p; } + TPrecisionQualifier getOperationPrecision() const { return operationPrecision != EpqNone ? + operationPrecision : + type.getQualifier().precision; } + TString getCompleteString() const + { + TString cs = type.getCompleteString(); + if (getOperationPrecision() != type.getQualifier().precision) { + cs += ", operation at "; + cs += GetPrecisionQualifierString(getOperationPrecision()); + } + + return cs; + } + + // Crack the op into the individual dimensions of texturing operation. + void crackTexture(TSampler sampler, TCrackedTextureOp& cracked) const + { + cracked.query = false; + cracked.proj = false; + cracked.lod = false; + cracked.fetch = false; + cracked.offset = false; + cracked.offsets = false; + cracked.gather = false; + cracked.grad = false; + cracked.subpass = false; + cracked.lodClamp = false; +#ifdef AMD_EXTENSIONS + cracked.fragMask = false; +#endif + + switch (op) { + case EOpImageQuerySize: + case EOpImageQuerySamples: + case EOpTextureQuerySize: + case EOpTextureQueryLod: + case EOpTextureQueryLevels: + case EOpTextureQuerySamples: + case EOpSparseTexelsResident: + cracked.query = true; + break; + case EOpTexture: + case EOpSparseTexture: + break; + case EOpTextureClamp: + case EOpSparseTextureClamp: + cracked.lodClamp = true; + break; + case EOpTextureProj: + cracked.proj = true; + break; + case EOpTextureLod: + case EOpSparseTextureLod: + cracked.lod = true; + break; + case EOpTextureOffset: + case EOpSparseTextureOffset: + cracked.offset = true; + break; + case EOpTextureOffsetClamp: + case EOpSparseTextureOffsetClamp: + cracked.offset = true; + cracked.lodClamp = true; + break; + case EOpTextureFetch: + case EOpSparseTextureFetch: + cracked.fetch = true; + if (sampler.dim == Esd1D || (sampler.dim == Esd2D && ! sampler.ms) || sampler.dim == Esd3D) + cracked.lod = true; + break; + case EOpTextureFetchOffset: + case EOpSparseTextureFetchOffset: + cracked.fetch = true; + cracked.offset = true; + if (sampler.dim == Esd1D || (sampler.dim == Esd2D && ! sampler.ms) || sampler.dim == Esd3D) + cracked.lod = true; + break; + case EOpTextureProjOffset: + cracked.offset = true; + cracked.proj = true; + break; + case EOpTextureLodOffset: + case EOpSparseTextureLodOffset: + cracked.offset = true; + cracked.lod = true; + break; + case EOpTextureProjLod: + cracked.lod = true; + cracked.proj = true; + break; + case EOpTextureProjLodOffset: + cracked.offset = true; + cracked.lod = true; + cracked.proj = true; + break; + case EOpTextureGrad: + case EOpSparseTextureGrad: + cracked.grad = true; + break; + case EOpTextureGradClamp: + case EOpSparseTextureGradClamp: + cracked.grad = true; + cracked.lodClamp = true; + break; + case EOpTextureGradOffset: + case EOpSparseTextureGradOffset: + cracked.grad = true; + cracked.offset = true; + break; + case EOpTextureProjGrad: + cracked.grad = true; + cracked.proj = true; + break; + case EOpTextureProjGradOffset: + cracked.grad = true; + cracked.offset = true; + cracked.proj = true; + break; + case EOpTextureGradOffsetClamp: + case EOpSparseTextureGradOffsetClamp: + cracked.grad = true; + cracked.offset = true; + cracked.lodClamp = true; + break; + case EOpTextureGather: + case EOpSparseTextureGather: + cracked.gather = true; + break; + case EOpTextureGatherOffset: + case EOpSparseTextureGatherOffset: + cracked.gather = true; + cracked.offset = true; + break; + case EOpTextureGatherOffsets: + case EOpSparseTextureGatherOffsets: + cracked.gather = true; + cracked.offsets = true; + break; +#ifdef AMD_EXTENSIONS + case EOpTextureGatherLod: + case EOpSparseTextureGatherLod: + cracked.gather = true; + cracked.lod = true; + break; + case EOpTextureGatherLodOffset: + case EOpSparseTextureGatherLodOffset: + cracked.gather = true; + cracked.offset = true; + cracked.lod = true; + break; + case EOpTextureGatherLodOffsets: + case EOpSparseTextureGatherLodOffsets: + cracked.gather = true; + cracked.offsets = true; + cracked.lod = true; + break; + case EOpImageLoadLod: + case EOpImageStoreLod: + case EOpSparseImageLoadLod: + cracked.lod = true; + break; + case EOpFragmentMaskFetch: + cracked.subpass = sampler.dim == EsdSubpass; + cracked.fragMask = true; + break; + case EOpFragmentFetch: + cracked.subpass = sampler.dim == EsdSubpass; + cracked.fragMask = true; + break; +#endif + case EOpSubpassLoad: + case EOpSubpassLoadMS: + cracked.subpass = true; + break; + default: + break; + } + } + +protected: + TIntermOperator(TOperator o) : TIntermTyped(EbtFloat), op(o), operationPrecision(EpqNone) {} + TIntermOperator(TOperator o, TType& t) : TIntermTyped(t), op(o), operationPrecision(EpqNone) {} + TOperator op; + // The result precision is in the inherited TType, and is usually meant to be both + // the operation precision and the result precision. However, some more complex things, + // like built-in function calls, distinguish between the two, in which case non-EqpNone + // 'operationPrecision' overrides the result precision as far as operation precision + // is concerned. + TPrecisionQualifier operationPrecision; +}; + +// +// Nodes for all the basic binary math operators. +// +class TIntermBinary : public TIntermOperator { +public: + TIntermBinary(TOperator o) : TIntermOperator(o) {} + virtual void traverse(TIntermTraverser*); + virtual void setLeft(TIntermTyped* n) { left = n; } + virtual void setRight(TIntermTyped* n) { right = n; } + virtual TIntermTyped* getLeft() const { return left; } + virtual TIntermTyped* getRight() const { return right; } + virtual TIntermBinary* getAsBinaryNode() { return this; } + virtual const TIntermBinary* getAsBinaryNode() const { return this; } + virtual void updatePrecision(); +protected: + TIntermTyped* left; + TIntermTyped* right; +}; + +// +// Nodes for unary math operators. +// +class TIntermUnary : public TIntermOperator { +public: + TIntermUnary(TOperator o, TType& t) : TIntermOperator(o, t), operand(0) {} + TIntermUnary(TOperator o) : TIntermOperator(o), operand(0) {} + virtual void traverse(TIntermTraverser*); + virtual void setOperand(TIntermTyped* o) { operand = o; } + virtual TIntermTyped* getOperand() { return operand; } + virtual const TIntermTyped* getOperand() const { return operand; } + virtual TIntermUnary* getAsUnaryNode() { return this; } + virtual const TIntermUnary* getAsUnaryNode() const { return this; } + virtual void updatePrecision(); +protected: + TIntermTyped* operand; +}; + +typedef TVector TIntermSequence; +typedef TVector TQualifierList; +// +// Nodes that operate on an arbitrary sized set of children. +// +class TIntermAggregate : public TIntermOperator { +public: + TIntermAggregate() : TIntermOperator(EOpNull), userDefined(false), pragmaTable(nullptr) { } + TIntermAggregate(TOperator o) : TIntermOperator(o), pragmaTable(nullptr) { } + ~TIntermAggregate() { delete pragmaTable; } + virtual TIntermAggregate* getAsAggregate() { return this; } + virtual const TIntermAggregate* getAsAggregate() const { return this; } + virtual void setOperator(TOperator o) { op = o; } + virtual TIntermSequence& getSequence() { return sequence; } + virtual const TIntermSequence& getSequence() const { return sequence; } + virtual void setName(const TString& n) { name = n; } + virtual const TString& getName() const { return name; } + virtual void traverse(TIntermTraverser*); + virtual void setUserDefined() { userDefined = true; } + virtual bool isUserDefined() { return userDefined; } + virtual TQualifierList& getQualifierList() { return qualifier; } + virtual const TQualifierList& getQualifierList() const { return qualifier; } + void setOptimize(bool o) { optimize = o; } + void setDebug(bool d) { debug = d; } + bool getOptimize() const { return optimize; } + bool getDebug() const { return debug; } + void setPragmaTable(const TPragmaTable& pTable); + const TPragmaTable& getPragmaTable() const { return *pragmaTable; } +protected: + TIntermAggregate(const TIntermAggregate&); // disallow copy constructor + TIntermAggregate& operator=(const TIntermAggregate&); // disallow assignment operator + TIntermSequence sequence; + TQualifierList qualifier; + TString name; + bool userDefined; // used for user defined function names + bool optimize; + bool debug; + TPragmaTable* pragmaTable; +}; + +// +// For if tests. +// +class TIntermSelection : public TIntermTyped { +public: + TIntermSelection(TIntermTyped* cond, TIntermNode* trueB, TIntermNode* falseB) : + TIntermTyped(EbtVoid), condition(cond), trueBlock(trueB), falseBlock(falseB), + flatten(false), dontFlatten(false) {} + TIntermSelection(TIntermTyped* cond, TIntermNode* trueB, TIntermNode* falseB, const TType& type) : + TIntermTyped(type), condition(cond), trueBlock(trueB), falseBlock(falseB), + flatten(false), dontFlatten(false) {} + virtual void traverse(TIntermTraverser*); + virtual TIntermTyped* getCondition() const { return condition; } + virtual TIntermNode* getTrueBlock() const { return trueBlock; } + virtual TIntermNode* getFalseBlock() const { return falseBlock; } + virtual TIntermSelection* getAsSelectionNode() { return this; } + virtual const TIntermSelection* getAsSelectionNode() const { return this; } + + void setFlatten() { flatten = true; } + void setDontFlatten() { dontFlatten = true; } + bool getFlatten() const { return flatten; } + bool getDontFlatten() const { return dontFlatten; } + +protected: + TIntermTyped* condition; + TIntermNode* trueBlock; + TIntermNode* falseBlock; + bool flatten; // true if flatten requested + bool dontFlatten; // true if requested to not flatten +}; + +// +// For switch statements. Designed use is that a switch will have sequence of nodes +// that are either case/default nodes or a *single* node that represents all the code +// in between (if any) consecutive case/defaults. So, a traversal need only deal with +// 0 or 1 nodes per case/default statement. +// +class TIntermSwitch : public TIntermNode { +public: + TIntermSwitch(TIntermTyped* cond, TIntermAggregate* b) : condition(cond), body(b), + flatten(false), dontFlatten(false) {} + virtual void traverse(TIntermTraverser*); + virtual TIntermNode* getCondition() const { return condition; } + virtual TIntermAggregate* getBody() const { return body; } + virtual TIntermSwitch* getAsSwitchNode() { return this; } + virtual const TIntermSwitch* getAsSwitchNode() const { return this; } + + void setFlatten() { flatten = true; } + void setDontFlatten() { dontFlatten = true; } + bool getFlatten() const { return flatten; } + bool getDontFlatten() const { return dontFlatten; } + +protected: + TIntermTyped* condition; + TIntermAggregate* body; + bool flatten; // true if flatten requested + bool dontFlatten; // true if requested to not flatten +}; + +enum TVisit +{ + EvPreVisit, + EvInVisit, + EvPostVisit +}; + +// +// For traversing the tree. User should derive from this, +// put their traversal specific data in it, and then pass +// it to a Traverse method. +// +// When using this, just fill in the methods for nodes you want visited. +// Return false from a pre-visit to skip visiting that node's subtree. +// +// Explicitly set postVisit to true if you want post visiting, otherwise, +// filled in methods will only be called at pre-visit time (before processing +// the subtree). Similarly for inVisit for in-order visiting of nodes with +// multiple children. +// +// If you only want post-visits, explicitly turn off preVisit (and inVisit) +// and turn on postVisit. +// +// In general, for the visit*() methods, return true from interior nodes +// to have the traversal continue on to children. +// +// If you process children yourself, or don't want them processed, return false. +// +class TIntermTraverser { +public: + POOL_ALLOCATOR_NEW_DELETE(glslang::GetThreadPoolAllocator()) + TIntermTraverser(bool preVisit = true, bool inVisit = false, bool postVisit = false, bool rightToLeft = false) : + preVisit(preVisit), + inVisit(inVisit), + postVisit(postVisit), + rightToLeft(rightToLeft), + depth(0), + maxDepth(0) { } + virtual ~TIntermTraverser() { } + + virtual void visitSymbol(TIntermSymbol*) { } + virtual void visitConstantUnion(TIntermConstantUnion*) { } + virtual bool visitBinary(TVisit, TIntermBinary*) { return true; } + virtual bool visitUnary(TVisit, TIntermUnary*) { return true; } + virtual bool visitSelection(TVisit, TIntermSelection*) { return true; } + virtual bool visitAggregate(TVisit, TIntermAggregate*) { return true; } + virtual bool visitLoop(TVisit, TIntermLoop*) { return true; } + virtual bool visitBranch(TVisit, TIntermBranch*) { return true; } + virtual bool visitSwitch(TVisit, TIntermSwitch*) { return true; } + + int getMaxDepth() const { return maxDepth; } + + void incrementDepth(TIntermNode *current) + { + depth++; + maxDepth = (std::max)(maxDepth, depth); + path.push_back(current); + } + + void decrementDepth() + { + depth--; + path.pop_back(); + } + + TIntermNode *getParentNode() + { + return path.size() == 0 ? NULL : path.back(); + } + + const bool preVisit; + const bool inVisit; + const bool postVisit; + const bool rightToLeft; + +protected: + TIntermTraverser& operator=(TIntermTraverser&); + + int depth; + int maxDepth; + + // All the nodes from root to the current node's parent during traversing. + TVector path; +}; + +// KHR_vulkan_glsl says "Two arrays sized with specialization constants are the same type only if +// sized with the same symbol, involving no operations" +inline bool SameSpecializationConstants(TIntermTyped* node1, TIntermTyped* node2) +{ + return node1->getAsSymbolNode() && node2->getAsSymbolNode() && + node1->getAsSymbolNode()->getId() == node2->getAsSymbolNode()->getId(); +} + +} // end namespace glslang + +#endif // __INTERMEDIATE_H diff --git a/third_party/glslang-spirv/Include/revision.h b/third_party/glslang-spirv/Include/revision.h new file mode 100644 index 000000000..218f8b67f --- /dev/null +++ b/third_party/glslang-spirv/Include/revision.h @@ -0,0 +1,6 @@ +// This header is generated by the make-revision script. +// For the version, it uses the latest git tag followed by the number of commits. +// For the date, it uses the current date (when then script is run). + +#define GLSLANG_REVISION "Overload400-PrecQual.2000" +#define GLSLANG_DATE "12-Apr-2017" diff --git a/third_party/glslang-spirv/Include/revision.template b/third_party/glslang-spirv/Include/revision.template new file mode 100644 index 000000000..4a16beeb0 --- /dev/null +++ b/third_party/glslang-spirv/Include/revision.template @@ -0,0 +1,13 @@ +// The file revision.h should be updated to the latest version, somehow, on +// check-in, if glslang has changed. +// +// revision.template is the source for revision.h when using SubWCRev as the +// method of updating revision.h. You don't have to do it this way, the +// requirement is only that revision.h gets updated. +// +// revision.h is under source control so that not all consumers of glslang +// source have to figure out how to create revision.h just to get a build +// going. However, if it is not updated, it can be a version behind. + +#define GLSLANG_REVISION "$WCREV$" +#define GLSLANG_DATE "$WCDATE$" diff --git a/third_party/glslang-spirv/Logger.cpp b/third_party/glslang-spirv/Logger.cpp new file mode 100644 index 000000000..48bd4e3ad --- /dev/null +++ b/third_party/glslang-spirv/Logger.cpp @@ -0,0 +1,68 @@ +// +// Copyright (C) 2016 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#include "Logger.h" + +#include +#include +#include + +namespace spv { + +void SpvBuildLogger::tbdFunctionality(const std::string& f) +{ + if (std::find(std::begin(tbdFeatures), std::end(tbdFeatures), f) == std::end(tbdFeatures)) + tbdFeatures.push_back(f); +} + +void SpvBuildLogger::missingFunctionality(const std::string& f) +{ + if (std::find(std::begin(missingFeatures), std::end(missingFeatures), f) == std::end(missingFeatures)) + missingFeatures.push_back(f); +} + +std::string SpvBuildLogger::getAllMessages() const { + std::ostringstream messages; + for (auto it = tbdFeatures.cbegin(); it != tbdFeatures.cend(); ++it) + messages << "TBD functionality: " << *it << "\n"; + for (auto it = missingFeatures.cbegin(); it != missingFeatures.cend(); ++it) + messages << "Missing functionality: " << *it << "\n"; + for (auto it = warnings.cbegin(); it != warnings.cend(); ++it) + messages << "warning: " << *it << "\n"; + for (auto it = errors.cbegin(); it != errors.cend(); ++it) + messages << "error: " << *it << "\n"; + return messages.str(); +} + +} // end spv namespace diff --git a/third_party/glslang-spirv/Logger.h b/third_party/glslang-spirv/Logger.h new file mode 100644 index 000000000..2e4ddaf51 --- /dev/null +++ b/third_party/glslang-spirv/Logger.h @@ -0,0 +1,74 @@ +// +// Copyright (C) 2016 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#ifndef GLSLANG_SPIRV_LOGGER_H +#define GLSLANG_SPIRV_LOGGER_H + +#include +#include + +namespace spv { + +// A class for holding all SPIR-V build status messages, including +// missing/TBD functionalities, warnings, and errors. +class SpvBuildLogger { +public: + SpvBuildLogger() {} + + // Registers a TBD functionality. + void tbdFunctionality(const std::string& f); + // Registers a missing functionality. + void missingFunctionality(const std::string& f); + + // Logs a warning. + void warning(const std::string& w) { warnings.push_back(w); } + // Logs an error. + void error(const std::string& e) { errors.push_back(e); } + + // Returns all messages accumulated in the order of: + // TBD functionalities, missing functionalities, warnings, errors. + std::string getAllMessages() const; + +private: + SpvBuildLogger(const SpvBuildLogger&); + + std::vector tbdFeatures; + std::vector missingFeatures; + std::vector warnings; + std::vector errors; +}; + +} // end spv namespace + +#endif // GLSLANG_SPIRV_LOGGER_H diff --git a/third_party/glslang-spirv/SPVRemapper.cpp b/third_party/glslang-spirv/SPVRemapper.cpp index a76720a0b..f14d85729 100644 --- a/third_party/glslang-spirv/SPVRemapper.cpp +++ b/third_party/glslang-spirv/SPVRemapper.cpp @@ -1,11 +1,11 @@ // -//Copyright (C) 2015 LunarG, Inc. +// Copyright (C) 2015 LunarG, Inc. // -//All rights reserved. +// All rights reserved. // -//Redistribution and use in source and binary forms, with or without -//modification, are permitted provided that the following conditions -//are met: +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: // // Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. @@ -19,18 +19,18 @@ // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // -//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -//"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -//LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -//FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -//COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -//INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -//BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -//LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -//CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -//ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -//POSSIBILITY OF SUCH DAMAGE. +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. // #include "SPVRemapper.h" @@ -42,6 +42,7 @@ #include #include +#include "Include/Common.h" namespace spv { @@ -102,10 +103,10 @@ namespace spv { switch (opCode) { case spv::OpTypeVector: // fall through - case spv::OpTypeMatrix: // ... - case spv::OpTypeSampler: // ... - case spv::OpTypeArray: // ... - case spv::OpTypeRuntimeArray: // ... + case spv::OpTypeMatrix: // ... + case spv::OpTypeSampler: // ... + case spv::OpTypeArray: // ... + case spv::OpTypeRuntimeArray: // ... case spv::OpTypePipe: return range_t(2, 3); case spv::OpTypeStruct: // fall through case spv::OpTypeFunction: return range_t(2, maxCount); @@ -126,6 +127,38 @@ namespace spv { } } + // Return the size of a type in 32-bit words. This currently only + // handles ints and floats, and is only invoked by queries which must be + // integer types. If ever needed, it can be generalized. + unsigned spirvbin_t::typeSizeInWords(spv::Id id) const + { + const unsigned typeStart = idPos(id); + const spv::Op opCode = asOpCode(typeStart); + + if (errorLatch) + return 0; + + switch (opCode) { + case spv::OpTypeInt: // fall through... + case spv::OpTypeFloat: return (spv[typeStart+2]+31)/32; + default: + return 0; + } + } + + // Looks up the type of a given const or variable ID, and + // returns its size in 32-bit words. + unsigned spirvbin_t::idTypeSizeInWords(spv::Id id) const + { + const auto tid_it = idTypeSizeMap.find(id); + if (tid_it == idTypeSizeMap.end()) { + error("type size for ID not found"); + return 0; + } + + return tid_it->second; + } + // Is this an opcode we should remove when using --strip? bool spirvbin_t::isStripOp(spv::Op opCode) const { @@ -139,6 +172,7 @@ namespace spv { } } + // Return true if this opcode is flow control bool spirvbin_t::isFlowCtrl(spv::Op opCode) const { switch (opCode) { @@ -154,6 +188,7 @@ namespace spv { } } + // Return true if this opcode defines a type bool spirvbin_t::isTypeOp(spv::Op opCode) const { switch (opCode) { @@ -181,17 +216,23 @@ namespace spv { } } + // Return true if this opcode defines a constant bool spirvbin_t::isConstOp(spv::Op opCode) const { switch (opCode) { - case spv::OpConstantNull: error("unimplemented constant type"); - case spv::OpConstantSampler: error("unimplemented constant type"); + case spv::OpConstantNull: + case spv::OpConstantSampler: + error("unimplemented constant type"); + return true; case spv::OpConstantTrue: case spv::OpConstantFalse: case spv::OpConstantComposite: - case spv::OpConstant: return true; - default: return false; + case spv::OpConstant: + return true; + + default: + return false; } } @@ -217,19 +258,31 @@ namespace spv { { assert(id != spv::NoResult && newId != spv::NoResult); + if (id > bound()) { + error(std::string("ID out of range: ") + std::to_string(id)); + return spirvbin_t::unused; + } + if (id >= idMapL.size()) idMapL.resize(id+1, unused); if (newId != unmapped && newId != unused) { - if (isOldIdUnused(id)) + if (isOldIdUnused(id)) { error(std::string("ID unused in module: ") + std::to_string(id)); + return spirvbin_t::unused; + } - if (!isOldIdUnmapped(id)) + if (!isOldIdUnmapped(id)) { error(std::string("ID already mapped: ") + std::to_string(id) + " -> " - + std::to_string(localId(id))); + + std::to_string(localId(id))); - if (isNewIdMapped(newId)) + return spirvbin_t::unused; + } + + if (isNewIdMapped(newId)) { error(std::string("ID already used in module: ") + std::to_string(newId)); + return spirvbin_t::unused; + } msg(4, 4, std::string("map: ") + std::to_string(id) + " -> " + std::to_string(newId)); setMapped(newId); @@ -255,7 +308,6 @@ namespace spv { return literal; } - void spirvbin_t::applyMap() { msg(3, 2, std::string("Applying map: ")); @@ -264,12 +316,15 @@ namespace spv { process(inst_fn_nop, // ignore instructions [this](spv::Id& id) { id = localId(id); + + if (errorLatch) + return; + assert(id != unused && id != unmapped); } ); } - // Find free IDs for anything we haven't mapped void spirvbin_t::mapRemainder() { @@ -283,25 +338,31 @@ namespace spv { continue; // Find a new mapping for any used but unmapped IDs - if (isOldIdUnmapped(id)) + if (isOldIdUnmapped(id)) { localId(id, unusedId = nextUnusedId(unusedId)); + if (errorLatch) + return; + } - if (isOldIdUnmapped(id)) + if (isOldIdUnmapped(id)) { error(std::string("old ID not mapped: ") + std::to_string(id)); + return; + } // Track max bound maxBound = std::max(maxBound, localId(id) + 1); + + if (errorLatch) + return; } bound(maxBound); // reset header ID bound to as big as it now needs to be } + // Mark debug instructions for stripping void spirvbin_t::stripDebug() { - if ((options & STRIP) == 0) - return; - - // build local Id and name maps + // Strip instructions in the stripOp set: debug info. process( [&](spv::Op opCode, unsigned start) { // remember opcodes we want to strip later @@ -312,6 +373,32 @@ namespace spv { op_fn_nop); } + // Mark instructions that refer to now-removed IDs for stripping + void spirvbin_t::stripDeadRefs() + { + process( + [&](spv::Op opCode, unsigned start) { + // strip opcodes pointing to removed data + switch (opCode) { + case spv::OpName: + case spv::OpMemberName: + case spv::OpDecorate: + case spv::OpMemberDecorate: + if (idPosR.find(asId(start+1)) == idPosR.end()) + stripInst(start); + break; + default: + break; // leave it alone + } + + return true; + }, + op_fn_nop); + + strip(); + } + + // Update local maps of ID, type, etc positions void spirvbin_t::buildLocalMaps() { msg(2, 2, std::string("build local maps: ")); @@ -320,10 +407,9 @@ namespace spv { idMapL.clear(); // preserve nameMap, so we don't clear that. fnPos.clear(); - fnPosDCE.clear(); fnCalls.clear(); typeConstPos.clear(); - typeConstPosR.clear(); + idPosR.clear(); entryPoint = spv::NoResult; largestNewId = 0; @@ -335,9 +421,27 @@ namespace spv { // build local Id and name maps process( [&](spv::Op opCode, unsigned start) { - // remember opcodes we want to strip later - if ((options & STRIP) && isStripOp(opCode)) - stripInst(start); + unsigned word = start+1; + spv::Id typeId = spv::NoResult; + + if (spv::InstructionDesc[opCode].hasType()) + typeId = asId(word++); + + // If there's a result ID, remember the size of its type + if (spv::InstructionDesc[opCode].hasResult()) { + const spv::Id resultId = asId(word++); + idPosR[resultId] = start; + + if (typeId != spv::NoResult) { + const unsigned idTypeSize = typeSizeInWords(typeId); + + if (errorLatch) + return false; + + if (idTypeSize != 0) + idTypeSizeMap[resultId] = idTypeSize; + } + } if (opCode == spv::Op::OpName) { const spv::Id target = asId(start+1); @@ -349,24 +453,31 @@ namespace spv { } else if (opCode == spv::Op::OpEntryPoint) { entryPoint = asId(start + 2); } else if (opCode == spv::Op::OpFunction) { - if (fnStart != 0) + if (fnStart != 0) { error("nested function found"); + return false; + } + fnStart = start; fnRes = asId(start + 2); } else if (opCode == spv::Op::OpFunctionEnd) { assert(fnRes != spv::NoResult); - if (fnStart == 0) + if (fnStart == 0) { error("function end without function start"); + return false; + } + fnPos[fnRes] = range_t(fnStart, start + asWordCount(start)); fnStart = 0; } else if (isConstOp(opCode)) { + if (errorLatch) + return false; + assert(asId(start + 2) != spv::NoResult); typeConstPos.insert(start); - typeConstPosR[asId(start + 2)] = start; } else if (isTypeOp(opCode)) { assert(asId(start + 1) != spv::NoResult); typeConstPos.insert(start); - typeConstPosR[asId(start + 1)] = start; } return false; @@ -381,30 +492,37 @@ namespace spv { { msg(2, 2, std::string("validating: ")); - if (spv.size() < header_size) + if (spv.size() < header_size) { error("file too short: "); + return; + } - if (magic() != spv::MagicNumber) + if (magic() != spv::MagicNumber) { error("bad magic number"); + return; + } // field 1 = version // field 2 = generator magic // field 3 = result bound - if (schemaNum() != 0) + if (schemaNum() != 0) { error("bad schema, must be 0"); + return; + } } - int spirvbin_t::processInstruction(unsigned word, instfn_t instFn, idfn_t idFn) { const auto instructionStart = word; const unsigned wordCount = asWordCount(instructionStart); - const spv::Op opCode = asOpCode(instructionStart); const int nextInst = word++ + wordCount; + spv::Op opCode = asOpCode(instructionStart); - if (nextInst > int(spv.size())) + if (nextInst > int(spv.size())) { error("spir instruction terminated too early"); + return -1; + } // Base for computing number of operands; will be updated as more is learned unsigned numOperands = wordCount - 1; @@ -435,10 +553,31 @@ namespace spv { return nextInst; } + // Circular buffer so we can look back at previous unmapped values during the mapping pass. + static const unsigned idBufferSize = 4; + spv::Id idBuffer[idBufferSize]; + unsigned idBufferPos = 0; + // Store IDs from instruction in our map for (int op = 0; numOperands > 0; ++op, --numOperands) { + // SpecConstantOp is special: it includes the operands of another opcode which is + // given as a literal in the 3rd word. We will switch over to pretending that the + // opcode being processed is the literal opcode value of the SpecConstantOp. See the + // SPIRV spec for details. This way we will handle IDs and literals as appropriate for + // the embedded op. + if (opCode == spv::OpSpecConstantOp) { + if (op == 0) { + opCode = asOpCode(word++); // this is the opcode embedded in the SpecConstantOp. + --numOperands; + } + } + switch (spv::InstructionDesc[opCode].operands.getClass(op)) { case spv::OperandId: + case spv::OperandScope: + case spv::OperandMemorySemantics: + idBuffer[idBufferPos] = asId(word); + idBufferPos = (idBufferPos + 1) % idBufferSize; idFn(asId(word++)); break; @@ -456,13 +595,28 @@ namespace spv { // word += numOperands; return nextInst; - case spv::OperandVariableLiteralId: - while (numOperands > 0) { - ++word; // immediate - idFn(asId(word++)); // ID - numOperands -= 2; + case spv::OperandVariableLiteralId: { + if (opCode == OpSwitch) { + // word-2 is the position of the selector ID. OpSwitch Literals match its type. + // In case the IDs are currently being remapped, we get the word[-2] ID from + // the circular idBuffer. + const unsigned literalSizePos = (idBufferPos+idBufferSize-2) % idBufferSize; + const unsigned literalSize = idTypeSizeInWords(idBuffer[literalSizePos]); + const unsigned numLiteralIdPairs = (nextInst-word) / (1+literalSize); + + if (errorLatch) + return -1; + + for (unsigned arg=0; arg 0) { fnLocalVars.erase(id); idMap.erase(id); @@ -824,6 +999,9 @@ namespace spv { } ); + if (errorLatch) + return; + process( [&](spv::Op opCode, unsigned start) { if (opCode == spv::OpLoad && fnLocalVars.count(asId(start+3)) > 0) @@ -832,6 +1010,9 @@ namespace spv { }, op_fn_nop); + if (errorLatch) + return; + // Chase replacements to their origins, in case there is a chain such as: // 2 = store 1 // 3 = load 2 @@ -865,6 +1046,9 @@ namespace spv { } ); + if (errorLatch) + return; + strip(); // strip out data we decided to eliminate } @@ -890,7 +1074,6 @@ namespace spv { if (call_it == fnCalls.end() || call_it->second == 0) { changed = true; stripRange.push_back(fn->second); - fnPosDCE.insert(*fn); // decrease counts of called functions process( @@ -909,6 +1092,9 @@ namespace spv { fn->second.first, fn->second.second); + if (errorLatch) + return; + fn = fnPos.erase(fn); } else ++fn; } @@ -925,21 +1111,37 @@ namespace spv { // Count function variable use process( [&](spv::Op opCode, unsigned start) { - if (opCode == spv::OpVariable) { ++varUseCount[asId(start+2)]; return true; } - return false; + if (opCode == spv::OpVariable) { + ++varUseCount[asId(start+2)]; + return true; + } else if (opCode == spv::OpEntryPoint) { + const int wordCount = asWordCount(start); + for (int i = 4; i < wordCount; i++) { + ++varUseCount[asId(start+i)]; + } + return true; + } else + return false; }, [&](spv::Id& id) { if (varUseCount[id]) ++varUseCount[id]; } ); + if (errorLatch) + return; + // Remove single-use function variables + associated decorations and names process( [&](spv::Op opCode, unsigned start) { - if ((opCode == spv::OpVariable && varUseCount[asId(start+2)] == 1) || - (opCode == spv::OpDecorate && varUseCount[asId(start+1)] == 1) || - (opCode == spv::OpName && varUseCount[asId(start+1)] == 1)) { - stripInst(start); - } + spv::Id id = spv::NoResult; + if (opCode == spv::OpVariable) + id = asId(start+2); + if (opCode == spv::OpDecorate || opCode == spv::OpName) + id = asId(start+1); + + if (id != spv::NoResult && varUseCount[id] == 1) + stripInst(start); + return true; }, op_fn_nop); @@ -956,28 +1158,37 @@ namespace spv { std::unordered_map typeUseCount; - // Count total type usage - process(inst_fn_nop, - [&](spv::Id& id) { if (isType[id]) ++typeUseCount[id]; } - ); + // This is not the most efficient algorithm, but this is an offline tool, and + // it's easy to write this way. Can be improved opportunistically if needed. + bool changed = true; + while (changed) { + changed = false; + strip(); + typeUseCount.clear(); - // Remove types from deleted code - for (const auto& fn : fnPosDCE) + // Count total type usage process(inst_fn_nop, - [&](spv::Id& id) { if (isType[id]) --typeUseCount[id]; }, - fn.second.first, fn.second.second); + [&](spv::Id& id) { if (isType[id]) ++typeUseCount[id]; } + ); - // Remove single reference types - for (const auto typeStart : typeConstPos) { - const spv::Id typeId = asTypeConstId(typeStart); - if (typeUseCount[typeId] == 1) { - --typeUseCount[typeId]; - stripInst(typeStart); + if (errorLatch) + return; + + // Remove single reference types + for (const auto typeStart : typeConstPos) { + const spv::Id typeId = asTypeConstId(typeStart); + if (typeUseCount[typeId] == 1) { + changed = true; + --typeUseCount[typeId]; + stripInst(typeStart); + } } + + if (errorLatch) + return; } } - #ifdef NOTDEF bool spirvbin_t::matchType(const spirvbin_t::globaltypes_t& globalTypes, spv::Id lt, spv::Id gt) const { @@ -1037,7 +1248,6 @@ namespace spv { } } - // Look for an equivalent type in the globalTypes map spv::Id spirvbin_t::findType(const spirvbin_t::globaltypes_t& globalTypes, spv::Id lt) const { @@ -1050,12 +1260,14 @@ namespace spv { } #endif // NOTDEF - // Return start position in SPV of given type. error if not found. - unsigned spirvbin_t::typePos(spv::Id id) const + // Return start position in SPV of given Id. error if not found. + unsigned spirvbin_t::idPos(spv::Id id) const { - const auto tid_it = typeConstPosR.find(id); - if (tid_it == typeConstPosR.end()) - error("type ID not found"); + const auto tid_it = idPosR.find(id); + if (tid_it == idPosR.end()) { + error("ID not found"); + return 0; + } return tid_it->second; } @@ -1073,11 +1285,11 @@ namespace spv { case spv::OpTypeInt: return 3 + (spv[typeStart+3]); case spv::OpTypeFloat: return 5; case spv::OpTypeVector: - return 6 + hashType(typePos(spv[typeStart+2])) * (spv[typeStart+3] - 1); + return 6 + hashType(idPos(spv[typeStart+2])) * (spv[typeStart+3] - 1); case spv::OpTypeMatrix: - return 30 + hashType(typePos(spv[typeStart+2])) * (spv[typeStart+3] - 1); + return 30 + hashType(idPos(spv[typeStart+2])) * (spv[typeStart+3] - 1); case spv::OpTypeImage: - return 120 + hashType(typePos(spv[typeStart+2])) + + return 120 + hashType(idPos(spv[typeStart+2])) + spv[typeStart+3] + // dimensionality spv[typeStart+4] * 8 * 16 + // depth spv[typeStart+5] * 4 * 16 + // arrayed @@ -1088,24 +1300,24 @@ namespace spv { case spv::OpTypeSampledImage: return 502; case spv::OpTypeArray: - return 501 + hashType(typePos(spv[typeStart+2])) * spv[typeStart+3]; + return 501 + hashType(idPos(spv[typeStart+2])) * spv[typeStart+3]; case spv::OpTypeRuntimeArray: - return 5000 + hashType(typePos(spv[typeStart+2])); + return 5000 + hashType(idPos(spv[typeStart+2])); case spv::OpTypeStruct: { std::uint32_t hash = 10000; for (unsigned w=2; w < wordCount; ++w) - hash += w * hashType(typePos(spv[typeStart+w])); + hash += w * hashType(idPos(spv[typeStart+w])); return hash; } case spv::OpTypeOpaque: return 6000 + spv[typeStart+2]; - case spv::OpTypePointer: return 100000 + hashType(typePos(spv[typeStart+3])); + case spv::OpTypePointer: return 100000 + hashType(idPos(spv[typeStart+3])); case spv::OpTypeFunction: { std::uint32_t hash = 200000; for (unsigned w=2; w < wordCount; ++w) - hash += w * hashType(typePos(spv[typeStart+w])); + hash += w * hashType(idPos(spv[typeStart+w])); return hash; } @@ -1122,14 +1334,14 @@ namespace spv { case spv::OpConstantFalse: return 300008; case spv::OpConstantComposite: { - std::uint32_t hash = 300011 + hashType(typePos(spv[typeStart+1])); + std::uint32_t hash = 300011 + hashType(idPos(spv[typeStart+1])); for (unsigned w=3; w < wordCount; ++w) - hash += w * hashType(typePos(spv[typeStart+w])); + hash += w * hashType(idPos(spv[typeStart+w])); return hash; } case spv::OpConstant: { - std::uint32_t hash = 400011 + hashType(typePos(spv[typeStart+1])); + std::uint32_t hash = 400011 + hashType(idPos(spv[typeStart+1])); for (unsigned w=3; w < wordCount; ++w) hash += w * spv[typeStart+w]; return hash; @@ -1154,12 +1366,17 @@ namespace spv { const spv::Id resId = asTypeConstId(typeStart); const std::uint32_t hashval = hashType(typeStart); - if (isOldIdUnmapped(resId)) + if (errorLatch) + return; + + if (isOldIdUnmapped(resId)) { localId(resId, nextUnusedId(hashval % softTypeIdLimit + firstMappedID)); + if (errorLatch) + return; + } } } - // Strip a single binary by removing ranges given in stripRange void spirvbin_t::strip() { @@ -1175,7 +1392,7 @@ namespace spv { int strippedPos = 0; for (unsigned word = 0; word < unsigned(spv.size()); ++word) { - if (strip_it != stripRange.end() && word >= strip_it->second) + while (strip_it != stripRange.end() && word >= strip_it->second) ++strip_it; if (strip_it == stripRange.end() || word < strip_it->first || word >= strip_it->second) @@ -1196,25 +1413,56 @@ namespace spv { // Set up opcode tables from SpvDoc spv::Parameterize(); - validate(); // validate header - buildLocalMaps(); + validate(); // validate header + buildLocalMaps(); // build ID maps msg(3, 4, std::string("ID bound: ") + std::to_string(bound())); + if (options & STRIP) stripDebug(); + if (errorLatch) return; + strip(); // strip out data we decided to eliminate + if (errorLatch) return; if (options & OPT_LOADSTORE) optLoadStore(); - if (options & OPT_FWD_LS) forwardLoadStores(); - if (options & DCE_FUNCS) dceFuncs(); - if (options & DCE_VARS) dceVars(); - if (options & DCE_TYPES) dceTypes(); - if (options & MAP_TYPES) mapTypeConst(); - if (options & MAP_NAMES) mapNames(); - if (options & MAP_FUNCS) mapFnBodies(); + if (errorLatch) return; - mapRemainder(); // map any unmapped IDs - applyMap(); // Now remap each shader to the new IDs we've come up with - strip(); // strip out data we decided to eliminate + if (options & OPT_FWD_LS) forwardLoadStores(); + if (errorLatch) return; + + if (options & DCE_FUNCS) dceFuncs(); + if (errorLatch) return; + + if (options & DCE_VARS) dceVars(); + if (errorLatch) return; + + if (options & DCE_TYPES) dceTypes(); + if (errorLatch) return; + + strip(); // strip out data we decided to eliminate + if (errorLatch) return; + + stripDeadRefs(); // remove references to things we DCEed + if (errorLatch) return; + + // after the last strip, we must clean any debug info referring to now-deleted data + + if (options & MAP_TYPES) mapTypeConst(); + if (errorLatch) return; + + if (options & MAP_NAMES) mapNames(); + if (errorLatch) return; + + if (options & MAP_FUNCS) mapFnBodies(); + if (errorLatch) return; + + if (options & MAP_ALL) { + mapRemainder(); // map any unmapped IDs + if (errorLatch) return; + + applyMap(); // Now remap each shader to the new IDs we've come up with + if (errorLatch) return; + } } // remap from a memory image diff --git a/third_party/glslang-spirv/SPVRemapper.h b/third_party/glslang-spirv/SPVRemapper.h index e5e8e1bde..97e3f31fa 100644 --- a/third_party/glslang-spirv/SPVRemapper.h +++ b/third_party/glslang-spirv/SPVRemapper.h @@ -1,11 +1,11 @@ // -//Copyright (C) 2015 LunarG, Inc. +// Copyright (C) 2015 LunarG, Inc. // -//All rights reserved. +// All rights reserved. // -//Redistribution and use in source and binary forms, with or without -//modification, are permitted provided that the following conditions -//are met: +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: // // Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. @@ -19,18 +19,18 @@ // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // -//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -//"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -//LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -//FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -//COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -//INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -//BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -//LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -//CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -//ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -//POSSIBILITY OF SUCH DAMAGE. +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. // #ifndef SPIRVREMAPPER_H @@ -38,7 +38,8 @@ #include #include -#include +#include +#include namespace spv { @@ -74,7 +75,8 @@ public: } // namespace SPV #if !defined (use_cpp11) -#include +#include +#include namespace spv { class spirvbin_t : public spirvbin_base_t @@ -82,7 +84,7 @@ class spirvbin_t : public spirvbin_base_t public: spirvbin_t(int /*verbose = 0*/) { } - void remap(std::vector& /*spv*/, unsigned int /*opts = 0*/) + void remap(std::vector& /*spv*/, unsigned int /*opts = 0*/) { printf("Tool not compiled for C++11, which is required for SPIR-V remapping.\n"); exit(5); @@ -110,8 +112,11 @@ namespace spv { class spirvbin_t : public spirvbin_base_t { public: - spirvbin_t(int verbose = 0) : entryPoint(spv::NoResult), largestNewId(0), verbose(verbose) { } - + spirvbin_t(int verbose = 0) : entryPoint(spv::NoResult), largestNewId(0), verbose(verbose), errorLatch(false) + { } + + virtual ~spirvbin_t() { } + // remap on an existing binary in memory void remap(std::vector& spv, std::uint32_t opts = DO_EVERYTHING); @@ -159,17 +164,22 @@ private: typedef std::set posmap_t; typedef std::unordered_map posmap_rev_t; - // handle error - void error(const std::string& txt) const { errorHandler(txt); } + // Maps and ID to the size of its base type, if known. + typedef std::unordered_map typesize_map_t; + + // handle error + void error(const std::string& txt) const { errorLatch = true; errorHandler(txt); } + + bool isConstOp(spv::Op opCode) const; + bool isTypeOp(spv::Op opCode) const; + bool isStripOp(spv::Op opCode) const; + bool isFlowCtrl(spv::Op opCode) const; + range_t literalRange(spv::Op opCode) const; + range_t typeRange(spv::Op opCode) const; + range_t constRange(spv::Op opCode) const; + unsigned typeSizeInWords(spv::Id id) const; + unsigned idTypeSizeInWords(spv::Id id) const; - bool isConstOp(spv::Op opCode) const; - bool isTypeOp(spv::Op opCode) const; - bool isStripOp(spv::Op opCode) const; - bool isFlowCtrl(spv::Op opCode) const; - range_t literalRange(spv::Op opCode) const; - range_t typeRange(spv::Op opCode) const; - range_t constRange(spv::Op opCode) const; - spv::Id& asId(unsigned word) { return spv[word]; } const spv::Id& asId(unsigned word) const { return spv[word]; } spv::Op asOpCode(unsigned word) const { return opOpCode(spv[word]); } @@ -177,10 +187,10 @@ private: spv::Decoration asDecoration(unsigned word) const { return spv::Decoration(spv[word]); } unsigned asWordCount(unsigned word) const { return opWordCount(spv[word]); } spv::Id asTypeConstId(unsigned word) const { return asId(word + (isTypeOp(asOpCode(word)) ? 1 : 2)); } - unsigned typePos(spv::Id id) const; + unsigned idPos(spv::Id id) const; - static unsigned opWordCount(spirword_t data) { return data >> spv::WordCountShift; } - static spv::Op opOpCode(spirword_t data) { return spv::Op(data & spv::OpCodeMask); } + static unsigned opWordCount(spirword_t data) { return data >> spv::WordCountShift; } + static spv::Op opOpCode(spirword_t data) { return spv::Op(data & spv::OpCodeMask); } // Header access & set methods spirword_t magic() const { return spv[0]; } // return magic number @@ -233,9 +243,10 @@ private: void applyMap(); // remap per local name map void mapRemainder(); // map any IDs we haven't touched yet - void stripDebug(); // strip debug info + void stripDebug(); // strip all debug info + void stripDeadRefs(); // strips debug info for now-dead references after DCE void strip(); // remove debug symbols - + std::vector spv; // SPIR words namemap_t nameMap; // ID names from OpName @@ -258,14 +269,14 @@ private: // Function start and end. use unordered_map because we'll have // many fewer functions than IDs. std::unordered_map fnPos; - std::unordered_map fnPosDCE; // deleted functions // Which functions are called, anywhere in the module, with a call count std::unordered_map fnCalls; - - posmap_t typeConstPos; // word positions that define types & consts (ordered) - posmap_rev_t typeConstPosR; // reverse map from IDs to positions - + + posmap_t typeConstPos; // word positions that define types & consts (ordered) + posmap_rev_t idPosR; // reverse map from IDs to positions + typesize_map_t idTypeSizeMap; // maps each ID to its type size, if known. + std::vector idMapL; // ID {M}ap from {L}ocal to {G}lobal IDs spv::Id entryPoint; // module entry point @@ -278,6 +289,11 @@ private: std::uint32_t options; int verbose; // verbosity level + // Error latch: this is set if the error handler is ever executed. It would be better to + // use a try/catch block and throw, but that's not desired for certain environments, so + // this is the alternative. + mutable bool errorLatch; + static errorfn_t errorHandler; static logfn_t logHandler; }; diff --git a/third_party/glslang-spirv/SpvBuilder.cpp b/third_party/glslang-spirv/SpvBuilder.cpp index ba8ab9e95..0afcc6433 100644 --- a/third_party/glslang-spirv/SpvBuilder.cpp +++ b/third_party/glslang-spirv/SpvBuilder.cpp @@ -1,12 +1,12 @@ // -//Copyright (C) 2014-2015 LunarG, Inc. -//Copyright (C) 2015-2016 Google, Inc. +// Copyright (C) 2014-2015 LunarG, Inc. +// Copyright (C) 2015-2016 Google, Inc. // -//All rights reserved. +// All rights reserved. // -//Redistribution and use in source and binary forms, with or without -//modification, are permitted provided that the following conditions -//are met: +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: // // Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. @@ -20,51 +20,57 @@ // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // -//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -//"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -//LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -//FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -//COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -//INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -//BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -//LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -//CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -//ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -//POSSIBILITY OF SUCH DAMAGE. - -// -// Author: John Kessenich, LunarG -// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. // // Helper for making SPIR-V IR. Generally, this is documented in the header // SpvBuilder.h. // -#include -#include -#include +#include +#include #include +#include #include "SpvBuilder.h" +#ifdef AMD_EXTENSIONS + #include "hex_float.h" +#endif + #ifndef _WIN32 #include #endif namespace spv { -Builder::Builder(unsigned int magicNumber) : +Builder::Builder(unsigned int spvVersion, unsigned int magicNumber, SpvBuildLogger* buildLogger) : + spvVersion(spvVersion), source(SourceLanguageUnknown), sourceVersion(0), + sourceFileStringId(NoResult), + currentLine(0), + emitOpLines(false), addressModel(AddressingModelLogical), memoryModel(MemoryModelGLSL450), builderNumber(magicNumber), buildPoint(0), uniqueId(0), - mainFunction(0) + entryPointFunction(0), + generatingOpCodeForSpecConst(false), + logger(buildLogger) { clearAccessChain(); } @@ -82,6 +88,26 @@ Id Builder::import(const char* name) return import->getResultId(); } +// Emit an OpLine if we've been asked to emit OpLines and the line number +// has changed since the last time, and is a valid line number. +void Builder::setLine(int lineNum) +{ + if (lineNum != 0 && lineNum != currentLine) { + currentLine = lineNum; + if (emitOpLines) + addLine(sourceFileStringId, currentLine, 0); + } +} + +void Builder::addLine(Id fileName, int lineNum, int column) +{ + Instruction* line = new Instruction(OpLine); + line->addIdOperand(fileName); + line->addImmediateOperand(lineNum); + line->addImmediateOperand(column); + buildPoint->addInstruction(std::unique_ptr(line)); +} + // For creating new groupedTypes (will return old type if the requested one was already made). Id Builder::makeVoidType() { @@ -377,6 +403,8 @@ Id Builder::makeFunctionType(Id returnType, const std::vector& paramTypes) Id Builder::makeImageType(Id sampledType, Dim dim, bool depth, bool arrayed, bool ms, unsigned sampled, ImageFormat format) { + assert(sampled == 1 || sampled == 2); + // try to find it Instruction* type; for (int t = 0; t < (int)groupedTypes[OpTypeImage].size(); ++t) { @@ -408,27 +436,27 @@ Id Builder::makeImageType(Id sampledType, Dim dim, bool depth, bool arrayed, boo // deal with capabilities switch (dim) { case DimBuffer: - if (sampled) + if (sampled == 1) addCapability(CapabilitySampledBuffer); else addCapability(CapabilityImageBuffer); break; case Dim1D: - if (sampled) + if (sampled == 1) addCapability(CapabilitySampled1D); else addCapability(CapabilityImage1D); break; case DimCube: if (arrayed) { - if (sampled) + if (sampled == 1) addCapability(CapabilitySampledCubeArray); else addCapability(CapabilityImageCubeArray); } break; case DimRect: - if (sampled) + if (sampled == 1) addCapability(CapabilitySampledRect); else addCapability(CapabilityImageRect); @@ -441,10 +469,14 @@ Id Builder::makeImageType(Id sampledType, Dim dim, bool depth, bool arrayed, boo } if (ms) { - if (arrayed) - addCapability(CapabilityImageMSArray); - if (! sampled) - addCapability(CapabilityStorageImageMultisample); + if (sampled == 2) { + // Images used with subpass data are not storage + // images, so don't require the capability for them. + if (dim != Dim::DimSubpassData) + addCapability(CapabilityStorageImageMultisample); + if (arrayed) + addCapability(CapabilityImageMSArray); + } } return type->getResultId(); @@ -604,7 +636,7 @@ Id Builder::findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned valu return 0; } -// Version of findScalarConstant (see above) for scalars that take two operands (e.g. a 'double'). +// Version of findScalarConstant (see above) for scalars that take two operands (e.g. a 'double' or 'int64'). Id Builder::findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned v1, unsigned v2) const { Instruction* constant; @@ -644,6 +676,21 @@ bool Builder::isConstantOpCode(Op opcode) const } } +// Return true if consuming 'opcode' means consuming a specialization constant. +bool Builder::isSpecConstantOpCode(Op opcode) const +{ + switch (opcode) { + case OpSpecConstantTrue: + case OpSpecConstantFalse: + case OpSpecConstant: + case OpSpecConstantComposite: + case OpSpecConstantOp: + return true; + default: + return false; + } +} + Id Builder::makeBoolConstant(bool b, bool specConstant) { Id typeId = makeBoolType(); @@ -694,11 +741,38 @@ Id Builder::makeIntConstant(Id typeId, unsigned value, bool specConstant) return c->getResultId(); } +Id Builder::makeInt64Constant(Id typeId, unsigned long long value, bool specConstant) +{ + Op opcode = specConstant ? OpSpecConstant : OpConstant; + + unsigned op1 = value & 0xFFFFFFFF; + unsigned op2 = value >> 32; + + // See if we already made it. Applies only to regular constants, because specialization constants + // must remain distinct for the purpose of applying a SpecId decoration. + if (! specConstant) { + Id existing = findScalarConstant(OpTypeInt, opcode, typeId, op1, op2); + if (existing) + return existing; + } + + Instruction* c = new Instruction(getUniqueId(), typeId, opcode); + c->addImmediateOperand(op1); + c->addImmediateOperand(op2); + constantsTypesGlobals.push_back(std::unique_ptr(c)); + groupedConstants[OpTypeInt].push_back(c); + module.mapInstruction(c); + + return c->getResultId(); +} + Id Builder::makeFloatConstant(float f, bool specConstant) { Op opcode = specConstant ? OpSpecConstant : OpConstant; Id typeId = makeFloatType(32); - unsigned value = *(unsigned int*)&f; + union { float fl; unsigned int ui; } u; + u.fl = f; + unsigned value = u.ui; // See if we already made it. Applies only to regular constants, because specialization constants // must remain distinct for the purpose of applying a SpecId decoration. @@ -721,7 +795,9 @@ Id Builder::makeDoubleConstant(double d, bool specConstant) { Op opcode = specConstant ? OpSpecConstant : OpConstant; Id typeId = makeFloatType(64); - unsigned long long value = *(unsigned long long*)&d; + union { double db; unsigned long long ull; } u; + u.db = d; + unsigned long long value = u.ull; unsigned op1 = value & 0xFFFFFFFF; unsigned op2 = value >> 32; @@ -743,6 +819,36 @@ Id Builder::makeDoubleConstant(double d, bool specConstant) return c->getResultId(); } +#ifdef AMD_EXTENSIONS +Id Builder::makeFloat16Constant(float f16, bool specConstant) +{ + Op opcode = specConstant ? OpSpecConstant : OpConstant; + Id typeId = makeFloatType(16); + + spvutils::HexFloat> fVal(f16); + spvutils::HexFloat> f16Val(0); + fVal.castTo(f16Val, spvutils::kRoundToZero); + + unsigned value = f16Val.value().getAsFloat().get_value(); + + // See if we already made it. Applies only to regular constants, because specialization constants + // must remain distinct for the purpose of applying a SpecId decoration. + if (!specConstant) { + Id existing = findScalarConstant(OpTypeFloat, opcode, typeId, value); + if (existing) + return existing; + } + + Instruction* c = new Instruction(getUniqueId(), typeId, opcode); + c->addImmediateOperand(value); + constantsTypesGlobals.push_back(std::unique_ptr(c)); + groupedConstants[OpTypeFloat].push_back(c); + module.mapInstruction(c); + + return c->getResultId(); +} +#endif + Id Builder::findCompositeConstant(Op typeClass, const std::vector& comps) const { Instruction* constant = 0; @@ -852,20 +958,9 @@ void Builder::addMemberName(Id id, int memberNumber, const char* string) names.push_back(std::unique_ptr(name)); } -void Builder::addLine(Id target, Id fileName, int lineNum, int column) -{ - Instruction* line = new Instruction(OpLine); - line->addIdOperand(target); - line->addIdOperand(fileName); - line->addImmediateOperand(lineNum); - line->addImmediateOperand(column); - - lines.push_back(std::unique_ptr(line)); -} - void Builder::addDecoration(Id id, Decoration decoration, int num) { - if (decoration == (spv::Decoration)spv::BadValue) + if (decoration == spv::DecorationMax) return; Instruction* dec = new Instruction(OpDecorate); dec->addIdOperand(id); @@ -889,22 +984,22 @@ void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decorat } // Comments in header -Function* Builder::makeMain() +Function* Builder::makeEntryPoint(const char* entryPoint) { - assert(! mainFunction); + assert(! entryPointFunction); Block* entry; std::vector params; - std::vector precisions; + std::vector> decorations; - mainFunction = makeFunctionEntry(NoPrecision, makeVoidType(), "main", params, precisions, &entry); + entryPointFunction = makeFunctionEntry(NoPrecision, makeVoidType(), entryPoint, params, decorations, &entry); - return mainFunction; + return entryPointFunction; } // Comments in header Function* Builder::makeFunctionEntry(Decoration precision, Id returnType, const char* name, - const std::vector& paramTypes, const std::vector& precisions, Block **entry) + const std::vector& paramTypes, const std::vector>& decorations, Block **entry) { // Make the function and initial instructions in it Id typeId = makeFunctionType(returnType, paramTypes); @@ -913,8 +1008,10 @@ Function* Builder::makeFunctionEntry(Decoration precision, Id returnType, const // Set up the precisions setPrecision(function->getId(), precision); - for (unsigned p = 0; p < (unsigned)precisions.size(); ++p) - setPrecision(firstParamId + p, precisions[p]); + for (unsigned p = 0; p < (unsigned)decorations.size(); ++p) { + for (int d = 0; d < (int)decorations[p].size(); ++d) + addDecoration(firstParamId + p, decorations[p][d]); + } // CFG if (entry) { @@ -1049,7 +1146,8 @@ Id Builder::createAccessChain(StorageClass storageClass, Id base, const std::vec Id Builder::createArrayLength(Id base, unsigned int member) { - Instruction* length = new Instruction(getUniqueId(), makeIntType(32), OpArrayLength); + spv::Id intType = makeIntType(32); + Instruction* length = new Instruction(getUniqueId(), intType, OpArrayLength); length->addIdOperand(base); length->addImmediateOperand(member); buildPoint->addInstruction(std::unique_ptr(length)); @@ -1059,6 +1157,11 @@ Id Builder::createArrayLength(Id base, unsigned int member) Id Builder::createCompositeExtract(Id composite, Id typeId, unsigned index) { + // Generate code for spec constants if in spec constant operation + // generation mode. + if (generatingOpCodeForSpecConst) { + return createSpecConstantOp(OpCompositeExtract, typeId, std::vector(1, composite), std::vector(1, index)); + } Instruction* extract = new Instruction(getUniqueId(), typeId, OpCompositeExtract); extract->addIdOperand(composite); extract->addImmediateOperand(index); @@ -1067,8 +1170,13 @@ Id Builder::createCompositeExtract(Id composite, Id typeId, unsigned index) return extract->getResultId(); } -Id Builder::createCompositeExtract(Id composite, Id typeId, std::vector& indexes) +Id Builder::createCompositeExtract(Id composite, Id typeId, const std::vector& indexes) { + // Generate code for spec constants if in spec constant operation + // generation mode. + if (generatingOpCodeForSpecConst) { + return createSpecConstantOp(OpCompositeExtract, typeId, std::vector(1, composite), indexes); + } Instruction* extract = new Instruction(getUniqueId(), typeId, OpCompositeExtract); extract->addIdOperand(composite); for (int i = 0; i < (int)indexes.size(); ++i) @@ -1141,8 +1249,8 @@ void Builder::createNoResultOp(Op opCode, Id operand) void Builder::createNoResultOp(Op opCode, const std::vector& operands) { Instruction* op = new Instruction(opCode); - for (auto operand : operands) - op->addIdOperand(operand); + for (auto it = operands.cbegin(); it != operands.cend(); ++it) + op->addIdOperand(*it); buildPoint->addInstruction(std::unique_ptr(op)); } @@ -1166,7 +1274,11 @@ void Builder::createMemoryBarrier(unsigned executionScope, unsigned memorySemant // An opcode that has one operands, a result id, and a type Id Builder::createUnaryOp(Op opCode, Id typeId, Id operand) { - assert(operand != 0); + // Generate code for spec constants if in spec constant operation + // generation mode. + if (generatingOpCodeForSpecConst) { + return createSpecConstantOp(opCode, typeId, std::vector(1, operand), std::vector()); + } Instruction* op = new Instruction(getUniqueId(), typeId, opCode); op->addIdOperand(operand); buildPoint->addInstruction(std::unique_ptr(op)); @@ -1176,8 +1288,13 @@ Id Builder::createUnaryOp(Op opCode, Id typeId, Id operand) Id Builder::createBinOp(Op opCode, Id typeId, Id left, Id right) { - assert(left != 0); - assert(right != 0); + // Generate code for spec constants if in spec constant operation + // generation mode. + if (generatingOpCodeForSpecConst) { + std::vector operands(2); + operands[0] = left; operands[1] = right; + return createSpecConstantOp(opCode, typeId, operands, std::vector()); + } Instruction* op = new Instruction(getUniqueId(), typeId, opCode); op->addIdOperand(left); op->addIdOperand(right); @@ -1188,9 +1305,16 @@ Id Builder::createBinOp(Op opCode, Id typeId, Id left, Id right) Id Builder::createTriOp(Op opCode, Id typeId, Id op1, Id op2, Id op3) { - assert(op1 != 0); - assert(op2 != 0); - assert(op3 != 0); + // Generate code for spec constants if in spec constant operation + // generation mode. + if (generatingOpCodeForSpecConst) { + std::vector operands(3); + operands[0] = op1; + operands[1] = op2; + operands[2] = op3; + return createSpecConstantOp( + opCode, typeId, operands, std::vector()); + } Instruction* op = new Instruction(getUniqueId(), typeId, opCode); op->addIdOperand(op1); op->addIdOperand(op2); @@ -1203,13 +1327,27 @@ Id Builder::createTriOp(Op opCode, Id typeId, Id op1, Id op2, Id op3) Id Builder::createOp(Op opCode, Id typeId, const std::vector& operands) { Instruction* op = new Instruction(getUniqueId(), typeId, opCode); - for (auto operand : operands) - op->addIdOperand(operand); + for (auto it = operands.cbegin(); it != operands.cend(); ++it) + op->addIdOperand(*it); buildPoint->addInstruction(std::unique_ptr(op)); return op->getResultId(); } +Id Builder::createSpecConstantOp(Op opCode, Id typeId, const std::vector& operands, const std::vector& literals) +{ + Instruction* op = new Instruction(getUniqueId(), typeId, OpSpecConstantOp); + op->addImmediateOperand((unsigned) opCode); + for (auto it = operands.cbegin(); it != operands.cend(); ++it) + op->addIdOperand(*it); + for (auto it = literals.cbegin(); it != literals.cend(); ++it) + op->addImmediateOperand(*it); + module.mapInstruction(op); + constantsTypesGlobals.push_back(std::unique_ptr(op)); + + return op->getResultId(); +} + Id Builder::createFunctionCall(spv::Function* function, const std::vector& args) { Instruction* op = new Instruction(getUniqueId(), function->getReturnType(), OpFunctionCall); @@ -1227,6 +1365,11 @@ Id Builder::createRvalueSwizzle(Decoration precision, Id typeId, Id source, cons if (channels.size() == 1) return setPrecision(createCompositeExtract(source, typeId, channels.front()), precision); + if (generatingOpCodeForSpecConst) { + std::vector operands(2); + operands[0] = operands[1] = source; + return setPrecision(createSpecConstantOp(OpVectorShuffle, typeId, operands, channels), precision); + } Instruction* swizzle = new Instruction(getUniqueId(), typeId, OpVectorShuffle); assert(isVector(source)); swizzle->addIdOperand(source); @@ -1241,14 +1384,16 @@ Id Builder::createRvalueSwizzle(Decoration precision, Id typeId, Id source, cons // Comments in header Id Builder::createLvalueSwizzle(Id typeId, Id target, Id source, const std::vector& channels) { - assert(getNumComponents(source) == (int)channels.size()); if (channels.size() == 1 && getNumComponents(source) == 1) return createCompositeInsert(source, target, typeId, channels.front()); Instruction* swizzle = new Instruction(getUniqueId(), typeId, OpVectorShuffle); - assert(isVector(source)); + assert(isVector(target)); swizzle->addIdOperand(target); + + assert(getNumComponents(source) == (int)channels.size()); + assert(isVector(source)); swizzle->addIdOperand(source); // Set up an identity shuffle from the base value to the result value @@ -1292,16 +1437,31 @@ Id Builder::smearScalar(Decoration precision, Id scalar, Id vectorType) if (numComponents == 1) return scalar; - Instruction* smear = new Instruction(getUniqueId(), vectorType, OpCompositeConstruct); - for (int c = 0; c < numComponents; ++c) - smear->addIdOperand(scalar); - buildPoint->addInstruction(std::unique_ptr(smear)); + Instruction* smear = nullptr; + if (generatingOpCodeForSpecConst) { + auto members = std::vector(numComponents, scalar); + // Sometime even in spec-constant-op mode, the temporary vector created by + // promoting a scalar might not be a spec constant. This should depend on + // the scalar. + // e.g.: + // const vec2 spec_const_result = a_spec_const_vec2 + a_front_end_const_scalar; + // In such cases, the temporary vector created from a_front_end_const_scalar + // is not a spec constant vector, even though the binary operation node is marked + // as 'specConstant' and we are in spec-constant-op mode. + auto result_id = makeCompositeConstant(vectorType, members, isSpecConstant(scalar)); + smear = module.getInstruction(result_id); + } else { + smear = new Instruction(getUniqueId(), vectorType, OpCompositeConstruct); + for (int c = 0; c < numComponents; ++c) + smear->addIdOperand(scalar); + buildPoint->addInstruction(std::unique_ptr(smear)); + } return setPrecision(smear->getResultId(), precision); } // Comments in header -Id Builder::createBuiltinCall(Id resultType, Id builtins, int entryPoint, std::vector& args) +Id Builder::createBuiltinCall(Id resultType, Id builtins, int entryPoint, const std::vector& args) { Instruction* inst = new Instruction(getUniqueId(), resultType, OpExtInst); inst->addIdOperand(builtins); @@ -1328,10 +1488,10 @@ Id Builder::createTextureCall(Decoration precision, Id resultType, bool sparse, bool explicitLod = false; texArgs[numArgs++] = parameters.sampler; texArgs[numArgs++] = parameters.coords; - if (parameters.Dref) + if (parameters.Dref != NoResult) texArgs[numArgs++] = parameters.Dref; - if (parameters.comp) - texArgs[numArgs++] = parameters.comp; + if (parameters.component != NoResult) + texArgs[numArgs++] = parameters.component; // // Set up the optional arguments @@ -1362,8 +1522,10 @@ Id Builder::createTextureCall(Decoration precision, Id resultType, bool sparse, if (parameters.offset) { if (isConstant(parameters.offset)) mask = (ImageOperandsMask)(mask | ImageOperandsConstOffsetMask); - else + else { + addCapability(CapabilityImageGatherExtended); mask = (ImageOperandsMask)(mask | ImageOperandsOffsetMask); + } texArgs[numArgs++] = parameters.offset; } if (parameters.offsets) { @@ -1514,7 +1676,7 @@ Id Builder::createTextureCall(Decoration precision, Id resultType, bool sparse, } // Comments in header -Id Builder::createTextureQueryCall(Op opCode, const TextureParameters& parameters) +Id Builder::createTextureQueryCall(Op opCode, const TextureParameters& parameters, bool isUnsignedResult) { // All these need a capability addCapability(CapabilityImageQuery); @@ -1547,10 +1709,12 @@ Id Builder::createTextureQueryCall(Op opCode, const TextureParameters& parameter } if (isArrayedImageType(getImageType(parameters.sampler))) ++numComponents; + + Id intType = isUnsignedResult ? makeUintType(32) : makeIntType(32); if (numComponents == 1) - resultType = makeIntType(32); + resultType = intType; else - resultType = makeVectorType(makeIntType(32), numComponents); + resultType = makeVectorType(intType, numComponents); break; } @@ -1559,7 +1723,7 @@ Id Builder::createTextureQueryCall(Op opCode, const TextureParameters& parameter break; case OpImageQueryLevels: case OpImageQuerySamples: - resultType = makeIntType(32); + resultType = isUnsignedResult ? makeUintType(32) : makeIntType(32); break; default: assert(0); @@ -1651,6 +1815,20 @@ Id Builder::createCompositeConstruct(Id typeId, const std::vector& constitue { assert(isAggregateType(typeId) || (getNumTypeConstituents(typeId) > 1 && getNumTypeConstituents(typeId) == (int)constituents.size())); + if (generatingOpCodeForSpecConst) { + // Sometime, even in spec-constant-op mode, the constant composite to be + // constructed may not be a specialization constant. + // e.g.: + // const mat2 m2 = mat2(a_spec_const, a_front_end_const, another_front_end_const, third_front_end_const); + // The first column vector should be a spec constant one, as a_spec_const is a spec constant. + // The second column vector should NOT be spec constant, as it does not contain any spec constants. + // To handle such cases, we check the constituents of the constant vector to determine whether this + // vector should be created as a spec constant. + return makeCompositeConstant(typeId, constituents, + std::any_of(constituents.begin(), constituents.end(), + [&](spv::Id id) { return isSpecConstant(id); })); + } + Instruction* op = new Instruction(getUniqueId(), typeId, OpCompositeConstruct); for (int c = 0; c < (int)constituents.size(); ++c) op->addIdOperand(constituents[c]); @@ -1671,34 +1849,72 @@ Id Builder::createConstructor(Decoration precision, const std::vector& sourc if (sources.size() == 1 && isScalar(sources[0]) && numTargetComponents > 1) return smearScalar(precision, sources[0], resultTypeId); + // accumulate the arguments for OpCompositeConstruct + std::vector constituents; Id scalarTypeId = getScalarTypeId(resultTypeId); - std::vector constituents; // accumulate the arguments for OpCompositeConstruct - for (unsigned int i = 0; i < sources.size(); ++i) { - assert(! isAggregate(sources[i])); - unsigned int sourceSize = getNumComponents(sources[i]); + + // lambda to store the result of visiting an argument component + const auto latchResult = [&](Id comp) { + if (numTargetComponents > 1) + constituents.push_back(comp); + else + result = comp; + ++targetComponent; + }; + + // lambda to visit a vector argument's components + const auto accumulateVectorConstituents = [&](Id sourceArg) { + unsigned int sourceSize = getNumComponents(sourceArg); unsigned int sourcesToUse = sourceSize; if (sourcesToUse + targetComponent > numTargetComponents) sourcesToUse = numTargetComponents - targetComponent; for (unsigned int s = 0; s < sourcesToUse; ++s) { - Id arg = sources[i]; - if (sourceSize > 1) { - std::vector swiz; - swiz.push_back(s); - arg = createRvalueSwizzle(precision, scalarTypeId, arg, swiz); - } - - if (numTargetComponents > 1) - constituents.push_back(arg); - else - result = arg; - ++targetComponent; + std::vector swiz; + swiz.push_back(s); + latchResult(createRvalueSwizzle(precision, scalarTypeId, sourceArg, swiz)); } + }; + + // lambda to visit a matrix argument's components + const auto accumulateMatrixConstituents = [&](Id sourceArg) { + unsigned int sourceSize = getNumColumns(sourceArg) * getNumRows(sourceArg); + unsigned int sourcesToUse = sourceSize; + if (sourcesToUse + targetComponent > numTargetComponents) + sourcesToUse = numTargetComponents - targetComponent; + + int col = 0; + int row = 0; + for (unsigned int s = 0; s < sourcesToUse; ++s) { + if (row >= getNumRows(sourceArg)) { + row = 0; + col++; + } + std::vector indexes; + indexes.push_back(col); + indexes.push_back(row); + latchResult(createCompositeExtract(sourceArg, scalarTypeId, indexes)); + row++; + } + }; + + // Go through the source arguments, each one could have either + // a single or multiple components to contribute. + for (unsigned int i = 0; i < sources.size(); ++i) { + if (isScalar(sources[i])) + latchResult(sources[i]); + else if (isVector(sources[i])) + accumulateVectorConstituents(sources[i]); + else if (isMatrix(sources[i])) + accumulateMatrixConstituents(sources[i]); + else + assert(0); if (targetComponent >= numTargetComponents) break; } + // If the result is a vector, make it from the gathered constituents. if (constituents.size() > 0) result = createCompositeConstruct(resultTypeId, constituents); @@ -1712,6 +1928,9 @@ Id Builder::createMatrixConstructor(Decoration precision, const std::vector& int numCols = getTypeNumColumns(resultTypeId); int numRows = getTypeNumRows(resultTypeId); + Instruction* instr = module.getInstruction(componentTypeId); + Id bitCount = instr->getIdOperand(0); + // Will use a two step process // 1. make a compile-time 2D array of values // 2. construct a matrix from that array @@ -1720,8 +1939,8 @@ Id Builder::createMatrixConstructor(Decoration precision, const std::vector& // initialize the array to the identity matrix Id ids[maxMatrixSize][maxMatrixSize]; - Id one = makeFloatConstant(1.0); - Id zero = makeFloatConstant(0.0); + Id one = (bitCount == 64 ? makeDoubleConstant(1.0) : makeFloatConstant(1.0)); + Id zero = (bitCount == 64 ? makeDoubleConstant(0.0) : makeFloatConstant(0.0)); for (int col = 0; col < 4; ++col) { for (int row = 0; row < 4; ++row) { if (col == row) @@ -1772,7 +1991,6 @@ Id Builder::createMatrixConstructor(Decoration precision, const std::vector& } } - // Step 2: Construct a matrix from that array. // First make the column vectors, then make the matrix. @@ -1793,9 +2011,10 @@ Id Builder::createMatrixConstructor(Decoration precision, const std::vector& } // Comments in header -Builder::If::If(Id cond, Builder& gb) : +Builder::If::If(Id cond, unsigned int ctrl, Builder& gb) : builder(gb), condition(cond), + control(ctrl), elseBlock(0) { function = &builder.getBuildPoint()->getParent(); @@ -1836,7 +2055,7 @@ void Builder::If::makeEndIf() // Go back to the headerBlock and make the flow control split builder.setBuildPoint(headerBlock); - builder.createSelectionMerge(mergeBlock, SelectionControlMaskNone); + builder.createSelectionMerge(mergeBlock, control); if (elseBlock) builder.createConditionalBranch(condition, thenBlock, elseBlock); else @@ -1848,7 +2067,8 @@ void Builder::If::makeEndIf() } // Comments in header -void Builder::makeSwitch(Id selector, int numSegments, const std::vector& caseValues, const std::vector& valueIndexToSegment, int defaultSegment, +void Builder::makeSwitch(Id selector, unsigned int control, int numSegments, const std::vector& caseValues, + const std::vector& valueIndexToSegment, int defaultSegment, std::vector& segmentBlocks) { Function& function = buildPoint->getParent(); @@ -1860,7 +2080,7 @@ void Builder::makeSwitch(Id selector, int numSegments, const std::vector& c Block* mergeBlock = new Block(getUniqueId(), function); // make and insert the switch's selection-merge instruction - createSelectionMerge(mergeBlock, SelectionControlMaskNone); + createSelectionMerge(mergeBlock, control); // make the switch instruction Instruction* switchInst = new Instruction(NoResult, NoType, OpSwitch); @@ -1924,8 +2144,15 @@ Block& Builder::makeNewBlock() Builder::LoopBlocks& Builder::makeNewLoop() { - // Older MSVC versions don't allow inlining of blocks below. - LoopBlocks blocks = {makeNewBlock(), makeNewBlock(), makeNewBlock(), makeNewBlock()}; + // This verbosity is needed to simultaneously get the same behavior + // everywhere (id's in the same order), have a syntax that works + // across lots of versions of C++, have no warnings from pedantic + // compilation modes, and leave the rest of the code alone. + Block& head = makeNewBlock(); + Block& body = makeNewBlock(); + Block& merge = makeNewBlock(); + Block& continue_target = makeNewBlock(); + LoopBlocks blocks(head, body, merge, continue_target); loops.push(blocks); return loops.top(); } @@ -1969,10 +2196,11 @@ void Builder::accessChainPushSwizzle(std::vector& swizzle, Id preSwizz accessChain.preSwizzleBaseType = preSwizzleBaseType; // if needed, propagate the swizzle for the current access chain - if (accessChain.swizzle.size()) { + if (accessChain.swizzle.size() > 0) { std::vector oldSwizzle = accessChain.swizzle; accessChain.swizzle.resize(0); for (unsigned int i = 0; i < swizzle.size(); ++i) { + assert(swizzle[i] < oldSwizzle.size()); accessChain.swizzle.push_back(oldSwizzle[swizzle[i]]); } } else @@ -1989,27 +2217,18 @@ void Builder::accessChainStore(Id rvalue) transferAccessChainSwizzle(true); Id base = collapseAccessChain(); + Id source = rvalue; - if (accessChain.swizzle.size() && accessChain.component != NoResult) - MissingFunctionality("simultaneous l-value swizzle and dynamic component selection"); + // dynamic component should be gone + assert(accessChain.component == NoResult); // If swizzle still exists, it is out-of-order or not full, we must load the target vector, // extract and insert elements to perform writeMask and/or swizzle. - Id source = NoResult; - if (accessChain.swizzle.size()) { + if (accessChain.swizzle.size() > 0) { Id tempBaseId = createLoad(base); - source = createLvalueSwizzle(getTypeId(tempBaseId), tempBaseId, rvalue, accessChain.swizzle); + source = createLvalueSwizzle(getTypeId(tempBaseId), tempBaseId, source, accessChain.swizzle); } - // dynamic component selection - if (accessChain.component != NoResult) { - Id tempBaseId = (source == NoResult) ? createLoad(base) : source; - source = createVectorInsertDynamic(tempBaseId, getTypeId(tempBaseId), rvalue, accessChain.component); - } - - if (source == NoResult) - source = rvalue; - createStore(source, base); } @@ -2019,7 +2238,7 @@ Id Builder::accessChainLoad(Decoration precision, Id resultType) Id id; if (accessChain.isRValue) { - // transfer access chain, but keep it static, so we can stay in registers + // transfer access chain, but try to stay in registers transferAccessChainSwizzle(false); if (accessChain.indexChain.size() > 0) { Id swizzleBase = accessChain.preSwizzleBaseType != NoType ? accessChain.preSwizzleBaseType : resultType; @@ -2067,16 +2286,16 @@ Id Builder::accessChainLoad(Decoration precision, Id resultType) return id; // Do remaining swizzling - // First, static swizzling - if (accessChain.swizzle.size()) { - // static swizzle + + // Do the basic swizzle + if (accessChain.swizzle.size() > 0) { Id swizzledType = getScalarTypeId(getTypeId(id)); if (accessChain.swizzle.size() > 1) swizzledType = makeVectorType(swizzledType, (int)accessChain.swizzle.size()); id = createRvalueSwizzle(precision, swizzledType, id, accessChain.swizzle); } - // dynamic single-component selection + // Do the dynamic component if (accessChain.component != NoResult) id = setPrecision(createVectorExtractDynamic(id, resultType, accessChain.component), precision); @@ -2112,9 +2331,9 @@ Id Builder::accessChainGetInferredType() type = getContainedTypeId(type); // dereference each index - for (auto deref : accessChain.indexChain) { + for (auto it = accessChain.indexChain.cbegin(); it != accessChain.indexChain.cend(); ++it) { if (isStructType(type)) - type = getContainedTypeId(type, getConstantScalar(deref)); + type = getContainedTypeId(type, getConstantScalar(*it)); else type = getContainedTypeId(type); } @@ -2123,8 +2342,7 @@ Id Builder::accessChainGetInferredType() if (accessChain.swizzle.size() == 1) type = getContainedTypeId(type); else if (accessChain.swizzle.size() > 1) - type = makeVectorType(getContainedTypeId(type), - static_cast(accessChain.swizzle.size())); + type = makeVectorType(getContainedTypeId(type), (int)accessChain.swizzle.size()); // dereference component selection if (accessChain.component) @@ -2133,23 +2351,63 @@ Id Builder::accessChainGetInferredType() return type; } +// comment in header +void Builder::eliminateDeadDecorations() { + std::unordered_set reachable_blocks; + std::unordered_set unreachable_definitions; + // Collect IDs defined in unreachable blocks. For each function, label the + // reachable blocks first. Then for each unreachable block, collect the + // result IDs of the instructions in it. + for (std::vector::const_iterator fi = module.getFunctions().cbegin(); + fi != module.getFunctions().cend(); fi++) { + Function* f = *fi; + Block* entry = f->getEntryBlock(); + inReadableOrder(entry, [&reachable_blocks](const Block* b) { + reachable_blocks.insert(b); + }); + for (std::vector::const_iterator bi = f->getBlocks().cbegin(); + bi != f->getBlocks().cend(); bi++) { + Block* b = *bi; + if (!reachable_blocks.count(b)) { + for (std::vector >::const_iterator + ii = b->getInstructions().cbegin(); + ii != b->getInstructions().cend(); ii++) { + Instruction* i = ii->get(); + unreachable_definitions.insert(i->getResultId()); + } + } + } + } + decorations.erase(std::remove_if(decorations.begin(), decorations.end(), + [&unreachable_definitions](std::unique_ptr& I) -> bool { + Instruction* inst = I.get(); + Id decoration_id = inst->getIdOperand(0); + return unreachable_definitions.count(decoration_id) != 0; + }), + decorations.end()); +} + void Builder::dump(std::vector& out) const { // Header, before first instructions: out.push_back(MagicNumber); - out.push_back(Version); + out.push_back(spvVersion); out.push_back(builderNumber); out.push_back(uniqueId + 1); out.push_back(0); // Capabilities - for (auto cap : capabilities) { + for (auto it = capabilities.cbegin(); it != capabilities.cend(); ++it) { Instruction capInst(0, 0, OpCapability); - capInst.addImmediateOperand(cap); + capInst.addImmediateOperand(*it); capInst.dump(out); } - // TBD: OpExtension ... + for (auto it = extensions.cbegin(); it != extensions.cend(); ++it) { + Instruction extInst(0, 0, OpExtension); + extInst.addStringOperand(it->c_str()); + extInst.dump(out); + } dumpInstructions(out, imports); Instruction memInst(0, 0, OpMemoryModel); @@ -2162,16 +2420,13 @@ void Builder::dump(std::vector& out) const dumpInstructions(out, executionModes); // Debug instructions - if (source != SourceLanguageUnknown) { - Instruction sourceInst(0, 0, OpSource); - sourceInst.addImmediateOperand(source); - sourceInst.addImmediateOperand(sourceVersion); - sourceInst.dump(out); - } - for (int e = 0; e < (int)extensions.size(); ++e) { - Instruction extInst(0, 0, OpSourceExtension); - extInst.addStringOperand(extensions[e]); - extInst.dump(out); + dumpInstructions(out, strings); + dumpModuleProcesses(out); + dumpSourceInstructions(out); + for (int e = 0; e < (int)sourceExtensions.size(); ++e) { + Instruction sourceExtInst(0, 0, OpSourceExtension); + sourceExtInst.addStringOperand(sourceExtensions[e]); + sourceExtInst.dump(out); } dumpInstructions(out, names); dumpInstructions(out, lines); @@ -2190,26 +2445,66 @@ void Builder::dump(std::vector& out) const // Protected methods. // -// Turn the described access chain in 'accessChain' into an instruction +// Turn the described access chain in 'accessChain' into an instruction(s) // computing its address. This *cannot* include complex swizzles, which must -// be handled after this is called, but it does include swizzles that select -// an individual element, as a single address of a scalar type can be -// computed by an OpAccessChain instruction. +// be handled after this is called. +// +// Can generate code. Id Builder::collapseAccessChain() { assert(accessChain.isRValue == false); - if (accessChain.indexChain.size() > 0) { - if (accessChain.instr == 0) { - StorageClass storageClass = (StorageClass)module.getStorageClass(getTypeId(accessChain.base)); - accessChain.instr = createAccessChain(storageClass, accessChain.base, accessChain.indexChain); - } - + // did we already emit an access chain for this? + if (accessChain.instr != NoResult) return accessChain.instr; - } else + + // If we have a dynamic component, we can still transfer + // that into a final operand to the access chain. We need to remap the + // dynamic component through the swizzle to get a new dynamic component to + // update. + // + // This was not done in transferAccessChainSwizzle() because it might + // generate code. + remapDynamicSwizzle(); + if (accessChain.component != NoResult) { + // transfer the dynamic component to the access chain + accessChain.indexChain.push_back(accessChain.component); + accessChain.component = NoResult; + } + + // note that non-trivial swizzling is left pending + + // do we have an access chain? + if (accessChain.indexChain.size() == 0) return accessChain.base; - // note that non-trivial swizzling is left pending... + // emit the access chain + StorageClass storageClass = (StorageClass)module.getStorageClass(getTypeId(accessChain.base)); + accessChain.instr = createAccessChain(storageClass, accessChain.base, accessChain.indexChain); + + return accessChain.instr; +} + +// For a dynamic component selection of a swizzle. +// +// Turn the swizzle and dynamic component into just a dynamic component. +// +// Generates code. +void Builder::remapDynamicSwizzle() +{ + // do we have a swizzle to remap a dynamic component through? + if (accessChain.component != NoResult && accessChain.swizzle.size() > 1) { + // build a vector of the swizzle for the component to map into + std::vector components; + for (int c = 0; c < accessChain.swizzle.size(); ++c) + components.push_back(makeUintConstant(accessChain.swizzle[c])); + Id mapType = makeVectorType(makeUintType(32), (int)accessChain.swizzle.size()); + Id map = makeCompositeConstant(mapType, components); + + // use it + accessChain.component = createVectorExtractDynamic(map, makeUintType(32), accessChain.component); + accessChain.swizzle.clear(); + } } // clear out swizzle if it is redundant, that is reselecting the same components @@ -2235,38 +2530,30 @@ void Builder::simplifyAccessChainSwizzle() // To the extent any swizzling can become part of the chain // of accesses instead of a post operation, make it so. -// If 'dynamic' is true, include transfering a non-static component index, -// otherwise, only transfer static indexes. +// If 'dynamic' is true, include transferring the dynamic component, +// otherwise, leave it pending. // -// Also, Boolean vectors are likely to be special. While -// for external storage, they should only be integer types, -// function-local bool vectors could use sub-word indexing, -// so keep that as a separate Insert/Extract on a loaded vector. +// Does not generate code. just updates the access chain. void Builder::transferAccessChainSwizzle(bool dynamic) { - // too complex? - if (accessChain.swizzle.size() > 1) - return; - // non existent? if (accessChain.swizzle.size() == 0 && accessChain.component == NoResult) return; - // single component... - - // skip doing it for Boolean vectors - if (isBoolType(getContainedTypeId(accessChain.preSwizzleBaseType))) + // too complex? + // (this requires either a swizzle, or generating code for a dynamic component) + if (accessChain.swizzle.size() > 1) return; + // single component, either in the swizzle and/or dynamic component if (accessChain.swizzle.size() == 1) { - // handle static component + assert(accessChain.component == NoResult); + // handle static component selection accessChain.indexChain.push_back(makeUintConstant(accessChain.swizzle.front())); accessChain.swizzle.clear(); - // note, the only valid remaining dynamic access would be to this one - // component, so don't bother even looking at accessChain.component accessChain.preSwizzleBaseType = NoType; - accessChain.component = NoResult; } else if (dynamic && accessChain.component != NoResult) { + assert(accessChain.swizzle.size() == 0); // handle dynamic component accessChain.indexChain.push_back(accessChain.component); accessChain.preSwizzleBaseType = NoType; @@ -2284,7 +2571,7 @@ void Builder::createAndSetNoPredecessorBlock(const char* /*name*/) buildPoint->getParent().addBlock(block); setBuildPoint(block); - //if (name) + // if (name) // addName(block->getId(), name); } @@ -2305,12 +2592,15 @@ void Builder::createSelectionMerge(Block* mergeBlock, unsigned int control) buildPoint->addInstruction(std::unique_ptr(merge)); } -void Builder::createLoopMerge(Block* mergeBlock, Block* continueBlock, unsigned int control) +void Builder::createLoopMerge(Block* mergeBlock, Block* continueBlock, unsigned int control, + unsigned int dependencyLength) { Instruction* merge = new Instruction(OpLoopMerge); merge->addIdOperand(mergeBlock->getId()); merge->addIdOperand(continueBlock->getId()); merge->addImmediateOperand(control); + if ((control & LoopControlDependencyLengthMask) != 0) + merge->addImmediateOperand(dependencyLength); buildPoint->addInstruction(std::unique_ptr(merge)); } @@ -2325,6 +2615,48 @@ void Builder::createConditionalBranch(Id condition, Block* thenBlock, Block* els elseBlock->addPredecessor(buildPoint); } +// OpSource +// [OpSourceContinued] +// ... +void Builder::dumpSourceInstructions(std::vector& out) const +{ + const int maxWordCount = 0xFFFF; + const int opSourceWordCount = 4; + const int nonNullBytesPerInstruction = 4 * (maxWordCount - opSourceWordCount) - 1; + + if (source != SourceLanguageUnknown) { + // OpSource Language Version File Source + Instruction sourceInst(NoResult, NoType, OpSource); + sourceInst.addImmediateOperand(source); + sourceInst.addImmediateOperand(sourceVersion); + // File operand + if (sourceFileStringId != NoResult) { + sourceInst.addIdOperand(sourceFileStringId); + // Source operand + if (sourceText.size() > 0) { + int nextByte = 0; + std::string subString; + while ((int)sourceText.size() - nextByte > 0) { + subString = sourceText.substr(nextByte, nonNullBytesPerInstruction); + if (nextByte == 0) { + // OpSource + sourceInst.addStringOperand(subString.c_str()); + sourceInst.dump(out); + } else { + // OpSourcContinued + Instruction sourceContinuedInst(OpSourceContinued); + sourceContinuedInst.addStringOperand(subString.c_str()); + sourceContinuedInst.dump(out); + } + nextByte += nonNullBytesPerInstruction; + } + } else + sourceInst.dump(out); + } else + sourceInst.dump(out); + } +} + void Builder::dumpInstructions(std::vector& out, const std::vector >& instructions) const { for (int i = 0; i < (int)instructions.size(); ++i) { @@ -2332,19 +2664,13 @@ void Builder::dumpInstructions(std::vector& out, const std::vector } } -void TbdFunctionality(const char* tbd) +void Builder::dumpModuleProcesses(std::vector& out) const { - static std::unordered_set issued; - - if (issued.find(tbd) == issued.end()) { - printf("TBD functionality: %s\n", tbd); - issued.insert(tbd); + for (int i = 0; i < (int)moduleProcesses.size(); ++i) { + Instruction moduleProcessed(OpModuleProcessed); + moduleProcessed.addStringOperand(moduleProcesses[i]); + moduleProcessed.dump(out); } } -void MissingFunctionality(const char* fun) -{ - printf("Missing functionality: %s\n", fun); -} - }; // end spv namespace diff --git a/third_party/glslang-spirv/SpvBuilder.h b/third_party/glslang-spirv/SpvBuilder.h index c3c316de0..173d10ef2 100644 --- a/third_party/glslang-spirv/SpvBuilder.h +++ b/third_party/glslang-spirv/SpvBuilder.h @@ -1,12 +1,12 @@ // -//Copyright (C) 2014-2015 LunarG, Inc. -//Copyright (C) 2015-2016 Google, Inc. +// Copyright (C) 2014-2015 LunarG, Inc. +// Copyright (C) 2015-2016 Google, Inc. // -//All rights reserved. +// All rights reserved. // -//Redistribution and use in source and binary forms, with or without -//modification, are permitted provided that the following conditions -//are met: +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: // // Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. @@ -20,22 +20,18 @@ // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // -//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -//"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -//LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -//FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -//COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -//INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -//BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -//LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -//CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -//ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -//POSSIBILITY OF SUCH DAMAGE. - -// -// Author: John Kessenich, LunarG -// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. // // "Builder" is an interface to fully build SPIR-V IR. Allocate one of @@ -49,20 +45,22 @@ #ifndef SpvBuilder_H #define SpvBuilder_H +#include "Logger.h" #include "spirv.hpp" #include "spvIR.h" #include -#include -#include #include +#include #include +#include +#include namespace spv { class Builder { public: - Builder(unsigned int userNumber); + Builder(unsigned int spvVersion, unsigned int userNumber, SpvBuildLogger* logger); virtual ~Builder(); static const int maxMatrixSize = 4; @@ -72,7 +70,18 @@ public: source = lang; sourceVersion = version; } - void addSourceExtension(const char* ext) { extensions.push_back(ext); } + void setSourceFile(const std::string& file) + { + Instruction* fileString = new Instruction(getUniqueId(), NoType, OpString); + fileString->addStringOperand(file.c_str()); + sourceFileStringId = fileString->getResultId(); + strings.push_back(std::unique_ptr(fileString)); + } + void setSourceText(const std::string& text) { sourceText = text; } + void addSourceExtension(const char* ext) { sourceExtensions.push_back(ext); } + void addModuleProcessed(const std::string& p) { moduleProcesses.push_back(p.c_str()); } + void setEmitOpLines() { emitOpLines = true; } + void addExtension(const char* ext) { extensions.insert(ext); } Id import(const char*); void setMemoryModel(spv::AddressingModel addr, spv::MemoryModel mem) { @@ -93,7 +102,11 @@ public: return id; } - Module* getModule() { return &module; } + // Log the current line, and if different than the last one, + // issue a new OpLine, using the current file name. + void setLine(int line); + // Low-level OpLine. See setLine() for a layered helper. + void addLine(Id fileName, int line, int column); // For creating new types (will return old type if the requested one was already made). Id makeVoidType(); @@ -137,6 +150,9 @@ public: bool isSampledImage(Id resultId) const { return isSampledImageType(getTypeId(resultId)); } bool isBoolType(Id typeId) const { return groupedTypes[OpTypeBool].size() > 0 && typeId == groupedTypes[OpTypeBool].back()->getResultId(); } + bool isIntType(Id typeId) const { return getTypeClass(typeId) == OpTypeInt && module.getInstruction(typeId)->getImmediateOperand(1) != 0; } + bool isUintType(Id typeId) const { return getTypeClass(typeId) == OpTypeInt && module.getInstruction(typeId)->getImmediateOperand(1) == 0; } + bool isFloatType(Id typeId) const { return getTypeClass(typeId) == OpTypeFloat; } bool isPointerType(Id typeId) const { return getTypeClass(typeId) == OpTypePointer; } bool isScalarType(Id typeId) const { return getTypeClass(typeId) == OpTypeFloat || getTypeClass(typeId) == OpTypeInt || getTypeClass(typeId) == OpTypeBool; } bool isVectorType(Id typeId) const { return getTypeClass(typeId) == OpTypeVector; } @@ -149,11 +165,20 @@ public: bool isSampledImageType(Id typeId) const { return getTypeClass(typeId) == OpTypeSampledImage; } bool isConstantOpCode(Op opcode) const; + bool isSpecConstantOpCode(Op opcode) const; bool isConstant(Id resultId) const { return isConstantOpCode(getOpCode(resultId)); } bool isConstantScalar(Id resultId) const { return getOpCode(resultId) == OpConstant; } + bool isSpecConstant(Id resultId) const { return isSpecConstantOpCode(getOpCode(resultId)); } unsigned int getConstantScalar(Id resultId) const { return module.getInstruction(resultId)->getImmediateOperand(0); } StorageClass getStorageClass(Id resultId) const { return getTypeStorageClass(getTypeId(resultId)); } + int getScalarTypeWidth(Id typeId) const + { + Id scalarTypeId = getScalarTypeId(typeId); + assert(getTypeClass(scalarTypeId) == OpTypeInt || getTypeClass(scalarTypeId) == OpTypeFloat); + return module.getInstruction(scalarTypeId)->getImmediateOperand(0); + } + int getTypeNumColumns(Id typeId) const { assert(isMatrixType(typeId)); @@ -188,8 +213,17 @@ public: Id makeBoolConstant(bool b, bool specConstant = false); Id makeIntConstant(int i, bool specConstant = false) { return makeIntConstant(makeIntType(32), (unsigned)i, specConstant); } Id makeUintConstant(unsigned u, bool specConstant = false) { return makeIntConstant(makeUintType(32), u, specConstant); } + Id makeInt64Constant(long long i, bool specConstant = false) { return makeInt64Constant(makeIntType(64), (unsigned long long)i, specConstant); } + Id makeUint64Constant(unsigned long long u, bool specConstant = false) { return makeInt64Constant(makeUintType(64), u, specConstant); } +#ifdef AMD_EXTENSIONS + Id makeInt16Constant(short i, bool specConstant = false) { return makeIntConstant(makeIntType(16), (unsigned)((unsigned short)i), specConstant); } + Id makeUint16Constant(unsigned short u, bool specConstant = false) { return makeIntConstant(makeUintType(16), (unsigned)u, specConstant); } +#endif Id makeFloatConstant(float f, bool specConstant = false); Id makeDoubleConstant(double d, bool specConstant = false); +#ifdef AMD_EXTENSIONS + Id makeFloat16Constant(float f16, bool specConstant = false); +#endif // Turn the array of constants into a proper spv constant of the requested type. Id makeCompositeConstant(Id type, const std::vector& comps, bool specConst = false); @@ -199,7 +233,6 @@ public: void addExecutionMode(Function*, ExecutionMode mode, int value1 = -1, int value2 = -1, int value3 = -1); void addName(Id, const char* name); void addMemberName(Id, int member, const char* name); - void addLine(Id target, Id fileName, int line, int column); void addDecoration(Id, Decoration, int num = -1); void addMemberDecoration(Id, unsigned int member, Decoration, int num = -1); @@ -207,15 +240,15 @@ public: void setBuildPoint(Block* bp) { buildPoint = bp; } Block* getBuildPoint() const { return buildPoint; } - // Make the main function. The returned pointer is only valid + // Make the entry-point function. The returned pointer is only valid // for the lifetime of this builder. - Function* makeMain(); + Function* makeEntryPoint(const char*); // Make a shader-style function, and create its entry block if entry is non-zero. // Return the function, pass back the entry. // The returned pointer is only valid for the lifetime of this builder. Function* makeFunctionEntry(Decoration precision, Id returnType, const char* name, const std::vector& paramTypes, - const std::vector& precisions, Block **entry = 0); + const std::vector>& precisions, Block **entry = 0); // Create a return. An 'implicit' return is one not appearing in the source // code. In the case of an implicit return, no post-return block is inserted. @@ -247,7 +280,7 @@ public: // Create an OpCompositeExtract instruction Id createCompositeExtract(Id composite, Id typeId, unsigned index); - Id createCompositeExtract(Id composite, Id typeId, std::vector& indexes); + Id createCompositeExtract(Id composite, Id typeId, const std::vector& indexes); Id createCompositeInsert(Id object, Id composite, Id typeId, unsigned index); Id createCompositeInsert(Id object, Id composite, Id typeId, const std::vector& indexes); @@ -264,6 +297,7 @@ public: Id createTriOp(Op, Id typeId, Id operand1, Id operand2, Id operand3); Id createOp(Op, Id typeId, const std::vector& operands); Id createFunctionCall(spv::Function*, const std::vector&); + Id createSpecConstantOp(Op, Id typeId, const std::vector& operands, const std::vector& literals); // Take an rvalue (source) and a set of channels to extract from it to // make a new rvalue, which is returned. @@ -296,7 +330,7 @@ public: // Generally, the type of 'scalar' does not need to be the same type as the components in 'vector'. // The type of the created vector is a vector of components of the same type as the scalar. // - // Note: One of the arguments will change, with the result coming back that way rather than + // Note: One of the arguments will change, with the result coming back that way rather than // through the return value. void promoteScalar(Decoration precision, Id& left, Id& right); @@ -306,7 +340,7 @@ public: Id smearScalar(Decoration precision, Id scalarVal, Id vectorType); // Create a call to a built-in function. - Id createBuiltinCall(Id resultType, Id builtins, int entryPoint, std::vector& args); + Id createBuiltinCall(Id resultType, Id builtins, int entryPoint, const std::vector& args); // List of parameters used to create a texture operation struct TextureParameters { @@ -320,7 +354,7 @@ public: Id gradX; Id gradY; Id sample; - Id comp; + Id component; Id texelOut; Id lodClamp; }; @@ -330,7 +364,7 @@ public: // Emit the OpTextureQuery* instruction that was passed in. // Figure out the right return value and type, and return it. - Id createTextureQueryCall(Op, const TextureParameters&); + Id createTextureQueryCall(Op, const TextureParameters&, bool isUnsignedResult); Id createSamplePositionCall(Decoration precision, Id, Id); @@ -352,7 +386,7 @@ public: // Helper to use for building nested control flow with if-then-else. class If { public: - If(Id condition, Builder& builder); + If(Id condition, unsigned int ctrl, Builder& builder); ~If() {} void makeBeginElse(); @@ -364,6 +398,7 @@ public: Builder& builder; Id condition; + unsigned int control; Function* function; Block* headerBlock; Block* thenBlock; @@ -383,8 +418,8 @@ public: // Returns the right set of basic blocks to start each code segment with, so that the caller's // recursion stack can hold the memory for it. // - void makeSwitch(Id condition, int numSegments, const std::vector& caseValues, const std::vector& valueToSegment, int defaultSegment, - std::vector& segmentBB); // return argument + void makeSwitch(Id condition, unsigned int control, int numSegments, const std::vector& caseValues, + const std::vector& valueToSegment, int defaultSegment, std::vector& segmentBB); // return argument // Add a branch to the innermost switch's merge block. void addSwitchBreak(); @@ -396,7 +431,12 @@ public: void endSwitch(std::vector& segmentBB); struct LoopBlocks { + LoopBlocks(Block& head, Block& body, Block& merge, Block& continue_target) : + head(head), body(body), merge(merge), continue_target(continue_target) { } Block &head, &body, &merge, &continue_target; + private: + LoopBlocks(); + LoopBlocks& operator=(const LoopBlocks&); }; // Start a new loop and prepare the builder to generate code for it. Until @@ -460,7 +500,7 @@ public: // // the SPIR-V builder maintains a single active chain that - // the following methods operated on + // the following methods operate on // // for external save and restore @@ -493,12 +533,15 @@ public: // push new swizzle onto the end of any existing swizzle, merging into a single swizzle void accessChainPushSwizzle(std::vector& swizzle, Id preSwizzleBaseType); - // push a variable component selection onto the access chain; supporting only one, so unsided + // push a dynamic component selection onto the access chain, only applicable with a + // non-trivial swizzle or no swizzle void accessChainPushComponent(Id component, Id preSwizzleBaseType) { - accessChain.component = component; - if (accessChain.preSwizzleBaseType == NoType) - accessChain.preSwizzleBaseType = preSwizzleBaseType; + if (accessChain.swizzle.size() != 1) { + accessChain.component = component; + if (accessChain.preSwizzleBaseType == NoType) + accessChain.preSwizzleBaseType = preSwizzleBaseType; + } } // use accessChain and swizzle to store value @@ -514,27 +557,48 @@ public: // based on the type of the base and the chain of dereferences. Id accessChainGetInferredType(); + // Remove OpDecorate instructions whose operands are defined in unreachable + // blocks. + void eliminateDeadDecorations(); void dump(std::vector&) const; void createBranch(Block* block); void createConditionalBranch(Id condition, Block* thenBlock, Block* elseBlock); - void createLoopMerge(Block* mergeBlock, Block* continueBlock, unsigned int control); + void createLoopMerge(Block* mergeBlock, Block* continueBlock, unsigned int control, unsigned int dependencyLength); void createSelectionMerge(Block* mergeBlock, unsigned int control); + // Sets to generate opcode for specialization constants. + void setToSpecConstCodeGenMode() { generatingOpCodeForSpecConst = true; } + // Sets to generate opcode for non-specialization constants (normal mode). + void setToNormalCodeGenMode() { generatingOpCodeForSpecConst = false; } + // Check if the builder is generating code for spec constants. + bool isInSpecConstCodeGenMode() { return generatingOpCodeForSpecConst; } + protected: Id makeIntConstant(Id typeId, unsigned value, bool specConstant); + Id makeInt64Constant(Id typeId, unsigned long long value, bool specConstant); Id findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned value) const; Id findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned v1, unsigned v2) const; Id findCompositeConstant(Op typeClass, const std::vector& comps) const; Id collapseAccessChain(); + void remapDynamicSwizzle(); void transferAccessChainSwizzle(bool dynamic); void simplifyAccessChainSwizzle(); void createAndSetNoPredecessorBlock(const char*); + void dumpSourceInstructions(std::vector&) const; void dumpInstructions(std::vector&, const std::vector >&) const; + void dumpModuleProcesses(std::vector&) const; + unsigned int spvVersion; // the version of SPIR-V to emit in the header SourceLanguage source; int sourceVersion; - std::vector extensions; + spv::Id sourceFileStringId; + std::string sourceText; + int currentLine; + bool emitOpLines; + std::set extensions; + std::vector sourceExtensions; + std::vector moduleProcesses; AddressingModel addressModel; MemoryModel memoryModel; std::set capabilities; @@ -542,10 +606,12 @@ public: Module module; Block* buildPoint; Id uniqueId; - Function* mainFunction; + Function* entryPointFunction; + bool generatingOpCodeForSpecConst; AccessChain accessChain; // special blocks of instructions for output + std::vector > strings; std::vector > imports; std::vector > entryPoints; std::vector > executionModes; @@ -565,14 +631,11 @@ public: // Our loop stack. std::stack loops; + + // The stream for outputting warnings and errors. + SpvBuildLogger* logger; }; // end Builder class -// Use for non-fatal notes about what's not complete -void TbdFunctionality(const char*); - -// Use for fatal missing functionality -void MissingFunctionality(const char*); - }; // end spv namespace #endif // SpvBuilder_H diff --git a/third_party/glslang-spirv/bitutils.h b/third_party/glslang-spirv/bitutils.h new file mode 100644 index 000000000..31288ab69 --- /dev/null +++ b/third_party/glslang-spirv/bitutils.h @@ -0,0 +1,81 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// 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 LIBSPIRV_UTIL_BITUTILS_H_ +#define LIBSPIRV_UTIL_BITUTILS_H_ + +#include +#include + +namespace spvutils { + +// Performs a bitwise copy of source to the destination type Dest. +template +Dest BitwiseCast(Src source) { + Dest dest; + static_assert(sizeof(source) == sizeof(dest), + "BitwiseCast: Source and destination must have the same size"); + std::memcpy(&dest, &source, sizeof(dest)); + return dest; +} + +// SetBits returns an integer of type with bits set +// for position through , counting from the least +// significant bit. In particular when Num == 0, no positions are set to 1. +// A static assert will be triggered if First + Num > sizeof(T) * 8, that is, +// a bit that will not fit in the underlying type is set. +template +struct SetBits { + static_assert(First < sizeof(T) * 8, + "Tried to set a bit that is shifted too far."); + const static T get = (T(1) << First) | SetBits::get; +}; + +template +struct SetBits { + const static T get = T(0); +}; + +// This is all compile-time so we can put our tests right here. +static_assert(SetBits::get == uint32_t(0x00000000), + "SetBits failed"); +static_assert(SetBits::get == uint32_t(0x00000001), + "SetBits failed"); +static_assert(SetBits::get == uint32_t(0x80000000), + "SetBits failed"); +static_assert(SetBits::get == uint32_t(0x00000006), + "SetBits failed"); +static_assert(SetBits::get == uint32_t(0xc0000000), + "SetBits failed"); +static_assert(SetBits::get == uint32_t(0x7FFFFFFF), + "SetBits failed"); +static_assert(SetBits::get == uint32_t(0xFFFFFFFF), + "SetBits failed"); +static_assert(SetBits::get == uint32_t(0xFFFF0000), + "SetBits failed"); + +static_assert(SetBits::get == uint64_t(0x0000000000000001LL), + "SetBits failed"); +static_assert(SetBits::get == uint64_t(0x8000000000000000LL), + "SetBits failed"); +static_assert(SetBits::get == uint64_t(0xc000000000000000LL), + "SetBits failed"); +static_assert(SetBits::get == uint64_t(0x0000000080000000LL), + "SetBits failed"); +static_assert(SetBits::get == uint64_t(0x00000000FFFF0000LL), + "SetBits failed"); + +} // namespace spvutils + +#endif // LIBSPIRV_UTIL_BITUTILS_H_ diff --git a/third_party/glslang-spirv/disassemble.cpp b/third_party/glslang-spirv/disassemble.cpp index b2d30bec8..c950a66a0 100644 --- a/third_party/glslang-spirv/disassemble.cpp +++ b/third_party/glslang-spirv/disassemble.cpp @@ -1,11 +1,11 @@ // -//Copyright (C) 2014-2015 LunarG, Inc. +// Copyright (C) 2014-2015 LunarG, Inc. // -//All rights reserved. +// All rights reserved. // -//Redistribution and use in source and binary forms, with or without -//modification, are permitted provided that the following conditions -//are met: +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: // // Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. @@ -19,47 +19,59 @@ // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // -//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -//"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -//LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -//FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -//COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -//INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -//BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -//LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -//CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -//ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -//POSSIBILITY OF SUCH DAMAGE. - -// -// Author: John Kessenich, LunarG -// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. // // Disassembler for SPIR-V. // -#include -#include -#include +#include +#include +#include #include #include #include #include -namespace spv { - // Include C-based headers that don't have a namespace - #include "GLSL.std.450.h" -} -const char* GlslStd450DebugNames[spv::GLSLstd450Count]; - #include "disassemble.h" #include "doc.h" namespace spv { + extern "C" { + // Include C-based headers that don't have a namespace + #include "GLSL.std.450.h" +#ifdef AMD_EXTENSIONS + #include "GLSL.ext.AMD.h" +#endif +#ifdef NV_EXTENSIONS + #include "GLSL.ext.NV.h" +#endif + } +} +const char* GlslStd450DebugNames[spv::GLSLstd450Count]; -void Kill(std::ostream& out, const char* message) +namespace spv { + +#ifdef AMD_EXTENSIONS +static const char* GLSLextAMDGetDebugNames(const char*, unsigned); +#endif + +#ifdef NV_EXTENSIONS +static const char* GLSLextNVGetDebugNames(const char*, unsigned); +#endif + +static void Kill(std::ostream& out, const char* message) { out << std::endl << "Disassembly failed: " << message << std::endl; exit(1); @@ -68,6 +80,12 @@ void Kill(std::ostream& out, const char* message) // used to identify the extended instruction library imported when printing enum ExtInstSet { GLSL450Inst, +#ifdef AMD_EXTENSIONS + GLSLextAMDInst, +#endif +#ifdef NV_EXTENSIONS + GLSLextNVInst, +#endif OpenCLExtInst, }; @@ -209,10 +227,12 @@ void SpirvStream::outputIndent() void SpirvStream::formatId(Id id, std::stringstream& idStream) { - if (id >= bound) - Kill(out, "Bad "); - if (id != 0) { + // On instructions with no IDs, this is called with "0", which does not + // have to be within ID bounds on null shaders. + if (id >= bound) + Kill(out, "Bad "); + idStream << id; if (idDescriptor[id].size() > 0) idStream << "(" << idDescriptor[id] << ")"; @@ -326,7 +346,7 @@ void SpirvStream::disassembleInstruction(Id resultId, Id /*typeId*/, Op opCode, idDescriptor[resultId] = (const char*)(&stream[word]); } else { - if (idDescriptor[resultId].size() == 0) { + if (resultId != 0 && idDescriptor[resultId].size() == 0) { switch (opCode) { case OpTypeInt: idDescriptor[resultId] = "int"; @@ -450,14 +470,38 @@ void SpirvStream::disassembleInstruction(Id resultId, Id /*typeId*/, Op opCode, --numOperands; if (opCode == OpExtInst) { ExtInstSet extInstSet = GLSL450Inst; - if (0 == memcmp("OpenCL", (const char*)(idDescriptor[stream[word-2]].c_str()), 6)) { + const char* name = idDescriptor[stream[word - 2]].c_str(); + if (0 == memcmp("OpenCL", name, 6)) { extInstSet = OpenCLExtInst; +#ifdef AMD_EXTENSIONS + } else if (strcmp(spv::E_SPV_AMD_shader_ballot, name) == 0 || + strcmp(spv::E_SPV_AMD_shader_trinary_minmax, name) == 0 || + strcmp(spv::E_SPV_AMD_shader_explicit_vertex_parameter, name) == 0 || + strcmp(spv::E_SPV_AMD_gcn_shader, name) == 0) { + extInstSet = GLSLextAMDInst; +#endif +#ifdef NV_EXTENSIONS + }else if (strcmp(spv::E_SPV_NV_sample_mask_override_coverage, name) == 0 || + strcmp(spv::E_SPV_NV_geometry_shader_passthrough, name) == 0 || + strcmp(spv::E_SPV_NV_viewport_array2, name) == 0 || + strcmp(spv::E_SPV_NVX_multiview_per_view_attributes, name) == 0) { + extInstSet = GLSLextNVInst; +#endif } unsigned entrypoint = stream[word - 1]; if (extInstSet == GLSL450Inst) { if (entrypoint < GLSLstd450Count) { out << "(" << GlslStd450DebugNames[entrypoint] << ")"; } +#ifdef AMD_EXTENSIONS + } else if (extInstSet == GLSLextAMDInst) { + out << "(" << GLSLextAMDGetDebugNames(name, entrypoint) << ")"; +#endif +#ifdef NV_EXTENSIONS + } + else if (extInstSet == GLSLextNVInst) { + out << "(" << GLSLextNVGetDebugNames(name, entrypoint) << ")"; +#endif } } break; @@ -481,7 +525,7 @@ void SpirvStream::disassembleInstruction(Id resultId, Id /*typeId*/, Op opCode, return; } -void GLSLstd450GetDebugNames(const char** names) +static void GLSLstd450GetDebugNames(const char** names) { for (int i = 0; i < GLSLstd450Count; ++i) names[i] = "Unknown"; @@ -565,9 +609,84 @@ void GLSLstd450GetDebugNames(const char** names) names[GLSLstd450InterpolateAtOffset] = "InterpolateAtOffset"; } +#ifdef AMD_EXTENSIONS +static const char* GLSLextAMDGetDebugNames(const char* name, unsigned entrypoint) +{ + if (strcmp(name, spv::E_SPV_AMD_shader_ballot) == 0) { + switch (entrypoint) { + case SwizzleInvocationsAMD: return "SwizzleInvocationsAMD"; + case SwizzleInvocationsMaskedAMD: return "SwizzleInvocationsMaskedAMD"; + case WriteInvocationAMD: return "WriteInvocationAMD"; + case MbcntAMD: return "MbcntAMD"; + default: return "Bad"; + } + } else if (strcmp(name, spv::E_SPV_AMD_shader_trinary_minmax) == 0) { + switch (entrypoint) { + case FMin3AMD: return "FMin3AMD"; + case UMin3AMD: return "UMin3AMD"; + case SMin3AMD: return "SMin3AMD"; + case FMax3AMD: return "FMax3AMD"; + case UMax3AMD: return "UMax3AMD"; + case SMax3AMD: return "SMax3AMD"; + case FMid3AMD: return "FMid3AMD"; + case UMid3AMD: return "UMid3AMD"; + case SMid3AMD: return "SMid3AMD"; + default: return "Bad"; + } + } else if (strcmp(name, spv::E_SPV_AMD_shader_explicit_vertex_parameter) == 0) { + switch (entrypoint) { + case InterpolateAtVertexAMD: return "InterpolateAtVertexAMD"; + default: return "Bad"; + } + } + else if (strcmp(name, spv::E_SPV_AMD_gcn_shader) == 0) { + switch (entrypoint) { + case CubeFaceIndexAMD: return "CubeFaceIndexAMD"; + case CubeFaceCoordAMD: return "CubeFaceCoordAMD"; + case TimeAMD: return "TimeAMD"; + default: + break; + } + } + + return "Bad"; +} +#endif + + +#ifdef NV_EXTENSIONS +static const char* GLSLextNVGetDebugNames(const char* name, unsigned entrypoint) +{ + if (strcmp(name, spv::E_SPV_NV_sample_mask_override_coverage) == 0 || + strcmp(name, spv::E_SPV_NV_geometry_shader_passthrough) == 0 || + strcmp(name, spv::E_ARB_shader_viewport_layer_array) == 0 || + strcmp(name, spv::E_SPV_NV_viewport_array2) == 0 || + strcmp(spv::E_SPV_NVX_multiview_per_view_attributes, name) == 0) { + switch (entrypoint) { + case DecorationOverrideCoverageNV: return "OverrideCoverageNV"; + case DecorationPassthroughNV: return "PassthroughNV"; + case CapabilityGeometryShaderPassthroughNV: return "GeometryShaderPassthroughNV"; + case DecorationViewportRelativeNV: return "ViewportRelativeNV"; + case BuiltInViewportMaskNV: return "ViewportMaskNV"; + case CapabilityShaderViewportMaskNV: return "ShaderViewportMaskNV"; + case DecorationSecondaryViewportRelativeNV: return "SecondaryViewportRelativeNV"; + case BuiltInSecondaryPositionNV: return "SecondaryPositionNV"; + case BuiltInSecondaryViewportMaskNV: return "SecondaryViewportMaskNV"; + case CapabilityShaderStereoViewNV: return "ShaderStereoViewNV"; + case BuiltInPositionPerViewNV: return "PositionPerViewNV"; + case BuiltInViewportMaskPerViewNV: return "ViewportMaskPerViewNV"; + case CapabilityPerViewAttributesNV: return "PerViewAttributesNV"; + default: return "Bad"; + } + } + return "Bad"; +} +#endif + void Disassemble(std::ostream& out, const std::vector& stream) { SpirvStream SpirvStream(out, stream); + spv::Parameterize(); GLSLstd450GetDebugNames(GlslStd450DebugNames); SpirvStream.validate(); SpirvStream.processInstructions(); diff --git a/third_party/glslang-spirv/disassemble.h b/third_party/glslang-spirv/disassemble.h index be537a371..47cef65a5 100644 --- a/third_party/glslang-spirv/disassemble.h +++ b/third_party/glslang-spirv/disassemble.h @@ -1,11 +1,11 @@ // -//Copyright (C) 2014-2015 LunarG, Inc. +// Copyright (C) 2014-2015 LunarG, Inc. // -//All rights reserved. +// All rights reserved. // -//Redistribution and use in source and binary forms, with or without -//modification, are permitted provided that the following conditions -//are met: +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: // // Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. @@ -19,22 +19,18 @@ // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // -//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -//"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -//LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -//FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -//COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -//INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -//BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -//LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -//CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -//ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -//POSSIBILITY OF SUCH DAMAGE. - -// -// Author: John Kessenich, LunarG -// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. // // Disassembler for SPIR-V. diff --git a/third_party/glslang-spirv/doc.cpp b/third_party/glslang-spirv/doc.cpp index 7cf1c87f0..809af4c1c 100644 --- a/third_party/glslang-spirv/doc.cpp +++ b/third_party/glslang-spirv/doc.cpp @@ -1,11 +1,11 @@ // -//Copyright (C) 2014-2015 LunarG, Inc. +// Copyright (C) 2014-2015 LunarG, Inc. // -//All rights reserved. +// All rights reserved. // -//Redistribution and use in source and binary forms, with or without -//modification, are permitted provided that the following conditions -//are met: +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: // // Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. @@ -19,25 +19,21 @@ // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // -//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -//"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -//LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -//FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -//COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -//INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -//BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -//LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -//CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -//ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -//POSSIBILITY OF SUCH DAMAGE. +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. // -// Author: John Kessenich, LunarG -// - -// -// 1) Programatically fill in instruction/operand information. +// 1) Programmatically fill in instruction/operand information. // This can be used for disassembly, printing documentation, etc. // // 2) Print documentation from this parameterization. @@ -45,10 +41,24 @@ #include "doc.h" -#include -#include +#include +#include #include +namespace spv { + extern "C" { + // Include C-based headers that don't have a namespace + #include "GLSL.ext.KHR.h" + #include "GLSL.ext.EXT.h" +#ifdef AMD_EXTENSIONS + #include "GLSL.ext.AMD.h" +#endif +#ifdef NV_EXTENSIONS + #include "GLSL.ext.NV.h" +#endif + } +} + namespace spv { // @@ -59,12 +69,12 @@ namespace spv { // Also, the ceilings are declared next to these, to help keep them in sync. // Ceilings should be // - one more than the maximum value an enumerant takes on, for non-mask enumerants -// (for non-sparse enums, this is the number of enumurants) +// (for non-sparse enums, this is the number of enumerants) // - the number of bits consumed by the set of masks -// (for non-sparse mask enums, this is the number of enumurants) +// (for non-sparse mask enums, this is the number of enumerants) // -const int SourceLanguageCeiling = 5; +const int SourceLanguageCeiling = 6; // HLSL todo: need official enumerant const char* SourceString(int source) { @@ -74,6 +84,7 @@ const char* SourceString(int source) case 2: return "GLSL"; case 3: return "OpenCL_C"; case 4: return "OpenCL_CPP"; + case 5: return "HLSL"; case SourceLanguageCeiling: default: return "Bad"; @@ -165,12 +176,13 @@ const char* ExecutionModeString(int mode) case 31: return "ContractionOff"; case 32: return "Bad"; + case 4446: return "PostDepthCoverage"; case ExecutionModeCeiling: default: return "Bad"; } } -const int StorageClassCeiling = 12; +const int StorageClassCeiling = 13; const char* StorageClassString(int StorageClass) { @@ -187,6 +199,7 @@ const char* StorageClassString(int StorageClass) case 9: return "PushConstant"; case 10: return "AtomicCounter"; case 11: return "Image"; + case 12: return "StorageBuffer"; case StorageClassCeiling: default: return "Bad"; @@ -246,6 +259,16 @@ const char* DecorationString(int decoration) case DecorationCeiling: default: return "Bad"; + +#ifdef AMD_EXTENSIONS + case 4999: return "ExplicitInterpAMD"; +#endif +#ifdef NV_EXTENSIONS + case 5248: return "OverrideCoverageNV"; + case 5250: return "PassthroughNV"; + case 5252: return "ViewportRelativeNV"; + case 5256: return "SecondaryViewportRelativeNV"; +#endif } } @@ -299,6 +322,38 @@ const char* BuiltInString(int builtIn) case 42: return "VertexIndex"; // TBD: put next to VertexId? case 43: return "InstanceIndex"; // TBD: put next to InstanceId? + case 4416: return "SubgroupEqMaskKHR"; + case 4417: return "SubgroupGeMaskKHR"; + case 4418: return "SubgroupGtMaskKHR"; + case 4419: return "SubgroupLeMaskKHR"; + case 4420: return "SubgroupLtMaskKHR"; + case 4438: return "DeviceIndex"; + case 4440: return "ViewIndex"; + case 4424: return "BaseVertex"; + case 4425: return "BaseInstance"; + case 4426: return "DrawIndex"; + case 5014: return "FragStencilRefEXT"; + +#ifdef AMD_EXTENSIONS + case 4992: return "BaryCoordNoPerspAMD"; + case 4993: return "BaryCoordNoPerspCentroidAMD"; + case 4994: return "BaryCoordNoPerspSampleAMD"; + case 4995: return "BaryCoordSmoothAMD"; + case 4996: return "BaryCoordSmoothCentroidAMD"; + case 4997: return "BaryCoordSmoothSampleAMD"; + case 4998: return "BaryCoordPullModelAMD"; +#endif + +#ifdef NV_EXTENSIONS + case 5253: return "ViewportMaskNV"; + case 5257: return "SecondaryPositionNV"; + case 5258: return "SecondaryViewportMaskNV"; + case 5261: return "PositionPerViewNV"; + case 5262: return "ViewportMaskPerViewNV"; +#endif + + case 5264: return "FullyCoveredEXT"; + case BuiltInCeiling: default: return "Bad"; } @@ -586,13 +641,15 @@ const char* SelectControlString(int cont) } } -const int LoopControlCeiling = 2; +const int LoopControlCeiling = 4; const char* LoopControlString(int cont) { switch (cont) { case 0: return "Unroll"; case 1: return "DontUnroll"; + case 2: return "DependencyInfinite"; + case 3: return "DependencyLength"; case LoopControlCeiling: default: return "Bad"; @@ -777,6 +834,39 @@ const char* CapabilityString(int info) case 56: return "StorageImageWriteWithoutFormat"; case 57: return "MultiViewport"; + case 4423: return "SubgroupBallotKHR"; + case 4427: return "DrawParameters"; + case 4431: return "SubgroupVoteKHR"; + + case 4433: return "StorageUniformBufferBlock16"; + case 4434: return "StorageUniform16"; + case 4435: return "StoragePushConstant16"; + case 4436: return "StorageInputOutput16"; + + case 4437: return "DeviceGroup"; + case 4439: return "MultiView"; + + case 5013: return "StencilExportEXT"; + +#ifdef AMD_EXTENSIONS + case 5009: return "ImageGatherBiasLodAMD"; + case 5010: return "FragmentMaskAMD"; + case 5015: return "ImageReadWriteLodAMD"; +#endif + + case 4445: return "AtomicStorageOps"; + + case 4447: return "SampleMaskPostDepthCoverage"; +#ifdef NV_EXTENSIONS + case 5251: return "GeometryShaderPassthroughNV"; + case 5254: return "ShaderViewportIndexLayerNV"; + case 5255: return "ShaderViewportMaskNV"; + case 5259: return "ShaderStereoViewNV"; + case 5260: return "PerViewAttributesNV"; +#endif + + case 5265: return "FragmentFullyCoveredEXT"; + case CapabilityCeiling: default: return "Bad"; } @@ -1107,6 +1197,27 @@ const char* OpcodeString(int op) case 319: return "OpAtomicFlagClear"; case 320: return "OpImageSparseRead"; + case 4421: return "OpSubgroupBallotKHR"; + case 4422: return "OpSubgroupFirstInvocationKHR"; + case 4428: return "OpSubgroupAllKHR"; + case 4429: return "OpSubgroupAnyKHR"; + case 4430: return "OpSubgroupAllEqualKHR"; + case 4432: return "OpSubgroupReadInvocationKHR"; + +#ifdef AMD_EXTENSIONS + case 5000: return "OpGroupIAddNonUniformAMD"; + case 5001: return "OpGroupFAddNonUniformAMD"; + case 5002: return "OpGroupFMinNonUniformAMD"; + case 5003: return "OpGroupUMinNonUniformAMD"; + case 5004: return "OpGroupSMinNonUniformAMD"; + case 5005: return "OpGroupFMaxNonUniformAMD"; + case 5006: return "OpGroupUMaxNonUniformAMD"; + case 5007: return "OpGroupSMaxNonUniformAMD"; + + case 5011: return "OpFragmentMaskFetchAMD"; + case 5012: return "OpFragmentFetchAMD"; +#endif + case OpcodeCeiling: default: return "Bad"; @@ -1115,7 +1226,7 @@ const char* OpcodeString(int op) // The set of objects that hold all the instruction/operand // parameterization information. -InstructionParameters InstructionDesc[OpcodeCeiling]; +InstructionParameters InstructionDesc[OpCodeMask + 1]; OperandParameters ExecutionModeOperands[ExecutionModeCeiling]; OperandParameters DecorationOperands[DecorationCeiling]; @@ -2447,6 +2558,7 @@ void Parameterize() InstructionDesc[OpLoopMerge].operands.push(OperandId, "'Merge Block'"); InstructionDesc[OpLoopMerge].operands.push(OperandId, "'Continue Target'"); InstructionDesc[OpLoopMerge].operands.push(OperandLoop, ""); + InstructionDesc[OpLoopMerge].operands.push(OperandOptionalLiteral, ""); InstructionDesc[OpSelectionMerge].operands.push(OperandId, "'Merge Block'"); InstructionDesc[OpSelectionMerge].operands.push(OperandSelect, ""); @@ -2706,6 +2818,77 @@ void Parameterize() InstructionDesc[OpEnqueueMarker].operands.push(OperandId, "'Num Events'"); InstructionDesc[OpEnqueueMarker].operands.push(OperandId, "'Wait Events'"); InstructionDesc[OpEnqueueMarker].operands.push(OperandId, "'Ret Event'"); + + InstructionDesc[OpSubgroupBallotKHR].operands.push(OperandId, "'Predicate'"); + + InstructionDesc[OpSubgroupFirstInvocationKHR].operands.push(OperandId, "'Value'"); + + InstructionDesc[OpSubgroupAnyKHR].capabilities.push_back(CapabilitySubgroupVoteKHR); + InstructionDesc[OpSubgroupAnyKHR].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpSubgroupAnyKHR].operands.push(OperandId, "'Predicate'"); + + InstructionDesc[OpSubgroupAllKHR].capabilities.push_back(CapabilitySubgroupVoteKHR); + InstructionDesc[OpSubgroupAllKHR].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpSubgroupAllKHR].operands.push(OperandId, "'Predicate'"); + + InstructionDesc[OpSubgroupAllEqualKHR].capabilities.push_back(CapabilitySubgroupVoteKHR); + InstructionDesc[OpSubgroupAllEqualKHR].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpSubgroupAllEqualKHR].operands.push(OperandId, "'Predicate'"); + + InstructionDesc[OpSubgroupReadInvocationKHR].capabilities.push_back(CapabilityGroups); + InstructionDesc[OpSubgroupReadInvocationKHR].operands.push(OperandId, "'Value'"); + InstructionDesc[OpSubgroupReadInvocationKHR].operands.push(OperandId, "'Index'"); + +#ifdef AMD_EXTENSIONS + InstructionDesc[OpGroupIAddNonUniformAMD].capabilities.push_back(CapabilityGroups); + InstructionDesc[OpGroupIAddNonUniformAMD].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupIAddNonUniformAMD].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupIAddNonUniformAMD].operands.push(OperandId, "'X'"); + + InstructionDesc[OpGroupFAddNonUniformAMD].capabilities.push_back(CapabilityGroups); + InstructionDesc[OpGroupFAddNonUniformAMD].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupFAddNonUniformAMD].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupFAddNonUniformAMD].operands.push(OperandId, "'X'"); + + InstructionDesc[OpGroupUMinNonUniformAMD].capabilities.push_back(CapabilityGroups); + InstructionDesc[OpGroupUMinNonUniformAMD].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupUMinNonUniformAMD].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupUMinNonUniformAMD].operands.push(OperandId, "'X'"); + + InstructionDesc[OpGroupSMinNonUniformAMD].capabilities.push_back(CapabilityGroups); + InstructionDesc[OpGroupSMinNonUniformAMD].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupSMinNonUniformAMD].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupSMinNonUniformAMD].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupFMinNonUniformAMD].capabilities.push_back(CapabilityGroups); + InstructionDesc[OpGroupFMinNonUniformAMD].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupFMinNonUniformAMD].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupFMinNonUniformAMD].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupUMaxNonUniformAMD].capabilities.push_back(CapabilityGroups); + InstructionDesc[OpGroupUMaxNonUniformAMD].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupUMaxNonUniformAMD].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupUMaxNonUniformAMD].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupSMaxNonUniformAMD].capabilities.push_back(CapabilityGroups); + InstructionDesc[OpGroupSMaxNonUniformAMD].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupSMaxNonUniformAMD].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupSMaxNonUniformAMD].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupFMaxNonUniformAMD].capabilities.push_back(CapabilityGroups); + InstructionDesc[OpGroupFMaxNonUniformAMD].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupFMaxNonUniformAMD].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupFMaxNonUniformAMD].operands.push(OperandId, "X"); + + InstructionDesc[OpFragmentMaskFetchAMD].capabilities.push_back(CapabilityFragmentMaskAMD); + InstructionDesc[OpFragmentMaskFetchAMD].operands.push(OperandId, "'Image'"); + InstructionDesc[OpFragmentMaskFetchAMD].operands.push(OperandId, "'Coordinate'"); + + InstructionDesc[OpFragmentFetchAMD].capabilities.push_back(CapabilityFragmentMaskAMD); + InstructionDesc[OpFragmentFetchAMD].operands.push(OperandId, "'Image'"); + InstructionDesc[OpFragmentFetchAMD].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpFragmentFetchAMD].operands.push(OperandId, "'Fragment Index'"); +#endif } }; // end spv namespace diff --git a/third_party/glslang-spirv/doc.h b/third_party/glslang-spirv/doc.h index 948b6fe04..710ca1a52 100644 --- a/third_party/glslang-spirv/doc.h +++ b/third_party/glslang-spirv/doc.h @@ -1,11 +1,11 @@ // -//Copyright (C) 2014-2015 LunarG, Inc. +// Copyright (C) 2014-2015 LunarG, Inc. // -//All rights reserved. +// All rights reserved. // -//Redistribution and use in source and binary forms, with or without -//modification, are permitted provided that the following conditions -//are met: +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: // // Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. @@ -19,27 +19,25 @@ // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // -//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -//"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -//LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -//FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -//COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -//INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -//BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -//LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -//CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -//ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -//POSSIBILITY OF SUCH DAMAGE. - -// -// Author: John Kessenich, LunarG -// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. // // Parameterize the SPIR-V enumerants. // +#pragma once + #include "spirv.hpp" #include @@ -67,6 +65,8 @@ const char* SamplerFilterModeString(int); const char* ImageFormatString(int); const char* ImageChannelOrderString(int); const char* ImageChannelTypeString(int); +const char* ImageChannelDataTypeString(int type); +const char* ImageOperandsString(int format); const char* ImageOperands(int); const char* FPFastMathString(int); const char* FPRoundingModeString(int); @@ -81,6 +81,7 @@ const char* KernelEnqueueFlagsString(int); const char* KernelProfilingInfoString(int); const char* CapabilityString(int); const char* OpcodeString(int); +const char* ScopeString(int mem); // For grouping opcodes into subsections enum OpcodeClass { @@ -150,7 +151,7 @@ enum OperandClass { OperandMemorySemantics, OperandMemoryAccess, OperandScope, - OperandGroupOperation, + OperandGroupOperation, OperandKernelEnqueueFlags, OperandKernelProfilingInfo, OperandCapability, diff --git a/third_party/glslang-spirv/hex_float.h b/third_party/glslang-spirv/hex_float.h new file mode 100644 index 000000000..905b21a45 --- /dev/null +++ b/third_party/glslang-spirv/hex_float.h @@ -0,0 +1,1078 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// 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 LIBSPIRV_UTIL_HEX_FLOAT_H_ +#define LIBSPIRV_UTIL_HEX_FLOAT_H_ + +#include +#include +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) && _MSC_VER < 1800 +namespace std { +bool isnan(double f) +{ + return ::_isnan(f) != 0; +} +bool isinf(double f) +{ + return ::_finite(f) == 0; +} +} +#endif + +#include "bitutils.h" + +namespace spvutils { + +class Float16 { + public: + Float16(uint16_t v) : val(v) {} + Float16() {} + static bool isNan(const Float16& val) { + return ((val.val & 0x7C00) == 0x7C00) && ((val.val & 0x3FF) != 0); + } + // Returns true if the given value is any kind of infinity. + static bool isInfinity(const Float16& val) { + return ((val.val & 0x7C00) == 0x7C00) && ((val.val & 0x3FF) == 0); + } + Float16(const Float16& other) { val = other.val; } + uint16_t get_value() const { return val; } + + // Returns the maximum normal value. + static Float16 max() { return Float16(0x7bff); } + // Returns the lowest normal value. + static Float16 lowest() { return Float16(0xfbff); } + + private: + uint16_t val; +}; + +// To specialize this type, you must override uint_type to define +// an unsigned integer that can fit your floating point type. +// You must also add a isNan function that returns true if +// a value is Nan. +template +struct FloatProxyTraits { + typedef void uint_type; +}; + +template <> +struct FloatProxyTraits { + typedef uint32_t uint_type; + static bool isNan(float f) { return std::isnan(f); } + // Returns true if the given value is any kind of infinity. + static bool isInfinity(float f) { return std::isinf(f); } + // Returns the maximum normal value. + static float max() { return std::numeric_limits::max(); } + // Returns the lowest normal value. + static float lowest() { return std::numeric_limits::lowest(); } +}; + +template <> +struct FloatProxyTraits { + typedef uint64_t uint_type; + static bool isNan(double f) { return std::isnan(f); } + // Returns true if the given value is any kind of infinity. + static bool isInfinity(double f) { return std::isinf(f); } + // Returns the maximum normal value. + static double max() { return std::numeric_limits::max(); } + // Returns the lowest normal value. + static double lowest() { return std::numeric_limits::lowest(); } +}; + +template <> +struct FloatProxyTraits { + typedef uint16_t uint_type; + static bool isNan(Float16 f) { return Float16::isNan(f); } + // Returns true if the given value is any kind of infinity. + static bool isInfinity(Float16 f) { return Float16::isInfinity(f); } + // Returns the maximum normal value. + static Float16 max() { return Float16::max(); } + // Returns the lowest normal value. + static Float16 lowest() { return Float16::lowest(); } +}; + +// Since copying a floating point number (especially if it is NaN) +// does not guarantee that bits are preserved, this class lets us +// store the type and use it as a float when necessary. +template +class FloatProxy { + public: + typedef typename FloatProxyTraits::uint_type uint_type; + + // Since this is to act similar to the normal floats, + // do not initialize the data by default. + FloatProxy() {} + + // Intentionally non-explicit. This is a proxy type so + // implicit conversions allow us to use it more transparently. + FloatProxy(T val) { data_ = BitwiseCast(val); } + + // Intentionally non-explicit. This is a proxy type so + // implicit conversions allow us to use it more transparently. + FloatProxy(uint_type val) { data_ = val; } + + // This is helpful to have and is guaranteed not to stomp bits. + FloatProxy operator-() const { + return static_cast(data_ ^ + (uint_type(0x1) << (sizeof(T) * 8 - 1))); + } + + // Returns the data as a floating point value. + T getAsFloat() const { return BitwiseCast(data_); } + + // Returns the raw data. + uint_type data() const { return data_; } + + // Returns true if the value represents any type of NaN. + bool isNan() { return FloatProxyTraits::isNan(getAsFloat()); } + // Returns true if the value represents any type of infinity. + bool isInfinity() { return FloatProxyTraits::isInfinity(getAsFloat()); } + + // Returns the maximum normal value. + static FloatProxy max() { + return FloatProxy(FloatProxyTraits::max()); + } + // Returns the lowest normal value. + static FloatProxy lowest() { + return FloatProxy(FloatProxyTraits::lowest()); + } + + private: + uint_type data_; +}; + +template +bool operator==(const FloatProxy& first, const FloatProxy& second) { + return first.data() == second.data(); +} + +// Reads a FloatProxy value as a normal float from a stream. +template +std::istream& operator>>(std::istream& is, FloatProxy& value) { + T float_val; + is >> float_val; + value = FloatProxy(float_val); + return is; +} + +// This is an example traits. It is not meant to be used in practice, but will +// be the default for any non-specialized type. +template +struct HexFloatTraits { + // Integer type that can store this hex-float. + typedef void uint_type; + // Signed integer type that can store this hex-float. + typedef void int_type; + // The numerical type that this HexFloat represents. + typedef void underlying_type; + // The type needed to construct the underlying type. + typedef void native_type; + // The number of bits that are actually relevant in the uint_type. + // This allows us to deal with, for example, 24-bit values in a 32-bit + // integer. + static const uint32_t num_used_bits = 0; + // Number of bits that represent the exponent. + static const uint32_t num_exponent_bits = 0; + // Number of bits that represent the fractional part. + static const uint32_t num_fraction_bits = 0; + // The bias of the exponent. (How much we need to subtract from the stored + // value to get the correct value.) + static const uint32_t exponent_bias = 0; +}; + +// Traits for IEEE float. +// 1 sign bit, 8 exponent bits, 23 fractional bits. +template <> +struct HexFloatTraits> { + typedef uint32_t uint_type; + typedef int32_t int_type; + typedef FloatProxy underlying_type; + typedef float native_type; + static const uint_type num_used_bits = 32; + static const uint_type num_exponent_bits = 8; + static const uint_type num_fraction_bits = 23; + static const uint_type exponent_bias = 127; +}; + +// Traits for IEEE double. +// 1 sign bit, 11 exponent bits, 52 fractional bits. +template <> +struct HexFloatTraits> { + typedef uint64_t uint_type; + typedef int64_t int_type; + typedef FloatProxy underlying_type; + typedef double native_type; + static const uint_type num_used_bits = 64; + static const uint_type num_exponent_bits = 11; + static const uint_type num_fraction_bits = 52; + static const uint_type exponent_bias = 1023; +}; + +// Traits for IEEE half. +// 1 sign bit, 5 exponent bits, 10 fractional bits. +template <> +struct HexFloatTraits> { + typedef uint16_t uint_type; + typedef int16_t int_type; + typedef uint16_t underlying_type; + typedef uint16_t native_type; + static const uint_type num_used_bits = 16; + static const uint_type num_exponent_bits = 5; + static const uint_type num_fraction_bits = 10; + static const uint_type exponent_bias = 15; +}; + +enum round_direction { + kRoundToZero, + kRoundToNearestEven, + kRoundToPositiveInfinity, + kRoundToNegativeInfinity +}; + +// Template class that houses a floating pointer number. +// It exposes a number of constants based on the provided traits to +// assist in interpreting the bits of the value. +template > +class HexFloat { + public: + typedef typename Traits::uint_type uint_type; + typedef typename Traits::int_type int_type; + typedef typename Traits::underlying_type underlying_type; + typedef typename Traits::native_type native_type; + + explicit HexFloat(T f) : value_(f) {} + + T value() const { return value_; } + void set_value(T f) { value_ = f; } + + // These are all written like this because it is convenient to have + // compile-time constants for all of these values. + + // Pass-through values to save typing. + static const uint32_t num_used_bits = Traits::num_used_bits; + static const uint32_t exponent_bias = Traits::exponent_bias; + static const uint32_t num_exponent_bits = Traits::num_exponent_bits; + static const uint32_t num_fraction_bits = Traits::num_fraction_bits; + + // Number of bits to shift left to set the highest relevant bit. + static const uint32_t top_bit_left_shift = num_used_bits - 1; + // How many nibbles (hex characters) the fractional part takes up. + static const uint32_t fraction_nibbles = (num_fraction_bits + 3) / 4; + // If the fractional part does not fit evenly into a hex character (4-bits) + // then we have to left-shift to get rid of leading 0s. This is the amount + // we have to shift (might be 0). + static const uint32_t num_overflow_bits = + fraction_nibbles * 4 - num_fraction_bits; + + // The representation of the fraction, not the actual bits. This + // includes the leading bit that is usually implicit. + static const uint_type fraction_represent_mask = + spvutils::SetBits::get; + + // The topmost bit in the nibble-aligned fraction. + static const uint_type fraction_top_bit = + uint_type(1) << (num_fraction_bits + num_overflow_bits - 1); + + // The least significant bit in the exponent, which is also the bit + // immediately to the left of the significand. + static const uint_type first_exponent_bit = uint_type(1) + << (num_fraction_bits); + + // The mask for the encoded fraction. It does not include the + // implicit bit. + static const uint_type fraction_encode_mask = + spvutils::SetBits::get; + + // The bit that is used as a sign. + static const uint_type sign_mask = uint_type(1) << top_bit_left_shift; + + // The bits that represent the exponent. + static const uint_type exponent_mask = + spvutils::SetBits::get; + + // How far left the exponent is shifted. + static const uint32_t exponent_left_shift = num_fraction_bits; + + // How far from the right edge the fraction is shifted. + static const uint32_t fraction_right_shift = + static_cast(sizeof(uint_type) * 8) - num_fraction_bits; + + // The maximum representable unbiased exponent. + static const int_type max_exponent = + (exponent_mask >> num_fraction_bits) - exponent_bias; + // The minimum representable exponent for normalized numbers. + static const int_type min_exponent = -static_cast(exponent_bias); + + // Returns the bits associated with the value. + uint_type getBits() const { return spvutils::BitwiseCast(value_); } + + // Returns the bits associated with the value, without the leading sign bit. + uint_type getUnsignedBits() const { + return static_cast(spvutils::BitwiseCast(value_) & + ~sign_mask); + } + + // Returns the bits associated with the exponent, shifted to start at the + // lsb of the type. + const uint_type getExponentBits() const { + return static_cast((getBits() & exponent_mask) >> + num_fraction_bits); + } + + // Returns the exponent in unbiased form. This is the exponent in the + // human-friendly form. + const int_type getUnbiasedExponent() const { + return static_cast(getExponentBits() - exponent_bias); + } + + // Returns just the significand bits from the value. + const uint_type getSignificandBits() const { + return getBits() & fraction_encode_mask; + } + + // If the number was normalized, returns the unbiased exponent. + // If the number was denormal, normalize the exponent first. + const int_type getUnbiasedNormalizedExponent() const { + if ((getBits() & ~sign_mask) == 0) { // special case if everything is 0 + return 0; + } + int_type exp = getUnbiasedExponent(); + if (exp == min_exponent) { // We are in denorm land. + uint_type significand_bits = getSignificandBits(); + while ((significand_bits & (first_exponent_bit >> 1)) == 0) { + significand_bits = static_cast(significand_bits << 1); + exp = static_cast(exp - 1); + } + significand_bits &= fraction_encode_mask; + } + return exp; + } + + // Returns the signficand after it has been normalized. + const uint_type getNormalizedSignificand() const { + int_type unbiased_exponent = getUnbiasedNormalizedExponent(); + uint_type significand = getSignificandBits(); + for (int_type i = unbiased_exponent; i <= min_exponent; ++i) { + significand = static_cast(significand << 1); + } + significand &= fraction_encode_mask; + return significand; + } + + // Returns true if this number represents a negative value. + bool isNegative() const { return (getBits() & sign_mask) != 0; } + + // Sets this HexFloat from the individual components. + // Note this assumes EVERY significand is normalized, and has an implicit + // leading one. This means that the only way that this method will set 0, + // is if you set a number so denormalized that it underflows. + // Do not use this method with raw bits extracted from a subnormal number, + // since subnormals do not have an implicit leading 1 in the significand. + // The significand is also expected to be in the + // lowest-most num_fraction_bits of the uint_type. + // The exponent is expected to be unbiased, meaning an exponent of + // 0 actually means 0. + // If underflow_round_up is set, then on underflow, if a number is non-0 + // and would underflow, we round up to the smallest denorm. + void setFromSignUnbiasedExponentAndNormalizedSignificand( + bool negative, int_type exponent, uint_type significand, + bool round_denorm_up) { + bool significand_is_zero = significand == 0; + + if (exponent <= min_exponent) { + // If this was denormalized, then we have to shift the bit on, meaning + // the significand is not zero. + significand_is_zero = false; + significand |= first_exponent_bit; + significand = static_cast(significand >> 1); + } + + while (exponent < min_exponent) { + significand = static_cast(significand >> 1); + ++exponent; + } + + if (exponent == min_exponent) { + if (significand == 0 && !significand_is_zero && round_denorm_up) { + significand = static_cast(0x1); + } + } + + uint_type new_value = 0; + if (negative) { + new_value = static_cast(new_value | sign_mask); + } + exponent = static_cast(exponent + exponent_bias); + assert(exponent >= 0); + + // put it all together + exponent = static_cast((exponent << exponent_left_shift) & + exponent_mask); + significand = static_cast(significand & fraction_encode_mask); + new_value = static_cast(new_value | (exponent | significand)); + value_ = BitwiseCast(new_value); + } + + // Increments the significand of this number by the given amount. + // If this would spill the significand into the implicit bit, + // carry is set to true and the significand is shifted to fit into + // the correct location, otherwise carry is set to false. + // All significands and to_increment are assumed to be within the bounds + // for a valid significand. + static uint_type incrementSignificand(uint_type significand, + uint_type to_increment, bool* carry) { + significand = static_cast(significand + to_increment); + *carry = false; + if (significand & first_exponent_bit) { + *carry = true; + // The implicit 1-bit will have carried, so we should zero-out the + // top bit and shift back. + significand = static_cast(significand & ~first_exponent_bit); + significand = static_cast(significand >> 1); + } + return significand; + } + + // These exist because MSVC throws warnings on negative right-shifts + // even if they are not going to be executed. Eg: + // constant_number < 0? 0: constant_number + // These convert the negative left-shifts into right shifts. + + template + uint_type negatable_left_shift(int_type N, uint_type val) + { + if(N >= 0) + return val << N; + + return val >> -N; + } + + template + uint_type negatable_right_shift(int_type N, uint_type val) + { + if(N >= 0) + return val >> N; + + return val << -N; + } + + // Returns the significand, rounded to fit in a significand in + // other_T. This is shifted so that the most significant + // bit of the rounded number lines up with the most significant bit + // of the returned significand. + template + typename other_T::uint_type getRoundedNormalizedSignificand( + round_direction dir, bool* carry_bit) { + typedef typename other_T::uint_type other_uint_type; + static const int_type num_throwaway_bits = + static_cast(num_fraction_bits) - + static_cast(other_T::num_fraction_bits); + + static const uint_type last_significant_bit = + (num_throwaway_bits < 0) + ? 0 + : negatable_left_shift(num_throwaway_bits, 1u); + static const uint_type first_rounded_bit = + (num_throwaway_bits < 1) + ? 0 + : negatable_left_shift(num_throwaway_bits - 1, 1u); + + static const uint_type throwaway_mask_bits = + num_throwaway_bits > 0 ? num_throwaway_bits : 0; + static const uint_type throwaway_mask = + spvutils::SetBits::get; + + *carry_bit = false; + other_uint_type out_val = 0; + uint_type significand = getNormalizedSignificand(); + // If we are up-casting, then we just have to shift to the right location. + if (num_throwaway_bits <= 0) { + out_val = static_cast(significand); + uint_type shift_amount = static_cast(-num_throwaway_bits); + out_val = static_cast(out_val << shift_amount); + return out_val; + } + + // If every non-representable bit is 0, then we don't have any casting to + // do. + if ((significand & throwaway_mask) == 0) { + return static_cast( + negatable_right_shift(num_throwaway_bits, significand)); + } + + bool round_away_from_zero = false; + // We actually have to narrow the significand here, so we have to follow the + // rounding rules. + switch (dir) { + case kRoundToZero: + break; + case kRoundToPositiveInfinity: + round_away_from_zero = !isNegative(); + break; + case kRoundToNegativeInfinity: + round_away_from_zero = isNegative(); + break; + case kRoundToNearestEven: + // Have to round down, round bit is 0 + if ((first_rounded_bit & significand) == 0) { + break; + } + if (((significand & throwaway_mask) & ~first_rounded_bit) != 0) { + // If any subsequent bit of the rounded portion is non-0 then we round + // up. + round_away_from_zero = true; + break; + } + // We are exactly half-way between 2 numbers, pick even. + if ((significand & last_significant_bit) != 0) { + // 1 for our last bit, round up. + round_away_from_zero = true; + break; + } + break; + } + + if (round_away_from_zero) { + return static_cast( + negatable_right_shift(num_throwaway_bits, incrementSignificand( + significand, last_significant_bit, carry_bit))); + } else { + return static_cast( + negatable_right_shift(num_throwaway_bits, significand)); + } + } + + // Casts this value to another HexFloat. If the cast is widening, + // then round_dir is ignored. If the cast is narrowing, then + // the result is rounded in the direction specified. + // This number will retain Nan and Inf values. + // It will also saturate to Inf if the number overflows, and + // underflow to (0 or min depending on rounding) if the number underflows. + template + void castTo(other_T& other, round_direction round_dir) { + other = other_T(static_cast(0)); + bool negate = isNegative(); + if (getUnsignedBits() == 0) { + if (negate) { + other.set_value(-other.value()); + } + return; + } + uint_type significand = getSignificandBits(); + bool carried = false; + typename other_T::uint_type rounded_significand = + getRoundedNormalizedSignificand(round_dir, &carried); + + int_type exponent = getUnbiasedExponent(); + if (exponent == min_exponent) { + // If we are denormal, normalize the exponent, so that we can encode + // easily. + exponent = static_cast(exponent + 1); + for (uint_type check_bit = first_exponent_bit >> 1; check_bit != 0; + check_bit = static_cast(check_bit >> 1)) { + exponent = static_cast(exponent - 1); + if (check_bit & significand) break; + } + } + + bool is_nan = + (getBits() & exponent_mask) == exponent_mask && significand != 0; + bool is_inf = + !is_nan && + ((exponent + carried) > static_cast(other_T::exponent_bias) || + (significand == 0 && (getBits() & exponent_mask) == exponent_mask)); + + // If we are Nan or Inf we should pass that through. + if (is_inf) { + other.set_value(BitwiseCast( + static_cast( + (negate ? other_T::sign_mask : 0) | other_T::exponent_mask))); + return; + } + if (is_nan) { + typename other_T::uint_type shifted_significand; + shifted_significand = static_cast( + negatable_left_shift( + static_cast(other_T::num_fraction_bits) - + static_cast(num_fraction_bits), significand)); + + // We are some sort of Nan. We try to keep the bit-pattern of the Nan + // as close as possible. If we had to shift off bits so we are 0, then we + // just set the last bit. + other.set_value(BitwiseCast( + static_cast( + (negate ? other_T::sign_mask : 0) | other_T::exponent_mask | + (shifted_significand == 0 ? 0x1 : shifted_significand)))); + return; + } + + bool round_underflow_up = + isNegative() ? round_dir == kRoundToNegativeInfinity + : round_dir == kRoundToPositiveInfinity; + typedef typename other_T::int_type other_int_type; + // setFromSignUnbiasedExponentAndNormalizedSignificand will + // zero out any underflowing value (but retain the sign). + other.setFromSignUnbiasedExponentAndNormalizedSignificand( + negate, static_cast(exponent), rounded_significand, + round_underflow_up); + return; + } + + private: + T value_; + + static_assert(num_used_bits == + Traits::num_exponent_bits + Traits::num_fraction_bits + 1, + "The number of bits do not fit"); + static_assert(sizeof(T) == sizeof(uint_type), "The type sizes do not match"); +}; + +// Returns 4 bits represented by the hex character. +inline uint8_t get_nibble_from_character(int character) { + const char* dec = "0123456789"; + const char* lower = "abcdef"; + const char* upper = "ABCDEF"; + const char* p = nullptr; + if ((p = strchr(dec, character))) { + return static_cast(p - dec); + } else if ((p = strchr(lower, character))) { + return static_cast(p - lower + 0xa); + } else if ((p = strchr(upper, character))) { + return static_cast(p - upper + 0xa); + } + + assert(false && "This was called with a non-hex character"); + return 0; +} + +// Outputs the given HexFloat to the stream. +template +std::ostream& operator<<(std::ostream& os, const HexFloat& value) { + typedef HexFloat HF; + typedef typename HF::uint_type uint_type; + typedef typename HF::int_type int_type; + + static_assert(HF::num_used_bits != 0, + "num_used_bits must be non-zero for a valid float"); + static_assert(HF::num_exponent_bits != 0, + "num_exponent_bits must be non-zero for a valid float"); + static_assert(HF::num_fraction_bits != 0, + "num_fractin_bits must be non-zero for a valid float"); + + const uint_type bits = spvutils::BitwiseCast(value.value()); + const char* const sign = (bits & HF::sign_mask) ? "-" : ""; + const uint_type exponent = static_cast( + (bits & HF::exponent_mask) >> HF::num_fraction_bits); + + uint_type fraction = static_cast((bits & HF::fraction_encode_mask) + << HF::num_overflow_bits); + + const bool is_zero = exponent == 0 && fraction == 0; + const bool is_denorm = exponent == 0 && !is_zero; + + // exponent contains the biased exponent we have to convert it back into + // the normal range. + int_type int_exponent = static_cast(exponent - HF::exponent_bias); + // If the number is all zeros, then we actually have to NOT shift the + // exponent. + int_exponent = is_zero ? 0 : int_exponent; + + // If we are denorm, then start shifting, and decreasing the exponent until + // our leading bit is 1. + + if (is_denorm) { + while ((fraction & HF::fraction_top_bit) == 0) { + fraction = static_cast(fraction << 1); + int_exponent = static_cast(int_exponent - 1); + } + // Since this is denormalized, we have to consume the leading 1 since it + // will end up being implicit. + fraction = static_cast(fraction << 1); // eat the leading 1 + fraction &= HF::fraction_represent_mask; + } + + uint_type fraction_nibbles = HF::fraction_nibbles; + // We do not have to display any trailing 0s, since this represents the + // fractional part. + while (fraction_nibbles > 0 && (fraction & 0xF) == 0) { + // Shift off any trailing values; + fraction = static_cast(fraction >> 4); + --fraction_nibbles; + } + + const auto saved_flags = os.flags(); + const auto saved_fill = os.fill(); + + os << sign << "0x" << (is_zero ? '0' : '1'); + if (fraction_nibbles) { + // Make sure to keep the leading 0s in place, since this is the fractional + // part. + os << "." << std::setw(static_cast(fraction_nibbles)) + << std::setfill('0') << std::hex << fraction; + } + os << "p" << std::dec << (int_exponent >= 0 ? "+" : "") << int_exponent; + + os.flags(saved_flags); + os.fill(saved_fill); + + return os; +} + +// Returns true if negate_value is true and the next character on the +// input stream is a plus or minus sign. In that case we also set the fail bit +// on the stream and set the value to the zero value for its type. +template +inline bool RejectParseDueToLeadingSign(std::istream& is, bool negate_value, + HexFloat& value) { + if (negate_value) { + auto next_char = is.peek(); + if (next_char == '-' || next_char == '+') { + // Fail the parse. Emulate standard behaviour by setting the value to + // the zero value, and set the fail bit on the stream. + value = HexFloat(typename HexFloat::uint_type(0)); + is.setstate(std::ios_base::failbit); + return true; + } + } + return false; +} + +// Parses a floating point number from the given stream and stores it into the +// value parameter. +// If negate_value is true then the number may not have a leading minus or +// plus, and if it successfully parses, then the number is negated before +// being stored into the value parameter. +// If the value cannot be correctly parsed or overflows the target floating +// point type, then set the fail bit on the stream. +// TODO(dneto): Promise C++11 standard behavior in how the value is set in +// the error case, but only after all target platforms implement it correctly. +// In particular, the Microsoft C++ runtime appears to be out of spec. +template +inline std::istream& ParseNormalFloat(std::istream& is, bool negate_value, + HexFloat& value) { + if (RejectParseDueToLeadingSign(is, negate_value, value)) { + return is; + } + T val; + is >> val; + if (negate_value) { + val = -val; + } + value.set_value(val); + // In the failure case, map -0.0 to 0.0. + if (is.fail() && value.getUnsignedBits() == 0u) { + value = HexFloat(typename HexFloat::uint_type(0)); + } + if (val.isInfinity()) { + // Fail the parse. Emulate standard behaviour by setting the value to + // the closest normal value, and set the fail bit on the stream. + value.set_value((value.isNegative() | negate_value) ? T::lowest() + : T::max()); + is.setstate(std::ios_base::failbit); + } + return is; +} + +// Specialization of ParseNormalFloat for FloatProxy values. +// This will parse the float as it were a 32-bit floating point number, +// and then round it down to fit into a Float16 value. +// The number is rounded towards zero. +// If negate_value is true then the number may not have a leading minus or +// plus, and if it successfully parses, then the number is negated before +// being stored into the value parameter. +// If the value cannot be correctly parsed or overflows the target floating +// point type, then set the fail bit on the stream. +// TODO(dneto): Promise C++11 standard behavior in how the value is set in +// the error case, but only after all target platforms implement it correctly. +// In particular, the Microsoft C++ runtime appears to be out of spec. +template <> +inline std::istream& +ParseNormalFloat, HexFloatTraits>>( + std::istream& is, bool negate_value, + HexFloat, HexFloatTraits>>& value) { + // First parse as a 32-bit float. + HexFloat> float_val(0.0f); + ParseNormalFloat(is, negate_value, float_val); + + // Then convert to 16-bit float, saturating at infinities, and + // rounding toward zero. + float_val.castTo(value, kRoundToZero); + + // Overflow on 16-bit behaves the same as for 32- and 64-bit: set the + // fail bit and set the lowest or highest value. + if (Float16::isInfinity(value.value().getAsFloat())) { + value.set_value(value.isNegative() ? Float16::lowest() : Float16::max()); + is.setstate(std::ios_base::failbit); + } + return is; +} + +// Reads a HexFloat from the given stream. +// If the float is not encoded as a hex-float then it will be parsed +// as a regular float. +// This may fail if your stream does not support at least one unget. +// Nan values can be encoded with "0x1.p+exponent_bias". +// This would normally overflow a float and round to +// infinity but this special pattern is the exact representation for a NaN, +// and therefore is actually encoded as the correct NaN. To encode inf, +// either 0x0p+exponent_bias can be specified or any exponent greater than +// exponent_bias. +// Examples using IEEE 32-bit float encoding. +// 0x1.0p+128 (+inf) +// -0x1.0p-128 (-inf) +// +// 0x1.1p+128 (+Nan) +// -0x1.1p+128 (-Nan) +// +// 0x1p+129 (+inf) +// -0x1p+129 (-inf) +template +std::istream& operator>>(std::istream& is, HexFloat& value) { + using HF = HexFloat; + using uint_type = typename HF::uint_type; + using int_type = typename HF::int_type; + + value.set_value(static_cast(0.f)); + + if (is.flags() & std::ios::skipws) { + // If the user wants to skip whitespace , then we should obey that. + while (std::isspace(is.peek())) { + is.get(); + } + } + + auto next_char = is.peek(); + bool negate_value = false; + + if (next_char != '-' && next_char != '0') { + return ParseNormalFloat(is, negate_value, value); + } + + if (next_char == '-') { + negate_value = true; + is.get(); + next_char = is.peek(); + } + + if (next_char == '0') { + is.get(); // We may have to unget this. + auto maybe_hex_start = is.peek(); + if (maybe_hex_start != 'x' && maybe_hex_start != 'X') { + is.unget(); + return ParseNormalFloat(is, negate_value, value); + } else { + is.get(); // Throw away the 'x'; + } + } else { + return ParseNormalFloat(is, negate_value, value); + } + + // This "looks" like a hex-float so treat it as one. + bool seen_p = false; + bool seen_dot = false; + uint_type fraction_index = 0; + + uint_type fraction = 0; + int_type exponent = HF::exponent_bias; + + // Strip off leading zeros so we don't have to special-case them later. + while ((next_char = is.peek()) == '0') { + is.get(); + } + + bool is_denorm = + true; // Assume denorm "representation" until we hear otherwise. + // NB: This does not mean the value is actually denorm, + // it just means that it was written 0. + bool bits_written = false; // Stays false until we write a bit. + while (!seen_p && !seen_dot) { + // Handle characters that are left of the fractional part. + if (next_char == '.') { + seen_dot = true; + } else if (next_char == 'p') { + seen_p = true; + } else if (::isxdigit(next_char)) { + // We know this is not denormalized since we have stripped all leading + // zeroes and we are not a ".". + is_denorm = false; + int number = get_nibble_from_character(next_char); + for (int i = 0; i < 4; ++i, number <<= 1) { + uint_type write_bit = (number & 0x8) ? 0x1 : 0x0; + if (bits_written) { + // If we are here the bits represented belong in the fractional + // part of the float, and we have to adjust the exponent accordingly. + fraction = static_cast( + fraction | + static_cast( + write_bit << (HF::top_bit_left_shift - fraction_index++))); + exponent = static_cast(exponent + 1); + } + bits_written |= write_bit != 0; + } + } else { + // We have not found our exponent yet, so we have to fail. + is.setstate(std::ios::failbit); + return is; + } + is.get(); + next_char = is.peek(); + } + bits_written = false; + while (seen_dot && !seen_p) { + // Handle only fractional parts now. + if (next_char == 'p') { + seen_p = true; + } else if (::isxdigit(next_char)) { + int number = get_nibble_from_character(next_char); + for (int i = 0; i < 4; ++i, number <<= 1) { + uint_type write_bit = (number & 0x8) ? 0x01 : 0x00; + bits_written |= write_bit != 0; + if (is_denorm && !bits_written) { + // Handle modifying the exponent here this way we can handle + // an arbitrary number of hex values without overflowing our + // integer. + exponent = static_cast(exponent - 1); + } else { + fraction = static_cast( + fraction | + static_cast( + write_bit << (HF::top_bit_left_shift - fraction_index++))); + } + } + } else { + // We still have not found our 'p' exponent yet, so this is not a valid + // hex-float. + is.setstate(std::ios::failbit); + return is; + } + is.get(); + next_char = is.peek(); + } + + bool seen_sign = false; + int8_t exponent_sign = 1; + int_type written_exponent = 0; + while (true) { + if ((next_char == '-' || next_char == '+')) { + if (seen_sign) { + is.setstate(std::ios::failbit); + return is; + } + seen_sign = true; + exponent_sign = (next_char == '-') ? -1 : 1; + } else if (::isdigit(next_char)) { + // Hex-floats express their exponent as decimal. + written_exponent = static_cast(written_exponent * 10); + written_exponent = + static_cast(written_exponent + (next_char - '0')); + } else { + break; + } + is.get(); + next_char = is.peek(); + } + + written_exponent = static_cast(written_exponent * exponent_sign); + exponent = static_cast(exponent + written_exponent); + + bool is_zero = is_denorm && (fraction == 0); + if (is_denorm && !is_zero) { + fraction = static_cast(fraction << 1); + exponent = static_cast(exponent - 1); + } else if (is_zero) { + exponent = 0; + } + + if (exponent <= 0 && !is_zero) { + fraction = static_cast(fraction >> 1); + fraction |= static_cast(1) << HF::top_bit_left_shift; + } + + fraction = (fraction >> HF::fraction_right_shift) & HF::fraction_encode_mask; + + const int_type max_exponent = + SetBits::get; + + // Handle actual denorm numbers + while (exponent < 0 && !is_zero) { + fraction = static_cast(fraction >> 1); + exponent = static_cast(exponent + 1); + + fraction &= HF::fraction_encode_mask; + if (fraction == 0) { + // We have underflowed our fraction. We should clamp to zero. + is_zero = true; + exponent = 0; + } + } + + // We have overflowed so we should be inf/-inf. + if (exponent > max_exponent) { + exponent = max_exponent; + fraction = 0; + } + + uint_type output_bits = static_cast( + static_cast(negate_value ? 1 : 0) << HF::top_bit_left_shift); + output_bits |= fraction; + + uint_type shifted_exponent = static_cast( + static_cast(exponent << HF::exponent_left_shift) & + HF::exponent_mask); + output_bits |= shifted_exponent; + + T output_float = spvutils::BitwiseCast(output_bits); + value.set_value(output_float); + + return is; +} + +// Writes a FloatProxy value to a stream. +// Zero and normal numbers are printed in the usual notation, but with +// enough digits to fully reproduce the value. Other values (subnormal, +// NaN, and infinity) are printed as a hex float. +template +std::ostream& operator<<(std::ostream& os, const FloatProxy& value) { + auto float_val = value.getAsFloat(); + switch (std::fpclassify(float_val)) { + case FP_ZERO: + case FP_NORMAL: { + auto saved_precision = os.precision(); + os.precision(std::numeric_limits::digits10); + os << float_val; + os.precision(saved_precision); + } break; + default: + os << HexFloat>(value); + break; + } + return os; +} + +template <> +inline std::ostream& operator<<(std::ostream& os, + const FloatProxy& value) { + os << HexFloat>(value); + return os; +} +} + +#endif // LIBSPIRV_UTIL_HEX_FLOAT_H_ diff --git a/third_party/glslang-spirv/spirv.hpp b/third_party/glslang-spirv/spirv.hpp index 526781fc8..c6776638e 100644 --- a/third_party/glslang-spirv/spirv.hpp +++ b/third_party/glslang-spirv/spirv.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016 The Khronos Group Inc. +// Copyright (c) 2014-2018 The Khronos Group Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and/or associated documentation files (the "Materials"), @@ -46,11 +46,11 @@ namespace spv { typedef unsigned int Id; -#define SPV_VERSION 0x10000 +#define SPV_VERSION 0x10200 #define SPV_REVISION 3 static const unsigned int MagicNumber = 0x07230203; -static const unsigned int Version = 0x00010000; +static const unsigned int Version = 0x00010200; static const unsigned int Revision = 3; static const unsigned int OpCodeMask = 0xffff; static const unsigned int WordCountShift = 16; @@ -61,6 +61,8 @@ enum SourceLanguage { SourceLanguageGLSL = 2, SourceLanguageOpenCL_C = 3, SourceLanguageOpenCL_CPP = 4, + SourceLanguageHLSL = 5, + SourceLanguageMax = 0x7fffffff, }; enum ExecutionModel { @@ -71,18 +73,21 @@ enum ExecutionModel { ExecutionModelFragment = 4, ExecutionModelGLCompute = 5, ExecutionModelKernel = 6, + ExecutionModelMax = 0x7fffffff, }; enum AddressingModel { AddressingModelLogical = 0, AddressingModelPhysical32 = 1, AddressingModelPhysical64 = 2, + AddressingModelMax = 0x7fffffff, }; enum MemoryModel { MemoryModelSimple = 0, MemoryModelGLSL450 = 1, MemoryModelOpenCL = 2, + MemoryModelMax = 0x7fffffff, }; enum ExecutionMode { @@ -117,6 +122,16 @@ enum ExecutionMode { ExecutionModeOutputTriangleStrip = 29, ExecutionModeVecTypeHint = 30, ExecutionModeContractionOff = 31, + ExecutionModeInitializer = 33, + ExecutionModeFinalizer = 34, + ExecutionModeSubgroupSize = 35, + ExecutionModeSubgroupsPerWorkgroup = 36, + ExecutionModeSubgroupsPerWorkgroupId = 37, + ExecutionModeLocalSizeId = 38, + ExecutionModeLocalSizeHintId = 39, + ExecutionModePostDepthCoverage = 4446, + ExecutionModeStencilRefReplacingEXT = 5027, + ExecutionModeMax = 0x7fffffff, }; enum StorageClass { @@ -132,6 +147,8 @@ enum StorageClass { StorageClassPushConstant = 9, StorageClassAtomicCounter = 10, StorageClassImage = 11, + StorageClassStorageBuffer = 12, + StorageClassMax = 0x7fffffff, }; enum Dim { @@ -142,6 +159,7 @@ enum Dim { DimRect = 4, DimBuffer = 5, DimSubpassData = 6, + DimMax = 0x7fffffff, }; enum SamplerAddressingMode { @@ -150,11 +168,13 @@ enum SamplerAddressingMode { SamplerAddressingModeClamp = 2, SamplerAddressingModeRepeat = 3, SamplerAddressingModeRepeatMirrored = 4, + SamplerAddressingModeMax = 0x7fffffff, }; enum SamplerFilterMode { SamplerFilterModeNearest = 0, SamplerFilterModeLinear = 1, + SamplerFilterModeMax = 0x7fffffff, }; enum ImageFormat { @@ -198,6 +218,7 @@ enum ImageFormat { ImageFormatRg8ui = 37, ImageFormatR16ui = 38, ImageFormatR8ui = 39, + ImageFormatMax = 0x7fffffff, }; enum ImageChannelOrder { @@ -220,6 +241,8 @@ enum ImageChannelOrder { ImageChannelOrdersRGBx = 16, ImageChannelOrdersRGBA = 17, ImageChannelOrdersBGRA = 18, + ImageChannelOrderABGR = 19, + ImageChannelOrderMax = 0x7fffffff, }; enum ImageChannelDataType { @@ -240,6 +263,7 @@ enum ImageChannelDataType { ImageChannelDataTypeFloat = 14, ImageChannelDataTypeUnormInt24 = 15, ImageChannelDataTypeUnormInt101010_2 = 16, + ImageChannelDataTypeMax = 0x7fffffff, }; enum ImageOperandsShift { @@ -251,6 +275,7 @@ enum ImageOperandsShift { ImageOperandsConstOffsetsShift = 5, ImageOperandsSampleShift = 6, ImageOperandsMinLodShift = 7, + ImageOperandsMax = 0x7fffffff, }; enum ImageOperandsMask { @@ -271,6 +296,7 @@ enum FPFastMathModeShift { FPFastMathModeNSZShift = 2, FPFastMathModeAllowRecipShift = 3, FPFastMathModeFastShift = 4, + FPFastMathModeMax = 0x7fffffff, }; enum FPFastMathModeMask { @@ -287,17 +313,20 @@ enum FPRoundingMode { FPRoundingModeRTZ = 1, FPRoundingModeRTP = 2, FPRoundingModeRTN = 3, + FPRoundingModeMax = 0x7fffffff, }; enum LinkageType { LinkageTypeExport = 0, LinkageTypeImport = 1, + LinkageTypeMax = 0x7fffffff, }; enum AccessQualifier { AccessQualifierReadOnly = 0, AccessQualifierWriteOnly = 1, AccessQualifierReadWrite = 2, + AccessQualifierMax = 0x7fffffff, }; enum FunctionParameterAttribute { @@ -309,6 +338,7 @@ enum FunctionParameterAttribute { FunctionParameterAttributeNoCapture = 5, FunctionParameterAttributeNoWrite = 6, FunctionParameterAttributeNoReadWrite = 7, + FunctionParameterAttributeMax = 0x7fffffff, }; enum Decoration { @@ -355,6 +385,15 @@ enum Decoration { DecorationNoContraction = 42, DecorationInputAttachmentIndex = 43, DecorationAlignment = 44, + DecorationMaxByteOffset = 45, + DecorationAlignmentId = 46, + DecorationMaxByteOffsetId = 47, + DecorationExplicitInterpAMD = 4999, + DecorationOverrideCoverageNV = 5248, + DecorationPassthroughNV = 5250, + DecorationViewportRelativeNV = 5252, + DecorationSecondaryViewportRelativeNV = 5256, + DecorationMax = 0x7fffffff, }; enum BuiltIn { @@ -399,11 +438,37 @@ enum BuiltIn { BuiltInSubgroupLocalInvocationId = 41, BuiltInVertexIndex = 42, BuiltInInstanceIndex = 43, + BuiltInSubgroupEqMaskKHR = 4416, + BuiltInSubgroupGeMaskKHR = 4417, + BuiltInSubgroupGtMaskKHR = 4418, + BuiltInSubgroupLeMaskKHR = 4419, + BuiltInSubgroupLtMaskKHR = 4420, + BuiltInBaseVertex = 4424, + BuiltInBaseInstance = 4425, + BuiltInDrawIndex = 4426, + BuiltInDeviceIndex = 4438, + BuiltInViewIndex = 4440, + BuiltInBaryCoordNoPerspAMD = 4992, + BuiltInBaryCoordNoPerspCentroidAMD = 4993, + BuiltInBaryCoordNoPerspSampleAMD = 4994, + BuiltInBaryCoordSmoothAMD = 4995, + BuiltInBaryCoordSmoothCentroidAMD = 4996, + BuiltInBaryCoordSmoothSampleAMD = 4997, + BuiltInBaryCoordPullModelAMD = 4998, + BuiltInFragStencilRefEXT = 5014, + BuiltInViewportMaskNV = 5253, + BuiltInSecondaryPositionNV = 5257, + BuiltInSecondaryViewportMaskNV = 5258, + BuiltInPositionPerViewNV = 5261, + BuiltInViewportMaskPerViewNV = 5262, + BuiltInFullyCoveredEXT = 5264, + BuiltInMax = 0x7fffffff, }; enum SelectionControlShift { SelectionControlFlattenShift = 0, SelectionControlDontFlattenShift = 1, + SelectionControlMax = 0x7fffffff, }; enum SelectionControlMask { @@ -415,12 +480,17 @@ enum SelectionControlMask { enum LoopControlShift { LoopControlUnrollShift = 0, LoopControlDontUnrollShift = 1, + LoopControlDependencyInfiniteShift = 2, + LoopControlDependencyLengthShift = 3, + LoopControlMax = 0x7fffffff, }; enum LoopControlMask { LoopControlMaskNone = 0, LoopControlUnrollMask = 0x00000001, LoopControlDontUnrollMask = 0x00000002, + LoopControlDependencyInfiniteMask = 0x00000004, + LoopControlDependencyLengthMask = 0x00000008, }; enum FunctionControlShift { @@ -428,6 +498,7 @@ enum FunctionControlShift { FunctionControlDontInlineShift = 1, FunctionControlPureShift = 2, FunctionControlConstShift = 3, + FunctionControlMax = 0x7fffffff, }; enum FunctionControlMask { @@ -449,6 +520,7 @@ enum MemorySemanticsShift { MemorySemanticsCrossWorkgroupMemoryShift = 9, MemorySemanticsAtomicCounterMemoryShift = 10, MemorySemanticsImageMemoryShift = 11, + MemorySemanticsMax = 0x7fffffff, }; enum MemorySemanticsMask { @@ -469,6 +541,7 @@ enum MemoryAccessShift { MemoryAccessVolatileShift = 0, MemoryAccessAlignedShift = 1, MemoryAccessNontemporalShift = 2, + MemoryAccessMax = 0x7fffffff, }; enum MemoryAccessMask { @@ -484,22 +557,26 @@ enum Scope { ScopeWorkgroup = 2, ScopeSubgroup = 3, ScopeInvocation = 4, + ScopeMax = 0x7fffffff, }; enum GroupOperation { GroupOperationReduce = 0, GroupOperationInclusiveScan = 1, GroupOperationExclusiveScan = 2, + GroupOperationMax = 0x7fffffff, }; enum KernelEnqueueFlags { KernelEnqueueFlagsNoWait = 0, KernelEnqueueFlagsWaitKernel = 1, KernelEnqueueFlagsWaitWorkGroup = 2, + KernelEnqueueFlagsMax = 0x7fffffff, }; enum KernelProfilingInfoShift { KernelProfilingInfoCmdExecTimeShift = 0, + KernelProfilingInfoMax = 0x7fffffff, }; enum KernelProfilingInfoMask { @@ -564,6 +641,40 @@ enum Capability { CapabilityStorageImageReadWithoutFormat = 55, CapabilityStorageImageWriteWithoutFormat = 56, CapabilityMultiViewport = 57, + CapabilitySubgroupDispatch = 58, + CapabilityNamedBarrier = 59, + CapabilityPipeStorage = 60, + CapabilitySubgroupBallotKHR = 4423, + CapabilityDrawParameters = 4427, + CapabilitySubgroupVoteKHR = 4431, + CapabilityStorageBuffer16BitAccess = 4433, + CapabilityStorageUniformBufferBlock16 = 4433, + CapabilityStorageUniform16 = 4434, + CapabilityUniformAndStorageBuffer16BitAccess = 4434, + CapabilityStoragePushConstant16 = 4435, + CapabilityStorageInputOutput16 = 4436, + CapabilityDeviceGroup = 4437, + CapabilityMultiView = 4439, + CapabilityVariablePointersStorageBuffer = 4441, + CapabilityVariablePointers = 4442, + CapabilityAtomicStorageOps = 4445, + CapabilitySampleMaskPostDepthCoverage = 4447, + CapabilityImageGatherBiasLodAMD = 5009, + CapabilityFragmentMaskAMD = 5010, + CapabilityStencilExportEXT = 5013, + CapabilityImageReadWriteLodAMD = 5015, + CapabilitySampleMaskOverrideCoverageNV = 5249, + CapabilityGeometryShaderPassthroughNV = 5251, + CapabilityShaderViewportIndexLayerEXT = 5254, + CapabilityShaderViewportIndexLayerNV = 5254, + CapabilityShaderViewportMaskNV = 5255, + CapabilityShaderStereoViewNV = 5259, + CapabilityPerViewAttributesNV = 5260, + CapabilityFragmentFullyCoveredEXT = 5265, + CapabilitySubgroupShuffleINTEL = 5568, + CapabilitySubgroupBufferBlockIOINTEL = 5569, + CapabilitySubgroupImageBlockIOINTEL = 5570, + CapabilityMax = 0x7fffffff, }; enum Op { @@ -861,6 +972,43 @@ enum Op { OpAtomicFlagTestAndSet = 318, OpAtomicFlagClear = 319, OpImageSparseRead = 320, + OpSizeOf = 321, + OpTypePipeStorage = 322, + OpConstantPipeStorage = 323, + OpCreatePipeFromPipeStorage = 324, + OpGetKernelLocalSizeForSubgroupCount = 325, + OpGetKernelMaxNumSubgroups = 326, + OpTypeNamedBarrier = 327, + OpNamedBarrierInitialize = 328, + OpMemoryNamedBarrier = 329, + OpModuleProcessed = 330, + OpExecutionModeId = 331, + OpDecorateId = 332, + OpSubgroupBallotKHR = 4421, + OpSubgroupFirstInvocationKHR = 4422, + OpSubgroupAllKHR = 4428, + OpSubgroupAnyKHR = 4429, + OpSubgroupAllEqualKHR = 4430, + OpSubgroupReadInvocationKHR = 4432, + OpGroupIAddNonUniformAMD = 5000, + OpGroupFAddNonUniformAMD = 5001, + OpGroupFMinNonUniformAMD = 5002, + OpGroupUMinNonUniformAMD = 5003, + OpGroupSMinNonUniformAMD = 5004, + OpGroupFMaxNonUniformAMD = 5005, + OpGroupUMaxNonUniformAMD = 5006, + OpGroupSMaxNonUniformAMD = 5007, + OpFragmentMaskFetchAMD = 5011, + OpFragmentFetchAMD = 5012, + OpSubgroupShuffleINTEL = 5571, + OpSubgroupShuffleDownINTEL = 5572, + OpSubgroupShuffleUpINTEL = 5573, + OpSubgroupShuffleXorINTEL = 5574, + OpSubgroupBlockReadINTEL = 5575, + OpSubgroupBlockWriteINTEL = 5576, + OpSubgroupImageBlockReadINTEL = 5577, + OpSubgroupImageBlockWriteINTEL = 5578, + OpMax = 0x7fffffff, }; // Overload operator| for mask bit combining @@ -877,3 +1025,4 @@ inline KernelProfilingInfoMask operator|(KernelProfilingInfoMask a, KernelProfil } // end namespace spv #endif // #ifndef spirv_HPP + diff --git a/third_party/glslang-spirv/spvIR.h b/third_party/glslang-spirv/spvIR.h index 63e460ebb..faa2701ff 100644 --- a/third_party/glslang-spirv/spvIR.h +++ b/third_party/glslang-spirv/spvIR.h @@ -1,11 +1,11 @@ // -//Copyright (C) 2014 LunarG, Inc. +// Copyright (C) 2014 LunarG, Inc. // -//All rights reserved. +// All rights reserved. // -//Redistribution and use in source and binary forms, with or without -//modification, are permitted provided that the following conditions -//are met: +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: // // Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. @@ -19,30 +19,26 @@ // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // -//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -//"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -//LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -//FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -//COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -//INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -//BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -//LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -//CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -//ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -//POSSIBILITY OF SUCH DAMAGE. - -// -// Author: John Kessenich, LunarG -// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. // SPIRV-IR // // Simple in-memory representation (IR) of SPIRV. Just for holding // Each function's CFG of blocks. Has this hierarchy: -// - Module, which is a list of -// - Function, which is a list of -// - Block, which is a list of +// - Module, which is a list of +// - Function, which is a list of +// - Block, which is a list of // - Instruction // @@ -68,17 +64,18 @@ class Module; const Id NoResult = 0; const Id NoType = 0; -const unsigned int BadValue = 0xFFFFFFFF; -const Decoration NoPrecision = (Decoration)BadValue; -const MemorySemanticsMask MemorySemanticsAllMemory = - (MemorySemanticsMask)(MemorySemanticsAcquireMask | - MemorySemanticsReleaseMask | - MemorySemanticsAcquireReleaseMask | - MemorySemanticsSequentiallyConsistentMask | - MemorySemanticsUniformMemoryMask | - MemorySemanticsSubgroupMemoryMask | +const Decoration NoPrecision = DecorationMax; + +#ifdef __GNUC__ +# define POTENTIALLY_UNUSED __attribute__((unused)) +#else +# define POTENTIALLY_UNUSED +#endif + +POTENTIALLY_UNUSED +const MemorySemanticsMask MemorySemanticsAllMemory = + (MemorySemanticsMask)(MemorySemanticsUniformMemoryMask | MemorySemanticsWorkgroupMemoryMask | - MemorySemanticsCrossWorkgroupMemoryMask | MemorySemanticsAtomicCounterMemoryMask | MemorySemanticsImageMemoryMask); @@ -95,7 +92,6 @@ public: void addImmediateOperand(unsigned int immediate) { operands.push_back(immediate); } void addStringOperand(const char* str) { - originalString = str; unsigned int word; char* wordString = (char*)&word; char* wordPtr = wordString; @@ -128,7 +124,6 @@ public: Id getTypeId() const { return typeId; } Id getIdOperand(int op) const { return operands[op]; } unsigned int getImmediateOperand(int op) const { return operands[op]; } - const char* getStringOperand() const { return originalString.c_str(); } // Write out the binary form. void dump(std::vector& out) const @@ -159,7 +154,6 @@ protected: Id typeId; Op opCode; std::vector operands; - std::string originalString; // could be optimized away; convenience for getting string operand Block* block; }; @@ -180,13 +174,11 @@ public: void addInstruction(std::unique_ptr inst); void addPredecessor(Block* pred) { predecessors.push_back(pred); pred->successors.push_back(this);} void addLocalVariable(std::unique_ptr inst) { localVariables.push_back(std::move(inst)); } - void insertInstruction(size_t pos, std::unique_ptr inst); - - size_t getInstructionCount() { return instructions.size(); } - Instruction* getInstruction(size_t i) { return instructions[i].get(); } - void removeInstruction(size_t i) { instructions.erase(instructions.begin() + i); } const std::vector& getPredecessors() const { return predecessors; } const std::vector& getSuccessors() const { return successors; } + const std::vector >& getInstructions() const { + return instructions; + } void setUnreachable() { unreachable = true; } bool isUnreachable() const { return unreachable; } // Returns the block's merge instruction, if one exists (otherwise null). @@ -205,10 +197,6 @@ public: bool isTerminated() const { - if (instructions.size() == 0) { - return false; - } - switch (instructions.back()->getOpCode()) { case OpBranch: case OpBranchConditional: @@ -224,7 +212,6 @@ public: void dump(std::vector& out) const { - // OpLabel instructions[0]->dump(out); for (int i = 0; i < (int)localVariables.size(); ++i) localVariables[i]->dump(out); @@ -232,51 +219,7 @@ public: instructions[i]->dump(out); } - // Moves all instructions from a target block into this block, and removes - // the target block from our list of successors. - // This function assumes this block unconditionally branches to the target - // block directly. - void merge(Block* target_block) { - if (isTerminated()) { - instructions.erase(instructions.end() - 1); - } - - // Find the target block in our successors first. - for (auto it = successors.begin(); it != successors.end(); ++it) { - if (*it == target_block) { - it = successors.erase(it); - break; - } - } - - // Add target block's successors to our successors. - successors.insert(successors.end(), target_block->successors.begin(), - target_block->successors.end()); - - // For each successor, replace the target block in their predecessors with - // us. - for (auto block : successors) { - std::replace(block->predecessors.begin(), block->predecessors.end(), - target_block, this); - } - - // Move instructions from target block into this block. - for (auto it = target_block->instructions.begin(); - it != target_block->instructions.end();) { - if ((*it)->getOpCode() == spv::Op::OpLabel) { - ++it; - continue; - } - - instructions.push_back(std::move(*it)); - it = target_block->instructions.erase(it); - } - - target_block->predecessors.clear(); - target_block->successors.clear(); - } - - protected: +protected: Block(const Block&); Block& operator=(Block&); @@ -288,7 +231,7 @@ public: std::vector > localVariables; Function& parent; - // track whether this block is known to be uncreachable (not necessarily + // track whether this block is known to be uncreachable (not necessarily // true for all unreachable blocks, but should be set at least // for the extraneous ones introduced by the builder). bool unreachable; @@ -329,19 +272,13 @@ public: Module& getParent() const { return parent; } Block* getEntryBlock() const { return blocks.front(); } Block* getLastBlock() const { return blocks.back(); } - Block* findBlockById(Id id) - { - for (auto block : blocks) { - if (block->getId() == id) { - return block; - } - } - - return nullptr; - } - std::vector& getBlocks() { return blocks; } + const std::vector& getBlocks() const { return blocks; } void addLocalVariable(std::unique_ptr inst); Id getReturnType() const { return functionInstruction.getTypeId(); } + + void setImplicitThis() { implicitThis = true; } + bool hasImplicitThis() const { return implicitThis; } + void dump(std::vector& out) const { // OpFunction @@ -365,6 +302,7 @@ protected: Instruction functionInstruction; std::vector parameterInstructions; std::vector blocks; + bool implicitThis; // true if this is a member function expecting to be passed a 'this' as the first argument }; // @@ -380,8 +318,6 @@ public: } void addFunction(Function *fun) { functions.push_back(fun); } - const std::vector& getFunctions() const { return functions; } - std::vector& getFunctions() { return functions; } void mapInstruction(Instruction *instruction) { @@ -393,6 +329,7 @@ public: } Instruction* getInstruction(Id id) const { return idToInstruction[id]; } + const std::vector& getFunctions() const { return functions; } spv::Id getTypeId(Id resultId) const { return idToInstruction[resultId]->getTypeId(); } StorageClass getStorageClass(Id typeId) const { @@ -424,7 +361,7 @@ protected: // - the OpFunction instruction // - all the OpFunctionParameter instructions __inline Function::Function(Id id, Id resultType, Id functionType, Id firstParamId, Module& parent) - : parent(parent), functionInstruction(id, resultType, OpFunction) + : parent(parent), functionInstruction(id, resultType, OpFunction), implicitThis(false) { // OpFunction functionInstruction.addImmediateOperand(FunctionControlMaskNone); @@ -465,14 +402,6 @@ __inline void Block::addInstruction(std::unique_ptr inst) parent.getParent().mapInstruction(raw_instruction); } -__inline void Block::insertInstruction(size_t pos, std::unique_ptr inst) { - Instruction* raw_instruction = inst.get(); - instructions.insert(instructions.begin() + pos, std::move(inst)); - raw_instruction->setBlock(this); - if (raw_instruction->getResultId()) - parent.getParent().mapInstruction(raw_instruction); -} - }; // end spv namespace #endif // spvIR_H