summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Fernando Sahmkow2019-06-25 20:15:40 -0400
committerGravatar FernandoS272019-07-09 08:14:39 -0400
commit01b21ee1e8e7455dd84ee7f22d33426caaaafdb3 (patch)
treeed443fde0bf5e5bd140d0f7787c44e87ad5f5505 /src
parentshader_ir: Unify blocks in decompiled shaders. (diff)
downloadyuzu-01b21ee1e8e7455dd84ee7f22d33426caaaafdb3.tar.gz
yuzu-01b21ee1e8e7455dd84ee7f22d33426caaaafdb3.tar.xz
yuzu-01b21ee1e8e7455dd84ee7f22d33426caaaafdb3.zip
shader_ir: Corrections, documenting and asserting control_flow
Diffstat (limited to 'src')
-rw-r--r--src/video_core/shader/control_flow.cpp80
-rw-r--r--src/video_core/shader/control_flow.h16
-rw-r--r--src/video_core/shader/decode.cpp10
3 files changed, 54 insertions, 52 deletions
diff --git a/src/video_core/shader/control_flow.cpp b/src/video_core/shader/control_flow.cpp
index c99d95b57..deef0cd3a 100644
--- a/src/video_core/shader/control_flow.cpp
+++ b/src/video_core/shader/control_flow.cpp
@@ -1,3 +1,6 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
1 4
2#include <list> 5#include <list>
3#include <map> 6#include <map>
@@ -17,16 +20,18 @@ using Tegra::Shader::OpCode;
17 20
18constexpr s32 unassigned_branch = -2; 21constexpr s32 unassigned_branch = -2;
19 22
23/***
24 * 'ControlStack' represents a static stack of control jumps such as SSY and PBK
25 * stacks in Maxwell.
26 ***/
20struct ControlStack { 27struct ControlStack {
21 std::array<u32, 20> stack; 28 static constexpr std::size_t stack_fixed_size = 20;
29 std::array<u32, stack_fixed_size> stack{};
22 u32 index{}; 30 u32 index{};
23 31
24 ControlStack() {} 32 ControlStack() {}
25 33
26 ControlStack(const ControlStack& cp) { 34 ControlStack(const ControlStack& cp) = default;
27 index = cp.index;
28 std::memcpy(stack.data(), cp.stack.data(), index * sizeof(u32));
29 }
30 35
31 bool Compare(const ControlStack& cs) const { 36 bool Compare(const ControlStack& cs) const {
32 if (index != cs.index) { 37 if (index != cs.index) {
@@ -35,6 +40,7 @@ struct ControlStack {
35 return std::memcmp(stack.data(), cs.stack.data(), index * sizeof(u32)) == 0; 40 return std::memcmp(stack.data(), cs.stack.data(), index * sizeof(u32)) == 0;
36 } 41 }
37 42
43 /// This compare just compares the top of the stack against one another
38 bool SoftCompare(const ControlStack& cs) const { 44 bool SoftCompare(const ControlStack& cs) const {
39 if (index == 0 || cs.index == 0) { 45 if (index == 0 || cs.index == 0) {
40 return index == cs.index; 46 return index == cs.index;
@@ -51,7 +57,7 @@ struct ControlStack {
51 } 57 }
52 58
53 bool Push(u32 address) { 59 bool Push(u32 address) {
54 if (index >= 20) { 60 if (index >= stack.size()) {
55 return false; 61 return false;
56 } 62 }
57 stack[index] = address; 63 stack[index] = address;
@@ -70,21 +76,23 @@ struct ControlStack {
70 76
71struct Query { 77struct Query {
72 Query() {} 78 Query() {}
73 Query(const Query& q) : address{q.address}, ssy_stack{q.ssy_stack}, pbk_stack{q.pbk_stack} {} 79 Query(const Query& q) = default;
74 u32 address; 80 u32 address{};
75 ControlStack ssy_stack{}; 81 ControlStack ssy_stack{};
76 ControlStack pbk_stack{}; 82 ControlStack pbk_stack{};
77}; 83};
78 84
79struct BlockStack { 85struct BlockStack {
80 BlockStack() = default; 86 BlockStack() = default;
81 BlockStack(const BlockStack& b) : ssy_stack{b.ssy_stack}, pbk_stack{b.pbk_stack} {} 87 BlockStack(const BlockStack& b) = default;
82 BlockStack(const Query& q) : ssy_stack{q.ssy_stack}, pbk_stack{q.pbk_stack} {} 88 BlockStack(const Query& q) : ssy_stack{q.ssy_stack}, pbk_stack{q.pbk_stack} {}
83 ControlStack ssy_stack{}; 89 ControlStack ssy_stack{};
84 ControlStack pbk_stack{}; 90 ControlStack pbk_stack{};
85}; 91};
86 92
87struct BlockBranchInfo { 93struct BlockBranchInfo {
94 BlockBranchInfo() = default;
95 BlockBranchInfo(const BlockBranchInfo& b) = default;
88 Condition condition{}; 96 Condition condition{};
89 s32 address{exit_branch}; 97 s32 address{exit_branch};
90 bool kill{}; 98 bool kill{};
@@ -94,7 +102,7 @@ struct BlockBranchInfo {
94}; 102};
95 103
96struct BlockInfo { 104struct BlockInfo {
97 BlockInfo() {} 105 BlockInfo() = default;
98 u32 start{}; 106 u32 start{};
99 u32 end{}; 107 u32 end{};
100 bool visited{}; 108 bool visited{};
@@ -107,24 +115,15 @@ struct BlockInfo {
107 115
108struct CFGRebuildState { 116struct CFGRebuildState {
109 explicit CFGRebuildState(const ProgramCode& program_code, const std::size_t program_size) 117 explicit CFGRebuildState(const ProgramCode& program_code, const std::size_t program_size)
110 : program_code{program_code}, program_size{program_size} { 118 : program_code{program_code}, program_size{program_size} {}
111 // queries.clear();
112 block_info.clear();
113 labels.clear();
114 registered.clear();
115 ssy_labels.clear();
116 pbk_labels.clear();
117 inspect_queries.clear();
118 queries.clear();
119 }
120 119
121 std::vector<BlockInfo> block_info{}; 120 std::vector<BlockInfo> block_info{};
122 std::list<u32> inspect_queries{}; 121 std::list<u32> inspect_queries{};
123 std::list<Query> queries{}; 122 std::list<Query> queries{};
124 std::unordered_map<u32, u32> registered{}; 123 std::unordered_map<u32, u32> registered{};
125 std::unordered_set<u32> labels{}; 124 std::unordered_set<u32> labels{};
126 std::map<u32, u32> ssy_labels; 125 std::map<u32, u32> ssy_labels{};
127 std::map<u32, u32> pbk_labels; 126 std::map<u32, u32> pbk_labels{};
128 std::unordered_map<u32, BlockStack> stacks{}; 127 std::unordered_map<u32, BlockStack> stacks{};
129 const ProgramCode& program_code; 128 const ProgramCode& program_code;
130 const std::size_t program_size; 129 const std::size_t program_size;
@@ -156,7 +155,7 @@ BlockInfo* CreateBlockInfo(CFGRebuildState& state, u32 start, u32 end) {
156 auto& it = state.block_info.emplace_back(); 155 auto& it = state.block_info.emplace_back();
157 it.start = start; 156 it.start = start;
158 it.end = end; 157 it.end = end;
159 u32 index = state.block_info.size() - 1; 158 const u32 index = static_cast<u32>(state.block_info.size() - 1);
160 state.registered.insert({start, index}); 159 state.registered.insert({start, index});
161 return &it; 160 return &it;
162} 161}
@@ -172,11 +171,10 @@ enum class ParseResult : u32 {
172}; 171};
173 172
174ParseResult ParseCode(CFGRebuildState& state, u32 address, ParseInfo& parse_info) { 173ParseResult ParseCode(CFGRebuildState& state, u32 address, ParseInfo& parse_info) {
175
176 u32 offset = static_cast<u32>(address); 174 u32 offset = static_cast<u32>(address);
177 u32 end_address = static_cast<u32>(state.program_size - 10U) * 8U; 175 const u32 end_address = static_cast<u32>(state.program_size - 10U) * 8U;
178 176
179 auto insert_label = ([](CFGRebuildState& state, u32 address) { 177 const auto insert_label = ([](CFGRebuildState& state, u32 address) {
180 auto pair = state.labels.emplace(address); 178 auto pair = state.labels.emplace(address);
181 if (pair.second) { 179 if (pair.second) {
182 state.inspect_queries.push_back(address); 180 state.inspect_queries.push_back(address);
@@ -361,20 +359,18 @@ bool TryInspectAddress(CFGRebuildState& state) {
361 if (state.inspect_queries.empty()) { 359 if (state.inspect_queries.empty()) {
362 return false; 360 return false;
363 } 361 }
364 u32 address = state.inspect_queries.front(); 362 const u32 address = state.inspect_queries.front();
365 state.inspect_queries.pop_front(); 363 state.inspect_queries.pop_front();
366 auto search_result = TryGetBlock(state, address); 364 const auto search_result = TryGetBlock(state, address);
367 BlockInfo* block_info;
368 switch (search_result.first) { 365 switch (search_result.first) {
369 case BlockCollision::Found: { 366 case BlockCollision::Found: {
370 return true; 367 return true;
371 break;
372 } 368 }
373 case BlockCollision::Inside: { 369 case BlockCollision::Inside: {
374 // This case is the tricky one: 370 // This case is the tricky one:
375 // We need to Split the block in 2 sepparate blocks 371 // We need to Split the block in 2 sepparate blocks
376 auto it = search_result.second; 372 auto it = search_result.second;
377 block_info = CreateBlockInfo(state, address, it->end); 373 BlockInfo* block_info = CreateBlockInfo(state, address, it->end);
378 it->end = address - 1; 374 it->end = address - 1;
379 block_info->branch = it->branch; 375 block_info->branch = it->branch;
380 BlockBranchInfo forward_branch{}; 376 BlockBranchInfo forward_branch{};
@@ -382,34 +378,32 @@ bool TryInspectAddress(CFGRebuildState& state) {
382 forward_branch.ignore = true; 378 forward_branch.ignore = true;
383 it->branch = forward_branch; 379 it->branch = forward_branch;
384 return true; 380 return true;
385 break;
386 } 381 }
387 default: 382 default:
388 break; 383 break;
389 } 384 }
390 ParseInfo parse_info; 385 ParseInfo parse_info;
391 ParseResult parse_result = ParseCode(state, address, parse_info); 386 const ParseResult parse_result = ParseCode(state, address, parse_info);
392 if (parse_result == ParseResult::AbnormalFlow) { 387 if (parse_result == ParseResult::AbnormalFlow) {
393 // if it's the end of the program, end it safely
394 // if it's AbnormalFlow, we end it as false, ending the CFG reconstruction 388 // if it's AbnormalFlow, we end it as false, ending the CFG reconstruction
395 return false; 389 return false;
396 } 390 }
397 391
398 block_info = CreateBlockInfo(state, address, parse_info.end_address); 392 BlockInfo* block_info = CreateBlockInfo(state, address, parse_info.end_address);
399 block_info->branch = parse_info.branch_info; 393 block_info->branch = parse_info.branch_info;
400 if (parse_info.branch_info.condition.IsUnconditional()) { 394 if (parse_info.branch_info.condition.IsUnconditional()) {
401 return true; 395 return true;
402 } 396 }
403 397
404 u32 fallthrough_address = parse_info.end_address + 1; 398 const u32 fallthrough_address = parse_info.end_address + 1;
405 state.inspect_queries.push_front(fallthrough_address); 399 state.inspect_queries.push_front(fallthrough_address);
406 return true; 400 return true;
407} 401}
408 402
409bool TryQuery(CFGRebuildState& state) { 403bool TryQuery(CFGRebuildState& state) {
410 auto gather_labels = ([](ControlStack& cc, std::map<u32, u32>& labels, BlockInfo& block) { 404 const auto gather_labels = ([](ControlStack& cc, std::map<u32, u32>& labels, BlockInfo& block) {
411 auto gather_start = labels.lower_bound(block.start); 405 auto gather_start = labels.lower_bound(block.start);
412 auto gather_end = labels.upper_bound(block.end); 406 const auto gather_end = labels.upper_bound(block.end);
413 while (gather_start != gather_end) { 407 while (gather_start != gather_end) {
414 cc.Push(gather_start->second); 408 cc.Push(gather_start->second);
415 gather_start++; 409 gather_start++;
@@ -419,21 +413,21 @@ bool TryQuery(CFGRebuildState& state) {
419 return false; 413 return false;
420 } 414 }
421 Query& q = state.queries.front(); 415 Query& q = state.queries.front();
422 u32 block_index = state.registered[q.address]; 416 const u32 block_index = state.registered[q.address];
423 BlockInfo& block = state.block_info[block_index]; 417 BlockInfo& block = state.block_info[block_index];
424 // If the block is visted, check if the stacks match, else gather the ssy/pbk 418 // If the block is visted, check if the stacks match, else gather the ssy/pbk
425 // labels into the current stack and look if the branch at the end of the block 419 // labels into the current stack and look if the branch at the end of the block
426 // consumes a label. Schedule new queries accordingly 420 // consumes a label. Schedule new queries accordingly
427 if (block.visited) { 421 if (block.visited) {
428 BlockStack& stack = state.stacks[q.address]; 422 BlockStack& stack = state.stacks[q.address];
429 bool all_okay = (stack.ssy_stack.Size() == 0 || q.ssy_stack.Compare(stack.ssy_stack)) && 423 const bool all_okay =
430 (stack.pbk_stack.Size() == 0 || q.pbk_stack.Compare(stack.pbk_stack)); 424 (stack.ssy_stack.Size() == 0 || q.ssy_stack.Compare(stack.ssy_stack)) &&
425 (stack.pbk_stack.Size() == 0 || q.pbk_stack.Compare(stack.pbk_stack));
431 state.queries.pop_front(); 426 state.queries.pop_front();
432 return all_okay; 427 return all_okay;
433 } 428 }
434 block.visited = true; 429 block.visited = true;
435 BlockStack bs{q}; 430 state.stacks[q.address] = BlockStack{q};
436 state.stacks[q.address] = bs;
437 Query q2(q); 431 Query q2(q);
438 state.queries.pop_front(); 432 state.queries.pop_front();
439 gather_labels(q2.ssy_stack, state.ssy_labels, block); 433 gather_labels(q2.ssy_stack, state.ssy_labels, block);
diff --git a/src/video_core/shader/control_flow.h b/src/video_core/shader/control_flow.h
index 4a2cd622c..4689b0c10 100644
--- a/src/video_core/shader/control_flow.h
+++ b/src/video_core/shader/control_flow.h
@@ -1,3 +1,7 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
1#pragma once 5#pragma once
2 6
3#include <cstring> 7#include <cstring>
@@ -20,12 +24,15 @@ struct Condition {
20 ConditionCode cc{ConditionCode::T}; 24 ConditionCode cc{ConditionCode::T};
21 25
22 bool IsUnconditional() const { 26 bool IsUnconditional() const {
23 return (predicate == Pred::UnusedIndex) && (cc == ConditionCode::T); 27 return predicate == Pred::UnusedIndex && cc == ConditionCode::T;
28 }
29 bool operator==(const Condition& other) const {
30 return std::tie(predicate, cc) == std::tie(other.predicate, other.cc);
24 } 31 }
25}; 32};
26 33
27struct ShaderBlock { 34struct ShaderBlock {
28 ShaderBlock() {} 35 ShaderBlock() = default;
29 ShaderBlock(const ShaderBlock& sb) = default; 36 ShaderBlock(const ShaderBlock& sb) = default;
30 u32 start{}; 37 u32 start{};
31 u32 end{}; 38 u32 end{};
@@ -35,11 +42,12 @@ struct ShaderBlock {
35 bool kills{}; 42 bool kills{};
36 s32 address{}; 43 s32 address{};
37 bool operator==(const Branch& b) const { 44 bool operator==(const Branch& b) const {
38 return std::memcmp(this, &b, sizeof(Branch)) == 0; 45 return std::tie(cond, kills, address) == std::tie(b.cond, b.kills, b.address);
39 } 46 }
40 } branch; 47 } branch;
41 bool operator==(const ShaderBlock& sb) const { 48 bool operator==(const ShaderBlock& sb) const {
42 return std::memcmp(this, &sb, sizeof(ShaderBlock)) == 0; 49 return std::tie(start, end, ignore_branch, branch) ==
50 std::tie(sb.start, sb.end, sb.ignore_branch, sb.branch);
43 } 51 }
44}; 52};
45 53
diff --git a/src/video_core/shader/decode.cpp b/src/video_core/shader/decode.cpp
index f9b1960da..b4a282cbd 100644
--- a/src/video_core/shader/decode.cpp
+++ b/src/video_core/shader/decode.cpp
@@ -46,7 +46,7 @@ void ShaderIR::Decode() {
46 coverage_end = shader_info.end; 46 coverage_end = shader_info.end;
47 if (shader_info.decompilable) { 47 if (shader_info.decompilable) {
48 disable_flow_stack = true; 48 disable_flow_stack = true;
49 auto insert_block = ([this](NodeBlock& nodes, u32 label) { 49 const auto insert_block = ([this](NodeBlock& nodes, u32 label) {
50 if (label == exit_branch) { 50 if (label == exit_branch) {
51 return; 51 return;
52 } 52 }
@@ -88,7 +88,6 @@ void ShaderIR::Decode() {
88 for (u32 label = main_offset; label < shader_end; label++) { 88 for (u32 label = main_offset; label < shader_end; label++) {
89 basic_blocks.insert({label, DecodeRange(label, label + 1)}); 89 basic_blocks.insert({label, DecodeRange(label, label + 1)});
90 } 90 }
91 return;
92} 91}
93 92
94NodeBlock ShaderIR::DecodeRange(u32 begin, u32 end) { 93NodeBlock ShaderIR::DecodeRange(u32 begin, u32 end) {
@@ -104,16 +103,17 @@ void ShaderIR::DecodeRangeInner(NodeBlock& bb, u32 begin, u32 end) {
104} 103}
105 104
106void ShaderIR::InsertControlFlow(NodeBlock& bb, const ShaderBlock& block) { 105void ShaderIR::InsertControlFlow(NodeBlock& bb, const ShaderBlock& block) {
107 auto apply_conditions = ([&](const Condition& cond, Node n) -> Node { 106 const auto apply_conditions = ([&](const Condition& cond, Node n) -> Node {
108 Node result = n; 107 Node result = n;
109 if (cond.cc != ConditionCode::T) { 108 if (cond.cc != ConditionCode::T) {
110 result = Conditional(GetConditionCode(cond.cc), {result}); 109 result = Conditional(GetConditionCode(cond.cc), {result});
111 } 110 }
112 if (cond.predicate != Pred::UnusedIndex) { 111 if (cond.predicate != Pred::UnusedIndex) {
113 u32 pred = static_cast<u32>(cond.predicate); 112 u32 pred = static_cast<u32>(cond.predicate);
114 bool is_neg = pred > 7; 113 const bool is_neg = pred > 7;
115 if (is_neg) 114 if (is_neg) {
116 pred -= 8; 115 pred -= 8;
116 }
117 result = Conditional(GetPredicate(pred, is_neg), {result}); 117 result = Conditional(GetPredicate(pred, is_neg), {result});
118 } 118 }
119 return result; 119 return result;