Beginning of the AArch64 JIT branch.

This is the bare minimum required to run a few games on AArch64.
Was able to run starfield and Animal Crossing to the Nintendo logo.
QEmu emulation is literally the slowest thing in the world, it maxes out at around 12mhz on my Core i7-4930MX.
This commit is contained in:
Ryan Houdek 2014-06-05 20:33:35 -05:00
parent f107b5e176
commit 2b06257e16
17 changed files with 1984 additions and 15 deletions

View File

@ -31,19 +31,23 @@ set(SRCS BreakPoints.cpp
Logging/LogManager.cpp)
if(_M_ARM_32) #ARMv7
set(SRCS ${SRCS}
ArmCPUDetect.cpp
ArmEmitter.cpp
GenericFPURoundMode.cpp)
elseif(_M_X86) #X86
set(SRCS ${SRCS}
x64CPUDetect.cpp
x64FPURoundMode.cpp)
else() #Generic
set(SRCS ${SRCS}
GenericFPURoundMode.cpp
x64CPUDetect.cpp)
if(_M_ARM)
if (_M_ARM_32) #ARMv7
set(SRCS ${SRCS}
ArmEmitter.cpp)
else() #AArch64
set(SRCS ${SRCS}
Arm64Emitter.cpp)
endif()
set(SRCS ${SRCS}
ArmCPUDetect.cpp
GenericFPURoundMode.cpp)
else()
if(_M_X86) #X86
set(SRCS ${SRCS}
x64FPURoundMode.cpp)
endif()
set(SRCS ${SRCS} x64CPUDetect.cpp)
endif()
if(WIN32)
set(SRCS ${SRCS} ExtendedTrace.cpp)

View File

@ -197,8 +197,7 @@ if(_M_X86)
PowerPC/JitCommon/JitBackpatch.cpp
PowerPC/JitCommon/JitAsmCommon.cpp
PowerPC/JitCommon/Jit_Util.cpp)
endif()
if(_M_ARM_32)
elseif(_M_ARM_32)
set(SRCS ${SRCS}
ArmMemTools.cpp
PowerPC/JitArm32/Jit.cpp
@ -217,6 +216,16 @@ if(_M_ARM_32)
PowerPC/JitArm32/JitArm_SystemRegisters.cpp
PowerPC/JitArm32/JitArm_LoadStoreFloating.cpp
)
elseif(_M_ARM_64)
set(SRCS ${SRCS}
PowerPC/JitArm64/Jit.cpp
PowerPC/JitArm64/JitAsm.cpp
PowerPC/JitArm64/JitArm64Cache.cpp
PowerPC/JitArm64/JitArm64_RegCache.cpp
PowerPC/JitArm64/JitArm64_Branch.cpp
PowerPC/JitArm64/JitArm64_LoadStore.cpp
PowerPC/JitArm64/JitArm64_SystemRegisters.cpp
PowerPC/JitArm64/JitArm64_Tables.cpp)
endif()
set(LIBS

View File

@ -0,0 +1,287 @@
// Copyright 2014 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#include "Common/Arm64Emitter.h"
#include "Common/Common.h"
#include "Core/PatchEngine.h"
#include "Core/PowerPC/JitArm64/Jit.h"
#include "Core/PowerPC/JitArm64/JitArm64_RegCache.h"
#include "Core/PowerPC/JitArm64/JitArm64_Tables.h"
using namespace Arm64Gen;
static int CODE_SIZE = 1024*1024*32;
void JitArm64::Init()
{
AllocCodeSpace(CODE_SIZE);
jo.enableBlocklink = true;
gpr.Init(this);
fpr.Init(this);
blocks.Init();
asm_routines.Init();
code_block.m_stats = &js.st;
code_block.m_gpa = &js.gpa;
code_block.m_fpa = &js.fpa;
}
void JitArm64::ClearCache()
{
ClearCodeSpace();
blocks.Clear();
}
void JitArm64::Shutdown()
{
FreeCodeSpace();
blocks.Shutdown();
asm_routines.Shutdown();
}
void JitArm64::unknown_instruction(UGeckoInstruction inst)
{
WARN_LOG(DYNA_REC, "unknown_instruction %08x - Fix me ;)", inst.hex);
}
void JitArm64::FallBackToInterpreter(UGeckoInstruction inst)
{
gpr.Flush(FlushMode::FLUSH_ALL);
fpr.Flush(FlushMode::FLUSH_ALL);
Interpreter::_interpreterInstruction instr = GetInterpreterOp(inst);
MOVI2R(W0, inst.hex);
MOVI2R(X30, (u64)instr);
BLR(X30);
}
void JitArm64::HLEFunction(UGeckoInstruction inst)
{
WARN_LOG(DYNA_REC, "HLEFunction %08x - Fix me ;)", inst.hex);
exit(0);
}
void JitArm64::DoNothing(UGeckoInstruction inst)
{
// Yup, just don't do anything.
}
void JitArm64::Break(UGeckoInstruction inst)
{
WARN_LOG(DYNA_REC, "Breaking! %08x - Fix me ;)", inst.hex);
exit(0);
}
void JitArm64::DoDownCount()
{
ARM64Reg WA = gpr.GetReg();
LDR(INDEX_UNSIGNED, WA, X29, PPCSTATE_OFF(downcount));
if (js.downcountAmount < 4096) // We can enlarge this if we used rotations
{
SUBS(WA, WA, js.downcountAmount);
STR(INDEX_UNSIGNED, WA, X29, PPCSTATE_OFF(downcount));
}
else
{
ARM64Reg WB = gpr.GetReg();
MOVI2R(WB, js.downcountAmount);
SUBS(WA, WA, WB);
STR(INDEX_UNSIGNED, WA, X29, PPCSTATE_OFF(downcount));
gpr.Unlock(WB);
}
gpr.Unlock(WA);
}
// Exits
void JitArm64::WriteExit(u32 destination)
{
//If nobody has taken care of this yet (this can be removed when all branches are done)
JitBlock *b = js.curBlock;
JitBlock::LinkData linkData;
linkData.exitAddress = destination;
linkData.exitPtrs = GetWritableCodePtr();
linkData.linkStatus = false;
DoDownCount();
// Link opportunity!
int block;
if (jo.enableBlocklink && (block = blocks.GetBlockNumberFromStartAddress(destination)) >= 0)
{
// It exists! Joy of joy!
B(blocks.GetBlock(block)->checkedEntry);
linkData.linkStatus = true;
}
else
{
ARM64Reg WA = gpr.GetReg();
ARM64Reg XA = EncodeRegTo64(WA);
MOVI2R(WA, destination);
STR(INDEX_UNSIGNED, WA, X29, PPCSTATE_OFF(pc));
MOVI2R(XA, (u64)asm_routines.dispatcher);
BR(XA);
gpr.Unlock(WA);
}
b->linkData.push_back(linkData);
}
void JitArm64::WriteExceptionExit(ARM64Reg dest)
{
STR(INDEX_UNSIGNED, dest, X29, PPCSTATE_OFF(pc));
STR(INDEX_UNSIGNED, dest, X29, PPCSTATE_OFF(npc));
gpr.Unlock(dest);
DoDownCount();
MOVI2R(EncodeRegTo64(dest), (u64)&PowerPC::CheckExceptions);
BLR(EncodeRegTo64(dest));
LDR(INDEX_UNSIGNED, dest, X29, PPCSTATE_OFF(npc));
STR(INDEX_UNSIGNED, dest, X29, PPCSTATE_OFF(pc));
MOVI2R(EncodeRegTo64(dest), (u64)asm_routines.dispatcher);
BR(EncodeRegTo64(dest));
}
void JitArm64::WriteExitDestInR(ARM64Reg Reg)
{
STR(INDEX_UNSIGNED, Reg, X29, PPCSTATE_OFF(pc));
gpr.Unlock(Reg);
DoDownCount();
MOVI2R(EncodeRegTo64(Reg), (u64)asm_routines.dispatcher);
BR(EncodeRegTo64(Reg));
}
void STACKALIGN JitArm64::Run()
{
CompiledCode pExecAddr = (CompiledCode)asm_routines.enterCode;
pExecAddr();
}
void JitArm64::SingleStep()
{
CompiledCode pExecAddr = (CompiledCode)asm_routines.enterCode;
pExecAddr();
}
void STACKALIGN JitArm64::Jit(u32 em_address)
{
if (GetSpaceLeft() < 0x10000 || blocks.IsFull() || Core::g_CoreStartupParameter.bJITNoBlockCache)
{
ClearCache();
}
int block_num = blocks.AllocateBlock(PowerPC::ppcState.pc);
JitBlock *b = blocks.GetBlock(block_num);
const u8* BlockPtr = DoJit(PowerPC::ppcState.pc, &code_buffer, b);
blocks.FinalizeBlock(block_num, jo.enableBlocklink, BlockPtr);
}
const u8* JitArm64::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBlock *b)
{
int blockSize = code_buf->GetSize();
if (Core::g_CoreStartupParameter.bEnableDebugging)
{
// Comment out the following to disable breakpoints (speed-up)
blockSize = 1;
}
if (em_address == 0)
{
Core::SetState(Core::CORE_PAUSE);
WARN_LOG(DYNA_REC, "ERROR: Compiling at 0. LR=%08x CTR=%08x", LR, CTR);
}
js.isLastInstruction = false;
js.blockStart = em_address;
js.fifoBytesThisBlock = 0;
js.downcountAmount = 0;
js.skipnext = false;
js.curBlock = b;
u32 nextPC = em_address;
// Analyze the block, collect all instructions it is made of (including inlining,
// if that is enabled), reorder instructions for optimal performance, and join joinable instructions.
nextPC = analyzer.Analyze(em_address, &code_block, code_buf, blockSize);
PPCAnalyst::CodeOp *ops = code_buf->codebuffer;
const u8 *start = GetCodePtr();
b->checkedEntry = start;
b->runCount = 0;
// Downcount flag check, Only valid for linked blocks
{
FixupBranch bail = B(CC_PL);
ARM64Reg WA = gpr.GetReg();
ARM64Reg XA = EncodeRegTo64(WA);
MOVI2R(WA, js.blockStart);
STR(INDEX_UNSIGNED, WA, X29, PPCSTATE_OFF(pc));
MOVI2R(XA, (u64)asm_routines.doTiming);
BR(XA);
gpr.Unlock(WA);
SetJumpTarget(bail);
}
const u8 *normalEntry = GetCodePtr();
b->normalEntry = normalEntry;
gpr.Start(js.gpa);
fpr.Start(js.fpa);
if (!Core::g_CoreStartupParameter.bEnableDebugging)
js.downcountAmount += PatchEngine::GetSpeedhackCycles(em_address);
// Translate instructions
for (u32 i = 0; i < code_block.m_num_instructions; i++)
{
js.compilerPC = ops[i].address;
js.op = &ops[i];
js.instructionNumber = i;
const GekkoOPInfo *opinfo = ops[i].opinfo;
js.downcountAmount += opinfo->numCycles;
if (i == (code_block.m_num_instructions - 1))
{
// WARNING - cmp->branch merging will screw this up.
js.isLastInstruction = true;
js.next_inst = 0;
}
else
{
// help peephole optimizations
js.next_inst = ops[i + 1].inst;
js.next_compilerPC = ops[i + 1].address;
}
if (!ops[i].skip)
{
if (js.memcheck && (opinfo->flags & FL_USE_FPU))
{
// Don't do this yet
BRK(0x7777);
}
JitArm64Tables::CompileInstruction(ops[i]);
if (js.memcheck && (opinfo->flags & FL_LOADSTORE))
{
// Don't do this yet
BRK(0x666);
}
}
}
if (code_block.m_memory_exception)
BRK(0x500);
if (code_block.m_broken)
{
printf("Broken Block going to 0x%08x\n", nextPC);
WriteExit(nextPC);
}
b->codeSize = (u32)(GetCodePtr() - normalEntry);
b->originalSize = code_block.m_num_instructions;
FlushIcache();
return start;
}

