mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
Merge pull request #18059 from unknownbrackets/arm64-ir-jit
arm64jit: Add initial base for IR jit
This commit is contained in:
commit
daa0586641
32 changed files with 2656 additions and 22 deletions
|
@ -1591,6 +1591,17 @@ list(APPEND CoreExtra
|
|||
Core/MIPS/ARM64/Arm64RegCache.h
|
||||
Core/MIPS/ARM64/Arm64RegCacheFPU.cpp
|
||||
Core/MIPS/ARM64/Arm64RegCacheFPU.h
|
||||
Core/MIPS/ARM64/Arm64IRAsm.cpp
|
||||
Core/MIPS/ARM64/Arm64IRCompALU.cpp
|
||||
Core/MIPS/ARM64/Arm64IRCompBranch.cpp
|
||||
Core/MIPS/ARM64/Arm64IRCompFPU.cpp
|
||||
Core/MIPS/ARM64/Arm64IRCompLoadStore.cpp
|
||||
Core/MIPS/ARM64/Arm64IRCompSystem.cpp
|
||||
Core/MIPS/ARM64/Arm64IRCompVec.cpp
|
||||
Core/MIPS/ARM64/Arm64IRJit.cpp
|
||||
Core/MIPS/ARM64/Arm64IRJit.h
|
||||
Core/MIPS/ARM64/Arm64IRRegCache.cpp
|
||||
Core/MIPS/ARM64/Arm64IRRegCache.h
|
||||
GPU/Common/VertexDecoderArm64.cpp
|
||||
Core/Util/DisArm64.cpp
|
||||
)
|
||||
|
|
|
@ -514,7 +514,7 @@ void ARM64XEmitter::EncodeTestBranchInst(u32 op, ARM64Reg Rt, u8 bits, const voi
|
|||
|
||||
distance >>= 2;
|
||||
|
||||
_assert_msg_(distance >= -0x1FFF && distance < 0x1FFF, "%s: Received too large distance: %llx", __FUNCTION__, distance);
|
||||
_assert_msg_(distance >= -0x2000 && distance <= 0x1FFF, "%s: Received too large distance: %llx", __FUNCTION__, distance);
|
||||
|
||||
Rt = DecodeReg(Rt);
|
||||
Write32((b64Bit << 31) | (0x36 << 24) | (op << 24) | \
|
||||
|
|
|
@ -580,6 +580,15 @@
|
|||
<ClCompile Include="KeyMap.cpp" />
|
||||
<ClCompile Include="KeyMapDefaults.cpp" />
|
||||
<ClCompile Include="MemFault.cpp" />
|
||||
<ClCompile Include="MIPS\ARM64\Arm64IRAsm.cpp" />
|
||||
<ClCompile Include="MIPS\ARM64\Arm64IRCompALU.cpp" />
|
||||
<ClCompile Include="MIPS\ARM64\Arm64IRCompBranch.cpp" />
|
||||
<ClCompile Include="MIPS\ARM64\Arm64IRCompFPU.cpp" />
|
||||
<ClCompile Include="MIPS\ARM64\Arm64IRCompLoadStore.cpp" />
|
||||
<ClCompile Include="MIPS\ARM64\Arm64IRCompSystem.cpp" />
|
||||
<ClCompile Include="MIPS\ARM64\Arm64IRCompVec.cpp" />
|
||||
<ClCompile Include="MIPS\ARM64\Arm64IRJit.cpp" />
|
||||
<ClCompile Include="MIPS\ARM64\Arm64IRRegCache.cpp" />
|
||||
<ClCompile Include="MIPS\fake\FakeJit.cpp" />
|
||||
<ClCompile Include="MIPS\IR\IRAnalysis.cpp" />
|
||||
<ClCompile Include="MIPS\IR\IRAsm.cpp" />
|
||||
|
@ -1175,6 +1184,8 @@
|
|||
<ClInclude Include="KeyMap.h" />
|
||||
<ClInclude Include="KeyMapDefaults.h" />
|
||||
<ClInclude Include="MemFault.h" />
|
||||
<ClInclude Include="MIPS\ARM64\Arm64IRJit.h" />
|
||||
<ClInclude Include="MIPS\ARM64\Arm64IRRegCache.h" />
|
||||
<ClInclude Include="MIPS\fake\FakeJit.h" />
|
||||
<ClInclude Include="MIPS\IR\IRAnalysis.h" />
|
||||
<ClInclude Include="MIPS\IR\IRFrontend.h" />
|
||||
|
|
|
@ -1270,6 +1270,33 @@
|
|||
<ClCompile Include="MIPS\x86\X64IRCompALU.cpp">
|
||||
<Filter>MIPS\x86</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="MIPS\ARM64\Arm64IRCompLoadStore.cpp">
|
||||
<Filter>MIPS\ARM64</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="MIPS\ARM64\Arm64IRCompSystem.cpp">
|
||||
<Filter>MIPS\ARM64</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="MIPS\ARM64\Arm64IRCompVec.cpp">
|
||||
<Filter>MIPS\ARM64</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="MIPS\ARM64\Arm64IRJit.cpp">
|
||||
<Filter>MIPS\ARM64</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="MIPS\ARM64\Arm64IRRegCache.cpp">
|
||||
<Filter>MIPS\ARM64</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="MIPS\ARM64\Arm64IRAsm.cpp">
|
||||
<Filter>MIPS\ARM64</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="MIPS\ARM64\Arm64IRCompALU.cpp">
|
||||
<Filter>MIPS\ARM64</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="MIPS\ARM64\Arm64IRCompBranch.cpp">
|
||||
<Filter>MIPS\ARM64</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="MIPS\ARM64\Arm64IRCompFPU.cpp">
|
||||
<Filter>MIPS\ARM64</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="ELF\ElfReader.h">
|
||||
|
@ -2037,6 +2064,12 @@
|
|||
<ClInclude Include="MIPS\x86\X64IRJit.h">
|
||||
<Filter>MIPS\x86</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="MIPS\ARM64\Arm64IRJit.h">
|
||||
<Filter>MIPS\ARM64</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="MIPS\ARM64\Arm64IRRegCache.h">
|
||||
<Filter>MIPS\ARM64</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\LICENSE.TXT" />
|
||||
|
|
274
Core/MIPS/ARM64/Arm64IRAsm.cpp
Normal file
274
Core/MIPS/ARM64/Arm64IRAsm.cpp
Normal file
|
@ -0,0 +1,274 @@
|
|||
// Copyright (c) 2023- PPSSPP Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0 or later versions.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#include "ppsspp_config.h"
|
||||
// In other words, PPSSPP_ARCH(ARM64) || DISASM_ALL.
|
||||
#if PPSSPP_ARCH(ARM64) || (PPSSPP_PLATFORM(WINDOWS) && !defined(__LIBRETRO__))
|
||||
|
||||
#include "Common/Log.h"
|
||||
#include "Core/CoreTiming.h"
|
||||
#include "Core/MemMap.h"
|
||||
#include "Core/MIPS/ARM64/Arm64IRJit.h"
|
||||
#include "Core/MIPS/ARM64/Arm64IRRegCache.h"
|
||||
#include "Core/MIPS/JitCommon/JitCommon.h"
|
||||
#include "Core/MIPS/JitCommon/JitState.h"
|
||||
#include "Core/System.h"
|
||||
|
||||
namespace MIPSComp {
|
||||
|
||||
using namespace Arm64Gen;
|
||||
using namespace Arm64IRJitConstants;
|
||||
|
||||
static const bool enableDebug = false;
|
||||
static const bool enableDisasm = false;
|
||||
|
||||
static void ShowPC(void *membase, void *jitbase) {
|
||||
static int count = 0;
|
||||
if (currentMIPS) {
|
||||
u32 downcount = currentMIPS->downcount;
|
||||
ERROR_LOG(JIT, "[%08x] ShowPC Downcount : %08x %d %p %p", currentMIPS->pc, downcount, count, membase, jitbase);
|
||||
} else {
|
||||
ERROR_LOG(JIT, "Universe corrupt?");
|
||||
}
|
||||
//if (count > 2000)
|
||||
// exit(0);
|
||||
count++;
|
||||
}
|
||||
|
||||
void Arm64JitBackend::GenerateFixedCode(MIPSState *mipsState) {
|
||||
BeginWrite(GetMemoryProtectPageSize());
|
||||
const u8 *start = AlignCodePage();
|
||||
|
||||
if (jo.useStaticAlloc) {
|
||||
saveStaticRegisters_ = AlignCode16();
|
||||
STR(INDEX_UNSIGNED, DOWNCOUNTREG, CTXREG, offsetof(MIPSState, downcount));
|
||||
regs_.EmitSaveStaticRegisters();
|
||||
RET();
|
||||
|
||||
loadStaticRegisters_ = AlignCode16();
|
||||
regs_.EmitLoadStaticRegisters();
|
||||
LDR(INDEX_UNSIGNED, DOWNCOUNTREG, CTXREG, offsetof(MIPSState, downcount));
|
||||
RET();
|
||||
|
||||
start = saveStaticRegisters_;
|
||||
} else {
|
||||
saveStaticRegisters_ = nullptr;
|
||||
loadStaticRegisters_ = nullptr;
|
||||
}
|
||||
|
||||
restoreRoundingMode_ = AlignCode16();
|
||||
{
|
||||
MRS(SCRATCH2_64, FIELD_FPCR);
|
||||
// We are not in flush-to-zero mode outside the JIT, so let's turn it off.
|
||||
uint32_t mask = ~(4 << 22);
|
||||
// Assume we're always in round-to-nearest mode beforehand.
|
||||
mask &= ~(3 << 22);
|
||||
ANDI2R(SCRATCH2, SCRATCH2, mask);
|
||||
_MSR(FIELD_FPCR, SCRATCH2_64);
|
||||
RET();
|
||||
}
|
||||
|
||||
applyRoundingMode_ = AlignCode16();
|
||||
{
|
||||
LDR(INDEX_UNSIGNED, SCRATCH1, CTXREG, offsetof(MIPSState, fcr31));
|
||||
ANDI2R(SCRATCH2, SCRATCH1, 3);
|
||||
FixupBranch skip1 = TBZ(SCRATCH1, 24);
|
||||
ADDI2R(SCRATCH2, SCRATCH2, 4);
|
||||
SetJumpTarget(skip1);
|
||||
|
||||
// We can skip if the rounding mode is nearest (0) and flush is not set.
|
||||
// (as restoreRoundingMode cleared it out anyway)
|
||||
FixupBranch skip = CBZ(SCRATCH2);
|
||||
|
||||
// MIPS Rounding Mode: ARM Rounding Mode
|
||||
// 0: Round nearest 0
|
||||
// 1: Round to zero 3
|
||||
// 2: Round up (ceil) 1
|
||||
// 3: Round down (floor) 2
|
||||
ANDI2R(SCRATCH1, SCRATCH2, 3);
|
||||
CMPI2R(SCRATCH1, 1);
|
||||
|
||||
FixupBranch skipadd = B(CC_NEQ);
|
||||
ADDI2R(SCRATCH2, SCRATCH2, 2);
|
||||
SetJumpTarget(skipadd);
|
||||
FixupBranch skipsub = B(CC_LE);
|
||||
SUBI2R(SCRATCH2, SCRATCH2, 1);
|
||||
SetJumpTarget(skipsub);
|
||||
|
||||
// Actually change the system FPCR register
|
||||
MRS(SCRATCH1_64, FIELD_FPCR);
|
||||
// Clear both flush-to-zero and rounding before re-setting them.
|
||||
ANDI2R(SCRATCH1, SCRATCH1, ~((4 | 3) << 22));
|
||||
ORR(SCRATCH1, SCRATCH1, SCRATCH2, ArithOption(SCRATCH2, ST_LSL, 22));
|
||||
_MSR(FIELD_FPCR, SCRATCH1_64);
|
||||
|
||||
SetJumpTarget(skip);
|
||||
RET();
|
||||
}
|
||||
|
||||
updateRoundingMode_ = AlignCode16();
|
||||
{
|
||||
LDR(INDEX_UNSIGNED, SCRATCH1, CTXREG, offsetof(MIPSState, fcr31));
|
||||
|
||||
// Set SCRATCH2 to FZ:RM (FZ is bit 24, and RM are lowest 2 bits.)
|
||||
ANDI2R(SCRATCH2, SCRATCH1, 3);
|
||||
FixupBranch skip = TBZ(SCRATCH1, 24);
|
||||
ADDI2R(SCRATCH2, SCRATCH2, 4);
|
||||
SetJumpTarget(skip);
|
||||
|
||||
// Update currentRoundingFunc_ with the right convertS0ToSCRATCH1_ func.
|
||||
MOVP2R(SCRATCH1_64, convertS0ToSCRATCH1_);
|
||||
LSL(SCRATCH2, SCRATCH2, 3);
|
||||
LDR(SCRATCH2_64, SCRATCH1_64, SCRATCH2);
|
||||
MOVP2R(SCRATCH1_64, ¤tRoundingFunc_);
|
||||
STR(INDEX_UNSIGNED, SCRATCH2_64, SCRATCH1_64, 0);
|
||||
RET();
|
||||
}
|
||||
|
||||
hooks_.enterDispatcher = (IRNativeFuncNoArg)AlignCode16();
|
||||
|
||||
uint32_t regs_to_save = Arm64Gen::ALL_CALLEE_SAVED;
|
||||
uint32_t regs_to_save_fp = Arm64Gen::ALL_CALLEE_SAVED_FP;
|
||||
fp_.ABI_PushRegisters(regs_to_save, regs_to_save_fp);
|
||||
|
||||
// Fixed registers, these are always kept when in Jit context.
|
||||
MOVP2R(MEMBASEREG, Memory::base);
|
||||
MOVP2R(CTXREG, mipsState);
|
||||
// Pre-subtract this to save time later.
|
||||
MOVI2R(JITBASEREG, (intptr_t)GetBasePtr() - MIPS_EMUHACK_OPCODE);
|
||||
|
||||
LoadStaticRegisters();
|
||||
MovFromPC(SCRATCH1);
|
||||
outerLoopPCInSCRATCH1_ = GetCodePtr();
|
||||
MovToPC(SCRATCH1);
|
||||
outerLoop_ = GetCodePtr();
|
||||
SaveStaticRegisters(); // Advance can change the downcount, so must save/restore
|
||||
RestoreRoundingMode(true);
|
||||
QuickCallFunction(SCRATCH1_64, &CoreTiming::Advance);
|
||||
ApplyRoundingMode(true);
|
||||
LoadStaticRegisters();
|
||||
|
||||
dispatcherCheckCoreState_ = GetCodePtr();
|
||||
|
||||
MOVP2R(SCRATCH1_64, &coreState);
|
||||
LDR(INDEX_UNSIGNED, SCRATCH1, SCRATCH1_64, 0);
|
||||
FixupBranch badCoreState = CBNZ(SCRATCH1);
|
||||
|
||||
// Check downcount.
|
||||
TBNZ(DOWNCOUNTREG, 31, outerLoop_);
|
||||
FixupBranch skipToRealDispatch = B();
|
||||
|
||||
dispatcherPCInSCRATCH1_ = GetCodePtr();
|
||||
MovToPC(SCRATCH1);
|
||||
|
||||
hooks_.dispatcher = GetCodePtr();
|
||||
|
||||
FixupBranch bail = TBNZ(DOWNCOUNTREG, 31);
|
||||
SetJumpTarget(skipToRealDispatch);
|
||||
|
||||
dispatcherNoCheck_ = GetCodePtr();
|
||||
|
||||
// Debug
|
||||
if (enableDebug) {
|
||||
MOV(W0, DOWNCOUNTREG);
|
||||
MOV(X1, MEMBASEREG);
|
||||
MOV(X2, JITBASEREG);
|
||||
QuickCallFunction(SCRATCH1_64, &ShowPC);
|
||||
}
|
||||
|
||||
MovFromPC(SCRATCH1);
|
||||
#ifdef MASKED_PSP_MEMORY
|
||||
ANDI2R(SCRATCH1, SCRATCH1, Memory::MEMVIEW32_MASK);
|
||||
#endif
|
||||
hooks_.dispatchFetch = GetCodePtr();
|
||||
LDR(SCRATCH1, MEMBASEREG, SCRATCH1_64);
|
||||
LSR(SCRATCH2, SCRATCH1, 24); // or UBFX(SCRATCH2, SCRATCH1, 24, 8)
|
||||
// We don't mask SCRATCH1 as that's already baked into JITBASEREG.
|
||||
CMP(SCRATCH2, MIPS_EMUHACK_OPCODE >> 24);
|
||||
FixupBranch skipJump = B(CC_NEQ);
|
||||
ADD(SCRATCH1_64, JITBASEREG, SCRATCH1_64);
|
||||
BR(SCRATCH1_64);
|
||||
SetJumpTarget(skipJump);
|
||||
|
||||
// No block found, let's jit. We don't need to save static regs, they're all callee saved.
|
||||
RestoreRoundingMode(true);
|
||||
QuickCallFunction(SCRATCH1_64, &MIPSComp::JitAt);
|
||||
ApplyRoundingMode(true);
|
||||
|
||||
// Let's just dispatch again, we'll enter the block since we know it's there.
|
||||
B(dispatcherNoCheck_);
|
||||
|
||||
SetJumpTarget(bail);
|
||||
|
||||
MOVP2R(SCRATCH1_64, &coreState);
|
||||
LDR(INDEX_UNSIGNED, SCRATCH1, SCRATCH1_64, 0);
|
||||
CBZ(SCRATCH1, outerLoop_);
|
||||
|
||||
const uint8_t *quitLoop = GetCodePtr();
|
||||
SetJumpTarget(badCoreState);
|
||||
|
||||
SaveStaticRegisters();
|
||||
RestoreRoundingMode(true);
|
||||
|
||||
fp_.ABI_PopRegisters(regs_to_save, regs_to_save_fp);
|
||||
|
||||
RET();
|
||||
|
||||
hooks_.crashHandler = GetCodePtr();
|
||||
MOVP2R(SCRATCH1_64, &coreState);
|
||||
MOVI2R(SCRATCH2, CORE_RUNTIME_ERROR);
|
||||
STR(INDEX_UNSIGNED, SCRATCH2, SCRATCH1_64, 0);
|
||||
B(quitLoop);
|
||||
|
||||
// Generate some integer conversion funcs.
|
||||
// MIPS order!
|
||||
static const RoundingMode roundModes[8] = { ROUND_N, ROUND_Z, ROUND_P, ROUND_M, ROUND_N, ROUND_Z, ROUND_P, ROUND_M };
|
||||
for (size_t i = 0; i < ARRAY_SIZE(roundModes); ++i) {
|
||||
convertS0ToSCRATCH1_[i] = AlignCode16();
|
||||
|
||||
fp_.FCMP(S0, S0); // Detect NaN
|
||||
fp_.FCVTS(S0, S0, roundModes[i]);
|
||||
FixupBranch skip = B(CC_VC);
|
||||
MOVI2R(SCRATCH2, 0x7FFFFFFF);
|
||||
fp_.FMOV(S0, SCRATCH2);
|
||||
SetJumpTarget(skip);
|
||||
|
||||
RET();
|
||||
}
|
||||
|
||||
// Leave this at the end, add more stuff above.
|
||||
if (enableDisasm) {
|
||||
std::vector<std::string> lines = DisassembleArm64(start, (int)(GetCodePtr() - start));
|
||||
for (auto s : lines) {
|
||||
INFO_LOG(JIT, "%s", s.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// Let's spare the pre-generated code from unprotect-reprotect.
|
||||
AlignCodePage();
|
||||
jitStartOffset_ = (int)(GetCodePtr() - start);
|
||||
// Don't forget to zap the instruction cache! This must stay at the end of this function.
|
||||
FlushIcache();
|
||||
EndWrite();
|
||||
|
||||
// Update our current cached rounding mode func, too.
|
||||
UpdateFCR31(mipsState);
|
||||
}
|
||||
|
||||
} // namespace MIPSComp
|
||||
|
||||
#endif
|
254
Core/MIPS/ARM64/Arm64IRCompALU.cpp
Normal file
254
Core/MIPS/ARM64/Arm64IRCompALU.cpp
Normal file
|
@ -0,0 +1,254 @@
|
|||
// Copyright (c) 2023- PPSSPP Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0 or later versions.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#include "ppsspp_config.h"
|
||||
// In other words, PPSSPP_ARCH(ARM64) || DISASM_ALL.
|
||||
#if PPSSPP_ARCH(ARM64) || (PPSSPP_PLATFORM(WINDOWS) && !defined(__LIBRETRO__))
|
||||
|
||||
#include "Common/CPUDetect.h"
|
||||
#include "Core/MemMap.h"
|
||||
#include "Core/MIPS/ARM64/Arm64IRJit.h"
|
||||
#include "Core/MIPS/ARM64/Arm64IRRegCache.h"
|
||||
|
||||
// This file contains compilation for integer / arithmetic / logic related instructions.
|
||||
//
|
||||
// All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly.
|
||||
// Currently known non working ones should have DISABLE. No flags because that's in IR already.
|
||||
|
||||
// #define CONDITIONAL_DISABLE { CompIR_Generic(inst); return; }
|
||||
#define CONDITIONAL_DISABLE {}
|
||||
#define DISABLE { CompIR_Generic(inst); return; }
|
||||
#define INVALIDOP { _assert_msg_(false, "Invalid IR inst %d", (int)inst.op); CompIR_Generic(inst); return; }
|
||||
|
||||
namespace MIPSComp {
|
||||
|
||||
using namespace Arm64Gen;
|
||||
using namespace Arm64IRJitConstants;
|
||||
|
||||
void Arm64JitBackend::CompIR_Arith(IRInst inst) {
|
||||
CONDITIONAL_DISABLE;
|
||||
|
||||
bool allowPtrMath = inst.constant <= 0x7FFFFFFF;
|
||||
#ifdef MASKED_PSP_MEMORY
|
||||
// Since we modify it, we can't safely.
|
||||
allowPtrMath = false;
|
||||
#endif
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::Add:
|
||||
case IROp::Sub:
|
||||
CompIR_Generic(inst);
|
||||
break;
|
||||
|
||||
case IROp::AddConst:
|
||||
if (regs_.IsGPRMappedAsPointer(inst.dest) && inst.dest == inst.src1 && allowPtrMath) {
|
||||
regs_.MarkGPRAsPointerDirty(inst.dest);
|
||||
ADDI2R(regs_.RPtr(inst.dest), regs_.RPtr(inst.src1), (int)inst.constant, SCRATCH1_64);
|
||||
} else {
|
||||
regs_.Map(inst);
|
||||
ADDI2R(regs_.R(inst.dest), regs_.R(inst.src1), inst.constant, SCRATCH1);
|
||||
}
|
||||
break;
|
||||
|
||||
case IROp::SubConst:
|
||||
if (regs_.IsGPRMappedAsPointer(inst.dest) && inst.dest == inst.src1 && allowPtrMath) {
|
||||
regs_.MarkGPRAsPointerDirty(inst.dest);
|
||||
SUBI2R(regs_.RPtr(inst.dest), regs_.RPtr(inst.src1), (int)inst.constant, SCRATCH1_64);
|
||||
} else {
|
||||
regs_.Map(inst);
|
||||
SUBI2R(regs_.R(inst.dest), regs_.R(inst.src1), inst.constant, SCRATCH1);
|
||||
}
|
||||
break;
|
||||
|
||||
case IROp::Neg:
|
||||
CompIR_Generic(inst);
|
||||
break;
|
||||
|
||||
default:
|
||||
INVALIDOP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64JitBackend::CompIR_Assign(IRInst inst) {
|
||||
CONDITIONAL_DISABLE;
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::Mov:
|
||||
if (inst.dest != inst.src1) {
|
||||
regs_.Map(inst);
|
||||
MOV(regs_.R(inst.dest), regs_.R(inst.src1));
|
||||
}
|
||||
break;
|
||||
|
||||
case IROp::Ext8to32:
|
||||
case IROp::Ext16to32:
|
||||
CompIR_Generic(inst);
|
||||
break;
|
||||
|
||||
default:
|
||||
INVALIDOP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64JitBackend::CompIR_Bits(IRInst inst) {
|
||||
CONDITIONAL_DISABLE;
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::BSwap32:
|
||||
case IROp::ReverseBits:
|
||||
case IROp::BSwap16:
|
||||
case IROp::Clz:
|
||||
CompIR_Generic(inst);
|
||||
break;
|
||||
|
||||
default:
|
||||
INVALIDOP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64JitBackend::CompIR_Compare(IRInst inst) {
|
||||
CONDITIONAL_DISABLE;
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::Slt:
|
||||
case IROp::SltConst:
|
||||
case IROp::SltU:
|
||||
case IROp::SltUConst:
|
||||
CompIR_Generic(inst);
|
||||
break;
|
||||
|
||||
default:
|
||||
INVALIDOP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64JitBackend::CompIR_CondAssign(IRInst inst) {
|
||||
CONDITIONAL_DISABLE;
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::MovZ:
|
||||
case IROp::MovNZ:
|
||||
case IROp::Max:
|
||||
case IROp::Min:
|
||||
CompIR_Generic(inst);
|
||||
break;
|
||||
|
||||
default:
|
||||
INVALIDOP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64JitBackend::CompIR_Div(IRInst inst) {
|
||||
CONDITIONAL_DISABLE;
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::Div:
|
||||
case IROp::DivU:
|
||||
CompIR_Generic(inst);
|
||||
break;
|
||||
|
||||
default:
|
||||
INVALIDOP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64JitBackend::CompIR_HiLo(IRInst inst) {
|
||||
CONDITIONAL_DISABLE;
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::MtLo:
|
||||
case IROp::MtHi:
|
||||
case IROp::MfLo:
|
||||
case IROp::MfHi:
|
||||
CompIR_Generic(inst);
|
||||
break;
|
||||
|
||||
default:
|
||||
INVALIDOP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64JitBackend::CompIR_Logic(IRInst inst) {
|
||||
CONDITIONAL_DISABLE;
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::And:
|
||||
case IROp::Or:
|
||||
case IROp::Xor:
|
||||
case IROp::AndConst:
|
||||
case IROp::OrConst:
|
||||
case IROp::XorConst:
|
||||
case IROp::Not:
|
||||
CompIR_Generic(inst);
|
||||
break;
|
||||
|
||||
default:
|
||||
INVALIDOP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64JitBackend::CompIR_Mult(IRInst inst) {
|
||||
CONDITIONAL_DISABLE;
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::Mult:
|
||||
case IROp::MultU:
|
||||
case IROp::Madd:
|
||||
case IROp::MaddU:
|
||||
case IROp::Msub:
|
||||
case IROp::MsubU:
|
||||
CompIR_Generic(inst);
|
||||
break;
|
||||
|
||||
default:
|
||||
INVALIDOP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64JitBackend::CompIR_Shift(IRInst inst) {
|
||||
CONDITIONAL_DISABLE;
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::Shl:
|
||||
case IROp::Shr:
|
||||
case IROp::Sar:
|
||||
case IROp::Ror:
|
||||
case IROp::ShlImm:
|
||||
case IROp::ShrImm:
|
||||
case IROp::SarImm:
|
||||
case IROp::RorImm:
|
||||
CompIR_Generic(inst);
|
||||
break;
|
||||
|
||||
default:
|
||||
INVALIDOP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace MIPSComp
|
||||
|
||||
#endif
|
83
Core/MIPS/ARM64/Arm64IRCompBranch.cpp
Normal file
83
Core/MIPS/ARM64/Arm64IRCompBranch.cpp
Normal file
|
@ -0,0 +1,83 @@
|
|||
// Copyright (c) 2023- PPSSPP Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0 or later versions.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#include "ppsspp_config.h"
|
||||
// In other words, PPSSPP_ARCH(ARM64) || DISASM_ALL.
|
||||
#if PPSSPP_ARCH(ARM64) || (PPSSPP_PLATFORM(WINDOWS) && !defined(__LIBRETRO__))
|
||||
|
||||
#include "Core/MIPS/ARM64/Arm64IRJit.h"
|
||||
#include "Core/MIPS/ARM64/Arm64IRRegCache.h"
|
||||
|
||||
// This file contains compilation for exits.
|
||||
//
|
||||
// All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly.
|
||||
// Currently known non working ones should have DISABLE. No flags because that's in IR already.
|
||||
|
||||
// #define CONDITIONAL_DISABLE { CompIR_Generic(inst); return; }
|
||||
#define CONDITIONAL_DISABLE {}
|
||||
#define DISABLE { CompIR_Generic(inst); return; }
|
||||
#define INVALIDOP { _assert_msg_(false, "Invalid IR inst %d", (int)inst.op); CompIR_Generic(inst); return; }
|
||||
|
||||
namespace MIPSComp {
|
||||
|
||||
using namespace Arm64Gen;
|
||||
using namespace Arm64IRJitConstants;
|
||||
|
||||
void Arm64JitBackend::CompIR_Exit(IRInst inst) {
|
||||
CONDITIONAL_DISABLE;
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::ExitToConst:
|
||||
case IROp::ExitToReg:
|
||||
case IROp::ExitToPC:
|
||||
CompIR_Generic(inst);
|
||||
break;
|
||||
|
||||
default:
|
||||
INVALIDOP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64JitBackend::CompIR_ExitIf(IRInst inst) {
|
||||
CONDITIONAL_DISABLE;
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::ExitToConstIfEq:
|
||||
case IROp::ExitToConstIfNeq:
|
||||
case IROp::ExitToConstIfGtZ:
|
||||
case IROp::ExitToConstIfGeZ:
|
||||
case IROp::ExitToConstIfLtZ:
|
||||
case IROp::ExitToConstIfLeZ:
|
||||
CompIR_Generic(inst);
|
||||
break;
|
||||
|
||||
case IROp::ExitToConstIfFpTrue:
|
||||
case IROp::ExitToConstIfFpFalse:
|
||||
// Note: not used.
|
||||
DISABLE;
|
||||
break;
|
||||
|
||||
default:
|
||||
INVALIDOP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace MIPSComp
|
||||
|
||||
#endif
|
218
Core/MIPS/ARM64/Arm64IRCompFPU.cpp
Normal file
218
Core/MIPS/ARM64/Arm64IRCompFPU.cpp
Normal file
|
@ -0,0 +1,218 @@
|
|||
// Copyright (c) 2023- PPSSPP Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0 or later versions.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#include "ppsspp_config.h"
|
||||
// In other words, PPSSPP_ARCH(ARM64) || DISASM_ALL.
|
||||
#if PPSSPP_ARCH(ARM64) || (PPSSPP_PLATFORM(WINDOWS) && !defined(__LIBRETRO__))
|
||||
|
||||
#ifndef offsetof
|
||||
#include <cstddef>
|
||||
#endif
|
||||
|
||||
#include "Core/MIPS/ARM64/Arm64IRJit.h"
|
||||
#include "Core/MIPS/ARM64/Arm64IRRegCache.h"
|
||||
|
||||
// This file contains compilation for floating point related instructions.
|
||||
//
|
||||
// All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly.
|
||||
// Currently known non working ones should have DISABLE. No flags because that's in IR already.
|
||||
|
||||
// #define CONDITIONAL_DISABLE { CompIR_Generic(inst); return; }
|
||||
#define CONDITIONAL_DISABLE {}
|
||||
#define DISABLE { CompIR_Generic(inst); return; }
|
||||
#define INVALIDOP { _assert_msg_(false, "Invalid IR inst %d", (int)inst.op); CompIR_Generic(inst); return; }
|
||||
|
||||
namespace MIPSComp {
|
||||
|
||||
using namespace Arm64Gen;
|
||||
using namespace Arm64IRJitConstants;
|
||||
|
||||
void Arm64JitBackend::CompIR_FArith(IRInst inst) {
|
||||
CONDITIONAL_DISABLE;
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::FAdd:
|
||||
case IROp::FSub:
|
||||
case IROp::FMul:
|
||||
case IROp::FDiv:
|
||||
case IROp::FSqrt:
|
||||
case IROp::FNeg:
|
||||
CompIR_Generic(inst);
|
||||
break;
|
||||
|
||||
default:
|
||||
INVALIDOP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64JitBackend::CompIR_FAssign(IRInst inst) {
|
||||
CONDITIONAL_DISABLE;
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::FMov:
|
||||
case IROp::FAbs:
|
||||
case IROp::FSign:
|
||||
CompIR_Generic(inst);
|
||||
break;
|
||||
|
||||
default:
|
||||
INVALIDOP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64JitBackend::CompIR_FCompare(IRInst inst) {
|
||||
CONDITIONAL_DISABLE;
|
||||
|
||||
constexpr IRReg IRREG_VFPU_CC = IRREG_VFPU_CTRL_BASE + VFPU_CTRL_CC;
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::FCmp:
|
||||
switch (inst.dest) {
|
||||
case IRFpCompareMode::False:
|
||||
case IRFpCompareMode::EitherUnordered:
|
||||
case IRFpCompareMode::EqualOrdered:
|
||||
case IRFpCompareMode::EqualUnordered:
|
||||
case IRFpCompareMode::LessEqualOrdered:
|
||||
case IRFpCompareMode::LessEqualUnordered:
|
||||
case IRFpCompareMode::LessOrdered:
|
||||
case IRFpCompareMode::LessUnordered:
|
||||
CompIR_Generic(inst);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case IROp::FCmovVfpuCC:
|
||||
case IROp::FCmpVfpuBit:
|
||||
case IROp::FCmpVfpuAggregate:
|
||||
CompIR_Generic(inst);
|
||||
break;
|
||||
|
||||
default:
|
||||
INVALIDOP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64JitBackend::CompIR_FCondAssign(IRInst inst) {
|
||||
CONDITIONAL_DISABLE;
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::FMin:
|
||||
case IROp::FMax:
|
||||
CompIR_Generic(inst);
|
||||
break;
|
||||
|
||||
default:
|
||||
INVALIDOP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64JitBackend::CompIR_FCvt(IRInst inst) {
|
||||
CONDITIONAL_DISABLE;
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::FCvtWS:
|
||||
case IROp::FCvtSW:
|
||||
case IROp::FCvtScaledWS:
|
||||
case IROp::FCvtScaledSW:
|
||||
CompIR_Generic(inst);
|
||||
break;
|
||||
|
||||
default:
|
||||
INVALIDOP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64JitBackend::CompIR_FRound(IRInst inst) {
|
||||
CONDITIONAL_DISABLE;
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::FRound:
|
||||
case IROp::FTrunc:
|
||||
case IROp::FCeil:
|
||||
case IROp::FFloor:
|
||||
CompIR_Generic(inst);
|
||||
break;
|
||||
|
||||
default:
|
||||
INVALIDOP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64JitBackend::CompIR_FSat(IRInst inst) {
|
||||
CONDITIONAL_DISABLE;
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::FSat0_1:
|
||||
case IROp::FSatMinus1_1:
|
||||
CompIR_Generic(inst);
|
||||
break;
|
||||
|
||||
default:
|
||||
INVALIDOP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64JitBackend::CompIR_FSpecial(IRInst inst) {
|
||||
CONDITIONAL_DISABLE;
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::FSin:
|
||||
case IROp::FCos:
|
||||
case IROp::FRSqrt:
|
||||
case IROp::FRecip:
|
||||
case IROp::FAsin:
|
||||
CompIR_Generic(inst);
|
||||
break;
|
||||
|
||||
default:
|
||||
INVALIDOP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64JitBackend::CompIR_RoundingMode(IRInst inst) {
|
||||
CONDITIONAL_DISABLE;
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::RestoreRoundingMode:
|
||||
RestoreRoundingMode();
|
||||
break;
|
||||
|
||||
case IROp::ApplyRoundingMode:
|
||||
ApplyRoundingMode();
|
||||
break;
|
||||
|
||||
case IROp::UpdateRoundingMode:
|
||||
UpdateRoundingMode();
|
||||
break;
|
||||
|
||||
default:
|
||||
INVALIDOP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace MIPSComp
|
||||
|
||||
#endif
|
174
Core/MIPS/ARM64/Arm64IRCompLoadStore.cpp
Normal file
174
Core/MIPS/ARM64/Arm64IRCompLoadStore.cpp
Normal file
|
@ -0,0 +1,174 @@
|
|||
// Copyright (c) 2023- PPSSPP Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0 or later versions.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#include "ppsspp_config.h"
|
||||
// In other words, PPSSPP_ARCH(ARM64) || DISASM_ALL.
|
||||
#if PPSSPP_ARCH(ARM64) || (PPSSPP_PLATFORM(WINDOWS) && !defined(__LIBRETRO__))
|
||||
|
||||
#include "Core/MemMap.h"
|
||||
#include "Core/MIPS/ARM64/Arm64IRJit.h"
|
||||
#include "Core/MIPS/ARM64/Arm64IRRegCache.h"
|
||||
|
||||
// This file contains compilation for load/store instructions.
|
||||
//
|
||||
// All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly.
|
||||
// Currently known non working ones should have DISABLE. No flags because that's in IR already.
|
||||
|
||||
// #define CONDITIONAL_DISABLE { CompIR_Generic(inst); return; }
|
||||
#define CONDITIONAL_DISABLE {}
|
||||
#define DISABLE { CompIR_Generic(inst); return; }
|
||||
#define INVALIDOP { _assert_msg_(false, "Invalid IR inst %d", (int)inst.op); CompIR_Generic(inst); return; }
|
||||
|
||||
namespace MIPSComp {
|
||||
|
||||
using namespace Arm64Gen;
|
||||
using namespace Arm64IRJitConstants;
|
||||
|
||||
void Arm64JitBackend::CompIR_CondStore(IRInst inst) {
|
||||
CONDITIONAL_DISABLE;
|
||||
if (inst.op != IROp::Store32Conditional)
|
||||
INVALIDOP;
|
||||
|
||||
CompIR_Generic(inst);
|
||||
}
|
||||
|
||||
void Arm64JitBackend::CompIR_FLoad(IRInst inst) {
|
||||
CONDITIONAL_DISABLE;
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::LoadFloat:
|
||||
CompIR_Generic(inst);
|
||||
break;
|
||||
|
||||
default:
|
||||
INVALIDOP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64JitBackend::CompIR_FStore(IRInst inst) {
|
||||
CONDITIONAL_DISABLE;
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::StoreFloat:
|
||||
CompIR_Generic(inst);
|
||||
break;
|
||||
|
||||
default:
|
||||
INVALIDOP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64JitBackend::CompIR_Load(IRInst inst) {
|
||||
CONDITIONAL_DISABLE;
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::Load8:
|
||||
case IROp::Load8Ext:
|
||||
case IROp::Load16:
|
||||
case IROp::Load16Ext:
|
||||
case IROp::Load32:
|
||||
case IROp::Load32Linked:
|
||||
CompIR_Generic(inst);
|
||||
break;
|
||||
|
||||
default:
|
||||
INVALIDOP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64JitBackend::CompIR_LoadShift(IRInst inst) {
|
||||
CONDITIONAL_DISABLE;
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::Load32Left:
|
||||
case IROp::Load32Right:
|
||||
// Should not happen if the pass to split is active.
|
||||
DISABLE;
|
||||
break;
|
||||
|
||||
default:
|
||||
INVALIDOP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64JitBackend::CompIR_Store(IRInst inst) {
|
||||
CONDITIONAL_DISABLE;
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::Store8:
|
||||
case IROp::Store16:
|
||||
case IROp::Store32:
|
||||
CompIR_Generic(inst);
|
||||
break;
|
||||
|
||||
default:
|
||||
INVALIDOP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64JitBackend::CompIR_StoreShift(IRInst inst) {
|
||||
CONDITIONAL_DISABLE;
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::Store32Left:
|
||||
case IROp::Store32Right:
|
||||
// Should not happen if the pass to split is active.
|
||||
DISABLE;
|
||||
break;
|
||||
|
||||
default:
|
||||
INVALIDOP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64JitBackend::CompIR_VecLoad(IRInst inst) {
|
||||
CONDITIONAL_DISABLE;
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::LoadVec4:
|
||||
CompIR_Generic(inst);
|
||||
break;
|
||||
|
||||
default:
|
||||
INVALIDOP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64JitBackend::CompIR_VecStore(IRInst inst) {
|
||||
CONDITIONAL_DISABLE;
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::StoreVec4:
|
||||
CompIR_Generic(inst);
|
||||
break;
|
||||
|
||||
default:
|
||||
INVALIDOP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace MIPSComp
|
||||
|
||||
#endif
|
166
Core/MIPS/ARM64/Arm64IRCompSystem.cpp
Normal file
166
Core/MIPS/ARM64/Arm64IRCompSystem.cpp
Normal file
|
@ -0,0 +1,166 @@
|
|||
// Copyright (c) 2023- PPSSPP Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0 or later versions.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#include "ppsspp_config.h"
|
||||
// In other words, PPSSPP_ARCH(ARM64) || DISASM_ALL.
|
||||
#if PPSSPP_ARCH(ARM64) || (PPSSPP_PLATFORM(WINDOWS) && !defined(__LIBRETRO__))
|
||||
|
||||
#include "Common/Profiler/Profiler.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/HLE/HLE.h"
|
||||
#include "Core/HLE/ReplaceTables.h"
|
||||
#include "Core/MemMap.h"
|
||||
#include "Core/MIPS/ARM64/Arm64IRJit.h"
|
||||
#include "Core/MIPS/ARM64/Arm64IRRegCache.h"
|
||||
|
||||
// This file contains compilation for basic PC/downcount accounting, syscalls, debug funcs, etc.
|
||||
//
|
||||
// All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly.
|
||||
// Currently known non working ones should have DISABLE. No flags because that's in IR already.
|
||||
|
||||
// #define CONDITIONAL_DISABLE { CompIR_Generic(inst); return; }
|
||||
#define CONDITIONAL_DISABLE {}
|
||||
#define DISABLE { CompIR_Generic(inst); return; }
|
||||
#define INVALIDOP { _assert_msg_(false, "Invalid IR inst %d", (int)inst.op); CompIR_Generic(inst); return; }
|
||||
|
||||
namespace MIPSComp {
|
||||
|
||||
using namespace Arm64Gen;
|
||||
using namespace Arm64IRJitConstants;
|
||||
|
||||
void Arm64JitBackend::CompIR_Basic(IRInst inst) {
|
||||
CONDITIONAL_DISABLE;
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::Downcount:
|
||||
SUBI2R(DOWNCOUNTREG, DOWNCOUNTREG, (s64)(s32)inst.constant, SCRATCH1);
|
||||
break;
|
||||
|
||||
case IROp::SetConst:
|
||||
regs_.SetGPRImm(inst.dest, inst.constant);
|
||||
break;
|
||||
|
||||
case IROp::SetConstF:
|
||||
{
|
||||
regs_.Map(inst);
|
||||
float f;
|
||||
memcpy(&f, &inst.constant, sizeof(f));
|
||||
fp_.MOVI2F(regs_.F(inst.dest), f, SCRATCH1);
|
||||
break;
|
||||
}
|
||||
|
||||
case IROp::SetPC:
|
||||
regs_.Map(inst);
|
||||
MovToPC(regs_.R(inst.src1));
|
||||
break;
|
||||
|
||||
case IROp::SetPCConst:
|
||||
MOVI2R(SCRATCH1, inst.constant);
|
||||
MovToPC(SCRATCH1);
|
||||
break;
|
||||
|
||||
default:
|
||||
INVALIDOP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64JitBackend::CompIR_Breakpoint(IRInst inst) {
|
||||
CONDITIONAL_DISABLE;
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::Breakpoint:
|
||||
case IROp::MemoryCheck:
|
||||
CompIR_Generic(inst);
|
||||
break;
|
||||
|
||||
default:
|
||||
INVALIDOP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64JitBackend::CompIR_System(IRInst inst) {
|
||||
CONDITIONAL_DISABLE;
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::Syscall:
|
||||
case IROp::CallReplacement:
|
||||
case IROp::Break:
|
||||
CompIR_Generic(inst);
|
||||
break;
|
||||
|
||||
default:
|
||||
INVALIDOP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64JitBackend::CompIR_Transfer(IRInst inst) {
|
||||
CONDITIONAL_DISABLE;
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::SetCtrlVFPU:
|
||||
case IROp::SetCtrlVFPUReg:
|
||||
case IROp::SetCtrlVFPUFReg:
|
||||
case IROp::FpCondFromReg:
|
||||
case IROp::FpCondToReg:
|
||||
case IROp::FpCtrlFromReg:
|
||||
case IROp::FpCtrlToReg:
|
||||
case IROp::VfpuCtrlToReg:
|
||||
case IROp::FMovFromGPR:
|
||||
case IROp::FMovToGPR:
|
||||
CompIR_Generic(inst);
|
||||
break;
|
||||
|
||||
default:
|
||||
INVALIDOP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64JitBackend::CompIR_ValidateAddress(IRInst inst) {
|
||||
CONDITIONAL_DISABLE;
|
||||
|
||||
bool isWrite = inst.src2 & 1;
|
||||
int alignment = 0;
|
||||
switch (inst.op) {
|
||||
case IROp::ValidateAddress8:
|
||||
alignment = 1;
|
||||
break;
|
||||
|
||||
case IROp::ValidateAddress16:
|
||||
alignment = 2;
|
||||
break;
|
||||
|
||||
case IROp::ValidateAddress32:
|
||||
alignment = 4;
|
||||
break;
|
||||
|
||||
case IROp::ValidateAddress128:
|
||||
alignment = 16;
|
||||
break;
|
||||
|
||||
default:
|
||||
INVALIDOP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace MIPSComp
|
||||
|
||||
#endif
|
132
Core/MIPS/ARM64/Arm64IRCompVec.cpp
Normal file
132
Core/MIPS/ARM64/Arm64IRCompVec.cpp
Normal file
|
@ -0,0 +1,132 @@
|
|||
// Copyright (c) 2023- PPSSPP Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0 or later versions.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#include "ppsspp_config.h"
|
||||
// In other words, PPSSPP_ARCH(ARM64) || DISASM_ALL.
|
||||
#if PPSSPP_ARCH(ARM64) || (PPSSPP_PLATFORM(WINDOWS) && !defined(__LIBRETRO__))
|
||||
|
||||
#include <algorithm>
|
||||
#include "Common/CPUDetect.h"
|
||||
#include "Core/MemMap.h"
|
||||
#include "Core/MIPS/ARM64/Arm64IRJit.h"
|
||||
#include "Core/MIPS/ARM64/Arm64IRRegCache.h"
|
||||
|
||||
// This file contains compilation for vector instructions.
|
||||
//
|
||||
// All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly.
|
||||
// Currently known non working ones should have DISABLE. No flags because that's in IR already.
|
||||
|
||||
// #define CONDITIONAL_DISABLE { CompIR_Generic(inst); return; }
|
||||
#define CONDITIONAL_DISABLE {}
|
||||
#define DISABLE { CompIR_Generic(inst); return; }
|
||||
#define INVALIDOP { _assert_msg_(false, "Invalid IR inst %d", (int)inst.op); CompIR_Generic(inst); return; }
|
||||
|
||||
namespace MIPSComp {
|
||||
|
||||
using namespace Arm64Gen;
|
||||
using namespace Arm64IRJitConstants;
|
||||
|
||||
void Arm64JitBackend::CompIR_VecArith(IRInst inst) {
|
||||
CONDITIONAL_DISABLE;
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::Vec4Add:
|
||||
case IROp::Vec4Sub:
|
||||
case IROp::Vec4Mul:
|
||||
case IROp::Vec4Div:
|
||||
case IROp::Vec4Scale:
|
||||
case IROp::Vec4Neg:
|
||||
case IROp::Vec4Abs:
|
||||
CompIR_Generic(inst);
|
||||
break;
|
||||
|
||||
default:
|
||||
INVALIDOP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64JitBackend::CompIR_VecAssign(IRInst inst) {
|
||||
CONDITIONAL_DISABLE;
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::Vec4Init:
|
||||
case IROp::Vec4Shuffle:
|
||||
case IROp::Vec4Blend:
|
||||
case IROp::Vec4Mov:
|
||||
CompIR_Generic(inst);
|
||||
break;
|
||||
|
||||
default:
|
||||
INVALIDOP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64JitBackend::CompIR_VecClamp(IRInst inst) {
|
||||
CONDITIONAL_DISABLE;
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::Vec4ClampToZero:
|
||||
case IROp::Vec2ClampToZero:
|
||||
CompIR_Generic(inst);
|
||||
break;
|
||||
|
||||
default:
|
||||
INVALIDOP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64JitBackend::CompIR_VecHoriz(IRInst inst) {
|
||||
CONDITIONAL_DISABLE;
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::Vec4Dot:
|
||||
CompIR_Generic(inst);
|
||||
break;
|
||||
|
||||
default:
|
||||
INVALIDOP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64JitBackend::CompIR_VecPack(IRInst inst) {
|
||||
CONDITIONAL_DISABLE;
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::Vec2Unpack16To31:
|
||||
case IROp::Vec4Pack32To8:
|
||||
case IROp::Vec2Pack31To16:
|
||||
case IROp::Vec4Unpack8To32:
|
||||
case IROp::Vec2Unpack16To32:
|
||||
case IROp::Vec4DuplicateUpperBitsAndShift1:
|
||||
case IROp::Vec4Pack31To8:
|
||||
case IROp::Vec2Pack32To16:
|
||||
CompIR_Generic(inst);
|
||||
break;
|
||||
|
||||
default:
|
||||
INVALIDOP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace MIPSComp
|
||||
|
||||
#endif
|
372
Core/MIPS/ARM64/Arm64IRJit.cpp
Normal file
372
Core/MIPS/ARM64/Arm64IRJit.cpp
Normal file
|
@ -0,0 +1,372 @@
|
|||
// Copyright (c) 2023- PPSSPP Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0 or later versions.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#include "ppsspp_config.h"
|
||||
// In other words, PPSSPP_ARCH(ARM64) || DISASM_ALL.
|
||||
#if PPSSPP_ARCH(ARM64) || (PPSSPP_PLATFORM(WINDOWS) && !defined(__LIBRETRO__))
|
||||
|
||||
#include <cstddef>
|
||||
#include "Core/MemMap.h"
|
||||
#include "Core/MIPS/MIPSTables.h"
|
||||
#include "Core/MIPS/ARM64/Arm64IRJit.h"
|
||||
#include "Core/MIPS/ARM64/Arm64IRRegCache.h"
|
||||
|
||||
namespace MIPSComp {
|
||||
|
||||
using namespace Arm64Gen;
|
||||
using namespace Arm64IRJitConstants;
|
||||
|
||||
// Invalidations just need at most two MOVs and B.
|
||||
static constexpr int MIN_BLOCK_NORMAL_LEN = 12;
|
||||
// As long as we can fit a B, we should be fine.
|
||||
static constexpr int MIN_BLOCK_EXIT_LEN = 4;
|
||||
|
||||
Arm64JitBackend::Arm64JitBackend(JitOptions &jitopt, IRBlockCache &blocks)
|
||||
: IRNativeBackend(blocks), jo(jitopt), regs_(&jo), fp_(this) {
|
||||
// Automatically disable incompatible options.
|
||||
if (((intptr_t)Memory::base & 0x00000000FFFFFFFFUL) != 0) {
|
||||
jo.enablePointerify = false;
|
||||
}
|
||||
#ifdef MASKED_PSP_MEMORY
|
||||
jo.enablePointerify = false;
|
||||
#endif
|
||||
|
||||
// Since we store the offset, this is as big as it can be.
|
||||
AllocCodeSpace(1024 * 1024 * 16);
|
||||
|
||||
regs_.Init(this, &fp_);
|
||||
}
|
||||
|
||||
Arm64JitBackend::~Arm64JitBackend() {}
|
||||
|
||||
void Arm64JitBackend::UpdateFCR31(MIPSState *mipsState) {
|
||||
currentRoundingFunc_ = convertS0ToSCRATCH1_[mipsState->fcr31 & 3];
|
||||
}
|
||||
|
||||
static void NoBlockExits() {
|
||||
_assert_msg_(false, "Never exited block, invalid IR?");
|
||||
}
|
||||
|
||||
bool Arm64JitBackend::CompileBlock(IRBlock *block, int block_num, bool preload) {
|
||||
if (GetSpaceLeft() < 0x800)
|
||||
return false;
|
||||
|
||||
BeginWrite(std::min(GetSpaceLeft(), (size_t)block->GetNumInstructions() * 32));
|
||||
|
||||
u32 startPC = block->GetOriginalStart();
|
||||
bool wroteCheckedOffset = false;
|
||||
if (jo.enableBlocklink && !jo.useBackJump) {
|
||||
SetBlockCheckedOffset(block_num, (int)GetOffset(GetCodePointer()));
|
||||
wroteCheckedOffset = true;
|
||||
|
||||
// Check the sign bit to check if negative.
|
||||
FixupBranch normalEntry = TBZ(DOWNCOUNTREG, 31);
|
||||
MOVI2R(SCRATCH1, startPC);
|
||||
B(outerLoopPCInSCRATCH1_);
|
||||
SetJumpTarget(normalEntry);
|
||||
}
|
||||
|
||||
// Don't worry, the codespace isn't large enough to overflow offsets.
|
||||
const u8 *blockStart = GetCodePointer();
|
||||
block->SetTargetOffset((int)GetOffset(blockStart));
|
||||
compilingBlockNum_ = block_num;
|
||||
|
||||
regs_.Start(block);
|
||||
|
||||
std::map<const u8 *, int> addresses;
|
||||
for (int i = 0; i < block->GetNumInstructions(); ++i) {
|
||||
const IRInst &inst = block->GetInstructions()[i];
|
||||
regs_.SetIRIndex(i);
|
||||
// TODO: This might be a little wasteful when compiling if we're not debugging jit...
|
||||
addresses[GetCodePtr()] = i;
|
||||
|
||||
CompileIRInst(inst);
|
||||
|
||||
if (jo.Disabled(JitDisable::REGALLOC_GPR) || jo.Disabled(JitDisable::REGALLOC_FPR))
|
||||
regs_.FlushAll(jo.Disabled(JitDisable::REGALLOC_GPR), jo.Disabled(JitDisable::REGALLOC_FPR));
|
||||
|
||||
// Safety check, in case we get a bunch of really large jit ops without a lot of branching.
|
||||
if (GetSpaceLeft() < 0x800) {
|
||||
compilingBlockNum_ = -1;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// We should've written an exit above. If we didn't, bad things will happen.
|
||||
// Only check if debug stats are enabled - needlessly wastes jit space.
|
||||
if (DebugStatsEnabled()) {
|
||||
QuickCallFunction(SCRATCH2_64, &NoBlockExits);
|
||||
B(hooks_.crashHandler);
|
||||
}
|
||||
|
||||
int len = (int)GetOffset(GetCodePointer()) - block->GetTargetOffset();
|
||||
if (len < MIN_BLOCK_NORMAL_LEN) {
|
||||
// We need at least 10 bytes to invalidate blocks with.
|
||||
ReserveCodeSpace(MIN_BLOCK_NORMAL_LEN - len);
|
||||
}
|
||||
|
||||
if (!wroteCheckedOffset) {
|
||||
// Always record this, even if block link disabled - it's used for size calc.
|
||||
SetBlockCheckedOffset(block_num, (int)GetOffset(GetCodePointer()));
|
||||
}
|
||||
|
||||
if (jo.enableBlocklink && jo.useBackJump) {
|
||||
// Small blocks are common, check if it's < 32KB long.
|
||||
ptrdiff_t distance = blockStart - GetCodePointer();
|
||||
if (distance >= -0x8000 && distance < 0x8000) {
|
||||
TBZ(DOWNCOUNTREG, 31, blockStart);
|
||||
} else {
|
||||
FixupBranch toDispatch = TBNZ(DOWNCOUNTREG, 31);
|
||||
B(blockStart);
|
||||
SetJumpTarget(toDispatch);
|
||||
}
|
||||
|
||||
MOVI2R(SCRATCH1, startPC);
|
||||
B(outerLoopPCInSCRATCH1_);
|
||||
}
|
||||
|
||||
if (logBlocks_ > 0) {
|
||||
--logBlocks_;
|
||||
|
||||
INFO_LOG(JIT, "=============== ARM64 (%08x, %d bytes) ===============", startPC, len);
|
||||
for (const u8 *p = blockStart; p < GetCodePointer(); ) {
|
||||
auto it = addresses.find(p);
|
||||
if (it != addresses.end()) {
|
||||
const IRInst &inst = block->GetInstructions()[it->second];
|
||||
|
||||
char temp[512];
|
||||
DisassembleIR(temp, sizeof(temp), inst);
|
||||
INFO_LOG(JIT, "IR: #%d %s", it->second, temp);
|
||||
}
|
||||
|
||||
auto next = std::next(it);
|
||||
const u8 *nextp = next == addresses.end() ? GetCodePointer() : next->first;
|
||||
|
||||
auto lines = DisassembleArm64(p, (int)(nextp - p));
|
||||
for (const auto &line : lines)
|
||||
INFO_LOG(JIT, " A: %s", line.c_str());
|
||||
p = nextp;
|
||||
}
|
||||
}
|
||||
|
||||
EndWrite();
|
||||
FlushIcache();
|
||||
compilingBlockNum_ = -1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Arm64JitBackend::WriteConstExit(uint32_t pc) {
|
||||
int block_num = blocks_.GetBlockNumberFromStartAddress(pc);
|
||||
const IRNativeBlock *nativeBlock = GetNativeBlock(block_num);
|
||||
|
||||
int exitStart = (int)GetOffset(GetCodePointer());
|
||||
if (block_num >= 0 && jo.enableBlocklink && nativeBlock && nativeBlock->checkedOffset != 0) {
|
||||
B(GetBasePtr() + nativeBlock->checkedOffset);
|
||||
} else {
|
||||
MOVI2R(SCRATCH1, pc);
|
||||
B(dispatcherPCInSCRATCH1_);
|
||||
}
|
||||
|
||||
if (jo.enableBlocklink) {
|
||||
// In case of compression or early link, make sure it's large enough.
|
||||
int len = (int)GetOffset(GetCodePointer()) - exitStart;
|
||||
if (len < MIN_BLOCK_EXIT_LEN) {
|
||||
ReserveCodeSpace(MIN_BLOCK_EXIT_LEN - len);
|
||||
len = MIN_BLOCK_EXIT_LEN;
|
||||
}
|
||||
|
||||
AddLinkableExit(compilingBlockNum_, pc, exitStart, len);
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64JitBackend::OverwriteExit(int srcOffset, int len, int block_num) {
|
||||
_dbg_assert_(len >= MIN_BLOCK_EXIT_LEN);
|
||||
|
||||
const IRNativeBlock *nativeBlock = GetNativeBlock(block_num);
|
||||
if (nativeBlock) {
|
||||
u8 *writable = GetWritablePtrFromCodePtr(GetBasePtr()) + srcOffset;
|
||||
if (PlatformIsWXExclusive()) {
|
||||
ProtectMemoryPages(writable, len, MEM_PROT_READ | MEM_PROT_WRITE);
|
||||
}
|
||||
|
||||
ARM64XEmitter emitter(GetBasePtr() + srcOffset, writable);
|
||||
emitter.B(GetBasePtr() + nativeBlock->checkedOffset);
|
||||
int bytesWritten = (int)(emitter.GetWritableCodePtr() - writable);
|
||||
_dbg_assert_(bytesWritten <= MIN_BLOCK_EXIT_LEN);
|
||||
if (bytesWritten < len)
|
||||
emitter.ReserveCodeSpace(len - bytesWritten);
|
||||
emitter.FlushIcache();
|
||||
|
||||
if (PlatformIsWXExclusive()) {
|
||||
ProtectMemoryPages(writable, 16, MEM_PROT_READ | MEM_PROT_EXEC);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64JitBackend::CompIR_Generic(IRInst inst) {
|
||||
// If we got here, we're going the slow way.
|
||||
uint64_t value;
|
||||
memcpy(&value, &inst, sizeof(inst));
|
||||
|
||||
FlushAll();
|
||||
SaveStaticRegisters();
|
||||
MOVI2R(X0, value);
|
||||
QuickCallFunction(SCRATCH2_64, &DoIRInst);
|
||||
LoadStaticRegisters();
|
||||
|
||||
// We only need to check the return value if it's a potential exit.
|
||||
if ((GetIRMeta(inst.op)->flags & IRFLAG_EXIT) != 0) {
|
||||
MOV(SCRATCH1, X0);
|
||||
|
||||
ptrdiff_t distance = dispatcherPCInSCRATCH1_ - GetCodePointer();
|
||||
if (distance >= -0x100000 && distance < 0x100000) {
|
||||
// Convenient, we can do a simple branch if within 1MB.
|
||||
CBNZ(W0, dispatcherPCInSCRATCH1_);
|
||||
} else {
|
||||
// That's a shame, we need a long branch.
|
||||
FixupBranch keepOnKeepingOn = CBZ(W0);
|
||||
B(dispatcherPCInSCRATCH1_);
|
||||
SetJumpTarget(keepOnKeepingOn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64JitBackend::CompIR_Interpret(IRInst inst) {
|
||||
MIPSOpcode op(inst.constant);
|
||||
|
||||
// IR protects us against this being a branching instruction (well, hopefully.)
|
||||
FlushAll();
|
||||
SaveStaticRegisters();
|
||||
if (DebugStatsEnabled()) {
|
||||
MOVP2R(X0, MIPSGetName(op));
|
||||
QuickCallFunction(SCRATCH2_64, &NotifyMIPSInterpret);
|
||||
}
|
||||
MOVI2R(X0, inst.constant);
|
||||
QuickCallFunction(SCRATCH2_64, MIPSGetInterpretFunc(op));
|
||||
LoadStaticRegisters();
|
||||
}
|
||||
|
||||
void Arm64JitBackend::FlushAll() {
|
||||
regs_.FlushAll();
|
||||
}
|
||||
|
||||
bool Arm64JitBackend::DescribeCodePtr(const u8 *ptr, std::string &name) const {
|
||||
// Used in disassembly viewer and profiling tools.
|
||||
// Don't use spaces; profilers get confused or truncate them.
|
||||
if (ptr == dispatcherPCInSCRATCH1_) {
|
||||
name = "dispatcherPCInSCRATCH1";
|
||||
} else if (ptr == outerLoopPCInSCRATCH1_) {
|
||||
name = "outerLoopPCInSCRATCH1";
|
||||
} else if (ptr == dispatcherNoCheck_) {
|
||||
name = "dispatcherNoCheck";
|
||||
} else if (ptr == saveStaticRegisters_) {
|
||||
name = "saveStaticRegisters";
|
||||
} else if (ptr == loadStaticRegisters_) {
|
||||
name = "loadStaticRegisters";
|
||||
} else if (ptr == restoreRoundingMode_) {
|
||||
name = "restoreRoundingMode";
|
||||
} else if (ptr == applyRoundingMode_) {
|
||||
name = "applyRoundingMode";
|
||||
} else if (ptr == updateRoundingMode_) {
|
||||
name = "updateRoundingMode";
|
||||
} else if (ptr == currentRoundingFunc_) {
|
||||
name = "currentRoundingFunc";
|
||||
} else if (ptr >= convertS0ToSCRATCH1_[0] && ptr <= convertS0ToSCRATCH1_[7]) {
|
||||
name = "convertS0ToSCRATCH1";
|
||||
} else if (ptr >= GetBasePtr() && ptr < GetBasePtr() + jitStartOffset_) {
|
||||
name = "fixedCode";
|
||||
} else {
|
||||
return IRNativeBackend::DescribeCodePtr(ptr, name);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Arm64JitBackend::ClearAllBlocks() {
|
||||
ClearCodeSpace(jitStartOffset_);
|
||||
FlushIcacheSection(region + jitStartOffset_, region + region_size - jitStartOffset_);
|
||||
EraseAllLinks(-1);
|
||||
}
|
||||
|
||||
void Arm64JitBackend::InvalidateBlock(IRBlock *block, int block_num) {
|
||||
int offset = block->GetTargetOffset();
|
||||
u8 *writable = GetWritablePtrFromCodePtr(GetBasePtr()) + offset;
|
||||
|
||||
// Overwrite the block with a jump to compile it again.
|
||||
u32 pc = block->GetOriginalStart();
|
||||
if (pc != 0) {
|
||||
// Hopefully we always have at least 16 bytes, which should be all we need.
|
||||
if (PlatformIsWXExclusive()) {
|
||||
ProtectMemoryPages(writable, MIN_BLOCK_NORMAL_LEN, MEM_PROT_READ | MEM_PROT_WRITE);
|
||||
}
|
||||
|
||||
ARM64XEmitter emitter(GetBasePtr() + offset, writable);
|
||||
emitter.MOVI2R(SCRATCH1, pc);
|
||||
emitter.B(dispatcherPCInSCRATCH1_);
|
||||
int bytesWritten = (int)(emitter.GetWritableCodePtr() - writable);
|
||||
if (bytesWritten < MIN_BLOCK_NORMAL_LEN)
|
||||
emitter.ReserveCodeSpace(MIN_BLOCK_NORMAL_LEN - bytesWritten);
|
||||
emitter.FlushIcache();
|
||||
|
||||
if (PlatformIsWXExclusive()) {
|
||||
ProtectMemoryPages(writable, MIN_BLOCK_NORMAL_LEN, MEM_PROT_READ | MEM_PROT_EXEC);
|
||||
}
|
||||
}
|
||||
|
||||
EraseAllLinks(block_num);
|
||||
}
|
||||
|
||||
void Arm64JitBackend::RestoreRoundingMode(bool force) {
|
||||
QuickCallFunction(SCRATCH2_64, restoreRoundingMode_);
|
||||
}
|
||||
|
||||
void Arm64JitBackend::ApplyRoundingMode(bool force) {
|
||||
QuickCallFunction(SCRATCH2_64, applyRoundingMode_);
|
||||
}
|
||||
|
||||
void Arm64JitBackend::UpdateRoundingMode(bool force) {
|
||||
QuickCallFunction(SCRATCH2_64, updateRoundingMode_);
|
||||
}
|
||||
|
||||
void Arm64JitBackend::MovFromPC(ARM64Reg r) {
|
||||
LDR(INDEX_UNSIGNED, r, CTXREG, offsetof(MIPSState, pc));
|
||||
}
|
||||
|
||||
void Arm64JitBackend::MovToPC(ARM64Reg r) {
|
||||
STR(INDEX_UNSIGNED, r, CTXREG, offsetof(MIPSState, pc));
|
||||
}
|
||||
|
||||
void Arm64JitBackend::SaveStaticRegisters() {
|
||||
if (jo.useStaticAlloc) {
|
||||
QuickCallFunction(SCRATCH2_64, saveStaticRegisters_);
|
||||
} else {
|
||||
// Inline the single operation
|
||||
STR(INDEX_UNSIGNED, DOWNCOUNTREG, CTXREG, offsetof(MIPSState, downcount));
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64JitBackend::LoadStaticRegisters() {
|
||||
if (jo.useStaticAlloc) {
|
||||
QuickCallFunction(SCRATCH2_64, loadStaticRegisters_);
|
||||
} else {
|
||||
LDR(INDEX_UNSIGNED, DOWNCOUNTREG, CTXREG, offsetof(MIPSState, downcount));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace MIPSComp
|
||||
|
||||
#endif
|
153
Core/MIPS/ARM64/Arm64IRJit.h
Normal file
153
Core/MIPS/ARM64/Arm64IRJit.h
Normal file
|
@ -0,0 +1,153 @@
|
|||
// Copyright (c) 2023- PPSSPP Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0 or later versions.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ppsspp_config.h"
|
||||
// In other words, PPSSPP_ARCH(ARM64) || DISASM_ALL.
|
||||
#if PPSSPP_ARCH(ARM64) || (PPSSPP_PLATFORM(WINDOWS) && !defined(__LIBRETRO__))
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "Common/Arm64Emitter.h"
|
||||
#include "Core/MIPS/IR/IRJit.h"
|
||||
#include "Core/MIPS/IR/IRNativeCommon.h"
|
||||
#include "Core/MIPS/JitCommon/JitState.h"
|
||||
#include "Core/MIPS/JitCommon/JitCommon.h"
|
||||
#include "Core/MIPS/ARM64/Arm64IRRegCache.h"
|
||||
|
||||
namespace MIPSComp {
|
||||
|
||||
class Arm64JitBackend : public Arm64Gen::ARM64CodeBlock, public IRNativeBackend {
|
||||
public:
|
||||
Arm64JitBackend(JitOptions &jo, IRBlockCache &blocks);
|
||||
~Arm64JitBackend();
|
||||
|
||||
bool DescribeCodePtr(const u8 *ptr, std::string &name) const override;
|
||||
|
||||
void GenerateFixedCode(MIPSState *mipsState) override;
|
||||
bool CompileBlock(IRBlock *block, int block_num, bool preload) override;
|
||||
void ClearAllBlocks() override;
|
||||
void InvalidateBlock(IRBlock *block, int block_num) override;
|
||||
|
||||
void UpdateFCR31(MIPSState *mipsState) override;
|
||||
|
||||
protected:
|
||||
const CodeBlockCommon &CodeBlock() const override {
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
void RestoreRoundingMode(bool force = false);
|
||||
void ApplyRoundingMode(bool force = false);
|
||||
void UpdateRoundingMode(bool force = false);
|
||||
void MovFromPC(Arm64Gen::ARM64Reg r);
|
||||
void MovToPC(Arm64Gen::ARM64Reg r);
|
||||
|
||||
void SaveStaticRegisters();
|
||||
void LoadStaticRegisters();
|
||||
|
||||
// Note: destroys SCRATCH1.
|
||||
void FlushAll();
|
||||
|
||||
void WriteConstExit(uint32_t pc);
|
||||
void OverwriteExit(int srcOffset, int len, int block_num) override;
|
||||
|
||||
void CompIR_Arith(IRInst inst) override;
|
||||
void CompIR_Assign(IRInst inst) override;
|
||||
void CompIR_Basic(IRInst inst) override;
|
||||
void CompIR_Bits(IRInst inst) override;
|
||||
void CompIR_Breakpoint(IRInst inst) override;
|
||||
void CompIR_Compare(IRInst inst) override;
|
||||
void CompIR_CondAssign(IRInst inst) override;
|
||||
void CompIR_CondStore(IRInst inst) override;
|
||||
void CompIR_Div(IRInst inst) override;
|
||||
void CompIR_Exit(IRInst inst) override;
|
||||
void CompIR_ExitIf(IRInst inst) override;
|
||||
void CompIR_FArith(IRInst inst) override;
|
||||
void CompIR_FAssign(IRInst inst) override;
|
||||
void CompIR_FCompare(IRInst inst) override;
|
||||
void CompIR_FCondAssign(IRInst inst) override;
|
||||
void CompIR_FCvt(IRInst inst) override;
|
||||
void CompIR_FLoad(IRInst inst) override;
|
||||
void CompIR_FRound(IRInst inst) override;
|
||||
void CompIR_FSat(IRInst inst) override;
|
||||
void CompIR_FSpecial(IRInst inst) override;
|
||||
void CompIR_FStore(IRInst inst) override;
|
||||
void CompIR_Generic(IRInst inst) override;
|
||||
void CompIR_HiLo(IRInst inst) override;
|
||||
void CompIR_Interpret(IRInst inst) override;
|
||||
void CompIR_Load(IRInst inst) override;
|
||||
void CompIR_LoadShift(IRInst inst) override;
|
||||
void CompIR_Logic(IRInst inst) override;
|
||||
void CompIR_Mult(IRInst inst) override;
|
||||
void CompIR_RoundingMode(IRInst inst) override;
|
||||
void CompIR_Shift(IRInst inst) override;
|
||||
void CompIR_Store(IRInst inst) override;
|
||||
void CompIR_StoreShift(IRInst inst) override;
|
||||
void CompIR_System(IRInst inst) override;
|
||||
void CompIR_Transfer(IRInst inst) override;
|
||||
void CompIR_VecArith(IRInst inst) override;
|
||||
void CompIR_VecAssign(IRInst inst) override;
|
||||
void CompIR_VecClamp(IRInst inst) override;
|
||||
void CompIR_VecHoriz(IRInst inst) override;
|
||||
void CompIR_VecLoad(IRInst inst) override;
|
||||
void CompIR_VecPack(IRInst inst) override;
|
||||
void CompIR_VecStore(IRInst inst) override;
|
||||
void CompIR_ValidateAddress(IRInst inst) override;
|
||||
|
||||
JitOptions &jo;
|
||||
Arm64IRRegCache regs_;
|
||||
Arm64Gen::ARM64FloatEmitter fp_;
|
||||
|
||||
const u8 *outerLoop_ = nullptr;
|
||||
const u8 *outerLoopPCInSCRATCH1_ = nullptr;
|
||||
const u8 *dispatcherCheckCoreState_ = nullptr;
|
||||
const u8 *dispatcherPCInSCRATCH1_ = nullptr;
|
||||
const u8 *dispatcherNoCheck_ = nullptr;
|
||||
const u8 *restoreRoundingMode_ = nullptr;
|
||||
const u8 *applyRoundingMode_ = nullptr;
|
||||
const u8 *updateRoundingMode_ = nullptr;
|
||||
|
||||
const u8 *saveStaticRegisters_ = nullptr;
|
||||
const u8 *loadStaticRegisters_ = nullptr;
|
||||
|
||||
// Indexed by FPCR FZ:RN bits for convenience. Uses SCRATCH2.
|
||||
const u8 *convertS0ToSCRATCH1_[8];
|
||||
|
||||
// Note: mutable state used at runtime.
|
||||
const u8 *currentRoundingFunc_ = nullptr;
|
||||
|
||||
int jitStartOffset_ = 0;
|
||||
int compilingBlockNum_ = -1;
|
||||
int logBlocks_ = 0;
|
||||
};
|
||||
|
||||
class Arm64IRJit : public IRNativeJit {
|
||||
public:
|
||||
Arm64IRJit(MIPSState *mipsState)
|
||||
: IRNativeJit(mipsState), arm64Backend_(jo, blocks_) {
|
||||
Init(arm64Backend_);
|
||||
}
|
||||
|
||||
private:
|
||||
Arm64JitBackend arm64Backend_;
|
||||
};
|
||||
|
||||
} // namespace MIPSComp
|
||||
|
||||
#endif
|
577
Core/MIPS/ARM64/Arm64IRRegCache.cpp
Normal file
577
Core/MIPS/ARM64/Arm64IRRegCache.cpp
Normal file
|
@ -0,0 +1,577 @@
|
|||
// Copyright (c) 2023- PPSSPP Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0 or later versions.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#include "ppsspp_config.h"
|
||||
// In other words, PPSSPP_ARCH(ARM64) || DISASM_ALL.
|
||||
#if PPSSPP_ARCH(ARM64) || (PPSSPP_PLATFORM(WINDOWS) && !defined(__LIBRETRO__))
|
||||
|
||||
#ifndef offsetof
|
||||
#include <cstddef>
|
||||
#endif
|
||||
|
||||
#include "Common/CPUDetect.h"
|
||||
#include "Common/LogReporting.h"
|
||||
#include "Core/MemMap.h"
|
||||
#include "Core/MIPS/IR/IRInst.h"
|
||||
#include "Core/MIPS/IR/IRAnalysis.h"
|
||||
#include "Core/MIPS/ARM64/Arm64IRRegCache.h"
|
||||
#include "Core/MIPS/JitCommon/JitState.h"
|
||||
|
||||
using namespace Arm64Gen;
|
||||
using namespace Arm64IRJitConstants;
|
||||
|
||||
Arm64IRRegCache::Arm64IRRegCache(MIPSComp::JitOptions *jo)
|
||||
: IRNativeRegCacheBase(jo) {
|
||||
// The S/D/Q regs overlap, so we just use one slot. The numbers don't match ARM64Reg.
|
||||
config_.totalNativeRegs = NUM_X_REGS + NUM_X_FREGS;
|
||||
config_.mapFPUSIMD = true;
|
||||
// XMM regs are used for both FPU and Vec, so we don't need VREGs.
|
||||
config_.mapUseVRegs = false;
|
||||
}
|
||||
|
||||
void Arm64IRRegCache::Init(ARM64XEmitter *emitter, ARM64FloatEmitter *fp) {
|
||||
emit_ = emitter;
|
||||
fp_ = fp;
|
||||
}
|
||||
|
||||
const int *Arm64IRRegCache::GetAllocationOrder(MIPSLoc type, MIPSMap flags, int &count, int &base) const {
|
||||
if (type == MIPSLoc::REG) {
|
||||
// See register alloc remarks in Arm64Asm.cpp.
|
||||
base = W0;
|
||||
|
||||
// W19-W23 are most suitable for static allocation. Those that are chosen for static allocation
|
||||
// should be omitted here and added in GetStaticAllocations.
|
||||
static const int allocationOrder[] = {
|
||||
W19, W20, W21, W22, W23, W24, W0, W1, W2, W3, W4, W5, W6, W7, W8, W9, W10, W11, W12, W13, W14, W15,
|
||||
};
|
||||
static const int allocationOrderStaticAlloc[] = {
|
||||
W0, W1, W2, W3, W4, W5, W6, W7, W8, W9, W10, W11, W12, W13, W14, W15,
|
||||
};
|
||||
|
||||
if (jo_->useStaticAlloc) {
|
||||
count = ARRAY_SIZE(allocationOrderStaticAlloc);
|
||||
return allocationOrderStaticAlloc;
|
||||
}
|
||||
count = ARRAY_SIZE(allocationOrder);
|
||||
return allocationOrder;
|
||||
} else if (type == MIPSLoc::FREG) {
|
||||
base = S0 - NUM_X_REGS;
|
||||
|
||||
// We don't really need four temps, probably.
|
||||
// We start with S8 for call flushes.
|
||||
static const int allocationOrder[] = {
|
||||
// Reserve four full 128-bit temp registers, should be plenty.
|
||||
S8, S9, S10, S11, // Partially callee-save (bottom 64 bits)
|
||||
S12, S13, S14, S15, // Partially callee-save (bottom 64 bits)
|
||||
S16, S17, S18, S19,
|
||||
S20, S21, S22, S23,
|
||||
S24, S25, S26, S27,
|
||||
S28, S29, S30, S31,
|
||||
S4, S5, S6, S7,
|
||||
};
|
||||
|
||||
count = ARRAY_SIZE(allocationOrder);
|
||||
return allocationOrder;
|
||||
} else {
|
||||
_assert_msg_(false, "Allocation order not yet implemented");
|
||||
count = 0;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
const Arm64IRRegCache::StaticAllocation *Arm64IRRegCache::GetStaticAllocations(int &count) const {
|
||||
static const StaticAllocation allocs[] = {
|
||||
{ MIPS_REG_SP, W19, MIPSLoc::REG, true },
|
||||
{ MIPS_REG_V0, W20, MIPSLoc::REG },
|
||||
{ MIPS_REG_V1, W21, MIPSLoc::REG },
|
||||
{ MIPS_REG_A0, W22, MIPSLoc::REG },
|
||||
{ MIPS_REG_A1, W23, MIPSLoc::REG },
|
||||
{ MIPS_REG_RA, W24, MIPSLoc::REG },
|
||||
};
|
||||
|
||||
if (jo_->useStaticAlloc) {
|
||||
count = ARRAY_SIZE(allocs);
|
||||
return allocs;
|
||||
}
|
||||
return IRNativeRegCacheBase::GetStaticAllocations(count);
|
||||
}
|
||||
|
||||
void Arm64IRRegCache::EmitLoadStaticRegisters() {
|
||||
int count = 0;
|
||||
const StaticAllocation *allocs = GetStaticAllocations(count);
|
||||
for (int i = 0; i < count; ++i) {
|
||||
int offset = GetMipsRegOffset(allocs[i].mr);
|
||||
if (i + 1 < count && allocs[i].mr == allocs[i + 1].mr - 1) {
|
||||
_assert_(!allocs[i].pointerified && !allocs[i + 1].pointerified);
|
||||
emit_->LDP(INDEX_SIGNED, FromNativeReg(allocs[i].nr), FromNativeReg(allocs[i + 1].nr), CTXREG, offset);
|
||||
++i;
|
||||
} else {
|
||||
emit_->LDR(INDEX_UNSIGNED, FromNativeReg(allocs[i].nr), CTXREG, offset);
|
||||
if (allocs[i].pointerified && jo_->enablePointerify) {
|
||||
ARM64Reg r64 = FromNativeReg64(allocs[i].nr);
|
||||
uint32_t membaseHigh = (uint32_t)((uint64_t)Memory::base >> 32);
|
||||
emit_->MOVK(r64, membaseHigh & 0xFFFF, SHIFT_32);
|
||||
if (membaseHigh & 0xFFFF0000)
|
||||
emit_->MOVK(r64, membaseHigh >> 16, SHIFT_48);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64IRRegCache::EmitSaveStaticRegisters() {
|
||||
int count = 0;
|
||||
const StaticAllocation *allocs = GetStaticAllocations(count);
|
||||
// This only needs to run once (by Asm) so checks don't need to be fast.
|
||||
for (int i = 0; i < count; ++i) {
|
||||
int offset = GetMipsRegOffset(allocs[i].mr);
|
||||
if (i + 1 < count && allocs[i].mr == allocs[i + 1].mr - 1) {
|
||||
emit_->STP(INDEX_SIGNED, FromNativeReg(allocs[i].nr), FromNativeReg(allocs[i + 1].nr), CTXREG, offset);
|
||||
++i;
|
||||
} else {
|
||||
emit_->STR(INDEX_UNSIGNED, FromNativeReg(allocs[i].nr), CTXREG, offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64IRRegCache::FlushBeforeCall() {
|
||||
// These registers are not preserved by function calls.
|
||||
auto isGPRSaved = [&](IRNativeReg nreg) {
|
||||
ARM64Reg ar = FromNativeReg(nreg);
|
||||
return ar >= W19 && ar <= W29;
|
||||
};
|
||||
auto isFPRSaved = [&](IRNativeReg nreg) {
|
||||
ARM64Reg ar = FromNativeReg(nreg);
|
||||
return ar >= S8 && ar <= S15;
|
||||
};
|
||||
|
||||
// Go through by IR index first, to use STP where we can.
|
||||
for (int i = 1; i < TOTAL_MAPPABLE_IRREGS - 1; ++i) {
|
||||
if (mr[i].nReg == -1 || mr[i + 1].nReg == -1 || mr[i].isStatic || mr[i + 1].isStatic)
|
||||
continue;
|
||||
// Ignore multilane regs.
|
||||
if (mr[i].lane != -1 || mr[i + 1].lane != -1)
|
||||
continue;
|
||||
if (!nr[mr[i].nReg].isDirty || !nr[mr[i + 1].nReg].isDirty)
|
||||
continue;
|
||||
|
||||
int offset = GetMipsRegOffset(i);
|
||||
|
||||
// Okay, it's a maybe. Are we flushing both as GPRs?
|
||||
if (!isGPRSaved(mr[i].nReg) && !isGPRSaved(mr[i + 1].nReg) && offset <= 252) {
|
||||
// If either is mapped as a pointer, fix it.
|
||||
if (mr[i].loc == MIPSLoc::REG_AS_PTR)
|
||||
AdjustNativeRegAsPtr(mr[i].nReg, false);
|
||||
if (mr[i + 1].loc == MIPSLoc::REG_AS_PTR)
|
||||
AdjustNativeRegAsPtr(mr[i + 1].nReg, false);
|
||||
|
||||
// That means we should use STP.
|
||||
emit_->STP(INDEX_SIGNED, FromNativeReg(mr[i].nReg), FromNativeReg(mr[i + 1].nReg), CTXREG, offset);
|
||||
|
||||
DiscardNativeReg(mr[i].nReg);
|
||||
DiscardNativeReg(mr[i + 1].nReg);
|
||||
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Perhaps as FPRs? Note: these must be single lane at this point.
|
||||
// TODO: Could use STP on quads etc. too, i.e. i & i + 4.
|
||||
if (!isFPRSaved(mr[i].nReg) && !isFPRSaved(mr[i + 1].nReg) && offset <= 252) {
|
||||
fp_->STP(32, INDEX_SIGNED, FromNativeReg(mr[i].nReg), FromNativeReg(mr[i + 1].nReg), CTXREG, offset);
|
||||
|
||||
DiscardNativeReg(mr[i].nReg);
|
||||
DiscardNativeReg(mr[i + 1].nReg);
|
||||
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Alright, now go through any that didn't get flushed with STP.
|
||||
for (int i = 0; i < 19; ++i) {
|
||||
FlushNativeReg(GPRToNativeReg(ARM64Reg(W0 + i)));
|
||||
}
|
||||
FlushNativeReg(GPRToNativeReg(W30));
|
||||
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
FlushNativeReg(VFPToNativeReg(ARM64Reg(S0 + i)));
|
||||
}
|
||||
for (int i = 8; i < 16; ++i) {
|
||||
// These are preserved but only the low 64 bits.
|
||||
IRNativeReg nreg = VFPToNativeReg(ARM64Reg(S0 + i));
|
||||
if (nr[nreg].mipsReg != IRREG_INVALID && GetFPRLaneCount(nr[nreg].mipsReg - 32) > 2)
|
||||
FlushNativeReg(nreg);
|
||||
}
|
||||
for (int i = 16; i < 32; ++i) {
|
||||
FlushNativeReg(VFPToNativeReg(ARM64Reg(S0 + i)));
|
||||
}
|
||||
}
|
||||
|
||||
ARM64Reg Arm64IRRegCache::TryMapTempImm(IRReg r) {
|
||||
_dbg_assert_(IsValidGPR(r));
|
||||
|
||||
// If already mapped, no need for a temporary.
|
||||
if (IsGPRMapped(r)) {
|
||||
return R(r);
|
||||
}
|
||||
|
||||
if (mr[r].loc == MIPSLoc::IMM) {
|
||||
// Can we just use zero?
|
||||
if (mr[r].imm == 0)
|
||||
return WZR;
|
||||
|
||||
// Try our luck - check for an exact match in another xreg.
|
||||
for (int i = 1; i < TOTAL_MAPPABLE_IRREGS; ++i) {
|
||||
if (mr[i].loc == MIPSLoc::REG_IMM && mr[i].imm == mr[r].imm) {
|
||||
// Awesome, let's just use this reg.
|
||||
return FromNativeReg(mr[i].nReg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return INVALID_REG;
|
||||
}
|
||||
|
||||
ARM64Reg Arm64IRRegCache::GetAndLockTempGPR() {
|
||||
IRNativeReg reg = AllocateReg(MIPSLoc::REG, MIPSMap::INIT);
|
||||
if (reg != -1) {
|
||||
nr[reg].tempLockIRIndex = irIndex_;
|
||||
}
|
||||
return FromNativeReg(reg);
|
||||
}
|
||||
|
||||
ARM64Reg Arm64IRRegCache::GetAndLockTempFPR() {
|
||||
IRNativeReg reg = AllocateReg(MIPSLoc::FREG, MIPSMap::INIT);
|
||||
if (reg != -1) {
|
||||
nr[reg].tempLockIRIndex = irIndex_;
|
||||
}
|
||||
return FromNativeReg(reg);
|
||||
}
|
||||
|
||||
ARM64Reg Arm64IRRegCache::MapWithFPRTemp(const IRInst &inst) {
|
||||
return FromNativeReg(MapWithTemp(inst, MIPSLoc::FREG));
|
||||
}
|
||||
|
||||
ARM64Reg Arm64IRRegCache::MapGPR(IRReg mipsReg, MIPSMap mapFlags) {
|
||||
_dbg_assert_(IsValidGPR(mipsReg));
|
||||
|
||||
// Okay, not mapped, so we need to allocate an arm64 register.
|
||||
IRNativeReg nreg = MapNativeReg(MIPSLoc::REG, mipsReg, 1, mapFlags);
|
||||
return FromNativeReg(nreg);
|
||||
}
|
||||
|
||||
ARM64Reg Arm64IRRegCache::MapGPR2(IRReg mipsReg, MIPSMap mapFlags) {
|
||||
_dbg_assert_(IsValidGPR(mipsReg) && IsValidGPR(mipsReg + 1));
|
||||
|
||||
// Okay, not mapped, so we need to allocate an arm64 register.
|
||||
IRNativeReg nreg = MapNativeReg(MIPSLoc::REG, mipsReg, 2, mapFlags);
|
||||
return FromNativeReg64(nreg);
|
||||
}
|
||||
|
||||
ARM64Reg Arm64IRRegCache::MapGPRAsPointer(IRReg reg) {
|
||||
return FromNativeReg64(MapNativeRegAsPointer(reg));
|
||||
}
|
||||
|
||||
ARM64Reg Arm64IRRegCache::MapFPR(IRReg mipsReg, MIPSMap mapFlags) {
|
||||
_dbg_assert_(IsValidFPR(mipsReg));
|
||||
_dbg_assert_(mr[mipsReg + 32].loc == MIPSLoc::MEM || mr[mipsReg + 32].loc == MIPSLoc::FREG);
|
||||
|
||||
IRNativeReg nreg = MapNativeReg(MIPSLoc::FREG, mipsReg + 32, 1, mapFlags);
|
||||
if (nreg != -1)
|
||||
return FromNativeReg(nreg);
|
||||
return INVALID_REG;
|
||||
}
|
||||
|
||||
ARM64Reg Arm64IRRegCache::MapVec4(IRReg first, MIPSMap mapFlags) {
|
||||
_dbg_assert_(IsValidFPR(first));
|
||||
_dbg_assert_((first & 3) == 0);
|
||||
_dbg_assert_(mr[first + 32].loc == MIPSLoc::MEM || mr[first + 32].loc == MIPSLoc::FREG);
|
||||
|
||||
IRNativeReg nreg = MapNativeReg(MIPSLoc::FREG, first + 32, 4, mapFlags);
|
||||
if (nreg != -1)
|
||||
return EncodeRegToQuad(FromNativeReg(nreg));
|
||||
return INVALID_REG;
|
||||
}
|
||||
|
||||
void Arm64IRRegCache::AdjustNativeRegAsPtr(IRNativeReg nreg, bool state) {
|
||||
_assert_(nreg >= 0 && nreg < (IRNativeReg)WZR);
|
||||
ARM64Reg r = FromNativeReg64(nreg);
|
||||
if (state) {
|
||||
if (!jo_->enablePointerify) {
|
||||
#if defined(MASKED_PSP_MEMORY)
|
||||
// This destroys the value...
|
||||
_dbg_assert_(!nr[nreg].isDirty);
|
||||
emit_->ANDI2R(r, r, Memory::MEMVIEW32_MASK);
|
||||
#endif
|
||||
emit_->ADD(r, r, MEMBASEREG);
|
||||
} else {
|
||||
uint32_t membaseHigh = (uint32_t)((uint64_t)Memory::base >> 32);
|
||||
emit_->MOVK(r, membaseHigh & 0xFFFF, SHIFT_32);
|
||||
if (membaseHigh & 0xFFFF0000)
|
||||
emit_->MOVK(r, membaseHigh >> 16, SHIFT_48);
|
||||
}
|
||||
} else {
|
||||
if (!jo_->enablePointerify) {
|
||||
#if defined(MASKED_PSP_MEMORY)
|
||||
_dbg_assert_(!nr[nreg].isDirty);
|
||||
#endif
|
||||
emit_->SUB(r, r, MEMBASEREG);
|
||||
} else {
|
||||
// Nothing to do, just ignore the high 32 bits.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Arm64IRRegCache::IsNativeRegCompatible(IRNativeReg nreg, MIPSLoc type, MIPSMap flags) {
|
||||
// No special flags, skip the check for a little speed.
|
||||
return true;
|
||||
}
|
||||
|
||||
void Arm64IRRegCache::LoadNativeReg(IRNativeReg nreg, IRReg first, int lanes) {
|
||||
ARM64Reg r = FromNativeReg(nreg);
|
||||
_dbg_assert_(first != MIPS_REG_ZERO);
|
||||
if (nreg < NUM_X_REGS) {
|
||||
_assert_(lanes == 1 || (lanes == 2 && first == IRREG_LO));
|
||||
if (lanes == 1)
|
||||
emit_->LDR(INDEX_UNSIGNED, r, CTXREG, GetMipsRegOffset(first));
|
||||
else if (lanes == 2)
|
||||
emit_->LDR(INDEX_UNSIGNED, EncodeRegTo64(r), CTXREG, GetMipsRegOffset(first));
|
||||
else
|
||||
_assert_(false);
|
||||
} else {
|
||||
_dbg_assert_(nreg < NUM_X_REGS + NUM_X_FREGS);
|
||||
_assert_msg_(mr[first].loc == MIPSLoc::FREG, "Cannot load this type: %d", (int)mr[first].loc);
|
||||
if (lanes == 1)
|
||||
fp_->LDR(32, INDEX_UNSIGNED, r, CTXREG, GetMipsRegOffset(first));
|
||||
else if (lanes == 2)
|
||||
fp_->LDR(64, INDEX_UNSIGNED, r, CTXREG, GetMipsRegOffset(first));
|
||||
else if (lanes == 4)
|
||||
fp_->LDR(128, INDEX_UNSIGNED, r, CTXREG, GetMipsRegOffset(first));
|
||||
else
|
||||
_assert_(false);
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64IRRegCache::StoreNativeReg(IRNativeReg nreg, IRReg first, int lanes) {
|
||||
ARM64Reg r = FromNativeReg(nreg);
|
||||
_dbg_assert_(first != MIPS_REG_ZERO);
|
||||
if (nreg < NUM_X_REGS) {
|
||||
_assert_(lanes == 1 || (lanes == 2 && first == IRREG_LO));
|
||||
_assert_(mr[first].loc == MIPSLoc::REG || mr[first].loc == MIPSLoc::REG_IMM);
|
||||
if (lanes == 1)
|
||||
emit_->STR(INDEX_UNSIGNED, r, CTXREG, GetMipsRegOffset(first));
|
||||
else if (lanes == 2)
|
||||
emit_->STR(INDEX_UNSIGNED, EncodeRegTo64(r), CTXREG, GetMipsRegOffset(first));
|
||||
else
|
||||
_assert_(false);
|
||||
} else {
|
||||
_dbg_assert_(nreg < NUM_X_REGS + NUM_X_FREGS);
|
||||
_assert_msg_(mr[first].loc == MIPSLoc::FREG, "Cannot store this type: %d", (int)mr[first].loc);
|
||||
if (lanes == 1)
|
||||
fp_->STR(32, INDEX_UNSIGNED, r, CTXREG, GetMipsRegOffset(first));
|
||||
else if (lanes == 2)
|
||||
fp_->STR(64, INDEX_UNSIGNED, r, CTXREG, GetMipsRegOffset(first));
|
||||
else if (lanes == 4)
|
||||
fp_->STR(128, INDEX_UNSIGNED, r, CTXREG, GetMipsRegOffset(first));
|
||||
else
|
||||
_assert_(false);
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64IRRegCache::SetNativeRegValue(IRNativeReg nreg, uint32_t imm) {
|
||||
ARM64Reg r = FromNativeReg(nreg);
|
||||
_dbg_assert_(nreg >= 0 && nreg < (IRNativeReg)WZR);
|
||||
// On ARM64, MOVZ/MOVK is really fast.
|
||||
emit_->MOVI2R(r, imm);
|
||||
}
|
||||
|
||||
void Arm64IRRegCache::StoreRegValue(IRReg mreg, uint32_t imm) {
|
||||
_assert_(IsValidGPRNoZero(mreg));
|
||||
// Try to optimize using a different reg.
|
||||
ARM64Reg storeReg = INVALID_REG;
|
||||
if (imm == 0)
|
||||
storeReg = WZR;
|
||||
|
||||
// Could we get lucky? Check for an exact match in another xreg.
|
||||
for (int i = 1; i < TOTAL_MAPPABLE_IRREGS; ++i) {
|
||||
if (mr[i].loc == MIPSLoc::REG_IMM && mr[i].imm == imm) {
|
||||
// Awesome, let's just store this reg.
|
||||
storeReg = (ARM64Reg)mr[i].nReg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (storeReg == INVALID_REG) {
|
||||
emit_->MOVI2R(SCRATCH1, imm);
|
||||
storeReg = SCRATCH1;
|
||||
}
|
||||
emit_->STR(INDEX_UNSIGNED, storeReg, CTXREG, GetMipsRegOffset(mreg));
|
||||
}
|
||||
|
||||
void Arm64IRRegCache::FlushAll(bool gprs, bool fprs) {
|
||||
// Note: make sure not to change the registers when flushing:
|
||||
// Branching code may expect the armreg to retain its value.
|
||||
|
||||
// Try to flush in pairs when possible.
|
||||
for (int i = 1; i < TOTAL_MAPPABLE_IRREGS - 1; ++i) {
|
||||
if (mr[i].loc == MIPSLoc::MEM || mr[i].loc == MIPSLoc::MEM || mr[i].isStatic || mr[i + 1].isStatic)
|
||||
continue;
|
||||
// Ignore multilane regs. Could handle with more smartness...
|
||||
if (mr[i].lane != -1 || mr[i + 1].lane != -1)
|
||||
continue;
|
||||
if (mr[i].nReg != -1 && !nr[mr[i].nReg].isDirty)
|
||||
continue;
|
||||
if (mr[i + 1].nReg != -1 && !nr[mr[i + 1].nReg].isDirty)
|
||||
continue;
|
||||
if (mr[i].loc == MIPSLoc::MEM || mr[i + 1].loc == MIPSLoc::MEM)
|
||||
continue;
|
||||
|
||||
int offset = GetMipsRegOffset(i);
|
||||
|
||||
// If both are imms, let's materialize a single reg and store.
|
||||
if (mr[i].loc == MIPSLoc::IMM && mr[i + 1].loc == MIPSLoc::IMM) {
|
||||
if ((i & 1) == 0) {
|
||||
uint64_t fullImm = ((uint64_t) mr[i + 1].imm << 32) | mr[i].imm;
|
||||
emit_->MOVI2R(SCRATCH1_64, fullImm);
|
||||
emit_->STR(INDEX_UNSIGNED, SCRATCH1_64, CTXREG, offset);
|
||||
DiscardReg(i);
|
||||
DiscardReg(i + 1);
|
||||
++i;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Okay, two dirty regs in a row, in need of flushing. Both GPRs?
|
||||
if (IsValidGPR(i) && IsValidGPR(i + 1) && offset <= 252) {
|
||||
auto setupForFlush = [&](ARM64Reg &ar, IRReg r) {
|
||||
if (mr[r].loc == MIPSLoc::IMM) {
|
||||
ar = TryMapTempImm(r);
|
||||
if (ar == INVALID_REG) {
|
||||
// Both cannot be imms, so this is safe.
|
||||
ar = SCRATCH1;
|
||||
emit_->MOVI2R(ar, mr[r].imm);
|
||||
}
|
||||
} else if (mr[r].loc == MIPSLoc::REG_AS_PTR) {
|
||||
AdjustNativeRegAsPtr(r, false);
|
||||
ar = FromNativeReg(mr[r].nReg);
|
||||
} else {
|
||||
_dbg_assert_(mr[r].loc == MIPSLoc::REG || mr[r].loc == MIPSLoc::REG_IMM);
|
||||
ar = FromNativeReg(mr[r].nReg);
|
||||
}
|
||||
};
|
||||
|
||||
ARM64Reg armRegs[2]{ INVALID_REG, INVALID_REG };
|
||||
setupForFlush(armRegs[0], i);
|
||||
setupForFlush(armRegs[1], i + 1);
|
||||
|
||||
emit_->STP(INDEX_SIGNED, armRegs[0], armRegs[1], CTXREG, offset);
|
||||
DiscardReg(i);
|
||||
DiscardReg(i + 1);
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Perhaps as FPRs? Note: these must be single lane at this point.
|
||||
// TODO: Could use STP on quads etc. too, i.e. i & i + 4.
|
||||
if (i >= 32 && IsValidFPR(i - 32) && IsValidFPR(i + 1 - 32) && offset <= 252) {
|
||||
_dbg_assert_(mr[i].loc == MIPSLoc::FREG && mr[i + 1].loc == MIPSLoc::FREG);
|
||||
fp_->STP(32, INDEX_SIGNED, FromNativeReg(mr[i].nReg), FromNativeReg(mr[i + 1].nReg), CTXREG, offset);
|
||||
|
||||
DiscardNativeReg(mr[i].nReg);
|
||||
DiscardNativeReg(mr[i + 1].nReg);
|
||||
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Flush all the rest that weren't done via STP.
|
||||
IRNativeRegCacheBase::FlushAll(gprs, fprs);
|
||||
}
|
||||
|
||||
ARM64Reg Arm64IRRegCache::R(IRReg mipsReg) {
|
||||
_dbg_assert_(IsValidGPR(mipsReg));
|
||||
_dbg_assert_(mr[mipsReg].loc == MIPSLoc::REG || mr[mipsReg].loc == MIPSLoc::REG_IMM);
|
||||
if (mr[mipsReg].loc == MIPSLoc::REG || mr[mipsReg].loc == MIPSLoc::REG_IMM) {
|
||||
return FromNativeReg(mr[mipsReg].nReg);
|
||||
} else {
|
||||
ERROR_LOG_REPORT(JIT, "Reg %i not in arm64 reg", mipsReg);
|
||||
return INVALID_REG; // BAAAD
|
||||
}
|
||||
}
|
||||
|
||||
ARM64Reg Arm64IRRegCache::RPtr(IRReg mipsReg) {
|
||||
_dbg_assert_(IsValidGPR(mipsReg));
|
||||
_dbg_assert_(mr[mipsReg].loc == MIPSLoc::REG || mr[mipsReg].loc == MIPSLoc::REG_IMM || mr[mipsReg].loc == MIPSLoc::REG_AS_PTR);
|
||||
if (mr[mipsReg].loc == MIPSLoc::REG_AS_PTR) {
|
||||
return FromNativeReg64(mr[mipsReg].nReg);
|
||||
} else if (mr[mipsReg].loc == MIPSLoc::REG || mr[mipsReg].loc == MIPSLoc::REG_IMM) {
|
||||
int r = mr[mipsReg].nReg;
|
||||
_dbg_assert_(nr[r].pointerified);
|
||||
if (nr[r].pointerified) {
|
||||
return FromNativeReg64(mr[mipsReg].nReg);
|
||||
} else {
|
||||
ERROR_LOG(JIT, "Tried to use a non-pointer register as a pointer");
|
||||
return INVALID_REG;
|
||||
}
|
||||
} else {
|
||||
ERROR_LOG_REPORT(JIT, "Reg %i not in arm64 reg", mipsReg);
|
||||
return INVALID_REG; // BAAAD
|
||||
}
|
||||
}
|
||||
|
||||
ARM64Reg Arm64IRRegCache::F(IRReg mipsReg) {
|
||||
_dbg_assert_(IsValidFPR(mipsReg));
|
||||
_dbg_assert_(mr[mipsReg + 32].loc == MIPSLoc::FREG);
|
||||
if (mr[mipsReg + 32].loc == MIPSLoc::FREG) {
|
||||
return FromNativeReg(mr[mipsReg + 32].nReg);
|
||||
} else {
|
||||
ERROR_LOG_REPORT(JIT, "Reg %i not in arm64 reg", mipsReg);
|
||||
return INVALID_REG; // BAAAD
|
||||
}
|
||||
}
|
||||
|
||||
ARM64Reg Arm64IRRegCache::FD(IRReg mipsReg) {
|
||||
return EncodeRegToDouble(F(mipsReg));
|
||||
}
|
||||
|
||||
ARM64Reg Arm64IRRegCache::FQ(IRReg mipsReg) {
|
||||
return EncodeRegToQuad(F(mipsReg));
|
||||
}
|
||||
|
||||
IRNativeReg Arm64IRRegCache::GPRToNativeReg(ARM64Reg r) {
|
||||
_dbg_assert_msg_(r >= 0 && r < 0x40, "Not a GPR?");
|
||||
return (IRNativeReg)DecodeReg(r);
|
||||
}
|
||||
|
||||
IRNativeReg Arm64IRRegCache::VFPToNativeReg(ARM64Reg r) {
|
||||
_dbg_assert_msg_(r >= 0x40 && r < 0xE0, "Not VFP?");
|
||||
return (IRNativeReg)(NUM_X_REGS + (int)DecodeReg(r));
|
||||
}
|
||||
|
||||
ARM64Reg Arm64IRRegCache::FromNativeReg(IRNativeReg r) {
|
||||
if (r >= NUM_X_REGS)
|
||||
return EncodeRegToSingle((Arm64Gen::ARM64Reg)r);
|
||||
return (Arm64Gen::ARM64Reg)r;
|
||||
}
|
||||
|
||||
ARM64Reg Arm64IRRegCache::FromNativeReg64(IRNativeReg r) {
|
||||
_dbg_assert_msg_(r >= 0 && r < NUM_X_REGS, "Not a GPR?");
|
||||
return EncodeRegTo64((Arm64Gen::ARM64Reg)r);
|
||||
}
|
||||
|
||||
#endif
|
108
Core/MIPS/ARM64/Arm64IRRegCache.h
Normal file
108
Core/MIPS/ARM64/Arm64IRRegCache.h
Normal file
|
@ -0,0 +1,108 @@
|
|||
// Copyright (c) 2023- PPSSPP Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0 or later versions.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ppsspp_config.h"
|
||||
// In other words, PPSSPP_ARCH(ARM64) || DISASM_ALL.
|
||||
#if PPSSPP_ARCH(ARM64) || (PPSSPP_PLATFORM(WINDOWS) && !defined(__LIBRETRO__))
|
||||
|
||||
#include "Common/Arm64Emitter.h"
|
||||
#include "Core/MIPS/MIPS.h"
|
||||
#include "Core/MIPS/IR/IRJit.h"
|
||||
#include "Core/MIPS/IR/IRRegCache.h"
|
||||
|
||||
namespace Arm64IRJitConstants {
|
||||
|
||||
const Arm64Gen::ARM64Reg DOWNCOUNTREG = Arm64Gen::W25;
|
||||
// Note: this is actually offset from the base.
|
||||
const Arm64Gen::ARM64Reg JITBASEREG = Arm64Gen::X26;
|
||||
const Arm64Gen::ARM64Reg CTXREG = Arm64Gen::X27;
|
||||
const Arm64Gen::ARM64Reg MEMBASEREG = Arm64Gen::X28;
|
||||
const Arm64Gen::ARM64Reg SCRATCH1_64 = Arm64Gen::X16;
|
||||
const Arm64Gen::ARM64Reg SCRATCH2_64 = Arm64Gen::X17;
|
||||
const Arm64Gen::ARM64Reg SCRATCH1 = Arm64Gen::W16;
|
||||
const Arm64Gen::ARM64Reg SCRATCH2 = Arm64Gen::W17;
|
||||
// TODO: How many do we actually need?
|
||||
const Arm64Gen::ARM64Reg SCRATCHF1 = Arm64Gen::S0;
|
||||
const Arm64Gen::ARM64Reg SCRATCHF2 = Arm64Gen::S1;
|
||||
const Arm64Gen::ARM64Reg SCRATCHF3 = Arm64Gen::S2;
|
||||
const Arm64Gen::ARM64Reg SCRATCHF4 = Arm64Gen::S3;
|
||||
|
||||
} // namespace X64IRJitConstants
|
||||
|
||||
class Arm64IRRegCache : public IRNativeRegCacheBase {
|
||||
public:
|
||||
Arm64IRRegCache(MIPSComp::JitOptions *jo);
|
||||
|
||||
void Init(Arm64Gen::ARM64XEmitter *emitter, Arm64Gen::ARM64FloatEmitter *fp);
|
||||
|
||||
// May fail and return INVALID_REG if it needs flushing.
|
||||
Arm64Gen::ARM64Reg TryMapTempImm(IRReg reg);
|
||||
|
||||
// Returns an arm64 register containing the requested MIPS register.
|
||||
Arm64Gen::ARM64Reg MapGPR(IRReg reg, MIPSMap mapFlags = MIPSMap::INIT);
|
||||
Arm64Gen::ARM64Reg MapGPR2(IRReg reg, MIPSMap mapFlags = MIPSMap::INIT);
|
||||
Arm64Gen::ARM64Reg MapGPRAsPointer(IRReg reg);
|
||||
Arm64Gen::ARM64Reg MapFPR(IRReg reg, MIPSMap mapFlags = MIPSMap::INIT);
|
||||
Arm64Gen::ARM64Reg MapVec4(IRReg first, MIPSMap mapFlags = MIPSMap::INIT);
|
||||
|
||||
Arm64Gen::ARM64Reg MapWithFPRTemp(const IRInst &inst);
|
||||
|
||||
void FlushBeforeCall();
|
||||
void FlushAll(bool gprs = true, bool fprs = true) override;
|
||||
|
||||
Arm64Gen::ARM64Reg GetAndLockTempGPR();
|
||||
Arm64Gen::ARM64Reg GetAndLockTempFPR();
|
||||
|
||||
Arm64Gen::ARM64Reg R(IRReg preg); // Returns a cached register, while checking that it's NOT mapped as a pointer
|
||||
Arm64Gen::ARM64Reg RPtr(IRReg preg); // Returns a cached register, if it has been mapped as a pointer
|
||||
Arm64Gen::ARM64Reg F(IRReg preg);
|
||||
Arm64Gen::ARM64Reg FD(IRReg preg);
|
||||
Arm64Gen::ARM64Reg FQ(IRReg preg);
|
||||
|
||||
// These are called once on startup to generate functions, that you should then call.
|
||||
void EmitLoadStaticRegisters();
|
||||
void EmitSaveStaticRegisters();
|
||||
|
||||
protected:
|
||||
const StaticAllocation *GetStaticAllocations(int &count) const override;
|
||||
const int *GetAllocationOrder(MIPSLoc type, MIPSMap flags, int &count, int &base) const override;
|
||||
void AdjustNativeRegAsPtr(IRNativeReg nreg, bool state) override;
|
||||
|
||||
bool IsNativeRegCompatible(IRNativeReg nreg, MIPSLoc type, MIPSMap flags) override;
|
||||
void LoadNativeReg(IRNativeReg nreg, IRReg first, int lanes) override;
|
||||
void StoreNativeReg(IRNativeReg nreg, IRReg first, int lanes) override;
|
||||
void SetNativeRegValue(IRNativeReg nreg, uint32_t imm) override;
|
||||
void StoreRegValue(IRReg mreg, uint32_t imm) override;
|
||||
|
||||
private:
|
||||
IRNativeReg GPRToNativeReg(Arm64Gen::ARM64Reg r);
|
||||
IRNativeReg VFPToNativeReg(Arm64Gen::ARM64Reg r);
|
||||
Arm64Gen::ARM64Reg FromNativeReg(IRNativeReg r);
|
||||
Arm64Gen::ARM64Reg FromNativeReg64(IRNativeReg r);
|
||||
|
||||
Arm64Gen::ARM64XEmitter *emit_ = nullptr;
|
||||
Arm64Gen::ARM64FloatEmitter *fp_ = nullptr;
|
||||
|
||||
enum {
|
||||
NUM_X_REGS = 32,
|
||||
NUM_X_FREGS = 32,
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
|
@ -34,7 +34,6 @@ namespace MIPSComp {
|
|||
IRFrontend::IRFrontend(bool startDefaultPrefix) {
|
||||
js.startDefaultPrefix = startDefaultPrefix;
|
||||
js.hasSetRounding = false;
|
||||
// js.currentRoundingFunc = convertS0ToSCRATCH1[0];
|
||||
|
||||
// The debugger sets this so that "go" on a breakpoint will actually... go.
|
||||
// But if they reset, we can end up hitting it by mistake, since it's based on PC and ticks.
|
||||
|
|
|
@ -490,6 +490,10 @@ const u8 *IRNativeJit::GetCrashHandler() const {
|
|||
return backend_->GetNativeHooks().crashHandler;
|
||||
}
|
||||
|
||||
void IRNativeJit::UpdateFCR31() {
|
||||
backend_->UpdateFCR31(mips_);
|
||||
}
|
||||
|
||||
JitBlockCacheDebugInterface *IRNativeJit::GetBlockCacheDebugInterface() {
|
||||
return &debugInterface_;
|
||||
}
|
||||
|
|
|
@ -59,6 +59,8 @@ public:
|
|||
virtual void InvalidateBlock(IRBlock *block, int block_num) = 0;
|
||||
void FinalizeBlock(IRBlock *block, int block_num, const JitOptions &jo);
|
||||
|
||||
virtual void UpdateFCR31(MIPSState *mipsState) {}
|
||||
|
||||
const IRNativeHooks &GetNativeHooks() const {
|
||||
return hooks_;
|
||||
}
|
||||
|
@ -168,6 +170,8 @@ public:
|
|||
const u8 *GetDispatcher() const override;
|
||||
const u8 *GetCrashHandler() const override;
|
||||
|
||||
void UpdateFCR31() override;
|
||||
|
||||
JitBlockCacheDebugInterface *GetBlockCacheDebugInterface() override;
|
||||
|
||||
protected:
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#include "../ARM/ArmJit.h"
|
||||
#elif PPSSPP_ARCH(ARM64)
|
||||
#include "../ARM64/Arm64Jit.h"
|
||||
#include "../ARM64/Arm64IRJit.h"
|
||||
#elif PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)
|
||||
#include "../x86/Jit.h"
|
||||
#include "../x86/X64IRJit.h"
|
||||
|
@ -106,6 +107,8 @@ namespace MIPSComp {
|
|||
#if PPSSPP_ARCH(ARM)
|
||||
return new MIPSComp::ArmJit(mipsState);
|
||||
#elif PPSSPP_ARCH(ARM64)
|
||||
if (useIR)
|
||||
return new MIPSComp::Arm64IRJit(mipsState);
|
||||
return new MIPSComp::Arm64Jit(mipsState);
|
||||
#elif PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)
|
||||
if (useIR)
|
||||
|
|
|
@ -56,6 +56,8 @@ bool RiscVJitBackend::CompileBlock(IRBlock *block, int block_num, bool preload)
|
|||
if (GetSpaceLeft() < 0x800)
|
||||
return false;
|
||||
|
||||
BeginWrite(std::min(GetSpaceLeft(), (size_t)block->GetNumInstructions() * 32));
|
||||
|
||||
u32 startPC = block->GetOriginalStart();
|
||||
bool wroteCheckedOffset = false;
|
||||
if (jo.enableBlocklink && !jo.useBackJump) {
|
||||
|
@ -151,6 +153,7 @@ bool RiscVJitBackend::CompileBlock(IRBlock *block, int block_num, bool preload)
|
|||
}
|
||||
}
|
||||
|
||||
EndWrite();
|
||||
FlushIcache();
|
||||
compilingBlockNum_ = -1;
|
||||
|
||||
|
@ -163,8 +166,6 @@ void RiscVJitBackend::WriteConstExit(uint32_t pc) {
|
|||
|
||||
int exitStart = (int)GetOffset(GetCodePointer());
|
||||
if (block_num >= 0 && jo.enableBlocklink && nativeBlock && nativeBlock->checkedOffset != 0) {
|
||||
// Don't bother recording, we don't ever overwrite to "unlink".
|
||||
// Instead, we would mark the target block to jump to the dispatcher.
|
||||
QuickJ(SCRATCH1, GetBasePtr() + nativeBlock->checkedOffset);
|
||||
} else {
|
||||
LI(SCRATCH1, pc);
|
||||
|
|
|
@ -77,7 +77,6 @@ const int *RiscVRegCache::GetAllocationOrder(MIPSLoc type, MIPSMap flags, int &c
|
|||
}
|
||||
} else if (type == MIPSLoc::FREG) {
|
||||
// F8 through F15 are used for compression, so they are great.
|
||||
// TODO: Maybe we could remove some saved regs since we rarely need that many? Or maybe worth it?
|
||||
static const int allocationOrder[] = {
|
||||
F8, F9, F10, F11, F12, F13, F14, F15,
|
||||
F0, F1, F2, F3, F4, F5, F6, F7,
|
||||
|
@ -312,7 +311,6 @@ void RiscVRegCache::LoadNativeReg(IRNativeReg nreg, IRReg first, int lanes) {
|
|||
_dbg_assert_(r > X0);
|
||||
_dbg_assert_(first != MIPS_REG_ZERO);
|
||||
if (r <= X31) {
|
||||
// Multilane not yet supported.
|
||||
_assert_(lanes == 1 || (lanes == 2 && first == IRREG_LO));
|
||||
if (lanes == 1)
|
||||
emit_->LW(r, CTXREG, GetMipsRegOffset(first));
|
||||
|
|
|
@ -44,7 +44,7 @@ public:
|
|||
void Init(RiscVGen::RiscVEmitter *emitter);
|
||||
|
||||
// May fail and return INVALID_REG if it needs flushing.
|
||||
RiscVGen::RiscVReg TryMapTempImm(IRReg);
|
||||
RiscVGen::RiscVReg TryMapTempImm(IRReg reg);
|
||||
|
||||
// Returns an RV register containing the requested MIPS register.
|
||||
RiscVGen::RiscVReg MapGPR(IRReg reg, MIPSMap mapFlags = MIPSMap::INIT);
|
||||
|
|
|
@ -505,6 +505,7 @@ void X64JitBackend::CompIR_Logic(IRInst inst) {
|
|||
AND(32, regs_.R(inst.dest), regs_.R(inst.src2));
|
||||
}
|
||||
break;
|
||||
|
||||
case IROp::Or:
|
||||
regs_.Map(inst);
|
||||
if (inst.dest == inst.src1) {
|
||||
|
|
|
@ -236,7 +236,7 @@ int ReportBadAddress(uint32_t addr, uint32_t alignment, uint32_t isWrite) {
|
|||
return toss(MemoryExceptionType::ALIGNMENT);
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
|
||||
void X64JitBackend::CompIR_ValidateAddress(IRInst inst) {
|
||||
CONDITIONAL_DISABLE;
|
||||
|
|
|
@ -166,8 +166,6 @@ void X64JitBackend::WriteConstExit(uint32_t pc) {
|
|||
|
||||
int exitStart = (int)GetOffset(GetCodePointer());
|
||||
if (block_num >= 0 && jo.enableBlocklink && nativeBlock && nativeBlock->checkedOffset != 0) {
|
||||
// Don't bother recording, we don't ever overwrite to "unlink".
|
||||
// Instead, we would mark the target block to jump to the dispatcher.
|
||||
JMP(GetBasePtr() + nativeBlock->checkedOffset, true);
|
||||
} else {
|
||||
MOV(32, R(SCRATCH1), Imm32(pc));
|
||||
|
|
|
@ -164,12 +164,12 @@ private:
|
|||
class X64IRJit : public IRNativeJit {
|
||||
public:
|
||||
X64IRJit(MIPSState *mipsState)
|
||||
: IRNativeJit(mipsState), rvBackend_(jo, blocks_) {
|
||||
Init(rvBackend_);
|
||||
: IRNativeJit(mipsState), x64Backend_(jo, blocks_) {
|
||||
Init(x64Backend_);
|
||||
}
|
||||
|
||||
private:
|
||||
X64JitBackend rvBackend_;
|
||||
X64JitBackend x64Backend_;
|
||||
};
|
||||
|
||||
} // namespace MIPSComp
|
||||
|
|
|
@ -262,7 +262,7 @@ void X64IRRegCache::MapWithFlags(IRInst inst, X64Map destFlags, X64Map src1Flags
|
|||
X64Reg X64IRRegCache::MapGPR(IRReg mipsReg, MIPSMap mapFlags) {
|
||||
_dbg_assert_(IsValidGPR(mipsReg));
|
||||
|
||||
// Okay, not mapped, so we need to allocate an RV register.
|
||||
// Okay, not mapped, so we need to allocate an x64 register.
|
||||
IRNativeReg nreg = MapNativeReg(MIPSLoc::REG, mipsReg, 1, mapFlags);
|
||||
return FromNativeReg(nreg);
|
||||
}
|
||||
|
@ -270,7 +270,7 @@ X64Reg X64IRRegCache::MapGPR(IRReg mipsReg, MIPSMap mapFlags) {
|
|||
X64Reg X64IRRegCache::MapGPR2(IRReg mipsReg, MIPSMap mapFlags) {
|
||||
_dbg_assert_(IsValidGPR(mipsReg) && IsValidGPR(mipsReg + 1));
|
||||
|
||||
// Okay, not mapped, so we need to allocate an RV register.
|
||||
// Okay, not mapped, so we need to allocate an x64 register.
|
||||
IRNativeReg nreg = MapNativeReg(MIPSLoc::REG, mipsReg, 2, mapFlags);
|
||||
return FromNativeReg(nreg);
|
||||
}
|
||||
|
@ -326,7 +326,6 @@ void X64IRRegCache::LoadNativeReg(IRNativeReg nreg, IRReg first, int lanes) {
|
|||
X64Reg r = FromNativeReg(nreg);
|
||||
_dbg_assert_(first != MIPS_REG_ZERO);
|
||||
if (nreg < NUM_X_REGS) {
|
||||
// Multilane not yet supported.
|
||||
_assert_(lanes == 1 || (lanes == 2 && first == IRREG_LO));
|
||||
if (lanes == 1)
|
||||
emit_->MOV(32, ::R(r), MDisp(CTXREG, -128 + GetMipsRegOffset(first)));
|
||||
|
@ -354,7 +353,6 @@ void X64IRRegCache::StoreNativeReg(IRNativeReg nreg, IRReg first, int lanes) {
|
|||
X64Reg r = FromNativeReg(nreg);
|
||||
_dbg_assert_(first != MIPS_REG_ZERO);
|
||||
if (nreg < NUM_X_REGS) {
|
||||
// Multilane not yet supported.
|
||||
_assert_(lanes == 1 || (lanes == 2 && first == IRREG_LO));
|
||||
_assert_(mr[first].loc == MIPSLoc::REG || mr[first].loc == MIPSLoc::REG_IMM);
|
||||
if (lanes == 1)
|
||||
|
@ -434,9 +432,9 @@ X64Reg X64IRRegCache::RXPtr(IRReg mipsReg) {
|
|||
if (mr[mipsReg].loc == MIPSLoc::REG_AS_PTR) {
|
||||
return FromNativeReg(mr[mipsReg].nReg);
|
||||
} else if (mr[mipsReg].loc == MIPSLoc::REG || mr[mipsReg].loc == MIPSLoc::REG_IMM) {
|
||||
int rv = mr[mipsReg].nReg;
|
||||
_dbg_assert_(nr[rv].pointerified);
|
||||
if (nr[rv].pointerified) {
|
||||
int r = mr[mipsReg].nReg;
|
||||
_dbg_assert_(nr[r].pointerified);
|
||||
if (nr[r].pointerified) {
|
||||
return FromNativeReg(mr[mipsReg].nReg);
|
||||
} else {
|
||||
ERROR_LOG(JIT, "Tried to use a non-pointer register as a pointer");
|
||||
|
|
|
@ -78,9 +78,9 @@ public:
|
|||
void Init(Gen::XEmitter *emitter);
|
||||
|
||||
// May fail and return INVALID_REG if it needs flushing.
|
||||
Gen::X64Reg TryMapTempImm(IRReg, X64IRJitConstants::X64Map flags = X64IRJitConstants::X64Map::NONE);
|
||||
Gen::X64Reg TryMapTempImm(IRReg reg, X64IRJitConstants::X64Map flags = X64IRJitConstants::X64Map::NONE);
|
||||
|
||||
// Returns an RV register containing the requested MIPS register.
|
||||
// Returns an X64 register containing the requested MIPS register.
|
||||
Gen::X64Reg MapGPR(IRReg reg, MIPSMap mapFlags = MIPSMap::INIT);
|
||||
Gen::X64Reg MapGPR2(IRReg reg, MIPSMap mapFlags = MIPSMap::INIT);
|
||||
Gen::X64Reg MapGPRAsPointer(IRReg reg);
|
||||
|
|
|
@ -271,6 +271,8 @@
|
|||
<ClInclude Include="..\..\Core\MIPS\ARM64\Arm64Jit.h" />
|
||||
<ClInclude Include="..\..\Core\MIPS\ARM64\Arm64RegCache.h" />
|
||||
<ClInclude Include="..\..\Core\MIPS\ARM64\Arm64RegCacheFPU.h" />
|
||||
<ClInclude Include="..\..\Core\MIPS\ARM64\Arm64IRJit.h" />
|
||||
<ClInclude Include="..\..\Core\MIPS\ARM64\Arm64IRRegCache.h" />
|
||||
<ClInclude Include="..\..\Core\MIPS\ARM\ArmCompVFPUNEONUtil.h" />
|
||||
<ClInclude Include="..\..\Core\MIPS\ARM\ArmJit.h" />
|
||||
<ClInclude Include="..\..\Core\MIPS\ARM\ArmRegCache.h" />
|
||||
|
@ -520,6 +522,15 @@
|
|||
<ClCompile Include="..\..\Core\MIPS\ARM64\Arm64Jit.cpp" />
|
||||
<ClCompile Include="..\..\Core\MIPS\ARM64\Arm64RegCache.cpp" />
|
||||
<ClCompile Include="..\..\Core\MIPS\ARM64\Arm64RegCacheFPU.cpp" />
|
||||
<ClCompile Include="..\..\Core\MIPS\ARM64\Arm64IRAsm.cpp" />
|
||||
<ClCompile Include="..\..\Core\MIPS\ARM64\Arm64IRCompALU.cpp" />
|
||||
<ClCompile Include="..\..\Core\MIPS\ARM64\Arm64IRCompBranch.cpp" />
|
||||
<ClCompile Include="..\..\Core\MIPS\ARM64\Arm64IRCompFPU.cpp" />
|
||||
<ClCompile Include="..\..\Core\MIPS\ARM64\Arm64IRCompLoadStore.cpp" />
|
||||
<ClCompile Include="..\..\Core\MIPS\ARM64\Arm64IRCompSystem.cpp" />
|
||||
<ClCompile Include="..\..\Core\MIPS\ARM64\Arm64IRCompVec.cpp" />
|
||||
<ClCompile Include="..\..\Core\MIPS\ARM64\Arm64IRJit.cpp" />
|
||||
<ClCompile Include="..\..\Core\MIPS\ARM64\Arm64IRRegCache.cpp" />
|
||||
<ClCompile Include="..\..\Core\MIPS\ARM\ArmAsm.cpp" />
|
||||
<ClCompile Include="..\..\Core\MIPS\ARM\ArmCompALU.cpp" />
|
||||
<ClCompile Include="..\..\Core\MIPS\ARM\ArmCompBranch.cpp" />
|
||||
|
|
|
@ -782,6 +782,33 @@
|
|||
<ClCompile Include="..\..\Core\MIPS\ARM64\Arm64RegCacheFPU.cpp">
|
||||
<Filter>MIPS\ARM64</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\Core\MIPS\ARM64\Arm64IRAsm.cpp">
|
||||
<Filter>MIPS\ARM64</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\Core\MIPS\ARM64\Arm64IRCompALU.cpp">
|
||||
<Filter>MIPS\ARM64</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\Core\MIPS\ARM64\Arm64IRCompBranch.cpp">
|
||||
<Filter>MIPS\ARM64</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\Core\MIPS\ARM64\Arm64IRCompFPU.cpp">
|
||||
<Filter>MIPS\ARM64</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\Core\MIPS\ARM64\Arm64IRCompLoadStore.cpp">
|
||||
<Filter>MIPS\ARM64</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\Core\MIPS\ARM64\Arm64IRCompSystem.cpp">
|
||||
<Filter>MIPS\ARM64</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\Core\MIPS\ARM64\Arm64IRCompVec.cpp">
|
||||
<Filter>MIPS\ARM64</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\Core\MIPS\ARM64\Arm64IRJit.cpp">
|
||||
<Filter>MIPS\ARM64</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\Core\MIPS\ARM64\Arm64IRRegCache.cpp">
|
||||
<Filter>MIPS\ARM64</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\Core\HW\BufferQueue.cpp">
|
||||
<Filter>HW</Filter>
|
||||
</ClCompile>
|
||||
|
@ -1790,6 +1817,12 @@
|
|||
<ClInclude Include="..\..\Core\MIPS\ARM64\Arm64RegCacheFPU.h">
|
||||
<Filter>MIPS\ARM64</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\Core\MIPS\ARM64\Arm64IRJit.h">
|
||||
<Filter>MIPS\ARM64</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\Core\MIPS\ARM64\Arm64IRRegCache.h">
|
||||
<Filter>MIPS\ARM64</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\Core\HW\Camera.h">
|
||||
<Filter>HW</Filter>
|
||||
</ClInclude>
|
||||
|
|
|
@ -343,6 +343,15 @@ ARCH_FILES := \
|
|||
$(SRC)/Core/MIPS/ARM64/Arm64Jit.cpp \
|
||||
$(SRC)/Core/MIPS/ARM64/Arm64RegCache.cpp \
|
||||
$(SRC)/Core/MIPS/ARM64/Arm64RegCacheFPU.cpp \
|
||||
$(SRC)/Core/MIPS/ARM64/Arm64IRAsm.cpp \
|
||||
$(SRC)/Core/MIPS/ARM64/Arm64IRCompALU.cpp \
|
||||
$(SRC)/Core/MIPS/ARM64/Arm64IRCompBranch.cpp \
|
||||
$(SRC)/Core/MIPS/ARM64/Arm64IRCompFPU.cpp \
|
||||
$(SRC)/Core/MIPS/ARM64/Arm64IRCompLoadStore.cpp \
|
||||
$(SRC)/Core/MIPS/ARM64/Arm64IRCompSystem.cpp \
|
||||
$(SRC)/Core/MIPS/ARM64/Arm64IRCompVec.cpp \
|
||||
$(SRC)/Core/MIPS/ARM64/Arm64IRJit.cpp \
|
||||
$(SRC)/Core/MIPS/ARM64/Arm64IRRegCache.cpp \
|
||||
$(SRC)/Core/Util/DisArm64.cpp \
|
||||
$(SRC)/GPU/Common/VertexDecoderArm64.cpp \
|
||||
Arm64EmitterTest.cpp
|
||||
|
|
|
@ -757,6 +757,15 @@ ifeq ($(WITH_DYNAREC),1)
|
|||
$(COREDIR)/MIPS/ARM64/Arm64Jit.cpp \
|
||||
$(COREDIR)/MIPS/ARM64/Arm64RegCache.cpp \
|
||||
$(COREDIR)/MIPS/ARM64/Arm64RegCacheFPU.cpp \
|
||||
$(COREDIR)/MIPS/ARM64/Arm64IRAsm.cpp \
|
||||
$(COREDIR)/MIPS/ARM64/Arm64IRCompALU.cpp \
|
||||
$(COREDIR)/MIPS/ARM64/Arm64IRCompBranch.cpp \
|
||||
$(COREDIR)/MIPS/ARM64/Arm64IRCompFPU.cpp \
|
||||
$(COREDIR)/MIPS/ARM64/Arm64IRCompLoadStore.cpp \
|
||||
$(COREDIR)/MIPS/ARM64/Arm64IRCompSystem.cpp \
|
||||
$(COREDIR)/MIPS/ARM64/Arm64IRCompVec.cpp \
|
||||
$(COREDIR)/MIPS/ARM64/Arm64IRJit.cpp \
|
||||
$(COREDIR)/MIPS/ARM64/Arm64IRRegCache.cpp \
|
||||
$(COREDIR)/Util/DisArm64.cpp \
|
||||
$(GPUCOMMONDIR)/VertexDecoderArm64.cpp
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue