Skip to content

Commit

Permalink
DSPAnalyzer: Implement DSP analyzer skeleton and use it
Browse files Browse the repository at this point in the history
Attempts to simply make use of the interface. Cleanup will follow in
subsequent commits to make for nicer review.
  • Loading branch information
lioncash committed Dec 28, 2020
1 parent 8f4c6ad commit 5756ece
Show file tree
Hide file tree
Showing 8 changed files with 58 additions and 66 deletions.
67 changes: 31 additions & 36 deletions Source/Core/Core/DSP/DSPAnalyzer.cpp
Expand Up @@ -14,13 +14,6 @@

namespace DSP::Analyzer
{
namespace
{
constexpr size_t ISPACE = 65536;

// Holds data about all instructions in RAM.
std::array<u8, ISPACE> code_flags;

// Good candidates for idle skipping is mail wait loops. If we're time slicing
// between the main CPU and the DSP, if the DSP runs into one of these, it might
// as well give up its time slice immediately, after executing once.
Expand Down Expand Up @@ -65,14 +58,28 @@ constexpr u16 idle_skip_sigs[NUM_IDLE_SIGS][MAX_IDLE_SIG_SIZE + 1] = {
{0x00da, 0x0352, // LR $AX0.H, @0x0352
0x8600, // TSTAXH $AX0.H
0x0295, 0xFFFF, // JZ 0x????
0, 0}};
0, 0},
};

Analyzer::Analyzer(const SDSP& dsp) : m_dsp{dsp}
{
}

Analyzer::~Analyzer() = default;

void Analyzer::Analyze()
{
Reset();
AnalyzeRange(0x0000, 0x1000); // IRAM
AnalyzeRange(0x8000, 0x9000); // IROM
}

void Reset()
void Analyzer::Reset()
{
code_flags.fill(0);
m_code_flags.fill(0);
}

void AnalyzeRange(const SDSP& dsp, u16 start_addr, u16 end_addr)
void Analyzer::AnalyzeRange(u16 start_addr, u16 end_addr)
{
// First we run an extremely simplified version of a disassembler to find
// where all instructions start.
Expand All @@ -82,27 +89,27 @@ void AnalyzeRange(const SDSP& dsp, u16 start_addr, u16 end_addr)
u16 last_arithmetic = 0;
for (u16 addr = start_addr; addr < end_addr;)
{
const UDSPInstruction inst = dsp.ReadIMEM(addr);
const UDSPInstruction inst = m_dsp.ReadIMEM(addr);
const DSPOPCTemplate* opcode = GetOpTemplate(inst);
if (!opcode)
{
addr++;
continue;
}
code_flags[addr] |= CODE_START_OF_INST;
m_code_flags[addr] |= CODE_START_OF_INST;
// Look for loops.
if ((inst & 0xffe0) == 0x0060 || (inst & 0xff00) == 0x1100)
{
// BLOOP, BLOOPI
const u16 loop_end = dsp.ReadIMEM(addr + 1);
code_flags[addr] |= CODE_LOOP_START;
code_flags[loop_end] |= CODE_LOOP_END;
const u16 loop_end = m_dsp.ReadIMEM(addr + 1);
m_code_flags[addr] |= CODE_LOOP_START;
m_code_flags[loop_end] |= CODE_LOOP_END;
}
else if ((inst & 0xffe0) == 0x0040 || (inst & 0xff00) == 0x1000)
{
// LOOP, LOOPI
code_flags[addr] |= CODE_LOOP_START;
code_flags[static_cast<u16>(addr + 1u)] |= CODE_LOOP_END;
m_code_flags[addr] |= CODE_LOOP_START;
m_code_flags[static_cast<u16>(addr + 1u)] |= CODE_LOOP_END;
}

// Mark the last arithmetic/multiplier instruction before a branch.
Expand All @@ -114,15 +121,17 @@ void AnalyzeRange(const SDSP& dsp, u16 start_addr, u16 end_addr)

if (opcode->branch && !opcode->uncond_branch)
{
code_flags[last_arithmetic] |= CODE_UPDATE_SR;
m_code_flags[last_arithmetic] |= CODE_UPDATE_SR;
}

// If an instruction potentially raises exceptions, mark the following
// instruction as needing to check for exceptions
if (opcode->opcode == 0x00c0 || opcode->opcode == 0x1800 || opcode->opcode == 0x1880 ||
opcode->opcode == 0x1900 || opcode->opcode == 0x1980 || opcode->opcode == 0x2000 ||
opcode->extended)
code_flags[static_cast<u16>(addr + opcode->size)] |= CODE_CHECK_INT;
{
m_code_flags[static_cast<u16>(addr + opcode->size)] |= CODE_CHECK_INT;
}

addr += opcode->size;
}
Expand All @@ -139,30 +148,16 @@ void AnalyzeRange(const SDSP& dsp, u16 start_addr, u16 end_addr)
found = true;
if (idle_skip_sigs[s][i] == 0xFFFF)
continue;
if (idle_skip_sigs[s][i] != dsp.ReadIMEM(static_cast<u16>(addr + i)))
if (idle_skip_sigs[s][i] != m_dsp.ReadIMEM(static_cast<u16>(addr + i)))
break;
}
if (found)
{
INFO_LOG_FMT(DSPLLE, "Idle skip location found at {:02x} (sigNum:{})", addr, s + 1);
code_flags[addr] |= CODE_IDLE_SKIP;
m_code_flags[addr] |= CODE_IDLE_SKIP;
}
}
}
INFO_LOG_FMT(DSPLLE, "Finished analysis.");
}
} // Anonymous namespace

