glasm: Rework control flow introducing a syntax list
This commit regresses VertexA shaders, their transformation pass has to be adapted to the new control flow.
This commit is contained in:
parent
7ff5851608
commit
d54d7de40e
33 changed files with 437 additions and 505 deletions
|
@ -51,6 +51,7 @@ add_library(shader_recompiler STATIC
|
||||||
backend/spirv/emit_spirv_warp.cpp
|
backend/spirv/emit_spirv_warp.cpp
|
||||||
environment.h
|
environment.h
|
||||||
exception.h
|
exception.h
|
||||||
|
frontend/ir/abstract_syntax_list.h
|
||||||
frontend/ir/attribute.cpp
|
frontend/ir/attribute.cpp
|
||||||
frontend/ir/attribute.h
|
frontend/ir/attribute.h
|
||||||
frontend/ir/basic_block.cpp
|
frontend/ir/basic_block.cpp
|
||||||
|
|
|
@ -117,8 +117,6 @@ auto Arg(EmitContext& ctx, const IR::Value& arg) {
|
||||||
return Identity<const IR::Value&>{arg};
|
return Identity<const IR::Value&>{arg};
|
||||||
} else if constexpr (std::is_same_v<ArgType, u32>) {
|
} else if constexpr (std::is_same_v<ArgType, u32>) {
|
||||||
return Identity{arg.U32()};
|
return Identity{arg.U32()};
|
||||||
} else if constexpr (std::is_same_v<ArgType, IR::Block*>) {
|
|
||||||
return Identity{arg.Label()};
|
|
||||||
} else if constexpr (std::is_same_v<ArgType, IR::Attribute>) {
|
} else if constexpr (std::is_same_v<ArgType, IR::Attribute>) {
|
||||||
return Identity{arg.Attribute()};
|
return Identity{arg.Attribute()};
|
||||||
} else if constexpr (std::is_same_v<ArgType, IR::Patch>) {
|
} else if constexpr (std::is_same_v<ArgType, IR::Patch>) {
|
||||||
|
@ -177,6 +175,39 @@ void EmitInst(EmitContext& ctx, IR::Inst* inst) {
|
||||||
throw LogicError("Invalid opcode {}", inst->GetOpcode());
|
throw LogicError("Invalid opcode {}", inst->GetOpcode());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EmitCode(EmitContext& ctx, const IR::Program& program) {
|
||||||
|
const auto eval{
|
||||||
|
[&](const IR::U1& cond) { return ScalarS32{ctx.reg_alloc.Consume(IR::Value{cond})}; }};
|
||||||
|
for (const IR::AbstractSyntaxNode& node : program.syntax_list) {
|
||||||
|
switch (node.type) {
|
||||||
|
case IR::AbstractSyntaxNode::Type::Block:
|
||||||
|
for (IR::Inst& inst : node.block->Instructions()) {
|
||||||
|
EmitInst(ctx, &inst);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case IR::AbstractSyntaxNode::Type::If:
|
||||||
|
ctx.Add("MOV.S.CC RC,{};IF NE.x;", eval(node.if_node.cond));
|
||||||
|
break;
|
||||||
|
case IR::AbstractSyntaxNode::Type::EndIf:
|
||||||
|
ctx.Add("ENDIF;");
|
||||||
|
break;
|
||||||
|
case IR::AbstractSyntaxNode::Type::Loop:
|
||||||
|
ctx.Add("REP;");
|
||||||
|
break;
|
||||||
|
case IR::AbstractSyntaxNode::Type::Repeat:
|
||||||
|
ctx.Add("MOV.S.CC RC,{};BRK NE.x;ENDREP;", eval(node.repeat.cond));
|
||||||
|
break;
|
||||||
|
case IR::AbstractSyntaxNode::Type::Break:
|
||||||
|
ctx.Add("MOV.S.CC RC,{};BRK NE.x;", eval(node.repeat.cond));
|
||||||
|
break;
|
||||||
|
case IR::AbstractSyntaxNode::Type::Return:
|
||||||
|
case IR::AbstractSyntaxNode::Type::Unreachable:
|
||||||
|
ctx.Add("RET;");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void SetupOptions(std::string& header, Info info) {
|
void SetupOptions(std::string& header, Info info) {
|
||||||
if (info.uses_int64_bit_atomics) {
|
if (info.uses_int64_bit_atomics) {
|
||||||
header += "OPTION NV_shader_atomic_int64;";
|
header += "OPTION NV_shader_atomic_int64;";
|
||||||
|
@ -201,11 +232,7 @@ void SetupOptions(std::string& header, Info info) {
|
||||||
|
|
||||||
std::string EmitGLASM(const Profile&, IR::Program& program, Bindings&) {
|
std::string EmitGLASM(const Profile&, IR::Program& program, Bindings&) {
|
||||||
EmitContext ctx{program};
|
EmitContext ctx{program};
|
||||||
for (IR::Block* const block : program.blocks) {
|
EmitCode(ctx, program);
|
||||||
for (IR::Inst& inst : block->Instructions()) {
|
|
||||||
EmitInst(ctx, &inst);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::string header = "!!NVcp5.0\n"
|
std::string header = "!!NVcp5.0\n"
|
||||||
"OPTION NV_internal;";
|
"OPTION NV_internal;";
|
||||||
SetupOptions(header, program.info);
|
SetupOptions(header, program.info);
|
||||||
|
|
|
@ -22,13 +22,8 @@ class EmitContext;
|
||||||
void EmitPhi(EmitContext& ctx, IR::Inst& inst);
|
void EmitPhi(EmitContext& ctx, IR::Inst& inst);
|
||||||
void EmitVoid(EmitContext& ctx);
|
void EmitVoid(EmitContext& ctx);
|
||||||
void EmitIdentity(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
|
void EmitIdentity(EmitContext& ctx, IR::Inst& inst, const IR::Value& value);
|
||||||
void EmitBranch(EmitContext& ctx);
|
void EmitBranchConditionRef(EmitContext&);
|
||||||
void EmitBranchConditional(EmitContext& ctx);
|
|
||||||
void EmitLoopMerge(EmitContext& ctx);
|
|
||||||
void EmitSelectionMerge(EmitContext& ctx);
|
|
||||||
void EmitReturn(EmitContext& ctx);
|
|
||||||
void EmitJoin(EmitContext& ctx);
|
void EmitJoin(EmitContext& ctx);
|
||||||
void EmitUnreachable(EmitContext& ctx);
|
|
||||||
void EmitDemoteToHelperInvocation(EmitContext& ctx);
|
void EmitDemoteToHelperInvocation(EmitContext& ctx);
|
||||||
void EmitBarrier(EmitContext& ctx);
|
void EmitBarrier(EmitContext& ctx);
|
||||||
void EmitWorkgroupMemoryBarrier(EmitContext& ctx);
|
void EmitWorkgroupMemoryBarrier(EmitContext& ctx);
|
||||||
|
|
|
@ -91,7 +91,8 @@ void EmitBitFieldInsert(EmitContext& ctx, IR::Inst& inst, ScalarS32 base, Scalar
|
||||||
if (count.type != Type::Register && offset.type != Type::Register) {
|
if (count.type != Type::Register && offset.type != Type::Register) {
|
||||||
ctx.Add("BFI.S {},{{{},{},0,0}},{},{};", ret, count, offset, insert, base);
|
ctx.Add("BFI.S {},{{{},{},0,0}},{},{};", ret, count, offset, insert, base);
|
||||||
} else {
|
} else {
|
||||||
ctx.Add("MOV.S RC.x,{};MOV.U RC.y,{};"
|
ctx.Add("MOV.S RC.x,{};"
|
||||||
|
"MOV.S RC.y,{};"
|
||||||
"BFI.S {},RC,{},{};",
|
"BFI.S {},RC,{},{};",
|
||||||
count, offset, ret, insert, base);
|
count, offset, ret, insert, base);
|
||||||
}
|
}
|
||||||
|
@ -103,7 +104,8 @@ void EmitBitFieldSExtract(EmitContext& ctx, IR::Inst& inst, ScalarS32 base, Scal
|
||||||
if (count.type != Type::Register && offset.type != Type::Register) {
|
if (count.type != Type::Register && offset.type != Type::Register) {
|
||||||
ctx.Add("BFE.S {},{{{},{},0,0}},{};", ret, count, offset, base);
|
ctx.Add("BFE.S {},{{{},{},0,0}},{};", ret, count, offset, base);
|
||||||
} else {
|
} else {
|
||||||
ctx.Add("MOV.S RC.x,{};MOV.U RC.y,{};"
|
ctx.Add("MOV.S RC.x,{};"
|
||||||
|
"MOV.S RC.y,{};"
|
||||||
"BFE.S {},RC,{};",
|
"BFE.S {},RC,{};",
|
||||||
count, offset, ret, base);
|
count, offset, ret, base);
|
||||||
}
|
}
|
||||||
|
@ -115,7 +117,8 @@ void EmitBitFieldUExtract(EmitContext& ctx, IR::Inst& inst, ScalarU32 base, Scal
|
||||||
if (count.type != Type::Register && offset.type != Type::Register) {
|
if (count.type != Type::Register && offset.type != Type::Register) {
|
||||||
ctx.Add("BFE.U {},{{{},{},0,0}},{};", ret, count, offset, base);
|
ctx.Add("BFE.U {},{{{},{},0,0}},{};", ret, count, offset, base);
|
||||||
} else {
|
} else {
|
||||||
ctx.Add("MOV.U RC.x,{};MOV.U RC.y,{};"
|
ctx.Add("MOV.U RC.x,{};"
|
||||||
|
"MOV.U RC.y,{};"
|
||||||
"BFE.U {},RC,{};",
|
"BFE.U {},RC,{};",
|
||||||
count, offset, ret, base);
|
count, offset, ret, base);
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,34 +23,12 @@ void EmitPhi(EmitContext& ctx, IR::Inst& inst) {
|
||||||
|
|
||||||
void EmitVoid(EmitContext&) {}
|
void EmitVoid(EmitContext&) {}
|
||||||
|
|
||||||
void EmitBranch(EmitContext& ctx) {
|
void EmitBranchConditionRef(EmitContext&) {}
|
||||||
NotImplemented();
|
|
||||||
}
|
|
||||||
|
|
||||||
void EmitBranchConditional(EmitContext& ctx) {
|
|
||||||
NotImplemented();
|
|
||||||
}
|
|
||||||
|
|
||||||
void EmitLoopMerge(EmitContext& ctx) {
|
|
||||||
NotImplemented();
|
|
||||||
}
|
|
||||||
|
|
||||||
void EmitSelectionMerge(EmitContext& ctx) {
|
|
||||||
NotImplemented();
|
|
||||||
}
|
|
||||||
|
|
||||||
void EmitReturn(EmitContext& ctx) {
|
|
||||||
ctx.Add("RET;");
|
|
||||||
}
|
|
||||||
|
|
||||||
void EmitJoin(EmitContext& ctx) {
|
void EmitJoin(EmitContext& ctx) {
|
||||||
NotImplemented();
|
NotImplemented();
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitUnreachable(EmitContext& ctx) {
|
|
||||||
NotImplemented();
|
|
||||||
}
|
|
||||||
|
|
||||||
void EmitDemoteToHelperInvocation(EmitContext& ctx) {
|
void EmitDemoteToHelperInvocation(EmitContext& ctx) {
|
||||||
NotImplemented();
|
NotImplemented();
|
||||||
}
|
}
|
||||||
|
|
|
@ -463,7 +463,6 @@ EmitContext::EmitContext(const Profile& profile_, IR::Program& program, Bindings
|
||||||
DefineImages(program.info, image_binding);
|
DefineImages(program.info, image_binding);
|
||||||
DefineAttributeMemAccess(program.info);
|
DefineAttributeMemAccess(program.info);
|
||||||
DefineGlobalMemoryFunctions(program.info);
|
DefineGlobalMemoryFunctions(program.info);
|
||||||
DefineLabels(program);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EmitContext::~EmitContext() = default;
|
EmitContext::~EmitContext() = default;
|
||||||
|
@ -487,8 +486,6 @@ Id EmitContext::Def(const IR::Value& value) {
|
||||||
return Const(value.F32());
|
return Const(value.F32());
|
||||||
case IR::Type::F64:
|
case IR::Type::F64:
|
||||||
return Constant(F64[1], value.F64());
|
return Constant(F64[1], value.F64());
|
||||||
case IR::Type::Label:
|
|
||||||
return value.Label()->Definition<Id>();
|
|
||||||
default:
|
default:
|
||||||
throw NotImplementedException("Immediate type {}", value.Type());
|
throw NotImplementedException("Immediate type {}", value.Type());
|
||||||
}
|
}
|
||||||
|
@ -1139,12 +1136,6 @@ void EmitContext::DefineImages(const Info& info, u32& binding) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitContext::DefineLabels(IR::Program& program) {
|
|
||||||
for (IR::Block* const block : program.blocks) {
|
|
||||||
block->SetDefinition(OpLabel());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void EmitContext::DefineInputs(const Info& info) {
|
void EmitContext::DefineInputs(const Info& info) {
|
||||||
if (info.uses_workgroup_id) {
|
if (info.uses_workgroup_id) {
|
||||||
workgroup_id = DefineInput(*this, U32[3], false, spv::BuiltIn::WorkgroupId);
|
workgroup_id = DefineInput(*this, U32[3], false, spv::BuiltIn::WorkgroupId);
|
||||||
|
|
|
@ -296,7 +296,6 @@ private:
|
||||||
void DefineImages(const Info& info, u32& binding);
|
void DefineImages(const Info& info, u32& binding);
|
||||||
void DefineAttributeMemAccess(const Info& info);
|
void DefineAttributeMemAccess(const Info& info);
|
||||||
void DefineGlobalMemoryFunctions(const Info& info);
|
void DefineGlobalMemoryFunctions(const Info& info);
|
||||||
void DefineLabels(IR::Program& program);
|
|
||||||
|
|
||||||
void DefineInputs(const Info& info);
|
void DefineInputs(const Info& info);
|
||||||
void DefineOutputs(const IR::Program& program);
|
void DefineOutputs(const IR::Program& program);
|
||||||
|
|
|
@ -41,8 +41,6 @@ ArgType Arg(EmitContext& ctx, const IR::Value& arg) {
|
||||||
return arg;
|
return arg;
|
||||||
} else if constexpr (std::is_same_v<ArgType, u32>) {
|
} else if constexpr (std::is_same_v<ArgType, u32>) {
|
||||||
return arg.U32();
|
return arg.U32();
|
||||||
} else if constexpr (std::is_same_v<ArgType, IR::Block*>) {
|
|
||||||
return arg.Label();
|
|
||||||
} else if constexpr (std::is_same_v<ArgType, IR::Attribute>) {
|
} else if constexpr (std::is_same_v<ArgType, IR::Attribute>) {
|
||||||
return arg.Attribute();
|
return arg.Attribute();
|
||||||
} else if constexpr (std::is_same_v<ArgType, IR::Patch>) {
|
} else if constexpr (std::is_same_v<ArgType, IR::Patch>) {
|
||||||
|
@ -109,15 +107,74 @@ Id TypeId(const EmitContext& ctx, IR::Type type) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Traverse(EmitContext& ctx, IR::Program& program) {
|
||||||
|
IR::Block* current_block{};
|
||||||
|
for (const IR::AbstractSyntaxNode& node : program.syntax_list) {
|
||||||
|
switch (node.type) {
|
||||||
|
case IR::AbstractSyntaxNode::Type::Block:
|
||||||
|
const Id label{node.block->Definition<Id>()};
|
||||||
|
if (current_block) {
|
||||||
|
ctx.OpBranch(label);
|
||||||
|
}
|
||||||
|
current_block = node.block;
|
||||||
|
ctx.AddLabel(label);
|
||||||
|
for (IR::Inst& inst : node.block->Instructions()) {
|
||||||
|
EmitInst(ctx, &inst);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case IR::AbstractSyntaxNode::Type::If: {
|
||||||
|
const Id if_label{node.if_node.body->Definition<Id>()};
|
||||||
|
const Id endif_label{node.if_node.merge->Definition<Id>()};
|
||||||
|
ctx.OpSelectionMerge(endif_label, spv::SelectionControlMask::MaskNone);
|
||||||
|
ctx.OpBranchConditional(ctx.Def(node.if_node.cond), if_label, endif_label);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case IR::AbstractSyntaxNode::Type::Loop: {
|
||||||
|
const Id body_label{node.loop.body->Definition<Id>()};
|
||||||
|
const Id continue_label{node.loop.continue_block->Definition<Id>()};
|
||||||
|
const Id endloop_label{node.loop.merge->Definition<Id>()};
|
||||||
|
|
||||||
|
ctx.OpLoopMerge(endloop_label, continue_label, spv::LoopControlMask::MaskNone);
|
||||||
|
ctx.OpBranch(node.loop.body->Definition<Id>());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case IR::AbstractSyntaxNode::Type::Break: {
|
||||||
|
const Id break_label{node.break_node.merge->Definition<Id>()};
|
||||||
|
const Id skip_label{node.break_node.skip->Definition<Id>()};
|
||||||
|
ctx.OpBranchConditional(ctx.Def(node.break_node.cond), break_label, skip_label);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case IR::AbstractSyntaxNode::Type::EndIf:
|
||||||
|
if (current_block) {
|
||||||
|
ctx.OpBranch(node.end_if.merge->Definition<Id>());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case IR::AbstractSyntaxNode::Type::Repeat: {
|
||||||
|
const Id loop_header_label{node.repeat.loop_header->Definition<Id>()};
|
||||||
|
const Id merge_label{node.repeat.merge->Definition<Id>()};
|
||||||
|
ctx.OpBranchConditional(ctx.Def(node.repeat.cond), loop_header_label, merge_label);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case IR::AbstractSyntaxNode::Type::Return:
|
||||||
|
ctx.OpReturn();
|
||||||
|
break;
|
||||||
|
case IR::AbstractSyntaxNode::Type::Unreachable:
|
||||||
|
ctx.OpUnreachable();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (node.type != IR::AbstractSyntaxNode::Type::Block) {
|
||||||
|
current_block = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Id DefineMain(EmitContext& ctx, IR::Program& program) {
|
Id DefineMain(EmitContext& ctx, IR::Program& program) {
|
||||||
const Id void_function{ctx.TypeFunction(ctx.void_id)};
|
const Id void_function{ctx.TypeFunction(ctx.void_id)};
|
||||||
const Id main{ctx.OpFunction(ctx.void_id, spv::FunctionControlMask::MaskNone, void_function)};
|
const Id main{ctx.OpFunction(ctx.void_id, spv::FunctionControlMask::MaskNone, void_function)};
|
||||||
for (IR::Block* const block : program.blocks) {
|
for (IR::Block* const block : program.blocks) {
|
||||||
ctx.AddLabel(block->Definition<Id>());
|
block->SetDefinition(ctx.OpLabel());
|
||||||
for (IR::Inst& inst : block->Instructions()) {
|
|
||||||
EmitInst(ctx, &inst);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Traverse(ctx, program);
|
||||||
ctx.OpFunctionEnd();
|
ctx.OpFunctionEnd();
|
||||||
return main;
|
return main;
|
||||||
}
|
}
|
||||||
|
@ -411,6 +468,8 @@ Id EmitIdentity(EmitContext& ctx, const IR::Value& value) {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EmitBranchConditionRef(EmitContext&) {}
|
||||||
|
|
||||||
void EmitGetZeroFromOp(EmitContext&) {
|
void EmitGetZeroFromOp(EmitContext&) {
|
||||||
throw LogicError("Unreachable instruction");
|
throw LogicError("Unreachable instruction");
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,40 +7,21 @@
|
||||||
|
|
||||||
namespace Shader::Backend::SPIRV {
|
namespace Shader::Backend::SPIRV {
|
||||||
|
|
||||||
void EmitBranch(EmitContext& ctx, Id label) {
|
|
||||||
ctx.OpBranch(label);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EmitBranchConditional(EmitContext& ctx, Id condition, Id true_label, Id false_label) {
|
|
||||||
ctx.OpBranchConditional(condition, true_label, false_label);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EmitLoopMerge(EmitContext& ctx, Id merge_label, Id continue_label) {
|
|
||||||
ctx.OpLoopMerge(merge_label, continue_label, spv::LoopControlMask::MaskNone);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EmitSelectionMerge(EmitContext& ctx, Id merge_label) {
|
|
||||||
ctx.OpSelectionMerge(merge_label, spv::SelectionControlMask::MaskNone);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EmitReturn(EmitContext& ctx) {
|
|
||||||
ctx.OpReturn();
|
|
||||||
}
|
|
||||||
|
|
||||||
void EmitJoin(EmitContext&) {
|
void EmitJoin(EmitContext&) {
|
||||||
throw NotImplementedException("Join shouldn't be emitted");
|
throw NotImplementedException("Join shouldn't be emitted");
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitUnreachable(EmitContext& ctx) {
|
void EmitDemoteToHelperInvocation(EmitContext& ctx) {
|
||||||
ctx.OpUnreachable();
|
|
||||||
}
|
|
||||||
|
|
||||||
void EmitDemoteToHelperInvocation(EmitContext& ctx, Id continue_label) {
|
|
||||||
if (ctx.profile.support_demote_to_helper_invocation) {
|
if (ctx.profile.support_demote_to_helper_invocation) {
|
||||||
ctx.OpDemoteToHelperInvocationEXT();
|
ctx.OpDemoteToHelperInvocationEXT();
|
||||||
ctx.OpBranch(continue_label);
|
|
||||||
} else {
|
} else {
|
||||||
|
const Id kill_label{ctx.OpLabel()};
|
||||||
|
const Id impossible_label{ctx.OpLabel()};
|
||||||
|
ctx.OpSelectionMerge(impossible_label, spv::SelectionControlMask::MaskNone);
|
||||||
|
ctx.OpBranchConditional(ctx.true_value, kill_label, impossible_label);
|
||||||
|
ctx.AddLabel(kill_label);
|
||||||
ctx.OpKill();
|
ctx.OpKill();
|
||||||
|
ctx.AddLabel(impossible_label);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,14 +23,9 @@ class EmitContext;
|
||||||
Id EmitPhi(EmitContext& ctx, IR::Inst* inst);
|
Id EmitPhi(EmitContext& ctx, IR::Inst* inst);
|
||||||
void EmitVoid(EmitContext& ctx);
|
void EmitVoid(EmitContext& ctx);
|
||||||
Id EmitIdentity(EmitContext& ctx, const IR::Value& value);
|
Id EmitIdentity(EmitContext& ctx, const IR::Value& value);
|
||||||
void EmitBranch(EmitContext& ctx, Id label);
|
void EmitBranchConditionRef(EmitContext&);
|
||||||
void EmitBranchConditional(EmitContext& ctx, Id condition, Id true_label, Id false_label);
|
|
||||||
void EmitLoopMerge(EmitContext& ctx, Id merge_label, Id continue_label);
|
|
||||||
void EmitSelectionMerge(EmitContext& ctx, Id merge_label);
|
|
||||||
void EmitReturn(EmitContext& ctx);
|
|
||||||
void EmitJoin(EmitContext& ctx);
|
void EmitJoin(EmitContext& ctx);
|
||||||
void EmitUnreachable(EmitContext& ctx);
|
void EmitDemoteToHelperInvocation(EmitContext& ctx);
|
||||||
void EmitDemoteToHelperInvocation(EmitContext& ctx, Id continue_label);
|
|
||||||
void EmitBarrier(EmitContext& ctx);
|
void EmitBarrier(EmitContext& ctx);
|
||||||
void EmitWorkgroupMemoryBarrier(EmitContext& ctx);
|
void EmitWorkgroupMemoryBarrier(EmitContext& ctx);
|
||||||
void EmitDeviceMemoryBarrier(EmitContext& ctx);
|
void EmitDeviceMemoryBarrier(EmitContext& ctx);
|
||||||
|
|
56
src/shader_recompiler/frontend/ir/abstract_syntax_list.h
Normal file
56
src/shader_recompiler/frontend/ir/abstract_syntax_list.h
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
// Copyright 2021 yuzu Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "shader_recompiler/frontend/ir/value.h"
|
||||||
|
|
||||||
|
namespace Shader::IR {
|
||||||
|
|
||||||
|
class Block;
|
||||||
|
|
||||||
|
struct AbstractSyntaxNode {
|
||||||
|
enum class Type {
|
||||||
|
Block,
|
||||||
|
If,
|
||||||
|
EndIf,
|
||||||
|
Loop,
|
||||||
|
Repeat,
|
||||||
|
Break,
|
||||||
|
Return,
|
||||||
|
Unreachable,
|
||||||
|
};
|
||||||
|
Type type{};
|
||||||
|
union {
|
||||||
|
Block* block{};
|
||||||
|
struct {
|
||||||
|
U1 cond;
|
||||||
|
Block* body;
|
||||||
|
Block* merge;
|
||||||
|
} if_node;
|
||||||
|
struct {
|
||||||
|
Block* merge;
|
||||||
|
} end_if;
|
||||||
|
struct {
|
||||||
|
Block* body;
|
||||||
|
Block* continue_block;
|
||||||
|
Block* merge;
|
||||||
|
} loop;
|
||||||
|
struct {
|
||||||
|
U1 cond;
|
||||||
|
Block* loop_header;
|
||||||
|
Block* merge;
|
||||||
|
} repeat;
|
||||||
|
struct {
|
||||||
|
U1 cond;
|
||||||
|
Block* merge;
|
||||||
|
Block* skip;
|
||||||
|
} break_node;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
using AbstractSyntaxList = std::vector<AbstractSyntaxNode>;
|
||||||
|
|
||||||
|
} // namespace Shader::IR
|
|
@ -14,10 +14,7 @@
|
||||||
|
|
||||||
namespace Shader::IR {
|
namespace Shader::IR {
|
||||||
|
|
||||||
Block::Block(ObjectPool<Inst>& inst_pool_, u32 begin, u32 end)
|
Block::Block(ObjectPool<Inst>& inst_pool_) : inst_pool{&inst_pool_} {}
|
||||||
: inst_pool{&inst_pool_}, location_begin{begin}, location_end{end} {}
|
|
||||||
|
|
||||||
Block::Block(ObjectPool<Inst>& inst_pool_) : Block{inst_pool_, 0, 0} {}
|
|
||||||
|
|
||||||
Block::~Block() = default;
|
Block::~Block() = default;
|
||||||
|
|
||||||
|
@ -40,39 +37,15 @@ Block::iterator Block::PrependNewInst(iterator insertion_point, Opcode op,
|
||||||
return result_it;
|
return result_it;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Block::SetBranches(Condition cond, Block* branch_true_, Block* branch_false_) {
|
void Block::AddBranch(Block* block) {
|
||||||
branch_cond = cond;
|
if (std::ranges::find(imm_successors, block) != imm_successors.end()) {
|
||||||
branch_true = branch_true_;
|
throw LogicError("Successor already inserted");
|
||||||
branch_false = branch_false_;
|
|
||||||
}
|
}
|
||||||
|
if (std::ranges::find(block->imm_predecessors, this) != block->imm_predecessors.end()) {
|
||||||
void Block::SetBranch(Block* branch) {
|
throw LogicError("Predecessor already inserted");
|
||||||
branch_cond = Condition{true};
|
|
||||||
branch_true = branch;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Block::SetReturn() {
|
|
||||||
branch_cond = Condition{true};
|
|
||||||
branch_true = nullptr;
|
|
||||||
branch_false = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Block::IsVirtual() const noexcept {
|
|
||||||
return location_begin == location_end;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 Block::LocationBegin() const noexcept {
|
|
||||||
return location_begin;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 Block::LocationEnd() const noexcept {
|
|
||||||
return location_end;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Block::AddImmediatePredecessor(Block* block) {
|
|
||||||
if (std::ranges::find(imm_predecessors, block) == imm_predecessors.end()) {
|
|
||||||
imm_predecessors.push_back(block);
|
|
||||||
}
|
}
|
||||||
|
imm_successors.push_back(block);
|
||||||
|
block->imm_predecessors.push_back(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string BlockToIndex(const std::map<const Block*, size_t>& block_to_index,
|
static std::string BlockToIndex(const std::map<const Block*, size_t>& block_to_index,
|
||||||
|
@ -92,15 +65,11 @@ static size_t InstIndex(std::map<const Inst*, size_t>& inst_to_index, size_t& in
|
||||||
return it->second;
|
return it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string ArgToIndex(const std::map<const Block*, size_t>& block_to_index,
|
static std::string ArgToIndex(std::map<const Inst*, size_t>& inst_to_index, size_t& inst_index,
|
||||||
std::map<const Inst*, size_t>& inst_to_index, size_t& inst_index,
|
|
||||||
const Value& arg) {
|
const Value& arg) {
|
||||||
if (arg.IsEmpty()) {
|
if (arg.IsEmpty()) {
|
||||||
return "<null>";
|
return "<null>";
|
||||||
}
|
}
|
||||||
if (arg.IsLabel()) {
|
|
||||||
return BlockToIndex(block_to_index, arg.Label());
|
|
||||||
}
|
|
||||||
if (!arg.IsImmediate() || arg.IsIdentity()) {
|
if (!arg.IsImmediate() || arg.IsIdentity()) {
|
||||||
return fmt::format("%{}", InstIndex(inst_to_index, inst_index, arg.Inst()));
|
return fmt::format("%{}", InstIndex(inst_to_index, inst_index, arg.Inst()));
|
||||||
}
|
}
|
||||||
|
@ -140,8 +109,7 @@ std::string DumpBlock(const Block& block, const std::map<const Block*, size_t>&
|
||||||
if (const auto it{block_to_index.find(&block)}; it != block_to_index.end()) {
|
if (const auto it{block_to_index.find(&block)}; it != block_to_index.end()) {
|
||||||
ret += fmt::format(" ${}", it->second);
|
ret += fmt::format(" ${}", it->second);
|
||||||
}
|
}
|
||||||
ret += fmt::format(": begin={:04x} end={:04x}\n", block.LocationBegin(), block.LocationEnd());
|
ret += '\n';
|
||||||
|
|
||||||
for (const Inst& inst : block) {
|
for (const Inst& inst : block) {
|
||||||
const Opcode op{inst.GetOpcode()};
|
const Opcode op{inst.GetOpcode()};
|
||||||
ret += fmt::format("[{:016x}] ", reinterpret_cast<u64>(&inst));
|
ret += fmt::format("[{:016x}] ", reinterpret_cast<u64>(&inst));
|
||||||
|
@ -153,7 +121,7 @@ std::string DumpBlock(const Block& block, const std::map<const Block*, size_t>&
|
||||||
const size_t arg_count{inst.NumArgs()};
|
const size_t arg_count{inst.NumArgs()};
|
||||||
for (size_t arg_index = 0; arg_index < arg_count; ++arg_index) {
|
for (size_t arg_index = 0; arg_index < arg_count; ++arg_index) {
|
||||||
const Value arg{inst.Arg(arg_index)};
|
const Value arg{inst.Arg(arg_index)};
|
||||||
const std::string arg_str{ArgToIndex(block_to_index, inst_to_index, inst_index, arg)};
|
const std::string arg_str{ArgToIndex(inst_to_index, inst_index, arg)};
|
||||||
ret += arg_index != 0 ? ", " : " ";
|
ret += arg_index != 0 ? ", " : " ";
|
||||||
if (op == Opcode::Phi) {
|
if (op == Opcode::Phi) {
|
||||||
ret += fmt::format("[ {}, {} ]", arg_str,
|
ret += fmt::format("[ {}, {} ]", arg_str,
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include <boost/intrusive/list.hpp>
|
#include <boost/intrusive/list.hpp>
|
||||||
|
|
||||||
#include "common/bit_cast.h"
|
#include "common/bit_cast.h"
|
||||||
|
#include "common/common_types.h"
|
||||||
#include "shader_recompiler/frontend/ir/condition.h"
|
#include "shader_recompiler/frontend/ir/condition.h"
|
||||||
#include "shader_recompiler/frontend/ir/value.h"
|
#include "shader_recompiler/frontend/ir/value.h"
|
||||||
#include "shader_recompiler/object_pool.h"
|
#include "shader_recompiler/object_pool.h"
|
||||||
|
@ -27,7 +28,6 @@ public:
|
||||||
using reverse_iterator = InstructionList::reverse_iterator;
|
using reverse_iterator = InstructionList::reverse_iterator;
|
||||||
using const_reverse_iterator = InstructionList::const_reverse_iterator;
|
using const_reverse_iterator = InstructionList::const_reverse_iterator;
|
||||||
|
|
||||||
explicit Block(ObjectPool<Inst>& inst_pool_, u32 begin, u32 end);
|
|
||||||
explicit Block(ObjectPool<Inst>& inst_pool_);
|
explicit Block(ObjectPool<Inst>& inst_pool_);
|
||||||
~Block();
|
~Block();
|
||||||
|
|
||||||
|
@ -44,22 +44,8 @@ public:
|
||||||
iterator PrependNewInst(iterator insertion_point, Opcode op,
|
iterator PrependNewInst(iterator insertion_point, Opcode op,
|
||||||
std::initializer_list<Value> args = {}, u32 flags = 0);
|
std::initializer_list<Value> args = {}, u32 flags = 0);
|
||||||
|
|
||||||
/// Set the branches to jump to when all instructions have executed.
|
/// Adds a new branch to this basic block.
|
||||||
void SetBranches(Condition cond, Block* branch_true, Block* branch_false);
|
void AddBranch(Block* block);
|
||||||
/// Set the branch to unconditionally jump to when all instructions have executed.
|
|
||||||
void SetBranch(Block* branch);
|
|
||||||
/// Mark the block as a return block.
|
|
||||||
void SetReturn();
|
|
||||||
|
|
||||||
/// Returns true when the block does not implement any guest instructions directly.
|
|
||||||
[[nodiscard]] bool IsVirtual() const noexcept;
|
|
||||||
/// Gets the starting location of this basic block.
|
|
||||||
[[nodiscard]] u32 LocationBegin() const noexcept;
|
|
||||||
/// Gets the end location for this basic block.
|
|
||||||
[[nodiscard]] u32 LocationEnd() const noexcept;
|
|
||||||
|
|
||||||
/// Adds a new immediate predecessor to this basic block.
|
|
||||||
void AddImmediatePredecessor(Block* block);
|
|
||||||
|
|
||||||
/// Gets a mutable reference to the instruction list for this basic block.
|
/// Gets a mutable reference to the instruction list for this basic block.
|
||||||
[[nodiscard]] InstructionList& Instructions() noexcept {
|
[[nodiscard]] InstructionList& Instructions() noexcept {
|
||||||
|
@ -71,9 +57,13 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets an immutable span to the immediate predecessors.
|
/// Gets an immutable span to the immediate predecessors.
|
||||||
[[nodiscard]] std::span<Block* const> ImmediatePredecessors() const noexcept {
|
[[nodiscard]] std::span<Block* const> ImmPredecessors() const noexcept {
|
||||||
return imm_predecessors;
|
return imm_predecessors;
|
||||||
}
|
}
|
||||||
|
/// Gets an immutable span to the immediate successors.
|
||||||
|
[[nodiscard]] std::span<Block* const> ImmSuccessors() const noexcept {
|
||||||
|
return imm_successors;
|
||||||
|
}
|
||||||
|
|
||||||
/// Intrusively store the host definition of this instruction.
|
/// Intrusively store the host definition of this instruction.
|
||||||
template <typename DefinitionType>
|
template <typename DefinitionType>
|
||||||
|
@ -87,19 +77,6 @@ public:
|
||||||
return Common::BitCast<DefinitionType>(definition);
|
return Common::BitCast<DefinitionType>(definition);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] Condition BranchCondition() const noexcept {
|
|
||||||
return branch_cond;
|
|
||||||
}
|
|
||||||
[[nodiscard]] bool IsTerminationBlock() const noexcept {
|
|
||||||
return !branch_true && !branch_false;
|
|
||||||
}
|
|
||||||
[[nodiscard]] Block* TrueBranch() const noexcept {
|
|
||||||
return branch_true;
|
|
||||||
}
|
|
||||||
[[nodiscard]] Block* FalseBranch() const noexcept {
|
|
||||||
return branch_false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetSsaRegValue(IR::Reg reg, const Value& value) noexcept {
|
void SetSsaRegValue(IR::Reg reg, const Value& value) noexcept {
|
||||||
ssa_reg_values[RegIndex(reg)] = value;
|
ssa_reg_values[RegIndex(reg)] = value;
|
||||||
}
|
}
|
||||||
|
@ -178,22 +155,14 @@ public:
|
||||||
private:
|
private:
|
||||||
/// Memory pool for instruction list
|
/// Memory pool for instruction list
|
||||||
ObjectPool<Inst>* inst_pool;
|
ObjectPool<Inst>* inst_pool;
|
||||||
/// Starting location of this block
|
|
||||||
u32 location_begin;
|
|
||||||
/// End location of this block
|
|
||||||
u32 location_end;
|
|
||||||
|
|
||||||
/// List of instructions in this block
|
/// List of instructions in this block
|
||||||
InstructionList instructions;
|
InstructionList instructions;
|
||||||
|
|
||||||
/// Condition to choose the branch to take
|
|
||||||
Condition branch_cond{true};
|
|
||||||
/// Block to jump into when the branch condition evaluates as true
|
|
||||||
Block* branch_true{nullptr};
|
|
||||||
/// Block to jump into when the branch condition evaluates as false
|
|
||||||
Block* branch_false{nullptr};
|
|
||||||
/// Block immediate predecessors
|
/// Block immediate predecessors
|
||||||
std::vector<Block*> imm_predecessors;
|
std::vector<Block*> imm_predecessors;
|
||||||
|
/// Block immediate successors
|
||||||
|
std::vector<Block*> imm_successors;
|
||||||
|
|
||||||
/// Intrusively store the value of a register in the block.
|
/// Intrusively store the value of a register in the block.
|
||||||
std::array<Value, NUM_REGS> ssa_reg_values;
|
std::array<Value, NUM_REGS> ssa_reg_values;
|
||||||
|
|
|
@ -61,25 +61,28 @@ F64 IREmitter::Imm64(f64 value) const {
|
||||||
return F64{Value{value}};
|
return F64{Value{value}};
|
||||||
}
|
}
|
||||||
|
|
||||||
void IREmitter::Branch(Block* label) {
|
void IREmitter::Prologue() {
|
||||||
label->AddImmediatePredecessor(block);
|
Inst(Opcode::Prologue);
|
||||||
block->SetBranch(label);
|
|
||||||
Inst(Opcode::Branch, label);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IREmitter::BranchConditional(const U1& condition, Block* true_label, Block* false_label) {
|
void IREmitter::Epilogue() {
|
||||||
block->SetBranches(IR::Condition{true}, true_label, false_label);
|
Inst(Opcode::Epilogue);
|
||||||
true_label->AddImmediatePredecessor(block);
|
|
||||||
false_label->AddImmediatePredecessor(block);
|
|
||||||
Inst(Opcode::BranchConditional, condition, true_label, false_label);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IREmitter::LoopMerge(Block* merge_block, Block* continue_target) {
|
void IREmitter::BranchConditionRef(const U1& cond) {
|
||||||
Inst(Opcode::LoopMerge, merge_block, continue_target);
|
Inst(Opcode::BranchConditionRef, cond);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IREmitter::SelectionMerge(Block* merge_block) {
|
void IREmitter::DemoteToHelperInvocation() {
|
||||||
Inst(Opcode::SelectionMerge, merge_block);
|
Inst(Opcode::DemoteToHelperInvocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IREmitter::EmitVertex(const U32& stream) {
|
||||||
|
Inst(Opcode::EmitVertex, stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IREmitter::EndPrimitive(const U32& stream) {
|
||||||
|
Inst(Opcode::EndPrimitive, stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IREmitter::Barrier() {
|
void IREmitter::Barrier() {
|
||||||
|
@ -94,37 +97,6 @@ void IREmitter::DeviceMemoryBarrier() {
|
||||||
Inst(Opcode::DeviceMemoryBarrier);
|
Inst(Opcode::DeviceMemoryBarrier);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IREmitter::Return() {
|
|
||||||
block->SetReturn();
|
|
||||||
Inst(Opcode::Return);
|
|
||||||
}
|
|
||||||
|
|
||||||
void IREmitter::Unreachable() {
|
|
||||||
Inst(Opcode::Unreachable);
|
|
||||||
}
|
|
||||||
|
|
||||||
void IREmitter::DemoteToHelperInvocation(Block* continue_label) {
|
|
||||||
block->SetBranch(continue_label);
|
|
||||||
continue_label->AddImmediatePredecessor(block);
|
|
||||||
Inst(Opcode::DemoteToHelperInvocation, continue_label);
|
|
||||||
}
|
|
||||||
|
|
||||||
void IREmitter::Prologue() {
|
|
||||||
Inst(Opcode::Prologue);
|
|
||||||
}
|
|
||||||
|
|
||||||
void IREmitter::Epilogue() {
|
|
||||||
Inst(Opcode::Epilogue);
|
|
||||||
}
|
|
||||||
|
|
||||||
void IREmitter::EmitVertex(const U32& stream) {
|
|
||||||
Inst(Opcode::EmitVertex, stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
void IREmitter::EndPrimitive(const U32& stream) {
|
|
||||||
Inst(Opcode::EndPrimitive, stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
U32 IREmitter::GetReg(IR::Reg reg) {
|
U32 IREmitter::GetReg(IR::Reg reg) {
|
||||||
return Inst<U32>(Opcode::GetRegister, reg);
|
return Inst<U32>(Opcode::GetRegister, reg);
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,17 +32,10 @@ public:
|
||||||
[[nodiscard]] U64 Imm64(s64 value) const;
|
[[nodiscard]] U64 Imm64(s64 value) const;
|
||||||
[[nodiscard]] F64 Imm64(f64 value) const;
|
[[nodiscard]] F64 Imm64(f64 value) const;
|
||||||
|
|
||||||
void Branch(Block* label);
|
|
||||||
void BranchConditional(const U1& condition, Block* true_label, Block* false_label);
|
|
||||||
void LoopMerge(Block* merge_block, Block* continue_target);
|
|
||||||
void SelectionMerge(Block* merge_block);
|
|
||||||
void Return();
|
|
||||||
void Unreachable();
|
|
||||||
void DemoteToHelperInvocation(Block* continue_label);
|
|
||||||
|
|
||||||
void Prologue();
|
void Prologue();
|
||||||
void Epilogue();
|
void Epilogue();
|
||||||
|
void BranchConditionRef(const U1& cond);
|
||||||
|
void DemoteToHelperInvocation();
|
||||||
void EmitVertex(const U32& stream);
|
void EmitVertex(const U32& stream);
|
||||||
void EndPrimitive(const U32& stream);
|
void EndPrimitive(const U32& stream);
|
||||||
|
|
||||||
|
|
|
@ -56,19 +56,14 @@ Inst::~Inst() {
|
||||||
|
|
||||||
bool Inst::MayHaveSideEffects() const noexcept {
|
bool Inst::MayHaveSideEffects() const noexcept {
|
||||||
switch (op) {
|
switch (op) {
|
||||||
case Opcode::Branch:
|
case Opcode::Prologue:
|
||||||
case Opcode::BranchConditional:
|
case Opcode::Epilogue:
|
||||||
case Opcode::LoopMerge:
|
case Opcode::BranchConditionRef:
|
||||||
case Opcode::SelectionMerge:
|
|
||||||
case Opcode::Return:
|
|
||||||
case Opcode::Join:
|
case Opcode::Join:
|
||||||
case Opcode::Unreachable:
|
|
||||||
case Opcode::DemoteToHelperInvocation:
|
case Opcode::DemoteToHelperInvocation:
|
||||||
case Opcode::Barrier:
|
case Opcode::Barrier:
|
||||||
case Opcode::WorkgroupMemoryBarrier:
|
case Opcode::WorkgroupMemoryBarrier:
|
||||||
case Opcode::DeviceMemoryBarrier:
|
case Opcode::DeviceMemoryBarrier:
|
||||||
case Opcode::Prologue:
|
|
||||||
case Opcode::Epilogue:
|
|
||||||
case Opcode::EmitVertex:
|
case Opcode::EmitVertex:
|
||||||
case Opcode::EndPrimitive:
|
case Opcode::EndPrimitive:
|
||||||
case Opcode::SetAttribute:
|
case Opcode::SetAttribute:
|
||||||
|
|
|
@ -30,7 +30,6 @@ struct OpcodeMeta {
|
||||||
// using enum Type;
|
// using enum Type;
|
||||||
constexpr Type Void{Type::Void};
|
constexpr Type Void{Type::Void};
|
||||||
constexpr Type Opaque{Type::Opaque};
|
constexpr Type Opaque{Type::Opaque};
|
||||||
constexpr Type Label{Type::Label};
|
|
||||||
constexpr Type Reg{Type::Reg};
|
constexpr Type Reg{Type::Reg};
|
||||||
constexpr Type Pred{Type::Pred};
|
constexpr Type Pred{Type::Pred};
|
||||||
constexpr Type Attribute{Type::Attribute};
|
constexpr Type Attribute{Type::Attribute};
|
||||||
|
|
|
@ -7,27 +7,20 @@ OPCODE(Phi, Opaque,
|
||||||
OPCODE(Identity, Opaque, Opaque, )
|
OPCODE(Identity, Opaque, Opaque, )
|
||||||
OPCODE(Void, Void, )
|
OPCODE(Void, Void, )
|
||||||
|
|
||||||
// Control flow
|
// Special operations
|
||||||
OPCODE(Branch, Void, Label, )
|
OPCODE(Prologue, Void, )
|
||||||
OPCODE(BranchConditional, Void, U1, Label, Label, )
|
OPCODE(Epilogue, Void, )
|
||||||
OPCODE(LoopMerge, Void, Label, Label, )
|
OPCODE(BranchConditionRef, Void, U1, )
|
||||||
OPCODE(SelectionMerge, Void, Label, )
|
|
||||||
OPCODE(Return, Void, )
|
|
||||||
OPCODE(Join, Void, )
|
OPCODE(Join, Void, )
|
||||||
OPCODE(Unreachable, Void, )
|
OPCODE(DemoteToHelperInvocation, Void, )
|
||||||
OPCODE(DemoteToHelperInvocation, Void, Label, )
|
OPCODE(EmitVertex, Void, U32, )
|
||||||
|
OPCODE(EndPrimitive, Void, U32, )
|
||||||
|
|
||||||
// Barriers
|
// Barriers
|
||||||
OPCODE(Barrier, Void, )
|
OPCODE(Barrier, Void, )
|
||||||
OPCODE(WorkgroupMemoryBarrier, Void, )
|
OPCODE(WorkgroupMemoryBarrier, Void, )
|
||||||
OPCODE(DeviceMemoryBarrier, Void, )
|
OPCODE(DeviceMemoryBarrier, Void, )
|
||||||
|
|
||||||
// Special operations
|
|
||||||
OPCODE(Prologue, Void, )
|
|
||||||
OPCODE(Epilogue, Void, )
|
|
||||||
OPCODE(EmitVertex, Void, U32, )
|
|
||||||
OPCODE(EndPrimitive, Void, U32, )
|
|
||||||
|
|
||||||
// Context getters/setters
|
// Context getters/setters
|
||||||
OPCODE(GetRegister, U32, Reg, )
|
OPCODE(GetRegister, U32, Reg, )
|
||||||
OPCODE(SetRegister, Void, Reg, U32, )
|
OPCODE(SetRegister, Void, Reg, U32, )
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include <boost/container/flat_set.hpp>
|
#include <boost/container/flat_set.hpp>
|
||||||
#include <boost/container/small_vector.hpp>
|
#include <boost/container/small_vector.hpp>
|
||||||
|
|
||||||
|
@ -10,21 +12,21 @@
|
||||||
|
|
||||||
namespace Shader::IR {
|
namespace Shader::IR {
|
||||||
|
|
||||||
BlockList PostOrder(const BlockList& blocks) {
|
BlockList PostOrder(const AbstractSyntaxNode& root) {
|
||||||
boost::container::small_vector<Block*, 16> block_stack;
|
boost::container::small_vector<Block*, 16> block_stack;
|
||||||
boost::container::flat_set<Block*> visited;
|
boost::container::flat_set<Block*> visited;
|
||||||
|
|
||||||
BlockList post_order_blocks;
|
BlockList post_order_blocks;
|
||||||
post_order_blocks.reserve(blocks.size());
|
|
||||||
|
|
||||||
Block* const first_block{blocks.front()};
|
if (root.type != AbstractSyntaxNode::Type::Block) {
|
||||||
|
throw LogicError("First node in abstract syntax list root is not a block");
|
||||||
|
}
|
||||||
|
Block* const first_block{root.block};
|
||||||
visited.insert(first_block);
|
visited.insert(first_block);
|
||||||
block_stack.push_back(first_block);
|
block_stack.push_back(first_block);
|
||||||
|
|
||||||
const auto visit_branch = [&](Block* block, Block* branch) {
|
while (!block_stack.empty()) {
|
||||||
if (!branch) {
|
Block* const block{block_stack.back()};
|
||||||
return false;
|
const auto visit{[&](Block* branch) {
|
||||||
}
|
|
||||||
if (!visited.insert(branch).second) {
|
if (!visited.insert(branch).second) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -32,13 +34,9 @@ BlockList PostOrder(const BlockList& blocks) {
|
||||||
block_stack.push_back(block);
|
block_stack.push_back(block);
|
||||||
block_stack.push_back(branch);
|
block_stack.push_back(branch);
|
||||||
return true;
|
return true;
|
||||||
};
|
}};
|
||||||
while (!block_stack.empty()) {
|
|
||||||
Block* const block{block_stack.back()};
|
|
||||||
block_stack.pop_back();
|
block_stack.pop_back();
|
||||||
|
if (std::ranges::none_of(block->ImmSuccessors(), visit)) {
|
||||||
if (!visit_branch(block, block->TrueBranch()) &&
|
|
||||||
!visit_branch(block, block->FalseBranch())) {
|
|
||||||
post_order_blocks.push_back(block);
|
post_order_blocks.push_back(block);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,9 +5,10 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "shader_recompiler/frontend/ir/basic_block.h"
|
#include "shader_recompiler/frontend/ir/basic_block.h"
|
||||||
|
#include "shader_recompiler/frontend/ir/abstract_syntax_list.h"
|
||||||
|
|
||||||
namespace Shader::IR {
|
namespace Shader::IR {
|
||||||
|
|
||||||
BlockList PostOrder(const BlockList& blocks);
|
BlockList PostOrder(const AbstractSyntaxNode& root);
|
||||||
|
|
||||||
} // namespace Shader::IR
|
} // namespace Shader::IR
|
||||||
|
|
|
@ -7,8 +7,7 @@
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <boost/container/small_vector.hpp>
|
#include "shader_recompiler/frontend/ir/abstract_syntax_list.h"
|
||||||
|
|
||||||
#include "shader_recompiler/frontend/ir/basic_block.h"
|
#include "shader_recompiler/frontend/ir/basic_block.h"
|
||||||
#include "shader_recompiler/program_header.h"
|
#include "shader_recompiler/program_header.h"
|
||||||
#include "shader_recompiler/shader_info.h"
|
#include "shader_recompiler/shader_info.h"
|
||||||
|
@ -17,6 +16,7 @@
|
||||||
namespace Shader::IR {
|
namespace Shader::IR {
|
||||||
|
|
||||||
struct Program {
|
struct Program {
|
||||||
|
AbstractSyntaxList syntax_list;
|
||||||
BlockList blocks;
|
BlockList blocks;
|
||||||
BlockList post_order_blocks;
|
BlockList post_order_blocks;
|
||||||
Info info;
|
Info info;
|
||||||
|
|
|
@ -16,31 +16,30 @@ namespace Shader::IR {
|
||||||
enum class Type {
|
enum class Type {
|
||||||
Void = 0,
|
Void = 0,
|
||||||
Opaque = 1 << 0,
|
Opaque = 1 << 0,
|
||||||
Label = 1 << 1,
|
Reg = 1 << 1,
|
||||||
Reg = 1 << 2,
|
Pred = 1 << 2,
|
||||||
Pred = 1 << 3,
|
Attribute = 1 << 3,
|
||||||
Attribute = 1 << 4,
|
Patch = 1 << 4,
|
||||||
Patch = 1 << 5,
|
U1 = 1 << 5,
|
||||||
U1 = 1 << 6,
|
U8 = 1 << 6,
|
||||||
U8 = 1 << 7,
|
U16 = 1 << 7,
|
||||||
U16 = 1 << 8,
|
U32 = 1 << 8,
|
||||||
U32 = 1 << 9,
|
U64 = 1 << 9,
|
||||||
U64 = 1 << 10,
|
F16 = 1 << 10,
|
||||||
F16 = 1 << 11,
|
F32 = 1 << 11,
|
||||||
F32 = 1 << 12,
|
F64 = 1 << 12,
|
||||||
F64 = 1 << 13,
|
U32x2 = 1 << 13,
|
||||||
U32x2 = 1 << 14,
|
U32x3 = 1 << 14,
|
||||||
U32x3 = 1 << 15,
|
U32x4 = 1 << 15,
|
||||||
U32x4 = 1 << 16,
|
F16x2 = 1 << 16,
|
||||||
F16x2 = 1 << 17,
|
F16x3 = 1 << 17,
|
||||||
F16x3 = 1 << 18,
|
F16x4 = 1 << 18,
|
||||||
F16x4 = 1 << 19,
|
F32x2 = 1 << 19,
|
||||||
F32x2 = 1 << 20,
|
F32x3 = 1 << 20,
|
||||||
F32x3 = 1 << 21,
|
F32x4 = 1 << 21,
|
||||||
F32x4 = 1 << 22,
|
F64x2 = 1 << 22,
|
||||||
F64x2 = 1 << 23,
|
F64x3 = 1 << 23,
|
||||||
F64x3 = 1 << 24,
|
F64x4 = 1 << 24,
|
||||||
F64x4 = 1 << 25,
|
|
||||||
};
|
};
|
||||||
DECLARE_ENUM_FLAG_OPERATORS(Type)
|
DECLARE_ENUM_FLAG_OPERATORS(Type)
|
||||||
|
|
||||||
|
|
|
@ -9,8 +9,6 @@ namespace Shader::IR {
|
||||||
|
|
||||||
Value::Value(IR::Inst* value) noexcept : type{Type::Opaque}, inst{value} {}
|
Value::Value(IR::Inst* value) noexcept : type{Type::Opaque}, inst{value} {}
|
||||||
|
|
||||||
Value::Value(IR::Block* value) noexcept : type{Type::Label}, label{value} {}
|
|
||||||
|
|
||||||
Value::Value(IR::Reg value) noexcept : type{Type::Reg}, reg{value} {}
|
Value::Value(IR::Reg value) noexcept : type{Type::Reg}, reg{value} {}
|
||||||
|
|
||||||
Value::Value(IR::Pred value) noexcept : type{Type::Pred}, pred{value} {}
|
Value::Value(IR::Pred value) noexcept : type{Type::Pred}, pred{value} {}
|
||||||
|
@ -33,10 +31,6 @@ Value::Value(u64 value) noexcept : type{Type::U64}, imm_u64{value} {}
|
||||||
|
|
||||||
Value::Value(f64 value) noexcept : type{Type::F64}, imm_f64{value} {}
|
Value::Value(f64 value) noexcept : type{Type::F64}, imm_f64{value} {}
|
||||||
|
|
||||||
bool Value::IsLabel() const noexcept {
|
|
||||||
return type == Type::Label;
|
|
||||||
}
|
|
||||||
|
|
||||||
IR::Type Value::Type() const noexcept {
|
IR::Type Value::Type() const noexcept {
|
||||||
if (IsPhi()) {
|
if (IsPhi()) {
|
||||||
// The type of a phi node is stored in its flags
|
// The type of a phi node is stored in its flags
|
||||||
|
@ -60,8 +54,6 @@ bool Value::operator==(const Value& other) const {
|
||||||
return true;
|
return true;
|
||||||
case Type::Opaque:
|
case Type::Opaque:
|
||||||
return inst == other.inst;
|
return inst == other.inst;
|
||||||
case Type::Label:
|
|
||||||
return label == other.label;
|
|
||||||
case Type::Reg:
|
case Type::Reg:
|
||||||
return reg == other.reg;
|
return reg == other.reg;
|
||||||
case Type::Pred:
|
case Type::Pred:
|
||||||
|
|
|
@ -37,7 +37,6 @@ class Value {
|
||||||
public:
|
public:
|
||||||
Value() noexcept = default;
|
Value() noexcept = default;
|
||||||
explicit Value(IR::Inst* value) noexcept;
|
explicit Value(IR::Inst* value) noexcept;
|
||||||
explicit Value(IR::Block* value) noexcept;
|
|
||||||
explicit Value(IR::Reg value) noexcept;
|
explicit Value(IR::Reg value) noexcept;
|
||||||
explicit Value(IR::Pred value) noexcept;
|
explicit Value(IR::Pred value) noexcept;
|
||||||
explicit Value(IR::Attribute value) noexcept;
|
explicit Value(IR::Attribute value) noexcept;
|
||||||
|
@ -54,11 +53,9 @@ public:
|
||||||
[[nodiscard]] bool IsPhi() const noexcept;
|
[[nodiscard]] bool IsPhi() const noexcept;
|
||||||
[[nodiscard]] bool IsEmpty() const noexcept;
|
[[nodiscard]] bool IsEmpty() const noexcept;
|
||||||
[[nodiscard]] bool IsImmediate() const noexcept;
|
[[nodiscard]] bool IsImmediate() const noexcept;
|
||||||
[[nodiscard]] bool IsLabel() const noexcept;
|
|
||||||
[[nodiscard]] IR::Type Type() const noexcept;
|
[[nodiscard]] IR::Type Type() const noexcept;
|
||||||
|
|
||||||
[[nodiscard]] IR::Inst* Inst() const;
|
[[nodiscard]] IR::Inst* Inst() const;
|
||||||
[[nodiscard]] IR::Block* Label() const;
|
|
||||||
[[nodiscard]] IR::Inst* InstRecursive() const;
|
[[nodiscard]] IR::Inst* InstRecursive() const;
|
||||||
[[nodiscard]] IR::Value Resolve() const;
|
[[nodiscard]] IR::Value Resolve() const;
|
||||||
[[nodiscard]] IR::Reg Reg() const;
|
[[nodiscard]] IR::Reg Reg() const;
|
||||||
|
@ -80,7 +77,6 @@ private:
|
||||||
IR::Type type{};
|
IR::Type type{};
|
||||||
union {
|
union {
|
||||||
IR::Inst* inst{};
|
IR::Inst* inst{};
|
||||||
IR::Block* label;
|
|
||||||
IR::Reg reg;
|
IR::Reg reg;
|
||||||
IR::Pred pred;
|
IR::Pred pred;
|
||||||
IR::Attribute attribute;
|
IR::Attribute attribute;
|
||||||
|
@ -304,11 +300,6 @@ inline IR::Inst* Value::Inst() const {
|
||||||
return inst;
|
return inst;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline IR::Block* Value::Label() const {
|
|
||||||
DEBUG_ASSERT(type == Type::Label);
|
|
||||||
return label;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline IR::Inst* Value::InstRecursive() const {
|
inline IR::Inst* Value::InstRecursive() const {
|
||||||
DEBUG_ASSERT(type == Type::Opaque);
|
DEBUG_ASSERT(type == Type::Opaque);
|
||||||
if (IsIdentity()) {
|
if (IsIdentity()) {
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <ranges>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "shader_recompiler/frontend/ir/basic_block.h"
|
#include "shader_recompiler/frontend/ir/basic_block.h"
|
||||||
|
@ -15,6 +16,16 @@
|
||||||
|
|
||||||
namespace Shader::Maxwell {
|
namespace Shader::Maxwell {
|
||||||
namespace {
|
namespace {
|
||||||
|
IR::BlockList GenerateBlocks(const IR::AbstractSyntaxList& syntax_list) {
|
||||||
|
auto syntax_blocks{syntax_list | std::views::filter([](const auto& node) {
|
||||||
|
return node.type == IR::AbstractSyntaxNode::Type::Block;
|
||||||
|
})};
|
||||||
|
IR::BlockList blocks(std::ranges::distance(syntax_blocks));
|
||||||
|
std::ranges::transform(syntax_blocks, blocks.begin(),
|
||||||
|
[](const IR::AbstractSyntaxNode& node) { return node.block; });
|
||||||
|
return blocks;
|
||||||
|
}
|
||||||
|
|
||||||
void RemoveUnreachableBlocks(IR::Program& program) {
|
void RemoveUnreachableBlocks(IR::Program& program) {
|
||||||
// Some blocks might be unreachable if a function call exists unconditionally
|
// Some blocks might be unreachable if a function call exists unconditionally
|
||||||
// If this happens the number of blocks and post order blocks will mismatch
|
// If this happens the number of blocks and post order blocks will mismatch
|
||||||
|
@ -23,7 +34,7 @@ void RemoveUnreachableBlocks(IR::Program& program) {
|
||||||
}
|
}
|
||||||
const auto begin{program.blocks.begin() + 1};
|
const auto begin{program.blocks.begin() + 1};
|
||||||
const auto end{program.blocks.end()};
|
const auto end{program.blocks.end()};
|
||||||
const auto pred{[](IR::Block* block) { return block->ImmediatePredecessors().empty(); }};
|
const auto pred{[](IR::Block* block) { return block->ImmPredecessors().empty(); }};
|
||||||
program.blocks.erase(std::remove_if(begin, end, pred), end);
|
program.blocks.erase(std::remove_if(begin, end, pred), end);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,8 +121,9 @@ void AddNVNStorageBuffers(IR::Program& program) {
|
||||||
IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool,
|
IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool,
|
||||||
Environment& env, Flow::CFG& cfg) {
|
Environment& env, Flow::CFG& cfg) {
|
||||||
IR::Program program;
|
IR::Program program;
|
||||||
program.blocks = VisitAST(inst_pool, block_pool, env, cfg);
|
program.syntax_list = BuildASL(inst_pool, block_pool, env, cfg);
|
||||||
program.post_order_blocks = PostOrder(program.blocks);
|
program.blocks = GenerateBlocks(program.syntax_list);
|
||||||
|
program.post_order_blocks = PostOrder(program.syntax_list.front());
|
||||||
program.stage = env.ShaderStage();
|
program.stage = env.ShaderStage();
|
||||||
program.local_memory_size = env.LocalMemorySize();
|
program.local_memory_size = env.LocalMemorySize();
|
||||||
switch (program.stage) {
|
switch (program.stage) {
|
||||||
|
@ -159,9 +171,7 @@ IR::Program MergeDualVertexPrograms(IR::Program& vertex_a, IR::Program& vertex_b
|
||||||
Optimization::VertexATransformPass(vertex_a);
|
Optimization::VertexATransformPass(vertex_a);
|
||||||
Optimization::VertexBTransformPass(vertex_b);
|
Optimization::VertexBTransformPass(vertex_b);
|
||||||
std::swap(result.blocks, vertex_a.blocks);
|
std::swap(result.blocks, vertex_a.blocks);
|
||||||
for (IR::Block* block : vertex_b.blocks) {
|
result.blocks.insert(result.blocks.end(), vertex_b.blocks.begin(), vertex_b.blocks.end());
|
||||||
result.blocks.push_back(block);
|
|
||||||
}
|
|
||||||
result.stage = Stage::VertexB;
|
result.stage = Stage::VertexB;
|
||||||
result.info = vertex_a.info;
|
result.info = vertex_a.info;
|
||||||
result.local_memory_size = std::max(vertex_a.local_memory_size, vertex_b.local_memory_size);
|
result.local_memory_size = std::max(vertex_a.local_memory_size, vertex_b.local_memory_size);
|
||||||
|
@ -173,7 +183,7 @@ IR::Program MergeDualVertexPrograms(IR::Program& vertex_a, IR::Program& vertex_b
|
||||||
Optimization::JoinTextureInfo(result.info, vertex_b.info);
|
Optimization::JoinTextureInfo(result.info, vertex_b.info);
|
||||||
Optimization::JoinStorageInfo(result.info, vertex_b.info);
|
Optimization::JoinStorageInfo(result.info, vertex_b.info);
|
||||||
Optimization::DualVertexJoinPass(result);
|
Optimization::DualVertexJoinPass(result);
|
||||||
result.post_order_blocks = PostOrder(result.blocks);
|
result.post_order_blocks = PostOrder(result.syntax_list.front());
|
||||||
Optimization::DeadCodeEliminationPass(result);
|
Optimization::DeadCodeEliminationPass(result);
|
||||||
Optimization::VerificationPass(result);
|
Optimization::VerificationPass(result);
|
||||||
Optimization::CollectShaderInfoPass(env_vertex_b, result);
|
Optimization::CollectShaderInfoPass(env_vertex_b, result);
|
||||||
|
|
|
@ -36,7 +36,6 @@ using Tree = boost::intrusive::list<Statement,
|
||||||
// Avoid linear complexity on splice, size is never called
|
// Avoid linear complexity on splice, size is never called
|
||||||
boost::intrusive::constant_time_size<false>>;
|
boost::intrusive::constant_time_size<false>>;
|
||||||
using Node = Tree::iterator;
|
using Node = Tree::iterator;
|
||||||
using ConstNode = Tree::const_iterator;
|
|
||||||
|
|
||||||
enum class StatementType {
|
enum class StatementType {
|
||||||
Code,
|
Code,
|
||||||
|
@ -91,7 +90,8 @@ struct IndirectBranchCond {};
|
||||||
#pragma warning(disable : 26495) // Always initialize a member variable, expected in Statement
|
#pragma warning(disable : 26495) // Always initialize a member variable, expected in Statement
|
||||||
#endif
|
#endif
|
||||||
struct Statement : ListBaseHook {
|
struct Statement : ListBaseHook {
|
||||||
Statement(IR::Block* code_, Statement* up_) : code{code_}, up{up_}, type{StatementType::Code} {}
|
Statement(const Flow::Block* block_, Statement* up_)
|
||||||
|
: block{block_}, up{up_}, type{StatementType::Code} {}
|
||||||
Statement(Goto, Statement* cond_, Node label_, Statement* up_)
|
Statement(Goto, Statement* cond_, Node label_, Statement* up_)
|
||||||
: label{label_}, cond{cond_}, up{up_}, type{StatementType::Goto} {}
|
: label{label_}, cond{cond_}, up{up_}, type{StatementType::Goto} {}
|
||||||
Statement(Label, u32 id_, Statement* up_) : id{id_}, up{up_}, type{StatementType::Label} {}
|
Statement(Label, u32 id_, Statement* up_) : id{id_}, up{up_}, type{StatementType::Label} {}
|
||||||
|
@ -125,7 +125,7 @@ struct Statement : ListBaseHook {
|
||||||
}
|
}
|
||||||
|
|
||||||
union {
|
union {
|
||||||
IR::Block* code;
|
const Flow::Block* block;
|
||||||
Node label;
|
Node label;
|
||||||
Tree children;
|
Tree children;
|
||||||
IR::Condition guest_cond;
|
IR::Condition guest_cond;
|
||||||
|
@ -171,8 +171,8 @@ std::string DumpTree(const Tree& tree, u32 indentation = 0) {
|
||||||
switch (stmt->type) {
|
switch (stmt->type) {
|
||||||
case StatementType::Code:
|
case StatementType::Code:
|
||||||
ret += fmt::format("{} Block {:04x} -> {:04x} (0x{:016x});\n", indent,
|
ret += fmt::format("{} Block {:04x} -> {:04x} (0x{:016x});\n", indent,
|
||||||
stmt->code->LocationBegin(), stmt->code->LocationEnd(),
|
stmt->block->begin, stmt->block->end,
|
||||||
reinterpret_cast<uintptr_t>(stmt->code));
|
reinterpret_cast<uintptr_t>(stmt->block));
|
||||||
break;
|
break;
|
||||||
case StatementType::Goto:
|
case StatementType::Goto:
|
||||||
ret += fmt::format("{} if ({}) goto L{};\n", indent, DumpExpr(stmt->cond),
|
ret += fmt::format("{} if ({}) goto L{};\n", indent, DumpExpr(stmt->cond),
|
||||||
|
@ -407,11 +407,7 @@ private:
|
||||||
}};
|
}};
|
||||||
root.push_front(make_reset_variable());
|
root.push_front(make_reset_variable());
|
||||||
root.insert(ip, make_reset_variable());
|
root.insert(ip, make_reset_variable());
|
||||||
|
root.insert(ip, *pool.Create(&block, &root_stmt));
|
||||||
const u32 begin_offset{block.begin.Offset()};
|
|
||||||
const u32 end_offset{block.end.Offset()};
|
|
||||||
IR::Block* const ir_block{block_pool.Create(inst_pool, begin_offset, end_offset)};
|
|
||||||
root.insert(ip, *pool.Create(ir_block, &root_stmt));
|
|
||||||
|
|
||||||
switch (block.end_class) {
|
switch (block.end_class) {
|
||||||
case Flow::EndClass::Branch: {
|
case Flow::EndClass::Branch: {
|
||||||
|
@ -620,13 +616,13 @@ private:
|
||||||
Statement root_stmt{FunctionTag{}};
|
Statement root_stmt{FunctionTag{}};
|
||||||
};
|
};
|
||||||
|
|
||||||
IR::Block* TryFindForwardBlock(const Statement& stmt) {
|
[[nodiscard]] Statement* TryFindForwardBlock(Statement& stmt) {
|
||||||
const Tree& tree{stmt.up->children};
|
Tree& tree{stmt.up->children};
|
||||||
const ConstNode end{tree.cend()};
|
const Node end{tree.end()};
|
||||||
ConstNode forward_node{std::next(Tree::s_iterator_to(stmt))};
|
Node forward_node{std::next(Tree::s_iterator_to(stmt))};
|
||||||
while (forward_node != end && !HasChildren(forward_node->type)) {
|
while (forward_node != end && !HasChildren(forward_node->type)) {
|
||||||
if (forward_node->type == StatementType::Code) {
|
if (forward_node->type == StatementType::Code) {
|
||||||
return forward_node->code;
|
return &*forward_node;
|
||||||
}
|
}
|
||||||
++forward_node;
|
++forward_node;
|
||||||
}
|
}
|
||||||
|
@ -654,21 +650,29 @@ class TranslatePass {
|
||||||
public:
|
public:
|
||||||
TranslatePass(ObjectPool<IR::Inst>& inst_pool_, ObjectPool<IR::Block>& block_pool_,
|
TranslatePass(ObjectPool<IR::Inst>& inst_pool_, ObjectPool<IR::Block>& block_pool_,
|
||||||
ObjectPool<Statement>& stmt_pool_, Environment& env_, Statement& root_stmt,
|
ObjectPool<Statement>& stmt_pool_, Environment& env_, Statement& root_stmt,
|
||||||
IR::BlockList& block_list_)
|
IR::AbstractSyntaxList& syntax_list_)
|
||||||
: stmt_pool{stmt_pool_}, inst_pool{inst_pool_}, block_pool{block_pool_}, env{env_},
|
: stmt_pool{stmt_pool_}, inst_pool{inst_pool_}, block_pool{block_pool_}, env{env_},
|
||||||
block_list{block_list_} {
|
syntax_list{syntax_list_} {
|
||||||
Visit(root_stmt, nullptr, nullptr);
|
Visit(root_stmt, nullptr, nullptr);
|
||||||
|
|
||||||
IR::Block& first_block{*block_list.front()};
|
IR::Block& first_block{*syntax_list.front().block};
|
||||||
IR::IREmitter ir{first_block, first_block.begin()};
|
IR::IREmitter ir{first_block, first_block.begin()};
|
||||||
ir.Prologue();
|
ir.Prologue();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void Visit(Statement& parent, IR::Block* continue_block, IR::Block* break_block) {
|
void Visit(Statement& parent, IR::Block* break_block, IR::Block* fallthrough_block) {
|
||||||
|
IR::Block* current_block{};
|
||||||
|
const auto ensure_block{[&] {
|
||||||
|
if (current_block) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
current_block = block_pool.Create(inst_pool);
|
||||||
|
auto& node{syntax_list.emplace_back()};
|
||||||
|
node.type = IR::AbstractSyntaxNode::Type::Block;
|
||||||
|
node.block = current_block;
|
||||||
|
}};
|
||||||
Tree& tree{parent.children};
|
Tree& tree{parent.children};
|
||||||
IR::Block* current_block{nullptr};
|
|
||||||
|
|
||||||
for (auto it = tree.begin(); it != tree.end(); ++it) {
|
for (auto it = tree.begin(); it != tree.end(); ++it) {
|
||||||
Statement& stmt{*it};
|
Statement& stmt{*it};
|
||||||
switch (stmt.type) {
|
switch (stmt.type) {
|
||||||
|
@ -676,124 +680,157 @@ private:
|
||||||
// Labels can be ignored
|
// Labels can be ignored
|
||||||
break;
|
break;
|
||||||
case StatementType::Code: {
|
case StatementType::Code: {
|
||||||
if (current_block && current_block != stmt.code) {
|
ensure_block();
|
||||||
IR::IREmitter{*current_block}.Branch(stmt.code);
|
Translate(env, current_block, stmt.block->begin.Offset(), stmt.block->end.Offset());
|
||||||
}
|
|
||||||
current_block = stmt.code;
|
|
||||||
Translate(env, stmt.code);
|
|
||||||
block_list.push_back(stmt.code);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case StatementType::SetVariable: {
|
case StatementType::SetVariable: {
|
||||||
if (!current_block) {
|
ensure_block();
|
||||||
current_block = MergeBlock(parent, stmt);
|
|
||||||
}
|
|
||||||
IR::IREmitter ir{*current_block};
|
IR::IREmitter ir{*current_block};
|
||||||
ir.SetGotoVariable(stmt.id, VisitExpr(ir, *stmt.op));
|
ir.SetGotoVariable(stmt.id, VisitExpr(ir, *stmt.op));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case StatementType::SetIndirectBranchVariable: {
|
case StatementType::SetIndirectBranchVariable: {
|
||||||
if (!current_block) {
|
ensure_block();
|
||||||
current_block = MergeBlock(parent, stmt);
|
|
||||||
}
|
|
||||||
IR::IREmitter ir{*current_block};
|
IR::IREmitter ir{*current_block};
|
||||||
IR::U32 address{ir.IAdd(ir.GetReg(stmt.branch_reg), ir.Imm32(stmt.branch_offset))};
|
IR::U32 address{ir.IAdd(ir.GetReg(stmt.branch_reg), ir.Imm32(stmt.branch_offset))};
|
||||||
ir.SetIndirectBranchVariable(address);
|
ir.SetIndirectBranchVariable(address);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case StatementType::If: {
|
case StatementType::If: {
|
||||||
if (!current_block) {
|
ensure_block();
|
||||||
current_block = block_pool.Create(inst_pool);
|
|
||||||
block_list.push_back(current_block);
|
|
||||||
}
|
|
||||||
IR::Block* const merge_block{MergeBlock(parent, stmt)};
|
IR::Block* const merge_block{MergeBlock(parent, stmt)};
|
||||||
|
|
||||||
// Visit children
|
|
||||||
const size_t first_block_index{block_list.size()};
|
|
||||||
Visit(stmt, merge_block, break_block);
|
|
||||||
|
|
||||||
// Implement if header block
|
// Implement if header block
|
||||||
IR::Block* const first_if_block{block_list.at(first_block_index)};
|
|
||||||
IR::IREmitter ir{*current_block};
|
IR::IREmitter ir{*current_block};
|
||||||
const IR::U1 cond{VisitExpr(ir, *stmt.cond)};
|
const IR::U1 cond{VisitExpr(ir, *stmt.cond)};
|
||||||
ir.SelectionMerge(merge_block);
|
ir.BranchConditionRef(cond);
|
||||||
ir.BranchConditional(cond, first_if_block, merge_block);
|
|
||||||
|
|
||||||
|
const size_t if_node_index{syntax_list.size()};
|
||||||
|
syntax_list.emplace_back();
|
||||||
|
|
||||||
|
// Visit children
|
||||||
|
const size_t then_block_index{syntax_list.size()};
|
||||||
|
Visit(stmt, break_block, merge_block);
|
||||||
|
|
||||||
|
IR::Block* const then_block{syntax_list.at(then_block_index).block};
|
||||||
|
current_block->AddBranch(then_block);
|
||||||
|
current_block->AddBranch(merge_block);
|
||||||
current_block = merge_block;
|
current_block = merge_block;
|
||||||
|
|
||||||
|
auto& if_node{syntax_list[if_node_index]};
|
||||||
|
if_node.type = IR::AbstractSyntaxNode::Type::If;
|
||||||
|
if_node.if_node.cond = cond;
|
||||||
|
if_node.if_node.body = then_block;
|
||||||
|
if_node.if_node.merge = merge_block;
|
||||||
|
|
||||||
|
auto& endif_node{syntax_list.emplace_back()};
|
||||||
|
endif_node.type = IR::AbstractSyntaxNode::Type::EndIf;
|
||||||
|
endif_node.end_if.merge = merge_block;
|
||||||
|
|
||||||
|
auto& merge{syntax_list.emplace_back()};
|
||||||
|
merge.type = IR::AbstractSyntaxNode::Type::Block;
|
||||||
|
merge.block = merge_block;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case StatementType::Loop: {
|
case StatementType::Loop: {
|
||||||
IR::Block* const loop_header_block{block_pool.Create(inst_pool)};
|
IR::Block* const loop_header_block{block_pool.Create(inst_pool)};
|
||||||
if (current_block) {
|
if (current_block) {
|
||||||
IR::IREmitter{*current_block}.Branch(loop_header_block);
|
current_block->AddBranch(loop_header_block);
|
||||||
}
|
}
|
||||||
block_list.push_back(loop_header_block);
|
auto& header_node{syntax_list.emplace_back()};
|
||||||
|
header_node.type = IR::AbstractSyntaxNode::Type::Block;
|
||||||
|
header_node.block = loop_header_block;
|
||||||
|
|
||||||
IR::Block* const new_continue_block{block_pool.Create(inst_pool)};
|
IR::Block* const continue_block{block_pool.Create(inst_pool)};
|
||||||
IR::Block* const merge_block{MergeBlock(parent, stmt)};
|
IR::Block* const merge_block{MergeBlock(parent, stmt)};
|
||||||
|
|
||||||
|
const size_t loop_node_index{syntax_list.size()};
|
||||||
|
syntax_list.emplace_back();
|
||||||
|
|
||||||
// Visit children
|
// Visit children
|
||||||
const size_t first_block_index{block_list.size()};
|
const size_t body_block_index{syntax_list.size()};
|
||||||
Visit(stmt, new_continue_block, merge_block);
|
Visit(stmt, merge_block, continue_block);
|
||||||
|
|
||||||
// The continue block is located at the end of the loop
|
// The continue block is located at the end of the loop
|
||||||
block_list.push_back(new_continue_block);
|
IR::IREmitter ir{*continue_block};
|
||||||
|
const IR::U1 cond{VisitExpr(ir, *stmt.cond)};
|
||||||
|
ir.BranchConditionRef(cond);
|
||||||
|
|
||||||
// Implement loop header block
|
IR::Block* const body_block{syntax_list.at(body_block_index).block};
|
||||||
IR::Block* const first_loop_block{block_list.at(first_block_index)};
|
loop_header_block->AddBranch(body_block);
|
||||||
IR::IREmitter ir{*loop_header_block};
|
|
||||||
ir.LoopMerge(merge_block, new_continue_block);
|
|
||||||
ir.Branch(first_loop_block);
|
|
||||||
|
|
||||||
// Implement continue block
|
continue_block->AddBranch(loop_header_block);
|
||||||
IR::IREmitter continue_ir{*new_continue_block};
|
continue_block->AddBranch(merge_block);
|
||||||
const IR::U1 continue_cond{VisitExpr(continue_ir, *stmt.cond)};
|
|
||||||
continue_ir.BranchConditional(continue_cond, ir.block, merge_block);
|
|
||||||
|
|
||||||
current_block = merge_block;
|
current_block = merge_block;
|
||||||
|
|
||||||
|
auto& loop{syntax_list[loop_node_index]};
|
||||||
|
loop.type = IR::AbstractSyntaxNode::Type::Loop;
|
||||||
|
loop.loop.body = body_block;
|
||||||
|
loop.loop.continue_block = continue_block;
|
||||||
|
loop.loop.merge = merge_block;
|
||||||
|
|
||||||
|
auto& continue_block_node{syntax_list.emplace_back()};
|
||||||
|
continue_block_node.type = IR::AbstractSyntaxNode::Type::Block;
|
||||||
|
continue_block_node.block = continue_block;
|
||||||
|
|
||||||
|
auto& repeat{syntax_list.emplace_back()};
|
||||||
|
repeat.type = IR::AbstractSyntaxNode::Type::Repeat;
|
||||||
|
repeat.repeat.cond = cond;
|
||||||
|
repeat.repeat.loop_header = loop_header_block;
|
||||||
|
repeat.repeat.merge = merge_block;
|
||||||
|
|
||||||
|
auto& merge{syntax_list.emplace_back()};
|
||||||
|
merge.type = IR::AbstractSyntaxNode::Type::Block;
|
||||||
|
merge.block = merge_block;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case StatementType::Break: {
|
case StatementType::Break: {
|
||||||
if (!current_block) {
|
ensure_block();
|
||||||
current_block = block_pool.Create(inst_pool);
|
|
||||||
block_list.push_back(current_block);
|
|
||||||
}
|
|
||||||
IR::Block* const skip_block{MergeBlock(parent, stmt)};
|
IR::Block* const skip_block{MergeBlock(parent, stmt)};
|
||||||
|
|
||||||
IR::IREmitter ir{*current_block};
|
IR::IREmitter ir{*current_block};
|
||||||
ir.BranchConditional(VisitExpr(ir, *stmt.cond), break_block, skip_block);
|
const IR::U1 cond{VisitExpr(ir, *stmt.cond)};
|
||||||
|
ir.BranchConditionRef(cond);
|
||||||
|
current_block->AddBranch(break_block);
|
||||||
|
current_block->AddBranch(skip_block);
|
||||||
current_block = skip_block;
|
current_block = skip_block;
|
||||||
|
|
||||||
|
auto& break_node{syntax_list.emplace_back()};
|
||||||
|
break_node.type = IR::AbstractSyntaxNode::Type::Break;
|
||||||
|
break_node.break_node.cond = cond;
|
||||||
|
break_node.break_node.merge = break_block;
|
||||||
|
break_node.break_node.skip = skip_block;
|
||||||
|
|
||||||
|
auto& merge{syntax_list.emplace_back()};
|
||||||
|
merge.type = IR::AbstractSyntaxNode::Type::Block;
|
||||||
|
merge.block = skip_block;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case StatementType::Return: {
|
case StatementType::Return: {
|
||||||
if (!current_block) {
|
ensure_block();
|
||||||
current_block = block_pool.Create(inst_pool);
|
IR::IREmitter{*current_block}.Epilogue();
|
||||||
block_list.push_back(current_block);
|
|
||||||
}
|
|
||||||
IR::IREmitter ir{*current_block};
|
|
||||||
ir.Epilogue();
|
|
||||||
ir.Return();
|
|
||||||
current_block = nullptr;
|
current_block = nullptr;
|
||||||
|
syntax_list.emplace_back().type = IR::AbstractSyntaxNode::Type::Return;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case StatementType::Kill: {
|
case StatementType::Kill: {
|
||||||
if (!current_block) {
|
ensure_block();
|
||||||
current_block = block_pool.Create(inst_pool);
|
|
||||||
block_list.push_back(current_block);
|
|
||||||
}
|
|
||||||
IR::Block* demote_block{MergeBlock(parent, stmt)};
|
IR::Block* demote_block{MergeBlock(parent, stmt)};
|
||||||
IR::IREmitter{*current_block}.DemoteToHelperInvocation(demote_block);
|
IR::IREmitter{*current_block}.DemoteToHelperInvocation();
|
||||||
|
current_block->AddBranch(demote_block);
|
||||||
current_block = demote_block;
|
current_block = demote_block;
|
||||||
|
|
||||||
|
auto& merge{syntax_list.emplace_back()};
|
||||||
|
merge.type = IR::AbstractSyntaxNode::Type::Block;
|
||||||
|
merge.block = demote_block;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case StatementType::Unreachable: {
|
case StatementType::Unreachable: {
|
||||||
if (!current_block) {
|
ensure_block();
|
||||||
current_block = block_pool.Create(inst_pool);
|
|
||||||
block_list.push_back(current_block);
|
|
||||||
}
|
|
||||||
IR::IREmitter{*current_block}.Unreachable();
|
|
||||||
current_block = nullptr;
|
current_block = nullptr;
|
||||||
|
syntax_list.emplace_back().type = IR::AbstractSyntaxNode::Type::Unreachable;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
@ -801,42 +838,42 @@ private:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (current_block) {
|
if (current_block) {
|
||||||
IR::IREmitter ir{*current_block};
|
if (fallthrough_block) {
|
||||||
if (continue_block) {
|
current_block->AddBranch(fallthrough_block);
|
||||||
ir.Branch(continue_block);
|
|
||||||
} else {
|
} else {
|
||||||
ir.Unreachable();
|
syntax_list.emplace_back().type = IR::AbstractSyntaxNode::Type::Unreachable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::Block* MergeBlock(Statement& parent, Statement& stmt) {
|
IR::Block* MergeBlock(Statement& parent, Statement& stmt) {
|
||||||
if (IR::Block* const block{TryFindForwardBlock(stmt)}) {
|
Statement* merge_stmt{TryFindForwardBlock(stmt)};
|
||||||
return block;
|
if (!merge_stmt) {
|
||||||
}
|
|
||||||
// Create a merge block we can visit later
|
// Create a merge block we can visit later
|
||||||
IR::Block* const block{block_pool.Create(inst_pool)};
|
merge_stmt = stmt_pool.Create(&dummy_flow_block, &parent);
|
||||||
Statement* const merge_stmt{stmt_pool.Create(block, &parent)};
|
|
||||||
parent.children.insert(std::next(Tree::s_iterator_to(stmt)), *merge_stmt);
|
parent.children.insert(std::next(Tree::s_iterator_to(stmt)), *merge_stmt);
|
||||||
return block;
|
}
|
||||||
|
return block_pool.Create(inst_pool);
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjectPool<Statement>& stmt_pool;
|
ObjectPool<Statement>& stmt_pool;
|
||||||
ObjectPool<IR::Inst>& inst_pool;
|
ObjectPool<IR::Inst>& inst_pool;
|
||||||
ObjectPool<IR::Block>& block_pool;
|
ObjectPool<IR::Block>& block_pool;
|
||||||
Environment& env;
|
Environment& env;
|
||||||
IR::BlockList& block_list;
|
IR::AbstractSyntaxList& syntax_list;
|
||||||
|
// TODO: Make this constexpr when std::vector is constexpr
|
||||||
|
const Flow::Block dummy_flow_block;
|
||||||
};
|
};
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
IR::BlockList VisitAST(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool,
|
IR::AbstractSyntaxList BuildASL(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool,
|
||||||
Environment& env, Flow::CFG& cfg) {
|
Environment& env, Flow::CFG& cfg) {
|
||||||
ObjectPool<Statement> stmt_pool{64};
|
ObjectPool<Statement> stmt_pool{64};
|
||||||
GotoPass goto_pass{cfg, inst_pool, block_pool, stmt_pool};
|
GotoPass goto_pass{cfg, inst_pool, block_pool, stmt_pool};
|
||||||
Statement& root{goto_pass.RootStatement()};
|
Statement& root{goto_pass.RootStatement()};
|
||||||
IR::BlockList block_list;
|
IR::AbstractSyntaxList syntax_list;
|
||||||
TranslatePass{inst_pool, block_pool, stmt_pool, env, root, block_list};
|
TranslatePass{inst_pool, block_pool, stmt_pool, env, root, syntax_list};
|
||||||
return block_list;
|
return syntax_list;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Shader::Maxwell
|
} // namespace Shader::Maxwell
|
||||||
|
|
|
@ -4,12 +4,8 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
#include <span>
|
|
||||||
|
|
||||||
#include <boost/intrusive/list.hpp>
|
|
||||||
|
|
||||||
#include "shader_recompiler/environment.h"
|
#include "shader_recompiler/environment.h"
|
||||||
|
#include "shader_recompiler/frontend/ir/abstract_syntax_list.h"
|
||||||
#include "shader_recompiler/frontend/ir/basic_block.h"
|
#include "shader_recompiler/frontend/ir/basic_block.h"
|
||||||
#include "shader_recompiler/frontend/ir/value.h"
|
#include "shader_recompiler/frontend/ir/value.h"
|
||||||
#include "shader_recompiler/frontend/maxwell/control_flow.h"
|
#include "shader_recompiler/frontend/maxwell/control_flow.h"
|
||||||
|
@ -17,7 +13,7 @@
|
||||||
|
|
||||||
namespace Shader::Maxwell {
|
namespace Shader::Maxwell {
|
||||||
|
|
||||||
[[nodiscard]] IR::BlockList VisitAST(ObjectPool<IR::Inst>& inst_pool,
|
[[nodiscard]] IR::AbstractSyntaxList BuildASL(ObjectPool<IR::Inst>& inst_pool,
|
||||||
ObjectPool<IR::Block>& block_pool, Environment& env,
|
ObjectPool<IR::Block>& block_pool, Environment& env,
|
||||||
Flow::CFG& cfg);
|
Flow::CFG& cfg);
|
||||||
|
|
||||||
|
|
|
@ -23,13 +23,12 @@ static void Invoke(TranslatorVisitor& visitor, Location pc, u64 insn) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Translate(Environment& env, IR::Block* block) {
|
void Translate(Environment& env, IR::Block* block, u32 location_begin, u32 location_end) {
|
||||||
if (block->IsVirtual()) {
|
if (location_begin == location_end) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
TranslatorVisitor visitor{env, *block};
|
TranslatorVisitor visitor{env, *block};
|
||||||
const Location pc_end{block->LocationEnd()};
|
for (Location pc = location_begin; pc != location_end; ++pc) {
|
||||||
for (Location pc = block->LocationBegin(); pc != pc_end; ++pc) {
|
|
||||||
const u64 insn{env.ReadInstruction(pc.Offset())};
|
const u64 insn{env.ReadInstruction(pc.Offset())};
|
||||||
const Opcode opcode{Decode(insn)};
|
const Opcode opcode{Decode(insn)};
|
||||||
switch (opcode) {
|
switch (opcode) {
|
||||||
|
|
|
@ -9,6 +9,6 @@
|
||||||
|
|
||||||
namespace Shader::Maxwell {
|
namespace Shader::Maxwell {
|
||||||
|
|
||||||
void Translate(Environment& env, IR::Block* block);
|
void Translate(Environment& env, IR::Block* block, u32 location_begin, u32 location_end);
|
||||||
|
|
||||||
} // namespace Shader::Maxwell
|
} // namespace Shader::Maxwell
|
||||||
|
|
|
@ -353,24 +353,6 @@ IR::Value EvalImmediates(const IR::Inst& inst, Func&& func, std::index_sequence<
|
||||||
return IR::Value{func(Arg<typename Traits::template ArgType<I>>(inst.Arg(I))...)};
|
return IR::Value{func(Arg<typename Traits::template ArgType<I>>(inst.Arg(I))...)};
|
||||||
}
|
}
|
||||||
|
|
||||||
void FoldBranchConditional(IR::Inst& inst) {
|
|
||||||
const IR::U1 cond{inst.Arg(0)};
|
|
||||||
if (cond.IsImmediate()) {
|
|
||||||
// TODO: Convert to Branch
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const IR::Inst* cond_inst{cond.InstRecursive()};
|
|
||||||
if (cond_inst->GetOpcode() == IR::Opcode::LogicalNot) {
|
|
||||||
const IR::Value true_label{inst.Arg(1)};
|
|
||||||
const IR::Value false_label{inst.Arg(2)};
|
|
||||||
// Remove negation on the conditional (take the parameter out of LogicalNot) and swap
|
|
||||||
// the branches
|
|
||||||
inst.SetArg(0, cond_inst->Arg(0));
|
|
||||||
inst.SetArg(1, false_label);
|
|
||||||
inst.SetArg(2, true_label);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<IR::Value> FoldCompositeExtractImpl(IR::Value inst_value, IR::Opcode insert,
|
std::optional<IR::Value> FoldCompositeExtractImpl(IR::Value inst_value, IR::Opcode insert,
|
||||||
IR::Opcode construct, u32 first_index) {
|
IR::Opcode construct, u32 first_index) {
|
||||||
IR::Inst* const inst{inst_value.InstRecursive()};
|
IR::Inst* const inst{inst_value.InstRecursive()};
|
||||||
|
@ -581,8 +563,6 @@ void ConstantPropagation(IR::Block& block, IR::Inst& inst) {
|
||||||
return (base & ~(~(~0u << bits) << offset)) | (insert << offset);
|
return (base & ~(~(~0u << bits) << offset)) | (insert << offset);
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
case IR::Opcode::BranchConditional:
|
|
||||||
return FoldBranchConditional(inst);
|
|
||||||
case IR::Opcode::CompositeExtractF32x2:
|
case IR::Opcode::CompositeExtractF32x2:
|
||||||
return FoldCompositeExtract(inst, IR::Opcode::CompositeConstructF32x2,
|
return FoldCompositeExtract(inst, IR::Opcode::CompositeConstructF32x2,
|
||||||
IR::Opcode::CompositeInsertF32x2);
|
IR::Opcode::CompositeInsertF32x2);
|
||||||
|
|
|
@ -13,60 +13,16 @@
|
||||||
|
|
||||||
namespace Shader::Optimization {
|
namespace Shader::Optimization {
|
||||||
|
|
||||||
void VertexATransformPass(IR::Program& program) {
|
void VertexATransformPass(IR::Program&) {
|
||||||
bool replaced_join{};
|
throw NotImplementedException("VertexA pass");
|
||||||
bool eliminated_epilogue{};
|
|
||||||
for (IR::Block* const block : program.post_order_blocks) {
|
|
||||||
for (IR::Inst& inst : block->Instructions()) {
|
|
||||||
switch (inst.GetOpcode()) {
|
|
||||||
case IR::Opcode::Return:
|
|
||||||
inst.ReplaceOpcode(IR::Opcode::Join);
|
|
||||||
replaced_join = true;
|
|
||||||
break;
|
|
||||||
case IR::Opcode::Epilogue:
|
|
||||||
inst.Invalidate();
|
|
||||||
eliminated_epilogue = true;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (replaced_join && eliminated_epilogue) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VertexBTransformPass(IR::Program& program) {
|
void VertexBTransformPass(IR::Program&) {
|
||||||
for (IR::Block* const block : program.blocks) {
|
throw NotImplementedException("VertexA pass");
|
||||||
for (IR::Inst& inst : block->Instructions()) {
|
|
||||||
if (inst.GetOpcode() == IR::Opcode::Prologue) {
|
|
||||||
return inst.Invalidate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DualVertexJoinPass(IR::Program& program) {
|
void DualVertexJoinPass(IR::Program&) {
|
||||||
const auto& blocks = program.blocks;
|
throw NotImplementedException("VertexA pass");
|
||||||
const s64 sub_size = static_cast<s64>(blocks.size()) - 1;
|
|
||||||
if (sub_size < 1) {
|
|
||||||
throw LogicError("Dual Vertex Join pass failed, expected atleast 2 blocks");
|
|
||||||
}
|
|
||||||
for (s64 index = 0; index < sub_size; ++index) {
|
|
||||||
IR::Block* const current_block{blocks[index]};
|
|
||||||
IR::Block* const next_block{blocks[index + 1]};
|
|
||||||
for (IR::Inst& inst : current_block->Instructions()) {
|
|
||||||
if (inst.GetOpcode() == IR::Opcode::Join) {
|
|
||||||
IR::IREmitter ir{*current_block, IR::Block::InstructionList::s_iterator_to(inst)};
|
|
||||||
ir.Branch(next_block);
|
|
||||||
inst.Invalidate();
|
|
||||||
// Only 1 join should exist
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw LogicError("Dual Vertex Join pass failed, no join present");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Shader::Optimization
|
} // namespace Shader::Optimization
|
||||||
|
|
|
@ -12,7 +12,6 @@ namespace Shader::Optimization {
|
||||||
|
|
||||||
void IdentityRemovalPass(IR::Program& program) {
|
void IdentityRemovalPass(IR::Program& program) {
|
||||||
std::vector<IR::Inst*> to_invalidate;
|
std::vector<IR::Inst*> to_invalidate;
|
||||||
|
|
||||||
for (IR::Block* const block : program.blocks) {
|
for (IR::Block* const block : program.blocks) {
|
||||||
for (auto inst = block->begin(); inst != block->end();) {
|
for (auto inst = block->begin(); inst != block->end();) {
|
||||||
const size_t num_args{inst->NumArgs()};
|
const size_t num_args{inst->NumArgs()};
|
||||||
|
|
|
@ -202,7 +202,7 @@ public:
|
||||||
|
|
||||||
incomplete_phis[block].insert_or_assign(variable, phi);
|
incomplete_phis[block].insert_or_assign(variable, phi);
|
||||||
stack.back().result = IR::Value{&*phi};
|
stack.back().result = IR::Value{&*phi};
|
||||||
} else if (const std::span imm_preds{block->ImmediatePredecessors()};
|
} else if (const std::span imm_preds = block->ImmPredecessors();
|
||||||
imm_preds.size() == 1) {
|
imm_preds.size() == 1) {
|
||||||
// Optimize the common case of one predecessor: no phi needed
|
// Optimize the common case of one predecessor: no phi needed
|
||||||
stack.back().pc = Status::SetValue;
|
stack.back().pc = Status::SetValue;
|
||||||
|
@ -257,7 +257,7 @@ public:
|
||||||
private:
|
private:
|
||||||
template <typename Type>
|
template <typename Type>
|
||||||
IR::Value AddPhiOperands(Type variable, IR::Inst& phi, IR::Block* block) {
|
IR::Value AddPhiOperands(Type variable, IR::Inst& phi, IR::Block* block) {
|
||||||
for (IR::Block* const imm_pred : block->ImmediatePredecessors()) {
|
for (IR::Block* const imm_pred : block->ImmPredecessors()) {
|
||||||
phi.AddPhiOperand(imm_pred, ReadVariable(variable, imm_pred));
|
phi.AddPhiOperand(imm_pred, ReadVariable(variable, imm_pred));
|
||||||
}
|
}
|
||||||
return TryRemoveTrivialPhi(phi, block, UndefOpcode(variable));
|
return TryRemoveTrivialPhi(phi, block, UndefOpcode(variable));
|
||||||
|
|
Loading…
Reference in a new issue