From 38fc995f6cc2c2af29abc976ddb45b72873b2cc4 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sat, 29 Jun 2019 01:44:07 -0400 Subject: gl_shader_decompiler: Implement AST decompiling --- src/video_core/shader/ast.cpp | 10 ++--- src/video_core/shader/ast.h | 18 ++++----- src/video_core/shader/control_flow.cpp | 2 +- src/video_core/shader/control_flow.h | 2 +- src/video_core/shader/decode.cpp | 70 +++++++++++++++++++++++++++++++++- src/video_core/shader/decode/other.cpp | 8 ++-- src/video_core/shader/expr.cpp | 7 ++++ src/video_core/shader/expr.h | 2 + src/video_core/shader/shader_ir.cpp | 6 +-- src/video_core/shader/shader_ir.h | 25 ++++++++---- 10 files changed, 116 insertions(+), 34 deletions(-) (limited to 'src/video_core/shader') diff --git a/src/video_core/shader/ast.cpp b/src/video_core/shader/ast.cpp index 0bf289f98..68a96cc79 100644 --- a/src/video_core/shader/ast.cpp +++ b/src/video_core/shader/ast.cpp @@ -372,13 +372,13 @@ ASTManager::~ASTManager() { void ASTManager::Init() { main_node = ASTBase::Make(ASTNode{}); program = std::get_if(main_node->GetInnerData()); - true_condition = MakeExpr(true); + false_condition = MakeExpr(false); } ASTManager::ASTManager(ASTManager&& other) : 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}, - 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(); } @@ -390,7 +390,7 @@ ASTManager& ASTManager::operator=(ASTManager&& other) { variables = other.variables; program = other.program; main_node = other.main_node; - true_condition = other.true_condition; + false_condition = other.false_condition; other.main_node.reset(); return *this; @@ -594,7 +594,7 @@ void ASTManager::MoveOutward(ASTNode goto_node) { u32 var_index = NewVariable(); Expr var_condition = MakeExpr(var_index); ASTNode var_node = ASTBase::Make(parent, var_index, condition); - ASTNode var_node_init = ASTBase::Make(parent, var_index, true_condition); + ASTNode var_node_init = ASTBase::Make(parent, var_index, false_condition); zipper2.InsertBefore(var_node_init, parent); zipper.InsertAfter(var_node, prev); goto_node->SetGotoCondition(var_condition); @@ -605,7 +605,7 @@ void ASTManager::MoveOutward(ASTNode goto_node) { u32 var_index = NewVariable(); Expr var_condition = MakeExpr(var_index); ASTNode var_node = ASTBase::Make(parent, var_index, condition); - ASTNode var_node_init = ASTBase::Make(parent, var_index, true_condition); + ASTNode var_node_init = ASTBase::Make(parent, var_index, false_condition); if (is_if) { zipper2.InsertBefore(var_node_init, parent); } else { diff --git a/src/video_core/shader/ast.h b/src/video_core/shader/ast.h index 958989bcd..06ab20cc5 100644 --- a/src/video_core/shader/ast.h +++ b/src/video_core/shader/ast.h @@ -141,8 +141,6 @@ public: Expr condition; }; -using TransformCallback = std::function; - class ASTBase { public: explicit ASTBase(ASTNode parent, ASTData data) : parent{parent}, data{data} {} @@ -233,11 +231,7 @@ public: return std::holds_alternative(data); } - void TransformBlockEncoded(TransformCallback& callback) { - auto block = std::get_if(&data); - const u32 start = block->start; - const u32 end = block->end; - NodeBlock nodes = callback(start, end); + void TransformBlockEncoded(NodeBlock& nodes) { data = ASTBlockDecoded(nodes); } @@ -309,16 +303,20 @@ public: void SanityCheck(); - bool IsFullyDecompiled() { + bool IsFullyDecompiled() const { return gotos.size() == 0; } - ASTNode GetProgram() { + ASTNode GetProgram() const { return main_node; } void Clear(); + u32 GetVariables() const { + return variables; + } + private: bool IndirectlyRelated(ASTNode first, ASTNode second); @@ -343,7 +341,7 @@ private: u32 variables{}; ASTProgram* program{}; ASTNode main_node{}; - Expr true_condition{}; + Expr false_condition{}; }; } // namespace VideoCommon::Shader diff --git a/src/video_core/shader/control_flow.cpp b/src/video_core/shader/control_flow.cpp index deb3d3ebd..a29922815 100644 --- a/src/video_core/shader/control_flow.cpp +++ b/src/video_core/shader/control_flow.cpp @@ -425,7 +425,7 @@ void InsertBranch(ASTManager& mm, const BlockBranchInfo& branch) { } if (cond.predicate != Pred::UnusedIndex) { u32 pred = static_cast(cond.predicate); - bool negate; + bool negate = false; if (pred > 7) { negate = true; pred -= 8; diff --git a/src/video_core/shader/control_flow.h b/src/video_core/shader/control_flow.h index 2805d975c..347a35dcf 100644 --- a/src/video_core/shader/control_flow.h +++ b/src/video_core/shader/control_flow.h @@ -74,6 +74,6 @@ struct ShaderCharacteristics { }; std::unique_ptr ScanFlow(const ProgramCode& program_code, u32 program_size, - u32 start_address, ASTManager& manager); + u32 start_address, ASTManager& manager); } // namespace VideoCommon::Shader diff --git a/src/video_core/shader/decode.cpp b/src/video_core/shader/decode.cpp index 381e87415..e7e0903f6 100644 --- a/src/video_core/shader/decode.cpp +++ b/src/video_core/shader/decode.cpp @@ -35,10 +35,73 @@ constexpr bool IsSchedInstruction(u32 offset, u32 main_offset) { } // 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(node->GetInnerData()); + NodeBlock bb = ir.DecodeRange(block->start, block->end); + node->TransformBlockEncoded(bb); + } + } + +private: + ShaderIR& ir; +}; + void ShaderIR::Decode() { std::memcpy(&header, program_code.data(), sizeof(Tegra::Shader::Header)); - disable_flow_stack = false; + decompiled = false; const auto info = ScanFlow(program_code, program_size, main_offset, program_manager); if (info) { @@ -46,7 +109,10 @@ void ShaderIR::Decode() { coverage_begin = shader_info.start; coverage_end = shader_info.end; if (shader_info.decompiled) { - disable_flow_stack = true; + decompiled = true; + ASTDecoder decoder{*this}; + ASTNode program = GetASTProgram(); + decoder.Visit(program); return; } LOG_WARNING(HW_GPU, "Flow Stack Removing Failed! Falling back to old method"); diff --git a/src/video_core/shader/decode/other.cpp b/src/video_core/shader/decode/other.cpp index d46e0f823..6f678003c 100644 --- a/src/video_core/shader/decode/other.cpp +++ b/src/video_core/shader/decode/other.cpp @@ -157,7 +157,7 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) { UNIMPLEMENTED_IF_MSG(instr.bra.constant_buffer != 0, "Constant buffer flow is not supported"); - if (disable_flow_stack) { + if (decompiled) { break; } @@ -171,7 +171,7 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) { UNIMPLEMENTED_IF_MSG(instr.bra.constant_buffer != 0, "Constant buffer PBK is not supported"); - if (disable_flow_stack) { + if (decompiled) { break; } @@ -186,7 +186,7 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) { UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T, "SYNC condition code used: {}", static_cast(cc)); - if (disable_flow_stack) { + if (decompiled) { break; } @@ -198,7 +198,7 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) { const Tegra::Shader::ConditionCode cc = instr.flow_condition_code; UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T, "BRK condition code used: {}", static_cast(cc)); - if (disable_flow_stack) { + if (decompiled) { break; } diff --git a/src/video_core/shader/expr.cpp b/src/video_core/shader/expr.cpp index ebce6339b..ca633ffb1 100644 --- a/src/video_core/shader/expr.cpp +++ b/src/video_core/shader/expr.cpp @@ -72,4 +72,11 @@ bool ExprAreOpposite(Expr first, Expr second) { return false; } +bool ExprIsTrue(Expr first) { + if (ExprIsBoolean(first)) { + return ExprBooleanGet(first); + } + return false; +} + } // namespace VideoCommon::Shader diff --git a/src/video_core/shader/expr.h b/src/video_core/shader/expr.h index f012f6fcf..b954cffb0 100644 --- a/src/video_core/shader/expr.h +++ b/src/video_core/shader/expr.h @@ -115,4 +115,6 @@ Expr MakeExprAnd(Expr first, Expr second); Expr MakeExprOr(Expr first, Expr second); +bool ExprIsTrue(Expr first); + } // namespace VideoCommon::Shader diff --git a/src/video_core/shader/shader_ir.cpp b/src/video_core/shader/shader_ir.cpp index c79f80e04..004b1e16f 100644 --- a/src/video_core/shader/shader_ir.cpp +++ b/src/video_core/shader/shader_ir.cpp @@ -137,7 +137,7 @@ Node ShaderIR::GetOutputAttribute(Attribute::Index index, u64 element, Node buff return MakeNode(index, static_cast(element), std::move(buffer)); } -Node ShaderIR::GetInternalFlag(InternalFlag flag, bool negated) { +Node ShaderIR::GetInternalFlag(InternalFlag flag, bool negated) const { const Node node = MakeNode(flag); if (negated) { return Operation(OperationCode::LogicalNegate, node); @@ -367,13 +367,13 @@ OperationCode ShaderIR::GetPredicateCombiner(PredOperation operation) { return op->second; } -Node ShaderIR::GetConditionCode(Tegra::Shader::ConditionCode cc) { +Node ShaderIR::GetConditionCode(Tegra::Shader::ConditionCode cc) const { switch (cc) { case Tegra::Shader::ConditionCode::NEU: return GetInternalFlag(InternalFlag::Zero, true); default: UNIMPLEMENTED_MSG("Unimplemented condition code: {}", static_cast(cc)); - return GetPredicate(static_cast(Pred::NeverExecute)); + return MakeNode(Pred::NeverExecute, false); } } diff --git a/src/video_core/shader/shader_ir.h b/src/video_core/shader/shader_ir.h index a91cd7d67..48c7b722e 100644 --- a/src/video_core/shader/shader_ir.h +++ b/src/video_core/shader/shader_ir.h @@ -15,8 +15,8 @@ #include "video_core/engines/maxwell_3d.h" #include "video_core/engines/shader_bytecode.h" #include "video_core/engines/shader_header.h" -#include "video_core/shader/node.h" #include "video_core/shader/ast.h" +#include "video_core/shader/node.h" namespace VideoCommon::Shader { @@ -141,15 +141,27 @@ public: return header; } - bool IsFlowStackDisabled() const { - return disable_flow_stack; + bool IsDecompiled() const { + return decompiled; + } + + ASTNode GetASTProgram() const { + return program_manager.GetProgram(); + } + + u32 GetASTNumVariables() const { + return program_manager.GetVariables(); } u32 ConvertAddressToNvidiaSpace(const u32 address) const { return (address - main_offset) * sizeof(Tegra::Shader::Instruction); } + /// Returns a condition code evaluated from internal flags + Node GetConditionCode(Tegra::Shader::ConditionCode cc) const; + private: + friend class ASTDecoder; void Decode(); NodeBlock DecodeRange(u32 begin, u32 end); @@ -214,7 +226,7 @@ private: /// Generates a node representing an output attribute. Keeps track of used attributes. Node GetOutputAttribute(Tegra::Shader::Attribute::Index index, u64 element, Node buffer); /// 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 Node GetLocalMemory(Node address); /// Generates a node representing a shared memory address @@ -272,9 +284,6 @@ private: /// Returns a predicate combiner 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 const Sampler& GetSampler(const Tegra::Shader::Sampler& sampler, Tegra::Shader::TextureType type, bool is_array, bool is_shadow); @@ -358,7 +367,7 @@ private: const ProgramCode& program_code; const u32 main_offset; const std::size_t program_size; - bool disable_flow_stack{}; + bool decompiled{}; u32 coverage_begin{}; u32 coverage_end{}; -- cgit v1.2.3