void Analyze(const SDSP& dsp)
{
Reset();
AnalyzeRange(dsp, 0x0000, 0x1000); // IRAM
AnalyzeRange(dsp, 0x8000, 0x9000); // IROM
}

u8 GetCodeFlags(u16 address)
{
return code_flags[address];
}

} // namespace DSP::Analyzer
12 changes: 0 additions & 12 deletions Source/Core/Core/DSP/DSPAnalyzer.h
Expand Up @@ -66,16 +66,4 @@ class Analyzer
// DSP context for analysis to be run under.
const SDSP& m_dsp;
};

// This one should be called every time IRAM changes - which is basically
// every time that a new ucode gets uploaded, and never else. At that point,
// we can do as much static analysis as we want - but we should always throw
// all old analysis away. Luckily the entire address space is only 64K code
// words and the actual code space 8K instructions in total, so we can do
// some pretty expensive analysis if necessary.
void Analyze(const SDSP& dsp);

// Retrieves the flags set during analysis for code in memory.
u8 GetCodeFlags(u16 address);

} // namespace DSP::Analyzer
4 changes: 2 additions & 2 deletions Source/Core/Core/DSP/DSPCore.cpp
Expand Up @@ -115,7 +115,7 @@ class LLEAccelerator final : public Accelerator
SDSP& m_dsp;
};

SDSP::SDSP(DSPCore& core) : m_dsp_core{core}
SDSP::SDSP(DSPCore& core) : m_dsp_core{core}, m_analyzer{*this}
{
}

Expand Down Expand Up @@ -487,7 +487,7 @@ void DSPCore::Step()
void DSPCore::Reset()
{
m_dsp.Reset();
Analyzer::Analyze(m_dsp);
m_dsp.GetAnalyzer().Analyze();
}

void DSPCore::ClearIRAM()
Expand Down
6 changes: 6 additions & 0 deletions Source/Core/Core/DSP/DSPCore.h
Expand Up @@ -12,6 +12,7 @@
#include <string>

#include "Common/Event.h"
#include "Core/DSP/DSPAnalyzer.h"
#include "Core/DSP/DSPBreakpoints.h"
#include "Core/DSP/DSPCaptureLogger.h"

Expand Down Expand Up @@ -396,6 +397,10 @@ struct SDSP
// Saves and loads any necessary state.
void DoState(PointerWrap& p);

// DSP static analyzer.
Analyzer::Analyzer& GetAnalyzer() { return m_analyzer; }
const Analyzer::Analyzer& GetAnalyzer() const { return m_analyzer; }

DSP_Regs r{};
u16 pc = 0;

Expand Down Expand Up @@ -449,6 +454,7 @@ struct SDSP
u16 ReadIFXImpl(u16 address);

DSPCore& m_dsp_core;
Analyzer::Analyzer m_analyzer;
};

enum class State
Expand Down
14 changes: 8 additions & 6 deletions Source/Core/Core/DSP/Interpreter/DSPInterpreter.cpp
Expand Up @@ -42,14 +42,16 @@ void Interpreter::ExecuteInstruction(const UDSPInstruction inst)

void Interpreter::Step()
{
auto& state = m_dsp_core.DSPState();

m_dsp_core.CheckExceptions();
m_dsp_core.DSPState().step_counter++;
state.step_counter++;

const u16 opc = m_dsp_core.DSPState().FetchInstruction();
const u16 opc = state.FetchInstruction();
ExecuteInstruction(UDSPInstruction{opc});

const auto pc = m_dsp_core.DSPState().pc;
if ((Analyzer::GetCodeFlags(static_cast<u16>(pc - 1)) & Analyzer::CODE_LOOP_END) != 0)
const auto pc = state.pc;
if ((state.GetAnalyzer().GetCodeFlags(static_cast<u16>(pc - 1)) & Analyzer::CODE_LOOP_END) != 0)
HandleLoop();
}

Expand Down Expand Up @@ -114,7 +116,7 @@ int Interpreter::RunCyclesDebug(int cycles)
}

// Idle skipping.
if ((Analyzer::GetCodeFlags(state.pc) & Analyzer::CODE_IDLE_SKIP) != 0)
if ((state.GetAnalyzer().GetCodeFlags(state.pc) & Analyzer::CODE_IDLE_SKIP) != 0)
return 0;

Step();
Expand Down Expand Up @@ -170,7 +172,7 @@ int Interpreter::RunCycles(int cycles)
return 0;

// Idle skipping.
if ((Analyzer::GetCodeFlags(state.pc) & Analyzer::CODE_IDLE_SKIP) != 0)
if ((state.GetAnalyzer().GetCodeFlags(state.pc) & Analyzer::CODE_IDLE_SKIP) != 0)
return 0;

Step();
Expand Down
15 changes: 8 additions & 7 deletions Source/Core/Core/DSP/Jit/x64/DSPEmitter.cpp
Expand Up @@ -128,7 +128,7 @@ void DSPEmitter::checkExceptions(u32 retval)

bool DSPEmitter::FlagsNeeded() const
{
const u8 flags = Analyzer::GetCodeFlags(m_compile_pc);
const u8 flags = m_dsp_core.DSPState().GetAnalyzer().GetCodeFlags(m_compile_pc);

return !(flags & Analyzer::CODE_START_OF_INST) || (flags & Analyzer::CODE_UPDATE_SR);
}
Expand Down Expand Up @@ -242,9 +242,10 @@ void DSPEmitter::Compile(u16 start_addr)
bool fixup_pc = false;
m_block_size[start_addr] = 0;

