2019-01-22 15:17:25 +00:00
|
|
|
#include "build.h"
|
2015-07-13 21:56:42 +00:00
|
|
|
|
2015-07-25 06:16:53 +00:00
|
|
|
#if FEAT_SHREC == DYNAREC_JIT && HOST_CPU == CPU_X64
|
2019-04-15 16:02:34 +00:00
|
|
|
|
2019-06-10 11:57:10 +00:00
|
|
|
//#define CANONICAL_TEST
|
2019-01-14 20:15:36 +00:00
|
|
|
|
2020-03-22 09:08:05 +00:00
|
|
|
#include <xbyak/xbyak.h>
|
|
|
|
#include <xbyak/xbyak_util.h>
|
2021-01-29 10:46:05 +00:00
|
|
|
using namespace Xbyak::util;
|
2019-01-22 15:17:25 +00:00
|
|
|
|
|
|
|
#include "types.h"
|
2015-07-13 21:56:42 +00:00
|
|
|
#include "hw/sh4/sh4_opcode_list.h"
|
2019-01-12 22:48:48 +00:00
|
|
|
#include "hw/sh4/dyna/ngen.h"
|
2015-07-13 21:56:42 +00:00
|
|
|
#include "hw/sh4/modules/ccn.h"
|
2019-03-25 10:53:13 +00:00
|
|
|
#include "hw/sh4/modules/mmu.h"
|
2015-07-13 21:56:42 +00:00
|
|
|
#include "hw/sh4/sh4_interrupts.h"
|
|
|
|
|
|
|
|
#include "hw/sh4/sh4_core.h"
|
|
|
|
#include "hw/sh4/sh4_mem.h"
|
2019-01-12 22:48:48 +00:00
|
|
|
#include "x64_regalloc.h"
|
2021-01-26 12:16:48 +00:00
|
|
|
#include "xbyak_base.h"
|
2021-07-24 20:24:37 +00:00
|
|
|
#include "oslib/oslib.h"
|
2015-07-13 21:56:42 +00:00
|
|
|
|
|
|
|
struct DynaRBI : RuntimeBlockInfo
|
|
|
|
{
|
2021-03-13 11:44:59 +00:00
|
|
|
u32 Relink() override {
|
2015-07-13 21:56:42 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-03-13 11:44:59 +00:00
|
|
|
void Relocate(void* dst) override {
|
2015-07-13 21:56:42 +00:00
|
|
|
verify(false);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-01-29 10:46:05 +00:00
|
|
|
static void (*mainloop)();
|
2021-01-30 15:14:44 +00:00
|
|
|
static void (*handleException)();
|
2019-01-14 20:15:36 +00:00
|
|
|
|
2019-05-26 11:30:05 +00:00
|
|
|
u32 mem_writes, mem_reads;
|
|
|
|
|
2021-01-30 15:14:44 +00:00
|
|
|
static u64 jmp_rsp;
|
2021-01-29 10:46:05 +00:00
|
|
|
|
|
|
|
namespace MemSize {
|
|
|
|
enum {
|
|
|
|
S8,
|
|
|
|
S16,
|
|
|
|
S32,
|
|
|
|
S64,
|
|
|
|
Count
|
|
|
|
};
|
2019-01-14 20:15:36 +00:00
|
|
|
}
|
2021-01-29 10:46:05 +00:00
|
|
|
namespace MemOp {
|
|
|
|
enum {
|
|
|
|
R,
|
|
|
|
W,
|
|
|
|
Count
|
|
|
|
};
|
2019-01-14 20:15:36 +00:00
|
|
|
}
|
2021-01-29 10:46:05 +00:00
|
|
|
namespace MemType {
|
|
|
|
enum {
|
|
|
|
Fast,
|
2021-03-28 19:10:39 +00:00
|
|
|
StoreQueue,
|
2021-01-29 10:46:05 +00:00
|
|
|
Slow,
|
|
|
|
Count
|
|
|
|
};
|
2020-07-23 16:50:55 +00:00
|
|
|
}
|
2019-04-15 16:02:34 +00:00
|
|
|
|
2021-01-29 10:46:05 +00:00
|
|
|
static const void *MemHandlers[MemType::Count][MemSize::Count][MemOp::Count];
|
|
|
|
static const u8 *MemHandlerStart, *MemHandlerEnd;
|
2021-07-24 20:24:37 +00:00
|
|
|
static UnwindInfo unwinder;
|
2021-07-30 17:10:34 +00:00
|
|
|
#ifndef _WIN32
|
|
|
|
static float xmmSave[4];
|
|
|
|
#endif
|
2019-03-30 18:26:05 +00:00
|
|
|
|
2021-01-29 10:46:05 +00:00
|
|
|
void ngen_mainloop(void *)
|
2015-07-13 21:56:42 +00:00
|
|
|
{
|
2021-01-30 15:14:44 +00:00
|
|
|
verify(mainloop != nullptr);
|
2021-01-29 10:46:05 +00:00
|
|
|
try {
|
|
|
|
mainloop();
|
|
|
|
} catch (const SH4ThrownException&) {
|
|
|
|
ERROR_LOG(DYNAREC, "SH4ThrownException in mainloop");
|
2021-08-03 07:47:13 +00:00
|
|
|
throw FlycastException("Fatal: Unhandled SH4 exception");
|
2021-01-29 10:46:05 +00:00
|
|
|
}
|
2015-07-13 21:56:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ngen_init()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
RuntimeBlockInfo* ngen_AllocateBlock()
|
|
|
|
{
|
|
|
|
return new DynaRBI();
|
|
|
|
}
|
|
|
|
|
2019-01-11 22:52:20 +00:00
|
|
|
static void ngen_blockcheckfail(u32 pc) {
|
2019-04-19 09:45:05 +00:00
|
|
|
//printf("X64 JIT: SMC invalidation at %08X\n", pc);
|
2018-06-29 13:19:49 +00:00
|
|
|
rdv_BlockCheckFail(pc);
|
|
|
|
}
|
|
|
|
|
2019-04-29 16:23:00 +00:00
|
|
|
static void handle_sh4_exception(SH4ThrownException& ex, u32 pc)
|
|
|
|
{
|
|
|
|
if (pc & 1)
|
2019-04-15 16:02:34 +00:00
|
|
|
{
|
2019-04-29 16:23:00 +00:00
|
|
|
// Delay slot
|
|
|
|
AdjustDelaySlotException(ex);
|
|
|
|
pc--;
|
2019-03-25 10:53:13 +00:00
|
|
|
}
|
2019-04-29 16:23:00 +00:00
|
|
|
Do_Exception(pc, ex.expEvn, ex.callVect);
|
2021-10-07 14:18:32 +00:00
|
|
|
p_sh4rcb->cntx.cycle_counter += 4; // probably more is needed
|
2021-01-30 15:14:44 +00:00
|
|
|
handleException();
|
2019-03-25 10:53:13 +00:00
|
|
|
}
|
|
|
|
|
2019-05-16 14:57:35 +00:00
|
|
|
static void interpreter_fallback(u16 op, OpCallFP *oph, u32 pc)
|
2019-03-25 10:53:13 +00:00
|
|
|
{
|
|
|
|
try {
|
2019-04-18 12:15:01 +00:00
|
|
|
oph(op);
|
2019-03-25 10:53:13 +00:00
|
|
|
} catch (SH4ThrownException& ex) {
|
2019-04-29 16:23:00 +00:00
|
|
|
handle_sh4_exception(ex, pc);
|
2019-03-25 10:53:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-16 14:57:35 +00:00
|
|
|
static void do_sqw_mmu_no_ex(u32 addr, u32 pc)
|
2019-03-25 10:53:13 +00:00
|
|
|
{
|
|
|
|
try {
|
|
|
|
do_sqw_mmu(addr);
|
|
|
|
} catch (SH4ThrownException& ex) {
|
2019-04-29 16:23:00 +00:00
|
|
|
handle_sh4_exception(ex, pc);
|
2019-03-25 10:53:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-26 12:16:48 +00:00
|
|
|
const std::array<Xbyak::Reg32, 4> call_regs
|
|
|
|
#ifdef _WIN32
|
2021-01-29 10:46:05 +00:00
|
|
|
{ ecx, edx, r8d, r9d };
|
2021-01-26 12:16:48 +00:00
|
|
|
#else
|
2021-01-29 10:46:05 +00:00
|
|
|
{ edi, esi, edx, ecx };
|
2021-01-26 12:16:48 +00:00
|
|
|
#endif
|
|
|
|
const std::array<Xbyak::Reg64, 4> call_regs64
|
|
|
|
#ifdef _WIN32
|
2021-01-29 10:46:05 +00:00
|
|
|
{ rcx, rdx, r8, r9 };
|
2021-01-26 12:16:48 +00:00
|
|
|
#else
|
2021-01-29 10:46:05 +00:00
|
|
|
{ rdi, rsi, rdx, rcx };
|
2021-01-26 12:16:48 +00:00
|
|
|
#endif
|
2021-01-29 10:46:05 +00:00
|
|
|
const std::array<Xbyak::Xmm, 4> call_regsxmm { xmm0, xmm1, xmm2, xmm3 };
|
2021-01-26 12:16:48 +00:00
|
|
|
|
2021-11-20 11:29:07 +00:00
|
|
|
#ifdef _WIN32
|
|
|
|
constexpr u32 STACK_ALIGN = 0x28; // 32-byte shadow space + 8 byte alignment
|
|
|
|
#else
|
|
|
|
constexpr u32 STACK_ALIGN = 8;
|
|
|
|
#endif
|
|
|
|
|
2021-01-26 12:16:48 +00:00
|
|
|
class BlockCompiler : public BaseXbyakRec<BlockCompiler, true>
|
2019-01-12 22:48:48 +00:00
|
|
|
{
|
2015-07-13 21:56:42 +00:00
|
|
|
public:
|
2021-01-26 12:16:48 +00:00
|
|
|
using BaseCompiler = BaseXbyakRec<BlockCompiler, true>;
|
|
|
|
friend class BaseXbyakRec<BlockCompiler, true>;
|
2019-04-29 16:23:00 +00:00
|
|
|
|
2021-01-26 12:16:48 +00:00
|
|
|
BlockCompiler() : BaseCompiler(), regalloc(this) { }
|
|
|
|
BlockCompiler(u8 *code_ptr) : BaseCompiler(code_ptr), regalloc(this) { }
|
2015-07-13 21:56:42 +00:00
|
|
|
|
2019-09-29 16:18:46 +00:00
|
|
|
void compile(RuntimeBlockInfo* block, bool force_checks, bool reset, bool staging, bool optimise)
|
2019-01-12 22:48:48 +00:00
|
|
|
{
|
|
|
|
//printf("X86_64 compiling %08x to %p\n", block->addr, emit_GetCCPtr());
|
2019-04-29 16:23:00 +00:00
|
|
|
current_opid = -1;
|
2019-05-22 12:13:00 +00:00
|
|
|
|
2019-09-29 16:18:46 +00:00
|
|
|
CheckBlock(force_checks, block);
|
2018-06-29 13:19:49 +00:00
|
|
|
|
2021-11-20 11:29:07 +00:00
|
|
|
sub(rsp, STACK_ALIGN);
|
|
|
|
|
2019-03-25 10:53:13 +00:00
|
|
|
if (mmu_enabled() && block->has_fpu_op)
|
|
|
|
{
|
|
|
|
Xbyak::Label fpu_enabled;
|
|
|
|
mov(rax, (uintptr_t)&sr);
|
2019-04-18 12:15:01 +00:00
|
|
|
test(dword[rax], 0x8000); // test SR.FD bit
|
2019-03-25 10:53:13 +00:00
|
|
|
jz(fpu_enabled);
|
|
|
|
mov(call_regs[0], block->vaddr); // pc
|
|
|
|
mov(call_regs[1], 0x800); // event
|
|
|
|
mov(call_regs[2], 0x100); // vector
|
|
|
|
GenCall(Do_Exception);
|
|
|
|
jmp(exit_block, T_NEAR);
|
|
|
|
L(fpu_enabled);
|
|
|
|
}
|
2021-10-07 14:18:32 +00:00
|
|
|
mov(rax, (uintptr_t)&p_sh4rcb->cntx.cycle_counter);
|
2019-05-12 20:02:57 +00:00
|
|
|
sub(dword[rax], block->guest_cycles);
|
2021-10-07 14:18:32 +00:00
|
|
|
|
2019-04-29 16:23:00 +00:00
|
|
|
regalloc.DoAlloc(block);
|
2019-04-18 12:15:01 +00:00
|
|
|
|
2019-03-25 10:53:13 +00:00
|
|
|
for (current_opid = 0; current_opid < block->oplist.size(); current_opid++)
|
2019-01-12 22:48:48 +00:00
|
|
|
{
|
2019-03-25 10:53:13 +00:00
|
|
|
shil_opcode& op = block->oplist[current_opid];
|
2019-01-12 22:48:48 +00:00
|
|
|
|
2019-03-25 10:53:13 +00:00
|
|
|
regalloc.OpBegin(&op, current_opid);
|
2019-01-12 22:48:48 +00:00
|
|
|
|
2019-05-10 20:31:59 +00:00
|
|
|
switch (op.op)
|
|
|
|
{
|
2015-07-13 21:56:42 +00:00
|
|
|
case shop_ifb:
|
2019-05-10 20:31:59 +00:00
|
|
|
if (mmu_enabled())
|
2015-07-13 21:56:42 +00:00
|
|
|
{
|
2019-05-10 20:31:59 +00:00
|
|
|
mov(call_regs64[1], reinterpret_cast<uintptr_t>(*OpDesc[op.rs3._imm]->oph)); // op handler
|
|
|
|
mov(call_regs[2], block->vaddr + op.guest_offs - (op.delay_slot ? 1 : 0)); // pc
|
|
|
|
}
|
2019-04-18 12:15:01 +00:00
|
|
|
|
2019-05-10 20:31:59 +00:00
|
|
|
if (op.rs1._imm)
|
|
|
|
{
|
|
|
|
mov(rax, (size_t)&next_pc);
|
|
|
|
mov(dword[rax], op.rs2._imm);
|
|
|
|
}
|
2019-03-25 10:53:13 +00:00
|
|
|
|
2019-05-10 20:31:59 +00:00
|
|
|
mov(call_regs[0], op.rs3._imm);
|
2019-03-25 10:53:13 +00:00
|
|
|
|
2019-05-10 20:31:59 +00:00
|
|
|
if (!mmu_enabled())
|
|
|
|
GenCall(OpDesc[op.rs3._imm]->oph);
|
|
|
|
else
|
|
|
|
GenCall(interpreter_fallback);
|
2019-05-16 14:57:35 +00:00
|
|
|
|
2015-07-13 21:56:42 +00:00
|
|
|
break;
|
|
|
|
|
2015-07-14 18:17:45 +00:00
|
|
|
case shop_mov64:
|
|
|
|
{
|
2019-01-14 20:15:36 +00:00
|
|
|
verify(op.rd.is_r64());
|
|
|
|
verify(op.rs1.is_r64());
|
2015-07-14 18:17:45 +00:00
|
|
|
|
2019-01-14 20:15:36 +00:00
|
|
|
mov(rax, (uintptr_t)op.rs1.reg_ptr());
|
|
|
|
mov(rax, qword[rax]);
|
2019-01-12 22:48:48 +00:00
|
|
|
mov(rcx, (uintptr_t)op.rd.reg_ptr());
|
|
|
|
mov(qword[rcx], rax);
|
2015-07-14 18:17:45 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case shop_readm:
|
2019-04-29 16:23:00 +00:00
|
|
|
if (!GenReadMemImmediate(op, block))
|
2019-01-16 12:39:52 +00:00
|
|
|
{
|
|
|
|
// Not an immediate address
|
|
|
|
shil_param_to_host_reg(op.rs1, call_regs[0]);
|
|
|
|
if (!op.rs3.is_null())
|
|
|
|
{
|
|
|
|
if (op.rs3.is_imm())
|
|
|
|
add(call_regs[0], op.rs3._imm);
|
2019-03-25 10:53:13 +00:00
|
|
|
else if (regalloc.IsAllocg(op.rs3))
|
2019-01-18 16:02:50 +00:00
|
|
|
add(call_regs[0], regalloc.MapRegister(op.rs3));
|
2019-03-25 10:53:13 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
mov(rax, (uintptr_t)op.rs3.reg_ptr());
|
|
|
|
add(call_regs[0], dword[rax]);
|
|
|
|
}
|
2019-01-16 12:39:52 +00:00
|
|
|
}
|
2021-05-17 09:59:34 +00:00
|
|
|
genMmuLookup(block, op, 0);
|
2019-03-25 10:53:13 +00:00
|
|
|
|
2021-05-17 09:59:34 +00:00
|
|
|
int size = op.flags & 0x7f;
|
2021-01-29 10:46:05 +00:00
|
|
|
size = size == 1 ? MemSize::S8 : size == 2 ? MemSize::S16 : size == 4 ? MemSize::S32 : MemSize::S64;
|
|
|
|
GenCall((void (*)())MemHandlers[optimise ? MemType::Fast : MemType::Slow][size][MemOp::R], mmu_enabled());
|
|
|
|
|
|
|
|
if (size != MemSize::S64)
|
2019-06-10 12:48:54 +00:00
|
|
|
host_reg_to_shil_param(op.rd, eax);
|
2019-01-16 12:39:52 +00:00
|
|
|
else {
|
2021-01-26 12:16:48 +00:00
|
|
|
mov(rcx, (uintptr_t)op.rd.reg_ptr());
|
|
|
|
mov(qword[rcx], rax);
|
2019-01-16 12:39:52 +00:00
|
|
|
}
|
2019-01-12 22:48:48 +00:00
|
|
|
}
|
2019-04-29 16:23:00 +00:00
|
|
|
break;
|
2015-07-13 21:56:42 +00:00
|
|
|
|
2015-07-14 18:17:45 +00:00
|
|
|
case shop_writem:
|
|
|
|
{
|
2019-06-10 11:57:10 +00:00
|
|
|
if (!GenWriteMemImmediate(op, block))
|
2019-01-12 22:48:48 +00:00
|
|
|
{
|
2019-06-10 11:57:10 +00:00
|
|
|
shil_param_to_host_reg(op.rs1, call_regs[0]);
|
|
|
|
if (!op.rs3.is_null())
|
2019-03-25 10:53:13 +00:00
|
|
|
{
|
2019-06-10 11:57:10 +00:00
|
|
|
if (op.rs3.is_imm())
|
|
|
|
add(call_regs[0], op.rs3._imm);
|
|
|
|
else if (regalloc.IsAllocg(op.rs3))
|
|
|
|
add(call_regs[0], regalloc.MapRegister(op.rs3));
|
|
|
|
else
|
|
|
|
{
|
|
|
|
mov(rax, (uintptr_t)op.rs3.reg_ptr());
|
|
|
|
add(call_regs[0], dword[rax]);
|
|
|
|
}
|
2019-03-25 10:53:13 +00:00
|
|
|
}
|
2021-05-17 09:59:34 +00:00
|
|
|
genMmuLookup(block, op, 1);
|
2015-07-14 18:17:45 +00:00
|
|
|
|
2019-06-10 11:57:10 +00:00
|
|
|
u32 size = op.flags & 0x7f;
|
|
|
|
if (size != 8)
|
|
|
|
shil_param_to_host_reg(op.rs2, call_regs[1]);
|
|
|
|
else {
|
2021-01-26 12:16:48 +00:00
|
|
|
mov(rax, (uintptr_t)op.rs2.reg_ptr());
|
|
|
|
mov(call_regs64[1], qword[rax]);
|
2019-03-25 10:53:13 +00:00
|
|
|
}
|
2021-01-29 10:46:05 +00:00
|
|
|
|
|
|
|
size = size == 1 ? MemSize::S8 : size == 2 ? MemSize::S16 : size == 4 ? MemSize::S32 : MemSize::S64;
|
|
|
|
GenCall((void (*)())MemHandlers[optimise ? MemType::Fast : MemType::Slow][size][MemOp::W], mmu_enabled());
|
2019-01-12 22:48:48 +00:00
|
|
|
}
|
2015-07-13 21:56:42 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2021-03-07 15:59:17 +00:00
|
|
|
case shop_jcond:
|
|
|
|
case shop_jdyn:
|
|
|
|
case shop_mov32:
|
|
|
|
genBaseOpcode(op);
|
|
|
|
break;
|
|
|
|
|
2019-01-14 20:15:36 +00:00
|
|
|
#ifndef CANONICAL_TEST
|
2019-01-12 22:48:48 +00:00
|
|
|
case shop_sync_sr:
|
2019-01-14 20:15:36 +00:00
|
|
|
GenCall(UpdateSR);
|
2019-01-12 22:48:48 +00:00
|
|
|
break;
|
|
|
|
case shop_sync_fpscr:
|
2019-01-14 20:15:36 +00:00
|
|
|
GenCall(UpdateFPSCR);
|
2019-01-12 22:48:48 +00:00
|
|
|
break;
|
2019-01-14 20:15:36 +00:00
|
|
|
|
2019-05-26 11:30:05 +00:00
|
|
|
case shop_negc:
|
|
|
|
{
|
2019-06-10 11:57:10 +00:00
|
|
|
Xbyak::Reg32 rs2;
|
|
|
|
if (op.rs2.is_reg())
|
|
|
|
{
|
|
|
|
rs2 = regalloc.MapRegister(op.rs2);
|
|
|
|
if (regalloc.mapg(op.rd) == regalloc.mapg(op.rs2))
|
|
|
|
{
|
|
|
|
mov(ecx, rs2);
|
|
|
|
rs2 = ecx;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Xbyak::Reg32 rd = regalloc.MapRegister(op.rd);
|
|
|
|
if (op.rs1.is_imm())
|
|
|
|
mov(rd, op.rs1.imm_value());
|
|
|
|
else if (regalloc.mapg(op.rd) != regalloc.mapg(op.rs1))
|
|
|
|
mov(rd, regalloc.MapRegister(op.rs1));
|
|
|
|
Xbyak::Reg64 rd64 = rd.cvt64();
|
2019-05-26 11:30:05 +00:00
|
|
|
neg(rd64);
|
2019-06-10 11:57:10 +00:00
|
|
|
if (op.rs2.is_imm())
|
|
|
|
sub(rd64, op.rs2.imm_value());
|
|
|
|
else
|
|
|
|
sub(rd64, rs2.cvt64());
|
2019-05-26 11:30:05 +00:00
|
|
|
Xbyak::Reg64 rd2_64 = regalloc.MapRegister(op.rd2).cvt64();
|
|
|
|
mov(rd2_64, rd64);
|
|
|
|
shr(rd2_64, 63);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2021-01-31 16:31:47 +00:00
|
|
|
case shop_mul_s64:
|
|
|
|
movsxd(rax, regalloc.MapRegister(op.rs1));
|
|
|
|
if (op.rs2.is_reg())
|
|
|
|
movsxd(rcx, regalloc.MapRegister(op.rs2));
|
|
|
|
else
|
|
|
|
mov(rcx, (s64)(s32)op.rs2._imm);
|
|
|
|
mul(rcx);
|
|
|
|
mov(regalloc.MapRegister(op.rd), eax);
|
|
|
|
shr(rax, 32);
|
|
|
|
mov(regalloc.MapRegister(op.rd2), eax);
|
|
|
|
break;
|
|
|
|
|
2019-01-12 22:48:48 +00:00
|
|
|
case shop_pref:
|
2019-06-10 11:57:10 +00:00
|
|
|
{
|
2021-01-29 10:46:05 +00:00
|
|
|
Xbyak::Label no_sqw;
|
|
|
|
if (op.rs1.is_imm())
|
2019-06-10 11:57:10 +00:00
|
|
|
{
|
2021-01-29 10:46:05 +00:00
|
|
|
// this test shouldn't be necessary
|
|
|
|
if ((op.rs1._imm & 0xFC000000) != 0xE0000000)
|
|
|
|
break;
|
|
|
|
|
2019-06-10 11:57:10 +00:00
|
|
|
mov(call_regs[0], op.rs1._imm);
|
2021-01-29 10:46:05 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Xbyak::Reg32 rn;
|
|
|
|
if (regalloc.IsAllocg(op.rs1))
|
2019-06-10 11:57:10 +00:00
|
|
|
{
|
2021-01-29 10:46:05 +00:00
|
|
|
rn = regalloc.MapRegister(op.rs1);
|
2019-06-10 11:57:10 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-01-29 10:46:05 +00:00
|
|
|
mov(rax, (uintptr_t)op.rs1.reg_ptr());
|
|
|
|
mov(eax, dword[rax]);
|
|
|
|
rn = eax;
|
2019-06-10 11:57:10 +00:00
|
|
|
}
|
2021-01-29 10:46:05 +00:00
|
|
|
mov(ecx, rn);
|
|
|
|
shr(ecx, 26);
|
|
|
|
cmp(ecx, 0x38);
|
|
|
|
jne(no_sqw);
|
2019-03-25 10:53:13 +00:00
|
|
|
|
2021-01-29 10:46:05 +00:00
|
|
|
mov(call_regs[0], rn);
|
|
|
|
}
|
2019-03-25 10:53:13 +00:00
|
|
|
if (mmu_enabled())
|
|
|
|
{
|
|
|
|
mov(call_regs[1], block->vaddr + op.guest_offs - (op.delay_slot ? 1 : 0)); // pc
|
|
|
|
|
|
|
|
GenCall(do_sqw_mmu_no_ex);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (CCN_MMUCR.AT == 1)
|
|
|
|
{
|
|
|
|
GenCall(do_sqw_mmu);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
mov(call_regs64[1], (uintptr_t)sq_both);
|
2021-01-29 10:46:05 +00:00
|
|
|
mov(rax, (size_t)&do_sqw_nommu);
|
|
|
|
saveXmmRegisters();
|
|
|
|
call(qword[rax]);
|
|
|
|
restoreXmmRegisters();
|
2019-03-25 10:53:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
L(no_sqw);
|
|
|
|
}
|
2019-01-12 22:48:48 +00:00
|
|
|
break;
|
2019-03-25 10:53:13 +00:00
|
|
|
|
2019-01-12 22:48:48 +00:00
|
|
|
case shop_frswap:
|
2019-01-14 20:15:36 +00:00
|
|
|
mov(rax, (uintptr_t)op.rs1.reg_ptr());
|
|
|
|
mov(rcx, (uintptr_t)op.rd.reg_ptr());
|
2021-01-29 10:46:05 +00:00
|
|
|
if (cpu.has(Cpu::tAVX512F))
|
2019-01-18 16:02:50 +00:00
|
|
|
{
|
|
|
|
vmovaps(zmm0, zword[rax]);
|
|
|
|
vmovaps(zmm1, zword[rcx]);
|
|
|
|
vmovaps(zword[rax], zmm1);
|
|
|
|
vmovaps(zword[rcx], zmm0);
|
|
|
|
}
|
2021-01-29 10:46:05 +00:00
|
|
|
else if (cpu.has(Cpu::tAVX))
|
2019-01-18 16:02:50 +00:00
|
|
|
{
|
|
|
|
vmovaps(ymm0, yword[rax]);
|
|
|
|
vmovaps(ymm1, yword[rcx]);
|
|
|
|
vmovaps(yword[rax], ymm1);
|
|
|
|
vmovaps(yword[rcx], ymm0);
|
|
|
|
|
|
|
|
vmovaps(ymm0, yword[rax + 32]);
|
|
|
|
vmovaps(ymm1, yword[rcx + 32]);
|
|
|
|
vmovaps(yword[rax + 32], ymm1);
|
|
|
|
vmovaps(yword[rcx + 32], ymm0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
|
|
{
|
|
|
|
movaps(xmm0, xword[rax + (i * 16)]);
|
|
|
|
movaps(xmm1, xword[rcx + (i * 16)]);
|
|
|
|
movaps(xword[rax + (i * 16)], xmm1);
|
|
|
|
movaps(xword[rcx + (i * 16)], xmm0);
|
|
|
|
}
|
|
|
|
}
|
2019-01-12 22:48:48 +00:00
|
|
|
break;
|
2019-01-14 20:15:36 +00:00
|
|
|
#endif
|
2019-01-12 22:48:48 +00:00
|
|
|
|
2015-07-13 21:56:42 +00:00
|
|
|
default:
|
2021-01-26 12:16:48 +00:00
|
|
|
#ifndef CANONICAL_TEST
|
|
|
|
if (!genBaseOpcode(op))
|
|
|
|
#endif
|
|
|
|
shil_chf[op.op](&op);
|
2015-07-13 21:56:42 +00:00
|
|
|
break;
|
|
|
|
}
|
2019-01-12 22:48:48 +00:00
|
|
|
regalloc.OpEnd(&op);
|
2015-07-13 21:56:42 +00:00
|
|
|
}
|
2019-04-18 12:15:01 +00:00
|
|
|
regalloc.Cleanup();
|
2019-04-29 16:23:00 +00:00
|
|
|
current_opid = -1;
|
2015-07-13 21:56:42 +00:00
|
|
|
|
2015-08-05 03:00:01 +00:00
|
|
|
mov(rax, (size_t)&next_pc);
|
|
|
|
|
|
|
|
switch (block->BlockType) {
|
|
|
|
|
|
|
|
case BET_StaticJump:
|
|
|
|
case BET_StaticCall:
|
|
|
|
//next_pc = block->BranchBlock;
|
|
|
|
mov(dword[rax], block->BranchBlock);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case BET_Cond_0:
|
|
|
|
case BET_Cond_1:
|
|
|
|
{
|
|
|
|
//next_pc = next_pc_value;
|
|
|
|
//if (*jdyn == 0)
|
|
|
|
//next_pc = branch_pc_value;
|
|
|
|
|
|
|
|
mov(dword[rax], block->NextBlock);
|
|
|
|
|
|
|
|
if (block->has_jcond)
|
|
|
|
mov(rdx, (size_t)&Sh4cntx.jdyn);
|
|
|
|
else
|
|
|
|
mov(rdx, (size_t)&sr.T);
|
|
|
|
|
|
|
|
cmp(dword[rdx], block->BlockType & 1);
|
|
|
|
Xbyak::Label branch_not_taken;
|
|
|
|
|
|
|
|
jne(branch_not_taken, T_SHORT);
|
|
|
|
mov(dword[rax], block->BranchBlock);
|
|
|
|
L(branch_not_taken);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case BET_DynamicJump:
|
|
|
|
case BET_DynamicCall:
|
|
|
|
case BET_DynamicRet:
|
|
|
|
//next_pc = *jdyn;
|
|
|
|
mov(rdx, (size_t)&Sh4cntx.jdyn);
|
|
|
|
mov(edx, dword[rdx]);
|
|
|
|
mov(dword[rax], edx);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case BET_DynamicIntr:
|
|
|
|
case BET_StaticIntr:
|
|
|
|
if (block->BlockType == BET_DynamicIntr) {
|
|
|
|
//next_pc = *jdyn;
|
|
|
|
mov(rdx, (size_t)&Sh4cntx.jdyn);
|
|
|
|
mov(edx, dword[rdx]);
|
|
|
|
mov(dword[rax], edx);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
//next_pc = next_pc_value;
|
|
|
|
mov(dword[rax], block->NextBlock);
|
|
|
|
}
|
|
|
|
|
2019-01-14 20:15:36 +00:00
|
|
|
GenCall(UpdateINTC);
|
2015-08-05 03:00:01 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
die("Invalid block end type");
|
|
|
|
}
|
|
|
|
|
2019-03-25 10:53:13 +00:00
|
|
|
L(exit_block);
|
2021-11-20 11:29:07 +00:00
|
|
|
add(rsp, STACK_ALIGN);
|
2015-07-13 21:56:42 +00:00
|
|
|
ret();
|
|
|
|
|
|
|
|
ready();
|
|
|
|
|
|
|
|
block->code = (DynarecCodeEntryPtr)getCode();
|
2019-03-25 10:53:13 +00:00
|
|
|
block->host_code_size = getSize();
|
2015-07-13 21:56:42 +00:00
|
|
|
|
|
|
|
emit_Skip(getSize());
|
|
|
|
}
|
|
|
|
|
2019-01-14 20:15:36 +00:00
|
|
|
void ngen_CC_Start(const shil_opcode& op)
|
2015-07-14 18:17:45 +00:00
|
|
|
{
|
|
|
|
CC_pars.clear();
|
|
|
|
}
|
2015-07-13 21:56:42 +00:00
|
|
|
|
2019-01-14 20:15:36 +00:00
|
|
|
void ngen_CC_param(const shil_opcode& op, const shil_param& prm, CanonicalParamType tp) {
|
2015-07-14 18:17:45 +00:00
|
|
|
switch (tp)
|
|
|
|
{
|
2015-07-13 21:56:42 +00:00
|
|
|
|
2015-07-14 18:17:45 +00:00
|
|
|
case CPT_u32:
|
|
|
|
case CPT_ptr:
|
|
|
|
case CPT_f32:
|
2015-07-13 21:56:42 +00:00
|
|
|
{
|
2015-07-14 18:17:45 +00:00
|
|
|
CC_PS t = { tp, &prm };
|
|
|
|
CC_pars.push_back(t);
|
2015-07-13 21:56:42 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
2019-01-14 20:15:36 +00:00
|
|
|
// store from EAX
|
2015-07-14 18:17:45 +00:00
|
|
|
case CPT_u64rvL:
|
|
|
|
case CPT_u32rv:
|
|
|
|
mov(rcx, rax);
|
2019-01-12 22:48:48 +00:00
|
|
|
host_reg_to_shil_param(prm, ecx);
|
2015-07-14 18:17:45 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case CPT_u64rvH:
|
2019-01-14 20:15:36 +00:00
|
|
|
// assuming CPT_u64rvL has just been called
|
2015-07-14 18:17:45 +00:00
|
|
|
shr(rcx, 32);
|
2019-01-12 22:48:48 +00:00
|
|
|
host_reg_to_shil_param(prm, ecx);
|
2015-07-14 18:17:45 +00:00
|
|
|
break;
|
|
|
|
|
2019-01-14 20:15:36 +00:00
|
|
|
// store from xmm0
|
2015-07-14 18:17:45 +00:00
|
|
|
case CPT_f32rv:
|
2019-01-12 22:48:48 +00:00
|
|
|
host_reg_to_shil_param(prm, xmm0);
|
2015-07-14 18:17:45 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-14 20:15:36 +00:00
|
|
|
void ngen_CC_Call(const shil_opcode& op, void* function)
|
2015-07-14 18:17:45 +00:00
|
|
|
{
|
|
|
|
int regused = 0;
|
|
|
|
int xmmused = 0;
|
|
|
|
|
|
|
|
for (int i = CC_pars.size(); i-- > 0;)
|
2015-07-13 21:56:42 +00:00
|
|
|
{
|
2015-07-14 18:17:45 +00:00
|
|
|
verify(xmmused < 4 && regused < 4);
|
2019-01-14 20:15:36 +00:00
|
|
|
const shil_param& prm = *CC_pars[i].prm;
|
2015-07-14 18:17:45 +00:00
|
|
|
switch (CC_pars[i].type) {
|
|
|
|
//push the contents
|
|
|
|
|
|
|
|
case CPT_u32:
|
2019-01-12 22:48:48 +00:00
|
|
|
shil_param_to_host_reg(prm, call_regs[regused++]);
|
2015-07-14 18:17:45 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case CPT_f32:
|
2019-01-12 22:48:48 +00:00
|
|
|
shil_param_to_host_reg(prm, call_regsxmm[xmmused++]);
|
2015-07-14 18:17:45 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
//push the ptr itself
|
|
|
|
case CPT_ptr:
|
|
|
|
verify(prm.is_reg());
|
|
|
|
|
|
|
|
mov(call_regs64[regused++], (size_t)prm.reg_ptr());
|
|
|
|
|
|
|
|
break;
|
2019-01-16 12:39:52 +00:00
|
|
|
default:
|
|
|
|
// Other cases handled in ngen_CC_param
|
|
|
|
break;
|
2015-07-13 21:56:42 +00:00
|
|
|
}
|
|
|
|
}
|
2019-01-14 20:15:36 +00:00
|
|
|
GenCall((void (*)())function);
|
2015-07-14 18:17:45 +00:00
|
|
|
}
|
2015-07-13 21:56:42 +00:00
|
|
|
|
2019-01-12 22:48:48 +00:00
|
|
|
void RegPreload(u32 reg, Xbyak::Operand::Code nreg)
|
|
|
|
{
|
|
|
|
mov(rax, (size_t)GetRegPtr(reg));
|
|
|
|
mov(Xbyak::Reg32(nreg), dword[rax]);
|
|
|
|
}
|
|
|
|
void RegWriteback(u32 reg, Xbyak::Operand::Code nreg)
|
|
|
|
{
|
|
|
|
mov(rax, (size_t)GetRegPtr(reg));
|
|
|
|
mov(dword[rax], Xbyak::Reg32(nreg));
|
|
|
|
}
|
|
|
|
void RegPreload_FPU(u32 reg, s8 nreg)
|
|
|
|
{
|
|
|
|
mov(rax, (size_t)GetRegPtr(reg));
|
|
|
|
movss(Xbyak::Xmm(nreg), dword[rax]);
|
|
|
|
}
|
|
|
|
void RegWriteback_FPU(u32 reg, s8 nreg)
|
|
|
|
{
|
|
|
|
mov(rax, (size_t)GetRegPtr(reg));
|
|
|
|
movss(dword[rax], Xbyak::Xmm(nreg));
|
|
|
|
}
|
|
|
|
|
2021-01-29 10:46:05 +00:00
|
|
|
void genMainloop()
|
|
|
|
{
|
2021-07-24 20:24:37 +00:00
|
|
|
unwinder.start((void *)getCurr());
|
|
|
|
|
2021-01-29 10:46:05 +00:00
|
|
|
push(rbx);
|
2021-07-24 20:24:37 +00:00
|
|
|
unwinder.pushReg(getSize(), Xbyak::Operand::RBX);
|
2021-01-29 10:46:05 +00:00
|
|
|
push(rbp);
|
2021-07-24 20:24:37 +00:00
|
|
|
unwinder.pushReg(getSize(), Xbyak::Operand::RBP);
|
2021-01-29 10:46:05 +00:00
|
|
|
#ifdef _WIN32
|
|
|
|
push(rdi);
|
2021-07-24 20:24:37 +00:00
|
|
|
unwinder.pushReg(getSize(), Xbyak::Operand::RDI);
|
2021-01-29 10:46:05 +00:00
|
|
|
push(rsi);
|
2021-07-24 20:24:37 +00:00
|
|
|
unwinder.pushReg(getSize(), Xbyak::Operand::RSI);
|
2021-01-29 10:46:05 +00:00
|
|
|
#endif
|
|
|
|
push(r12);
|
2021-07-24 20:24:37 +00:00
|
|
|
unwinder.pushReg(getSize(), Xbyak::Operand::R12);
|
2021-01-29 10:46:05 +00:00
|
|
|
push(r13);
|
2021-07-24 20:24:37 +00:00
|
|
|
unwinder.pushReg(getSize(), Xbyak::Operand::R13);
|
2021-01-29 10:46:05 +00:00
|
|
|
push(r14);
|
2021-07-24 20:24:37 +00:00
|
|
|
unwinder.pushReg(getSize(), Xbyak::Operand::R14);
|
2021-01-29 10:46:05 +00:00
|
|
|
push(r15);
|
2021-07-24 20:24:37 +00:00
|
|
|
unwinder.pushReg(getSize(), Xbyak::Operand::R15);
|
2021-11-20 11:29:07 +00:00
|
|
|
sub(rsp, STACK_ALIGN);
|
|
|
|
unwinder.allocStack(getSize(), STACK_ALIGN);
|
2021-07-24 20:24:37 +00:00
|
|
|
unwinder.endProlog(getSize());
|
2021-01-29 10:46:05 +00:00
|
|
|
|
2021-01-30 15:14:44 +00:00
|
|
|
mov(qword[rip + &jmp_rsp], rsp);
|
2021-01-29 10:46:05 +00:00
|
|
|
|
|
|
|
//run_loop:
|
|
|
|
Xbyak::Label run_loop;
|
|
|
|
L(run_loop);
|
|
|
|
Xbyak::Label end_run_loop;
|
|
|
|
mov(rax, (size_t)&p_sh4rcb->cntx.CpuRunning);
|
|
|
|
mov(edx, dword[rax]);
|
|
|
|
|
|
|
|
test(edx, edx);
|
|
|
|
je(end_run_loop);
|
|
|
|
|
|
|
|
//slice_loop:
|
|
|
|
Xbyak::Label slice_loop;
|
|
|
|
L(slice_loop);
|
|
|
|
mov(rax, (size_t)&p_sh4rcb->cntx.pc);
|
|
|
|
mov(call_regs[0], dword[rax]);
|
|
|
|
call(bm_GetCodeByVAddr);
|
|
|
|
call(rax);
|
2021-10-07 14:18:32 +00:00
|
|
|
mov(rax, (uintptr_t)&p_sh4rcb->cntx.cycle_counter);
|
|
|
|
mov(ecx, dword[rax]);
|
2021-01-29 10:46:05 +00:00
|
|
|
test(ecx, ecx);
|
|
|
|
jg(slice_loop);
|
|
|
|
|
|
|
|
add(ecx, SH4_TIMESLICE);
|
2021-10-07 14:18:32 +00:00
|
|
|
mov(dword[rax], ecx);
|
2021-01-29 10:46:05 +00:00
|
|
|
call(UpdateSystem_INTC);
|
|
|
|
jmp(run_loop);
|
|
|
|
|
|
|
|
//end_run_loop:
|
|
|
|
L(end_run_loop);
|
2021-11-20 11:29:07 +00:00
|
|
|
add(rsp, STACK_ALIGN);
|
2021-01-29 10:46:05 +00:00
|
|
|
pop(r15);
|
|
|
|
pop(r14);
|
|
|
|
pop(r13);
|
|
|
|
pop(r12);
|
|
|
|
#ifdef _WIN32
|
|
|
|
pop(rsi);
|
|
|
|
pop(rdi);
|
|
|
|
#endif
|
|
|
|
pop(rbp);
|
|
|
|
pop(rbx);
|
|
|
|
ret();
|
2021-07-24 20:24:37 +00:00
|
|
|
size_t unwindSize = unwinder.end(getSize());
|
|
|
|
setSize(getSize() + unwindSize);
|
|
|
|
|
|
|
|
unwinder.start((void *)getCurr());
|
|
|
|
size_t startOffset = getSize();
|
2021-07-30 17:10:34 +00:00
|
|
|
#ifdef _WIN32
|
|
|
|
// 32-byte shadow space + 8 for stack 16-byte alignment
|
|
|
|
unwinder.allocStack(0, 40);
|
|
|
|
#else
|
|
|
|
// stack 16-byte alignment
|
|
|
|
unwinder.allocStack(0, 8);
|
|
|
|
#endif
|
2021-07-24 20:24:37 +00:00
|
|
|
unwinder.endProlog(0);
|
2021-01-29 10:46:05 +00:00
|
|
|
|
2021-01-30 15:14:44 +00:00
|
|
|
//handleException:
|
|
|
|
Xbyak::Label handleExceptionLabel;
|
|
|
|
L(handleExceptionLabel);
|
|
|
|
mov(rsp, qword[rip + &jmp_rsp]);
|
|
|
|
jmp(run_loop);
|
|
|
|
|
2021-01-29 10:46:05 +00:00
|
|
|
genMemHandlers();
|
|
|
|
|
2021-07-24 20:24:37 +00:00
|
|
|
size_t savedSize = getSize();
|
2021-07-30 17:10:34 +00:00
|
|
|
setSize(CODE_SIZE - 128 - startOffset);
|
2021-07-24 20:24:37 +00:00
|
|
|
unwindSize = unwinder.end(getSize());
|
|
|
|
verify(unwindSize <= 128);
|
|
|
|
setSize(savedSize);
|
|
|
|
|
2021-01-29 10:46:05 +00:00
|
|
|
ready();
|
|
|
|
mainloop = (void (*)())getCode();
|
2021-01-30 15:14:44 +00:00
|
|
|
handleException = (void(*)())handleExceptionLabel.getAddress();
|
2021-01-29 10:46:05 +00:00
|
|
|
|
|
|
|
emit_Skip(getSize());
|
|
|
|
}
|
|
|
|
|
2021-01-30 15:14:44 +00:00
|
|
|
bool rewriteMemAccess(host_context_t &context)
|
2021-01-29 10:46:05 +00:00
|
|
|
{
|
2021-05-17 09:59:34 +00:00
|
|
|
if (!_nvmem_enabled())
|
2021-01-29 10:46:05 +00:00
|
|
|
return false;
|
|
|
|
|
2021-01-30 15:14:44 +00:00
|
|
|
//printf("ngen_Rewrite pc %p\n", context.pc);
|
|
|
|
if (context.pc < (size_t)MemHandlerStart || context.pc >= (size_t)MemHandlerEnd)
|
2021-01-29 10:46:05 +00:00
|
|
|
return false;
|
|
|
|
|
2021-01-30 15:14:44 +00:00
|
|
|
u8 *retAddr = *(u8 **)context.rsp;
|
|
|
|
void *ca = *(s32 *)(retAddr - 4) + retAddr;
|
2021-01-29 10:46:05 +00:00
|
|
|
for (int size = 0; size < MemSize::Count; size++)
|
|
|
|
{
|
|
|
|
for (int op = 0; op < MemOp::Count; op++)
|
|
|
|
{
|
2021-01-30 15:14:44 +00:00
|
|
|
if ((void *)MemHandlers[MemType::Fast][size][op] != ca)
|
2021-01-29 10:46:05 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
//found !
|
|
|
|
const u8 *start = getCurr();
|
2022-12-09 16:37:49 +00:00
|
|
|
u32 memAddress = context.r9;
|
2021-03-29 15:30:56 +00:00
|
|
|
if (op == MemOp::W && size >= MemSize::S32 && (memAddress >> 26) == 0x38)
|
2021-03-28 19:10:39 +00:00
|
|
|
call(MemHandlers[MemType::StoreQueue][size][MemOp::W]);
|
|
|
|
else
|
|
|
|
call(MemHandlers[MemType::Slow][size][op]);
|
2021-01-29 10:46:05 +00:00
|
|
|
verify(getCurr() - start == 5);
|
|
|
|
|
|
|
|
ready();
|
|
|
|
|
2021-01-30 15:14:44 +00:00
|
|
|
context.pc = (uintptr_t)(retAddr - 5);
|
|
|
|
// remove the call from the stack
|
|
|
|
context.rsp += 8;
|
2022-12-09 16:37:49 +00:00
|
|
|
//restore the addr from r9 to arg0 (rcx or rdi) so it's valid again
|
2021-01-30 15:14:44 +00:00
|
|
|
#ifdef _WIN32
|
2022-12-09 16:37:49 +00:00
|
|
|
context.rcx = memAddress;
|
2021-01-30 15:14:44 +00:00
|
|
|
#else
|
2022-12-09 16:37:49 +00:00
|
|
|
context.rdi = memAddress;
|
2021-01-30 15:14:44 +00:00
|
|
|
#endif
|
2021-01-29 10:46:05 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2021-01-30 15:14:44 +00:00
|
|
|
ERROR_LOG(DYNAREC, "rewriteMemAccess code not found: host pc %p", (void *)context.pc);
|
2021-01-29 10:46:05 +00:00
|
|
|
die("Failed to match the code");
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-01-12 22:48:48 +00:00
|
|
|
private:
|
2021-05-17 09:59:34 +00:00
|
|
|
void genMmuLookup(const RuntimeBlockInfo* block, const shil_opcode& op, u32 write)
|
|
|
|
{
|
|
|
|
if (mmu_enabled())
|
|
|
|
{
|
2022-12-09 16:37:49 +00:00
|
|
|
#ifdef FAST_MMU
|
2021-05-17 09:59:34 +00:00
|
|
|
Xbyak::Label inCache;
|
|
|
|
Xbyak::Label done;
|
|
|
|
|
|
|
|
mov(eax, call_regs[0]);
|
|
|
|
shr(eax, 12);
|
2021-05-18 10:53:14 +00:00
|
|
|
if ((uintptr_t)mmuAddressLUT >> 32 != 0)
|
|
|
|
{
|
|
|
|
mov(r9, (uintptr_t)mmuAddressLUT);
|
|
|
|
mov(eax, dword[r9 + rax * 4]);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
mov(eax, dword[(uintptr_t)mmuAddressLUT + rax * 4]);
|
|
|
|
}
|
2021-05-17 09:59:34 +00:00
|
|
|
test(eax, eax);
|
|
|
|
jne(inCache);
|
2022-12-09 16:37:49 +00:00
|
|
|
#endif
|
2021-05-17 09:59:34 +00:00
|
|
|
mov(call_regs[1], write);
|
|
|
|
mov(call_regs[2], block->vaddr + op.guest_offs - (op.delay_slot ? 2 : 0)); // pc
|
|
|
|
GenCall(mmuDynarecLookup);
|
|
|
|
mov(call_regs[0], eax);
|
2022-12-09 16:37:49 +00:00
|
|
|
#ifdef FAST_MMU
|
2021-05-17 09:59:34 +00:00
|
|
|
jmp(done);
|
|
|
|
L(inCache);
|
|
|
|
and_(call_regs[0], 0xFFF);
|
|
|
|
or_(call_regs[0], eax);
|
|
|
|
L(done);
|
2022-12-09 16:37:49 +00:00
|
|
|
#endif
|
2021-05-17 09:59:34 +00:00
|
|
|
}
|
|
|
|
}
|
2019-04-29 16:23:00 +00:00
|
|
|
bool GenReadMemImmediate(const shil_opcode& op, RuntimeBlockInfo* block)
|
|
|
|
{
|
|
|
|
if (!op.rs1.is_imm())
|
|
|
|
return false;
|
|
|
|
u32 size = op.flags & 0x7f;
|
|
|
|
u32 addr = op.rs1._imm;
|
2021-05-14 17:03:57 +00:00
|
|
|
if (mmu_enabled() && mmu_is_translated(addr, size))
|
2019-04-29 16:23:00 +00:00
|
|
|
{
|
2021-05-14 17:03:57 +00:00
|
|
|
if ((addr >> 12) != (block->vaddr >> 12) && ((addr >> 12) != ((block->vaddr + block->guest_opcodes * 2 - 1) >> 12)))
|
2019-04-29 16:23:00 +00:00
|
|
|
// When full mmu is on, only consider addresses in the same 4k page
|
|
|
|
return false;
|
|
|
|
|
|
|
|
u32 paddr;
|
|
|
|
u32 rv;
|
2019-06-26 17:06:30 +00:00
|
|
|
switch (size)
|
|
|
|
{
|
|
|
|
case 1:
|
|
|
|
rv = mmu_data_translation<MMU_TT_DREAD, u8>(addr, paddr);
|
|
|
|
break;
|
|
|
|
case 2:
|
2019-04-29 16:23:00 +00:00
|
|
|
rv = mmu_data_translation<MMU_TT_DREAD, u16>(addr, paddr);
|
2019-06-26 17:06:30 +00:00
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
case 8:
|
2019-04-29 16:23:00 +00:00
|
|
|
rv = mmu_data_translation<MMU_TT_DREAD, u32>(addr, paddr);
|
2019-06-26 17:06:30 +00:00
|
|
|
break;
|
|
|
|
default:
|
2019-04-29 16:23:00 +00:00
|
|
|
die("Invalid immediate size");
|
2020-12-16 14:12:32 +00:00
|
|
|
return false;
|
2019-06-26 17:06:30 +00:00
|
|
|
}
|
2019-04-29 16:23:00 +00:00
|
|
|
if (rv != MMU_ERROR_NONE)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
addr = paddr;
|
|
|
|
}
|
|
|
|
bool isram = false;
|
2019-06-26 17:06:30 +00:00
|
|
|
void* ptr = _vmem_read_const(addr, isram, size > 4 ? 4 : size);
|
2019-04-29 16:23:00 +00:00
|
|
|
|
|
|
|
if (isram)
|
|
|
|
{
|
|
|
|
// Immediate pointer to RAM: super-duper fast access
|
|
|
|
mov(rax, reinterpret_cast<uintptr_t>(ptr));
|
|
|
|
switch (size)
|
|
|
|
{
|
2019-06-10 11:57:10 +00:00
|
|
|
case 1:
|
|
|
|
if (regalloc.IsAllocg(op.rd))
|
|
|
|
movsx(regalloc.MapRegister(op.rd), byte[rax]);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
movsx(eax, byte[rax]);
|
|
|
|
mov(rcx, (uintptr_t)op.rd.reg_ptr());
|
|
|
|
mov(dword[rcx], eax);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2019-04-29 16:23:00 +00:00
|
|
|
case 2:
|
|
|
|
if (regalloc.IsAllocg(op.rd))
|
|
|
|
movsx(regalloc.MapRegister(op.rd), word[rax]);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
movsx(eax, word[rax]);
|
|
|
|
mov(rcx, (uintptr_t)op.rd.reg_ptr());
|
|
|
|
mov(dword[rcx], eax);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 4:
|
|
|
|
if (regalloc.IsAllocg(op.rd))
|
|
|
|
mov(regalloc.MapRegister(op.rd), dword[rax]);
|
|
|
|
else if (regalloc.IsAllocf(op.rd))
|
|
|
|
movd(regalloc.MapXRegister(op.rd), dword[rax]);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
mov(eax, dword[rax]);
|
|
|
|
mov(rcx, (uintptr_t)op.rd.reg_ptr());
|
|
|
|
mov(dword[rcx], eax);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2019-06-10 11:57:10 +00:00
|
|
|
case 8:
|
|
|
|
mov(rcx, qword[rax]);
|
2021-01-26 12:16:48 +00:00
|
|
|
mov(rax, (uintptr_t)op.rd.reg_ptr());
|
|
|
|
mov(qword[rax], rcx);
|
2019-06-10 11:57:10 +00:00
|
|
|
break;
|
|
|
|
|
2019-04-29 16:23:00 +00:00
|
|
|
default:
|
|
|
|
die("Invalid immediate size");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Not RAM: the returned pointer is a memory handler
|
2019-06-26 17:06:30 +00:00
|
|
|
if (size == 8)
|
2019-04-29 16:23:00 +00:00
|
|
|
{
|
2019-06-26 17:06:30 +00:00
|
|
|
verify(!regalloc.IsAllocAny(op.rd));
|
2019-06-10 11:57:10 +00:00
|
|
|
|
2019-06-26 17:06:30 +00:00
|
|
|
// Need to call the handler twice
|
|
|
|
mov(call_regs[0], addr);
|
2019-04-29 16:23:00 +00:00
|
|
|
GenCall((void (*)())ptr);
|
2019-06-26 17:06:30 +00:00
|
|
|
mov(rcx, (size_t)op.rd.reg_ptr());
|
|
|
|
mov(dword[rcx], eax);
|
2019-04-29 16:23:00 +00:00
|
|
|
|
2019-06-26 17:06:30 +00:00
|
|
|
mov(call_regs[0], addr + 4);
|
2019-04-29 16:23:00 +00:00
|
|
|
GenCall((void (*)())ptr);
|
2019-06-26 17:06:30 +00:00
|
|
|
mov(rcx, (size_t)op.rd.reg_ptr() + 4);
|
|
|
|
mov(dword[rcx], eax);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
mov(call_regs[0], addr);
|
2019-04-29 16:23:00 +00:00
|
|
|
|
2019-06-26 17:06:30 +00:00
|
|
|
switch(size)
|
|
|
|
{
|
|
|
|
case 1:
|
|
|
|
GenCall((void (*)())ptr);
|
|
|
|
movsx(eax, al);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
GenCall((void (*)())ptr);
|
|
|
|
movsx(eax, ax);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 4:
|
|
|
|
GenCall((void (*)())ptr);
|
2019-04-29 16:23:00 +00:00
|
|
|
break;
|
2019-06-26 17:06:30 +00:00
|
|
|
|
|
|
|
default:
|
|
|
|
die("Invalid immediate size");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
host_reg_to_shil_param(op.rd, eax);
|
2019-04-29 16:23:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-06-10 11:57:10 +00:00
|
|
|
bool GenWriteMemImmediate(const shil_opcode& op, RuntimeBlockInfo* block)
|
|
|
|
{
|
|
|
|
if (!op.rs1.is_imm())
|
|
|
|
return false;
|
|
|
|
u32 size = op.flags & 0x7f;
|
|
|
|
u32 addr = op.rs1._imm;
|
2021-05-14 17:03:57 +00:00
|
|
|
if (mmu_enabled() && mmu_is_translated(addr, size))
|
2019-06-10 11:57:10 +00:00
|
|
|
{
|
2021-05-14 17:03:57 +00:00
|
|
|
if ((addr >> 12) != (block->vaddr >> 12) && ((addr >> 12) != ((block->vaddr + block->guest_opcodes * 2 - 1) >> 12)))
|
2019-06-10 11:57:10 +00:00
|
|
|
// When full mmu is on, only consider addresses in the same 4k page
|
|
|
|
return false;
|
|
|
|
|
|
|
|
u32 paddr;
|
|
|
|
u32 rv;
|
|
|
|
switch (size)
|
|
|
|
{
|
|
|
|
case 1:
|
|
|
|
rv = mmu_data_translation<MMU_TT_DWRITE, u8>(addr, paddr);
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
rv = mmu_data_translation<MMU_TT_DWRITE, u16>(addr, paddr);
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
case 8:
|
|
|
|
rv = mmu_data_translation<MMU_TT_DWRITE, u32>(addr, paddr);
|
|
|
|
break;
|
2019-06-26 17:06:30 +00:00
|
|
|
default:
|
|
|
|
die("Invalid immediate size");
|
2020-12-16 14:12:32 +00:00
|
|
|
return false;
|
2019-06-10 11:57:10 +00:00
|
|
|
}
|
|
|
|
if (rv != MMU_ERROR_NONE)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
addr = paddr;
|
|
|
|
}
|
|
|
|
bool isram = false;
|
2019-06-26 17:06:30 +00:00
|
|
|
void* ptr = _vmem_write_const(addr, isram, size > 4 ? 4 : size);
|
2019-06-10 11:57:10 +00:00
|
|
|
|
|
|
|
if (isram)
|
|
|
|
{
|
|
|
|
// Immediate pointer to RAM: super-duper fast access
|
|
|
|
mov(rax, reinterpret_cast<uintptr_t>(ptr));
|
|
|
|
switch (size)
|
|
|
|
{
|
|
|
|
case 1:
|
|
|
|
if (regalloc.IsAllocg(op.rs2))
|
2019-06-18 11:16:42 +00:00
|
|
|
mov(byte[rax], regalloc.MapRegister(op.rs2).cvt8());
|
2019-06-10 11:57:10 +00:00
|
|
|
else if (op.rs2.is_imm())
|
2019-06-18 11:16:42 +00:00
|
|
|
mov(byte[rax], (u8)op.rs2._imm);
|
2019-06-10 11:57:10 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
mov(rcx, (uintptr_t)op.rs2.reg_ptr());
|
2019-06-18 11:16:42 +00:00
|
|
|
mov(cl, byte[rcx]);
|
|
|
|
mov(byte[rax], cl);
|
2019-06-10 11:57:10 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
if (regalloc.IsAllocg(op.rs2))
|
2019-06-18 11:16:42 +00:00
|
|
|
mov(word[rax], regalloc.MapRegister(op.rs2).cvt16());
|
2019-06-10 11:57:10 +00:00
|
|
|
else if (op.rs2.is_imm())
|
2019-06-18 11:16:42 +00:00
|
|
|
mov(word[rax], (u16)op.rs2._imm);
|
2019-06-10 11:57:10 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
mov(rcx, (uintptr_t)op.rs2.reg_ptr());
|
2019-06-18 11:16:42 +00:00
|
|
|
mov(cx, word[rcx]);
|
|
|
|
mov(word[rax], cx);
|
2019-06-10 11:57:10 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 4:
|
|
|
|
if (regalloc.IsAllocg(op.rs2))
|
|
|
|
mov(dword[rax], regalloc.MapRegister(op.rs2));
|
|
|
|
else if (regalloc.IsAllocf(op.rs2))
|
|
|
|
movd(dword[rax], regalloc.MapXRegister(op.rs2));
|
|
|
|
else if (op.rs2.is_imm())
|
|
|
|
mov(dword[rax], op.rs2._imm);
|
|
|
|
else
|
|
|
|
{
|
2019-06-24 16:56:09 +00:00
|
|
|
mov(rcx, (uintptr_t)op.rs2.reg_ptr());
|
2019-06-10 11:57:10 +00:00
|
|
|
mov(ecx, dword[rcx]);
|
|
|
|
mov(dword[rax], ecx);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 8:
|
2021-01-26 12:16:48 +00:00
|
|
|
mov(rcx, (uintptr_t)op.rs2.reg_ptr());
|
|
|
|
mov(rcx, qword[rcx]);
|
|
|
|
mov(qword[rax], rcx);
|
2019-06-18 11:16:42 +00:00
|
|
|
break;
|
2019-06-10 11:57:10 +00:00
|
|
|
|
|
|
|
default:
|
|
|
|
die("Invalid immediate size");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Not RAM: the returned pointer is a memory handler
|
|
|
|
mov(call_regs[0], addr);
|
|
|
|
shil_param_to_host_reg(op.rs2, call_regs[1]);
|
|
|
|
|
|
|
|
GenCall((void (*)())ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-01-31 10:56:07 +00:00
|
|
|
void CheckBlock(bool force_checks, RuntimeBlockInfo* block)
|
|
|
|
{
|
|
|
|
if (mmu_enabled() || force_checks)
|
|
|
|
mov(call_regs[0], block->addr);
|
2019-01-12 22:48:48 +00:00
|
|
|
|
2019-04-29 16:23:00 +00:00
|
|
|
// FIXME This test shouldn't be necessary
|
2019-04-15 16:02:34 +00:00
|
|
|
// However the decoder makes various assumptions about the current PC value, which are simply not
|
|
|
|
// true in a virtualized memory model. So this can only work if virtual and phy addresses are the
|
|
|
|
// same at compile and run times.
|
2019-03-25 10:53:13 +00:00
|
|
|
if (mmu_enabled())
|
|
|
|
{
|
|
|
|
mov(rax, (uintptr_t)&next_pc);
|
|
|
|
cmp(dword[rax], block->vaddr);
|
|
|
|
jne(reinterpret_cast<const void*>(&ngen_blockcheckfail));
|
|
|
|
}
|
|
|
|
|
2019-09-29 16:18:46 +00:00
|
|
|
if (!force_checks)
|
|
|
|
return;
|
2019-01-12 22:48:48 +00:00
|
|
|
|
2019-09-29 16:18:46 +00:00
|
|
|
s32 sz=block->sh4_code_size;
|
|
|
|
u32 sa=block->addr;
|
2019-01-12 22:48:48 +00:00
|
|
|
|
2019-09-29 16:18:46 +00:00
|
|
|
void* ptr = (void*)GetMemPtr(sa, sz > 8 ? 8 : sz);
|
|
|
|
if (ptr)
|
|
|
|
{
|
|
|
|
while (sz > 0)
|
|
|
|
{
|
2020-03-10 13:47:45 +00:00
|
|
|
uintptr_t uintptr = reinterpret_cast<uintptr_t>(ptr);
|
|
|
|
mov(rax, uintptr);
|
2019-03-30 05:33:52 +00:00
|
|
|
|
2020-03-10 13:47:45 +00:00
|
|
|
if (sz >= 8 && !(uintptr & 7)) {
|
2019-09-29 16:18:46 +00:00
|
|
|
mov(rdx, *(u64*)ptr);
|
|
|
|
cmp(qword[rax], rdx);
|
|
|
|
sz -= 8;
|
|
|
|
sa += 8;
|
|
|
|
}
|
2020-03-10 13:47:45 +00:00
|
|
|
else if (sz >= 4 && !(uintptr & 3)) {
|
2019-09-29 16:18:46 +00:00
|
|
|
mov(edx, *(u32*)ptr);
|
|
|
|
cmp(dword[rax], edx);
|
|
|
|
sz -= 4;
|
|
|
|
sa += 4;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
mov(edx, *(u16*)ptr);
|
|
|
|
cmp(word[rax],dx);
|
|
|
|
sz -= 2;
|
|
|
|
sa += 2;
|
|
|
|
}
|
|
|
|
jne(reinterpret_cast<const void*>(CC_RX2RW(&ngen_blockcheckfail)));
|
|
|
|
ptr = (void*)GetMemPtr(sa, sz > 8 ? 8 : sz);
|
|
|
|
}
|
2019-03-29 18:23:37 +00:00
|
|
|
}
|
2019-01-12 22:48:48 +00:00
|
|
|
}
|
|
|
|
|
2021-01-29 10:46:05 +00:00
|
|
|
void genMemHandlers()
|
|
|
|
{
|
|
|
|
// make sure the memory handlers are set
|
|
|
|
verify(ReadMem8 != nullptr);
|
|
|
|
|
|
|
|
MemHandlerStart = getCurr();
|
2021-03-28 19:10:39 +00:00
|
|
|
for (int type = 0; type < MemType::Count; type++)
|
2021-01-29 10:46:05 +00:00
|
|
|
{
|
|
|
|
for (int size = 0; size < MemSize::Count; size++)
|
|
|
|
{
|
|
|
|
for (int op = 0; op < MemOp::Count; op++)
|
|
|
|
{
|
|
|
|
MemHandlers[type][size][op] = getCurr();
|
2021-05-17 09:59:34 +00:00
|
|
|
if (type == MemType::Fast && _nvmem_enabled())
|
2021-01-29 10:46:05 +00:00
|
|
|
{
|
|
|
|
mov(rax, (uintptr_t)virt_ram_base);
|
2022-12-09 16:37:49 +00:00
|
|
|
mov(r9, call_regs64[0]);
|
|
|
|
and_(call_regs[0], 0x1FFFFFFF);
|
|
|
|
|
2021-01-29 10:46:05 +00:00
|
|
|
switch (size)
|
|
|
|
{
|
|
|
|
case MemSize::S8:
|
|
|
|
if (op == MemOp::R)
|
|
|
|
movsx(eax, byte[rax + call_regs64[0]]);
|
|
|
|
else
|
|
|
|
mov(byte[rax + call_regs64[0]], call_regs[1].cvt8());
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MemSize::S16:
|
|
|
|
if (op == MemOp::R)
|
|
|
|
movsx(eax, word[rax + call_regs64[0]]);
|
|
|
|
else
|
|
|
|
mov(word[rax + call_regs64[0]], call_regs[1].cvt16());
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MemSize::S32:
|
|
|
|
if (op == MemOp::R)
|
|
|
|
mov(eax, dword[rax + call_regs64[0]]);
|
|
|
|
else
|
|
|
|
mov(dword[rax + call_regs64[0]], call_regs[1]);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MemSize::S64:
|
|
|
|
if (op == MemOp::R)
|
|
|
|
mov(rax, qword[rax + call_regs64[0]]);
|
|
|
|
else
|
|
|
|
mov(qword[rax + call_regs64[0]], call_regs64[1]);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2021-03-28 19:10:39 +00:00
|
|
|
else if (type == MemType::StoreQueue)
|
|
|
|
{
|
|
|
|
if (op != MemOp::W || size < MemSize::S32)
|
|
|
|
continue;
|
|
|
|
Xbyak::Label no_sqw;
|
|
|
|
|
|
|
|
mov(r9d, call_regs[0]);
|
|
|
|
shr(r9d, 26);
|
|
|
|
cmp(r9d, 0x38);
|
|
|
|
jne(no_sqw);
|
|
|
|
mov(rax, (uintptr_t)p_sh4rcb->sq_buffer);
|
|
|
|
and_(call_regs[0], 0x3F);
|
|
|
|
|
|
|
|
if (size == MemSize::S32)
|
|
|
|
mov(dword[rax + call_regs64[0]], call_regs[1]);
|
|
|
|
else
|
|
|
|
mov(qword[rax + call_regs64[0]], call_regs64[1]);
|
|
|
|
ret();
|
|
|
|
L(no_sqw);
|
|
|
|
if (size == MemSize::S32)
|
2021-05-17 09:59:34 +00:00
|
|
|
jmp((const void *)_vmem_WriteMem32); // tail call
|
2021-03-28 19:10:39 +00:00
|
|
|
else
|
2021-05-17 09:59:34 +00:00
|
|
|
jmp((const void *)_vmem_WriteMem64); // tail call
|
2021-03-28 19:10:39 +00:00
|
|
|
continue;
|
|
|
|
}
|
2021-01-29 10:46:05 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
// Slow path
|
|
|
|
if (op == MemOp::R)
|
|
|
|
{
|
|
|
|
switch (size) {
|
|
|
|
case MemSize::S8:
|
2021-11-20 11:29:07 +00:00
|
|
|
sub(rsp, STACK_ALIGN);
|
2021-05-17 09:59:34 +00:00
|
|
|
call((const void *)_vmem_ReadMem8);
|
2021-01-29 10:46:05 +00:00
|
|
|
movsx(eax, al);
|
2021-11-20 11:29:07 +00:00
|
|
|
add(rsp, STACK_ALIGN);
|
2021-01-29 10:46:05 +00:00
|
|
|
break;
|
|
|
|
case MemSize::S16:
|
2021-11-20 11:29:07 +00:00
|
|
|
sub(rsp, STACK_ALIGN);
|
2021-05-17 09:59:34 +00:00
|
|
|
call((const void *)_vmem_ReadMem16);
|
2021-01-29 10:46:05 +00:00
|
|
|
movsx(eax, ax);
|
2021-11-20 11:29:07 +00:00
|
|
|
add(rsp, STACK_ALIGN);
|
2021-01-29 10:46:05 +00:00
|
|
|
break;
|
|
|
|
case MemSize::S32:
|
2021-05-17 09:59:34 +00:00
|
|
|
jmp((const void *)_vmem_ReadMem32); // tail call
|
2021-01-29 10:46:05 +00:00
|
|
|
continue;
|
|
|
|
case MemSize::S64:
|
2021-05-17 09:59:34 +00:00
|
|
|
jmp((const void *)_vmem_ReadMem64); // tail call
|
2021-01-29 10:46:05 +00:00
|
|
|
continue;
|
|
|
|
default:
|
|
|
|
die("1..8 bytes");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
switch (size) {
|
|
|
|
case MemSize::S8:
|
2021-11-20 11:29:07 +00:00
|
|
|
jmp((const void *)_vmem_WriteMem8); // tail call
|
2021-01-29 10:46:05 +00:00
|
|
|
continue;
|
|
|
|
case MemSize::S16:
|
2021-05-17 09:59:34 +00:00
|
|
|
jmp((const void *)_vmem_WriteMem16); // tail call
|
2021-01-29 10:46:05 +00:00
|
|
|
continue;
|
|
|
|
case MemSize::S32:
|
2021-05-17 09:59:34 +00:00
|
|
|
jmp((const void *)_vmem_WriteMem32); // tail call
|
2021-01-29 10:46:05 +00:00
|
|
|
continue;
|
|
|
|
case MemSize::S64:
|
2021-05-17 09:59:34 +00:00
|
|
|
jmp((const void *)_vmem_WriteMem64); // tail call
|
2021-01-29 10:46:05 +00:00
|
|
|
continue;
|
|
|
|
default:
|
|
|
|
die("1..8 bytes");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ret();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
MemHandlerEnd = getCurr();
|
|
|
|
}
|
|
|
|
|
|
|
|
void saveXmmRegisters()
|
2019-01-14 20:15:36 +00:00
|
|
|
{
|
2019-01-18 16:02:50 +00:00
|
|
|
#ifndef _WIN32
|
2021-01-29 10:46:05 +00:00
|
|
|
if (current_opid == (size_t)-1)
|
|
|
|
return;
|
|
|
|
|
2021-07-30 17:10:34 +00:00
|
|
|
if (regalloc.IsMapped(xmm8, current_opid))
|
|
|
|
movd(ptr[rip + &xmmSave[0]], xmm8);
|
|
|
|
if (regalloc.IsMapped(xmm9, current_opid))
|
|
|
|
movd(ptr[rip + &xmmSave[1]], xmm9);
|
|
|
|
if (regalloc.IsMapped(xmm10, current_opid))
|
|
|
|
movd(ptr[rip + &xmmSave[2]], xmm10);
|
|
|
|
if (regalloc.IsMapped(xmm11, current_opid))
|
|
|
|
movd(ptr[rip + &xmmSave[3]], xmm11);
|
2019-01-18 16:02:50 +00:00
|
|
|
#endif
|
2021-01-29 10:46:05 +00:00
|
|
|
}
|
2019-01-14 20:15:36 +00:00
|
|
|
|
2021-01-29 10:46:05 +00:00
|
|
|
void restoreXmmRegisters()
|
|
|
|
{
|
2019-01-18 16:02:50 +00:00
|
|
|
#ifndef _WIN32
|
2021-01-29 10:46:05 +00:00
|
|
|
if (current_opid == (size_t)-1)
|
|
|
|
return;
|
|
|
|
|
2021-07-30 17:10:34 +00:00
|
|
|
if (regalloc.IsMapped(xmm8, current_opid))
|
|
|
|
movd(xmm8, ptr[rip + &xmmSave[0]]);
|
|
|
|
if (regalloc.IsMapped(xmm9, current_opid))
|
|
|
|
movd(xmm9, ptr[rip + &xmmSave[1]]);
|
|
|
|
if (regalloc.IsMapped(xmm10, current_opid))
|
|
|
|
movd(xmm10, ptr[rip + &xmmSave[2]]);
|
|
|
|
if (regalloc.IsMapped(xmm11, current_opid))
|
|
|
|
movd(xmm11, ptr[rip + &xmmSave[3]]);
|
2019-01-18 16:02:50 +00:00
|
|
|
#endif
|
2019-01-14 20:15:36 +00:00
|
|
|
}
|
|
|
|
|
2021-01-29 10:46:05 +00:00
|
|
|
template<class Ret, class... Params>
|
|
|
|
void GenCall(Ret(*function)(Params...), bool skip_floats = false)
|
|
|
|
{
|
|
|
|
if (!skip_floats)
|
|
|
|
saveXmmRegisters();
|
|
|
|
call(CC_RX2RW(function));
|
|
|
|
if (!skip_floats)
|
|
|
|
restoreXmmRegisters();
|
|
|
|
}
|
|
|
|
|
2019-01-12 22:48:48 +00:00
|
|
|
struct CC_PS
|
|
|
|
{
|
|
|
|
CanonicalParamType type;
|
2019-01-14 20:15:36 +00:00
|
|
|
const shil_param* prm;
|
2019-01-12 22:48:48 +00:00
|
|
|
};
|
2020-03-29 17:29:14 +00:00
|
|
|
std::vector<CC_PS> CC_pars;
|
2019-01-12 22:48:48 +00:00
|
|
|
|
|
|
|
X64RegAlloc regalloc;
|
2019-01-18 16:02:50 +00:00
|
|
|
Xbyak::util::Cpu cpu;
|
2019-03-25 10:53:13 +00:00
|
|
|
size_t current_opid;
|
2019-05-10 20:31:59 +00:00
|
|
|
Xbyak::Label exit_block;
|
2015-07-14 18:17:45 +00:00
|
|
|
};
|
2015-07-13 21:56:42 +00:00
|
|
|
|
2019-01-12 22:48:48 +00:00
|
|
|
void X64RegAlloc::Preload(u32 reg, Xbyak::Operand::Code nreg)
|
|
|
|
{
|
|
|
|
compiler->RegPreload(reg, nreg);
|
|
|
|
}
|
|
|
|
void X64RegAlloc::Writeback(u32 reg, Xbyak::Operand::Code nreg)
|
|
|
|
{
|
|
|
|
compiler->RegWriteback(reg, nreg);
|
|
|
|
}
|
|
|
|
void X64RegAlloc::Preload_FPU(u32 reg, s8 nreg)
|
|
|
|
{
|
|
|
|
compiler->RegPreload_FPU(reg, nreg);
|
|
|
|
}
|
|
|
|
void X64RegAlloc::Writeback_FPU(u32 reg, s8 nreg)
|
|
|
|
{
|
|
|
|
compiler->RegWriteback_FPU(reg, nreg);
|
|
|
|
}
|
|
|
|
|
2021-01-30 15:14:44 +00:00
|
|
|
static BlockCompiler* ccCompiler;
|
2015-07-13 21:56:42 +00:00
|
|
|
|
2019-09-29 16:18:46 +00:00
|
|
|
void ngen_Compile(RuntimeBlockInfo* block, bool smc_checks, bool reset, bool staging, bool optimise)
|
2015-07-14 18:17:45 +00:00
|
|
|
{
|
2015-07-21 13:42:10 +00:00
|
|
|
verify(emit_FreeSpace() >= 16 * 1024);
|
2021-11-19 22:18:45 +00:00
|
|
|
void* protStart = emit_GetCCPtr();
|
|
|
|
size_t protSize = emit_FreeSpace();
|
|
|
|
vmem_platform_jit_set_exec(protStart, protSize, false);
|
2015-07-13 21:56:42 +00:00
|
|
|
|
2021-01-30 15:14:44 +00:00
|
|
|
BlockCompiler compiler;
|
|
|
|
::ccCompiler = &compiler;
|
2021-01-29 10:46:05 +00:00
|
|
|
try {
|
2021-01-30 15:14:44 +00:00
|
|
|
compiler.compile(block, smc_checks, reset, staging, optimise);
|
2021-01-29 10:46:05 +00:00
|
|
|
} catch (const Xbyak::Error& e) {
|
|
|
|
ERROR_LOG(DYNAREC, "Fatal xbyak error: %s", e.what());
|
|
|
|
}
|
2021-01-30 15:14:44 +00:00
|
|
|
::ccCompiler = nullptr;
|
2021-11-19 22:18:45 +00:00
|
|
|
vmem_platform_jit_set_exec(protStart, protSize, true);
|
2015-07-14 18:17:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ngen_CC_Start(shil_opcode* op)
|
|
|
|
{
|
2021-01-30 15:14:44 +00:00
|
|
|
ccCompiler->ngen_CC_Start(*op);
|
2015-07-14 18:17:45 +00:00
|
|
|
}
|
2015-07-13 21:56:42 +00:00
|
|
|
|
2015-07-14 18:17:45 +00:00
|
|
|
void ngen_CC_Param(shil_opcode* op, shil_param* par, CanonicalParamType tp)
|
|
|
|
{
|
2021-01-30 15:14:44 +00:00
|
|
|
ccCompiler->ngen_CC_param(*op, *par, tp);
|
2015-07-13 21:56:42 +00:00
|
|
|
}
|
|
|
|
|
2019-01-14 20:15:36 +00:00
|
|
|
void ngen_CC_Call(shil_opcode* op, void* function)
|
2015-07-13 21:56:42 +00:00
|
|
|
{
|
2021-01-30 15:14:44 +00:00
|
|
|
ccCompiler->ngen_CC_Call(*op, function);
|
2015-07-13 21:56:42 +00:00
|
|
|
}
|
2015-07-14 18:17:45 +00:00
|
|
|
|
2015-07-13 21:56:42 +00:00
|
|
|
void ngen_CC_Finish(shil_opcode* op)
|
|
|
|
{
|
2015-07-14 01:35:34 +00:00
|
|
|
}
|
2019-04-29 16:23:00 +00:00
|
|
|
|
2021-01-30 15:14:44 +00:00
|
|
|
bool ngen_Rewrite(host_context_t &context, void *faultAddress)
|
2019-04-29 16:23:00 +00:00
|
|
|
{
|
2021-11-19 22:18:45 +00:00
|
|
|
void* protStart = emit_GetCCPtr();
|
|
|
|
size_t protSize = emit_FreeSpace();
|
|
|
|
vmem_platform_jit_set_exec(protStart, protSize, false);
|
|
|
|
|
2021-01-30 15:14:44 +00:00
|
|
|
u8 *retAddr = *(u8 **)context.rsp - 5;
|
|
|
|
BlockCompiler compiler(retAddr);
|
2021-11-19 22:18:45 +00:00
|
|
|
bool rc = false;
|
2021-01-29 10:46:05 +00:00
|
|
|
try {
|
2021-11-19 22:18:45 +00:00
|
|
|
rc = compiler.rewriteMemAccess(context);
|
|
|
|
vmem_platform_jit_set_exec(protStart, protSize, true);
|
2021-01-29 10:46:05 +00:00
|
|
|
} catch (const Xbyak::Error& e) {
|
|
|
|
ERROR_LOG(DYNAREC, "Fatal xbyak error: %s", e.what());
|
2019-04-29 16:23:00 +00:00
|
|
|
}
|
2021-11-19 22:18:45 +00:00
|
|
|
return rc;
|
2019-04-29 16:23:00 +00:00
|
|
|
}
|
|
|
|
|
2021-01-30 15:14:44 +00:00
|
|
|
void ngen_HandleException(host_context_t &context)
|
2019-04-29 16:23:00 +00:00
|
|
|
{
|
2021-01-30 15:14:44 +00:00
|
|
|
context.pc = (uintptr_t)handleException;
|
2019-04-29 16:23:00 +00:00
|
|
|
}
|
2021-01-29 10:46:05 +00:00
|
|
|
|
|
|
|
void ngen_ResetBlocks()
|
|
|
|
{
|
2021-07-24 20:24:37 +00:00
|
|
|
unwinder.clear();
|
2021-01-29 10:46:05 +00:00
|
|
|
// Avoid generating the main loop more than once
|
|
|
|
if (mainloop != nullptr && mainloop != emit_GetCCPtr())
|
|
|
|
return;
|
|
|
|
|
2021-11-19 22:18:45 +00:00
|
|
|
void* protStart = emit_GetCCPtr();
|
|
|
|
size_t protSize = emit_FreeSpace();
|
|
|
|
vmem_platform_jit_set_exec(protStart, protSize, false);
|
|
|
|
|
2021-01-30 15:14:44 +00:00
|
|
|
BlockCompiler compiler;
|
2021-01-29 10:46:05 +00:00
|
|
|
try {
|
2021-01-30 15:14:44 +00:00
|
|
|
compiler.genMainloop();
|
2021-01-29 10:46:05 +00:00
|
|
|
} catch (const Xbyak::Error& e) {
|
|
|
|
ERROR_LOG(DYNAREC, "Fatal xbyak error: %s", e.what());
|
|
|
|
}
|
2021-11-19 22:18:45 +00:00
|
|
|
vmem_platform_jit_set_exec(protStart, protSize, true);
|
2021-01-29 10:46:05 +00:00
|
|
|
}
|
|
|
|
|
2018-07-23 17:47:24 +00:00
|
|
|
#endif
|