View File

@ -0,0 +1,99 @@
// Copyright 2014 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#pragma once
#include "Common/Arm64Emitter.h"
#include "Core/PowerPC/CPUCoreBase.h"
#include "Core/PowerPC/PPCAnalyst.h"
#include "Core/PowerPC/JitArm64/JitArm64_RegCache.h"
#include "Core/PowerPC/JitArm64/JitArm64Cache.h"
#include "Core/PowerPC/JitArm64/JitAsm.h"
#include "Core/PowerPC/JitCommon/JitBase.h"
#define PPCSTATE_OFF(elem) ((s64)&PowerPC::ppcState.elem - (s64)&PowerPC::ppcState)
using namespace Arm64Gen;
class JitArm64 : public JitBase, public Arm64Gen::ARM64CodeBlock
{
public:
JitArm64() : code_buffer(32000) {}
~JitArm64() {}
void Init();
void Shutdown();
JitBaseBlockCache *GetBlockCache() { return &blocks; }
const u8 *BackPatch(u8 *codePtr, u32 em_address, void *ctx) { return NULL; }
bool IsInCodeSpace(u8 *ptr) { return IsInSpace(ptr); }
void ClearCache();
CommonAsmRoutinesBase *GetAsmRoutines()
{
return &asm_routines;
}
void Run();
void SingleStep();
void Jit(u32 em_address);
const char *GetName()
{
return "JITARM64";
}
// OPCODES
void unknown_instruction(UGeckoInstruction inst);
void FallBackToInterpreter(UGeckoInstruction inst);
void DoNothing(UGeckoInstruction inst);
void HLEFunction(UGeckoInstruction inst);
void DynaRunTable4(UGeckoInstruction inst);
void DynaRunTable19(UGeckoInstruction inst);
void DynaRunTable31(UGeckoInstruction inst);
void DynaRunTable59(UGeckoInstruction inst);
void DynaRunTable63(UGeckoInstruction inst);
// Force break
void Break(UGeckoInstruction inst);
// Branch
void sc(UGeckoInstruction inst);
void rfi(UGeckoInstruction inst);
void bx(UGeckoInstruction inst);
void bcx(UGeckoInstruction inst);
void bcctrx(UGeckoInstruction inst);
void bclrx(UGeckoInstruction inst);
// System Registers
void mtmsr(UGeckoInstruction inst);
// LoadStore
void icbi(UGeckoInstruction inst);
private:
Arm64GPRCache gpr;
Arm64FPRCache fpr;
JitArm64BlockCache blocks;
JitArm64AsmRoutineManager asm_routines;
PPCAnalyst::CodeBuffer code_buffer;
const u8* DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBlock *b);
void DoDownCount();
// Exits
void WriteExit(u32 destination);
void WriteExceptionExit(ARM64Reg dest);
void WriteExitDestInR(ARM64Reg dest);
FixupBranch JumpIfCRFieldBit(int field, int bit, bool jump_if_set);
};

View File

@ -0,0 +1,16 @@
// Copyright 2014 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#include "Core/PowerPC/JitInterface.h"
#include "Core/PowerPC/JitArm64/Jit.h"
#include "Core/PowerPC/JitArm64/JitArm64Cache.h"
void JitArm64BlockCache::WriteLinkBlock(u8* location, const u8* address)
{
}
void JitArm64BlockCache::WriteDestroyBlock(const u8* location, u32 address)
{
}

View File

@ -0,0 +1,17 @@
// Copyright 2014 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#pragma once
#include "Core/PowerPC/JitCommon/JitCache.h"
typedef void (*CompiledCode)();
class JitArm64BlockCache : public JitBaseBlockCache
{
private:
void WriteLinkBlock(u8* location, const u8* address);
void WriteDestroyBlock(const u8* location, u32 address);
};

View File

@ -0,0 +1,254 @@
// Copyright 2014 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#include "Common/Arm64Emitter.h"
#include "Common/Common.h"
#include "Core/Core.h"
#include "Core/CoreTiming.h"
#include "Core/PowerPC/PowerPC.h"
#include "Core/PowerPC/PPCTables.h"
#include "Core/PowerPC/JitArm64/Jit.h"
#include "Core/PowerPC/JitArm64/JitArm64_RegCache.h"
#include "Core/PowerPC/JitArm64/JitAsm.h"
using namespace Arm64Gen;
void JitArm64::sc(UGeckoInstruction inst)
{
INSTRUCTION_START
gpr.Flush(FlushMode::FLUSH_ALL);
fpr.Flush(FlushMode::FLUSH_ALL);
ARM64Reg WA = gpr.GetReg();
LDR(INDEX_UNSIGNED, WA, X29, PPCSTATE_OFF(Exceptions));
ORR(WA, WA, 31, 0); // Same as WA | EXCEPTION_SYSCALL
STR(INDEX_UNSIGNED, WA, X29, PPCSTATE_OFF(Exceptions));
MOVI2R(WA, js.compilerPC + 4);
STR(INDEX_UNSIGNED, WA, X29, PPCSTATE_OFF(pc));
// WA is unlocked in this function
WriteExceptionExit(WA);
}
void JitArm64::rfi(UGeckoInstruction inst)
{
INSTRUCTION_START
gpr.Flush(FlushMode::FLUSH_ALL);
fpr.Flush(FlushMode::FLUSH_ALL);
// See Interpreter rfi for details
const u32 mask = 0x87C0FFFF;
const u32 clearMSR13 = 0xFFFBFFFF; // Mask used to clear the bit MSR[13]
// MSR = ((MSR & ~mask) | (SRR1 & mask)) & clearMSR13;
// R0 = MSR location
// R1 = MSR contents
// R2 = Mask
// R3 = Mask
ARM64Reg WA = gpr.GetReg();
ARM64Reg WB = gpr.GetReg();
ARM64Reg WC = gpr.GetReg();
MOVI2R(WA, (~mask) & clearMSR13);
MOVI2R(WB, mask & clearMSR13);
LDR(INDEX_UNSIGNED, WC, X29, PPCSTATE_OFF(msr));
AND(WC, WC, WB, ArithOption(WC, ST_LSL, 0)); // rD = Masked MSR
LDR(INDEX_UNSIGNED, WA, X29, PPCSTATE_OFF(spr[SPR_SRR1])); // rB contains SRR1 here
AND(WA, WA, WB, ArithOption(WA, ST_LSL, 0)); // rB contains masked SRR1 here
ORR(WA, WA, WC, ArithOption(WA, ST_LSL, 0)); // rB = Masked MSR OR masked SRR1
STR(INDEX_UNSIGNED, WA, X29, PPCSTATE_OFF(msr)); // STR rB in to rA
LDR(INDEX_UNSIGNED, WA, X29, PPCSTATE_OFF(spr[SPR_SRR0]));
gpr.Unlock(WB, WC);
// WA is unlocked in this function
WriteExceptionExit(WA);
}
void JitArm64::bx(UGeckoInstruction inst)
{
INSTRUCTION_START
gpr.Flush(FlushMode::FLUSH_ALL);
fpr.Flush(FlushMode::FLUSH_ALL);
u32 destination;
if (inst.AA)
destination = SignExt26(inst.LI << 2);
else
destination = js.compilerPC + SignExt26(inst.LI << 2);
if (inst.LK)
{
u32 Jumpto = js.compilerPC + 4;
ARM64Reg WA = gpr.GetReg();
MOVI2R(WA, Jumpto);
STR(INDEX_UNSIGNED, WA, X29, PPCSTATE_OFF(spr[SPR_LR]));
gpr.Unlock(WA);
}
if (destination == js.compilerPC)
{
// make idle loops go faster
ARM64Reg WA = gpr.GetReg();
ARM64Reg XA = EncodeRegTo64(WA);
MOVI2R(XA, (u64)&CoreTiming::Idle);
BLR(XA);
MOVI2R(WA, js.compilerPC);
WriteExceptionExit(WA);
}
WriteExit(destination);
}
void JitArm64::bcx(UGeckoInstruction inst)
{
INSTRUCTION_START
gpr.Flush(FlushMode::FLUSH_ALL);
fpr.Flush(FlushMode::FLUSH_ALL);
ARM64Reg WA = gpr.GetReg();
FixupBranch pCTRDontBranch;
if ((inst.BO & BO_DONT_DECREMENT_FLAG) == 0) // Decrement and test CTR
{
LDR(INDEX_UNSIGNED, WA, X29, PPCSTATE_OFF(spr[SPR_CTR]));
SUBS(WA, WA, 1);
STR(INDEX_UNSIGNED, WA, X29, PPCSTATE_OFF(spr[SPR_CTR]));
if (inst.BO & BO_BRANCH_IF_CTR_0)
pCTRDontBranch = B(CC_NEQ);
else
pCTRDontBranch = B(CC_EQ);
}
FixupBranch pConditionDontBranch;
if ((inst.BO & BO_DONT_CHECK_CONDITION) == 0) // Test a CR bit
{
pConditionDontBranch = JumpIfCRFieldBit(inst.BI >> 2, 3 - (inst.BI & 3),
!(inst.BO_2 & BO_BRANCH_IF_TRUE));
}
if (inst.LK)
{
u32 Jumpto = js.compilerPC + 4;
MOVI2R(WA, Jumpto);
STR(INDEX_UNSIGNED, WA, X29, PPCSTATE_OFF(spr[SPR_LR]));
}
gpr.Unlock(WA);
u32 destination;
if (inst.AA)
destination = SignExt16(inst.BD << 2);
else
destination = js.compilerPC + SignExt16(inst.BD << 2);
WriteExit(destination);
if ((inst.BO & BO_DONT_CHECK_CONDITION) == 0)
SetJumpTarget( pConditionDontBranch );
if ((inst.BO & BO_DONT_DECREMENT_FLAG) == 0)
SetJumpTarget( pCTRDontBranch );
WriteExit(js.compilerPC + 4);
}
void JitArm64::bcctrx(UGeckoInstruction inst)
{
INSTRUCTION_START
// bcctrx doesn't decrement and/or test CTR
_assert_msg_(DYNA_REC, inst.BO_2 & BO_DONT_DECREMENT_FLAG, "bcctrx with decrement and test CTR option is invalid!");
if (inst.BO_2 & BO_DONT_CHECK_CONDITION)
{
// BO_2 == 1z1zz -> b always
//NPC = CTR & 0xfffffffc;
gpr.Flush(FlushMode::FLUSH_ALL);
fpr.Flush(FlushMode::FLUSH_ALL);
if (inst.LK_3)
{
ARM64Reg WB = gpr.GetReg();
u32 Jumpto = js.compilerPC + 4;
MOVI2R(WB, Jumpto);
STR(INDEX_UNSIGNED, WB, X29, PPCSTATE_OFF(spr[SPR_LR]));
gpr.Unlock(WB);
}
ARM64Reg WA = gpr.GetReg();
LDR(INDEX_UNSIGNED, WA, X29, PPCSTATE_OFF(spr[SPR_CTR]));
AND(WA, WA, 30, 29); // Wipe the bottom 2 bits.
WriteExitDestInR(WA);
}
else
{
// Rare condition seen in (just some versions of?) Nintendo's NES Emulator
// BO_2 == 001zy -> b if false
// BO_2 == 011zy -> b if true
_assert_msg_(DYNA_REC, false, "Haven't implemented rare form of bcctrx yet");
}
}
void JitArm64::bclrx(UGeckoInstruction inst)
{
INSTRUCTION_START
gpr.Flush(FlushMode::FLUSH_ALL);
fpr.Flush(FlushMode::FLUSH_ALL);
ARM64Reg WA = gpr.GetReg();
FixupBranch pCTRDontBranch;
if ((inst.BO & BO_DONT_DECREMENT_FLAG) == 0) // Decrement and test CTR
{
LDR(INDEX_UNSIGNED, WA, X29, PPCSTATE_OFF(spr[SPR_CTR]));
SUBS(WA, WA, 1);
STR(INDEX_UNSIGNED, WA, X29, PPCSTATE_OFF(spr[SPR_CTR]));
if (inst.BO & BO_BRANCH_IF_CTR_0)
pCTRDontBranch = B(CC_NEQ);
else
pCTRDontBranch = B(CC_EQ);
}
FixupBranch pConditionDontBranch;
if ((inst.BO & BO_DONT_CHECK_CONDITION) == 0) // Test a CR bit
{
pConditionDontBranch = JumpIfCRFieldBit(inst.BI >> 2, 3 - (inst.BI & 3),
!(inst.BO_2 & BO_BRANCH_IF_TRUE));
}
LDR(INDEX_UNSIGNED, WA, X29, PPCSTATE_OFF(spr[SPR_LR]));
AND(WA, WA, 30, 29); // Wipe the bottom 2 bits.
if (inst.LK)
{
ARM64Reg WB = gpr.GetReg();
u32 Jumpto = js.compilerPC + 4;
MOVI2R(WB, Jumpto);
STR(INDEX_UNSIGNED, WB, X29, PPCSTATE_OFF(spr[SPR_LR]));
gpr.Unlock(WB);
}
WriteExitDestInR(WA);
if ((inst.BO & BO_DONT_CHECK_CONDITION) == 0)
SetJumpTarget( pConditionDontBranch );
if ((inst.BO & BO_DONT_DECREMENT_FLAG) == 0)
SetJumpTarget( pCTRDontBranch );
WriteExit(js.compilerPC + 4);
}

View File

@ -0,0 +1,22 @@
// Copyright 2014 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#include "Common/Arm64Emitter.h"
#include "Common/Common.h"
#include "Core/Core.h"
#include "Core/CoreTiming.h"
#include "Core/PowerPC/PowerPC.h"
#include "Core/PowerPC/PPCTables.h"
#include "Core/PowerPC/JitArm64/Jit.h"
#include "Core/PowerPC/JitArm64/JitArm64_RegCache.h"
#include "Core/PowerPC/JitArm64/JitAsm.h"
using namespace Arm64Gen;
void JitArm64::icbi(UGeckoInstruction inst)
{
FallBackToInterpreter(inst);
WriteExit(js.compilerPC + 4);
}

View File

@ -0,0 +1,319 @@
// copyright 2014 dolphin emulator project
// licensed under gplv2
// refer to the license.txt file included.
#include "Core/PowerPC/JitArm64/Jit.h"
#include "Core/PowerPC/JitArm64/JitArm64_RegCache.h"
using namespace Arm64Gen;
void Arm64RegCache::Init(ARM64XEmitter *emitter)
{
m_emit = emitter;
GetAllocationOrder();
}
ARM64Reg Arm64RegCache::GetReg(void)
{
for (auto& it : m_host_registers)
{
if (!it.IsLocked())
{
it.Lock();
return it.GetReg();
}
}
// Holy cow, how did you run out of registers?
// We can't return anything reasonable in this case. Return INVALID_REG and watch the failure happen
_assert_msg_(_DYNA_REC_, false, "All available registers are locked dumb dumb");
return INVALID_REG;
}
void Arm64RegCache::LockRegister(ARM64Reg host_reg)
{
auto reg = std::find(m_host_registers.begin(), m_host_registers.end(), host_reg);
if (reg == m_host_registers.end())
_assert_msg_(DYNA_REC, false, "Don't try locking a register that isn't in the cache");
_assert_msg_(DYNA_REC, !reg->IsLocked(), "This register is already locked");
reg->Lock();
}
void Arm64RegCache::UnlockRegister(ARM64Reg host_reg)
{
auto reg = std::find(m_host_registers.begin(), m_host_registers.end(), host_reg);
if (reg == m_host_registers.end())
_assert_msg_(DYNA_REC, false, "Don't try unlocking a register that isn't in the cache");
_assert_msg_(DYNA_REC, reg->IsLocked(), "This register is already unlocked");
reg->Unlock();
}
// GPR Cache
void Arm64GPRCache::Start(PPCAnalyst::BlockRegStats &stats)
{
// To make this technique easy, let's just work on pairs of even/odd registers
// We could do simple odd/even as well to get a few spare temporary registers
// but it isn't really needed, we aren't starved for registers
for (int reg = 0; reg < 32; reg += 2)
{
u32 regs_used = (stats.IsUsed(reg) << 1) | stats.IsUsed(reg + 1);
switch (regs_used)
{
case 0x02: // Reg+0 used
{
ARM64Reg host_reg = GetReg();
m_guest_registers[reg].LoadToReg(host_reg);
m_emit->LDR(INDEX_UNSIGNED, host_reg, X29, PPCSTATE_OFF(gpr[reg]));
}
break;
case 0x01: // Reg+1 used
{
ARM64Reg host_reg = GetReg();
m_guest_registers[reg + 1].LoadToReg(host_reg);
m_emit->LDR(INDEX_UNSIGNED, host_reg, X29, PPCSTATE_OFF(gpr[reg + 1]));
}
break;
case 0x03: // Both registers used
{
// Get a 64bit host register
ARM64Reg host_reg = EncodeRegTo64(GetReg());
m_guest_registers[reg].LoadToAway(host_reg, REG_LOW);
m_guest_registers[reg + 1].LoadToAway(host_reg, REG_HIGH);
// host_reg is 64bit here.
// It'll load both guest_registers in one LDR
m_emit->LDR(INDEX_UNSIGNED, host_reg, X29, PPCSTATE_OFF(gpr[reg]));
}
break;
case 0x00: // Neither used
default:
break;
}
}
}
bool Arm64GPRCache::IsCalleeSaved(ARM64Reg reg)
{
static std::vector<ARM64Reg> callee_regs =
{
X28, X27, X26, X25, X24, X23, X22, X21, X20,
X19, INVALID_REG,
};
return std::find(callee_regs.begin(), callee_regs.end(), EncodeRegTo64(reg)) != callee_regs.end();
}
void Arm64GPRCache::Flush(FlushMode mode, PPCAnalyst::CodeOp* op)
{
for (int i = 0; i < 32; ++i)
{
bool flush = true;
if (mode == FLUSH_INTERPRETER)
{
if (!(op->regsOut[0] == i ||
op->regsOut[1] == i ||
op->regsIn[0] == i ||
op->regsIn[1] == i ||
op->regsIn[2] == i))
{
// This interpreted instruction doesn't use this register
flush = false;
}
}
if (m_guest_registers[i].GetType() == REG_REG)
{
// Has to be flushed if it isn't in a callee saved register
ARM64Reg host_reg = m_guest_registers[i].GetReg();
if (flush || !IsCalleeSaved(host_reg))
{
m_emit->STR(INDEX_UNSIGNED, host_reg, X29, PPCSTATE_OFF(gpr[i]));
Unlock(host_reg);
m_guest_registers[i].Flush();
}
}
else if (m_guest_registers[i].GetType() == REG_IMM)
{
if (flush)
{
ARM64Reg host_reg = GetReg();
m_emit->MOVI2R(host_reg, m_guest_registers[i].GetImm());
m_emit->STR(INDEX_UNSIGNED, host_reg, X29, PPCSTATE_OFF(gpr[i]));
Unlock(host_reg);
m_guest_registers[i].Flush();
}
}
else if (m_guest_registers[i].GetType() == REG_AWAY)
{
// We are away, that means that this register and the next are stored in a single 64bit register
// There is a very good chance that both the registers are out in some "temp" register
bool flush_2 = true;
if (mode == FLUSH_INTERPRETER)
{
if (!(op->regsOut[0] == (i + 1) ||
op->regsOut[1] == (i + 1) ||
op->regsIn[0] == (i + 1) ||
op->regsIn[1] == (i + 1) ||
op->regsIn[2] == (i + 1)))
{
// This interpreted instruction doesn't use this register
flush_2 = false;
}
}
ARM64Reg host_reg = m_guest_registers[i].GetAwayReg();
ARM64Reg host_reg_1 = m_guest_registers[i].GetReg();
ARM64Reg host_reg_2 = m_guest_registers[i + 1].GetReg();
// Flush if either of these shared registers are used.
if (flush ||
flush_2 ||
!IsCalleeSaved(host_reg) ||
!IsCalleeSaved(host_reg_1) ||
!IsCalleeSaved(host_reg_2))
{
if (host_reg_1 == INVALID_REG)
{
// We never loaded this register
// We've got to test the state of our shared register
// Currently it is always reg+1
if (host_reg_2 == INVALID_REG)
{
// We didn't load either of these registers
// This can happen in cases where we had to flush register state
// or if we hit an interpreted instruction before we could use it
// Dump the whole thing in one go and flush both registers
// 64bit host register will store 2 32bit store registers in one go
m_emit->STR(INDEX_UNSIGNED, host_reg, X29, PPCSTATE_OFF(gpr[i]));
}
else
{
// Alright, bottom register isn't used, but top one is
// Only store the top one
m_emit->STR(INDEX_UNSIGNED, host_reg_2, X29, PPCSTATE_OFF(gpr[i + 1]));
Unlock(host_reg_2);
}
}
else
{
m_emit->STR(INDEX_UNSIGNED, host_reg_1, X29, PPCSTATE_OFF(gpr[i]));
Unlock(host_reg_1);
}
// Flush both registers
m_guest_registers[i].Flush();
m_guest_registers[i + 1].Flush();
Unlock(DecodeReg(host_reg));
}
// Skip the next register since we've handled it here
++i;
}
}
}
ARM64Reg Arm64GPRCache::R(u32 preg)
{
OpArg& reg = m_guest_registers[preg];
switch (reg.GetType())
{
case REG_REG: // already in a reg
return reg.GetReg();
break;
case REG_IMM: // Is an immediate
{
ARM64Reg host_reg = GetReg();
m_emit->MOVI2R(host_reg, reg.GetImm());
}
break;
case REG_AWAY: // Register is away in a shared register
{
// Let's do the voodoo that we dodo
if (reg.GetReg() == INVALID_REG)
{
// Alright, we need to move to a valid location
ARM64Reg host_reg = GetReg();
reg.LoadAwayToReg(host_reg);
// Alright, we need to extract from our away register
// To our new 32bit register
if (reg.GetAwayLocation() == REG_LOW)
{
// We are in the low bits
// Just move it over to the low bits of the new register
m_emit->UBFM(EncodeRegTo64(host_reg), reg.GetAwayReg(), 0, 31);
}
else
{
// We are in the high bits
m_emit->UBFM(EncodeRegTo64(host_reg), reg.GetAwayReg(), 32, 63);
}
}
else
{
// We've already moved to a valid place to work on
return reg.GetReg();
}
}
break;
case REG_NOTLOADED: // Register isn't loaded at /all/
{
// This is kind of annoying, we shouldn't have gotten here
// This can happen with instructions that use multiple registers(eg lmw)
// The PPCAnalyst needs to be modified to handle these cases
_dbg_assert_msg_(DYNA_REC, false, "Hit REG_NOTLOADED type oparg. Fix the PPCAnalyst");
ARM64Reg host_reg = GetReg();
reg.LoadToReg(host_reg);
m_emit->LDR(INDEX_UNSIGNED, host_reg, X29, PPCSTATE_OFF(gpr[preg]));
return host_reg;
}
break;
default:
_dbg_assert_msg_(DYNA_REC, false, "Invalid OpArg Type!");
break;
}
// We've got an issue if we end up here
return INVALID_REG;
}
void Arm64GPRCache::GetAllocationOrder(void)
{
// Callee saved registers first in hopes that we will keep everything stored there first
const std::vector<ARM64Reg> allocation_order =
{
W28, W27, W26, W25, W24, W23, W22, W21, W20,
W19, W0, W1, W2, W3, W4, W5, W6, W7, W8, W9,
W10, W11, W12, W13, W14, W15, W16, W17, W18,
W30,
};
for (ARM64Reg reg : allocation_order)
m_host_registers.push_back(HostReg(reg));
}
// FPR Cache
void Arm64FPRCache::Flush(FlushMode mode, PPCAnalyst::CodeOp* op)
{
// XXX: Flush our stuff
}
ARM64Reg Arm64FPRCache::R(u32 preg)
{
// XXX: return a host reg holding a guest register
}
void Arm64FPRCache::GetAllocationOrder(void)
{
const std::vector<ARM64Reg> allocation_order =
{
D0, D1, D2, D3, D4, D5, D6, D7, D8, D9, D10,
D11, D12, D13, D14, D15, D16, D17, D18, D19,
D20, D21, D22, D23, D24, D25, D26, D27, D28,
D29, D30, D31,
};
for (ARM64Reg reg : allocation_order)
m_host_registers.push_back(HostReg(reg));
}

View File

@ -0,0 +1,242 @@
// copyright 2014 dolphin emulator project
// licensed under gplv2
// refer to the license.txt file included.
#pragma once
#include <vector>
#include "Common/Arm64Emitter.h"
#include "Core/PowerPC/Gekko.h"
#include "Core/PowerPC/PPCAnalyst.h"
// Dedicated host registers
// X29 = ppcState pointer
using namespace Arm64Gen;
enum RegType
{
REG_NOTLOADED = 0,
REG_REG, // Reg type is register
REG_IMM, // Reg is really a IMM
REG_AWAY, // Reg is away
};
enum RegLocation
{
REG_LOW = 0,
REG_HIGH,
};
enum FlushMode
{
// Flushes all registers, no exceptions
FLUSH_ALL = 0,
// Flushes registers in a conditional branch
// Doesn't wipe the state of the registers from the cache
FLUSH_MAINTAIN_STATE,
// Flushes only the required registers for an interpreter call
FLUSH_INTERPRETER,
};
class OpArg
{
public:
OpArg()
{
m_type = REG_NOTLOADED;
m_reg = INVALID_REG;
m_value = 0;
}
RegType GetType()
{
return m_type;
}
ARM64Reg GetReg()
{
return m_reg;
}
ARM64Reg GetAwayReg()
{
return m_away_reg;
}
RegLocation GetAwayLocation()
{
return m_away_location;
}
u32 GetImm()
{
return m_value;
}
void LoadToReg(ARM64Reg reg)
{
m_type = REG_REG;
m_reg = reg;
}
void LoadToAway(ARM64Reg reg, RegLocation location)
{
m_type = REG_AWAY;
m_reg = INVALID_REG;
m_away_reg = reg;
m_away_location = location;
}
void LoadAwayToReg(ARM64Reg reg)
{
// We are still an away type
// We just are also in another register
m_reg = reg;
}
void LoadToImm(u32 imm)
{
m_type = REG_IMM;
m_value = imm;
}
void Flush()
{
m_type = REG_NOTLOADED;
}
private:
// For REG_REG
RegType m_type; // store type
ARM64Reg m_reg; // host register we are in
// For REG_AWAY
// Host register that we are away in
// This is a 64bit register
ARM64Reg m_away_reg;
RegLocation m_away_location;
// For REG_IMM
u32 m_value; // IMM value
};
class HostReg
{
public:
HostReg() : m_reg(INVALID_REG), m_locked(false) {}
HostReg(ARM64Reg reg) : m_reg(reg), m_locked(false) {}
bool IsLocked(void) { return m_locked; }
void Lock(void) { m_locked = true; }
void Unlock(void) { m_locked = false; }
ARM64Reg GetReg(void) { return m_reg; }
bool operator==(const ARM64Reg& reg)
{
return reg == m_reg;
}
private:
ARM64Reg m_reg;
bool m_locked;
};
class Arm64RegCache
{
public:
Arm64RegCache(void) : m_emit(nullptr), m_reg_stats(nullptr) {};
virtual ~Arm64RegCache() {};
void Init(ARM64XEmitter *emitter);
virtual void Start(PPCAnalyst::BlockRegStats &stats) {}
// Flushes the register cache in different ways depending on the mode
virtual void Flush(FlushMode mode, PPCAnalyst::CodeOp* op) = 0;
// Returns a guest register inside of a host register
// Will dump an immediate to the host register as well
virtual ARM64Reg R(u32 reg) = 0;
// Returns a temporary register for use
// Requires unlocking after done
ARM64Reg GetReg(void);
// Locks a register so a cache cannot use it
// Useful for function calls
template<typename T = ARM64Reg, typename... Args>
void Lock(Args... args)
{
for (T reg : {args...})
{
LockRegister(reg);
}
}
// Unlocks a locked register
// Unlocks registers locked with both GetReg and LockRegister
template<typename T = ARM64Reg, typename... Args>
void Unlock(Args... args)
{
for (T reg : {args...})
{
UnlockRegister(reg);
}
}
protected:
// Get the order of the host registers
virtual void GetAllocationOrder(void) = 0;
// Lock a register
void LockRegister(ARM64Reg host_reg);
// Unlock a register
void UnlockRegister(ARM64Reg host_reg);
// Code emitter
ARM64XEmitter *m_emit;
// Host side registers that hold the host registers in order of use
std::vector<HostReg> m_host_registers;
// Register stats for the current block
PPCAnalyst::BlockRegStats *m_reg_stats;
};
class Arm64GPRCache : public Arm64RegCache
{
public:
~Arm64GPRCache() {}
void Start(PPCAnalyst::BlockRegStats &stats);
// Flushes the register cache in different ways depending on the mode
void Flush(FlushMode mode, PPCAnalyst::CodeOp* op = nullptr);
// Returns a guest register inside of a host register
// Will dump an immediate to the host register as well
ARM64Reg R(u32 preg);
protected:
// Get the order of the host registers
void GetAllocationOrder(void);
// Our guest GPRs
// PowerPC has 32 GPRs
OpArg m_guest_registers[32];
private:
bool IsCalleeSaved(ARM64Reg reg);
};
class Arm64FPRCache : public Arm64RegCache
{
public:
~Arm64FPRCache() {}
// Flushes the register cache in different ways depending on the mode
void Flush(FlushMode mode, PPCAnalyst::CodeOp* op = nullptr);
// Returns a guest register inside of a host register
// Will dump an immediate to the host register as well
ARM64Reg R(u32 preg);
protected:
// Get the order of the host registers
void GetAllocationOrder(void);
// Our guest FPRs
// Gekko has 32 paired registers(32x2)
OpArg m_guest_registers[32][2];
};

View File

