diff options
| author | 2019-11-07 01:30:01 -0500 | |
|---|---|---|
| committer | 2019-11-07 01:30:01 -0500 | |
| commit | b6ae48966d2e914d6c51f62e9eb818fb7aec7c1d (patch) | |
| tree | 3798491ae7e3fa7d3d88f7bc97ffe4d90397515e | |
| parent | buffer_cache: Add missing includes (#3079) (diff) | |
| parent | shader/control_flow: Specify constness on caller lambdas (diff) | |
| download | yuzu-b6ae48966d2e914d6c51f62e9eb818fb7aec7c1d.tar.gz yuzu-b6ae48966d2e914d6c51f62e9eb818fb7aec7c1d.tar.xz yuzu-b6ae48966d2e914d6c51f62e9eb818fb7aec7c1d.zip | |
Merge pull request #3032 from ReinUsesLisp/simplify-control-flow-brx
shader/control_flow: Abstract repeated code chunks in BRX tracking
Diffstat (limited to '')
| -rw-r--r-- | src/video_core/shader/control_flow.cpp | 214 |
1 files changed, 111 insertions, 103 deletions
diff --git a/src/video_core/shader/control_flow.cpp b/src/video_core/shader/control_flow.cpp index d47c63d9f..b427ac873 100644 --- a/src/video_core/shader/control_flow.cpp +++ b/src/video_core/shader/control_flow.cpp | |||
| @@ -16,7 +16,9 @@ | |||
| 16 | #include "video_core/shader/shader_ir.h" | 16 | #include "video_core/shader/shader_ir.h" |
| 17 | 17 | ||
| 18 | namespace VideoCommon::Shader { | 18 | namespace VideoCommon::Shader { |
| 19 | |||
| 19 | namespace { | 20 | namespace { |
| 21 | |||
| 20 | using Tegra::Shader::Instruction; | 22 | using Tegra::Shader::Instruction; |
| 21 | using Tegra::Shader::OpCode; | 23 | using Tegra::Shader::OpCode; |
| 22 | 24 | ||
| @@ -68,15 +70,15 @@ struct CFGRebuildState { | |||
| 68 | const ProgramCode& program_code; | 70 | const ProgramCode& program_code; |
| 69 | ConstBufferLocker& locker; | 71 | ConstBufferLocker& locker; |
| 70 | u32 start{}; | 72 | u32 start{}; |
| 71 | std::vector<BlockInfo> block_info{}; | 73 | std::vector<BlockInfo> block_info; |
| 72 | std::list<u32> inspect_queries{}; | 74 | std::list<u32> inspect_queries; |
| 73 | std::list<Query> queries{}; | 75 | std::list<Query> queries; |
| 74 | std::unordered_map<u32, u32> registered{}; | 76 | std::unordered_map<u32, u32> registered; |
| 75 | std::set<u32> labels{}; | 77 | std::set<u32> labels; |
| 76 | std::map<u32, u32> ssy_labels{}; | 78 | std::map<u32, u32> ssy_labels; |
| 77 | std::map<u32, u32> pbk_labels{}; | 79 | std::map<u32, u32> pbk_labels; |
| 78 | std::unordered_map<u32, BlockStack> stacks{}; | 80 | std::unordered_map<u32, BlockStack> stacks; |
| 79 | ASTManager* manager; | 81 | ASTManager* manager{}; |
| 80 | }; | 82 | }; |
| 81 | 83 | ||
| 82 | enum class BlockCollision : u32 { None, Found, Inside }; | 84 | enum class BlockCollision : u32 { None, Found, Inside }; |
| @@ -109,7 +111,7 @@ BlockInfo& CreateBlockInfo(CFGRebuildState& state, u32 start, u32 end) { | |||
| 109 | } | 111 | } |
| 110 | 112 | ||
| 111 | Pred GetPredicate(u32 index, bool negated) { | 113 | Pred GetPredicate(u32 index, bool negated) { |
| 112 | return static_cast<Pred>(index + (negated ? 8 : 0)); | 114 | return static_cast<Pred>(static_cast<u64>(index) + (negated ? 8ULL : 0ULL)); |
| 113 | } | 115 | } |
| 114 | 116 | ||
| 115 | /** | 117 | /** |
| @@ -136,15 +138,13 @@ struct BranchIndirectInfo { | |||
| 136 | s32 relative_position{}; | 138 | s32 relative_position{}; |
| 137 | }; | 139 | }; |
| 138 | 140 | ||
| 139 | std::optional<BranchIndirectInfo> TrackBranchIndirectInfo(const CFGRebuildState& state, | 141 | struct BufferInfo { |
| 140 | u32 start_address, u32 current_position) { | 142 | u32 index; |
| 141 | const u32 shader_start = state.start; | 143 | u32 offset; |
| 142 | u32 pos = current_position; | 144 | }; |
| 143 | BranchIndirectInfo result{}; | ||
| 144 | u64 track_register = 0; | ||
| 145 | 145 | ||
| 146 | // Step 0 Get BRX Info | 146 | std::optional<std::pair<s32, u64>> GetBRXInfo(const CFGRebuildState& state, u32& pos) { |
| 147 | const Instruction instr = {state.program_code[pos]}; | 147 | const Instruction instr = state.program_code[pos]; |
| 148 | const auto opcode = OpCode::Decode(instr); | 148 | const auto opcode = OpCode::Decode(instr); |
| 149 | if (opcode->get().GetId() != OpCode::Id::BRX) { | 149 | if (opcode->get().GetId() != OpCode::Id::BRX) { |
| 150 | return std::nullopt; | 150 | return std::nullopt; |
| @@ -152,86 +152,94 @@ std::optional<BranchIndirectInfo> TrackBranchIndirectInfo(const CFGRebuildState& | |||
| 152 | if (instr.brx.constant_buffer != 0) { | 152 | if (instr.brx.constant_buffer != 0) { |
| 153 | return std::nullopt; | 153 | return std::nullopt; |
| 154 | } | 154 | } |
| 155 | track_register = instr.gpr8.Value(); | 155 | --pos; |
| 156 | result.relative_position = instr.brx.GetBranchExtend(); | 156 | return std::make_pair(instr.brx.GetBranchExtend(), instr.gpr8.Value()); |
| 157 | pos--; | 157 | } |
| 158 | bool found_track = false; | ||
| 159 | 158 | ||
| 160 | // Step 1 Track LDC | 159 | template <typename Result, typename TestCallable, typename PackCallable> |
| 161 | while (pos >= shader_start) { | 160 | // requires std::predicate<TestCallable, Instruction, const OpCode::Matcher&> |
| 162 | if (IsSchedInstruction(pos, shader_start)) { | 161 | // requires std::invocable<PackCallable, Instruction, const OpCode::Matcher&> |
| 163 | pos--; | 162 | std::optional<Result> TrackInstruction(const CFGRebuildState& state, u32& pos, TestCallable test, |
| 163 | PackCallable pack) { | ||
| 164 | for (; pos >= state.start; --pos) { | ||
| 165 | if (IsSchedInstruction(pos, state.start)) { | ||
| 164 | continue; | 166 | continue; |
| 165 | } | 167 | } |
| 166 | const Instruction instr = {state.program_code[pos]}; | 168 | const Instruction instr = state.program_code[pos]; |
| 167 | const auto opcode = OpCode::Decode(instr); | 169 | const auto opcode = OpCode::Decode(instr); |
| 168 | if (opcode->get().GetId() == OpCode::Id::LD_C) { | 170 | if (!opcode) { |
| 169 | if (instr.gpr0.Value() == track_register && | 171 | continue; |
| 170 | instr.ld_c.type.Value() == Tegra::Shader::UniformType::Single) { | 172 | } |
| 171 | result.buffer = instr.cbuf36.index.Value(); | 173 | if (test(instr, opcode->get())) { |
| 172 | result.offset = static_cast<u32>(instr.cbuf36.GetOffset()); | 174 | --pos; |
| 173 | track_register = instr.gpr8.Value(); | 175 | return std::make_optional(pack(instr, opcode->get())); |
| 174 | pos--; | ||
| 175 | found_track = true; | ||
| 176 | break; | ||
| 177 | } | ||
| 178 | } | 176 | } |
| 179 | pos--; | ||
| 180 | } | 177 | } |
| 178 | return std::nullopt; | ||
| 179 | } | ||
| 181 | 180 | ||
| 182 | if (!found_track) { | 181 | std::optional<std::pair<BufferInfo, u64>> TrackLDC(const CFGRebuildState& state, u32& pos, |
| 183 | return std::nullopt; | 182 | u64 brx_tracked_register) { |
| 184 | } | 183 | return TrackInstruction<std::pair<BufferInfo, u64>>( |
| 185 | found_track = false; | 184 | state, pos, |
| 185 | [brx_tracked_register](auto instr, const auto& opcode) { | ||
| 186 | return opcode.GetId() == OpCode::Id::LD_C && | ||
| 187 | instr.gpr0.Value() == brx_tracked_register && | ||
| 188 | instr.ld_c.type.Value() == Tegra::Shader::UniformType::Single; | ||
| 189 | }, | ||
| 190 | [](auto instr, const auto& opcode) { | ||
| 191 | const BufferInfo info = {static_cast<u32>(instr.cbuf36.index.Value()), | ||
| 192 | static_cast<u32>(instr.cbuf36.GetOffset())}; | ||
| 193 | return std::make_pair(info, instr.gpr8.Value()); | ||
| 194 | }); | ||
| 195 | } | ||
| 186 | 196 | ||
| 187 | // Step 2 Track SHL | 197 | std::optional<u64> TrackSHLRegister(const CFGRebuildState& state, u32& pos, |
| 188 | while (pos >= shader_start) { | 198 | u64 ldc_tracked_register) { |
| 189 | if (IsSchedInstruction(pos, shader_start)) { | 199 | return TrackInstruction<u64>(state, pos, |
| 190 | pos--; | 200 | [ldc_tracked_register](auto instr, const auto& opcode) { |
| 191 | continue; | 201 | return opcode.GetId() == OpCode::Id::SHL_IMM && |
| 192 | } | 202 | instr.gpr0.Value() == ldc_tracked_register; |
| 193 | const Instruction instr = state.program_code[pos]; | 203 | }, |
| 194 | const auto opcode = OpCode::Decode(instr); | 204 | [](auto instr, const auto&) { return instr.gpr8.Value(); }); |
| 195 | if (opcode->get().GetId() == OpCode::Id::SHL_IMM) { | 205 | } |
| 196 | if (instr.gpr0.Value() == track_register) { | 206 | |
| 197 | track_register = instr.gpr8.Value(); | 207 | std::optional<u32> TrackIMNMXValue(const CFGRebuildState& state, u32& pos, |
| 198 | pos--; | 208 | u64 shl_tracked_register) { |
| 199 | found_track = true; | 209 | return TrackInstruction<u32>(state, pos, |
| 200 | break; | 210 | [shl_tracked_register](auto instr, const auto& opcode) { |
| 201 | } | 211 | return opcode.GetId() == OpCode::Id::IMNMX_IMM && |
| 202 | } | 212 | instr.gpr0.Value() == shl_tracked_register; |
| 203 | pos--; | 213 | }, |
| 214 | [](auto instr, const auto&) { | ||
| 215 | return static_cast<u32>(instr.alu.GetSignedImm20_20() + 1); | ||
| 216 | }); | ||
| 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; |