diff options
| author | 2019-06-25 20:15:40 -0400 | |
|---|---|---|
| committer | 2019-07-09 08:14:39 -0400 | |
| commit | 01b21ee1e8e7455dd84ee7f22d33426caaaafdb3 (patch) | |
| tree | ed443fde0bf5e5bd140d0f7787c44e87ad5f5505 /src | |
| parent | shader_ir: Unify blocks in decompiled shaders. (diff) | |
| download | yuzu-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.cpp | 80 | ||||
| -rw-r--r-- | src/video_core/shader/control_flow.h | 16 | ||||
| -rw-r--r-- | src/video_core/shader/decode.cpp | 10 |
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 | ||
| 18 | constexpr s32 unassigned_branch = -2; | 21 | constexpr 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 | ***/ | ||
| 20 | struct ControlStack { | 27 | struct 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 | ||
| 71 | struct Query { | 77 | struct 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 | ||
| 79 | struct BlockStack { | 85 | struct 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 | ||
| 87 | struct BlockBranchInfo { | 93 | struct 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 | ||
| 96 | struct BlockInfo { | 104 | struct 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 | ||
| 108 | struct CFGRebuildState { | 116 | struct 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 ⁢ | 160 | return ⁢ |
| 162 | } | 161 | } |
| @@ -172,11 +171,10 @@ enum class ParseResult : u32 { | |||
| 172 | }; | 171 | }; |
| 173 | 172 | ||
| 174 | ParseResult ParseCode(CFGRebuildState& state, u32 address, ParseInfo& parse_info) { | 173 | ParseResult 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 | ||
| 409 | bool TryQuery(CFGRebuildState& state) { | 403 | bool 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 | ||
| 27 | struct ShaderBlock { | 34 | struct 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 | ||
| 94 | NodeBlock ShaderIR::DecodeRange(u32 begin, u32 end) { | 93 | NodeBlock ShaderIR::DecodeRange(u32 begin, u32 end) { |
| @@ -104,16 +103,17 @@ void ShaderIR::DecodeRangeInner(NodeBlock& bb, u32 begin, u32 end) { | |||
| 104 | } | 103 | } |
| 105 | 104 | ||
| 106 | void ShaderIR::InsertControlFlow(NodeBlock& bb, const ShaderBlock& block) { | 105 | void 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; |