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:
parent
f107b5e176
commit
2b06257e16
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
};
|
||||
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
|
@ -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);
|
||||
};
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
|
|
@ -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];
|
||||
};
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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();
|
||||
}
|
|
@ -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()
|
||||
{
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
};
|
||||
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue