Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions ps2xRecomp/src/lib/control_flow_analyzer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,14 @@ namespace ps2recomp

for (const auto &inst : instructions)
{
// A guest-installed syscall handler runs as a separate invocation,
// so the scheduler resumes this thread at syscall+4 and needs an
// entry point there. +4, not +8: syscall has no delay slot.
if (inst.opcode == OPCODE_SPECIAL && inst.function == SPECIAL_SYSCALL)
{
queueResumeEntryTarget(inst.address + 4u);
}

bool isStaticJump = (inst.opcode == OPCODE_J || inst.opcode == OPCODE_JAL);
if (inst.isBranch && inst.opcode != OPCODE_J && inst.opcode != OPCODE_JAL)
{
Expand Down
40 changes: 40 additions & 0 deletions ps2xTest/src/code_generator_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,17 @@ static Instruction makeJr(uint32_t address, uint8_t rs)
return inst;
}

static Instruction makeSyscall(uint32_t address)
{
Instruction inst{};
inst.address = address;
inst.opcode = OPCODE_SPECIAL;
inst.function = SPECIAL_SYSCALL;
inst.hasDelaySlot = false;
inst.raw = (OPCODE_SPECIAL << 26) | SPECIAL_SYSCALL;
return inst;
}

static void printGeneratedCode(const std::string& name, const std::string& code)
{
#ifdef PRINT_GENERATED_CODE
Expand Down Expand Up @@ -556,6 +567,35 @@ void register_code_generator_tests()
"unresolved JALR should not pretend it has a resolved local jump table");
});

tc.Run("syscall marks the following instruction as a resume entry", [](TestCase &t) {
// Shape of a real SDK syscall wrapper:
// addiu $v1, $zero, <num> ; syscall ; jr $ra ; <delay slot>
Function func;
func.name = "syscall_wrapper";
func.start = 0x4000;
func.end = 0x4010;
func.isRecompiled = true;
func.isStub = false;

std::vector<Instruction> instructions{
makeAddiu(0x4000, 3, 0, 0x83),
makeSyscall(0x4004),
makeJr(0x4008, 31),
makeNop(0x400C),
};

CodeGenerator gen({}, {});
CodeGenerator::AnalysisResult analysis = gen.collectInternalBranchTargets(func, instructions);

// +4, not +8: syscall has no delay slot.
t.IsTrue(analysis.resumeEntryPoints.contains(0x4008u),
"syscall should mark the next instruction as resumable");
t.IsTrue(analysis.entryPoints.contains(0x4008u),
"syscall resume pc should emit a label in the owner");
t.IsFalse(analysis.resumeEntryPoints.contains(0x400Cu),
"syscall must not claim a delay slot it does not have");
});

tc.Run("resume entry targets emit a top-level pc switch in the owner wrapper", [](TestCase &t) {
Function func;
func.name = "resume_owner";
Expand Down