Switching from fork to main glslang spirv builder.

This commit is contained in:
Ben Vanik 2016-02-18 16:42:00 -08:00
parent 00594da417
commit 35e08d9428
13 changed files with 140 additions and 3485 deletions

View File

@ -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",

View File

@ -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_;
}

View File

@ -71,6 +71,13 @@ class ShaderTranslator {
return std::vector<uint8_t>();
}
// 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.

View File

@ -11,124 +11,182 @@
#include <cstring>
#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<spv::Builder>(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<uint8_t> SpirvShaderTranslator::CompleteTranslation() {
auto& e = emitter_;
auto& b = *builder_;
b.makeReturn(false);
std::vector<uint32_t> spirv_words;
e.Serialize(spirv_words);
b.dump(spirv_words);
// Cleanup builder.
builder_.reset();
// Copy bytes out.
// TODO(benvanik): avoid copy?
std::vector<uint8_t> 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<const uint32_t*>(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<Id> args) {
return builder_->createBuiltinCall(result_type, glsl_std_450_instruction_set_,
static_cast<int>(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");
}

View File

@ -14,8 +14,10 @@
#include <string>
#include <vector>
#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<uint8_t> 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<spv::Id> 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<spv::Builder> builder_;
spv::Id glsl_std_450_instruction_set_ = 0;
};
} // namespace gpu

View File

@ -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

View File

@ -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

View File

@ -7,6 +7,7 @@ project("xenia-ui-spirv")
kind("StaticLib")
language("C++")
links({
"glslang-spirv",
"spirv-tools",
"xenia-base",
})

File diff suppressed because it is too large Load Diff

View File

@ -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 <algorithm>
#include <map>
#include <stack>
#include <vector>
#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<Id> 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<Id> 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<int>(spv::Op::OpTypeBool)].size() > 0 &&
type_id ==
grouped_types_[static_cast<int>(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<spv::Dim>(
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<uint32_t>(value),
is_spec_constant);
}
Id MakeUintConstant(uint32_t value, bool is_spec_constant = false) {
return MakeIntegerConstant(MakeUintType(32), value, is_spec_constant);
}
template <typename T>
Id MakeUintConstant(T value, bool is_spec_constant = false) {
static_assert(sizeof(T) == sizeof(uint32_t), "Invalid type");
return MakeIntegerConstant(MakeUintType(32), static_cast<uint32_t>(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<Id> 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<Id> 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<Id> 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<uint32_t> indexes);
Id CreateCompositeInsert(Id object, Id composite, Id type_id, uint32_t index);
Id CreateCompositeInsert(Id object, Id composite, Id type_id,
std::vector<uint32_t> 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<Id>& operands);
Id CreateFunctionCall(Function* function, std::vector<spv::Id> 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<uint32_t> 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<uint32_t> 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<Id> args);
// Executes an instruction from the extended GLSL set.
Id CreateGlslStd450InstructionCall(spv::Decoration precision, Id result_type,
spv::GLSLstd450 instruction_ordinal,
std::initializer_list<Id> 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<Id> constituent_ids);
// vector or scalar constructor
Id CreateConstructor(spv::Decoration precision, std::vector<Id> source_ids,
Id result_type_id);
// matrix constructor
Id CreateMatrixConstructor(spv::Decoration precision, std::vector<Id> 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<int> case_values,
std::vector<int> value_index_to_segment, int default_segment,
std::vector<Block*>& 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<Block*>& segment_block, int next_segment);
// Finishes off the innermost switch.
void EndSwitch(std::vector<Block*>& 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<Id> index_chain;
Id instr; // cache the instruction that generates this access chain
std::vector<uint32_t> 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<uint32_t> 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<uint32_t>& out) const;
private:
// Maximum dimension for column/row in a matrix.
static const int kMaxMatrixSize = 4;
// Allocates a new <id>.
Id AllocateUniqueId() { return ++unique_id_; }
// Allocates a contiguous sequence of <id>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<Id> components) const;
Id CollapseAccessChain();
void SimplifyAccessChainSwizzle();
void TransferAccessChainSwizzle(bool dynamic);
void SerializeInstructions(
std::vector<uint32_t>& out,
const std::vector<Instruction*>& 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<const char*> source_extensions_;
spv::AddressingModel addressing_model_ = spv::AddressingModel::Logical;
spv::MemoryModel memory_model_ = spv::MemoryModel::GLSL450;
std::vector<spv::Capability> 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<Instruction*> imports_;
std::vector<Instruction*> entry_points_;
std::vector<Instruction*> execution_modes_;
std::vector<Instruction*> names_;
std::vector<Instruction*> lines_;
std::vector<Instruction*> decorations_;
std::vector<Instruction*> constants_types_globals_;
std::vector<Instruction*> externals_;
// not output, internally used for quick & dirty canonical (unique) creation
// All types appear before OpConstant.
std::vector<Instruction*>
grouped_constants_[static_cast<int>(spv::Op::OpConstant)];
std::vector<Instruction*>
grouped_types_[static_cast<int>(spv::Op::OpConstant)];
// Stack of switches.
std::stack<Block*> 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<Loop> loops_;
};
} // namespace spirv
} // namespace ui
} // namespace xe
#endif // XENIA_UI_SPIRV_SPIRV_EMITTER_H_

View File

@ -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 <iostream>
#include <vector>
#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<spv::Decoration>(BadValue);
const spv::MemorySemanticsMask MemorySemanticsAllMemory =
static_cast<spv::MemorySemanticsMask>(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<Id>& ids) {
for (auto id : ids) {
operands_.push_back(id);
}
}
void AddIdOperands(std::initializer_list<Id> ids) {
for (auto id : ids) {
operands_.push_back(id);
}
}
void AddImmediateOperand(uint32_t immediate) {
operands_.push_back(immediate);
}
template <typename T>
void AddImmediateOperand(T immediate) {
static_assert(sizeof(T) == sizeof(uint32_t), "Invalid operand size");
operands_.push_back(static_cast<uint32_t>(immediate));
}
void AddImmediateOperands(const std::vector<uint32_t>& immediates) {
for (auto immediate : immediates) {
operands_.push_back(immediate);
}
}
void AddImmediateOperands(std::initializer_list<uint32_t> 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<char*>(&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<int>(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<uint32_t>& out) const {
uint32_t word_count = 1;
if (type_id_) {
++word_count;
}
if (result_id_) {
++word_count;
}
word_count += static_cast<uint32_t>(operands_.size());
out.push_back((word_count << spv::WordCountShift) |
static_cast<uint32_t>(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<Id> 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<int>(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<uint32_t>& 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<Instruction*> instructions_;
std::vector<Block*> predecessors_;
std::vector<Instruction*> 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<uint32_t>& 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<Instruction*> parameter_instructions_;
std::vector<Block*> 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<uint32_t>& out) const {
for (auto function : functions_) {
function->Serialize(out);
}
}
private:
Module(const Module&) = delete;
std::vector<Function*> functions_;
// Maps from result id to instruction having that result id.
std::vector<Instruction*> 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<uint32_t>(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_

View File

@ -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

View File

@ -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_