summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDrChat <arkolbed@gmail.com>2018-02-18 20:13:38 -0600
committerDrChat <arkolbed@gmail.com>2018-02-18 20:13:38 -0600
commitf3f1a7fe42f8cd22655ebff3c64b342f8974dfd3 (patch)
treeda3615d5ba11da7357cf7789abc6210ec7417da4
parent4e1a55f585dd3127f753a13c91a0837a1d836e73 (diff)
[SPIR-V] Update glslang
WIP new vertex fetching
-rw-r--r--src/xenia/gpu/premake5.lua4
-rw-r--r--src/xenia/gpu/spirv_shader_translator.cc113
-rw-r--r--src/xenia/gpu/spirv_shader_translator.h3
-rw-r--r--third_party/glslang-spirv/GLSL.ext.AMD.h110
-rw-r--r--third_party/glslang-spirv/GLSL.ext.EXT.h39
-rw-r--r--third_party/glslang-spirv/GLSL.ext.KHR.h48
-rw-r--r--third_party/glslang-spirv/GLSL.ext.NV.h54
-rw-r--r--third_party/glslang-spirv/GlslangToSpv.cpp3340
-rw-r--r--third_party/glslang-spirv/GlslangToSpv.h64
-rw-r--r--third_party/glslang-spirv/InReadableOrder.cpp59
-rw-r--r--third_party/glslang-spirv/Include/BaseTypes.h387
-rw-r--r--third_party/glslang-spirv/Include/Common.h274
-rw-r--r--third_party/glslang-spirv/Include/ConstantUnion.h625
-rw-r--r--third_party/glslang-spirv/Include/InfoSink.h144
-rw-r--r--third_party/glslang-spirv/Include/InitializeGlobals.h44
-rw-r--r--third_party/glslang-spirv/Include/PoolAlloc.h317
-rw-r--r--third_party/glslang-spirv/Include/ResourceLimits.h140
-rw-r--r--third_party/glslang-spirv/Include/ShHandle.h176
-rw-r--r--third_party/glslang-spirv/Include/Types.h1924
-rw-r--r--third_party/glslang-spirv/Include/arrays.h329
-rw-r--r--third_party/glslang-spirv/Include/intermediate.h1486
-rw-r--r--third_party/glslang-spirv/Include/revision.h6
-rw-r--r--third_party/glslang-spirv/Include/revision.template13
-rw-r--r--third_party/glslang-spirv/Logger.cpp68
-rw-r--r--third_party/glslang-spirv/Logger.h74
-rw-r--r--third_party/glslang-spirv/SPVRemapper.cpp474
-rw-r--r--third_party/glslang-spirv/SPVRemapper.h100
-rw-r--r--third_party/glslang-spirv/SpvBuilder.cpp706
-rw-r--r--third_party/glslang-spirv/SpvBuilder.h169
-rw-r--r--third_party/glslang-spirv/bitutils.h81
-rw-r--r--third_party/glslang-spirv/disassemble.cpp191
-rw-r--r--third_party/glslang-spirv/disassemble.h38
-rw-r--r--third_party/glslang-spirv/doc.cpp243
-rw-r--r--third_party/glslang-spirv/doc.h45
-rw-r--r--third_party/glslang-spirv/hex_float.h1078
-rw-r--r--third_party/glslang-spirv/spirv.hpp155
-rw-r--r--third_party/glslang-spirv/spvIR.h159
37 files changed, 11966 insertions, 1314 deletions
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 <vector>
#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<spirv::ControlFlowSimplificationPass>());
- compiler_.AddPass(std::make_unique<spirv::ControlFlowAnalysisPass>());
-}
-
+SpirvShaderTranslator::SpirvShaderTranslator() {}
SpirvShaderTranslator::~SpirvShaderTranslator() = default;
void SpirvShaderTranslator::StartTranslation() {
// Create a new builder.
- builder_ = std::make_unique<spv::Builder>(0xFFFFFFFF);
+ builder_ = std::make_unique<spv::Builder>(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<uint8_t> 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<uint8_t> 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<spv::Block*> switch_segments;
- b.makeSwitch(alpha_test_func, 8, std::vector<int>({0, 1, 2, 3, 4, 5, 6, 7}),
- std::vector<int>({0, 1, 2, 3, 4, 5, 6, 7}), 7,
- switch_segments);
+ b.makeSwitch(
+ alpha_test_func, 0, 8, std::vector<int>({0, 1, 2, 3, 4, 5, 6, 7}),
+ std::vector<int>({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<uint8_t> 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(&params, 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(&params, 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<spv::Id> 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,22 +20,20 @@
// 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 <string>
-#include <map>
+#include <fstream>
+#include <iomanip>
#include <list>
-#include <vector>
+#include <map>
#include <stack>
-#include <fstream>
+#include <string>
+#include <vector>
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<unsigned int>& 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<unsigned>& 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<spv::Id>& operands, glslang::TBasicType typeProxy);
+ spv::Id createInvocationsOperation(glslang::TOperator op, spv::Id typeId, std::vector<spv::Id>& operands, glslang::TBasicType typeProxy);
+ spv::Id CreateInvocationsVectorOperation(spv::Op op, spv::GroupOperation groupOperation, spv::Id typeId, std::vector<spv::Id>& operands);
spv::Id createMiscOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId, std::vector<spv::Id>& 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<spv::Id> iOSet; // all input/output variables from either static use or declaration of interface
const glslang::TIntermediate* glslangIntermediate;
spv::Id stdBuiltins;
+ std::unordered_map<const char*, spv::Id> extBuiltinMap;
std::unordered_map<int, spv::Id> symbolValues;
- std::unordered_set<int> 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<int> rValueParameters; // set of formal function parameters passed as rValues, rather than a pointer
std::unordered_map<std::string, spv::Function*> functionMap;
std::unordered_map<const glslang::TTypeList*, spv::Id> structMap[glslang::ElpCount][glslang::ElmCount];
std::unordered_map<const glslang::TTypeList*, std::vector<int> > 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)
-{
- switch (profile) {
- case ENoProfile:
- case ECoreProfile:
- case ECompatibilityProfile:
- return spv::SourceLanguageGLSL;
- case EEsProfile:
- return spv::SourceLanguageESSL;
+spv::SourceLanguage TranslateSourceLanguage(glslang::EShSource source, EProfile profile)
+{
+ 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<spv::Decoration>& 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;
+ }
+}
+
+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
+// 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<std::string>& 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<unsigned int>& out)
+// Finish creating SPV, after the traversal is complete.
+void TGlslangToSpvTraverser::finishSpv()
{
+ if (! entryPointTerminated) {
+ builder.setBuildPoint(shaderEntry->getLastBlock());
+ builder.leaveFunction();
+ }
+
// finish off the entry-point SPV instruction by adding the Input/Output <id>
- for (auto it : iOSet)
- entryPoint->addIdOperand(it);
+ for (auto it = iOSet.cbegin(); it != iOSet.cend(); ++it)
+ entryPoint->addIdOperand(*it);
- builder.dump(out);
+ builder.eliminateDeadDecorations();
}
-TGlslangToSpvTraverser::~TGlslangToSpvTraverser()
+// Write the SPV into 'out'.
+void TGlslangToSpvTraverser::dumpSpv(std::vector<unsigned int>& out)
{
- if (! mainTerminated) {
- spv::Block* lastMainBlock = shaderEntry->getLastBlock();
- builder.setBuildPoint(lastMainBlock);
- builder.leaveFunction();
- }
+ 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<int>& 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<unsigned> 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<int>& 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<unsigned> 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<spv::Id> 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<spv::Id> 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<spv::Id> 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);
+ // Selection control:
+ const spv::SelectionControlMask control = TranslateSelectionControl(*node);
+
// make an "if" based on the value created by the condition
- spv::Builder::If ifBuilder(accessChainLoad(node->getCondition()->getType()), builder);
+ spv::Builder::If ifBuilder(accessChainLoad(node->getCondition()->getType()), control, builder);
- if (node->getTrueBlock()) {
- // emit the "then" statement
+ // 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<TIntermNode*> 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<spv::Block*> 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<unsigned> 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<unsigned>& 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<spv::Id> 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<spv::Id> 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<spv::Decoration> 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)
+{
+ 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)
{
- return node->getName() == "main(";
+ 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<spv::Decoration>& 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<spv::Id> paramTypes;
- std::vector<spv::Decoration> paramPrecisions;
+ std::vector<std::vector<spv::Decoration>> 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<spv::Id> operands;
@@ -2285,9 +3400,48 @@ 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<spv::Id> 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<spv::Id> 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();
@@ -2295,10 +3449,22 @@ spv::Id TGlslangToSpvTraverser::createImageTextureFunctionCall(glslang::TIntermO
// 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<spv::Id> 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;
}
- if (bias) {
- params.bias = 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.comp = arguments[2 + extraArgs];
+ params.component = arguments[2 + extraArgs];
++extraArgs;
- } else {
- params.comp = builder.makeIntConstant(0);
+ } else
+ params.component = builder.makeIntConstant(0);
+ }
+
+ // bias
+ if (bias) {
+ params.bias = arguments[2 + extraArgs];
+ ++extraArgs;
+ }
+
+ // 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<spv::Id> rValues;
std::vector<const glslang::TType*> 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(&paramType);
+ // 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<spv::Id> 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<spv::Id> 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<spv::Id> 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<spv::Id> results;
// do each vector op
for (int c = 0; c < numCols; ++c) {
std::vector<unsigned int> 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<spv::Id> 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<spv::Id>& 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<spv::Id> 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<spv::Id> 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<spv::Id>& 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<spv::Id> results;
+
+ // do each scalar op
+ for (int comp = 0; comp < numComponents; ++comp) {
+ std::vector<unsigned int> indexes;
+ indexes.push_back(comp);
+ spv::Id scalar = builder.createCompositeExtract(operands[0], scalarType, indexes);
+ std::vector<spv::Id> 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<spv::Id>& 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<spv::Id> 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<spv::Id> 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<spv::Decoration> 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<spv::Id> 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<spv::Id> 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<spv::Id> 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<glslang::TTypeLoc>::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<spv::Id> 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<unsigned int>& spirv, const char* baseName)
+void OutputSpvBin(const std::vector<unsigned int>& 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<unsigned int>& spirv, const char* baseName)
out.close();
}
+// Write SPIR-V out to a text file with 32-bit hexadecimal words
+void OutputSpvHex(const std::vector<unsigned int>& 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<unsigned int>& spirv)
+void GlslangToSpv(const glslang::TIntermediate& intermediate, std::vector<unsigned int>& spirv, SpvOptions* options)
+{
+ spv::SpvBuildLogger logger;
+ GlslangToSpv(intermediate, spirv, &logger, options);
+}
+
+void GlslangToSpv(const glslang::TIntermediate& intermediate, std::vector<unsigned int>& spirv,
+ spv::SpvBuildLogger* logger, SpvOptions* options)
{
TIntermNode* root = intermediate.getTreeRoot();
if (root == 0)
return;
- glslang::GetThreadPoolAllocator().push();
+ glslang::SpvOptions defaultOptions;
+ if (options == nullptr)
+ options = &defaultOptions;
- TGlslangToSpvTraverser it(&intermediate);
+ glslang::GetThreadPoolAllocator().push();
+ 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 <string>
+#include <vector>
+
+#include "Logger.h"
+
namespace glslang {
+struct SpvOptions {
+ SpvOptions() : generateDebugInfo(false), disableOptimizer(true),
+ optimizeSize(false) { }
+ bool generateDebugInfo;
+ bool disableOptimizer;
+ bool optimizeSize;
+};
+
void GetSpirvVersion(std::string&);
-void GlslangToSpv(const glslang::TIntermediate& intermediate, std::vector<unsigned int>& spirv);
-void OutputSpv(const std::vector<unsigned int>& spirv, const char* baseName);
+int GetSpirvGeneratorVersion();
+void GlslangToSpv(const glslang::TIntermediate& intermediate, std::vector<unsigned int>& spirv,
+ SpvOptions* options = nullptr);
+void GlslangToSpv(const glslang::TIntermediate& intermediate, std::vector<unsigned int>& spirv,
+ spv::SpvBuildLogger* logger, SpvOptions* options = nullptr);
+void OutputSpvBin(const std::vector<unsigned int>& spirv, const char* baseName);
+void OutputSpvHex(const std::vector<unsigned int>& 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 <cassert>
-#include <unordered_map>
+#include <unordered_set>
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<void(Block*)> callback_;
// Whether a block has already been visited or is being delayed.
- std::unordered_map<Block *, bool> visited_, delayed_;
+ std::unordered_set<Block *> 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 <basetsd.h>
+ #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 <sys/int_types.h>
+ #define UINT_PTR uintptr_t
+#else
+ #define safe_vsprintf(buf,max,format,args) vsnprintf((buf), (max), (format), (args))
+ #include <stdint.h>
+ #define UINT_PTR uintptr_t
+#endif
+
+#if defined(__ANDROID__) || _MSC_VER < 1700
+#include <sstream>
+namespace std {
+template<typename T>
+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 <set>
+#include <unordered_set>
+#include <vector>
+#include <map>
+#include <unordered_map>
+#include <list>
+#include <algorithm>
+#include <string>
+#include <cstdio>
+#include <cassert>
+
+#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<char> TStringAllocator;
+ typedef std::basic_string <char, std::char_traits<char>, TStringAllocator> TString;
+
+} // end namespace glslang
+
+// Repackage the std::hash for use by unordered map/set with a TString key.
+namespace std {
+
+ template<> struct hash<glslang::TString> {
+ 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<class T> inline T* NewPoolObject(T*)
+{
+ return new(GetThreadPoolAllocator().allocate(sizeof(T))) T;
+}
+
+template<class T> 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 T> class TVector : public std::vector<T, pool_allocator<T> > {
+public:
+ POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator())
+
+ typedef typename std::vector<T, pool_allocator<T> >::size_type size_type;
+ TVector() : std::vector<T, pool_allocator<T> >() {}
+ TVector(const pool_allocator<T>& a) : std::vector<T, pool_allocator<T> >(a) {}
+ TVector(size_type i) : std::vector<T, pool_allocator<T> >(i) {}
+ TVector(size_type i, const T& val) : std::vector<T, pool_allocator<T> >(i, val) {}
+};
+
+template <class T> class TList : public std::list<T, pool_allocator<T> > {
+};
+
+template <class K, class D, class CMP = std::less<K> >
+class TMap : public std::map<K, D, CMP, pool_allocator<std::pair<K const, D> > > {
+};
+
+template <class K, class D, class HASH = std::hash<K>, class PRED = std::equal_to<K> >
+class TUnorderedMap : public std::unordered_map<K, D, HASH, PRED, pool_allocator<std::pair<K const, D> > > {
+};
+
+//
+// Persistent string memory. Should only be used for strings that survive
+// across compiles/links.
+//
+typedef std::basic_string<char> TPersistString;
+
+//
+// templatized min and max functions.
+//
+template <class T> T Min(const T a, const T b) { return a < b ? a : b; }
+template <class T> 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<TString, TString> {
+public:
+ POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator())
+};
+
+const int MaxTokenLength = 1024;
+
+template <class T> 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 <class T> void RoundToPow2(T& number, int powerOf2)
+{
+ assert(IsPow2(powerOf2));
+ number = (number + powerOf2 - 1) & ~(powerOf2 - 1);
+}
+
+template <class T> 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<TConstUnion> 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 <cmath>
+
+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 <cstddef>
+#include <cstring>
+#include <vector>
+
+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<tAllocState> 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<TAllocation*>(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 T>
+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<class Other>
+ struct rebind {
+ typedef pool_allocator<Other> 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<T>& p) : allocator(p.allocator) { }
+
+ template<class Other>
+ pool_allocator(const pool_allocator<Other>& p) : allocator(p.getAllocator()) { }
+
+ pointer allocate(size_type n) {
+ return reinterpret_cast<pointer>(getAllocator().allocate(n * sizeof(T))); }
+ pointer allocate(size_type n, const void*) {
+ return reinterpret_cast<pointer>(getAllocator().allocate(n * sizeof(T))); }
+
+ void deallocate(void*, size_type) { }
+ void deallocate(pointer, size_type) { }
+
+ pointer _Charalloc(size_t n) {
+ return reinterpret_cast<pointer>(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<size_type>(-1) / sizeof(T); }
+ size_type max_size(int size) const { return static_cast<size_type>(-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<TCompiler*> TCompilerList;
+typedef glslang::TVector<TShHandleBase*> 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 <algorithm>
+
+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<<structReturnIndexBits)-1; // number of valid values
+ static const unsigned noReturnStruct = structReturnSlots; // value if no return struct type.
+
+ // Index into a language specific table of texture return structures.
+ unsigned int structReturnIndex : structReturnIndexBits;
+
+ // Encapsulate getting members' vector sizes packed into the vectorSize bitfield.
+ unsigned int getVectorSize() const { return vectorSize; }
+
+ bool isImage() const { return image && dim != EsdSubpass; }
+ bool isSubpass() const { return dim == EsdSubpass; }
+ bool isCombined() const { return combined; }
+ bool isPureSampler() const { return sampler; }
+ bool isTexture() const { return !sampler && !image; }
+ bool isShadow() const { return shadow; }
+ bool isArrayed() const { return arrayed; }
+ bool isMultiSample() const { return ms; }
+ bool hasReturnStruct() const { return structReturnIndex != noReturnStruct; }
+
+ void clear()
+ {
+ type = EbtVoid;
+ dim = EsdNone;
+ arrayed = false;
+ shadow = false;
+ ms = false;
+ image = false;
+ combined = false;
+ sampler = false;
+ external = false;
+ structReturnIndex = noReturnStruct;
+
+ // by default, returns a single vec4;
+ vectorSize = 4;
+ }
+
+ // make a combined sampler and texture
+ void set(TBasicType t, TSamplerDim d, bool a = false, bool s = false, bool m = false)
+ {
+ clear();
+ type = t;
+ dim = d;
+ arrayed = a;
+ shadow = s;
+ ms = m;
+ combined = true;
+ }
+
+ // make an image
+ void setImage(TBasicType t, TSamplerDim d, bool a = false, bool s = false, bool m = false)
+ {
+ clear();
+ type = t;
+ dim = d;
+ arrayed = a;
+ shadow = s;
+ ms = m;
+ image = true;
+ }
+
+ // make a texture with no sampler
+ void setTexture(TBasicType t, TSamplerDim d, bool a = false, bool s = false, bool m = false)
+ {
+ clear();
+ type = t;
+ dim = d;
+ arrayed = a;
+ shadow = s;
+ ms = m;
+ }
+
+ // make a subpass input attachment
+ void setSubpass(TBasicType t, bool m = false)
+ {
+ clear();
+ type = t;
+ image = true;
+ dim = EsdSubpass;
+ ms = m;
+ }
+
+ // make a pure sampler, no texture, no image, nothing combined, the 'sampler' keyword
+ void setPureSampler(bool s)
+ {
+ clear();
+ sampler = true;
+ shadow = s;
+ }
+
+ bool operator==(const TSampler& right) const
+ {
+ return type == right.type &&
+ dim == right.dim &&
+ arrayed == right.arrayed &&
+ shadow == right.shadow &&
+ ms == right.ms &&
+ image == right.image &&
+ combined == right.combined &&
+ sampler == right.sampler &&
+ external == right.external &&
+ vectorSize == right.vectorSize &&
+ structReturnIndex == right.structReturnIndex;
+ }
+
+ bool operator!=(const TSampler& right) const
+ {
+ return ! operator==(right);
+ }
+
+ TString getString() const
+ {
+ TString s;
+
+ if (sampler) {
+ s.append("sampler");
+ return s;
+ }
+
+ switch (type) {
+ case EbtFloat: break;
+ case EbtInt: s.append("i"); break;
+ case EbtUint: s.append("u"); break;
+ default: break; // some compilers want this
+ }
+ if (image) {
+ if (dim == EsdSubpass)
+ s.append("subpass");
+ else
+ s.append("image");
+ } else if (combined) {
+ s.append("sampler");
+ } else {
+ s.append("texture");
+ }
+ if (external) {
+ s.append("ExternalOES");
+ return s;
+ }
+ switch (dim) {
+ case Esd1D: s.append("1D"); break;
+ case Esd2D: s.append("2D"); break;
+ case Esd3D: s.append("3D"); break;
+ case EsdCube: s.append("Cube"); break;
+ case EsdRect: s.append("2DRect"); break;
+ case EsdBuffer: s.append("Buffer"); break;
+ case EsdSubpass: s.append("Input"); break;
+ default: break; // some compilers want this
+ }
+ if (ms)
+ s.append("MS");
+ if (arrayed)
+ s.append("Array");
+ if (shadow)
+ s.append("Shadow");
+
+ return s;
+ }
+};
+
+//
+// Need to have association of line numbers to types in a list for building structs.
+//
+class TType;
+struct TTypeLoc {
+ TType* type;
+ TSourceLoc loc;
+};
+typedef TVector<TTypeLoc> TTypeList;
+
+typedef TVector<TString*> 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<TTypeList*,TTypeList*> 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 <typename P>
+ 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<TTypeList*,TTypeList*>& 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<TArraySize>;
+ }
+ void dealloc()
+ {
+ delete sizes;
+ sizes = nullptr;
+ }
+
+ TVector<TArraySize>* 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<TIntermNode*> TIntermSequence;
+typedef TVector<TStorageQualifier> 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<TIntermNode *> 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 <algorithm>
+#include <iterator>
+#include <sstream>
+
+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 <string>
+#include <vector>
+
+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<std::string> tbdFeatures;
+ std::vector<std::string> missingFeatures;
+ std::vector<std::string> warnings;
+ std::vector<std::string> 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 <algorithm>
#include <cassert>
+#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 <id> 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<numLiteralIdPairs; ++arg) {
+ word += literalSize; // literal
+ idFn(asId(word++)); // label
+ }
+ } else {
+ assert(0); // currentely, only OpSwitch uses OperandVariableLiteralId
}
+
return nextInst;
+ }
case spv::OperandLiteralString: {
const int stringWordCount = literalStringWords(literalString(word));
@@ -499,9 +653,7 @@ namespace spv {
case spv::OperandSelect:
case spv::OperandLoop:
case spv::OperandFunction:
- case spv::OperandMemorySemantics:
case spv::OperandMemoryAccess:
- case spv::OperandScope:
case spv::OperandGroupOperation:
case spv::OperandKernelEnqueueFlags:
case spv::OperandKernelProfilingInfo:
@@ -531,9 +683,13 @@ namespace spv {
// basic parsing and InstructionDesc table borrowed from SpvDisassemble.cpp...
unsigned nextInst = unsigned(spv.size());
- for (unsigned word = begin; word < end; word = nextInst)
+ for (unsigned word = begin; word < end; word = nextInst) {
nextInst = processInstruction(word, instFn, idFn);
+ if (errorLatch)
+ return *this;
+ }
+
return *this;
}
@@ -548,8 +704,11 @@ namespace spv {
for (const char c : name.first)
hashval = hashval * 1009 + c;
- if (isOldIdUnmapped(name.second))
+ if (isOldIdUnmapped(name.second)) {
localId(name.second, nextUnusedId(hashval % softTypeIdLimit + firstMappedID));
+ if (errorLatch)
+ return;
+ }
}
}
@@ -571,8 +730,11 @@ namespace spv {
[&](spv::Op, unsigned start) { instPos.push_back(start); return true; },
op_fn_nop);
+ if (errorLatch)
+ return;
+
// Window size for context-sensitive canonicalization values
- // Emperical best size from a single data set. TODO: Would be a good tunable.
+ // Empirical best size from a single data set. TODO: Would be a good tunable.
// We essentially perform a little convolution around each instruction,
// to capture the flavor of nearby code, to hopefully match to similar
// code in other modules.
@@ -606,8 +768,12 @@ namespace spv {
hashval = hashval * 30103 + asOpCodeHash(instPos[i]); // 30103 = semiarbitrary prime
}
- if (isOldIdUnmapped(resId))
+ if (isOldIdUnmapped(resId)) {
localId(resId, nextUnusedId(hashval % softTypeIdLimit + firstMappedID));
+ if (errorLatch)
+ return;
+ }
+
}
}
}
@@ -700,6 +866,9 @@ namespace spv {
[&](spv::Id& id) { if (idMap.find(id) != idMap.end()) id = idMap[id]; }
);
+ if (errorLatch)
+ return;
+
// EXPERIMENTAL: Implicit output stores
fnLocalVars.clear();
idMap.clear();
@@ -720,11 +889,17 @@ namespace spv {
},
op_fn_nop);
+ if (errorLatch)
+ return;
+
process(
inst_fn_nop,
[&](spv::Id& id) { if (idMap.find(id) != idMap.end()) id = idMap[id]; }
);
+ if (errorLatch)
+ return;
+
strip(); // strip out data we decided to eliminate
}
@@ -816,7 +991,7 @@ namespace spv {
},
// If local var id used anywhere else, don't eliminate
- [&](spv::Id& id) {
+ [&](spv::Id& id) {
if (fnLocalVars.count(id) > 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<spv::Id, int> 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);
-
- // Remove single reference types
- for (const auto typeStart : typeConstPos) {
- const spv::Id typeId = asTypeConstId(typeStart);
- if (typeUseCount[typeId] == 1) {
- --typeUseCount[typeId];
- stripInst(typeStart);
+ [&](spv::Id& id) { if (isType[id]) ++typeUseCount[id]; }
+ );
+
+ 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 (errorLatch) return;
+
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;
- 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 & 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 <string>
#include <vector>
-#include <stdlib.h>
+#include <cstdlib>
+#include <exception>
namespace spv {
@@ -74,7 +75,8 @@ public:
} // namespace SPV
#if !defined (use_cpp11)
-#include <stdio.h>
+#include <cstdio>
+#include <cstdint>
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<unsigned int>& /*spv*/, unsigned int /*opts = 0*/)
+ void remap(std::vector<std::uint32_t>& /*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<std::uint32_t>& spv, std::uint32_t opts = DO_EVERYTHING);
@@ -159,17 +164,22 @@ private:
typedef std::set<int> posmap_t;
typedef std::unordered_map<spv::Id, int> posmap_rev_t;
+ // Maps and ID to the size of its base type, if known.
+ typedef std::unordered_map<spv::Id, unsigned> typesize_map_t;
+
// handle error
- void error(const std::string& txt) const { 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;
-
+ 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;
+
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<spirword_t> 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<spv::Id, range_t> fnPos;
- std::unordered_map<spv::Id, range_t> fnPosDCE; // deleted functions
// Which functions are called, anywhere in the module, with a call count
std::unordered_map<spv::Id, int> 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<spv::Id> 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 <assert.h>
-#include <stdio.h>
-#include <stdlib.h>
+#include <cassert>
+#include <cstdlib>
#include <unordered_set>
+#include <algorithm>
#include "SpvBuilder.h"
+#ifdef AMD_EXTENSIONS
+ #include "hex_float.h"
+#endif
+
#ifndef _WIN32
#include <cstdio>
#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<Instruction>(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<Id>& 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<Instruction>(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<spvutils::FloatProxy<float>> fVal(f16);
+ spvutils::HexFloat<spvutils::FloatProxy<spvutils::Float16>> 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<Instruction>(c));
+ groupedConstants[OpTypeFloat].push_back(c);
+ module.mapInstruction(c);
+
+ return c->getResultId();
+}
+#endif
+
Id Builder::findCompositeConstant(Op typeClass, const std::vector<Id>& 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<Instruction>(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<Instruction>(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<Id> params;
- std::vector<Decoration> precisions;
+ std::vector<std::vector<Decoration>> 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<Id>& paramTypes, const std::vector<Decoration>& precisions, Block **entry)
+ const std::vector<Id>& paramTypes, const std::vector<std::vector<Decoration>>& 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<Instruction>(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<Id>(1, composite), std::vector<Id>(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<unsigned>& indexes)
+Id Builder::createCompositeExtract(Id composite, Id typeId, const std::vector<unsigned>& indexes)
{
+ // Generate code for spec constants if in spec constant operation
+ // generation mode.
+ if (generatingOpCodeForSpecConst) {
+ return createSpecConstantOp(OpCompositeExtract, typeId, std::vector<Id>(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<Id>& 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<Instruction>(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<Id>(1, operand), std::vector<Id>());
+ }
Instruction* op = new Instruction(getUniqueId(), typeId, opCode);
op->addIdOperand(operand);
buildPoint->addInstruction(std::unique_ptr<Instruction>(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<Id> operands(2);
+ operands[0] = left; operands[1] = right;
+ return createSpecConstantOp(opCode, typeId, operands, std::vector<Id>());
+ }
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<Id> operands(3);
+ operands[0] = op1;
+ operands[1] = op2;
+ operands[2] = op3;
+ return createSpecConstantOp(
+ opCode, typeId, operands, std::vector<Id>());
+ }
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<Id>& 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<Instruction>(op));
return op->getResultId();
}
+Id Builder::createSpecConstantOp(Op opCode, Id typeId, const std::vector<Id>& operands, const std::vector<unsigned>& 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<Instruction>(op));
+
+ return op->getResultId();
+}
+
Id Builder::createFunctionCall(spv::Function* function, const std::vector<spv::Id>& 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<Id> 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<unsigned>& 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<Instruction>(smear));
+ Instruction* smear = nullptr;
+ if (generatingOpCodeForSpecConst) {
+ auto members = std::vector<spv::Id>(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<Instruction>(smear));
+ }
return setPrecision(smear->getResultId(), precision);
}
// Comments in header
-Id Builder::createBuiltinCall(Id resultType, Id builtins, int entryPoint, std::vector<Id>& args)
+Id Builder::createBuiltinCall(Id resultType, Id builtins, int entryPoint, const std::vector<Id>& 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<Id>& 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<Id>& sourc
if (sources.size() == 1 && isScalar(sources[0]) && numTargetComponents > 1)
return smearScalar(precision, sources[0], resultTypeId);
+ // accumulate the arguments for OpCompositeConstruct
+ std::vector<Id> constituents;
Id scalarTypeId = getScalarTypeId(resultTypeId);
- std::vector<Id> 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<unsigned> swiz;
- swiz.push_back(s);
- arg = createRvalueSwizzle(precision, scalarTypeId, arg, swiz);
- }
+ std::vector<unsigned> swiz;
+ swiz.push_back(s);
+ latchResult(createRvalueSwizzle(precision, scalarTypeId, sourceArg, swiz));
+ }
+ };
- if (numTargetComponents > 1)
- constituents.push_back(arg);
- else
- result = arg;
- ++targetComponent;
+ // 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<Id> 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<Id>&
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<Id>&
// 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<Id>&
}
}
-
// 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<Id>&
}
// 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<int>& caseValues, const std::vector<int>& valueIndexToSegment, int defaultSegment,
+void Builder::makeSwitch(Id selector, unsigned int control, int numSegments, const std::vector<int>& caseValues,
+ const std::vector<int>& valueIndexToSegment, int defaultSegment,
std::vector<Block*>& segmentBlocks)
{
Function& function = buildPoint->getParent();
@@ -1860,7 +2080,7 @@ void Builder::makeSwitch(Id selector, int numSegments, const std::vector<int>& 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<unsigned>& 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<unsigned> 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<int>(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<const Block*> reachable_blocks;
+ std::unordered_set<Id> 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<Function*>::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<Block*>::const_iterator bi = f->getBlocks().cbegin();
+ bi != f->getBlocks().cend(); bi++) {
+ Block* b = *bi;
+ if (!reachable_blocks.count(b)) {
+ for (std::vector<std::unique_ptr<Instruction> >::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<Instruction>& 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<unsigned int>& 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<unsigned int>& 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<unsigned int>& 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<Id> 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<Instruction>(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<Instruction>(merge));
}
@@ -2325,26 +2615,62 @@ void Builder::createConditionalBranch(Id condition, Block* thenBlock, Block* els
elseBlock->addPredecessor(buildPoint);
}
-void Builder::dumpInstructions(std::vector<unsigned int>& out, const std::vector<std::unique_ptr<Instruction> >& instructions) const
+// OpSource
+// [OpSourceContinued]
+// ...
+void Builder::dumpSourceInstructions(std::vector<unsigned int>& out) const
{
- for (int i = 0; i < (int)instructions.size(); ++i) {
- instructions[i]->dump(out);
+ 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 TbdFunctionality(const char* tbd)
+void Builder::dumpInstructions(std::vector<unsigned int>& out, const std::vector<std::unique_ptr<Instruction> >& instructions) const
{
- static std::unordered_set<const char*> issued;
-
- if (issued.find(tbd) == issued.end()) {
- printf("TBD functionality: %s\n", tbd);
- issued.insert(tbd);
+ for (int i = 0; i < (int)instructions.size(); ++i) {
+ instructions[i]->dump(out);
}
}
-void MissingFunctionality(const char* fun)
+void Builder::dumpModuleProcesses(std::vector<unsigned int>& out) const
{
- printf("Missing functionality: %s\n", fun);
+ for (int i = 0; i < (int)moduleProcesses.size(); ++i) {
+ Instruction moduleProcessed(OpModuleProcessed);
+ moduleProcessed.addStringOperand(moduleProcesses[i]);
+ moduleProcessed.dump(out);
+ }
}
}; // 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 <algorithm>
-#include <memory>
-#include <stack>
#include <map>
+#include <memory>
#include <set>
+#include <sstream>
+#include <stack>
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<Instruction>(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<Id>& 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<Id>& paramTypes,
- const std::vector<Decoration>& precisions, Block **entry = 0);
+ const std::vector<std::vector<Decoration>>& 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<unsigned>& indexes);
+ Id createCompositeExtract(Id composite, Id typeId, const std::vector<unsigned>& indexes);
Id createCompositeInsert(Id object, Id composite, Id typeId, unsigned index);
Id createCompositeInsert(Id object, Id composite, Id typeId, const std::vector<unsigned>& 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<Id>& operands);
Id createFunctionCall(spv::Function*, const std::vector<spv::Id>&);
+ Id createSpecConstantOp(Op, Id typeId, const std::vector<spv::Id>& operands, const std::vector<unsigned>& 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<Id>& args);
+ Id createBuiltinCall(Id resultType, Id builtins, int entryPoint, const std::vector<Id>& 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<int>& caseValues, const std::vector<int>& valueToSegment, int defaultSegment,
- std::vector<Block*>& segmentBB); // return argument
+ void makeSwitch(Id condition, unsigned int control, int numSegments, const std::vector<int>& caseValues,
+ const std::vector<int>& valueToSegment, int defaultSegment, std::vector<Block*>& segmentBB); // return argument
// Add a branch to the innermost switch's merge block.
void addSwitchBreak();
@@ -396,7 +431,12 @@ public:
void endSwitch(std::vector<Block*>& 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<unsigned>& 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<unsigned int>&) 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<Id>& comps) const;
Id collapseAccessChain();
+ void remapDynamicSwizzle();
void transferAccessChainSwizzle(bool dynamic);
void simplifyAccessChainSwizzle();
void createAndSetNoPredecessorBlock(const char*);
+ void dumpSourceInstructions(std::vector<unsigned int>&) const;
void dumpInstructions(std::vector<unsigned int>&, const std::vector<std::unique_ptr<Instruction> >&) const;
+ void dumpModuleProcesses(std::vector<unsigned int>&) const;
+ unsigned int spvVersion; // the version of SPIR-V to emit in the header
SourceLanguage source;
int sourceVersion;
- std::vector<const char*> extensions;
+ spv::Id sourceFileStringId;
+ std::string sourceText;
+ int currentLine;
+ bool emitOpLines;
+ std::set<std::string> extensions;
+ std::vector<const char*> sourceExtensions;
+ std::vector<const char*> moduleProcesses;
AddressingModel addressModel;
MemoryModel memoryModel;
std::set<spv::Capability> 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<std::unique_ptr<Instruction> > strings;
std::vector<std::unique_ptr<Instruction> > imports;
std::vector<std::unique_ptr<Instruction> > entryPoints;
std::vector<std::unique_ptr<Instruction> > executionModes;
@@ -565,13 +631,10 @@ public:
// Our loop stack.
std::stack<LoopBlocks> loops;
-}; // 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*);
+ // The stream for outputting warnings and errors.
+ SpvBuildLogger* logger;
+}; // end Builder class
}; // end spv namespace
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 <cstdint>
+#include <cstring>
+
+namespace spvutils {
+
+// Performs a bitwise copy of source to the destination type Dest.
+template <typename Dest, typename Src>
+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<T, First, Num> returns an integer of type <T> with bits set
+// for position <First> through <First + Num - 1>, 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 <typename T, size_t First = 0, size_t Num = 0>
+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<T, First + 1, Num - 1>::get;
+};
+
+template <typename T, size_t Last>
+struct SetBits<T, Last, 0> {
+ const static T get = T(0);
+};
+
+// This is all compile-time so we can put our tests right here.
+static_assert(SetBits<uint32_t, 0, 0>::get == uint32_t(0x00000000),
+ "SetBits failed");
+static_assert(SetBits<uint32_t, 0, 1>::get == uint32_t(0x00000001),
+ "SetBits failed");
+static_assert(SetBits<uint32_t, 31, 1>::get == uint32_t(0x80000000),
+ "SetBits failed");
+static_assert(SetBits<uint32_t, 1, 2>::get == uint32_t(0x00000006),
+ "SetBits failed");
+static_assert(SetBits<uint32_t, 30, 2>::get == uint32_t(0xc0000000),
+ "SetBits failed");
+static_assert(SetBits<uint32_t, 0, 31>::get == uint32_t(0x7FFFFFFF),
+ "SetBits failed");
+static_assert(SetBits<uint32_t, 0, 32>::get == uint32_t(0xFFFFFFFF),
+ "SetBits failed");
+static_assert(SetBits<uint32_t, 16, 16>::get == uint32_t(0xFFFF0000),
+ "SetBits failed");
+
+static_assert(SetBits<uint64_t, 0, 1>::get == uint64_t(0x0000000000000001LL),
+ "SetBits failed");
+static_assert(SetBits<uint64_t, 63, 1>::get == uint64_t(0x8000000000000000LL),
+ "SetBits failed");
+static_assert(SetBits<uint64_t, 62, 2>::get == uint64_t(0xc000000000000000LL),
+ "SetBits failed");
+static_assert(SetBits<uint64_t, 31, 1>::get == uint64_t(0x0000000080000000LL),
+ "SetBits failed");
+static_assert(SetBits<uint64_t, 16, 16>::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 <stdlib.h>
-#include <string.h>
-#include <assert.h>
+#include <cstdlib>
+#include <cstring>
+#include <cassert>
#include <iomanip>
#include <stack>
#include <sstream>
#include <cstring>
+#include "disassemble.h"
+#include "doc.h"
+
namespace spv {
- // Include C-based headers that don't have a namespace
- #include "GLSL.std.450.h"
+ 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];
-#include "disassemble.h"
-#include "doc.h"
-
namespace spv {
-void Kill(std::ostream& out, const char* message)
+#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 <id>");
-
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 <id>");
+
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<unsigned int>& 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,11 +41,25 @@
#include "doc.h"
-#include <stdio.h>
-#include <string.h>
+#include <cstdio>
+#include <cstring>
#include <algorithm>
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 {
//
// Whole set of functions that translate enumerants to their text strings for
@@ -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 <vector>
@@ -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 <cassert>
+#include <cctype>
+#include <cmath>
+#include <cstdint>
+#include <iomanip>
+#include <limits>
+#include <sstream>
+
+#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 <typename T>
+struct FloatProxyTraits {
+ typedef void uint_type;
+};
+
+template <>
+struct FloatProxyTraits<float> {
+ 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<float>::max(); }
+ // Returns the lowest normal value.
+ static float lowest() { return std::numeric_limits<float>::lowest(); }
+};
+
+template <>
+struct FloatProxyTraits<double> {
+ 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<double>::max(); }
+ // Returns the lowest normal value.
+ static double lowest() { return std::numeric_limits<double>::lowest(); }
+};
+
+template <>
+struct FloatProxyTraits<Float16> {
+ 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 <typename T>
+class FloatProxy {
+ public:
+ typedef typename FloatProxyTraits<T>::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<uint_type>(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<T> operator-() const {
+ return static_cast<uint_type>(data_ ^
+ (uint_type(0x1) << (sizeof(T) * 8 - 1)));
+ }
+
+ // Returns the data as a floating point value.
+ T getAsFloat() const { return BitwiseCast<T>(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<T>::isNan(getAsFloat()); }
+ // Returns true if the value represents any type of infinity.
+ bool isInfinity() { return FloatProxyTraits<T>::isInfinity(getAsFloat()); }
+
+ // Returns the maximum normal value.
+ static FloatProxy<T> max() {
+ return FloatProxy<T>(FloatProxyTraits<T>::max());
+ }
+ // Returns the lowest normal value.
+ static FloatProxy<T> lowest() {
+ return FloatProxy<T>(FloatProxyTraits<T>::lowest());
+ }
+
+ private:
+ uint_type data_;
+};
+
+template <typename T>
+bool operator==(const FloatProxy<T>& first, const FloatProxy<T>& second) {
+ return first.data() == second.data();
+}
+
+// Reads a FloatProxy value as a normal float from a stream.
+template <typename T>
+std::istream& operator>>(std::istream& is, FloatProxy<T>& value) {
+ T float_val;
+ is >> float_val;
+ value = FloatProxy<T>(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 <typename T>
+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<FloatProxy<float>> {
+ typedef uint32_t uint_type;
+ typedef int32_t int_type;
+ typedef FloatProxy<float> 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<FloatProxy<double>> {
+ typedef uint64_t uint_type;
+ typedef int64_t int_type;
+ typedef FloatProxy<double> 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<FloatProxy<Float16>> {
+ 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 <typename T, typename Traits = HexFloatTraits<T>>
+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<uint_type, 0,
+ num_fraction_bits + num_overflow_bits>::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<uint_type, 0, num_fraction_bits>::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<uint_type, num_fraction_bits, num_exponent_bits>::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<uint32_t>(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<int_type>(exponent_bias);
+
+ // Returns the bits associated with the value.
+ uint_type getBits() const { return spvutils::BitwiseCast<uint_type>(value_); }
+
+ // Returns the bits associated with the value, without the leading sign bit.
+ uint_type getUnsignedBits() const {
+ return static_cast<uint_type>(spvutils::BitwiseCast<uint_type>(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<uint_type>((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<int_type>(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<uint_type>(significand_bits << 1);
+ exp = static_cast<int_type>(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<uint_type>(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<uint_type>(significand >> 1);
+ }
+
+ while (exponent < min_exponent) {
+ significand = static_cast<uint_type>(significand >> 1);
+ ++exponent;
+ }
+
+ if (exponent == min_exponent) {
+ if (significand == 0 && !significand_is_zero && round_denorm_up) {
+ significand = static_cast<uint_type>(0x1);
+ }
+ }
+
+ uint_type new_value = 0;
+ if (negative) {
+ new_value = static_cast<uint_type>(new_value | sign_mask);
+ }
+ exponent = static_cast<int_type>(exponent + exponent_bias);
+ assert(exponent >= 0);
+
+ // put it all together
+ exponent = static_cast<uint_type>((exponent << exponent_left_shift) &
+ exponent_mask);
+ significand = static_cast<uint_type>(significand & fraction_encode_mask);
+ new_value = static_cast<uint_type>(new_value | (exponent | significand));
+ value_ = BitwiseCast<T>(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<uint_type>(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<uint_type>(significand & ~first_exponent_bit);
+ significand = static_cast<uint_type>(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 <typename int_type>
+ uint_type negatable_left_shift(int_type N, uint_type val)
+ {
+ if(N >= 0)
+ return val << N;
+
+ return val >> -N;
+ }
+
+ template <typename int_type>
+ 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>
+ 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<int_type>(num_fraction_bits) -
+ static_cast<int_type>(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<uint_type, 0, throwaway_mask_bits>::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<other_uint_type>(significand);
+ uint_type shift_amount = static_cast<uint_type>(-num_throwaway_bits);
+ out_val = static_cast<other_uint_type>(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<other_uint_type>(
+ 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<other_uint_type>(
+ negatable_right_shift(num_throwaway_bits, incrementSignificand(
+ significand, last_significant_bit, carry_bit)));
+ } else {
+ return static_cast<other_uint_type>(
+ 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 <typename other_T>
+ void castTo(other_T& other, round_direction round_dir) {
+ other = other_T(static_cast<typename other_T::native_type>(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<other_T>(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<int_type>(exponent + 1);
+ for (uint_type check_bit = first_exponent_bit >> 1; check_bit != 0;
+ check_bit = static_cast<uint_type>(check_bit >> 1)) {
+ exponent = static_cast<int_type>(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<int_type>(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<typename other_T::underlying_type>(
+ static_cast<typename other_T::uint_type>(
+ (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<typename other_T::uint_type>(
+ negatable_left_shift(
+ static_cast<int_type>(other_T::num_fraction_bits) -
+ static_cast<int_type>(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<typename other_T::underlying_type>(
+ static_cast<typename other_T::uint_type>(
+ (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<other_int_type>(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<uint8_t>(p - dec);
+ } else if ((p = strchr(lower, character))) {
+ return static_cast<uint8_t>(p - lower + 0xa);
+ } else if ((p = strchr(upper, character))) {
+ return static_cast<uint8_t>(p - upper + 0xa);
+ }
+
+ assert(false && "This was called with a non-hex character");
+ return 0;
+}
+
+// Outputs the given HexFloat to the stream.
+template <typename T, typename Traits>
+std::ostream& operator<<(std::ostream& os, const HexFloat<T, Traits>& value) {
+ typedef HexFloat<T, Traits> 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<uint_type>(value.value());
+ const char* const sign = (bits & HF::sign_mask) ? "-" : "";
+ const uint_type exponent = static_cast<uint_type>(
+ (bits & HF::exponent_mask) >> HF::num_fraction_bits);
+
+ uint_type fraction = static_cast<uint_type>((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<int_type>(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<uint_type>(fraction << 1);
+ int_exponent = static_cast<int_type>(int_exponent - 1);
+ }
+ // Since this is denormalized, we have to consume the leading 1 since it
+ // will end up being implicit.
+ fraction = static_cast<uint_type>(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<uint_type>(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<int>(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 <typename T, typename Traits>
+inline bool RejectParseDueToLeadingSign(std::istream& is, bool negate_value,
+ HexFloat<T, Traits>& 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<T, Traits>(typename HexFloat<T, Traits>::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 <typename T, typename Traits>
+inline std::istream& ParseNormalFloat(std::istream& is, bool negate_value,
+ HexFloat<T, Traits>& 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<T, Traits>(typename HexFloat<T, Traits>::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<Float16> 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<FloatProxy<Float16>, HexFloatTraits<FloatProxy<Float16>>>(
+ std::istream& is, bool negate_value,
+ HexFloat<FloatProxy<Float16>, HexFloatTraits<FloatProxy<Float16>>>& value) {
+ // First parse as a 32-bit float.
+ HexFloat<FloatProxy<float>> 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.<not zero>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 <typename T, typename Traits>
+std::istream& operator>>(std::istream& is, HexFloat<T, Traits>& value) {
+ using HF = HexFloat<T, Traits>;
+ using uint_type = typename HF::uint_type;
+ using int_type = typename HF::int_type;
+
+ value.set_value(static_cast<typename HF::native_type>(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<uint_type>(
+ fraction |
+ static_cast<uint_type>(
+ write_bit << (HF::top_bit_left_shift - fraction_index++)));
+ exponent = static_cast<int_type>(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<int_type>(exponent - 1);
+ } else {
+ fraction = static_cast<uint_type>(
+ fraction |
+ static_cast<uint_type>(
+ 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<int_type>(written_exponent * 10);
+ written_exponent =
+ static_cast<int_type>(written_exponent + (next_char - '0'));
+ } else {
+ break;
+ }
+ is.get();
+ next_char = is.peek();
+ }
+
+ written_exponent = static_cast<int_type>(written_exponent * exponent_sign);
+ exponent = static_cast<int_type>(exponent + written_exponent);
+
+ bool is_zero = is_denorm && (fraction == 0);
+ if (is_denorm && !is_zero) {
+ fraction = static_cast<uint_type>(fraction << 1);
+ exponent = static_cast<int_type>(exponent - 1);
+ } else if (is_zero) {
+ exponent = 0;
+ }
+
+ if (exponent <= 0 && !is_zero) {
+ fraction = static_cast<uint_type>(fraction >> 1);
+ fraction |= static_cast<uint_type>(1) << HF::top_bit_left_shift;
+ }
+
+ fraction = (fraction >> HF::fraction_right_shift) & HF::fraction_encode_mask;
+
+ const int_type max_exponent =
+ SetBits<uint_type, 0, HF::num_exponent_bits>::get;
+
+ // Handle actual denorm numbers
+ while (exponent < 0 && !is_zero) {
+ fraction = static_cast<uint_type>(fraction >> 1);
+ exponent = static_cast<int_type>(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<uint_type>(
+ static_cast<uint_type>(negate_value ? 1 : 0) << HF::top_bit_left_shift);
+ output_bits |= fraction;
+
+ uint_type shifted_exponent = static_cast<uint_type>(
+ static_cast<uint_type>(exponent << HF::exponent_left_shift) &
+ HF::exponent_mask);
+ output_bits |= shifted_exponent;
+
+ T output_float = spvutils::BitwiseCast<T>(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 <typename T>
+std::ostream& operator<<(std::ostream& os, const FloatProxy<T>& 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<T>::digits10);
+ os << float_val;
+ os.precision(saved_precision);
+ } break;
+ default:
+ os << HexFloat<FloatProxy<T>>(value);
+ break;
+ }
+ return os;
+}
+
+template <>
+inline std::ostream& operator<<<Float16>(std::ostream& os,
+ const FloatProxy<Float16>& value) {
+ os << HexFloat<FloatProxy<Float16>>(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<unsigned int>& out) const
@@ -159,7 +154,6 @@ protected:
Id typeId;
Op opCode;
std::vector<Id> 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<Instruction> inst);
void addPredecessor(Block* pred) { predecessors.push_back(pred); pred->successors.push_back(this);}
void addLocalVariable(std::unique_ptr<Instruction> inst) { localVariables.push_back(std::move(inst)); }
- void insertInstruction(size_t pos, std::unique_ptr<Instruction> 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<Block*>& getPredecessors() const { return predecessors; }
const std::vector<Block*>& getSuccessors() const { return successors; }
+ const std::vector<std::unique_ptr<Instruction> >& 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<unsigned int>& 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<std::unique_ptr<Instruction> > 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<Block*>& getBlocks() { return blocks; }
+ const std::vector<Block*>& getBlocks() const { return blocks; }
void addLocalVariable(std::unique_ptr<Instruction> inst);
Id getReturnType() const { return functionInstruction.getTypeId(); }
+
+ void setImplicitThis() { implicitThis = true; }
+ bool hasImplicitThis() const { return implicitThis; }
+
void dump(std::vector<unsigned int>& out) const
{
// OpFunction
@@ -365,6 +302,7 @@ protected:
Instruction functionInstruction;
std::vector<Instruction*> parameterInstructions;
std::vector<Block*> 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<Function*>& getFunctions() const { return functions; }
- std::vector<Function*>& getFunctions() { return functions; }
void mapInstruction(Instruction *instruction)
{
@@ -393,6 +329,7 @@ public:
}
Instruction* getInstruction(Id id) const { return idToInstruction[id]; }
+ const std::vector<Function*>& 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<Instruction> inst)
parent.getParent().mapInstruction(raw_instruction);
}
-__inline void Block::insertInstruction(size_t pos, std::unique_ptr<Instruction> 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