From 2ef696c85a37917102a9f869775180ab225f0d56 Mon Sep 17 00:00:00 2001
From: Fernando Sahmkow <fsahmkow27@gmail.com>
Date: Mon, 23 Sep 2019 11:15:09 -0400
Subject: [PATCH] Shader_IR: Implement BRX tracking.

---
 src/video_core/shader/control_flow.cpp | 113 +++++++++++++++++++++++++
 1 file changed, 113 insertions(+)

diff --git a/src/video_core/shader/control_flow.cpp b/src/video_core/shader/control_flow.cpp
index 9d21f45ded..70f758642a 100644
--- a/src/video_core/shader/control_flow.cpp
+++ b/src/video_core/shader/control_flow.cpp
@@ -124,6 +124,111 @@ enum class ParseResult : u32 {
     AbnormalFlow,
 };
 
+struct BranchIndirectInfo {
+    u32 buffer{};
+    u32 offset{};
+    u32 entries{};
+    s32 relative_position{};
+};
+
+std::optional<BranchIndirectInfo> TrackBranchIndirectInfo(const CFGRebuildState& state,
+                                                          u32 start_address, u32 current_position) {
+    const u32 shader_start = state.start;
+    u32 pos = current_position;
+    BranchIndirectInfo result{};
+    u64 track_register = 0;
+
+    // Step 0 Get BRX Info
+    const Instruction instr = {state.program_code[pos]};
+    const auto opcode = OpCode::Decode(instr);
+    if (opcode->get().GetId() != OpCode::Id::BRX) {
+        return {};
+    }
+    if (instr.brx.constant_buffer != 0) {
+        return {};
+    }
+    track_register = instr.gpr8.Value();
+    result.relative_position = instr.brx.GetBranchExtend();
+    pos--;
+    bool found_track = false;
+
+    // Step 1 Track LDC
+    while (pos >= shader_start) {
+        if (IsSchedInstruction(pos, shader_start)) {
+            pos--;
+            continue;
+        }
+        const Instruction instr = {state.program_code[pos]};
+        const auto opcode = OpCode::Decode(instr);
+        if (opcode->get().GetId() == OpCode::Id::LD_C) {
+            if (instr.gpr0.Value() == track_register &&
+                instr.ld_c.type.Value() == Tegra::Shader::UniformType::Single) {
+                result.buffer = instr.cbuf36.index;
+                result.offset = instr.cbuf36.GetOffset();
+                track_register = instr.gpr8.Value();
+                pos--;
+                found_track = true;
+                break;
+            }
+        }
+        pos--;
+    }
+
+    if (!found_track) {
+        return {};
+    }
+    found_track = false;
+
+    // Step 2 Track SHL
+    while (pos >= shader_start) {
+        if (IsSchedInstruction(pos, shader_start)) {
+            pos--;
+            continue;
+        }
+        const Instruction instr = {state.program_code[pos]};
+        const auto opcode = OpCode::Decode(instr);
+        if (opcode->get().GetId() == OpCode::Id::SHL_IMM) {
+            if (instr.gpr0.Value() == track_register) {
+                track_register = instr.gpr8.Value();
+                pos--;
+                found_track = true;
+                break;
+            }
+        }
+        pos--;
+    }
+
+    if (!found_track) {
+        return {};
+    }
+    found_track = false;
+
+    // Step 3 Track IMNMX
+    while (pos >= shader_start) {
+        if (IsSchedInstruction(pos, shader_start)) {
+            pos--;
+            continue;
+        }
+        const Instruction instr = {state.program_code[pos]};
+        const auto opcode = OpCode::Decode(instr);
+        if (opcode->get().GetId() == OpCode::Id::IMNMX_IMM) {
+            if (instr.gpr0.Value() == track_register) {
+                track_register = instr.gpr8.Value();
+                result.entries = instr.alu.GetSignedImm20_20();
+                pos--;
+                found_track = true;
+                break;
+            }
+        }
+        pos--;
+    }
+
+    if (!found_track) {
+        return {};
+    }
+    return {result};
+}
+
 std::pair<ParseResult, ParseInfo> ParseCode(CFGRebuildState& state, u32 address) {
     u32 offset = static_cast<u32>(address);
     const u32 end_address = static_cast<u32>(state.program_size / sizeof(Instruction));
@@ -298,6 +403,14 @@ std::pair<ParseResult, ParseInfo> ParseCode(CFGRebuildState& state, u32 address)
             break;
         }
         case OpCode::Id::BRX: {
+            auto tmp = TrackBranchIndirectInfo(state, address, offset);
+            if (tmp) {
+                auto result = *tmp;
+                LOG_CRITICAL(HW_GPU, "Track Successful, BRX: buffer:{}, offset:{}, entries:{}",
+                             result.buffer, result.offset, result.entries);
+            } else {
+                LOG_CRITICAL(HW_GPU, "Track Unsuccesful");
+            }
             return {ParseResult::AbnormalFlow, parse_info};
         }
         default: