summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Fernando Sahmkow2019-06-27 00:39:40 -0400
committerGravatar FernandoS272019-10-04 18:52:47 -0400
commitc17953978b16f82a3b2049f8b961275020c73dd0 (patch)
tree669f353dfa3e6a6198b404e326356ca1243a4e91 /src
parentMerge pull request #2941 from FernandoS27/fix-master (diff)
downloadyuzu-c17953978b16f82a3b2049f8b961275020c73dd0.tar.gz
yuzu-c17953978b16f82a3b2049f8b961275020c73dd0.tar.xz
yuzu-c17953978b16f82a3b2049f8b961275020c73dd0.zip
shader_ir: Initial Decompile Setup
Diffstat (limited to 'src')
-rw-r--r--src/video_core/CMakeLists.txt3
-rw-r--r--src/video_core/shader/ast.cpp180
-rw-r--r--src/video_core/shader/ast.h184
-rw-r--r--src/video_core/shader/control_flow.cpp58
-rw-r--r--src/video_core/shader/control_flow.h4
-rw-r--r--src/video_core/shader/expr.h86
6 files changed, 510 insertions, 5 deletions
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index e2f85c5f1..32049a2e7 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -105,9 +105,12 @@ add_library(video_core STATIC
105 shader/decode/warp.cpp 105 shader/decode/warp.cpp
106 shader/decode/xmad.cpp 106 shader/decode/xmad.cpp
107 shader/decode/other.cpp 107 shader/decode/other.cpp
108 shader/ast.cpp
109 shader/ast.h
108 shader/control_flow.cpp 110 shader/control_flow.cpp
109 shader/control_flow.h 111 shader/control_flow.h
110 shader/decode.cpp 112 shader/decode.cpp
113 shader/expr.h
111 shader/node_helper.cpp 114 shader/node_helper.cpp
112 shader/node_helper.h 115 shader/node_helper.h
113 shader/node.h 116 shader/node.h
diff --git a/src/video_core/shader/ast.cpp b/src/video_core/shader/ast.cpp
new file mode 100644
index 000000000..5d0e85f42
--- /dev/null
+++ b/src/video_core/shader/ast.cpp
@@ -0,0 +1,180 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <string>
6
7#include "common/assert.h"
8#include "common/common_types.h"
9#include "video_core/shader/ast.h"
10#include "video_core/shader/expr.h"
11
12namespace VideoCommon::Shader {
13
14class ExprPrinter final {
15public:
16 ExprPrinter() = default;
17
18 void operator()(ExprAnd const& expr) {
19 inner += "( ";
20 std::visit(*this, *expr.operand1);
21 inner += " && ";
22 std::visit(*this, *expr.operand2);
23 inner += ')';
24 }
25
26 void operator()(ExprOr const& expr) {
27 inner += "( ";
28 std::visit(*this, *expr.operand1);
29 inner += " || ";
30 std::visit(*this, *expr.operand2);
31 inner += ')';
32 }
33
34 void operator()(ExprNot const& expr) {
35 inner += "!";
36 std::visit(*this, *expr.operand1);
37 }
38
39 void operator()(ExprPredicate const& expr) {
40 u32 pred = static_cast<u32>(expr.predicate);
41 if (pred > 7) {
42 inner += "!";
43 pred -= 8;
44 }
45 inner += "P" + std::to_string(pred);
46 }
47
48 void operator()(ExprCondCode const& expr) {
49 u32 cc = static_cast<u32>(expr.cc);
50 inner += "CC" + std::to_string(cc);
51 }
52
53 void operator()(ExprVar const& expr) {
54 inner += "V" + std::to_string(expr.var_index);
55 }
56
57 void operator()(ExprBoolean const& expr) {
58 inner += expr.value ? "true" : "false";
59 }
60
61 std::string& GetResult() {
62 return inner;
63 }
64
65 std::string inner{};
66};
67
68class ASTPrinter {
69public:
70 ASTPrinter() = default;
71
72 void operator()(ASTProgram& ast) {
73 scope++;
74 inner += "program {\n";
75 for (ASTNode& node : ast.nodes) {
76 Visit(node);
77 }
78 inner += "}\n";
79 scope--;
80 }
81
82 void operator()(ASTIf& ast) {
83 ExprPrinter expr_parser{};
84 std::visit(expr_parser, *ast.condition);
85 inner += Ident() + "if (" + expr_parser.GetResult() + ") {\n";
86 scope++;
87 for (auto& node : ast.then_nodes) {
88 Visit(node);
89 }
90 scope--;
91 if (ast.else_nodes.size() > 0) {
92 inner += Ident() + "} else {\n";
93 scope++;
94 for (auto& node : ast.else_nodes) {
95 Visit(node);
96 }
97 scope--;
98 } else {
99 inner += Ident() + "}\n";
100 }
101 }
102
103 void operator()(ASTBlockEncoded& ast) {
104 inner += Ident() + "Block(" + std::to_string(ast.start) + ", " + std::to_string(ast.end) +
105 ");\n";
106 }
107
108 void operator()(ASTVarSet& ast) {
109 ExprPrinter expr_parser{};
110 std::visit(expr_parser, *ast.condition);
111 inner +=
112 Ident() + "V" + std::to_string(ast.index) + " := " + expr_parser.GetResult() + ";\n";
113 }
114
115 void operator()(ASTLabel& ast) {
116 inner += "Label_" + std::to_string(ast.index) + ":\n";
117 }
118
119 void operator()(ASTGoto& ast) {
120 ExprPrinter expr_parser{};
121 std::visit(expr_parser, *ast.condition);
122 inner += Ident() + "(" + expr_parser.GetResult() + ") -> goto Label_" +
123 std::to_string(ast.label) + ";\n";
124 }
125
126 void operator()(ASTDoWhile& ast) {
127 ExprPrinter expr_parser{};
128 std::visit(expr_parser, *ast.condition);
129 inner += Ident() + "do {\n";
130 scope++;
131 for (auto& node : ast.loop_nodes) {
132 Visit(node);
133 }
134 scope--;
135 inner += Ident() + "} while (" + expr_parser.GetResult() + ")\n";
136 }
137
138 void operator()(ASTReturn& ast) {
139 ExprPrinter expr_parser{};
140 std::visit(expr_parser, *ast.condition);
141 inner += Ident() + "(" + expr_parser.GetResult() + ") -> " +
142 (ast.kills ? "discard" : "exit") + ";\n";
143 }
144
145 std::string& Ident() {
146 if (memo_scope == scope) {
147 return tabs_memo;
148 }
149 tabs_memo = tabs.substr(0, scope * 2);
150 memo_scope = scope;
151 return tabs_memo;
152 }
153
154 void Visit(ASTNode& node) {
155 std::visit(*this, *node->GetInnerData());
156 }
157
158 std::string& GetResult() {
159 return inner;
160 }
161
162private:
163 std::string inner{};
164 u32 scope{};
165
166 std::string tabs_memo{};
167 u32 memo_scope{};
168
169 static std::string tabs;
170};
171
172std::string ASTPrinter::tabs = " ";
173
174std::string ASTManager::Print() {
175 ASTPrinter printer{};
176 printer.Visit(main_node);
177 return printer.GetResult();
178}
179
180} // namespace VideoCommon::Shader
diff --git a/src/video_core/shader/ast.h b/src/video_core/shader/ast.h
new file mode 100644
index 000000000..ca71543fb
--- /dev/null
+++ b/src/video_core/shader/ast.h
@@ -0,0 +1,184 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <list>
8#include <memory>
9#include <optional>
10#include <string>
11#include <unordered_map>
12#include <vector>
13
14#include "video_core/shader/expr.h"
15#include "video_core/shader/node.h"
16
17namespace VideoCommon::Shader {
18
19class ASTBase;
20class ASTProgram;
21class ASTIf;
22class ASTBlockEncoded;
23class ASTVarSet;
24class ASTGoto;
25class ASTLabel;
26class ASTDoWhile;
27class ASTReturn;
28
29using ASTData = std::variant<ASTProgram, ASTIf, ASTBlockEncoded, ASTVarSet, ASTGoto, ASTLabel,
30 ASTDoWhile, ASTReturn>;
31
32using ASTNode = std::shared_ptr<ASTBase>;
33
34class ASTProgram {
35public:
36 ASTProgram() = default;
37 std::list<ASTNode> nodes;
38};
39
40class ASTIf {
41public:
42 ASTIf(Expr condition, std::list<ASTNode> then_nodes, std::list<ASTNode> else_nodes)
43 : condition(condition), then_nodes{then_nodes}, else_nodes{then_nodes} {}
44 Expr condition;
45 std::list<ASTNode> then_nodes;
46 std::list<ASTNode> else_nodes;
47};
48
49class ASTBlockEncoded {
50public:
51 ASTBlockEncoded(u32 start, u32 end) : start{start}, end{end} {}
52 u32 start;
53 u32 end;
54};
55
56class ASTVarSet {
57public:
58 ASTVarSet(u32 index, Expr condition) : index{index}, condition{condition} {}
59 u32 index;
60 Expr condition;
61};
62
63class ASTLabel {
64public:
65 ASTLabel(u32 index) : index{index} {}
66 u32 index;
67};
68
69class ASTGoto {
70public:
71 ASTGoto(Expr condition, u32 label) : condition{condition}, label{label} {}
72 Expr condition;
73 u32 label;
74};
75
76class ASTDoWhile {
77public:
78 ASTDoWhile(Expr condition, std::list<ASTNode> loop_nodes)
79 : condition(condition), loop_nodes{loop_nodes} {}
80 Expr condition;
81 std::list<ASTNode> loop_nodes;
82};
83
84class ASTReturn {
85public:
86 ASTReturn(Expr condition, bool kills) : condition{condition}, kills{kills} {}
87 Expr condition;
88 bool kills;
89};
90
91class ASTBase {
92public:
93 explicit ASTBase(ASTNode parent, ASTData data) : parent{parent}, data{data} {}
94
95 template <class U, class... Args>
96 static ASTNode Make(ASTNode parent, Args&&... args) {
97 return std::make_shared<ASTBase>(parent, ASTData(U(std::forward<Args>(args)...)));
98 }
99
100 void SetParent(ASTNode new_parent) {
101 parent = new_parent;
102 }
103
104 ASTNode& GetParent() {
105 return parent;
106 }
107
108 const ASTNode& GetParent() const {
109 return parent;
110 }
111
112 u32 GetLevel() const {
113 u32 level = 0;
114 auto next = parent;
115 while (next) {
116 next = next->GetParent();
117 level++;
118 }
119 return level;
120 }
121
122 ASTData* GetInnerData() {
123 return &data;
124 }
125
126private:
127 ASTData data;
128 ASTNode parent;
129};
130
131class ASTManager final {
132public:
133 explicit ASTManager() {
134 main_node = ASTBase::Make<ASTProgram>(nullptr);
135 program = std::get_if<ASTProgram>(main_node->GetInnerData());
136 }
137
138 void DeclareLabel(u32 address) {
139 const auto pair = labels_map.emplace(address, labels_count);
140 if (pair.second) {
141 labels_count++;
142 labels.resize(labels_count);
143 }
144 }
145
146 void InsertLabel(u32 address) {
147 u32 index = labels_map[address];
148 ASTNode label = ASTBase::Make<ASTLabel>(main_node, index);
149 labels[index] = label;
150 program->nodes.push_back(label);
151 }
152
153 void InsertGoto(Expr condition, u32 address) {
154 u32 index = labels_map[address];
155 ASTNode goto_node = ASTBase::Make<ASTGoto>(main_node, condition, index);
156 gotos.push_back(goto_node);
157 program->nodes.push_back(goto_node);
158 }
159
160 void InsertBlock(u32 start_address, u32 end_address) {
161 ASTNode block = ASTBase::Make<ASTBlockEncoded>(main_node, start_address, end_address);
162 program->nodes.push_back(block);
163 }
164
165 void InsertReturn(Expr condition, bool kills) {
166 ASTNode node = ASTBase::Make<ASTReturn>(main_node, condition, kills);
167 program->nodes.push_back(node);
168 }
169
170 std::string Print();
171
172 void Decompile() {}
173
174private:
175 std::unordered_map<u32, u32> labels_map{};
176 u32 labels_count{};
177 std::vector<ASTNode> labels{};
178 std::list<ASTNode> gotos{};
179 u32 variables{};
180 ASTProgram* program;
181 ASTNode main_node;
182};
183
184} // namespace VideoCommon::Shader
diff --git a/src/video_core/shader/control_flow.cpp b/src/video_core/shader/control_flow.cpp
index ec3a76690..bea7f767c 100644
--- a/src/video_core/shader/control_flow.cpp
+++ b/src/video_core/shader/control_flow.cpp
@@ -4,13 +4,14 @@
4 4
5#include <list> 5#include <list>
6#include <map> 6#include <map>
7#include <set>
7#include <stack> 8#include <stack>
8#include <unordered_map> 9#include <unordered_map>
9#include <unordered_set>
10#include <vector> 10#include <vector>
11 11
12#include "common/assert.h" 12#include "common/assert.h"
13#include "common/common_types.h" 13#include "common/common_types.h"
14#include "video_core/shader/ast.h"
14#include "video_core/shader/control_flow.h" 15#include "video_core/shader/control_flow.h"
15#include "video_core/shader/shader_ir.h" 16#include "video_core/shader/shader_ir.h"
16 17
@@ -64,7 +65,7 @@ struct CFGRebuildState {
64 std::list<u32> inspect_queries{}; 65 std::list<u32> inspect_queries{};
65 std::list<Query> queries{}; 66 std::list<Query> queries{};
66 std::unordered_map<u32, u32> registered{}; 67 std::unordered_map<u32, u32> registered{};
67 std::unordered_set<u32> labels{}; 68 std::set<u32> labels{};
68 std::map<u32, u32> ssy_labels{}; 69 std::map<u32, u32> ssy_labels{};
69 std::map<u32, u32> pbk_labels{}; 70 std::map<u32, u32> pbk_labels{};
70 std::unordered_map<u32, BlockStack> stacks{}; 71 std::unordered_map<u32, BlockStack> stacks{};
@@ -415,6 +416,54 @@ bool TryQuery(CFGRebuildState& state) {
415} 416}
416} // Anonymous namespace 417} // Anonymous namespace
417 418
419void InsertBranch(ASTManager& mm, const BlockBranchInfo& branch) {
420 const auto get_expr = ([&](const Condition& cond) -> Expr {
421 Expr result{};
422 if (cond.cc != ConditionCode::T) {
423 result = MakeExpr<ExprCondCode>(cond.cc);
424 }
425 if (cond.predicate != Pred::UnusedIndex) {
426 Expr extra = MakeExpr<ExprPredicate>(cond.predicate);
427 if (result) {
428 return MakeExpr<ExprAnd>(extra, result);
429 }
430 return extra;
431 }
432 if (result) {
433 return result;
434 }
435 return MakeExpr<ExprBoolean>(true);
436 });
437 if (branch.address < 0) {
438 if (branch.kill) {
439 mm.InsertReturn(get_expr(branch.condition), true);
440 return;
441 }
442 mm.InsertReturn(get_expr(branch.condition), false);
443 return;
444 }
445 mm.InsertGoto(get_expr(branch.condition), branch.address);
446}
447
448void DecompileShader(CFGRebuildState& state) {
449 ASTManager manager{};
450 for (auto label : state.labels) {
451 manager.DeclareLabel(label);
452 }
453 for (auto& block : state.block_info) {
454 if (state.labels.count(block.start) != 0) {
455 manager.InsertLabel(block.start);
456 }
457 u32 end = block.branch.ignore ? block.end + 1 : block.end;
458 manager.InsertBlock(block.start, end);
459 if (!block.branch.ignore) {
460 InsertBranch(manager, block.branch);
461 }
462 }
463 manager.Decompile();
464 LOG_CRITICAL(HW_GPU, "Decompiled Shader:\n{} \n", manager.Print());
465}
466
418std::optional<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code, 467std::optional<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code,
419 std::size_t program_size, u32 start_address) { 468 std::size_t program_size, u32 start_address) {
420 CFGRebuildState state{program_code, program_size, start_address}; 469 CFGRebuildState state{program_code, program_size, start_address};
@@ -441,7 +490,10 @@ std::optional<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code,
441 490
442 // Sort and organize results 491 // Sort and organize results
443 std::sort(state.block_info.begin(), state.block_info.end(), 492 std::sort(state.block_info.begin(), state.block_info.end(),
444 [](const BlockInfo& a, const BlockInfo& b) { return a.start < b.start; }); 493 [](const BlockInfo& a, const BlockInfo& b) -> bool { return a.start < b.start; });
494 if (decompiled) {
495 DecompileShader(state);
496 }
445 ShaderCharacteristics result_out{}; 497 ShaderCharacteristics result_out{};
446 result_out.decompilable = decompiled; 498 result_out.decompilable = decompiled;
447 result_out.start = start_address; 499 result_out.start = start_address;
diff --git a/src/video_core/shader/control_flow.h b/src/video_core/shader/control_flow.h
index b0a5e4f8c..efd037f1a 100644
--- a/src/video_core/shader/control_flow.h
+++ b/src/video_core/shader/control_flow.h
@@ -6,7 +6,7 @@
6 6
7#include <list> 7#include <list>
8#include <optional> 8#include <optional>
9#include <unordered_set> 9#include <set>
10 10
11#include "video_core/engines/shader_bytecode.h" 11#include "video_core/engines/shader_bytecode.h"
12#include "video_core/shader/shader_ir.h" 12#include "video_core/shader/shader_ir.h"
@@ -70,7 +70,7 @@ struct ShaderCharacteristics {
70 bool decompilable{}; 70 bool decompilable{};
71 u32 start{}; 71 u32 start{};
72 u32 end{}; 72 u32 end{};
73 std::unordered_set<u32> labels{}; 73 std::set<u32> labels{};
74}; 74};
75 75
76std::optional<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code, 76std::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 000000000..94678f09a
--- /dev/null
+++ b/src/video_core/shader/expr.h
@@ -0,0 +1,86 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <variant>
8#include <memory>
9
10#include "video_core/engines/shader_bytecode.h"
11
12namespace VideoCommon::Shader {
13
14using Tegra::Shader::ConditionCode;
15using Tegra::Shader::Pred;
16
17class ExprAnd;
18class ExprOr;
19class ExprNot;
20class ExprPredicate;
21class ExprCondCode;
22class ExprVar;
23class ExprBoolean;
24
25using ExprData =
26 std::variant<ExprVar, ExprCondCode, ExprPredicate, ExprNot, ExprOr, ExprAnd, ExprBoolean>;
27using Expr = std::shared_ptr<ExprData>;
28
29class ExprAnd final {
30public:
31 ExprAnd(Expr a, Expr b) : operand1{a}, operand2{b} {}
32
33 Expr operand1;
34 Expr operand2;
35};
36
37class ExprOr final {
38public:
39 ExprOr(Expr a, Expr b) : operand1{a}, operand2{b} {}
40
41 Expr operand1;
42 Expr operand2;
43};
44
45class ExprNot final {
46public:
47 ExprNot(Expr a) : operand1{a} {}
48
49 Expr operand1;
50};
51
52class ExprVar final {
53public:
54 ExprVar(u32 index) : var_index{index} {}
55
56 u32 var_index;
57};
58
59class ExprPredicate final {
60public:
61 ExprPredicate(Pred predicate) : predicate{predicate} {}
62
63 Pred predicate;
64};
65
66class ExprCondCode final {
67public:
68 ExprCondCode(ConditionCode cc) : cc{cc} {}
69
70 ConditionCode cc;
71};
72
73class ExprBoolean final {
74public:
75 ExprBoolean(bool val) : value{val} {}
76
77 bool value;
78};
79
80template <typename T, typename... Args>
81Expr MakeExpr(Args&&... args) {
82 static_assert(std::is_convertible_v<T, ExprData>);
83 return std::make_shared<ExprData>(T(std::forward<Args>(args)...));
84}
85
86} // namespace VideoCommon::Shader