auto& analyzer = m_dsp_core.DSPState().GetAnalyzer();
while (m_compile_pc < start_addr + MAX_BLOCK_SIZE)
{
if (Analyzer::GetCodeFlags(m_compile_pc) & Analyzer::CODE_CHECK_INT)
if (analyzer.GetCodeFlags(m_compile_pc) & Analyzer::CODE_CHECK_INT)
checkExceptions(m_block_size[start_addr]);

const UDSPInstruction inst = m_dsp_core.DSPState().ReadIMEM(m_compile_pc);
Expand All @@ -262,7 +263,7 @@ void DSPEmitter::Compile(u16 start_addr)

// Handle loop condition, only if current instruction was flagged as a loop destination
// by the analyzer.
if (Analyzer::GetCodeFlags(static_cast<u16>(m_compile_pc - 1u)) & Analyzer::CODE_LOOP_END)
if ((analyzer.GetCodeFlags(static_cast<u16>(m_compile_pc - 1u)) & Analyzer::CODE_LOOP_END) != 0)
{
MOVZX(32, 16, EAX, M_SDSP_r_st(2));
TEST(32, R(EAX), R(EAX));
Expand All @@ -283,7 +284,7 @@ void DSPEmitter::Compile(u16 start_addr)
DSPJitRegCache c(m_gpr);
HandleLoop();
m_gpr.SaveRegs();
if (!Host::OnThread() && Analyzer::GetCodeFlags(start_addr) & Analyzer::CODE_IDLE_SKIP)
if (!Host::OnThread() && (analyzer.GetCodeFlags(start_addr) & Analyzer::CODE_IDLE_SKIP) != 0)
{
MOV(16, R(EAX), Imm16(DSP_IDLE_SKIP_CYCLES));
}
Expand Down Expand Up @@ -319,7 +320,7 @@ void DSPEmitter::Compile(u16 start_addr)
DSPJitRegCache c(m_gpr);
// don't update g_dsp.pc -- the branch insn already did
m_gpr.SaveRegs();
if (!Host::OnThread() && Analyzer::GetCodeFlags(start_addr) & Analyzer::CODE_IDLE_SKIP)
if (!Host::OnThread() && (analyzer.GetCodeFlags(start_addr) & Analyzer::CODE_IDLE_SKIP) != 0)
{
MOV(16, R(EAX), Imm16(DSP_IDLE_SKIP_CYCLES));
}
Expand All @@ -336,7 +337,7 @@ void DSPEmitter::Compile(u16 start_addr)
}

// End the block if we're before an idle skip address
if (Analyzer::GetCodeFlags(m_compile_pc) & Analyzer::CODE_IDLE_SKIP)
if ((analyzer.GetCodeFlags(m_compile_pc) & Analyzer::CODE_IDLE_SKIP) != 0)
{
break;
}
Expand Down Expand Up @@ -382,7 +383,7 @@ void DSPEmitter::Compile(u16 start_addr)
}

m_gpr.SaveRegs();
if (!Host::OnThread() && Analyzer::GetCodeFlags(start_addr) & Analyzer::CODE_IDLE_SKIP)
if (!Host::OnThread() && (analyzer.GetCodeFlags(start_addr) & Analyzer::CODE_IDLE_SKIP) != 0)
{
MOV(16, R(EAX), Imm16(DSP_IDLE_SKIP_CYCLES));
}
Expand Down
3 changes: 2 additions & 1 deletion Source/Core/Core/DSP/Jit/x64/DSPJitBranch.cpp
Expand Up @@ -82,7 +82,8 @@ void DSPEmitter::WriteBranchExit()
{
DSPJitRegCache c(m_gpr);
m_gpr.SaveRegs();
if (Analyzer::GetCodeFlags(m_start_address) & Analyzer::CODE_IDLE_SKIP)
if ((m_dsp_core.DSPState().GetAnalyzer().GetCodeFlags(m_start_address) &
Analyzer::CODE_IDLE_SKIP) != 0)
{
MOV(16, R(EAX), Imm16(0x1000));
}
Expand Down
3 changes: 1 addition & 2 deletions Source/Core/Core/HW/DSPLLE/DSPHost.cpp
Expand Up @@ -93,8 +93,7 @@ void CodeLoaded(DSPCore& dsp, const u8* ptr, size_t size)
UpdateDebugger();

dsp.ClearIRAM();

Analyzer::Analyze(state);
state.GetAnalyzer().Analyze();
}

void UpdateDebugger()
Expand Down

0 comments on commit 5756ece

Please sign in to comment.