@ -0,0 +1,60 @@
// Copyright 2014 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#include "Common/Arm64Emitter.h"
#include "Common/Common.h"
#include "Core/Core.h"
#include "Core/CoreTiming.h"
#include "Core/PowerPC/PowerPC.h"
#include "Core/PowerPC/PPCTables.h"
#include "Core/PowerPC/JitArm64/Jit.h"
#include "Core/PowerPC/JitArm64/JitAsm.h"
FixupBranch JitArm64::JumpIfCRFieldBit(int field, int bit, bool jump_if_set)
{
ARM64Reg WA = gpr.GetReg();
ARM64Reg XA = EncodeRegTo64(WA);
FixupBranch branch;
switch (bit)
{
case CR_SO_BIT: // check bit 61 set
LDR(INDEX_UNSIGNED, XA, X29, PPCSTATE_OFF(cr_val[field]));
branch = jump_if_set ? TBNZ(XA, 61) : TBZ(XA, 61);
break;
case CR_EQ_BIT: // check bits 31-0 == 0
LDR(INDEX_UNSIGNED, WA, X29, PPCSTATE_OFF(cr_val[field]));
branch = jump_if_set ? CBZ(WA) : CBNZ(WA);
break;
case CR_GT_BIT: // check val > 0
LDR(INDEX_UNSIGNED, XA, X29, PPCSTATE_OFF(cr_val[field]));
CMP(XA, SP);
branch = B(jump_if_set ? CC_GT : CC_LE);
break;
case CR_LT_BIT: // check bit 62 set
LDR(INDEX_UNSIGNED, XA, X29, PPCSTATE_OFF(cr_val[field]));
branch = jump_if_set ? TBNZ(XA, 62) : TBZ(XA, 62);
break;
default:
_assert_msg_(DYNA_REC, false, "Invalid CR bit");
}
gpr.Unlock(WA);
return branch;
}
void JitArm64::mtmsr(UGeckoInstruction inst)
{
INSTRUCTION_START
// Don't interpret this, if we do we get thrown out
//JITDISABLE(bJITSystemRegistersOff)
STR(INDEX_UNSIGNED, gpr.R(inst.RS), X29, PPCSTATE_OFF(msr));
gpr.Flush(FlushMode::FLUSH_ALL);
fpr.Flush(FlushMode::FLUSH_ALL);
WriteExit(js.compilerPC + 4);
}

View File

@ -0,0 +1,493 @@
// Copyright 2014 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#include "Core/PowerPC/JitInterface.h"
#include "Core/PowerPC/JitArm64/Jit.h"
#include "Core/PowerPC/JitArm64/JitArm64_Tables.h"
// Should be moved in to the Jit class
typedef void (JitArm64::*_Instruction) (UGeckoInstruction instCode);
static _Instruction dynaOpTable[64];
static _Instruction dynaOpTable4[1024];
static _Instruction dynaOpTable19[1024];
static _Instruction dynaOpTable31[1024];
static _Instruction dynaOpTable59[32];
static _Instruction dynaOpTable63[1024];
void JitArm64::DynaRunTable4(UGeckoInstruction inst) {(this->*dynaOpTable4 [inst.SUBOP10])(inst);}
void JitArm64::DynaRunTable19(UGeckoInstruction inst) {(this->*dynaOpTable19[inst.SUBOP10])(inst);}
void JitArm64::DynaRunTable31(UGeckoInstruction inst) {(this->*dynaOpTable31[inst.SUBOP10])(inst);}
void JitArm64::DynaRunTable59(UGeckoInstruction inst) {(this->*dynaOpTable59[inst.SUBOP5 ])(inst);}
void JitArm64::DynaRunTable63(UGeckoInstruction inst) {(this->*dynaOpTable63[inst.SUBOP10])(inst);}
struct GekkoOPTemplate
{
int opcode;
_Instruction Inst;
//GekkoOPInfo opinfo; // Doesn't need opinfo, Interpreter fills it out
};
static GekkoOPTemplate primarytable[] =
{
{4, &JitArm64::DynaRunTable4}, //"RunTable4", OPTYPE_SUBTABLE | (4<<24), 0}},
{19, &JitArm64::DynaRunTable19}, //"RunTable19", OPTYPE_SUBTABLE | (19<<24), 0}},
{31, &JitArm64::DynaRunTable31}, //"RunTable31", OPTYPE_SUBTABLE | (31<<24), 0}},
{59, &JitArm64::DynaRunTable59}, //"RunTable59", OPTYPE_SUBTABLE | (59<<24), 0}},
{63, &JitArm64::DynaRunTable63}, //"RunTable63", OPTYPE_SUBTABLE | (63<<24), 0}},
{16, &JitArm64::bcx}, //"bcx", OPTYPE_SYSTEM, FL_ENDBLOCK}},
{18, &JitArm64::bx}, //"bx", OPTYPE_SYSTEM, FL_ENDBLOCK}},
{1, &JitArm64::HLEFunction}, //"HLEFunction", OPTYPE_SYSTEM, FL_ENDBLOCK}},
{2, &JitArm64::FallBackToInterpreter}, //"DynaBlock", OPTYPE_SYSTEM, 0}},
{3, &JitArm64::Break}, //"twi", OPTYPE_SYSTEM, FL_ENDBLOCK}},
{17, &JitArm64::sc}, //"sc", OPTYPE_SYSTEM, FL_ENDBLOCK, 1}},
{7, &JitArm64::FallBackToInterpreter}, //"mulli", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_RC_BIT, 2}},
{8, &JitArm64::FallBackToInterpreter}, //"subfic", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_SET_CA}},
{10, &JitArm64::FallBackToInterpreter}, //"cmpli", OPTYPE_INTEGER, FL_IN_A | FL_SET_CRn}},
{11, &JitArm64::FallBackToInterpreter}, //"cmpi", OPTYPE_INTEGER, FL_IN_A | FL_SET_CRn}},
{12, &JitArm64::FallBackToInterpreter}, //"addic", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_SET_CA}},
{13, &JitArm64::FallBackToInterpreter}, //"addic_rc", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_SET_CR0}},
{14, &JitArm64::FallBackToInterpreter}, //"addi", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A0}},
{15, &JitArm64::FallBackToInterpreter}, //"addis", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A0}},
{20, &JitArm64::FallBackToInterpreter}, //"rlwimix", OPTYPE_INTEGER, FL_OUT_A | FL_IN_A | FL_IN_S | FL_RC_BIT}},
{21, &JitArm64::FallBackToInterpreter}, //"rlwinmx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_RC_BIT}},
{23, &JitArm64::FallBackToInterpreter}, //"rlwnmx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_IN_B | FL_RC_BIT}},
{24, &JitArm64::FallBackToInterpreter}, //"ori", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S}},
{25, &JitArm64::FallBackToInterpreter}, //"oris", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S}},
{26, &JitArm64::FallBackToInterpreter}, //"xori", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S}},
{27, &JitArm64::FallBackToInterpreter}, //"xoris", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S}},
{28, &JitArm64::FallBackToInterpreter}, //"andi_rc", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_SET_CR0}},
{29, &JitArm64::FallBackToInterpreter}, //"andis_rc", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_SET_CR0}},
{32, &JitArm64::FallBackToInterpreter}, //"lwz", OPTYPE_LOAD, FL_OUT_D | FL_IN_A}},
{33, &JitArm64::FallBackToInterpreter}, //"lwzu", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A}},
{34, &JitArm64::FallBackToInterpreter}, //"lbz", OPTYPE_LOAD, FL_OUT_D | FL_IN_A}},
{35, &JitArm64::FallBackToInterpreter}, //"lbzu", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A}},
{40, &JitArm64::FallBackToInterpreter}, //"lhz", OPTYPE_LOAD, FL_OUT_D | FL_IN_A}},
{41, &JitArm64::FallBackToInterpreter}, //"lhzu", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A}},
{42, &JitArm64::FallBackToInterpreter}, //"lha", OPTYPE_LOAD, FL_OUT_D | FL_IN_A}},
{43, &JitArm64::FallBackToInterpreter}, //"lhau", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A}},
{44, &JitArm64::FallBackToInterpreter}, //"sth", OPTYPE_STORE, FL_IN_A | FL_IN_S}},
{45, &JitArm64::FallBackToInterpreter}, //"sthu", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_S}},
{36, &JitArm64::FallBackToInterpreter}, //"stw", OPTYPE_STORE, FL_IN_A | FL_IN_S}},
{37, &JitArm64::FallBackToInterpreter}, //"stwu", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_S}},
{38, &JitArm64::FallBackToInterpreter}, //"stb", OPTYPE_STORE, FL_IN_A | FL_IN_S}},
{39, &JitArm64::FallBackToInterpreter}, //"stbu", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_S}},
{46, &JitArm64::FallBackToInterpreter}, //"lmw", OPTYPE_SYSTEM, FL_EVIL, 10}},
{47, &JitArm64::FallBackToInterpreter}, //"stmw", OPTYPE_SYSTEM, FL_EVIL, 10}},
{48, &JitArm64::FallBackToInterpreter}, //"lfs", OPTYPE_LOADFP, FL_IN_A}},
{49, &JitArm64::FallBackToInterpreter}, //"lfsu", OPTYPE_LOADFP, FL_OUT_A | FL_IN_A}},
{50, &JitArm64::FallBackToInterpreter}, //"lfd", OPTYPE_LOADFP, FL_IN_A}},
{51, &JitArm64::FallBackToInterpreter}, //"lfdu", OPTYPE_LOADFP, FL_OUT_A | FL_IN_A}},
{52, &JitArm64::FallBackToInterpreter}, //"stfs", OPTYPE_STOREFP, FL_IN_A}},
{53, &JitArm64::FallBackToInterpreter}, //"stfsu", OPTYPE_STOREFP, FL_OUT_A | FL_IN_A}},
{54, &JitArm64::FallBackToInterpreter}, //"stfd", OPTYPE_STOREFP, FL_IN_A}},
{55, &JitArm64::FallBackToInterpreter}, //"stfdu", OPTYPE_STOREFP, FL_OUT_A | FL_IN_A}},
{56, &JitArm64::FallBackToInterpreter}, //"psq_l", OPTYPE_PS, FL_IN_A}},
{57, &JitArm64::FallBackToInterpreter}, //"psq_lu", OPTYPE_PS, FL_OUT_A | FL_IN_A}},
{60, &JitArm64::FallBackToInterpreter}, //"psq_st", OPTYPE_PS, FL_IN_A}},
{61, &JitArm64::FallBackToInterpreter}, //"psq_stu", OPTYPE_PS, FL_OUT_A | FL_IN_A}},
//missing: 0, 5, 6, 9, 22, 30, 62, 58
{0, &JitArm64::FallBackToInterpreter}, //"unknown_instruction", OPTYPE_UNKNOWN, 0}},
{5, &JitArm64::FallBackToInterpreter}, //"unknown_instruction", OPTYPE_UNKNOWN, 0}},
{6, &JitArm64::FallBackToInterpreter}, //"unknown_instruction", OPTYPE_UNKNOWN, 0}},
{9, &JitArm64::FallBackToInterpreter}, //"unknown_instruction", OPTYPE_UNKNOWN, 0}},
{22, &JitArm64::FallBackToInterpreter}, //"unknown_instruction", OPTYPE_UNKNOWN, 0}},
{30, &JitArm64::FallBackToInterpreter}, //"unknown_instruction", OPTYPE_UNKNOWN, 0}},
{62, &JitArm64::FallBackToInterpreter}, //"unknown_instruction", OPTYPE_UNKNOWN, 0}},
{58, &JitArm64::FallBackToInterpreter}, //"unknown_instruction", OPTYPE_UNKNOWN, 0}},
};
static GekkoOPTemplate table4[] =
{ //SUBOP10
{0, &JitArm64::FallBackToInterpreter}, //"ps_cmpu0", OPTYPE_PS, FL_SET_CRn}},
{32, &JitArm64::FallBackToInterpreter}, //"ps_cmpo0", OPTYPE_PS, FL_SET_CRn}},
{40, &JitArm64::FallBackToInterpreter}, //"ps_neg", OPTYPE_PS, FL_RC_BIT}},
{136, &JitArm64::FallBackToInterpreter}, //"ps_nabs", OPTYPE_PS, FL_RC_BIT}},
{264, &JitArm64::FallBackToInterpreter}, //"ps_abs", OPTYPE_PS, FL_RC_BIT}},
{64, &JitArm64::FallBackToInterpreter}, //"ps_cmpu1", OPTYPE_PS, FL_RC_BIT}},
{72, &JitArm64::FallBackToInterpreter}, //"ps_mr", OPTYPE_PS, FL_RC_BIT}},
{96, &JitArm64::FallBackToInterpreter}, //"ps_cmpo1", OPTYPE_PS, FL_RC_BIT}},
{528, &JitArm64::FallBackToInterpreter}, //"ps_merge00", OPTYPE_PS, FL_RC_BIT}},
{560, &JitArm64::FallBackToInterpreter}, //"ps_merge01", OPTYPE_PS, FL_RC_BIT}},
{592, &JitArm64::FallBackToInterpreter}, //"ps_merge10", OPTYPE_PS, FL_RC_BIT}},
{624, &JitArm64::FallBackToInterpreter}, //"ps_merge11", OPTYPE_PS, FL_RC_BIT}},
{1014, &JitArm64::FallBackToInterpreter}, //"dcbz_l", OPTYPE_SYSTEM, 0}},
};
static GekkoOPTemplate table4_2[] =
{
{10, &JitArm64::FallBackToInterpreter}, //"ps_sum0", OPTYPE_PS, 0}},
{11, &JitArm64::FallBackToInterpreter}, //"ps_sum1", OPTYPE_PS, 0}},
{12, &JitArm64::FallBackToInterpreter}, //"ps_muls0", OPTYPE_PS, 0}},
{13, &JitArm64::FallBackToInterpreter}, //"ps_muls1", OPTYPE_PS, 0}},
{14, &JitArm64::FallBackToInterpreter}, //"ps_madds0", OPTYPE_PS, 0}},
{15, &JitArm64::FallBackToInterpreter}, //"ps_madds1", OPTYPE_PS, 0}},
{18, &JitArm64::FallBackToInterpreter}, //"ps_div", OPTYPE_PS, 0, 16}},
{20, &JitArm64::FallBackToInterpreter}, //"ps_sub", OPTYPE_PS, 0}},
{21, &JitArm64::FallBackToInterpreter}, //"ps_add", OPTYPE_PS, 0}},
{23, &JitArm64::FallBackToInterpreter}, //"ps_sel", OPTYPE_PS, 0}},
{24, &JitArm64::FallBackToInterpreter}, //"ps_res", OPTYPE_PS, 0}},
{25, &JitArm64::FallBackToInterpreter}, //"ps_mul", OPTYPE_PS, 0}},
{26, &JitArm64::FallBackToInterpreter}, //"ps_rsqrte", OPTYPE_PS, 0, 1}},
{28, &JitArm64::FallBackToInterpreter}, //"ps_msub", OPTYPE_PS, 0}},
{29, &JitArm64::FallBackToInterpreter}, //"ps_madd", OPTYPE_PS, 0}},
{30, &JitArm64::FallBackToInterpreter}, //"ps_nmsub", OPTYPE_PS, 0}},
{31, &JitArm64::FallBackToInterpreter}, //"ps_nmadd", OPTYPE_PS, 0}},
};
static GekkoOPTemplate table4_3[] =
{
{6, &JitArm64::FallBackToInterpreter}, //"psq_lx", OPTYPE_PS, 0}},
{7, &JitArm64::FallBackToInterpreter}, //"psq_stx", OPTYPE_PS, 0}},
{38, &JitArm64::FallBackToInterpreter}, //"psq_lux", OPTYPE_PS, 0}},
{39, &JitArm64::FallBackToInterpreter}, //"psq_stux", OPTYPE_PS, 0}},
};
static GekkoOPTemplate table19[] =
{
{528, &JitArm64::bcctrx}, //"bcctrx", OPTYPE_BRANCH, FL_ENDBLOCK}},
{16, &JitArm64::bclrx}, //"bclrx", OPTYPE_BRANCH, FL_ENDBLOCK}},
{257, &JitArm64::FallBackToInterpreter}, //"crand", OPTYPE_CR, FL_EVIL}},
{129, &JitArm64::FallBackToInterpreter}, //"crandc", OPTYPE_CR, FL_EVIL}},
{289, &JitArm64::FallBackToInterpreter}, //"creqv", OPTYPE_CR, FL_EVIL}},
{225, &JitArm64::FallBackToInterpreter}, //"crnand", OPTYPE_CR, FL_EVIL}},
{33, &JitArm64::FallBackToInterpreter}, //"crnor", OPTYPE_CR, FL_EVIL}},
{449, &JitArm64::FallBackToInterpreter}, //"cror", OPTYPE_CR, FL_EVIL}},
{417, &JitArm64::FallBackToInterpreter}, //"crorc", OPTYPE_CR, FL_EVIL}},
{193, &JitArm64::FallBackToInterpreter}, //"crxor", OPTYPE_CR, FL_EVIL}},
{150, &JitArm64::FallBackToInterpreter}, //"isync", OPTYPE_ICACHE, FL_EVIL}},
{0, &JitArm64::FallBackToInterpreter}, //"mcrf", OPTYPE_SYSTEM, FL_EVIL}},
{50, &JitArm64::rfi}, //"rfi", OPTYPE_SYSTEM, FL_ENDBLOCK | FL_CHECKEXCEPTIONS, 1}},
{18, &JitArm64::Break}, //"rfid", OPTYPE_SYSTEM, FL_ENDBLOCK | FL_CHECKEXCEPTIONS}}
};
static GekkoOPTemplate table31[] =
{
{28, &JitArm64::FallBackToInterpreter}, //"andx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}},
{60, &JitArm64::FallBackToInterpreter}, //"andcx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}},
{444, &JitArm64::FallBackToInterpreter}, //"orx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}},
{124, &JitArm64::FallBackToInterpreter}, //"norx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}},
{316, &JitArm64::FallBackToInterpreter}, //"xorx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}},
{412, &JitArm64::FallBackToInterpreter}, //"orcx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}},
{476, &JitArm64::FallBackToInterpreter}, //"nandx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}},
{284, &JitArm64::FallBackToInterpreter}, //"eqvx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}},
{0, &JitArm64::FallBackToInterpreter}, //"cmp", OPTYPE_INTEGER, FL_IN_AB | FL_SET_CRn}},
{32, &JitArm64::FallBackToInterpreter}, //"cmpl", OPTYPE_INTEGER, FL_IN_AB | FL_SET_CRn}},
{26, &JitArm64::FallBackToInterpreter}, //"cntlzwx",OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_RC_BIT}},
{922, &JitArm64::FallBackToInterpreter}, //"extshx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_RC_BIT}},
{954, &JitArm64::FallBackToInterpreter}, //"extsbx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_RC_BIT}},
{536, &JitArm64::FallBackToInterpreter}, //"srwx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_B | FL_IN_S | FL_RC_BIT}},
{792, &JitArm64::FallBackToInterpreter}, //"srawx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_B | FL_IN_S | FL_RC_BIT}},
{824, &JitArm64::FallBackToInterpreter}, //"srawix", OPTYPE_INTEGER, FL_OUT_A | FL_IN_B | FL_IN_S | FL_RC_BIT}},
{24, &JitArm64::FallBackToInterpreter}, //"slwx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_B | FL_IN_S | FL_RC_BIT}},
{54, &JitArm64::FallBackToInterpreter}, //"dcbst", OPTYPE_DCACHE, 0, 4}},
{86, &JitArm64::FallBackToInterpreter}, //"dcbf", OPTYPE_DCACHE, 0, 4}},
{246, &JitArm64::FallBackToInterpreter}, //"dcbtst", OPTYPE_DCACHE, 0, 1}},
{278, &JitArm64::FallBackToInterpreter}, //"dcbt", OPTYPE_DCACHE, 0, 1}},
{470, &JitArm64::FallBackToInterpreter}, //"dcbi", OPTYPE_DCACHE, 0, 4}},
{758, &JitArm64::FallBackToInterpreter}, //"dcba", OPTYPE_DCACHE, 0, 4}},
{1014, &JitArm64::FallBackToInterpreter}, //"dcbz", OPTYPE_DCACHE, 0, 4}},
//load word
{23, &JitArm64::FallBackToInterpreter}, //"lwzx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}},
{55, &JitArm64::FallBackToInterpreter}, //"lwzux", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A | FL_IN_B}},
//load halfword
{279, &JitArm64::FallBackToInterpreter}, //"lhzx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}},
{311, &JitArm64::FallBackToInterpreter}, //"lhzux", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A | FL_IN_B}},
//load halfword signextend
{343, &JitArm64::FallBackToInterpreter}, //"lhax", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}},
{375, &JitArm64::FallBackToInterpreter}, //"lhaux", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A | FL_IN_B}},
//load byte
{87, &JitArm64::FallBackToInterpreter}, //"lbzx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}},
{119, &JitArm64::FallBackToInterpreter}, //"lbzux", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A | FL_IN_B}},
//load byte reverse
{534, &JitArm64::FallBackToInterpreter}, //"lwbrx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}},
{790, &JitArm64::FallBackToInterpreter}, //"lhbrx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}},
// Conditional load/store (Wii SMP)
{150, &JitArm64::FallBackToInterpreter}, //"stwcxd", OPTYPE_STORE, FL_EVIL | FL_SET_CR0}},
{20, &JitArm64::FallBackToInterpreter}, //"lwarx", OPTYPE_LOAD, FL_EVIL | FL_OUT_D | FL_IN_A0B | FL_SET_CR0}},
//load string (interpret these)
{533, &JitArm64::FallBackToInterpreter}, //"lswx", OPTYPE_LOAD, FL_EVIL | FL_IN_A | FL_OUT_D}},
{597, &JitArm64::FallBackToInterpreter}, //"lswi", OPTYPE_LOAD, FL_EVIL | FL_IN_AB | FL_OUT_D}},
//store word
{151, &JitArm64::FallBackToInterpreter}, //"stwx", OPTYPE_STORE, FL_IN_A0 | FL_IN_B}},
{183, &JitArm64::FallBackToInterpreter}, //"stwux", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_B}},
//store halfword
{407, &JitArm64::FallBackToInterpreter}, //"sthx", OPTYPE_STORE, FL_IN_A0 | FL_IN_B}},
{439, &JitArm64::FallBackToInterpreter}, //"sthux", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_B}},
//store byte
{215, &JitArm64::FallBackToInterpreter}, //"stbx", OPTYPE_STORE, FL_IN_A0 | FL_IN_B}},
{247, &JitArm64::FallBackToInterpreter}, //"stbux", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_B}},
//store bytereverse
{662, &JitArm64::FallBackToInterpreter}, //"stwbrx", OPTYPE_STORE, FL_IN_A0 | FL_IN_B}},
{918, &JitArm64::FallBackToInterpreter}, //"sthbrx", OPTYPE_STORE, FL_IN_A | FL_IN_B}},
{661, &JitArm64::FallBackToInterpreter}, //"stswx", OPTYPE_STORE, FL_EVIL}},
{725, &JitArm64::FallBackToInterpreter}, //"stswi", OPTYPE_STORE, FL_EVIL}},
// fp load/store
{535, &JitArm64::FallBackToInterpreter}, //"lfsx", OPTYPE_LOADFP, FL_IN_A0 | FL_IN_B}},
{567, &JitArm64::FallBackToInterpreter}, //"lfsux", OPTYPE_LOADFP, FL_IN_A | FL_IN_B}},
{599, &JitArm64::FallBackToInterpreter}, //"lfdx", OPTYPE_LOADFP, FL_IN_A0 | FL_IN_B}},
{631, &JitArm64::FallBackToInterpreter}, //"lfdux", OPTYPE_LOADFP, FL_IN_A | FL_IN_B}},
{663, &JitArm64::FallBackToInterpreter}, //"stfsx", OPTYPE_STOREFP, FL_IN_A0 | FL_IN_B}},
{695, &JitArm64::FallBackToInterpreter}, //"stfsux", OPTYPE_STOREFP, FL_IN_A | FL_IN_B}},
{727, &JitArm64::FallBackToInterpreter}, //"stfdx", OPTYPE_STOREFP, FL_IN_A0 | FL_IN_B}},
{759, &JitArm64::FallBackToInterpreter}, //"stfdux", OPTYPE_STOREFP, FL_IN_A | FL_IN_B}},
{983, &JitArm64::FallBackToInterpreter}, //"stfiwx", OPTYPE_STOREFP, FL_IN_A0 | FL_IN_B}},
{19, &JitArm64::FallBackToInterpreter}, //"mfcr", OPTYPE_SYSTEM, FL_OUT_D}},
{83, &JitArm64::FallBackToInterpreter}, //"mfmsr", OPTYPE_SYSTEM, FL_OUT_D}},
{144, &JitArm64::FallBackToInterpreter}, //"mtcrf", OPTYPE_SYSTEM, 0}},
{146, &JitArm64::mtmsr}, //"mtmsr", OPTYPE_SYSTEM, FL_ENDBLOCK}},
{210, &JitArm64::FallBackToInterpreter}, //"mtsr", OPTYPE_SYSTEM, 0}},
{242, &JitArm64::FallBackToInterpreter}, //"mtsrin", OPTYPE_SYSTEM, 0}},
{339, &JitArm64::FallBackToInterpreter}, //"mfspr", OPTYPE_SPR, FL_OUT_D}},
{467, &JitArm64::FallBackToInterpreter}, //"mtspr", OPTYPE_SPR, 0, 2}},
{371, &JitArm64::FallBackToInterpreter}, //"mftb", OPTYPE_SYSTEM, FL_OUT_D | FL_TIMER}},
{512, &JitArm64::FallBackToInterpreter}, //"mcrxr", OPTYPE_SYSTEM, 0}},
{595, &JitArm64::FallBackToInterpreter}, //"mfsr", OPTYPE_SYSTEM, FL_OUT_D, 2}},
{659, &JitArm64::FallBackToInterpreter}, //"mfsrin", OPTYPE_SYSTEM, FL_OUT_D, 2}},
{4, &JitArm64::Break}, //"tw", OPTYPE_SYSTEM, FL_ENDBLOCK, 1}},
{598, &JitArm64::FallBackToInterpreter}, //"sync", OPTYPE_SYSTEM, 0, 2}},
{982, &JitArm64::icbi}, //"icbi", OPTYPE_SYSTEM, FL_ENDBLOCK, 3}},
// Unused instructions on GC
{310, &JitArm64::FallBackToInterpreter}, //"eciwx", OPTYPE_INTEGER, FL_RC_BIT}},
{438, &JitArm64::FallBackToInterpreter}, //"ecowx", OPTYPE_INTEGER, FL_RC_BIT}},
{854, &JitArm64::FallBackToInterpreter}, //"eieio", OPTYPE_INTEGER, FL_RC_BIT}},
{306, &JitArm64::FallBackToInterpreter}, //"tlbie", OPTYPE_SYSTEM, 0}},
{370, &JitArm64::FallBackToInterpreter}, //"tlbia", OPTYPE_SYSTEM, 0}},
{566, &JitArm64::FallBackToInterpreter}, //"tlbsync", OPTYPE_SYSTEM, 0}},
};
static GekkoOPTemplate table31_2[] =
{
{266, &JitArm64::FallBackToInterpreter}, //"addx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT}},
{778, &JitArm64::FallBackToInterpreter}, //"addx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT}},
{10, &JitArm64::FallBackToInterpreter}, //"addcx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_SET_CA | FL_RC_BIT}},
{522, &JitArm64::FallBackToInterpreter}, //"addcox", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_SET_CA | FL_RC_BIT}},
{138, &JitArm64::FallBackToInterpreter}, //"addex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT}},
{650, &JitArm64::FallBackToInterpreter}, //"addeox", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT}},
{234, &JitArm64::FallBackToInterpreter}, //"addmex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT}},
{202, &JitArm64::FallBackToInterpreter}, //"addzex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT}},
{491, &JitArm64::FallBackToInterpreter}, //"divwx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 39}},
{1003, &JitArm64::FallBackToInterpreter}, //"divwox", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 39}},
{459, &JitArm64::FallBackToInterpreter}, //"divwux", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 39}},
{971, &JitArm64::FallBackToInterpreter}, //"divwuox", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 39}},
{75, &JitArm64::FallBackToInterpreter}, //"mulhwx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 4}},
{11, &JitArm64::FallBackToInterpreter}, //"mulhwux", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 4}},
{235, &JitArm64::FallBackToInterpreter}, //"mullwx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 4}},
{747, &JitArm64::FallBackToInterpreter}, //"mullwox", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 4}},
{104, &JitArm64::FallBackToInterpreter}, //"negx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT}},
{40, &JitArm64::FallBackToInterpreter}, //"subfx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT}},
{552, &JitArm64::FallBackToInterpreter}, //"subox", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT}},
{8, &JitArm64::FallBackToInterpreter}, //"subfcx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_SET_CA | FL_RC_BIT}},
{520, &JitArm64::FallBackToInterpreter}, //"subfcox", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_SET_CA | FL_RC_BIT}},
{136, &JitArm64::FallBackToInterpreter}, //"subfex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT}},
{232, &JitArm64::FallBackToInterpreter}, //"subfmex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT}},
{200, &JitArm64::FallBackToInterpreter}, //"subfzex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT}},
};
static GekkoOPTemplate table59[] =
{
{18, &JitArm64::FallBackToInterpreter}, //{"fdivsx", OPTYPE_FPU, FL_RC_BIT_F, 16}},
{20, &JitArm64::FallBackToInterpreter}, //"fsubsx", OPTYPE_FPU, FL_RC_BIT_F}},
{21, &JitArm64::FallBackToInterpreter}, //"faddsx", OPTYPE_FPU, FL_RC_BIT_F}},
// {22, &JitArm64::FallBackToInterpreter}, //"fsqrtsx", OPTYPE_FPU, FL_RC_BIT_F}},
{24, &JitArm64::FallBackToInterpreter}, //"fresx", OPTYPE_FPU, FL_RC_BIT_F}},
{25, &JitArm64::FallBackToInterpreter}, //"fmulsx", OPTYPE_FPU, FL_RC_BIT_F}},
{28, &JitArm64::FallBackToInterpreter}, //"fmsubsx", OPTYPE_FPU, FL_RC_BIT_F}},
{29, &JitArm64::FallBackToInterpreter}, //"fmaddsx", OPTYPE_FPU, FL_RC_BIT_F}},
{30, &JitArm64::FallBackToInterpreter}, //"fnmsubsx", OPTYPE_FPU, FL_RC_BIT_F}},
{31, &JitArm64::FallBackToInterpreter}, //"fnmaddsx", OPTYPE_FPU, FL_RC_BIT_F}},
};
static GekkoOPTemplate table63[] =
{
{264, &JitArm64::FallBackToInterpreter}, //"fabsx", OPTYPE_FPU, FL_RC_BIT_F}},
{32, &JitArm64::FallBackToInterpreter}, //"fcmpo", OPTYPE_FPU, FL_RC_BIT_F}},
{0, &JitArm64::FallBackToInterpreter}, //"fcmpu", OPTYPE_FPU, FL_RC_BIT_F}},
{14, &JitArm64::FallBackToInterpreter}, //"fctiwx", OPTYPE_FPU, FL_RC_BIT_F}},
{15, &JitArm64::FallBackToInterpreter}, //"fctiwzx", OPTYPE_FPU, FL_RC_BIT_F}},
{72, &JitArm64::FallBackToInterpreter}, //"fmrx", OPTYPE_FPU, FL_RC_BIT_F}},
{136, &JitArm64::FallBackToInterpreter}, //"fnabsx", OPTYPE_FPU, FL_RC_BIT_F}},
{40, &JitArm64::FallBackToInterpreter}, //"fnegx", OPTYPE_FPU, FL_RC_BIT_F}},
{12, &JitArm64::FallBackToInterpreter}, //"frspx", OPTYPE_FPU, FL_RC_BIT_F}},
{64, &JitArm64::FallBackToInterpreter}, //"mcrfs", OPTYPE_SYSTEMFP, 0}},
{583, &JitArm64::FallBackToInterpreter}, //"mffsx", OPTYPE_SYSTEMFP, 0}},
{70, &JitArm64::FallBackToInterpreter}, //"mtfsb0x", OPTYPE_SYSTEMFP, 0, 2}},
{38, &JitArm64::FallBackToInterpreter}, //"mtfsb1x", OPTYPE_SYSTEMFP, 0, 2}},
{134, &JitArm64::FallBackToInterpreter}, //"mtfsfix", OPTYPE_SYSTEMFP, 0, 2}},
{711, &JitArm64::FallBackToInterpreter}, //"mtfsfx", OPTYPE_SYSTEMFP, 0, 2}},
};
static GekkoOPTemplate table63_2[] =
{
{18, &JitArm64::FallBackToInterpreter}, //"fdivx", OPTYPE_FPU, FL_RC_BIT_F, 30}},
{20, &JitArm64::FallBackToInterpreter}, //"fsubx", OPTYPE_FPU, FL_RC_BIT_F}},
{21, &JitArm64::FallBackToInterpreter}, //"faddx", OPTYPE_FPU, FL_RC_BIT_F}},
{22, &JitArm64::FallBackToInterpreter}, //"fsqrtx", OPTYPE_FPU, FL_RC_BIT_F}},
{23, &JitArm64::FallBackToInterpreter}, //"fselx", OPTYPE_FPU, FL_RC_BIT_F}},
{25, &JitArm64::FallBackToInterpreter}, //"fmulx", OPTYPE_FPU, FL_RC_BIT_F}},
{26, &JitArm64::FallBackToInterpreter}, //"frsqrtex", OPTYPE_FPU, FL_RC_BIT_F}},
{28, &JitArm64::FallBackToInterpreter}, //"fmsubx", OPTYPE_FPU, FL_RC_BIT_F}},
{29, &JitArm64::FallBackToInterpreter}, //"fmaddx", OPTYPE_FPU, FL_RC_BIT_F}},
{30, &JitArm64::FallBackToInterpreter}, //"fnmsubx", OPTYPE_FPU, FL_RC_BIT_F}},
{31, &JitArm64::FallBackToInterpreter}, //"fnmaddx", OPTYPE_FPU, FL_RC_BIT_F}},
};
namespace JitArm64Tables
{
void CompileInstruction(PPCAnalyst::CodeOp & op)
{
JitArm64 *jitarm = (JitArm64 *)jit;
(jitarm->*dynaOpTable[op.inst.OPCD])(op.inst);
GekkoOPInfo *info = op.opinfo;
if (info)
{
#ifdef OPLOG
if (!strcmp(info->opname, OP_TO_LOG)){ ///"mcrfs"
rsplocations.push_back(jit.js.compilerPC);
}
#endif
info->compileCount++;
info->lastUse = jit->js.compilerPC;
}
}
void InitTables()
{
// once initialized, tables are read-only
static bool initialized = false;
if (initialized)
return;
//clear
for (int i = 0; i < 32; i++)
{
dynaOpTable59[i] = &JitArm64::unknown_instruction;
}
for (int i = 0; i < 1024; i++)
{
dynaOpTable4 [i] = &JitArm64::unknown_instruction;
dynaOpTable19[i] = &JitArm64::unknown_instruction;
dynaOpTable31[i] = &JitArm64::unknown_instruction;
dynaOpTable63[i] = &JitArm64::unknown_instruction;
}
for (int i = 0; i < (int)(sizeof(primarytable) / sizeof(GekkoOPTemplate)); i++)
{
dynaOpTable[primarytable[i].opcode] = primarytable[i].Inst;
}
for (int i = 0; i < 32; i++)
{
int fill = i << 5;
for (int j = 0; j < (int)(sizeof(table4_2) / sizeof(GekkoOPTemplate)); j++)
{
int op = fill+table4_2[j].opcode;
dynaOpTable4[op] = table4_2[j].Inst;
}
}
for (int i = 0; i < 16; i++)
{
int fill = i << 6;
for (int j = 0; j < (int)(sizeof(table4_3) / sizeof(GekkoOPTemplate)); j++)
{
int op = fill+table4_3[j].opcode;
dynaOpTable4[op] = table4_3[j].Inst;
}
}
for (int i = 0; i < (int)(sizeof(table4) / sizeof(GekkoOPTemplate)); i++)
{
int op = table4[i].opcode;
dynaOpTable4[op] = table4[i].Inst;
}
for (int i = 0; i < (int)(sizeof(table31) / sizeof(GekkoOPTemplate)); i++)
{
int op = table31[i].opcode;
dynaOpTable31[op] = table31[i].Inst;
}
for (int i = 0; i < 1; i++)
{
int fill = i << 9;
for (int j = 0; j < (int)(sizeof(table31_2) / sizeof(GekkoOPTemplate)); j++)
{
int op = fill + table31_2[j].opcode;
dynaOpTable31[op] = table31_2[j].Inst;
}
}
for (int i = 0; i < (int)(sizeof(table19) / sizeof(GekkoOPTemplate)); i++)
{
int op = table19[i].opcode;
dynaOpTable19[op] = table19[i].Inst;
}
for (int i = 0; i < (int)(sizeof(table59) / sizeof(GekkoOPTemplate)); i++)
{
int op = table59[i].opcode;
dynaOpTable59[op] = table59[i].Inst;
}
for (int i = 0; i < (int)(sizeof(table63) / sizeof(GekkoOPTemplate)); i++)
{
int op = table63[i].opcode;
dynaOpTable63[op] = table63[i].Inst;
}
for (int i = 0; i < 32; i++)
{
int fill = i << 5;
for (int j = 0; j < (int)(sizeof(table63_2) / sizeof(GekkoOPTemplate)); j++)
{
int op = fill + table63_2[j].opcode;
dynaOpTable63[op] = table63_2[j].Inst;
}
}
initialized = true;
}
} // namespace

