Implement remaining odd float operations in live recompiler (#149)

This commit is contained in:
Wiseguy 2025-07-17 22:44:49 -04:00 committed by GitHub
parent 6e7a5bdb2f
commit 7b8b3c1920
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -238,15 +238,27 @@ constexpr int get_fpr_double_context_offset(int fpr_index) {
return offsetof(recomp_context, f0.d) + sizeof(recomp_context::f0) * fpr_index; return offsetof(recomp_context, f0.d) + sizeof(recomp_context::f0) * fpr_index;
} }
constexpr int get_fpr_u32l_context_offset(int fpr_index) { constexpr bool is_fpr_u32l(N64Recomp::Operand operand) {
return
operand == N64Recomp::Operand::FdU32L ||
operand == N64Recomp::Operand::FsU32L ||
operand == N64Recomp::Operand::FtU32L;
return false;
}
constexpr void get_fpr_u32l_context_offset(int fpr_index, sljit_compiler* compiler, int odd_float_address_register, sljit_sw& out, sljit_sw& outw) {
if (fpr_index & 1) { if (fpr_index & 1) {
// TODO implement odd floats. assert(compiler != nullptr);
assert(false); // Load ctx->f_odd into the address register.
return -1; sljit_emit_op1(compiler, SLJIT_MOV_P, odd_float_address_register, 0, SLJIT_MEM1(Registers::ctx), offsetof(recomp_context, f_odd));
// return fmt::format("ctx->f_odd[({} - 1) * 2]", fpr_index); // sljit_emit_op0(compiler, SLJIT_BREAKPOINT);
out = SLJIT_MEM1(odd_float_address_register);
// Set a memory offset of ((fpr_index - 1) * 2) * sizeof(*f_odd).
outw = ((fpr_index - 1) * 2) * sizeof(*recomp_context::f_odd);
} }
else { else {
return offsetof(recomp_context, f0.u32l) + sizeof(recomp_context::f0) * fpr_index; out = SLJIT_MEM1(Registers::ctx);
outw = offsetof(recomp_context, f0.u32l) + sizeof(recomp_context::f0) * fpr_index;
} }
} }
@ -265,7 +277,10 @@ void get_gpr_values(int gpr, sljit_sw& out, sljit_sw& outw) {
} }
} }
bool get_operand_values(N64Recomp::Operand operand, const N64Recomp::InstructionContext& context, sljit_sw& out, sljit_sw& outw) { bool get_operand_values(N64Recomp::Operand operand, const N64Recomp::InstructionContext& context, sljit_sw& out, sljit_sw& outw,
sljit_compiler* compiler, int odd_float_address_register
)
{
using namespace N64Recomp; using namespace N64Recomp;
switch (operand) { switch (operand) {
@ -303,16 +318,13 @@ bool get_operand_values(N64Recomp::Operand operand, const N64Recomp::Instruction
outw = get_fpr_double_context_offset(context.ft); outw = get_fpr_double_context_offset(context.ft);
break; break;
case Operand::FdU32L: case Operand::FdU32L:
out = SLJIT_MEM1(Registers::ctx); get_fpr_u32l_context_offset(context.fd, compiler, odd_float_address_register, out, outw);
outw = get_fpr_u32l_context_offset(context.fd);
break; break;
case Operand::FsU32L: case Operand::FsU32L:
out = SLJIT_MEM1(Registers::ctx); get_fpr_u32l_context_offset(context.fs, compiler, odd_float_address_register, out, outw);
outw = get_fpr_u32l_context_offset(context.fs);
break; break;
case Operand::FtU32L: case Operand::FtU32L:
out = SLJIT_MEM1(Registers::ctx); get_fpr_u32l_context_offset(context.ft, compiler, odd_float_address_register, out, outw);
outw = get_fpr_u32l_context_offset(context.ft);
break; break;
case Operand::FdU32H: case Operand::FdU32H:
assert(false); assert(false);
@ -389,16 +401,30 @@ void N64Recomp::LiveGenerator::process_binary_op(const BinaryOp& op, const Instr
if (outputs_to_zero(op.output, ctx)) { if (outputs_to_zero(op.output, ctx)) {
return; return;
} }
// Float u32l input operands are not allowed in a binary operation.
if (is_fpr_u32l(op.operands.operands[0]) || is_fpr_u32l(op.operands.operands[1])) {
assert(false);
errored = true;
return;
}
// A float u32l output operand is only allowed for lwc1, which has an op type of LW.
if (is_fpr_u32l(op.output) && op.type != BinaryOpType::LW) {
assert(false);
errored = true;
return;
}
sljit_sw dst; sljit_sw dst;
sljit_sw dstw; sljit_sw dstw;
sljit_sw src1; sljit_sw src1;
sljit_sw src1w; sljit_sw src1w;
sljit_sw src2; sljit_sw src2;
sljit_sw src2w; sljit_sw src2w;
bool output_good = get_operand_values(op.output, ctx, dst, dstw); bool output_good = get_operand_values(op.output, ctx, dst, dstw, compiler, Registers::arithmetic_temp2);
bool input0_good = get_operand_values(op.operands.operands[0], ctx, src1, src1w); bool input0_good = get_operand_values(op.operands.operands[0], ctx, src1, src1w, nullptr, 0);
bool input1_good = get_operand_values(op.operands.operands[1], ctx, src2, src2w); bool input1_good = get_operand_values(op.operands.operands[1], ctx, src2, src2w, nullptr, 0);
if (!output_good || !input0_good || !input1_good) { if (!output_good || !input0_good || !input1_good) {
assert(false); assert(false);
@ -866,12 +892,19 @@ void N64Recomp::LiveGenerator::process_unary_op(const UnaryOp& op, const Instruc
return; return;
} }
// A unary op may have a float u32l as the source or destination, but not both.
if (is_fpr_u32l(op.input) && is_fpr_u32l(op.output)) {
assert(false);
errored = true;
return;
}
sljit_sw dst; sljit_sw dst;
sljit_sw dstw; sljit_sw dstw;
sljit_sw src; sljit_sw src;
sljit_sw srcw; sljit_sw srcw;
bool output_good = get_operand_values(op.output, ctx, dst, dstw); bool output_good = get_operand_values(op.output, ctx, dst, dstw, compiler, Registers::arithmetic_temp3);
bool input_good = get_operand_values(op.input, ctx, src, srcw); bool input_good = get_operand_values(op.input, ctx, src, srcw, compiler, Registers::arithmetic_temp3);
if (!output_good || !input_good) { if (!output_good || !input_good) {
assert(false); assert(false);
@ -1089,7 +1122,13 @@ void N64Recomp::LiveGenerator::process_unary_op(const UnaryOp& op, const Instruc
emit_l_from_d_func(do_floor_l_d); emit_l_from_d_func(do_floor_l_d);
break; break;
case UnaryOpType::None: case UnaryOpType::None:
jit_op = SLJIT_MOV; // Only write 32 bits to the output is a fpr u32l operand.
if (is_fpr_u32l(op.output)) {
jit_op = SLJIT_MOV32;
}
else {
jit_op = SLJIT_MOV;
}
break; break;
case UnaryOpType::ToS32: case UnaryOpType::ToS32:
case UnaryOpType::ToInt32: case UnaryOpType::ToInt32:
@ -1128,7 +1167,7 @@ void N64Recomp::LiveGenerator::process_store_op(const StoreOp& op, const Instruc
sljit_sw srcw; sljit_sw srcw;
sljit_sw imm = (sljit_sw)(int16_t)ctx.imm16; 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, compiler, Registers::arithmetic_temp2);
// Only LO16 relocs are valid on stores. // Only LO16 relocs are valid on stores.
if (ctx.reloc_type != RelocType::R_MIPS_NONE && ctx.reloc_type != RelocType::R_MIPS_LO16) { if (ctx.reloc_type != RelocType::R_MIPS_NONE && ctx.reloc_type != RelocType::R_MIPS_LO16) {
@ -1456,6 +1495,13 @@ void N64Recomp::LiveGenerator::emit_branch_condition(const ConditionalBranchOp&
return; return;
} }
// Branch conditions do not allow float u32l operands.
if (is_fpr_u32l(op.operands.operands[0]) || is_fpr_u32l(op.operands.operands[1])) {
assert(false);
errored = true;
return;
}
sljit_s32 condition_type; sljit_s32 condition_type;
bool cmp_signed = op.operands.operand_operations[0] == UnaryOpType::ToS64; bool cmp_signed = op.operands.operand_operations[0] == UnaryOpType::ToS64;
// Comparisons need to be inverted to account for the fact that the generator is expected to generate a code block that only runs if // Comparisons need to be inverted to account for the fact that the generator is expected to generate a code block that only runs if
@ -1509,8 +1555,8 @@ void N64Recomp::LiveGenerator::emit_branch_condition(const ConditionalBranchOp&
sljit_sw src2; sljit_sw src2;
sljit_sw src2w; sljit_sw src2w;
get_operand_values(op.operands.operands[0], ctx, src1, src1w); get_operand_values(op.operands.operands[0], ctx, src1, src1w, nullptr, 0);
get_operand_values(op.operands.operands[1], ctx, src2, src2w); get_operand_values(op.operands.operands[1], ctx, src2, src2w, nullptr, 0);
// Relocations aren't valid on conditional branches. // Relocations aren't valid on conditional branches.
if(ctx.reloc_type != RelocType::R_MIPS_NONE) { if(ctx.reloc_type != RelocType::R_MIPS_NONE) {