shader: Implement BRX

This commit is contained in:
FernandoS27 2021-03-27 22:30:24 +01:00 committed by ameerj
parent 39a379632e
commit 34aba9627a
21 changed files with 437 additions and 48 deletions

View file

@ -17,6 +17,7 @@
#include "shader_recompiler/environment.h"
#include "shader_recompiler/frontend/ir/basic_block.h"
#include "shader_recompiler/frontend/ir/ir_emitter.h"
#include "shader_recompiler/frontend/maxwell/decode.h"
#include "shader_recompiler/frontend/maxwell/structured_control_flow.h"
#include "shader_recompiler/frontend/maxwell/translate/translate.h"
#include "shader_recompiler/object_pool.h"
@ -46,12 +47,15 @@ enum class StatementType {
Break,
Return,
Kill,
Unreachable,
Function,
Identity,
Not,
Or,
SetVariable,
SetIndirectBranchVariable,
Variable,
IndirectBranchCond,
};
bool HasChildren(StatementType type) {
@ -72,12 +76,15 @@ struct Loop {};
struct Break {};
struct Return {};
struct Kill {};
struct Unreachable {};
struct FunctionTag {};
struct Identity {};
struct Not {};
struct Or {};
struct SetVariable {};
struct SetIndirectBranchVariable {};
struct Variable {};
struct IndirectBranchCond {};
#ifdef _MSC_VER
#pragma warning(push)
@ -96,6 +103,7 @@ struct Statement : ListBaseHook {
: cond{cond_}, up{up_}, type{StatementType::Break} {}
Statement(Return) : type{StatementType::Return} {}
Statement(Kill) : type{StatementType::Kill} {}
Statement(Unreachable) : type{StatementType::Unreachable} {}
Statement(FunctionTag) : children{}, type{StatementType::Function} {}
Statement(Identity, IR::Condition cond_) : guest_cond{cond_}, type{StatementType::Identity} {}
Statement(Not, Statement* op_) : op{op_}, type{StatementType::Not} {}
@ -103,7 +111,12 @@ struct Statement : ListBaseHook {
: op_a{op_a_}, op_b{op_b_}, type{StatementType::Or} {}
Statement(SetVariable, u32 id_, Statement* op_, Statement* up_)
: op{op_}, id{id_}, up{up_}, type{StatementType::SetVariable} {}
Statement(SetIndirectBranchVariable, IR::Reg branch_reg_, s32 branch_offset_)
: branch_offset{branch_offset_},
branch_reg{branch_reg_}, type{StatementType::SetIndirectBranchVariable} {}
Statement(Variable, u32 id_) : id{id_}, type{StatementType::Variable} {}
Statement(IndirectBranchCond, u32 location_)
: location{location_}, type{StatementType::IndirectBranchCond} {}
~Statement() {
if (HasChildren(type)) {
@ -118,11 +131,14 @@ struct Statement : ListBaseHook {
IR::Condition guest_cond;
Statement* op;
Statement* op_a;
u32 location;
s32 branch_offset;
};
union {
Statement* cond;
Statement* op_b;
u32 id;
IR::Reg branch_reg;
};
Statement* up{};
StatementType type;
@ -141,6 +157,8 @@ std::string DumpExpr(const Statement* stmt) {
return fmt::format("{} || {}", DumpExpr(stmt->op_a), DumpExpr(stmt->op_b));
case StatementType::Variable:
return fmt::format("goto_L{}", stmt->id);
case StatementType::IndirectBranchCond:
return fmt::format("(indirect_branch == {:x})", stmt->location);
default:
return "<invalid type>";
}
@ -182,14 +200,22 @@ std::string DumpTree(const Tree& tree, u32 indentation = 0) {
case StatementType::Kill:
ret += fmt::format("{} kill;\n", indent);
break;
case StatementType::Unreachable:
ret += fmt::format("{} unreachable;\n", indent);
break;
case StatementType::SetVariable:
ret += fmt::format("{} goto_L{} = {};\n", indent, stmt->id, DumpExpr(stmt->op));
break;
case StatementType::SetIndirectBranchVariable:
ret += fmt::format("{} indirect_branch = {} + {};\n", indent, stmt->branch_reg,
stmt->branch_offset);
break;
case StatementType::Function:
case StatementType::Identity:
case StatementType::Not:
case StatementType::Or:
case StatementType::Variable:
case StatementType::IndirectBranchCond:
throw LogicError("Statement can't be printed");
}
}
@ -417,6 +443,17 @@ private:
}
break;
}
case Flow::EndClass::IndirectBranch:
root.insert(ip, *pool.Create(SetIndirectBranchVariable{}, block.branch_reg,
block.branch_offset));
for (Flow::Block* const branch : block.indirect_branches) {
const Node indirect_label{local_labels.at(branch)};
Statement* cond{pool.Create(IndirectBranchCond{}, branch->begin.Offset())};
Statement* goto_stmt{pool.Create(Goto{}, cond, indirect_label, &root_stmt)};
gotos.push_back(root.insert(ip, *goto_stmt));
}
root.insert(ip, *pool.Create(Unreachable{}));
break;
case Flow::EndClass::Call: {
Flow::Function& call{cfg.Functions()[block.function_call]};
const Node call_return_label{local_labels.at(block.return_block)};
@ -623,6 +660,8 @@ IR::Block* TryFindForwardBlock(const Statement& stmt) {
return ir.LogicalOr(VisitExpr(ir, *stmt.op_a), VisitExpr(ir, *stmt.op_b));
case StatementType::Variable:
return ir.GetGotoVariable(stmt.id);
case StatementType::IndirectBranchCond:
return ir.IEqual(ir.GetIndirectBranchVariable(), ir.Imm32(stmt.location));
default:
throw NotImplementedException("Statement type {}", stmt.type);
}
@ -670,6 +709,15 @@ private:
ir.SetGotoVariable(stmt.id, VisitExpr(ir, *stmt.op));
break;
}
case StatementType::SetIndirectBranchVariable: {
if (!current_block) {
current_block = MergeBlock(parent, stmt);
}
IR::IREmitter ir{*current_block};
IR::U32 address{ir.IAdd(ir.GetReg(stmt.branch_reg), ir.Imm32(stmt.branch_offset))};
ir.SetIndirectBranchVariable(address);
break;
}
case StatementType::If: {
if (!current_block) {
current_block = block_pool.Create(inst_pool);
@ -756,6 +804,15 @@ private:
current_block = demote_block;
break;
}
case StatementType::Unreachable: {
if (!current_block) {
current_block = block_pool.Create(inst_pool);
block_list.push_back(current_block);
}
IR::IREmitter{*current_block}.Unreachable();
current_block = nullptr;
break;
}
default:
throw NotImplementedException("Statement type {}", stmt.type);
}