View File

@ -0,0 +1,14 @@
// Copyright 2014 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#pragma once
#include "Core/PowerPC/Gekko.h"
#include "Core/PowerPC/PPCTables.h"
namespace JitArm64Tables
{
void CompileInstruction(PPCAnalyst::CodeOp & op);
void InitTables();
}

View File

@ -0,0 +1,80 @@
// Copyright 2014 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#include "Common/Arm64Emitter.h"
#include "Core/PowerPC/PowerPC.h"
#include "Core/PowerPC/JitArm64/Jit.h"
#include "Core/PowerPC/JitArm64/JitAsm.h"
#include "Core/PowerPC/JitCommon/JitCache.h"
using namespace Arm64Gen;
void JitArm64AsmRoutineManager::Generate()
{
enterCode = GetCodePtr();
MOVI2R(X29, (u64)&PowerPC::ppcState);
dispatcher = GetCodePtr();
printf("Dispatcher is %p\n", dispatcher);
// Downcount Check
// 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);
dispatcherNoCheck = GetCodePtr();
// This block of code gets the address of the compiled block of code
// It runs though to the compiling portion if it isn't found
LDR(INDEX_UNSIGNED, W28, X29, PPCSTATE_OFF(pc)); // Load the current PC into W28
BFM(W28, WSP, 3, 2); // Wipe the top 3 bits. Same as PC & JIT_ICACHE_MASK
MOVI2R(X27, (u64)jit->GetBlockCache()->iCache);
LDR(W27, X27, X28);
FixupBranch JitBlock = TBNZ(W27, 7); // Test the 7th bit
// Success, it is our Jitblock.
MOVI2R(X30, (u64)jit->GetBlockCache()->GetCodePointers());
UBFM(X27, X27, 61, 60); // Same as X27 << 3
LDR(X30, X30, X27); // Load the block address in to R14
BR(X30);
// No need to jump anywhere after here, the block will go back to dispatcher start
SetJumpTarget(JitBlock);
MOVI2R(X30, (u64)&Jit);
BLR(X30);
B(dispatcherNoCheck);
SetJumpTarget(bail);
doTiming = GetCodePtr();
MOVI2R(X30, (u64)&CoreTiming::Advance);
BLR(X30);
// Does exception checking
LDR(INDEX_UNSIGNED, W0, X29, PPCSTATE_OFF(pc));
STR(INDEX_UNSIGNED, W0, X29, PPCSTATE_OFF(npc));
MOVI2R(X30, (u64)&PowerPC::CheckExceptions);
BLR(X30);
LDR(INDEX_UNSIGNED, W0, X29, PPCSTATE_OFF(npc));
STR(INDEX_UNSIGNED, W0, X29, PPCSTATE_OFF(pc));
// Check the state pointer to see if we are exiting
// Gets checked on every exception check
MOVI2R(W0, (u64)PowerPC::GetStatePtr());
LDR(INDEX_UNSIGNED, W0, W0, 0);
FixupBranch Exit = CBNZ(W0);
B(dispatcher);
SetJumpTarget(Exit);
FlushIcache();
}
void JitArm64AsmRoutineManager::GenerateCommon()
{
}

View File

@ -0,0 +1,29 @@
// Copyright 2014 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#pragma once
#include "Common/Arm64Emitter.h"
#include "Core/PowerPC/JitCommon/JitAsmCommon.h"
class JitArm64AsmRoutineManager : public CommonAsmRoutinesBase, public Arm64Gen::ARM64CodeBlock
{
private:
void Generate();
void GenerateCommon();
public:
void Init()
{
AllocCodeSpace(8192);
Generate();
WriteProtect();
}
void Shutdown()
{
FreeCodeSpace();
}
};

View File

@ -29,6 +29,11 @@
#include "Core/PowerPC/JitArm32/JitArm_Tables.h"
#endif
#if _M_ARM_64
#include "Core/PowerPC/JitArm64/Jit.h"
#include "Core/PowerPC/JitArm64/JitArm64_Tables.h"
#endif
static bool bFakeVMEM = false;
bool bMMU = false;
@ -66,6 +71,13 @@ namespace JitInterface
break;
}
#endif
#if _M_ARM_64
case 4:
{
ptr = new JitArm64();
break;
}
#endif
default:
{
PanicAlert("Unrecognizable cpu_core: %d", core);
@ -100,6 +112,13 @@ namespace JitInterface
break;
}
#endif
#if _M_ARM_64
case 4:
{
JitArm64Tables::InitTables();
break;
}
#endif
default:
{
PanicAlert("Unrecognizable cpu_core: %d", core);

View File

@ -69,6 +69,11 @@ struct BlockRegStats
std::min(firstRead[reg], firstWrite[reg]);
}
bool IsUsed(int reg)
{
return (numReads[reg] + numWrites[reg]) > 0;
}
inline void SetInputRegister(int reg, short opindex)
{
if (firstRead[reg] == -1)