mirror of
https://git.suyu.dev/suyu/suyu.git
synced 2024-11-25 22:57:22 +00:00
gl_shader_decompiler: Implement AST decompiling
This commit is contained in:
parent
6fdd501113
commit
38fc995f6c
11 changed files with 358 additions and 63 deletions
|
@ -20,6 +20,7 @@
|
||||||
#include "video_core/renderer_opengl/gl_rasterizer.h"
|
#include "video_core/renderer_opengl/gl_rasterizer.h"
|
||||||
#include "video_core/renderer_opengl/gl_shader_decompiler.h"
|
#include "video_core/renderer_opengl/gl_shader_decompiler.h"
|
||||||
#include "video_core/shader/node.h"
|
#include "video_core/shader/node.h"
|
||||||
|
#include "video_core/shader/ast.h"
|
||||||
#include "video_core/shader/shader_ir.h"
|
#include "video_core/shader/shader_ir.h"
|
||||||
|
|
||||||
namespace OpenGL::GLShader {
|
namespace OpenGL::GLShader {
|
||||||
|
@ -334,43 +335,26 @@ constexpr bool IsVertexShader(ProgramType stage) {
|
||||||
return stage == ProgramType::VertexA || stage == ProgramType::VertexB;
|
return stage == ProgramType::VertexA || stage == ProgramType::VertexB;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ASTDecompiler;
|
||||||
|
class ExprDecompiler;
|
||||||
|
|
||||||
class GLSLDecompiler final {
|
class GLSLDecompiler final {
|
||||||
public:
|
public:
|
||||||
explicit GLSLDecompiler(const Device& device, const ShaderIR& ir, ProgramType stage,
|
explicit GLSLDecompiler(const Device& device, const ShaderIR& ir, ProgramType stage,
|
||||||
std::string suffix)
|
std::string suffix)
|
||||||
: device{device}, ir{ir}, stage{stage}, suffix{suffix}, header{ir.GetHeader()} {}
|
: device{device}, ir{ir}, stage{stage}, suffix{suffix}, header{ir.GetHeader()} {}
|
||||||
|
|
||||||
void Decompile() {
|
void DecompileBranchMode() {
|
||||||
DeclareVertex();
|
|
||||||
DeclareGeometry();
|
|
||||||
DeclareRegisters();
|
|
||||||
DeclarePredicates();
|
|
||||||
DeclareLocalMemory();
|
|
||||||
DeclareSharedMemory();
|
|
||||||
DeclareInternalFlags();
|
|
||||||
DeclareInputAttributes();
|
|
||||||
DeclareOutputAttributes();
|
|
||||||
DeclareConstantBuffers();
|
|
||||||
DeclareGlobalMemory();
|
|
||||||
DeclareSamplers();
|
|
||||||
DeclarePhysicalAttributeReader();
|
|
||||||
DeclareImages();
|
|
||||||
|
|
||||||
code.AddLine("void execute_{}() {{", suffix);
|
|
||||||
++code.scope;
|
|
||||||
|
|
||||||
// VM's program counter
|
// VM's program counter
|
||||||
const auto first_address = ir.GetBasicBlocks().begin()->first;
|
const auto first_address = ir.GetBasicBlocks().begin()->first;
|
||||||
code.AddLine("uint jmp_to = {}U;", first_address);
|
code.AddLine("uint jmp_to = {}U;", first_address);
|
||||||
|
|
||||||
// TODO(Subv): Figure out the actual depth of the flow stack, for now it seems
|
// TODO(Subv): Figure out the actual depth of the flow stack, for now it seems
|
||||||
// unlikely that shaders will use 20 nested SSYs and PBKs.
|
// unlikely that shaders will use 20 nested SSYs and PBKs.
|
||||||
if (!ir.IsFlowStackDisabled()) {
|
|
||||||
constexpr u32 FLOW_STACK_SIZE = 20;
|
constexpr u32 FLOW_STACK_SIZE = 20;
|
||||||
for (const auto stack : std::array{MetaStackClass::Ssy, MetaStackClass::Pbk}) {
|
for (const auto stack : std::array{MetaStackClass::Ssy, MetaStackClass::Pbk}) {
|
||||||
code.AddLine("uint {}[{}];", FlowStackName(stack), FLOW_STACK_SIZE);
|
code.AddLine("uint {}[{}];", FlowStackName(stack), FLOW_STACK_SIZE);
|
||||||
code.AddLine("uint {} = 0U;", FlowStackTopName(stack));
|
code.AddLine("uint {} = 0u;", FlowStackTopName(stack));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
code.AddLine("while (true) {{");
|
code.AddLine("while (true) {{");
|
||||||
|
@ -392,10 +376,37 @@ public:
|
||||||
code.AddLine("default: return;");
|
code.AddLine("default: return;");
|
||||||
code.AddLine("}}");
|
code.AddLine("}}");
|
||||||
|
|
||||||
for (std::size_t i = 0; i < 2; ++i) {
|
|
||||||
--code.scope;
|
--code.scope;
|
||||||
code.AddLine("}}");
|
code.AddLine("}}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DecompileAST();
|
||||||
|
|
||||||
|
void Decompile() {
|
||||||
|
DeclareVertex();
|
||||||
|
DeclareGeometry();
|
||||||
|
DeclareRegisters();
|
||||||
|
DeclarePredicates();
|
||||||
|
DeclareLocalMemory();
|
||||||
|
DeclareInternalFlags();
|
||||||
|
DeclareInputAttributes();
|
||||||
|
DeclareOutputAttributes();
|
||||||
|
DeclareConstantBuffers();
|
||||||
|
DeclareGlobalMemory();
|
||||||
|
DeclareSamplers();
|
||||||
|
DeclarePhysicalAttributeReader();
|
||||||
|
|
||||||
|
code.AddLine("void execute_{}() {{", suffix);
|
||||||
|
++code.scope;
|
||||||
|
|
||||||
|
if (ir.IsDecompiled()) {
|
||||||
|
DecompileAST();
|
||||||
|
} else {
|
||||||
|
DecompileBranchMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
--code.scope;
|
||||||
|
code.AddLine("}}");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GetResult() {
|
std::string GetResult() {
|
||||||
|
@ -424,6 +435,9 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
friend class ASTDecompiler;
|
||||||
|
friend class ExprDecompiler;
|
||||||
|
|
||||||
void DeclareVertex() {
|
void DeclareVertex() {
|
||||||
if (!IsVertexShader(stage))
|
if (!IsVertexShader(stage))
|
||||||
return;
|
return;
|
||||||
|
@ -1821,7 +1835,7 @@ private:
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
Expression Exit(Operation operation) {
|
Expression WriteExit() {
|
||||||
if (stage != ProgramType::Fragment) {
|
if (stage != ProgramType::Fragment) {
|
||||||
code.AddLine("return;");
|
code.AddLine("return;");
|
||||||
return {};
|
return {};
|
||||||
|
@ -1861,6 +1875,10 @@ private:
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Expression Exit(Operation operation) {
|
||||||
|
return WriteExit();
|
||||||
|
}
|
||||||
|
|
||||||
Expression Discard(Operation operation) {
|
Expression Discard(Operation operation) {
|
||||||
// Enclose "discard" in a conditional, so that GLSL compilation does not complain
|
// Enclose "discard" in a conditional, so that GLSL compilation does not complain
|
||||||
// about unexecuted instructions that may follow this.
|
// about unexecuted instructions that may follow this.
|
||||||
|
@ -2253,6 +2271,201 @@ private:
|
||||||
ShaderWriter code;
|
ShaderWriter code;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const std::string flow_var = "flow_var_";
|
||||||
|
|
||||||
|
class ExprDecompiler {
|
||||||
|
public:
|
||||||
|
ExprDecompiler(GLSLDecompiler& decomp) : decomp{decomp} {}
|
||||||
|
|
||||||
|
void operator()(VideoCommon::Shader::ExprAnd& expr) {
|
||||||
|
inner += "( ";
|
||||||
|
std::visit(*this, *expr.operand1);
|
||||||
|
inner += " && ";
|
||||||
|
std::visit(*this, *expr.operand2);
|
||||||
|
inner += ')';
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(VideoCommon::Shader::ExprOr& expr) {
|
||||||
|
inner += "( ";
|
||||||
|
std::visit(*this, *expr.operand1);
|
||||||
|
inner += " || ";
|
||||||
|
std::visit(*this, *expr.operand2);
|
||||||
|
inner += ')';
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(VideoCommon::Shader::ExprNot& expr) {
|
||||||
|
inner += '!';
|
||||||
|
std::visit(*this, *expr.operand1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(VideoCommon::Shader::ExprPredicate& expr) {
|
||||||
|
auto pred = static_cast<Tegra::Shader::Pred>(expr.predicate);
|
||||||
|
inner += decomp.GetPredicate(pred);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(VideoCommon::Shader::ExprCondCode& expr) {
|
||||||
|
Node cc = decomp.ir.GetConditionCode(expr.cc);
|
||||||
|
std::string target;
|
||||||
|
|
||||||
|
if (const auto pred = std::get_if<PredicateNode>(&*cc)) {
|
||||||
|
const auto index = pred->GetIndex();
|
||||||
|
switch (index) {
|
||||||
|
case Tegra::Shader::Pred::NeverExecute:
|
||||||
|
target = "false";
|
||||||
|
case Tegra::Shader::Pred::UnusedIndex:
|
||||||
|
target = "true";
|
||||||
|
default:
|
||||||
|
target = decomp.GetPredicate(index);
|
||||||
|
}
|
||||||
|
} else if (const auto flag = std::get_if<InternalFlagNode>(&*cc)) {
|
||||||
|
target = decomp.GetInternalFlag(flag->GetFlag());
|
||||||
|
}
|
||||||
|
inner += target;
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(VideoCommon::Shader::ExprVar& expr) {
|
||||||
|
inner += flow_var + std::to_string(expr.var_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(VideoCommon::Shader::ExprBoolean& expr) {
|
||||||
|
inner += expr.value ? "true" : "false";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string& GetResult() {
|
||||||
|
return inner;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string inner{};
|
||||||
|
GLSLDecompiler& decomp;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ASTDecompiler {
|
||||||
|
public:
|
||||||
|
ASTDecompiler(GLSLDecompiler& decomp) : decomp{decomp} {}
|
||||||
|
|
||||||
|
void operator()(VideoCommon::Shader::ASTProgram& ast) {
|
||||||
|
ASTNode current = ast.nodes.GetFirst();
|
||||||
|
while (current) {
|
||||||
|
Visit(current);
|
||||||
|
current = current->GetNext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(VideoCommon::Shader::ASTIfThen& ast) {
|
||||||
|
ExprDecompiler expr_parser{decomp};
|
||||||
|
std::visit(expr_parser, *ast.condition);
|
||||||
|
decomp.code.AddLine("if ({}) {{", expr_parser.GetResult());
|
||||||
|
decomp.code.scope++;
|
||||||
|
ASTNode current = ast.nodes.GetFirst();
|
||||||
|
while (current) {
|
||||||
|
Visit(current);
|
||||||
|
current = current->GetNext();
|
||||||
|
}
|
||||||
|
decomp.code.scope--;
|
||||||
|
decomp.code.AddLine("}}");
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(VideoCommon::Shader::ASTIfElse& ast) {
|
||||||
|
decomp.code.AddLine("else {{");
|
||||||
|
decomp.code.scope++;
|
||||||
|
ASTNode current = ast.nodes.GetFirst();
|
||||||
|
while (current) {
|
||||||
|
Visit(current);
|
||||||
|
current = current->GetNext();
|
||||||
|
}
|
||||||
|
decomp.code.scope--;
|
||||||
|
decomp.code.AddLine("}}");
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(VideoCommon::Shader::ASTBlockEncoded& ast) {
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(VideoCommon::Shader::ASTBlockDecoded& ast) {
|
||||||
|
decomp.VisitBlock(ast.nodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(VideoCommon::Shader::ASTVarSet& ast) {
|
||||||
|
ExprDecompiler expr_parser{decomp};
|
||||||
|
std::visit(expr_parser, *ast.condition);
|
||||||
|
decomp.code.AddLine("{}{} = {};", flow_var, ast.index, expr_parser.GetResult());
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(VideoCommon::Shader::ASTLabel& ast) {
|
||||||
|
decomp.code.AddLine("// Label_{}:", ast.index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(VideoCommon::Shader::ASTGoto& ast) {
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(VideoCommon::Shader::ASTDoWhile& ast) {
|
||||||
|
ExprDecompiler expr_parser{decomp};
|
||||||
|
std::visit(expr_parser, *ast.condition);
|
||||||
|
decomp.code.AddLine("do {{");
|
||||||
|
decomp.code.scope++;
|
||||||
|
ASTNode current = ast.nodes.GetFirst();
|
||||||
|
while (current) {
|
||||||
|
Visit(current);
|
||||||
|
current = current->GetNext();
|
||||||
|
}
|
||||||
|
decomp.code.scope--;
|
||||||
|
decomp.code.AddLine("}} while({});", expr_parser.GetResult());
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(VideoCommon::Shader::ASTReturn& ast) {
|
||||||
|
bool is_true = VideoCommon::Shader::ExprIsTrue(ast.condition);
|
||||||
|
if (!is_true) {
|
||||||
|
ExprDecompiler expr_parser{decomp};
|
||||||
|
std::visit(expr_parser, *ast.condition);
|
||||||
|
decomp.code.AddLine("if ({}) {{", expr_parser.GetResult());
|
||||||
|
decomp.code.scope++;
|
||||||
|
}
|
||||||
|
if (ast.kills) {
|
||||||
|
decomp.code.AddLine("discard;");
|
||||||
|
} else {
|
||||||
|
decomp.WriteExit();
|
||||||
|
}
|
||||||
|
if (!is_true) {
|
||||||
|
decomp.code.scope--;
|
||||||
|
decomp.code.AddLine("}}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(VideoCommon::Shader::ASTBreak& ast) {
|
||||||
|
bool is_true = VideoCommon::Shader::ExprIsTrue(ast.condition);
|
||||||
|
if (!is_true) {
|
||||||
|
ExprDecompiler expr_parser{decomp};
|
||||||
|
std::visit(expr_parser, *ast.condition);
|
||||||
|
decomp.code.AddLine("if ({}) {{", expr_parser.GetResult());
|
||||||
|
decomp.code.scope++;
|
||||||
|
}
|
||||||
|
decomp.code.AddLine("break;");
|
||||||
|
if (!is_true) {
|
||||||
|
decomp.code.scope--;
|
||||||
|
decomp.code.AddLine("}}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Visit(VideoCommon::Shader::ASTNode& node) {
|
||||||
|
std::visit(*this, *node->GetInnerData());
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
GLSLDecompiler& decomp;
|
||||||
|
};
|
||||||
|
|
||||||
|
void GLSLDecompiler::DecompileAST() {
|
||||||
|
u32 num_flow_variables = ir.GetASTNumVariables();
|
||||||
|
for (u32 i = 0; i < num_flow_variables; i++) {
|
||||||
|
code.AddLine("bool {}{} = false;", flow_var, i);
|
||||||
|
}
|
||||||
|
ASTDecompiler decompiler{*this};
|
||||||
|
VideoCommon::Shader::ASTNode program = ir.GetASTProgram();
|
||||||
|
decompiler.Visit(program);
|
||||||
|
}
|
||||||
|
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
std::string GetCommonDeclarations() {
|
std::string GetCommonDeclarations() {
|
||||||
|
|
|
@ -372,13 +372,13 @@ ASTManager::~ASTManager() {
|
||||||
void ASTManager::Init() {
|
void ASTManager::Init() {
|
||||||
main_node = ASTBase::Make<ASTProgram>(ASTNode{});
|
main_node = ASTBase::Make<ASTProgram>(ASTNode{});
|
||||||
program = std::get_if<ASTProgram>(main_node->GetInnerData());
|
program = std::get_if<ASTProgram>(main_node->GetInnerData());
|
||||||
true_condition = MakeExpr<ExprBoolean>(true);
|
false_condition = MakeExpr<ExprBoolean>(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
ASTManager::ASTManager(ASTManager&& other)
|
ASTManager::ASTManager(ASTManager&& other)
|
||||||
: labels_map(std::move(other.labels_map)), labels_count{other.labels_count},
|
: labels_map(std::move(other.labels_map)), labels_count{other.labels_count},
|
||||||
gotos(std::move(other.gotos)), labels(std::move(other.labels)), variables{other.variables},
|
gotos(std::move(other.gotos)), labels(std::move(other.labels)), variables{other.variables},
|
||||||
program{other.program}, main_node{other.main_node}, true_condition{other.true_condition} {
|
program{other.program}, main_node{other.main_node}, false_condition{other.false_condition} {
|
||||||
other.main_node.reset();
|
other.main_node.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -390,7 +390,7 @@ ASTManager& ASTManager::operator=(ASTManager&& other) {
|
||||||
variables = other.variables;
|
variables = other.variables;
|
||||||
program = other.program;
|
program = other.program;
|
||||||
main_node = other.main_node;
|
main_node = other.main_node;
|
||||||
true_condition = other.true_condition;
|
false_condition = other.false_condition;
|
||||||
|
|
||||||
other.main_node.reset();
|
other.main_node.reset();
|
||||||
return *this;
|
return *this;
|
||||||
|
@ -594,7 +594,7 @@ void ASTManager::MoveOutward(ASTNode goto_node) {
|
||||||
u32 var_index = NewVariable();
|
u32 var_index = NewVariable();
|
||||||
Expr var_condition = MakeExpr<ExprVar>(var_index);
|
Expr var_condition = MakeExpr<ExprVar>(var_index);
|
||||||
ASTNode var_node = ASTBase::Make<ASTVarSet>(parent, var_index, condition);
|
ASTNode var_node = ASTBase::Make<ASTVarSet>(parent, var_index, condition);
|
||||||
ASTNode var_node_init = ASTBase::Make<ASTVarSet>(parent, var_index, true_condition);
|
ASTNode var_node_init = ASTBase::Make<ASTVarSet>(parent, var_index, false_condition);
|
||||||
zipper2.InsertBefore(var_node_init, parent);
|
zipper2.InsertBefore(var_node_init, parent);
|
||||||
zipper.InsertAfter(var_node, prev);
|
zipper.InsertAfter(var_node, prev);
|
||||||
goto_node->SetGotoCondition(var_condition);
|
goto_node->SetGotoCondition(var_condition);
|
||||||
|
@ -605,7 +605,7 @@ void ASTManager::MoveOutward(ASTNode goto_node) {
|
||||||
u32 var_index = NewVariable();
|
u32 var_index = NewVariable();
|
||||||
Expr var_condition = MakeExpr<ExprVar>(var_index);
|
Expr var_condition = MakeExpr<ExprVar>(var_index);
|
||||||
ASTNode var_node = ASTBase::Make<ASTVarSet>(parent, var_index, condition);
|
ASTNode var_node = ASTBase::Make<ASTVarSet>(parent, var_index, condition);
|
||||||
ASTNode var_node_init = ASTBase::Make<ASTVarSet>(parent, var_index, true_condition);
|
ASTNode var_node_init = ASTBase::Make<ASTVarSet>(parent, var_index, false_condition);
|
||||||
if (is_if) {
|
if (is_if) {
|
||||||
zipper2.InsertBefore(var_node_init, parent);
|
zipper2.InsertBefore(var_node_init, parent);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -141,8 +141,6 @@ public:
|
||||||
Expr condition;
|
Expr condition;
|
||||||
};
|
};
|
||||||
|
|
||||||
using TransformCallback = std::function<NodeBlock(u32 start, u32 end)>;
|
|
||||||
|
|
||||||
class ASTBase {
|
class ASTBase {
|
||||||
public:
|
public:
|
||||||
explicit ASTBase(ASTNode parent, ASTData data) : parent{parent}, data{data} {}
|
explicit ASTBase(ASTNode parent, ASTData data) : parent{parent}, data{data} {}
|
||||||
|
@ -233,11 +231,7 @@ public:
|
||||||
return std::holds_alternative<ASTBlockEncoded>(data);
|
return std::holds_alternative<ASTBlockEncoded>(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TransformBlockEncoded(TransformCallback& callback) {
|
void TransformBlockEncoded(NodeBlock& nodes) {
|
||||||
auto block = std::get_if<ASTBlockEncoded>(&data);
|
|
||||||
const u32 start = block->start;
|
|
||||||
const u32 end = block->end;
|
|
||||||
NodeBlock nodes = callback(start, end);
|
|
||||||
data = ASTBlockDecoded(nodes);
|
data = ASTBlockDecoded(nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -309,16 +303,20 @@ public:
|
||||||
|
|
||||||
void SanityCheck();
|
void SanityCheck();
|
||||||
|
|
||||||
bool IsFullyDecompiled() {
|
bool IsFullyDecompiled() const {
|
||||||
return gotos.size() == 0;
|
return gotos.size() == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
ASTNode GetProgram() {
|
ASTNode GetProgram() const {
|
||||||
return main_node;
|
return main_node;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Clear();
|
void Clear();
|
||||||
|
|
||||||
|
u32 GetVariables() const {
|
||||||
|
return variables;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool IndirectlyRelated(ASTNode first, ASTNode second);
|
bool IndirectlyRelated(ASTNode first, ASTNode second);
|
||||||
|
|
||||||
|
@ -343,7 +341,7 @@ private:
|
||||||
u32 variables{};
|
u32 variables{};
|
||||||
ASTProgram* program{};
|
ASTProgram* program{};
|
||||||
ASTNode main_node{};
|
ASTNode main_node{};
|
||||||
Expr true_condition{};
|
Expr false_condition{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace VideoCommon::Shader
|
} // namespace VideoCommon::Shader
|
||||||
|
|
|
@ -425,7 +425,7 @@ void InsertBranch(ASTManager& mm, const BlockBranchInfo& branch) {
|
||||||
}
|
}
|
||||||
if (cond.predicate != Pred::UnusedIndex) {
|
if (cond.predicate != Pred::UnusedIndex) {
|
||||||
u32 pred = static_cast<u32>(cond.predicate);
|
u32 pred = static_cast<u32>(cond.predicate);
|
||||||
bool negate;
|
bool negate = false;
|
||||||
if (pred > 7) {
|
if (pred > 7) {
|
||||||
negate = true;
|
negate = true;
|
||||||
pred -= 8;
|
pred -= 8;
|
||||||
|
|
|
@ -35,10 +35,73 @@ constexpr bool IsSchedInstruction(u32 offset, u32 main_offset) {
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
class ASTDecoder {
|
||||||
|
public:
|
||||||
|
ASTDecoder(ShaderIR& ir) : ir(ir) {}
|
||||||
|
|
||||||
|
void operator()(ASTProgram& ast) {
|
||||||
|
ASTNode current = ast.nodes.GetFirst();
|
||||||
|
while (current) {
|
||||||
|
Visit(current);
|
||||||
|
current = current->GetNext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(ASTIfThen& ast) {
|
||||||
|
ASTNode current = ast.nodes.GetFirst();
|
||||||
|
while (current) {
|
||||||
|
Visit(current);
|
||||||
|
current = current->GetNext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(ASTIfElse& ast) {
|
||||||
|
ASTNode current = ast.nodes.GetFirst();
|
||||||
|
while (current) {
|
||||||
|
Visit(current);
|
||||||
|
current = current->GetNext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(ASTBlockEncoded& ast) {}
|
||||||
|
|
||||||
|
void operator()(ASTBlockDecoded& ast) {}
|
||||||
|
|
||||||
|
void operator()(ASTVarSet& ast) {}
|
||||||
|
|
||||||
|
void operator()(ASTLabel& ast) {}
|
||||||
|
|
||||||
|
void operator()(ASTGoto& ast) {}
|
||||||
|
|
||||||
|
void operator()(ASTDoWhile& ast) {
|
||||||
|
ASTNode current = ast.nodes.GetFirst();
|
||||||
|
while (current) {
|
||||||
|
Visit(current);
|
||||||
|
current = current->GetNext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(ASTReturn& ast) {}
|
||||||
|
|
||||||
|
void operator()(ASTBreak& ast) {}
|
||||||
|
|
||||||
|
void Visit(ASTNode& node) {
|
||||||
|
std::visit(*this, *node->GetInnerData());
|
||||||
|
if (node->IsBlockEncoded()) {
|
||||||
|
auto block = std::get_if<ASTBlockEncoded>(node->GetInnerData());
|
||||||
|
NodeBlock bb = ir.DecodeRange(block->start, block->end);
|
||||||
|
node->TransformBlockEncoded(bb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ShaderIR& ir;
|
||||||
|
};
|
||||||
|
|
||||||
void ShaderIR::Decode() {
|
void ShaderIR::Decode() {
|
||||||
std::memcpy(&header, program_code.data(), sizeof(Tegra::Shader::Header));
|
std::memcpy(&header, program_code.data(), sizeof(Tegra::Shader::Header));
|
||||||
|
|
||||||
disable_flow_stack = false;
|
decompiled = false;
|
||||||
const auto info =
|
const auto info =
|
||||||
ScanFlow(program_code, program_size, main_offset, program_manager);
|
ScanFlow(program_code, program_size, main_offset, program_manager);
|
||||||
if (info) {
|
if (info) {
|
||||||
|
@ -46,7 +109,10 @@ void ShaderIR::Decode() {
|
||||||
coverage_begin = shader_info.start;
|
coverage_begin = shader_info.start;
|
||||||
coverage_end = shader_info.end;
|
coverage_end = shader_info.end;
|
||||||
if (shader_info.decompiled) {
|
if (shader_info.decompiled) {
|
||||||
disable_flow_stack = true;
|
decompiled = true;
|
||||||
|
ASTDecoder decoder{*this};
|
||||||
|
ASTNode program = GetASTProgram();
|
||||||
|
decoder.Visit(program);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
LOG_WARNING(HW_GPU, "Flow Stack Removing Failed! Falling back to old method");
|
LOG_WARNING(HW_GPU, "Flow Stack Removing Failed! Falling back to old method");
|
||||||
|
|
|
@ -157,7 +157,7 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) {
|
||||||
UNIMPLEMENTED_IF_MSG(instr.bra.constant_buffer != 0,
|
UNIMPLEMENTED_IF_MSG(instr.bra.constant_buffer != 0,
|
||||||
"Constant buffer flow is not supported");
|
"Constant buffer flow is not supported");
|
||||||
|
|
||||||
if (disable_flow_stack) {
|
if (decompiled) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,7 +171,7 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) {
|
||||||
UNIMPLEMENTED_IF_MSG(instr.bra.constant_buffer != 0,
|
UNIMPLEMENTED_IF_MSG(instr.bra.constant_buffer != 0,
|
||||||
"Constant buffer PBK is not supported");
|
"Constant buffer PBK is not supported");
|
||||||
|
|
||||||
if (disable_flow_stack) {
|
if (decompiled) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,7 +186,7 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) {
|
||||||
UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T, "SYNC condition code used: {}",
|
UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T, "SYNC condition code used: {}",
|
||||||
static_cast<u32>(cc));
|
static_cast<u32>(cc));
|
||||||
|
|
||||||
if (disable_flow_stack) {
|
if (decompiled) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,7 +198,7 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) {
|
||||||
const Tegra::Shader::ConditionCode cc = instr.flow_condition_code;
|
const Tegra::Shader::ConditionCode cc = instr.flow_condition_code;
|
||||||
UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T, "BRK condition code used: {}",
|
UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T, "BRK condition code used: {}",
|
||||||
static_cast<u32>(cc));
|
static_cast<u32>(cc));
|
||||||
if (disable_flow_stack) {
|
if (decompiled) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -72,4 +72,11 @@ bool ExprAreOpposite(Expr first, Expr second) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ExprIsTrue(Expr first) {
|
||||||
|
if (ExprIsBoolean(first)) {
|
||||||
|
return ExprBooleanGet(first);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace VideoCommon::Shader
|
} // namespace VideoCommon::Shader
|
||||||
|
|
|
@ -115,4 +115,6 @@ Expr MakeExprAnd(Expr first, Expr second);
|
||||||
|
|
||||||
Expr MakeExprOr(Expr first, Expr second);
|
Expr MakeExprOr(Expr first, Expr second);
|
||||||
|
|
||||||
|
bool ExprIsTrue(Expr first);
|
||||||
|
|
||||||
} // namespace VideoCommon::Shader
|
} // namespace VideoCommon::Shader
|
||||||
|
|
|
@ -137,7 +137,7 @@ Node ShaderIR::GetOutputAttribute(Attribute::Index index, u64 element, Node buff
|
||||||
return MakeNode<AbufNode>(index, static_cast<u32>(element), std::move(buffer));
|
return MakeNode<AbufNode>(index, static_cast<u32>(element), std::move(buffer));
|
||||||
}
|
}
|
||||||
|
|
||||||
Node ShaderIR::GetInternalFlag(InternalFlag flag, bool negated) {
|
Node ShaderIR::GetInternalFlag(InternalFlag flag, bool negated) const {
|
||||||
const Node node = MakeNode<InternalFlagNode>(flag);
|
const Node node = MakeNode<InternalFlagNode>(flag);
|
||||||
if (negated) {
|
if (negated) {
|
||||||
return Operation(OperationCode::LogicalNegate, node);
|
return Operation(OperationCode::LogicalNegate, node);
|
||||||
|
@ -367,13 +367,13 @@ OperationCode ShaderIR::GetPredicateCombiner(PredOperation operation) {
|
||||||
return op->second;
|
return op->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
Node ShaderIR::GetConditionCode(Tegra::Shader::ConditionCode cc) {
|
Node ShaderIR::GetConditionCode(Tegra::Shader::ConditionCode cc) const {
|
||||||
switch (cc) {
|
switch (cc) {
|
||||||
case Tegra::Shader::ConditionCode::NEU:
|
case Tegra::Shader::ConditionCode::NEU:
|
||||||
return GetInternalFlag(InternalFlag::Zero, true);
|
return GetInternalFlag(InternalFlag::Zero, true);
|
||||||
default:
|
default:
|
||||||
UNIMPLEMENTED_MSG("Unimplemented condition code: {}", static_cast<u32>(cc));
|
UNIMPLEMENTED_MSG("Unimplemented condition code: {}", static_cast<u32>(cc));
|
||||||
return GetPredicate(static_cast<u64>(Pred::NeverExecute));
|
return MakeNode<PredicateNode>(Pred::NeverExecute, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,8 +15,8 @@
|
||||||
#include "video_core/engines/maxwell_3d.h"
|
#include "video_core/engines/maxwell_3d.h"
|
||||||
#include "video_core/engines/shader_bytecode.h"
|
#include "video_core/engines/shader_bytecode.h"
|
||||||
#include "video_core/engines/shader_header.h"
|
#include "video_core/engines/shader_header.h"
|
||||||
#include "video_core/shader/node.h"
|
|
||||||
#include "video_core/shader/ast.h"
|
#include "video_core/shader/ast.h"
|
||||||
|
#include "video_core/shader/node.h"
|
||||||
|
|
||||||
namespace VideoCommon::Shader {
|
namespace VideoCommon::Shader {
|
||||||
|
|
||||||
|
@ -141,15 +141,27 @@ public:
|
||||||
return header;
|
return header;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsFlowStackDisabled() const {
|
bool IsDecompiled() const {
|
||||||
return disable_flow_stack;
|
return decompiled;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASTNode GetASTProgram() const {
|
||||||
|
return program_manager.GetProgram();
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 GetASTNumVariables() const {
|
||||||
|
return program_manager.GetVariables();
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 ConvertAddressToNvidiaSpace(const u32 address) const {
|
u32 ConvertAddressToNvidiaSpace(const u32 address) const {
|
||||||
return (address - main_offset) * sizeof(Tegra::Shader::Instruction);
|
return (address - main_offset) * sizeof(Tegra::Shader::Instruction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a condition code evaluated from internal flags
|
||||||
|
Node GetConditionCode(Tegra::Shader::ConditionCode cc) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
friend class ASTDecoder;
|
||||||
void Decode();
|
void Decode();
|
||||||
|
|
||||||
NodeBlock DecodeRange(u32 begin, u32 end);
|
NodeBlock DecodeRange(u32 begin, u32 end);
|
||||||
|
@ -214,7 +226,7 @@ private:
|
||||||
/// Generates a node representing an output attribute. Keeps track of used attributes.
|
/// Generates a node representing an output attribute. Keeps track of used attributes.
|
||||||
Node GetOutputAttribute(Tegra::Shader::Attribute::Index index, u64 element, Node buffer);
|
Node GetOutputAttribute(Tegra::Shader::Attribute::Index index, u64 element, Node buffer);
|
||||||
/// Generates a node representing an internal flag
|
/// Generates a node representing an internal flag
|
||||||
Node GetInternalFlag(InternalFlag flag, bool negated = false);
|
Node GetInternalFlag(InternalFlag flag, bool negated = false) const;
|
||||||
/// Generates a node representing a local memory address
|
/// Generates a node representing a local memory address
|
||||||
Node GetLocalMemory(Node address);
|
Node GetLocalMemory(Node address);
|
||||||
/// Generates a node representing a shared memory address
|
/// Generates a node representing a shared memory address
|
||||||
|
@ -272,9 +284,6 @@ private:
|
||||||
/// Returns a predicate combiner operation
|
/// Returns a predicate combiner operation
|
||||||
OperationCode GetPredicateCombiner(Tegra::Shader::PredOperation operation);
|
OperationCode GetPredicateCombiner(Tegra::Shader::PredOperation operation);
|
||||||
|
|
||||||
/// Returns a condition code evaluated from internal flags
|
|
||||||
Node GetConditionCode(Tegra::Shader::ConditionCode cc);
|
|
||||||
|
|
||||||
/// Accesses a texture sampler
|
/// Accesses a texture sampler
|
||||||
const Sampler& GetSampler(const Tegra::Shader::Sampler& sampler,
|
const Sampler& GetSampler(const Tegra::Shader::Sampler& sampler,
|
||||||
Tegra::Shader::TextureType type, bool is_array, bool is_shadow);
|
Tegra::Shader::TextureType type, bool is_array, bool is_shadow);
|
||||||
|
@ -358,7 +367,7 @@ private:
|
||||||
const ProgramCode& program_code;
|
const ProgramCode& program_code;
|
||||||
const u32 main_offset;
|
const u32 main_offset;
|
||||||
const std::size_t program_size;
|
const std::size_t program_size;
|
||||||
bool disable_flow_stack{};
|
bool decompiled{};
|
||||||
|
|
||||||
u32 coverage_begin{};
|
u32 coverage_begin{};
|
||||||
u32 coverage_end{};
|
u32 coverage_end{};
|
||||||
|
|
Loading…
Reference in a new issue