diff options
| author | 2019-10-27 02:24:48 -0300 | |
|---|---|---|
| committer | 2019-11-07 01:44:08 -0300 | |
| commit | 46c30472830bd7965027a7c1464d6e0d6ba40796 (patch) | |
| tree | 0d949d5bde0271cbc81781ab1a21f919584899d6 /src | |
| parent | shader/control_flow: Silence Intellisense cast warnings (diff) | |
| download | yuzu-46c30472830bd7965027a7c1464d6e0d6ba40796.tar.gz yuzu-46c30472830bd7965027a7c1464d6e0d6ba40796.tar.xz yuzu-46c30472830bd7965027a7c1464d6e0d6ba40796.zip | |
shader/control_flow: Abstract repeated code chunks in BRX tracking
Remove copied and pasted for cycles into a common templated function.
Diffstat (limited to 'src')
| -rw-r--r-- | src/video_core/shader/control_flow.cpp | 194 |
1 files changed, 101 insertions, 93 deletions
diff --git a/src/video_core/shader/control_flow.cpp b/src/video_core/shader/control_flow.cpp index 1896abc22..278de4084 100644 --- a/src/video_core/shader/control_flow.cpp +++ b/src/video_core/shader/control_flow.cpp | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <functional> | ||
| 5 | #include <list> | 6 | #include <list> |
| 6 | #include <map> | 7 | #include <map> |
| 7 | #include <set> | 8 | #include <set> |
| @@ -16,7 +17,9 @@ | |||
| 16 | #include "video_core/shader/shader_ir.h" | 17 | #include "video_core/shader/shader_ir.h" |
| 17 | 18 | ||
| 18 | namespace VideoCommon::Shader { | 19 | namespace VideoCommon::Shader { |
| 20 | |||
| 19 | namespace { | 21 | namespace { |
| 22 | |||
| 20 | using Tegra::Shader::Instruction; | 23 | using Tegra::Shader::Instruction; |
| 21 | using Tegra::Shader::OpCode; | 24 | using Tegra::Shader::OpCode; |
| 22 | 25 | ||
| @@ -136,15 +139,13 @@ struct BranchIndirectInfo { | |||
| 136 | s32 relative_position{}; | 139 | s32 relative_position{}; |
| 137 | }; | 140 | }; |
| 138 | 141 | ||
| 139 | std::optional<BranchIndirectInfo> TrackBranchIndirectInfo(const CFGRebuildState& state, | 142 | struct BufferInfo { |
| 140 | u32 start_address, u32 current_position) { | 143 | u32 index; |
| 141 | const u32 shader_start = state.start; | 144 | u32 offset; |
| 142 | u32 pos = current_position; | 145 | }; |
| 143 | BranchIndirectInfo result{}; | ||
| 144 | u64 track_register = 0; | ||
| 145 | 146 | ||
| 146 | // Step 0 Get BRX Info | 147 | std::optional<std::pair<s32, u64>> GetBRXInfo(const CFGRebuildState& state, u32& pos) { |
| 147 | const Instruction instr = {state.program_code[pos]}; | 148 | const Instruction instr = state.program_code[pos]; |
| 148 | const auto opcode = OpCode::Decode(instr); | 149 | const auto opcode = OpCode::Decode(instr); |
| 149 | if (opcode->get().GetId() != OpCode::Id::BRX) { | 150 | if (opcode->get().GetId() != OpCode::Id::BRX) { |
| 150 | return std::nullopt; | 151 | return std::nullopt; |
| @@ -152,86 +153,93 @@ std::optional<BranchIndirectInfo> TrackBranchIndirectInfo(const CFGRebuildState& | |||
| 152 | if (instr.brx.constant_buffer != 0) { | 153 | if (instr.brx.constant_buffer != 0) { |
| 153 | return std::nullopt; | 154 | return std::nullopt; |
| 154 | } | 155 | } |
| 155 | track_register = instr.gpr8.Value(); | 156 | --pos; |
| 156 | result.relative_position = instr.brx.GetBranchExtend(); | 157 | return std::make_pair(instr.brx.GetBranchExtend(), instr.gpr8.Value()); |
| 157 | pos--; | 158 | } |
| 158 | bool found_track = false; | ||
| 159 | 159 | ||
| 160 | // Step 1 Track LDC | 160 | template <typename Result> |
| 161 | while (pos >= shader_start) { | 161 | std::optional<Result> TrackInstruction( |
| 162 | if (IsSchedInstruction(pos, shader_start)) { | 162 | const CFGRebuildState& state, u32& pos, |
| 163 | pos--; | 163 | std::function<bool(Instruction, const OpCode::Matcher&)>&& test, |
| 164 | std::function<Result(Instruction, const OpCode::Matcher&)>&& pack) { | ||
| 165 | for (; pos >= state.start; --pos) { | ||
| 166 | if (IsSchedInstruction(pos, state.start)) { | ||
| 164 | continue; | 167 | continue; |
| 165 | } | 168 | } |
| 166 | const Instruction instr = {state.program_code[pos]}; | 169 | const Instruction instr = state.program_code[pos]; |
| 167 | const auto opcode = OpCode::Decode(instr); | 170 | const auto opcode = OpCode::Decode(instr); |
| 168 | if (opcode->get().GetId() == OpCode::Id::LD_C) { | 171 | if (!opcode) { |
| 169 | if (instr.gpr0.Value() == track_register && | 172 | continue; |
| 170 | instr.ld_c.type.Value() == Tegra::Shader::UniformType::Single) { | 173 | } |
| 171 | result.buffer = instr.cbuf36.index.Value(); | 174 | if (test(instr, opcode->get())) { |
| 172 | result.offset = static_cast<u32>(instr.cbuf36.GetOffset()); | 175 | --pos; |
| 173 | track_register = instr.gpr8.Value(); | 176 | return std::make_optional(pack(instr, opcode->get())); |
| 174 | pos--; | ||
| 175 | found_track = true; | ||
| 176 | break; | ||
| 177 | } | ||
| 178 | } | 177 | } |
| 179 | pos--; | ||
| 180 | } | 178 | } |
| 179 | return std::nullopt; | ||
| 180 | } | ||
| 181 | 181 | ||
| 182 | if (!found_track) { | 182 | std::optional<std::pair<BufferInfo, u64>> TrackLDC(const CFGRebuildState& state, u32& pos, |
| 183 | return std::nullopt; | 183 | u64 brx_tracked_register) { |
| 184 | } | 184 | return TrackInstruction<std::pair<BufferInfo, u64>>( |
| 185 | found_track = false; | 185 | state, pos, |
| 186 | [brx_tracked_register](auto instr, auto& opcode) { | ||
| 187 | return opcode.GetId() == OpCode::Id::LD_C && | ||
| 188 | instr.gpr0.Value() == brx_tracked_register && | ||
| 189 | instr.ld_c.type.Value() == Tegra::Shader::UniformType::Single; | ||
| 190 | }, | ||
| 191 | [](auto instr, auto& opcode) { | ||
| 192 | const BufferInfo info = {static_cast<u32>(instr.cbuf36.index.Value()), | ||
| 193 | static_cast<u32>(instr.cbuf36.GetOffset())}; | ||
| 194 | return std::make_pair(info, instr.gpr8.Value()); | ||
| 195 | }); | ||
| 196 | } | ||
| 186 | 197 | ||
| 187 | // Step 2 Track SHL | 198 | std::optional<u64> TrackSHLRegister(const CFGRebuildState& state, u32& pos, |
| 188 | while (pos >= shader_start) { | 199 | u64 ldc_tracked_register) { |
| 189 | if (IsSchedInstruction(pos, shader_start)) { | 200 | return TrackInstruction<u64>(state, pos, |
| 190 | pos--; | 201 | [ldc_tracked_register](auto instr, auto& opcode) { |
| 191 | continue; | 202 | return opcode.GetId() == OpCode::Id::SHL_IMM && |
| 192 | } | 203 | instr.gpr0.Value() == ldc_tracked_register; |
| 193 | const Instruction instr = state.program_code[pos]; | 204 | }, |
| 194 | const auto opcode = OpCode::Decode(instr); | 205 | [](auto instr, auto&) { return instr.gpr8.Value(); }); |
| 195 | if (opcode->get().GetId() == OpCode::Id::SHL_IMM) { | 206 | } |
| 196 | if (instr.gpr0.Value() == track_register) { | 207 | |
| 197 | track_register = instr.gpr8.Value(); | 208 | std::optional<u32> TrackIMNMXValue(const CFGRebuildState& state, u32& pos, |
| 198 | pos--; | 209 | u64 shl_tracked_register) { |
| 199 | found_track = true; | 210 | return TrackInstruction<u32>( |
| 200 | break; | 211 | state, pos, |
| 201 | } | 212 | [shl_tracked_register](auto instr, auto& opcode) { |
| 202 | } | 213 | return opcode.GetId() == OpCode::Id::IMNMX_IMM && |
| 203 | pos--; | 214 | instr.gpr0.Value() == shl_tracked_register; |
| 215 | }, | ||
| 216 | [](auto instr, auto&) { return static_cast<u32>(instr.alu.GetSignedImm20_20() + 1); }); | ||
| 217 | } | ||
| 218 | |||
| 219 | std::optional<BranchIndirectInfo> TrackBranchIndirectInfo(const CFGRebuildState& state, u32 pos) { | ||
| 220 | const auto brx_info = GetBRXInfo(state, pos); | ||
| 221 | if (!brx_info) { | ||
| 222 | return std::nullopt; | ||
| 204 | } | 223 | } |
| 224 | const auto [relative_position, brx_tracked_register] = *brx_info; | ||
| 205 | 225 | ||
| 206 | if (!found_track) { | 226 | const auto ldc_info = TrackLDC(state, pos, brx_tracked_register); |
| 227 | if (!ldc_info) { | ||
| 207 | return std::nullopt; | 228 | return std::nullopt; |
| 208 | } | 229 | } |
| 209 | found_track = false; | 230 | const auto [buffer_info, ldc_tracked_register] = *ldc_info; |
| 210 | 231 | ||
| 211 | // Step 3 Track IMNMX | 232 | const auto shl_tracked_register = TrackSHLRegister(state, pos, ldc_tracked_register); |
| 212 | while (pos >= shader_start) { | 233 | if (!shl_tracked_register) { |
| 213 | if (IsSchedInstruction(pos, shader_start)) { | 234 | return std::nullopt; |
| 214 | pos--; | ||
| 215 | continue; | ||
| 216 | } | ||
| 217 | const Instruction instr = state.program_code[pos]; | ||
| 218 | const auto opcode = OpCode::Decode(instr); | ||
| 219 | if (opcode->get().GetId() == OpCode::Id::IMNMX_IMM) { | ||
| 220 | if (instr.gpr0.Value() == track_register) { | ||
| 221 | track_register = instr.gpr8.Value(); | ||
| 222 | result.entries = instr.alu.GetSignedImm20_20() + 1; | ||
| 223 | pos--; | ||
| 224 | found_track = true; | ||
| 225 | break; | ||
| 226 | } | ||
| 227 | } | ||
| 228 | pos--; | ||
| 229 | } | 235 | } |
| 230 | 236 | ||
| 231 | if (!found_track) { | 237 | const auto entries = TrackIMNMXValue(state, pos, *shl_tracked_register); |
| 238 | if (!entries) { | ||
| 232 | return std::nullopt; | 239 | return std::nullopt; |
| 233 | } | 240 | } |
| 234 | return result; | 241 | |
| 242 | return BranchIndirectInfo{buffer_info.index, buffer_info.offset, *entries, relative_position}; | ||
| 235 | } | 243 | } |
| 236 | 244 | ||
| 237 | std::pair<ParseResult, ParseInfo> ParseCode(CFGRebuildState& state, u32 address) { | 245 | std::pair<ParseResult, ParseInfo> ParseCode(CFGRebuildState& state, u32 address) { |
| @@ -420,30 +428,30 @@ std::pair<ParseResult, ParseInfo> ParseCode(CFGRebuildState& state, u32 address) | |||
| 420 | break; | 428 | break; |
| 421 | } | 429 | } |
| 422 | case OpCode::Id::BRX: { | 430 | case OpCode::Id::BRX: { |
| 423 | auto tmp = TrackBranchIndirectInfo(state, address, offset); | 431 | const auto tmp = TrackBranchIndirectInfo(state, offset); |
| 424 | if (tmp) { | 432 | if (!tmp) { |
| 425 | auto result = *tmp; | ||
| 426 | std::vector<CaseBranch> branches{}; | ||
| 427 | s32 pc_target = offset + result.relative_position; | ||
| 428 | for (u32 i = 0; i < result.entries; i++) { | ||
| 429 | auto k = state.locker.ObtainKey(result.buffer, result.offset + i * 4); | ||
| 430 | if (!k) { | ||
| 431 | return {ParseResult::AbnormalFlow, parse_info}; | ||
| 432 | } | ||
| 433 | u32 value = *k; | ||
| 434 | u32 target = static_cast<u32>((value >> 3) + pc_target); | ||
| 435 | insert_label(state, target); | ||
| 436 | branches.emplace_back(value, target); | ||
| 437 | } | ||
| 438 | parse_info.end_address = offset; | ||
| 439 | parse_info.branch_info = MakeBranchInfo<MultiBranch>( | ||
| 440 | static_cast<u32>(instr.gpr8.Value()), std::move(branches)); | ||
| 441 | |||
| 442 | return {ParseResult::ControlCaught, parse_info}; | ||
| 443 | } else { | ||
| 444 | LOG_WARNING(HW_GPU, "BRX Track Unsuccesful"); | 433 | LOG_WARNING(HW_GPU, "BRX Track Unsuccesful"); |
| 434 | return {ParseResult::AbnormalFlow, parse_info}; | ||
| 445 | } | 435 | } |
| 446 | return {ParseResult::AbnormalFlow, parse_info}; | 436 | |
| 437 | const auto result = *tmp; | ||
| 438 | const s32 pc_target = offset + result.relative_position; | ||
| 439 | std::vector<CaseBranch> branches; | ||
| 440 | for (u32 i = 0; i < result.entries; i++) { | ||
| 441 | auto key = state.locker.ObtainKey(result.buffer, result.offset + i * 4); | ||
| 442 | if (!key) { | ||
| 443 | return {ParseResult::AbnormalFlow, parse_info}; | ||
| 444 | } | ||
| 445 | u32 value = *key; | ||
| 446 | u32 target = static_cast<u32>((value >> 3) + pc_target); | ||
| 447 | insert_label(state, target); | ||
| 448 | branches.emplace_back(value, target); | ||
| 449 | } | ||
| 450 | parse_info.end_address = offset; | ||
| 451 | parse_info.branch_info = MakeBranchInfo<MultiBranch>( | ||
| 452 | static_cast<u32>(instr.gpr8.Value()), std::move(branches)); | ||
| 453 | |||
| 454 | return {ParseResult::ControlCaught, parse_info}; | ||
| 447 | } | 455 | } |
| 448 | default: | 456 | default: |
| 449 | break; | 457 | break; |