From c17953978b16f82a3b2049f8b961275020c73dd0 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Thu, 27 Jun 2019 00:39:40 -0400 Subject: [PATCH 01/19] shader_ir: Initial Decompile Setup --- src/video_core/CMakeLists.txt | 3 + src/video_core/shader/ast.cpp | 180 ++++++++++++++++++++++++ src/video_core/shader/ast.h | 184 +++++++++++++++++++++++++ src/video_core/shader/control_flow.cpp | 58 +++++++- src/video_core/shader/control_flow.h | 4 +- src/video_core/shader/expr.h | 86 ++++++++++++ 6 files changed, 510 insertions(+), 5 deletions(-) create mode 100644 src/video_core/shader/ast.cpp create mode 100644 src/video_core/shader/ast.h create mode 100644 src/video_core/shader/expr.h diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index e2f85c5f1..32049a2e7 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -105,9 +105,12 @@ add_library(video_core STATIC shader/decode/warp.cpp shader/decode/xmad.cpp shader/decode/other.cpp + shader/ast.cpp + shader/ast.h shader/control_flow.cpp shader/control_flow.h shader/decode.cpp + shader/expr.h shader/node_helper.cpp shader/node_helper.h shader/node.h diff --git a/src/video_core/shader/ast.cpp b/src/video_core/shader/ast.cpp new file mode 100644 index 000000000..5d0e85f42 --- /dev/null +++ b/src/video_core/shader/ast.cpp @@ -0,0 +1,180 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include + +#include "common/assert.h" +#include "common/common_types.h" +#include "video_core/shader/ast.h" +#include "video_core/shader/expr.h" + +namespace VideoCommon::Shader { + +class ExprPrinter final { +public: + ExprPrinter() = default; + + void operator()(ExprAnd const& expr) { + inner += "( "; + std::visit(*this, *expr.operand1); + inner += " && "; + std::visit(*this, *expr.operand2); + inner += ')'; + } + + void operator()(ExprOr const& expr) { + inner += "( "; + std::visit(*this, *expr.operand1); + inner += " || "; + std::visit(*this, *expr.operand2); + inner += ')'; + } + + void operator()(ExprNot const& expr) { + inner += "!"; + std::visit(*this, *expr.operand1); + } + + void operator()(ExprPredicate const& expr) { + u32 pred = static_cast(expr.predicate); + if (pred > 7) { + inner += "!"; + pred -= 8; + } + inner += "P" + std::to_string(pred); + } + + void operator()(ExprCondCode const& expr) { + u32 cc = static_cast(expr.cc); + inner += "CC" + std::to_string(cc); + } + + void operator()(ExprVar const& expr) { + inner += "V" + std::to_string(expr.var_index); + } + + void operator()(ExprBoolean const& expr) { + inner += expr.value ? "true" : "false"; + } + + std::string& GetResult() { + return inner; + } + + std::string inner{}; +}; + +class ASTPrinter { +public: + ASTPrinter() = default; + + void operator()(ASTProgram& ast) { + scope++; + inner += "program {\n"; + for (ASTNode& node : ast.nodes) { + Visit(node); + } + inner += "}\n"; + scope--; + } + + void operator()(ASTIf& ast) { + ExprPrinter expr_parser{}; + std::visit(expr_parser, *ast.condition); + inner += Ident() + "if (" + expr_parser.GetResult() + ") {\n"; + scope++; + for (auto& node : ast.then_nodes) { + Visit(node); + } + scope--; + if (ast.else_nodes.size() > 0) { + inner += Ident() + "} else {\n"; + scope++; + for (auto& node : ast.else_nodes) { + Visit(node); + } + scope--; + } else { + inner += Ident() + "}\n"; + } + } + + void operator()(ASTBlockEncoded& ast) { + inner += Ident() + "Block(" + std::to_string(ast.start) + ", " + std::to_string(ast.end) + + ");\n"; + } + + void operator()(ASTVarSet& ast) { + ExprPrinter expr_parser{}; + std::visit(expr_parser, *ast.condition); + inner += + Ident() + "V" + std::to_string(ast.index) + " := " + expr_parser.GetResult() + ";\n"; + } + + void operator()(ASTLabel& ast) { + inner += "Label_" + std::to_string(ast.index) + ":\n"; + } + + void operator()(ASTGoto& ast) { + ExprPrinter expr_parser{}; + std::visit(expr_parser, *ast.condition); + inner += Ident() + "(" + expr_parser.GetResult() + ") -> goto Label_" + + std::to_string(ast.label) + ";\n"; + } + + void operator()(ASTDoWhile& ast) { + ExprPrinter expr_parser{}; + std::visit(expr_parser, *ast.condition); + inner += Ident() + "do {\n"; + scope++; + for (auto& node : ast.loop_nodes) { + Visit(node); + } + scope--; + inner += Ident() + "} while (" + expr_parser.GetResult() + ")\n"; + } + + void operator()(ASTReturn& ast) { + ExprPrinter expr_parser{}; + std::visit(expr_parser, *ast.condition); + inner += Ident() + "(" + expr_parser.GetResult() + ") -> " + + (ast.kills ? "discard" : "exit") + ";\n"; + } + + std::string& Ident() { + if (memo_scope == scope) { + return tabs_memo; + } + tabs_memo = tabs.substr(0, scope * 2); + memo_scope = scope; + return tabs_memo; + } + + void Visit(ASTNode& node) { + std::visit(*this, *node->GetInnerData()); + } + + std::string& GetResult() { + return inner; + } + +private: + std::string inner{}; + u32 scope{}; + + std::string tabs_memo{}; + u32 memo_scope{}; + + static std::string tabs; +}; + +std::string ASTPrinter::tabs = " "; + +std::string ASTManager::Print() { + ASTPrinter printer{}; + printer.Visit(main_node); + return printer.GetResult(); +} + +} // namespace VideoCommon::Shader diff --git a/src/video_core/shader/ast.h b/src/video_core/shader/ast.h new file mode 100644 index 000000000..ca71543fb --- /dev/null +++ b/src/video_core/shader/ast.h @@ -0,0 +1,184 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "video_core/shader/expr.h" +#include "video_core/shader/node.h" + +namespace VideoCommon::Shader { + +class ASTBase; +class ASTProgram; +class ASTIf; +class ASTBlockEncoded; +class ASTVarSet; +class ASTGoto; +class ASTLabel; +class ASTDoWhile; +class ASTReturn; + +using ASTData = std::variant; + +using ASTNode = std::shared_ptr; + +class ASTProgram { +public: + ASTProgram() = default; + std::list nodes; +}; + +class ASTIf { +public: + ASTIf(Expr condition, std::list then_nodes, std::list else_nodes) + : condition(condition), then_nodes{then_nodes}, else_nodes{then_nodes} {} + Expr condition; + std::list then_nodes; + std::list else_nodes; +}; + +class ASTBlockEncoded { +public: + ASTBlockEncoded(u32 start, u32 end) : start{start}, end{end} {} + u32 start; + u32 end; +}; + +class ASTVarSet { +public: + ASTVarSet(u32 index, Expr condition) : index{index}, condition{condition} {} + u32 index; + Expr condition; +}; + +class ASTLabel { +public: + ASTLabel(u32 index) : index{index} {} + u32 index; +}; + +class ASTGoto { +public: + ASTGoto(Expr condition, u32 label) : condition{condition}, label{label} {} + Expr condition; + u32 label; +}; + +class ASTDoWhile { +public: + ASTDoWhile(Expr condition, std::list loop_nodes) + : condition(condition), loop_nodes{loop_nodes} {} + Expr condition; + std::list loop_nodes; +}; + +class ASTReturn { +public: + ASTReturn(Expr condition, bool kills) : condition{condition}, kills{kills} {} + Expr condition; + bool kills; +}; + +class ASTBase { +public: + explicit ASTBase(ASTNode parent, ASTData data) : parent{parent}, data{data} {} + + template + static ASTNode Make(ASTNode parent, Args&&... args) { + return std::make_shared(parent, ASTData(U(std::forward(args)...))); + } + + void SetParent(ASTNode new_parent) { + parent = new_parent; + } + + ASTNode& GetParent() { + return parent; + } + + const ASTNode& GetParent() const { + return parent; + } + + u32 GetLevel() const { + u32 level = 0; + auto next = parent; + while (next) { + next = next->GetParent(); + level++; + } + return level; + } + + ASTData* GetInnerData() { + return &data; + } + +private: + ASTData data; + ASTNode parent; +}; + +class ASTManager final { +public: + explicit ASTManager() { + main_node = ASTBase::Make(nullptr); + program = std::get_if(main_node->GetInnerData()); + } + + void DeclareLabel(u32 address) { + const auto pair = labels_map.emplace(address, labels_count); + if (pair.second) { + labels_count++; + labels.resize(labels_count); + } + } + + void InsertLabel(u32 address) { + u32 index = labels_map[address]; + ASTNode label = ASTBase::Make(main_node, index); + labels[index] = label; + program->nodes.push_back(label); + } + + void InsertGoto(Expr condition, u32 address) { + u32 index = labels_map[address]; + ASTNode goto_node = ASTBase::Make(main_node, condition, index); + gotos.push_back(goto_node); + program->nodes.push_back(goto_node); + } + + void InsertBlock(u32 start_address, u32 end_address) { + ASTNode block = ASTBase::Make(main_node, start_address, end_address); + program->nodes.push_back(block); + } + + void InsertReturn(Expr condition, bool kills) { + ASTNode node = ASTBase::Make(main_node, condition, kills); + program->nodes.push_back(node); + } + + std::string Print(); + + void Decompile() {} + +private: + std::unordered_map labels_map{}; + u32 labels_count{}; + std::vector labels{}; + std::list gotos{}; + u32 variables{}; + ASTProgram* program; + ASTNode main_node; +}; + +} // namespace VideoCommon::Shader diff --git a/src/video_core/shader/control_flow.cpp b/src/video_core/shader/control_flow.cpp index ec3a76690..bea7f767c 100644 --- a/src/video_core/shader/control_flow.cpp +++ b/src/video_core/shader/control_flow.cpp @@ -4,13 +4,14 @@ #include #include +#include #include #include -#include #include #include "common/assert.h" #include "common/common_types.h" +#include "video_core/shader/ast.h" #include "video_core/shader/control_flow.h" #include "video_core/shader/shader_ir.h" @@ -64,7 +65,7 @@ struct CFGRebuildState { std::list inspect_queries{}; std::list queries{}; std::unordered_map registered{}; - std::unordered_set labels{}; + std::set labels{}; std::map ssy_labels{}; std::map pbk_labels{}; std::unordered_map stacks{}; @@ -415,6 +416,54 @@ bool TryQuery(CFGRebuildState& state) { } } // Anonymous namespace +void InsertBranch(ASTManager& mm, const BlockBranchInfo& branch) { + const auto get_expr = ([&](const Condition& cond) -> Expr { + Expr result{}; + if (cond.cc != ConditionCode::T) { + result = MakeExpr(cond.cc); + } + if (cond.predicate != Pred::UnusedIndex) { + Expr extra = MakeExpr(cond.predicate); + if (result) { + return MakeExpr(extra, result); + } + return extra; + } + if (result) { + return result; + } + return MakeExpr(true); + }); + if (branch.address < 0) { + if (branch.kill) { + mm.InsertReturn(get_expr(branch.condition), true); + return; + } + mm.InsertReturn(get_expr(branch.condition), false); + return; + } + mm.InsertGoto(get_expr(branch.condition), branch.address); +} + +void DecompileShader(CFGRebuildState& state) { + ASTManager manager{}; + for (auto label : state.labels) { + manager.DeclareLabel(label); + } + for (auto& block : state.block_info) { + if (state.labels.count(block.start) != 0) { + manager.InsertLabel(block.start); + } + u32 end = block.branch.ignore ? block.end + 1 : block.end; + manager.InsertBlock(block.start, end); + if (!block.branch.ignore) { + InsertBranch(manager, block.branch); + } + } + manager.Decompile(); + LOG_CRITICAL(HW_GPU, "Decompiled Shader:\n{} \n", manager.Print()); +} + std::optional ScanFlow(const ProgramCode& program_code, std::size_t program_size, u32 start_address) { CFGRebuildState state{program_code, program_size, start_address}; @@ -441,7 +490,10 @@ std::optional ScanFlow(const ProgramCode& program_code, // Sort and organize results std::sort(state.block_info.begin(), state.block_info.end(), - [](const BlockInfo& a, const BlockInfo& b) { return a.start < b.start; }); + [](const BlockInfo& a, const BlockInfo& b) -> bool { return a.start < b.start; }); + if (decompiled) { + DecompileShader(state); + } ShaderCharacteristics result_out{}; result_out.decompilable = decompiled; result_out.start = start_address; diff --git a/src/video_core/shader/control_flow.h b/src/video_core/shader/control_flow.h index b0a5e4f8c..efd037f1a 100644 --- a/src/video_core/shader/control_flow.h +++ b/src/video_core/shader/control_flow.h @@ -6,7 +6,7 @@ #include #include -#include +#include #include "video_core/engines/shader_bytecode.h" #include "video_core/shader/shader_ir.h" @@ -70,7 +70,7 @@ struct ShaderCharacteristics { bool decompilable{}; u32 start{}; u32 end{}; - std::unordered_set labels{}; + std::set labels{}; }; std::optional ScanFlow(const ProgramCode& program_code, diff --git a/src/video_core/shader/expr.h b/src/video_core/shader/expr.h new file mode 100644 index 000000000..94678f09a --- /dev/null +++ b/src/video_core/shader/expr.h @@ -0,0 +1,86 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include + +#include "video_core/engines/shader_bytecode.h" + +namespace VideoCommon::Shader { + +using Tegra::Shader::ConditionCode; +using Tegra::Shader::Pred; + +class ExprAnd; +class ExprOr; +class ExprNot; +class ExprPredicate; +class ExprCondCode; +class ExprVar; +class ExprBoolean; + +using ExprData = + std::variant; +using Expr = std::shared_ptr; + +class ExprAnd final { +public: + ExprAnd(Expr a, Expr b) : operand1{a}, operand2{b} {} + + Expr operand1; + Expr operand2; +}; + +class ExprOr final { +public: + ExprOr(Expr a, Expr b) : operand1{a}, operand2{b} {} + + Expr operand1; + Expr operand2; +}; + +class ExprNot final { +public: + ExprNot(Expr a) : operand1{a} {} + + Expr operand1; +}; + +class ExprVar final { +public: + ExprVar(u32 index) : var_index{index} {} + + u32 var_index; +}; + +class ExprPredicate final { +public: + ExprPredicate(Pred predicate) : predicate{predicate} {} + + Pred predicate; +}; + +class ExprCondCode final { +public: + ExprCondCode(ConditionCode cc) : cc{cc} {} + + ConditionCode cc; +}; + +class ExprBoolean final { +public: + ExprBoolean(bool val) : value{val} {} + + bool value; +}; + +template +Expr MakeExpr(Args&&... args) { + static_assert(std::is_convertible_v); + return std::make_shared(T(std::forward(args)...)); +} + +} // namespace VideoCommon::Shader From 4fde66e6094b57201d208b8abd3d7715341cd5db Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Thu, 27 Jun 2019 18:57:47 -0400 Subject: [PATCH 02/19] shader_ir: Add basic goto elimination --- src/video_core/shader/ast.cpp | 348 ++++++++++++++++++++++++++++++++-- src/video_core/shader/ast.h | 178 ++++++++++++++--- 2 files changed, 486 insertions(+), 40 deletions(-) diff --git a/src/video_core/shader/ast.cpp b/src/video_core/shader/ast.cpp index 5d0e85f42..56a1b29f3 100644 --- a/src/video_core/shader/ast.cpp +++ b/src/video_core/shader/ast.cpp @@ -11,6 +11,147 @@ namespace VideoCommon::Shader { +ASTZipper::ASTZipper() = default; +ASTZipper::ASTZipper(ASTNode new_first) : first{}, last{} { + first = new_first; + last = new_first; + ASTNode current = first; + while (current) { + current->manager = this; + last = current; + current = current->next; + } +} + +void ASTZipper::PushBack(ASTNode new_node) { + new_node->previous = last; + if (last) { + last->next = new_node; + } + new_node->next.reset(); + last = new_node; + if (!first) { + first = new_node; + } + new_node->manager = this; +} + +void ASTZipper::PushFront(ASTNode new_node) { + new_node->previous.reset(); + new_node->next = first; + if (first) { + first->previous = first; + } + first = new_node; + if (!last) { + last = new_node; + } + new_node->manager = this; +} + +void ASTZipper::InsertAfter(ASTNode new_node, ASTNode at_node) { + if (!at_node) { + PushFront(new_node); + return; + } + new_node->previous = at_node; + if (at_node == last) { + last = new_node; + } + new_node->next = at_node->next; + at_node->next = new_node; + new_node->manager = this; +} + +void ASTZipper::SetParent(ASTNode new_parent) { + ASTNode current = first; + while (current) { + current->parent = new_parent; + current = current->next; + } +} + +void ASTZipper::DetachTail(ASTNode node) { + ASSERT(node->manager == this); + if (node == first) { + first.reset(); + last.reset(); + return; + } + + last = node->previous; + node->previous.reset(); +} + +void ASTZipper::DetachSegment(ASTNode start, ASTNode end) { + ASSERT(start->manager == this && end->manager == this); + ASTNode prev = start->previous; + ASTNode post = end->next; + if (!prev) { + first = post; + } else { + prev->next = post; + } + if (!post) { + last = prev; + } else { + post->previous = prev; + } + start->previous.reset(); + end->next.reset(); + ASTNode current = start; + bool found = false; + while (current) { + current->manager = nullptr; + current->parent.reset(); + found |= current == end; + current = current->next; + } + ASSERT(found); +} + +void ASTZipper::DetachSingle(ASTNode node) { + ASSERT(node->manager == this); + ASTNode prev = node->previous; + ASTNode post = node->next; + node->previous.reset(); + node->next.reset(); + if (!prev) { + first = post; + } else { + prev->next = post; + } + if (!post) { + last = prev; + } else { + post->previous = prev; + } + + node->manager = nullptr; + node->parent.reset(); +} + + +void ASTZipper::Remove(ASTNode node) { + ASSERT(node->manager == this); + ASTNode next = node->next; + ASTNode previous = node->previous; + if (previous) { + previous->next = next; + } + if (next) { + next->previous = previous; + } + node->parent.reset(); + node->manager = nullptr; + if (node == last) { + last = previous; + } + if (node == first) { + first = next; + } +} + class ExprPrinter final { public: ExprPrinter() = default; @@ -72,32 +213,39 @@ public: void operator()(ASTProgram& ast) { scope++; inner += "program {\n"; - for (ASTNode& node : ast.nodes) { - Visit(node); + ASTNode current = ast.nodes.GetFirst(); + while (current) { + Visit(current); + current = current->GetNext(); } inner += "}\n"; scope--; } - void operator()(ASTIf& ast) { + void operator()(ASTIfThen& ast) { ExprPrinter expr_parser{}; std::visit(expr_parser, *ast.condition); inner += Ident() + "if (" + expr_parser.GetResult() + ") {\n"; scope++; - for (auto& node : ast.then_nodes) { - Visit(node); + ASTNode current = ast.nodes.GetFirst(); + while (current) { + Visit(current); + current = current->GetNext(); } scope--; - if (ast.else_nodes.size() > 0) { - inner += Ident() + "} else {\n"; - scope++; - for (auto& node : ast.else_nodes) { - Visit(node); - } - scope--; - } else { - inner += Ident() + "}\n"; + inner += Ident() + "}\n"; + } + + void operator()(ASTIfElse& ast) { + inner += Ident() + "else {\n"; + scope++; + ASTNode current = ast.nodes.GetFirst(); + while (current) { + Visit(current); + current = current->GetNext(); } + scope--; + inner += Ident() + "}\n"; } void operator()(ASTBlockEncoded& ast) { @@ -128,8 +276,10 @@ public: std::visit(expr_parser, *ast.condition); inner += Ident() + "do {\n"; scope++; - for (auto& node : ast.loop_nodes) { - Visit(node); + ASTNode current = ast.nodes.GetFirst(); + while (current) { + Visit(current); + current = current->GetNext(); } scope--; inner += Ident() + "} while (" + expr_parser.GetResult() + ")\n"; @@ -142,6 +292,12 @@ public: (ast.kills ? "discard" : "exit") + ";\n"; } + void operator()(ASTBreak& ast) { + ExprPrinter expr_parser{}; + std::visit(expr_parser, *ast.condition); + inner += Ident() + "(" + expr_parser.GetResult() + ") -> break;\n"; + } + std::string& Ident() { if (memo_scope == scope) { return tabs_memo; @@ -177,4 +333,164 @@ std::string ASTManager::Print() { return printer.GetResult(); } +#pragma optimize("", off) + +void ASTManager::Decompile() { + auto it = gotos.begin(); + while (it != gotos.end()) { + ASTNode goto_node = *it; + u32 label_index = goto_node->GetGotoLabel(); + ASTNode label = labels[label_index]; + if (IndirectlyRelated(goto_node, label)) { + while (!DirectlyRelated(goto_node, label)) { + MoveOutward(goto_node); + } + } + if (DirectlyRelated(goto_node, label)) { + u32 goto_level = goto_node->GetLevel(); + u32 label_level = goto_node->GetLevel(); + while (label_level > goto_level) { + MoveOutward(goto_node); + goto_level++; + } + } + if (label->GetParent() == goto_node->GetParent()) { + bool is_loop = false; + ASTNode current = goto_node->GetPrevious(); + while (current) { + if (current == label) { + is_loop = true; + break; + } + current = current->GetPrevious(); + } + + if (is_loop) { + EncloseDoWhile(goto_node, label); + } else { + EncloseIfThen(goto_node, label); + } + it = gotos.erase(it); + continue; + } + it++; + } + /* + for (ASTNode label : labels) { + auto& manager = label->GetManager(); + manager.Remove(label); + } + labels.clear(); + */ +} + +bool ASTManager::IndirectlyRelated(ASTNode first, ASTNode second) { + return !(first->GetParent() == second->GetParent() || DirectlyRelated(first, second)); +} + +bool ASTManager::DirectlyRelated(ASTNode first, ASTNode second) { + if (first->GetParent() == second->GetParent()) { + return false; + } + u32 first_level = first->GetLevel(); + u32 second_level = second->GetLevel(); + u32 min_level; + u32 max_level; + ASTNode max; + ASTNode min; + if (first_level > second_level) { + min_level = second_level; + min = second; + max_level = first_level; + max = first; + } else { + min_level = first_level; + min = first; + max_level = second_level; + max = second; + } + + while (min_level < max_level) { + min_level++; + min = min->GetParent(); + } + + return (min->GetParent() == max->GetParent()); +} + +void ASTManager::EncloseDoWhile(ASTNode goto_node, ASTNode label) { + ASTZipper& zipper = goto_node->GetManager(); + ASTNode loop_start = label->GetNext(); + if (loop_start == goto_node) { + zipper.Remove(goto_node); + return; + } + ASTNode parent = label->GetParent(); + Expr condition = goto_node->GetGotoCondition(); + zipper.DetachSegment(loop_start, goto_node); + ASTNode do_while_node = ASTBase::Make(parent, condition, ASTZipper(loop_start)); + zipper.InsertAfter(do_while_node, label); + ASTZipper* sub_zipper = do_while_node->GetSubNodes(); + sub_zipper->SetParent(do_while_node); + sub_zipper->Remove(goto_node); +} + +void ASTManager::EncloseIfThen(ASTNode goto_node, ASTNode label) { + ASTZipper& zipper = goto_node->GetManager(); + ASTNode if_end = label->GetPrevious(); + if (if_end == goto_node) { + zipper.Remove(goto_node); + return; + } + ASTNode prev = goto_node->GetPrevious(); + ASTNode parent = label->GetParent(); + Expr condition = goto_node->GetGotoCondition(); + Expr neg_condition = MakeExpr(condition); + zipper.DetachSegment(goto_node, if_end); + ASTNode if_node = ASTBase::Make(parent, condition, ASTZipper(goto_node)); + zipper.InsertAfter(if_node, prev); + ASTZipper* sub_zipper = if_node->GetSubNodes(); + sub_zipper->SetParent(if_node); + sub_zipper->Remove(goto_node); +} + +void ASTManager::MoveOutward(ASTNode goto_node) { + ASTZipper& zipper = goto_node->GetManager(); + ASTNode parent = goto_node->GetParent(); + bool is_loop = parent->IsLoop(); + bool is_if = parent->IsIfThen() || parent->IsIfElse(); + + ASTNode prev = goto_node->GetPrevious(); + + Expr condition = goto_node->GetGotoCondition(); + u32 var_index = NewVariable(); + Expr var_condition = MakeExpr(var_index); + ASTNode var_node = ASTBase::Make(parent, var_index, condition); + zipper.DetachSingle(goto_node); + zipper.InsertAfter(var_node, prev); + goto_node->SetGotoCondition(var_condition); + if (is_loop) { + ASTNode break_node = ASTBase::Make(parent, var_condition); + zipper.InsertAfter(break_node, var_node); + } else if (is_if) { + ASTNode post = var_node->GetNext(); + if (post) { + zipper.DetachTail(post); + ASTNode if_node = ASTBase::Make(parent, var_condition, ASTZipper(post)); + zipper.InsertAfter(if_node, var_node); + ASTZipper* sub_zipper = if_node->GetSubNodes(); + sub_zipper->SetParent(if_node); + } + } else { + UNREACHABLE(); + } + ASTZipper& zipper2 = parent->GetManager(); + ASTNode next = parent->GetNext(); + if (is_if && next && next->IsIfElse()) { + zipper2.InsertAfter(goto_node, next); + return; + } + zipper2.InsertAfter(goto_node, parent); +} + } // namespace VideoCommon::Shader diff --git a/src/video_core/shader/ast.h b/src/video_core/shader/ast.h index ca71543fb..22ac8884c 100644 --- a/src/video_core/shader/ast.h +++ b/src/video_core/shader/ast.h @@ -18,32 +18,71 @@ namespace VideoCommon::Shader { class ASTBase; class ASTProgram; -class ASTIf; +class ASTIfThen; +class ASTIfElse; class ASTBlockEncoded; class ASTVarSet; class ASTGoto; class ASTLabel; class ASTDoWhile; class ASTReturn; +class ASTBreak; -using ASTData = std::variant; +using ASTData = std::variant; using ASTNode = std::shared_ptr; -class ASTProgram { -public: - ASTProgram() = default; - std::list nodes; +enum class ASTZipperType : u32 { + Program, + IfThen, + IfElse, + Loop, }; -class ASTIf { +class ASTZipper final { public: - ASTIf(Expr condition, std::list then_nodes, std::list else_nodes) - : condition(condition), then_nodes{then_nodes}, else_nodes{then_nodes} {} + ASTZipper(); + ASTZipper(ASTNode first); + + ASTNode GetFirst() { + return first; + } + + ASTNode GetLast() { + return last; + } + + void PushBack(ASTNode new_node); + void PushFront(ASTNode new_node); + void InsertAfter(ASTNode new_node, ASTNode at_node); + void SetParent(ASTNode new_parent); + void DetachTail(ASTNode node); + void DetachSingle(ASTNode node); + void DetachSegment(ASTNode start, ASTNode end); + void Remove(ASTNode node); + + ASTNode first{}; + ASTNode last{}; +}; + +class ASTProgram { +public: + ASTProgram() : nodes{} {}; + ASTZipper nodes; +}; + +class ASTIfThen { +public: + ASTIfThen(Expr condition, ASTZipper nodes) : condition(condition), nodes{nodes} {} Expr condition; - std::list then_nodes; - std::list else_nodes; + ASTZipper nodes; +}; + +class ASTIfElse { +public: + ASTIfElse(ASTZipper nodes) : nodes{nodes} {} + ASTZipper nodes; }; class ASTBlockEncoded { @@ -75,10 +114,9 @@ public: class ASTDoWhile { public: - ASTDoWhile(Expr condition, std::list loop_nodes) - : condition(condition), loop_nodes{loop_nodes} {} + ASTDoWhile(Expr condition, ASTZipper nodes) : condition(condition), nodes{nodes} {} Expr condition; - std::list loop_nodes; + ASTZipper nodes; }; class ASTReturn { @@ -88,6 +126,12 @@ public: bool kills; }; +class ASTBreak { +public: + ASTBreak(Expr condition) : condition{condition} {} + Expr condition; +}; + class ASTBase { public: explicit ASTBase(ASTNode parent, ASTData data) : parent{parent}, data{data} {} @@ -111,9 +155,9 @@ public: u32 GetLevel() const { u32 level = 0; - auto next = parent; - while (next) { - next = next->GetParent(); + auto next_parent = parent; + while (next_parent) { + next_parent = next_parent->GetParent(); level++; } return level; @@ -123,15 +167,83 @@ public: return &data; } + ASTNode GetNext() { + return next; + } + + ASTNode GetPrevious() { + return previous; + } + + ASTZipper& GetManager() { + return *manager; + } + + u32 GetGotoLabel() const { + auto inner = std::get_if(&data); + if (inner) { + return inner->label; + } + return -1; + } + + Expr GetGotoCondition() const { + auto inner = std::get_if(&data); + if (inner) { + return inner->condition; + } + return nullptr; + } + + void SetGotoCondition(Expr new_condition) { + auto inner = std::get_if(&data); + if (inner) { + inner->condition = new_condition; + } + } + + bool IsIfThen() const { + return std::holds_alternative(data); + } + + bool IsIfElse() const { + return std::holds_alternative(data); + } + + bool IsLoop() const { + return std::holds_alternative(data); + } + + ASTZipper* GetSubNodes() { + if (std::holds_alternative(data)) { + return &std::get_if(&data)->nodes; + } + if (std::holds_alternative(data)) { + return &std::get_if(&data)->nodes; + } + if (std::holds_alternative(data)) { + return &std::get_if(&data)->nodes; + } + if (std::holds_alternative(data)) { + return &std::get_if(&data)->nodes; + } + return nullptr; + } + private: + friend class ASTZipper; + ASTData data; ASTNode parent; + ASTNode next{}; + ASTNode previous{}; + ASTZipper* manager{}; }; class ASTManager final { public: explicit ASTManager() { - main_node = ASTBase::Make(nullptr); + main_node = ASTBase::Make(ASTNode{}); program = std::get_if(main_node->GetInnerData()); } @@ -147,31 +259,49 @@ public: u32 index = labels_map[address]; ASTNode label = ASTBase::Make(main_node, index); labels[index] = label; - program->nodes.push_back(label); + program->nodes.PushBack(label); } void InsertGoto(Expr condition, u32 address) { u32 index = labels_map[address]; ASTNode goto_node = ASTBase::Make(main_node, condition, index); gotos.push_back(goto_node); - program->nodes.push_back(goto_node); + program->nodes.PushBack(goto_node); } void InsertBlock(u32 start_address, u32 end_address) { ASTNode block = ASTBase::Make(main_node, start_address, end_address); - program->nodes.push_back(block); + program->nodes.PushBack(block); } void InsertReturn(Expr condition, bool kills) { ASTNode node = ASTBase::Make(main_node, condition, kills); - program->nodes.push_back(node); + program->nodes.PushBack(node); } std::string Print(); - void Decompile() {} + void Decompile(); + + private: + bool IndirectlyRelated(ASTNode first, ASTNode second); + + bool DirectlyRelated(ASTNode first, ASTNode second); + + void EncloseDoWhile(ASTNode goto_node, ASTNode label); + + void EncloseIfThen(ASTNode goto_node, ASTNode label); + + void MoveOutward(ASTNode goto_node) ; + + u32 NewVariable() { + u32 new_var = variables; + variables++; + return new_var; + } + std::unordered_map labels_map{}; u32 labels_count{}; std::vector labels{}; From 8be6e1c5221066a49b6ad27efbd20a999a7c16b3 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Fri, 28 Jun 2019 20:54:21 -0400 Subject: [PATCH 03/19] shader_ir: Corrections to outward movements and misc stuffs --- CMakeModules/GenerateSCMRev.cmake | 4 + src/common/CMakeLists.txt | 4 + src/video_core/CMakeLists.txt | 1 + src/video_core/shader/ast.cpp | 185 ++++++++++++++++++------- src/video_core/shader/ast.h | 53 ++++++- src/video_core/shader/control_flow.cpp | 14 +- src/video_core/shader/expr.cpp | 75 ++++++++++ src/video_core/shader/expr.h | 36 ++++- 8 files changed, 314 insertions(+), 58 deletions(-) create mode 100644 src/video_core/shader/expr.cpp diff --git a/CMakeModules/GenerateSCMRev.cmake b/CMakeModules/GenerateSCMRev.cmake index a1ace89cb..d3ab22c08 100644 --- a/CMakeModules/GenerateSCMRev.cmake +++ b/CMakeModules/GenerateSCMRev.cmake @@ -83,9 +83,13 @@ set(HASH_FILES "${VIDEO_CORE}/shader/decode/video.cpp" "${VIDEO_CORE}/shader/decode/warp.cpp" "${VIDEO_CORE}/shader/decode/xmad.cpp" + "${VIDEO_CORE}/shader/ast.cpp" + "${VIDEO_CORE}/shader/ast.h" "${VIDEO_CORE}/shader/control_flow.cpp" "${VIDEO_CORE}/shader/control_flow.h" "${VIDEO_CORE}/shader/decode.cpp" + "${VIDEO_CORE}/shader/expr.cpp" + "${VIDEO_CORE}/shader/expr.h" "${VIDEO_CORE}/shader/node.h" "${VIDEO_CORE}/shader/node_helper.cpp" "${VIDEO_CORE}/shader/node_helper.h" diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index dfed8b51d..afc5ff736 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -60,9 +60,13 @@ add_custom_command(OUTPUT scm_rev.cpp "${VIDEO_CORE}/shader/decode/video.cpp" "${VIDEO_CORE}/shader/decode/warp.cpp" "${VIDEO_CORE}/shader/decode/xmad.cpp" + "${VIDEO_CORE}/shader/ast.cpp" + "${VIDEO_CORE}/shader/ast.h" "${VIDEO_CORE}/shader/control_flow.cpp" "${VIDEO_CORE}/shader/control_flow.h" "${VIDEO_CORE}/shader/decode.cpp" + "${VIDEO_CORE}/shader/expr.cpp" + "${VIDEO_CORE}/shader/expr.h" "${VIDEO_CORE}/shader/node.h" "${VIDEO_CORE}/shader/node_helper.cpp" "${VIDEO_CORE}/shader/node_helper.h" diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 32049a2e7..33fa88762 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -110,6 +110,7 @@ add_library(video_core STATIC shader/control_flow.cpp shader/control_flow.h shader/decode.cpp + shader/expr.cpp shader/expr.h shader/node_helper.cpp shader/node_helper.h diff --git a/src/video_core/shader/ast.cpp b/src/video_core/shader/ast.cpp index 56a1b29f3..d521a7b52 100644 --- a/src/video_core/shader/ast.cpp +++ b/src/video_core/shader/ast.cpp @@ -12,18 +12,22 @@ namespace VideoCommon::Shader { ASTZipper::ASTZipper() = default; -ASTZipper::ASTZipper(ASTNode new_first) : first{}, last{} { + +void ASTZipper::Init(ASTNode new_first, ASTNode parent) { + ASSERT(new_first->manager == nullptr); first = new_first; last = new_first; ASTNode current = first; while (current) { current->manager = this; + current->parent = parent; last = current; current = current->next; } } void ASTZipper::PushBack(ASTNode new_node) { + ASSERT(new_node->manager == nullptr); new_node->previous = last; if (last) { last->next = new_node; @@ -37,38 +41,55 @@ void ASTZipper::PushBack(ASTNode new_node) { } void ASTZipper::PushFront(ASTNode new_node) { + ASSERT(new_node->manager == nullptr); new_node->previous.reset(); new_node->next = first; if (first) { - first->previous = first; + first->previous = new_node; } - first = new_node; - if (!last) { + if (last == first) { last = new_node; } + first = new_node; new_node->manager = this; } void ASTZipper::InsertAfter(ASTNode new_node, ASTNode at_node) { + ASSERT(new_node->manager == nullptr); if (!at_node) { PushFront(new_node); return; } + ASTNode next = at_node->next; + if (next) { + next->previous = new_node; + } new_node->previous = at_node; if (at_node == last) { last = new_node; } - new_node->next = at_node->next; + new_node->next = next; at_node->next = new_node; new_node->manager = this; } -void ASTZipper::SetParent(ASTNode new_parent) { - ASTNode current = first; - while (current) { - current->parent = new_parent; - current = current->next; +void ASTZipper::InsertBefore(ASTNode new_node, ASTNode at_node) { + ASSERT(new_node->manager == nullptr); + if (!at_node) { + PushBack(new_node); + return; } + ASTNode previous = at_node->previous; + if (previous) { + previous->next = new_node; + } + new_node->next = at_node; + if (at_node == first) { + first = new_node; + } + new_node->previous = previous; + at_node->previous = new_node; + new_node->manager = this; } void ASTZipper::DetachTail(ASTNode node) { @@ -80,11 +101,22 @@ void ASTZipper::DetachTail(ASTNode node) { } last = node->previous; + last->next.reset(); node->previous.reset(); + ASTNode current = node; + while (current) { + current->manager = nullptr; + current->parent.reset(); + current = current->next; + } } void ASTZipper::DetachSegment(ASTNode start, ASTNode end) { ASSERT(start->manager == this && end->manager == this); + if (start == end) { + DetachSingle(start); + return; + } ASTNode prev = start->previous; ASTNode post = end->next; if (!prev) { @@ -131,7 +163,6 @@ void ASTZipper::DetachSingle(ASTNode node) { node->parent.reset(); } - void ASTZipper::Remove(ASTNode node) { ASSERT(node->manager == this); ASTNode next = node->next; @@ -178,12 +209,7 @@ public: } void operator()(ExprPredicate const& expr) { - u32 pred = static_cast(expr.predicate); - if (pred > 7) { - inner += "!"; - pred -= 8; - } - inner += "P" + std::to_string(pred); + inner += "P" + std::to_string(expr.predicate); } void operator()(ExprCondCode const& expr) { @@ -253,6 +279,10 @@ public: ");\n"; } + void operator()(ASTBlockDecoded& ast) { + inner += Ident() + "Block;\n"; + } + void operator()(ASTVarSet& ast) { ExprPrinter expr_parser{}; std::visit(expr_parser, *ast.condition); @@ -282,7 +312,7 @@ public: current = current->GetNext(); } scope--; - inner += Ident() + "} while (" + expr_parser.GetResult() + ")\n"; + inner += Ident() + "} while (" + expr_parser.GetResult() + ");\n"; } void operator()(ASTReturn& ast) { @@ -333,8 +363,6 @@ std::string ASTManager::Print() { return printer.GetResult(); } -#pragma optimize("", off) - void ASTManager::Decompile() { auto it = gotos.begin(); while (it != gotos.end()) { @@ -348,11 +376,12 @@ void ASTManager::Decompile() { } if (DirectlyRelated(goto_node, label)) { u32 goto_level = goto_node->GetLevel(); - u32 label_level = goto_node->GetLevel(); - while (label_level > goto_level) { + u32 label_level = label->GetLevel(); + while (label_level < goto_level) { MoveOutward(goto_node); - goto_level++; + goto_level--; } + // TODO(Blinkhawk): Implement Lifting and Inward Movements } if (label->GetParent() == goto_node->GetParent()) { bool is_loop = false; @@ -375,13 +404,11 @@ void ASTManager::Decompile() { } it++; } - /* for (ASTNode label : labels) { auto& manager = label->GetManager(); manager.Remove(label); } labels.clear(); - */ } bool ASTManager::IndirectlyRelated(ASTNode first, ASTNode second) { @@ -410,87 +437,149 @@ bool ASTManager::DirectlyRelated(ASTNode first, ASTNode second) { max = second; } - while (min_level < max_level) { - min_level++; - min = min->GetParent(); + while (max_level > min_level) { + max_level--; + max = max->GetParent(); } return (min->GetParent() == max->GetParent()); } +void ASTManager::ShowCurrentState(std::string state) { + LOG_CRITICAL(HW_GPU, "\nState {}:\n\n{}\n", state, Print()); + SanityCheck(); +} + +void ASTManager::SanityCheck() { + for (auto label : labels) { + if (!label->GetParent()) { + LOG_CRITICAL(HW_GPU, "Sanity Check Failed"); + } + } +} + void ASTManager::EncloseDoWhile(ASTNode goto_node, ASTNode label) { + // ShowCurrentState("Before DoWhile Enclose"); + enclose_count++; ASTZipper& zipper = goto_node->GetManager(); ASTNode loop_start = label->GetNext(); if (loop_start == goto_node) { zipper.Remove(goto_node); + // ShowCurrentState("Ignore DoWhile Enclose"); return; } ASTNode parent = label->GetParent(); Expr condition = goto_node->GetGotoCondition(); zipper.DetachSegment(loop_start, goto_node); - ASTNode do_while_node = ASTBase::Make(parent, condition, ASTZipper(loop_start)); - zipper.InsertAfter(do_while_node, label); + ASTNode do_while_node = ASTBase::Make(parent, condition); ASTZipper* sub_zipper = do_while_node->GetSubNodes(); - sub_zipper->SetParent(do_while_node); + sub_zipper->Init(loop_start, do_while_node); + zipper.InsertAfter(do_while_node, label); sub_zipper->Remove(goto_node); + // ShowCurrentState("After DoWhile Enclose"); } void ASTManager::EncloseIfThen(ASTNode goto_node, ASTNode label) { + // ShowCurrentState("Before IfThen Enclose"); + enclose_count++; ASTZipper& zipper = goto_node->GetManager(); ASTNode if_end = label->GetPrevious(); if (if_end == goto_node) { zipper.Remove(goto_node); + // ShowCurrentState("Ignore IfThen Enclose"); return; } ASTNode prev = goto_node->GetPrevious(); - ASTNode parent = label->GetParent(); Expr condition = goto_node->GetGotoCondition(); - Expr neg_condition = MakeExpr(condition); + bool do_else = false; + if (prev->IsIfThen()) { + Expr if_condition = prev->GetIfCondition(); + do_else = ExprAreEqual(if_condition, condition); + } + ASTNode parent = label->GetParent(); zipper.DetachSegment(goto_node, if_end); - ASTNode if_node = ASTBase::Make(parent, condition, ASTZipper(goto_node)); - zipper.InsertAfter(if_node, prev); + ASTNode if_node; + if (do_else) { + if_node = ASTBase::Make(parent); + } else { + Expr neg_condition = MakeExprNot(condition); + if_node = ASTBase::Make(parent, neg_condition); + } ASTZipper* sub_zipper = if_node->GetSubNodes(); - sub_zipper->SetParent(if_node); + sub_zipper->Init(goto_node, if_node); + zipper.InsertAfter(if_node, prev); sub_zipper->Remove(goto_node); + // ShowCurrentState("After IfThen Enclose"); } void ASTManager::MoveOutward(ASTNode goto_node) { + // ShowCurrentState("Before MoveOutward"); + outward_count++; ASTZipper& zipper = goto_node->GetManager(); ASTNode parent = goto_node->GetParent(); + ASTZipper& zipper2 = parent->GetManager(); + ASTNode grandpa = parent->GetParent(); bool is_loop = parent->IsLoop(); - bool is_if = parent->IsIfThen() || parent->IsIfElse(); + bool is_else = parent->IsIfElse(); + bool is_if = parent->IsIfThen(); ASTNode prev = goto_node->GetPrevious(); + ASTNode post = goto_node->GetNext(); Expr condition = goto_node->GetGotoCondition(); - u32 var_index = NewVariable(); - Expr var_condition = MakeExpr(var_index); - ASTNode var_node = ASTBase::Make(parent, var_index, condition); zipper.DetachSingle(goto_node); - zipper.InsertAfter(var_node, prev); - goto_node->SetGotoCondition(var_condition); if (is_loop) { + 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); + zipper2.InsertBefore(var_node_init, parent); + zipper.InsertAfter(var_node, prev); + goto_node->SetGotoCondition(var_condition); ASTNode break_node = ASTBase::Make(parent, var_condition); zipper.InsertAfter(break_node, var_node); - } else if (is_if) { - ASTNode post = var_node->GetNext(); + } else if (is_if || is_else) { if (post) { + 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); + if (is_if) { + zipper2.InsertBefore(var_node_init, parent); + } else { + zipper2.InsertBefore(var_node_init, parent->GetPrevious()); + } + zipper.InsertAfter(var_node, prev); + goto_node->SetGotoCondition(var_condition); zipper.DetachTail(post); - ASTNode if_node = ASTBase::Make(parent, var_condition, ASTZipper(post)); - zipper.InsertAfter(if_node, var_node); + ASTNode if_node = ASTBase::Make(parent, MakeExprNot(var_condition)); ASTZipper* sub_zipper = if_node->GetSubNodes(); - sub_zipper->SetParent(if_node); + sub_zipper->Init(post, if_node); + zipper.InsertAfter(if_node, var_node); + } else { + Expr if_condition; + if (is_if) { + if_condition = parent->GetIfCondition(); + } else { + ASTNode if_node = parent->GetPrevious(); + if_condition = MakeExprNot(if_node->GetIfCondition()); + } + Expr new_condition = MakeExprAnd(if_condition, condition); + goto_node->SetGotoCondition(new_condition); } } else { UNREACHABLE(); } - ASTZipper& zipper2 = parent->GetManager(); ASTNode next = parent->GetNext(); if (is_if && next && next->IsIfElse()) { zipper2.InsertAfter(goto_node, next); + goto_node->SetParent(grandpa); + // ShowCurrentState("After MoveOutward"); return; } zipper2.InsertAfter(goto_node, parent); + goto_node->SetParent(grandpa); + // ShowCurrentState("After MoveOutward"); } } // namespace VideoCommon::Shader diff --git a/src/video_core/shader/ast.h b/src/video_core/shader/ast.h index 22ac8884c..4276f66a9 100644 --- a/src/video_core/shader/ast.h +++ b/src/video_core/shader/ast.h @@ -4,6 +4,7 @@ #pragma once +#include #include #include #include @@ -21,6 +22,7 @@ class ASTProgram; class ASTIfThen; class ASTIfElse; class ASTBlockEncoded; +class ASTBlockDecoded; class ASTVarSet; class ASTGoto; class ASTLabel; @@ -28,7 +30,7 @@ class ASTDoWhile; class ASTReturn; class ASTBreak; -using ASTData = std::variant; using ASTNode = std::shared_ptr; @@ -43,7 +45,8 @@ enum class ASTZipperType : u32 { class ASTZipper final { public: ASTZipper(); - ASTZipper(ASTNode first); + + void Init(ASTNode first, ASTNode parent); ASTNode GetFirst() { return first; @@ -56,7 +59,7 @@ public: void PushBack(ASTNode new_node); void PushFront(ASTNode new_node); void InsertAfter(ASTNode new_node, ASTNode at_node); - void SetParent(ASTNode new_parent); + void InsertBefore(ASTNode new_node, ASTNode at_node); void DetachTail(ASTNode node); void DetachSingle(ASTNode node); void DetachSegment(ASTNode start, ASTNode end); @@ -74,14 +77,14 @@ public: class ASTIfThen { public: - ASTIfThen(Expr condition, ASTZipper nodes) : condition(condition), nodes{nodes} {} + ASTIfThen(Expr condition) : condition(condition), nodes{} {} Expr condition; ASTZipper nodes; }; class ASTIfElse { public: - ASTIfElse(ASTZipper nodes) : nodes{nodes} {} + ASTIfElse() : nodes{} {} ASTZipper nodes; }; @@ -92,6 +95,12 @@ public: u32 end; }; +class ASTBlockDecoded { +public: + ASTBlockDecoded(NodeBlock& new_nodes) : nodes(std::move(new_nodes)) {} + NodeBlock nodes; +}; + class ASTVarSet { public: ASTVarSet(u32 index, Expr condition) : index{index}, condition{condition} {} @@ -114,7 +123,7 @@ public: class ASTDoWhile { public: - ASTDoWhile(Expr condition, ASTZipper nodes) : condition(condition), nodes{nodes} {} + ASTDoWhile(Expr condition) : condition(condition), nodes{} {} Expr condition; ASTZipper nodes; }; @@ -132,6 +141,8 @@ public: Expr condition; }; +using TransformCallback = std::function; + class ASTBase { public: explicit ASTBase(ASTNode parent, ASTData data) : parent{parent}, data{data} {} @@ -195,6 +206,14 @@ public: return nullptr; } + Expr GetIfCondition() const { + auto inner = std::get_if(&data); + if (inner) { + return inner->condition; + } + return nullptr; + } + void SetGotoCondition(Expr new_condition) { auto inner = std::get_if(&data); if (inner) { @@ -210,6 +229,18 @@ public: return std::holds_alternative(data); } + bool IsBlockEncoded() const { + 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); + data = ASTBlockDecoded(nodes); + } + bool IsLoop() const { return std::holds_alternative(data); } @@ -245,6 +276,7 @@ public: explicit ASTManager() { main_node = ASTBase::Make(ASTNode{}); program = std::get_if(main_node->GetInnerData()); + true_condition = MakeExpr(true); } void DeclareLabel(u32 address) { @@ -283,7 +315,13 @@ public: void Decompile(); + void ShowCurrentState(std::string state); + void SanityCheck(); + + bool IsFullyDecompiled() { + return gotos.size() == 0; + } private: bool IndirectlyRelated(ASTNode first, ASTNode second); @@ -309,6 +347,9 @@ private: u32 variables{}; ASTProgram* program; ASTNode main_node; + Expr true_condition; + u32 outward_count{}; + u32 enclose_count{}; }; } // namespace VideoCommon::Shader diff --git a/src/video_core/shader/control_flow.cpp b/src/video_core/shader/control_flow.cpp index bea7f767c..7a21d870f 100644 --- a/src/video_core/shader/control_flow.cpp +++ b/src/video_core/shader/control_flow.cpp @@ -423,7 +423,16 @@ void InsertBranch(ASTManager& mm, const BlockBranchInfo& branch) { result = MakeExpr(cond.cc); } if (cond.predicate != Pred::UnusedIndex) { - Expr extra = MakeExpr(cond.predicate); + u32 pred = static_cast(cond.predicate); + bool negate; + if (pred > 7) { + negate = true; + pred -= 8; + } + Expr extra = MakeExpr(pred); + if (negate) { + extra = MakeExpr(extra); + } if (result) { return MakeExpr(extra, result); } @@ -460,8 +469,9 @@ void DecompileShader(CFGRebuildState& state) { InsertBranch(manager, block.branch); } } + //manager.ShowCurrentState("Before Decompiling"); manager.Decompile(); - LOG_CRITICAL(HW_GPU, "Decompiled Shader:\n{} \n", manager.Print()); + //manager.ShowCurrentState("After Decompiling"); } std::optional ScanFlow(const ProgramCode& program_code, diff --git a/src/video_core/shader/expr.cpp b/src/video_core/shader/expr.cpp new file mode 100644 index 000000000..ebce6339b --- /dev/null +++ b/src/video_core/shader/expr.cpp @@ -0,0 +1,75 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include + +#include "video_core/shader/expr.h" + +namespace VideoCommon::Shader { + +bool ExprAnd::operator==(const ExprAnd& b) const { + return (*operand1 == *b.operand1) && (*operand2 == *b.operand2); +} + +bool ExprOr::operator==(const ExprOr& b) const { + return (*operand1 == *b.operand1) && (*operand2 == *b.operand2); +} + +bool ExprNot::operator==(const ExprNot& b) const { + return (*operand1 == *b.operand1); +} + +bool ExprIsBoolean(Expr expr) { + return std::holds_alternative(*expr); +} + +bool ExprBooleanGet(Expr expr) { + return std::get_if(expr.get())->value; +} + +Expr MakeExprNot(Expr first) { + if (std::holds_alternative(*first)) { + return std::get_if(first.get())->operand1; + } + return MakeExpr(first); +} + +Expr MakeExprAnd(Expr first, Expr second) { + if (ExprIsBoolean(first)) { + return ExprBooleanGet(first) ? second : first; + } + if (ExprIsBoolean(second)) { + return ExprBooleanGet(second) ? first : second; + } + return MakeExpr(first, second); +} + +Expr MakeExprOr(Expr first, Expr second) { + if (ExprIsBoolean(first)) { + return ExprBooleanGet(first) ? first : second; + } + if (ExprIsBoolean(second)) { + return ExprBooleanGet(second) ? second : first; + } + return MakeExpr(first, second); +} + +bool ExprAreEqual(Expr first, Expr second) { + return (*first) == (*second); +} + +bool ExprAreOpposite(Expr first, Expr second) { + if (std::holds_alternative(*first)) { + return ExprAreEqual(std::get_if(first.get())->operand1, second); + } + if (std::holds_alternative(*second)) { + return ExprAreEqual(std::get_if(second.get())->operand1, first); + } + return false; +} + +} // namespace VideoCommon::Shader diff --git a/src/video_core/shader/expr.h b/src/video_core/shader/expr.h index 94678f09a..f012f6fcf 100644 --- a/src/video_core/shader/expr.h +++ b/src/video_core/shader/expr.h @@ -30,6 +30,8 @@ class ExprAnd final { public: ExprAnd(Expr a, Expr b) : operand1{a}, operand2{b} {} + bool operator==(const ExprAnd& b) const; + Expr operand1; Expr operand2; }; @@ -38,6 +40,8 @@ class ExprOr final { public: ExprOr(Expr a, Expr b) : operand1{a}, operand2{b} {} + bool operator==(const ExprOr& b) const; + Expr operand1; Expr operand2; }; @@ -46,6 +50,8 @@ class ExprNot final { public: ExprNot(Expr a) : operand1{a} {} + bool operator==(const ExprNot& b) const; + Expr operand1; }; @@ -53,20 +59,32 @@ class ExprVar final { public: ExprVar(u32 index) : var_index{index} {} + bool operator==(const ExprVar& b) const { + return var_index == b.var_index; + } + u32 var_index; }; class ExprPredicate final { public: - ExprPredicate(Pred predicate) : predicate{predicate} {} + ExprPredicate(u32 predicate) : predicate{predicate} {} - Pred predicate; + bool operator==(const ExprPredicate& b) const { + return predicate == b.predicate; + } + + u32 predicate; }; class ExprCondCode final { public: ExprCondCode(ConditionCode cc) : cc{cc} {} + bool operator==(const ExprCondCode& b) const { + return cc == b.cc; + } + ConditionCode cc; }; @@ -74,6 +92,10 @@ class ExprBoolean final { public: ExprBoolean(bool val) : value{val} {} + bool operator==(const ExprBoolean& b) const { + return value == b.value; + } + bool value; }; @@ -83,4 +105,14 @@ Expr MakeExpr(Args&&... args) { return std::make_shared(T(std::forward(args)...)); } +bool ExprAreEqual(Expr first, Expr second); + +bool ExprAreOpposite(Expr first, Expr second); + +Expr MakeExprNot(Expr first); + +Expr MakeExprAnd(Expr first, Expr second); + +Expr MakeExprOr(Expr first, Expr second); + } // namespace VideoCommon::Shader From 6fdd501113d5094f9148046c3b17cf2239e99aa5 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Fri, 28 Jun 2019 22:59:43 -0400 Subject: [PATCH 04/19] shader_ir: Declare Manager and pass it to appropiate programs. --- src/video_core/shader/ast.cpp | 139 ++++++++++++++++++++++++- src/video_core/shader/ast.h | 74 ++++++------- src/video_core/shader/control_flow.cpp | 65 ++++++------ src/video_core/shader/control_flow.h | 8 +- src/video_core/shader/decode.cpp | 28 +---- src/video_core/shader/shader_ir.cpp | 2 +- src/video_core/shader/shader_ir.h | 2 + 7 files changed, 214 insertions(+), 104 deletions(-) diff --git a/src/video_core/shader/ast.cpp b/src/video_core/shader/ast.cpp index d521a7b52..0bf289f98 100644 --- a/src/video_core/shader/ast.cpp +++ b/src/video_core/shader/ast.cpp @@ -363,6 +363,71 @@ std::string ASTManager::Print() { return printer.GetResult(); } +ASTManager::ASTManager() = default; + +ASTManager::~ASTManager() { + Clear(); +} + +void ASTManager::Init() { + main_node = ASTBase::Make(ASTNode{}); + program = std::get_if(main_node->GetInnerData()); + true_condition = MakeExpr(true); +} + +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} { + other.main_node.reset(); +} + +ASTManager& ASTManager::operator=(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; + + other.main_node.reset(); + return *this; +} + +void ASTManager::DeclareLabel(u32 address) { + const auto pair = labels_map.emplace(address, labels_count); + if (pair.second) { + labels_count++; + labels.resize(labels_count); + } +} + +void ASTManager::InsertLabel(u32 address) { + u32 index = labels_map[address]; + ASTNode label = ASTBase::Make(main_node, index); + labels[index] = label; + program->nodes.PushBack(label); +} + +void ASTManager::InsertGoto(Expr condition, u32 address) { + u32 index = labels_map[address]; + ASTNode goto_node = ASTBase::Make(main_node, condition, index); + gotos.push_back(goto_node); + program->nodes.PushBack(goto_node); +} + +void ASTManager::InsertBlock(u32 start_address, u32 end_address) { + ASTNode block = ASTBase::Make(main_node, start_address, end_address); + program->nodes.PushBack(block); +} + +void ASTManager::InsertReturn(Expr condition, bool kills) { + ASTNode node = ASTBase::Make(main_node, condition, kills); + program->nodes.PushBack(node); +} + void ASTManager::Decompile() { auto it = gotos.begin(); while (it != gotos.end()) { @@ -460,7 +525,6 @@ void ASTManager::SanityCheck() { void ASTManager::EncloseDoWhile(ASTNode goto_node, ASTNode label) { // ShowCurrentState("Before DoWhile Enclose"); - enclose_count++; ASTZipper& zipper = goto_node->GetManager(); ASTNode loop_start = label->GetNext(); if (loop_start == goto_node) { @@ -481,7 +545,6 @@ void ASTManager::EncloseDoWhile(ASTNode goto_node, ASTNode label) { void ASTManager::EncloseIfThen(ASTNode goto_node, ASTNode label) { // ShowCurrentState("Before IfThen Enclose"); - enclose_count++; ASTZipper& zipper = goto_node->GetManager(); ASTNode if_end = label->GetPrevious(); if (if_end == goto_node) { @@ -514,7 +577,6 @@ void ASTManager::EncloseIfThen(ASTNode goto_node, ASTNode label) { void ASTManager::MoveOutward(ASTNode goto_node) { // ShowCurrentState("Before MoveOutward"); - outward_count++; ASTZipper& zipper = goto_node->GetManager(); ASTNode parent = goto_node->GetParent(); ASTZipper& zipper2 = parent->GetManager(); @@ -582,4 +644,75 @@ void ASTManager::MoveOutward(ASTNode goto_node) { // ShowCurrentState("After MoveOutward"); } +class ASTClearer { +public: + ASTClearer() = default; + + 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) { + ast.nodes.clear(); + } + + 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()); + node->Clear(); + } +}; + +void ASTManager::Clear() { + if (!main_node) { + return; + } + ASTClearer clearer{}; + clearer.Visit(main_node); + main_node.reset(); + program = nullptr; + labels_map.clear(); + labels.clear(); + gotos.clear(); +} + } // namespace VideoCommon::Shader diff --git a/src/video_core/shader/ast.h b/src/video_core/shader/ast.h index 4276f66a9..958989bcd 100644 --- a/src/video_core/shader/ast.h +++ b/src/video_core/shader/ast.h @@ -30,8 +30,8 @@ class ASTDoWhile; class ASTReturn; class ASTBreak; -using ASTData = std::variant; +using ASTData = std::variant; using ASTNode = std::shared_ptr; @@ -261,6 +261,13 @@ public: return nullptr; } + void Clear() { + next.reset(); + previous.reset(); + parent.reset(); + manager = nullptr; + } + private: friend class ASTZipper; @@ -273,43 +280,26 @@ private: class ASTManager final { public: - explicit ASTManager() { - main_node = ASTBase::Make(ASTNode{}); - program = std::get_if(main_node->GetInnerData()); - true_condition = MakeExpr(true); - } + ASTManager(); + ~ASTManager(); - void DeclareLabel(u32 address) { - const auto pair = labels_map.emplace(address, labels_count); - if (pair.second) { - labels_count++; - labels.resize(labels_count); - } - } + ASTManager(const ASTManager& o) = delete; + ASTManager& operator=(const ASTManager& other) = delete; - void InsertLabel(u32 address) { - u32 index = labels_map[address]; - ASTNode label = ASTBase::Make(main_node, index); - labels[index] = label; - program->nodes.PushBack(label); - } + ASTManager(ASTManager&& other); + ASTManager& operator=(ASTManager&& other); - void InsertGoto(Expr condition, u32 address) { - u32 index = labels_map[address]; - ASTNode goto_node = ASTBase::Make(main_node, condition, index); - gotos.push_back(goto_node); - program->nodes.PushBack(goto_node); - } + void Init(); - void InsertBlock(u32 start_address, u32 end_address) { - ASTNode block = ASTBase::Make(main_node, start_address, end_address); - program->nodes.PushBack(block); - } + void DeclareLabel(u32 address); - void InsertReturn(Expr condition, bool kills) { - ASTNode node = ASTBase::Make(main_node, condition, kills); - program->nodes.PushBack(node); - } + void InsertLabel(u32 address); + + void InsertGoto(Expr condition, u32 address); + + void InsertBlock(u32 start_address, u32 end_address); + + void InsertReturn(Expr condition, bool kills); std::string Print(); @@ -323,6 +313,12 @@ public: return gotos.size() == 0; } + ASTNode GetProgram() { + return main_node; + } + + void Clear(); + private: bool IndirectlyRelated(ASTNode first, ASTNode second); @@ -332,7 +328,7 @@ private: void EncloseIfThen(ASTNode goto_node, ASTNode label); - void MoveOutward(ASTNode goto_node) ; + void MoveOutward(ASTNode goto_node); u32 NewVariable() { u32 new_var = variables; @@ -345,11 +341,9 @@ private: std::vector labels{}; std::list gotos{}; u32 variables{}; - ASTProgram* program; - ASTNode main_node; - Expr true_condition; - u32 outward_count{}; - u32 enclose_count{}; + ASTProgram* program{}; + ASTNode main_node{}; + Expr true_condition{}; }; } // namespace VideoCommon::Shader diff --git a/src/video_core/shader/control_flow.cpp b/src/video_core/shader/control_flow.cpp index 7a21d870f..deb3d3ebd 100644 --- a/src/video_core/shader/control_flow.cpp +++ b/src/video_core/shader/control_flow.cpp @@ -57,8 +57,8 @@ struct BlockInfo { struct CFGRebuildState { explicit CFGRebuildState(const ProgramCode& program_code, const std::size_t program_size, - const u32 start) - : start{start}, program_code{program_code}, program_size{program_size} {} + const u32 start, ASTManager& manager) + : program_code{program_code}, program_size{program_size}, start{start}, manager{manager} {} u32 start{}; std::vector block_info{}; @@ -71,6 +71,7 @@ struct CFGRebuildState { std::unordered_map stacks{}; const ProgramCode& program_code; const std::size_t program_size; + ASTManager& manager; }; enum class BlockCollision : u32 { None, Found, Inside }; @@ -455,29 +456,28 @@ void InsertBranch(ASTManager& mm, const BlockBranchInfo& branch) { } void DecompileShader(CFGRebuildState& state) { - ASTManager manager{}; + state.manager.Init(); for (auto label : state.labels) { - manager.DeclareLabel(label); + state.manager.DeclareLabel(label); } for (auto& block : state.block_info) { if (state.labels.count(block.start) != 0) { - manager.InsertLabel(block.start); + state.manager.InsertLabel(block.start); } u32 end = block.branch.ignore ? block.end + 1 : block.end; - manager.InsertBlock(block.start, end); + state.manager.InsertBlock(block.start, end); if (!block.branch.ignore) { - InsertBranch(manager, block.branch); + InsertBranch(state.manager, block.branch); } } - //manager.ShowCurrentState("Before Decompiling"); - manager.Decompile(); - //manager.ShowCurrentState("After Decompiling"); + // state.manager.ShowCurrentState("Before Decompiling"); + state.manager.Decompile(); + // state.manager.ShowCurrentState("After Decompiling"); } -std::optional ScanFlow(const ProgramCode& program_code, - std::size_t program_size, u32 start_address) { - CFGRebuildState state{program_code, program_size, start_address}; - +std::unique_ptr ScanFlow(const ProgramCode& program_code, u32 program_size, + u32 start_address, ASTManager& manager) { + CFGRebuildState state{program_code, program_size, start_address, manager}; // Inspect Code and generate blocks state.labels.clear(); state.labels.emplace(start_address); @@ -503,12 +503,21 @@ std::optional ScanFlow(const ProgramCode& program_code, [](const BlockInfo& a, const BlockInfo& b) -> bool { return a.start < b.start; }); if (decompiled) { DecompileShader(state); + decompiled = state.manager.IsFullyDecompiled(); + if (!decompiled) { + LOG_CRITICAL(HW_GPU, "Failed to remove all the gotos!:"); + state.manager.ShowCurrentState("Of Shader"); + state.manager.Clear(); + } } - ShaderCharacteristics result_out{}; - result_out.decompilable = decompiled; - result_out.start = start_address; - result_out.end = start_address; - for (const auto& block : state.block_info) { + auto result_out = std::make_unique(); + result_out->decompiled = decompiled; + result_out->start = start_address; + if (decompiled) { + result_out->end = state.block_info.back().end + 1; + return std::move(result_out); + } + for (auto& block : state.block_info) { ShaderBlock new_block{}; new_block.start = block.start; new_block.end = block.end; @@ -518,26 +527,20 @@ std::optional ScanFlow(const ProgramCode& program_code, new_block.branch.kills = block.branch.kill; new_block.branch.address = block.branch.address; } - result_out.end = std::max(result_out.end, block.end); - result_out.blocks.push_back(new_block); + result_out->end = std::max(result_out->end, block.end); + result_out->blocks.push_back(new_block); } - if (result_out.decompilable) { - result_out.labels = std::move(state.labels); - return {std::move(result_out)}; - } - - // If it's not decompilable, merge the unlabelled blocks together - auto back = result_out.blocks.begin(); + auto back = result_out->blocks.begin(); auto next = std::next(back); - while (next != result_out.blocks.end()) { + while (next != result_out->blocks.end()) { if (state.labels.count(next->start) == 0 && next->start == back->end + 1) { back->end = next->end; - next = result_out.blocks.erase(next); + next = result_out->blocks.erase(next); continue; } back = next; ++next; } - return {std::move(result_out)}; + return std::move(result_out); } } // namespace VideoCommon::Shader diff --git a/src/video_core/shader/control_flow.h b/src/video_core/shader/control_flow.h index efd037f1a..2805d975c 100644 --- a/src/video_core/shader/control_flow.h +++ b/src/video_core/shader/control_flow.h @@ -10,6 +10,7 @@ #include "video_core/engines/shader_bytecode.h" #include "video_core/shader/shader_ir.h" +#include "video_core/shader/ast.h" namespace VideoCommon::Shader { @@ -67,13 +68,12 @@ struct ShaderBlock { struct ShaderCharacteristics { std::list blocks{}; - bool decompilable{}; + bool decompiled{}; u32 start{}; u32 end{}; - std::set labels{}; }; -std::optional ScanFlow(const ProgramCode& program_code, - std::size_t program_size, u32 start_address); +std::unique_ptr ScanFlow(const ProgramCode& program_code, u32 program_size, + 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 47a9fd961..381e87415 100644 --- a/src/video_core/shader/decode.cpp +++ b/src/video_core/shader/decode.cpp @@ -39,36 +39,14 @@ void ShaderIR::Decode() { std::memcpy(&header, program_code.data(), sizeof(Tegra::Shader::Header)); disable_flow_stack = false; - const auto info = ScanFlow(program_code, program_size, main_offset); + const auto info = + ScanFlow(program_code, program_size, main_offset, program_manager); if (info) { const auto& shader_info = *info; coverage_begin = shader_info.start; coverage_end = shader_info.end; - if (shader_info.decompilable) { + if (shader_info.decompiled) { disable_flow_stack = true; - const auto insert_block = [this](NodeBlock& nodes, u32 label) { - if (label == static_cast(exit_branch)) { - return; - } - basic_blocks.insert({label, nodes}); - }; - const auto& blocks = shader_info.blocks; - NodeBlock current_block; - u32 current_label = static_cast(exit_branch); - for (auto& block : blocks) { - if (shader_info.labels.count(block.start) != 0) { - insert_block(current_block, current_label); - current_block.clear(); - current_label = block.start; - } - if (!block.ignore_branch) { - DecodeRangeInner(current_block, block.start, block.end); - InsertControlFlow(current_block, block); - } else { - DecodeRangeInner(current_block, block.start, block.end + 1); - } - } - insert_block(current_block, current_label); return; } LOG_WARNING(HW_GPU, "Flow Stack Removing Failed! Falling back to old method"); diff --git a/src/video_core/shader/shader_ir.cpp b/src/video_core/shader/shader_ir.cpp index 2c357f310..c79f80e04 100644 --- a/src/video_core/shader/shader_ir.cpp +++ b/src/video_core/shader/shader_ir.cpp @@ -23,7 +23,7 @@ using Tegra::Shader::PredOperation; using Tegra::Shader::Register; ShaderIR::ShaderIR(const ProgramCode& program_code, u32 main_offset, const std::size_t size) - : program_code{program_code}, main_offset{main_offset}, program_size{size} { + : program_code{program_code}, main_offset{main_offset}, program_size{size}, program_manager{} { Decode(); } diff --git a/src/video_core/shader/shader_ir.h b/src/video_core/shader/shader_ir.h index 6f666ee30..a91cd7d67 100644 --- a/src/video_core/shader/shader_ir.h +++ b/src/video_core/shader/shader_ir.h @@ -16,6 +16,7 @@ #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" namespace VideoCommon::Shader { @@ -364,6 +365,7 @@ private: std::map basic_blocks; NodeBlock global_code; + ASTManager program_manager; std::set used_registers; std::set used_predicates; From 38fc995f6cc2c2af29abc976ddb45b72873b2cc4 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sat, 29 Jun 2019 01:44:07 -0400 Subject: [PATCH 05/19] gl_shader_decompiler: Implement AST decompiling --- .../renderer_opengl/gl_shader_decompiler.cpp | 271 ++++++++++++++++-- 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 +- 11 files changed, 358 insertions(+), 63 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 8fa9e6534..2955c6abf 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -20,6 +20,7 @@ #include "video_core/renderer_opengl/gl_rasterizer.h" #include "video_core/renderer_opengl/gl_shader_decompiler.h" #include "video_core/shader/node.h" +#include "video_core/shader/ast.h" #include "video_core/shader/shader_ir.h" namespace OpenGL::GLShader { @@ -334,43 +335,26 @@ constexpr bool IsVertexShader(ProgramType stage) { return stage == ProgramType::VertexA || stage == ProgramType::VertexB; } +class ASTDecompiler; +class ExprDecompiler; + class GLSLDecompiler final { public: explicit GLSLDecompiler(const Device& device, const ShaderIR& ir, ProgramType stage, std::string suffix) : device{device}, ir{ir}, stage{stage}, suffix{suffix}, header{ir.GetHeader()} {} - void Decompile() { - DeclareVertex(); - DeclareGeometry(); - DeclareRegisters(); - DeclarePredicates(); - DeclareLocalMemory(); - DeclareSharedMemory(); - DeclareInternalFlags(); - DeclareInputAttributes(); - DeclareOutputAttributes(); - DeclareConstantBuffers(); - DeclareGlobalMemory(); - DeclareSamplers(); - DeclarePhysicalAttributeReader(); - DeclareImages(); - - code.AddLine("void execute_{}() {{", suffix); - ++code.scope; - + void DecompileBranchMode() { // VM's program counter const auto first_address = ir.GetBasicBlocks().begin()->first; code.AddLine("uint jmp_to = {}U;", first_address); // 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. - if (!ir.IsFlowStackDisabled()) { - constexpr u32 FLOW_STACK_SIZE = 20; - for (const auto stack : std::array{MetaStackClass::Ssy, MetaStackClass::Pbk}) { - code.AddLine("uint {}[{}];", FlowStackName(stack), FLOW_STACK_SIZE); - code.AddLine("uint {} = 0U;", FlowStackTopName(stack)); - } + constexpr u32 FLOW_STACK_SIZE = 20; + for (const auto stack : std::array{MetaStackClass::Ssy, MetaStackClass::Pbk}) { + code.AddLine("uint {}[{}];", FlowStackName(stack), FLOW_STACK_SIZE); + code.AddLine("uint {} = 0u;", FlowStackTopName(stack)); } code.AddLine("while (true) {{"); @@ -392,10 +376,37 @@ public: code.AddLine("default: return;"); code.AddLine("}}"); - for (std::size_t i = 0; i < 2; ++i) { - --code.scope; - code.AddLine("}}"); + --code.scope; + 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() { @@ -424,6 +435,9 @@ public: } private: + friend class ASTDecompiler; + friend class ExprDecompiler; + void DeclareVertex() { if (!IsVertexShader(stage)) return; @@ -1821,7 +1835,7 @@ private: return {}; } - Expression Exit(Operation operation) { + Expression WriteExit() { if (stage != ProgramType::Fragment) { code.AddLine("return;"); return {}; @@ -1861,6 +1875,10 @@ private: return {}; } + Expression Exit(Operation operation) { + return WriteExit(); + } + Expression Discard(Operation operation) { // Enclose "discard" in a conditional, so that GLSL compilation does not complain // about unexecuted instructions that may follow this. @@ -2253,6 +2271,201 @@ private: 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(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(&*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(&*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 std::string GetCommonDeclarations() { 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{}; From 47e4f6a52c5eb34916e2c1f4c876e6e8624e3840 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Fri, 16 Aug 2019 16:25:02 -0400 Subject: [PATCH 06/19] Shader_Ir: Refactor Decompilation process and allow multiple decompilation modes. --- CMakeModules/GenerateSCMRev.cmake | 2 + src/common/CMakeLists.txt | 2 + src/video_core/CMakeLists.txt | 2 + .../renderer_opengl/gl_shader_decompiler.cpp | 8 +- .../renderer_opengl/gl_shader_gen.cpp | 25 ++++- src/video_core/shader/ast.cpp | 98 ++++++++++++++++++- src/video_core/shader/ast.h | 20 +++- src/video_core/shader/compiler_settings.cpp | 26 +++++ src/video_core/shader/compiler_settings.h | 25 +++++ src/video_core/shader/control_flow.cpp | 92 +++++++++++------ src/video_core/shader/control_flow.h | 10 +- src/video_core/shader/decode.cpp | 86 +++++++++++----- src/video_core/shader/decode/other.cpp | 8 +- src/video_core/shader/shader_ir.cpp | 6 +- src/video_core/shader/shader_ir.h | 10 +- 15 files changed, 338 insertions(+), 82 deletions(-) create mode 100644 src/video_core/shader/compiler_settings.cpp create mode 100644 src/video_core/shader/compiler_settings.h diff --git a/CMakeModules/GenerateSCMRev.cmake b/CMakeModules/GenerateSCMRev.cmake index d3ab22c08..09eabe2c7 100644 --- a/CMakeModules/GenerateSCMRev.cmake +++ b/CMakeModules/GenerateSCMRev.cmake @@ -87,6 +87,8 @@ set(HASH_FILES "${VIDEO_CORE}/shader/ast.h" "${VIDEO_CORE}/shader/control_flow.cpp" "${VIDEO_CORE}/shader/control_flow.h" + "${VIDEO_CORE}/shader/compiler_settings.cpp" + "${VIDEO_CORE}/shader/compiler_settings.h" "${VIDEO_CORE}/shader/decode.cpp" "${VIDEO_CORE}/shader/expr.cpp" "${VIDEO_CORE}/shader/expr.h" diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index afc5ff736..0ed96c0d4 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -64,6 +64,8 @@ add_custom_command(OUTPUT scm_rev.cpp "${VIDEO_CORE}/shader/ast.h" "${VIDEO_CORE}/shader/control_flow.cpp" "${VIDEO_CORE}/shader/control_flow.h" + "${VIDEO_CORE}/shader/compiler_settings.cpp" + "${VIDEO_CORE}/shader/compiler_settings.h" "${VIDEO_CORE}/shader/decode.cpp" "${VIDEO_CORE}/shader/expr.cpp" "${VIDEO_CORE}/shader/expr.h" diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 33fa88762..eaa694ff8 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -109,6 +109,8 @@ add_library(video_core STATIC shader/ast.h shader/control_flow.cpp shader/control_flow.h + shader/compiler_settings.cpp + shader/compiler_settings.h shader/decode.cpp shader/expr.cpp shader/expr.h diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 2955c6abf..b8c3442bc 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -352,9 +352,11 @@ public: // 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. constexpr u32 FLOW_STACK_SIZE = 20; - for (const auto stack : std::array{MetaStackClass::Ssy, MetaStackClass::Pbk}) { - code.AddLine("uint {}[{}];", FlowStackName(stack), FLOW_STACK_SIZE); - code.AddLine("uint {} = 0u;", FlowStackTopName(stack)); + if (!ir.IsFlowStackDisabled()) { + for (const auto stack : std::array{MetaStackClass::Ssy, MetaStackClass::Pbk}) { + code.AddLine("uint {}[{}];", FlowStackName(stack), FLOW_STACK_SIZE); + code.AddLine("uint {} = 0u;", FlowStackTopName(stack)); + } } code.AddLine("while (true) {{"); diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index 3a8d9e1da..72a49ebdc 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -11,6 +11,8 @@ namespace OpenGL::GLShader { using Tegra::Engines::Maxwell3D; +using VideoCommon::Shader::CompileDepth; +using VideoCommon::Shader::CompilerSettings; using VideoCommon::Shader::ProgramCode; using VideoCommon::Shader::ShaderIR; @@ -31,13 +33,17 @@ layout (std140, binding = EMULATION_UBO_BINDING) uniform vs_config { )"; - const ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET, setup.program.size_a); + CompilerSettings settings; + settings.depth = CompileDepth::NoFlowStack; + + const ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET, setup.program.size_a, settings); const auto stage = setup.IsDualProgram() ? ProgramType::VertexA : ProgramType::VertexB; ProgramResult program = Decompile(device, program_ir, stage, "vertex"); out += program.first; if (setup.IsDualProgram()) { - const ShaderIR program_ir_b(setup.program.code_b, PROGRAM_OFFSET, setup.program.size_b); + const ShaderIR program_ir_b(setup.program.code_b, PROGRAM_OFFSET, setup.program.size_b, + settings); ProgramResult program_b = Decompile(device, program_ir_b, ProgramType::VertexB, "vertex_b"); out += program_b.first; } @@ -80,7 +86,10 @@ layout (std140, binding = EMULATION_UBO_BINDING) uniform gs_config { )"; - const ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET, setup.program.size_a); + CompilerSettings settings; + settings.depth = CompileDepth::NoFlowStack; + + const ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET, setup.program.size_a, settings); ProgramResult program = Decompile(device, program_ir, ProgramType::Geometry, "geometry"); out += program.first; @@ -114,7 +123,10 @@ layout (std140, binding = EMULATION_UBO_BINDING) uniform fs_config { }; )"; - const ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET, setup.program.size_a); + CompilerSettings settings; + settings.depth = CompileDepth::NoFlowStack; + + const ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET, setup.program.size_a, settings); ProgramResult program = Decompile(device, program_ir, ProgramType::Fragment, "fragment"); out += program.first; @@ -133,7 +145,10 @@ ProgramResult GenerateComputeShader(const Device& device, const ShaderSetup& set std::string out = "// Shader Unique Id: CS" + id + "\n\n"; out += GetCommonDeclarations(); - const ShaderIR program_ir(setup.program.code, COMPUTE_OFFSET, setup.program.size_a); + CompilerSettings settings; + settings.depth = CompileDepth::NoFlowStack; + + const ShaderIR program_ir(setup.program.code, COMPUTE_OFFSET, setup.program.size_a, settings); ProgramResult program = Decompile(device, program_ir, ProgramType::Compute, "compute"); out += program.first; diff --git a/src/video_core/shader/ast.cpp b/src/video_core/shader/ast.cpp index 68a96cc79..14c50e1c6 100644 --- a/src/video_core/shader/ast.cpp +++ b/src/video_core/shader/ast.cpp @@ -363,7 +363,7 @@ std::string ASTManager::Print() { return printer.GetResult(); } -ASTManager::ASTManager() = default; +ASTManager::ASTManager(bool full_decompile) : full_decompile{full_decompile} {}; ASTManager::~ASTManager() { Clear(); @@ -383,6 +383,7 @@ ASTManager::ASTManager(ASTManager&& other) } ASTManager& ASTManager::operator=(ASTManager&& other) { + full_decompile = other.full_decompile; labels_map = std::move(other.labels_map); labels_count = other.labels_count; gotos = std::move(other.gotos); @@ -434,6 +435,13 @@ void ASTManager::Decompile() { ASTNode goto_node = *it; u32 label_index = goto_node->GetGotoLabel(); ASTNode label = labels[label_index]; + if (!full_decompile) { + // We only decompile backward jumps + if (!IsBackwardsJump(goto_node, label)) { + it++; + continue; + } + } if (IndirectlyRelated(goto_node, label)) { while (!DirectlyRelated(goto_node, label)) { MoveOutward(goto_node); @@ -469,11 +477,91 @@ void ASTManager::Decompile() { } it++; } - for (ASTNode label : labels) { - auto& manager = label->GetManager(); - manager.Remove(label); + if (full_decompile) { + for (ASTNode label : labels) { + auto& manager = label->GetManager(); + manager.Remove(label); + } + labels.clear(); + } else { + auto it = labels.begin(); + while (it != labels.end()) { + bool can_remove = true; + ASTNode label = *it; + for (ASTNode goto_node : gotos) { + u32 label_index = goto_node->GetGotoLabel(); + ASTNode glabel = labels[label_index]; + if (glabel == label) { + can_remove = false; + break; + } + } + if (can_remove) { + auto& manager = label->GetManager(); + manager.Remove(label); + labels.erase(it); + } + } } - labels.clear(); +} + +bool ASTManager::IsBackwardsJump(ASTNode goto_node, ASTNode label_node) const { + u32 goto_level = goto_node->GetLevel(); + u32 label_level = label_node->GetLevel(); + while (goto_level > label_level) { + goto_level--; + goto_node = goto_node->GetParent(); + } + while (label_level > goto_level) { + label_level--; + label_node = label_node->GetParent(); + } + while (goto_node->GetParent() != label_node->GetParent()) { + goto_node = goto_node->GetParent(); + label_node = label_node->GetParent(); + } + ASTNode current = goto_node->GetPrevious(); + while (current) { + if (current == label_node) { + return true; + } + current = current->GetPrevious(); + } + return false; +} + +ASTNode CommonParent(ASTNode first, ASTNode second) { + if (first->GetParent() == second->GetParent()) { + return first->GetParent(); + } + u32 first_level = first->GetLevel(); + u32 second_level = second->GetLevel(); + u32 min_level; + u32 max_level; + ASTNode max; + ASTNode min; + if (first_level > second_level) { + min_level = second_level; + min = second; + max_level = first_level; + max = first; + } else { + min_level = first_level; + min = first; + max_level = second_level; + max = second; + } + + while (max_level > min_level) { + max_level--; + max = max->GetParent(); + } + + while (min->GetParent() != max->GetParent()) { + min = min->GetParent(); + max = max->GetParent(); + } + return min->GetParent(); } bool ASTManager::IndirectlyRelated(ASTNode first, ASTNode second) { diff --git a/src/video_core/shader/ast.h b/src/video_core/shader/ast.h index 06ab20cc5..849d0612c 100644 --- a/src/video_core/shader/ast.h +++ b/src/video_core/shader/ast.h @@ -274,7 +274,7 @@ private: class ASTManager final { public: - ASTManager(); + ASTManager(bool full_decompile); ~ASTManager(); ASTManager(const ASTManager& o) = delete; @@ -304,7 +304,18 @@ public: void SanityCheck(); bool IsFullyDecompiled() const { - return gotos.size() == 0; + if (full_decompile) { + return gotos.size() == 0; + } else { + for (ASTNode goto_node : gotos) { + u32 label_index = goto_node->GetGotoLabel(); + ASTNode glabel = labels[label_index]; + if (IsBackwardsJump(goto_node, glabel)) { + return false; + } + } + return true; + } } ASTNode GetProgram() const { @@ -318,6 +329,10 @@ public: } private: + bool IsBackwardsJump(ASTNode goto_node, ASTNode label_node) const; + + ASTNode CommonParent(ASTNode first, ASTNode second); + bool IndirectlyRelated(ASTNode first, ASTNode second); bool DirectlyRelated(ASTNode first, ASTNode second); @@ -334,6 +349,7 @@ private: return new_var; } + bool full_decompile{}; std::unordered_map labels_map{}; u32 labels_count{}; std::vector labels{}; diff --git a/src/video_core/shader/compiler_settings.cpp b/src/video_core/shader/compiler_settings.cpp new file mode 100644 index 000000000..cddcbd4f0 --- /dev/null +++ b/src/video_core/shader/compiler_settings.cpp @@ -0,0 +1,26 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "video_core/shader/compiler_settings.h" + +namespace VideoCommon::Shader { + +std::string CompileDepthAsString(const CompileDepth cd) { + switch (cd) { + case CompileDepth::BruteForce: + return "Brute Force Compile"; + case CompileDepth::FlowStack: + return "Simple Flow Stack Mode"; + case CompileDepth::NoFlowStack: + return "Remove Flow Stack"; + case CompileDepth::DecompileBackwards: + return "Decompile Backward Jumps"; + case CompileDepth::FullDecompile: + return "Full Decompilation"; + default: + return "Unknown Compiler Process"; + } +} + +} // namespace VideoCommon::Shader diff --git a/src/video_core/shader/compiler_settings.h b/src/video_core/shader/compiler_settings.h new file mode 100644 index 000000000..e1fb5bc3a --- /dev/null +++ b/src/video_core/shader/compiler_settings.h @@ -0,0 +1,25 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "video_core/engines/shader_bytecode.h" + +namespace VideoCommon::Shader { + +enum class CompileDepth : u32 { + BruteForce = 0, + FlowStack = 1, + NoFlowStack = 2, + DecompileBackwards = 3, + FullDecompile = 4, +}; + +std::string CompileDepthAsString(CompileDepth cd); + +struct CompilerSettings { + CompileDepth depth; +}; + +} // namespace VideoCommon::Shader diff --git a/src/video_core/shader/control_flow.cpp b/src/video_core/shader/control_flow.cpp index a29922815..c4351969b 100644 --- a/src/video_core/shader/control_flow.cpp +++ b/src/video_core/shader/control_flow.cpp @@ -57,8 +57,8 @@ struct BlockInfo { struct CFGRebuildState { explicit CFGRebuildState(const ProgramCode& program_code, const std::size_t program_size, - const u32 start, ASTManager& manager) - : program_code{program_code}, program_size{program_size}, start{start}, manager{manager} {} + const u32 start) + : program_code{program_code}, program_size{program_size}, start{start} {} u32 start{}; std::vector block_info{}; @@ -71,7 +71,7 @@ struct CFGRebuildState { std::unordered_map stacks{}; const ProgramCode& program_code; const std::size_t program_size; - ASTManager& manager; + ASTManager* manager; }; enum class BlockCollision : u32 { None, Found, Inside }; @@ -456,67 +456,91 @@ void InsertBranch(ASTManager& mm, const BlockBranchInfo& branch) { } void DecompileShader(CFGRebuildState& state) { - state.manager.Init(); + state.manager->Init(); for (auto label : state.labels) { - state.manager.DeclareLabel(label); + state.manager->DeclareLabel(label); } for (auto& block : state.block_info) { if (state.labels.count(block.start) != 0) { - state.manager.InsertLabel(block.start); + state.manager->InsertLabel(block.start); } u32 end = block.branch.ignore ? block.end + 1 : block.end; - state.manager.InsertBlock(block.start, end); + state.manager->InsertBlock(block.start, end); if (!block.branch.ignore) { - InsertBranch(state.manager, block.branch); + InsertBranch(*state.manager, block.branch); } } - // state.manager.ShowCurrentState("Before Decompiling"); - state.manager.Decompile(); - // state.manager.ShowCurrentState("After Decompiling"); + state.manager->Decompile(); } std::unique_ptr ScanFlow(const ProgramCode& program_code, u32 program_size, - u32 start_address, ASTManager& manager) { - CFGRebuildState state{program_code, program_size, start_address, manager}; + u32 start_address, + const CompilerSettings& settings) { + auto result_out = std::make_unique(); + if (settings.depth == CompileDepth::BruteForce) { + result_out->settings.depth = CompileDepth::BruteForce; + return std::move(result_out); + } + + CFGRebuildState state{program_code, program_size, start_address}; // Inspect Code and generate blocks state.labels.clear(); state.labels.emplace(start_address); state.inspect_queries.push_back(state.start); while (!state.inspect_queries.empty()) { if (!TryInspectAddress(state)) { - return {}; + result_out->settings.depth = CompileDepth::BruteForce; + return std::move(result_out); } } - // Decompile Stacks - state.queries.push_back(Query{state.start, {}, {}}); - bool decompiled = true; - while (!state.queries.empty()) { - if (!TryQuery(state)) { - decompiled = false; - break; + bool use_flow_stack = true; + + bool decompiled = false; + + if (settings.depth != CompileDepth::FlowStack) { + // Decompile Stacks + state.queries.push_back(Query{state.start, {}, {}}); + decompiled = true; + while (!state.queries.empty()) { + if (!TryQuery(state)) { + decompiled = false; + break; + } } } + use_flow_stack = !decompiled; + // Sort and organize results std::sort(state.block_info.begin(), state.block_info.end(), [](const BlockInfo& a, const BlockInfo& b) -> bool { return a.start < b.start; }); - if (decompiled) { + if (decompiled && settings.depth != CompileDepth::NoFlowStack) { + ASTManager manager{settings.depth != CompileDepth::DecompileBackwards}; + state.manager = &manager; DecompileShader(state); - decompiled = state.manager.IsFullyDecompiled(); + decompiled = state.manager->IsFullyDecompiled(); if (!decompiled) { - LOG_CRITICAL(HW_GPU, "Failed to remove all the gotos!:"); - state.manager.ShowCurrentState("Of Shader"); - state.manager.Clear(); + if (settings.depth == CompileDepth::FullDecompile) { + LOG_CRITICAL(HW_GPU, "Failed to remove all the gotos!:"); + } else { + LOG_CRITICAL(HW_GPU, "Failed to remove all backward gotos!:"); + } + state.manager->ShowCurrentState("Of Shader"); + state.manager->Clear(); + } else { + auto result_out = std::make_unique(); + result_out->start = start_address; + result_out->settings.depth = settings.depth; + result_out->manager = std::move(manager); + result_out->end = state.block_info.back().end + 1; + return std::move(result_out); } } - auto result_out = std::make_unique(); - result_out->decompiled = decompiled; result_out->start = start_address; - if (decompiled) { - result_out->end = state.block_info.back().end + 1; - return std::move(result_out); - } + result_out->settings.depth = + use_flow_stack ? CompileDepth::FlowStack : CompileDepth::NoFlowStack; + result_out->blocks.clear(); for (auto& block : state.block_info) { ShaderBlock new_block{}; new_block.start = block.start; @@ -530,6 +554,10 @@ std::unique_ptr ScanFlow(const ProgramCode& program_code, result_out->end = std::max(result_out->end, block.end); result_out->blocks.push_back(new_block); } + if (!use_flow_stack) { + result_out->labels = std::move(state.labels); + return std::move(result_out); + } auto back = result_out->blocks.begin(); auto next = std::next(back); while (next != result_out->blocks.end()) { diff --git a/src/video_core/shader/control_flow.h b/src/video_core/shader/control_flow.h index 347a35dcf..8d0d08422 100644 --- a/src/video_core/shader/control_flow.h +++ b/src/video_core/shader/control_flow.h @@ -9,8 +9,9 @@ #include #include "video_core/engines/shader_bytecode.h" -#include "video_core/shader/shader_ir.h" #include "video_core/shader/ast.h" +#include "video_core/shader/compiler_settings.h" +#include "video_core/shader/shader_ir.h" namespace VideoCommon::Shader { @@ -68,12 +69,15 @@ struct ShaderBlock { struct ShaderCharacteristics { std::list blocks{}; - bool decompiled{}; + std::set labels{}; u32 start{}; u32 end{}; + ASTManager manager{true}; + CompilerSettings settings{}; }; std::unique_ptr ScanFlow(const ProgramCode& program_code, u32 program_size, - u32 start_address, ASTManager& manager); + u32 start_address, + const CompilerSettings& settings); } // namespace VideoCommon::Shader diff --git a/src/video_core/shader/decode.cpp b/src/video_core/shader/decode.cpp index e7e0903f6..6d4359295 100644 --- a/src/video_core/shader/decode.cpp +++ b/src/video_core/shader/decode.cpp @@ -102,35 +102,71 @@ void ShaderIR::Decode() { std::memcpy(&header, program_code.data(), sizeof(Tegra::Shader::Header)); decompiled = false; - const auto info = - ScanFlow(program_code, program_size, main_offset, program_manager); - if (info) { - const auto& shader_info = *info; - coverage_begin = shader_info.start; - coverage_end = shader_info.end; - if (shader_info.decompiled) { - 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"); - // we can't decompile it, fallback to standard method + auto info = ScanFlow(program_code, program_size, main_offset, settings); + auto& shader_info = *info; + coverage_begin = shader_info.start; + coverage_end = shader_info.end; + switch (shader_info.settings.depth) { + case CompileDepth::FlowStack: { for (const auto& block : shader_info.blocks) { basic_blocks.insert({block.start, DecodeRange(block.start, block.end + 1)}); } - return; + break; } - LOG_WARNING(HW_GPU, "Flow Analysis Failed! Falling back to brute force compiling"); - - // Now we need to deal with an undecompilable shader. We need to brute force - // a shader that captures every position. - coverage_begin = main_offset; - const u32 shader_end = static_cast(program_size / sizeof(u64)); - coverage_end = shader_end; - for (u32 label = main_offset; label < shader_end; label++) { - basic_blocks.insert({label, DecodeRange(label, label + 1)}); + case CompileDepth::NoFlowStack: { + disable_flow_stack = true; + const auto insert_block = [this](NodeBlock& nodes, u32 label) { + if (label == static_cast(exit_branch)) { + return; + } + basic_blocks.insert({label, nodes}); + }; + const auto& blocks = shader_info.blocks; + NodeBlock current_block; + u32 current_label = static_cast(exit_branch); + for (auto& block : blocks) { + if (shader_info.labels.count(block.start) != 0) { + insert_block(current_block, current_label); + current_block.clear(); + current_label = block.start; + } + if (!block.ignore_branch) { + DecodeRangeInner(current_block, block.start, block.end); + InsertControlFlow(current_block, block); + } else { + DecodeRangeInner(current_block, block.start, block.end + 1); + } + } + insert_block(current_block, current_label); + break; + } + case CompileDepth::DecompileBackwards: + case CompileDepth::FullDecompile: { + program_manager = std::move(shader_info.manager); + disable_flow_stack = true; + decompiled = true; + ASTDecoder decoder{*this}; + ASTNode program = GetASTProgram(); + decoder.Visit(program); + break; + } + default: + LOG_CRITICAL(HW_GPU, "Unknown decompilation mode!"); + [[fallthrough]]; + case CompileDepth::BruteForce: { + coverage_begin = main_offset; + const u32 shader_end = static_cast(program_size / sizeof(u64)); + coverage_end = shader_end; + for (u32 label = main_offset; label < shader_end; label++) { + basic_blocks.insert({label, DecodeRange(label, label + 1)}); + } + break; + } + } + if (settings.depth != shader_info.settings.depth) { + LOG_WARNING( + HW_GPU, "Decompiling to this setting \"{}\" failed, downgrading to this setting \"{}\"", + CompileDepthAsString(settings.depth), CompileDepthAsString(shader_info.settings.depth)); } } diff --git a/src/video_core/shader/decode/other.cpp b/src/video_core/shader/decode/other.cpp index 6f678003c..d46e0f823 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 (decompiled) { + if (disable_flow_stack) { 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 (decompiled) { + if (disable_flow_stack) { 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 (decompiled) { + if (disable_flow_stack) { 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 (decompiled) { + if (disable_flow_stack) { break; } diff --git a/src/video_core/shader/shader_ir.cpp b/src/video_core/shader/shader_ir.cpp index 004b1e16f..04e364634 100644 --- a/src/video_core/shader/shader_ir.cpp +++ b/src/video_core/shader/shader_ir.cpp @@ -22,8 +22,10 @@ using Tegra::Shader::PredCondition; using Tegra::Shader::PredOperation; using Tegra::Shader::Register; -ShaderIR::ShaderIR(const ProgramCode& program_code, u32 main_offset, const std::size_t size) - : program_code{program_code}, main_offset{main_offset}, program_size{size}, program_manager{} { +ShaderIR::ShaderIR(const ProgramCode& program_code, u32 main_offset, const std::size_t size, + CompilerSettings settings) + : program_code{program_code}, main_offset{main_offset}, program_size{size}, basic_blocks{}, + program_manager{true}, settings{settings} { Decode(); } diff --git a/src/video_core/shader/shader_ir.h b/src/video_core/shader/shader_ir.h index 48c7b722e..7a91c9bb6 100644 --- a/src/video_core/shader/shader_ir.h +++ b/src/video_core/shader/shader_ir.h @@ -16,6 +16,7 @@ #include "video_core/engines/shader_bytecode.h" #include "video_core/engines/shader_header.h" #include "video_core/shader/ast.h" +#include "video_core/shader/compiler_settings.h" #include "video_core/shader/node.h" namespace VideoCommon::Shader { @@ -65,7 +66,8 @@ struct GlobalMemoryUsage { class ShaderIR final { public: - explicit ShaderIR(const ProgramCode& program_code, u32 main_offset, std::size_t size); + explicit ShaderIR(const ProgramCode& program_code, u32 main_offset, std::size_t size, + CompilerSettings settings); ~ShaderIR(); const std::map& GetBasicBlocks() const { @@ -141,6 +143,10 @@ public: return header; } + bool IsFlowStackDisabled() const { + return disable_flow_stack; + } + bool IsDecompiled() const { return decompiled; } @@ -368,6 +374,7 @@ private: const u32 main_offset; const std::size_t program_size; bool decompiled{}; + bool disable_flow_stack{}; u32 coverage_begin{}; u32 coverage_end{}; @@ -375,6 +382,7 @@ private: std::map basic_blocks; NodeBlock global_code; ASTManager program_manager; + CompilerSettings settings{}; std::set used_registers; std::set used_predicates; From 0366c18d87f8c60ff6a99db668a7f2d810aaeeb0 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Wed, 21 Aug 2019 11:54:47 -0400 Subject: [PATCH 07/19] Shader_IR: mark labels as unused for partial decompile. --- src/video_core/shader/ast.cpp | 4 +--- src/video_core/shader/ast.h | 8 ++++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/video_core/shader/ast.cpp b/src/video_core/shader/ast.cpp index 14c50e1c6..74b9a8f9a 100644 --- a/src/video_core/shader/ast.cpp +++ b/src/video_core/shader/ast.cpp @@ -497,9 +497,7 @@ void ASTManager::Decompile() { } } if (can_remove) { - auto& manager = label->GetManager(); - manager.Remove(label); - labels.erase(it); + label->MarkLabelUnused(); } } } diff --git a/src/video_core/shader/ast.h b/src/video_core/shader/ast.h index 849d0612c..07deb58e4 100644 --- a/src/video_core/shader/ast.h +++ b/src/video_core/shader/ast.h @@ -112,6 +112,7 @@ class ASTLabel { public: ASTLabel(u32 index) : index{index} {} u32 index; + bool unused{}; }; class ASTGoto { @@ -204,6 +205,13 @@ public: return nullptr; } + void MarkLabelUnused() const { + auto inner = std::get_if(&data); + if (inner) { + inner->unused = true; + } + } + Expr GetIfCondition() const { auto inner = std::get_if(&data); if (inner) { From ca9901867e91cd0be0cc75094ee8ea2fb2767c47 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sun, 25 Aug 2019 15:32:00 -0400 Subject: [PATCH 08/19] vk_shader_compiler: Implement the decompiler in SPIR-V --- .../renderer_vulkan/vk_shader_decompiler.cpp | 298 ++++++++++++++++-- src/video_core/shader/ast.h | 22 +- src/video_core/shader/shader_ir.h | 4 + 3 files changed, 301 insertions(+), 23 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp index 77fc58f25..505e49570 100644 --- a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp +++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp @@ -88,6 +88,9 @@ bool IsPrecise(Operation operand) { } // namespace +class ASTDecompiler; +class ExprDecompiler; + class SPIRVDecompiler : public Sirit::Module { public: explicit SPIRVDecompiler(const VKDevice& device, const ShaderIR& ir, ShaderStage stage) @@ -97,27 +100,7 @@ public: AddExtension("SPV_KHR_variable_pointers"); } - void Decompile() { - AllocateBindings(); - AllocateLabels(); - - DeclareVertex(); - DeclareGeometry(); - DeclareFragment(); - DeclareRegisters(); - DeclarePredicates(); - DeclareLocalMemory(); - DeclareInternalFlags(); - DeclareInputAttributes(); - DeclareOutputAttributes(); - DeclareConstantBuffers(); - DeclareGlobalBuffers(); - DeclareSamplers(); - - execute_function = - Emit(OpFunction(t_void, spv::FunctionControlMask::Inline, TypeFunction(t_void))); - Emit(OpLabel()); - + void DecompileBranchMode() { const u32 first_address = ir.GetBasicBlocks().begin()->first; const Id loop_label = OpLabel("loop"); const Id merge_label = OpLabel("merge"); @@ -174,6 +157,43 @@ public: Emit(continue_label); Emit(OpBranch(loop_label)); Emit(merge_label); + } + + void DecompileAST(); + + void Decompile() { + const bool is_fully_decompiled = ir.IsDecompiled(); + AllocateBindings(); + if (!is_fully_decompiled) { + AllocateLabels(); + } + + DeclareVertex(); + DeclareGeometry(); + DeclareFragment(); + DeclareRegisters(); + DeclarePredicates(); + if (is_fully_decompiled) { + DeclareFlowVariables(); + } + DeclareLocalMemory(); + DeclareInternalFlags(); + DeclareInputAttributes(); + DeclareOutputAttributes(); + DeclareConstantBuffers(); + DeclareGlobalBuffers(); + DeclareSamplers(); + + execute_function = + Emit(OpFunction(t_void, spv::FunctionControlMask::Inline, TypeFunction(t_void))); + Emit(OpLabel()); + + if (is_fully_decompiled) { + DecompileAST(); + } else { + DecompileBranchMode(); + } + Emit(OpReturn()); Emit(OpFunctionEnd()); } @@ -206,6 +226,9 @@ public: } private: + friend class ASTDecompiler; + friend class ExprDecompiler; + static constexpr auto INTERNAL_FLAGS_COUNT = static_cast(InternalFlag::Amount); void AllocateBindings() { @@ -294,6 +317,14 @@ private: } } + void DeclareFlowVariables() { + for (u32 i = 0; i < ir.GetASTNumVariables(); i++) { + const Id id = OpVariable(t_prv_bool, spv::StorageClass::Private, v_false); + Name(id, fmt::format("flow_var_{}", static_cast(i))); + flow_variables.emplace(i, AddGlobalVariable(id)); + } + } + void DeclareLocalMemory() { if (const u64 local_memory_size = header.GetLocalMemorySize(); local_memory_size > 0) { const auto element_count = static_cast(Common::AlignUp(local_memory_size, 4) / 4); @@ -1019,7 +1050,7 @@ private: return {}; } - Id Exit(Operation operation) { + Id PreExit() { switch (stage) { case ShaderStage::Vertex: { // TODO(Rodrigo): We should use VK_EXT_depth_range_unrestricted instead, but it doesn't @@ -1067,6 +1098,11 @@ private: } } + return {}; + } + + Id Exit(Operation operation) { + PreExit(); BranchingOp([&]() { Emit(OpReturn()); }); return {}; } @@ -1545,6 +1581,7 @@ private: Id per_vertex{}; std::map registers; std::map predicates; + std::map flow_variables; Id local_memory{}; std::array internal_flags{}; std::map input_attributes; @@ -1580,6 +1617,223 @@ private: std::map labels; }; +class ExprDecompiler { +public: + ExprDecompiler(SPIRVDecompiler& decomp) : decomp{decomp} {} + + void operator()(VideoCommon::Shader::ExprAnd& expr) { + const Id type_def = decomp.GetTypeDefinition(Type::Bool); + const Id op1 = Visit(expr.operand1); + const Id op2 = Visit(expr.operand2); + current_id = decomp.Emit(decomp.OpLogicalAnd(type_def, op1, op2)); + } + + void operator()(VideoCommon::Shader::ExprOr& expr) { + const Id type_def = decomp.GetTypeDefinition(Type::Bool); + const Id op1 = Visit(expr.operand1); + const Id op2 = Visit(expr.operand2); + current_id = decomp.Emit(decomp.OpLogicalOr(type_def, op1, op2)); + } + + void operator()(VideoCommon::Shader::ExprNot& expr) { + const Id type_def = decomp.GetTypeDefinition(Type::Bool); + const Id op1 = Visit(expr.operand1); + current_id = decomp.Emit(decomp.OpLogicalNot(type_def, op1)); + } + + void operator()(VideoCommon::Shader::ExprPredicate& expr) { + auto pred = static_cast(expr.predicate); + current_id = decomp.Emit(decomp.OpLoad(decomp.t_bool, decomp.predicates.at(pred))); + } + + void operator()(VideoCommon::Shader::ExprCondCode& expr) { + Node cc = decomp.ir.GetConditionCode(expr.cc); + Id target; + + if (const auto pred = std::get_if(&*cc)) { + const auto index = pred->GetIndex(); + switch (index) { + case Tegra::Shader::Pred::NeverExecute: + target = decomp.v_false; + case Tegra::Shader::Pred::UnusedIndex: + target = decomp.v_true; + default: + target = decomp.predicates.at(index); + } + } else if (const auto flag = std::get_if(&*cc)) { + target = decomp.internal_flags.at(static_cast(flag->GetFlag())); + } + current_id = decomp.Emit(decomp.OpLoad(decomp.t_bool, target)); + } + + void operator()(VideoCommon::Shader::ExprVar& expr) { + current_id = decomp.Emit(decomp.OpLoad(decomp.t_bool, decomp.flow_variables.at(expr.var_index))); + } + + void operator()(VideoCommon::Shader::ExprBoolean& expr) { + current_id = expr.value ? decomp.v_true : decomp.v_false; + } + + Id GetResult() { + return current_id; + } + + Id Visit(VideoCommon::Shader::Expr& node) { + std::visit(*this, *node); + return current_id; + } + +private: + Id current_id; + SPIRVDecompiler& decomp; +}; + +class ASTDecompiler { +public: + ASTDecompiler(SPIRVDecompiler& 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}; + const Id condition = expr_parser.Visit(ast.condition); + const Id then_label = decomp.OpLabel(); + const Id endif_label = decomp.OpLabel(); + decomp.Emit(decomp.OpSelectionMerge(endif_label, spv::SelectionControlMask::MaskNone)); + decomp.Emit(decomp.OpBranchConditional(condition, then_label, endif_label)); + decomp.Emit(then_label); + ASTNode current = ast.nodes.GetFirst(); + while (current) { + Visit(current); + current = current->GetNext(); + } + decomp.Emit(endif_label); + } + + void operator()(VideoCommon::Shader::ASTIfElse& ast) { + UNREACHABLE(); + } + + void operator()(VideoCommon::Shader::ASTBlockEncoded& ast) { + UNREACHABLE(); + } + + void operator()(VideoCommon::Shader::ASTBlockDecoded& ast) { + decomp.VisitBasicBlock(ast.nodes); + } + + void operator()(VideoCommon::Shader::ASTVarSet& ast) { + ExprDecompiler expr_parser{decomp}; + const Id condition = expr_parser.Visit(ast.condition); + decomp.Emit(decomp.OpStore(decomp.flow_variables.at(ast.index), condition)); + } + + void operator()(VideoCommon::Shader::ASTLabel& ast) { + // Do nothing + } + + void operator()(VideoCommon::Shader::ASTGoto& ast) { + UNREACHABLE(); + } + + void operator()(VideoCommon::Shader::ASTDoWhile& ast) { + const Id loop_label = decomp.OpLabel(); + const Id endloop_label = decomp.OpLabel(); + const Id loop_start_block = decomp.OpLabel(); + const Id loop_end_block = decomp.OpLabel(); + current_loop_exit = endloop_label; + decomp.Emit(loop_label); + decomp.Emit(decomp.OpLoopMerge(endloop_label, loop_end_block, spv::LoopControlMask::MaskNone)); + decomp.Emit(decomp.OpBranch(loop_start_block)); + decomp.Emit(loop_start_block); + ASTNode current = ast.nodes.GetFirst(); + while (current) { + Visit(current); + current = current->GetNext(); + } + decomp.Emit(decomp.OpBranch(loop_end_block)); + decomp.Emit(loop_end_block); + ExprDecompiler expr_parser{decomp}; + const Id condition = expr_parser.Visit(ast.condition); + decomp.Emit(decomp.OpBranchConditional(condition, loop_label, endloop_label)); + decomp.Emit(endloop_label); + } + + void operator()(VideoCommon::Shader::ASTReturn& ast) { + bool is_true = VideoCommon::Shader::ExprIsTrue(ast.condition); + if (!is_true) { + ExprDecompiler expr_parser{decomp}; + const Id condition = expr_parser.Visit(ast.condition); + const Id then_label = decomp.OpLabel(); + const Id endif_label = decomp.OpLabel(); + decomp.Emit(decomp.OpSelectionMerge(endif_label, spv::SelectionControlMask::MaskNone)); + decomp.Emit(decomp.OpBranchConditional(condition, then_label, endif_label)); + decomp.Emit(then_label); + if (ast.kills) { + decomp.Emit(decomp.OpKill()); + } else { + decomp.PreExit(); + decomp.Emit(decomp.OpReturn()); + } + decomp.Emit(endif_label); + } else { + decomp.Emit(decomp.OpLabel()); + if (ast.kills) { + decomp.Emit(decomp.OpKill()); + } else { + decomp.PreExit(); + decomp.Emit(decomp.OpReturn()); + } + decomp.Emit(decomp.OpLabel()); + } + } + + void operator()(VideoCommon::Shader::ASTBreak& ast) { + bool is_true = VideoCommon::Shader::ExprIsTrue(ast.condition); + if (!is_true) { + ExprDecompiler expr_parser{decomp}; + const Id condition = expr_parser.Visit(ast.condition); + const Id then_label = decomp.OpLabel(); + const Id endif_label = decomp.OpLabel(); + decomp.Emit(decomp.OpSelectionMerge(endif_label, spv::SelectionControlMask::MaskNone)); + decomp.Emit(decomp.OpBranchConditional(condition, then_label, endif_label)); + decomp.Emit(then_label); + decomp.Emit(decomp.OpBranch(current_loop_exit)); + decomp.Emit(endif_label); + } else { + decomp.Emit(decomp.OpLabel()); + decomp.Emit(decomp.OpBranch(current_loop_exit)); + decomp.Emit(decomp.OpLabel()); + } + } + + void Visit(VideoCommon::Shader::ASTNode& node) { + std::visit(*this, *node->GetInnerData()); + } + +private: + SPIRVDecompiler& decomp; + Id current_loop_exit; +}; + +void SPIRVDecompiler::DecompileAST() { + u32 num_flow_variables = ir.GetASTNumVariables(); + for (u32 i = 0; i < num_flow_variables; i++) { + const Id id = OpVariable(t_prv_bool, spv::StorageClass::Private, v_false); + Name(id, fmt::format("flow_var_{}", i)); + flow_variables.emplace(i, AddGlobalVariable(id)); + } + ASTDecompiler decompiler{*this}; + VideoCommon::Shader::ASTNode program = ir.GetASTProgram(); + decompiler.Visit(program); +} + DecompilerResult Decompile(const VKDevice& device, const VideoCommon::Shader::ShaderIR& ir, Maxwell::ShaderStage stage) { auto decompiler = std::make_unique(device, ir, stage); diff --git a/src/video_core/shader/ast.h b/src/video_core/shader/ast.h index 07deb58e4..12db336df 100644 --- a/src/video_core/shader/ast.h +++ b/src/video_core/shader/ast.h @@ -205,13 +205,29 @@ public: return nullptr; } - void MarkLabelUnused() const { + void MarkLabelUnused() { auto inner = std::get_if(&data); if (inner) { inner->unused = true; } } + bool IsLabelUnused() const { + auto inner = std::get_if(&data); + if (inner) { + return inner->unused; + } + return true; + } + + u32 GetLabelIndex() const { + auto inner = std::get_if(&data); + if (inner) { + return inner->index; + } + return -1; + } + Expr GetIfCondition() const { auto inner = std::get_if(&data); if (inner) { @@ -336,6 +352,10 @@ public: return variables; } + const std::vector& GetLabels() const { + return labels; + } + private: bool IsBackwardsJump(ASTNode goto_node, ASTNode label_node) const; diff --git a/src/video_core/shader/shader_ir.h b/src/video_core/shader/shader_ir.h index 7a91c9bb6..105981d67 100644 --- a/src/video_core/shader/shader_ir.h +++ b/src/video_core/shader/shader_ir.h @@ -151,6 +151,10 @@ public: return decompiled; } + const ASTManager& GetASTManager() const { + return program_manager; + } + ASTNode GetASTProgram() const { return program_manager.GetProgram(); } From 2e9a810423ef36178ac3947f8feeb7b9a5b29bce Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Fri, 20 Sep 2019 21:12:06 -0400 Subject: [PATCH 09/19] Shader_IR: allow else derivation to be optional. --- src/video_core/renderer_vulkan/vk_shader_decompiler.cpp | 6 ++++-- src/video_core/shader/ast.cpp | 9 ++++++--- src/video_core/shader/ast.h | 3 ++- src/video_core/shader/compiler_settings.h | 3 ++- src/video_core/shader/control_flow.cpp | 3 ++- src/video_core/shader/control_flow.h | 2 +- src/video_core/shader/shader_ir.cpp | 2 +- 7 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp index 505e49570..72fbc69c4 100644 --- a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp +++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp @@ -1667,7 +1667,8 @@ public: } void operator()(VideoCommon::Shader::ExprVar& expr) { - current_id = decomp.Emit(decomp.OpLoad(decomp.t_bool, decomp.flow_variables.at(expr.var_index))); + current_id = + decomp.Emit(decomp.OpLoad(decomp.t_bool, decomp.flow_variables.at(expr.var_index))); } void operator()(VideoCommon::Shader::ExprBoolean& expr) { @@ -1749,7 +1750,8 @@ public: const Id loop_end_block = decomp.OpLabel(); current_loop_exit = endloop_label; decomp.Emit(loop_label); - decomp.Emit(decomp.OpLoopMerge(endloop_label, loop_end_block, spv::LoopControlMask::MaskNone)); + decomp.Emit( + decomp.OpLoopMerge(endloop_label, loop_end_block, spv::LoopControlMask::MaskNone)); decomp.Emit(decomp.OpBranch(loop_start_block)); decomp.Emit(loop_start_block); ASTNode current = ast.nodes.GetFirst(); diff --git a/src/video_core/shader/ast.cpp b/src/video_core/shader/ast.cpp index 74b9a8f9a..7e5e916ab 100644 --- a/src/video_core/shader/ast.cpp +++ b/src/video_core/shader/ast.cpp @@ -363,7 +363,8 @@ std::string ASTManager::Print() { return printer.GetResult(); } -ASTManager::ASTManager(bool full_decompile) : full_decompile{full_decompile} {}; +ASTManager::ASTManager(bool full_decompile, bool disable_else_derivation) + : full_decompile{full_decompile}, disable_else_derivation{disable_else_derivation} {}; ASTManager::~ASTManager() { Clear(); @@ -378,7 +379,8 @@ void ASTManager::Init() { 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}, false_condition{other.false_condition} { + program{other.program}, main_node{other.main_node}, false_condition{other.false_condition}, + disable_else_derivation{other.disable_else_derivation} { other.main_node.reset(); } @@ -392,6 +394,7 @@ ASTManager& ASTManager::operator=(ASTManager&& other) { program = other.program; main_node = other.main_node; false_condition = other.false_condition; + disable_else_derivation = other.disable_else_derivation; other.main_node.reset(); return *this; @@ -641,7 +644,7 @@ void ASTManager::EncloseIfThen(ASTNode goto_node, ASTNode label) { ASTNode prev = goto_node->GetPrevious(); Expr condition = goto_node->GetGotoCondition(); bool do_else = false; - if (prev->IsIfThen()) { + if (!disable_else_derivation && prev->IsIfThen()) { Expr if_condition = prev->GetIfCondition(); do_else = ExprAreEqual(if_condition, condition); } diff --git a/src/video_core/shader/ast.h b/src/video_core/shader/ast.h index 12db336df..1b73f301f 100644 --- a/src/video_core/shader/ast.h +++ b/src/video_core/shader/ast.h @@ -298,7 +298,7 @@ private: class ASTManager final { public: - ASTManager(bool full_decompile); + ASTManager(bool full_decompile, bool disable_else_derivation); ~ASTManager(); ASTManager(const ASTManager& o) = delete; @@ -378,6 +378,7 @@ private: } bool full_decompile{}; + bool disable_else_derivation{}; std::unordered_map labels_map{}; u32 labels_count{}; std::vector labels{}; diff --git a/src/video_core/shader/compiler_settings.h b/src/video_core/shader/compiler_settings.h index e1fb5bc3a..916018c01 100644 --- a/src/video_core/shader/compiler_settings.h +++ b/src/video_core/shader/compiler_settings.h @@ -19,7 +19,8 @@ enum class CompileDepth : u32 { std::string CompileDepthAsString(CompileDepth cd); struct CompilerSettings { - CompileDepth depth; + CompileDepth depth{CompileDepth::NoFlowStack}; + bool disable_else_derivation{true}; }; } // namespace VideoCommon::Shader diff --git a/src/video_core/shader/control_flow.cpp b/src/video_core/shader/control_flow.cpp index c4351969b..c2fa734e7 100644 --- a/src/video_core/shader/control_flow.cpp +++ b/src/video_core/shader/control_flow.cpp @@ -516,7 +516,8 @@ std::unique_ptr ScanFlow(const ProgramCode& program_code, std::sort(state.block_info.begin(), state.block_info.end(), [](const BlockInfo& a, const BlockInfo& b) -> bool { return a.start < b.start; }); if (decompiled && settings.depth != CompileDepth::NoFlowStack) { - ASTManager manager{settings.depth != CompileDepth::DecompileBackwards}; + ASTManager manager{settings.depth != CompileDepth::DecompileBackwards, + settings.disable_else_derivation}; state.manager = &manager; DecompileShader(state); decompiled = state.manager->IsFullyDecompiled(); diff --git a/src/video_core/shader/control_flow.h b/src/video_core/shader/control_flow.h index 8d0d08422..74e54a5c7 100644 --- a/src/video_core/shader/control_flow.h +++ b/src/video_core/shader/control_flow.h @@ -72,7 +72,7 @@ struct ShaderCharacteristics { std::set labels{}; u32 start{}; u32 end{}; - ASTManager manager{true}; + ASTManager manager{true, true}; CompilerSettings settings{}; }; diff --git a/src/video_core/shader/shader_ir.cpp b/src/video_core/shader/shader_ir.cpp index 04e364634..c1f2b88c8 100644 --- a/src/video_core/shader/shader_ir.cpp +++ b/src/video_core/shader/shader_ir.cpp @@ -25,7 +25,7 @@ using Tegra::Shader::Register; ShaderIR::ShaderIR(const ProgramCode& program_code, u32 main_offset, const std::size_t size, CompilerSettings settings) : program_code{program_code}, main_offset{main_offset}, program_size{size}, basic_blocks{}, - program_manager{true}, settings{settings} { + program_manager{true, true}, settings{settings} { Decode(); } From 466cd52ad47b125182baf1544c44e52a741fa58f Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sat, 21 Sep 2019 00:45:13 -0400 Subject: [PATCH 10/19] vk_shader_compiler: Correct SPIR-V AST Decompiling --- .../renderer_vulkan/vk_shader_decompiler.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp index 72fbc69c4..4527e9261 100644 --- a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp +++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp @@ -1714,6 +1714,7 @@ public: Visit(current); current = current->GetNext(); } + decomp.Emit(decomp.OpBranch(endif_label)); decomp.Emit(endif_label); } @@ -1749,6 +1750,7 @@ public: const Id loop_start_block = decomp.OpLabel(); const Id loop_end_block = decomp.OpLabel(); current_loop_exit = endloop_label; + decomp.Emit(decomp.OpBranch(loop_label)); decomp.Emit(loop_label); decomp.Emit( decomp.OpLoopMerge(endloop_label, loop_end_block, spv::LoopControlMask::MaskNone)); @@ -1759,8 +1761,6 @@ public: Visit(current); current = current->GetNext(); } - decomp.Emit(decomp.OpBranch(loop_end_block)); - decomp.Emit(loop_end_block); ExprDecompiler expr_parser{decomp}; const Id condition = expr_parser.Visit(ast.condition); decomp.Emit(decomp.OpBranchConditional(condition, loop_label, endloop_label)); @@ -1785,7 +1785,9 @@ public: } decomp.Emit(endif_label); } else { - decomp.Emit(decomp.OpLabel()); + const Id next_block = decomp.OpLabel(); + decomp.Emit(decomp.OpBranch(next_block)); + decomp.Emit(next_block); if (ast.kills) { decomp.Emit(decomp.OpKill()); } else { @@ -1809,7 +1811,9 @@ public: decomp.Emit(decomp.OpBranch(current_loop_exit)); decomp.Emit(endif_label); } else { - decomp.Emit(decomp.OpLabel()); + const Id next_block = decomp.OpLabel(); + decomp.Emit(decomp.OpBranch(next_block)); + decomp.Emit(next_block); decomp.Emit(decomp.OpBranch(current_loop_exit)); decomp.Emit(decomp.OpLabel()); } @@ -1834,6 +1838,9 @@ void SPIRVDecompiler::DecompileAST() { ASTDecompiler decompiler{*this}; VideoCommon::Shader::ASTNode program = ir.GetASTProgram(); decompiler.Visit(program); + const Id next_block = OpLabel(); + Emit(OpBranch(next_block)); + Emit(next_block); } DecompilerResult Decompile(const VKDevice& device, const VideoCommon::Shader::ShaderIR& ir, From b3c46d694846c8ea4fbdcfccda8a41a9f88622f9 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sat, 21 Sep 2019 13:07:02 -0400 Subject: [PATCH 11/19] Shader_IR: corrections and clang-format --- src/video_core/shader/ast.cpp | 132 ++++++++++++++++------------------ src/video_core/shader/expr.h | 2 +- 2 files changed, 64 insertions(+), 70 deletions(-) diff --git a/src/video_core/shader/ast.cpp b/src/video_core/shader/ast.cpp index 7e5e916ab..7c8507280 100644 --- a/src/video_core/shader/ast.cpp +++ b/src/video_core/shader/ast.cpp @@ -13,7 +13,7 @@ namespace VideoCommon::Shader { ASTZipper::ASTZipper() = default; -void ASTZipper::Init(ASTNode new_first, ASTNode parent) { +void ASTZipper::Init(const ASTNode new_first, const ASTNode parent) { ASSERT(new_first->manager == nullptr); first = new_first; last = new_first; @@ -26,7 +26,7 @@ void ASTZipper::Init(ASTNode new_first, ASTNode parent) { } } -void ASTZipper::PushBack(ASTNode new_node) { +void ASTZipper::PushBack(const ASTNode new_node) { ASSERT(new_node->manager == nullptr); new_node->previous = last; if (last) { @@ -40,7 +40,7 @@ void ASTZipper::PushBack(ASTNode new_node) { new_node->manager = this; } -void ASTZipper::PushFront(ASTNode new_node) { +void ASTZipper::PushFront(const ASTNode new_node) { ASSERT(new_node->manager == nullptr); new_node->previous.reset(); new_node->next = first; @@ -54,13 +54,13 @@ void ASTZipper::PushFront(ASTNode new_node) { new_node->manager = this; } -void ASTZipper::InsertAfter(ASTNode new_node, ASTNode at_node) { +void ASTZipper::InsertAfter(const ASTNode new_node, const ASTNode at_node) { ASSERT(new_node->manager == nullptr); if (!at_node) { PushFront(new_node); return; } - ASTNode next = at_node->next; + const ASTNode next = at_node->next; if (next) { next->previous = new_node; } @@ -73,13 +73,13 @@ void ASTZipper::InsertAfter(ASTNode new_node, ASTNode at_node) { new_node->manager = this; } -void ASTZipper::InsertBefore(ASTNode new_node, ASTNode at_node) { +void ASTZipper::InsertBefore(const ASTNode new_node, const ASTNode at_node) { ASSERT(new_node->manager == nullptr); if (!at_node) { PushBack(new_node); return; } - ASTNode previous = at_node->previous; + const ASTNode previous = at_node->previous; if (previous) { previous->next = new_node; } @@ -92,7 +92,7 @@ void ASTZipper::InsertBefore(ASTNode new_node, ASTNode at_node) { new_node->manager = this; } -void ASTZipper::DetachTail(ASTNode node) { +void ASTZipper::DetachTail(const ASTNode node) { ASSERT(node->manager == this); if (node == first) { first.reset(); @@ -111,14 +111,14 @@ void ASTZipper::DetachTail(ASTNode node) { } } -void ASTZipper::DetachSegment(ASTNode start, ASTNode end) { +void ASTZipper::DetachSegment(const ASTNode start, const ASTNode end) { ASSERT(start->manager == this && end->manager == this); if (start == end) { DetachSingle(start); return; } - ASTNode prev = start->previous; - ASTNode post = end->next; + const ASTNode prev = start->previous; + const ASTNode post = end->next; if (!prev) { first = post; } else { @@ -142,10 +142,10 @@ void ASTZipper::DetachSegment(ASTNode start, ASTNode end) { ASSERT(found); } -void ASTZipper::DetachSingle(ASTNode node) { +void ASTZipper::DetachSingle(const ASTNode node) { ASSERT(node->manager == this); - ASTNode prev = node->previous; - ASTNode post = node->next; + const ASTNode prev = node->previous; + const ASTNode post = node->next; node->previous.reset(); node->next.reset(); if (!prev) { @@ -163,10 +163,10 @@ void ASTZipper::DetachSingle(ASTNode node) { node->parent.reset(); } -void ASTZipper::Remove(ASTNode node) { +void ASTZipper::Remove(const ASTNode node) { ASSERT(node->manager == this); - ASTNode next = node->next; - ASTNode previous = node->previous; + const ASTNode next = node->next; + const ASTNode previous = node->previous; if (previous) { previous->next = next; } @@ -409,35 +409,35 @@ void ASTManager::DeclareLabel(u32 address) { } void ASTManager::InsertLabel(u32 address) { - u32 index = labels_map[address]; - ASTNode label = ASTBase::Make(main_node, index); + const u32 index = labels_map[address]; + const ASTNode label = ASTBase::Make(main_node, index); labels[index] = label; program->nodes.PushBack(label); } void ASTManager::InsertGoto(Expr condition, u32 address) { - u32 index = labels_map[address]; - ASTNode goto_node = ASTBase::Make(main_node, condition, index); + const u32 index = labels_map[address]; + const ASTNode goto_node = ASTBase::Make(main_node, condition, index); gotos.push_back(goto_node); program->nodes.PushBack(goto_node); } void ASTManager::InsertBlock(u32 start_address, u32 end_address) { - ASTNode block = ASTBase::Make(main_node, start_address, end_address); + const ASTNode block = ASTBase::Make(main_node, start_address, end_address); program->nodes.PushBack(block); } void ASTManager::InsertReturn(Expr condition, bool kills) { - ASTNode node = ASTBase::Make(main_node, condition, kills); + const ASTNode node = ASTBase::Make(main_node, condition, kills); program->nodes.PushBack(node); } void ASTManager::Decompile() { auto it = gotos.begin(); while (it != gotos.end()) { - ASTNode goto_node = *it; - u32 label_index = goto_node->GetGotoLabel(); - ASTNode label = labels[label_index]; + const ASTNode goto_node = *it; + const u32 label_index = goto_node->GetGotoLabel(); + const ASTNode label = labels[label_index]; if (!full_decompile) { // We only decompile backward jumps if (!IsBackwardsJump(goto_node, label)) { @@ -452,7 +452,7 @@ void ASTManager::Decompile() { } if (DirectlyRelated(goto_node, label)) { u32 goto_level = goto_node->GetLevel(); - u32 label_level = label->GetLevel(); + const u32 label_level = label->GetLevel(); while (label_level < goto_level) { MoveOutward(goto_node); goto_level--; @@ -481,7 +481,7 @@ void ASTManager::Decompile() { it++; } if (full_decompile) { - for (ASTNode label : labels) { + for (const ASTNode label : labels) { auto& manager = label->GetManager(); manager.Remove(label); } @@ -491,8 +491,8 @@ void ASTManager::Decompile() { while (it != labels.end()) { bool can_remove = true; ASTNode label = *it; - for (ASTNode goto_node : gotos) { - u32 label_index = goto_node->GetGotoLabel(); + for (const ASTNode goto_node : gotos) { + const u32 label_index = goto_node->GetGotoLabel(); ASTNode glabel = labels[label_index]; if (glabel == label) { can_remove = false; @@ -535,8 +535,8 @@ ASTNode CommonParent(ASTNode first, ASTNode second) { if (first->GetParent() == second->GetParent()) { return first->GetParent(); } - u32 first_level = first->GetLevel(); - u32 second_level = second->GetLevel(); + const u32 first_level = first->GetLevel(); + const u32 second_level = second->GetLevel(); u32 min_level; u32 max_level; ASTNode max; @@ -573,8 +573,8 @@ bool ASTManager::DirectlyRelated(ASTNode first, ASTNode second) { if (first->GetParent() == second->GetParent()) { return false; } - u32 first_level = first->GetLevel(); - u32 second_level = second->GetLevel(); + const u32 first_level = first->GetLevel(); + const u32 second_level = second->GetLevel(); u32 min_level; u32 max_level; ASTNode max; @@ -613,42 +613,37 @@ void ASTManager::SanityCheck() { } void ASTManager::EncloseDoWhile(ASTNode goto_node, ASTNode label) { - // ShowCurrentState("Before DoWhile Enclose"); ASTZipper& zipper = goto_node->GetManager(); - ASTNode loop_start = label->GetNext(); + const ASTNode loop_start = label->GetNext(); if (loop_start == goto_node) { zipper.Remove(goto_node); - // ShowCurrentState("Ignore DoWhile Enclose"); return; } - ASTNode parent = label->GetParent(); - Expr condition = goto_node->GetGotoCondition(); + const ASTNode parent = label->GetParent(); + const Expr condition = goto_node->GetGotoCondition(); zipper.DetachSegment(loop_start, goto_node); - ASTNode do_while_node = ASTBase::Make(parent, condition); + const ASTNode do_while_node = ASTBase::Make(parent, condition); ASTZipper* sub_zipper = do_while_node->GetSubNodes(); sub_zipper->Init(loop_start, do_while_node); zipper.InsertAfter(do_while_node, label); sub_zipper->Remove(goto_node); - // ShowCurrentState("After DoWhile Enclose"); } void ASTManager::EncloseIfThen(ASTNode goto_node, ASTNode label) { - // ShowCurrentState("Before IfThen Enclose"); ASTZipper& zipper = goto_node->GetManager(); - ASTNode if_end = label->GetPrevious(); + const ASTNode if_end = label->GetPrevious(); if (if_end == goto_node) { zipper.Remove(goto_node); - // ShowCurrentState("Ignore IfThen Enclose"); return; } - ASTNode prev = goto_node->GetPrevious(); - Expr condition = goto_node->GetGotoCondition(); + const ASTNode prev = goto_node->GetPrevious(); + const Expr condition = goto_node->GetGotoCondition(); bool do_else = false; if (!disable_else_derivation && prev->IsIfThen()) { - Expr if_condition = prev->GetIfCondition(); + const Expr if_condition = prev->GetIfCondition(); do_else = ExprAreEqual(if_condition, condition); } - ASTNode parent = label->GetParent(); + const ASTNode parent = label->GetParent(); zipper.DetachSegment(goto_node, if_end); ASTNode if_node; if (do_else) { @@ -667,34 +662,35 @@ void ASTManager::EncloseIfThen(ASTNode goto_node, ASTNode label) { void ASTManager::MoveOutward(ASTNode goto_node) { // ShowCurrentState("Before MoveOutward"); ASTZipper& zipper = goto_node->GetManager(); - ASTNode parent = goto_node->GetParent(); + const ASTNode parent = goto_node->GetParent(); ASTZipper& zipper2 = parent->GetManager(); - ASTNode grandpa = parent->GetParent(); - bool is_loop = parent->IsLoop(); - bool is_else = parent->IsIfElse(); - bool is_if = parent->IsIfThen(); + const ASTNode grandpa = parent->GetParent(); + const bool is_loop = parent->IsLoop(); + const bool is_else = parent->IsIfElse(); + const bool is_if = parent->IsIfThen(); - ASTNode prev = goto_node->GetPrevious(); - ASTNode post = goto_node->GetNext(); + const ASTNode prev = goto_node->GetPrevious(); + const ASTNode post = goto_node->GetNext(); - Expr condition = goto_node->GetGotoCondition(); + const Expr condition = goto_node->GetGotoCondition(); zipper.DetachSingle(goto_node); if (is_loop) { - 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, false_condition); + const u32 var_index = NewVariable(); + const Expr var_condition = MakeExpr(var_index); + const ASTNode var_node = ASTBase::Make(parent, var_index, condition); + const 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); - ASTNode break_node = ASTBase::Make(parent, var_condition); + const ASTNode break_node = ASTBase::Make(parent, var_condition); zipper.InsertAfter(break_node, var_node); } else if (is_if || is_else) { if (post) { - 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, false_condition); + const u32 var_index = NewVariable(); + const Expr var_condition = MakeExpr(var_index); + const ASTNode var_node = ASTBase::Make(parent, var_index, condition); + const ASTNode var_node_init = + ASTBase::Make(parent, var_index, false_condition); if (is_if) { zipper2.InsertBefore(var_node_init, parent); } else { @@ -703,7 +699,7 @@ void ASTManager::MoveOutward(ASTNode goto_node) { zipper.InsertAfter(var_node, prev); goto_node->SetGotoCondition(var_condition); zipper.DetachTail(post); - ASTNode if_node = ASTBase::Make(parent, MakeExprNot(var_condition)); + const ASTNode if_node = ASTBase::Make(parent, MakeExprNot(var_condition)); ASTZipper* sub_zipper = if_node->GetSubNodes(); sub_zipper->Init(post, if_node); zipper.InsertAfter(if_node, var_node); @@ -721,16 +717,14 @@ void ASTManager::MoveOutward(ASTNode goto_node) { } else { UNREACHABLE(); } - ASTNode next = parent->GetNext(); + const ASTNode next = parent->GetNext(); if (is_if && next && next->IsIfElse()) { zipper2.InsertAfter(goto_node, next); goto_node->SetParent(grandpa); - // ShowCurrentState("After MoveOutward"); return; } zipper2.InsertAfter(goto_node, parent); goto_node->SetParent(grandpa); - // ShowCurrentState("After MoveOutward"); } class ASTClearer { diff --git a/src/video_core/shader/expr.h b/src/video_core/shader/expr.h index b954cffb0..60598977a 100644 --- a/src/video_core/shader/expr.h +++ b/src/video_core/shader/expr.h @@ -4,8 +4,8 @@ #pragma once -#include #include +#include #include "video_core/engines/shader_bytecode.h" From 189a50bc2addbf43824be396f205e900af7523b3 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sun, 22 Sep 2019 12:33:13 -0400 Subject: [PATCH 12/19] gl_shader_decompiler: Refactor and address feedback. --- .../renderer_opengl/gl_shader_decompiler.cpp | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index b8c3442bc..572fae353 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -355,7 +355,7 @@ public: if (!ir.IsFlowStackDisabled()) { for (const auto stack : std::array{MetaStackClass::Ssy, MetaStackClass::Pbk}) { code.AddLine("uint {}[{}];", FlowStackName(stack), FLOW_STACK_SIZE); - code.AddLine("uint {} = 0u;", FlowStackTopName(stack)); + code.AddLine("uint {} = 0U;", FlowStackTopName(stack)); } } @@ -1837,10 +1837,9 @@ private: return {}; } - Expression WriteExit() { + void PreExit() { if (stage != ProgramType::Fragment) { - code.AddLine("return;"); - return {}; + return; } const auto& used_registers = ir.GetRegisters(); const auto SafeGetRegister = [&](u32 reg) -> Expression { @@ -1872,13 +1871,12 @@ private: // already contains one past the last color register. code.AddLine("gl_FragDepth = {};", SafeGetRegister(current_reg + 1).AsFloat()); } - - code.AddLine("return;"); - return {}; } Expression Exit(Operation operation) { - return WriteExit(); + PreExit(); + code.AddLine("return;"); + return {}; } Expression Discard(Operation operation) { @@ -2277,7 +2275,7 @@ const std::string flow_var = "flow_var_"; class ExprDecompiler { public: - ExprDecompiler(GLSLDecompiler& decomp) : decomp{decomp} {} + explicit ExprDecompiler(GLSLDecompiler& decomp) : decomp{decomp} {} void operator()(VideoCommon::Shader::ExprAnd& expr) { inner += "( "; @@ -2301,12 +2299,12 @@ public: } void operator()(VideoCommon::Shader::ExprPredicate& expr) { - auto pred = static_cast(expr.predicate); + const auto pred = static_cast(expr.predicate); inner += decomp.GetPredicate(pred); } void operator()(VideoCommon::Shader::ExprCondCode& expr) { - Node cc = decomp.ir.GetConditionCode(expr.cc); + const Node cc = decomp.ir.GetConditionCode(expr.cc); std::string target; if (const auto pred = std::get_if(&*cc)) { @@ -2321,6 +2319,8 @@ public: } } else if (const auto flag = std::get_if(&*cc)) { target = decomp.GetInternalFlag(flag->GetFlag()); + } else { + UNREACHABLE(); } inner += target; } @@ -2338,13 +2338,13 @@ public: } private: - std::string inner{}; + std::string inner; GLSLDecompiler& decomp; }; class ASTDecompiler { public: - ASTDecompiler(GLSLDecompiler& decomp) : decomp{decomp} {} + explicit ASTDecompiler(GLSLDecompiler& decomp) : decomp{decomp} {} void operator()(VideoCommon::Shader::ASTProgram& ast) { ASTNode current = ast.nodes.GetFirst(); @@ -2417,7 +2417,7 @@ public: } void operator()(VideoCommon::Shader::ASTReturn& ast) { - bool is_true = VideoCommon::Shader::ExprIsTrue(ast.condition); + const bool is_true = VideoCommon::Shader::ExprIsTrue(ast.condition); if (!is_true) { ExprDecompiler expr_parser{decomp}; std::visit(expr_parser, *ast.condition); @@ -2427,7 +2427,8 @@ public: if (ast.kills) { decomp.code.AddLine("discard;"); } else { - decomp.WriteExit(); + decomp.PreExit(); + decomp.code.AddLine("return;"); } if (!is_true) { decomp.code.scope--; @@ -2436,7 +2437,7 @@ public: } void operator()(VideoCommon::Shader::ASTBreak& ast) { - bool is_true = VideoCommon::Shader::ExprIsTrue(ast.condition); + const bool is_true = VideoCommon::Shader::ExprIsTrue(ast.condition); if (!is_true) { ExprDecompiler expr_parser{decomp}; std::visit(expr_parser, *ast.condition); @@ -2459,7 +2460,7 @@ private: }; void GLSLDecompiler::DecompileAST() { - u32 num_flow_variables = ir.GetASTNumVariables(); + const u32 num_flow_variables = ir.GetASTNumVariables(); for (u32 i = 0; i < num_flow_variables; i++) { code.AddLine("bool {}{} = false;", flow_var, i); } From 100a4bd98856cf16c1fe16b6c1f1ab2863916c37 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sun, 22 Sep 2019 20:15:09 -0400 Subject: [PATCH 13/19] vk_shader_compiler: Don't enclose branches with if(true) to avoid crashing AMD --- .../renderer_vulkan/vk_shader_decompiler.cpp | 49 +++++++++++++------ 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp index 4527e9261..11effe4a1 100644 --- a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp +++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp @@ -646,7 +646,9 @@ private: Emit(OpBranchConditional(condition, true_label, skip_label)); Emit(true_label); + ++conditional_nest_count; VisitBasicBlock(conditional->GetCode()); + --conditional_nest_count; Emit(OpBranch(skip_label)); Emit(skip_label); @@ -1011,7 +1013,10 @@ private: UNIMPLEMENTED_IF(!target); Emit(OpStore(jmp_to, Constant(t_uint, target->GetValue()))); - BranchingOp([&]() { Emit(OpBranch(continue_label)); }); + Emit(OpBranch(continue_label)); + if (conditional_nest_count == 0) { + Emit(OpLabel()); + } return {}; } @@ -1019,7 +1024,10 @@ private: const Id op_a = VisitOperand(operation, 0); Emit(OpStore(jmp_to, op_a)); - BranchingOp([&]() { Emit(OpBranch(continue_label)); }); + Emit(OpBranch(continue_label)); + if (conditional_nest_count == 0) { + Emit(OpLabel()); + } return {}; } @@ -1046,7 +1054,10 @@ private: Emit(OpStore(flow_stack_top, previous)); Emit(OpStore(jmp_to, target)); - BranchingOp([&]() { Emit(OpBranch(continue_label)); }); + Emit(OpBranch(continue_label)); + if (conditional_nest_count == 0) { + Emit(OpLabel()); + } return {}; } @@ -1103,12 +1114,28 @@ private: Id Exit(Operation operation) { PreExit(); - BranchingOp([&]() { Emit(OpReturn()); }); + if (conditional_nest_count > 0) { + Emit(OpReturn()); + } else { + const Id dummy = OpLabel(); + Emit(OpBranch(dummy)); + Emit(dummy); + Emit(OpReturn()); + Emit(OpLabel()); + } return {}; } Id Discard(Operation operation) { - BranchingOp([&]() { Emit(OpKill()); }); + if (conditional_nest_count > 0) { + Emit(OpKill()); + } else { + const Id dummy = OpLabel(); + Emit(OpBranch(dummy)); + Emit(dummy); + Emit(OpKill()); + Emit(OpLabel()); + } return {}; } @@ -1303,17 +1330,6 @@ private: return {}; } - void BranchingOp(std::function call) { - const Id true_label = OpLabel(); - const Id skip_label = OpLabel(); - Emit(OpSelectionMerge(skip_label, spv::SelectionControlMask::Flatten)); - Emit(OpBranchConditional(v_true, true_label, skip_label, 1, 0)); - Emit(true_label); - call(); - - Emit(skip_label); - } - std::tuple CreateFlowStack() { // TODO(Rodrigo): Figure out the actual depth of the flow stack, for now it seems unlikely // that shaders will use 20 nested SSYs and PBKs. @@ -1519,6 +1535,7 @@ private: const ShaderIR& ir; const ShaderStage stage; const Tegra::Shader::Header header; + u64 conditional_nest_count{}; const Id t_void = Name(TypeVoid(), "void"); From 5ea740beb52ee8ccbabef81397ce9458077c6a42 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Mon, 23 Sep 2019 08:10:29 -0400 Subject: [PATCH 14/19] Shader_IR: Correct OutwardMoves for Ifs --- src/video_core/shader/ast.cpp | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/src/video_core/shader/ast.cpp b/src/video_core/shader/ast.cpp index 7c8507280..54f0240e1 100644 --- a/src/video_core/shader/ast.cpp +++ b/src/video_core/shader/ast.cpp @@ -685,34 +685,23 @@ void ASTManager::MoveOutward(ASTNode goto_node) { const ASTNode break_node = ASTBase::Make(parent, var_condition); zipper.InsertAfter(break_node, var_node); } else if (is_if || is_else) { + const u32 var_index = NewVariable(); + const Expr var_condition = MakeExpr(var_index); + const ASTNode var_node = ASTBase::Make(parent, var_index, condition); + const ASTNode var_node_init = ASTBase::Make(parent, var_index, false_condition); + if (is_if) { + zipper2.InsertBefore(var_node_init, parent); + } else { + zipper2.InsertBefore(var_node_init, parent->GetPrevious()); + } + zipper.InsertAfter(var_node, prev); + goto_node->SetGotoCondition(var_condition); if (post) { - const u32 var_index = NewVariable(); - const Expr var_condition = MakeExpr(var_index); - const ASTNode var_node = ASTBase::Make(parent, var_index, condition); - const ASTNode var_node_init = - ASTBase::Make(parent, var_index, false_condition); - if (is_if) { - zipper2.InsertBefore(var_node_init, parent); - } else { - zipper2.InsertBefore(var_node_init, parent->GetPrevious()); - } - zipper.InsertAfter(var_node, prev); - goto_node->SetGotoCondition(var_condition); zipper.DetachTail(post); const ASTNode if_node = ASTBase::Make(parent, MakeExprNot(var_condition)); ASTZipper* sub_zipper = if_node->GetSubNodes(); sub_zipper->Init(post, if_node); zipper.InsertAfter(if_node, var_node); - } else { - Expr if_condition; - if (is_if) { - if_condition = parent->GetIfCondition(); - } else { - ASTNode if_node = parent->GetPrevious(); - if_condition = MakeExprNot(if_node->GetIfCondition()); - } - Expr new_condition = MakeExprAnd(if_condition, condition); - goto_node->SetGotoCondition(new_condition); } } else { UNREACHABLE(); From 7c756baa777cd27b319c1a397bd45270d2ffe041 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Mon, 23 Sep 2019 08:15:31 -0400 Subject: [PATCH 15/19] Shader_IR: clean up AST handling and add documentation. --- src/video_core/shader/ast.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/video_core/shader/ast.cpp b/src/video_core/shader/ast.cpp index 54f0240e1..fc440526f 100644 --- a/src/video_core/shader/ast.cpp +++ b/src/video_core/shader/ast.cpp @@ -432,6 +432,12 @@ void ASTManager::InsertReturn(Expr condition, bool kills) { program->nodes.PushBack(node); } +// The decompile algorithm is based on +// "Taming control flow: A structured approach to eliminating goto statements" +// by AM Erosa, LJ Hendren 1994. In general, the idea is to get gotos to be +// on the same structured level as the label which they jump to. This is done, +// through outward/inward movements and lifting. Once they are at the same +// level, you can enclose them in an "if" structure or a "do-while" structure. void ASTManager::Decompile() { auto it = gotos.begin(); while (it != gotos.end()) { @@ -656,11 +662,9 @@ void ASTManager::EncloseIfThen(ASTNode goto_node, ASTNode label) { sub_zipper->Init(goto_node, if_node); zipper.InsertAfter(if_node, prev); sub_zipper->Remove(goto_node); - // ShowCurrentState("After IfThen Enclose"); } void ASTManager::MoveOutward(ASTNode goto_node) { - // ShowCurrentState("Before MoveOutward"); ASTZipper& zipper = goto_node->GetManager(); const ASTNode parent = goto_node->GetParent(); ASTZipper& zipper2 = parent->GetManager(); From 000ad558dd21a0f1f0be57ddbb59540956314896 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Tue, 24 Sep 2019 10:57:45 -0400 Subject: [PATCH 16/19] vk_shader_decompiler: Clean code and be const correct. --- .../renderer_opengl/gl_shader_decompiler.cpp | 2 +- .../renderer_vulkan/vk_shader_decompiler.cpp | 12 +++++------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 572fae353..bff1067a4 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -19,8 +19,8 @@ #include "video_core/renderer_opengl/gl_device.h" #include "video_core/renderer_opengl/gl_rasterizer.h" #include "video_core/renderer_opengl/gl_shader_decompiler.h" -#include "video_core/shader/node.h" #include "video_core/shader/ast.h" +#include "video_core/shader/node.h" #include "video_core/shader/shader_ir.h" namespace OpenGL::GLShader { diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp index 11effe4a1..4bc7da198 100644 --- a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp +++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp @@ -1659,12 +1659,12 @@ public: } void operator()(VideoCommon::Shader::ExprPredicate& expr) { - auto pred = static_cast(expr.predicate); + const auto pred = static_cast(expr.predicate); current_id = decomp.Emit(decomp.OpLoad(decomp.t_bool, decomp.predicates.at(pred))); } void operator()(VideoCommon::Shader::ExprCondCode& expr) { - Node cc = decomp.ir.GetConditionCode(expr.cc); + const Node cc = decomp.ir.GetConditionCode(expr.cc); Id target; if (const auto pred = std::get_if(&*cc)) { @@ -1785,8 +1785,7 @@ public: } void operator()(VideoCommon::Shader::ASTReturn& ast) { - bool is_true = VideoCommon::Shader::ExprIsTrue(ast.condition); - if (!is_true) { + if (!VideoCommon::Shader::ExprIsTrue(ast.condition)) { ExprDecompiler expr_parser{decomp}; const Id condition = expr_parser.Visit(ast.condition); const Id then_label = decomp.OpLabel(); @@ -1816,8 +1815,7 @@ public: } void operator()(VideoCommon::Shader::ASTBreak& ast) { - bool is_true = VideoCommon::Shader::ExprIsTrue(ast.condition); - if (!is_true) { + if (!VideoCommon::Shader::ExprIsTrue(ast.condition)) { ExprDecompiler expr_parser{decomp}; const Id condition = expr_parser.Visit(ast.condition); const Id then_label = decomp.OpLabel(); @@ -1846,7 +1844,7 @@ private: }; void SPIRVDecompiler::DecompileAST() { - u32 num_flow_variables = ir.GetASTNumVariables(); + const u32 num_flow_variables = ir.GetASTNumVariables(); for (u32 i = 0; i < num_flow_variables; i++) { const Id id = OpVariable(t_prv_bool, spv::StorageClass::Private, v_false); Name(id, fmt::format("flow_var_{}", i)); From 507a9c6a402d6ee277e0a8f0cda57d04526c05dd Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Wed, 25 Sep 2019 14:34:08 -0400 Subject: [PATCH 17/19] vk_shader_decompiler: Correct Branches inside conditionals. --- .../renderer_vulkan/vk_shader_decompiler.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp index 4bc7da198..2b55a3727 100644 --- a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp +++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp @@ -650,7 +650,11 @@ private: VisitBasicBlock(conditional->GetCode()); --conditional_nest_count; - Emit(OpBranch(skip_label)); + if (inside_branch == 0) { + Emit(OpBranch(skip_label)); + } else { + inside_branch--; + } Emit(skip_label); return {}; @@ -1014,6 +1018,7 @@ private: Emit(OpStore(jmp_to, Constant(t_uint, target->GetValue()))); Emit(OpBranch(continue_label)); + inside_branch = conditional_nest_count; if (conditional_nest_count == 0) { Emit(OpLabel()); } @@ -1025,6 +1030,7 @@ private: Emit(OpStore(jmp_to, op_a)); Emit(OpBranch(continue_label)); + inside_branch = conditional_nest_count; if (conditional_nest_count == 0) { Emit(OpLabel()); } @@ -1055,6 +1061,7 @@ private: Emit(OpStore(flow_stack_top, previous)); Emit(OpStore(jmp_to, target)); Emit(OpBranch(continue_label)); + inside_branch = conditional_nest_count; if (conditional_nest_count == 0) { Emit(OpLabel()); } @@ -1114,6 +1121,7 @@ private: Id Exit(Operation operation) { PreExit(); + inside_branch = conditional_nest_count; if (conditional_nest_count > 0) { Emit(OpReturn()); } else { @@ -1127,6 +1135,7 @@ private: } Id Discard(Operation operation) { + inside_branch = conditional_nest_count; if (conditional_nest_count > 0) { Emit(OpKill()); } else { @@ -1536,6 +1545,7 @@ private: const ShaderStage stage; const Tegra::Shader::Header header; u64 conditional_nest_count{}; + u64 inside_branch{}; const Id t_void = Name(TypeVoid(), "void"); From 3c09d9abe6d268ada063fd67c08d09fc0fcad613 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sat, 28 Sep 2019 15:16:19 -0400 Subject: [PATCH 18/19] Shader_Ir: Address Feedback and clang format. --- .../renderer_vulkan/vk_shader_decompiler.cpp | 43 +++++------- src/video_core/shader/ast.cpp | 14 ++-- src/video_core/shader/ast.h | 65 ++++++++++--------- src/video_core/shader/expr.h | 14 ++-- 4 files changed, 68 insertions(+), 68 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp index 2b55a3727..8bcd04221 100644 --- a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp +++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp @@ -1646,34 +1646,34 @@ private: class ExprDecompiler { public: - ExprDecompiler(SPIRVDecompiler& decomp) : decomp{decomp} {} + explicit ExprDecompiler(SPIRVDecompiler& decomp) : decomp{decomp} {} - void operator()(VideoCommon::Shader::ExprAnd& expr) { + Id operator()(VideoCommon::Shader::ExprAnd& expr) { const Id type_def = decomp.GetTypeDefinition(Type::Bool); const Id op1 = Visit(expr.operand1); const Id op2 = Visit(expr.operand2); - current_id = decomp.Emit(decomp.OpLogicalAnd(type_def, op1, op2)); + return decomp.Emit(decomp.OpLogicalAnd(type_def, op1, op2)); } - void operator()(VideoCommon::Shader::ExprOr& expr) { + Id operator()(VideoCommon::Shader::ExprOr& expr) { const Id type_def = decomp.GetTypeDefinition(Type::Bool); const Id op1 = Visit(expr.operand1); const Id op2 = Visit(expr.operand2); - current_id = decomp.Emit(decomp.OpLogicalOr(type_def, op1, op2)); + return decomp.Emit(decomp.OpLogicalOr(type_def, op1, op2)); } - void operator()(VideoCommon::Shader::ExprNot& expr) { + Id operator()(VideoCommon::Shader::ExprNot& expr) { const Id type_def = decomp.GetTypeDefinition(Type::Bool); const Id op1 = Visit(expr.operand1); - current_id = decomp.Emit(decomp.OpLogicalNot(type_def, op1)); + return decomp.Emit(decomp.OpLogicalNot(type_def, op1)); } - void operator()(VideoCommon::Shader::ExprPredicate& expr) { + Id operator()(VideoCommon::Shader::ExprPredicate& expr) { const auto pred = static_cast(expr.predicate); - current_id = decomp.Emit(decomp.OpLoad(decomp.t_bool, decomp.predicates.at(pred))); + return decomp.Emit(decomp.OpLoad(decomp.t_bool, decomp.predicates.at(pred))); } - void operator()(VideoCommon::Shader::ExprCondCode& expr) { + Id operator()(VideoCommon::Shader::ExprCondCode& expr) { const Node cc = decomp.ir.GetConditionCode(expr.cc); Id target; @@ -1690,35 +1690,28 @@ public: } else if (const auto flag = std::get_if(&*cc)) { target = decomp.internal_flags.at(static_cast(flag->GetFlag())); } - current_id = decomp.Emit(decomp.OpLoad(decomp.t_bool, target)); + return decomp.Emit(decomp.OpLoad(decomp.t_bool, target)); } - void operator()(VideoCommon::Shader::ExprVar& expr) { - current_id = - decomp.Emit(decomp.OpLoad(decomp.t_bool, decomp.flow_variables.at(expr.var_index))); + Id operator()(VideoCommon::Shader::ExprVar& expr) { + return decomp.Emit(decomp.OpLoad(decomp.t_bool, decomp.flow_variables.at(expr.var_index))); } - void operator()(VideoCommon::Shader::ExprBoolean& expr) { - current_id = expr.value ? decomp.v_true : decomp.v_false; - } - - Id GetResult() { - return current_id; + Id operator()(VideoCommon::Shader::ExprBoolean& expr) { + return expr.value ? decomp.v_true : decomp.v_false; } Id Visit(VideoCommon::Shader::Expr& node) { - std::visit(*this, *node); - return current_id; + return std::visit(*this, *node); } private: - Id current_id; SPIRVDecompiler& decomp; }; class ASTDecompiler { public: - ASTDecompiler(SPIRVDecompiler& decomp) : decomp{decomp} {} + explicit ASTDecompiler(SPIRVDecompiler& decomp) : decomp{decomp} {} void operator()(VideoCommon::Shader::ASTProgram& ast) { ASTNode current = ast.nodes.GetFirst(); @@ -1850,7 +1843,7 @@ public: private: SPIRVDecompiler& decomp; - Id current_loop_exit; + Id current_loop_exit{}; }; void SPIRVDecompiler::DecompileAST() { diff --git a/src/video_core/shader/ast.cpp b/src/video_core/shader/ast.cpp index fc440526f..c4548f0bc 100644 --- a/src/video_core/shader/ast.cpp +++ b/src/video_core/shader/ast.cpp @@ -442,8 +442,11 @@ void ASTManager::Decompile() { auto it = gotos.begin(); while (it != gotos.end()) { const ASTNode goto_node = *it; - const u32 label_index = goto_node->GetGotoLabel(); - const ASTNode label = labels[label_index]; + const auto label_index = goto_node->GetGotoLabel(); + if (!label_index) { + return; + } + const ASTNode label = labels[*label_index]; if (!full_decompile) { // We only decompile backward jumps if (!IsBackwardsJump(goto_node, label)) { @@ -498,8 +501,11 @@ void ASTManager::Decompile() { bool can_remove = true; ASTNode label = *it; for (const ASTNode goto_node : gotos) { - const u32 label_index = goto_node->GetGotoLabel(); - ASTNode glabel = labels[label_index]; + const auto label_index = goto_node->GetGotoLabel(); + if (!label_index) { + return; + } + ASTNode glabel = labels[*label_index]; if (glabel == label) { can_remove = false; break; diff --git a/src/video_core/shader/ast.h b/src/video_core/shader/ast.h index 1b73f301f..8efd4c147 100644 --- a/src/video_core/shader/ast.h +++ b/src/video_core/shader/ast.h @@ -44,7 +44,7 @@ enum class ASTZipperType : u32 { class ASTZipper final { public: - ASTZipper(); + explicit ASTZipper(); void Init(ASTNode first, ASTNode parent); @@ -71,74 +71,74 @@ public: class ASTProgram { public: - ASTProgram() : nodes{} {}; - ASTZipper nodes; + explicit ASTProgram() = default; + ASTZipper nodes{}; }; class ASTIfThen { public: - ASTIfThen(Expr condition) : condition(condition), nodes{} {} + explicit ASTIfThen(Expr condition) : condition(condition) {} Expr condition; - ASTZipper nodes; + ASTZipper nodes{}; }; class ASTIfElse { public: - ASTIfElse() : nodes{} {} - ASTZipper nodes; + explicit ASTIfElse() = default; + ASTZipper nodes{}; }; class ASTBlockEncoded { public: - ASTBlockEncoded(u32 start, u32 end) : start{start}, end{end} {} + explicit ASTBlockEncoded(u32 start, u32 end) : start{start}, end{end} {} u32 start; u32 end; }; class ASTBlockDecoded { public: - ASTBlockDecoded(NodeBlock& new_nodes) : nodes(std::move(new_nodes)) {} + explicit ASTBlockDecoded(NodeBlock& new_nodes) : nodes(std::move(new_nodes)) {} NodeBlock nodes; }; class ASTVarSet { public: - ASTVarSet(u32 index, Expr condition) : index{index}, condition{condition} {} + explicit ASTVarSet(u32 index, Expr condition) : index{index}, condition{condition} {} u32 index; Expr condition; }; class ASTLabel { public: - ASTLabel(u32 index) : index{index} {} + explicit ASTLabel(u32 index) : index{index} {} u32 index; bool unused{}; }; class ASTGoto { public: - ASTGoto(Expr condition, u32 label) : condition{condition}, label{label} {} + explicit ASTGoto(Expr condition, u32 label) : condition{condition}, label{label} {} Expr condition; u32 label; }; class ASTDoWhile { public: - ASTDoWhile(Expr condition) : condition(condition), nodes{} {} + explicit ASTDoWhile(Expr condition) : condition(condition) {} Expr condition; - ASTZipper nodes; + ASTZipper nodes{}; }; class ASTReturn { public: - ASTReturn(Expr condition, bool kills) : condition{condition}, kills{kills} {} + explicit ASTReturn(Expr condition, bool kills) : condition{condition}, kills{kills} {} Expr condition; bool kills; }; class ASTBreak { public: - ASTBreak(Expr condition) : condition{condition} {} + explicit ASTBreak(Expr condition) : condition{condition} {} Expr condition; }; @@ -177,11 +177,11 @@ public: return &data; } - ASTNode GetNext() { + ASTNode GetNext() const { return next; } - ASTNode GetPrevious() { + ASTNode GetPrevious() const { return previous; } @@ -189,12 +189,12 @@ public: return *manager; } - u32 GetGotoLabel() const { + std::optional GetGotoLabel() const { auto inner = std::get_if(&data); if (inner) { - return inner->label; + return {inner->label}; } - return -1; + return {}; } Expr GetGotoCondition() const { @@ -220,12 +220,12 @@ public: return true; } - u32 GetLabelIndex() const { + std::optional GetLabelIndex() const { auto inner = std::get_if(&data); if (inner) { - return inner->index; + return {inner->index}; } - return -1; + return {}; } Expr GetIfCondition() const { @@ -290,7 +290,7 @@ private: friend class ASTZipper; ASTData data; - ASTNode parent; + ASTNode parent{}; ASTNode next{}; ASTNode previous{}; ASTZipper* manager{}; @@ -327,13 +327,18 @@ public: void SanityCheck(); + void Clear(); + bool IsFullyDecompiled() const { if (full_decompile) { return gotos.size() == 0; } else { for (ASTNode goto_node : gotos) { - u32 label_index = goto_node->GetGotoLabel(); - ASTNode glabel = labels[label_index]; + auto label_index = goto_node->GetGotoLabel(); + if (!label_index) { + return false; + } + ASTNode glabel = labels[*label_index]; if (IsBackwardsJump(goto_node, glabel)) { return false; } @@ -346,8 +351,6 @@ public: return main_node; } - void Clear(); - u32 GetVariables() const { return variables; } @@ -372,9 +375,7 @@ private: void MoveOutward(ASTNode goto_node); u32 NewVariable() { - u32 new_var = variables; - variables++; - return new_var; + return variables++; } bool full_decompile{}; diff --git a/src/video_core/shader/expr.h b/src/video_core/shader/expr.h index 60598977a..4c399cef9 100644 --- a/src/video_core/shader/expr.h +++ b/src/video_core/shader/expr.h @@ -28,7 +28,7 @@ using Expr = std::shared_ptr; class ExprAnd final { public: - ExprAnd(Expr a, Expr b) : operand1{a}, operand2{b} {} + explicit ExprAnd(Expr a, Expr b) : operand1{a}, operand2{b} {} bool operator==(const ExprAnd& b) const; @@ -38,7 +38,7 @@ public: class ExprOr final { public: - ExprOr(Expr a, Expr b) : operand1{a}, operand2{b} {} + explicit ExprOr(Expr a, Expr b) : operand1{a}, operand2{b} {} bool operator==(const ExprOr& b) const; @@ -48,7 +48,7 @@ public: class ExprNot final { public: - ExprNot(Expr a) : operand1{a} {} + explicit ExprNot(Expr a) : operand1{a} {} bool operator==(const ExprNot& b) const; @@ -57,7 +57,7 @@ public: class ExprVar final { public: - ExprVar(u32 index) : var_index{index} {} + explicit ExprVar(u32 index) : var_index{index} {} bool operator==(const ExprVar& b) const { return var_index == b.var_index; @@ -68,7 +68,7 @@ public: class ExprPredicate final { public: - ExprPredicate(u32 predicate) : predicate{predicate} {} + explicit ExprPredicate(u32 predicate) : predicate{predicate} {} bool operator==(const ExprPredicate& b) const { return predicate == b.predicate; @@ -79,7 +79,7 @@ public: class ExprCondCode final { public: - ExprCondCode(ConditionCode cc) : cc{cc} {} + explicit ExprCondCode(ConditionCode cc) : cc{cc} {} bool operator==(const ExprCondCode& b) const { return cc == b.cc; @@ -90,7 +90,7 @@ public: class ExprBoolean final { public: - ExprBoolean(bool val) : value{val} {} + explicit ExprBoolean(bool val) : value{val} {} bool operator==(const ExprBoolean& b) const { return value == b.value; From e6eae4b815bf4bc480d62677fdf9bdbf5d6cba82 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Fri, 4 Oct 2019 17:23:16 -0400 Subject: [PATCH 19/19] Shader_ir: Address feedback --- .../renderer_opengl/gl_shader_decompiler.cpp | 12 +++-- .../renderer_opengl/gl_shader_gen.cpp | 13 +---- src/video_core/shader/ast.cpp | 48 +++---------------- src/video_core/shader/ast.h | 12 ++--- src/video_core/shader/control_flow.cpp | 2 +- src/video_core/shader/decode.cpp | 2 +- 6 files changed, 24 insertions(+), 65 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index bff1067a4..6a610a3bc 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -2271,7 +2271,11 @@ private: ShaderWriter code; }; -const std::string flow_var = "flow_var_"; +static constexpr std::string_view flow_var = "flow_var_"; + +std::string GetFlowVariable(u32 i) { + return fmt::format("{}{}", flow_var, i); +} class ExprDecompiler { public: @@ -2326,7 +2330,7 @@ public: } void operator()(VideoCommon::Shader::ExprVar& expr) { - inner += flow_var + std::to_string(expr.var_index); + inner += GetFlowVariable(expr.var_index); } void operator()(VideoCommon::Shader::ExprBoolean& expr) { @@ -2391,7 +2395,7 @@ public: 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()); + decomp.code.AddLine("{} = {};", GetFlowVariable(ast.index), expr_parser.GetResult()); } void operator()(VideoCommon::Shader::ASTLabel& ast) { @@ -2462,7 +2466,7 @@ private: void GLSLDecompiler::DecompileAST() { const u32 num_flow_variables = ir.GetASTNumVariables(); for (u32 i = 0; i < num_flow_variables; i++) { - code.AddLine("bool {}{} = false;", flow_var, i); + code.AddLine("bool {} = false;", GetFlowVariable(i)); } ASTDecompiler decompiler{*this}; VideoCommon::Shader::ASTNode program = ir.GetASTProgram(); diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index 72a49ebdc..b5a43e79e 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -19,6 +19,8 @@ using VideoCommon::Shader::ShaderIR; static constexpr u32 PROGRAM_OFFSET = 10; static constexpr u32 COMPUTE_OFFSET = 0; +static constexpr CompilerSettings settings{CompileDepth::NoFlowStack, true}; + ProgramResult GenerateVertexShader(const Device& device, const ShaderSetup& setup) { const std::string id = fmt::format("{:016x}", setup.program.unique_identifier); @@ -33,9 +35,6 @@ layout (std140, binding = EMULATION_UBO_BINDING) uniform vs_config { )"; - CompilerSettings settings; - settings.depth = CompileDepth::NoFlowStack; - const ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET, setup.program.size_a, settings); const auto stage = setup.IsDualProgram() ? ProgramType::VertexA : ProgramType::VertexB; ProgramResult program = Decompile(device, program_ir, stage, "vertex"); @@ -86,9 +85,6 @@ layout (std140, binding = EMULATION_UBO_BINDING) uniform gs_config { )"; - CompilerSettings settings; - settings.depth = CompileDepth::NoFlowStack; - const ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET, setup.program.size_a, settings); ProgramResult program = Decompile(device, program_ir, ProgramType::Geometry, "geometry"); out += program.first; @@ -123,8 +119,6 @@ layout (std140, binding = EMULATION_UBO_BINDING) uniform fs_config { }; )"; - CompilerSettings settings; - settings.depth = CompileDepth::NoFlowStack; const ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET, setup.program.size_a, settings); ProgramResult program = Decompile(device, program_ir, ProgramType::Fragment, "fragment"); @@ -145,9 +139,6 @@ ProgramResult GenerateComputeShader(const Device& device, const ShaderSetup& set std::string out = "// Shader Unique Id: CS" + id + "\n\n"; out += GetCommonDeclarations(); - CompilerSettings settings; - settings.depth = CompileDepth::NoFlowStack; - const ShaderIR program_ir(setup.program.code, COMPUTE_OFFSET, setup.program.size_a, settings); ProgramResult program = Decompile(device, program_ir, ProgramType::Compute, "compute"); out += program.first; diff --git a/src/video_core/shader/ast.cpp b/src/video_core/shader/ast.cpp index c4548f0bc..2eb065c3d 100644 --- a/src/video_core/shader/ast.cpp +++ b/src/video_core/shader/ast.cpp @@ -376,7 +376,7 @@ void ASTManager::Init() { false_condition = MakeExpr(false); } -ASTManager::ASTManager(ASTManager&& other) +ASTManager::ASTManager(ASTManager&& other) noexcept : 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}, false_condition{other.false_condition}, @@ -384,7 +384,7 @@ ASTManager::ASTManager(ASTManager&& other) other.main_node.reset(); } -ASTManager& ASTManager::operator=(ASTManager&& other) { +ASTManager& ASTManager::operator=(ASTManager&& other) noexcept { full_decompile = other.full_decompile; labels_map = std::move(other.labels_map); labels_count = other.labels_count; @@ -490,7 +490,7 @@ void ASTManager::Decompile() { it++; } if (full_decompile) { - for (const ASTNode label : labels) { + for (const ASTNode& label : labels) { auto& manager = label->GetManager(); manager.Remove(label); } @@ -500,12 +500,12 @@ void ASTManager::Decompile() { while (it != labels.end()) { bool can_remove = true; ASTNode label = *it; - for (const ASTNode goto_node : gotos) { + for (const ASTNode& goto_node : gotos) { const auto label_index = goto_node->GetGotoLabel(); if (!label_index) { return; } - ASTNode glabel = labels[*label_index]; + ASTNode& glabel = labels[*label_index]; if (glabel == label) { can_remove = false; break; @@ -543,40 +543,6 @@ bool ASTManager::IsBackwardsJump(ASTNode goto_node, ASTNode label_node) const { return false; } -ASTNode CommonParent(ASTNode first, ASTNode second) { - if (first->GetParent() == second->GetParent()) { - return first->GetParent(); - } - const u32 first_level = first->GetLevel(); - const u32 second_level = second->GetLevel(); - u32 min_level; - u32 max_level; - ASTNode max; - ASTNode min; - if (first_level > second_level) { - min_level = second_level; - min = second; - max_level = first_level; - max = first; - } else { - min_level = first_level; - min = first; - max_level = second_level; - max = second; - } - - while (max_level > min_level) { - max_level--; - max = max->GetParent(); - } - - while (min->GetParent() != max->GetParent()) { - min = min->GetParent(); - max = max->GetParent(); - } - return min->GetParent(); -} - bool ASTManager::IndirectlyRelated(ASTNode first, ASTNode second) { return !(first->GetParent() == second->GetParent() || DirectlyRelated(first, second)); } @@ -608,7 +574,7 @@ bool ASTManager::DirectlyRelated(ASTNode first, ASTNode second) { max = max->GetParent(); } - return (min->GetParent() == max->GetParent()); + return min->GetParent() == max->GetParent(); } void ASTManager::ShowCurrentState(std::string state) { @@ -617,7 +583,7 @@ void ASTManager::ShowCurrentState(std::string state) { } void ASTManager::SanityCheck() { - for (auto label : labels) { + for (auto& label : labels) { if (!label->GetParent()) { LOG_CRITICAL(HW_GPU, "Sanity Check Failed"); } diff --git a/src/video_core/shader/ast.h b/src/video_core/shader/ast.h index 8efd4c147..ba234138e 100644 --- a/src/video_core/shader/ast.h +++ b/src/video_core/shader/ast.h @@ -97,7 +97,7 @@ public: class ASTBlockDecoded { public: - explicit ASTBlockDecoded(NodeBlock& new_nodes) : nodes(std::move(new_nodes)) {} + explicit ASTBlockDecoded(NodeBlock&& new_nodes) : nodes(std::move(new_nodes)) {} NodeBlock nodes; }; @@ -255,8 +255,8 @@ public: return std::holds_alternative(data); } - void TransformBlockEncoded(NodeBlock& nodes) { - data = ASTBlockDecoded(nodes); + void TransformBlockEncoded(NodeBlock&& nodes) { + data = ASTBlockDecoded(std::move(nodes)); } bool IsLoop() const { @@ -304,8 +304,8 @@ public: ASTManager(const ASTManager& o) = delete; ASTManager& operator=(const ASTManager& other) = delete; - ASTManager(ASTManager&& other); - ASTManager& operator=(ASTManager&& other); + ASTManager(ASTManager&& other) noexcept; + ASTManager& operator=(ASTManager&& other) noexcept; void Init(); @@ -362,8 +362,6 @@ public: private: bool IsBackwardsJump(ASTNode goto_node, ASTNode label_node) const; - ASTNode CommonParent(ASTNode first, ASTNode second); - bool IndirectlyRelated(ASTNode first, ASTNode second); bool DirectlyRelated(ASTNode first, ASTNode second); diff --git a/src/video_core/shader/control_flow.cpp b/src/video_core/shader/control_flow.cpp index c2fa734e7..3c3a41ba6 100644 --- a/src/video_core/shader/control_flow.cpp +++ b/src/video_core/shader/control_flow.cpp @@ -58,7 +58,7 @@ struct BlockInfo { struct CFGRebuildState { explicit CFGRebuildState(const ProgramCode& program_code, const std::size_t program_size, const u32 start) - : program_code{program_code}, program_size{program_size}, start{start} {} + : start{start}, program_code{program_code}, program_size{program_size} {} u32 start{}; std::vector block_info{}; diff --git a/src/video_core/shader/decode.cpp b/src/video_core/shader/decode.cpp index 6d4359295..2626b1616 100644 --- a/src/video_core/shader/decode.cpp +++ b/src/video_core/shader/decode.cpp @@ -90,7 +90,7 @@ public: if (node->IsBlockEncoded()) { auto block = std::get_if(node->GetInnerData()); NodeBlock bb = ir.DecodeRange(block->start, block->end); - node->TransformBlockEncoded(bb); + node->TransformBlockEncoded(std::move(bb)); } }