From 35e08d94281d6ec8a2fd2550c9a5f6bb6b39822c Mon Sep 17 00:00:00 2001 From: Ben Vanik Date: Thu, 18 Feb 2016 16:42:00 -0800 Subject: [PATCH] Switching from fork to main glslang spirv builder. --- src/xenia/gpu/premake5.lua | 2 + src/xenia/gpu/shader_translator.cc | 4 +- src/xenia/gpu/shader_translator.h | 7 + src/xenia/gpu/spirv_shader_translator.cc | 138 +- src/xenia/gpu/spirv_shader_translator.h | 18 +- src/xenia/gpu/vulkan/vulkan_shader.cc | 2 + src/xenia/gpu/vulkan/vulkan_shader.h | 7 + src/xenia/ui/spirv/premake5.lua | 1 + src/xenia/ui/spirv/spirv_emitter.cc | 2241 ---------------------- src/xenia/ui/spirv/spirv_emitter.h | 731 ------- src/xenia/ui/spirv/spirv_ir.h | 421 ---- src/xenia/ui/spirv/spirv_optimizer.cc | 22 - src/xenia/ui/spirv/spirv_optimizer.h | 31 - 13 files changed, 140 insertions(+), 3485 deletions(-) delete mode 100644 src/xenia/ui/spirv/spirv_emitter.cc delete mode 100644 src/xenia/ui/spirv/spirv_emitter.h delete mode 100644 src/xenia/ui/spirv/spirv_ir.h delete mode 100644 src/xenia/ui/spirv/spirv_optimizer.cc delete mode 100644 src/xenia/ui/spirv/spirv_optimizer.h diff --git a/src/xenia/gpu/premake5.lua b/src/xenia/gpu/premake5.lua index e63184edb..1f6a1eea6 100644 --- a/src/xenia/gpu/premake5.lua +++ b/src/xenia/gpu/premake5.lua @@ -7,6 +7,7 @@ project("xenia-gpu") kind("StaticLib") language("C++") links({ + "glslang-spirv", "snappy", "spirv-tools", "xenia-base", @@ -29,6 +30,7 @@ project("xenia-gpu-shader-compiler") language("C++") links({ "gflags", + "glslang-spirv", "spirv-tools", "xenia-base", "xenia-gpu", diff --git a/src/xenia/gpu/shader_translator.cc b/src/xenia/gpu/shader_translator.cc index 1c9f31962..f117619cd 100644 --- a/src/xenia/gpu/shader_translator.cc +++ b/src/xenia/gpu/shader_translator.cc @@ -137,7 +137,7 @@ bool ShaderTranslator::Translate(Shader* shader) { constant_register_map_.packed_byte_length += 4 * xe::bit_count(constant_register_map_.int_bitmap); // Direct map between words and words we upload. - for (int i = 0; i < 4; ++i) { + for (int i = 0; i < 8; ++i) { if (constant_register_map_.bool_bitmap[i]) { constant_register_map_.packed_byte_length += 4; } @@ -161,6 +161,8 @@ bool ShaderTranslator::Translate(Shader* shader) { } } + PostTranslation(shader); + return shader->is_valid_; } diff --git a/src/xenia/gpu/shader_translator.h b/src/xenia/gpu/shader_translator.h index 8c8a8c176..21bae4a53 100644 --- a/src/xenia/gpu/shader_translator.h +++ b/src/xenia/gpu/shader_translator.h @@ -71,6 +71,13 @@ class ShaderTranslator { return std::vector(); } + // Handles post-translation tasks when the shader has been fully translated. + virtual void PostTranslation(Shader* shader) {} + // Sets the host disassembly on a shader. + void set_host_disassembly(Shader* shader, std::string value) { + shader->host_disassembly_ = std::move(value); + } + // Handles translation for control flow label addresses. // This is triggered once for each label required (due to control flow // operations) before any of the instructions within the target exec. diff --git a/src/xenia/gpu/spirv_shader_translator.cc b/src/xenia/gpu/spirv_shader_translator.cc index b9af44c22..52848cedd 100644 --- a/src/xenia/gpu/spirv_shader_translator.cc +++ b/src/xenia/gpu/spirv_shader_translator.cc @@ -11,124 +11,182 @@ #include +#include "xenia/base/logging.h" + namespace xe { namespace gpu { +using spv::GLSLstd450; +using spv::Id; +using spv::Op; + SpirvShaderTranslator::SpirvShaderTranslator() = default; SpirvShaderTranslator::~SpirvShaderTranslator() = default; void SpirvShaderTranslator::StartTranslation() { - auto& e = emitter_; + // Create a new builder. + builder_ = std::make_unique(0xFFFFFFFF); + auto& b = *builder_; - auto fn = e.MakeMainEntry(); - auto float_1_0 = e.MakeFloatConstant(1.0f); - auto acos = e.CreateGlslStd450InstructionCall( - spv::Decoration::Invariant, e.MakeFloatType(32), spv::GLSLstd450::kAcos, - {float_1_0}); - e.MakeReturn(true); + // Import required modules. + glsl_std_450_instruction_set_ = b.import("GLSL.std.450"); + + // Configure environment. + b.setSource(spv::SourceLanguage::SourceLanguageUnknown, 0); + b.setMemoryModel(spv::AddressingModel::AddressingModelLogical, + spv::MemoryModel::MemoryModelGLSL450); + b.addCapability(spv::Capability::CapabilityShader); + b.addCapability(spv::Capability::CapabilityGenericPointer); + if (is_vertex_shader()) { + b.addCapability(spv::Capability::CapabilityClipDistance); + b.addCapability(spv::Capability::CapabilityCullDistance); + } + if (is_pixel_shader()) { + b.addCapability(spv::Capability::CapabilityDerivativeControl); + } + + // main() entry point. + auto mainFn = b.makeMain(); + if (is_vertex_shader()) { + b.addEntryPoint(spv::ExecutionModel::ExecutionModelVertex, mainFn, "main"); + } else { + b.addEntryPoint(spv::ExecutionModel::ExecutionModelFragment, mainFn, + "main"); + b.addExecutionMode(mainFn, spv::ExecutionModeOriginUpperLeft); + } + + // TODO(benvanik): transform feedback. + if (false) { + b.addCapability(spv::Capability::CapabilityTransformFeedback); + b.addExecutionMode(mainFn, spv::ExecutionMode::ExecutionModeXfb); + } + + auto float_1_0 = b.makeFloatConstant(2.0f); + auto acos = CreateGlslStd450InstructionCall( + spv::Decoration::DecorationInvariant, b.makeFloatType(32), + GLSLstd450::kAcos, {float_1_0}); } std::vector SpirvShaderTranslator::CompleteTranslation() { - auto& e = emitter_; + auto& b = *builder_; + + b.makeReturn(false); std::vector spirv_words; - e.Serialize(spirv_words); + b.dump(spirv_words); + // Cleanup builder. + builder_.reset(); + + // Copy bytes out. + // TODO(benvanik): avoid copy? std::vector spirv_bytes; spirv_bytes.resize(spirv_words.size() * 4); std::memcpy(spirv_bytes.data(), spirv_words.data(), spirv_bytes.size()); return spirv_bytes; } +void SpirvShaderTranslator::PostTranslation(Shader* shader) { + // TODO(benvanik): only if needed? could be slowish. + auto disasm = disassembler_.Disassemble( + reinterpret_cast(shader->translated_binary().data()), + shader->translated_binary().size() / 4); + if (disasm->has_error()) { + XELOGE("Failed to disassemble SPIRV - invalid?"); + return; + } + set_host_disassembly(shader, disasm->to_string()); +} + void SpirvShaderTranslator::ProcessLabel(uint32_t cf_index) { - auto& e = emitter_; + auto& b = *builder_; EmitUnimplementedTranslationError(); } void SpirvShaderTranslator::ProcessControlFlowNopInstruction() { - auto& e = emitter_; + auto& b = *builder_; EmitUnimplementedTranslationError(); } void SpirvShaderTranslator::ProcessExecInstructionBegin( const ParsedExecInstruction& instr) { - auto& e = emitter_; + auto& b = *builder_; EmitUnimplementedTranslationError(); } void SpirvShaderTranslator::ProcessExecInstructionEnd( const ParsedExecInstruction& instr) { - auto& e = emitter_; + auto& b = *builder_; EmitUnimplementedTranslationError(); } void SpirvShaderTranslator::ProcessLoopStartInstruction( const ParsedLoopStartInstruction& instr) { - auto& e = emitter_; + auto& b = *builder_; EmitUnimplementedTranslationError(); } void SpirvShaderTranslator::ProcessLoopEndInstruction( const ParsedLoopEndInstruction& instr) { - auto& e = emitter_; + auto& b = *builder_; EmitUnimplementedTranslationError(); } void SpirvShaderTranslator::ProcessCallInstruction( const ParsedCallInstruction& instr) { - auto& e = emitter_; + auto& b = *builder_; EmitUnimplementedTranslationError(); } void SpirvShaderTranslator::ProcessReturnInstruction( const ParsedReturnInstruction& instr) { - auto& e = emitter_; + auto& b = *builder_; EmitUnimplementedTranslationError(); } void SpirvShaderTranslator::ProcessJumpInstruction( const ParsedJumpInstruction& instr) { - auto& e = emitter_; + auto& b = *builder_; EmitUnimplementedTranslationError(); } void SpirvShaderTranslator::ProcessAllocInstruction( const ParsedAllocInstruction& instr) { - auto& e = emitter_; + auto& b = *builder_; EmitUnimplementedTranslationError(); } void SpirvShaderTranslator::ProcessVertexFetchInstruction( const ParsedVertexFetchInstruction& instr) { - auto& e = emitter_; + auto& b = *builder_; EmitUnimplementedTranslationError(); } void SpirvShaderTranslator::ProcessTextureFetchInstruction( const ParsedTextureFetchInstruction& instr) { - auto& e = emitter_; + auto& b = *builder_; EmitUnimplementedTranslationError(); } void SpirvShaderTranslator::ProcessAluInstruction( const ParsedAluInstruction& instr) { - auto& e = emitter_; + auto& b = *builder_; switch (instr.type) { case ParsedAluInstruction::Type::kNop: - e.CreateNop(); + b.createNoResultOp(spv::Op::OpNop); break; case ParsedAluInstruction::Type::kVector: ProcessVectorAluInstruction(instr); @@ -141,14 +199,14 @@ void SpirvShaderTranslator::ProcessAluInstruction( void SpirvShaderTranslator::ProcessVectorAluInstruction( const ParsedAluInstruction& instr) { - auto& e = emitter_; + auto& b = *builder_; EmitUnimplementedTranslationError(); } void SpirvShaderTranslator::ProcessScalarAluInstruction( const ParsedAluInstruction& instr) { - auto& e = emitter_; + auto& b = *builder_; spv::Id value_id = LoadFromOperand(instr.operands[0]); @@ -157,11 +215,19 @@ void SpirvShaderTranslator::ProcessScalarAluInstruction( EmitUnimplementedTranslationError(); } -spv::Id SpirvShaderTranslator::LoadFromOperand(const InstructionOperand& op) { - auto& e = emitter_; +Id SpirvShaderTranslator::CreateGlslStd450InstructionCall( + spv::Decoration precision, Id result_type, GLSLstd450 instruction_ordinal, + std::vector args) { + return builder_->createBuiltinCall(result_type, glsl_std_450_instruction_set_, + static_cast(instruction_ordinal), + args); +} - spv::Id current_type_id = e.MakeFloatType(32); - spv::Id current_value_id = e.CreateUndefined(current_type_id); +spv::Id SpirvShaderTranslator::LoadFromOperand(const InstructionOperand& op) { + auto& b = *builder_; + + spv::Id current_type_id = b.makeFloatType(32); + spv::Id current_value_id = b.createUndefined(current_type_id); // storage_addressing_mode switch (op.storage_source) { @@ -186,13 +252,13 @@ spv::Id SpirvShaderTranslator::LoadFromOperand(const InstructionOperand& op) { } if (op.is_absolute_value) { - current_value_id = e.CreateGlslStd450InstructionCall( - spv::Decoration::RelaxedPrecision, current_type_id, - spv::GLSLstd450::kFAbs, {current_value_id}); + current_value_id = CreateGlslStd450InstructionCall( + spv::Decoration::DecorationRelaxedPrecision, current_type_id, + GLSLstd450::kFAbs, {current_value_id}); } if (op.is_negated) { current_value_id = - e.CreateUnaryOp(spv::Op::OpFNegate, current_type_id, current_value_id); + b.createUnaryOp(spv::Op::OpFNegate, current_type_id, current_value_id); } // swizzle @@ -202,7 +268,7 @@ spv::Id SpirvShaderTranslator::LoadFromOperand(const InstructionOperand& op) { void SpirvShaderTranslator::StoreToResult(spv::Id source_value_id, const InstructionResult& result) { - auto& e = emitter_; + auto& b = *builder_; if (result.storage_target == InstructionStorageTarget::kNone) { // No-op? @@ -236,7 +302,7 @@ void SpirvShaderTranslator::StoreToResult(spv::Id source_value_id, } spv::Id current_value_id = source_value_id; - spv::Id current_type_id = e.GetTypeId(source_value_id); + spv::Id current_type_id = b.getTypeId(source_value_id); // Clamp the input value. if (result.is_clamped) { @@ -248,7 +314,7 @@ void SpirvShaderTranslator::StoreToResult(spv::Id source_value_id, // swizzle // Convert to the appropriate type, if needed. - spv::Id desired_type_id = e.MakeFloatType(32); + spv::Id desired_type_id = b.makeFloatType(32); if (current_value_id != desired_type_id) { EmitTranslationError("Type conversion on storage not yet implemented"); } diff --git a/src/xenia/gpu/spirv_shader_translator.h b/src/xenia/gpu/spirv_shader_translator.h index fc068a33d..2b233103b 100644 --- a/src/xenia/gpu/spirv_shader_translator.h +++ b/src/xenia/gpu/spirv_shader_translator.h @@ -14,8 +14,10 @@ #include #include +#include "third_party/glslang-spirv/SpvBuilder.h" +#include "third_party/spirv/GLSL.std.450.hpp11" #include "xenia/gpu/shader_translator.h" -#include "xenia/ui/spirv/spirv_emitter.h" +#include "xenia/ui/spirv/spirv_disassembler.h" namespace xe { namespace gpu { @@ -28,6 +30,7 @@ class SpirvShaderTranslator : public ShaderTranslator { protected: void StartTranslation() override; std::vector CompleteTranslation() override; + void PostTranslation(Shader* shader) override; void ProcessLabel(uint32_t cf_index) override; void ProcessControlFlowNopInstruction() override; @@ -48,9 +51,16 @@ class SpirvShaderTranslator : public ShaderTranslator { void ProcessAluInstruction(const ParsedAluInstruction& instr) override; private: + void SetupPushConstants(); + void ProcessVectorAluInstruction(const ParsedAluInstruction& instr); void ProcessScalarAluInstruction(const ParsedAluInstruction& instr); + // Creates a call to the given GLSL intrinsic. + spv::Id SpirvShaderTranslator::CreateGlslStd450InstructionCall( + spv::Decoration precision, spv::Id result_type, + spv::GLSLstd450 instruction_ordinal, std::vector args); + // Loads an operand into a value. // The value returned will be in the form described in the operand (number of // components, etc). @@ -60,7 +70,11 @@ class SpirvShaderTranslator : public ShaderTranslator { // the proper components will be selected. void StoreToResult(spv::Id source_value_id, const InstructionResult& result); - xe::ui::spirv::SpirvEmitter emitter_; + xe::ui::spirv::SpirvDisassembler disassembler_; + + // TODO(benvanik): replace with something better, make reusable, etc. + std::unique_ptr builder_; + spv::Id glsl_std_450_instruction_set_ = 0; }; } // namespace gpu diff --git a/src/xenia/gpu/vulkan/vulkan_shader.cc b/src/xenia/gpu/vulkan/vulkan_shader.cc index 00b68af42..8624480a3 100644 --- a/src/xenia/gpu/vulkan/vulkan_shader.cc +++ b/src/xenia/gpu/vulkan/vulkan_shader.cc @@ -22,6 +22,8 @@ VulkanShader::VulkanShader(ShaderType shader_type, uint64_t data_hash, VulkanShader::~VulkanShader() = default; +bool VulkanShader::Prepare() { return true; } + } // namespace vulkan } // namespace gpu } // namespace xe diff --git a/src/xenia/gpu/vulkan/vulkan_shader.h b/src/xenia/gpu/vulkan/vulkan_shader.h index 9277ae44f..cc1d51e2a 100644 --- a/src/xenia/gpu/vulkan/vulkan_shader.h +++ b/src/xenia/gpu/vulkan/vulkan_shader.h @@ -24,6 +24,13 @@ class VulkanShader : public Shader { VulkanShader(ShaderType shader_type, uint64_t data_hash, const uint32_t* dword_ptr, uint32_t dword_count); ~VulkanShader() override; + + VkShaderModule shader_module() const { return shader_module_; } + + bool Prepare(); + + private: + VkShaderModule shader_module_ = nullptr; }; } // namespace vulkan diff --git a/src/xenia/ui/spirv/premake5.lua b/src/xenia/ui/spirv/premake5.lua index 94e52a0d5..423ad7bb6 100644 --- a/src/xenia/ui/spirv/premake5.lua +++ b/src/xenia/ui/spirv/premake5.lua @@ -7,6 +7,7 @@ project("xenia-ui-spirv") kind("StaticLib") language("C++") links({ + "glslang-spirv", "spirv-tools", "xenia-base", }) diff --git a/src/xenia/ui/spirv/spirv_emitter.cc b/src/xenia/ui/spirv/spirv_emitter.cc deleted file mode 100644 index 6be5b0a62..000000000 --- a/src/xenia/ui/spirv/spirv_emitter.cc +++ /dev/null @@ -1,2241 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2015 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -// Contents originally forked from: -// https://github.com/KhronosGroup/glslang/ -// -// Copyright (C) 2014 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. - -#include "xenia/ui/spirv/spirv_emitter.h" - -#include - -#include "xenia/base/assert.h" -#include "xenia/base/logging.h" - -namespace xe { -namespace ui { -namespace spirv { - -SpirvEmitter::SpirvEmitter() { ClearAccessChain(); } - -SpirvEmitter::~SpirvEmitter() = default; - -Id SpirvEmitter::ImportExtendedInstructions(const char* name) { - auto import = - new Instruction(AllocateUniqueId(), NoType, Op::OpExtInstImport); - import->AddStringOperand(name); - - imports_.push_back(import); - return import->result_id(); -} - -// For creating new grouped_types_ (will return old type if the requested one -// was already made). -Id SpirvEmitter::MakeVoidType() { - Instruction* type; - auto& grouped_type = grouped_types_[static_cast(Op::OpTypeVoid)]; - if (grouped_type.empty()) { - type = new Instruction(AllocateUniqueId(), NoType, Op::OpTypeVoid); - grouped_type.push_back(type); - constants_types_globals_.push_back(type); - module_.MapInstruction(type); - } else { - type = grouped_type.back(); - } - - return type->result_id(); -} - -Id SpirvEmitter::MakeBoolType() { - Instruction* type; - auto& grouped_type = grouped_types_[static_cast(Op::OpTypeBool)]; - if (grouped_type.empty()) { - type = new Instruction(AllocateUniqueId(), NoType, Op::OpTypeBool); - grouped_type.push_back(type); - constants_types_globals_.push_back(type); - module_.MapInstruction(type); - } else { - type = grouped_type.back(); - } - - return type->result_id(); -} - -Id SpirvEmitter::MakeSamplerType() { - Instruction* type; - auto& grouped_type = grouped_types_[static_cast(Op::OpTypeSampler)]; - if (grouped_type.empty()) { - type = new Instruction(AllocateUniqueId(), NoType, Op::OpTypeSampler); - grouped_type.push_back(type); - constants_types_globals_.push_back(type); - module_.MapInstruction(type); - } else { - type = grouped_type.back(); - } - return type->result_id(); -} - -Id SpirvEmitter::MakePointer(spv::StorageClass storage_class, Id pointee) { - // try to find it - auto& grouped_type = grouped_types_[static_cast(Op::OpTypePointer)]; - for (auto& type : grouped_type) { - if (type->immediate_operand(0) == (unsigned)storage_class && - type->id_operand(1) == pointee) { - return type->result_id(); - } - } - - // not found, make it - auto type = new Instruction(AllocateUniqueId(), NoType, Op::OpTypePointer); - type->AddImmediateOperand(storage_class); - type->AddIdOperand(pointee); - grouped_type.push_back(type); - constants_types_globals_.push_back(type); - module_.MapInstruction(type); - - return type->result_id(); -} - -Id SpirvEmitter::MakeIntegerType(int bit_width, bool is_signed) { - // try to find it - auto& grouped_type = grouped_types_[static_cast(Op::OpTypeInt)]; - for (auto& type : grouped_type) { - if (type->immediate_operand(0) == (unsigned)bit_width && - type->immediate_operand(1) == (is_signed ? 1u : 0u)) { - return type->result_id(); - } - } - - // not found, make it - auto type = new Instruction(AllocateUniqueId(), NoType, Op::OpTypeInt); - type->AddImmediateOperand(bit_width); - type->AddImmediateOperand(is_signed ? 1 : 0); - grouped_type.push_back(type); - constants_types_globals_.push_back(type); - module_.MapInstruction(type); - - return type->result_id(); -} - -Id SpirvEmitter::MakeFloatType(int bit_width) { - // try to find it - auto& grouped_type = grouped_types_[static_cast(Op::OpTypeFloat)]; - for (auto& type : grouped_type) { - if (type->immediate_operand(0) == (unsigned)bit_width) { - return type->result_id(); - } - } - - // not found, make it - auto type = new Instruction(AllocateUniqueId(), NoType, Op::OpTypeFloat); - type->AddImmediateOperand(bit_width); - grouped_type.push_back(type); - constants_types_globals_.push_back(type); - module_.MapInstruction(type); - - return type->result_id(); -} - -// Make a struct without checking for duplication. -// See makeStructResultType() for non-decorated structs -// needed as the result of some instructions, which does -// check for duplicates. -Id SpirvEmitter::MakeStructType(std::initializer_list members, - const char* name) { - // Don't look for previous one, because in the general case, - // structs can be duplicated except for decorations. - - // not found, make it - Instruction* type = - new Instruction(AllocateUniqueId(), NoType, Op::OpTypeStruct); - type->AddIdOperands(members); - auto& grouped_type = grouped_types_[static_cast(Op::OpTypeStruct)]; - grouped_type.push_back(type); - constants_types_globals_.push_back(type); - module_.MapInstruction(type); - AddName(type->result_id(), name); - - return type->result_id(); -} - -// Make a struct for the simple results of several instructions, -// checking for duplication. -Id SpirvEmitter::MakePairStructType(Id type0, Id type1) { - // try to find it - auto& grouped_type = grouped_types_[static_cast(Op::OpTypeStruct)]; - for (auto& type : grouped_type) { - if (type->operand_count() != 2) { - continue; - } - if (type->id_operand(0) != type0 || type->id_operand(1) != type1) { - continue; - } - return type->result_id(); - } - - // not found, make it - return MakeStructType({type0, type1}, "ResType"); -} - -Id SpirvEmitter::MakeVectorType(Id component_type, int component_count) { - // try to find it - auto& grouped_type = grouped_types_[static_cast(Op::OpTypeVector)]; - for (auto& type : grouped_type) { - if (type->id_operand(0) == component_type && - type->immediate_operand(1) == (unsigned)component_count) { - return type->result_id(); - } - } - - // not found, make it - auto type = new Instruction(AllocateUniqueId(), NoType, Op::OpTypeVector); - type->AddIdOperand(component_type); - type->AddImmediateOperand(component_count); - grouped_type.push_back(type); - constants_types_globals_.push_back(type); - module_.MapInstruction(type); - - return type->result_id(); -} - -Id SpirvEmitter::MakeMatrix2DType(Id component_type, int cols, int rows) { - assert(cols <= kMaxMatrixSize && rows <= kMaxMatrixSize); - - Id column = MakeVectorType(component_type, rows); - - // try to find it - auto& grouped_type = grouped_types_[static_cast(Op::OpTypeMatrix)]; - for (auto& type : grouped_type) { - if (type->id_operand(0) == column && - type->immediate_operand(1) == (unsigned)cols) { - return type->result_id(); - } - } - - // not found, make it - auto type = new Instruction(AllocateUniqueId(), NoType, Op::OpTypeMatrix); - type->AddIdOperand(column); - type->AddImmediateOperand(cols); - grouped_type.push_back(type); - constants_types_globals_.push_back(type); - module_.MapInstruction(type); - - return type->result_id(); -} - -Id SpirvEmitter::MakeArrayType(Id element_type, int length) { - // First, we need a constant instruction for the size - Id length_id = MakeUintConstant(length); - - // try to find existing type - auto& grouped_type = grouped_types_[static_cast(Op::OpTypeArray)]; - for (auto& type : grouped_type) { - if (type->id_operand(0) == element_type && - type->id_operand(1) == length_id) { - return type->result_id(); - } - } - - // not found, make it - auto type = new Instruction(AllocateUniqueId(), NoType, Op::OpTypeArray); - type->AddIdOperand(element_type); - type->AddIdOperand(length_id); - grouped_type.push_back(type); - constants_types_globals_.push_back(type); - module_.MapInstruction(type); - - return type->result_id(); -} - -Id SpirvEmitter::MakeRuntimeArray(Id element_type) { - auto type = - new Instruction(AllocateUniqueId(), NoType, Op::OpTypeRuntimeArray); - type->AddIdOperand(element_type); - constants_types_globals_.push_back(type); - module_.MapInstruction(type); - - return type->result_id(); -} - -Id SpirvEmitter::MakeFunctionType(Id return_type, - std::initializer_list param_types) { - // try to find it - auto& grouped_type = grouped_types_[static_cast(Op::OpTypeFunction)]; - for (auto& type : grouped_type) { - if (type->id_operand(0) == return_type && - param_types.size() == type->operand_count() - 1) { - bool mismatch = false; - for (int i = 0; i < param_types.size(); ++i) { - if (type->id_operand(i + 1) != *(param_types.begin() + i)) { - mismatch = true; - break; - } - } - if (!mismatch) { - return type->result_id(); - } - } - } - - // not found, make it - auto type = new Instruction(AllocateUniqueId(), NoType, Op::OpTypeFunction); - type->AddIdOperand(return_type); - type->AddIdOperands(param_types); - grouped_type.push_back(type); - constants_types_globals_.push_back(type); - module_.MapInstruction(type); - - return type->result_id(); -} - -Id SpirvEmitter::MakeImageType(Id sampled_type, spv::Dim dim, bool has_depth, - bool is_arrayed, bool is_multisampled, - int sampled, spv::ImageFormat format) { - // try to find it - auto& grouped_type = grouped_types_[static_cast(Op::OpTypeImage)]; - for (auto& type : grouped_type) { - if (type->id_operand(0) == sampled_type && - type->immediate_operand(1) == (unsigned int)dim && - type->immediate_operand(2) == (has_depth ? 1u : 0u) && - type->immediate_operand(3) == (is_arrayed ? 1u : 0u) && - type->immediate_operand(4) == (is_multisampled ? 1u : 0u) && - type->immediate_operand(5) == sampled && - type->immediate_operand(6) == static_cast(format)) { - return type->result_id(); - } - } - - // not found, make it - auto type = new Instruction(AllocateUniqueId(), NoType, Op::OpTypeImage); - type->AddIdOperand(sampled_type); - type->AddImmediateOperand(dim); - type->AddImmediateOperand(has_depth ? 1 : 0); - type->AddImmediateOperand(is_arrayed ? 1 : 0); - type->AddImmediateOperand(is_multisampled ? 1 : 0); - type->AddImmediateOperand(sampled); - type->AddImmediateOperand(format); - - grouped_type.push_back(type); - constants_types_globals_.push_back(type); - module_.MapInstruction(type); - - return type->result_id(); -} - -Id SpirvEmitter::MakeSampledImageType(Id image_type) { - // try to find it - auto& grouped_type = grouped_types_[static_cast(Op::OpTypeSampledImage)]; - for (auto& type : grouped_type) { - if (type->id_operand(0) == image_type) { - return type->result_id(); - } - } - - // not found, make it - auto type = - new Instruction(AllocateUniqueId(), NoType, Op::OpTypeSampledImage); - type->AddIdOperand(image_type); - - grouped_type.push_back(type); - constants_types_globals_.push_back(type); - module_.MapInstruction(type); - - return type->result_id(); -} - -Id SpirvEmitter::GetDerefTypeId(Id result_id) const { - Id type_id = GetTypeId(result_id); - assert(IsPointerType(type_id)); - return module_.instruction(type_id)->immediate_operand(1); -} - -Op SpirvEmitter::GetMostBasicTypeClass(Id type_id) const { - auto instr = module_.instruction(type_id); - - Op type_class = instr->opcode(); - switch (type_class) { - case Op::OpTypeVoid: - case Op::OpTypeBool: - case Op::OpTypeInt: - case Op::OpTypeFloat: - case Op::OpTypeStruct: - return type_class; - case Op::OpTypeVector: - case Op::OpTypeMatrix: - case Op::OpTypeArray: - case Op::OpTypeRuntimeArray: - return GetMostBasicTypeClass(instr->id_operand(0)); - case Op::OpTypePointer: - return GetMostBasicTypeClass(instr->id_operand(1)); - default: - assert(0); - return Op::OpTypeFloat; - } -} - -int SpirvEmitter::GetTypeComponentCount(Id type_id) const { - auto instr = module_.instruction(type_id); - - switch (instr->opcode()) { - case Op::OpTypeBool: - case Op::OpTypeInt: - case Op::OpTypeFloat: - return 1; - case Op::OpTypeVector: - case Op::OpTypeMatrix: - return instr->immediate_operand(1); - default: - assert(0); - return 1; - } -} - -// Return the lowest-level type of scalar that an homogeneous composite is made -// out of. -// Typically, this is just to find out if something is made out of ints or -// floats. -// However, it includes returning a structure, if say, it is an array of -// structure. -Id SpirvEmitter::GetScalarTypeId(Id type_id) const { - auto instr = module_.instruction(type_id); - - Op type_class = instr->opcode(); - switch (type_class) { - case Op::OpTypeVoid: - case Op::OpTypeBool: - case Op::OpTypeInt: - case Op::OpTypeFloat: - case Op::OpTypeStruct: - return instr->result_id(); - case Op::OpTypeVector: - case Op::OpTypeMatrix: - case Op::OpTypeArray: - case Op::OpTypeRuntimeArray: - case Op::OpTypePointer: - return GetScalarTypeId(GetContainedTypeId(type_id)); - default: - assert(0); - return NoResult; - } -} - -// Return the type of 'member' of a composite. -Id SpirvEmitter::GetContainedTypeId(Id type_id, int member) const { - auto instr = module_.instruction(type_id); - - Op type_class = instr->opcode(); - switch (type_class) { - case Op::OpTypeVector: - case Op::OpTypeMatrix: - case Op::OpTypeArray: - case Op::OpTypeRuntimeArray: - return instr->id_operand(0); - case Op::OpTypePointer: - return instr->id_operand(1); - case Op::OpTypeStruct: - return instr->id_operand(member); - default: - assert(0); - return NoResult; - } -} - -// Return the immediately contained type of a given composite type. -Id SpirvEmitter::GetContainedTypeId(Id type_id) const { - return GetContainedTypeId(type_id, 0); -} - -// See if a scalar constant of this type has already been created, so it -// can be reused rather than duplicated. (Required by the specification). -Id SpirvEmitter::FindScalarConstant(Op type_class, Op opcode, Id type_id, - uint32_t value) const { - auto& grouped_constant = grouped_constants_[static_cast(type_class)]; - for (auto constant : grouped_constant) { - if (constant->opcode() == opcode && constant->type_id() == type_id && - constant->immediate_operand(0) == value) { - return constant->result_id(); - } - } - return 0; -} - -// Version of findScalarConstant (see above) for scalars that take two operands -// (e.g. a 'double'). -Id SpirvEmitter::FindScalarConstant(Op type_class, Op opcode, Id type_id, - uint32_t v1, uint32_t v2) const { - auto& grouped_constant = grouped_constants_[static_cast(type_class)]; - for (auto constant : grouped_constant) { - if (constant->opcode() == opcode && constant->type_id() == type_id && - constant->immediate_operand(0) == v1 && - constant->immediate_operand(1) == v2) { - return constant->result_id(); - } - } - return 0; -} - -// Return true if consuming 'opcode' means consuming a constant. -// "constant" here means after final transform to executable code, -// the value consumed will be a constant, so includes specialization. -bool SpirvEmitter::IsConstantOpCode(Op opcode) const { - switch (opcode) { - case Op::OpUndef: - case Op::OpConstantTrue: - case Op::OpConstantFalse: - case Op::OpConstant: - case Op::OpConstantComposite: - case Op::OpConstantSampler: - case Op::OpConstantNull: - case Op::OpSpecConstantTrue: - case Op::OpSpecConstantFalse: - case Op::OpSpecConstant: - case Op::OpSpecConstantComposite: - case Op::OpSpecConstantOp: - return true; - default: - return false; - } -} - -Id SpirvEmitter::MakeBoolConstant(bool value, bool is_spec_constant) { - Id type_id = MakeBoolType(); - Op opcode = is_spec_constant - ? (value ? Op::OpSpecConstantTrue : Op::OpSpecConstantFalse) - : (value ? Op::OpConstantTrue : Op::OpConstantFalse); - - // See if we already made it - Id existing = 0; - auto& grouped_constant = grouped_constants_[static_cast(Op::OpTypeBool)]; - for (auto& constant : grouped_constant) { - if (constant->type_id() == type_id && constant->opcode() == opcode) { - return constant->result_id(); - } - } - - // Make it - auto c = new Instruction(AllocateUniqueId(), type_id, opcode); - constants_types_globals_.push_back(c); - grouped_constants_[static_cast(Op::OpTypeBool)].push_back(c); - module_.MapInstruction(c); - - return c->result_id(); -} - -Id SpirvEmitter::MakeIntegerConstant(Id type_id, uint32_t value, - bool is_spec_constant) { - Op opcode = is_spec_constant ? Op::OpSpecConstant : Op::OpConstant; - Id existing = FindScalarConstant(Op::OpTypeInt, opcode, type_id, value); - if (existing) { - return existing; - } - - auto c = new Instruction(AllocateUniqueId(), type_id, opcode); - c->AddImmediateOperand(value); - constants_types_globals_.push_back(c); - grouped_constants_[static_cast(Op::OpTypeInt)].push_back(c); - module_.MapInstruction(c); - - return c->result_id(); -} - -Id SpirvEmitter::MakeFloatConstant(float value, bool is_spec_constant) { - Op opcode = is_spec_constant ? Op::OpSpecConstant : Op::OpConstant; - Id type_id = MakeFloatType(32); - uint32_t uint32_value = *reinterpret_cast(&value); - Id existing = - FindScalarConstant(Op::OpTypeFloat, opcode, type_id, uint32_value); - if (existing) { - return existing; - } - - auto c = new Instruction(AllocateUniqueId(), type_id, opcode); - c->AddImmediateOperand(uint32_value); - constants_types_globals_.push_back(c); - grouped_constants_[static_cast(Op::OpTypeFloat)].push_back(c); - module_.MapInstruction(c); - - return c->result_id(); -} - -Id SpirvEmitter::MakeDoubleConstant(double value, bool is_spec_constant) { - Op opcode = is_spec_constant ? Op::OpSpecConstant : Op::OpConstant; - Id type_id = MakeFloatType(64); - uint64_t uint64_value = *reinterpret_cast(&value); - uint32_t op1 = static_cast(uint64_value & 0xFFFFFFFF); - uint32_t op2 = static_cast(uint64_value >> 32); - Id existing = FindScalarConstant(Op::OpTypeFloat, opcode, type_id, op1, op2); - if (existing) { - return existing; - } - - auto c = new Instruction(AllocateUniqueId(), type_id, opcode); - c->AddImmediateOperand(op1); - c->AddImmediateOperand(op2); - constants_types_globals_.push_back(c); - grouped_constants_[static_cast(Op::OpTypeFloat)].push_back(c); - module_.MapInstruction(c); - - return c->result_id(); -} - -Id SpirvEmitter::FindCompositeConstant( - Op type_class, std::initializer_list components) const { - auto& grouped_constant = grouped_constants_[static_cast(type_class)]; - for (auto& constant : grouped_constant) { - // same shape? - if (constant->operand_count() != components.size()) { - continue; - } - - // same contents? - bool mismatch = false; - for (int op = 0; op < constant->operand_count(); ++op) { - if (constant->id_operand(op) != *(components.begin() + op)) { - mismatch = true; - break; - } - } - if (!mismatch) { - return constant->result_id(); - } - } - - return NoResult; -} - -Id SpirvEmitter::MakeCompositeConstant(Id type_id, - std::initializer_list components) { - assert(type_id); - Op type_class = GetTypeClass(type_id); - - switch (type_class) { - case Op::OpTypeVector: - case Op::OpTypeArray: - case Op::OpTypeStruct: - case Op::OpTypeMatrix: - break; - default: - assert(0); - return MakeFloatConstant(0.0); - } - - Id existing = FindCompositeConstant(type_class, components); - if (existing) { - return existing; - } - - auto c = - new Instruction(AllocateUniqueId(), type_id, Op::OpConstantComposite); - c->AddIdOperands(components); - constants_types_globals_.push_back(c); - grouped_constants_[static_cast(type_class)].push_back(c); - module_.MapInstruction(c); - - return c->result_id(); -} - -Instruction* SpirvEmitter::AddEntryPoint(spv::ExecutionModel execution_model, - Function* entry_point, - const char* name) { - auto instr = new Instruction(Op::OpEntryPoint); - instr->AddImmediateOperand(execution_model); - instr->AddIdOperand(entry_point->id()); - instr->AddStringOperand(name); - - entry_points_.push_back(instr); - - return instr; -} - -// Currently relying on the fact that all 'value' of interest are small -// non-negative values. -void SpirvEmitter::AddExecutionMode(Function* entry_point, - spv::ExecutionMode execution_mode, - int value1, int value2, int value3) { - auto instr = new Instruction(Op::OpExecutionMode); - instr->AddIdOperand(entry_point->id()); - instr->AddImmediateOperand(execution_mode); - if (value1 >= 0) { - instr->AddImmediateOperand(value1); - } - if (value2 >= 0) { - instr->AddImmediateOperand(value2); - } - if (value3 >= 0) { - instr->AddImmediateOperand(value3); - } - - execution_modes_.push_back(instr); -} - -void SpirvEmitter::AddName(Id target_id, const char* value) { - if (!value) { - return; - } - auto instr = new Instruction(Op::OpName); - instr->AddIdOperand(target_id); - instr->AddStringOperand(value); - - names_.push_back(instr); -} - -void SpirvEmitter::AddMemberName(Id target_id, int member, const char* value) { - if (!value) { - return; - } - auto instr = new Instruction(Op::OpMemberName); - instr->AddIdOperand(target_id); - instr->AddImmediateOperand(member); - instr->AddStringOperand(value); - - names_.push_back(instr); -} - -void SpirvEmitter::AddLine(Id target_id, Id file_name, int line_number, - int column_number) { - auto instr = new Instruction(Op::OpLine); - instr->AddIdOperand(target_id); - instr->AddIdOperand(file_name); - instr->AddImmediateOperand(line_number); - instr->AddImmediateOperand(column_number); - - lines_.push_back(instr); -} - -void SpirvEmitter::AddDecoration(Id target_id, spv::Decoration decoration, - int num) { - if (decoration == static_cast(BadValue)) { - return; - } - auto instr = new Instruction(Op::OpDecorate); - instr->AddIdOperand(target_id); - instr->AddImmediateOperand(decoration); - if (num >= 0) { - instr->AddImmediateOperand(num); - } - - decorations_.push_back(instr); -} - -void SpirvEmitter::AddMemberDecoration(Id target_id, int member, - spv::Decoration decoration, int num) { - auto instr = new Instruction(Op::OpMemberDecorate); - instr->AddIdOperand(target_id); - instr->AddImmediateOperand(member); - instr->AddImmediateOperand(decoration); - if (num >= 0) { - instr->AddImmediateOperand(num); - } - - decorations_.push_back(instr); -} - -Function* SpirvEmitter::MakeMainEntry() { - assert(!main_function_); - Block* entry = nullptr; - main_function_ = MakeFunctionEntry(MakeVoidType(), "main", {}, &entry); - return main_function_; -} - -Function* SpirvEmitter::MakeFunctionEntry(Id return_type, const char* name, - std::initializer_list param_types, - Block** entry) { - Id type_id = MakeFunctionType(return_type, param_types); - Id first_param_id = - param_types.size() ? AllocateUniqueIds((int)param_types.size()) : 0; - auto function = new Function(AllocateUniqueId(), return_type, type_id, - first_param_id, module_); - if (entry) { - *entry = new Block(AllocateUniqueId(), *function); - function->push_block(*entry); - set_build_point(*entry); - } - AddName(function->id(), name); - return function; -} - -void SpirvEmitter::MakeReturn(bool implicit, Id return_value) { - if (return_value) { - auto inst = new Instruction(NoResult, NoType, Op::OpReturnValue); - inst->AddIdOperand(return_value); - build_point_->AddInstruction(inst); - } else { - build_point_->AddInstruction( - new Instruction(NoResult, NoType, Op::OpReturn)); - } - - if (!implicit) { - CreateAndSetNoPredecessorBlock("post-return"); - } -} - -void SpirvEmitter::LeaveFunction() { - Block* block = build_point_; - Function& function = build_point_->parent(); - assert(block); - - // If our function did not contain a return, add a return void now. - if (!block->is_terminated()) { - // Whether we're in an unreachable (non-entry) block. - bool unreachable = - function.entry_block() != block && !block->predecessor_count(); - - if (unreachable) { - // Given that this block is at the end of a function, it must be right - // after an explicit return, just remove it. - function.pop_block(block); - } else { - // We'll add a return instruction at the end of the current block, - // which for a non-void function is really error recovery (?), as the - // source being translated should have had an explicit return, which would - // have been followed by an unreachable block, which was handled above. - if (function.return_type() == MakeVoidType()) { - MakeReturn(true); - } else { - MakeReturn(true, CreateUndefined(function.return_type())); - } - } - } -} - -void SpirvEmitter::MakeDiscard() { - build_point_->AddInstruction(new Instruction(Op::OpKill)); - CreateAndSetNoPredecessorBlock("post-discard"); -} - -Id SpirvEmitter::CreateVariable(spv::StorageClass storage_class, Id type, - const char* name) { - Id pointer_type = MakePointer(storage_class, type); - auto instr = - new Instruction(AllocateUniqueId(), pointer_type, Op::OpVariable); - instr->AddImmediateOperand(storage_class); - - switch (storage_class) { - case spv::StorageClass::Function: - // Validation rules require the declaration in the entry block. - build_point_->parent().AddLocalVariable(instr); - break; - default: - constants_types_globals_.push_back(instr); - module_.MapInstruction(instr); - break; - } - - AddName(instr->result_id(), name); - - return instr->result_id(); -} - -Id SpirvEmitter::CreateUndefined(Id type) { - auto instr = new Instruction(AllocateUniqueId(), type, Op::OpUndef); - build_point_->AddInstruction(instr); - return instr->result_id(); -} - -void SpirvEmitter::CreateStore(Id pointer_id, Id value_id) { - auto instr = new Instruction(Op::OpStore); - instr->AddIdOperand(pointer_id); - instr->AddIdOperand(value_id); - build_point_->AddInstruction(instr); -} - -Id SpirvEmitter::CreateLoad(Id pointer_id) { - auto instr = new Instruction(AllocateUniqueId(), GetDerefTypeId(pointer_id), - Op::OpLoad); - instr->AddIdOperand(pointer_id); - build_point_->AddInstruction(instr); - - return instr->result_id(); -} - -Id SpirvEmitter::CreateAccessChain(spv::StorageClass storage_class, Id base_id, - std::vector index_ids) { - // Figure out the final resulting type. - auto base_type_id = GetTypeId(base_id); - assert(IsPointerType(base_type_id) && index_ids.size()); - auto type_id = GetContainedTypeId(base_type_id); - for (auto index_id : index_ids) { - if (IsStructType(type_id)) { - assert(IsConstantScalar(index_id)); - type_id = GetContainedTypeId(type_id, GetConstantScalar(index_id)); - } else { - type_id = GetContainedTypeId(type_id, index_id); - } - } - auto chain_type_id = MakePointer(storage_class, type_id); - - // Make the instruction - auto instr = - new Instruction(AllocateUniqueId(), chain_type_id, Op::OpAccessChain); - instr->AddIdOperand(base_id); - instr->AddIdOperands(index_ids); - build_point_->AddInstruction(instr); - - return instr->result_id(); -} - -Id SpirvEmitter::CreateArrayLength(Id struct_id, int array_member) { - auto instr = - new Instruction(AllocateUniqueId(), MakeIntType(32), Op::OpArrayLength); - instr->AddIdOperand(struct_id); - instr->AddImmediateOperand(array_member); - build_point_->AddInstruction(instr); - - return instr->result_id(); -} - -Id SpirvEmitter::CreateCompositeExtract(Id composite, Id type_id, - uint32_t index) { - auto instr = - new Instruction(AllocateUniqueId(), type_id, Op::OpCompositeExtract); - instr->AddIdOperand(composite); - instr->AddImmediateOperand(index); - build_point_->AddInstruction(instr); - - return instr->result_id(); -} - -Id SpirvEmitter::CreateCompositeExtract(Id composite, Id type_id, - std::vector indices) { - auto instr = - new Instruction(AllocateUniqueId(), type_id, Op::OpCompositeExtract); - instr->AddIdOperand(composite); - instr->AddImmediateOperands(indices); - build_point_->AddInstruction(instr); - - return instr->result_id(); -} - -Id SpirvEmitter::CreateCompositeInsert(Id object, Id composite, Id type_id, - uint32_t index) { - auto instr = - new Instruction(AllocateUniqueId(), type_id, Op::OpCompositeInsert); - instr->AddIdOperand(object); - instr->AddIdOperand(composite); - instr->AddImmediateOperand(index); - build_point_->AddInstruction(instr); - - return instr->result_id(); -} - -Id SpirvEmitter::CreateCompositeInsert(Id object, Id composite, Id type_id, - std::vector indices) { - auto instr = - new Instruction(AllocateUniqueId(), type_id, Op::OpCompositeInsert); - instr->AddIdOperand(object); - instr->AddIdOperand(composite); - instr->AddImmediateOperands(indices); - build_point_->AddInstruction(instr); - - return instr->result_id(); -} - -Id SpirvEmitter::CreateVectorExtractDynamic(Id vector, Id type_id, - Id component_index) { - auto instr = - new Instruction(AllocateUniqueId(), type_id, Op::OpVectorExtractDynamic); - instr->AddIdOperand(vector); - instr->AddIdOperand(component_index); - build_point_->AddInstruction(instr); - - return instr->result_id(); -} - -Id SpirvEmitter::CreateVectorInsertDynamic(Id vector, Id type_id, Id component, - Id component_index) { - auto instr = - new Instruction(AllocateUniqueId(), type_id, Op::OpVectorInsertDynamic); - instr->AddIdOperand(vector); - instr->AddIdOperand(component); - instr->AddIdOperand(component_index); - build_point_->AddInstruction(instr); - - return instr->result_id(); -} - -void SpirvEmitter::CreateNop() { - auto instr = new Instruction(spv::Op::OpNop); - build_point_->AddInstruction(instr); -} - -void SpirvEmitter::CreateControlBarrier( - spv::Scope execution_scope, spv::Scope memory_scope, - spv::MemorySemanticsMask memory_semantics) { - auto instr = new Instruction(Op::OpControlBarrier); - instr->AddImmediateOperand(MakeUintConstant(execution_scope)); - instr->AddImmediateOperand(MakeUintConstant(memory_scope)); - instr->AddImmediateOperand(MakeUintConstant(memory_semantics)); - build_point_->AddInstruction(instr); -} - -void SpirvEmitter::CreateMemoryBarrier( - spv::Scope execution_scope, spv::MemorySemanticsMask memory_semantics) { - auto instr = new Instruction(Op::OpMemoryBarrier); - instr->AddImmediateOperand(MakeUintConstant(execution_scope)); - instr->AddImmediateOperand(MakeUintConstant(memory_semantics)); - build_point_->AddInstruction(instr); -} - -Id SpirvEmitter::CreateUnaryOp(Op opcode, Id type_id, Id operand) { - auto instr = new Instruction(AllocateUniqueId(), type_id, opcode); - instr->AddIdOperand(operand); - build_point_->AddInstruction(instr); - - return instr->result_id(); -} - -Id SpirvEmitter::CreateBinOp(Op opcode, Id type_id, Id left, Id right) { - auto instr = new Instruction(AllocateUniqueId(), type_id, opcode); - instr->AddIdOperand(left); - instr->AddIdOperand(right); - build_point_->AddInstruction(instr); - - return instr->result_id(); -} - -Id SpirvEmitter::CreateTriOp(Op opcode, Id type_id, Id op1, Id op2, Id op3) { - auto instr = new Instruction(AllocateUniqueId(), type_id, opcode); - instr->AddIdOperand(op1); - instr->AddIdOperand(op2); - instr->AddIdOperand(op3); - build_point_->AddInstruction(instr); - - return instr->result_id(); -} - -Id SpirvEmitter::CreateOp(Op opcode, Id type_id, - const std::vector& operands) { - auto instr = new Instruction(AllocateUniqueId(), type_id, opcode); - instr->AddIdOperands(operands); - build_point_->AddInstruction(instr); - - return instr->result_id(); -} - -Id SpirvEmitter::CreateFunctionCall(Function* function, - std::vector args) { - auto instr = new Instruction(AllocateUniqueId(), function->return_type(), - Op::OpFunctionCall); - instr->AddIdOperand(function->id()); - instr->AddIdOperands(args); - build_point_->AddInstruction(instr); - - return instr->result_id(); -} - -Id SpirvEmitter::CreateSwizzle(Id type_id, Id source, - std::vector channels) { - if (channels.size() == 1) { - return CreateCompositeExtract(source, type_id, channels.front()); - } - auto instr = - new Instruction(AllocateUniqueId(), type_id, Op::OpVectorShuffle); - assert(IsVector(source)); - instr->AddIdOperand(source); - instr->AddIdOperand(source); - instr->AddImmediateOperands(channels); - build_point_->AddInstruction(instr); - - return instr->result_id(); -} - -Id SpirvEmitter::CreateLvalueSwizzle(Id type_id, Id target, Id source, - std::vector channels) { - assert(GetComponentCount(source) == channels.size()); - if (channels.size() == 1 && GetComponentCount(source) == 1) { - return CreateCompositeInsert(source, target, type_id, channels.front()); - } - - auto instr = - new Instruction(AllocateUniqueId(), type_id, Op::OpVectorShuffle); - assert(IsVector(source)); - assert(IsVector(target)); - instr->AddIdOperand(target); - instr->AddIdOperand(source); - - // Set up an identity shuffle from the base value to the result value. - uint32_t components[4] = {0, 1, 2, 3}; - - // Punch in the l-value swizzle. - int component_count = GetComponentCount(target); - for (int i = 0; i < (int)channels.size(); ++i) { - components[channels[i]] = component_count + i; - } - - // finish the instruction with these components selectors. - for (int i = 0; i < component_count; ++i) { - instr->AddImmediateOperand(components[i]); - } - build_point_->AddInstruction(instr); - - return instr->result_id(); -} - -void SpirvEmitter::PromoteScalar(spv::Decoration precision, Id& left, - Id& right) { - int direction = GetComponentCount(right) - GetComponentCount(left); - if (direction > 0) { - left = SmearScalar(precision, left, GetTypeId(right)); - } else if (direction < 0) { - right = SmearScalar(precision, right, GetTypeId(left)); - } -} - -Id SpirvEmitter::SmearScalar(spv::Decoration precision, Id scalar_value, - Id vector_type_id) { - assert(GetComponentCount(scalar_value) == 1); - int component_count = GetTypeComponentCount(vector_type_id); - if (component_count == 1) { - return scalar_value; - } - - auto instr = new Instruction(AllocateUniqueId(), vector_type_id, - Op::OpCompositeConstruct); - for (int i = 0; i < component_count; ++i) { - instr->AddIdOperand(scalar_value); - } - build_point_->AddInstruction(instr); - - return instr->result_id(); -} - -Id SpirvEmitter::CreateExtendedInstructionCall(spv::Decoration precision, - Id result_type, - Id instruction_set, - int instruction_ordinal, - std::initializer_list args) { - auto instr = new Instruction(AllocateUniqueId(), result_type, Op::OpExtInst); - instr->AddIdOperand(instruction_set); - instr->AddImmediateOperand(instruction_ordinal); - instr->AddIdOperands(args); - - build_point_->AddInstruction(instr); - return instr->result_id(); -} - -Id SpirvEmitter::CreateGlslStd450InstructionCall( - spv::Decoration precision, Id result_type, - spv::GLSLstd450 instruction_ordinal, std::initializer_list args) { - if (!glsl_std_450_instruction_set_) { - glsl_std_450_instruction_set_ = ImportExtendedInstructions("GLSL.std.450"); - } - return CreateExtendedInstructionCall( - precision, result_type, glsl_std_450_instruction_set_, - static_cast(instruction_ordinal), args); -} - -// Accept all parameters needed to create a texture instruction. -// Create the correct instruction based on the inputs, and make the call. -Id SpirvEmitter::CreateTextureCall(spv::Decoration precision, Id result_type, - bool fetch, bool proj, bool gather, - const TextureParameters& parameters) { - static const int kMaxTextureArgs = 10; - Id tex_args[kMaxTextureArgs] = {}; - - // Set up the fixed arguments. - int arg_count = 0; - bool is_explicit = false; - tex_args[arg_count++] = parameters.sampler; - tex_args[arg_count++] = parameters.coords; - if (parameters.depth_ref) { - tex_args[arg_count++] = parameters.depth_ref; - } - if (parameters.comp) { - tex_args[arg_count++] = parameters.comp; - } - - // Set up the optional arguments. - int opt_arg_index = arg_count; // track which operand, if it exists, is the - // mask of optional arguments speculatively - // make room for the mask operand. - ++arg_count; - auto mask = spv::ImageOperandsMask::MaskNone; // the mask operand - if (parameters.bias) { - mask = mask | spv::ImageOperandsMask::Bias; - tex_args[arg_count++] = parameters.bias; - } - if (parameters.lod) { - mask = mask | spv::ImageOperandsMask::Lod; - tex_args[arg_count++] = parameters.lod; - is_explicit = true; - } - if (parameters.grad_x) { - mask = mask | spv::ImageOperandsMask::Grad; - tex_args[arg_count++] = parameters.grad_x; - tex_args[arg_count++] = parameters.grad_y; - is_explicit = true; - } - if (parameters.offset) { - if (IsConstant(parameters.offset)) { - mask = mask | spv::ImageOperandsMask::ConstOffset; - } else { - mask = mask | spv::ImageOperandsMask::Offset; - } - tex_args[arg_count++] = parameters.offset; - } - if (parameters.offsets) { - mask = mask | spv::ImageOperandsMask::ConstOffsets; - tex_args[arg_count++] = parameters.offsets; - } - if (parameters.sample) { - mask = mask | spv::ImageOperandsMask::Sample; - tex_args[arg_count++] = parameters.sample; - } - if (mask == spv::ImageOperandsMask::MaskNone) { - --arg_count; // undo speculative reservation for the mask argument - } else { - tex_args[opt_arg_index] = static_cast(mask); - } - - // Set up the instruction. - Op opcode; - opcode = Op::OpImageSampleImplicitLod; - if (fetch) { - opcode = Op::OpImageFetch; - } else if (gather) { - if (parameters.depth_ref) { - opcode = Op::OpImageDrefGather; - } else { - opcode = Op::OpImageGather; - } - } else if (is_explicit) { - if (parameters.depth_ref) { - if (proj) { - opcode = Op::OpImageSampleProjDrefExplicitLod; - } else { - opcode = Op::OpImageSampleDrefExplicitLod; - } - } else { - if (proj) { - opcode = Op::OpImageSampleProjExplicitLod; - } else { - opcode = Op::OpImageSampleExplicitLod; - } - } - } else { - if (parameters.depth_ref) { - if (proj) { - opcode = Op::OpImageSampleProjDrefImplicitLod; - } else { - opcode = Op::OpImageSampleDrefImplicitLod; - } - } else { - if (proj) { - opcode = Op::OpImageSampleProjImplicitLod; - } else { - opcode = Op::OpImageSampleImplicitLod; - } - } - } - - // See if the result type is expecting a smeared result. - // This happens when a legacy shadow*() call is made, which gets a vec4 back - // instead of a float. - Id smeared_type = result_type; - if (!IsScalarType(result_type)) { - switch (opcode) { - case Op::OpImageSampleDrefImplicitLod: - case Op::OpImageSampleDrefExplicitLod: - case Op::OpImageSampleProjDrefImplicitLod: - case Op::OpImageSampleProjDrefExplicitLod: - result_type = GetScalarTypeId(result_type); - break; - default: - break; - } - } - - // Build the SPIR-V instruction - auto instr = new Instruction(AllocateUniqueId(), result_type, opcode); - for (int op = 0; op < opt_arg_index; ++op) { - instr->AddIdOperand(tex_args[op]); - } - if (opt_arg_index < arg_count) { - instr->AddImmediateOperand(tex_args[opt_arg_index]); - } - for (int op = opt_arg_index + 1; op < arg_count; ++op) { - instr->AddIdOperand(tex_args[op]); - } - SetPrecision(instr->result_id(), precision); - build_point_->AddInstruction(instr); - - Id result_id = instr->result_id(); - - // When a smear is needed, do it, as per what was computed above when - // result_type was changed to a scalar type. - if (result_type != smeared_type) { - result_id = SmearScalar(precision, result_id, smeared_type); - } - - return result_id; -} - -Id SpirvEmitter::CreateTextureQueryCall(Op opcode, - const TextureParameters& parameters) { - // Figure out the result type. - Id result_type = 0; - switch (opcode) { - case Op::OpImageQuerySize: - case Op::OpImageQuerySizeLod: { - int component_count; - switch (GetTypeDimensionality(GetImageType(parameters.sampler))) { - case spv::Dim::Dim1D: - case spv::Dim::Buffer: - component_count = 1; - break; - case spv::Dim::Dim2D: - case spv::Dim::Cube: - case spv::Dim::Rect: - component_count = 2; - break; - case spv::Dim::Dim3D: - component_count = 3; - break; - case spv::Dim::SubpassData: - CheckNotImplemented("input-attachment dim"); - break; - default: - assert(0); - break; - } - if (IsArrayedImageType(GetImageType(parameters.sampler))) { - ++component_count; - } - if (component_count == 1) { - result_type = MakeIntType(32); - } else { - result_type = MakeVectorType(MakeIntType(32), component_count); - } - break; - } - case Op::OpImageQueryLod: - result_type = MakeVectorType(MakeFloatType(32), 2); - break; - case Op::OpImageQueryLevels: - case Op::OpImageQuerySamples: - result_type = MakeIntType(32); - break; - default: - assert(0); - break; - } - - auto instr = new Instruction(AllocateUniqueId(), result_type, opcode); - instr->AddIdOperand(parameters.sampler); - if (parameters.coords) { - instr->AddIdOperand(parameters.coords); - } - if (parameters.lod) { - instr->AddIdOperand(parameters.lod); - } - build_point_->AddInstruction(instr); - - return instr->result_id(); -} - -Id SpirvEmitter::CreateCompare(spv::Decoration precision, Id value1, Id value2, - bool is_equal) { - Id bool_type_id = MakeBoolType(); - Id value_type_id = GetTypeId(value1); - - assert(value_type_id == GetTypeId(value2)); - assert(!IsScalar(value1)); - - // Vectors. - if (IsVectorType(value_type_id)) { - Op op; - if (GetMostBasicTypeClass(value_type_id) == Op::OpTypeFloat) { - op = is_equal ? Op::OpFOrdEqual : Op::OpFOrdNotEqual; - } else { - op = is_equal ? Op::OpIEqual : Op::OpINotEqual; - } - - Id bool_vector_type_id = - MakeVectorType(bool_type_id, GetTypeComponentCount(value_type_id)); - Id bool_vector = CreateBinOp(op, bool_vector_type_id, value1, value2); - SetPrecision(bool_vector, precision); - - // Reduce vector compares with any() and all(). - op = is_equal ? Op::OpAll : Op::OpAny; - - return CreateUnaryOp(op, bool_type_id, bool_vector); - } - - CheckNotImplemented("Composite comparison of non-vectors"); - return NoResult; - - // Recursively handle aggregates, which include matrices, arrays, and - // structures - // and accumulate the results. - - // Matrices - - // Arrays - - // int numElements; - // const llvm::ArrayType* arrayType = - // llvm::dyn_cast(value1->getType()); - // if (arrayType) - // numElements = (int)arrayType->getNumElements(); - // else { - // // better be structure - // const llvm::StructType* structType = - // llvm::dyn_cast(value1->getType()); - // assert(structType); - // numElements = structType->getNumElements(); - //} - - // assert(numElements > 0); - - // for (int element = 0; element < numElements; ++element) { - // // Get intermediate comparison values - // llvm::Value* element1 = builder.CreateExtractValue(value1, element, - // "element1"); - // setInstructionPrecision(element1, precision); - // llvm::Value* element2 = builder.CreateExtractValue(value2, element, - // "element2"); - // setInstructionPrecision(element2, precision); - - // llvm::Value* subResult = createCompare(precision, element1, element2, - // equal, "comp"); - - // // Accumulate intermediate comparison - // if (element == 0) - // result = subResult; - // else { - // if (equal) - // result = builder.CreateAnd(result, subResult); - // else - // result = builder.CreateOr(result, subResult); - // setInstructionPrecision(result, precision); - // } - //} - - // return result; -} - -// OpCompositeConstruct -Id SpirvEmitter::CreateCompositeConstruct(Id type_id, - std::vector constituent_ids) { - assert(IsAggregateType(type_id) || - (GetTypeComponentCount(type_id) > 1 && - GetTypeComponentCount(type_id) == constituent_ids.size())); - - auto instr = - new Instruction(AllocateUniqueId(), type_id, Op::OpCompositeConstruct); - instr->AddIdOperands(constituent_ids); - build_point_->AddInstruction(instr); - - return instr->result_id(); -} - -Id SpirvEmitter::CreateConstructor(spv::Decoration precision, - std::vector source_ids, - Id result_type_id) { - Id result = 0; - int target_component_count = GetTypeComponentCount(result_type_id); - int target_component = 0; - - // Special case: when calling a vector constructor with a single scalar - // argument, smear the scalar - if (source_ids.size() == 1 && IsScalar(source_ids[0]) && - target_component_count > 1) { - return SmearScalar(precision, source_ids[0], result_type_id); - } - - // Accumulate the arguments for OpCompositeConstruct. - Id scalar_type_id = GetScalarTypeId(result_type_id); - std::vector constituent_ids; - for (auto source_id : source_ids) { - assert(!IsAggregate(source_id)); - int source_component_count = GetComponentCount(source_id); - int sources_to_use = source_component_count; - if (sources_to_use + target_component > target_component_count) { - sources_to_use = target_component_count - target_component; - } - for (int s = 0; s < sources_to_use; ++s) { - Id arg = source_id; - if (source_component_count > 1) { - arg = CreateSwizzle(scalar_type_id, arg, {static_cast(s)}); - } - if (target_component_count > 1) { - constituent_ids.push_back(arg); - } else { - result = arg; - } - ++target_component; - } - if (target_component >= target_component_count) { - break; - } - } - - if (!constituent_ids.empty()) { - result = CreateCompositeConstruct(result_type_id, constituent_ids); - } - - SetPrecision(result, precision); - - return result; -} - -Id SpirvEmitter::CreateMatrixConstructor(spv::Decoration precision, - std::vector sources, - Id result_type_id) { - Id component_type_id = GetScalarTypeId(result_type_id); - int column_count = GetTypeColumnCount(result_type_id); - int row_count = GetTypeRowCount(result_type_id); - - // Will use a two step process: - // 1. make a compile-time 2D array of values - // 2. construct a matrix from that array - - // Step 1. - - // Initialize the array to the identity matrix. - Id ids[kMaxMatrixSize][kMaxMatrixSize]; - Id one = MakeFloatConstant(1.0); - Id zero = MakeFloatConstant(0.0); - for (int col = 0; col < kMaxMatrixSize; ++col) { - for (int row = 0; row < kMaxMatrixSize; ++row) { - if (col == row) { - ids[col][row] = one; - } else { - ids[col][row] = zero; - } - } - } - - // Modify components as dictated by the arguments. - if (sources.size() == 1 && IsScalar(sources[0])) { - // A single scalar; resets the diagonals. - for (int col = 0; col < kMaxMatrixSize; ++col) { - ids[col][col] = sources[0]; - } - } else if (IsMatrix(sources[0])) { - // Constructing from another matrix; copy over the parts that exist in both - // the argument and constructee. - Id matrix = sources[0]; - int min_column_count = std::min(column_count, GetColumnCount(matrix)); - int min_row_count = std::min(row_count, GetRowCount(matrix)); - for (int col = 0; col < min_column_count; ++col) { - std::vector indexes; - indexes.push_back(col); - for (int row = 0; row < min_row_count; ++row) { - indexes.push_back(row); - ids[col][row] = - CreateCompositeExtract(matrix, component_type_id, indexes); - indexes.pop_back(); - SetPrecision(ids[col][row], precision); - } - } - } else { - // Fill in the matrix in column-major order with whatever argument - // components are available. - int row = 0; - int col = 0; - for (auto source : sources) { - Id arg_component = source; - for (int comp = 0; comp < GetComponentCount(source); ++comp) { - if (GetComponentCount(source) > 1) { - arg_component = - CreateCompositeExtract(source, component_type_id, comp); - SetPrecision(arg_component, precision); - } - ids[col][row++] = arg_component; - if (row == row_count) { - row = 0; - ++col; - } - } - } - } - - // Step 2: construct a matrix from that array. - - // Make the column vectors. - Id column_type_id = GetContainedTypeId(result_type_id); - std::vector matrix_columns; - for (int col = 0; col < column_count; ++col) { - std::vector vector_components; - for (int row = 0; row < row_count; ++row) { - vector_components.push_back(ids[col][row]); - } - matrix_columns.push_back( - CreateCompositeConstruct(column_type_id, vector_components)); - } - - // Make the matrix. - return CreateCompositeConstruct(result_type_id, matrix_columns); -} - -SpirvEmitter::If::If(SpirvEmitter& emitter, Id condition) - : emitter_(emitter), condition_(condition) { - function_ = &emitter_.build_point()->parent(); - - // make the blocks, but only put the then-block into the function, - // the else-block and merge-block will be added later, in order, after - // earlier code is emitted - then_block_ = new Block(emitter_.AllocateUniqueId(), *function_); - merge_block_ = new Block(emitter_.AllocateUniqueId(), *function_); - - // Save the current block, so that we can add in the flow control split when - // makeEndIf is called. - header_block_ = emitter_.build_point(); - - function_->push_block(then_block_); - emitter_.set_build_point(then_block_); -} - -void SpirvEmitter::If::MakeBeginElse() { - // Close out the "then" by having it jump to the merge_block - emitter_.CreateBranch(merge_block_); - - // Make the first else block and add it to the function - else_block_ = new Block(emitter_.AllocateUniqueId(), *function_); - function_->push_block(else_block_); - - // Start building the else block - emitter_.set_build_point(else_block_); -} - -void SpirvEmitter::If::MakeEndIf() { - // jump to the merge block - emitter_.CreateBranch(merge_block_); - - // Go back to the header_block and make the flow control split - emitter_.set_build_point(header_block_); - emitter_.CreateSelectionMerge(merge_block_, - spv::SelectionControlMask::MaskNone); - if (else_block_) { - emitter_.CreateConditionalBranch(condition_, then_block_, else_block_); - } else { - emitter_.CreateConditionalBranch(condition_, then_block_, merge_block_); - } - - // add the merge block to the function - function_->push_block(merge_block_); - emitter_.set_build_point(merge_block_); -} - -void SpirvEmitter::MakeSwitch(Id selector, int segment_count, - std::vector case_values, - std::vector value_index_to_segment, - int default_segment, - std::vector& segment_blocks) { - Function& function = build_point_->parent(); - - // Make all the blocks. - for (int s = 0; s < segment_count; ++s) { - segment_blocks.push_back(new Block(AllocateUniqueId(), function)); - } - - Block* merge_block = new Block(AllocateUniqueId(), function); - - // Make and insert the switch's selection-merge instruction. - CreateSelectionMerge(merge_block, spv::SelectionControlMask::MaskNone); - - // Make the switch instruction. - auto switchInst = new Instruction(NoResult, NoType, Op::OpSwitch); - switchInst->AddIdOperand(selector); - switchInst->AddIdOperand(default_segment >= 0 - ? segment_blocks[default_segment]->id() - : merge_block->id()); - for (size_t i = 0; i < case_values.size(); ++i) { - switchInst->AddImmediateOperand(case_values[i]); - switchInst->AddIdOperand(segment_blocks[value_index_to_segment[i]]->id()); - } - build_point_->AddInstruction(switchInst); - - // Push the merge block. - switch_merges_.push(merge_block); -} - -void SpirvEmitter::AddSwitchBreak() { - // Branch to the top of the merge block stack. - CreateBranch(switch_merges_.top()); - CreateAndSetNoPredecessorBlock("post-switch-break"); -} - -void SpirvEmitter::NextSwitchSegment(std::vector& segment_block, - int next_segment) { - int last_segment = next_segment - 1; - if (last_segment >= 0) { - // Close out previous segment by jumping, if necessary, to next segment. - if (!build_point_->is_terminated()) { - CreateBranch(segment_block[next_segment]); - } - } - Block* block = segment_block[next_segment]; - block->parent().push_block(block); - set_build_point(block); -} - -void SpirvEmitter::EndSwitch(std::vector& segment_block) { - // Close out previous segment by jumping, if necessary, to next segment. - if (!build_point_->is_terminated()) { - AddSwitchBreak(); - } - - switch_merges_.top()->parent().push_block(switch_merges_.top()); - set_build_point(switch_merges_.top()); - - switch_merges_.pop(); -} - -void SpirvEmitter::MakeNewLoop(bool test_first) { - loops_.push(Loop(*this, test_first)); - const Loop& loop = loops_.top(); - - // The loop test is always emitted before the loop body. - // But if the loop test executes at the bottom of the loop, then - // execute the test only on the second and subsequent iterations. - - // Remember the block that branches to the loop header. This - // is required for the test-after-body case. - Block* preheader = build_point(); - - // Branch into the loop - CreateBranch(loop.header); - - // Set ourselves inside the loop - loop.function->push_block(loop.header); - set_build_point(loop.header); - - if (!test_first) { - // Generate code to defer the loop test until the second and - // subsequent iterations. - - // It's always the first iteration when coming from the preheader. - // All other branches to this loop header will need to indicate "false", - // but we don't yet know where they will come from. - loop.is_first_iteration->AddIdOperand(MakeBoolConstant(true)); - loop.is_first_iteration->AddIdOperand(preheader->id()); - build_point()->AddInstruction(loop.is_first_iteration); - - // Mark the end of the structured loop. This must exist in the loop header - // block. - CreateLoopMerge(loop.merge, loop.header, spv::LoopControlMask::MaskNone); - - // Generate code to see if this is the first iteration of the loop. - // It needs to be in its own block, since the loop merge and - // the selection merge instructions can't both be in the same - // (header) block. - Block* firstIterationCheck = new Block(AllocateUniqueId(), *loop.function); - CreateBranch(firstIterationCheck); - loop.function->push_block(firstIterationCheck); - set_build_point(firstIterationCheck); - - // Control flow after this "if" normally reconverges at the loop body. - // However, the loop test has a "break branch" out of this selection - // construct because it can transfer control to the loop merge block. - CreateSelectionMerge(loop.body, spv::SelectionControlMask::MaskNone); - - Block* loopTest = new Block(AllocateUniqueId(), *loop.function); - CreateConditionalBranch(loop.is_first_iteration->result_id(), loop.body, - loopTest); - - loop.function->push_block(loopTest); - set_build_point(loopTest); - } -} - -void SpirvEmitter::CreateLoopTestBranch(Id condition) { - const Loop& loop = loops_.top(); - - // Generate the merge instruction. If the loop test executes before - // the body, then this is a loop merge. Otherwise the loop merge - // has already been generated and this is a conditional merge. - if (loop.test_first) { - CreateLoopMerge(loop.merge, loop.header, spv::LoopControlMask::MaskNone); - // Branching to the "body" block will keep control inside - // the loop. - CreateConditionalBranch(condition, loop.body, loop.merge); - loop.function->push_block(loop.body); - set_build_point(loop.body); - } else { - // The branch to the loop merge block is the allowed exception - // to the structured control flow. Otherwise, control flow will - // continue to loop.body block. Since that is already the target - // of a merge instruction, and a block can't be the target of more - // than one merge instruction, we need to make an intermediate block. - Block* stayInLoopBlock = new Block(AllocateUniqueId(), *loop.function); - CreateSelectionMerge(stayInLoopBlock, spv::SelectionControlMask::MaskNone); - - // This is the loop test. - CreateConditionalBranch(condition, stayInLoopBlock, loop.merge); - - // The dummy block just branches to the real loop body. - loop.function->push_block(stayInLoopBlock); - set_build_point(stayInLoopBlock); - CreateBranchToBody(); - } -} - -void SpirvEmitter::CreateBranchToBody() { - const Loop& loop = loops_.top(); - assert(loop.body); - - // This is a reconvergence of control flow, so no merge instruction - // is required. - CreateBranch(loop.body); - loop.function->push_block(loop.body); - set_build_point(loop.body); -} - -void SpirvEmitter::CreateLoopContinue() { - CreateBranchToLoopHeaderFromInside(loops_.top()); - // Set up a block for dead code. - CreateAndSetNoPredecessorBlock("post-loop-continue"); -} - -void SpirvEmitter::CreateLoopExit() { - CreateBranch(loops_.top().merge); - // Set up a block for dead code. - CreateAndSetNoPredecessorBlock("post-loop-break"); -} - -void SpirvEmitter::CloseLoop() { - const Loop& loop = loops_.top(); - - // Branch back to the top. - CreateBranchToLoopHeaderFromInside(loop); - - // Add the merge block and set the build point to it. - loop.function->push_block(loop.merge); - set_build_point(loop.merge); - - loops_.pop(); -} - -void SpirvEmitter::ClearAccessChain() { - access_chain_.base = NoResult; - access_chain_.index_chain.clear(); - access_chain_.instr = NoResult; - access_chain_.swizzle.clear(); - access_chain_.component = NoResult; - access_chain_.pre_swizzle_base_type = NoType; - access_chain_.is_rvalue = false; -} - -// Turn the described access chain in 'accessChain' into an instruction -// 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. -Id SpirvEmitter::CollapseAccessChain() { - assert(access_chain_.is_rvalue == false); - - if (!access_chain_.index_chain.empty()) { - if (!access_chain_.instr) { - auto storage_class = module_.storage_class(GetTypeId(access_chain_.base)); - access_chain_.instr = CreateAccessChain(storage_class, access_chain_.base, - access_chain_.index_chain); - } - return access_chain_.instr; - } else { - return access_chain_.base; - } - - // Note that non-trivial swizzling is left pending... -} - -// Clear out swizzle if it is redundant, that is reselecting the same components -// that would be present without the swizzle. -void SpirvEmitter::SimplifyAccessChainSwizzle() { - // If the swizzle has fewer components than the vector, it is subsetting, and - // must stay to preserve that fact. - if (GetTypeComponentCount(access_chain_.pre_swizzle_base_type) > - access_chain_.swizzle.size()) { - return; - } - - // If components are out of order, it is a swizzle. - for (size_t i = 0; i < access_chain_.swizzle.size(); ++i) { - if (i != access_chain_.swizzle[i]) { - return; - } - } - - // Otherwise, there is no need to track this swizzle. - access_chain_.swizzle.clear(); - if (access_chain_.component == NoResult) { - access_chain_.pre_swizzle_base_type = NoType; - } -} - -// 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. -// -// 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. -void SpirvEmitter::TransferAccessChainSwizzle(bool dynamic) { - // too complex? - if (access_chain_.swizzle.size() > 1) { - return; - } - - // non existent? - if (access_chain_.swizzle.empty() && access_chain_.component == NoResult) { - return; - } - - // single component... - - // skip doing it for Boolean vectors - if (IsBoolType(GetContainedTypeId(access_chain_.pre_swizzle_base_type))) { - return; - } - - if (access_chain_.swizzle.size() == 1) { - // handle static component - access_chain_.index_chain.push_back( - MakeUintConstant(access_chain_.swizzle.front())); - access_chain_.swizzle.clear(); - // note, the only valid remaining dynamic access would be to this one - // component, so don't bother even looking at access_chain_.component - access_chain_.pre_swizzle_base_type = NoType; - access_chain_.component = NoResult; - } else if (dynamic && access_chain_.component != NoResult) { - // handle dynamic component - access_chain_.index_chain.push_back(access_chain_.component); - access_chain_.pre_swizzle_base_type = NoType; - access_chain_.component = NoResult; - } -} - -void SpirvEmitter::PushAccessChainSwizzle(std::vector swizzle, - Id pre_swizzle_base_type) { - // Swizzles can be stacked in GLSL, but simplified to a single - // one here; the base type doesn't change. - if (access_chain_.pre_swizzle_base_type == NoType) { - access_chain_.pre_swizzle_base_type = pre_swizzle_base_type; - } - - // If needed, propagate the swizzle for the current access chain. - if (access_chain_.swizzle.size()) { - std::vector oldSwizzle = access_chain_.swizzle; - access_chain_.swizzle.resize(0); - for (unsigned int i = 0; i < swizzle.size(); ++i) { - access_chain_.swizzle.push_back(oldSwizzle[swizzle[i]]); - } - } else { - access_chain_.swizzle = swizzle; - } - - // Determine if we need to track this swizzle anymore. - SimplifyAccessChainSwizzle(); -} - -void SpirvEmitter::CreateAccessChainStore(Id rvalue) { - assert(access_chain_.is_rvalue == false); - - TransferAccessChainSwizzle(true); - Id base = CollapseAccessChain(); - - if (access_chain_.swizzle.size() && access_chain_.component != NoResult) { - CheckNotImplemented( - "simultaneous l-value swizzle and dynamic component selection"); - return; - } - - // 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 (access_chain_.swizzle.size()) { - Id temp_base_id = CreateLoad(base); - source = CreateLvalueSwizzle(GetTypeId(temp_base_id), temp_base_id, rvalue, - access_chain_.swizzle); - } - - // Dynamic component selection. - if (access_chain_.component != NoResult) { - Id temp_base_id = (source == NoResult) ? CreateLoad(base) : source; - source = CreateVectorInsertDynamic(temp_base_id, GetTypeId(temp_base_id), - rvalue, access_chain_.component); - } - - if (source == NoResult) { - source = rvalue; - } - - CreateStore(source, base); -} - -Id SpirvEmitter::CreateAccessChainLoad(Id result_type_id) { - Id id; - - if (access_chain_.is_rvalue) { - // Transfer access chain, but keep it static, so we can stay in registers. - TransferAccessChainSwizzle(false); - if (!access_chain_.index_chain.empty()) { - Id swizzle_base_type_id = access_chain_.pre_swizzle_base_type != NoType - ? access_chain_.pre_swizzle_base_type - : result_type_id; - - // If all the accesses are constants we can use OpCompositeExtract. - std::vector indexes; - bool constant = true; - for (auto index : access_chain_.index_chain) { - if (IsConstantScalar(index)) { - indexes.push_back(GetConstantScalar(index)); - } else { - constant = false; - break; - } - } - - if (constant) { - id = CreateCompositeExtract(access_chain_.base, swizzle_base_type_id, - indexes); - } else { - // Make a new function variable for this r-value. - Id lvalue = CreateVariable(spv::StorageClass::Function, - GetTypeId(access_chain_.base), "indexable"); - - // Store into it. - CreateStore(access_chain_.base, lvalue); - - // Move base to the new variable. - access_chain_.base = lvalue; - access_chain_.is_rvalue = false; - - // Load through the access chain. - id = CreateLoad(CollapseAccessChain()); - } - } else { - id = access_chain_.base; - } - } else { - TransferAccessChainSwizzle(true); - // Load through the access chain. - id = CreateLoad(CollapseAccessChain()); - } - - // Done, unless there are swizzles to do. - if (access_chain_.swizzle.empty() && access_chain_.component == NoResult) { - return id; - } - - // Do remaining swizzling. - // First, static swizzling. - if (access_chain_.swizzle.size()) { - // Static swizzle. - Id swizzledType = GetScalarTypeId(GetTypeId(id)); - if (access_chain_.swizzle.size() > 1) { - swizzledType = - MakeVectorType(swizzledType, (int)access_chain_.swizzle.size()); - } - id = CreateSwizzle(swizzledType, id, access_chain_.swizzle); - } - - // Dynamic single-component selection. - if (access_chain_.component != NoResult) { - id = - CreateVectorExtractDynamic(id, result_type_id, access_chain_.component); - } - - return id; -} - -Id SpirvEmitter::CreateAccessChainLValue() { - assert(access_chain_.is_rvalue == false); - - TransferAccessChainSwizzle(true); - Id lvalue = CollapseAccessChain(); - - // If swizzle 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. - // This does not go with getting a direct l-value pointer. - assert(access_chain_.swizzle.empty()); - assert(access_chain_.component == NoResult); - - return lvalue; -} - -void SpirvEmitter::Serialize(std::vector& out) const { - // Header, before first instructions: - out.push_back(spv::MagicNumber); - out.push_back(spv::Version); - out.push_back(builder_number_); - out.push_back(unique_id_ + 1); - out.push_back(0); - - for (auto capability : capabilities_) { - Instruction capInst(0, 0, Op::OpCapability); - capInst.AddImmediateOperand(capability); - capInst.Serialize(out); - } - - // TBD: OpExtension ... - - SerializeInstructions(out, imports_); - Instruction memInst(0, 0, Op::OpMemoryModel); - memInst.AddImmediateOperand(addressing_model_); - memInst.AddImmediateOperand(memory_model_); - memInst.Serialize(out); - - // Instructions saved up while building: - SerializeInstructions(out, entry_points_); - SerializeInstructions(out, execution_modes_); - - // Debug instructions: - if (source_language_ != spv::SourceLanguage::Unknown) { - Instruction sourceInst(0, 0, Op::OpSource); - sourceInst.AddImmediateOperand(source_language_); - sourceInst.AddImmediateOperand(source_version_); - sourceInst.Serialize(out); - } - for (auto extension : source_extensions_) { - Instruction extInst(0, 0, Op::OpSourceExtension); - extInst.AddStringOperand(extension); - extInst.Serialize(out); - } - SerializeInstructions(out, names_); - SerializeInstructions(out, lines_); - - // Annotation instructions: - SerializeInstructions(out, decorations_); - - SerializeInstructions(out, constants_types_globals_); - SerializeInstructions(out, externals_); - - // The functions: - module_.Serialize(out); -} - -void SpirvEmitter::SerializeInstructions( - std::vector& out, - const std::vector& instructions) const { - for (auto instruction : instructions) { - instruction->Serialize(out); - } -} - -// Utility method for creating a new block and setting the insert point to -// be in it. This is useful for flow-control operations that need a "dummy" -// block proceeding them (e.g. instructions after a discard, etc). -void SpirvEmitter::CreateAndSetNoPredecessorBlock(const char* name) { - Block* block = new Block(AllocateUniqueId(), build_point_->parent()); - block->set_unreachable(true); - build_point_->parent().push_block(block); - set_build_point(block); - - AddName(block->id(), name); -} - -void SpirvEmitter::CreateBranch(Block* block) { - auto instr = new Instruction(Op::OpBranch); - instr->AddIdOperand(block->id()); - build_point_->AddInstruction(instr); - block->AddPredecessor(build_point_); -} - -void SpirvEmitter::CreateSelectionMerge(Block* merge_block, - spv::SelectionControlMask control) { - auto instr = new Instruction(Op::OpSelectionMerge); - instr->AddIdOperand(merge_block->id()); - instr->AddImmediateOperand(control); - build_point_->AddInstruction(instr); -} - -void SpirvEmitter::CreateLoopMerge(Block* merge_block, Block* continueBlock, - spv::LoopControlMask control) { - auto instr = new Instruction(Op::OpLoopMerge); - instr->AddIdOperand(merge_block->id()); - instr->AddIdOperand(continueBlock->id()); - instr->AddImmediateOperand(control); - build_point_->AddInstruction(instr); -} - -void SpirvEmitter::CreateConditionalBranch(Id condition, Block* then_block, - Block* else_block) { - auto instr = new Instruction(Op::OpBranchConditional); - instr->AddIdOperand(condition); - instr->AddIdOperand(then_block->id()); - instr->AddIdOperand(else_block->id()); - build_point_->AddInstruction(instr); - then_block->AddPredecessor(build_point_); - else_block->AddPredecessor(build_point_); -} - -SpirvEmitter::Loop::Loop(SpirvEmitter& emitter, bool testFirstArg) - : function(&emitter.build_point()->parent()), - header(new Block(emitter.AllocateUniqueId(), *function)), - merge(new Block(emitter.AllocateUniqueId(), *function)), - body(new Block(emitter.AllocateUniqueId(), *function)), - test_first(testFirstArg), - is_first_iteration(nullptr) { - if (!test_first) { - // You may be tempted to rewrite this as - // new Instruction(builder.getUniqueId(), builder.makeBoolType(), OpPhi); - // This will cause subtle test failures because builder.getUniqueId(), - // and builder.makeBoolType() can then get run in a compiler-specific - // order making tests fail for certain configurations. - Id instructionId = emitter.AllocateUniqueId(); - is_first_iteration = - new Instruction(instructionId, emitter.MakeBoolType(), Op::OpPhi); - } -} - -// Create a branch to the header of the given loop, from inside -// the loop body. -// Adjusts the phi node for the first-iteration value if needeed. -void SpirvEmitter::CreateBranchToLoopHeaderFromInside(const Loop& loop) { - CreateBranch(loop.header); - if (loop.is_first_iteration) { - loop.is_first_iteration->AddIdOperand(MakeBoolConstant(false)); - loop.is_first_iteration->AddIdOperand(build_point()->id()); - } -} - -void SpirvEmitter::CheckNotImplemented(const char* message) { - xe::FatalError("Missing functionality: %s", message); -} - -} // namespace spirv -} // namespace ui -} // namespace xe diff --git a/src/xenia/ui/spirv/spirv_emitter.h b/src/xenia/ui/spirv/spirv_emitter.h deleted file mode 100644 index ccd0fcdbd..000000000 --- a/src/xenia/ui/spirv/spirv_emitter.h +++ /dev/null @@ -1,731 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2015 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -// Contents originally forked from: -// https://github.com/KhronosGroup/glslang/ -// -// Copyright (C) 2014 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 XENIA_UI_SPIRV_SPIRV_EMITTER_H_ -#define XENIA_UI_SPIRV_SPIRV_EMITTER_H_ - -#include -#include -#include -#include - -#include "xenia/base/assert.h" -#include "xenia/ui/spirv/spirv_ir.h" -#include "xenia/ui/spirv/spirv_util.h" - -namespace xe { -namespace ui { -namespace spirv { - -class SpirvEmitter { - public: - SpirvEmitter(); - ~SpirvEmitter(); - - // Document what source language and text this module was translated from. - void SetSourceLanguage(spv::SourceLanguage language, int version) { - source_language_ = language; - source_version_ = version; - } - - // Document an extension to the source language. Informational only. - void AddSourceExtension(const char* ext) { - source_extensions_.push_back(ext); - } - - // Set addressing model and memory model for the entire module. - void SetMemoryModel(spv::AddressingModel addressing_model, - spv::MemoryModel memory_model) { - addressing_model_ = addressing_model; - memory_model_ = memory_model; - } - - // Declare a capability used by this module. - void DeclareCapability(spv::Capability cap) { capabilities_.push_back(cap); } - - // Import an extended set of instructions that can be later referenced by the - // returned id. - Id ImportExtendedInstructions(const char* name); - - // For creating new types (will return old type if the requested one was - // already made). - Id MakeVoidType(); - Id MakeBoolType(); - Id MakePointer(spv::StorageClass storage_class, Id pointee); - Id MakeIntegerType(int bit_width, bool is_signed); - Id MakeIntType(int bit_width) { return MakeIntegerType(bit_width, true); } - Id MakeUintType(int bit_width) { return MakeIntegerType(bit_width, false); } - Id MakeFloatType(int bit_width); - Id MakeStructType(std::initializer_list members, const char* name); - Id MakePairStructType(Id type0, Id type1); - Id MakeVectorType(Id component_type, int component_count); - Id MakeMatrix2DType(Id component_type, int cols, int rows); - Id MakeArrayType(Id element_type, int length); - Id MakeRuntimeArray(Id element_type); - Id MakeFunctionType(Id return_type, std::initializer_list param_types); - Id MakeImageType(Id sampled_type, spv::Dim dim, bool has_depth, - bool is_arrayed, bool is_multisampled, int sampled, - spv::ImageFormat format); - Id MakeSamplerType(); - Id MakeSampledImageType(Id image_type); - - // For querying about types. - Id GetTypeId(Id result_id) const { return module_.type_id(result_id); } - Id GetDerefTypeId(Id result_id) const; - Op GetOpcode(Id id) const { return module_.instruction(id)->opcode(); } - Op GetTypeClass(Id type_id) const { return GetOpcode(type_id); } - Op GetMostBasicTypeClass(Id type_id) const; - int GetComponentCount(Id result_id) const { - return GetTypeComponentCount(GetTypeId(result_id)); - } - int GetTypeComponentCount(Id type_id) const; - Id GetScalarTypeId(Id type_id) const; - Id GetContainedTypeId(Id type_id) const; - Id GetContainedTypeId(Id type_id, int member) const; - spv::StorageClass GetTypeStorageClass(Id type_id) const { - return module_.storage_class(type_id); - } - - bool IsPointer(Id result_id) const { - return IsPointerType(GetTypeId(result_id)); - } - bool IsScalar(Id result_id) const { - return IsScalarType(GetTypeId(result_id)); - } - bool IsVector(Id result_id) const { - return IsVectorType(GetTypeId(result_id)); - } - bool IsMatrix(Id result_id) const { - return IsMatrixType(GetTypeId(result_id)); - } - bool IsAggregate(Id result_id) const { - return IsAggregateType(GetTypeId(result_id)); - } - bool IsBoolType(Id type_id) const { - return grouped_types_[static_cast(spv::Op::OpTypeBool)].size() > 0 && - type_id == - grouped_types_[static_cast(spv::Op::OpTypeBool)] - .back() - ->result_id(); - } - - bool IsPointerType(Id type_id) const { - return GetTypeClass(type_id) == spv::Op::OpTypePointer; - } - bool IsScalarType(Id type_id) const { - return GetTypeClass(type_id) == spv::Op::OpTypeFloat || - GetTypeClass(type_id) == spv::Op::OpTypeInt || - GetTypeClass(type_id) == spv::Op::OpTypeBool; - } - bool IsVectorType(Id type_id) const { - return GetTypeClass(type_id) == spv::Op::OpTypeVector; - } - bool IsMatrixType(Id type_id) const { - return GetTypeClass(type_id) == spv::Op::OpTypeMatrix; - } - bool IsStructType(Id type_id) const { - return GetTypeClass(type_id) == spv::Op::OpTypeStruct; - } - bool IsArrayType(Id type_id) const { - return GetTypeClass(type_id) == spv::Op::OpTypeArray; - } - bool IsAggregateType(Id type_id) const { - return IsArrayType(type_id) || IsStructType(type_id); - } - bool IsImageType(Id type_id) const { - return GetTypeClass(type_id) == spv::Op::OpTypeImage; - } - bool IsSamplerType(Id type_id) const { - return GetTypeClass(type_id) == spv::Op::OpTypeSampler; - } - bool IsSampledImageType(Id type_id) const { - return GetTypeClass(type_id) == spv::Op::OpTypeSampledImage; - } - - bool IsConstantOpCode(Op opcode) const; - bool IsConstant(Id result_id) const { - return IsConstantOpCode(GetOpcode(result_id)); - } - bool IsConstantScalar(Id result_id) const { - return GetOpcode(result_id) == spv::Op::OpConstant; - } - uint32_t GetConstantScalar(Id result_id) const { - return module_.instruction(result_id)->immediate_operand(0); - } - spv::StorageClass GetStorageClass(Id result_id) const { - return GetTypeStorageClass(GetTypeId(result_id)); - } - - int GetTypeColumnCount(Id type_id) const { - assert(IsMatrixType(type_id)); - return GetTypeComponentCount(type_id); - } - int GetColumnCount(Id result_id) const { - return GetTypeColumnCount(GetTypeId(result_id)); - } - int GetTypeRowCount(Id type_id) const { - assert(IsMatrixType(type_id)); - return GetTypeComponentCount(GetContainedTypeId(type_id)); - } - int GetRowCount(Id result_id) const { - return GetTypeRowCount(GetTypeId(result_id)); - } - - spv::Dim GetTypeDimensionality(Id type_id) const { - assert(IsImageType(type_id)); - return static_cast( - module_.instruction(type_id)->immediate_operand(1)); - } - Id GetImageType(Id result_id) const { - Id type_id = GetTypeId(result_id); - assert(IsImageType(type_id) || IsSampledImageType(type_id)); - return IsSampledImageType(type_id) - ? module_.instruction(type_id)->id_operand(0) - : type_id; - } - bool IsArrayedImageType(Id type_id) const { - assert(IsImageType(type_id)); - return module_.instruction(type_id)->immediate_operand(3) != 0; - } - - // For making new constants (will return old constant if the requested one was - // already made). - Id MakeBoolConstant(bool value, bool is_spec_constant = false); - Id MakeIntConstant(int value, bool is_spec_constant = false) { - return MakeIntegerConstant(MakeIntType(32), static_cast(value), - is_spec_constant); - } - Id MakeUintConstant(uint32_t value, bool is_spec_constant = false) { - return MakeIntegerConstant(MakeUintType(32), value, is_spec_constant); - } - template - Id MakeUintConstant(T value, bool is_spec_constant = false) { - static_assert(sizeof(T) == sizeof(uint32_t), "Invalid type"); - return MakeIntegerConstant(MakeUintType(32), static_cast(value), - is_spec_constant); - } - Id MakeFloatConstant(float value, bool is_spec_constant = false); - Id MakeDoubleConstant(double value, bool is_spec_constant = false); - - // Turns the array of constants into a proper constant of the requested type. - Id MakeCompositeConstant(Id type, std::initializer_list components); - - // Declares an entry point and its execution model. - Instruction* AddEntryPoint(spv::ExecutionModel execution_model, - Function* entry_point, const char* name); - void AddExecutionMode(Function* entry_point, - spv::ExecutionMode execution_mode, int value1 = -1, - int value2 = -1, int value3 = -1); - void AddName(Id target_id, const char* name); - void AddMemberName(Id target_id, int member, const char* name); - void AddLine(Id target_id, Id file_name, int line_number, int column_number); - void AddDecoration(Id target_id, spv::Decoration decoration, int num = -1); - void AddMemberDecoration(Id target_id, int member, spv::Decoration, - int num = -1); - - // At the end of what block do the next create*() instructions go? - Block* build_point() const { return build_point_; } - void set_build_point(Block* build_point) { build_point_ = build_point; } - - // Makes the main function. - Function* MakeMainEntry(); - - // Makes a shader-style function, and create its entry block if entry is - // non-zero. - // Return the function, pass back the entry. - Function* MakeFunctionEntry(Id return_type, const char* name, - std::initializer_list param_types, - Block** entry = 0); - - // Creates a return statement. - // An 'implicit' return is one not appearing in the source code. In the case - // of an implicit return, no post-return block is inserted. - void MakeReturn(bool implicit, Id return_value = 0); - - // Generates all the code needed to finish up a function. - void LeaveFunction(); - - // Creates a fragment-shader discard (kill). - void MakeDiscard(); - - // Creates a global or function local or IO variable. - Id CreateVariable(spv::StorageClass storage_class, Id type, - const char* name = 0); - - // Creates an intermediate object whose value is undefined. - Id CreateUndefined(Id type); - - // Stores the given value into the specified pointer. - void CreateStore(Id pointer_id, Id value_id); - - // Loads the value from the given pointer. - Id CreateLoad(Id pointer_id); - - // Creates a pointer into a composite object that can be used with OpLoad and - // OpStore. - Id CreateAccessChain(spv::StorageClass storage_class, Id base_id, - std::vector index_ids); - - // Queries the length of a run-time array. - Id CreateArrayLength(Id struct_id, int array_member); - - Id CreateCompositeExtract(Id composite, Id type_id, uint32_t index); - Id CreateCompositeExtract(Id composite, Id type_id, - std::vector indexes); - Id CreateCompositeInsert(Id object, Id composite, Id type_id, uint32_t index); - Id CreateCompositeInsert(Id object, Id composite, Id type_id, - std::vector indexes); - - Id CreateVectorExtractDynamic(Id vector, Id type_id, Id component_index); - Id CreateVectorInsertDynamic(Id vector, Id type_id, Id component, - Id component_index); - - // Does nothing. - void CreateNop(); - - // Waits for other invocations of this module to reach the current point of - // execution. - void CreateControlBarrier(spv::Scope execution_scope, spv::Scope memory_scope, - spv::MemorySemanticsMask memory_semantics); - // Controls the order that memory accesses are observed. - void CreateMemoryBarrier(spv::Scope execution_scope, - spv::MemorySemanticsMask memory_semantics); - - Id CreateUnaryOp(Op opcode, Id type_id, Id operand); - Id CreateBinOp(Op opcode, Id type_id, Id operand1, Id operand2); - Id CreateTriOp(Op opcode, Id type_id, Id operand1, Id operand2, Id operand3); - Id CreateOp(Op opcode, Id type_id, const std::vector& operands); - Id CreateFunctionCall(Function* function, std::vector args); - - // Takes an rvalue (source) and a set of channels to extract from it to - // make a new rvalue. - Id CreateSwizzle(Id type_id, Id source, std::vector channels); - - // Takes a copy of an lvalue (target) and a source of components, and sets the - // source components into the lvalue where the 'channels' say to put them. - Id CreateLvalueSwizzle(Id type_id, Id target, Id source, - std::vector channels); - - // If the value passed in is an instruction and the precision is not EMpNone, - // it gets tagged with the requested precision. - void SetPrecision(Id value, spv::Decoration precision) { - CheckNotImplemented("setPrecision"); - } - - // Smears a scalar to a vector for the following forms: - // - PromoteScalar(scalar, vector) // smear scalar to width of vector - // - PromoteScalar(vector, scalar) // smear scalar to width of vector - // - PromoteScalar(pointer, scalar) // smear scalar to width of what pointer - // points to - // - PromoteScalar(scalar, scalar) // do nothing - // Other forms are not allowed. - // - // Note: One of the arguments will change, with the result coming back that - // way rather than through the return value. - void PromoteScalar(spv::Decoration precision, Id& left, Id& right); - - // Makes a value by smearing the scalar to fill the type. - Id SmearScalar(spv::Decoration precision, Id scalar_value, Id vector_type_id); - - // Executes an instruction in an imported set of extended instructions. - Id CreateExtendedInstructionCall(spv::Decoration precision, Id result_type, - Id instruction_set, int instruction_ordinal, - std::initializer_list args); - // Executes an instruction from the extended GLSL set. - Id CreateGlslStd450InstructionCall(spv::Decoration precision, Id result_type, - spv::GLSLstd450 instruction_ordinal, - std::initializer_list args); - - // List of parameters used to create a texture operation - struct TextureParameters { - Id sampler; - Id coords; - Id bias; - Id lod; - Id depth_ref; - Id offset; - Id offsets; - Id grad_x; - Id grad_y; - Id sample; - Id comp; - }; - - // Selects the correct texture operation based on all inputs, and emit the - // correct instruction. - Id CreateTextureCall(spv::Decoration precision, Id result_type, bool fetch, - bool proj, bool gather, - const TextureParameters& parameters); - - // Emits the OpTextureQuery* instruction that was passed in and figures out - // the right return value and type. - Id CreateTextureQueryCall(Op opcode, const TextureParameters& parameters); - - Id CreateSamplePositionCall(spv::Decoration precision, Id, Id); - Id CreateBitFieldExtractCall(spv::Decoration precision, Id, Id, Id, - bool isSigned); - Id CreateBitFieldInsertCall(spv::Decoration precision, Id, Id, Id, Id); - - // Reduction comparision for composites: For equal and not-equal resulting in - // a scalar. - Id CreateCompare(spv::Decoration precision, Id value1, Id value2, - bool is_equal); - - // OpCompositeConstruct - Id CreateCompositeConstruct(Id type_id, std::vector constituent_ids); - - // vector or scalar constructor - Id CreateConstructor(spv::Decoration precision, std::vector source_ids, - Id result_type_id); - - // matrix constructor - Id CreateMatrixConstructor(spv::Decoration precision, std::vector sources, - Id constructee); - - // Helper to use for building nested control flow with if-then-else. - class If { - public: - If(SpirvEmitter& emitter, Id condition); - ~If() = default; - - void MakeBeginElse(); - void MakeEndIf(); - - private: - If(const If&) = delete; - If& operator=(If&) = delete; - - SpirvEmitter& emitter_; - Id condition_; - Function* function_ = nullptr; - Block* header_block_ = nullptr; - Block* then_block_ = nullptr; - Block* else_block_ = nullptr; - Block* merge_block_ = nullptr; - }; - - // Makes a switch statement. - // A switch has 'numSegments' of pieces of code, not containing any - // case/default labels, all separated by one or more case/default labels. - // Each possible case value v is a jump to the caseValues[v] segment. The - // defaultSegment is also in this number space. How to compute the value is - // given by 'condition', as in switch(condition). - // - // The SPIR-V Builder will maintain the stack of post-switch merge blocks for - // nested switches. - // - // Use a defaultSegment < 0 if there is no default segment (to branch to post - // switch). - // - // 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 segment_count, std::vector case_values, - std::vector value_index_to_segment, int default_segment, - std::vector& segment_blocks); - - // Adds a branch to the innermost switch's merge block. - void AddSwitchBreak(); - - // Move sto the next code segment, passing in the return argument in - // MakeSwitch(). - void NextSwitchSegment(std::vector& segment_block, int next_segment); - - // Finishes off the innermost switch. - void EndSwitch(std::vector& segment_block); - - // Starts the beginning of a new loop, and prepare the builder to - // generate code for the loop test. - // The test_first parameter is true when the loop test executes before - // the body (it is false for do-while loops). - void MakeNewLoop(bool test_first); - - // Adds the branch for the loop test, based on the given condition. - // The true branch goes to the first block in the loop body, and - // the false branch goes to the loop's merge block. The builder insertion - // point will be placed at the start of the body. - void CreateLoopTestBranch(Id condition); - - // Generates an unconditional branch to the loop body. - // The builder insertion point will be placed at the start of the body. - // Use this when there is no loop test. - void CreateBranchToBody(); - - // Adds a branch to the test of the current (innermost) loop. - // The way we generate code, that's also the loop header. - void CreateLoopContinue(); - - // Adds an exit (e.g. "break") for the innermost loop that you're in. - void CreateLoopExit(); - - // Close the innermost loop that you're in. - void CloseLoop(); - - // Access chain design for an R-Value vs. L-Value: - // - // There is a single access chain the builder is building at - // any particular time. Such a chain can be used to either to a load or - // a store, when desired. - // - // Expressions can be r-values, l-values, or both, or only r-values: - // a[b.c].d = .... // l-value - // ... = a[b.c].d; // r-value, that also looks like an l-value - // ++a[b.c].d; // r-value and l-value - // (x + y)[2]; // r-value only, can't possibly be l-value - // - // Computing an r-value means generating code. Hence, - // r-values should only be computed when they are needed, not speculatively. - // - // Computing an l-value means saving away information for later use in the - // compiler, - // no code is generated until the l-value is later dereferenced. It is okay - // to speculatively generate an l-value, just not okay to speculatively - // dereference it. - // - // The base of the access chain (the left-most variable or expression - // from which everything is based) can be set either as an l-value - // or as an r-value. Most efficient would be to set an l-value if one - // is available. If an expression was evaluated, the resulting r-value - // can be set as the chain base. - // - // The users of this single access chain can save and restore if they - // want to nest or manage multiple chains. - // - struct AccessChain { - Id base; // for l-values, pointer to the base object, for r-values, the - // base object - std::vector index_chain; - Id instr; // cache the instruction that generates this access chain - std::vector swizzle; // each std::vector element selects the next - // GLSL component number - Id component; // a dynamic component index, can coexist with a swizzle, - // done after the swizzle, NoResult if not present - Id pre_swizzle_base_type; // dereferenced type, before swizzle or component - // is - // applied; NoType unless a swizzle or component is - // present - bool is_rvalue; // true if 'base' is an r-value, otherwise, base is an - // l-value - }; - - // - // the SPIR-V builder maintains a single active chain that - // the following methods operated on - // - - // for external save and restore - AccessChain access_chain() { return access_chain_; } - void set_access_chain(AccessChain new_chain) { access_chain_ = new_chain; } - - void ClearAccessChain(); - - // set new base as an l-value base - void set_access_chain_lvalue(Id lvalue) { - assert(IsPointer(lvalue)); - access_chain_.base = lvalue; - } - - // set new base value as an r-value - void set_access_chain_rvalue(Id rvalue) { - access_chain_.is_rvalue = true; - access_chain_.base = rvalue; - } - - // push offset onto the end of the chain - void PushAccessChainOffset(Id offset) { - access_chain_.index_chain.push_back(offset); - } - - // push new swizzle onto the end of any existing swizzle, merging into a - // single swizzle - void PushAccessChainSwizzle(std::vector swizzle, - Id pre_swizzle_base_type); - - // push a variable component selection onto the access chain; supporting only - // one, so unsided - void PushAccessChainComponent(Id component, Id pre_swizzle_base_type) { - access_chain_.component = component; - if (access_chain_.pre_swizzle_base_type == NoType) { - access_chain_.pre_swizzle_base_type = pre_swizzle_base_type; - } - } - - // use accessChain and swizzle to store value - void CreateAccessChainStore(Id rvalue); - - // use accessChain and swizzle to load an r-value - Id CreateAccessChainLoad(Id result_type_id); - - // get the direct pointer for an l-value - Id CreateAccessChainLValue(); - - void Serialize(std::vector& out) const; - - private: - // Maximum dimension for column/row in a matrix. - static const int kMaxMatrixSize = 4; - - // Allocates a new . - Id AllocateUniqueId() { return ++unique_id_; } - - // Allocates a contiguous sequence of s. - Id AllocateUniqueIds(int count) { - Id id = unique_id_ + 1; - unique_id_ += count; - return id; - } - - Id MakeIntegerConstant(Id type_id, uint32_t value, bool is_spec_constant); - Id FindScalarConstant(Op type_class, Op opcode, Id type_id, - uint32_t value) const; - Id FindScalarConstant(Op type_class, Op opcode, Id type_id, uint32_t v1, - uint32_t v2) const; - Id FindCompositeConstant(Op type_class, - std::initializer_list components) const; - - Id CollapseAccessChain(); - void SimplifyAccessChainSwizzle(); - void TransferAccessChainSwizzle(bool dynamic); - - void SerializeInstructions( - std::vector& out, - const std::vector& instructions) const; - - void CreateAndSetNoPredecessorBlock(const char* name); - void CreateBranch(Block* block); - void CreateSelectionMerge(Block* merge_block, - spv::SelectionControlMask control); - void CreateLoopMerge(Block* merge_block, Block* continueBlock, - spv::LoopControlMask control); - void CreateConditionalBranch(Id condition, Block* then_block, - Block* else_block); - - struct Loop; // Defined below. - void CreateBranchToLoopHeaderFromInside(const Loop& loop); - - // Asserts on unimplemented functionality. - void CheckNotImplemented(const char* message); - - spv::SourceLanguage source_language_ = spv::SourceLanguage::Unknown; - int source_version_ = 0; - std::vector source_extensions_; - spv::AddressingModel addressing_model_ = spv::AddressingModel::Logical; - spv::MemoryModel memory_model_ = spv::MemoryModel::GLSL450; - std::vector capabilities_; - int builder_number_ = 0; - Module module_; - Block* build_point_ = nullptr; - Id unique_id_ = 0; - Function* main_function_ = nullptr; - AccessChain access_chain_; - Id glsl_std_450_instruction_set_ = 0; - - // special blocks of instructions for output - std::vector imports_; - std::vector entry_points_; - std::vector execution_modes_; - std::vector names_; - std::vector lines_; - std::vector decorations_; - std::vector constants_types_globals_; - std::vector externals_; - - // not output, internally used for quick & dirty canonical (unique) creation - // All types appear before OpConstant. - std::vector - grouped_constants_[static_cast(spv::Op::OpConstant)]; - std::vector - grouped_types_[static_cast(spv::Op::OpConstant)]; - - // Stack of switches. - std::stack switch_merges_; - - // Data that needs to be kept in order to properly handle loops. - struct Loop { - // Constructs a default Loop structure containing new header, merge, and - // body blocks for the current function. - // The test_first argument indicates whether the loop test executes at - // the top of the loop rather than at the bottom. In the latter case, - // also create a phi instruction whose value indicates whether we're on - // the first iteration of the loop. The phi instruction is initialized - // with no values or predecessor operands. - Loop(SpirvEmitter& emitter, bool test_first); - - // The function containing the loop. - Function* const function; - // The header is the first block generated for the loop. - // It dominates all the blocks in the loop, i.e. it is always - // executed before any others. - // If the loop test is executed before the body (as in "while" and - // "for" loops), then the header begins with the test code. - // Otherwise, the loop is a "do-while" loop and the header contains the - // start of the body of the loop (if the body exists). - Block* const header; - // The merge block marks the end of the loop. Control is transferred - // to the merge block when either the loop test fails, or when a - // nested "break" is encountered. - Block* const merge; - // The body block is the first basic block in the body of the loop, i.e. - // the code that is to be repeatedly executed, aside from loop control. - // This member is null until we generate code that references the loop - // body block. - Block* const body; - // True when the loop test executes before the body. - const bool test_first; - // When the test executes after the body, this is defined as the phi - // instruction that tells us whether we are on the first iteration of - // the loop. Otherwise this is null. This is non-const because - // it has to be initialized outside of the initializer-list. - Instruction* is_first_iteration; - }; - - // Our loop stack. - std::stack loops_; -}; - -} // namespace spirv -} // namespace ui -} // namespace xe - -#endif // XENIA_UI_SPIRV_SPIRV_EMITTER_H_ diff --git a/src/xenia/ui/spirv/spirv_ir.h b/src/xenia/ui/spirv/spirv_ir.h deleted file mode 100644 index e75459844..000000000 --- a/src/xenia/ui/spirv/spirv_ir.h +++ /dev/null @@ -1,421 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2015 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -// Contents originally forked from: -// https://github.com/KhronosGroup/glslang/ -// -// Copyright (C) 2014 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. - -// 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 -// - Instruction -// - -#ifndef XENIA_UI_SPIRV_SPIRV_IR_H_ -#define XENIA_UI_SPIRV_SPIRV_IR_H_ - -#include -#include - -#include "xenia/ui/spirv/spirv_util.h" - -namespace xe { -namespace ui { -namespace spirv { - -using spv::Id; -using spv::Op; - -class Function; -class Module; - -const Id NoResult = 0; -const Id NoType = 0; - -const uint32_t BadValue = 0xFFFFFFFF; -const spv::Decoration NoPrecision = static_cast(BadValue); -const spv::MemorySemanticsMask MemorySemanticsAllMemory = - static_cast(0x3FF); - -class Instruction { - public: - Instruction(Id result_id, Id type_id, Op opcode) - : result_id_(result_id), type_id_(type_id), opcode_(opcode) {} - explicit Instruction(Op opcode) : opcode_(opcode) {} - ~Instruction() = default; - - void AddIdOperand(Id id) { operands_.push_back(id); } - - void AddIdOperands(const std::vector& ids) { - for (auto id : ids) { - operands_.push_back(id); - } - } - void AddIdOperands(std::initializer_list ids) { - for (auto id : ids) { - operands_.push_back(id); - } - } - - void AddImmediateOperand(uint32_t immediate) { - operands_.push_back(immediate); - } - - template - void AddImmediateOperand(T immediate) { - static_assert(sizeof(T) == sizeof(uint32_t), "Invalid operand size"); - operands_.push_back(static_cast(immediate)); - } - - void AddImmediateOperands(const std::vector& immediates) { - for (auto immediate : immediates) { - operands_.push_back(immediate); - } - } - - void AddImmediateOperands(std::initializer_list immediates) { - for (auto immediate : immediates) { - operands_.push_back(immediate); - } - } - - void AddStringOperand(const char* str) { - original_string_ = str; - uint32_t word; - char* word_string = reinterpret_cast(&word); - char* word_ptr = word_string; - int char_count = 0; - char c; - do { - c = *(str++); - *(word_ptr++) = c; - ++char_count; - if (char_count == 4) { - AddImmediateOperand(word); - word_ptr = word_string; - char_count = 0; - } - } while (c != 0); - - // deal with partial last word - if (char_count > 0) { - // pad with 0s - for (; char_count < 4; ++char_count) { - *(word_ptr++) = 0; - } - AddImmediateOperand(word); - } - } - - Op opcode() const { return opcode_; } - int operand_count() const { return static_cast(operands_.size()); } - Id result_id() const { return result_id_; } - Id type_id() const { return type_id_; } - Id id_operand(int op) const { return operands_[op]; } - uint32_t immediate_operand(int op) const { return operands_[op]; } - const char* string_operand() const { return original_string_.c_str(); } - - // Write out the binary form. - void Serialize(std::vector& out) const { - uint32_t word_count = 1; - if (type_id_) { - ++word_count; - } - if (result_id_) { - ++word_count; - } - word_count += static_cast(operands_.size()); - - out.push_back((word_count << spv::WordCountShift) | - static_cast(opcode_)); - if (type_id_) { - out.push_back(type_id_); - } - if (result_id_) { - out.push_back(result_id_); - } - for (auto operand : operands_) { - out.push_back(operand); - } - } - - private: - Instruction(const Instruction&) = delete; - - Id result_id_ = NoResult; - Id type_id_ = NoType; - Op opcode_; - std::vector operands_; - std::string original_string_; // could be optimized away; convenience for - // getting string operand -}; - -class Block { - public: - Block(Id id, Function& parent); - ~Block() { - for (size_t i = 0; i < instructions_.size(); ++i) { - delete instructions_[i]; - } - for (size_t i = 0; i < local_variables_.size(); ++i) { - delete local_variables_[i]; - } - } - - Id id() { return instructions_.front()->result_id(); } - - Function& parent() const { return parent_; } - - void AddInstruction(Instruction* instr); - void AddLocalVariable(Instruction* instr) { - local_variables_.push_back(instr); - } - - void AddPredecessor(Block* predecessor) { - predecessors_.push_back(predecessor); - } - - int predecessor_count() const { - return static_cast(predecessors_.size()); - } - - bool is_unreachable() const { return unreachable_; } - void set_unreachable(bool value) { unreachable_ = value; } - - bool is_terminated() const { - switch (instructions_.back()->opcode()) { - case spv::Op::OpBranch: - case spv::Op::OpBranchConditional: - case spv::Op::OpSwitch: - case spv::Op::OpKill: - case spv::Op::OpReturn: - case spv::Op::OpReturnValue: - return true; - default: - return false; - } - } - - void Serialize(std::vector& out) const { - // skip the degenerate unreachable blocks - // TODO: code gen: skip all unreachable blocks (transitive closure) - // (but, until that's done safer to keep non-degenerate - // unreachable blocks, in case others depend on something) - if (unreachable_ && instructions_.size() <= 2) { - return; - } - - instructions_[0]->Serialize(out); - for (auto variable : local_variables_) { - variable->Serialize(out); - } - for (int i = 1; i < instructions_.size(); ++i) { - instructions_[i]->Serialize(out); - } - } - - private: - Block(const Block&) = delete; - Block& operator=(Block&) = delete; - - // To enforce keeping parent and ownership in sync: - friend Function; - - std::vector instructions_; - std::vector predecessors_; - std::vector local_variables_; - Function& parent_; - - // 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_; -}; - -class Function { - public: - Function(Id id, Id resultType, Id functionType, Id firstParam, - Module& parent); - ~Function() { - for (size_t i = 0; i < parameter_instructions_.size(); ++i) { - delete parameter_instructions_[i]; - } - for (size_t i = 0; i < blocks_.size(); ++i) { - delete blocks_[i]; - } - } - - Id id() const { return function_instruction_.result_id(); } - Id param_id(int p) { return parameter_instructions_[p]->result_id(); } - - void push_block(Block* block) { blocks_.push_back(block); } - void pop_block(Block* block) { blocks_.pop_back(); } - - Module& parent() const { return parent_; } - Block* entry_block() const { return blocks_.front(); } - Block* last_block() const { return blocks_.back(); } - - void AddLocalVariable(Instruction* instr); - - Id return_type() const { return function_instruction_.type_id(); } - - void Serialize(std::vector& out) const { - // OpFunction - function_instruction_.Serialize(out); - - // OpFunctionParameter - for (auto instruction : parameter_instructions_) { - instruction->Serialize(out); - } - - // Blocks - for (auto block : blocks_) { - block->Serialize(out); - } - - Instruction end(0, 0, spv::Op::OpFunctionEnd); - end.Serialize(out); - } - - private: - Function(const Function&) = delete; - Function& operator=(Function&) = delete; - - Module& parent_; - Instruction function_instruction_; - std::vector parameter_instructions_; - std::vector blocks_; -}; - -class Module { - public: - Module() = default; - ~Module() { - for (size_t i = 0; i < functions_.size(); ++i) { - delete functions_[i]; - } - } - - void AddFunction(Function* function) { functions_.push_back(function); } - - void MapInstruction(Instruction* instr) { - spv::Id result_id = instr->result_id(); - // Map the instruction's result id. - if (result_id >= id_to_instruction_.size()) { - id_to_instruction_.resize(result_id + 16); - } - id_to_instruction_[result_id] = instr; - } - - Instruction* instruction(Id id) const { return id_to_instruction_[id]; } - - spv::Id type_id(Id result_id) const { - return id_to_instruction_[result_id]->type_id(); - } - - spv::StorageClass storage_class(Id type_id) const { - return (spv::StorageClass)id_to_instruction_[type_id]->immediate_operand(0); - } - - void Serialize(std::vector& out) const { - for (auto function : functions_) { - function->Serialize(out); - } - } - - private: - Module(const Module&) = delete; - - std::vector functions_; - - // Maps from result id to instruction having that result id. - std::vector id_to_instruction_; -}; - -inline Function::Function(Id id, Id result_type_id, Id function_type_id, - Id first_param_id, Module& parent) - : parent_(parent), - function_instruction_(id, result_type_id, spv::Op::OpFunction) { - // OpFunction - function_instruction_.AddImmediateOperand( - static_cast(spv::FunctionControlMask::MaskNone)); - function_instruction_.AddIdOperand(function_type_id); - parent.MapInstruction(&function_instruction_); - parent.AddFunction(this); - - // OpFunctionParameter - Instruction* type_instr = parent.instruction(function_type_id); - int param_count = type_instr->operand_count() - 1; - for (int p = 0; p < param_count; ++p) { - auto param = - new Instruction(first_param_id + p, type_instr->id_operand(p + 1), - spv::Op::OpFunctionParameter); - parent.MapInstruction(param); - parameter_instructions_.push_back(param); - } -} - -inline void Function::AddLocalVariable(Instruction* instr) { - blocks_[0]->AddLocalVariable(instr); - parent_.MapInstruction(instr); -} - -inline Block::Block(Id id, Function& parent) - : parent_(parent), unreachable_(false) { - instructions_.push_back(new Instruction(id, NoType, spv::Op::OpLabel)); -} - -inline void Block::AddInstruction(Instruction* inst) { - instructions_.push_back(inst); - if (inst->result_id()) { - parent_.parent().MapInstruction(inst); - } -} - -} // namespace spirv -} // namespace ui -} // namespace xe - -#endif // XENIA_UI_SPIRV_SPIRV_IR_H_ diff --git a/src/xenia/ui/spirv/spirv_optimizer.cc b/src/xenia/ui/spirv/spirv_optimizer.cc deleted file mode 100644 index f21026f14..000000000 --- a/src/xenia/ui/spirv/spirv_optimizer.cc +++ /dev/null @@ -1,22 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2015 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#include "xenia/ui/spirv/spirv_optimizer.h" - -namespace xe { -namespace ui { -namespace spirv { - -SpirvOptimizer::SpirvOptimizer() = default; - -SpirvOptimizer::~SpirvOptimizer() = default; - -} // namespace spirv -} // namespace ui -} // namespace xe diff --git a/src/xenia/ui/spirv/spirv_optimizer.h b/src/xenia/ui/spirv/spirv_optimizer.h deleted file mode 100644 index b838feebc..000000000 --- a/src/xenia/ui/spirv/spirv_optimizer.h +++ /dev/null @@ -1,31 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2015 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#ifndef XENIA_UI_SPIRV_SPIRV_OPTIMIZER_H_ -#define XENIA_UI_SPIRV_SPIRV_OPTIMIZER_H_ - -#include "xenia/ui/spirv/spirv_util.h" - -namespace xe { -namespace ui { -namespace spirv { - -class SpirvOptimizer { - public: - SpirvOptimizer(); - ~SpirvOptimizer(); - - private: -}; - -} // namespace spirv -} // namespace ui -} // namespace xe - -#endif // XENIA_UI_SPIRV_SPIRV_OPTIMIZER_H_