diff options
| author | 2019-06-26 12:56:03 -0400 | |
|---|---|---|
| committer | 2019-07-09 08:14:42 -0400 | |
| commit | e7a88f0ab32625c1422583ce63d0f8f20086f7c3 (patch) | |
| tree | 39e652f3ec8a3c27e424fa2f46c6b8dd4c97e96e /src | |
| parent | shader_ir: Correct parsing of scheduling instructions and correct sizing (diff) | |
| download | yuzu-e7a88f0ab32625c1422583ce63d0f8f20086f7c3.tar.gz yuzu-e7a88f0ab32625c1422583ce63d0f8f20086f7c3.tar.xz yuzu-e7a88f0ab32625c1422583ce63d0f8f20086f7c3.zip | |
control_flow: Address feedback.
Diffstat (limited to 'src')
| -rw-r--r-- | src/video_core/shader/control_flow.cpp | 126 |
1 files changed, 37 insertions, 89 deletions
diff --git a/src/video_core/shader/control_flow.cpp b/src/video_core/shader/control_flow.cpp index 1775dfd81..7b424d65d 100644 --- a/src/video_core/shader/control_flow.cpp +++ b/src/video_core/shader/control_flow.cpp | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #include <list> | 5 | #include <list> |
| 6 | #include <map> | 6 | #include <map> |
| 7 | #include <stack> | ||
| 7 | #include <unordered_map> | 8 | #include <unordered_map> |
| 8 | #include <unordered_set> | 9 | #include <unordered_set> |
| 9 | #include <vector> | 10 | #include <vector> |
| @@ -20,68 +21,18 @@ using Tegra::Shader::OpCode; | |||
| 20 | 21 | ||
| 21 | constexpr s32 unassigned_branch = -2; | 22 | constexpr s32 unassigned_branch = -2; |
| 22 | 23 | ||
| 23 | /** | ||
| 24 | * 'ControlStack' represents a static stack of control jumps such as SSY and PBK | ||
| 25 | * stacks in Maxwell. | ||
| 26 | **/ | ||
| 27 | struct ControlStack { | ||
| 28 | static constexpr std::size_t stack_fixed_size = 20; | ||
| 29 | std::array<u32, stack_fixed_size> stack{}; | ||
| 30 | u32 index{}; | ||
| 31 | |||
| 32 | bool Compare(const ControlStack& cs) const { | ||
| 33 | if (index != cs.index) { | ||
| 34 | return false; | ||
| 35 | } | ||
| 36 | return std::memcmp(stack.data(), cs.stack.data(), index * sizeof(u32)) == 0; | ||
| 37 | } | ||
| 38 | |||
| 39 | /// This compare just compares the top of the stack against one another | ||
| 40 | bool SoftCompare(const ControlStack& cs) const { | ||
| 41 | if (index == 0 || cs.index == 0) { | ||
| 42 | return index == cs.index; | ||
| 43 | } | ||
| 44 | return Top() == cs.Top(); | ||
| 45 | } | ||
| 46 | |||
| 47 | u32 Size() const { | ||
| 48 | return index; | ||
| 49 | } | ||
| 50 | |||
| 51 | u32 Top() const { | ||
| 52 | return stack[index - 1]; | ||
| 53 | } | ||
| 54 | |||
| 55 | bool Push(u32 address) { | ||
| 56 | if (index >= stack.size()) { | ||
| 57 | return false; | ||
| 58 | } | ||
| 59 | stack[index] = address; | ||
| 60 | index++; | ||
| 61 | return true; | ||
| 62 | } | ||
| 63 | |||
| 64 | bool Pop() { | ||
| 65 | if (index == 0) { | ||
| 66 | return false; | ||
| 67 | } | ||
| 68 | index--; | ||
| 69 | return true; | ||
| 70 | } | ||
| 71 | }; | ||
| 72 | |||
| 73 | struct Query { | 24 | struct Query { |
| 74 | u32 address{}; | 25 | u32 address{}; |
| 75 | ControlStack ssy_stack{}; | 26 | std::stack<u32> ssy_stack{}; |
| 76 | ControlStack pbk_stack{}; | 27 | std::stack<u32> pbk_stack{}; |
| 77 | }; | 28 | }; |
| 78 | 29 | ||
| 79 | struct BlockStack { | 30 | struct BlockStack { |
| 80 | BlockStack() = default; | 31 | BlockStack() = default; |
| 81 | BlockStack(const BlockStack& b) = default; | 32 | BlockStack(const BlockStack& b) = default; |
| 82 | BlockStack(const Query& q) : ssy_stack{q.ssy_stack}, pbk_stack{q.pbk_stack} {} | 33 | BlockStack(const Query& q) : ssy_stack{q.ssy_stack}, pbk_stack{q.pbk_stack} {} |
| 83 | ControlStack ssy_stack{}; | 34 | std::stack<u32> ssy_stack{}; |
| 84 | ControlStack pbk_stack{}; | 35 | std::stack<u32> pbk_stack{}; |
| 85 | }; | 36 | }; |
| 86 | 37 | ||
| 87 | struct BlockBranchInfo { | 38 | struct BlockBranchInfo { |
| @@ -144,13 +95,13 @@ struct ParseInfo { | |||
| 144 | u32 end_address{}; | 95 | u32 end_address{}; |
| 145 | }; | 96 | }; |
| 146 | 97 | ||
| 147 | BlockInfo* CreateBlockInfo(CFGRebuildState& state, u32 start, u32 end) { | 98 | BlockInfo& CreateBlockInfo(CFGRebuildState& state, u32 start, u32 end) { |
| 148 | auto& it = state.block_info.emplace_back(); | 99 | auto& it = state.block_info.emplace_back(); |
| 149 | it.start = start; | 100 | it.start = start; |
| 150 | it.end = end; | 101 | it.end = end; |
| 151 | const u32 index = static_cast<u32>(state.block_info.size() - 1); | 102 | const u32 index = static_cast<u32>(state.block_info.size() - 1); |
| 152 | state.registered.insert({start, index}); | 103 | state.registered.insert({start, index}); |
| 153 | return ⁢ | 104 | return it; |
| 154 | } | 105 | } |
| 155 | 106 | ||
| 156 | Pred GetPredicate(u32 index, bool negated) { | 107 | Pred GetPredicate(u32 index, bool negated) { |
| @@ -174,16 +125,17 @@ enum class ParseResult : u32 { | |||
| 174 | AbnormalFlow, | 125 | AbnormalFlow, |
| 175 | }; | 126 | }; |
| 176 | 127 | ||
| 177 | ParseResult ParseCode(CFGRebuildState& state, u32 address, ParseInfo& parse_info) { | 128 | std::pair<ParseResult, ParseInfo> ParseCode(CFGRebuildState& state, u32 address) { |
| 178 | u32 offset = static_cast<u32>(address); | 129 | u32 offset = static_cast<u32>(address); |
| 179 | const u32 end_address = static_cast<u32>(state.program_size / sizeof(Instruction)); | 130 | const u32 end_address = static_cast<u32>(state.program_size / sizeof(Instruction)); |
| 131 | ParseInfo parse_info{}; | ||
| 180 | 132 | ||
| 181 | const auto insert_label = ([](CFGRebuildState& state, u32 address) { | 133 | const auto insert_label = [](CFGRebuildState& state, u32 address) { |
| 182 | auto pair = state.labels.emplace(address); | 134 | const auto pair = state.labels.emplace(address); |
| 183 | if (pair.second) { | 135 | if (pair.second) { |
| 184 | state.inspect_queries.push_back(address); | 136 | state.inspect_queries.push_back(address); |
| 185 | } | 137 | } |
| 186 | }); | 138 | }; |
| 187 | 139 | ||
| 188 | while (true) { | 140 | while (true) { |
| 189 | if (offset >= end_address) { | 141 | if (offset >= end_address) { |
| @@ -229,11 +181,11 @@ ParseResult ParseCode(CFGRebuildState& state, u32 address, ParseInfo& parse_info | |||
| 229 | parse_info.branch_info.ignore = false; | 181 | parse_info.branch_info.ignore = false; |
| 230 | parse_info.end_address = offset; | 182 | parse_info.end_address = offset; |
| 231 | 183 | ||
| 232 | return ParseResult::ControlCaught; | 184 | return {ParseResult::ControlCaught, parse_info}; |
| 233 | } | 185 | } |
| 234 | case OpCode::Id::BRA: { | 186 | case OpCode::Id::BRA: { |
| 235 | if (instr.bra.constant_buffer != 0) { | 187 | if (instr.bra.constant_buffer != 0) { |
| 236 | return ParseResult::AbnormalFlow; | 188 | return {ParseResult::AbnormalFlow, parse_info}; |
| 237 | } | 189 | } |
| 238 | const auto pred_index = static_cast<u32>(instr.pred.pred_index); | 190 | const auto pred_index = static_cast<u32>(instr.pred.pred_index); |
| 239 | parse_info.branch_info.condition.predicate = | 191 | parse_info.branch_info.condition.predicate = |
| @@ -248,7 +200,7 @@ ParseResult ParseCode(CFGRebuildState& state, u32 address, ParseInfo& parse_info | |||
| 248 | offset++; | 200 | offset++; |
| 249 | continue; | 201 | continue; |
| 250 | } | 202 | } |
| 251 | u32 branch_offset = offset + instr.bra.GetBranchTarget(); | 203 | const u32 branch_offset = offset + instr.bra.GetBranchTarget(); |
| 252 | if (branch_offset == 0) { | 204 | if (branch_offset == 0) { |
| 253 | parse_info.branch_info.address = exit_branch; | 205 | parse_info.branch_info.address = exit_branch; |
| 254 | } else { | 206 | } else { |
| @@ -261,10 +213,9 @@ ParseResult ParseCode(CFGRebuildState& state, u32 address, ParseInfo& parse_info | |||
| 261 | parse_info.branch_info.ignore = false; | 213 | parse_info.branch_info.ignore = false; |
| 262 | parse_info.end_address = offset; | 214 | parse_info.end_address = offset; |
| 263 | 215 | ||
| 264 | return ParseResult::ControlCaught; | 216 | return {ParseResult::ControlCaught, parse_info}; |
| 265 | } | 217 | } |
| 266 | case OpCode::Id::SYNC: { | 218 | case OpCode::Id::SYNC: { |
| 267 | parse_info.branch_info.condition; | ||
| 268 | const auto pred_index = static_cast<u32>(instr.pred.pred_index); | 219 | const auto pred_index = static_cast<u32>(instr.pred.pred_index); |
| 269 | parse_info.branch_info.condition.predicate = | 220 | parse_info.branch_info.condition.predicate = |
| 270 | GetPredicate(pred_index, instr.negate_pred != 0); | 221 | GetPredicate(pred_index, instr.negate_pred != 0); |
| @@ -285,10 +236,9 @@ ParseResult ParseCode(CFGRebuildState& state, u32 address, ParseInfo& parse_info | |||
| 285 | parse_info.branch_info.ignore = false; | 236 | parse_info.branch_info.ignore = false; |
| 286 | parse_info.end_address = offset; | 237 | parse_info.end_address = offset; |
| 287 | 238 | ||
| 288 | return ParseResult::ControlCaught; | 239 | return {ParseResult::ControlCaught, parse_info}; |
| 289 | } | 240 | } |
| 290 | case OpCode::Id::BRK: { | 241 | case OpCode::Id::BRK: { |
| 291 | parse_info.branch_info.condition; | ||
| 292 | const auto pred_index = static_cast<u32>(instr.pred.pred_index); | 242 | const auto pred_index = static_cast<u32>(instr.pred.pred_index); |
| 293 | parse_info.branch_info.condition.predicate = | 243 | parse_info.branch_info.condition.predicate = |
| 294 | GetPredicate(pred_index, instr.negate_pred != 0); | 244 | GetPredicate(pred_index, instr.negate_pred != 0); |
| @@ -309,10 +259,9 @@ ParseResult ParseCode(CFGRebuildState& state, u32 address, ParseInfo& parse_info | |||
| 309 | parse_info.branch_info.ignore = false; | 259 | parse_info.branch_info.ignore = false; |
| 310 | parse_info.end_address = offset; | 260 | parse_info.end_address = offset; |
| 311 | 261 | ||
| 312 | return ParseResult::ControlCaught; | 262 | return {ParseResult::ControlCaught, parse_info}; |
| 313 | } | 263 | } |
| 314 | case OpCode::Id::KIL: { | 264 | case OpCode::Id::KIL: { |
| 315 | parse_info.branch_info.condition; | ||
| 316 | const auto pred_index = static_cast<u32>(instr.pred.pred_index); | 265 | const auto pred_index = static_cast<u32>(instr.pred.pred_index); |
| 317 | parse_info.branch_info.condition.predicate = | 266 | parse_info.branch_info.condition.predicate = |
| 318 | GetPredicate(pred_index, instr.negate_pred != 0); | 267 | GetPredicate(pred_index, instr.negate_pred != 0); |
| @@ -333,7 +282,7 @@ ParseResult ParseCode(CFGRebuildState& state, u32 address, ParseInfo& parse_info | |||
| 333 | parse_info.branch_info.ignore = false; | 282 | parse_info.branch_info.ignore = false; |
| 334 | parse_info.end_address = offset; | 283 | parse_info.end_address = offset; |
| 335 | 284 | ||
| 336 | return ParseResult::ControlCaught; | 285 | return {ParseResult::ControlCaught, parse_info}; |
| 337 | } | 286 | } |
| 338 | case OpCode::Id::SSY: { | 287 | case OpCode::Id::SSY: { |
| 339 | const u32 target = offset + instr.bra.GetBranchTarget(); | 288 | const u32 target = offset + instr.bra.GetBranchTarget(); |
| @@ -348,7 +297,7 @@ ParseResult ParseCode(CFGRebuildState& state, u32 address, ParseInfo& parse_info | |||
| 348 | break; | 297 | break; |
| 349 | } | 298 | } |
| 350 | case OpCode::Id::BRX: { | 299 | case OpCode::Id::BRX: { |
| 351 | return ParseResult::AbnormalFlow; | 300 | return {ParseResult::AbnormalFlow, parse_info}; |
| 352 | } | 301 | } |
| 353 | default: | 302 | default: |
| 354 | break; | 303 | break; |
| @@ -360,7 +309,7 @@ ParseResult ParseCode(CFGRebuildState& state, u32 address, ParseInfo& parse_info | |||
| 360 | parse_info.branch_info.is_sync = false; | 309 | parse_info.branch_info.is_sync = false; |
| 361 | parse_info.branch_info.is_brk = false; | 310 | parse_info.branch_info.is_brk = false; |
| 362 | parse_info.end_address = offset - 1; | 311 | parse_info.end_address = offset - 1; |
| 363 | return ParseResult::BlockEnd; | 312 | return {ParseResult::BlockEnd, parse_info}; |
| 364 | } | 313 | } |
| 365 | 314 | ||
| 366 | bool TryInspectAddress(CFGRebuildState& state) { | 315 | bool TryInspectAddress(CFGRebuildState& state) { |
| @@ -377,10 +326,10 @@ bool TryInspectAddress(CFGRebuildState& state) { | |||
| 377 | case BlockCollision::Inside: { | 326 | case BlockCollision::Inside: { |
| 378 | // This case is the tricky one: | 327 | // This case is the tricky one: |
| 379 | // We need to Split the block in 2 sepparate blocks | 328 | // We need to Split the block in 2 sepparate blocks |
| 380 | auto it = search_result.second; | 329 | const auto it = search_result.second; |
| 381 | BlockInfo* block_info = CreateBlockInfo(state, address, it->end); | 330 | BlockInfo& block_info = CreateBlockInfo(state, address, it->end); |
| 382 | it->end = address - 1; | 331 | it->end = address - 1; |
| 383 | block_info->branch = it->branch; | 332 | block_info.branch = it->branch; |
| 384 | BlockBranchInfo forward_branch{}; | 333 | BlockBranchInfo forward_branch{}; |
| 385 | forward_branch.address = address; | 334 | forward_branch.address = address; |
| 386 | forward_branch.ignore = true; | 335 | forward_branch.ignore = true; |
| @@ -390,15 +339,14 @@ bool TryInspectAddress(CFGRebuildState& state) { | |||
| 390 | default: | 339 | default: |
| 391 | break; | 340 | break; |
| 392 | } | 341 | } |
| 393 | ParseInfo parse_info; | 342 | const auto [parse_result, parse_info] = ParseCode(state, address); |
| 394 | const ParseResult parse_result = ParseCode(state, address, parse_info); | ||
| 395 | if (parse_result == ParseResult::AbnormalFlow) { | 343 | if (parse_result == ParseResult::AbnormalFlow) { |
| 396 | // if it's AbnormalFlow, we end it as false, ending the CFG reconstruction | 344 | // if it's AbnormalFlow, we end it as false, ending the CFG reconstruction |
| 397 | return false; | 345 | return false; |
| 398 | } | 346 | } |
| 399 | 347 | ||
| 400 | BlockInfo* block_info = CreateBlockInfo(state, address, parse_info.end_address); | 348 | BlockInfo& block_info = CreateBlockInfo(state, address, parse_info.end_address); |
| 401 | block_info->branch = parse_info.branch_info; | 349 | block_info.branch = parse_info.branch_info; |
| 402 | if (parse_info.branch_info.condition.IsUnconditional()) { | 350 | if (parse_info.branch_info.condition.IsUnconditional()) { |
| 403 | return true; | 351 | return true; |
| 404 | } | 352 | } |
| @@ -409,14 +357,15 @@ bool TryInspectAddress(CFGRebuildState& state) { | |||
| 409 | } | 357 | } |
| 410 | 358 | ||
| 411 | bool TryQuery(CFGRebuildState& state) { | 359 | bool TryQuery(CFGRebuildState& state) { |
| 412 | const auto gather_labels = ([](ControlStack& cc, std::map<u32, u32>& labels, BlockInfo& block) { | 360 | const auto gather_labels = [](std::stack<u32>& cc, std::map<u32, u32>& labels, |
| 361 | BlockInfo& block) { | ||
| 413 | auto gather_start = labels.lower_bound(block.start); | 362 | auto gather_start = labels.lower_bound(block.start); |
| 414 | const auto gather_end = labels.upper_bound(block.end); | 363 | const auto gather_end = labels.upper_bound(block.end); |
| 415 | while (gather_start != gather_end) { | 364 | while (gather_start != gather_end) { |
| 416 | cc.Push(gather_start->second); | 365 | cc.push(gather_start->second); |
| 417 | gather_start++; | 366 | gather_start++; |
| 418 | } | 367 | } |
| 419 | }); | 368 | }; |
| 420 | if (state.queries.empty()) { | 369 | if (state.queries.empty()) { |
| 421 | return false; | 370 | return false; |
| 422 | } | 371 | } |
| @@ -428,9 +377,8 @@ bool TryQuery(CFGRebuildState& state) { | |||
| 428 | // consumes a label. Schedule new queries accordingly | 377 | // consumes a label. Schedule new queries accordingly |
| 429 | if (block.visited) { | 378 | if (block.visited) { |
| 430 | BlockStack& stack = state.stacks[q.address]; | 379 | BlockStack& stack = state.stacks[q.address]; |
| 431 | const bool all_okay = | 380 | const bool all_okay = (stack.ssy_stack.size() == 0 || q.ssy_stack == stack.ssy_stack) && |
| 432 | (stack.ssy_stack.Size() == 0 || q.ssy_stack.Compare(stack.ssy_stack)) && | 381 | (stack.pbk_stack.size() == 0 || q.pbk_stack == stack.pbk_stack); |
| 433 | (stack.pbk_stack.Size() == 0 || q.pbk_stack.Compare(stack.pbk_stack)); | ||
| 434 | state.queries.pop_front(); | 382 | state.queries.pop_front(); |
| 435 | return all_okay; | 383 | return all_okay; |
| 436 | } | 384 | } |
| @@ -447,15 +395,15 @@ bool TryQuery(CFGRebuildState& state) { | |||
| 447 | Query conditional_query{q2}; | 395 | Query conditional_query{q2}; |
| 448 | if (block.branch.is_sync) { | 396 | if (block.branch.is_sync) { |
| 449 | if (block.branch.address == unassigned_branch) { | 397 | if (block.branch.address == unassigned_branch) { |
| 450 | block.branch.address = conditional_query.ssy_stack.Top(); | 398 | block.branch.address = conditional_query.ssy_stack.top(); |
| 451 | } | 399 | } |
| 452 | conditional_query.ssy_stack.Pop(); | 400 | conditional_query.ssy_stack.pop(); |
| 453 | } | 401 | } |
| 454 | if (block.branch.is_brk) { | 402 | if (block.branch.is_brk) { |
| 455 | if (block.branch.address == unassigned_branch) { | 403 | if (block.branch.address == unassigned_branch) { |
| 456 | block.branch.address = conditional_query.pbk_stack.Top(); | 404 | block.branch.address = conditional_query.pbk_stack.top(); |
| 457 | } | 405 | } |
| 458 | conditional_query.pbk_stack.Pop(); | 406 | conditional_query.pbk_stack.pop(); |
| 459 | } | 407 | } |
| 460 | conditional_query.address = block.branch.address; | 408 | conditional_query.address = block.branch.address; |
| 461 | state.queries.push_back(conditional_query); | 409 | state.queries.push_back(conditional_query); |