ARM64 emitter work, dispatcher work, etc. More work needed.

This commit is contained in:
Henrik Rydgard
2015-04-06 18:13:03 +02:00
parent 8945b2476d
commit 524583d53e
10 changed files with 222 additions and 36 deletions
+19
View File
@@ -1328,6 +1328,22 @@ void ARM64XEmitter::MVN(ARM64Reg Rd, ARM64Reg Rm)
{
ORN(Rd, Is64Bit(Rd) ? SP : WSP, Rm, ArithOption(Rm, ST_LSL, 0));
}
void ARM64XEmitter::LSL(ARM64Reg Rd, ARM64Reg Rm, int shift)
{
ORR(Rd, Is64Bit(Rd) ? SP : WSP, Rm, ArithOption(Rm, ST_LSL, shift));
}
void ARM64XEmitter::LSR(ARM64Reg Rd, ARM64Reg Rm, int shift)
{
ORR(Rd, Is64Bit(Rd) ? SP : WSP, Rm, ArithOption(Rm, ST_LSR, shift));
}
void ARM64XEmitter::ASR(ARM64Reg Rd, ARM64Reg Rm, int shift)
{
ORR(Rd, Is64Bit(Rd) ? SP : WSP, Rm, ArithOption(Rm, ST_ASR, shift));
}
void ARM64XEmitter::ROR(ARM64Reg Rd, ARM64Reg Rm, int shift)
{
ORR(Rd, Is64Bit(Rd) ? SP : WSP, Rm, ArithOption(Rm, ST_ROR, shift));
}
// Logical (immediate)
void ARM64XEmitter::AND(ARM64Reg Rd, ARM64Reg Rn, u32 immr, u32 imms)
@@ -3023,6 +3039,7 @@ void ARM64XEmitter::ANDI2R(ARM64Reg Rd, ARM64Reg Rn, u64 imm, ARM64Reg scratch)
if (IsImmLogical(imm, Is64Bit(Rn) ? 64 : 32, &n, &imm_s, &imm_r)) {
AND(Rd, Rn, imm_r, imm_s);
} else {
_assert_msg_(JIT, scratch != INVALID_REG, "ANDSI2R - failed to construct immediate value from %08x, need scratch", (u32)imm)
MOVI2R(scratch, imm);
AND(Rd, Rn, scratch);
}
@@ -3033,6 +3050,7 @@ void ARM64XEmitter::ORI2R(ARM64Reg Rd, ARM64Reg Rn, u64 imm, ARM64Reg scratch) {
if (IsImmLogical(imm, Is64Bit(Rn) ? 64 : 32, &n, &imm_s, &imm_r)) {
ORR(Rd, Rn, imm_r, imm_s);
} else {
_assert_msg_(JIT, scratch != INVALID_REG, "ORI2R - failed to construct immediate value from %08x, need scratch", (u32)imm)
MOVI2R(scratch, imm);
ORR(Rd, Rn, scratch);
}
@@ -3043,6 +3061,7 @@ void ARM64XEmitter::ANDSI2R(ARM64Reg Rd, ARM64Reg Rn, u64 imm, ARM64Reg scratch)
if (IsImmLogical(imm, Is64Bit(Rn) ? 64 : 32, &n, &imm_s, &imm_r)) {
ANDS(Rd, Rn, imm_r, imm_s);
} else {
_assert_msg_(JIT, scratch != INVALID_REG, "ANDSI2R - failed to construct immediate value from %08x, need scratch", (u32)imm)
MOVI2R(scratch, imm);
ANDS(Rd, Rn, scratch);
}
+14 -5
View File
@@ -511,8 +511,13 @@ public:
void ANDS(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm) { ANDS(Rd, Rn, Rm, ArithOption(Rd, 0)); }
void BICS(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm) { BICS(Rd, Rn, Rm, ArithOption(Rd, 0)); }
// Convenience wrappers around ORR. These match the official convenience syntax.
void MOV(ARM64Reg Rd, ARM64Reg Rm);
void MVN(ARM64Reg Rd, ARM64Reg Rm);
void LSR(ARM64Reg Rd, ARM64Reg Rm, int shift);
void LSL(ARM64Reg Rd, ARM64Reg Rm, int shift);
void ASR(ARM64Reg Rd, ARM64Reg Rm, int shift);
void ROR(ARM64Reg Rd, ARM64Reg Rm, int shift);
// Logical (immediate)
void AND(ARM64Reg Rd, ARM64Reg Rn, u32 immr, u32 imms);
@@ -621,12 +626,16 @@ public:
// Wrapper around MOVZ+MOVK
void MOVI2R(ARM64Reg Rd, u64 imm, bool optimize = true);
template <class P>
void MOVP2R(ARM64Reg Rd, P *ptr) {
MOVI2R(Rd, (uintptr_t)ptr);
}
// Wrapper around AND x, y, imm etc
void ANDI2R(ARM64Reg Rd, ARM64Reg Rn, u64 imm, ARM64Reg scratch);
void ANDSI2R(ARM64Reg Rd, ARM64Reg Rn, u64 imm, ARM64Reg scratch);
void TSTI2R(ARM64Reg Rn, u64 imm, ARM64Reg scratch) { ANDSI2R(Is64Bit(Rn) ? SP : WSP, Rn, imm, scratch); }
void ORI2R(ARM64Reg Rd, ARM64Reg Rn, u64 imm, ARM64Reg scratch);
// Wrapper around AND x, y, imm etc. If you are sure the imm will work, no need to pass a scratch register.
void ANDI2R(ARM64Reg Rd, ARM64Reg Rn, u64 imm, ARM64Reg scratch = INVALID_REG);
void ANDSI2R(ARM64Reg Rd, ARM64Reg Rn, u64 imm, ARM64Reg scratch = INVALID_REG);
void TSTI2R(ARM64Reg Rn, u64 imm, ARM64Reg scratch = INVALID_REG) { ANDSI2R(Is64Bit(Rn) ? SP : WSP, Rn, imm, scratch); }
void ORI2R(ARM64Reg Rd, ARM64Reg Rn, u64 imm, ARM64Reg scratch = INVALID_REG);
// ABI related
void ABI_PushRegisters(BitSet32 registers);
+146 -13
View File
@@ -35,7 +35,23 @@ static const bool enableDebug = false;
//static bool enableStatistics = false; //unused?
//The standard ARM calling convention allocates the 16 ARM registers as:
// ARM64 calling conventions
// Standard: http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf
// Apple: https://developer.apple.com/library/ios/documentation/Xcode/Conceptual/iPhoneOSABIReference/Articles/ARM64FunctionCallingConventions.html
// Summary:
// ===========
// SP ("x31") is not a GPR so irrelevant.
// x0-x7: 8 parameter/result registers
// x8: "Indirect result location register" (points to struct return values? I think we can map this)
// x9-x15: 7 temporary registers (no need to save)
// x16: temporary register/procedure call scratch register 1
// x17: temporary register/procedure call scratch register 2
// x18: unavailable (reserved for use by the OS or linker or whatever - iOS, for example, uses it)
// x19-x28: 10 callee-saved registers
// x29: the frame pointer register
// x30: link register for procedure calls
// 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).
@@ -44,13 +60,19 @@ static const bool enableDebug = false;
// 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.
// Mappable registers:
// R2, R3, R4, R5, R6, R8, R11
// So: Scratch registers: x16, x17
// Mappable registers in priority order:
// x19, x20, x21, x22, x23, x24, x25, x27, x28, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x0, x1,
// That's a whole lot of registers so we might be able to statically allocate a bunch of common MIPS registers.
// We should put statically allocated registers in the 7 callee-save regs that are left over after the system regs (x19-x25), so we don't have to bother with
// saving them when we call out of the JIT. We will perform regular dynamic register allocation in the rest (x0-x15)
// STATIC ALLOCATION ARM64 (these are all callee-save registers):
// x26 : JIT base reg
// x27 : MIPS state
// x28 : Memory base pointer.
// x29 : Down counter
// STATIC ALLOCATION ARM:
// R10 : MIPS state
// R11 : Memory base pointer.
// R7 : Down counter
extern volatile CoreState coreState;
void ShowPC(u32 sp) {
@@ -71,12 +93,123 @@ namespace MIPSComp {
using namespace Arm64JitConstants;
void Arm64Jit::GenerateFixedCode()
{
// Uncomment if you want to see the output...
// INFO_LOG(JIT, "THE DISASM ========================");
// DisassembleArm(enterCode, GetCodePtr() - enterCode);
// INFO_LOG(JIT, "END OF THE DISASM ========================");
void Arm64Jit::GenerateFixedCode() {
enterCode = AlignCode16();
const u32 ALL_CALLEE_SAVED = 0x7FF80000;
BitSet32 regs_to_save(ALL_CALLEE_SAVED);
enterCode = GetCodePtr();
ABI_PushRegisters(regs_to_save);
// Fixed registers, these are always kept when in Jit context.
// R8 is used to hold flags during delay slots. Not always needed.
// R13 cannot be used as it's the stack pointer.
// TODO: Consider statically allocating:
// * r2-r4
// Really starting to run low on registers already though...
// R11, R10, R9
MOVP2R(MEMBASEREG, Memory::base);
MOVP2R(CTXREG, mips_);
MOVP2R(JITBASEREG, GetBasePtr());
// TODO: Preserve ASIMD registers
RestoreDowncount();
MovFromPC(SCRATCH1);
outerLoopPCInR0 = GetCodePtr();
MovToPC(SCRATCH1);
outerLoop = GetCodePtr();
SaveDowncount();
RestoreRoundingMode(true);
QuickCallFunction(SCRATCH1_64, &CoreTiming::Advance);
ApplyRoundingMode(true);
RestoreDowncount();
FixupBranch skipToRealDispatch = B(); //skip the sync and compare first time
dispatcherCheckCoreState = GetCodePtr();
// The result of slice decrementation should be in flags if somebody jumped here
// IMPORTANT - We jump on negative, not carry!!!
FixupBranch bailCoreState = B(CC_MI);
MOVP2R(SCRATCH1_64, &coreState);
LDR(INDEX_UNSIGNED, SCRATCH1, SCRATCH1_64, 0);
CMP(SCRATCH1, 0);
FixupBranch badCoreState = B(CC_NEQ);
FixupBranch skipToRealDispatch2 = B(); //skip the sync and compare first time
dispatcherPCInSCRATCH1 = GetCodePtr();
// TODO: Do we always need to write PC to RAM here?
MovToPC(SCRATCH1);
// At this point : flags = EQ. Fine for the next check, no need to jump over it.
dispatcher = GetCodePtr();
// The result of slice decrementation should be in flags if somebody jumped here
// IMPORTANT - We jump on negative, not carry!!!
FixupBranch bail = B(CC_MI);
SetJumpTarget(skipToRealDispatch);
SetJumpTarget(skipToRealDispatch2);
dispatcherNoCheck = GetCodePtr();
// Debug
if (enableDebug) {
// MOV(SCRATCH1, R13);
// QuickCallFunction(R1, (void *)&ShowPC);
}
LDR(INDEX_UNSIGNED, SCRATCH1, CTXREG, offsetof(MIPSState, pc));
LDR(SCRATCH1, MEMBASEREG, SCRATCH1_64);
ANDI2R(SCRATCH2, SCRATCH1, 0xFF000000); // rotation is to the right, in 2-bit increments.
ANDI2R(SCRATCH1, SCRATCH1, 0x00FFFFFF); // TODO: Replace this and the next op by a bit field extract
LSR(SCRATCH2, SCRATCH2, 24);
CMP(SCRATCH2, MIPS_EMUHACK_OPCODE);
FixupBranch skipJump = B(CC_NEQ);
// 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
// LDR(R0, R9, R0); here, replacing the next instructions.
#ifdef IOS
// On iOS, R9 (JITBASEREG) is volatile. We have to reload it.
MOVI2R(JITBASEREG, (u32)GetBasePtr());
#endif
ADD(SCRATCH1_64, SCRATCH1_64, JITBASEREG);
BR(SCRATCH1_64);
SetJumpTarget(skipJump);
// No block found, let's jit
SaveDowncount();
RestoreRoundingMode(true);
QuickCallFunction(SCRATCH1_64, (void *)&MIPSComp::JitAt);
ApplyRoundingMode(true);
RestoreDowncount();
B(dispatcherNoCheck); // no point in special casing this
SetJumpTarget(bail);
SetJumpTarget(bailCoreState);
MOVP2R(SCRATCH1_64, &coreState);
LDR(INDEX_UNSIGNED, SCRATCH1, SCRATCH1_64, 0);
CMP(SCRATCH1, 0);
B(CC_EQ, outerLoop);
SetJumpTarget(badCoreState);
breakpointBailout = GetCodePtr();
// TODO: Restore ASIMD registers
SaveDowncount();
RestoreRoundingMode(true);
ABI_PopRegisters(regs_to_save);
INFO_LOG(JIT, "THE DISASM ========================");
DisassembleArm64(enterCode, GetCodePtr() - enterCode);
INFO_LOG(JIT, "END OF THE DISASM ========================");
// Don't forget to zap the instruction cache!
FlushIcache();
+2 -2
View File
@@ -244,8 +244,8 @@ void Arm64Jit::BranchRSZeroComp(MIPSOpcode op, CCFlags cc, bool andLink, bool li
// Take the branch
if (andLink)
{
gpr.SetRegImm(SCRATCHREG1, js.compilerPC + 8);
STR(INDEX_UNSIGNED, SCRATCHREG1, CTXREG, MIPS_REG_RA * 4);
gpr.SetRegImm(SCRATCH1, js.compilerPC + 8);
STR(INDEX_UNSIGNED, SCRATCH1, CTXREG, MIPS_REG_RA * 4);
}
WriteExit(targetAddr, js.nextExit++);
+7 -7
View File
@@ -104,20 +104,20 @@ void Arm64Jit::FlushAll()
void Arm64Jit::FlushPrefixV()
{
if ((js.prefixSFlag & JitState::PREFIX_DIRTY) != 0) {
gpr.SetRegImm(SCRATCHREG1, js.prefixS);
STR(INDEX_UNSIGNED, SCRATCHREG1, CTXREG, offsetof(MIPSState, vfpuCtrl[VFPU_CTRL_SPREFIX]));
gpr.SetRegImm(SCRATCH1, js.prefixS);
STR(INDEX_UNSIGNED, SCRATCH1, CTXREG, offsetof(MIPSState, vfpuCtrl[VFPU_CTRL_SPREFIX]));
js.prefixSFlag = (JitState::PrefixState) (js.prefixSFlag & ~JitState::PREFIX_DIRTY);
}
if ((js.prefixTFlag & JitState::PREFIX_DIRTY) != 0) {
gpr.SetRegImm(SCRATCHREG1, js.prefixT);
STR(INDEX_UNSIGNED, SCRATCHREG1, CTXREG, offsetof(MIPSState, vfpuCtrl[VFPU_CTRL_TPREFIX]));
gpr.SetRegImm(SCRATCH1, js.prefixT);
STR(INDEX_UNSIGNED, SCRATCH1, CTXREG, offsetof(MIPSState, vfpuCtrl[VFPU_CTRL_TPREFIX]));
js.prefixTFlag = (JitState::PrefixState) (js.prefixTFlag & ~JitState::PREFIX_DIRTY);
}
if ((js.prefixDFlag & JitState::PREFIX_DIRTY) != 0) {
gpr.SetRegImm(SCRATCHREG1, js.prefixD);
STR(INDEX_UNSIGNED, SCRATCHREG1, CTXREG, offsetof(MIPSState, vfpuCtrl[VFPU_CTRL_DPREFIX]));
gpr.SetRegImm(SCRATCH1, js.prefixD);
STR(INDEX_UNSIGNED, SCRATCH1, CTXREG, offsetof(MIPSState, vfpuCtrl[VFPU_CTRL_DPREFIX]));
js.prefixDFlag = (JitState::PrefixState) (js.prefixDFlag & ~JitState::PREFIX_DIRTY);
}
}
@@ -442,7 +442,7 @@ void Arm64Jit::WriteExit(u32 destination, int exit_num)
b->linkStatus[exit_num] = true;
} else {
gpr.SetRegImm(X0, destination);
B((const void *)dispatcherPCInR0);
B((const void *)dispatcherPCInSCRATCH1);
}
}
+1 -1
View File
@@ -280,7 +280,7 @@ public:
const u8 *outerLoop;
const u8 *outerLoopPCInR0;
const u8 *dispatcherCheckCoreState;
const u8 *dispatcherPCInR0;
const u8 *dispatcherPCInSCRATCH1;
const u8 *dispatcher;
const u8 *dispatcherNoCheck;
+2 -2
View File
@@ -328,8 +328,8 @@ void Arm64RegCache::FlushR(MIPSGPReg r) {
case ML_IMM:
// IMM is always "dirty".
if (r != MIPS_REG_ZERO) {
SetRegImm(SCRATCHREG1, mr[r].imm);
emit_->STR(INDEX_UNSIGNED, SCRATCHREG1, CTXREG, GetMipsRegOffset(r));
SetRegImm(SCRATCH1, mr[r].imm);
emit_->STR(INDEX_UNSIGNED, SCRATCH1, CTXREG, GetMipsRegOffset(r));
}
break;
+8 -6
View File
@@ -24,12 +24,14 @@
namespace Arm64JitConstants {
// Bogus mappings, TODO ARM64
const Arm64Gen::ARM64Reg JITBASEREG = Arm64Gen::W0;
const Arm64Gen::ARM64Reg CTXREG = Arm64Gen::X1;
const Arm64Gen::ARM64Reg MEMBASEREG = Arm64Gen::X2;
const Arm64Gen::ARM64Reg SCRATCHREG1 = Arm64Gen::W3;
const Arm64Gen::ARM64Reg SCRATCHREG2 = Arm64Gen::W4;
const Arm64Gen::ARM64Reg DOWNCOUNTREG = Arm64Gen::W5;
const Arm64Gen::ARM64Reg JITBASEREG = Arm64Gen::X26;
const Arm64Gen::ARM64Reg CTXREG = Arm64Gen::X27;
const Arm64Gen::ARM64Reg MEMBASEREG = Arm64Gen::X28;
const Arm64Gen::ARM64Reg DOWNCOUNTREG = Arm64Gen::W29; // no need to use the full register width
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;
enum {
TOTAL_MAPPABLE_MIPSREGS = 36,
+17
View File
@@ -631,6 +631,9 @@ void JitCompareScreen::UpdateDisasm() {
}
UI::EventReturn JitCompareScreen::OnAddressChange(UI::EventParams &e) {
if (!MIPSComp::jit) {
return UI::EVENT_DONE;
}
JitBlockCache *blockCache = MIPSComp::jit->GetBlockCache();
u32 addr;
if (blockAddr_->GetText().size() > 8)
@@ -666,6 +669,10 @@ UI::EventReturn JitCompareScreen::OnNextBlock(UI::EventParams &e) {
}
UI::EventReturn JitCompareScreen::OnBlockAddress(UI::EventParams &e) {
if (!MIPSComp::jit) {
return UI::EVENT_DONE;
}
JitBlockCache *blockCache = MIPSComp::jit->GetBlockCache();
if (Memory::IsValidAddress(e.a)) {
currentBlock_ = blockCache->GetBlockNumberFromStartAddress(e.a);
@@ -677,6 +684,10 @@ UI::EventReturn JitCompareScreen::OnBlockAddress(UI::EventParams &e) {
}
UI::EventReturn JitCompareScreen::OnRandomBlock(UI::EventParams &e) {
if (!MIPSComp::jit) {
return UI::EVENT_DONE;
}
JitBlockCache *blockCache = MIPSComp::jit->GetBlockCache();
int numBlocks = blockCache->GetNumBlocks();
if (numBlocks > 0) {
@@ -687,6 +698,9 @@ UI::EventReturn JitCompareScreen::OnRandomBlock(UI::EventParams &e) {
}
UI::EventReturn JitCompareScreen::OnRandomVFPUBlock(UI::EventParams &e) {
if (!MIPSComp::jit) {
return UI::EVENT_DONE;
}
JitBlockCache *blockCache = MIPSComp::jit->GetBlockCache();
int numBlocks = blockCache->GetNumBlocks();
if (numBlocks > 0) {
@@ -714,6 +728,9 @@ UI::EventReturn JitCompareScreen::OnRandomVFPUBlock(UI::EventParams &e) {
UI::EventReturn JitCompareScreen::OnCurrentBlock(UI::EventParams &e) {
if (!MIPSComp::jit) {
return UI::EVENT_DONE;
}
JitBlockCache *blockCache = MIPSComp::jit->GetBlockCache();
std::vector<int> blockNum;
blockCache->GetBlockNumbersFromAddress(currentMIPS->pc, &blockNum);
+6
View File
@@ -439,6 +439,12 @@ void LogoScreen::render() {
#ifdef _WIN32
dc.DrawText(screenManager()->getThin3DContext()->GetInfoString(T3DInfo::APINAME), bounds.centerX(), bounds.y2() - 100, colorAlpha(0xFFFFFFFF, alphaText), ALIGN_CENTER);
#elif defined(ANDROID)
#ifdef ARM64
dc.SetFontScale(2.0f, 2.0f);
dc.DrawText("EXPERIMENTAL ARM64 BUILD", bounds.centerX(), bounds.y2() - 100, colorAlpha(0xFF3030FF, alphaText), ALIGN_CENTER);
dc.SetFontScale(1.0f, 1.0f);
#endif
#endif
dc.End();