From c17953978b16f82a3b2049f8b961275020c73dd0 Mon Sep 17 00:00:00 2001
From: Fernando Sahmkow <fsahmkow27@gmail.com>
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 e2f85c5f1c..32049a2e79 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 0000000000..5d0e85f427
--- /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 <string>
+
+#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<u32>(expr.predicate);
+        if (pred > 7) {
+            inner += "!";
+            pred -= 8;
+        }
+        inner += "P" + std::to_string(pred);
+    }
+
+    void operator()(ExprCondCode const& expr) {
+        u32 cc = static_cast<u32>(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 0000000000..ca71543fba
--- /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 <list>
+#include <memory>
+#include <optional>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#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<ASTProgram, ASTIf, ASTBlockEncoded, ASTVarSet, ASTGoto, ASTLabel,
+                             ASTDoWhile, ASTReturn>;
+
+using ASTNode = std::shared_ptr<ASTBase>;
+
+class ASTProgram {
+public:
+    ASTProgram() = default;
+    std::list<ASTNode> nodes;
+};
+
+class ASTIf {
+public:
+    ASTIf(Expr condition, std::list<ASTNode> then_nodes, std::list<ASTNode> else_nodes)
+        : condition(condition), then_nodes{then_nodes}, else_nodes{then_nodes} {}
+    Expr condition;
+    std::list<ASTNode> then_nodes;
+    std::list<ASTNode> 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<ASTNode> loop_nodes)
+        : condition(condition), loop_nodes{loop_nodes} {}
+    Expr condition;
+    std::list<ASTNode> 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 <class U, class... Args>
+    static ASTNode Make(ASTNode parent, Args&&... args) {
+        return std::make_shared<ASTBase>(parent, ASTData(U(std::forward<Args>(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<ASTProgram>(nullptr);
+        program = std::get_if<ASTProgram>(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<ASTLabel>(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<ASTGoto>(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<ASTBlockEncoded>(main_node, start_address, end_address);
+        program->nodes.push_back(block);
+    }
+
+    void InsertReturn(Expr condition, bool kills) {
+        ASTNode node = ASTBase::Make<ASTReturn>(main_node, condition, kills);
+        program->nodes.push_back(node);
+    }
+
+    std::string Print();
+
+    void Decompile() {}
+
+private:
+    std::unordered_map<u32, u32> labels_map{};
+    u32 labels_count{};
+    std::vector<ASTNode> labels{};
+    std::list<ASTNode> 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 ec3a766900..bea7f767cd 100644
--- a/src/video_core/shader/control_flow.cpp
+++ b/src/video_core/shader/control_flow.cpp
@@ -4,13 +4,14 @@
 
 #include <list>
 #include <map>
+#include <set>
 #include <stack>
 #include <unordered_map>
-#include <unordered_set>
 #include <vector>
 
 #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<u32> inspect_queries{};
     std::list<Query> queries{};
     std::unordered_map<u32, u32> registered{};
-    std::unordered_set<u32> labels{};
+    std::set<u32> labels{};
     std::map<u32, u32> ssy_labels{};
     std::map<u32, u32> pbk_labels{};
     std::unordered_map<u32, BlockStack> 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<ExprCondCode>(cond.cc);
+        }
+        if (cond.predicate != Pred::UnusedIndex) {
+            Expr extra = MakeExpr<ExprPredicate>(cond.predicate);
+            if (result) {
+                return MakeExpr<ExprAnd>(extra, result);
+            }
+            return extra;
+        }
+        if (result) {
+            return result;
+        }
+        return MakeExpr<ExprBoolean>(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<ShaderCharacteristics> 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<ShaderCharacteristics> 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 b0a5e4f8c9..efd037f1ad 100644
--- a/src/video_core/shader/control_flow.h
+++ b/src/video_core/shader/control_flow.h
@@ -6,7 +6,7 @@
 
 #include <list>
 #include <optional>
-#include <unordered_set>
+#include <set>
 
 #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<u32> labels{};
+    std::set<u32> labels{};
 };
 
 std::optional<ShaderCharacteristics> 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 0000000000..94678f09a2
--- /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 <variant>
+#include <memory>
+
+#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<ExprVar, ExprCondCode, ExprPredicate, ExprNot, ExprOr, ExprAnd, ExprBoolean>;
+using Expr = std::shared_ptr<ExprData>;
+
+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 <typename T, typename... Args>
+Expr MakeExpr(Args&&... args) {
+    static_assert(std::is_convertible_v<T, ExprData>);
+    return std::make_shared<ExprData>(T(std::forward<Args>(args)...));
+}
+
+} // namespace VideoCommon::Shader

From 4fde66e6094b57201d208b8abd3d7715341cd5db Mon Sep 17 00:00:00 2001
From: Fernando Sahmkow <fsahmkow27@gmail.com>
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 5d0e85f427..56a1b29f32 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<ASTDoWhile>(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<ExprNot>(condition);
+    zipper.DetachSegment(goto_node, if_end);
+    ASTNode if_node = ASTBase::Make<ASTIfThen>(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<ExprVar>(var_index);
+    ASTNode var_node = ASTBase::Make<ASTVarSet>(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<ASTBreak>(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<ASTIfThen>(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 ca71543fba..22ac8884cc 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<ASTProgram, ASTIf, ASTBlockEncoded, ASTVarSet, ASTGoto, ASTLabel,
-                             ASTDoWhile, ASTReturn>;
+using ASTData = std::variant<ASTProgram, ASTIfThen, ASTIfElse, ASTBlockEncoded, ASTVarSet, ASTGoto,
+                             ASTLabel, ASTDoWhile, ASTReturn, ASTBreak>;
 
 using ASTNode = std::shared_ptr<ASTBase>;
 
-class ASTProgram {
-public:
-    ASTProgram() = default;
-    std::list<ASTNode> nodes;
+enum class ASTZipperType : u32 {
+    Program,
+    IfThen,
+    IfElse,
+    Loop,
 };
 
-class ASTIf {
+class ASTZipper final {
 public:
-    ASTIf(Expr condition, std::list<ASTNode> then_nodes, std::list<ASTNode> 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<ASTNode> then_nodes;
-    std::list<ASTNode> 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<ASTNode> loop_nodes)
-        : condition(condition), loop_nodes{loop_nodes} {}
+    ASTDoWhile(Expr condition, ASTZipper nodes) : condition(condition), nodes{nodes} {}
     Expr condition;
-    std::list<ASTNode> 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<ASTGoto>(&data);
+        if (inner) {
+            return inner->label;
+        }
+        return -1;
+    }
+
+    Expr GetGotoCondition() const {
+        auto inner = std::get_if<ASTGoto>(&data);
+        if (inner) {
+            return inner->condition;
+        }
+        return nullptr;
+    }
+
+    void SetGotoCondition(Expr new_condition) {
+        auto inner = std::get_if<ASTGoto>(&data);
+        if (inner) {
+            inner->condition = new_condition;
+        }
+    }
+
+    bool IsIfThen() const {
+        return std::holds_alternative<ASTIfThen>(data);
+    }
+
+    bool IsIfElse() const {
+        return std::holds_alternative<ASTIfElse>(data);
+    }
+
+    bool IsLoop() const {
+        return std::holds_alternative<ASTDoWhile>(data);
+    }
+
+    ASTZipper* GetSubNodes() {
+        if (std::holds_alternative<ASTProgram>(data)) {
+            return &std::get_if<ASTProgram>(&data)->nodes;
+        }
+        if (std::holds_alternative<ASTIfThen>(data)) {
+            return &std::get_if<ASTIfThen>(&data)->nodes;
+        }
+        if (std::holds_alternative<ASTIfElse>(data)) {
+            return &std::get_if<ASTIfElse>(&data)->nodes;
+        }
+        if (std::holds_alternative<ASTDoWhile>(data)) {
+            return &std::get_if<ASTDoWhile>(&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<ASTProgram>(nullptr);
+        main_node = ASTBase::Make<ASTProgram>(ASTNode{});
         program = std::get_if<ASTProgram>(main_node->GetInnerData());
     }
 
@@ -147,31 +259,49 @@ public:
         u32 index = labels_map[address];
         ASTNode label = ASTBase::Make<ASTLabel>(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<ASTGoto>(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<ASTBlockEncoded>(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<ASTReturn>(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<u32, u32> labels_map{};
     u32 labels_count{};
     std::vector<ASTNode> labels{};

From 8be6e1c5221066a49b6ad27efbd20a999a7c16b3 Mon Sep 17 00:00:00 2001
From: Fernando Sahmkow <fsahmkow27@gmail.com>
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 a1ace89cb4..d3ab22c08d 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 dfed8b51dc..afc5ff7369 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 32049a2e79..33fa88762b 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 56a1b29f32..d521a7b52e 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<u32>(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<ASTDoWhile>(parent, condition, ASTZipper(loop_start));
-    zipper.InsertAfter(do_while_node, label);
+    ASTNode do_while_node = ASTBase::Make<ASTDoWhile>(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<ExprNot>(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<ASTIfThen>(parent, condition, ASTZipper(goto_node));
-    zipper.InsertAfter(if_node, prev);
+    ASTNode if_node;
+    if (do_else) {
+        if_node = ASTBase::Make<ASTIfElse>(parent);
+    } else {
+        Expr neg_condition = MakeExprNot(condition);
+        if_node = ASTBase::Make<ASTIfThen>(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<ExprVar>(var_index);
-    ASTNode var_node = ASTBase::Make<ASTVarSet>(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<ExprVar>(var_index);
+        ASTNode var_node = ASTBase::Make<ASTVarSet>(parent, var_index, condition);
+        ASTNode var_node_init = ASTBase::Make<ASTVarSet>(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<ASTBreak>(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<ExprVar>(var_index);
+            ASTNode var_node = ASTBase::Make<ASTVarSet>(parent, var_index, condition);
+            ASTNode var_node_init = ASTBase::Make<ASTVarSet>(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<ASTIfThen>(parent, var_condition, ASTZipper(post));
-            zipper.InsertAfter(if_node, var_node);
+            ASTNode if_node = ASTBase::Make<ASTIfThen>(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 22ac8884cc..4276f66a93 100644
--- a/src/video_core/shader/ast.h
+++ b/src/video_core/shader/ast.h
@@ -4,6 +4,7 @@
 
 #pragma once
 
+#include <functional>
 #include <list>
 #include <memory>
 #include <optional>
@@ -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<ASTProgram, ASTIfThen, ASTIfElse, ASTBlockEncoded, ASTVarSet, ASTGoto,
+using ASTData = std::variant<ASTProgram, ASTIfThen, ASTIfElse, ASTBlockEncoded, ASTBlockDecoded, ASTVarSet, ASTGoto,
                              ASTLabel, ASTDoWhile, ASTReturn, ASTBreak>;
 
 using ASTNode = std::shared_ptr<ASTBase>;
@@ -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<NodeBlock(u32 start, u32 end)>;
+
 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<ASTIfThen>(&data);
+        if (inner) {
+            return inner->condition;
+        }
+        return nullptr;
+    }
+
     void SetGotoCondition(Expr new_condition) {
         auto inner = std::get_if<ASTGoto>(&data);
         if (inner) {
@@ -210,6 +229,18 @@ public:
         return std::holds_alternative<ASTIfElse>(data);
     }
 
+    bool IsBlockEncoded() const {
+        return std::holds_alternative<ASTBlockEncoded>(data);
+    }
+
+    void TransformBlockEncoded(TransformCallback& callback) {
+        auto block = std::get_if<ASTBlockEncoded>(&data);
+        const u32 start = block->start;
+        const u32 end = block->end;
+        NodeBlock nodes = callback(start, end);
+        data = ASTBlockDecoded(nodes);
+    }
+
     bool IsLoop() const {
         return std::holds_alternative<ASTDoWhile>(data);
     }
@@ -245,6 +276,7 @@ public:
     explicit ASTManager() {
         main_node = ASTBase::Make<ASTProgram>(ASTNode{});
         program = std::get_if<ASTProgram>(main_node->GetInnerData());
+        true_condition = MakeExpr<ExprBoolean>(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 bea7f767cd..7a21d870fa 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<ExprCondCode>(cond.cc);
         }
         if (cond.predicate != Pred::UnusedIndex) {
-            Expr extra = MakeExpr<ExprPredicate>(cond.predicate);
+            u32 pred = static_cast<u32>(cond.predicate);
+            bool negate;
+            if (pred > 7) {
+                negate = true;
+                pred -= 8;
+            }
+            Expr extra = MakeExpr<ExprPredicate>(pred);
+            if (negate) {
+                extra = MakeExpr<ExprNot>(extra);
+            }
             if (result) {
                 return MakeExpr<ExprAnd>(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<ShaderCharacteristics> 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 0000000000..ebce6339bd
--- /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 <memory>
+#include <variant>
+
+#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<ExprBoolean>(*expr);
+}
+
+bool ExprBooleanGet(Expr expr) {
+    return std::get_if<ExprBoolean>(expr.get())->value;
+}
+
+Expr MakeExprNot(Expr first) {
+    if (std::holds_alternative<ExprNot>(*first)) {
+        return std::get_if<ExprNot>(first.get())->operand1;
+    }
+    return MakeExpr<ExprNot>(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<ExprAnd>(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<ExprOr>(first, second);
+}
+
+bool ExprAreEqual(Expr first, Expr second) {
+    return (*first) == (*second);
+}
+
+bool ExprAreOpposite(Expr first, Expr second) {
+    if (std::holds_alternative<ExprNot>(*first)) {
+        return ExprAreEqual(std::get_if<ExprNot>(first.get())->operand1, second);
+    }
+    if (std::holds_alternative<ExprNot>(*second)) {
+        return ExprAreEqual(std::get_if<ExprNot>(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 94678f09a2..f012f6fcff 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<ExprData>(T(std::forward<Args>(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 <fsahmkow27@gmail.com>
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 d521a7b52e..0bf289f987 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<ASTProgram>(ASTNode{});
+    program = std::get_if<ASTProgram>(main_node->GetInnerData());
+    true_condition = MakeExpr<ExprBoolean>(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<ASTLabel>(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<ASTGoto>(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<ASTBlockEncoded>(main_node, start_address, end_address);
+    program->nodes.PushBack(block);
+}
+
+void ASTManager::InsertReturn(Expr condition, bool kills) {
+    ASTNode node = ASTBase::Make<ASTReturn>(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 4276f66a93..958989bcd5 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<ASTProgram, ASTIfThen, ASTIfElse, ASTBlockEncoded, ASTBlockDecoded, ASTVarSet, ASTGoto,
-                             ASTLabel, ASTDoWhile, ASTReturn, ASTBreak>;
+using ASTData = std::variant<ASTProgram, ASTIfThen, ASTIfElse, ASTBlockEncoded, ASTBlockDecoded,
+                             ASTVarSet, ASTGoto, ASTLabel, ASTDoWhile, ASTReturn, ASTBreak>;
 
 using ASTNode = std::shared_ptr<ASTBase>;
 
@@ -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<ASTProgram>(ASTNode{});
-        program = std::get_if<ASTProgram>(main_node->GetInnerData());
-        true_condition = MakeExpr<ExprBoolean>(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<ASTLabel>(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<ASTGoto>(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<ASTBlockEncoded>(main_node, start_address, end_address);
-        program->nodes.PushBack(block);
-    }
+    void DeclareLabel(u32 address);
 
-    void InsertReturn(Expr condition, bool kills) {
-        ASTNode node = ASTBase::Make<ASTReturn>(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<ASTNode> labels{};
     std::list<ASTNode> 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 7a21d870fa..deb3d3ebd9 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<BlockInfo> block_info{};
@@ -71,6 +71,7 @@ struct CFGRebuildState {
     std::unordered_map<u32, BlockStack> 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<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code,
-                                              std::size_t program_size, u32 start_address) {
-    CFGRebuildState state{program_code, program_size, start_address};
-
+std::unique_ptr<ShaderCharacteristics> 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<ShaderCharacteristics> 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<ShaderCharacteristics>();
+    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<ShaderCharacteristics> 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 efd037f1ad..2805d975ca 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<ShaderBlock> blocks{};
-    bool decompilable{};
+    bool decompiled{};
     u32 start{};
     u32 end{};
-    std::set<u32> labels{};
 };
 
-std::optional<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code,
-                                              std::size_t program_size, u32 start_address);
+std::unique_ptr<ShaderCharacteristics> 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 47a9fd9615..381e874157 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<u32>(exit_branch)) {
-                    return;
-                }
-                basic_blocks.insert({label, nodes});
-            };
-            const auto& blocks = shader_info.blocks;
-            NodeBlock current_block;
-            u32 current_label = static_cast<u32>(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 2c357f310f..c79f80e04d 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 6f666ee300..a91cd7d67b 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<u32, NodeBlock> basic_blocks;
     NodeBlock global_code;
+    ASTManager program_manager;
 
     std::set<u32> used_registers;
     std::set<Tegra::Shader::Pred> used_predicates;

From 38fc995f6cc2c2af29abc976ddb45b72873b2cc4 Mon Sep 17 00:00:00 2001
From: Fernando Sahmkow <fsahmkow27@gmail.com>
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 8fa9e65340..2955c6abf1 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<Tegra::Shader::Pred>(expr.predicate);
+        inner += decomp.GetPredicate(pred);
+    }
+
+    void operator()(VideoCommon::Shader::ExprCondCode& expr) {
+        Node cc = decomp.ir.GetConditionCode(expr.cc);
+        std::string target;
+
+        if (const auto pred = std::get_if<PredicateNode>(&*cc)) {
+            const auto index = pred->GetIndex();
+            switch (index) {
+            case Tegra::Shader::Pred::NeverExecute:
+                target = "false";
+            case Tegra::Shader::Pred::UnusedIndex:
+                target = "true";
+            default:
+                target = decomp.GetPredicate(index);
+            }
+        } else if (const auto flag = std::get_if<InternalFlagNode>(&*cc)) {
+            target = decomp.GetInternalFlag(flag->GetFlag());
+        }
+        inner += target;
+    }
+
+    void operator()(VideoCommon::Shader::ExprVar& expr) {
+        inner += flow_var + std::to_string(expr.var_index);
+    }
+
+    void operator()(VideoCommon::Shader::ExprBoolean& expr) {
+        inner += expr.value ? "true" : "false";
+    }
+
+    std::string& GetResult() {
+        return inner;
+    }
+
+private:
+    std::string inner{};
+    GLSLDecompiler& decomp;
+};
+
+class ASTDecompiler {
+public:
+    ASTDecompiler(GLSLDecompiler& decomp) : decomp{decomp} {}
+
+    void operator()(VideoCommon::Shader::ASTProgram& ast) {
+        ASTNode current = ast.nodes.GetFirst();
+        while (current) {
+            Visit(current);
+            current = current->GetNext();
+        }
+    }
+
+    void operator()(VideoCommon::Shader::ASTIfThen& ast) {
+        ExprDecompiler expr_parser{decomp};
+        std::visit(expr_parser, *ast.condition);
+        decomp.code.AddLine("if ({}) {{", expr_parser.GetResult());
+        decomp.code.scope++;
+        ASTNode current = ast.nodes.GetFirst();
+        while (current) {
+            Visit(current);
+            current = current->GetNext();
+        }
+        decomp.code.scope--;
+        decomp.code.AddLine("}}");
+    }
+
+    void operator()(VideoCommon::Shader::ASTIfElse& ast) {
+        decomp.code.AddLine("else {{");
+        decomp.code.scope++;
+        ASTNode current = ast.nodes.GetFirst();
+        while (current) {
+            Visit(current);
+            current = current->GetNext();
+        }
+        decomp.code.scope--;
+        decomp.code.AddLine("}}");
+    }
+
+    void operator()(VideoCommon::Shader::ASTBlockEncoded& ast) {
+        UNREACHABLE();
+    }
+
+    void operator()(VideoCommon::Shader::ASTBlockDecoded& ast) {
+        decomp.VisitBlock(ast.nodes);
+    }
+
+    void operator()(VideoCommon::Shader::ASTVarSet& ast) {
+        ExprDecompiler expr_parser{decomp};
+        std::visit(expr_parser, *ast.condition);
+        decomp.code.AddLine("{}{} = {};", flow_var, ast.index, expr_parser.GetResult());
+    }
+
+    void operator()(VideoCommon::Shader::ASTLabel& ast) {
+        decomp.code.AddLine("// Label_{}:", ast.index);
+    }
+
+    void operator()(VideoCommon::Shader::ASTGoto& ast) {
+        UNREACHABLE();
+    }
+
+    void operator()(VideoCommon::Shader::ASTDoWhile& ast) {
+        ExprDecompiler expr_parser{decomp};
+        std::visit(expr_parser, *ast.condition);
+        decomp.code.AddLine("do {{");
+        decomp.code.scope++;
+        ASTNode current = ast.nodes.GetFirst();
+        while (current) {
+            Visit(current);
+            current = current->GetNext();
+        }
+        decomp.code.scope--;
+        decomp.code.AddLine("}} while({});", expr_parser.GetResult());
+    }
+
+    void operator()(VideoCommon::Shader::ASTReturn& ast) {
+        bool is_true = VideoCommon::Shader::ExprIsTrue(ast.condition);
+        if (!is_true) {
+            ExprDecompiler expr_parser{decomp};
+            std::visit(expr_parser, *ast.condition);
+            decomp.code.AddLine("if ({}) {{", expr_parser.GetResult());
+            decomp.code.scope++;
+        }
+        if (ast.kills) {
+            decomp.code.AddLine("discard;");
+        } else {
+            decomp.WriteExit();
+        }
+        if (!is_true) {
+            decomp.code.scope--;
+            decomp.code.AddLine("}}");
+        }
+    }
+
+    void operator()(VideoCommon::Shader::ASTBreak& ast) {
+        bool is_true = VideoCommon::Shader::ExprIsTrue(ast.condition);
+        if (!is_true) {
+            ExprDecompiler expr_parser{decomp};
+            std::visit(expr_parser, *ast.condition);
+            decomp.code.AddLine("if ({}) {{", expr_parser.GetResult());
+            decomp.code.scope++;
+        }
+        decomp.code.AddLine("break;");
+        if (!is_true) {
+            decomp.code.scope--;
+            decomp.code.AddLine("}}");
+        }
+    }
+
+    void Visit(VideoCommon::Shader::ASTNode& node) {
+        std::visit(*this, *node->GetInnerData());
+    }
+
+private:
+    GLSLDecompiler& decomp;
+};
+
+void GLSLDecompiler::DecompileAST() {
+    u32 num_flow_variables = ir.GetASTNumVariables();
+    for (u32 i = 0; i < num_flow_variables; i++) {
+        code.AddLine("bool {}{} = false;", flow_var, i);
+    }
+    ASTDecompiler decompiler{*this};
+    VideoCommon::Shader::ASTNode program = ir.GetASTProgram();
+    decompiler.Visit(program);
+}
+
 } // Anonymous namespace
 
 std::string GetCommonDeclarations() {
diff --git a/src/video_core/shader/ast.cpp b/src/video_core/shader/ast.cpp
index 0bf289f987..68a96cc79e 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<ASTProgram>(ASTNode{});
     program = std::get_if<ASTProgram>(main_node->GetInnerData());
-    true_condition = MakeExpr<ExprBoolean>(true);
+    false_condition = MakeExpr<ExprBoolean>(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<ExprVar>(var_index);
         ASTNode var_node = ASTBase::Make<ASTVarSet>(parent, var_index, condition);
-        ASTNode var_node_init = ASTBase::Make<ASTVarSet>(parent, var_index, true_condition);
+        ASTNode var_node_init = ASTBase::Make<ASTVarSet>(parent, var_index, false_condition);
         zipper2.InsertBefore(var_node_init, parent);
         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<ExprVar>(var_index);
             ASTNode var_node = ASTBase::Make<ASTVarSet>(parent, var_index, condition);
-            ASTNode var_node_init = ASTBase::Make<ASTVarSet>(parent, var_index, true_condition);
+            ASTNode var_node_init = ASTBase::Make<ASTVarSet>(parent, var_index, false_condition);
             if (is_if) {
                 zipper2.InsertBefore(var_node_init, parent);
             } else {
diff --git a/src/video_core/shader/ast.h b/src/video_core/shader/ast.h
index 958989bcd5..06ab20cc52 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<NodeBlock(u32 start, u32 end)>;
-
 class ASTBase {
 public:
     explicit ASTBase(ASTNode parent, ASTData data) : parent{parent}, data{data} {}
@@ -233,11 +231,7 @@ public:
         return std::holds_alternative<ASTBlockEncoded>(data);
     }
 
-    void TransformBlockEncoded(TransformCallback& callback) {
-        auto block = std::get_if<ASTBlockEncoded>(&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 deb3d3ebd9..a299228150 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<u32>(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 2805d975ca..347a35dcfe 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<ShaderCharacteristics> 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 381e874157..e7e0903f67 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<ASTBlockEncoded>(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 d46e0f8232..6f678003c8 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<u32>(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<u32>(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 ebce6339bd..ca633ffb10 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 f012f6fcff..b954cffb03 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 c79f80e04d..004b1e16f9 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<AbufNode>(index, static_cast<u32>(element), std::move(buffer));
 }
 
-Node ShaderIR::GetInternalFlag(InternalFlag flag, bool negated) {
+Node ShaderIR::GetInternalFlag(InternalFlag flag, bool negated) const {
     const Node node = MakeNode<InternalFlagNode>(flag);
     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<u32>(cc));
-        return GetPredicate(static_cast<u64>(Pred::NeverExecute));
+        return MakeNode<PredicateNode>(Pred::NeverExecute, false);
     }
 }
 
diff --git a/src/video_core/shader/shader_ir.h b/src/video_core/shader/shader_ir.h
index a91cd7d67b..48c7b722ee 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 <fsahmkow27@gmail.com>
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 d3ab22c08d..09eabe2c73 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 afc5ff7369..0ed96c0d4d 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 33fa88762b..eaa694ff8d 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 2955c6abf1..b8c3442bcd 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 3a8d9e1da4..72a49ebdcf 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 68a96cc79e..14c50e1c68 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 06ab20cc52..849d0612c7 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<u32, u32> labels_map{};
     u32 labels_count{};
     std::vector<ASTNode> labels{};
diff --git a/src/video_core/shader/compiler_settings.cpp b/src/video_core/shader/compiler_settings.cpp
new file mode 100644
index 0000000000..cddcbd4f0e
--- /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 0000000000..e1fb5bc3a4
--- /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 a299228150..c4351969b5 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<BlockInfo> block_info{};
@@ -71,7 +71,7 @@ struct CFGRebuildState {
     std::unordered_map<u32, BlockStack> 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<ShaderCharacteristics> 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<ShaderCharacteristics>();
+    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<ShaderCharacteristics>();
+            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<ShaderCharacteristics>();
-    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<ShaderCharacteristics> 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 347a35dcfe..8d0d084224 100644
--- a/src/video_core/shader/control_flow.h
+++ b/src/video_core/shader/control_flow.h
@@ -9,8 +9,9 @@
 #include <set>
 
 #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<ShaderBlock> blocks{};
-    bool decompiled{};
+    std::set<u32> labels{};
     u32 start{};
     u32 end{};
+    ASTManager manager{true};
+    CompilerSettings settings{};
 };
 
 std::unique_ptr<ShaderCharacteristics> 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 e7e0903f67..6d4359295e 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<u32>(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<u32>(exit_branch)) {
+                return;
+            }
+            basic_blocks.insert({label, nodes});
+        };
+        const auto& blocks = shader_info.blocks;
+        NodeBlock current_block;
+        u32 current_label = static_cast<u32>(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<u32>(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 6f678003c8..d46e0f8232 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<u32>(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<u32>(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 004b1e16f9..04e3646345 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 48c7b722ee..7a91c9bb67 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<u32, NodeBlock>& 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<u32, NodeBlock> basic_blocks;
     NodeBlock global_code;
     ASTManager program_manager;
+    CompilerSettings settings{};
 
     std::set<u32> used_registers;
     std::set<Tegra::Shader::Pred> used_predicates;

From 0366c18d87f8c60ff6a99db668a7f2d810aaeeb0 Mon Sep 17 00:00:00 2001
From: Fernando Sahmkow <fsahmkow27@gmail.com>
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 14c50e1c68..74b9a8f9a9 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 849d0612c7..07deb58e4d 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<ASTLabel>(&data);
+        if (inner) {
+            inner->unused = true;
+        }
+    }
+
     Expr GetIfCondition() const {
         auto inner = std::get_if<ASTIfThen>(&data);
         if (inner) {

From ca9901867e91cd0be0cc75094ee8ea2fb2767c47 Mon Sep 17 00:00:00 2001
From: Fernando Sahmkow <fsahmkow27@gmail.com>
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 77fc58f25a..505e495702 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<std::size_t>(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<u32>(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<u32>(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<u32, Id> registers;
     std::map<Tegra::Shader::Pred, Id> predicates;
+    std::map<u32, Id> flow_variables;
     Id local_memory{};
     std::array<Id, INTERNAL_FLAGS_COUNT> internal_flags{};
     std::map<Attribute::Index, Id> input_attributes;
@@ -1580,6 +1617,223 @@ private:
     std::map<u32, Id> 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<Tegra::Shader::Pred>(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<PredicateNode>(&*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<InternalFlagNode>(&*cc)) {
+            target = decomp.internal_flags.at(static_cast<u32>(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<SPIRVDecompiler>(device, ir, stage);
diff --git a/src/video_core/shader/ast.h b/src/video_core/shader/ast.h
index 07deb58e4d..12db336df2 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<ASTLabel>(&data);
         if (inner) {
             inner->unused = true;
         }
     }
 
+    bool IsLabelUnused() const {
+        auto inner = std::get_if<ASTLabel>(&data);
+        if (inner) {
+            return inner->unused;
+        }
+        return true;
+    }
+
+    u32 GetLabelIndex() const {
+        auto inner = std::get_if<ASTLabel>(&data);
+        if (inner) {
+            return inner->index;
+        }
+        return -1;
+    }
+
     Expr GetIfCondition() const {
         auto inner = std::get_if<ASTIfThen>(&data);
         if (inner) {
@@ -336,6 +352,10 @@ public:
         return variables;
     }
 
+    const std::vector<ASTNode>& 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 7a91c9bb67..105981d67a 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 <fsahmkow27@gmail.com>
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 505e495702..72fbc69c4a 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 74b9a8f9a9..7e5e916abb 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 12db336df2..1b73f301fb 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<u32, u32> labels_map{};
     u32 labels_count{};
     std::vector<ASTNode> labels{};
diff --git a/src/video_core/shader/compiler_settings.h b/src/video_core/shader/compiler_settings.h
index e1fb5bc3a4..916018c011 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 c4351969b5..c2fa734e76 100644
--- a/src/video_core/shader/control_flow.cpp
+++ b/src/video_core/shader/control_flow.cpp
@@ -516,7 +516,8 @@ std::unique_ptr<ShaderCharacteristics> 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 8d0d084224..74e54a5c77 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<u32> 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 04e3646345..c1f2b88c80 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 <fsahmkow27@gmail.com>
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 72fbc69c4a..4527e92611 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 <fsahmkow27@gmail.com>
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 7e5e916abb..7c8507280a 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<ASTLabel>(main_node, index);
+    const u32 index = labels_map[address];
+    const ASTNode label = ASTBase::Make<ASTLabel>(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<ASTGoto>(main_node, condition, index);
+    const u32 index = labels_map[address];
+    const ASTNode goto_node = ASTBase::Make<ASTGoto>(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<ASTBlockEncoded>(main_node, start_address, end_address);
+    const ASTNode block = ASTBase::Make<ASTBlockEncoded>(main_node, start_address, end_address);
     program->nodes.PushBack(block);
 }
 
 void ASTManager::InsertReturn(Expr condition, bool kills) {
-    ASTNode node = ASTBase::Make<ASTReturn>(main_node, condition, kills);
+    const ASTNode node = ASTBase::Make<ASTReturn>(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<ASTDoWhile>(parent, condition);
+    const ASTNode do_while_node = ASTBase::Make<ASTDoWhile>(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<ExprVar>(var_index);
-        ASTNode var_node = ASTBase::Make<ASTVarSet>(parent, var_index, condition);
-        ASTNode var_node_init = ASTBase::Make<ASTVarSet>(parent, var_index, false_condition);
+        const u32 var_index = NewVariable();
+        const Expr var_condition = MakeExpr<ExprVar>(var_index);
+        const ASTNode var_node = ASTBase::Make<ASTVarSet>(parent, var_index, condition);
+        const ASTNode var_node_init = ASTBase::Make<ASTVarSet>(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<ASTBreak>(parent, var_condition);
+        const ASTNode break_node = ASTBase::Make<ASTBreak>(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<ExprVar>(var_index);
-            ASTNode var_node = ASTBase::Make<ASTVarSet>(parent, var_index, condition);
-            ASTNode var_node_init = ASTBase::Make<ASTVarSet>(parent, var_index, false_condition);
+            const u32 var_index = NewVariable();
+            const Expr var_condition = MakeExpr<ExprVar>(var_index);
+            const ASTNode var_node = ASTBase::Make<ASTVarSet>(parent, var_index, condition);
+            const ASTNode var_node_init =
+                ASTBase::Make<ASTVarSet>(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<ASTIfThen>(parent, MakeExprNot(var_condition));
+            const ASTNode if_node = ASTBase::Make<ASTIfThen>(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 b954cffb03..60598977a3 100644
--- a/src/video_core/shader/expr.h
+++ b/src/video_core/shader/expr.h
@@ -4,8 +4,8 @@
 
 #pragma once
 
-#include <variant>
 #include <memory>
+#include <variant>
 
 #include "video_core/engines/shader_bytecode.h"
 

From 189a50bc2addbf43824be396f205e900af7523b3 Mon Sep 17 00:00:00 2001
From: Fernando Sahmkow <fsahmkow27@gmail.com>
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 b8c3442bcd..572fae3533 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<Tegra::Shader::Pred>(expr.predicate);
+        const auto pred = static_cast<Tegra::Shader::Pred>(expr.predicate);
         inner += decomp.GetPredicate(pred);
     }
 
     void operator()(VideoCommon::Shader::ExprCondCode& expr) {
-        Node cc = decomp.ir.GetConditionCode(expr.cc);
+        const Node cc = decomp.ir.GetConditionCode(expr.cc);
         std::string target;
 
         if (const auto pred = std::get_if<PredicateNode>(&*cc)) {
@@ -2321,6 +2319,8 @@ public:
             }
         } else if (const auto flag = std::get_if<InternalFlagNode>(&*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 <fsahmkow27@gmail.com>
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 4527e92611..11effe4a1a 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<Type::Uint>(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<void()> 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<Id, Id> 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 <fsahmkow27@gmail.com>
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 7c8507280a..54f0240e15 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<ASTBreak>(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<ExprVar>(var_index);
+        const ASTNode var_node = ASTBase::Make<ASTVarSet>(parent, var_index, condition);
+        const ASTNode var_node_init = ASTBase::Make<ASTVarSet>(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<ExprVar>(var_index);
-            const ASTNode var_node = ASTBase::Make<ASTVarSet>(parent, var_index, condition);
-            const ASTNode var_node_init =
-                ASTBase::Make<ASTVarSet>(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<ASTIfThen>(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 <fsahmkow27@gmail.com>
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 54f0240e15..fc440526f1 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 <fsahmkow27@gmail.com>
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 572fae3533..bff1067a4b 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 11effe4a1a..4bc7da198e 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<Tegra::Shader::Pred>(expr.predicate);
+        const auto pred = static_cast<Tegra::Shader::Pred>(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<PredicateNode>(&*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 <fsahmkow27@gmail.com>
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 4bc7da198e..2b55a3727a 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 <fsahmkow27@gmail.com>
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 2b55a3727a..8bcd04221f 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<Tegra::Shader::Pred>(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<InternalFlagNode>(&*cc)) {
             target = decomp.internal_flags.at(static_cast<u32>(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 fc440526f1..c4548f0bc9 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 1b73f301fb..8efd4c1477 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<u32> GetGotoLabel() const {
         auto inner = std::get_if<ASTGoto>(&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<u32> GetLabelIndex() const {
         auto inner = std::get_if<ASTLabel>(&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 60598977a3..4c399cef9c 100644
--- a/src/video_core/shader/expr.h
+++ b/src/video_core/shader/expr.h
@@ -28,7 +28,7 @@ using Expr = std::shared_ptr<ExprData>;
 
 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 <fsahmkow27@gmail.com>
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 bff1067a4b..6a610a3bca 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 72a49ebdcf..b5a43e79e7 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 c4548f0bc9..2eb065c3df 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<ExprBoolean>(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 8efd4c1477..ba234138eb 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<ASTBlockEncoded>(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 c2fa734e76..3c3a41ba62 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<BlockInfo> block_info{};
diff --git a/src/video_core/shader/decode.cpp b/src/video_core/shader/decode.cpp
index 6d4359295e..2626b16160 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<ASTBlockEncoded>(node->GetInnerData());
             NodeBlock bb = ir.DecodeRange(block->start, block->end);
-            node->TransformBlockEncoded(bb);
+            node->TransformBlockEncoded(std::move(bb));
         }
     }