mirror of
https://github.com/N64Recomp/N64Recomp.git
synced 2025-05-14 08:12:19 +00:00
Add support for cop1 writes and reads to live recompiler, WIP relocation support
This commit is contained in:
parent
514ad596ae
commit
a13fa4d423
3 changed files with 110 additions and 37 deletions
|
@ -235,8 +235,14 @@ void get_gpr_values(int gpr, sljit_sw& out, sljit_sw& outw) {
|
|||
}
|
||||
}
|
||||
|
||||
void get_operand_values(N64Recomp::Operand operand, const N64Recomp::InstructionContext& context, sljit_sw& out, sljit_sw& outw) {
|
||||
void get_operand_values(N64Recomp::Operand operand, const N64Recomp::InstructionContext& context, sljit_sw& out, sljit_sw& outw, bool& needs_relocation) {
|
||||
using namespace N64Recomp;
|
||||
bool relocation_valid = false;
|
||||
|
||||
// Relocations are only valid for ImmS16 and ImmU16 operands.
|
||||
assert(context.reloc_type == RelocType::R_MIPS_NONE ||
|
||||
(operand == Operand::ImmS16 || operand == Operand::ImmU16));
|
||||
|
||||
switch (operand) {
|
||||
case Operand::Rd:
|
||||
get_gpr_values(context.rd, out, outw);
|
||||
|
@ -305,22 +311,14 @@ void get_operand_values(N64Recomp::Operand operand, const N64Recomp::Instruction
|
|||
outw = get_fpr_u64_context_offset(context.ft);
|
||||
break;
|
||||
case Operand::ImmU16:
|
||||
//if (context.reloc_type != N64Recomp::RelocType::R_MIPS_NONE) {
|
||||
// assert(false);
|
||||
//}
|
||||
//else {
|
||||
out = SLJIT_IMM;
|
||||
outw = (sljit_sw)(uint16_t)context.imm16;
|
||||
//}
|
||||
relocation_valid = true;
|
||||
out = SLJIT_IMM;
|
||||
outw = (sljit_sw)(uint16_t)context.imm16;
|
||||
break;
|
||||
case Operand::ImmS16:
|
||||
//if (context.reloc_type != N64Recomp::RelocType::R_MIPS_NONE) {
|
||||
// assert(false);
|
||||
//}
|
||||
//else {
|
||||
out = SLJIT_IMM;
|
||||
outw = (sljit_sw)(int16_t)context.imm16;
|
||||
//}
|
||||
relocation_valid = true;
|
||||
out = SLJIT_IMM;
|
||||
outw = (sljit_sw)(int16_t)context.imm16;
|
||||
break;
|
||||
case Operand::Sa:
|
||||
out = SLJIT_IMM;
|
||||
|
@ -347,6 +345,13 @@ void get_operand_values(N64Recomp::Operand operand, const N64Recomp::Instruction
|
|||
outw = 0;
|
||||
break;
|
||||
}
|
||||
if (context.reloc_type != N64Recomp::RelocType::R_MIPS_NONE) {
|
||||
assert(relocation_valid && "Relocation present but not valid on the given operand!");
|
||||
needs_relocation = true;
|
||||
}
|
||||
else {
|
||||
needs_relocation = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool outputs_to_zero(N64Recomp::Operand output, const N64Recomp::InstructionContext& ctx) {
|
||||
|
@ -371,13 +376,25 @@ void N64Recomp::LiveGenerator::process_binary_op(const BinaryOp& op, const Instr
|
|||
bool failed = false;
|
||||
sljit_sw dst;
|
||||
sljit_sw dstw;
|
||||
bool relocation_needed_dst;
|
||||
sljit_sw src1;
|
||||
sljit_sw src1w;
|
||||
bool relocation_needed_src1;
|
||||
sljit_sw src2;
|
||||
sljit_sw src2w;
|
||||
get_operand_values(op.output, ctx, dst, dstw);
|
||||
get_operand_values(op.operands.operands[0], ctx, src1, src1w);
|
||||
get_operand_values(op.operands.operands[1], ctx, src2, src2w);
|
||||
bool relocation_needed_src2;
|
||||
get_operand_values(op.output, ctx, dst, dstw, relocation_needed_dst);
|
||||
get_operand_values(op.operands.operands[0], ctx, src1, src1w, relocation_needed_src1);
|
||||
get_operand_values(op.operands.operands[1], ctx, src2, src2w, relocation_needed_src2);
|
||||
|
||||
// Relocations are only valid on the second operand.
|
||||
assert(!relocation_needed_dst && !relocation_needed_src1);
|
||||
|
||||
// TODO perform relocation.
|
||||
// If a relocation is needed for the second operand, perform the relocation and change src2/src2w to use the relocated value.
|
||||
if (relocation_needed_src2) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
// TODO validate that the unary ops are valid for the current binary op.
|
||||
assert(op.operands.operand_operations[0] == UnaryOpType::None ||
|
||||
|
@ -686,22 +703,27 @@ void N64Recomp::LiveGenerator::process_binary_op(const BinaryOp& op, const Instr
|
|||
|
||||
void N64Recomp::LiveGenerator::process_unary_op(const UnaryOp& op, const InstructionContext& ctx) const {
|
||||
// Skip instructions that output to $zero
|
||||
if (op.output == Operand::Rd && ctx.rd == 0) {
|
||||
return;
|
||||
}
|
||||
if (op.output == Operand::Rt && ctx.rt == 0) {
|
||||
return;
|
||||
}
|
||||
if (op.output == Operand::Rs && ctx.rs == 0) {
|
||||
if (outputs_to_zero(op.output, ctx)) {
|
||||
return;
|
||||
}
|
||||
|
||||
sljit_sw dst;
|
||||
sljit_sw dstw;
|
||||
bool relocation_needed_dst;
|
||||
sljit_sw src;
|
||||
sljit_sw srcw;
|
||||
get_operand_values(op.output, ctx, dst, dstw);
|
||||
get_operand_values(op.input, ctx, src, srcw);
|
||||
bool relocation_needed_src;
|
||||
get_operand_values(op.output, ctx, dst, dstw, relocation_needed_dst);
|
||||
get_operand_values(op.input, ctx, src, srcw, relocation_needed_src);
|
||||
|
||||
// Relocations aren't valid on the output operand.
|
||||
assert(!relocation_needed_dst);
|
||||
|
||||
// TODO perform relocation.
|
||||
// If a relocation is needed for the input operand, perform the relocation and change src/srcw to use the relocated value.
|
||||
if (relocation_needed_src) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
sljit_s32 jit_op;
|
||||
|
||||
|
@ -747,9 +769,13 @@ void N64Recomp::LiveGenerator::process_unary_op(const UnaryOp& op, const Instruc
|
|||
void N64Recomp::LiveGenerator::process_store_op(const StoreOp& op, const InstructionContext& ctx) const {
|
||||
sljit_sw src;
|
||||
sljit_sw srcw;
|
||||
bool relocation_needed_src;
|
||||
sljit_sw imm = (sljit_sw)(int16_t)ctx.imm16;
|
||||
|
||||
get_operand_values(op.value_input, ctx, src, srcw);
|
||||
get_operand_values(op.value_input, ctx, src, srcw, relocation_needed_src);
|
||||
|
||||
// Relocations aren't valid on the input operand.
|
||||
assert(!relocation_needed_src);
|
||||
|
||||
// Add the base register (rs) and the immediate to get the address and store it in the arithemtic temp.
|
||||
sljit_emit_op2(compiler, SLJIT_ADD, Registers::arithmetic_temp1, 0, SLJIT_MEM1(Registers::ctx), get_gpr_context_offset(ctx.rs), SLJIT_IMM, imm);
|
||||
|
@ -939,15 +965,20 @@ void N64Recomp::LiveGenerator::emit_branch_condition(const ConditionalBranchOp&
|
|||
}
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
assert(false && "Invalid branch condition comparison operation!");
|
||||
}
|
||||
sljit_sw src1;
|
||||
sljit_sw src1w;
|
||||
bool relocation_needed_src1;
|
||||
sljit_sw src2;
|
||||
sljit_sw src2w;
|
||||
bool relocation_needed_src2;
|
||||
|
||||
get_operand_values(op.operands.operands[0], ctx, src1, src1w);
|
||||
get_operand_values(op.operands.operands[1], ctx, src2, src2w);
|
||||
get_operand_values(op.operands.operands[0], ctx, src1, src1w, relocation_needed_src1);
|
||||
get_operand_values(op.operands.operands[1], ctx, src2, src2w, relocation_needed_src2);
|
||||
|
||||
// Relocations aren't valid on the input operands.
|
||||
assert(!relocation_needed_src1 && !relocation_needed_src2);
|
||||
|
||||
// Create a compare jump and track it as the pending branch jump.
|
||||
context->cur_branch_jump = sljit_emit_cmp(compiler, condition_type, src1, src1w, src2, src2w);
|
||||
|
@ -1023,19 +1054,57 @@ void N64Recomp::LiveGenerator::emit_check_nan(int fpr, bool is_double) const {
|
|||
}
|
||||
|
||||
void N64Recomp::LiveGenerator::emit_cop0_status_read(int reg) const {
|
||||
assert(false);
|
||||
// Skip the read if the target is the zero register.
|
||||
if (reg != 0) {
|
||||
// Load ctx into R0.
|
||||
sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R0, 0, Registers::ctx, 0);
|
||||
|
||||
// Call cop0_status_read.
|
||||
sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS2V(P,32), SLJIT_IMM, sljit_sw(inputs.cop0_status_read));
|
||||
|
||||
// Store the result in the output register.
|
||||
sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_MEM1(Registers::ctx), get_gpr_context_offset(reg), SLJIT_R0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void N64Recomp::LiveGenerator::emit_cop0_status_write(int reg) const {
|
||||
assert(false);
|
||||
sljit_sw src;
|
||||
sljit_sw srcw;
|
||||
get_gpr_values(reg, src, srcw);
|
||||
|
||||
// Load ctx and the input register value into R0 and R1
|
||||
sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R0, 0, Registers::ctx, 0);
|
||||
sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R1, 0, src, srcw);
|
||||
|
||||
// Call cop0_status_write.
|
||||
sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS2V(P,32), SLJIT_IMM, sljit_sw(inputs.cop0_status_write));
|
||||
}
|
||||
|
||||
void N64Recomp::LiveGenerator::emit_cop1_cs_read(int reg) const {
|
||||
assert(false);
|
||||
// Skip the read if the target is the zero register.
|
||||
if (reg != 0) {
|
||||
// Load ctx into R0.
|
||||
sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R0, 0, Registers::ctx, 0);
|
||||
|
||||
// Call cop1_cs_read.
|
||||
sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS2V(P,32), SLJIT_IMM, sljit_sw(inputs.cop1_cs_read));
|
||||
|
||||
// Store the result in the output register.
|
||||
sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_MEM1(Registers::ctx), get_gpr_context_offset(reg), SLJIT_R0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void N64Recomp::LiveGenerator::emit_cop1_cs_write(int reg) const {
|
||||
assert(false);
|
||||
sljit_sw src;
|
||||
sljit_sw srcw;
|
||||
get_gpr_values(reg, src, srcw);
|
||||
|
||||
// Load ctx and the input register value into R0 and R1
|
||||
sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R0, 0, Registers::ctx, 0);
|
||||
sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R1, 0, src, srcw);
|
||||
|
||||
// Call cop1_cs_write.
|
||||
sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS2V(P,32), SLJIT_IMM, sljit_sw(inputs.cop1_cs_write));
|
||||
}
|
||||
|
||||
void N64Recomp::LiveGenerator::emit_muldiv(InstrId instr_id, int reg1, int reg2) const {
|
||||
|
@ -1194,7 +1263,7 @@ void N64Recomp::LiveGenerator::emit_muldiv(InstrId instr_id, int reg1, int reg2)
|
|||
do_div_op(true, false);
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
assert(false && "Invalid mul/div instruction id!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,6 +48,8 @@ namespace N64Recomp {
|
|||
uint32_t base_event_index;
|
||||
void (*cop0_status_write)(recomp_context* ctx, gpr value);
|
||||
gpr (*cop0_status_read)(recomp_context* ctx);
|
||||
void (*cop1_cs_read)(recomp_context* ctx, gpr value);
|
||||
gpr (*cop1_cs_write)(recomp_context* ctx);
|
||||
void (*switch_error)(const char* func, uint32_t vram, uint32_t jtbl);
|
||||
void (*do_break)(uint32_t vram);
|
||||
recomp_func_t* (*get_function)(int32_t vram);
|
||||
|
|
|
@ -312,6 +312,8 @@ bool process_instruction(GeneratorType& generator, const N64Recomp::Context& con
|
|||
// Create a static function add it to the static function list for this section.
|
||||
jal_target_name = fmt::format("static_{}_{:08X}", func.section_index, target_func_vram);
|
||||
static_funcs_out[func.section_index].push_back(target_func_vram);
|
||||
// TODO skip lookup for static functions.
|
||||
call_by_lookup = true;
|
||||
break;
|
||||
case JalResolutionResult::Ambiguous:
|
||||
fmt::print(stderr, "[Info] Ambiguous jal target 0x{:08X} in function {}, falling back to function lookup\n", target_func_vram, func.name);
|
||||
|
|
Loading…
Add table
Reference in a new issue