ARM Support without GLSL

This commit is contained in:
Ryan Houdek
2013-02-26 13:49:00 -06:00
parent 46adbfa9ed
commit 717b976875
133 changed files with 9048 additions and 948 deletions
+29 -7
View File
@@ -9,7 +9,6 @@ set(SRCS Src/ActionReplay.cpp
Src/DSPEmulator.cpp
Src/GeckoCodeConfig.cpp
Src/GeckoCode.cpp
Src/MemTools.cpp
Src/Movie.cpp
Src/NetPlay.cpp
Src/NetPlayClient.cpp
@@ -153,6 +152,7 @@ set(SRCS Src/ActionReplay.cpp
Src/PowerPC/PPCTables.cpp
Src/PowerPC/Profiler.cpp
Src/PowerPC/SignatureDB.cpp
Src/PowerPC/JitInterface.cpp
Src/PowerPC/Interpreter/Interpreter_Branch.cpp
Src/PowerPC/Interpreter/Interpreter.cpp
Src/PowerPC/Interpreter/Interpreter_FloatingPoint.cpp
@@ -162,6 +162,15 @@ set(SRCS Src/ActionReplay.cpp
Src/PowerPC/Interpreter/Interpreter_Paired.cpp
Src/PowerPC/Interpreter/Interpreter_SystemRegisters.cpp
Src/PowerPC/Interpreter/Interpreter_Tables.cpp
Src/PowerPC/JitCommon/JitAsmCommon.cpp
Src/PowerPC/JitCommon/JitBackpatch.cpp
Src/PowerPC/JitCommon/JitBase.cpp
Src/PowerPC/JitCommon/JitCache.cpp
Src/PowerPC/JitCommon/Jit_Util.cpp)
if(NOT _M_GENERIC)
set(SRCS ${SRCS}
Src/x64MemTools.cpp
Src/PowerPC/Jit64IL/IR.cpp
Src/PowerPC/Jit64IL/IR_X86.cpp
Src/PowerPC/Jit64IL/JitILAsm.cpp
@@ -186,12 +195,25 @@ set(SRCS Src/ActionReplay.cpp
Src/PowerPC/Jit64/Jit_LoadStorePaired.cpp
Src/PowerPC/Jit64/Jit_Paired.cpp
Src/PowerPC/Jit64/JitRegCache.cpp
Src/PowerPC/Jit64/Jit_SystemRegisters.cpp
Src/PowerPC/JitCommon/JitAsmCommon.cpp
Src/PowerPC/JitCommon/JitBackpatch.cpp
Src/PowerPC/JitCommon/JitBase.cpp
Src/PowerPC/JitCommon/JitCache.cpp
Src/PowerPC/JitCommon/Jit_Util.cpp)
Src/PowerPC/Jit64/Jit_SystemRegisters.cpp)
endif()
if(_M_ARM)
set(SRCS ${SRCS}
Src/ArmMemTools.cpp
Src/PowerPC/JitArm32/Jit.cpp
Src/PowerPC/JitArm32/JitAsm.cpp
Src/PowerPC/JitArm32/JitArm_BackPatch.cpp
Src/PowerPC/JitArm32/JitArm_Tables.cpp
Src/PowerPC/JitArm32/JitArmCache.cpp
Src/PowerPC/JitArm32/JitRegCache.cpp
Src/PowerPC/JitArm32/JitFPRCache.cpp
Src/PowerPC/JitArm32/JitArm_Branch.cpp
Src/PowerPC/JitArm32/JitArm_Integer.cpp
Src/PowerPC/JitArm32/JitArm_LoadStore.cpp
Src/PowerPC/JitArm32/JitArm_FloatingPoint.cpp
Src/PowerPC/JitArm32/JitArm_SystemRegisters.cpp
Src/PowerPC/JitArm32/JitArm_LoadStoreFloating.cpp)
endif()
set(LIBS bdisasm inputcommon videosoftware sfml-network)
+7 -5
View File
@@ -332,7 +332,7 @@
<ClCompile Include="Src\IPC_HLE\WII_IPC_HLE_Device_usb.cpp" />
<ClCompile Include="Src\IPC_HLE\WII_IPC_HLE_Device_usb_kbd.cpp" />
<ClCompile Include="Src\IPC_HLE\WII_IPC_HLE_WiiMote.cpp" />
<ClCompile Include="Src\MemTools.cpp" />
<ClCompile Include="Src\x64MemTools.cpp" />
<ClCompile Include="Src\Movie.cpp" />
<ClCompile Include="Src\NetPlay.cpp" />
<ClCompile Include="Src\NetPlayClient.cpp" />
@@ -378,6 +378,7 @@
<ClCompile Include="Src\PowerPC\JitCommon\JitBase.cpp" />
<ClCompile Include="Src\PowerPC\JitCommon\JitCache.cpp" />
<ClCompile Include="Src\PowerPC\JitCommon\Jit_Util.cpp" />
<ClCompile Include="Src\PowerPC\JitInterface.cpp" />
<ClCompile Include="Src\PowerPC\LUT_frsqrtex.cpp" />
<ClCompile Include="Src\PowerPC\PowerPC.cpp" />
<ClCompile Include="Src\PowerPC\PPCAnalyst.cpp" />
@@ -387,10 +388,10 @@
<ClCompile Include="Src\PowerPC\Profiler.cpp" />
<ClCompile Include="Src\PowerPC\SignatureDB.cpp" />
<ClCompile Include="Src\State.cpp">
<OpenMPSupport Condition="'$(Configuration)|$(Platform)'=='DebugFast|Win32'">false</OpenMPSupport>
<OpenMPSupport Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</OpenMPSupport>
<OpenMPSupport Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</OpenMPSupport>
</ClCompile>
<OpenMPSupport Condition="'$(Configuration)|$(Platform)'=='DebugFast|Win32'">false</OpenMPSupport>
<OpenMPSupport Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</OpenMPSupport>
<OpenMPSupport Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</OpenMPSupport>
</ClCompile>
<ClCompile Include="Src\stdafx.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
@@ -563,6 +564,7 @@
<ClInclude Include="Src\PowerPC\JitCommon\JitBase.h" />
<ClInclude Include="Src\PowerPC\JitCommon\JitCache.h" />
<ClInclude Include="Src\PowerPC\JitCommon\Jit_Util.h" />
<ClInclude Include="Src\PowerPC\JitInterface.h" />
<ClInclude Include="Src\PowerPC\LUT_frsqrtex.h" />
<ClInclude Include="Src\PowerPC\PowerPC.h" />
<ClInclude Include="Src\PowerPC\PPCAnalyst.h" />
+7 -2
View File
@@ -5,9 +5,8 @@
<ClCompile Include="Src\Console.cpp" />
<ClCompile Include="Src\Core.cpp" />
<ClCompile Include="Src\CoreParameter.cpp" />
<ClCompile Include="Src\CoreRerecording.cpp" />
<ClCompile Include="Src\CoreTiming.cpp" />
<ClCompile Include="Src\MemTools.cpp" />
<ClCompile Include="Src\x64MemTools.cpp" />
<ClCompile Include="Src\PatchEngine.cpp" />
<ClCompile Include="Src\DSPEmulator.cpp" />
<ClCompile Include="Src\State.cpp" />
@@ -562,6 +561,9 @@
<ClCompile Include="Src\HW\GCMemcard.cpp">
<Filter>HW %28Flipper/Hollywood%29\GCMemcard</Filter>
</ClCompile>
<ClCompile Include="Src\PowerPC\JitInterface.cpp">
<Filter>PowerPC</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Src\ConfigManager.h" />
@@ -1048,6 +1050,9 @@
<ClInclude Include="Src\HW\GCMemcard.h">
<Filter>HW %28Flipper/Hollywood%29\GCMemcard</Filter>
</ClInclude>
<ClInclude Include="Src\PowerPC\JitInterface.h">
<Filter>PowerPC</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="CMakeLists.txt" />
+103
View File
@@ -0,0 +1,103 @@
// Copyright (C) 2003 Dolphin Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#include <stdio.h>
#include <signal.h>
#ifdef ANDROID
#include <asm/sigcontext.h>
#else
#include <sys/ucontext.h> // Look in here for the context definition.
#include <execinfo.h>
#endif
#include "Common.h"
#include "MemTools.h"
#include "HW/Memmap.h"
#include "PowerPC/PowerPC.h"
#include "PowerPC/JitInterface.h"
#include "PowerPC/JitCommon/JitBase.h"
namespace EMM
{
#ifdef ANDROID
typedef struct sigcontext mcontext_t;
typedef struct ucontext {
uint32_t uc_flags;
struct ucontext* uc_link;
stack_t uc_stack;
mcontext_t uc_mcontext;
// Other fields are not used by Google Breakpad. Don't define them.
} ucontext_t;
#endif
void sigsegv_handler(int signal, siginfo_t *info, void *raw_context)
{
if (signal != SIGSEGV)
{
// We are not interested in other signals - handle it as usual.
return;
}
ucontext_t *context = (ucontext_t *)raw_context;
int sicode = info->si_code;
if (sicode != SEGV_MAPERR && sicode != SEGV_ACCERR)
{
// Huh? Return.
return;
}
// Get all the information we can out of the context.
mcontext_t *ctx = &context->uc_mcontext;
void *fault_memory_ptr = (void*)ctx->arm_r10;
u8 *fault_instruction_ptr = (u8 *)ctx->arm_pc;
if (!JitInterface::IsInCodeSpace(fault_instruction_ptr)) {
// Let's not prevent debugging.
return;
}
u64 bad_address = (u64)fault_memory_ptr;
u64 memspace_bottom = (u64)Memory::base;
if (bad_address < memspace_bottom) {
PanicAlertT("Exception handler - access below memory space. %08llx%08llx",
bad_address >> 32, bad_address);
}
u32 em_address = (u32)(bad_address - memspace_bottom);
int access_type = 0;
CONTEXT fake_ctx;
fake_ctx.reg_pc = ctx->arm_pc;
const u8 *new_rip = jit->BackPatch(fault_instruction_ptr, access_type, em_address, &fake_ctx);
if (new_rip) {
ctx->arm_pc = fake_ctx.reg_pc;
}
}
void InstallExceptionHandler()
{
struct sigaction sa;
sa.sa_handler = 0;
sa.sa_sigaction = &sigsegv_handler;
sa.sa_flags = SA_SIGINFO;
sigemptyset(&sa.sa_mask);
sigaction(SIGSEGV, &sa, NULL);
}
} // namespace
+1 -1
View File
@@ -35,7 +35,7 @@
#define BACKEND_OPENAL "OpenAL"
#define BACKEND_PULSEAUDIO "Pulse"
#define BACKEND_XAUDIO2 "XAudio2"
#define BACKEND_OPENSLES "OpenSLES"
struct SConfig : NonCopyable
{
// Wii Devices
+3 -4
View File
@@ -54,7 +54,6 @@
#include "IPC_HLE/WII_IPC_HLE_Device_usb.h"
#include "PowerPC/PowerPC.h"
#include "PowerPC/JitCommon/JitBase.h"
#include "DSPEmulator.h"
#include "ConfigManager.h"
@@ -140,7 +139,7 @@ void DisplayMessage(const char *message, int time_in_ms)
if (_CoreParameter.bRenderToMain &&
SConfig::GetInstance().m_InterfaceStatusbar)
{
Host_UpdateStatusBar(message);
Host_UpdateStatusBar(message);
}
else
Host_UpdateTitle(message);
@@ -189,7 +188,7 @@ bool IsGPUThread()
return IsCPUThread();
}
}
// This is called from the GUI thread. See the booting call schedule in
// BootManager.cpp
bool Init()
@@ -310,7 +309,7 @@ void CpuThread()
g_video_backend->Video_Prepare();
}
#if defined(_M_X64)
#if defined(_M_X64) || _M_ARM
EMM::InstallExceptionHandler(); // Let's run under memory watch
#endif
+1 -1
View File
@@ -25,7 +25,7 @@
#include "DSPAnalyzer.h"
#include "Jit/DSPJitUtil.h"
#include "x64Emitter.h"
#include "ABI.h"
#include "x64ABI.h"
#define MAX_BLOCK_SIZE 250
#define DSP_IDLE_SKIP_CYCLES 0x1000
@@ -252,7 +252,9 @@ static void gdsp_idma_out(u16 dsp_addr, u32 addr, u32 size)
ERROR_LOG(DSPLLE, "*** idma_out IRAM_DSP (0x%04x) -> RAM (0x%08x) : size (0x%08x)", dsp_addr / 2, addr, size);
}
#if _M_SSE >= 0x301
static const __m128i s_mask = _mm_set_epi32(0x0E0F0C0DL, 0x0A0B0809L, 0x06070405L, 0x02030001L);
#endif
// TODO: These should eat clock cycles.
static void gdsp_ddma_in(u16 dsp_addr, u32 addr, u32 size)
@@ -25,7 +25,7 @@
#include "DSPJitUtil.h"
#endif
#include "x64Emitter.h"
#include "ABI.h"
#include "x64ABI.h"
using namespace Gen;
// CLR $acR
@@ -21,7 +21,7 @@
#include "../DSPAnalyzer.h"
#include "DSPJitUtil.h"
#include "x64Emitter.h"
#include "ABI.h"
#include "x64ABI.h"
using namespace Gen;
@@ -24,7 +24,7 @@
#include "../DSPEmitter.h"
#include "DSPJitUtil.h"
#include "x64Emitter.h"
#include "ABI.h"
#include "x64ABI.h"
using namespace Gen;
// In: RAX: s64 _Value
@@ -18,7 +18,7 @@
#include "../DSPEmitter.h"
#include "DSPJitUtil.h"
#include "x64Emitter.h"
#include "ABI.h"
#include "x64ABI.h"
using namespace Gen;
@@ -22,7 +22,7 @@
#include "../DSPEmitter.h"
#include "DSPJitUtil.h"
#include "x64Emitter.h"
#include "ABI.h"
#include "x64ABI.h"
using namespace Gen;
// SRS @M, $(0x18+S)
+1 -1
View File
@@ -20,7 +20,7 @@
#include "../DSPEmitter.h"
#include "DSPJitUtil.h"
#include "x64Emitter.h"
#include "ABI.h"
#include "x64ABI.h"
using namespace Gen;
//clobbers:
@@ -27,7 +27,7 @@
#include "DSPJitUtil.h"
#endif
#include "x64Emitter.h"
#include "ABI.h"
#include "x64ABI.h"
using namespace Gen;
// Returns s64 in RAX
+1 -1
View File
@@ -20,7 +20,7 @@
#include "../DSPEmitter.h"
#include "DSPJitUtil.h"
#include "x64Emitter.h"
#include "ABI.h"
#include "x64ABI.h"
using namespace Gen;
-126
View File
@@ -419,132 +419,6 @@ u32 Read_Instruction(const u32 em_address)
return inst.hex;
}
u32 Read_Opcode_JIT_Uncached(const u32 _Address)
{
u8* iCache;
u32 addr;
if (_Address & JIT_ICACHE_VMEM_BIT)
{
iCache = jit->GetBlockCache()->GetICacheVMEM();
addr = _Address & JIT_ICACHE_MASK;
}
else if (_Address & JIT_ICACHE_EXRAM_BIT)
{
iCache = jit->GetBlockCache()->GetICacheEx();
addr = _Address & JIT_ICACHEEX_MASK;
}
else
{
iCache = jit->GetBlockCache()->GetICache();
addr = _Address & JIT_ICACHE_MASK;
}
u32 inst = *(u32*)(iCache + addr);
if (inst == JIT_ICACHE_INVALID_WORD)
{
u32 cache_block_start = addr & ~0x1f;
u32 mem_block_start = _Address & ~0x1f;
u8 *pMem = Memory::GetPointer(mem_block_start);
memcpy(iCache + cache_block_start, pMem, 32);
inst = *(u32*)(iCache + addr);
}
inst = Common::swap32(inst);
if ((inst & 0xfc000000) == 0)
{
inst = jit->GetBlockCache()->GetOriginalFirstOp(inst);
}
return inst;
}
u32 Read_Opcode_JIT(u32 _Address)
{
#ifdef FAST_ICACHE
if (bMMU && !bFakeVMEM && (_Address & ADDR_MASK_MEM1))
{
_Address = Memory::TranslateAddress(_Address, FLAG_OPCODE);
if (_Address == 0)
{
return 0;
}
}
u32 inst = 0;
// Bypass the icache for the external interrupt exception handler
if ( (_Address & 0x0FFFFF00) == 0x00000500 )
inst = Read_Opcode_JIT_Uncached(_Address);
else
inst = PowerPC::ppcState.iCache.ReadInstruction(_Address);
#else
u32 inst = Memory::ReadUnchecked_U32(_Address);
#endif
return inst;
}
// The following function is deprecated in favour of FAST_ICACHE
u32 Read_Opcode_JIT_LC(const u32 _Address)
{
#ifdef JIT_UNLIMITED_ICACHE
if ((_Address & ~JIT_ICACHE_MASK) != 0x80000000 && (_Address & ~JIT_ICACHE_MASK) != 0x00000000 &&
(_Address & ~JIT_ICACHE_MASK) != 0x7e000000 && // TLB area
(_Address & ~JIT_ICACHEEX_MASK) != 0x90000000 && (_Address & ~JIT_ICACHEEX_MASK) != 0x10000000)
{
PanicAlertT("iCacheJIT: Reading Opcode from %x. Please report.", _Address);
ERROR_LOG(MEMMAP, "iCacheJIT: Reading Opcode from %x. Please report.", _Address);
return 0;
}
u8* iCache;
u32 addr;
if (_Address & JIT_ICACHE_VMEM_BIT)
{
iCache = jit->GetBlockCache()->GetICacheVMEM();
addr = _Address & JIT_ICACHE_MASK;
}
else if (_Address & JIT_ICACHE_EXRAM_BIT)
{
iCache = jit->GetBlockCache()->GetICacheEx();
addr = _Address & JIT_ICACHEEX_MASK;
}
else
{
iCache = jit->GetBlockCache()->GetICache();
addr = _Address & JIT_ICACHE_MASK;
}
u32 inst = *(u32*)(iCache + addr);
if (inst == JIT_ICACHE_INVALID_WORD)
inst = Memory::ReadUnchecked_U32(_Address);
else
inst = Common::swap32(inst);
#else
u32 inst = Memory::ReadUnchecked_U32(_Address);
#endif
if ((inst & 0xfc000000) == 0)
{
inst = jit->GetBlockCache()->GetOriginalFirstOp(inst);
}
return inst;
}
// WARNING! No checks!
// We assume that _Address is cached
void Write_Opcode_JIT(const u32 _Address, const u32 _Value)
{
#ifdef JIT_UNLIMITED_ICACHE
if (_Address & JIT_ICACHE_VMEM_BIT)
{
*(u32*)(jit->GetBlockCache()->GetICacheVMEM() + (_Address & JIT_ICACHE_MASK)) = Common::swap32(_Value);
}
else if (_Address & JIT_ICACHE_EXRAM_BIT)
{
*(u32*)(jit->GetBlockCache()->GetICacheEx() + (_Address & JIT_ICACHEEX_MASK)) = Common::swap32(_Value);
}
else
*(u32*)(jit->GetBlockCache()->GetICache() + (_Address & JIT_ICACHE_MASK)) = Common::swap32(_Value);
#else
Memory::WriteUnchecked_U32(_Value, _Address);
#endif
}
void WriteBigEData(const u8 *_pData, const u32 _Address, const u32 _iSize)
{
memcpy(GetPointer(_Address), _pData, _iSize);
-5
View File
@@ -119,11 +119,6 @@ inline u32 ReadFast32(const u32 _Address)
// used by interpreter to read instructions, uses iCache
u32 Read_Opcode(const u32 _Address);
// used by JIT to read instructions
u32 Read_Opcode_JIT(const u32 _Address);
// used by JIT. uses iCacheJIT. Reads in the "Locked cache" mode
u32 Read_Opcode_JIT_LC(const u32 _Address);
void Write_Opcode_JIT(const u32 _Address, const u32 _Value);
// this is used by Debugger a lot.
// For now, just reads from memory!
u32 Read_Instruction(const u32 _Address);
@@ -28,8 +28,6 @@
#undef _interlockedbittestandreset
#undef _interlockedbittestandset64
#undef _interlockedbittestandreset64
#else
#include <xmmintrin.h>
#endif
#include "../../Core.h"
@@ -514,4 +512,4 @@ void Interpreter::fsqrtx(UGeckoInstruction _inst)
rPS0(_inst.FD) = sqrt(b);
UpdateFPRF(rPS0(_inst.FD));
if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD));
}
}
@@ -24,8 +24,7 @@
#include "Interpreter.h"
#include "../../Core.h"
#include "../JitCommon/JitBase.h"
#include "../JitCommon/JitCache.h"
#include "../JitInterface.h"
#include "Interpreter_FPUtils.h"
@@ -363,34 +362,24 @@ void Interpreter::dcbf(UGeckoInstruction _inst)
{
NPC = PC + 12;
}*/
// Invalidate the jit block cache on dcbf
if (jit)
{
u32 address = Helper_Get_EA_X(_inst);
jit->GetBlockCache()->InvalidateICache(address & ~0x1f, 32);
}
JitInterface::InvalidateICache(address & ~0x1f, 32);
}
void Interpreter::dcbi(UGeckoInstruction _inst)
{
// Removes a block from data cache. Since we don't emulate the data cache, we don't need to do anything to the data cache
// However, we invalidate the jit block cache on dcbi
if (jit)
{
u32 address = Helper_Get_EA_X(_inst);
jit->GetBlockCache()->InvalidateICache(address & ~0x1f, 32);
}
JitInterface::InvalidateICache(address & ~0x1f, 32);
}
void Interpreter::dcbst(UGeckoInstruction _inst)
{
// Cache line flush. Since we don't emulate the data cache, we don't need to do anything.
// Invalidate the jit block cache on dcbst in case new code has been loaded via the data cache
if (jit)
{
u32 address = Helper_Get_EA_X(_inst);
jit->GetBlockCache()->InvalidateICache(address & ~0x1f, 32);
}
JitInterface::InvalidateICache(address & ~0x1f, 32);
}
void Interpreter::dcbt(UGeckoInstruction _inst)
@@ -409,7 +398,7 @@ void Interpreter::dcbz(UGeckoInstruction _inst)
// HACK but works... we think
if (!Core::g_CoreStartupParameter.bDCBZOFF)
Memory::Memset(Helper_Get_EA_X(_inst) & (~31), 0, 32);
if (!jit)
if (!JitInterface::GetCore())
PowerPC::CheckExceptions();
}
@@ -26,13 +26,6 @@
#undef _interlockedbittestandreset
#undef _interlockedbittestandset64
#undef _interlockedbittestandreset64
#else
static const unsigned short FPU_ROUND_NEAR = 0 << 10;
static const unsigned short FPU_ROUND_DOWN = 1 << 10;
static const unsigned short FPU_ROUND_UP = 2 << 10;
static const unsigned short FPU_ROUND_CHOP = 3 << 10;
static const unsigned short FPU_ROUND_MASK = 3 << 10;
#include <xmmintrin.h>
#endif
#include "CPUDetect.h"
@@ -43,6 +36,7 @@ static const unsigned short FPU_ROUND_MASK = 3 << 10;
#include "../../HW/SystemTimers.h"
#include "../../Core.h"
#include "Interpreter.h"
#include "FPURoundMode.h"
#include "Interpreter_FPUtils.h"
@@ -61,38 +55,11 @@ mffsx: 80036650 (huh?)
// That is, set rounding mode etc when entering jit code or the interpreter loop
// Restore rounding mode when calling anything external
const u32 MASKS = 0x1F80; // mask away the interrupts.
const u32 DAZ = 0x40;
const u32 FTZ = 0x8000;
static void FPSCRtoFPUSettings(UReg_FPSCR fp)
{
// Set FPU rounding mode to mimic the PowerPC's
#ifdef _M_IX86
// This shouldn't really be needed anymore since we use SSE
#ifdef _WIN32
const int table[4] =
{
_RC_NEAR,
_RC_CHOP,
_RC_UP,
_RC_DOWN
};
_set_controlfp(_MCW_RC, table[fp.RN]);
#else
const unsigned short table[4] =
{
FPU_ROUND_NEAR,
FPU_ROUND_CHOP,
FPU_ROUND_UP,
FPU_ROUND_DOWN
};
unsigned short mode;
asm ("fstcw %0" : "=m" (mode) : );
mode = (mode & ~FPU_ROUND_MASK) | table[fp.RN];
asm ("fldcw %0" : : "m" (mode));
#endif
#endif
FPURoundMode::SetRoundMode(fp.RN);
if (fp.VE || fp.OE || fp.UE || fp.ZE || fp.XE)
{
//PanicAlert("FPSCR - exceptions enabled. Please report. VE=%i OE=%i UE=%i ZE=%i XE=%i",
@@ -101,14 +68,6 @@ static void FPSCRtoFPUSettings(UReg_FPSCR fp)
}
// Also corresponding SSE rounding mode setting
static const u32 ssetable[4] =
{
(0 << 13) | MASKS,
(3 << 13) | MASKS,
(2 << 13) | MASKS,
(1 << 13) | MASKS,
};
u32 csr = ssetable[FPSCR.RN];
if (FPSCR.NI)
{
// Either one of these two breaks Beyond Good & Evil.
@@ -116,7 +75,7 @@ static void FPSCRtoFPUSettings(UReg_FPSCR fp)
// csr |= DAZ;
// csr |= FTZ;
}
_mm_setcsr(csr);
FPURoundMode::SetSIMDMode(FPSCR.RN);
}
void Interpreter::mtfsb0x(UGeckoInstruction _inst)
@@ -267,7 +267,7 @@ static GekkoOPTemplate table31[] =
{19, Interpreter::mfcr, {"mfcr", OPTYPE_SYSTEM, FL_OUT_D, 0, 0, 0, 0}},
{83, Interpreter::mfmsr, {"mfmsr", OPTYPE_SYSTEM, FL_OUT_D, 0, 0, 0, 0}},
{144, Interpreter::mtcrf, {"mtcrf", OPTYPE_SYSTEM, 0, 0, 0, 0, 0}},
{146, Interpreter::mtmsr, {"mtmsr", OPTYPE_SYSTEM, FL_ENDBLOCK, 0, 0, 0, 0}},
{146, Interpreter::mtmsr, {"mtmsr", OPTYPE_SYSTEM, FL_IN_S | FL_ENDBLOCK, 0, 0, 0, 0}},
{210, Interpreter::mtsr, {"mtsr", OPTYPE_SYSTEM, 0, 0, 0, 0, 0}},
{242, Interpreter::mtsrin, {"mtsrin", OPTYPE_SYSTEM, 0, 0, 0, 0, 0}},
{339, Interpreter::mfspr, {"mfspr", OPTYPE_SPR, FL_OUT_D, 0, 0, 0, 0}},
+2 -2
View File
@@ -24,7 +24,7 @@
#include "Common.h"
#include "x64Emitter.h"
#include "ABI.h"
#include "x64ABI.h"
#include "Thunk.h"
#include "../../HLE/HLE.h"
#include "../../Core.h"
@@ -357,7 +357,7 @@ void Jit64::WriteExternalExceptionExit()
MOV(32, R(EAX), M(&PC));
MOV(32, M(&NPC), R(EAX));
ABI_CallFunction(reinterpret_cast<void *>(&PowerPC::CheckExternalExceptions));
SUB(32, M(&CoreTiming::downcount), js.downcountAmount > 127 ? Imm32(js.downcountAmount) : Imm8(js.downcountAmount));
SUB(32, M(&CoreTiming::downcount), js.downcountAmount > 127 ? Imm32(js.downcountAmount) : Imm8(js.downcountAmount));
JMP(asm_routines.dispatcher, true);
}
+1 -1
View File
@@ -62,7 +62,7 @@
if (js.memcheck) \
SetJumpTarget(memException);
class Jit64 : public JitBase
class Jit64 : public Jitx86Base
{
private:
GPRRegCache gpr;
@@ -15,7 +15,7 @@
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#include "ABI.h"
#include "x64ABI.h"
#include "x64Emitter.h"
#include "../../HW/Memmap.h"
@@ -24,7 +24,7 @@
#include "../../CoreTiming.h"
#include "MemoryUtil.h"
#include "ABI.h"
#include "x64ABI.h"
#include "Jit.h"
#include "../JitCommon/JitCache.h"
@@ -204,7 +204,7 @@ void Jit64AsmRoutineManager::Generate()
MOV(32, M(&NPC), R(EAX));
ABI_CallFunction(reinterpret_cast<void *>(&PowerPC::CheckExternalExceptions));
SetJumpTarget(noExtException);
TEST(32, M((void*)PowerPC::GetStatePtr()), Imm32(0xFFFFFFFF));
J_CC(CC_Z, outerLoop, true);
@@ -18,7 +18,6 @@
#ifndef _JIT64ASM_H
#define _JIT64ASM_H
#include "x64Emitter.h"
#include "../JitCommon/JitAsmCommon.h"
// In Dolphin, we don't use inline assembly. Instead, we generate all machine-near
@@ -28,7 +28,7 @@
#include "../../HW/Memmap.h"
#include "../PPCTables.h"
#include "x64Emitter.h"
#include "ABI.h"
#include "x64ABI.h"
#include "Jit.h"
#include "JitAsm.h"
@@ -27,7 +27,7 @@
#include "../PPCTables.h"
#include "CPUDetect.h"
#include "x64Emitter.h"
#include "ABI.h"
#include "x64ABI.h"
#include "Jit.h"
#include "JitAsm.h"
@@ -28,7 +28,7 @@
#include "../PPCTables.h"
#include "CPUDetect.h"
#include "x64Emitter.h"
#include "ABI.h"
#include "x64ABI.h"
#include "Jit.h"
#include "JitAsm.h"
@@ -23,7 +23,7 @@
#include "../PowerPC.h"
#include "../PPCTables.h"
#include "x64Emitter.h"
#include "ABI.h"
#include "x64ABI.h"
#include "Thunk.h"
#include "Jit.h"
@@ -1991,8 +1991,10 @@ void JitIL::WriteCode() {
}
void ProfiledReJit() {
jit->SetCodePtr(jit->js.rewriteStart);
DoWriteCode(&((JitIL *)jit)->ibuild, (JitIL *)jit, true, false);
jit->js.curBlock->codeSize = (int)(jit->GetCodePtr() - jit->js.rewriteStart);
jit->GetBlockCache()->FinalizeBlock(jit->js.curBlock->blockNum, jit->jo.enableBlocklink, jit->js.curBlock->normalEntry);
JitIL *jitil = (JitIL *)jit;
jitil->SetCodePtr(jitil->js.rewriteStart);
DoWriteCode(&jitil->ibuild, jitil, true, false);
jitil->js.curBlock->codeSize = (int)(jitil->GetCodePtr() - jitil->js.rewriteStart);
jitil->GetBlockCache()->FinalizeBlock(jitil->js.curBlock->blockNum, jitil->jo.enableBlocklink,
jitil->js.curBlock->normalEntry);
}
@@ -19,7 +19,7 @@
#include "Common.h"
#include "x64Emitter.h"
#include "ABI.h"
#include "x64ABI.h"
#include "Thunk.h"
#include "../../HLE/HLE.h"
#include "../../Core.h"
+1 -1
View File
@@ -57,7 +57,7 @@
#define DISABLE64
#endif
class JitIL : public JitBase
class JitIL : public Jitx86Base
{
private:
@@ -15,7 +15,7 @@
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#include "ABI.h"
#include "x64ABI.h"
#include "x64Emitter.h"
#include "../../HW/Memmap.h"
@@ -25,7 +25,7 @@
#include "MemoryUtil.h"
#include "CPUDetect.h"
#include "ABI.h"
#include "x64ABI.h"
#include "Thunk.h"
#include "../../HW/GPFifo.h"
@@ -211,14 +211,14 @@ void JitILAsmRoutineManager::Generate()
doTiming = GetCodePtr();
ABI_CallFunction(reinterpret_cast<void *>(&CoreTiming::Advance));
testExceptions = GetCodePtr();
MOV(32, R(EAX), M(&PC));
MOV(32, M(&NPC), R(EAX));
ABI_CallFunction(reinterpret_cast<void *>(&PowerPC::CheckExceptions));
MOV(32, R(EAX), M(&NPC));
MOV(32, M(&PC), R(EAX));
TEST(32, M((void*)PowerPC::GetStatePtr()), Imm32(0xFFFFFFFF));
J_CC(CC_Z, outer_loop, true);
//Landing pad for drec space
@@ -27,7 +27,7 @@
#include "../../HW/Memmap.h"
#include "../PPCTables.h"
#include "x64Emitter.h"
#include "ABI.h"
#include "x64ABI.h"
#include "JitIL.h"
#include "JitILAsm.h"
@@ -27,7 +27,7 @@
#include "../PPCTables.h"
#include "CPUDetect.h"
#include "x64Emitter.h"
#include "ABI.h"
#include "x64ABI.h"
#include "JitIL.h"
#include "JitILAsm.h"
@@ -25,7 +25,7 @@
#include "../PPCTables.h"
#include "CPUDetect.h"
#include "x64Emitter.h"
#include "ABI.h"
#include "x64ABI.h"
#include "JitIL.h"
#include "JitILAsm.h"
@@ -23,7 +23,7 @@
#include "../PowerPC.h"
#include "../PPCTables.h"
#include "x64Emitter.h"
#include "ABI.h"
#include "x64ABI.h"
#include "Thunk.h"
#include "JitIL.h"
@@ -0,0 +1,498 @@
// Copyright (C) 2003 Dolphin Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#include <map>
#include "Common.h"
#include "../../HLE/HLE.h"
#include "../../Core.h"
#include "../../PatchEngine.h"
#include "../../CoreTiming.h"
#include "../../ConfigManager.h"
#include "../PowerPC.h"
#include "../Profiler.h"
#include "../PPCTables.h"
#include "../PPCAnalyst.h"
#include "../../HW/Memmap.h"
#include "../../HW/GPFifo.h"
#include "Jit.h"
#include "JitArm_Tables.h"
#include "ArmEmitter.h"
#include "../JitInterface.h"
using namespace ArmGen;
using namespace PowerPC;
static int CODE_SIZE = 1024*1024*32;
namespace CPUCompare
{
extern u32 m_BlockStart;
}
void JitArm::Init()
{
AllocCodeSpace(CODE_SIZE);
blocks.Init();
asm_routines.Init();
// TODO: Investigate why the register cache crashes when only doing Init with
// the pointer to this. Seems for some reason it doesn't set the emitter pointer
// In the class for some reason?
gpr.Init(this);
gpr.SetEmitter(this);
fpr.Init(this);
fpr.SetEmitter(this);
jo.enableBlocklink = true;
jo.optimizeGatherPipe = false;
}
void JitArm::ClearCache()
{
ClearCodeSpace();
blocks.Clear();
}
void JitArm::Shutdown()
{
FreeCodeSpace();
blocks.Shutdown();
asm_routines.Shutdown();
}
// This is only called by Default() in this file. It will execute an instruction with the interpreter functions.
void JitArm::WriteCallInterpreter(UGeckoInstruction inst)
{
gpr.Flush();
fpr.Flush();
Interpreter::_interpreterInstruction instr = GetInterpreterOp(inst);
MOVI2R(R0, inst.hex);
MOVI2R(R12, (u32)instr);
BL(R12);
}
void JitArm::unknown_instruction(UGeckoInstruction inst)
{
PanicAlert("unknown_instruction %08x - Fix me ;)", inst.hex);
}
void JitArm::Default(UGeckoInstruction _inst)
{
WriteCallInterpreter(_inst.hex);
}
void JitArm::HLEFunction(UGeckoInstruction _inst)
{
gpr.Flush();
fpr.Flush();
MOVI2R(R0, js.compilerPC);
MOVI2R(R1, _inst.hex);
QuickCallFunction(R14, (void*)&HLE::Execute);
ARMReg rA = gpr.GetReg();
LDR(rA, R9, STRUCT_OFF(PowerPC::ppcState, npc));
WriteExitDestInR(rA);
}
void JitArm::DoNothing(UGeckoInstruction _inst)
{
// Yup, just don't do anything.
}
static const bool ImHereDebug = false;
static const bool ImHereLog = false;
static std::map<u32, int> been_here;
static void ImHere()
{
static File::IOFile f;
if (ImHereLog)
{
if (!f)
{
f.Open("log32.txt", "w");
}
fprintf(f.GetHandle(), "%08x\n", PC);
}
if (been_here.find(PC) != been_here.end())
{
been_here.find(PC)->second++;
if ((been_here.find(PC)->second) & 1023)
return;
}
DEBUG_LOG(DYNA_REC, "I'm here - PC = %08x , LR = %08x", PC, LR);
been_here[PC] = 1;
}
void JitArm::Cleanup()
{
if (jo.optimizeGatherPipe && js.fifoBytesThisBlock > 0)
QuickCallFunction(R14, (void*)&GPFifo::CheckGatherPipe);
}
void JitArm::DoDownCount()
{
ARMReg rA = gpr.GetReg();
ARMReg rB = gpr.GetReg();
MOVI2R(rA, (u32)&CoreTiming::downcount);
LDR(rB, rA);
if(js.downcountAmount < 255) // We can enlarge this if we used rotations
{
SUBS(rB, rB, js.downcountAmount);
STR(rA, rB);
}
else
{
ARMReg rC = gpr.GetReg(false);
MOVI2R(rC, js.downcountAmount);
SUBS(rB, rB, rC);
STR(rA, rB);
}
gpr.Unlock(rA, rB);
}
void JitArm::WriteExitDestInR(ARMReg Reg)
{
STR(R9, Reg, STRUCT_OFF(PowerPC::ppcState, pc));
Cleanup();
DoDownCount();
MOVI2R(Reg, (u32)asm_routines.dispatcher);
B(Reg);
gpr.Unlock(Reg);
}
void JitArm::WriteRfiExitDestInR(ARMReg Reg)
{
STR(R9, Reg, STRUCT_OFF(PowerPC::ppcState, pc));
Cleanup();
DoDownCount();
MOVI2R(Reg, (u32)asm_routines.testExceptions);
B(Reg);
gpr.Unlock(Reg); // This was locked in the instruction beforehand
}
void JitArm::WriteExceptionExit()
{
ARMReg A = gpr.GetReg(false);
Cleanup();
DoDownCount();
MOVI2R(A, (u32)asm_routines.testExceptions);
B(A);
}
void JitArm::WriteExit(u32 destination, int exit_num)
{
Cleanup();
DoDownCount();
//If nobody has taken care of this yet (this can be removed when all branches are done)
JitBlock *b = js.curBlock;
b->exitAddress[exit_num] = destination;
b->exitPtrs[exit_num] = GetWritableCodePtr();
// Link opportunity!
int block = blocks.GetBlockNumberFromStartAddress(destination);
if (block >= 0 && jo.enableBlocklink)
{
// It exists! Joy of joy!
B(blocks.GetBlock(block)->checkedEntry);
b->linkStatus[exit_num] = true;
}
else
{
ARMReg A = gpr.GetReg(false);
MOVI2R(A, destination);
STR(R9, A, STRUCT_OFF(PowerPC::ppcState, pc));
MOVI2R(A, (u32)asm_routines.dispatcher);
B(A);
}
}
void STACKALIGN JitArm::Run()
{
CompiledCode pExecAddr = (CompiledCode)asm_routines.enterCode;
pExecAddr();
}
void JitArm::SingleStep()
{
CompiledCode pExecAddr = (CompiledCode)asm_routines.enterCode;
pExecAddr();
}
void JitArm::Trace()
{
char regs[500] = "";
char fregs[750] = "";
#ifdef JIT_LOG_GPR
for (int i = 0; i < 32; i++)
{
char reg[50];
sprintf(reg, "r%02d: %08x ", i, PowerPC::ppcState.gpr[i]);
strncat(regs, reg, 500);
}
#endif
#ifdef JIT_LOG_FPR
for (int i = 0; i < 32; i++)
{
char reg[50];
sprintf(reg, "f%02d: %016x ", i, riPS0(i));
strncat(fregs, reg, 750);
}
#endif
DEBUG_LOG(DYNA_REC, "JITARM PC: %08x SRR0: %08x SRR1: %08x CRfast: %02x%02x%02x%02x%02x%02x%02x%02x FPSCR: %08x MSR: %08x LR: %08x %s %s",
PC, SRR0, SRR1, PowerPC::ppcState.cr_fast[0], PowerPC::ppcState.cr_fast[1], PowerPC::ppcState.cr_fast[2], PowerPC::ppcState.cr_fast[3],
PowerPC::ppcState.cr_fast[4], PowerPC::ppcState.cr_fast[5], PowerPC::ppcState.cr_fast[6], PowerPC::ppcState.cr_fast[7], PowerPC::ppcState.fpscr,
PowerPC::ppcState.msr, PowerPC::ppcState.spr[8], regs, fregs);
}
void JitArm::PrintDebug(UGeckoInstruction inst, u32 level)
{
if (level > 0)
printf("Start: %08x OP '%s' Info\n", (u32)GetCodePtr(), PPCTables::GetInstructionName(inst));
if (level > 1)
{
GekkoOPInfo* Info = GetOpInfo(inst.hex);
printf("\tOuts\n");
if (Info->flags & FL_OUT_A)
printf("\t-OUT_A: %x\n", inst.RA);
if(Info->flags & FL_OUT_D)
printf("\t-OUT_D: %x\n", inst.RD);
printf("\tIns\n");
// A, AO, B, C, S
if(Info->flags & FL_IN_A)
printf("\t-IN_A: %x\n", inst.RA);
if(Info->flags & FL_IN_A0)
printf("\t-IN_A0: %x\n", inst.RA);
if(Info->flags & FL_IN_B)
printf("\t-IN_B: %x\n", inst.RB);
if(Info->flags & FL_IN_C)
printf("\t-IN_C: %x\n", inst.RC);
if(Info->flags & FL_IN_S)
printf("\t-IN_S: %x\n", inst.RS);
}
}
void STACKALIGN JitArm::Jit(u32 em_address)
{
if (GetSpaceLeft() < 0x10000 || blocks.IsFull() || Core::g_CoreStartupParameter.bJITNoBlockCache)
{
ClearCache();
}
int block_num = blocks.AllocateBlock(em_address);
JitBlock *b = blocks.GetBlock(block_num);
const u8* BlockPtr = DoJit(em_address, &code_buffer, b);
blocks.FinalizeBlock(block_num, jo.enableBlocklink, BlockPtr);
}
void JitArm::Break(UGeckoInstruction inst)
{
BKPT(0x4444);
}
const u8* JitArm::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBlock *b)
{
int blockSize = code_buf->GetSize();
// Memory exception on instruction fetch
bool memory_exception = false;
// A broken block is a block that does not end in a branch
bool broken_block = false;
if (Core::g_CoreStartupParameter.bEnableDebugging)
{
// Comment out the following to disable breakpoints (speed-up)
blockSize = 1;
broken_block = true;
Trace();
}
if (em_address == 0)
{
Core::SetState(Core::CORE_PAUSE);
PanicAlert("ERROR: Compiling at 0. LR=%08x CTR=%08x", LR, CTR);
}
if (Core::g_CoreStartupParameter.bMMU && (em_address & JIT_ICACHE_VMEM_BIT))
{
if (!Memory::TranslateAddress(em_address, Memory::FLAG_OPCODE))
{
// Memory exception occurred during instruction fetch
memory_exception = true;
}
}
int size = 0;
js.isLastInstruction = false;
js.blockStart = em_address;
js.fifoBytesThisBlock = 0;
js.curBlock = b;
js.block_flags = 0;
js.cancel = false;
// 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.
u32 nextPC = em_address;
u32 merged_addresses[32];
const int capacity_of_merged_addresses = sizeof(merged_addresses) / sizeof(merged_addresses[0]);
int size_of_merged_addresses = 0;
if (!memory_exception)
{
// If there is a memory exception inside a block (broken_block==true), compile up to that instruction.
nextPC = PPCAnalyst::Flatten(em_address, &size, &js.st, &js.gpa, &js.fpa, broken_block, code_buf, blockSize, merged_addresses, capacity_of_merged_addresses, size_of_merged_addresses);
}
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 skip = B_CC(CC_PL);
ARMABI_MOVI2M((u32)&PC, js.blockStart);
ARMReg rA = gpr.GetReg(false);
MOVI2R(rA, (u32)asm_routines.doTiming);
B(rA);
SetJumpTarget(skip);
}
const u8 *normalEntry = GetCodePtr();
b->normalEntry = normalEntry;
if(ImHereDebug)
QuickCallFunction(R14, (void *)&ImHere); //Used to get a trace of the last few blocks before a crash, sometimes VERY useful
if (js.fpa.any)
{
// This block uses FPU - needs to add FP exception bailout
ARMReg A = gpr.GetReg();
ARMReg C = gpr.GetReg();
Operand2 Shift(2, 10); // 1 << 13
MOVI2R(C, js.blockStart); // R3
LDR(A, R9, STRUCT_OFF(PowerPC::ppcState, msr));
TST(A, Shift);
FixupBranch b1 = B_CC(CC_NEQ);
STR(R9, C, STRUCT_OFF(PowerPC::ppcState, pc));
MOVI2R(A, (u32)asm_routines.fpException);
B(A);
SetJumpTarget(b1);
gpr.Unlock(A, C);
}
// Conditionally add profiling code.
if (Profiler::g_ProfileBlocks) {
ARMReg rA = gpr.GetReg();
ARMReg rB = gpr.GetReg();
MOVI2R(rA, (u32)&b->runCount); // Load in to register
LDR(rB, rA); // Load the actual value in to R11.
ADD(rB, rB, 1); // Add one to the value
STR(rA, rB); // Now store it back in the memory location
// get start tic
PROFILER_QUERY_PERFORMANCE_COUNTER(&b->ticStart);
gpr.Unlock(rA, rB);
}
gpr.Start(js.gpa);
fpr.Start(js.fpa);
js.downcountAmount = 0;
if (!Core::g_CoreStartupParameter.bEnableDebugging)
{
for (int i = 0; i < size_of_merged_addresses; ++i)
{
const u32 address = merged_addresses[i];
js.downcountAmount += PatchEngine::GetSpeedhackCycles(address);
}
}
js.skipnext = false;
js.blockSize = size;
js.compilerPC = nextPC;
// Translate instructions
for (int i = 0; i < (int)size; i++)
{
js.compilerPC = ops[i].address;
js.op = &ops[i];
js.instructionNumber = i;
const GekkoOPInfo *opinfo = ops[i].opinfo;
js.downcountAmount += (opinfo->numCyclesMinusOne + 1);
if (i == (int)size - 1)
{
// WARNING - cmp->branch merging will screw this up.
js.isLastInstruction = true;
js.next_inst = 0;
if (Profiler::g_ProfileBlocks) {
// CAUTION!!! push on stack regs you use, do your stuff, then pop
PROFILER_VPUSH;
// get end tic
PROFILER_QUERY_PERFORMANCE_COUNTER(&b->ticStop);
// tic counter += (end tic - start tic)
PROFILER_ADD_DIFF_LARGE_INTEGER(&b->ticCounter, &b->ticStop, &b->ticStart);
PROFILER_VPOP;
}
}
else
{
// help peephole optimizations
js.next_inst = ops[i + 1].inst;
js.next_compilerPC = ops[i + 1].address;
}
if (jo.optimizeGatherPipe && js.fifoBytesThisBlock >= 32)
{
js.fifoBytesThisBlock -= 32;
// TODO: This needs thunkmanager for ARM
//ARMABI_CallFunction(thunks.ProtectFunction((void *)&GPFifo::CheckGatherPipe, 0));
}
if (Core::g_CoreStartupParameter.bEnableDebugging)
{
// Add run count
ARMReg RA = gpr.GetReg();
ARMReg RB = gpr.GetReg();
MOVI2R(RA, (u32)&opinfo->runCount);
LDR(RB, RA);
ADD(RB, RB, 1);
STR(RA, RB);
gpr.Unlock(RA, RB);
}
if (!ops[i].skip)
{
PrintDebug(ops[i].inst, 0);
if (js.memcheck && (opinfo->flags & FL_USE_FPU))
{
// Don't do this yet
BKPT(0x7777);
}
JitArmTables::CompileInstruction(ops[i]);
if (js.memcheck && (opinfo->flags & FL_LOADSTORE))
{
// Don't do this yet
BKPT(0x666);
}
}
}
if (memory_exception)
BKPT(0x500);
if (broken_block)
{
printf("Broken Block going to 0x%08x\n", nextPC);
WriteExit(nextPC, 0);
}
b->flags = js.block_flags;
b->codeSize = (u32)(GetCodePtr() - normalEntry);
b->originalSize = size;
FlushIcache();
return start;
}
+186
View File
@@ -0,0 +1,186 @@
// Copyright (C) 2003 Dolphin Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
// ========================
// See comments in Jit.cpp.
// ========================
// Mystery: Capcom vs SNK 800aa278
// CR flags approach:
// * Store that "N+Z flag contains CR0" or "S+Z flag contains CR3".
// * All flag altering instructions flush this
// * A flush simply does a conditional write to the appropriate CRx.
// * If flag available, branch code can become absolutely trivial.
// Settings
// ----------
#ifndef _JITARM_H
#define _JITARM_H
#include "../CPUCoreBase.h"
#include "../PPCAnalyst.h"
#include "JitArmCache.h"
#include "JitRegCache.h"
#include "JitFPRCache.h"
#include "JitAsm.h"
#include "../JitCommon/JitBase.h"
// Use these to control the instruction selection
// #define INSTRUCTION_START Default(inst); return;
// #define INSTRUCTION_START PPCTables::CountInstruction(inst);
#define INSTRUCTION_START
#define JITDISABLE(type) \
if (Core::g_CoreStartupParameter.bJITOff || \
Core::g_CoreStartupParameter.bJIT##type##Off) \
{Default(inst); return;}
class JitArm : public JitBase, public ArmGen::ARMXCodeBlock
{
private:
JitArmBlockCache blocks;
JitArmAsmRoutineManager asm_routines;
// TODO: Make arm specific versions of these, shouldn't be too hard to
// make it so we allocate some space at the start(?) of code generation
// and keep the registers in a cache. Will burn this bridge when we get to
// it.
ArmRegCache gpr;
ArmFPRCache fpr;
PPCAnalyst::CodeBuffer code_buffer;
void DoDownCount();
void PrintDebug(UGeckoInstruction inst, u32 level);
void Helper_UpdateCR1(ARMReg value);
public:
JitArm() : code_buffer(32000) {}
~JitArm() {}
void Init();
void Shutdown();
// Jit!
void Jit(u32 em_address);
const u8* DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBlock *b);
JitBaseBlockCache *GetBlockCache() { return &blocks; }
const u8 *BackPatch(u8 *codePtr, int accessType, u32 em_address, void *ctx);
bool IsInCodeSpace(u8 *ptr) { return IsInSpace(ptr); }
void Trace();
void ClearCache();
const u8 *GetDispatcher() {
return asm_routines.dispatcher;
}
CommonAsmRoutinesBase *GetAsmRoutines() {
return &asm_routines;
}
const char *GetName() {
return "JITARM";
}
// Run!
void Run();
void SingleStep();
// Utilities for use by opcodes
void WriteExit(u32 destination, int exit_num);
void WriteExitDestInR(ARMReg Reg);
void WriteRfiExitDestInR(ARMReg Reg);
void WriteExceptionExit();
void WriteCallInterpreter(UGeckoInstruction _inst);
void Cleanup();
void GenerateRC(int cr = 0);
void ComputeRC(int cr = 0);
// TODO: This shouldn't be here
void StoreFromReg(ARMReg dest, ARMReg value, int accessSize, s32 offset);
void LoadToReg(ARMReg dest, ARMReg addr, int accessSize, s32 offset);
// OPCODES
void unknown_instruction(UGeckoInstruction _inst);
void Default(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);
// Breakin shit
void Break(UGeckoInstruction _inst);
// Branch
void bx(UGeckoInstruction _inst);
void bcx(UGeckoInstruction _inst);
void bclrx(UGeckoInstruction _inst);
void sc(UGeckoInstruction _inst);
void rfi(UGeckoInstruction _inst);
void bcctrx(UGeckoInstruction _inst);
// Integer
void addi(UGeckoInstruction _inst);
void addis(UGeckoInstruction _inst);
void addx(UGeckoInstruction _inst);
void cmp (UGeckoInstruction _inst);
void cmpi(UGeckoInstruction _inst);
void cmpli(UGeckoInstruction _inst);
void negx(UGeckoInstruction _inst);
void mulli(UGeckoInstruction _inst);
void ori(UGeckoInstruction _inst);
void oris(UGeckoInstruction _inst);
void orx(UGeckoInstruction _inst);
void rlwimix(UGeckoInstruction _inst);
void rlwinmx(UGeckoInstruction _inst);
void extshx(UGeckoInstruction inst);
void extsbx(UGeckoInstruction inst);
// System Registers
void mtmsr(UGeckoInstruction _inst);
void mtspr(UGeckoInstruction _inst);
void mfspr(UGeckoInstruction _inst);
// LoadStore
void icbi(UGeckoInstruction _inst);
void lbz(UGeckoInstruction _inst);
void lhz(UGeckoInstruction _inst);
void lwz(UGeckoInstruction _inst);
void lwzx(UGeckoInstruction _inst);
void stw(UGeckoInstruction _inst);
void stwu(UGeckoInstruction _inst);
// Floating point
void fabsx(UGeckoInstruction _inst);
void faddx(UGeckoInstruction _inst);
void fmrx(UGeckoInstruction _inst);
// Floating point loadStore
void lfs(UGeckoInstruction _inst);
};
#endif // _JIT64_H
@@ -0,0 +1,46 @@
// Copyright (C) 2003 Dolphin Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
// Enable define below to enable oprofile integration. For this to work,
// it requires at least oprofile version 0.9.4, and changing the build
// system to link the Dolphin executable against libopagent. Since the
// dependency is a little inconvenient and this is possibly a slight
// performance hit, it's not enabled by default, but it's useful for
// locating performance issues.
#include "../JitInterface.h"
#include "JitArmCache.h"
using namespace ArmGen;
void JitArmBlockCache::WriteLinkBlock(u8* location, const u8* address)
{
ARMXEmitter emit(location);
emit.B(address);
}
void JitArmBlockCache::WriteDestroyBlock(const u8* location, u32 address)
{
ARMXEmitter emit((u8 *)location);
emit.MOVI2R(R10, (u32)&PC);
emit.MOVI2R(R11, address);
emit.MOVI2R(R12, (u32)jit->GetAsmRoutines()->dispatcher);
emit.STR(R10, R11);
emit.B(R12);
}
@@ -0,0 +1,33 @@
// Copyright (C) 2003 Dolphin Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#ifndef _JITARMCACHE_H
#define _JITARMCACHE_H
#include "../JitCommon/JitCache.h"
typedef void (*CompiledCode)();
class JitArmBlockCache : public JitBaseBlockCache
{
private:
void WriteLinkBlock(u8* location, const u8* address);
void WriteDestroyBlock(const u8* location, u32 address);
};
#endif
@@ -0,0 +1,164 @@
// Copyright (C) 2003 Dolphin Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#include <string>
#include "Common.h"
#include "../../HW/Memmap.h"
#include "Jit.h"
#include "../JitCommon/JitBackpatch.h"
#include "StringUtil.h"
#ifdef _M_X64
static void BackPatchError(const std::string &text, u8 *codePtr, u32 emAddress) {
u64 code_addr = (u64)codePtr;
disassembler disasm;
char disbuf[256];
memset(disbuf, 0, 256);
#ifdef _M_IX86
disasm.disasm32(0, code_addr, codePtr, disbuf);
#else
disasm.disasm64(0, code_addr, codePtr, disbuf);
#endif
PanicAlert("%s\n\n"
"Error encountered accessing emulated address %08x.\n"
"Culprit instruction: \n%s\nat %#llx",
text.c_str(), emAddress, disbuf, code_addr);
return;
}
#endif
// This generates some fairly heavy trampolines, but:
// 1) It's really necessary. We don't know anything about the context.
// 2) It doesn't really hurt. Only instructions that access I/O will get these, and there won't be
// that many of them in a typical program/game.
bool DisamLoadStore(const u32 inst, ARMReg &rD, u8 &accessSize, bool &Store)
{
u8 op = (inst >> 20) & 0xFF;
printf("op: 0x%08x\n", op);
switch (op)
{
case 0x58: // STR
{
rD = (ARMReg)((inst >> 16) & 0xF);
Store = true;
accessSize = 32;
}
break;
case 0x59: // LDR
{
rD = (ARMReg)((inst >> 16) & 0xF);
Store = false;
accessSize = 32;
}
break;
case 0x05: // LDRH
{
rD = (ARMReg)((inst >> 16) & 0xF);
Store = false;
accessSize = 16;
}
break;
case 0x45 + 0x18: // LDRB
{
rD = (ARMReg)((inst >> 16) & 0xF);
Store = false;
accessSize = 8;
}
break;
case 0x44 + 0x18: // STRB
default:
return false;
}
return true;
}
const u8 *JitArm::BackPatch(u8 *codePtr, int accessType, u32 emAddress, void *ctx_void)
{
// TODO: This ctx needs to be filled with our information
CONTEXT *ctx = (CONTEXT *)ctx_void;
// We need to get the destination register before we start
u32 Value = *(u32*)codePtr;
ARMReg rD;
u8 accessSize;
bool Store;
if (!DisamLoadStore(Value, rD, accessSize, Store))
{
printf("Invalid backpatch at location 0x%08x(0x%08x)\n", ctx->reg_pc, Value);
exit(0);
}
if (Store)
{
const u32 ARMREGOFFSET = 4 * 7;
ARMXEmitter emitter(codePtr - ARMREGOFFSET);
switch (accessSize)
{
case 8: // 8bit
//emitter.MOVI2R(R14, (u32)&Memory::Write_U8, false); // 1-2
return 0;
break;
case 16: // 16bit
//emitter.MOVI2R(R14, (u32)&Memory::Write_U16, false); // 1-2
return 0;
break;
case 32: // 32bit
emitter.MOVI2R(R14, (u32)&Memory::Write_U32, false); // 1-2
break;
}
emitter.PUSH(4, R0, R1, R2, R3); // 3
emitter.MOV(R0, rD); // Value - 4
emitter.MOV(R1, R10); // Addr- 5
emitter.BL(R14); // 6
emitter.POP(4, R0, R1, R2, R3); // 7
emitter.NOP(1); // 8
u32 newPC = ctx->reg_pc - (ARMREGOFFSET + 4 * 4);
ctx->reg_pc = newPC;
emitter.FlushIcache();
return codePtr;
}
else
{
const u32 ARMREGOFFSET = 4 * 6;
ARMXEmitter emitter(codePtr - ARMREGOFFSET);
switch (accessSize)
{
case 8: // 8bit
emitter.MOVI2R(R14, (u32)&Memory::Read_U8, false); // 2
break;
case 16: // 16bit
emitter.MOVI2R(R14, (u32)&Memory::Read_U16, false); // 2
break;
case 32: // 32bit
emitter.MOVI2R(R14, (u32)&Memory::Read_U32, false); // 2
break;
}
emitter.PUSH(4, R0, R1, R2, R3); // 3
emitter.MOV(R0, R10); // 4
emitter.BL(R14); // 5
emitter.MOV(R14, R0); // 6
emitter.POP(4, R0, R1, R2, R3); // 7
emitter.MOV(rD, R14); // 8
ctx->reg_pc -= ARMREGOFFSET + (4 * 4);
emitter.FlushIcache();
return codePtr;
}
return 0;
}
@@ -0,0 +1,347 @@
// Copyright (C) 2003 Dolphin Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#include "Common.h"
#include "Thunk.h"
#include "../../Core.h"
#include "../PowerPC.h"
#include "../../CoreTiming.h"
#include "../PPCTables.h"
#include "ArmEmitter.h"
#include "Jit.h"
#include "JitRegCache.h"
#include "JitAsm.h"
// The branches are known good, or at least reasonably good.
// No need for a disable-mechanism.
// If defined, clears CR0 at blr and bl-s. If the assumption that
// flags never carry over between functions holds, then the task for
// an optimizer becomes much easier.
// #define ACID_TEST
// Zelda and many more games seem to pass the Acid Test.
using namespace ArmGen;
void JitArm::sc(UGeckoInstruction inst)
{
INSTRUCTION_START
JITDISABLE(Branch)
gpr.Flush();
fpr.Flush();
ARMABI_MOVI2M((u32)&PC, js.compilerPC + 4); // Destroys R12 and R14
ARMReg rA = gpr.GetReg();
LDR(rA, R9, STRUCT_OFF(PowerPC::ppcState, Exceptions));
ORR(rA, rA, EXCEPTION_SYSCALL);
STR(R9, rA, STRUCT_OFF(PowerPC::ppcState, Exceptions));
gpr.Unlock(rA);
WriteExceptionExit();
}
void JitArm::rfi(UGeckoInstruction inst)
{
INSTRUCTION_START
JITDISABLE(Branch)
gpr.Flush();
fpr.Flush();
// 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
ARMReg rA = gpr.GetReg();
ARMReg rB = gpr.GetReg();
ARMReg rC = gpr.GetReg();
ARMReg rD = gpr.GetReg();
MOVI2R(rA, (u32)&MSR);
MOVI2R(rB, (~mask) & clearMSR13);
MOVI2R(rC, mask & clearMSR13);
LDR(rD, rA);
AND(rD, rD, rB); // rD = Masked MSR
STR(rA, rD);
MOVI2R(rB, (u32)&SRR1);
LDR(rB, rB); // rB contains SRR1 here
AND(rB, rB, rC); // rB contains masked SRR1 here
ORR(rB, rD, rB); // rB = Masked MSR OR masked SRR1
STR(rA, rB); // STR rB in to rA
MOVI2R(rA, (u32)&SRR0);
LDR(rA, rA);
gpr.Unlock(rB, rC, rD);
WriteRfiExitDestInR(rA); // rA gets unlocked here
//AND(32, M(&MSR), Imm32((~mask) & clearMSR13));
//MOV(32, R(EAX), M(&SRR1));
//AND(32, R(EAX), Imm32(mask & clearMSR13));
//OR(32, M(&MSR), R(EAX));
// NPC = SRR0;
//MOV(32, R(EAX), M(&SRR0));
//WriteRfiExitDestInEAX();
}
void JitArm::bx(UGeckoInstruction inst)
{
INSTRUCTION_START
JITDISABLE(Branch)
// We must always process the following sentence
// even if the blocks are merged by PPCAnalyst::Flatten().
if (inst.LK)
ARMABI_MOVI2M((u32)&LR, js.compilerPC + 4);
// If this is not the last instruction of a block,
// we will skip the rest process.
// Because PPCAnalyst::Flatten() merged the blocks.
if (!js.isLastInstruction) {
return;
}
gpr.Flush();
fpr.Flush();
u32 destination;
if (inst.AA)
destination = SignExt26(inst.LI << 2);
else
destination = js.compilerPC + SignExt26(inst.LI << 2);
#ifdef ACID_TEST
// TODO: Not implemented yet.
//if (inst.LK)
//AND(32, M(&PowerPC::ppcState.cr), Imm32(~(0xFF000000)));
#endif
if (destination == js.compilerPC)
{
//PanicAlert("Idle loop detected at %08x", destination);
// CALL(ProtectFunction(&CoreTiming::Idle, 0));
// JMP(Asm::testExceptions, true);
// make idle loops go faster
js.downcountAmount += 8;
}
WriteExit(destination, 0);
}
void JitArm::bcx(UGeckoInstruction inst)
{
INSTRUCTION_START
JITDISABLE(Branch)
// USES_CR
_assert_msg_(DYNA_REC, js.isLastInstruction, "bcx not last instruction of block");
gpr.Flush();
fpr.Flush();
ARMReg rA = gpr.GetReg();
ARMReg rB = gpr.GetReg();
FixupBranch pCTRDontBranch;
if ((inst.BO & BO_DONT_DECREMENT_FLAG) == 0) // Decrement and test CTR
{
MOVI2R(rA, (u32)&CTR);
LDR(rB, rA);
SUBS(rB, rB, 1);
STR(rA, rB);
//SUB(32, M(&CTR), Imm8(1));
if (inst.BO & BO_BRANCH_IF_CTR_0)
pCTRDontBranch = B_CC(CC_NEQ);
else
pCTRDontBranch = B_CC(CC_EQ);
}
FixupBranch pConditionDontBranch;
if ((inst.BO & BO_DONT_CHECK_CONDITION) == 0) // Test a CR bit
{
LDRB(rA, R9, STRUCT_OFF(PowerPC::ppcState, cr_fast) + (inst.BI >> 2));
TST(rA, 8 >> (inst.BI & 3));
//TEST(8, M(&PowerPC::ppcState.cr_fast[inst.BI >> 2]), Imm8(8 >> (inst.BI & 3)));
if (inst.BO & BO_BRANCH_IF_TRUE) // Conditional branch
pConditionDontBranch = B_CC(CC_EQ); // Zero
else
pConditionDontBranch = B_CC(CC_NEQ); // Not Zero
}
gpr.Unlock(rA, rB);
if (inst.LK)
ARMABI_MOVI2M((u32)&LR, js.compilerPC + 4); // Careful, destroys R14, R12
u32 destination;
if(inst.AA)
destination = SignExt16(inst.BD << 2);
else
destination = js.compilerPC + SignExt16(inst.BD << 2);
WriteExit(destination, 0);
if ((inst.BO & BO_DONT_CHECK_CONDITION) == 0)
SetJumpTarget( pConditionDontBranch );
if ((inst.BO & BO_DONT_DECREMENT_FLAG) == 0)
SetJumpTarget( pCTRDontBranch );
WriteExit(js.compilerPC + 4, 1);
}
void JitArm::bcctrx(UGeckoInstruction inst)
{
INSTRUCTION_START
JITDISABLE(Branch)
gpr.Flush();
fpr.Flush();
// bcctrx doesn't decrement and/or test CTR
_dbg_assert_msg_(POWERPC, 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;
if(inst.LK_3)
ARMABI_MOVI2M((u32)&LR, js.compilerPC + 4);
ARMReg rA = gpr.GetReg();
ARMReg rB = gpr.GetReg();
MOVI2R(rA, (u32)&CTR);
MVN(rB, 0x3); // 0xFFFFFFFC
LDR(rA, rA);
AND(rA, rA, rB);
gpr.Unlock(rB);
WriteExitDestInR(rA);
}
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
ARMReg rA = gpr.GetReg();
ARMReg rB = gpr.GetReg();
LDRB(rA, R9, STRUCT_OFF(PowerPC::ppcState, cr_fast) + (inst.BI >> 2));
TST(rA, 8 >> (inst.BI & 3));
CCFlags branch;
if (inst.BO_2 & BO_BRANCH_IF_TRUE)
branch = CC_EQ;
else
branch = CC_NEQ;
FixupBranch b = B_CC(branch);
MOVI2R(rA, (u32)&CTR);
LDR(rA, rA);
MVN(rB, 0x3); // 0xFFFFFFFC
AND(rA, rA, rB);
if (inst.LK_3){
ARMReg rC = gpr.GetReg(false);
u32 Jumpto = js.compilerPC + 4;
MOVI2R(rB, (u32)&LR);
MOVI2R(rC, Jumpto);
STR(rB, rC);
//ARMABI_MOVI2M((u32)&LR, js.compilerPC + 4);
}
gpr.Unlock(rB); // rA gets unlocked in WriteExitDestInR
WriteExitDestInR(rA);
SetJumpTarget(b);
WriteExit(js.compilerPC + 4, 1);
}
}
void JitArm::bclrx(UGeckoInstruction inst)
{
INSTRUCTION_START
JITDISABLE(Branch)
if (!js.isLastInstruction &&
(inst.BO & (1 << 4)) && (inst.BO & (1 << 2))) {
if (inst.LK)
{
ARMABI_MOVI2M((u32)&LR, js.compilerPC + 4);
}
return;
}
gpr.Flush();
fpr.Flush();
ARMReg rA = gpr.GetReg();
ARMReg rB = gpr.GetReg();
FixupBranch pCTRDontBranch;
if ((inst.BO & BO_DONT_DECREMENT_FLAG) == 0) // Decrement and test CTR
{
MOVI2R(rA, (u32)&CTR);
LDR(rB, rA);
SUBS(rB, rB, 1);
STR(rA, rB);
//SUB(32, M(&CTR), Imm8(1));
if (inst.BO & BO_BRANCH_IF_CTR_0)
pCTRDontBranch = B_CC(CC_NEQ);
else
pCTRDontBranch = B_CC(CC_EQ);
}
FixupBranch pConditionDontBranch;
if ((inst.BO & BO_DONT_CHECK_CONDITION) == 0) // Test a CR bit
{
LDRB(rA, R9, STRUCT_OFF(PowerPC::ppcState, cr_fast) + (inst.BI >> 2));
TST(rA, 8 >> (inst.BI & 3));
//TEST(8, M(&PowerPC::ppcState.cr_fast[inst.BI >> 2]), Imm8(8 >> (inst.BI & 3)));
if (inst.BO & BO_BRANCH_IF_TRUE) // Conditional branch
pConditionDontBranch = B_CC(CC_EQ); // Zero
else
pConditionDontBranch = B_CC(CC_NEQ); // Not Zero
}
// This below line can be used to prove that blr "eats flags" in practice.
// This observation will let us do a lot of fun observations.
#ifdef ACID_TEST
// TODO: Not yet implemented
// AND(32, M(&PowerPC::ppcState.cr), Imm32(~(0xFF000000)));
#endif
//MOV(32, R(EAX), M(&LR));
//AND(32, R(EAX), Imm32(0xFFFFFFFC));
MOVI2R(rA, (u32)&LR);
MVN(rB, 0x3); // 0xFFFFFFFC
LDR(rA, rA);
AND(rA, rA, rB);
if (inst.LK){
ARMReg rC = gpr.GetReg(false);
u32 Jumpto = js.compilerPC + 4;
MOVI2R(rB, (u32)&LR);
MOVI2R(rC, Jumpto);
STR(rB, rC);
//ARMABI_MOVI2M((u32)&LR, js.compilerPC + 4);
}
gpr.Unlock(rB); // rA gets unlocked in WriteExitDestInR
WriteExitDestInR(rA);
if ((inst.BO & BO_DONT_CHECK_CONDITION) == 0)
SetJumpTarget( pConditionDontBranch );
if ((inst.BO & BO_DONT_DECREMENT_FLAG) == 0)
SetJumpTarget( pCTRDontBranch );
WriteExit(js.compilerPC + 4, 1);
}
@@ -0,0 +1,80 @@
// Copyright (C) 2003 Dolphin Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#include "Common.h"
#include "Thunk.h"
#include "../../Core.h"
#include "../PowerPC.h"
#include "../../ConfigManager.h"
#include "../../CoreTiming.h"
#include "../PPCTables.h"
#include "ArmEmitter.h"
#include "../../HW/Memmap.h"
#include "Jit.h"
#include "JitRegCache.h"
#include "JitFPRCache.h"
#include "JitAsm.h"
void JitArm::Helper_UpdateCR1(ARMReg value)
{
// Should just update exception flags, not do any compares.
PanicAlert("CR1");
}
void JitArm::fabsx(UGeckoInstruction inst)
{
INSTRUCTION_START
JITDISABLE(FloatingPoint)
ARMReg vD = fpr.R0(inst.FD);
ARMReg vB = fpr.R0(inst.FB);
VABS(vD, vB);
if (inst.Rc) Helper_UpdateCR1(vD);
}
void JitArm::faddx(UGeckoInstruction inst)
{
INSTRUCTION_START
JITDISABLE(FloatingPoint)
ARMReg vD = fpr.R0(inst.FD);
ARMReg vA = fpr.R0(inst.FA);
ARMReg vB = fpr.R0(inst.FB);
VADD(vD, vA, vB);
if (inst.Rc) Helper_UpdateCR1(vD);
}
void JitArm::fmrx(UGeckoInstruction inst)
{
INSTRUCTION_START
JITDISABLE(FloatingPoint)
Default(inst); return;
ARMReg vD = fpr.R0(inst.FD);
ARMReg vB = fpr.R0(inst.FB);
VMOV(vD, vB);
if (inst.Rc) Helper_UpdateCR1(vD);
}
@@ -0,0 +1,297 @@
// Copyright (C) 2003 Dolphin Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#include "Common.h"
#include "Thunk.h"
#include "../../Core.h"
#include "../PowerPC.h"
#include "../../CoreTiming.h"
#include "../PPCTables.h"
#include "ArmEmitter.h"
#include "Jit.h"
#include "JitRegCache.h"
#include "JitAsm.h"
extern u32 Helper_Mask(u8 mb, u8 me);
// ADDI and RLWINMX broken for now
// Assumes that Sign and Zero flags were set by the last operation. Preserves all flags and registers.
// Jit64 ComputerRC is signed
// JIT64 GenerateRC is unsigned
void JitArm::GenerateRC(int cr) {
ARMReg rB = gpr.GetReg();
MOV(rB, 0x4); // Result > 0
SetCC(CC_EQ); MOV(rB, 0x2); // Result == 0
SetCC(CC_MI); MOV(rB, 0x8); // Result < 0
SetCC();
STRB(R9, rB, STRUCT_OFF(PowerPC::ppcState, cr_fast) + cr);
gpr.Unlock(rB);
}
void JitArm::ComputeRC(int cr) {
ARMReg rB = gpr.GetReg();
MOV(rB, 0x2); // Result == 0
SetCC(CC_LT); MOV(rB, 0x8); // Result < 0
SetCC(CC_GT); MOV(rB, 0x4); // Result > 0
SetCC();
STRB(R9, rB, STRUCT_OFF(PowerPC::ppcState, cr_fast) + cr);
gpr.Unlock(rB);
}
void JitArm::addi(UGeckoInstruction inst)
{
INSTRUCTION_START
JITDISABLE(Integer)
ARMReg RD = gpr.R(inst.RD);
if (inst.RA)
{
ARMReg rA = gpr.GetReg(false);
ARMReg RA = gpr.R(inst.RA);
MOVI2R(rA, (u32)inst.SIMM_16);
ADD(RD, RA, rA);
}
else
MOVI2R(RD, inst.SIMM_16);
}
void JitArm::addis(UGeckoInstruction inst)
{
INSTRUCTION_START
JITDISABLE(Integer)
ARMReg RD = gpr.R(inst.RD);
if (inst.RA)
{
ARMReg rA = gpr.GetReg(false);
ARMReg RA = gpr.R(inst.RA);
MOVI2R(rA, inst.SIMM_16 << 16);
ADD(RD, RA, rA);
}
else
MOVI2R(RD, inst.SIMM_16 << 16);
}
void JitArm::addx(UGeckoInstruction inst)
{
INSTRUCTION_START
JITDISABLE(Integer)
ARMReg RA = gpr.R(inst.RA);
ARMReg RB = gpr.R(inst.RB);
ARMReg RD = gpr.R(inst.RD);
ADDS(RD, RA, RB);
if (inst.Rc) ComputeRC();
}
// Wrong - 28/10/2012
void JitArm::mulli(UGeckoInstruction inst)
{
INSTRUCTION_START
JITDISABLE(Integer)
Default(inst); return;
ARMReg RA = gpr.R(inst.RA);
ARMReg RD = gpr.R(inst.RD);
ARMReg rA = gpr.GetReg();
MOVI2R(rA, inst.SIMM_16);
MUL(RD, RA, rA);
gpr.Unlock(rA);
}
void JitArm::ori(UGeckoInstruction inst)
{
INSTRUCTION_START
JITDISABLE(Integer)
ARMReg RA = gpr.R(inst.RA);
ARMReg RS = gpr.R(inst.RS);
ARMReg rA = gpr.GetReg();
MOVI2R(rA, inst.UIMM);
ORR(RA, RS, rA);
gpr.Unlock(rA);
}
void JitArm::oris(UGeckoInstruction inst)
{
INSTRUCTION_START
JITDISABLE(Integer)
ARMReg RA = gpr.R(inst.RA);
ARMReg RS = gpr.R(inst.RS);
ARMReg rA = gpr.GetReg();
MOVI2R(rA, inst.UIMM << 16);
ORR(RA, RS, rA);
gpr.Unlock(rA);
}
void JitArm::orx(UGeckoInstruction inst)
{
INSTRUCTION_START
JITDISABLE(Integer)
ARMReg rA = gpr.R(inst.RA);
ARMReg rS = gpr.R(inst.RS);
ARMReg rB = gpr.R(inst.RB);
ORRS(rA, rS, rB);
if (inst.Rc)
ComputeRC();
}
void JitArm::extshx(UGeckoInstruction inst)
{
INSTRUCTION_START
JITDISABLE(Integer)
ARMReg RA, RS;
RA = gpr.R(inst.RA);
RS = gpr.R(inst.RS);
SXTH(RA, RS);
if (inst.Rc){
CMP(RA, 0);
ComputeRC();
}
}
void JitArm::extsbx(UGeckoInstruction inst)
{
INSTRUCTION_START
JITDISABLE(Integer)
ARMReg RA, RS;
RA = gpr.R(inst.RA);
RS = gpr.R(inst.RS);
SXTB(RA, RS);
if (inst.Rc){
CMP(RA, 0);
ComputeRC();
}
}
void JitArm::cmp (UGeckoInstruction inst)
{
INSTRUCTION_START
JITDISABLE(Integer)
ARMReg RA = gpr.R(inst.RA);
ARMReg RB = gpr.R(inst.RB);
int crf = inst.CRFD;
CMP(RA, RB);
ComputeRC(crf);
}
void JitArm::cmpi(UGeckoInstruction inst)
{
INSTRUCTION_START
JITDISABLE(Integer)
ARMReg RA = gpr.R(inst.RA);
ARMReg rA = gpr.GetReg();
int crf = inst.CRFD;
MOVI2R(rA, inst.SIMM_16);
CMP(RA, rA);
gpr.Unlock(rA);
ComputeRC(crf);
}
void JitArm::cmpli(UGeckoInstruction inst)
{
INSTRUCTION_START
JITDISABLE(Integer)
ARMReg RA = gpr.R(inst.RA);
ARMReg rA = gpr.GetReg();
int crf = inst.CRFD;
MOVI2R(rA, (u32)inst.UIMM);
CMP(RA, rA);
// Unsigned GenerateRC()
MOV(rA, 0x2); // Result == 0
SetCC(CC_LO); MOV(rA, 0x8); // Result < 0
SetCC(CC_HI); MOV(rA, 0x4); // Result > 0
SetCC();
STRB(R9, rA, STRUCT_OFF(PowerPC::ppcState, cr_fast) + crf);
gpr.Unlock(rA);
}
// Wrong - 27/10/2012
void JitArm::negx(UGeckoInstruction inst)
{
INSTRUCTION_START
JITDISABLE(Integer)
Default(inst);return;
ARMReg RA = gpr.R(inst.RA);
ARMReg RD = gpr.R(inst.RD);
RSBS(RD, RA, 0);
if (inst.Rc)
{
GenerateRC();
}
if (inst.OE)
{
BKPT(0x333);
//GenerateOverflow();
}
}
void JitArm::rlwimix(UGeckoInstruction inst)
{
INSTRUCTION_START
JITDISABLE(Integer)
u32 mask = Helper_Mask(inst.MB,inst.ME);
ARMReg RA = gpr.R(inst.RA);
ARMReg RS = gpr.R(inst.RS);
ARMReg rA = gpr.GetReg();
ARMReg rB = gpr.GetReg();
MOVI2R(rA, mask);
Operand2 Shift(32 - inst.SH, ST_ROR, RS); // This rotates left, while ARM has only rotate right, so swap it.
if (inst.Rc)
{
BIC (rB, RA, rA); // RA & ~mask
AND (rA, rA, Shift);
ORRS(RA, rB, rA);
GenerateRC();
}
else
{
BIC (rB, RA, rA); // RA & ~mask
AND (rA, rA, Shift);
ORR(RA, rB, rA);
}
gpr.Unlock(rA, rB);
}
void JitArm::rlwinmx(UGeckoInstruction inst)
{
INSTRUCTION_START
JITDISABLE(Integer)
u32 mask = Helper_Mask(inst.MB,inst.ME);
ARMReg RA = gpr.R(inst.RA);
ARMReg RS = gpr.R(inst.RS);
ARMReg rA = gpr.GetReg();
MOVI2R(rA, mask);
Operand2 Shift(32 - inst.SH, ST_ROR, RS); // This rotates left, while ARM has only rotate right, so swap it.
if (inst.Rc)
{
ANDS(RA, rA, Shift);
GenerateRC();
}
else
AND (RA, rA, Shift);
gpr.Unlock(rA);
//m_GPR[inst.RA] = _rotl(m_GPR[inst.RS],inst.SH) & mask;
}
@@ -0,0 +1,414 @@
// Copyright (C) 2003 Dolphin Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#include "Common.h"
#include "Thunk.h"
#include "../../Core.h"
#include "../PowerPC.h"
#include "../../ConfigManager.h"
#include "../../CoreTiming.h"
#include "../PPCTables.h"
#include "ArmEmitter.h"
#include "../../HW/Memmap.h"
#include "Jit.h"
#include "JitRegCache.h"
#include "JitAsm.h"
#ifdef ANDROID
#define FASTMEM 0
#else
#define FASTMEM 0
#endif
void JitArm::stw(UGeckoInstruction inst)
{
INSTRUCTION_START
JITDISABLE(LoadStore)
ARMReg RS = gpr.R(inst.RS);
#if FASTMEM
// R10 contains the dest address
ARMReg Value = R11;
ARMReg RA;
if (inst.RA)
RA = gpr.R(inst.RA);
MOV(Value, RS);
if (inst.RA)
{
MOVI2R(R10, inst.SIMM_16, false);
ADD(R10, R10, RA);
}
else
{
MOVI2R(R10, (u32)inst.SIMM_16, false);
NOP(1);
}
StoreFromReg(R10, Value, 32, 0);
#else
ARMReg ValueReg = gpr.GetReg();
ARMReg Addr = gpr.GetReg();
ARMReg Function = gpr.GetReg();
MOV(ValueReg, RS);
if (inst.RA)
{
MOVI2R(Addr, inst.SIMM_16);
ARMReg RA = gpr.R(inst.RA);
ADD(Addr, Addr, RA);
}
else
MOVI2R(Addr, (u32)inst.SIMM_16);
MOVI2R(Function, (u32)&Memory::Write_U32);
PUSH(4, R0, R1, R2, R3);
MOV(R0, ValueReg);
MOV(R1, Addr);
BL(Function);
POP(4, R0, R1, R2, R3);
gpr.Unlock(ValueReg, Addr, Function);
#endif
}
void JitArm::stwu(UGeckoInstruction inst)
{
INSTRUCTION_START
JITDISABLE(LoadStore)
ARMReg RA = gpr.R(inst.RA);
ARMReg RS = gpr.R(inst.RS);
ARMReg ValueReg = gpr.GetReg();
ARMReg Addr = gpr.GetReg();
ARMReg Function = gpr.GetReg();
MOVI2R(Addr, inst.SIMM_16);
ADD(Addr, Addr, RA);
// Check and set the update before writing since calling a function can
// mess with the "special registers R11+ which may cause some issues.
LDR(Function, R9, STRUCT_OFF(PowerPC::ppcState, Exceptions));
CMP(Function, EXCEPTION_DSI);
FixupBranch DoNotWrite = B_CC(CC_EQ);
MOV(RA, Addr);
SetJumpTarget(DoNotWrite);
MOV(ValueReg, RS);
MOVI2R(Function, (u32)&Memory::Write_U32);
PUSH(4, R0, R1, R2, R3);
MOV(R0, ValueReg);
MOV(R1, Addr);
BL(Function);
POP(4, R0, R1, R2, R3);
gpr.Unlock(ValueReg, Addr, Function);
}
void JitArm::StoreFromReg(ARMReg dest, ARMReg value, int accessSize, s32 offset)
{
ARMReg rA = gpr.GetReg();
// All this gets replaced on backpatch
MOVI2R(rA, Memory::MEMVIEW32_MASK, false); // 1-2
AND(dest, dest, rA); // 3
MOVI2R(rA, (u32)Memory::base, false); // 4-5
ADD(dest, dest, rA); // 6
switch (accessSize)
{
case 32:
REV(value, value); // 7
break;
case 16:
REV16(value, value);
break;
case 8:
NOP(1);
break;
}
switch (accessSize)
{
case 32:
STR(dest, value); // 8
break;
case 16:
// Not implemented
break;
case 8:
// Not implemented
break;
}
gpr.Unlock(rA);
}
void JitArm::LoadToReg(ARMReg dest, ARMReg addr, int accessSize, s32 offset)
{
ARMReg rA = gpr.GetReg();
MOVI2R(rA, offset, false); // -3
ADD(addr, addr, rA); // - 1
// All this gets replaced on backpatch
MOVI2R(rA, Memory::MEMVIEW32_MASK, false); // 2
AND(addr, addr, rA); // 3
MOVI2R(rA, (u32)Memory::base, false); // 5
ADD(addr, addr, rA); // 6
switch (accessSize)
{
case 32:
LDR(dest, addr); // 7
break;
case 16:
LDRH(dest, addr);
break;
case 8:
LDRB(dest, addr);
break;
}
switch (accessSize)
{
case 32:
REV(dest, dest); // 9
break;
case 16:
REV16(dest, dest);
break;
case 8:
NOP(1);
break;
}
gpr.Unlock(rA);
}
void JitArm::lbz(UGeckoInstruction inst)
{
INSTRUCTION_START
JITDISABLE(LoadStore)
ARMReg rA = gpr.GetReg();
ARMReg rB = gpr.GetReg();
ARMReg RD = gpr.R(inst.RD);
LDR(rA, R9, STRUCT_OFF(PowerPC::ppcState, Exceptions));
CMP(rA, EXCEPTION_DSI);
FixupBranch DoNotLoad = B_CC(CC_EQ);
#if FASTMEM
// Backpatch route
// Gets loaded in to RD
// Address is in R10
gpr.Unlock(rA, rB);
if (inst.RA)
{
ARMReg RA = gpr.R(inst.RA);
MOV(R10, RA); // - 4
}
else
MOV(R10, 0); // - 4
LoadToReg(RD, R10, 8, inst.SIMM_16);
#else
if (inst.RA)
{
MOVI2R(rB, inst.SIMM_16);
ARMReg RA = gpr.R(inst.RA);
ADD(rB, rB, RA);
}
else
MOVI2R(rB, (u32)inst.SIMM_16);
MOVI2R(rA, (u32)&Memory::Read_U8);
PUSH(4, R0, R1, R2, R3);
MOV(R0, rB);
BL(rA);
MOV(rA, R0);
POP(4, R0, R1, R2, R3);
MOV(RD, rA);
gpr.Unlock(rA, rB);
#endif
SetJumpTarget(DoNotLoad);
}
void JitArm::lhz(UGeckoInstruction inst)
{
INSTRUCTION_START
JITDISABLE(LoadStore)
ARMReg rA = gpr.GetReg();
ARMReg rB = gpr.GetReg();
ARMReg RD = gpr.R(inst.RD);
LDR(rA, R9, STRUCT_OFF(PowerPC::ppcState, Exceptions));
CMP(rA, EXCEPTION_DSI);
FixupBranch DoNotLoad = B_CC(CC_EQ);
#if 0 // FASTMEM
// Backpatch route
// Gets loaded in to RD
// Address is in R10
gpr.Unlock(rA, rB);
if (inst.RA)
{
ARMReg RA = gpr.R(inst.RA);
printf("lhz jump to here: 0x%08x\n", (u32)GetCodePtr());
MOV(R10, RA); // - 4
}
else
{
printf("lhz jump to here: 0x%08x\n", (u32)GetCodePtr());
MOV(R10, 0); // - 4
}
LoadToReg(RD, R10, 16, (u32)inst.SIMM_16);
#else
if (inst.RA)
{
MOVI2R(rB, inst.SIMM_16);
ARMReg RA = gpr.R(inst.RA);
ADD(rB, rB, RA);
}
else
MOVI2R(rB, (u32)inst.SIMM_16);
MOVI2R(rA, (u32)&Memory::Read_U16);
PUSH(4, R0, R1, R2, R3);
MOV(R0, rB);
BL(rA);
MOV(rA, R0);
POP(4, R0, R1, R2, R3);
MOV(RD, rA);
gpr.Unlock(rA, rB);
#endif
SetJumpTarget(DoNotLoad);
}
void JitArm::lwz(UGeckoInstruction inst)
{
INSTRUCTION_START
JITDISABLE(LoadStore)
ARMReg rA = gpr.GetReg();
ARMReg rB = gpr.GetReg();
ARMReg RD = gpr.R(inst.RD);
LDR(rA, R9, STRUCT_OFF(PowerPC::ppcState, Exceptions));
CMP(rA, EXCEPTION_DSI);
FixupBranch DoNotLoad = B_CC(CC_EQ);
#if FASTMEM
// Backpatch route
// Gets loaded in to RD
// Address is in R10
gpr.Unlock(rA, rB);
if (inst.RA)
{
ARMReg RA = gpr.R(inst.RA);
MOV(R10, RA); // - 4
}
else
MOV(R10, 0); // - 4
LoadToReg(RD, R10, 32, (u32)inst.SIMM_16);
#else
if (inst.RA)
{
MOVI2R(rB, inst.SIMM_16);
ARMReg RA = gpr.R(inst.RA);
ADD(rB, rB, RA);
}
else
MOVI2R(rB, (u32)inst.SIMM_16);
MOVI2R(rA, (u32)&Memory::Read_U32);
PUSH(4, R0, R1, R2, R3);
MOV(R0, rB);
BL(rA);
MOV(rA, R0);
POP(4, R0, R1, R2, R3);
MOV(RD, rA);
gpr.Unlock(rA, rB);
#endif
SetJumpTarget(DoNotLoad);
if (SConfig::GetInstance().m_LocalCoreStartupParameter.bSkipIdle &&
(inst.hex & 0xFFFF0000) == 0x800D0000 &&
(Memory::ReadUnchecked_U32(js.compilerPC + 4) == 0x28000000 ||
(SConfig::GetInstance().m_LocalCoreStartupParameter.bWii && Memory::ReadUnchecked_U32(js.compilerPC + 4) == 0x2C000000)) &&
Memory::ReadUnchecked_U32(js.compilerPC + 8) == 0x4182fff8)
{
gpr.Flush();
fpr.Flush();
// if it's still 0, we can wait until the next event
TST(RD, RD);
FixupBranch noIdle = B_CC(CC_NEQ);
rA = gpr.GetReg();
MOVI2R(rA, (u32)&PowerPC::OnIdle);
MOVI2R(R0, PowerPC::ppcState.gpr[inst.RA] + (s32)(s16)inst.SIMM_16);
BL(rA);
gpr.Unlock(rA);
WriteExceptionExit();
SetJumpTarget(noIdle);
//js.compilerPC += 8;
return;
}
}
void JitArm::lwzx(UGeckoInstruction inst)
{
INSTRUCTION_START
JITDISABLE(LoadStore)
ARMReg rA = gpr.GetReg();
ARMReg rB = gpr.GetReg();
ARMReg RB = gpr.R(inst.RB);
ARMReg RD = gpr.R(inst.RD);
LDR(rA, R9, STRUCT_OFF(PowerPC::ppcState, Exceptions));
CMP(rA, EXCEPTION_DSI);
FixupBranch DoNotLoad = B_CC(CC_EQ);
#if FASTMEM
// Backpatch route
// Gets loaded in to RD
// Address is in R10
gpr.Unlock(rA, rB);
if (inst.RA)
{
ARMReg RA = gpr.R(inst.RA);
ADD(R10, RA, RB); // - 4
}
else
MOV(R10, RB); // -4
LoadToReg(RD, R10, 32, 0);
#else
if (inst.RA)
{
ARMReg RA = gpr.R(inst.RA);
ADD(rB, RA, RB);
}
else
MOV(rB, RB);
MOVI2R(rA, (u32)&Memory::Read_U32);
PUSH(4, R0, R1, R2, R3);
MOV(R0, rB);
BL(rA);
MOV(rA, R0);
POP(4, R0, R1, R2, R3);
MOV(RD, rA);
gpr.Unlock(rA, rB);
#endif
SetJumpTarget(DoNotLoad);
//// u32 temp = Memory::Read_U32(_inst.RA ? (m_GPR[_inst.RA] + m_GPR[_inst.RB]) : m_GPR[_inst.RB]);
}
void JitArm::icbi(UGeckoInstruction inst)
{
Default(inst);
WriteExit(js.compilerPC + 4, 0);
}
@@ -0,0 +1,72 @@
// Copyright (C) 2003 Dolphin Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#include "Common.h"
#include "Thunk.h"
#include "../../Core.h"
#include "../PowerPC.h"
#include "../../ConfigManager.h"
#include "../../CoreTiming.h"
#include "../PPCTables.h"
#include "ArmEmitter.h"
#include "../../HW/Memmap.h"
#include "Jit.h"
#include "JitRegCache.h"
#include "JitFPRCache.h"
#include "JitAsm.h"
void JitArm::lfs(UGeckoInstruction inst)
{
INSTRUCTION_START
JITDISABLE(LoadStoreFloating)
Default(inst); return;
ARMReg rA = gpr.GetReg();
ARMReg rB = gpr.GetReg();
LDR(rA, R9, STRUCT_OFF(PowerPC::ppcState, Exceptions));
CMP(rA, EXCEPTION_DSI);
FixupBranch DoNotLoad = B_CC(CC_EQ);
if (inst.RA)
{
MOVI2R(rB, inst.SIMM_16);
ARMReg RA = gpr.R(inst.RA);
ADD(rB, rB, RA);
}
else
MOVI2R(rB, (u32)inst.SIMM_16);
MOVI2R(rA, (u32)&Memory::Read_U32);
PUSH(4, R0, R1, R2, R3);
MOV(R0, rB);
BL(rA);
MOV(rA, R0);
POP(4, R0, R1, R2, R3);
ARMReg v0 = fpr.R0(inst.FD, false);
ARMReg v1 = fpr.R1(inst.FD, false);
VMOV(v0, rA, false);
VMOV(v1, rA, false);
gpr.Unlock(rA, rB);
SetJumpTarget(DoNotLoad);
}
@@ -0,0 +1,111 @@
// Copyright (C) 2003 Dolphin Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#include "Common.h"
#include "Thunk.h"
#include "../../Core.h"
#include "../PowerPC.h"
#include "../../CoreTiming.h"
#include "../PPCTables.h"
#include "ArmEmitter.h"
#include "Jit.h"
#include "JitRegCache.h"
#include "JitAsm.h"
void JitArm::mtspr(UGeckoInstruction inst)
{
INSTRUCTION_START
JITDISABLE(SystemRegisters)
u32 iIndex = (inst.SPRU << 5) | (inst.SPRL & 0x1F);
ARMReg RD = gpr.R(inst.RD);
switch (iIndex)
{
case SPR_LR:
case SPR_CTR:
case SPR_XER:
// These are safe to do the easy way, see the bottom of this function.
break;
case SPR_GQR0:
case SPR_GQR0 + 1:
case SPR_GQR0 + 2:
case SPR_GQR0 + 3:
case SPR_GQR0 + 4:
case SPR_GQR0 + 5:
case SPR_GQR0 + 6:
case SPR_GQR0 + 7:
// Prevent recompiler from compiling in old quantizer values.
// If the value changed, destroy all blocks using this quantizer
// This will create a little bit of block churn, but hopefully not too bad.
{
/*
MOV(32, R(EAX), M(&PowerPC::ppcState.spr[iIndex])); // Load old value
CMP(32, R(EAX), gpr.R(inst.RD));
FixupBranch skip_destroy = J_CC(CC_E, false);
int gqr = iIndex - SPR_GQR0;
ABI_CallFunctionC(ProtectFunction(&Jit64::DestroyBlocksWithFlag, 1), (u32)BLOCK_USE_GQR0 << gqr);
SetJumpTarget(skip_destroy);*/
}
// TODO - break block if quantizers are written to.
default:
Default(inst);
return;
}
// OK, this is easy.
ARMReg rA = gpr.GetReg(false);
MOVI2R(rA, (u32)&PowerPC::ppcState.spr);
STR(rA, RD, iIndex * 4);
}
void JitArm::mfspr(UGeckoInstruction inst)
{
INSTRUCTION_START
JITDISABLE(SystemRegisters)
u32 iIndex = (inst.SPRU << 5) | (inst.SPRL & 0x1F);
ARMReg RD = gpr.R(inst.RD);
switch (iIndex)
{
case SPR_WPAR:
case SPR_DEC:
case SPR_TL:
case SPR_TU:
Default(inst);
return;
default:
ARMReg rA = gpr.GetReg(false);
MOVI2R(rA, (u32)&PowerPC::ppcState.spr);
LDR(RD, rA, iIndex * 4);
break;
}
}
void JitArm::mtmsr(UGeckoInstruction inst)
{
INSTRUCTION_START
// Don't interpret this, if we do we get thrown out
//JITDISABLE(SystemRegisters)
ARMReg rA = gpr.GetReg();
MOVI2R(rA, (u32)&MSR);
STR(rA, gpr.R(inst.RS));
gpr.Unlock(rA);
WriteExit(js.compilerPC + 4, 0);
}
@@ -0,0 +1,502 @@
// Copyright (C) 2003 Dolphin Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#include "Jit.h"
#include "../JitInterface.h"
#include "JitArm_Tables.h"
// Should be moved in to the Jit class
typedef void (JitArm::*_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 JitArm::DynaRunTable4(UGeckoInstruction _inst) {(this->*dynaOpTable4 [_inst.SUBOP10])(_inst);}
void JitArm::DynaRunTable19(UGeckoInstruction _inst) {(this->*dynaOpTable19[_inst.SUBOP10])(_inst);}
void JitArm::DynaRunTable31(UGeckoInstruction _inst) {(this->*dynaOpTable31[_inst.SUBOP10])(_inst);}
void JitArm::DynaRunTable59(UGeckoInstruction _inst) {(this->*dynaOpTable59[_inst.SUBOP5 ])(_inst);}
void JitArm::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, &JitArm::DynaRunTable4}, //"RunTable4", OPTYPE_SUBTABLE | (4<<24), 0}},
{19, &JitArm::DynaRunTable19}, //"RunTable19", OPTYPE_SUBTABLE | (19<<24), 0}},
{31, &JitArm::DynaRunTable31}, //"RunTable31", OPTYPE_SUBTABLE | (31<<24), 0}},
{59, &JitArm::DynaRunTable59}, //"RunTable59", OPTYPE_SUBTABLE | (59<<24), 0}},
{63, &JitArm::DynaRunTable63}, //"RunTable63", OPTYPE_SUBTABLE | (63<<24), 0}},
{16, &JitArm::bcx}, //"bcx", OPTYPE_SYSTEM, FL_ENDBLOCK}},
{18, &JitArm::bx}, //"bx", OPTYPE_SYSTEM, FL_ENDBLOCK}},
{1, &JitArm::HLEFunction}, //"HLEFunction", OPTYPE_SYSTEM, FL_ENDBLOCK}},
{2, &JitArm::Default}, //"DynaBlock", OPTYPE_SYSTEM, 0}},
{3, &JitArm::Break}, //"twi", OPTYPE_SYSTEM, FL_ENDBLOCK}},
{17, &JitArm::sc}, //"sc", OPTYPE_SYSTEM, FL_ENDBLOCK, 1}},
{7, &JitArm::mulli}, //"mulli", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_RC_BIT, 2}},
{8, &JitArm::Default}, //"subfic", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_SET_CA}},
{10, &JitArm::cmpli}, //"cmpli", OPTYPE_INTEGER, FL_IN_A | FL_SET_CRn}},
{11, &JitArm::cmpi}, //"cmpi", OPTYPE_INTEGER, FL_IN_A | FL_SET_CRn}},
{12, &JitArm::Default}, //"addic", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_SET_CA}},
{13, &JitArm::Default}, //"addic_rc", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_SET_CR0}},
{14, &JitArm::addi}, //"addi", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A0}},
{15, &JitArm::addis}, //"addis", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A0}},
{20, &JitArm::rlwimix}, //"rlwimix", OPTYPE_INTEGER, FL_OUT_A | FL_IN_A | FL_IN_S | FL_RC_BIT}},
{21, &JitArm::rlwinmx}, //"rlwinmx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_RC_BIT}},
{23, &JitArm::Default}, //"rlwnmx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_IN_B | FL_RC_BIT}},
{24, &JitArm::ori}, //"ori", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S}},
{25, &JitArm::oris}, //"oris", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S}},
{26, &JitArm::Default}, //"xori", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S}},
{27, &JitArm::Default}, //"xoris", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S}},
{28, &JitArm::Default}, //"andi_rc", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_SET_CR0}},
{29, &JitArm::Default}, //"andis_rc", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_SET_CR0}},
{32, &JitArm::lwz}, //"lwz", OPTYPE_LOAD, FL_OUT_D | FL_IN_A}},
{33, &JitArm::Default}, //"lwzu", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A}},
{34, &JitArm::lbz}, //"lbz", OPTYPE_LOAD, FL_OUT_D | FL_IN_A}},
{35, &JitArm::Default}, //"lbzu", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A}},
{40, &JitArm::lhz}, //"lhz", OPTYPE_LOAD, FL_OUT_D | FL_IN_A}},
{41, &JitArm::Default}, //"lhzu", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A}},
{42, &JitArm::Default}, //"lha", OPTYPE_LOAD, FL_OUT_D | FL_IN_A}},
{43, &JitArm::Default}, //"lhau", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A}},
{44, &JitArm::Default}, //"sth", OPTYPE_STORE, FL_IN_A | FL_IN_S}},
{45, &JitArm::Default}, //"sthu", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_S}},
{36, &JitArm::stw}, //"stw", OPTYPE_STORE, FL_IN_A | FL_IN_S}},
{37, &JitArm::stwu}, //"stwu", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_S}},
{38, &JitArm::Default}, //"stb", OPTYPE_STORE, FL_IN_A | FL_IN_S}},
{39, &JitArm::Default}, //"stbu", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_S}},
{46, &JitArm::Default}, //"lmw", OPTYPE_SYSTEM, FL_EVIL, 10}},
{47, &JitArm::Default}, //"stmw", OPTYPE_SYSTEM, FL_EVIL, 10}},
{48, &JitArm::lfs}, //"lfs", OPTYPE_LOADFP, FL_IN_A}},
{49, &JitArm::Default}, //"lfsu", OPTYPE_LOADFP, FL_OUT_A | FL_IN_A}},
{50, &JitArm::Default}, //"lfd", OPTYPE_LOADFP, FL_IN_A}},
{51, &JitArm::Default}, //"lfdu", OPTYPE_LOADFP, FL_OUT_A | FL_IN_A}},
{52, &JitArm::Default}, //"stfs", OPTYPE_STOREFP, FL_IN_A}},
{53, &JitArm::Default}, //"stfsu", OPTYPE_STOREFP, FL_OUT_A | FL_IN_A}},
{54, &JitArm::Default}, //"stfd", OPTYPE_STOREFP, FL_IN_A}},
{55, &JitArm::Default}, //"stfdu", OPTYPE_STOREFP, FL_OUT_A | FL_IN_A}},
{56, &JitArm::Default}, //"psq_l", OPTYPE_PS, FL_IN_A}},
{57, &JitArm::Default}, //"psq_lu", OPTYPE_PS, FL_OUT_A | FL_IN_A}},
{60, &JitArm::Default}, //"psq_st", OPTYPE_PS, FL_IN_A}},
{61, &JitArm::Default}, //"psq_stu", OPTYPE_PS, FL_OUT_A | FL_IN_A}},
//missing: 0, 5, 6, 9, 22, 30, 62, 58
{0, &JitArm::Default}, //"unknown_instruction", OPTYPE_UNKNOWN, 0}},
{5, &JitArm::Default}, //"unknown_instruction", OPTYPE_UNKNOWN, 0}},
{6, &JitArm::Default}, //"unknown_instruction", OPTYPE_UNKNOWN, 0}},
{9, &JitArm::Default}, //"unknown_instruction", OPTYPE_UNKNOWN, 0}},
{22, &JitArm::Default}, //"unknown_instruction", OPTYPE_UNKNOWN, 0}},
{30, &JitArm::Default}, //"unknown_instruction", OPTYPE_UNKNOWN, 0}},
{62, &JitArm::Default}, //"unknown_instruction", OPTYPE_UNKNOWN, 0}},
{58, &JitArm::Default}, //"unknown_instruction", OPTYPE_UNKNOWN, 0}},
};
static GekkoOPTemplate table4[] =
{ //SUBOP10
{0, &JitArm::Default}, //"ps_cmpu0", OPTYPE_PS, FL_SET_CRn}},
{32, &JitArm::Default}, //"ps_cmpo0", OPTYPE_PS, FL_SET_CRn}},
{40, &JitArm::Default}, //"ps_neg", OPTYPE_PS, FL_RC_BIT}},
{136, &JitArm::Default}, //"ps_nabs", OPTYPE_PS, FL_RC_BIT}},
{264, &JitArm::Default}, //"ps_abs", OPTYPE_PS, FL_RC_BIT}},
{64, &JitArm::Default}, //"ps_cmpu1", OPTYPE_PS, FL_RC_BIT}},
{72, &JitArm::Default}, //"ps_mr", OPTYPE_PS, FL_RC_BIT}},
{96, &JitArm::Default}, //"ps_cmpo1", OPTYPE_PS, FL_RC_BIT}},
{528, &JitArm::Default}, //"ps_merge00", OPTYPE_PS, FL_RC_BIT}},
{560, &JitArm::Default}, //"ps_merge01", OPTYPE_PS, FL_RC_BIT}},
{592, &JitArm::Default}, //"ps_merge10", OPTYPE_PS, FL_RC_BIT}},
{624, &JitArm::Default}, //"ps_merge11", OPTYPE_PS, FL_RC_BIT}},
{1014, &JitArm::Default}, //"dcbz_l", OPTYPE_SYSTEM, 0}},
};
static GekkoOPTemplate table4_2[] =
{
{10, &JitArm::Default}, //"ps_sum0", OPTYPE_PS, 0}},
{11, &JitArm::Default}, //"ps_sum1", OPTYPE_PS, 0}},
{12, &JitArm::Default}, //"ps_muls0", OPTYPE_PS, 0}},
{13, &JitArm::Default}, //"ps_muls1", OPTYPE_PS, 0}},
{14, &JitArm::Default}, //"ps_madds0", OPTYPE_PS, 0}},
{15, &JitArm::Default}, //"ps_madds1", OPTYPE_PS, 0}},
{18, &JitArm::Default}, //"ps_div", OPTYPE_PS, 0, 16}},
{20, &JitArm::Default}, //"ps_sub", OPTYPE_PS, 0}},
{21, &JitArm::Default}, //"ps_add", OPTYPE_PS, 0}},
{23, &JitArm::Default}, //"ps_sel", OPTYPE_PS, 0}},
{24, &JitArm::Default}, //"ps_res", OPTYPE_PS, 0}},
{25, &JitArm::Default}, //"ps_mul", OPTYPE_PS, 0}},
{26, &JitArm::Default}, //"ps_rsqrte", OPTYPE_PS, 0, 1}},
{28, &JitArm::Default}, //"ps_msub", OPTYPE_PS, 0}},
{29, &JitArm::Default}, //"ps_madd", OPTYPE_PS, 0}},
{30, &JitArm::Default}, //"ps_nmsub", OPTYPE_PS, 0}},
{31, &JitArm::Default}, //"ps_nmadd", OPTYPE_PS, 0}},
};
static GekkoOPTemplate table4_3[] =
{
{6, &JitArm::Default}, //"psq_lx", OPTYPE_PS, 0}},
{7, &JitArm::Default}, //"psq_stx", OPTYPE_PS, 0}},
{38, &JitArm::Default}, //"psq_lux", OPTYPE_PS, 0}},
{39, &JitArm::Default}, //"psq_stux", OPTYPE_PS, 0}},
};
static GekkoOPTemplate table19[] =
{
{528, &JitArm::bcctrx}, //"bcctrx", OPTYPE_BRANCH, FL_ENDBLOCK}},
{16, &JitArm::bclrx}, //"bclrx", OPTYPE_BRANCH, FL_ENDBLOCK}},
{257, &JitArm::Default}, //"crand", OPTYPE_CR, FL_EVIL}},
{129, &JitArm::Default}, //"crandc", OPTYPE_CR, FL_EVIL}},
{289, &JitArm::Default}, //"creqv", OPTYPE_CR, FL_EVIL}},
{225, &JitArm::Default}, //"crnand", OPTYPE_CR, FL_EVIL}},
{33, &JitArm::Default}, //"crnor", OPTYPE_CR, FL_EVIL}},
{449, &JitArm::Default}, //"cror", OPTYPE_CR, FL_EVIL}},
{417, &JitArm::Default}, //"crorc", OPTYPE_CR, FL_EVIL}},
{193, &JitArm::Default}, //"crxor", OPTYPE_CR, FL_EVIL}},
{150, &JitArm::DoNothing}, //"isync", OPTYPE_ICACHE, FL_EVIL}},
{0, &JitArm::Default}, //"mcrf", OPTYPE_SYSTEM, FL_EVIL}},
{50, &JitArm::rfi}, //"rfi", OPTYPE_SYSTEM, FL_ENDBLOCK | FL_CHECKEXCEPTIONS, 1}},
{18, &JitArm::Break}, //"rfid", OPTYPE_SYSTEM, FL_ENDBLOCK | FL_CHECKEXCEPTIONS}}
};
static GekkoOPTemplate table31[] =
{
{28, &JitArm::Default}, //"andx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}},
{60, &JitArm::Default}, //"andcx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}},
{444, &JitArm::orx}, //"orx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}},
{124, &JitArm::Default}, //"norx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}},
{316, &JitArm::Default}, //"xorx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}},
{412, &JitArm::Default}, //"orcx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}},
{476, &JitArm::Default}, //"nandx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}},
{284, &JitArm::Default}, //"eqvx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}},
{0, &JitArm::cmp}, //"cmp", OPTYPE_INTEGER, FL_IN_AB | FL_SET_CRn}},
{32, &JitArm::Default}, //"cmpl", OPTYPE_INTEGER, FL_IN_AB | FL_SET_CRn}},
{26, &JitArm::Default}, //"cntlzwx",OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_RC_BIT}},
{922, &JitArm::extshx}, //"extshx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_RC_BIT}},
{954, &JitArm::extsbx}, //"extsbx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_RC_BIT}},
{536, &JitArm::Default}, //"srwx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_B | FL_IN_S | FL_RC_BIT}},
{792, &JitArm::Default}, //"srawx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_B | FL_IN_S | FL_RC_BIT}},
{824, &JitArm::Default}, //"srawix", OPTYPE_INTEGER, FL_OUT_A | FL_IN_B | FL_IN_S | FL_RC_BIT}},
{24, &JitArm::Default}, //"slwx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_B | FL_IN_S | FL_RC_BIT}},
{54, &JitArm::Default}, //"dcbst", OPTYPE_DCACHE, 0, 4}},
{86, &JitArm::Default}, //"dcbf", OPTYPE_DCACHE, 0, 4}},
{246, &JitArm::Default}, //"dcbtst", OPTYPE_DCACHE, 0, 1}},
{278, &JitArm::Default}, //"dcbt", OPTYPE_DCACHE, 0, 1}},
{470, &JitArm::Default}, //"dcbi", OPTYPE_DCACHE, 0, 4}},
{758, &JitArm::Default}, //"dcba", OPTYPE_DCACHE, 0, 4}},
{1014, &JitArm::Default}, //"dcbz", OPTYPE_DCACHE, 0, 4}},
//load word
{23, &JitArm::lwzx}, //"lwzx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}},
{55, &JitArm::Default}, //"lwzux", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A | FL_IN_B}},
//load halfword
{279, &JitArm::Default}, //"lhzx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}},
{311, &JitArm::Default}, //"lhzux", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A | FL_IN_B}},
//load halfword signextend
{343, &JitArm::Default}, //"lhax", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}},
{375, &JitArm::Default}, //"lhaux", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A | FL_IN_B}},
//load byte
{87, &JitArm::Default}, //"lbzx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}},
{119, &JitArm::Default}, //"lbzux", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A | FL_IN_B}},
//load byte reverse
{534, &JitArm::Default}, //"lwbrx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}},
{790, &JitArm::Default}, //"lhbrx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}},
// Conditional load/store (Wii SMP)
{150, &JitArm::Default}, //"stwcxd", OPTYPE_STORE, FL_EVIL | FL_SET_CR0}},
{20, &JitArm::Default}, //"lwarx", OPTYPE_LOAD, FL_EVIL | FL_OUT_D | FL_IN_A0B | FL_SET_CR0}},
//load string (interpret these)
{533, &JitArm::Default}, //"lswx", OPTYPE_LOAD, FL_EVIL | FL_IN_A | FL_OUT_D}},
{597, &JitArm::Default}, //"lswi", OPTYPE_LOAD, FL_EVIL | FL_IN_AB | FL_OUT_D}},
//store word
{151, &JitArm::Default}, //"stwx", OPTYPE_STORE, FL_IN_A0 | FL_IN_B}},
{183, &JitArm::Default}, //"stwux", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_B}},
//store halfword
{407, &JitArm::Default}, //"sthx", OPTYPE_STORE, FL_IN_A0 | FL_IN_B}},
{439, &JitArm::Default}, //"sthux", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_B}},
//store byte
{215, &JitArm::Default}, //"stbx", OPTYPE_STORE, FL_IN_A0 | FL_IN_B}},
{247, &JitArm::Default}, //"stbux", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_B}},
//store bytereverse
{662, &JitArm::Default}, //"stwbrx", OPTYPE_STORE, FL_IN_A0 | FL_IN_B}},
{918, &JitArm::Default}, //"sthbrx", OPTYPE_STORE, FL_IN_A | FL_IN_B}},
{661, &JitArm::Default}, //"stswx", OPTYPE_STORE, FL_EVIL}},
{725, &JitArm::Default}, //"stswi", OPTYPE_STORE, FL_EVIL}},
// fp load/store
{535, &JitArm::Default}, //"lfsx", OPTYPE_LOADFP, FL_IN_A0 | FL_IN_B}},
{567, &JitArm::Default}, //"lfsux", OPTYPE_LOADFP, FL_IN_A | FL_IN_B}},
{599, &JitArm::Default}, //"lfdx", OPTYPE_LOADFP, FL_IN_A0 | FL_IN_B}},
{631, &JitArm::Default}, //"lfdux", OPTYPE_LOADFP, FL_IN_A | FL_IN_B}},
{663, &JitArm::Default}, //"stfsx", OPTYPE_STOREFP, FL_IN_A0 | FL_IN_B}},
{695, &JitArm::Default}, //"stfsux", OPTYPE_STOREFP, FL_IN_A | FL_IN_B}},
{727, &JitArm::Default}, //"stfdx", OPTYPE_STOREFP, FL_IN_A0 | FL_IN_B}},
{759, &JitArm::Default}, //"stfdux", OPTYPE_STOREFP, FL_IN_A | FL_IN_B}},
{983, &JitArm::Default}, //"stfiwx", OPTYPE_STOREFP, FL_IN_A0 | FL_IN_B}},
{19, &JitArm::Default}, //"mfcr", OPTYPE_SYSTEM, FL_OUT_D}},
{83, &JitArm::Default}, //"mfmsr", OPTYPE_SYSTEM, FL_OUT_D}},
{144, &JitArm::Default}, //"mtcrf", OPTYPE_SYSTEM, 0}},
{146, &JitArm::mtmsr}, //"mtmsr", OPTYPE_SYSTEM, FL_ENDBLOCK}},
{210, &JitArm::Default}, //"mtsr", OPTYPE_SYSTEM, 0}},
{242, &JitArm::Default}, //"mtsrin", OPTYPE_SYSTEM, 0}},
{339, &JitArm::mfspr}, //"mfspr", OPTYPE_SPR, FL_OUT_D}},
{467, &JitArm::mtspr}, //"mtspr", OPTYPE_SPR, 0, 2}},
{371, &JitArm::Default}, //"mftb", OPTYPE_SYSTEM, FL_OUT_D | FL_TIMER}},
{512, &JitArm::Default}, //"mcrxr", OPTYPE_SYSTEM, 0}},
{595, &JitArm::Default}, //"mfsr", OPTYPE_SYSTEM, FL_OUT_D, 2}},
{659, &JitArm::Default}, //"mfsrin", OPTYPE_SYSTEM, FL_OUT_D, 2}},
{4, &JitArm::Break}, //"tw", OPTYPE_SYSTEM, FL_ENDBLOCK, 1}},
{598, &JitArm::Default}, //"sync", OPTYPE_SYSTEM, 0, 2}},
{982, &JitArm::icbi}, //"icbi", OPTYPE_SYSTEM, FL_ENDBLOCK, 3}},
// Unused instructions on GC
{310, &JitArm::Default}, //"eciwx", OPTYPE_INTEGER, FL_RC_BIT}},
{438, &JitArm::Default}, //"ecowx", OPTYPE_INTEGER, FL_RC_BIT}},
{854, &JitArm::Default}, //"eieio", OPTYPE_INTEGER, FL_RC_BIT}},
{306, &JitArm::Default}, //"tlbie", OPTYPE_SYSTEM, 0}},
{370, &JitArm::Default}, //"tlbia", OPTYPE_SYSTEM, 0}},
{566, &JitArm::Default}, //"tlbsync", OPTYPE_SYSTEM, 0}},
};
static GekkoOPTemplate table31_2[] =
{
{266, &JitArm::addx}, //"addx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT}},
{778, &JitArm::addx}, //"addx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT}},
{10, &JitArm::Default}, //"addcx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_SET_CA | FL_RC_BIT}},
{138, &JitArm::Default}, //"addex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT}},
{234, &JitArm::Default}, //"addmex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT}},
{202, &JitArm::Default}, //"addzex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT}},
{491, &JitArm::Default}, //"divwx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 39}},
{1003, &JitArm::Default}, //"divwox", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 39}},
{459, &JitArm::Default}, //"divwux", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 39}},
{971, &JitArm::Default}, //"divwuox", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 39}},
{75, &JitArm::Default}, //"mulhwx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 4}},
{11, &JitArm::Default}, //"mulhwux", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 4}},
{235, &JitArm::Default}, //"mullwx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 4}},
{747, &JitArm::Default}, //"mullwox", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 4}},
{104, &JitArm::negx}, //"negx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT}},
{40, &JitArm::Default}, //"subfx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT}},
{552, &JitArm::Default}, //"subox", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT}},
{8, &JitArm::Default}, //"subfcx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_SET_CA | FL_RC_BIT}},
{136, &JitArm::Default}, //"subfex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT}},
{232, &JitArm::Default}, //"subfmex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT}},
{200, &JitArm::Default}, //"subfzex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT}},
};
static GekkoOPTemplate table59[] =
{
{18, &JitArm::Default}, //{"fdivsx", OPTYPE_FPU, FL_RC_BIT_F, 16}},
{20, &JitArm::Default}, //"fsubsx", OPTYPE_FPU, FL_RC_BIT_F}},
{21, &JitArm::Default}, //"faddsx", OPTYPE_FPU, FL_RC_BIT_F}},
// {22, &JitArm::Default}, //"fsqrtsx", OPTYPE_FPU, FL_RC_BIT_F}}, // Not implemented on gekko
{24, &JitArm::Default}, //"fresx", OPTYPE_FPU, FL_RC_BIT_F}},
{25, &JitArm::Default}, //"fmulsx", OPTYPE_FPU, FL_RC_BIT_F}},
{28, &JitArm::Default}, //"fmsubsx", OPTYPE_FPU, FL_RC_BIT_F}},
{29, &JitArm::Default}, //"fmaddsx", OPTYPE_FPU, FL_RC_BIT_F}},
{30, &JitArm::Default}, //"fnmsubsx", OPTYPE_FPU, FL_RC_BIT_F}},
{31, &JitArm::Default}, //"fnmaddsx", OPTYPE_FPU, FL_RC_BIT_F}},
};
static GekkoOPTemplate table63[] =
{
{264, &JitArm::fabsx}, //"fabsx", OPTYPE_FPU, FL_RC_BIT_F}},
{32, &JitArm::Default}, //"fcmpo", OPTYPE_FPU, FL_RC_BIT_F}},
{0, &JitArm::Default}, //"fcmpu", OPTYPE_FPU, FL_RC_BIT_F}},
{14, &JitArm::Default}, //"fctiwx", OPTYPE_FPU, FL_RC_BIT_F}},
{15, &JitArm::Default}, //"fctiwzx", OPTYPE_FPU, FL_RC_BIT_F}},
{72, &JitArm::fmrx}, //"fmrx", OPTYPE_FPU, FL_RC_BIT_F}},
{136, &JitArm::Default}, //"fnabsx", OPTYPE_FPU, FL_RC_BIT_F}},
{40, &JitArm::Default}, //"fnegx", OPTYPE_FPU, FL_RC_BIT_F}},
{12, &JitArm::Default}, //"frspx", OPTYPE_FPU, FL_RC_BIT_F}},
{64, &JitArm::Default}, //"mcrfs", OPTYPE_SYSTEMFP, 0}},
{583, &JitArm::Default}, //"mffsx", OPTYPE_SYSTEMFP, 0}},
{70, &JitArm::Default}, //"mtfsb0x", OPTYPE_SYSTEMFP, 0, 2}},
{38, &JitArm::Default}, //"mtfsb1x", OPTYPE_SYSTEMFP, 0, 2}},
{134, &JitArm::Default}, //"mtfsfix", OPTYPE_SYSTEMFP, 0, 2}},
{711, &JitArm::Default}, //"mtfsfx", OPTYPE_SYSTEMFP, 0, 2}},
};
static GekkoOPTemplate table63_2[] =
{
{18, &JitArm::Default}, //"fdivx", OPTYPE_FPU, FL_RC_BIT_F, 30}},
{20, &JitArm::Default}, //"fsubx", OPTYPE_FPU, FL_RC_BIT_F}},
{21, &JitArm::faddx}, //"faddx", OPTYPE_FPU, FL_RC_BIT_F}},
{22, &JitArm::Default}, //"fsqrtx", OPTYPE_FPU, FL_RC_BIT_F}},
{23, &JitArm::Default}, //"fselx", OPTYPE_FPU, FL_RC_BIT_F}},
{25, &JitArm::Default}, //"fmulx", OPTYPE_FPU, FL_RC_BIT_F}},
{26, &JitArm::Default}, //"frsqrtex", OPTYPE_FPU, FL_RC_BIT_F}},
{28, &JitArm::Default}, //"fmsubx", OPTYPE_FPU, FL_RC_BIT_F}},
{29, &JitArm::Default}, //"fmaddx", OPTYPE_FPU, FL_RC_BIT_F}},
{30, &JitArm::Default}, //"fnmsubx", OPTYPE_FPU, FL_RC_BIT_F}},
{31, &JitArm::Default}, //"fnmaddx", OPTYPE_FPU, FL_RC_BIT_F}},
};
namespace JitArmTables
{
void CompileInstruction(PPCAnalyst::CodeOp & op)
{
JitArm *jitarm = (JitArm *)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] = &JitArm::unknown_instruction;
}
for (int i = 0; i < 1024; i++)
{
dynaOpTable4 [i] = &JitArm::unknown_instruction;
dynaOpTable19[i] = &JitArm::unknown_instruction;
dynaOpTable31[i] = &JitArm::unknown_instruction;
dynaOpTable63[i] = &JitArm::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,29 @@
// Copyright (C) 2003 Dolphin Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#ifndef JITARM_TABLES_H
#define JITARM_TABLES_H
#include "../Gekko.h"
#include "../PPCTables.h"
namespace JitArmTables
{
void CompileInstruction(PPCAnalyst::CodeOp & op);
void InitTables();
}
#endif
@@ -0,0 +1,174 @@
// Copyright (C) 2003 Dolphin Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#include "../../HW/Memmap.h"
#include "../PowerPC.h"
#include "../../CoreTiming.h"
#include "MemoryUtil.h"
#include "Jit.h"
#include "../JitCommon/JitCache.h"
#include "../../HW/GPFifo.h"
#include "../../Core.h"
#include "JitAsm.h"
#include "ArmEmitter.h"
using namespace ArmGen;
//TODO - make an option
//#if _DEBUG
// bool enableDebug = false;
//#else
// bool enableDebug = false;
//#endif
JitArmAsmRoutineManager asm_routines;
void JitArmAsmRoutineManager::Generate()
{
enterCode = GetCodePtr();
PUSH(2, R11, _LR); // R11 is frame pointer in Debug.
MOVI2R(R0, (u32)&CoreTiming::downcount);
MOVI2R(R9, (u32)&PowerPC::ppcState);
FixupBranch skipToRealDispatcher = B();
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(CC_MI);
SetJumpTarget(skipToRealDispatcher);
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(R12, R9, STRUCT_OFF(PowerPC::ppcState, pc));// Load the current PC into R12
MOVI2R(R14, JIT_ICACHE_MASK); // Potential for optimization
AND(R12, R12, R14); // R12 contains PC & JIT_ICACHE_MASK here.
// Confirmed good to this point 08-03-12
MOVI2R(R14, (u32)jit->GetBlockCache()->GetICache());
// Confirmed That this loads the base iCache Location correctly 08-04-12
LDR(R12, R14, R12, true, true); // R12 contains iCache[PC & JIT_ICACHE_MASK] here
// R12 Confirmed this is the correct iCache Location loaded.
TST(R12, 0xFC); // Test to see if it is a JIT block.
SetCC(CC_EQ); // Only run next part if R12 is zero
// Success, it is our Jitblock.
MOVI2R(R14, (u32)jit->GetBlockCache()->GetCodePointers());
// LDR R14 right here to get CodePointers()[0] pointer.
REV(R12, R12); // Reversing this gives us our JITblock.
LSL(R12, R12, 2); // Multiply by four because address locations are u32 in size
LDR(R14, R14, R12, true, true); // Load the block address in to R14
B(R14);
FixupBranch NextBlock = B(); // Jump to end so we can start a new block
SetCC(); // Return to always executing codes
// If we get to this point, that means that we don't have the block cached to execute
// So call ArmJit to compile the block and then execute it.
MOVI2R(R14, (u32)&Jit);
LDR(R0, R9, STRUCT_OFF(PowerPC::ppcState, pc));
BL(R14);
B(dispatcherNoCheck);
// fpException()
// Floating Point Exception Check, Jumped to if false
fpException = GetCodePtr();
LDR(R0, R9, STRUCT_OFF(PowerPC::ppcState, Exceptions));
ORR(R0, R0, EXCEPTION_FPU_UNAVAILABLE);
STR(R9, R0, STRUCT_OFF(PowerPC::ppcState, Exceptions));
QuickCallFunction(R14, (void*)&PowerPC::CheckExceptions);
LDR(R0, R9, STRUCT_OFF(PowerPC::ppcState, npc));
STR(R9, R0, STRUCT_OFF(PowerPC::ppcState, pc));
B(dispatcher);
SetJumpTarget(bail);
doTiming = GetCodePtr();
// XXX: In JIT64, Advance() gets called /after/ the exception checking
// once it jumps back to the start of outerLoop
QuickCallFunction(R14, (void*)&CoreTiming::Advance);
// Does exception checking
testExceptions = GetCodePtr();
LDR(R0, R9, STRUCT_OFF(PowerPC::ppcState, pc));
STR(R9, R0, STRUCT_OFF(PowerPC::ppcState, npc));
QuickCallFunction(R14, (void*)&PowerPC::CheckExceptions);
LDR(R0, R9, STRUCT_OFF(PowerPC::ppcState, npc));
STR(R9, R0, STRUCT_OFF(PowerPC::ppcState, pc));
// Check the state pointer to see if we are exiting
// Gets checked on every exception check
MOVI2R(R0, (u32)PowerPC::GetStatePtr());
MVN(R1, 0);
LDR(R0, R0);
TST(R0, R1);
FixupBranch Exit = B_CC(CC_NEQ);
SetJumpTarget(NextBlock);
B(dispatcher);
SetJumpTarget(Exit);
POP(2, R11, _PC);
FlushIcache();
}
void JitArmAsmRoutineManager::GenerateCommon()
{
/* fifoDirectWrite8 = AlignCode4();
GenFifoWrite(8);
fifoDirectWrite16 = AlignCode4();
GenFifoWrite(16);
fifoDirectWrite32 = AlignCode4();
GenFifoWrite(32);
fifoDirectWriteFloat = AlignCode4();
GenFifoFloatWrite();
fifoDirectWriteXmm64 = AlignCode4();
GenFifoXmm64Write();
GenQuantizedLoads();
GenQuantizedStores();
GenQuantizedSingleStores();
*/
//CMPSD(R(XMM0), M(&zero),
// TODO
// Fast write routines - special case the most common hardware write
// TODO: use this.
// Even in x86, the param values will be in the right registers.
/*
const u8 *fastMemWrite8 = AlignCode16();
CMP(32, R(ABI_PARAM2), Imm32(0xCC008000));
FixupBranch skip_fast_write = J_CC(CC_NE, false);
MOV(32, EAX, M(&m_gatherPipeCount));
MOV(8, MDisp(EAX, (u32)&m_gatherPipe), ABI_PARAM1);
ADD(32, 1, M(&m_gatherPipeCount));
RET();
SetJumpTarget(skip_fast_write);
CALL((void *)&Memory::Write_U8);*/
}
@@ -0,0 +1,43 @@
// Copyright (C) 2003 Dolphin Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#ifndef _JITARMASM_H
#define _JITARMASM_H
#include "ArmEmitter.h"
#include "../JitCommon/JitAsmCommon.h"
using namespace ArmGen;
class JitArmAsmRoutineManager : public CommonAsmRoutinesBase, public ARMXCodeBlock
{
private:
void Generate();
void GenerateCommon();
public:
void Init() {
AllocCodeSpace(8192);
Generate();
WriteProtect();
}
void Shutdown() {
FreeCodeSpace();
}
};
extern JitArmAsmRoutineManager asm_routines;
#endif // _JIT64ASM_H
@@ -0,0 +1,167 @@
// Copyright (C) 2003 Dolphin Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#include "JitRegCache.h"
ArmRegCache::ArmRegCache()
{
emit = 0;
}
void ArmRegCache::Init(ARMXEmitter *emitter)
{
emit = emitter;
ARMReg *PPCRegs = GetPPCAllocationOrder(NUMPPCREG);
ARMReg *Regs = GetAllocationOrder(NUMARMREG);
for(u8 a = 0; a < 32; ++a)
{
// This gives us the memory locations of the gpr registers so we can
// load them.
regs[a].location = (u8*)&PowerPC::ppcState.gpr[a];
regs[a].UsesLeft = 0;
}
for(u8 a = 0; a < NUMPPCREG; ++a)
{
ArmCRegs[a].PPCReg = 33;
ArmCRegs[a].Reg = PPCRegs[a];
ArmCRegs[a].LastLoad = 0;
}
for(u8 a = 0; a < NUMARMREG; ++a)
{
ArmRegs[a].Reg = Regs[a];
ArmRegs[a].free = true;
}
}
void ArmRegCache::Start(PPCAnalyst::BlockRegStats &stats)
{
for(u8 a = 0; a < NUMPPCREG; ++a)
{
ArmCRegs[a].PPCReg = 33;
ArmCRegs[a].LastLoad = 0;
}
for(u8 a = 0; a < 32; ++a)
regs[a].UsesLeft = stats.GetTotalNumAccesses(a);
}
ARMReg *ArmRegCache::GetPPCAllocationOrder(int &count)
{
// This will return us the allocation order of the registers we can use on
// the ppc side.
static ARMReg allocationOrder[] =
{
R0, R1, R2, R3, R4, R5, R6, R7, R8
};
count = sizeof(allocationOrder) / sizeof(const int);
return allocationOrder;
}
ARMReg *ArmRegCache::GetAllocationOrder(int &count)
{
// This will return us the allocation order of the registers we can use on
// the host side.
static ARMReg allocationOrder[] =
{
R14, R12, R11, R10
};
count = sizeof(allocationOrder) / sizeof(const int);
return allocationOrder;
}
ARMReg ArmRegCache::GetReg(bool AutoLock)
{
for(u8 a = 0; a < NUMARMREG; ++a)
if(ArmRegs[a].free)
{
// Alright, this one is free
if (AutoLock)
ArmRegs[a].free = false;
return ArmRegs[a].Reg;
}
// Uh Oh, we have all them locked....
_assert_msg_(_DYNA_REC_, false, "All available registers are locked dumb dumb");
return R0;
}
void ArmRegCache::Lock(ARMReg Reg)
{
for(u8 RegNum = 0; RegNum < NUMARMREG; ++RegNum)
if(ArmRegs[RegNum].Reg == Reg)
{
_assert_msg_(_DYNA_REC, ArmRegs[RegNum].free, "This register is already locked");
ArmRegs[RegNum].free = false;
}
_assert_msg_(_DYNA_REC, false, "Register %d can't be used with lock", Reg);
}
void ArmRegCache::Unlock(ARMReg R0, ARMReg R1, ARMReg R2, ARMReg R3)
{
for(u8 RegNum = 0; RegNum < NUMARMREG; ++RegNum)
{
if(ArmRegs[RegNum].Reg == R0)
{
_assert_msg_(_DYNA_REC, !ArmRegs[RegNum].free, "This register is already unlocked");
ArmRegs[RegNum].free = true;
}
if( R1 != INVALID_REG && ArmRegs[RegNum].Reg == R1) ArmRegs[RegNum].free = true;
if( R2 != INVALID_REG && ArmRegs[RegNum].Reg == R2) ArmRegs[RegNum].free = true;
if( R3 != INVALID_REG && ArmRegs[RegNum].Reg == R3) ArmRegs[RegNum].free = true;
}
}
ARMReg ArmRegCache::R(u32 preg)
{
u32 HighestUsed = 0;
u8 Num = 0;
for(u8 a = 0; a < NUMPPCREG; ++a){
++ArmCRegs[a].LastLoad;
if (ArmCRegs[a].LastLoad > HighestUsed)
{
HighestUsed = ArmCRegs[a].LastLoad;
Num = a;
}
}
// Check if already Loaded
for(u8 a = 0; a < NUMPPCREG; ++a)
if (ArmCRegs[a].PPCReg == preg)
{
ArmCRegs[a].LastLoad = 0;
return ArmCRegs[a].Reg;
}
// Check if we have a free register
for (u8 a = 0; a < NUMPPCREG; ++a)
if (ArmCRegs[a].PPCReg == 33)
{
emit->LDR(ArmCRegs[a].Reg, R9, STRUCT_OFF(PowerPC::ppcState, gpr) + preg * 4);
ArmCRegs[a].PPCReg = preg;
ArmCRegs[a].LastLoad = 0;
return ArmCRegs[a].Reg;
}
// Alright, we couldn't get a free space, dump that least used register
emit->STR(R9, ArmCRegs[Num].Reg, STRUCT_OFF(PowerPC::ppcState, gpr) + ArmCRegs[Num].PPCReg * 4);
emit->LDR(ArmCRegs[Num].Reg, R9, STRUCT_OFF(PowerPC::ppcState, gpr) + preg * 4);
ArmCRegs[Num].PPCReg = preg;
ArmCRegs[Num].LastLoad = 0;
return ArmCRegs[Num].Reg;
}
void ArmRegCache::Flush()
{
for(u8 a = 0; a < NUMPPCREG; ++a)
if (ArmCRegs[a].PPCReg != 33)
{
emit->STR(R9, ArmCRegs[a].Reg, STRUCT_OFF(PowerPC::ppcState, gpr) + ArmCRegs[a].PPCReg * 4);
ArmCRegs[a].PPCReg = 33;
ArmCRegs[a].LastLoad = 0;
}
}
@@ -0,0 +1,92 @@
// Copyright (C) 2003 Dolphin Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#ifndef _JITARMREGCACHE_H
#define _JITARMREGCACHE_H
#include "ArmEmitter.h"
#include "../Gekko.h"
#include "../PPCAnalyst.h"
using namespace ArmGen;
// This ARM Register cache actually pre loads the most used registers before
// the block to increase speed since every memory load requires two
// instructions to load it. We are going to use R0-RMAX as registers for the
// use of PPC Registers.
// Allocation order as follows
#define ARMREGS 16
// Allocate R0 to R9 for PPC first.
// For General registers on the host side, start with R14 and go down as we go
// R13 is reserved for our stack pointer, don't ever use that. Unless you save
// it
// So we have R14, R12, R11, R10 to work with instructions
struct PPCCachedReg
{
const u8 *location;
u32 UsesLeft;
};
struct JRCPPC
{
u32 PPCReg; // Tied to which PPC Register
bool PS1;
ARMReg Reg; // Tied to which ARM Register
u32 LastLoad;
};
struct JRCReg
{
ARMReg Reg; // Which reg this is.
bool free;
};
class ArmRegCache
{
private:
PPCCachedReg regs[32];
JRCPPC ArmCRegs[ARMREGS];
JRCReg ArmRegs[ARMREGS]; // Four registers remaining
int NUMPPCREG;
int NUMARMREG;
ARMReg *GetAllocationOrder(int &count);
ARMReg *GetPPCAllocationOrder(int &count);
protected:
ARMXEmitter *emit;
public:
ArmRegCache();
~ArmRegCache() {}
void Init(ARMXEmitter *emitter);
void Start(PPCAnalyst::BlockRegStats &stats);
void SetEmitter(ARMXEmitter *emitter) {emit = emitter;}
ARMReg GetReg(bool AutoLock = true); // Return a ARM register we can use.
void Lock(ARMReg reg);
void Unlock(ARMReg R0, ARMReg R1 = INVALID_REG, ARMReg R2 = INVALID_REG, ARMReg R3 =
INVALID_REG);
void Flush();
ARMReg R(u32 preg); // Returns a cached register
};
#endif
@@ -15,7 +15,7 @@
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#include "ABI.h"
#include "x64ABI.h"
#include "Thunk.h"
#include "CPUDetect.h"
#include "x64Emitter.h"
@@ -26,7 +26,7 @@
#include "../../CoreTiming.h"
#include "MemoryUtil.h"
#include "ABI.h"
#include "x64ABI.h"
#include "../JitCommon/JitCache.h"
#include "../../HW/GPFifo.h"
@@ -21,16 +21,8 @@
#include "../JitCommon/Jit_Util.h"
#include "Thunk.h"
class CommonAsmRoutines : public EmuCodeBlock {
protected:
void GenQuantizedLoads();
void GenQuantizedStores();
void GenQuantizedSingleStores();
class CommonAsmRoutinesBase {
public:
void GenFifoWrite(int size);
void GenFifoXmm64Write();
void GenFifoFloatWrite();
const u8 *fifoDirectWrite8;
const u8 *fifoDirectWrite16;
@@ -72,8 +64,23 @@ public:
// In: XMM0: Bottom 32-bit slot holds the float to be written.
const u8 **singleStoreQuantized;
};
class CommonAsmRoutines : public CommonAsmRoutinesBase, public EmuCodeBlock
{
protected:
void GenQuantizedLoads();
void GenQuantizedStores();
void GenQuantizedSingleStores();
public:
void GenFifoWrite(int size);
void GenFifoXmm64Write();
void GenFifoFloatWrite();
private:
ThunkManager thunks;
};
#endif
@@ -25,7 +25,7 @@
#include "../../HW/Memmap.h"
#include "x64Emitter.h"
#include "ABI.h"
#include "x64ABI.h"
#include "Thunk.h"
#include "x64Analyzer.h"
@@ -160,7 +160,7 @@ const u8 *TrampolineCache::GetWriteTrampoline(const InstructionInfo &info)
// 1) It's really necessary. We don't know anything about the context.
// 2) It doesn't really hurt. Only instructions that access I/O will get these, and there won't be
// that many of them in a typical program/game.
const u8 *JitBase::BackPatch(u8 *codePtr, int accessType, u32 emAddress, void *ctx_void)
const u8 *Jitx86Base::BackPatch(u8 *codePtr, int accessType, u32 emAddress, void *ctx_void)
{
#ifdef _M_X64
CONTEXT *ctx = (CONTEXT *)ctx_void;
@@ -34,6 +34,9 @@
// from the real context.
struct CONTEXT
{
#ifdef _M_ARM
u32 reg_pc;
#else
#ifdef _M_X64
u64 Rip;
u64 Rax;
@@ -41,6 +44,7 @@
u32 Eip;
u32 Eax;
#endif
#endif
};
#endif
@@ -32,12 +32,9 @@
#define JIT_OPCODE 0
class JitBase : public CPUCoreBase, public EmuCodeBlock
class JitBase : public CPUCoreBase
{
protected:
JitBlockCache blocks;
TrampolineCache trampolines;
struct JitOptions
{
bool optimizeStack;
@@ -85,14 +82,29 @@ public:
// This should probably be removed from public:
JitOptions jo;
JitState js;
JitBlockCache *GetBlockCache() { return &blocks; }
virtual JitBaseBlockCache *GetBlockCache() = 0;
virtual void Jit(u32 em_address) = 0;
virtual const u8 *BackPatch(u8 *codePtr, int accessType, u32 em_address, void *ctx) = 0;
virtual const CommonAsmRoutinesBase *GetAsmRoutines() = 0;
virtual bool IsInCodeSpace(u8 *ptr) = 0;
};
class Jitx86Base : public JitBase, public EmuCodeBlock
{
protected:
JitBlockCache blocks;
TrampolineCache trampolines;
public:
JitBlockCache *GetBlockCache() { return &blocks; }
const u8 *BackPatch(u8 *codePtr, int accessType, u32 em_address, void *ctx);
virtual const CommonAsmRoutines *GetAsmRoutines() = 0;
bool IsInCodeSpace(u8 *ptr) { return IsInSpace(ptr); }
};
extern JitBase *jit;
@@ -32,15 +32,13 @@
#include "MemoryUtil.h"
#include "../../HW/Memmap.h"
#include "../JitInterface.h"
#include "../../CoreTiming.h"
#include "../PowerPC.h"
#include "../PPCTables.h"
#include "../PPCAnalyst.h"
#include "x64Emitter.h"
#include "x64Analyzer.h"
#include "JitCache.h"
#include "JitBase.h"
@@ -68,12 +66,12 @@ bool JitBlock::ContainsAddress(u32 em_address)
return (em_address >= originalAddress && em_address < originalAddress + 4 * originalSize);
}
bool JitBlockCache::IsFull() const
bool JitBaseBlockCache::IsFull() const
{
return GetNumBlocks() >= MAX_NUM_BLOCKS - 1;
}
void JitBlockCache::Init()
void JitBaseBlockCache::Init()
{
MAX_NUM_BLOCKS = 65536*2;
@@ -91,11 +89,11 @@ bool JitBlock::ContainsAddress(u32 em_address)
}
else
{
PanicAlert("JitBlockCache::Init() - iCache is already initialized");
PanicAlert("JitBaseBlockCache::Init() - iCache is already initialized");
}
if (iCache == 0 || iCacheEx == 0 || iCacheVMEM == 0)
{
PanicAlert("JitBlockCache::Init() - unable to allocate iCache");
PanicAlert("JitBaseBlockCache::Init() - unable to allocate iCache");
}
memset(iCache, JIT_ICACHE_INVALID_BYTE, JIT_ICACHE_SIZE);
memset(iCacheEx, JIT_ICACHE_INVALID_BYTE, JIT_ICACHEEX_SIZE);
@@ -104,7 +102,7 @@ bool JitBlock::ContainsAddress(u32 em_address)
Clear();
}
void JitBlockCache::Shutdown()
void JitBaseBlockCache::Shutdown()
{
delete[] blocks;
delete[] blockCodePointers;
@@ -133,7 +131,7 @@ bool JitBlock::ContainsAddress(u32 em_address)
// This clears the JIT cache. It's called from JitCache.cpp when the JIT cache
// is full and when saving and loading states.
void JitBlockCache::Clear()
void JitBaseBlockCache::Clear()
{
if (IsFull())
Core::DisplayMessage("Clearing block cache.", 3000);
@@ -151,7 +149,7 @@ bool JitBlock::ContainsAddress(u32 em_address)
memset(blockCodePointers, 0, sizeof(u8*)*MAX_NUM_BLOCKS);
}
void JitBlockCache::ClearSafe()
void JitBaseBlockCache::ClearSafe()
{
#ifdef JIT_UNLIMITED_ICACHE
memset(iCache, JIT_ICACHE_INVALID_BYTE, JIT_ICACHE_SIZE);
@@ -160,7 +158,7 @@ bool JitBlock::ContainsAddress(u32 em_address)
#endif
}
/*void JitBlockCache::DestroyBlocksWithFlag(BlockFlag death_flag)
/*void JitBaseBlockCache::DestroyBlocksWithFlag(BlockFlag death_flag)
{
for (int i = 0; i < num_blocks; i++)
{
@@ -171,23 +169,23 @@ bool JitBlock::ContainsAddress(u32 em_address)
}
}*/
void JitBlockCache::Reset()
void JitBaseBlockCache::Reset()
{
Shutdown();
Init();
}
JitBlock *JitBlockCache::GetBlock(int no)
JitBlock *JitBaseBlockCache::GetBlock(int no)
{
return &blocks[no];
}
int JitBlockCache::GetNumBlocks() const
int JitBaseBlockCache::GetNumBlocks() const
{
return num_blocks;
}
bool JitBlockCache::RangeIntersect(int s1, int e1, int s2, int e2) const
bool JitBaseBlockCache::RangeIntersect(int s1, int e1, int s2, int e2) const
{
// check if any endpoint is inside the other range
if ((s1 >= s2 && s1 <= e2) ||
@@ -199,7 +197,7 @@ bool JitBlock::ContainsAddress(u32 em_address)
return false;
}
int JitBlockCache::AllocateBlock(u32 em_address)
int JitBaseBlockCache::AllocateBlock(u32 em_address)
{
JitBlock &b = blocks[num_blocks];
b.invalid = false;
@@ -215,12 +213,12 @@ bool JitBlock::ContainsAddress(u32 em_address)
return num_blocks - 1;
}
void JitBlockCache::FinalizeBlock(int block_num, bool block_link, const u8 *code_ptr)
void JitBaseBlockCache::FinalizeBlock(int block_num, bool block_link, const u8 *code_ptr)
{
blockCodePointers[block_num] = code_ptr;
JitBlock &b = blocks[block_num];
b.originalFirstOpcode = Memory::Read_Opcode_JIT(b.originalAddress);
Memory::Write_Opcode_JIT(b.originalAddress, (JIT_OPCODE << 26) | block_num);
b.originalFirstOpcode = JitInterface::Read_Opcode_JIT(b.originalAddress);
JitInterface::Write_Opcode_JIT(b.originalAddress, (JIT_OPCODE << 26) | block_num);
// Convert the logical address to a physical address for the block map
u32 pAddr = b.originalAddress & 0x1FFFFFFF;
@@ -264,29 +262,29 @@ bool JitBlock::ContainsAddress(u32 em_address)
#endif
}
const u8 **JitBlockCache::GetCodePointers()
const u8 **JitBaseBlockCache::GetCodePointers()
{
return blockCodePointers;
}
#ifdef JIT_UNLIMITED_ICACHE
u8* JitBlockCache::GetICache()
u8* JitBaseBlockCache::GetICache()
{
return iCache;
}
u8* JitBlockCache::GetICacheEx()
u8* JitBaseBlockCache::GetICacheEx()
{
return iCacheEx;
}
u8* JitBlockCache::GetICacheVMEM()
u8* JitBaseBlockCache::GetICacheVMEM()
{
return iCacheVMEM;
}
#endif
int JitBlockCache::GetBlockNumberFromStartAddress(u32 addr)
int JitBaseBlockCache::GetBlockNumberFromStartAddress(u32 addr)
{
if (!blocks)
return -1;
@@ -317,24 +315,24 @@ bool JitBlock::ContainsAddress(u32 em_address)
return inst;
}
void JitBlockCache::GetBlockNumbersFromAddress(u32 em_address, std::vector<int> *block_numbers)
void JitBaseBlockCache::GetBlockNumbersFromAddress(u32 em_address, std::vector<int> *block_numbers)
{
for (int i = 0; i < num_blocks; i++)
if (blocks[i].ContainsAddress(em_address))
block_numbers->push_back(i);
}
u32 JitBlockCache::GetOriginalFirstOp(int block_num)
u32 JitBaseBlockCache::GetOriginalFirstOp(int block_num)
{
if (block_num >= num_blocks)
{
//PanicAlert("JitBlockCache::GetOriginalFirstOp - block_num = %u is out of range", block_num);
//PanicAlert("JitBaseBlockCache::GetOriginalFirstOp - block_num = %u is out of range", block_num);
return block_num;
}
return blocks[block_num].originalFirstOpcode;
}
CompiledCode JitBlockCache::GetCompiledCodeFromBlock(int block_num)
CompiledCode JitBaseBlockCache::GetCompiledCodeFromBlock(int block_num)
{
return (CompiledCode)blockCodePointers[block_num];
}
@@ -345,7 +343,7 @@ bool JitBlock::ContainsAddress(u32 em_address)
//Can be faster by doing a queue for blocks to link up, and only process those
//Should probably be done
void JitBlockCache::LinkBlockExits(int i)
void JitBaseBlockCache::LinkBlockExits(int i)
{
JitBlock &b = blocks[i];
if (b.invalid)
@@ -360,8 +358,7 @@ bool JitBlock::ContainsAddress(u32 em_address)
int destinationBlock = GetBlockNumberFromStartAddress(b.exitAddress[e]);
if (destinationBlock != -1)
{
XEmitter emit(b.exitPtrs[e]);
emit.JMP(blocks[destinationBlock].checkedEntry, true);
WriteLinkBlock(b.exitPtrs[e], blocks[destinationBlock].checkedEntry);
b.linkStatus[e] = true;
}
}
@@ -370,7 +367,7 @@ bool JitBlock::ContainsAddress(u32 em_address)
using namespace std;
void JitBlockCache::LinkBlock(int i)
void JitBaseBlockCache::LinkBlock(int i)
{
LinkBlockExits(i);
JitBlock &b = blocks[i];
@@ -386,7 +383,7 @@ bool JitBlock::ContainsAddress(u32 em_address)
}
}
void JitBlockCache::UnlinkBlock(int i)
void JitBaseBlockCache::UnlinkBlock(int i)
{
JitBlock &b = blocks[i];
pair<multimap<u32, int>::iterator, multimap<u32, int>::iterator> ppp;
@@ -403,7 +400,7 @@ bool JitBlock::ContainsAddress(u32 em_address)
}
}
void JitBlockCache::DestroyBlock(int block_num, bool invalidate)
void JitBaseBlockCache::DestroyBlock(int block_num, bool invalidate)
{
if (block_num < 0 || block_num >= num_blocks)
{
@@ -419,7 +416,7 @@ bool JitBlock::ContainsAddress(u32 em_address)
}
b.invalid = true;
#ifdef JIT_UNLIMITED_ICACHE
Memory::Write_Opcode_JIT(b.originalAddress, b.originalFirstOpcode?b.originalFirstOpcode:JIT_ICACHE_INVALID_WORD);
JitInterface::Write_Opcode_JIT(b.originalAddress, b.originalFirstOpcode?b.originalFirstOpcode:JIT_ICACHE_INVALID_WORD);
#else
if (Memory::ReadFast32(b.originalAddress) == block_num)
Memory::WriteUnchecked_U32(b.originalFirstOpcode, b.originalAddress);
@@ -430,18 +427,10 @@ bool JitBlock::ContainsAddress(u32 em_address)
// Send anyone who tries to run this block back to the dispatcher.
// Not entirely ideal, but .. pretty good.
// Spurious entrances from previously linked blocks can only come through checkedEntry
XEmitter emit((u8 *)b.checkedEntry);
emit.MOV(32, M(&PC), Imm32(b.originalAddress));
emit.JMP(jit->GetAsmRoutines()->dispatcher, true);
// this is not needed really
/*
emit.SetCodePtr((u8 *)blockCodePointers[blocknum]);
emit.MOV(32, M(&PC), Imm32(b.originalAddress));
emit.JMP(asm_routines.dispatcher, true);
*/
WriteDestroyBlock(b.checkedEntry, b.originalAddress);
}
void JitBlockCache::InvalidateICache(u32 address, const u32 length)
void JitBaseBlockCache::InvalidateICache(u32 address, const u32 length)
{
// Convert the logical address to a physical address for the block map
u32 pAddr = address & 0x1FFFFFFF;
@@ -449,7 +438,7 @@ bool JitBlock::ContainsAddress(u32 em_address)
// Optimize the common case of length == 32 which is used by Interpreter::dcb*
bool destroy_block = true;
if (length == 32)
{
{
if (!valid_block[pAddr / 32])
destroy_block = false;
else
@@ -462,7 +451,7 @@ bool JitBlock::ContainsAddress(u32 em_address)
{
std::map<pair<u32,u32>, u32>::iterator it1 = block_map.lower_bound(std::make_pair(pAddr, 0)), it2 = it1;
while (it2 != block_map.end() && it2->first.second < pAddr + length)
{
{
#ifdef JIT_UNLIMITED_ICACHE
JitBlock &b = blocks[it2->second];
if (b.originalAddress & JIT_ICACHE_VMEM_BIT)
@@ -516,3 +505,14 @@ bool JitBlock::ContainsAddress(u32 em_address)
}
#endif
}
void JitBlockCache::WriteLinkBlock(u8* location, const u8* address)
{
XEmitter emit(location);
emit.JMP(address, true);
}
void JitBlockCache::WriteDestroyBlock(const u8* location, u32 address)
{
XEmitter emit((u8 *)location);
emit.MOV(32, M(&PC), Imm32(address));
emit.JMP(jit->GetAsmRoutines()->dispatcher, true);
}
@@ -78,7 +78,8 @@ struct JitBlock
typedef void (*CompiledCode)();
class JitBlockCache
class JitBaseBlockCache
{
const u8 **blockCodePointers;
JitBlock *blocks;
@@ -97,9 +98,13 @@ class JitBlockCache
void LinkBlockExits(int i);
void LinkBlock(int i);
void UnlinkBlock(int i);
// Virtual for overloaded
virtual void WriteLinkBlock(u8* location, const u8* address) = 0;
virtual void WriteDestroyBlock(const u8* location, u32 address) = 0;
public:
JitBlockCache() :
JitBaseBlockCache() :
blockCodePointers(0), blocks(0), num_blocks(0),
#ifdef JIT_UNLIMITED_ICACHE
iCache(0), iCacheEx(0), iCacheVMEM(0),
@@ -146,4 +151,11 @@ public:
//void DestroyBlocksWithFlag(BlockFlag death_flag);
};
// x86 BlockCache
class JitBlockCache : public JitBaseBlockCache
{
private:
void WriteLinkBlock(u8* location, const u8* address);
void WriteDestroyBlock(const u8* location, u32 address);
};
#endif
@@ -25,7 +25,7 @@
#include "../../HW/Memmap.h"
#include "../PPCTables.h"
#include "x64Emitter.h"
#include "ABI.h"
#include "x64ABI.h"
#include "JitBase.h"
#include "Jit_Util.h"
@@ -0,0 +1,350 @@
// Copyright (C) 2003 Dolphin Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#include <algorithm>
#ifdef _WIN32
#include <windows.h>
#endif
#include "JitInterface.h"
#include "JitCommon/JitBase.h"
#ifndef _M_GENERIC
#include "Jit64IL/JitIL.h"
#include "Jit64/Jit.h"
#include "Jit64/Jit64_Tables.h"
#include "Jit64IL/JitIL_Tables.h"
#endif
#ifdef _M_ARM
#include "JitArm32/Jit.h"
#include "JitArm32/JitArm_Tables.h"
#endif
#include "Profiler.h"
#include "PPCSymbolDB.h"
#include "HW/Memmap.h"
#include "ConfigManager.h"
bool bFakeVMEM = false;
bool bMMU = false;
namespace JitInterface
{
void DoState(PointerWrap &p)
{
if (jit && p.GetMode() == PointerWrap::MODE_READ)
jit->GetBlockCache()->ClearSafe();
}
CPUCoreBase *InitJitCore(int core)
{
bFakeVMEM = SConfig::GetInstance().m_LocalCoreStartupParameter.iTLBHack == 1;
bMMU = SConfig::GetInstance().m_LocalCoreStartupParameter.bMMU;
CPUCoreBase *ptr = NULL;
switch(core)
{
#ifndef _M_GENERIC
case 1:
{
ptr = new Jit64();
break;
}
case 2:
{
ptr = new JitIL();
break;
}
#endif
#ifdef _M_ARM
case 3:
{
ptr = new JitArm();
break;
}
#endif
default:
{
PanicAlert("Unrecognizable cpu_core: %d", core);
return NULL;
break;
}
}
jit = static_cast<JitBase*>(ptr);
jit->Init();
return ptr;
}
void InitTables(int core)
{
switch(core)
{
#ifndef _M_GENERIC
case 1:
{
Jit64Tables::InitTables();
break;
}
case 2:
{
JitILTables::InitTables();
break;
}
#endif
#ifdef _M_ARM
case 3:
{
JitArmTables::InitTables();
break;
}
#endif
default:
{
PanicAlert("Unrecognizable cpu_core: %d", core);
break;
}
}
}
CPUCoreBase *GetCore()
{
return jit;
}
void WriteProfileResults(const char *filename)
{
// Can't really do this with no jit core available
#ifndef _M_GENERIC
std::vector<BlockStat> stats;
stats.reserve(jit->GetBlockCache()->GetNumBlocks());
u64 cost_sum = 0;
#ifdef _WIN32
u64 timecost_sum = 0;
u64 countsPerSec;
QueryPerformanceFrequency((LARGE_INTEGER *)&countsPerSec);
#endif
for (int i = 0; i < jit->GetBlockCache()->GetNumBlocks(); i++)
{
const JitBlock *block = jit->GetBlockCache()->GetBlock(i);
// Rough heuristic. Mem instructions should cost more.
u64 cost = block->originalSize * (block->runCount / 4);
#ifdef _WIN32
u64 timecost = block->ticCounter;
#endif
// Todo: tweak.
if (block->runCount >= 1)
stats.push_back(BlockStat(i, cost));
cost_sum += cost;
#ifdef _WIN32
timecost_sum += timecost;
#endif
}
sort(stats.begin(), stats.end());
File::IOFile f(filename, "w");
if (!f)
{
PanicAlert("failed to open %s", filename);
return;
}
fprintf(f.GetHandle(), "origAddr\tblkName\tcost\ttimeCost\tpercent\ttimePercent\tOvAllinBlkTime(ms)\tblkCodeSize\n");
for (unsigned int i = 0; i < stats.size(); i++)
{
const JitBlock *block = jit->GetBlockCache()->GetBlock(stats[i].blockNum);
if (block)
{
std::string name = g_symbolDB.GetDescription(block->originalAddress);
double percent = 100.0 * (double)stats[i].cost / (double)cost_sum;
#ifdef _WIN32
double timePercent = 100.0 * (double)block->ticCounter / (double)timecost_sum;
fprintf(f.GetHandle(), "%08x\t%s\t%llu\t%llu\t%.2lf\t%llf\t%lf\t%i\n",
block->originalAddress, name.c_str(), stats[i].cost,
block->ticCounter, percent, timePercent,
(double)block->ticCounter*1000.0/(double)countsPerSec, block->codeSize);
#else
fprintf(f.GetHandle(), "%08x\t%s\t%llu\t???\t%.2lf\t???\t???\t%i\n",
block->originalAddress, name.c_str(), stats[i].cost, percent, block->codeSize);
#endif
}
}
#endif
}
bool IsInCodeSpace(u8 *ptr)
{
return jit->IsInCodeSpace(ptr);
}
const u8 *BackPatch(u8 *codePtr, int accessType, u32 em_address, void *ctx)
{
return jit->BackPatch(codePtr, accessType, em_address, ctx);
}
void ClearCache()
{
if (jit)
jit->ClearCache();
}
void ClearSafe()
{
if (jit)
jit->GetBlockCache()->ClearSafe();
}
void InvalidateICache(u32 address, u32 size)
{
if (jit)
jit->GetBlockCache()->InvalidateICache(address, size);
}
// Memory functions
u32 Read_Opcode_JIT_Uncached(const u32 _Address)
{
u8* iCache;
u32 addr;
if (_Address & JIT_ICACHE_VMEM_BIT)
{
iCache = jit->GetBlockCache()->GetICacheVMEM();
addr = _Address & JIT_ICACHE_MASK;
}
else if (_Address & JIT_ICACHE_EXRAM_BIT)
{
iCache = jit->GetBlockCache()->GetICacheEx();
addr = _Address & JIT_ICACHEEX_MASK;
}
else
{
iCache = jit->GetBlockCache()->GetICache();
addr = _Address & JIT_ICACHE_MASK;
}
u32 inst = *(u32*)(iCache + addr);
if (inst == JIT_ICACHE_INVALID_WORD)
{
u32 cache_block_start = addr & ~0x1f;
u32 mem_block_start = _Address & ~0x1f;
u8 *pMem = Memory::GetPointer(mem_block_start);
memcpy(iCache + cache_block_start, pMem, 32);
inst = *(u32*)(iCache + addr);
}
inst = Common::swap32(inst);
if ((inst & 0xfc000000) == 0)
{
inst = jit->GetBlockCache()->GetOriginalFirstOp(inst);
}
return inst;
}
u32 Read_Opcode_JIT(u32 _Address)
{
#ifdef FAST_ICACHE
if (bMMU && !bFakeVMEM && (_Address & Memory::ADDR_MASK_MEM1))
{
_Address = Memory::TranslateAddress(_Address, Memory::FLAG_OPCODE);
if (_Address == 0)
{
return 0;
}
}
u32 inst = 0;
// Bypass the icache for the external interrupt exception handler
if ( (_Address & 0x0FFFFF00) == 0x00000500 )
inst = Read_Opcode_JIT_Uncached(_Address);
else
inst = PowerPC::ppcState.iCache.ReadInstruction(_Address);
#else
u32 inst = Memory::ReadUnchecked_U32(_Address);
#endif
return inst;
}
// The following function is deprecated in favour of FAST_ICACHE
u32 Read_Opcode_JIT_LC(const u32 _Address)
{
#ifdef JIT_UNLIMITED_ICACHE
if ((_Address & ~JIT_ICACHE_MASK) != 0x80000000 && (_Address & ~JIT_ICACHE_MASK) != 0x00000000 &&
(_Address & ~JIT_ICACHE_MASK) != 0x7e000000 && // TLB area
(_Address & ~JIT_ICACHEEX_MASK) != 0x90000000 && (_Address & ~JIT_ICACHEEX_MASK) != 0x10000000)
{
PanicAlertT("iCacheJIT: Reading Opcode from %x. Please report.", _Address);
ERROR_LOG(MEMMAP, "iCacheJIT: Reading Opcode from %x. Please report.", _Address);
return 0;
}
u8* iCache;
u32 addr;
if (_Address & JIT_ICACHE_VMEM_BIT)
{
iCache = jit->GetBlockCache()->GetICacheVMEM();
addr = _Address & JIT_ICACHE_MASK;
}
else if (_Address & JIT_ICACHE_EXRAM_BIT)
{
iCache = jit->GetBlockCache()->GetICacheEx();
addr = _Address & JIT_ICACHEEX_MASK;
}
else
{
iCache = jit->GetBlockCache()->GetICache();
addr = _Address & JIT_ICACHE_MASK;
}
u32 inst = *(u32*)(iCache + addr);
if (inst == JIT_ICACHE_INVALID_WORD)
inst = Memory::ReadUnchecked_U32(_Address);
else
inst = Common::swap32(inst);
#else
u32 inst = Memory::ReadUnchecked_U32(_Address);
#endif
if ((inst & 0xfc000000) == 0)
{
inst = jit->GetBlockCache()->GetOriginalFirstOp(inst);
}
return inst;
}
// WARNING! No checks!
// We assume that _Address is cached
void Write_Opcode_JIT(const u32 _Address, const u32 _Value)
{
#ifdef JIT_UNLIMITED_ICACHE
if (_Address & JIT_ICACHE_VMEM_BIT)
{
*(u32*)(jit->GetBlockCache()->GetICacheVMEM() + (_Address & JIT_ICACHE_MASK)) = Common::swap32(_Value);
}
else if (_Address & JIT_ICACHE_EXRAM_BIT)
{
*(u32*)(jit->GetBlockCache()->GetICacheEx() + (_Address & JIT_ICACHEEX_MASK)) = Common::swap32(_Value);
}
else
*(u32*)(jit->GetBlockCache()->GetICache() + (_Address & JIT_ICACHE_MASK)) = Common::swap32(_Value);
#else
Memory::WriteUnchecked_U32(_Value, _Address);
#endif
}
void Shutdown()
{
if (jit)
{
jit->Shutdown();
delete jit;
jit = NULL;
}
}
}
@@ -0,0 +1,56 @@
// Copyright (C) 2003 Dolphin Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#include "ChunkFile.h"
#include "CPUCoreBase.h"
namespace JitInterface
{
void DoState(PointerWrap &p);
CPUCoreBase *InitJitCore(int core);
void InitTables(int core);
CPUCoreBase *GetCore();
// Debugging
void WriteProfileResults(const char *filename);
// Memory Utilities
bool IsInCodeSpace(u8 *ptr);
const u8 *BackPatch(u8 *codePtr, int accessType, u32 em_address, void *ctx);
// used by JIT to read instructions
u32 Read_Opcode_JIT(const u32 _Address);
// used by JIT. uses iCacheJIT. Reads in the "Locked cache" mode
u32 Read_Opcode_JIT_LC(const u32 _Address);
void Write_Opcode_JIT(const u32 _Address, const u32 _Value);
// Clearing CodeCache
void ClearCache();
void ClearSafe();
void InvalidateICache(u32 address, u32 size);
void Shutdown();
}
extern bool bFakeVMEM;
extern bool bMMU;
#ifdef _M_ARM
#include "JitArm32/Jit.h"
#endif
+2 -1
View File
@@ -21,6 +21,7 @@
#include "StringUtil.h"
#include "Interpreter/Interpreter.h"
#include "../HW/Memmap.h"
#include "JitInterface.h"
#include "PPCTables.h"
#include "PPCSymbolDB.h"
#include "SignatureDB.h"
@@ -368,7 +369,7 @@ u32 Flatten(u32 address, int *realsize, BlockStats *st, BlockRegStats *gpa,
}
else
{
inst = Memory::Read_Opcode_JIT(address);
inst = JitInterface::Read_Opcode_JIT(address);
}
}
+3 -4
View File
@@ -20,6 +20,7 @@
#include "PowerPC.h"
#include "JitCommon/JitBase.h"
#include "JitCommon/JitCache.h"
#include "JitInterface.h"
namespace PowerPC
{
@@ -76,8 +77,7 @@ namespace PowerPC
memset(lookup_table_ex, 0xff, sizeof(lookup_table_ex));
memset(lookup_table_vmem, 0xff, sizeof(lookup_table_vmem));
#endif
if (jit)
jit->GetBlockCache()->ClearSafe();
JitInterface::ClearSafe();
}
void InstructionCache::Init()
@@ -109,8 +109,7 @@ namespace PowerPC
}
#endif
valid[set] = 0;
if (jit)
jit->GetBlockCache()->InvalidateICache(addr & ~0x1f, 32);
JitInterface::InvalidateICache(addr & ~0x1f, 32);
}
u32 InstructionCache::ReadInstruction(u32 addr)
+2 -16
View File
@@ -24,11 +24,7 @@
#include "FileUtil.h"
#include "Interpreter/Interpreter.h"
#include "Interpreter/Interpreter_Tables.h"
#include "Jit64IL/JitIL_Tables.h"
#include "Jit64/Jit64_Tables.h"
#include "Jit64IL/JitIL.h"
#include "Jit64/Jit.h"
#include "JitInterface.h"
struct op_inf
{
@@ -165,19 +161,9 @@ void InitTables(int cpu_core)
// Interpreter
break;
}
case 1:
{
Jit64Tables::InitTables();
break;
}
case 2:
{
JitILTables::InitTables();
break;
}
default:
{
PanicAlert("Unrecognizable cpu_core: %d", cpu_core);
JitInterface::InitTables(cpu_core);
break;
}
}
+19 -51
View File
@@ -29,12 +29,10 @@
#include "../HW/SystemTimers.h"
#include "Interpreter/Interpreter.h"
#include "JitCommon/JitBase.h"
#include "Jit64IL/JitIL.h"
#include "Jit64/Jit.h"
#include "PowerPC.h"
#include "PPCTables.h"
#include "CPUCoreBase.h"
#include "JitInterface.h"
#include "../Host.h"
#include "HW/EXI.h"
@@ -87,8 +85,7 @@ void DoState(PointerWrap &p)
// SystemTimers::DecrementerSet();
// SystemTimers::TimeBaseSet();
if (jit && p.GetMode() == PointerWrap::MODE_READ)
jit->GetBlockCache()->ClearSafe();
JitInterface::DoState(p);
}
void ResetRegisters()
@@ -131,28 +128,18 @@ void ResetRegisters()
void Init(int cpu_core)
{
enum {
FPU_PREC_24 = 0 << 8,
FPU_PREC_53 = 2 << 8,
FPU_PREC_64 = 3 << 8,
FPU_PREC_MASK = 3 << 8,
};
#ifdef _M_IX86
// sets the floating-point lib to 53-bit
// PowerPC has a 53bit floating pipeline only
// eg: sscanf is very sensitive
#ifdef _WIN32
_control87(_PC_53, MCW_PC);
#else
unsigned short _mode;
asm ("fstcw %0" : : "m" (_mode));
_mode = (_mode & ~FPU_PREC_MASK) | FPU_PREC_53;
asm ("fldcw %0" : : "m" (_mode));
#endif
#else
//x64 doesn't need this - fpu is done with SSE
//but still - set any useful sse options here
#endif
FPURoundMode::SetPrecisionMode(FPURoundMode::PREC_53);
memset(ppcState.mojs, 0, sizeof(ppcState.mojs));
memset(ppcState.sr, 0, sizeof(ppcState.sr));
ppcState.DebugCount = 0;
ppcState.dtlb_last = 0;
ppcState.dtlb_last = 0;
memset(ppcState.dtlb_va, 0, sizeof(ppcState.dtlb_va));
memset(ppcState.dtlb_pa, 0, sizeof(ppcState.dtlb_pa));
ppcState.itlb_last = 0;
memset(ppcState.itlb_va, 0, sizeof(ppcState.itlb_va));
memset(ppcState.itlb_pa, 0, sizeof(ppcState.itlb_pa));
memset(ppcState.mojs, 0, sizeof(ppcState.mojs));
memset(ppcState.sr, 0, sizeof(ppcState.sr));
@@ -179,27 +166,13 @@ void Init(int cpu_core)
cpu_core_base = interpreter;
break;
}
case 1:
{
cpu_core_base = new Jit64();
break;
}
case 2:
{
cpu_core_base = new JitIL();
break;
}
default:
{
PanicAlert("Unrecognizable cpu_core: %d", cpu_core);
break;
}
default:
cpu_core_base = JitInterface::InitJitCore(cpu_core);
break;
}
if (cpu_core_base != interpreter)
{
jit = static_cast<JitBase*>(cpu_core_base);
jit->Init();
mode = MODE_JIT;
}
else
@@ -213,12 +186,7 @@ void Init(int cpu_core)
void Shutdown()
{
if (jit)
{
jit->Shutdown();
delete jit;
jit = NULL;
}
JitInterface::Shutdown();
interpreter->Shutdown();
cpu_core_base = NULL;
state = CPU_POWERDOWN;
@@ -244,7 +212,7 @@ void SetMode(CoreMode new_mode)
case MODE_JIT: // Switching from interpreter to JIT.
// Don't really need to do much. It'll work, the cache will refill itself.
cpu_core_base = jit;
cpu_core_base = JitInterface::GetCore();
break;
}
}
+2 -76
View File
@@ -15,17 +15,7 @@
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#include "JitCommon/JitBase.h"
#include <vector>
#include <algorithm>
#ifdef _WIN32
#include <windows.h>
#endif
#include "PPCSymbolDB.h"
#include "FileUtil.h"
#include "JitInterface.h"
namespace Profiler
{
@@ -33,73 +23,9 @@ namespace Profiler
bool g_ProfileBlocks;
bool g_ProfileInstructions;
struct BlockStat
{
BlockStat(int bn, u64 c) : blockNum(bn), cost(c) {}
int blockNum;
u64 cost;
bool operator <(const BlockStat &other) const
{ return cost > other.cost; }
};
void WriteProfileResults(const char *filename)
{
std::vector<BlockStat> stats;
stats.reserve(jit->GetBlockCache()->GetNumBlocks());
u64 cost_sum = 0;
#ifdef _WIN32
u64 timecost_sum = 0;
u64 countsPerSec;
QueryPerformanceFrequency((LARGE_INTEGER *)&countsPerSec);
#endif
for (int i = 0; i < jit->GetBlockCache()->GetNumBlocks(); i++)
{
const JitBlock *block = jit->GetBlockCache()->GetBlock(i);
if (block && !block->invalid)
{
// Rough heuristic. Mem instructions should cost more.
u64 cost = block->originalSize * (block->runCount / 4);
#ifdef _WIN32
u64 timecost = block->ticCounter;
#endif
// Todo: tweak.
if (block->runCount >= 1)
stats.push_back(BlockStat(i, cost));
cost_sum += cost;
#ifdef _WIN32
timecost_sum += timecost;
#endif
}
}
sort(stats.begin(), stats.end());
File::IOFile f(filename, "w");
if (!f)
{
PanicAlert("failed to open %s", filename);
return;
}
fprintf(f.GetHandle(), "origAddr\tblkName\tcost\trunCount\ttimeCost\tpercent\ttimePercent\tOvAllinBlkTime(ms)\tblkCodeSize\n");
for (unsigned int i = 0; i < stats.size(); i++)
{
const JitBlock *block = jit->GetBlockCache()->GetBlock(stats[i].blockNum);
if (block && !block->invalid)
{
std::string name = g_symbolDB.GetDescription(block->originalAddress);
double percent = 100.0 * (double)stats[i].cost / (double)cost_sum;
#ifdef _WIN32
double timePercent = 100.0 * (double)block->ticCounter / (double)timecost_sum;
fprintf(f.GetHandle(), "%08x\t%s\t%llu\t%llu\t%llu\t%.2lf\t%llf\t%lf\t%i\n",
block->originalAddress, name.c_str(), stats[i].cost, block->runCount,
block->ticCounter, percent, timePercent,
(double)block->ticCounter*1000.0/(double)countsPerSec, block->codeSize);
#else
fprintf(f.GetHandle(), "%08x\t%s\t%llu\t???\t%.2lf\t???\t???\t%i\n",
block->originalAddress, name.c_str(), stats[i].cost, percent, block->codeSize);
#endif
}
}
JitInterface::WriteProfileResults(filename);
}
} // namespace
+9
View File
@@ -57,6 +57,15 @@
#define PROFILER_VPOP
#endif
struct BlockStat
{
BlockStat(int bn, u64 c) : blockNum(bn), cost(c) {}
int blockNum;
u64 cost;
bool operator <(const BlockStat &other) const
{ return cost > other.cost; }
};
namespace Profiler
{
@@ -52,8 +52,10 @@
#include "MemTools.h"
#include "HW/Memmap.h"
#include "PowerPC/PowerPC.h"
#include "PowerPC/JitInterface.h"
#ifndef _M_GENERIC
#include "PowerPC/JitCommon/JitBase.h"
#include "PowerPC/JitCommon/JitBackpatch.h"
#endif
#include "x64Analyzer.h"
namespace EMM
@@ -77,7 +79,7 @@ LONG NTAPI Handler(PEXCEPTION_POINTERS pPtrs)
PVOID codeAddr = pPtrs->ExceptionRecord->ExceptionAddress;
unsigned char *codePtr = (unsigned char*)codeAddr;
if (!jit->IsInCodeSpace(codePtr)) {
if (!JitInterface::IsInCodeSpace(codePtr)) {
// Let's not prevent debugging.
return (DWORD)EXCEPTION_CONTINUE_SEARCH;
}
@@ -107,7 +109,7 @@ LONG NTAPI Handler(PEXCEPTION_POINTERS pPtrs)
//We could emulate the memory accesses here, but then they would still be around to take up
//execution resources. Instead, we backpatch into a generic memory call and retry.
const u8 *new_rip = jit->BackPatch(codePtr, accessType, emAddress, ctx);
const u8 *new_rip = JitInterface::BackPatch(codePtr, accessType, emAddress, ctx);
// Rip/Eip needs to be updated.
if (new_rip)
@@ -182,6 +184,7 @@ void print_trace(const char * msg)
void sigsegv_handler(int signal, siginfo_t *info, void *raw_context)
{
#ifndef _M_GENERIC
if (signal != SIGSEGV)
{
// We are not interested in other signals - handle it as usual.
@@ -203,7 +206,7 @@ void sigsegv_handler(int signal, siginfo_t *info, void *raw_context)
#else
u8 *fault_instruction_ptr = (u8 *)CREG_EIP(ctx);
#endif
if (!jit->IsInCodeSpace(fault_instruction_ptr)) {
if (!JitInterface::IsInCodeSpace(fault_instruction_ptr)) {
// Let's not prevent debugging.
return;
}
@@ -240,6 +243,7 @@ void sigsegv_handler(int signal, siginfo_t *info, void *raw_context)
CREG_EIP(ctx) = fake_ctx.Eip;
#endif
}
#endif
}
void InstallExceptionHandler()