2012-11-01 16:19:01 +01:00
|
|
|
// Copyright (c) 2012- 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
|
2012-11-04 23:58:25 +01:00
|
|
|
// the Free Software Foundation, version 2.0 or later versions.
|
2012-11-01 16:19:01 +01:00
|
|
|
|
|
|
|
|
// 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/.
|
|
|
|
|
|
2016-10-12 17:32:52 +02:00
|
|
|
#include "ppsspp_config.h"
|
|
|
|
|
#if PPSSPP_ARCH(ARM)
|
2012-11-01 16:19:01 +01:00
|
|
|
|
2013-03-29 20:51:14 +01:00
|
|
|
#include "Core/MemMap.h"
|
|
|
|
|
#include "Core/MIPS/MIPS.h"
|
|
|
|
|
#include "Core/System.h"
|
|
|
|
|
#include "Core/CoreTiming.h"
|
2014-03-25 22:15:04 -07:00
|
|
|
#include "Common/MemoryUtil.h"
|
|
|
|
|
#include "Common/CPUDetect.h"
|
|
|
|
|
#include "Common/ArmEmitter.h"
|
2013-11-08 18:51:52 +01:00
|
|
|
#include "Core/MIPS/ARM/ArmJit.h"
|
2014-12-13 00:09:37 +01:00
|
|
|
#include "Core/MIPS/JitCommon/JitCommon.h"
|
2012-11-01 16:19:01 +01:00
|
|
|
|
|
|
|
|
using namespace ArmGen;
|
|
|
|
|
|
|
|
|
|
//static int temp32; // unused?
|
|
|
|
|
|
2013-12-09 15:28:47 +01:00
|
|
|
static const bool enableDebug = false;
|
2015-10-08 19:58:37 +02:00
|
|
|
static const bool disasm = false;
|
2012-11-01 16:19:01 +01:00
|
|
|
|
|
|
|
|
//static bool enableStatistics = false; //unused?
|
|
|
|
|
|
|
|
|
|
//The standard ARM calling convention allocates the 16 ARM registers as:
|
|
|
|
|
|
|
|
|
|
// r15 is the program counter.
|
|
|
|
|
// r14 is the link register. (The BL instruction, used in a subroutine call, stores the return address in this register).
|
|
|
|
|
// r13 is the stack pointer. (The Push/Pop instructions in "Thumb" operating mode use this register only).
|
|
|
|
|
// r12 is the Intra-Procedure-call scratch register.
|
|
|
|
|
// r4 to r11: used to hold local variables.
|
|
|
|
|
// r0 to r3: used to hold argument values passed to a subroutine, and also hold results returned from a subroutine.
|
|
|
|
|
|
2013-12-09 15:28:47 +01:00
|
|
|
// Mappable registers:
|
|
|
|
|
// R2, R3, R4, R5, R6, R8, R11
|
2012-11-01 16:19:01 +01:00
|
|
|
|
|
|
|
|
// STATIC ALLOCATION ARM:
|
2013-01-08 00:26:42 +01:00
|
|
|
// R10 : MIPS state
|
2013-12-09 15:28:47 +01:00
|
|
|
// R11 : Memory base pointer.
|
|
|
|
|
// R7 : Down counter
|
2012-11-01 16:19:01 +01:00
|
|
|
extern volatile CoreState coreState;
|
|
|
|
|
|
2013-01-08 23:52:11 +01:00
|
|
|
void ShowPC(u32 sp) {
|
2017-03-09 11:36:16 +01:00
|
|
|
ERROR_LOG(JIT, "ShowPC : %08x ArmSP : %08x", currentMIPS->pc, sp);
|
2017-03-09 14:22:33 +01:00
|
|
|
// Sleep(1);
|
2013-12-09 15:28:47 +01:00
|
|
|
}
|
2013-01-08 21:24:42 +01:00
|
|
|
|
|
|
|
|
void DisassembleArm(const u8 *data, int size);
|
|
|
|
|
|
2012-11-01 16:19:01 +01:00
|
|
|
// PLAN: no more block numbers - crazy opcodes just contain offset within
|
|
|
|
|
// dynarec buffer
|
|
|
|
|
// At this offset - 4, there is an int specifying the block number.
|
|
|
|
|
|
2013-01-11 01:59:26 +01:00
|
|
|
namespace MIPSComp {
|
2013-01-08 23:52:11 +01:00
|
|
|
|
2014-12-13 21:11:36 +01:00
|
|
|
using namespace ArmJitConstants;
|
|
|
|
|
|
2015-10-08 19:58:37 +02:00
|
|
|
void ArmJit::GenerateFixedCode() {
|
2022-11-20 09:35:05 -08:00
|
|
|
BeginWrite(GetMemoryProtectPageSize());
|
2016-08-28 13:35:27 +02:00
|
|
|
const u8 *start = AlignCodePage();
|
2015-10-08 19:58:37 +02:00
|
|
|
|
2015-10-10 10:03:34 +02:00
|
|
|
// LR == SCRATCHREG2 on ARM32 so it needs to be pushed.
|
2015-10-08 19:58:37 +02:00
|
|
|
restoreRoundingMode = AlignCode16(); {
|
2015-10-08 23:11:57 +02:00
|
|
|
PUSH(1, R_LR);
|
2015-10-08 19:58:37 +02:00
|
|
|
VMRS(SCRATCHREG2);
|
2015-10-10 13:11:38 +02:00
|
|
|
// Outside the JIT we run with round-to-nearest and flush0 off.
|
2015-10-08 19:58:37 +02:00
|
|
|
BIC(SCRATCHREG2, SCRATCHREG2, AssumeMakeOperand2((3 | 4) << 22));
|
|
|
|
|
VMSR(SCRATCHREG2);
|
2015-10-08 23:11:57 +02:00
|
|
|
POP(1, R_PC);
|
2015-10-08 19:58:37 +02:00
|
|
|
}
|
|
|
|
|
|
2015-10-10 10:03:34 +02:00
|
|
|
// Must preserve SCRATCHREG1 (R0), destroys SCRATCHREG2 (LR)
|
2015-10-08 19:58:37 +02:00
|
|
|
applyRoundingMode = AlignCode16(); {
|
2015-10-08 23:11:57 +02:00
|
|
|
PUSH(2, SCRATCHREG1, R_LR);
|
2015-10-08 19:58:37 +02:00
|
|
|
LDR(SCRATCHREG2, CTXREG, offsetof(MIPSState, fcr31));
|
|
|
|
|
|
|
|
|
|
TST(SCRATCHREG2, AssumeMakeOperand2(1 << 24));
|
|
|
|
|
AND(SCRATCHREG2, SCRATCHREG2, Operand2(3));
|
|
|
|
|
SetCC(CC_NEQ);
|
|
|
|
|
ADD(SCRATCHREG2, SCRATCHREG2, Operand2(4));
|
|
|
|
|
SetCC(CC_AL);
|
|
|
|
|
|
2015-10-10 13:11:38 +02:00
|
|
|
// We can skip if the rounding mode is nearest (0) and flush is not set.
|
|
|
|
|
// (as restoreRoundingMode cleared it out anyway)
|
|
|
|
|
CMP(SCRATCHREG2, Operand2(0));
|
2015-10-08 19:58:37 +02:00
|
|
|
FixupBranch skip = B_CC(CC_EQ);
|
|
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
AND(SCRATCHREG1, SCRATCHREG2, Operand2(3));
|
|
|
|
|
CMP(SCRATCHREG1, Operand2(1));
|
|
|
|
|
|
|
|
|
|
SetCC(CC_EQ); ADD(SCRATCHREG2, SCRATCHREG2, Operand2(2));
|
|
|
|
|
SetCC(CC_GT); SUB(SCRATCHREG2, SCRATCHREG2, Operand2(1));
|
|
|
|
|
SetCC(CC_AL);
|
|
|
|
|
|
|
|
|
|
VMRS(SCRATCHREG1);
|
|
|
|
|
// Assume we're always in round-to-nearest mode beforehand.
|
|
|
|
|
// But we need to clear flush to zero in this case anyway.
|
|
|
|
|
BIC(SCRATCHREG1, SCRATCHREG1, AssumeMakeOperand2((3 | 4) << 22));
|
|
|
|
|
ORR(SCRATCHREG1, SCRATCHREG1, Operand2(SCRATCHREG2, ST_LSL, 22));
|
|
|
|
|
VMSR(SCRATCHREG1);
|
|
|
|
|
|
|
|
|
|
SetJumpTarget(skip);
|
2015-10-08 23:11:57 +02:00
|
|
|
POP(2, SCRATCHREG1, R_PC);
|
2015-10-08 19:58:37 +02:00
|
|
|
}
|
|
|
|
|
|
2015-10-08 23:11:57 +02:00
|
|
|
FlushLitPool();
|
|
|
|
|
|
2015-10-08 19:58:37 +02:00
|
|
|
enterDispatcher = AlignCode16();
|
2012-11-01 16:19:01 +01:00
|
|
|
|
2013-12-09 13:45:17 +01:00
|
|
|
DEBUG_LOG(JIT, "Base: %08x", (u32)Memory::base);
|
2013-01-08 13:49:52 +01:00
|
|
|
|
2012-11-01 16:19:01 +01:00
|
|
|
SetCC(CC_AL);
|
2013-01-07 22:33:09 +01:00
|
|
|
|
2013-12-29 14:25:34 -08:00
|
|
|
PUSH(9, R4, R5, R6, R7, R8, R9, R10, R11, R_LR);
|
2013-01-12 12:18:02 +01:00
|
|
|
// Take care to 8-byte align stack for function calls.
|
2013-01-30 00:02:04 +01:00
|
|
|
// We are misaligned here because of an odd number of args for PUSH.
|
|
|
|
|
// It's not like x86 where you need to account for an extra 4 bytes
|
|
|
|
|
// consumed by CALL.
|
2013-12-29 14:25:34 -08:00
|
|
|
SUB(R_SP, R_SP, 4);
|
2013-01-30 00:02:04 +01:00
|
|
|
// Now we are correctly aligned and plan to stay that way.
|
2022-04-13 10:36:37 +02:00
|
|
|
VPUSH(D8, 8);
|
2013-01-30 00:02:04 +01:00
|
|
|
|
2013-01-07 22:33:09 +01:00
|
|
|
// Fixed registers, these are always kept when in Jit context.
|
2013-04-27 00:22:18 +02:00
|
|
|
// R8 is used to hold flags during delay slots. Not always needed.
|
2013-01-07 22:33:09 +01:00
|
|
|
// R13 cannot be used as it's the stack pointer.
|
2013-01-30 00:02:04 +01:00
|
|
|
// TODO: Consider statically allocating:
|
2013-04-27 00:22:18 +02:00
|
|
|
// * r2-r4
|
2013-01-30 00:02:04 +01:00
|
|
|
// Really starting to run low on registers already though...
|
2013-04-27 00:22:18 +02:00
|
|
|
|
2014-12-17 01:11:33 -08:00
|
|
|
// R11, R10, R9
|
|
|
|
|
MOVP2R(MEMBASEREG, Memory::base);
|
|
|
|
|
MOVP2R(CTXREG, mips_);
|
|
|
|
|
MOVP2R(JITBASEREG, GetBasePtr());
|
2014-03-25 22:15:04 -07:00
|
|
|
|
2013-07-27 17:27:26 +02:00
|
|
|
RestoreDowncount();
|
2013-02-14 00:02:09 +01:00
|
|
|
MovFromPC(R0);
|
|
|
|
|
outerLoopPCInR0 = GetCodePtr();
|
|
|
|
|
MovToPC(R0);
|
2012-11-01 16:19:01 +01:00
|
|
|
outerLoop = GetCodePtr();
|
2013-07-27 17:27:26 +02:00
|
|
|
SaveDowncount();
|
2014-10-12 11:34:26 -07:00
|
|
|
RestoreRoundingMode(true);
|
2014-03-25 22:07:11 -07:00
|
|
|
QuickCallFunction(R0, &CoreTiming::Advance);
|
2014-10-12 11:34:26 -07:00
|
|
|
ApplyRoundingMode(true);
|
2013-07-27 17:27:26 +02:00
|
|
|
RestoreDowncount();
|
2017-03-14 12:32:20 +01:00
|
|
|
FixupBranch skipToCoreStateCheck = B(); //skip the downcount check
|
2013-01-07 22:33:09 +01:00
|
|
|
|
|
|
|
|
dispatcherCheckCoreState = GetCodePtr();
|
|
|
|
|
|
2013-01-23 22:24:20 -08:00
|
|
|
// The result of slice decrementation should be in flags if somebody jumped here
|
|
|
|
|
// IMPORTANT - We jump on negative, not carry!!!
|
|
|
|
|
FixupBranch bailCoreState = B_CC(CC_MI);
|
|
|
|
|
|
2017-03-14 12:32:20 +01:00
|
|
|
SetJumpTarget(skipToCoreStateCheck);
|
|
|
|
|
|
2016-10-12 17:32:52 +02:00
|
|
|
MOVI2R(R0, (u32)(uintptr_t)&coreState);
|
2013-01-08 00:26:42 +01:00
|
|
|
LDR(R0, R0);
|
|
|
|
|
CMP(R0, 0);
|
|
|
|
|
FixupBranch badCoreState = B_CC(CC_NEQ);
|
2013-01-08 23:52:11 +01:00
|
|
|
FixupBranch skipToRealDispatch2 = B(); //skip the sync and compare first time
|
2013-01-07 22:33:09 +01:00
|
|
|
|
2013-02-15 23:33:35 +01:00
|
|
|
dispatcherPCInR0 = GetCodePtr();
|
2013-12-09 13:45:17 +01:00
|
|
|
// TODO: Do we always need to write PC to RAM here?
|
2013-02-15 23:33:35 +01:00
|
|
|
MovToPC(R0);
|
|
|
|
|
|
2013-01-08 23:52:11 +01:00
|
|
|
// At this point : flags = EQ. Fine for the next check, no need to jump over it.
|
2012-11-01 16:19:01 +01:00
|
|
|
dispatcher = GetCodePtr();
|
2013-01-08 21:24:42 +01:00
|
|
|
|
2012-11-01 16:19:01 +01:00
|
|
|
// The result of slice decrementation should be in flags if somebody jumped here
|
|
|
|
|
// IMPORTANT - We jump on negative, not carry!!!
|
2013-01-08 21:24:42 +01:00
|
|
|
FixupBranch bail = B_CC(CC_MI);
|
2013-01-08 00:26:42 +01:00
|
|
|
|
2013-01-08 23:52:11 +01:00
|
|
|
SetJumpTarget(skipToRealDispatch2);
|
2012-11-01 16:19:01 +01:00
|
|
|
|
|
|
|
|
dispatcherNoCheck = GetCodePtr();
|
|
|
|
|
|
2013-01-08 00:26:42 +01:00
|
|
|
// Debug
|
2013-12-09 15:28:47 +01:00
|
|
|
if (enableDebug) {
|
|
|
|
|
MOV(R0, R13);
|
|
|
|
|
QuickCallFunction(R1, (void *)&ShowPC);
|
|
|
|
|
}
|
2013-01-08 23:52:11 +01:00
|
|
|
|
2013-02-16 02:06:02 +01:00
|
|
|
LDR(R0, CTXREG, offsetof(MIPSState, pc));
|
2013-12-09 13:45:17 +01:00
|
|
|
// TODO: In practice, do we ever run code from uncached space (| 0x40000000)? If not, we can remove this BIC.
|
2013-01-09 00:12:38 +01:00
|
|
|
BIC(R0, R0, Operand2(0xC0, 4)); // &= 0x3FFFFFFF
|
2021-02-09 09:38:03 +01:00
|
|
|
dispatcherFetch = GetCodePtr();
|
2013-11-12 01:21:04 -08:00
|
|
|
LDR(R0, MEMBASEREG, R0);
|
2013-12-18 00:09:08 +01:00
|
|
|
AND(R1, R0, Operand2(0xFF, 4)); // rotation is to the right, in 2-bit increments.
|
|
|
|
|
BIC(R0, R0, Operand2(0xFF, 4));
|
2013-01-08 00:26:42 +01:00
|
|
|
CMP(R1, Operand2(MIPS_EMUHACK_OPCODE >> 24, 4));
|
2013-01-09 00:12:38 +01:00
|
|
|
SetCC(CC_EQ);
|
2013-01-12 12:18:02 +01:00
|
|
|
// IDEA - we have 26 bits, why not just use offsets from base of code?
|
|
|
|
|
// Another idea: Shift the bloc number left by two in the op, this would let us do
|
2013-03-14 12:47:29 +10:00
|
|
|
// LDR(R0, R9, R0); here, replacing the next instructions.
|
2021-03-02 20:57:25 -08:00
|
|
|
#if PPSSPP_PLATFORM(IOS)
|
2014-12-17 01:11:33 -08:00
|
|
|
// On iOS, R9 (JITBASEREG) is volatile. We have to reload it.
|
2016-10-12 17:32:52 +02:00
|
|
|
MOVI2R(JITBASEREG, (u32)(uintptr_t)GetBasePtr());
|
2013-03-14 02:17:39 -07:00
|
|
|
#endif
|
2014-12-17 01:11:33 -08:00
|
|
|
ADD(R0, R0, JITBASEREG);
|
2012-11-02 11:58:56 +01:00
|
|
|
B(R0);
|
2013-01-09 00:12:38 +01:00
|
|
|
SetCC(CC_AL);
|
2012-11-01 16:19:01 +01:00
|
|
|
|
2013-12-09 13:45:17 +01:00
|
|
|
// No block found, let's jit
|
2013-07-27 17:27:26 +02:00
|
|
|
SaveDowncount();
|
2014-10-12 11:34:26 -07:00
|
|
|
RestoreRoundingMode(true);
|
2014-12-13 00:09:37 +01:00
|
|
|
QuickCallFunction(R2, (void *)&MIPSComp::JitAt);
|
2014-10-12 11:34:26 -07:00
|
|
|
ApplyRoundingMode(true);
|
2013-07-27 17:27:26 +02:00
|
|
|
RestoreDowncount();
|
2013-01-08 00:26:42 +01:00
|
|
|
|
2012-11-23 19:41:35 +01:00
|
|
|
B(dispatcherNoCheck); // no point in special casing this
|
2012-11-01 16:19:01 +01:00
|
|
|
|
|
|
|
|
SetJumpTarget(bail);
|
2013-01-23 22:24:20 -08:00
|
|
|
SetJumpTarget(bailCoreState);
|
2012-11-01 16:19:01 +01:00
|
|
|
|
2016-10-12 17:32:52 +02:00
|
|
|
MOVI2R(R0, (u32)(uintptr_t)&coreState);
|
2013-01-08 00:26:42 +01:00
|
|
|
LDR(R0, R0);
|
|
|
|
|
CMP(R0, 0);
|
|
|
|
|
B_CC(CC_EQ, outerLoop);
|
2012-11-01 16:19:01 +01:00
|
|
|
|
2019-02-12 10:58:20 +01:00
|
|
|
const uint8_t *quitLoop = GetCodePtr();
|
2013-01-08 00:26:42 +01:00
|
|
|
SetJumpTarget(badCoreState);
|
2014-03-25 22:15:04 -07:00
|
|
|
|
2017-03-09 11:36:16 +01:00
|
|
|
SaveDowncount();
|
|
|
|
|
RestoreRoundingMode(true);
|
|
|
|
|
|
2022-04-13 10:36:37 +02:00
|
|
|
VPOP(D8, 8);
|
2014-03-25 22:15:04 -07:00
|
|
|
|
2013-12-29 14:25:34 -08:00
|
|
|
ADD(R_SP, R_SP, 4);
|
2013-01-12 12:18:02 +01:00
|
|
|
|
2013-12-29 14:25:34 -08:00
|
|
|
POP(9, R4, R5, R6, R7, R8, R9, R10, R11, R_PC); // Returns
|
2013-01-08 00:26:42 +01:00
|
|
|
|
2019-02-12 10:58:20 +01:00
|
|
|
crashHandler = GetCodePtr();
|
|
|
|
|
MOVP2R(R0, &coreState);
|
2020-07-13 09:17:42 +02:00
|
|
|
MOVI2R(R1, CORE_RUNTIME_ERROR);
|
2019-02-12 10:58:20 +01:00
|
|
|
STR(R1, R0, 0);
|
|
|
|
|
B(quitLoop);
|
|
|
|
|
|
2013-01-09 00:12:02 +01:00
|
|
|
// Uncomment if you want to see the output...
|
2015-10-08 19:58:37 +02:00
|
|
|
if (disasm) {
|
|
|
|
|
INFO_LOG(JIT, "THE DISASM ========================");
|
|
|
|
|
DisassembleArm(start, GetCodePtr() - start);
|
|
|
|
|
INFO_LOG(JIT, "END OF THE DISASM ========================");
|
|
|
|
|
}
|
2013-01-08 23:52:11 +01:00
|
|
|
|
|
|
|
|
// Don't forget to zap the instruction cache!
|
2013-03-04 13:49:58 +10:00
|
|
|
FlushLitPool();
|
2013-03-04 14:36:23 +10:00
|
|
|
FlushIcache();
|
2016-08-28 13:35:27 +02:00
|
|
|
|
2016-08-28 14:52:08 +02:00
|
|
|
// Let's spare the pre-generated code from unprotect-reprotect.
|
|
|
|
|
AlignCodePage();
|
|
|
|
|
EndWrite();
|
2013-01-11 01:59:26 +01:00
|
|
|
}
|
|
|
|
|
|
2013-11-08 18:51:52 +01:00
|
|
|
} // namespace MIPSComp
|
2016-10-12 17:32:52 +02:00
|
|
|
|
|
|
|
|
#endif // PPSSPP_ARCH(ARM)
|