mirror of
https://github.com/cemu-project/Cemu.git
synced 2025-07-02 21:11:17 +12:00
1695 lines
49 KiB
C++
1695 lines
49 KiB
C++
#include "BackendAArch64.h"
|
|
|
|
#pragma push_macro("CSIZE")
|
|
#undef CSIZE
|
|
#include <xbyak_aarch64.h>
|
|
#pragma pop_macro("CSIZE")
|
|
#include <xbyak_aarch64_util.h>
|
|
|
|
#include <cstddef>
|
|
|
|
#include "../PPCRecompiler.h"
|
|
#include "Common/precompiled.h"
|
|
#include "Common/cpu_features.h"
|
|
#include "HW/Espresso/Interpreter/PPCInterpreterInternal.h"
|
|
#include "HW/Espresso/Interpreter/PPCInterpreterHelper.h"
|
|
#include "HW/Espresso/PPCState.h"
|
|
|
|
using namespace Xbyak_aarch64;
|
|
|
|
constexpr uint32 TEMP_GPR_1_ID = 25;
|
|
constexpr uint32 TEMP_GPR_2_ID = 26;
|
|
constexpr uint32 PPC_RECOMPILER_INSTANCE_DATA_REG_ID = 27;
|
|
constexpr uint32 MEMORY_BASE_REG_ID = 28;
|
|
constexpr uint32 HCPU_REG_ID = 29;
|
|
|
|
constexpr uint32 TEMP_FPR_ID = 31;
|
|
|
|
struct FPReg
|
|
{
|
|
explicit FPReg(size_t index)
|
|
: index(index), VReg(index), QReg(index), DReg(index), SReg(index), HReg(index), BReg(index)
|
|
{
|
|
}
|
|
const size_t index;
|
|
const VReg VReg;
|
|
const QReg QReg;
|
|
const DReg DReg;
|
|
const SReg SReg;
|
|
const HReg HReg;
|
|
const BReg BReg;
|
|
};
|
|
|
|
struct GPReg
|
|
{
|
|
explicit GPReg(size_t index)
|
|
: index(index), XReg(index), WReg(index)
|
|
{
|
|
}
|
|
const size_t index;
|
|
const XReg XReg;
|
|
const WReg WReg;
|
|
};
|
|
|
|
static const XReg HCPU_REG{HCPU_REG_ID}, PPC_REC_INSTANCE_REG{PPC_RECOMPILER_INSTANCE_DATA_REG_ID}, MEM_BASE_REG{MEMORY_BASE_REG_ID};
|
|
static const GPReg TEMP_GPR1{TEMP_GPR_1_ID};
|
|
static const GPReg TEMP_GPR2{TEMP_GPR_2_ID};
|
|
static const GPReg LR{TEMP_GPR_2_ID};
|
|
|
|
static const FPReg TEMP_FPR{TEMP_FPR_ID};
|
|
|
|
static const util::Cpu s_cpu;
|
|
|
|
class AArch64Allocator : public Allocator
|
|
{
|
|
private:
|
|
#ifdef XBYAK_USE_MMAP_ALLOCATOR
|
|
inline static MmapAllocator s_allocator;
|
|
#else
|
|
inline static Allocator s_allocator;
|
|
#endif
|
|
Allocator* m_allocatorImpl;
|
|
bool m_freeDisabled = false;
|
|
|
|
public:
|
|
AArch64Allocator()
|
|
: m_allocatorImpl(reinterpret_cast<Allocator*>(&s_allocator)) {}
|
|
|
|
uint32* alloc(size_t size) override
|
|
{
|
|
return m_allocatorImpl->alloc(size);
|
|
}
|
|
|
|
void setFreeDisabled(bool disabled)
|
|
{
|
|
m_freeDisabled = disabled;
|
|
}
|
|
|
|
void free(uint32* p) override
|
|
{
|
|
if (!m_freeDisabled)
|
|
m_allocatorImpl->free(p);
|
|
}
|
|
|
|
[[nodiscard]] bool useProtect() const override
|
|
{
|
|
return !m_freeDisabled && m_allocatorImpl->useProtect();
|
|
}
|
|
};
|
|
|
|
struct UnconditionalJumpInfo
|
|
{
|
|
IMLSegment* target;
|
|
};
|
|
|
|
struct ConditionalRegJumpInfo
|
|
{
|
|
IMLSegment* target;
|
|
WReg regBool;
|
|
bool mustBeTrue;
|
|
};
|
|
|
|
struct NegativeRegValueJumpInfo
|
|
{
|
|
IMLSegment* target;
|
|
WReg regValue;
|
|
};
|
|
|
|
using JumpInfo = std::variant<
|
|
UnconditionalJumpInfo,
|
|
ConditionalRegJumpInfo,
|
|
NegativeRegValueJumpInfo>;
|
|
|
|
struct AArch64GenContext_t : CodeGenerator
|
|
{
|
|
explicit AArch64GenContext_t(Allocator* allocator = nullptr);
|
|
void enterRecompilerCode();
|
|
void leaveRecompilerCode();
|
|
|
|
void r_name(IMLInstruction* imlInstruction);
|
|
void name_r(IMLInstruction* imlInstruction);
|
|
bool r_s32(IMLInstruction* imlInstruction);
|
|
bool r_r(IMLInstruction* imlInstruction);
|
|
bool r_r_s32(IMLInstruction* imlInstruction);
|
|
bool r_r_s32_carry(IMLInstruction* imlInstruction);
|
|
bool r_r_r(IMLInstruction* imlInstruction);
|
|
bool r_r_r_carry(IMLInstruction* imlInstruction);
|
|
void compare(IMLInstruction* imlInstruction);
|
|
void compare_s32(IMLInstruction* imlInstruction);
|
|
bool load(IMLInstruction* imlInstruction, bool indexed);
|
|
bool store(IMLInstruction* imlInstruction, bool indexed);
|
|
void atomic_cmp_store(IMLInstruction* imlInstruction);
|
|
bool macro(IMLInstruction* imlInstruction);
|
|
void call_imm(IMLInstruction* imlInstruction);
|
|
bool fpr_load(IMLInstruction* imlInstruction, bool indexed);
|
|
bool fpr_store(IMLInstruction* imlInstruction, bool indexed);
|
|
void fpr_r_r(IMLInstruction* imlInstruction);
|
|
void fpr_r_r_r(IMLInstruction* imlInstruction);
|
|
void fpr_r_r_r_r(IMLInstruction* imlInstruction);
|
|
void fpr_r(IMLInstruction* imlInstruction);
|
|
void fpr_compare(IMLInstruction* imlInstruction);
|
|
void cjump(IMLInstruction* imlInstruction, IMLSegment* imlSegment);
|
|
void jump(IMLSegment* imlSegment);
|
|
void conditionalJumpCycleCheck(IMLSegment* imlSegment);
|
|
|
|
static constexpr size_t MAX_JUMP_INSTR_COUNT = 2;
|
|
std::list<std::pair<size_t, JumpInfo>> jumps;
|
|
void prepareJump(JumpInfo&& jumpInfo)
|
|
{
|
|
jumps.emplace_back(getSize(), jumpInfo);
|
|
for (int i = 0; i < MAX_JUMP_INSTR_COUNT; ++i)
|
|
nop();
|
|
}
|
|
|
|
std::map<IMLSegment*, size_t> segmentStarts;
|
|
void storeSegmentStart(IMLSegment* imlSegment)
|
|
{
|
|
segmentStarts[imlSegment] = getSize();
|
|
}
|
|
|
|
bool processAllJumps()
|
|
{
|
|
for (auto jump : jumps)
|
|
{
|
|
auto jumpStart = jump.first;
|
|
auto jumpInfo = jump.second;
|
|
bool success = std::visit(
|
|
[&, this](const auto& jump) {
|
|
setSize(jumpStart);
|
|
sint64 targetAddress = segmentStarts.at(jump.target);
|
|
sint64 addressOffset = targetAddress - jumpStart;
|
|
return handleJump(addressOffset, jump);
|
|
},
|
|
jumpInfo);
|
|
if (!success)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool handleJump(sint64 addressOffset, const UnconditionalJumpInfo& jump)
|
|
{
|
|
// in +/-128MB
|
|
if (-0x8000000 <= addressOffset && addressOffset <= 0x7ffffff)
|
|
{
|
|
b(addressOffset);
|
|
return true;
|
|
}
|
|
|
|
cemu_assert_suspicious();
|
|
|
|
return false;
|
|
}
|
|
|
|
bool handleJump(sint64 addressOffset, const ConditionalRegJumpInfo& jump)
|
|
{
|
|
bool mustBeTrue = jump.mustBeTrue;
|
|
|
|
// in +/-32KB
|
|
if (-0x8000 <= addressOffset && addressOffset <= 0x7fff)
|
|
{
|
|
if (mustBeTrue)
|
|
tbnz(jump.regBool, 0, addressOffset);
|
|
else
|
|
tbz(jump.regBool, 0, addressOffset);
|
|
return true;
|
|
}
|
|
|
|
// in +/-1MB
|
|
if (-0x100000 <= addressOffset && addressOffset <= 0xfffff)
|
|
{
|
|
if (mustBeTrue)
|
|
cbnz(jump.regBool, addressOffset);
|
|
else
|
|
cbz(jump.regBool, addressOffset);
|
|
return true;
|
|
}
|
|
|
|
Label skipJump;
|
|
if (mustBeTrue)
|
|
tbz(jump.regBool, 0, skipJump);
|
|
else
|
|
tbnz(jump.regBool, 0, skipJump);
|
|
addressOffset -= 4;
|
|
|
|
// in +/-128MB
|
|
if (-0x8000000 <= addressOffset && addressOffset <= 0x7ffffff)
|
|
{
|
|
b(addressOffset);
|
|
L(skipJump);
|
|
return true;
|
|
}
|
|
|
|
cemu_assert_suspicious();
|
|
|
|
return false;
|
|
}
|
|
|
|
bool handleJump(sint64 addressOffset, const NegativeRegValueJumpInfo& jump)
|
|
{
|
|
// in +/-32KB
|
|
if (-0x8000 <= addressOffset && addressOffset <= 0x7fff)
|
|
{
|
|
tbnz(jump.regValue, 31, addressOffset);
|
|
return true;
|
|
}
|
|
|
|
// in +/-1MB
|
|
if (-0x100000 <= addressOffset && addressOffset <= 0xfffff)
|
|
{
|
|
tst(jump.regValue, 0x80000000);
|
|
addressOffset -= 4;
|
|
bne(addressOffset);
|
|
return true;
|
|
}
|
|
|
|
Label skipJump;
|
|
tbz(jump.regValue, 31, skipJump);
|
|
addressOffset -= 4;
|
|
|
|
// in +/-128MB
|
|
if (-0x8000000 <= addressOffset && addressOffset <= 0x7ffffff)
|
|
{
|
|
b(addressOffset);
|
|
L(skipJump);
|
|
return true;
|
|
}
|
|
|
|
cemu_assert_suspicious();
|
|
|
|
return false;
|
|
}
|
|
};
|
|
|
|
template<std::derived_from<VRegSc> T>
|
|
T fpReg(const IMLReg& imlReg)
|
|
{
|
|
cemu_assert_debug(imlReg.GetRegFormat() == IMLRegFormat::F64);
|
|
auto regId = imlReg.GetRegID();
|
|
cemu_assert_debug(regId >= IMLArchAArch64::PHYSREG_FPR_BASE && regId < IMLArchAArch64::PHYSREG_FPR_BASE + IMLArchAArch64::PHYSREG_FPR_COUNT);
|
|
return T(regId - IMLArchAArch64::PHYSREG_FPR_BASE);
|
|
}
|
|
|
|
template<std::derived_from<RReg> T>
|
|
T gpReg(const IMLReg& imlReg)
|
|
{
|
|
auto regFormat = imlReg.GetRegFormat();
|
|
if (std::is_same_v<T, WReg>)
|
|
cemu_assert_debug(regFormat == IMLRegFormat::I32);
|
|
else if (std::is_same_v<T, XReg>)
|
|
cemu_assert_debug(regFormat == IMLRegFormat::I64);
|
|
else
|
|
cemu_assert_unimplemented();
|
|
|
|
auto regId = imlReg.GetRegID();
|
|
cemu_assert_debug(regId >= IMLArchAArch64::PHYSREG_GPR_BASE && regId < IMLArchAArch64::PHYSREG_GPR_BASE + IMLArchAArch64::PHYSREG_GPR_COUNT);
|
|
return T(regId - IMLArchAArch64::PHYSREG_GPR_BASE);
|
|
}
|
|
|
|
template<std::derived_from<VRegSc> To, std::derived_from<VRegSc> From>
|
|
To aliasAs(const From& reg)
|
|
{
|
|
return To(reg.getIdx());
|
|
}
|
|
|
|
template<std::derived_from<RReg> To, std::derived_from<RReg> From>
|
|
To aliasAs(const From& reg)
|
|
{
|
|
return To(reg.getIdx());
|
|
}
|
|
|
|
AArch64GenContext_t::AArch64GenContext_t(Allocator* allocator)
|
|
: CodeGenerator(DEFAULT_MAX_CODE_SIZE, AutoGrow, allocator)
|
|
{
|
|
}
|
|
|
|
constexpr uint64 ones(uint32 size)
|
|
{
|
|
return (size == 64) ? 0xffffffffffffffff : ((uint64)1 << size) - 1;
|
|
}
|
|
|
|
constexpr bool isAdrImmValidFPR(sint32 imm, uint32 bits)
|
|
{
|
|
uint32 times = bits / 8;
|
|
uint32 sh = std::countr_zero(times);
|
|
return (0 <= imm && imm <= 4095 * times) && ((uint64)imm & ones(sh)) == 0;
|
|
}
|
|
|
|
constexpr bool isAdrImmValidGPR(sint32 imm, uint32 bits = 32)
|
|
{
|
|
uint32 size = std::countr_zero(bits / 8u);
|
|
sint32 times = 1 << size;
|
|
return (0 <= imm && imm <= 4095 * times) && ((uint64)imm & ones(size)) == 0;
|
|
}
|
|
|
|
constexpr bool isAdrImmRangeValid(sint32 rangeStart, sint32 rangeOffset, sint32 bits, std::invocable<sint32, uint32> auto check)
|
|
{
|
|
for (sint32 i = rangeStart; i <= rangeStart + rangeOffset; i += bits / 8)
|
|
if (!check(i, bits))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
constexpr bool isAdrImmRangeValidGPR(sint32 rangeStart, sint32 rangeOffset, sint32 bits = 32)
|
|
{
|
|
return isAdrImmRangeValid(rangeStart, rangeOffset, bits, isAdrImmValidGPR);
|
|
}
|
|
|
|
constexpr bool isAdrImmRangeValidFpr(sint32 rangeStart, sint32 rangeOffset, sint32 bits)
|
|
{
|
|
return isAdrImmRangeValid(rangeStart, rangeOffset, bits, isAdrImmValidFPR);
|
|
}
|
|
|
|
// Verify that all of the offsets for the PPCInterpreter_t members that we use in r_name/name_r have a valid imm value for AdrUimm
|
|
static_assert(isAdrImmRangeValidGPR(offsetof(PPCInterpreter_t, gpr), sizeof(uint32) * 31));
|
|
static_assert(isAdrImmValidGPR(offsetof(PPCInterpreter_t, spr.LR)));
|
|
static_assert(isAdrImmValidGPR(offsetof(PPCInterpreter_t, spr.CTR)));
|
|
static_assert(isAdrImmValidGPR(offsetof(PPCInterpreter_t, spr.XER)));
|
|
static_assert(isAdrImmRangeValidGPR(offsetof(PPCInterpreter_t, spr.UGQR), sizeof(PPCInterpreter_t::spr.UGQR[0]) * (SPR_UGQR7 - SPR_UGQR0)));
|
|
static_assert(isAdrImmRangeValidGPR(offsetof(PPCInterpreter_t, temporaryGPR_reg), sizeof(uint32) * 3));
|
|
static_assert(isAdrImmValidGPR(offsetof(PPCInterpreter_t, xer_ca), 8));
|
|
static_assert(isAdrImmValidGPR(offsetof(PPCInterpreter_t, xer_so), 8));
|
|
static_assert(isAdrImmRangeValidGPR(offsetof(PPCInterpreter_t, cr), PPCREC_NAME_CR_LAST - PPCREC_NAME_CR, 8));
|
|
static_assert(isAdrImmValidGPR(offsetof(PPCInterpreter_t, reservedMemAddr)));
|
|
static_assert(isAdrImmValidGPR(offsetof(PPCInterpreter_t, reservedMemValue)));
|
|
static_assert(isAdrImmRangeValidFpr(offsetof(PPCInterpreter_t, fpr), sizeof(FPR_t) * 63, 64));
|
|
static_assert(isAdrImmRangeValidFpr(offsetof(PPCInterpreter_t, temporaryFPR), sizeof(FPR_t) * 7, 128));
|
|
|
|
void AArch64GenContext_t::r_name(IMLInstruction* imlInstruction)
|
|
{
|
|
uint32 name = imlInstruction->op_r_name.name;
|
|
|
|
if (imlInstruction->op_r_name.regR.GetBaseFormat() == IMLRegFormat::I64)
|
|
{
|
|
XReg regRXReg = gpReg<XReg>(imlInstruction->op_r_name.regR);
|
|
WReg regR = aliasAs<WReg>(regRXReg);
|
|
if (name >= PPCREC_NAME_R0 && name < PPCREC_NAME_R0 + 32)
|
|
{
|
|
ldr(regR, AdrUimm(HCPU_REG, offsetof(PPCInterpreter_t, gpr) + sizeof(uint32) * (name - PPCREC_NAME_R0)));
|
|
}
|
|
else if (name >= PPCREC_NAME_SPR0 && name < PPCREC_NAME_SPR0 + 999)
|
|
{
|
|
uint32 sprIndex = (name - PPCREC_NAME_SPR0);
|
|
if (sprIndex == SPR_LR)
|
|
ldr(regR, AdrUimm(HCPU_REG, offsetof(PPCInterpreter_t, spr.LR)));
|
|
else if (sprIndex == SPR_CTR)
|
|
ldr(regR, AdrUimm(HCPU_REG, offsetof(PPCInterpreter_t, spr.CTR)));
|
|
else if (sprIndex == SPR_XER)
|
|
ldr(regR, AdrUimm(HCPU_REG, offsetof(PPCInterpreter_t, spr.XER)));
|
|
else if (sprIndex >= SPR_UGQR0 && sprIndex <= SPR_UGQR7)
|
|
ldr(regR, AdrUimm(HCPU_REG, offsetof(PPCInterpreter_t, spr.UGQR) + sizeof(PPCInterpreter_t::spr.UGQR[0]) * (sprIndex - SPR_UGQR0)));
|
|
else
|
|
cemu_assert_suspicious();
|
|
}
|
|
else if (name >= PPCREC_NAME_TEMPORARY && name < PPCREC_NAME_TEMPORARY + 4)
|
|
{
|
|
ldr(regR, AdrUimm(HCPU_REG, offsetof(PPCInterpreter_t, temporaryGPR_reg) + sizeof(uint32) * (name - PPCREC_NAME_TEMPORARY)));
|
|
}
|
|
else if (name == PPCREC_NAME_XER_CA)
|
|
{
|
|
ldrb(regR, AdrUimm(HCPU_REG, offsetof(PPCInterpreter_t, xer_ca)));
|
|
}
|
|
else if (name == PPCREC_NAME_XER_SO)
|
|
{
|
|
ldrb(regR, AdrUimm(HCPU_REG, offsetof(PPCInterpreter_t, xer_so)));
|
|
}
|
|
else if (name >= PPCREC_NAME_CR && name <= PPCREC_NAME_CR_LAST)
|
|
{
|
|
ldrb(regR, AdrUimm(HCPU_REG, offsetof(PPCInterpreter_t, cr) + (name - PPCREC_NAME_CR)));
|
|
}
|
|
else if (name == PPCREC_NAME_CPU_MEMRES_EA)
|
|
{
|
|
ldr(regR, AdrUimm(HCPU_REG, offsetof(PPCInterpreter_t, reservedMemAddr)));
|
|
}
|
|
else if (name == PPCREC_NAME_CPU_MEMRES_VAL)
|
|
{
|
|
ldr(regR, AdrUimm(HCPU_REG, offsetof(PPCInterpreter_t, reservedMemValue)));
|
|
}
|
|
else
|
|
{
|
|
cemu_assert_suspicious();
|
|
}
|
|
}
|
|
else if (imlInstruction->op_r_name.regR.GetBaseFormat() == IMLRegFormat::F64)
|
|
{
|
|
auto imlRegR = imlInstruction->op_r_name.regR;
|
|
|
|
if (name >= PPCREC_NAME_FPR_HALF && name < (PPCREC_NAME_FPR_HALF + 64))
|
|
{
|
|
uint32 regIndex = (name - PPCREC_NAME_FPR_HALF) / 2;
|
|
uint32 pairIndex = (name - PPCREC_NAME_FPR_HALF) % 2;
|
|
uint32 offset = offsetof(PPCInterpreter_t, fpr) + sizeof(FPR_t) * regIndex + (pairIndex ? sizeof(double) : 0);
|
|
ldr(fpReg<DReg>(imlRegR), AdrUimm(HCPU_REG, offset));
|
|
}
|
|
else if (name >= PPCREC_NAME_TEMPORARY_FPR0 && name < (PPCREC_NAME_TEMPORARY_FPR0 + 8))
|
|
{
|
|
ldr(fpReg<QReg>(imlRegR), AdrUimm(HCPU_REG, offsetof(PPCInterpreter_t, temporaryFPR) + sizeof(FPR_t) * (name - PPCREC_NAME_TEMPORARY_FPR0)));
|
|
}
|
|
else
|
|
{
|
|
cemu_assert_suspicious();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cemu_assert_suspicious();
|
|
}
|
|
}
|
|
|
|
void AArch64GenContext_t::name_r(IMLInstruction* imlInstruction)
|
|
{
|
|
uint32 name = imlInstruction->op_r_name.name;
|
|
|
|
if (imlInstruction->op_r_name.regR.GetBaseFormat() == IMLRegFormat::I64)
|
|
{
|
|
XReg regRXReg = gpReg<XReg>(imlInstruction->op_r_name.regR);
|
|
WReg regR = aliasAs<WReg>(regRXReg);
|
|
if (name >= PPCREC_NAME_R0 && name < PPCREC_NAME_R0 + 32)
|
|
{
|
|
str(regR, AdrUimm(HCPU_REG, offsetof(PPCInterpreter_t, gpr) + sizeof(uint32) * (name - PPCREC_NAME_R0)));
|
|
}
|
|
else if (name >= PPCREC_NAME_SPR0 && name < PPCREC_NAME_SPR0 + 999)
|
|
{
|
|
uint32 sprIndex = (name - PPCREC_NAME_SPR0);
|
|
if (sprIndex == SPR_LR)
|
|
str(regR, AdrUimm(HCPU_REG, offsetof(PPCInterpreter_t, spr.LR)));
|
|
else if (sprIndex == SPR_CTR)
|
|
str(regR, AdrUimm(HCPU_REG, offsetof(PPCInterpreter_t, spr.CTR)));
|
|
else if (sprIndex == SPR_XER)
|
|
str(regR, AdrUimm(HCPU_REG, offsetof(PPCInterpreter_t, spr.XER)));
|
|
else if (sprIndex >= SPR_UGQR0 && sprIndex <= SPR_UGQR7)
|
|
str(regR, AdrUimm(HCPU_REG, offsetof(PPCInterpreter_t, spr.UGQR) + sizeof(PPCInterpreter_t::spr.UGQR[0]) * (sprIndex - SPR_UGQR0)));
|
|
else
|
|
cemu_assert_suspicious();
|
|
}
|
|
else if (name >= PPCREC_NAME_TEMPORARY && name < PPCREC_NAME_TEMPORARY + 4)
|
|
{
|
|
str(regR, AdrUimm(HCPU_REG, offsetof(PPCInterpreter_t, temporaryGPR_reg) + sizeof(uint32) * (name - PPCREC_NAME_TEMPORARY)));
|
|
}
|
|
else if (name == PPCREC_NAME_XER_CA)
|
|
{
|
|
strb(regR, AdrUimm(HCPU_REG, offsetof(PPCInterpreter_t, xer_ca)));
|
|
}
|
|
else if (name == PPCREC_NAME_XER_SO)
|
|
{
|
|
strb(regR, AdrUimm(HCPU_REG, offsetof(PPCInterpreter_t, xer_so)));
|
|
}
|
|
else if (name >= PPCREC_NAME_CR && name <= PPCREC_NAME_CR_LAST)
|
|
{
|
|
strb(regR, AdrUimm(HCPU_REG, offsetof(PPCInterpreter_t, cr) + (name - PPCREC_NAME_CR)));
|
|
}
|
|
else if (name == PPCREC_NAME_CPU_MEMRES_EA)
|
|
{
|
|
str(regR, AdrUimm(HCPU_REG, offsetof(PPCInterpreter_t, reservedMemAddr)));
|
|
}
|
|
else if (name == PPCREC_NAME_CPU_MEMRES_VAL)
|
|
{
|
|
str(regR, AdrUimm(HCPU_REG, offsetof(PPCInterpreter_t, reservedMemValue)));
|
|
}
|
|
else
|
|
{
|
|
cemu_assert_suspicious();
|
|
}
|
|
}
|
|
else if (imlInstruction->op_r_name.regR.GetBaseFormat() == IMLRegFormat::F64)
|
|
{
|
|
auto imlRegR = imlInstruction->op_r_name.regR;
|
|
if (name >= PPCREC_NAME_FPR_HALF && name < (PPCREC_NAME_FPR_HALF + 64))
|
|
{
|
|
uint32 regIndex = (name - PPCREC_NAME_FPR_HALF) / 2;
|
|
uint32 pairIndex = (name - PPCREC_NAME_FPR_HALF) % 2;
|
|
sint32 offset = offsetof(PPCInterpreter_t, fpr) + sizeof(FPR_t) * regIndex + pairIndex * sizeof(double);
|
|
str(fpReg<DReg>(imlRegR), AdrUimm(HCPU_REG, offset));
|
|
}
|
|
else if (name >= PPCREC_NAME_TEMPORARY_FPR0 && name < (PPCREC_NAME_TEMPORARY_FPR0 + 8))
|
|
{
|
|
str(fpReg<QReg>(imlRegR), AdrUimm(HCPU_REG, offsetof(PPCInterpreter_t, temporaryFPR) + sizeof(FPR_t) * (name - PPCREC_NAME_TEMPORARY_FPR0)));
|
|
}
|
|
else
|
|
{
|
|
cemu_assert_suspicious();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cemu_assert_suspicious();
|
|
}
|
|
}
|
|
|
|
bool AArch64GenContext_t::r_r(IMLInstruction* imlInstruction)
|
|
{
|
|
WReg regR = gpReg<WReg>(imlInstruction->op_r_r.regR);
|
|
WReg regA = gpReg<WReg>(imlInstruction->op_r_r.regA);
|
|
|
|
if (imlInstruction->operation == PPCREC_IML_OP_ASSIGN)
|
|
{
|
|
mov(regR, regA);
|
|
}
|
|
else if (imlInstruction->operation == PPCREC_IML_OP_ENDIAN_SWAP)
|
|
{
|
|
rev(regR, regA);
|
|
}
|
|
else if (imlInstruction->operation == PPCREC_IML_OP_ASSIGN_S8_TO_S32)
|
|
{
|
|
sxtb(regR, regA);
|
|
}
|
|
else if (imlInstruction->operation == PPCREC_IML_OP_ASSIGN_S16_TO_S32)
|
|
{
|
|
sxth(regR, regA);
|
|
}
|
|
else if (imlInstruction->operation == PPCREC_IML_OP_NOT)
|
|
{
|
|
mvn(regR, regA);
|
|
}
|
|
else if (imlInstruction->operation == PPCREC_IML_OP_NEG)
|
|
{
|
|
neg(regR, regA);
|
|
}
|
|
else if (imlInstruction->operation == PPCREC_IML_OP_CNTLZW)
|
|
{
|
|
clz(regR, regA);
|
|
}
|
|
else
|
|
{
|
|
cemuLog_log(LogType::Recompiler, "PPCRecompilerAArch64Gen_imlInstruction_r_r(): Unsupported operation {:x}", imlInstruction->operation);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool AArch64GenContext_t::r_s32(IMLInstruction* imlInstruction)
|
|
{
|
|
sint32 imm32 = imlInstruction->op_r_immS32.immS32;
|
|
WReg reg = gpReg<WReg>(imlInstruction->op_r_immS32.regR);
|
|
|
|
if (imlInstruction->operation == PPCREC_IML_OP_ASSIGN)
|
|
{
|
|
mov(reg, imm32);
|
|
}
|
|
else if (imlInstruction->operation == PPCREC_IML_OP_LEFT_ROTATE)
|
|
{
|
|
ror(reg, reg, 32 - (imm32 & 0x1f));
|
|
}
|
|
else
|
|
{
|
|
cemuLog_log(LogType::Recompiler, "PPCRecompilerAArch64Gen_imlInstruction_r_s32(): Unsupported operation {:x}", imlInstruction->operation);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool AArch64GenContext_t::r_r_s32(IMLInstruction* imlInstruction)
|
|
{
|
|
WReg regR = gpReg<WReg>(imlInstruction->op_r_r_s32.regR);
|
|
WReg regA = gpReg<WReg>(imlInstruction->op_r_r_s32.regA);
|
|
sint32 immS32 = imlInstruction->op_r_r_s32.immS32;
|
|
|
|
if (imlInstruction->operation == PPCREC_IML_OP_ADD)
|
|
{
|
|
add_imm(regR, regA, immS32, TEMP_GPR1.WReg);
|
|
}
|
|
else if (imlInstruction->operation == PPCREC_IML_OP_SUB)
|
|
{
|
|
sub_imm(regR, regA, immS32, TEMP_GPR1.WReg);
|
|
}
|
|
else if (imlInstruction->operation == PPCREC_IML_OP_AND)
|
|
{
|
|
mov(TEMP_GPR1.WReg, immS32);
|
|
and_(regR, regA, TEMP_GPR1.WReg);
|
|
}
|
|
else if (imlInstruction->operation == PPCREC_IML_OP_OR)
|
|
{
|
|
mov(TEMP_GPR1.WReg, immS32);
|
|
orr(regR, regA, TEMP_GPR1.WReg);
|
|
}
|
|
else if (imlInstruction->operation == PPCREC_IML_OP_XOR)
|
|
{
|
|
mov(TEMP_GPR1.WReg, immS32);
|
|
eor(regR, regA, TEMP_GPR1.WReg);
|
|
}
|
|
else if (imlInstruction->operation == PPCREC_IML_OP_MULTIPLY_SIGNED)
|
|
{
|
|
mov(TEMP_GPR1.WReg, immS32);
|
|
mul(regR, regA, TEMP_GPR1.WReg);
|
|
}
|
|
else if (imlInstruction->operation == PPCREC_IML_OP_LEFT_SHIFT)
|
|
{
|
|
lsl(regR, regA, (uint32)immS32 & 0x1f);
|
|
}
|
|
else if (imlInstruction->operation == PPCREC_IML_OP_RIGHT_SHIFT_U)
|
|
{
|
|
lsr(regR, regA, (uint32)immS32 & 0x1f);
|
|
}
|
|
else if (imlInstruction->operation == PPCREC_IML_OP_RIGHT_SHIFT_S)
|
|
{
|
|
asr(regR, regA, (uint32)immS32 & 0x1f);
|
|
}
|
|
else
|
|
{
|
|
cemuLog_log(LogType::Recompiler, "PPCRecompilerAArch64Gen_imlInstruction_r_r_s32(): Unsupported operation {:x}", imlInstruction->operation);
|
|
cemu_assert_suspicious();
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool AArch64GenContext_t::r_r_s32_carry(IMLInstruction* imlInstruction)
|
|
{
|
|
WReg regR = gpReg<WReg>(imlInstruction->op_r_r_s32_carry.regR);
|
|
WReg regA = gpReg<WReg>(imlInstruction->op_r_r_s32_carry.regA);
|
|
WReg regCarry = gpReg<WReg>(imlInstruction->op_r_r_s32_carry.regCarry);
|
|
|
|
sint32 immS32 = imlInstruction->op_r_r_s32_carry.immS32;
|
|
if (imlInstruction->operation == PPCREC_IML_OP_ADD)
|
|
{
|
|
adds_imm(regR, regA, immS32, TEMP_GPR1.WReg);
|
|
cset(regCarry, Cond::CS);
|
|
}
|
|
else if (imlInstruction->operation == PPCREC_IML_OP_ADD_WITH_CARRY)
|
|
{
|
|
mov(TEMP_GPR1.WReg, immS32);
|
|
cmp(regCarry, 1);
|
|
adcs(regR, regA, TEMP_GPR1.WReg);
|
|
cset(regCarry, Cond::CS);
|
|
}
|
|
else
|
|
{
|
|
cemu_assert_suspicious();
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool AArch64GenContext_t::r_r_r(IMLInstruction* imlInstruction)
|
|
{
|
|
WReg regResult = gpReg<WReg>(imlInstruction->op_r_r_r.regR);
|
|
XReg reg64Result = aliasAs<XReg>(regResult);
|
|
WReg regOperand1 = gpReg<WReg>(imlInstruction->op_r_r_r.regA);
|
|
WReg regOperand2 = gpReg<WReg>(imlInstruction->op_r_r_r.regB);
|
|
|
|
if (imlInstruction->operation == PPCREC_IML_OP_ADD)
|
|
{
|
|
add(regResult, regOperand1, regOperand2);
|
|
}
|
|
else if (imlInstruction->operation == PPCREC_IML_OP_SUB)
|
|
{
|
|
sub(regResult, regOperand1, regOperand2);
|
|
}
|
|
else if (imlInstruction->operation == PPCREC_IML_OP_OR)
|
|
{
|
|
orr(regResult, regOperand1, regOperand2);
|
|
}
|
|
else if (imlInstruction->operation == PPCREC_IML_OP_AND)
|
|
{
|
|
and_(regResult, regOperand1, regOperand2);
|
|
}
|
|
else if (imlInstruction->operation == PPCREC_IML_OP_XOR)
|
|
{
|
|
eor(regResult, regOperand1, regOperand2);
|
|
}
|
|
else if (imlInstruction->operation == PPCREC_IML_OP_MULTIPLY_SIGNED)
|
|
{
|
|
mul(regResult, regOperand1, regOperand2);
|
|
}
|
|
else if (imlInstruction->operation == PPCREC_IML_OP_SLW)
|
|
{
|
|
tst(regOperand2, 32);
|
|
lsl(regResult, regOperand1, regOperand2);
|
|
csel(regResult, regResult, wzr, Cond::EQ);
|
|
}
|
|
else if (imlInstruction->operation == PPCREC_IML_OP_SRW)
|
|
{
|
|
tst(regOperand2, 32);
|
|
lsr(regResult, regOperand1, regOperand2);
|
|
csel(regResult, regResult, wzr, Cond::EQ);
|
|
}
|
|
else if (imlInstruction->operation == PPCREC_IML_OP_LEFT_ROTATE)
|
|
{
|
|
neg(TEMP_GPR1.WReg, regOperand2);
|
|
ror(regResult, regOperand1, TEMP_GPR1.WReg);
|
|
}
|
|
else if (imlInstruction->operation == PPCREC_IML_OP_RIGHT_SHIFT_S)
|
|
{
|
|
asr(regResult, regOperand1, regOperand2);
|
|
}
|
|
else if (imlInstruction->operation == PPCREC_IML_OP_RIGHT_SHIFT_U)
|
|
{
|
|
lsr(regResult, regOperand1, regOperand2);
|
|
}
|
|
else if (imlInstruction->operation == PPCREC_IML_OP_LEFT_SHIFT)
|
|
{
|
|
lsl(regResult, regOperand1, regOperand2);
|
|
}
|
|
else if (imlInstruction->operation == PPCREC_IML_OP_DIVIDE_SIGNED)
|
|
{
|
|
sdiv(regResult, regOperand1, regOperand2);
|
|
}
|
|
else if (imlInstruction->operation == PPCREC_IML_OP_DIVIDE_UNSIGNED)
|
|
{
|
|
udiv(regResult, regOperand1, regOperand2);
|
|
}
|
|
else if (imlInstruction->operation == PPCREC_IML_OP_MULTIPLY_HIGH_SIGNED)
|
|
{
|
|
smull(reg64Result, regOperand1, regOperand2);
|
|
lsr(reg64Result, reg64Result, 32);
|
|
}
|
|
else if (imlInstruction->operation == PPCREC_IML_OP_MULTIPLY_HIGH_UNSIGNED)
|
|
{
|
|
umull(reg64Result, regOperand1, regOperand2);
|
|
lsr(reg64Result, reg64Result, 32);
|
|
}
|
|
else
|
|
{
|
|
cemuLog_log(LogType::Recompiler, "PPCRecompilerAArch64Gen_imlInstruction_r_r_r(): Unsupported operation {:x}", imlInstruction->operation);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool AArch64GenContext_t::r_r_r_carry(IMLInstruction* imlInstruction)
|
|
{
|
|
WReg regR = gpReg<WReg>(imlInstruction->op_r_r_r_carry.regR);
|
|
WReg regA = gpReg<WReg>(imlInstruction->op_r_r_r_carry.regA);
|
|
WReg regB = gpReg<WReg>(imlInstruction->op_r_r_r_carry.regB);
|
|
WReg regCarry = gpReg<WReg>(imlInstruction->op_r_r_r_carry.regCarry);
|
|
|
|
if (imlInstruction->operation == PPCREC_IML_OP_ADD)
|
|
{
|
|
adds(regR, regA, regB);
|
|
cset(regCarry, Cond::CS);
|
|
}
|
|
else if (imlInstruction->operation == PPCREC_IML_OP_ADD_WITH_CARRY)
|
|
{
|
|
cmp(regCarry, 1);
|
|
adcs(regR, regA, regB);
|
|
cset(regCarry, Cond::CS);
|
|
}
|
|
else
|
|
{
|
|
cemu_assert_suspicious();
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
Cond ImlCondToArm64Cond(IMLCondition condition)
|
|
{
|
|
switch (condition)
|
|
{
|
|
case IMLCondition::EQ:
|
|
return Cond::EQ;
|
|
case IMLCondition::NEQ:
|
|
return Cond::NE;
|
|
case IMLCondition::UNSIGNED_GT:
|
|
return Cond::HI;
|
|
case IMLCondition::UNSIGNED_LT:
|
|
return Cond::LO;
|
|
case IMLCondition::SIGNED_GT:
|
|
return Cond::GT;
|
|
case IMLCondition::SIGNED_LT:
|
|
return Cond::LT;
|
|
default:
|
|
{
|
|
cemu_assert_suspicious();
|
|
return Cond::EQ;
|
|
}
|
|
}
|
|
}
|
|
|
|
void AArch64GenContext_t::compare(IMLInstruction* imlInstruction)
|
|
{
|
|
WReg regR = gpReg<WReg>(imlInstruction->op_compare.regR);
|
|
WReg regA = gpReg<WReg>(imlInstruction->op_compare.regA);
|
|
WReg regB = gpReg<WReg>(imlInstruction->op_compare.regB);
|
|
Cond cond = ImlCondToArm64Cond(imlInstruction->op_compare.cond);
|
|
cmp(regA, regB);
|
|
cset(regR, cond);
|
|
}
|
|
|
|
void AArch64GenContext_t::compare_s32(IMLInstruction* imlInstruction)
|
|
{
|
|
WReg regR = gpReg<WReg>(imlInstruction->op_compare.regR);
|
|
WReg regA = gpReg<WReg>(imlInstruction->op_compare.regA);
|
|
sint32 imm = imlInstruction->op_compare_s32.immS32;
|
|
auto cond = ImlCondToArm64Cond(imlInstruction->op_compare.cond);
|
|
cmp_imm(regA, imm, TEMP_GPR1.WReg);
|
|
cset(regR, cond);
|
|
}
|
|
|
|
void AArch64GenContext_t::cjump(IMLInstruction* imlInstruction, IMLSegment* imlSegment)
|
|
{
|
|
auto regBool = gpReg<WReg>(imlInstruction->op_conditional_jump.registerBool);
|
|
prepareJump(ConditionalRegJumpInfo{
|
|
.target = imlSegment->nextSegmentBranchTaken,
|
|
.regBool = regBool,
|
|
.mustBeTrue = imlInstruction->op_conditional_jump.mustBeTrue,
|
|
});
|
|
}
|
|
|
|
void AArch64GenContext_t::jump(IMLSegment* imlSegment)
|
|
{
|
|
prepareJump(UnconditionalJumpInfo{.target = imlSegment->nextSegmentBranchTaken});
|
|
}
|
|
|
|
void AArch64GenContext_t::conditionalJumpCycleCheck(IMLSegment* imlSegment)
|
|
{
|
|
ldr(TEMP_GPR1.WReg, AdrUimm(HCPU_REG, offsetof(PPCInterpreter_t, remainingCycles)));
|
|
prepareJump(NegativeRegValueJumpInfo{
|
|
.target = imlSegment->nextSegmentBranchTaken,
|
|
.regValue = TEMP_GPR1.WReg,
|
|
});
|
|
}
|
|
|
|
void* PPCRecompiler_virtualHLE(PPCInterpreter_t* ppcInterpreter, uint32 hleFuncId)
|
|
{
|
|
void* prevRSPTemp = ppcInterpreter->rspTemp;
|
|
if (hleFuncId == 0xFFD0)
|
|
{
|
|
ppcInterpreter->remainingCycles -= 500; // let subtract about 500 cycles for each HLE call
|
|
ppcInterpreter->gpr[3] = 0;
|
|
PPCInterpreter_nextInstruction(ppcInterpreter);
|
|
return PPCInterpreter_getCurrentInstance();
|
|
}
|
|
else
|
|
{
|
|
auto hleCall = PPCInterpreter_getHLECall(hleFuncId);
|
|
cemu_assert(hleCall != nullptr);
|
|
hleCall(ppcInterpreter);
|
|
}
|
|
ppcInterpreter->rspTemp = prevRSPTemp;
|
|
return PPCInterpreter_getCurrentInstance();
|
|
}
|
|
|
|
bool AArch64GenContext_t::macro(IMLInstruction* imlInstruction)
|
|
{
|
|
if (imlInstruction->operation == PPCREC_IML_MACRO_B_TO_REG)
|
|
{
|
|
WReg branchDstReg = gpReg<WReg>(imlInstruction->op_macro.paramReg);
|
|
|
|
mov(TEMP_GPR1.WReg, offsetof(PPCRecompilerInstanceData_t, ppcRecompilerDirectJumpTable));
|
|
add(TEMP_GPR1.WReg, TEMP_GPR1.WReg, branchDstReg, ShMod::LSL, 1);
|
|
ldr(TEMP_GPR1.XReg, AdrExt(PPC_REC_INSTANCE_REG, TEMP_GPR1.WReg, ExtMod::UXTW));
|
|
mov(LR.WReg, branchDstReg);
|
|
br(TEMP_GPR1.XReg);
|
|
return true;
|
|
}
|
|
else if (imlInstruction->operation == PPCREC_IML_MACRO_BL)
|
|
{
|
|
uint32 newLR = imlInstruction->op_macro.param + 4;
|
|
|
|
mov(TEMP_GPR1.WReg, newLR);
|
|
str(TEMP_GPR1.WReg, AdrUimm(HCPU_REG, offsetof(PPCInterpreter_t, spr.LR)));
|
|
|
|
uint32 newIP = imlInstruction->op_macro.param2;
|
|
uint64 lookupOffset = (uint64)offsetof(PPCRecompilerInstanceData_t, ppcRecompilerDirectJumpTable) + (uint64)newIP * 2ULL;
|
|
mov(TEMP_GPR1.XReg, lookupOffset);
|
|
ldr(TEMP_GPR1.XReg, AdrReg(PPC_REC_INSTANCE_REG, TEMP_GPR1.XReg));
|
|
mov(LR.WReg, newIP);
|
|
br(TEMP_GPR1.XReg);
|
|
return true;
|
|
}
|
|
else if (imlInstruction->operation == PPCREC_IML_MACRO_B_FAR)
|
|
{
|
|
uint32 newIP = imlInstruction->op_macro.param2;
|
|
uint64 lookupOffset = (uint64)offsetof(PPCRecompilerInstanceData_t, ppcRecompilerDirectJumpTable) + (uint64)newIP * 2ULL;
|
|
mov(TEMP_GPR1.XReg, lookupOffset);
|
|
ldr(TEMP_GPR1.XReg, AdrReg(PPC_REC_INSTANCE_REG, TEMP_GPR1.XReg));
|
|
mov(LR.WReg, newIP);
|
|
br(TEMP_GPR1.XReg);
|
|
return true;
|
|
}
|
|
else if (imlInstruction->operation == PPCREC_IML_MACRO_LEAVE)
|
|
{
|
|
uint32 currentInstructionAddress = imlInstruction->op_macro.param;
|
|
mov(TEMP_GPR1.XReg, (uint64)offsetof(PPCRecompilerInstanceData_t, ppcRecompilerDirectJumpTable)); // newIP = 0 special value for recompiler exit
|
|
ldr(TEMP_GPR1.XReg, AdrReg(PPC_REC_INSTANCE_REG, TEMP_GPR1.XReg));
|
|
mov(LR.WReg, currentInstructionAddress);
|
|
br(TEMP_GPR1.XReg);
|
|
return true;
|
|
}
|
|
else if (imlInstruction->operation == PPCREC_IML_MACRO_DEBUGBREAK)
|
|
{
|
|
brk(0xf000);
|
|
return true;
|
|
}
|
|
else if (imlInstruction->operation == PPCREC_IML_MACRO_COUNT_CYCLES)
|
|
{
|
|
uint32 cycleCount = imlInstruction->op_macro.param;
|
|
AdrUimm adrCycles = AdrUimm(HCPU_REG, offsetof(PPCInterpreter_t, remainingCycles));
|
|
ldr(TEMP_GPR1.WReg, adrCycles);
|
|
sub_imm(TEMP_GPR1.WReg, TEMP_GPR1.WReg, cycleCount, TEMP_GPR2.WReg);
|
|
str(TEMP_GPR1.WReg, adrCycles);
|
|
return true;
|
|
}
|
|
else if (imlInstruction->operation == PPCREC_IML_MACRO_HLE)
|
|
{
|
|
uint32 ppcAddress = imlInstruction->op_macro.param;
|
|
uint32 funcId = imlInstruction->op_macro.param2;
|
|
Label cyclesLeftLabel;
|
|
|
|
// update instruction pointer
|
|
mov(TEMP_GPR1.WReg, ppcAddress);
|
|
str(TEMP_GPR1.WReg, AdrUimm(HCPU_REG, offsetof(PPCInterpreter_t, instructionPointer)));
|
|
// set parameters
|
|
str(x30, AdrPreImm(sp, -16));
|
|
|
|
mov(x0, HCPU_REG);
|
|
mov(w1, funcId);
|
|
// call HLE function
|
|
|
|
mov(TEMP_GPR1.XReg, (uint64)PPCRecompiler_virtualHLE);
|
|
blr(TEMP_GPR1.XReg);
|
|
|
|
mov(HCPU_REG, x0);
|
|
|
|
ldr(x30, AdrPostImm(sp, 16));
|
|
|
|
// check if cycles where decreased beyond zero, if yes -> leave recompiler
|
|
ldr(TEMP_GPR1.WReg, AdrUimm(HCPU_REG, offsetof(PPCInterpreter_t, remainingCycles)));
|
|
tbz(TEMP_GPR1.WReg, 31, cyclesLeftLabel); // check if negative
|
|
|
|
mov(TEMP_GPR1.XReg, offsetof(PPCRecompilerInstanceData_t, ppcRecompilerDirectJumpTable));
|
|
ldr(TEMP_GPR1.XReg, AdrReg(PPC_REC_INSTANCE_REG, TEMP_GPR1.XReg));
|
|
ldr(LR.WReg, AdrUimm(HCPU_REG, offsetof(PPCInterpreter_t, instructionPointer)));
|
|
// branch to recompiler exit
|
|
br(TEMP_GPR1.XReg);
|
|
|
|
L(cyclesLeftLabel);
|
|
// check if instruction pointer was changed
|
|
// assign new instruction pointer to LR.WReg
|
|
ldr(LR.WReg, AdrUimm(HCPU_REG, offsetof(PPCInterpreter_t, instructionPointer)));
|
|
mov(TEMP_GPR1.XReg, offsetof(PPCRecompilerInstanceData_t, ppcRecompilerDirectJumpTable));
|
|
add(TEMP_GPR1.XReg, TEMP_GPR1.XReg, LR.XReg, ShMod::LSL, 1);
|
|
ldr(TEMP_GPR1.XReg, AdrReg(PPC_REC_INSTANCE_REG, TEMP_GPR1.XReg));
|
|
// branch to [ppcRecompilerDirectJumpTable + PPCInterpreter_t::instructionPointer * 2]
|
|
br(TEMP_GPR1.XReg);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
cemuLog_log(LogType::Recompiler, "Unknown recompiler macro operation %d\n", imlInstruction->operation);
|
|
cemu_assert_suspicious();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool AArch64GenContext_t::load(IMLInstruction* imlInstruction, bool indexed)
|
|
{
|
|
cemu_assert_debug(imlInstruction->op_storeLoad.registerData.GetRegFormat() == IMLRegFormat::I32);
|
|
cemu_assert_debug(imlInstruction->op_storeLoad.registerMem.GetRegFormat() == IMLRegFormat::I32);
|
|
if (indexed)
|
|
cemu_assert_debug(imlInstruction->op_storeLoad.registerMem2.GetRegFormat() == IMLRegFormat::I32);
|
|
|
|
sint32 memOffset = imlInstruction->op_storeLoad.immS32;
|
|
bool signExtend = imlInstruction->op_storeLoad.flags2.signExtend;
|
|
bool switchEndian = imlInstruction->op_storeLoad.flags2.swapEndian;
|
|
WReg memReg = gpReg<WReg>(imlInstruction->op_storeLoad.registerMem);
|
|
WReg dataReg = gpReg<WReg>(imlInstruction->op_storeLoad.registerData);
|
|
|
|
add_imm(TEMP_GPR1.WReg, memReg, memOffset, TEMP_GPR1.WReg);
|
|
if (indexed)
|
|
add(TEMP_GPR1.WReg, TEMP_GPR1.WReg, gpReg<WReg>(imlInstruction->op_storeLoad.registerMem2));
|
|
|
|
auto adr = AdrExt(MEM_BASE_REG, TEMP_GPR1.WReg, ExtMod::UXTW);
|
|
if (imlInstruction->op_storeLoad.copyWidth == 32)
|
|
{
|
|
ldr(dataReg, adr);
|
|
if (switchEndian)
|
|
rev(dataReg, dataReg);
|
|
}
|
|
else if (imlInstruction->op_storeLoad.copyWidth == 16)
|
|
{
|
|
if (switchEndian)
|
|
{
|
|
ldrh(dataReg, adr);
|
|
rev(dataReg, dataReg);
|
|
if (signExtend)
|
|
asr(dataReg, dataReg, 16);
|
|
else
|
|
lsr(dataReg, dataReg, 16);
|
|
}
|
|
else
|
|
{
|
|
if (signExtend)
|
|
ldrsh(dataReg, adr);
|
|
else
|
|
ldrh(dataReg, adr);
|
|
}
|
|
}
|
|
else if (imlInstruction->op_storeLoad.copyWidth == 8)
|
|
{
|
|
if (signExtend)
|
|
ldrsb(dataReg, adr);
|
|
else
|
|
ldrb(dataReg, adr);
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool AArch64GenContext_t::store(IMLInstruction* imlInstruction, bool indexed)
|
|
{
|
|
cemu_assert_debug(imlInstruction->op_storeLoad.registerData.GetRegFormat() == IMLRegFormat::I32);
|
|
cemu_assert_debug(imlInstruction->op_storeLoad.registerMem.GetRegFormat() == IMLRegFormat::I32);
|
|
if (indexed)
|
|
cemu_assert_debug(imlInstruction->op_storeLoad.registerMem2.GetRegFormat() == IMLRegFormat::I32);
|
|
|
|
WReg dataReg = gpReg<WReg>(imlInstruction->op_storeLoad.registerData);
|
|
WReg memReg = gpReg<WReg>(imlInstruction->op_storeLoad.registerMem);
|
|
sint32 memOffset = imlInstruction->op_storeLoad.immS32;
|
|
bool swapEndian = imlInstruction->op_storeLoad.flags2.swapEndian;
|
|
|
|
add_imm(TEMP_GPR1.WReg, memReg, memOffset, TEMP_GPR1.WReg);
|
|
if (indexed)
|
|
add(TEMP_GPR1.WReg, TEMP_GPR1.WReg, gpReg<WReg>(imlInstruction->op_storeLoad.registerMem2));
|
|
AdrExt adr = AdrExt(MEM_BASE_REG, TEMP_GPR1.WReg, ExtMod::UXTW);
|
|
if (imlInstruction->op_storeLoad.copyWidth == 32)
|
|
{
|
|
if (swapEndian)
|
|
{
|
|
rev(TEMP_GPR2.WReg, dataReg);
|
|
str(TEMP_GPR2.WReg, adr);
|
|
}
|
|
else
|
|
{
|
|
str(dataReg, adr);
|
|
}
|
|
}
|
|
else if (imlInstruction->op_storeLoad.copyWidth == 16)
|
|
{
|
|
if (swapEndian)
|
|
{
|
|
rev(TEMP_GPR2.WReg, dataReg);
|
|
lsr(TEMP_GPR2.WReg, TEMP_GPR2.WReg, 16);
|
|
strh(TEMP_GPR2.WReg, adr);
|
|
}
|
|
else
|
|
{
|
|
strh(dataReg, adr);
|
|
}
|
|
}
|
|
else if (imlInstruction->op_storeLoad.copyWidth == 8)
|
|
{
|
|
strb(dataReg, adr);
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void AArch64GenContext_t::atomic_cmp_store(IMLInstruction* imlInstruction)
|
|
{
|
|
WReg outReg = gpReg<WReg>(imlInstruction->op_atomic_compare_store.regBoolOut);
|
|
WReg eaReg = gpReg<WReg>(imlInstruction->op_atomic_compare_store.regEA);
|
|
WReg valReg = gpReg<WReg>(imlInstruction->op_atomic_compare_store.regWriteValue);
|
|
WReg cmpValReg = gpReg<WReg>(imlInstruction->op_atomic_compare_store.regCompareValue);
|
|
|
|
if (s_cpu.isAtomicSupported())
|
|
{
|
|
mov(TEMP_GPR2.WReg, cmpValReg);
|
|
add(TEMP_GPR1.XReg, MEM_BASE_REG, eaReg, ExtMod::UXTW);
|
|
casal(TEMP_GPR2.WReg, valReg, AdrNoOfs(TEMP_GPR1.XReg));
|
|
cmp(TEMP_GPR2.WReg, cmpValReg);
|
|
cset(outReg, Cond::EQ);
|
|
}
|
|
else
|
|
{
|
|
Label notEqual;
|
|
Label storeFailed;
|
|
|
|
add(TEMP_GPR1.XReg, MEM_BASE_REG, eaReg, ExtMod::UXTW);
|
|
L(storeFailed);
|
|
ldaxr(TEMP_GPR2.WReg, AdrNoOfs(TEMP_GPR1.XReg));
|
|
cmp(TEMP_GPR2.WReg, cmpValReg);
|
|
bne(notEqual);
|
|
stlxr(TEMP_GPR2.WReg, valReg, AdrNoOfs(TEMP_GPR1.XReg));
|
|
cbnz(TEMP_GPR2.WReg, storeFailed);
|
|
|
|
L(notEqual);
|
|
cset(outReg, Cond::EQ);
|
|
}
|
|
}
|
|
|
|
bool AArch64GenContext_t::fpr_load(IMLInstruction* imlInstruction, bool indexed)
|
|
{
|
|
const IMLReg& dataReg = imlInstruction->op_storeLoad.registerData;
|
|
SReg dataSReg = fpReg<SReg>(dataReg);
|
|
DReg dataDReg = fpReg<DReg>(dataReg);
|
|
WReg realRegisterMem = gpReg<WReg>(imlInstruction->op_storeLoad.registerMem);
|
|
WReg indexReg = indexed ? gpReg<WReg>(imlInstruction->op_storeLoad.registerMem2) : wzr;
|
|
sint32 adrOffset = imlInstruction->op_storeLoad.immS32;
|
|
uint8 mode = imlInstruction->op_storeLoad.mode;
|
|
|
|
if (mode == PPCREC_FPR_LD_MODE_SINGLE)
|
|
{
|
|
add_imm(TEMP_GPR1.WReg, realRegisterMem, adrOffset, TEMP_GPR1.WReg);
|
|
if (indexed)
|
|
add(TEMP_GPR1.WReg, TEMP_GPR1.WReg, indexReg);
|
|
ldr(TEMP_GPR2.WReg, AdrExt(MEM_BASE_REG, TEMP_GPR1.WReg, ExtMod::UXTW));
|
|
rev(TEMP_GPR2.WReg, TEMP_GPR2.WReg);
|
|
fmov(dataSReg, TEMP_GPR2.WReg);
|
|
|
|
if (imlInstruction->op_storeLoad.flags2.notExpanded)
|
|
{
|
|
// leave value as single
|
|
}
|
|
else
|
|
{
|
|
fcvt(dataDReg, dataSReg);
|
|
}
|
|
}
|
|
else if (mode == PPCREC_FPR_LD_MODE_DOUBLE)
|
|
{
|
|
add_imm(TEMP_GPR1.WReg, realRegisterMem, adrOffset, TEMP_GPR1.WReg);
|
|
if (indexed)
|
|
add(TEMP_GPR1.WReg, TEMP_GPR1.WReg, indexReg);
|
|
ldr(TEMP_GPR2.XReg, AdrExt(MEM_BASE_REG, TEMP_GPR1.WReg, ExtMod::UXTW));
|
|
rev(TEMP_GPR2.XReg, TEMP_GPR2.XReg);
|
|
fmov(dataDReg, TEMP_GPR2.XReg);
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// store to memory
|
|
bool AArch64GenContext_t::fpr_store(IMLInstruction* imlInstruction, bool indexed)
|
|
{
|
|
const IMLReg& dataImlReg = imlInstruction->op_storeLoad.registerData;
|
|
DReg dataDReg = fpReg<DReg>(dataImlReg);
|
|
SReg dataSReg = fpReg<SReg>(dataImlReg);
|
|
WReg memReg = gpReg<WReg>(imlInstruction->op_storeLoad.registerMem);
|
|
WReg indexReg = indexed ? gpReg<WReg>(imlInstruction->op_storeLoad.registerMem2) : wzr;
|
|
sint32 memOffset = imlInstruction->op_storeLoad.immS32;
|
|
uint8 mode = imlInstruction->op_storeLoad.mode;
|
|
|
|
if (mode == PPCREC_FPR_ST_MODE_SINGLE)
|
|
{
|
|
add_imm(TEMP_GPR1.WReg, memReg, memOffset, TEMP_GPR1.WReg);
|
|
if (indexed)
|
|
add(TEMP_GPR1.WReg, TEMP_GPR1.WReg, indexReg);
|
|
|
|
if (imlInstruction->op_storeLoad.flags2.notExpanded)
|
|
{
|
|
// value is already in single format
|
|
fmov(TEMP_GPR2.WReg, dataSReg);
|
|
}
|
|
else
|
|
{
|
|
fcvt(TEMP_FPR.SReg, dataDReg);
|
|
fmov(TEMP_GPR2.WReg, TEMP_FPR.SReg);
|
|
}
|
|
rev(TEMP_GPR2.WReg, TEMP_GPR2.WReg);
|
|
str(TEMP_GPR2.WReg, AdrExt(MEM_BASE_REG, TEMP_GPR1.WReg, ExtMod::UXTW));
|
|
}
|
|
else if (mode == PPCREC_FPR_ST_MODE_DOUBLE)
|
|
{
|
|
add_imm(TEMP_GPR1.WReg, memReg, memOffset, TEMP_GPR1.WReg);
|
|
if (indexed)
|
|
add(TEMP_GPR1.WReg, TEMP_GPR1.WReg, indexReg);
|
|
fmov(TEMP_GPR2.XReg, dataDReg);
|
|
rev(TEMP_GPR2.XReg, TEMP_GPR2.XReg);
|
|
str(TEMP_GPR2.XReg, AdrExt(MEM_BASE_REG, TEMP_GPR1.WReg, ExtMod::UXTW));
|
|
}
|
|
else if (mode == PPCREC_FPR_ST_MODE_UI32_FROM_PS0)
|
|
{
|
|
add_imm(TEMP_GPR1.WReg, memReg, memOffset, TEMP_GPR1.WReg);
|
|
if (indexed)
|
|
add(TEMP_GPR1.WReg, TEMP_GPR1.WReg, indexReg);
|
|
fmov(TEMP_GPR2.WReg, dataSReg);
|
|
rev(TEMP_GPR2.WReg, TEMP_GPR2.WReg);
|
|
str(TEMP_GPR2.WReg, AdrExt(MEM_BASE_REG, TEMP_GPR1.WReg, ExtMod::UXTW));
|
|
}
|
|
else
|
|
{
|
|
cemu_assert_suspicious();
|
|
cemuLog_log(LogType::Recompiler, "PPCRecompilerAArch64Gen_imlInstruction_fpr_store(): Unsupported mode %d\n", mode);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// FPR op FPR
|
|
void AArch64GenContext_t::fpr_r_r(IMLInstruction* imlInstruction)
|
|
{
|
|
auto imlRegR = imlInstruction->op_fpr_r_r.regR;
|
|
auto imlRegA = imlInstruction->op_fpr_r_r.regA;
|
|
|
|
if (imlInstruction->operation == PPCREC_IML_OP_FPR_FLOAT_TO_INT)
|
|
{
|
|
fcvtzs(gpReg<WReg>(imlRegR), fpReg<DReg>(imlRegA));
|
|
return;
|
|
}
|
|
else if (imlInstruction->operation == PPCREC_IML_OP_FPR_INT_TO_FLOAT)
|
|
{
|
|
scvtf(fpReg<DReg>(imlRegR), gpReg<WReg>(imlRegA));
|
|
return;
|
|
}
|
|
else if (imlInstruction->operation == PPCREC_IML_OP_FPR_BITCAST_INT_TO_FLOAT)
|
|
{
|
|
cemu_assert_debug(imlRegR.GetRegFormat() == IMLRegFormat::F64); // assuming target is always F64 for now
|
|
// exact operation depends on size of types. Floats are automatically promoted to double if the target is F64
|
|
DReg regFprDReg = fpReg<DReg>(imlRegR);
|
|
SReg regFprSReg = fpReg<SReg>(imlRegR);
|
|
if (imlRegA.GetRegFormat() == IMLRegFormat::I32)
|
|
{
|
|
fmov(regFprSReg, gpReg<WReg>(imlRegA));
|
|
// float to double
|
|
fcvt(regFprDReg, regFprSReg);
|
|
}
|
|
else if (imlRegA.GetRegFormat() == IMLRegFormat::I64)
|
|
{
|
|
fmov(regFprDReg, gpReg<XReg>(imlRegA));
|
|
}
|
|
else
|
|
{
|
|
cemu_assert_unimplemented();
|
|
}
|
|
return;
|
|
}
|
|
|
|
DReg regR = fpReg<DReg>(imlRegR);
|
|
DReg regA = fpReg<DReg>(imlRegA);
|
|
|
|
if (imlInstruction->operation == PPCREC_IML_OP_FPR_ASSIGN)
|
|
{
|
|
fmov(regR, regA);
|
|
}
|
|
else if (imlInstruction->operation == PPCREC_IML_OP_FPR_MULTIPLY)
|
|
{
|
|
fmul(regR, regR, regA);
|
|
}
|
|
else if (imlInstruction->operation == PPCREC_IML_OP_FPR_DIVIDE)
|
|
{
|
|
fdiv(regR, regR, regA);
|
|
}
|
|
else if (imlInstruction->operation == PPCREC_IML_OP_FPR_ADD)
|
|
{
|
|
fadd(regR, regR, regA);
|
|
}
|
|
else if (imlInstruction->operation == PPCREC_IML_OP_FPR_SUB)
|
|
{
|
|
fsub(regR, regR, regA);
|
|
}
|
|
else if (imlInstruction->operation == PPCREC_IML_OP_FPR_FCTIWZ)
|
|
{
|
|
fcvtzs(regR, regA);
|
|
}
|
|
else
|
|
{
|
|
cemu_assert_suspicious();
|
|
}
|
|
}
|
|
|
|
void AArch64GenContext_t::fpr_r_r_r(IMLInstruction* imlInstruction)
|
|
{
|
|
DReg regR = fpReg<DReg>(imlInstruction->op_fpr_r_r_r.regR);
|
|
DReg regA = fpReg<DReg>(imlInstruction->op_fpr_r_r_r.regA);
|
|
DReg regB = fpReg<DReg>(imlInstruction->op_fpr_r_r_r.regB);
|
|
|
|
if (imlInstruction->operation == PPCREC_IML_OP_FPR_MULTIPLY)
|
|
{
|
|
fmul(regR, regA, regB);
|
|
}
|
|
else if (imlInstruction->operation == PPCREC_IML_OP_FPR_ADD)
|
|
{
|
|
fadd(regR, regA, regB);
|
|
}
|
|
else if (imlInstruction->operation == PPCREC_IML_OP_FPR_SUB)
|
|
{
|
|
fsub(regR, regA, regB);
|
|
}
|
|
else
|
|
{
|
|
cemu_assert_suspicious();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* FPR = op (fprA, fprB, fprC)
|
|
*/
|
|
void AArch64GenContext_t::fpr_r_r_r_r(IMLInstruction* imlInstruction)
|
|
{
|
|
DReg regR = fpReg<DReg>(imlInstruction->op_fpr_r_r_r_r.regR);
|
|
DReg regA = fpReg<DReg>(imlInstruction->op_fpr_r_r_r_r.regA);
|
|
DReg regB = fpReg<DReg>(imlInstruction->op_fpr_r_r_r_r.regB);
|
|
DReg regC = fpReg<DReg>(imlInstruction->op_fpr_r_r_r_r.regC);
|
|
|
|
if (imlInstruction->operation == PPCREC_IML_OP_FPR_SELECT)
|
|
{
|
|
fcmp(regA, 0.0);
|
|
fcsel(regR, regC, regB, Cond::GE);
|
|
}
|
|
else
|
|
{
|
|
cemu_assert_suspicious();
|
|
}
|
|
}
|
|
|
|
void AArch64GenContext_t::fpr_r(IMLInstruction* imlInstruction)
|
|
{
|
|
DReg regRDReg = fpReg<DReg>(imlInstruction->op_fpr_r.regR);
|
|
SReg regRSReg = fpReg<SReg>(imlInstruction->op_fpr_r.regR);
|
|
|
|
if (imlInstruction->operation == PPCREC_IML_OP_FPR_NEGATE)
|
|
{
|
|
fneg(regRDReg, regRDReg);
|
|
}
|
|
else if (imlInstruction->operation == PPCREC_IML_OP_FPR_LOAD_ONE)
|
|
{
|
|
fmov(regRDReg, 1.0);
|
|
}
|
|
else if (imlInstruction->operation == PPCREC_IML_OP_FPR_ABS)
|
|
{
|
|
fabs(regRDReg, regRDReg);
|
|
}
|
|
else if (imlInstruction->operation == PPCREC_IML_OP_FPR_NEGATIVE_ABS)
|
|
{
|
|
fabs(regRDReg, regRDReg);
|
|
fneg(regRDReg, regRDReg);
|
|
}
|
|
else if (imlInstruction->operation == PPCREC_IML_OP_FPR_ROUND_TO_SINGLE_PRECISION_BOTTOM)
|
|
{
|
|
// convert to 32bit single
|
|
fcvt(regRSReg, regRDReg);
|
|
// convert back to 64bit double
|
|
fcvt(regRDReg, regRSReg);
|
|
}
|
|
else if (imlInstruction->operation == PPCREC_IML_OP_FPR_EXPAND_F32_TO_F64)
|
|
{
|
|
// convert bottom to 64bit double
|
|
fcvt(regRDReg, regRSReg);
|
|
}
|
|
else
|
|
{
|
|
cemu_assert_unimplemented();
|
|
}
|
|
}
|
|
|
|
Cond ImlFPCondToArm64Cond(IMLCondition cond)
|
|
{
|
|
switch (cond)
|
|
{
|
|
case IMLCondition::UNORDERED_GT:
|
|
return Cond::GT;
|
|
case IMLCondition::UNORDERED_LT:
|
|
return Cond::MI;
|
|
case IMLCondition::UNORDERED_EQ:
|
|
return Cond::EQ;
|
|
case IMLCondition::UNORDERED_U:
|
|
return Cond::VS;
|
|
default:
|
|
{
|
|
cemu_assert_suspicious();
|
|
return Cond::EQ;
|
|
}
|
|
}
|
|
}
|
|
|
|
void AArch64GenContext_t::fpr_compare(IMLInstruction* imlInstruction)
|
|
{
|
|
WReg regR = gpReg<WReg>(imlInstruction->op_fpr_compare.regR);
|
|
DReg regA = fpReg<DReg>(imlInstruction->op_fpr_compare.regA);
|
|
DReg regB = fpReg<DReg>(imlInstruction->op_fpr_compare.regB);
|
|
auto cond = ImlFPCondToArm64Cond(imlInstruction->op_fpr_compare.cond);
|
|
fcmp(regA, regB);
|
|
cset(regR, cond);
|
|
}
|
|
|
|
void AArch64GenContext_t::call_imm(IMLInstruction* imlInstruction)
|
|
{
|
|
str(x30, AdrPreImm(sp, -16));
|
|
mov(TEMP_GPR1.XReg, imlInstruction->op_call_imm.callAddress);
|
|
blr(TEMP_GPR1.XReg);
|
|
ldr(x30, AdrPostImm(sp, 16));
|
|
}
|
|
|
|
bool PPCRecompiler_generateAArch64Code(struct PPCRecFunction_t* PPCRecFunction, struct ppcImlGenContext_t* ppcImlGenContext)
|
|
{
|
|
AArch64Allocator allocator;
|
|
AArch64GenContext_t aarch64GenContext{&allocator};
|
|
|
|
// generate iml instruction code
|
|
bool codeGenerationFailed = false;
|
|
for (IMLSegment* segIt : ppcImlGenContext->segmentList2)
|
|
{
|
|
if (codeGenerationFailed)
|
|
break;
|
|
segIt->x64Offset = aarch64GenContext.getSize();
|
|
|
|
aarch64GenContext.storeSegmentStart(segIt);
|
|
|
|
for (size_t i = 0; i < segIt->imlList.size(); i++)
|
|
{
|
|
IMLInstruction* imlInstruction = segIt->imlList.data() + i;
|
|
if (imlInstruction->type == PPCREC_IML_TYPE_R_NAME)
|
|
{
|
|
aarch64GenContext.r_name(imlInstruction);
|
|
}
|
|
else if (imlInstruction->type == PPCREC_IML_TYPE_NAME_R)
|
|
{
|
|
aarch64GenContext.name_r(imlInstruction);
|
|
}
|
|
else if (imlInstruction->type == PPCREC_IML_TYPE_R_R)
|
|
{
|
|
if (!aarch64GenContext.r_r(imlInstruction))
|
|
codeGenerationFailed = true;
|
|
}
|
|
else if (imlInstruction->type == PPCREC_IML_TYPE_R_S32)
|
|
{
|
|
if (!aarch64GenContext.r_s32(imlInstruction))
|
|
codeGenerationFailed = true;
|
|
}
|
|
else if (imlInstruction->type == PPCREC_IML_TYPE_R_R_S32)
|
|
{
|
|
if (!aarch64GenContext.r_r_s32(imlInstruction))
|
|
codeGenerationFailed = true;
|
|
}
|
|
else if (imlInstruction->type == PPCREC_IML_TYPE_R_R_S32_CARRY)
|
|
{
|
|
if (!aarch64GenContext.r_r_s32_carry(imlInstruction))
|
|
codeGenerationFailed = true;
|
|
}
|
|
else if (imlInstruction->type == PPCREC_IML_TYPE_R_R_R)
|
|
{
|
|
if (!aarch64GenContext.r_r_r(imlInstruction))
|
|
codeGenerationFailed = true;
|
|
}
|
|
else if (imlInstruction->type == PPCREC_IML_TYPE_R_R_R_CARRY)
|
|
{
|
|
if (!aarch64GenContext.r_r_r_carry(imlInstruction))
|
|
codeGenerationFailed = true;
|
|
}
|
|
else if (imlInstruction->type == PPCREC_IML_TYPE_COMPARE)
|
|
{
|
|
aarch64GenContext.compare(imlInstruction);
|
|
}
|
|
else if (imlInstruction->type == PPCREC_IML_TYPE_COMPARE_S32)
|
|
{
|
|
aarch64GenContext.compare_s32(imlInstruction);
|
|
}
|
|
else if (imlInstruction->type == PPCREC_IML_TYPE_CONDITIONAL_JUMP)
|
|
{
|
|
aarch64GenContext.cjump(imlInstruction, segIt);
|
|
}
|
|
else if (imlInstruction->type == PPCREC_IML_TYPE_JUMP)
|
|
{
|
|
aarch64GenContext.jump(segIt);
|
|
}
|
|
else if (imlInstruction->type == PPCREC_IML_TYPE_CJUMP_CYCLE_CHECK)
|
|
{
|
|
aarch64GenContext.conditionalJumpCycleCheck(segIt);
|
|
}
|
|
else if (imlInstruction->type == PPCREC_IML_TYPE_MACRO)
|
|
{
|
|
if (!aarch64GenContext.macro(imlInstruction))
|
|
codeGenerationFailed = true;
|
|
}
|
|
else if (imlInstruction->type == PPCREC_IML_TYPE_LOAD)
|
|
{
|
|
if (!aarch64GenContext.load(imlInstruction, false))
|
|
codeGenerationFailed = true;
|
|
}
|
|
else if (imlInstruction->type == PPCREC_IML_TYPE_LOAD_INDEXED)
|
|
{
|
|
if (!aarch64GenContext.load(imlInstruction, true))
|
|
codeGenerationFailed = true;
|
|
}
|
|
else if (imlInstruction->type == PPCREC_IML_TYPE_STORE)
|
|
{
|
|
if (!aarch64GenContext.store(imlInstruction, false))
|
|
codeGenerationFailed = true;
|
|
}
|
|
else if (imlInstruction->type == PPCREC_IML_TYPE_STORE_INDEXED)
|
|
{
|
|
if (!aarch64GenContext.store(imlInstruction, true))
|
|
codeGenerationFailed = true;
|
|
}
|
|
else if (imlInstruction->type == PPCREC_IML_TYPE_ATOMIC_CMP_STORE)
|
|
{
|
|
aarch64GenContext.atomic_cmp_store(imlInstruction);
|
|
}
|
|
else if (imlInstruction->type == PPCREC_IML_TYPE_CALL_IMM)
|
|
{
|
|
aarch64GenContext.call_imm(imlInstruction);
|
|
}
|
|
else if (imlInstruction->type == PPCREC_IML_TYPE_NO_OP)
|
|
{
|
|
// no op
|
|
}
|
|
else if (imlInstruction->type == PPCREC_IML_TYPE_FPR_LOAD)
|
|
{
|
|
if (!aarch64GenContext.fpr_load(imlInstruction, false))
|
|
codeGenerationFailed = true;
|
|
}
|
|
else if (imlInstruction->type == PPCREC_IML_TYPE_FPR_LOAD_INDEXED)
|
|
{
|
|
if (!aarch64GenContext.fpr_load(imlInstruction, true))
|
|
codeGenerationFailed = true;
|
|
}
|
|
else if (imlInstruction->type == PPCREC_IML_TYPE_FPR_STORE)
|
|
{
|
|
if (!aarch64GenContext.fpr_store(imlInstruction, false))
|
|
codeGenerationFailed = true;
|
|
}
|
|
else if (imlInstruction->type == PPCREC_IML_TYPE_FPR_STORE_INDEXED)
|
|
{
|
|
if (!aarch64GenContext.fpr_store(imlInstruction, true))
|
|
codeGenerationFailed = true;
|
|
}
|
|
else if (imlInstruction->type == PPCREC_IML_TYPE_FPR_R_R)
|
|
{
|
|
aarch64GenContext.fpr_r_r(imlInstruction);
|
|
}
|
|
else if (imlInstruction->type == PPCREC_IML_TYPE_FPR_R_R_R)
|
|
{
|
|
aarch64GenContext.fpr_r_r_r(imlInstruction);
|
|
}
|
|
else if (imlInstruction->type == PPCREC_IML_TYPE_FPR_R_R_R_R)
|
|
{
|
|
aarch64GenContext.fpr_r_r_r_r(imlInstruction);
|
|
}
|
|
else if (imlInstruction->type == PPCREC_IML_TYPE_FPR_R)
|
|
{
|
|
aarch64GenContext.fpr_r(imlInstruction);
|
|
}
|
|
else if (imlInstruction->type == PPCREC_IML_TYPE_FPR_COMPARE)
|
|
{
|
|
aarch64GenContext.fpr_compare(imlInstruction);
|
|
}
|
|
else
|
|
{
|
|
codeGenerationFailed = true;
|
|
cemu_assert_suspicious();
|
|
cemuLog_log(LogType::Recompiler, "PPCRecompiler_generateAArch64Code(): Unsupported iml type {}", imlInstruction->type);
|
|
}
|
|
}
|
|
}
|
|
|
|
// handle failed code generation
|
|
if (codeGenerationFailed)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!aarch64GenContext.processAllJumps())
|
|
{
|
|
cemuLog_log(LogType::Recompiler, "PPCRecompiler_generateAArch64Code(): some jumps exceeded the +/-128MB offset.");
|
|
return false;
|
|
}
|
|
|
|
aarch64GenContext.readyRE();
|
|
|
|
// set code
|
|
PPCRecFunction->x86Code = aarch64GenContext.getCode<void*>();
|
|
PPCRecFunction->x86Size = aarch64GenContext.getMaxSize();
|
|
// set free disabled to skip freeing the code from the CodeGenerator destructor
|
|
allocator.setFreeDisabled(true);
|
|
return true;
|
|
}
|
|
|
|
void PPCRecompiler_cleanupAArch64Code(void* code, size_t size)
|
|
{
|
|
AArch64Allocator allocator;
|
|
if (allocator.useProtect())
|
|
CodeArray::protect(code, size, CodeArray::PROTECT_RW);
|
|
allocator.free(static_cast<uint32*>(code));
|
|
}
|
|
|
|
void AArch64GenContext_t::enterRecompilerCode()
|
|
{
|
|
constexpr size_t STACK_SIZE = 160 /* x19 .. x30 + v8.d[0] .. v15.d[0] */;
|
|
static_assert(STACK_SIZE % 16 == 0);
|
|
sub(sp, sp, STACK_SIZE);
|
|
mov(x9, sp);
|
|
|
|
stp(x19, x20, AdrPostImm(x9, 16));
|
|
stp(x21, x22, AdrPostImm(x9, 16));
|
|
stp(x23, x24, AdrPostImm(x9, 16));
|
|
stp(x25, x26, AdrPostImm(x9, 16));
|
|
stp(x27, x28, AdrPostImm(x9, 16));
|
|
stp(x29, x30, AdrPostImm(x9, 16));
|
|
st4((v8.d - v11.d)[0], AdrPostImm(x9, 32));
|
|
st4((v12.d - v15.d)[0], AdrPostImm(x9, 32));
|
|
mov(HCPU_REG, x1); // call argument 2
|
|
mov(PPC_REC_INSTANCE_REG, (uint64)ppcRecompilerInstanceData);
|
|
mov(MEM_BASE_REG, (uint64)memory_base);
|
|
|
|
// branch to recFunc
|
|
blr(x0); // call argument 1
|
|
|
|
mov(x9, sp);
|
|
ldp(x19, x20, AdrPostImm(x9, 16));
|
|
ldp(x21, x22, AdrPostImm(x9, 16));
|
|
ldp(x23, x24, AdrPostImm(x9, 16));
|
|
ldp(x25, x26, AdrPostImm(x9, 16));
|
|
ldp(x27, x28, AdrPostImm(x9, 16));
|
|
ldp(x29, x30, AdrPostImm(x9, 16));
|
|
ld4((v8.d - v11.d)[0], AdrPostImm(x9, 32));
|
|
ld4((v12.d - v15.d)[0], AdrPostImm(x9, 32));
|
|
|
|
add(sp, sp, STACK_SIZE);
|
|
|
|
ret();
|
|
}
|
|
|
|
void AArch64GenContext_t::leaveRecompilerCode()
|
|
{
|
|
str(LR.WReg, AdrUimm(HCPU_REG, offsetof(PPCInterpreter_t, instructionPointer)));
|
|
ret();
|
|
}
|
|
|
|
bool initializedInterfaceFunctions = false;
|
|
AArch64GenContext_t enterRecompilerCode_ctx{};
|
|
|
|
AArch64GenContext_t leaveRecompilerCode_unvisited_ctx{};
|
|
AArch64GenContext_t leaveRecompilerCode_visited_ctx{};
|
|
void PPCRecompilerAArch64Gen_generateRecompilerInterfaceFunctions()
|
|
{
|
|
if (initializedInterfaceFunctions)
|
|
return;
|
|
initializedInterfaceFunctions = true;
|
|
|
|
enterRecompilerCode_ctx.enterRecompilerCode();
|
|
enterRecompilerCode_ctx.readyRE();
|
|
PPCRecompiler_enterRecompilerCode = enterRecompilerCode_ctx.getCode<decltype(PPCRecompiler_enterRecompilerCode)>();
|
|
|
|
leaveRecompilerCode_unvisited_ctx.leaveRecompilerCode();
|
|
leaveRecompilerCode_unvisited_ctx.readyRE();
|
|
PPCRecompiler_leaveRecompilerCode_unvisited = leaveRecompilerCode_unvisited_ctx.getCode<decltype(PPCRecompiler_leaveRecompilerCode_unvisited)>();
|
|
|
|
leaveRecompilerCode_visited_ctx.leaveRecompilerCode();
|
|
leaveRecompilerCode_visited_ctx.readyRE();
|
|
PPCRecompiler_leaveRecompilerCode_visited = leaveRecompilerCode_visited_ctx.getCode<decltype(PPCRecompiler_leaveRecompilerCode_visited)>();
|
|
}
|