mirror of
https://github.com/N64Recomp/N64Recomp.git
synced 2025-06-29 05:26:17 +00:00
Merge a8b247ed03
into 989a86b369
This commit is contained in:
commit
f73deacc3a
7 changed files with 92 additions and 46 deletions
|
@ -1435,6 +1435,27 @@ void N64Recomp::LiveGenerator::emit_jtbl_addend_declaration(const JumpTable& jtb
|
||||||
// Nothing to do here, the live recompiler performs a subtraction to get the switch's case.
|
// Nothing to do here, the live recompiler performs a subtraction to get the switch's case.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static sljit_s32 get_condition_type(bool cmp_signed, N64Recomp::BinaryOpType comparison) {
|
||||||
|
// Comparisons need to be inverted to account for the fact that the generator is expected to generate a code block that only runs if
|
||||||
|
// the condition is met, meaning the branch should be taken if the condition isn't met.
|
||||||
|
switch (comparison) {
|
||||||
|
case N64Recomp::BinaryOpType::Equal:
|
||||||
|
return SLJIT_NOT_EQUAL;
|
||||||
|
case N64Recomp::BinaryOpType::NotEqual:
|
||||||
|
return SLJIT_EQUAL;
|
||||||
|
case N64Recomp::BinaryOpType::GreaterEq:
|
||||||
|
return cmp_signed ? SLJIT_SIG_LESS : SLJIT_LESS;
|
||||||
|
case N64Recomp::BinaryOpType::Greater:
|
||||||
|
return cmp_signed ? SLJIT_SIG_LESS_EQUAL : SLJIT_LESS_EQUAL;
|
||||||
|
case N64Recomp::BinaryOpType::LessEq:
|
||||||
|
return cmp_signed ? SLJIT_SIG_GREATER : SLJIT_GREATER;
|
||||||
|
case N64Recomp::BinaryOpType::Less:
|
||||||
|
return cmp_signed ? SLJIT_SIG_GREATER_EQUAL : SLJIT_GREATER_EQUAL;
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void N64Recomp::LiveGenerator::emit_branch_condition(const ConditionalBranchOp& op, const InstructionContext& ctx) const {
|
void N64Recomp::LiveGenerator::emit_branch_condition(const ConditionalBranchOp& op, const InstructionContext& ctx) const {
|
||||||
// Make sure there's no pending jump.
|
// Make sure there's no pending jump.
|
||||||
if(context->cur_branch_jump != nullptr) {
|
if(context->cur_branch_jump != nullptr) {
|
||||||
|
@ -1456,53 +1477,12 @@ void N64Recomp::LiveGenerator::emit_branch_condition(const ConditionalBranchOp&
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
sljit_s32 condition_type = get_condition_type(cmp_signed, op.comparison);
|
||||||
// the condition is met, meaning the branch should be taken if the condition isn't met.
|
if (condition_type < 0) {
|
||||||
switch (op.comparison) {
|
assert(false && "Invalid branch condition comparison operation!");
|
||||||
case BinaryOpType::Equal:
|
errored = true;
|
||||||
condition_type = SLJIT_NOT_EQUAL;
|
return;
|
||||||
break;
|
|
||||||
case BinaryOpType::NotEqual:
|
|
||||||
condition_type = SLJIT_EQUAL;
|
|
||||||
break;
|
|
||||||
case BinaryOpType::GreaterEq:
|
|
||||||
if (cmp_signed) {
|
|
||||||
condition_type = SLJIT_SIG_LESS;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
condition_type = SLJIT_LESS;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case BinaryOpType::Greater:
|
|
||||||
if (cmp_signed) {
|
|
||||||
condition_type = SLJIT_SIG_LESS_EQUAL;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
condition_type = SLJIT_LESS_EQUAL;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case BinaryOpType::LessEq:
|
|
||||||
if (cmp_signed) {
|
|
||||||
condition_type = SLJIT_SIG_GREATER;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
condition_type = SLJIT_GREATER;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case BinaryOpType::Less:
|
|
||||||
if (cmp_signed) {
|
|
||||||
condition_type = SLJIT_SIG_GREATER_EQUAL;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
condition_type = SLJIT_GREATER_EQUAL;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
assert(false && "Invalid branch condition comparison operation!");
|
|
||||||
errored = true;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
sljit_sw src1;
|
sljit_sw src1;
|
||||||
sljit_sw src1w;
|
sljit_sw src1w;
|
||||||
|
@ -1876,6 +1856,29 @@ void N64Recomp::LiveGenerator::emit_do_break(uint32_t instr_vram) const {
|
||||||
sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS1V(32), SLJIT_IMM, sljit_sw(inputs.do_break));
|
sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS1V(32), SLJIT_IMM, sljit_sw(inputs.do_break));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void N64Recomp::LiveGenerator::emit_trap(const TrapOp& op, const InstructionContext& ctx, uint32_t instr_vram) const {
|
||||||
|
bool cmp_signed = op.operands.operand_operations[0] == UnaryOpType::ToS64;
|
||||||
|
sljit_s32 condition_type = get_condition_type(cmp_signed, op.comparison);
|
||||||
|
if (condition_type < 0) {
|
||||||
|
assert(false && "Invalid trap condition!");
|
||||||
|
errored = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sljit_sw src1;
|
||||||
|
sljit_sw src1w;
|
||||||
|
sljit_sw src2;
|
||||||
|
sljit_sw src2w;
|
||||||
|
get_operand_values(op.operands.operands[0], ctx, src1, src1w);
|
||||||
|
get_operand_values(op.operands.operands[1], ctx, src2, src2w);
|
||||||
|
|
||||||
|
// Emit a comparison evaluating the trap condition
|
||||||
|
struct sljit_jump *ignore_trap = sljit_emit_cmp(compiler, condition_type, src1, src1w, src2, src2w);
|
||||||
|
// Emit code to take the exception path
|
||||||
|
emit_do_break(instr_vram);
|
||||||
|
// Step over the exception path
|
||||||
|
sljit_set_label(ignore_trap, sljit_emit_label(compiler));
|
||||||
|
}
|
||||||
|
|
||||||
void N64Recomp::LiveGenerator::emit_pause_self() const {
|
void N64Recomp::LiveGenerator::emit_pause_self() const {
|
||||||
// Load rdram into R0.
|
// Load rdram into R0.
|
||||||
sljit_emit_op2(compiler, SLJIT_ADD, SLJIT_R0, 0, Registers::rdram, 0, SLJIT_IMM, rdram_offset);
|
sljit_emit_op2(compiler, SLJIT_ADD, SLJIT_R0, 0, Registers::rdram, 0, SLJIT_IMM, rdram_offset);
|
||||||
|
|
|
@ -58,6 +58,7 @@ namespace N64Recomp {
|
||||||
virtual void emit_muldiv(InstrId instr_id, int reg1, int reg2) const = 0;
|
virtual void emit_muldiv(InstrId instr_id, int reg1, int reg2) const = 0;
|
||||||
virtual void emit_syscall(uint32_t instr_vram) const = 0;
|
virtual void emit_syscall(uint32_t instr_vram) const = 0;
|
||||||
virtual void emit_do_break(uint32_t instr_vram) const = 0;
|
virtual void emit_do_break(uint32_t instr_vram) const = 0;
|
||||||
|
virtual void emit_trap(const TrapOp& op, const InstructionContext& ctx, uint32_t instr_vram) const = 0;
|
||||||
virtual void emit_pause_self() const = 0;
|
virtual void emit_pause_self() const = 0;
|
||||||
virtual void emit_trigger_event(uint32_t event_index) const = 0;
|
virtual void emit_trigger_event(uint32_t event_index) const = 0;
|
||||||
virtual void emit_comment(const std::string& comment) const = 0;
|
virtual void emit_comment(const std::string& comment) const = 0;
|
||||||
|
@ -95,6 +96,7 @@ namespace N64Recomp {
|
||||||
void emit_muldiv(InstrId instr_id, int reg1, int reg2) const final;
|
void emit_muldiv(InstrId instr_id, int reg1, int reg2) const final;
|
||||||
void emit_syscall(uint32_t instr_vram) const final;
|
void emit_syscall(uint32_t instr_vram) const final;
|
||||||
void emit_do_break(uint32_t instr_vram) const final;
|
void emit_do_break(uint32_t instr_vram) const final;
|
||||||
|
void emit_trap(const TrapOp& op, const InstructionContext& ctx, uint32_t instr_vram) const final;
|
||||||
void emit_pause_self() const final;
|
void emit_pause_self() const final;
|
||||||
void emit_trigger_event(uint32_t event_index) const final;
|
void emit_trigger_event(uint32_t event_index) const final;
|
||||||
void emit_comment(const std::string& comment) const final;
|
void emit_comment(const std::string& comment) const final;
|
||||||
|
|
|
@ -127,6 +127,7 @@ namespace N64Recomp {
|
||||||
void emit_muldiv(InstrId instr_id, int reg1, int reg2) const final;
|
void emit_muldiv(InstrId instr_id, int reg1, int reg2) const final;
|
||||||
void emit_syscall(uint32_t instr_vram) const final;
|
void emit_syscall(uint32_t instr_vram) const final;
|
||||||
void emit_do_break(uint32_t instr_vram) const final;
|
void emit_do_break(uint32_t instr_vram) const final;
|
||||||
|
void emit_trap(const TrapOp& op, const InstructionContext& ctx, uint32_t instr_vram) const final;
|
||||||
void emit_pause_self() const final;
|
void emit_pause_self() const final;
|
||||||
void emit_trigger_event(uint32_t event_index) const final;
|
void emit_trigger_event(uint32_t event_index) const final;
|
||||||
void emit_comment(const std::string& comment) const final;
|
void emit_comment(const std::string& comment) const final;
|
||||||
|
|
|
@ -204,10 +204,18 @@ namespace N64Recomp {
|
||||||
bool likely;
|
bool likely;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct TrapOp {
|
||||||
|
// The type of binary operation to use for this compare
|
||||||
|
BinaryOpType comparison;
|
||||||
|
// The input operands.
|
||||||
|
BinaryOperands operands;
|
||||||
|
};
|
||||||
|
|
||||||
extern const std::unordered_map<InstrId, UnaryOp> unary_ops;
|
extern const std::unordered_map<InstrId, UnaryOp> unary_ops;
|
||||||
extern const std::unordered_map<InstrId, BinaryOp> binary_ops;
|
extern const std::unordered_map<InstrId, BinaryOp> binary_ops;
|
||||||
extern const std::unordered_map<InstrId, ConditionalBranchOp> conditional_branch_ops;
|
extern const std::unordered_map<InstrId, ConditionalBranchOp> conditional_branch_ops;
|
||||||
extern const std::unordered_map<InstrId, StoreOp> store_ops;
|
extern const std::unordered_map<InstrId, StoreOp> store_ops;
|
||||||
|
extern const std::unordered_map<InstrId, TrapOp> trap_ops;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -550,6 +550,14 @@ void N64Recomp::CGenerator::emit_do_break(uint32_t instr_vram) const {
|
||||||
fmt::print(output_file, "do_break({});\n", instr_vram);
|
fmt::print(output_file, "do_break({});\n", instr_vram);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void N64Recomp::CGenerator::emit_trap(const TrapOp& op, const InstructionContext& ctx, uint32_t instr_vram) const {
|
||||||
|
// Thread local variables to prevent allocations when possible.
|
||||||
|
// TODO these thread locals probably don't actually help right now, so figure out a better way to prevent allocations.
|
||||||
|
thread_local std::string expr_string{};
|
||||||
|
get_binary_expr_string(op.comparison, op.operands, ctx, "", expr_string);
|
||||||
|
fmt::print(output_file, "if ({}) {{\n do_break({});\n}}\n", expr_string, instr_vram);
|
||||||
|
}
|
||||||
|
|
||||||
void N64Recomp::CGenerator::emit_pause_self() const {
|
void N64Recomp::CGenerator::emit_pause_self() const {
|
||||||
fmt::print(output_file, "pause_self(rdram);\n");
|
fmt::print(output_file, "pause_self(rdram);\n");
|
||||||
}
|
}
|
||||||
|
|
|
@ -177,4 +177,19 @@ namespace N64Recomp {
|
||||||
{ InstrId::cpu_sdc1, { StoreOpType::SDC1, Operand::FtU64 }},
|
{ InstrId::cpu_sdc1, { StoreOpType::SDC1, Operand::FtU64 }},
|
||||||
{ InstrId::cpu_swc1, { StoreOpType::SWC1, Operand::FtU32L }},
|
{ InstrId::cpu_swc1, { StoreOpType::SWC1, Operand::FtU32L }},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const std::unordered_map<InstrId, TrapOp> trap_ops {
|
||||||
|
{ InstrId::cpu_tge, { BinaryOpType::GreaterEq, {{ UnaryOpType::ToS64, UnaryOpType::ToS64 }, { Operand::Rs, Operand::Rt }}}},
|
||||||
|
{ InstrId::cpu_tgeu, { BinaryOpType::GreaterEq, {{ UnaryOpType::ToU64, UnaryOpType::ToU64 }, { Operand::Rs, Operand::Rt }}}},
|
||||||
|
{ InstrId::cpu_tlt, { BinaryOpType::Less, {{ UnaryOpType::ToS64, UnaryOpType::ToS64 }, { Operand::Rs, Operand::Rt }}}},
|
||||||
|
{ InstrId::cpu_tltu, { BinaryOpType::Less, {{ UnaryOpType::ToU64, UnaryOpType::ToU64 }, { Operand::Rs, Operand::Rt }}}},
|
||||||
|
{ InstrId::cpu_teq, { BinaryOpType::Equal, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::Rs, Operand::Rt }}}},
|
||||||
|
{ InstrId::cpu_tne, { BinaryOpType::NotEqual, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::Rs, Operand::Rt }}}},
|
||||||
|
{ InstrId::cpu_tgei, { BinaryOpType::GreaterEq, {{ UnaryOpType::ToS64, UnaryOpType::ToS64 }, { Operand::Rs, Operand::ImmS16 }}}},
|
||||||
|
{ InstrId::cpu_tgeiu, { BinaryOpType::GreaterEq, {{ UnaryOpType::ToU64, UnaryOpType::ToU64 }, { Operand::Rs, Operand::ImmS16 }}}},
|
||||||
|
{ InstrId::cpu_tlti, { BinaryOpType::Less, {{ UnaryOpType::ToS64, UnaryOpType::ToS64 }, { Operand::Rs, Operand::ImmS16 }}}},
|
||||||
|
{ InstrId::cpu_tltiu, { BinaryOpType::Less, {{ UnaryOpType::ToU64, UnaryOpType::ToU64 }, { Operand::Rs, Operand::ImmS16 }}}},
|
||||||
|
{ InstrId::cpu_teqi, { BinaryOpType::Equal, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::Rs, Operand::ImmS16 }}}},
|
||||||
|
{ InstrId::cpu_tnei, { BinaryOpType::NotEqual, {{ UnaryOpType::None, UnaryOpType::None }, { Operand::Rs, Operand::ImmS16 }}}},
|
||||||
|
};
|
||||||
}
|
}
|
|
@ -738,6 +738,15 @@ bool process_instruction(GeneratorType& generator, const N64Recomp::Context& con
|
||||||
handled = true;
|
handled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto find_trap_it = trap_ops.find(instr_id);
|
||||||
|
if (find_trap_it != trap_ops.end()) {
|
||||||
|
print_indent();
|
||||||
|
const TrapOp& op = find_trap_it->second;
|
||||||
|
|
||||||
|
generator.emit_trap(op, instruction_context, instr_vram);
|
||||||
|
handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (!handled) {
|
if (!handled) {
|
||||||
fmt::print(stderr, "Unhandled instruction: {}\n", instr.getOpcodeName());
|
fmt::print(stderr, "Unhandled instruction: {}\n", instr.getOpcodeName());
|
||||||
return false;
|
return false;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue