commit
b47d44ab15
|
@ -219,22 +219,10 @@ set(SRCS
|
||||||
PowerPC/JitCommon/JitAsmCommon.cpp
|
PowerPC/JitCommon/JitAsmCommon.cpp
|
||||||
PowerPC/JitCommon/JitBase.cpp
|
PowerPC/JitCommon/JitBase.cpp
|
||||||
PowerPC/JitCommon/JitCache.cpp
|
PowerPC/JitCommon/JitCache.cpp
|
||||||
PowerPC/JitILCommon/IR.cpp
|
|
||||||
PowerPC/JitILCommon/JitILBase_Branch.cpp
|
|
||||||
PowerPC/JitILCommon/JitILBase_LoadStore.cpp
|
|
||||||
PowerPC/JitILCommon/JitILBase_SystemRegisters.cpp
|
|
||||||
PowerPC/JitILCommon/JitILBase_LoadStoreFloating.cpp
|
|
||||||
PowerPC/JitILCommon/JitILBase_LoadStorePaired.cpp
|
|
||||||
PowerPC/JitILCommon/JitILBase_Paired.cpp
|
|
||||||
PowerPC/JitILCommon/JitILBase_FloatingPoint.cpp
|
|
||||||
PowerPC/JitILCommon/JitILBase_Integer.cpp
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if(_M_X86)
|
if(_M_X86)
|
||||||
set(SRCS ${SRCS}
|
set(SRCS ${SRCS}
|
||||||
PowerPC/Jit64IL/IR_X86.cpp
|
|
||||||
PowerPC/Jit64IL/JitIL.cpp
|
|
||||||
PowerPC/Jit64IL/JitIL_Tables.cpp
|
|
||||||
PowerPC/Jit64/FPURegCache.cpp
|
PowerPC/Jit64/FPURegCache.cpp
|
||||||
PowerPC/Jit64/GPRRegCache.cpp
|
PowerPC/Jit64/GPRRegCache.cpp
|
||||||
PowerPC/Jit64/Jit64_Tables.cpp
|
PowerPC/Jit64/Jit64_Tables.cpp
|
||||||
|
|
|
@ -581,8 +581,6 @@ void SConfig::LoadCoreSettings(IniFile& ini)
|
||||||
core->Get("SlotB", (int*)&m_EXIDevice[1], ExpansionInterface::EXIDEVICE_NONE);
|
core->Get("SlotB", (int*)&m_EXIDevice[1], ExpansionInterface::EXIDEVICE_NONE);
|
||||||
core->Get("SerialPort1", (int*)&m_EXIDevice[2], ExpansionInterface::EXIDEVICE_NONE);
|
core->Get("SerialPort1", (int*)&m_EXIDevice[2], ExpansionInterface::EXIDEVICE_NONE);
|
||||||
core->Get("BBA_MAC", &m_bba_mac);
|
core->Get("BBA_MAC", &m_bba_mac);
|
||||||
core->Get("TimeProfiling", &bJITILTimeProfiling, false);
|
|
||||||
core->Get("OutputIR", &bJITILOutputIR, false);
|
|
||||||
for (int i = 0; i < SerialInterface::MAX_SI_CHANNELS; ++i)
|
for (int i = 0; i < SerialInterface::MAX_SI_CHANNELS; ++i)
|
||||||
{
|
{
|
||||||
core->Get(StringFromFormat("SIDevice%i", i), (u32*)&m_SIDevice[i],
|
core->Get(StringFromFormat("SIDevice%i", i), (u32*)&m_SIDevice[i],
|
||||||
|
|
|
@ -74,9 +74,8 @@ struct SConfig : NonCopyable
|
||||||
bool bAutomaticStart = false;
|
bool bAutomaticStart = false;
|
||||||
bool bBootToPause = false;
|
bool bBootToPause = false;
|
||||||
|
|
||||||
int iCPUCore;
|
int iCPUCore; // Uses the values of PowerPC::CPUCore
|
||||||
|
|
||||||
// JIT (shared between JIT and JITIL)
|
|
||||||
bool bJITNoBlockCache = false;
|
bool bJITNoBlockCache = false;
|
||||||
bool bJITNoBlockLinking = false;
|
bool bJITNoBlockLinking = false;
|
||||||
bool bJITOff = false;
|
bool bJITOff = false;
|
||||||
|
@ -91,8 +90,6 @@ struct SConfig : NonCopyable
|
||||||
bool bJITPairedOff = false;
|
bool bJITPairedOff = false;
|
||||||
bool bJITSystemRegistersOff = false;
|
bool bJITSystemRegistersOff = false;
|
||||||
bool bJITBranchOff = false;
|
bool bJITBranchOff = false;
|
||||||
bool bJITILTimeProfiling = false;
|
|
||||||
bool bJITILOutputIR = false;
|
|
||||||
|
|
||||||
bool bFastmem;
|
bool bFastmem;
|
||||||
bool bFPRF = false;
|
bool bFPRF = false;
|
||||||
|
|
|
@ -249,18 +249,6 @@
|
||||||
<ClCompile Include="PowerPC\Interpreter\Interpreter_SystemRegisters.cpp" />
|
<ClCompile Include="PowerPC\Interpreter\Interpreter_SystemRegisters.cpp" />
|
||||||
<ClCompile Include="PowerPC\Interpreter\Interpreter_Tables.cpp" />
|
<ClCompile Include="PowerPC\Interpreter\Interpreter_Tables.cpp" />
|
||||||
<ClCompile Include="PowerPC\Jit64Common\ConstantPool.cpp" />
|
<ClCompile Include="PowerPC\Jit64Common\ConstantPool.cpp" />
|
||||||
<ClCompile Include="PowerPC\JitILCommon\IR.cpp" />
|
|
||||||
<ClCompile Include="PowerPC\JitILCommon\JitILBase_Branch.cpp" />
|
|
||||||
<ClCompile Include="PowerPC\JitILCommon\JitILBase_FloatingPoint.cpp" />
|
|
||||||
<ClCompile Include="PowerPC\JitILCommon\JitILBase_Integer.cpp" />
|
|
||||||
<ClCompile Include="PowerPC\JitILCommon\JitILBase_LoadStore.cpp" />
|
|
||||||
<ClCompile Include="PowerPC\JitILCommon\JitILBase_LoadStoreFloating.cpp" />
|
|
||||||
<ClCompile Include="PowerPC\JitILCommon\JitILBase_LoadStorePaired.cpp" />
|
|
||||||
<ClCompile Include="PowerPC\JitILCommon\JitILBase_Paired.cpp" />
|
|
||||||
<ClCompile Include="PowerPC\JitILCommon\JitILBase_SystemRegisters.cpp" />
|
|
||||||
<ClCompile Include="PowerPC\Jit64IL\IR_X86.cpp" />
|
|
||||||
<ClCompile Include="PowerPC\Jit64IL\JitIL.cpp" />
|
|
||||||
<ClCompile Include="PowerPC\Jit64IL\JitIL_Tables.cpp" />
|
|
||||||
<ClCompile Include="PowerPC\Jit64\FPURegCache.cpp" />
|
<ClCompile Include="PowerPC\Jit64\FPURegCache.cpp" />
|
||||||
<ClCompile Include="PowerPC\Jit64\GPRRegCache.cpp" />
|
<ClCompile Include="PowerPC\Jit64\GPRRegCache.cpp" />
|
||||||
<ClCompile Include="PowerPC\Jit64\Jit.cpp" />
|
<ClCompile Include="PowerPC\Jit64\Jit.cpp" />
|
||||||
|
@ -496,14 +484,11 @@
|
||||||
<ClInclude Include="PowerPC\Interpreter\Interpreter.h" />
|
<ClInclude Include="PowerPC\Interpreter\Interpreter.h" />
|
||||||
<ClInclude Include="PowerPC\Interpreter\Interpreter_FPUtils.h" />
|
<ClInclude Include="PowerPC\Interpreter\Interpreter_FPUtils.h" />
|
||||||
<ClInclude Include="PowerPC\Jit64Common\ConstantPool.h" />
|
<ClInclude Include="PowerPC\Jit64Common\ConstantPool.h" />
|
||||||
<ClInclude Include="PowerPC\Jit64IL\JitIL.h" />
|
|
||||||
<ClInclude Include="PowerPC\Jit64\FPURegCache.h" />
|
<ClInclude Include="PowerPC\Jit64\FPURegCache.h" />
|
||||||
<ClInclude Include="PowerPC\Jit64\GPRRegCache.h" />
|
<ClInclude Include="PowerPC\Jit64\GPRRegCache.h" />
|
||||||
<ClInclude Include="PowerPC\Jit64\Jit.h" />
|
<ClInclude Include="PowerPC\Jit64\Jit.h" />
|
||||||
<ClInclude Include="PowerPC\Jit64\JitAsm.h" />
|
<ClInclude Include="PowerPC\Jit64\JitAsm.h" />
|
||||||
<ClInclude Include="PowerPC\Jit64\JitRegCache.h" />
|
<ClInclude Include="PowerPC\Jit64\JitRegCache.h" />
|
||||||
<ClInclude Include="PowerPC\JitILCommon\IR.h" />
|
|
||||||
<ClInclude Include="PowerPC\JitILCommon\JitILBase.h" />
|
|
||||||
<ClInclude Include="PowerPC\Jit64Common\BlockCache.h" />
|
<ClInclude Include="PowerPC\Jit64Common\BlockCache.h" />
|
||||||
<ClInclude Include="PowerPC\Jit64Common\EmuCodeBlock.h" />
|
<ClInclude Include="PowerPC\Jit64Common\EmuCodeBlock.h" />
|
||||||
<ClInclude Include="PowerPC\Jit64Common\FarCodeCache.h" />
|
<ClInclude Include="PowerPC\Jit64Common\FarCodeCache.h" />
|
||||||
|
|
|
@ -43,9 +43,6 @@
|
||||||
<Filter Include="PowerPC\JitCommon">
|
<Filter Include="PowerPC\JitCommon">
|
||||||
<UniqueIdentifier>{c88ec388-371f-4401-851c-a32dcdc0b88b}</UniqueIdentifier>
|
<UniqueIdentifier>{c88ec388-371f-4401-851c-a32dcdc0b88b}</UniqueIdentifier>
|
||||||
</Filter>
|
</Filter>
|
||||||
<Filter Include="PowerPC\JitIL">
|
|
||||||
<UniqueIdentifier>{f26d3866-92d1-4623-9445-caf9a065ed74}</UniqueIdentifier>
|
|
||||||
</Filter>
|
|
||||||
<Filter Include="DSPCore\Interpreter">
|
<Filter Include="DSPCore\Interpreter">
|
||||||
<UniqueIdentifier>{6204f663-bbd0-4eb5-bc15-e3778d8b6091}</UniqueIdentifier>
|
<UniqueIdentifier>{6204f663-bbd0-4eb5-bc15-e3778d8b6091}</UniqueIdentifier>
|
||||||
</Filter>
|
</Filter>
|
||||||
|
@ -112,9 +109,6 @@
|
||||||
<Filter Include="HW %28Flipper/Hollywood%29\Wii IPC">
|
<Filter Include="HW %28Flipper/Hollywood%29\Wii IPC">
|
||||||
<UniqueIdentifier>{2b41ab45-ba8c-45dc-92cc-9107c1fa3e36}</UniqueIdentifier>
|
<UniqueIdentifier>{2b41ab45-ba8c-45dc-92cc-9107c1fa3e36}</UniqueIdentifier>
|
||||||
</Filter>
|
</Filter>
|
||||||
<Filter Include="PowerPC\JitILCommon">
|
|
||||||
<UniqueIdentifier>{827afa93-1a80-4835-93ae-b5516d95867f}</UniqueIdentifier>
|
|
||||||
</Filter>
|
|
||||||
<Filter Include="PowerPC\Jit64Common">
|
<Filter Include="PowerPC\Jit64Common">
|
||||||
<UniqueIdentifier>{81956f71-d9fe-454f-96a6-855195d611c4}</UniqueIdentifier>
|
<UniqueIdentifier>{81956f71-d9fe-454f-96a6-855195d611c4}</UniqueIdentifier>
|
||||||
</Filter>
|
</Filter>
|
||||||
|
@ -631,15 +625,6 @@
|
||||||
<ClCompile Include="PowerPC\JitCommon\JitCache.cpp">
|
<ClCompile Include="PowerPC\JitCommon\JitCache.cpp">
|
||||||
<Filter>PowerPC\JitCommon</Filter>
|
<Filter>PowerPC\JitCommon</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="PowerPC\Jit64IL\IR_X86.cpp">
|
|
||||||
<Filter>PowerPC\JitIL</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="PowerPC\Jit64IL\JitIL.cpp">
|
|
||||||
<Filter>PowerPC\JitIL</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="PowerPC\Jit64IL\JitIL_Tables.cpp">
|
|
||||||
<Filter>PowerPC\JitIL</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="PowerPC\Jit64\FPURegCache.cpp">
|
<ClCompile Include="PowerPC\Jit64\FPURegCache.cpp">
|
||||||
<Filter>PowerPC\Jit64</Filter>
|
<Filter>PowerPC\Jit64</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
@ -676,33 +661,6 @@
|
||||||
<ClCompile Include="PowerPC\Jit64\JitRegCache.cpp">
|
<ClCompile Include="PowerPC\Jit64\JitRegCache.cpp">
|
||||||
<Filter>PowerPC\Jit64</Filter>
|
<Filter>PowerPC\Jit64</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="PowerPC\JitILCommon\JitILBase_Branch.cpp">
|
|
||||||
<Filter>PowerPC\JitILCommon</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="PowerPC\JitILCommon\JitILBase_FloatingPoint.cpp">
|
|
||||||
<Filter>PowerPC\JitILCommon</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="PowerPC\JitILCommon\JitILBase_Integer.cpp">
|
|
||||||
<Filter>PowerPC\JitILCommon</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="PowerPC\JitILCommon\JitILBase_LoadStore.cpp">
|
|
||||||
<Filter>PowerPC\JitILCommon</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="PowerPC\JitILCommon\JitILBase_LoadStoreFloating.cpp">
|
|
||||||
<Filter>PowerPC\JitILCommon</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="PowerPC\JitILCommon\JitILBase_LoadStorePaired.cpp">
|
|
||||||
<Filter>PowerPC\JitILCommon</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="PowerPC\JitILCommon\JitILBase_Paired.cpp">
|
|
||||||
<Filter>PowerPC\JitILCommon</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="PowerPC\JitILCommon\JitILBase_SystemRegisters.cpp">
|
|
||||||
<Filter>PowerPC\JitILCommon</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="PowerPC\JitILCommon\IR.cpp">
|
|
||||||
<Filter>PowerPC\JitILCommon</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="HW\GCKeyboardEmu.cpp">
|
<ClCompile Include="HW\GCKeyboardEmu.cpp">
|
||||||
<Filter>HW %28Flipper/Hollywood%29\GCKeyboard</Filter>
|
<Filter>HW %28Flipper/Hollywood%29\GCKeyboard</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
@ -1328,9 +1286,6 @@
|
||||||
<ClInclude Include="PowerPC\JitCommon\JitCache.h">
|
<ClInclude Include="PowerPC\JitCommon\JitCache.h">
|
||||||
<Filter>PowerPC\JitCommon</Filter>
|
<Filter>PowerPC\JitCommon</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="PowerPC\Jit64IL\JitIL.h">
|
|
||||||
<Filter>PowerPC\JitIL</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="PowerPC\Jit64\FPURegCache.h">
|
<ClInclude Include="PowerPC\Jit64\FPURegCache.h">
|
||||||
<Filter>PowerPC\Jit64</Filter>
|
<Filter>PowerPC\Jit64</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
@ -1343,12 +1298,6 @@
|
||||||
<ClInclude Include="PowerPC\Jit64\JitAsm.h">
|
<ClInclude Include="PowerPC\Jit64\JitAsm.h">
|
||||||
<Filter>PowerPC\Jit64</Filter>
|
<Filter>PowerPC\Jit64</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="PowerPC\JitILCommon\JitILBase.h">
|
|
||||||
<Filter>PowerPC\JitILCommon</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="PowerPC\JitILCommon\IR.h">
|
|
||||||
<Filter>PowerPC\JitILCommon</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="MachineContext.h" />
|
<ClInclude Include="MachineContext.h" />
|
||||||
<ClInclude Include="HW\GCKeyboardEmu.h">
|
<ClInclude Include="HW\GCKeyboardEmu.h">
|
||||||
<Filter>HW %28Flipper/Hollywood%29\GCKeyboard</Filter>
|
<Filter>HW %28Flipper/Hollywood%29\GCKeyboard</Filter>
|
||||||
|
|
|
@ -81,7 +81,7 @@ struct DTMHeader
|
||||||
bool bProgressive;
|
bool bProgressive;
|
||||||
bool bDSPHLE;
|
bool bDSPHLE;
|
||||||
bool bFastDiscSpeed;
|
bool bFastDiscSpeed;
|
||||||
u8 CPUCore; // 0 = interpreter, 1 = JIT, 2 = JITIL
|
u8 CPUCore; // Uses the values of PowerPC::CPUCore
|
||||||
bool bEFBAccessEnable;
|
bool bEFBAccessEnable;
|
||||||
bool bEFBCopyEnable;
|
bool bEFBCopyEnable;
|
||||||
bool bSkipEFBCopyToRam;
|
bool bSkipEFBCopyToRam;
|
||||||
|
|
|
@ -19,7 +19,6 @@ namespace PPCAnalyst
|
||||||
class CodeBuffer;
|
class CodeBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The following register assignments are common to Jit64 and Jit64IL:
|
|
||||||
// RSCRATCH and RSCRATCH2 are always scratch registers and can be used without
|
// RSCRATCH and RSCRATCH2 are always scratch registers and can be used without
|
||||||
// limitation.
|
// limitation.
|
||||||
constexpr Gen::X64Reg RSCRATCH = Gen::RAX;
|
constexpr Gen::X64Reg RSCRATCH = Gen::RAX;
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,688 +0,0 @@
|
||||||
// Copyright 2008 Dolphin Emulator Project
|
|
||||||
// Licensed under GPLv2+
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#include "Core/PowerPC/Jit64IL/JitIL.h"
|
|
||||||
|
|
||||||
#include <cinttypes>
|
|
||||||
#include <ctime> // For profiling
|
|
||||||
#include <map>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "Common/CommonTypes.h"
|
|
||||||
#include "Common/FileUtil.h"
|
|
||||||
#include "Common/Intrinsics.h"
|
|
||||||
#include "Common/Logging/Log.h"
|
|
||||||
#include "Common/StringUtil.h"
|
|
||||||
#include "Common/x64ABI.h"
|
|
||||||
#include "Core/HLE/HLE.h"
|
|
||||||
#include "Core/HW/CPU.h"
|
|
||||||
#include "Core/PatchEngine.h"
|
|
||||||
#include "Core/PowerPC/Jit64Common/Jit64PowerPCState.h"
|
|
||||||
#include "Core/PowerPC/PowerPC.h"
|
|
||||||
#include "Core/PowerPC/Profiler.h"
|
|
||||||
|
|
||||||
using namespace Gen;
|
|
||||||
using namespace PowerPC;
|
|
||||||
|
|
||||||
// Dolphin's PowerPC->x86 JIT dynamic recompiler
|
|
||||||
// (Nearly) all code by ector (hrydgard)
|
|
||||||
// Features:
|
|
||||||
// * x86 & x64 support, lots of shared code.
|
|
||||||
// * Basic block linking
|
|
||||||
// * Fast dispatcher
|
|
||||||
|
|
||||||
// Unfeatures:
|
|
||||||
// * Does not recompile all instructions - sometimes falls back to inserting a CALL to the
|
|
||||||
// corresponding Interpreter function.
|
|
||||||
|
|
||||||
// Various notes below
|
|
||||||
|
|
||||||
// Register allocation
|
|
||||||
// RAX - Generic quicktemp register
|
|
||||||
// RBX - point to base of memory map
|
|
||||||
// RSI RDI R12 R13 R14 R15 - free for allocation
|
|
||||||
// RCX RDX R8 R9 R10 R11 - allocate in emergencies. These need to be flushed before functions are
|
|
||||||
// called.
|
|
||||||
// RSP - stack pointer, do not generally use, very dangerous
|
|
||||||
// RBP - ?
|
|
||||||
|
|
||||||
// IMPORTANT:
|
|
||||||
// Make sure that all generated code and all emulator state sits under the 2GB boundary so that
|
|
||||||
// RIP addressing can be used easily. Windows will always allocate static code under the 2GB
|
|
||||||
// boundary.
|
|
||||||
// Also make sure to use VirtualAlloc and specify EXECUTE permission.
|
|
||||||
|
|
||||||
// Open questions
|
|
||||||
// * Should there be any statically allocated registers? r3, r4, r5, r8, r0 come to mind.. maybe sp
|
|
||||||
// * Does it make sense to finish off the remaining non-jitted instructions? Seems we are hitting
|
|
||||||
// diminishing returns.
|
|
||||||
|
|
||||||
// Other considerations
|
|
||||||
//
|
|
||||||
// Many instructions have shorter forms for EAX. However, I believe their performance boost
|
|
||||||
// will be as small to be negligible, so I haven't dirtied up the code with that. AMD recommends it
|
|
||||||
// in their
|
|
||||||
// optimization manuals, though.
|
|
||||||
//
|
|
||||||
// We support block linking. Reserve space at the exits of every block for a full 5-byte jmp. Save
|
|
||||||
// 16-bit offsets
|
|
||||||
// from the starts of each block, marking the exits so that they can be nicely patched at any time.
|
|
||||||
//
|
|
||||||
// Blocks do NOT use call/ret, they only jmp to each other and to the dispatcher when necessary.
|
|
||||||
//
|
|
||||||
// All blocks that can be precompiled will be precompiled. Code will be memory protected - any write
|
|
||||||
// will mark
|
|
||||||
// the region as non-compilable, and all links to the page will be torn out and replaced with
|
|
||||||
// dispatcher jmps.
|
|
||||||
//
|
|
||||||
// Alternatively, icbi instruction SHOULD mark where we can't compile
|
|
||||||
//
|
|
||||||
// Seldom-happening events is handled by adding a decrement of a counter to all blr instructions
|
|
||||||
// (which are
|
|
||||||
// expensive anyway since we need to return to dispatcher, except when they can be predicted).
|
|
||||||
|
|
||||||
// TODO: SERIOUS synchronization problem with the video backend setting tokens and breakpoints in
|
|
||||||
// dual core mode!!!
|
|
||||||
// Somewhat fixed by disabling idle skipping when certain interrupts are enabled
|
|
||||||
// This is no permanent reliable fix
|
|
||||||
// TODO: Zeldas go whacko when you hang the gfx thread
|
|
||||||
|
|
||||||
// Idea - Accurate exception handling
|
|
||||||
// Compute register state at a certain instruction by running the JIT in "dry mode", and stopping at
|
|
||||||
// the right place.
|
|
||||||
// Not likely to be done :P
|
|
||||||
|
|
||||||
// Optimization Ideas -
|
|
||||||
/*
|
|
||||||
* Assume SP is in main RAM (in Wii mode too?) - partly done
|
|
||||||
* Assume all floating point loads and double precision loads+stores are to/from main ram
|
|
||||||
(single precision can be used in write gather pipe, specialized fast check added)
|
|
||||||
* AMD only - use movaps instead of movapd when loading ps from memory?
|
|
||||||
* HLE functions like floorf, sin, memcpy, etc - they can be much faster
|
|
||||||
* ABI optimizations - drop F0-F13 on blr, for example. Watch out for context switching.
|
|
||||||
CR2-CR4 are non-volatile, rest of CR is volatile -> dropped on blr.
|
|
||||||
R5-R12 are volatile -> dropped on blr.
|
|
||||||
* classic inlining across calls.
|
|
||||||
|
|
||||||
Low hanging fruit:
|
|
||||||
stfd -- guaranteed in memory
|
|
||||||
cmpl
|
|
||||||
mulli
|
|
||||||
stfs
|
|
||||||
stwu
|
|
||||||
lb/stzx
|
|
||||||
|
|
||||||
bcx - optimize!
|
|
||||||
bcctr
|
|
||||||
stfs
|
|
||||||
psq_st
|
|
||||||
addx
|
|
||||||
orx
|
|
||||||
rlwimix
|
|
||||||
fcmpo
|
|
||||||
DSP_UpdateARAMDMA
|
|
||||||
lfd
|
|
||||||
stwu
|
|
||||||
cntlzwx
|
|
||||||
bcctrx
|
|
||||||
WriteBigEData
|
|
||||||
|
|
||||||
TODO
|
|
||||||
lha
|
|
||||||
srawx
|
|
||||||
addic_rc
|
|
||||||
addex
|
|
||||||
subfcx
|
|
||||||
subfex
|
|
||||||
|
|
||||||
fmaddx
|
|
||||||
fmulx
|
|
||||||
faddx
|
|
||||||
fnegx
|
|
||||||
frspx
|
|
||||||
frsqrtex
|
|
||||||
ps_sum0
|
|
||||||
ps_muls0
|
|
||||||
ps_adds1
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#include <windows.h>
|
|
||||||
#else
|
|
||||||
#include <memory>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#if defined(__clang__)
|
|
||||||
#if !__has_builtin(__builtin_ia32_rdtsc)
|
|
||||||
static inline uint64_t __rdtsc()
|
|
||||||
{
|
|
||||||
uint32_t lo, hi;
|
|
||||||
#ifdef _LP64
|
|
||||||
__asm__ __volatile__("xorl %%eax,%%eax \n cpuid" ::: "%rax", "%rbx", "%rcx", "%rdx");
|
|
||||||
__asm__ __volatile__("rdtsc" : "=a"(lo), "=d"(hi));
|
|
||||||
return (uint64_t)hi << 32 | lo;
|
|
||||||
#else
|
|
||||||
__asm__ __volatile__("xor %%eax,%%eax;"
|
|
||||||
"push %%ebx;"
|
|
||||||
"cpuid;"
|
|
||||||
"pop %%ebx;" ::
|
|
||||||
: "%eax", "%ecx", "%edx");
|
|
||||||
__asm__ __volatile__("rdtsc" : "=a"(lo), "=d"(hi));
|
|
||||||
#endif
|
|
||||||
return (uint64_t)hi << 32 | lo;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace JitILProfiler
|
|
||||||
{
|
|
||||||
struct Block
|
|
||||||
{
|
|
||||||
u32 index;
|
|
||||||
u64 codeHash;
|
|
||||||
u64 totalElapsed;
|
|
||||||
u64 numberOfCalls;
|
|
||||||
|
|
||||||
Block() : index(0), codeHash(0), totalElapsed(0), numberOfCalls(0) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
static std::vector<Block> blocks;
|
|
||||||
static u32 blockIndex;
|
|
||||||
static u64 beginTime;
|
|
||||||
static Block& Add(u64 codeHash)
|
|
||||||
{
|
|
||||||
const u32 _blockIndex = (u32)blocks.size();
|
|
||||||
blocks.emplace_back();
|
|
||||||
Block& block = blocks.back();
|
|
||||||
block.index = _blockIndex;
|
|
||||||
block.codeHash = codeHash;
|
|
||||||
return block;
|
|
||||||
}
|
|
||||||
|
|
||||||
// These functions need to be static because they are called with
|
|
||||||
// ABI_CallFunction().
|
|
||||||
static void Begin(u32 index)
|
|
||||||
{
|
|
||||||
blockIndex = index;
|
|
||||||
beginTime = __rdtsc();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void End()
|
|
||||||
{
|
|
||||||
const u64 endTime = __rdtsc();
|
|
||||||
const u64 duration = endTime - beginTime;
|
|
||||||
Block& block = blocks[blockIndex];
|
|
||||||
block.totalElapsed += duration;
|
|
||||||
++block.numberOfCalls;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct JitILProfilerFinalizer
|
|
||||||
{
|
|
||||||
virtual ~JitILProfilerFinalizer()
|
|
||||||
{
|
|
||||||
std::string filename = StringFromFormat("JitIL_profiling_%d.csv", (int)time(nullptr));
|
|
||||||
File::IOFile file(filename, "w");
|
|
||||||
setvbuf(file.GetHandle(), nullptr, _IOFBF, 1024 * 1024);
|
|
||||||
fprintf(file.GetHandle(), "code hash,total elapsed,number of calls,elapsed per call\n");
|
|
||||||
for (auto& block : blocks)
|
|
||||||
{
|
|
||||||
const u64 codeHash = block.codeHash;
|
|
||||||
const u64 totalElapsed = block.totalElapsed;
|
|
||||||
const u64 numberOfCalls = block.numberOfCalls;
|
|
||||||
const double elapsedPerCall = totalElapsed / (double)numberOfCalls;
|
|
||||||
fprintf(file.GetHandle(), "%016" PRIx64 ",%" PRId64 ",%" PRId64 ",%f\n", codeHash,
|
|
||||||
totalElapsed, numberOfCalls, elapsedPerCall);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static std::unique_ptr<JitILProfilerFinalizer> finalizer;
|
|
||||||
static void Init()
|
|
||||||
{
|
|
||||||
finalizer = std::make_unique<JitILProfilerFinalizer>();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Shutdown()
|
|
||||||
{
|
|
||||||
finalizer.reset();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void JitIL::Init()
|
|
||||||
{
|
|
||||||
InitializeInstructionTables();
|
|
||||||
EnableBlockLink();
|
|
||||||
|
|
||||||
jo.optimizeGatherPipe = true;
|
|
||||||
jo.accurateSinglePrecision = false;
|
|
||||||
UpdateMemoryOptions();
|
|
||||||
|
|
||||||
const size_t routines_size = asm_routines.CODE_SIZE;
|
|
||||||
const size_t trampolines_size = jo.memcheck ? TRAMPOLINE_CODE_SIZE_MMU : TRAMPOLINE_CODE_SIZE;
|
|
||||||
const size_t farcode_size = jo.memcheck ? FARCODE_SIZE_MMU : FARCODE_SIZE;
|
|
||||||
const size_t constpool_size = m_const_pool.CONST_POOL_SIZE;
|
|
||||||
AllocCodeSpace(CODE_SIZE + routines_size + trampolines_size + farcode_size + constpool_size);
|
|
||||||
AddChildCodeSpace(&asm_routines, routines_size);
|
|
||||||
AddChildCodeSpace(&trampolines, trampolines_size);
|
|
||||||
AddChildCodeSpace(&m_far_code, farcode_size);
|
|
||||||
m_const_pool.Init(AllocChildCodeSpace(constpool_size), constpool_size);
|
|
||||||
|
|
||||||
blocks.Init();
|
|
||||||
asm_routines.Init(nullptr);
|
|
||||||
m_far_code.Init();
|
|
||||||
Clear();
|
|
||||||
|
|
||||||
code_block.m_stats = &js.st;
|
|
||||||
code_block.m_gpa = &js.gpa;
|
|
||||||
code_block.m_fpa = &js.fpa;
|
|
||||||
|
|
||||||
if (SConfig::GetInstance().bJITILTimeProfiling)
|
|
||||||
{
|
|
||||||
JitILProfiler::Init();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitIL::ClearCache()
|
|
||||||
{
|
|
||||||
blocks.Clear();
|
|
||||||
trampolines.ClearCodeSpace();
|
|
||||||
m_far_code.ClearCodeSpace();
|
|
||||||
ClearCodeSpace();
|
|
||||||
Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitIL::Shutdown()
|
|
||||||
{
|
|
||||||
if (SConfig::GetInstance().bJITILTimeProfiling)
|
|
||||||
{
|
|
||||||
JitILProfiler::Shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
FreeCodeSpace();
|
|
||||||
|
|
||||||
blocks.Shutdown();
|
|
||||||
m_far_code.Shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitIL::FallBackToInterpreter(UGeckoInstruction _inst)
|
|
||||||
{
|
|
||||||
ibuild.EmitFallBackToInterpreter(ibuild.EmitIntConst(_inst.hex),
|
|
||||||
ibuild.EmitIntConst(js.compilerPC));
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitIL::HLEFunction(UGeckoInstruction _inst)
|
|
||||||
{
|
|
||||||
ABI_CallFunctionCC(HLE::Execute, js.compilerPC, _inst.hex);
|
|
||||||
MOV(32, R(RSCRATCH), PPCSTATE(npc));
|
|
||||||
WriteExitDestInOpArg(R(RSCRATCH));
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitIL::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("log64.txt", "w");
|
|
||||||
}
|
|
||||||
fprintf(f.GetHandle(), "%08x r0: %08x r5: %08x r6: %08x\n", PC, PowerPC::ppcState.gpr[0],
|
|
||||||
PowerPC::ppcState.gpr[5], PowerPC::ppcState.gpr[6]);
|
|
||||||
f.Flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (been_here.find(PC) != been_here.end())
|
|
||||||
{
|
|
||||||
been_here.find(PC)->second++;
|
|
||||||
if ((been_here.find(PC)->second) & 1023)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
INFO_LOG(DYNA_REC, "I'm here - PC = %08x , LR = %08x", PC, LR);
|
|
||||||
been_here[PC] = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitIL::Cleanup()
|
|
||||||
{
|
|
||||||
// SPEED HACK: MMCR0/MMCR1 should be checked at run-time, not at compile time.
|
|
||||||
if (MMCR0.Hex || MMCR1.Hex)
|
|
||||||
ABI_CallFunctionCCC(PowerPC::UpdatePerformanceMonitor, js.downcountAmount, js.numLoadStoreInst,
|
|
||||||
js.numFloatingPointInst);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitIL::WriteExit(u32 destination)
|
|
||||||
{
|
|
||||||
Cleanup();
|
|
||||||
if (SConfig::GetInstance().bJITILTimeProfiling)
|
|
||||||
{
|
|
||||||
ABI_CallFunction(JitILProfiler::End);
|
|
||||||
}
|
|
||||||
SUB(32, PPCSTATE(downcount), Imm32(js.downcountAmount));
|
|
||||||
|
|
||||||
// If nobody has taken care of this yet (this can be removed when all branches are done)
|
|
||||||
JitBlock* b = js.curBlock;
|
|
||||||
JitBlock::LinkData linkData;
|
|
||||||
linkData.exitAddress = destination;
|
|
||||||
linkData.exitPtrs = GetWritableCodePtr();
|
|
||||||
linkData.linkStatus = false;
|
|
||||||
|
|
||||||
MOV(32, PPCSTATE(pc), Imm32(destination));
|
|
||||||
JMP(asm_routines.dispatcher, true);
|
|
||||||
|
|
||||||
b->linkData.push_back(linkData);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitIL::WriteExitDestInOpArg(const OpArg& arg)
|
|
||||||
{
|
|
||||||
MOV(32, PPCSTATE(pc), arg);
|
|
||||||
Cleanup();
|
|
||||||
if (SConfig::GetInstance().bJITILTimeProfiling)
|
|
||||||
{
|
|
||||||
ABI_CallFunction(JitILProfiler::End);
|
|
||||||
}
|
|
||||||
SUB(32, PPCSTATE(downcount), Imm32(js.downcountAmount));
|
|
||||||
JMP(asm_routines.dispatcher, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitIL::WriteRfiExitDestInOpArg(const OpArg& arg)
|
|
||||||
{
|
|
||||||
MOV(32, PPCSTATE(pc), arg);
|
|
||||||
MOV(32, PPCSTATE(npc), arg);
|
|
||||||
Cleanup();
|
|
||||||
if (SConfig::GetInstance().bJITILTimeProfiling)
|
|
||||||
{
|
|
||||||
ABI_CallFunction(JitILProfiler::End);
|
|
||||||
}
|
|
||||||
ABI_CallFunction(PowerPC::CheckExceptions);
|
|
||||||
SUB(32, PPCSTATE(downcount), Imm32(js.downcountAmount));
|
|
||||||
JMP(asm_routines.dispatcher, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitIL::WriteExceptionExit()
|
|
||||||
{
|
|
||||||
Cleanup();
|
|
||||||
if (SConfig::GetInstance().bJITILTimeProfiling)
|
|
||||||
{
|
|
||||||
ABI_CallFunction(JitILProfiler::End);
|
|
||||||
}
|
|
||||||
MOV(32, R(EAX), PPCSTATE(pc));
|
|
||||||
MOV(32, PPCSTATE(npc), R(EAX));
|
|
||||||
ABI_CallFunction(PowerPC::CheckExceptions);
|
|
||||||
SUB(32, PPCSTATE(downcount), Imm32(js.downcountAmount));
|
|
||||||
JMP(asm_routines.dispatcher, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitIL::Run()
|
|
||||||
{
|
|
||||||
CompiledCode pExecAddr = (CompiledCode)asm_routines.enterCode;
|
|
||||||
pExecAddr();
|
|
||||||
// Will return when PowerPC::state changes
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitIL::SingleStep()
|
|
||||||
{
|
|
||||||
CompiledCode pExecAddr = (CompiledCode)asm_routines.enterCode;
|
|
||||||
pExecAddr();
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitIL::Trace()
|
|
||||||
{
|
|
||||||
std::string regs;
|
|
||||||
std::string fregs;
|
|
||||||
|
|
||||||
#ifdef JIT_LOG_GPR
|
|
||||||
for (int i = 0; i < 32; i++)
|
|
||||||
{
|
|
||||||
regs += StringFromFormat("r%02d: %08x ", i, PowerPC::ppcState.gpr[i]);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef JIT_LOG_FPR
|
|
||||||
for (int i = 0; i < 32; i++)
|
|
||||||
{
|
|
||||||
fregs += StringFromFormat("f%02d: %016x ", i, riPS0(i));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
DEBUG_LOG(DYNA_REC, "JITIL PC: %08x SRR0: %08x SRR1: %08x CRval: "
|
|
||||||
"%016lx%016lx%016lx%016lx%016lx%016lx%016lx%016lx FPSCR: %08x MSR: %08x LR: "
|
|
||||||
"%08x %s %s",
|
|
||||||
PC, SRR0, SRR1, (unsigned long)PowerPC::ppcState.cr_val[0],
|
|
||||||
(unsigned long)PowerPC::ppcState.cr_val[1], (unsigned long)PowerPC::ppcState.cr_val[2],
|
|
||||||
(unsigned long)PowerPC::ppcState.cr_val[3], (unsigned long)PowerPC::ppcState.cr_val[4],
|
|
||||||
(unsigned long)PowerPC::ppcState.cr_val[5], (unsigned long)PowerPC::ppcState.cr_val[6],
|
|
||||||
(unsigned long)PowerPC::ppcState.cr_val[7], PowerPC::ppcState.fpscr,
|
|
||||||
PowerPC::ppcState.msr, PowerPC::ppcState.spr[8], regs.c_str(), fregs.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitIL::Jit(u32 em_address)
|
|
||||||
{
|
|
||||||
if (IsAlmostFull() || m_far_code.IsAlmostFull() || trampolines.IsAlmostFull() ||
|
|
||||||
SConfig::GetInstance().bJITNoBlockCache)
|
|
||||||
{
|
|
||||||
ClearCache();
|
|
||||||
}
|
|
||||||
|
|
||||||
int blockSize = code_buffer.GetSize();
|
|
||||||
|
|
||||||
if (SConfig::GetInstance().bEnableDebugging)
|
|
||||||
{
|
|
||||||
// We can link blocks as long as we are not single stepping and there are no breakpoints here
|
|
||||||
EnableBlockLink();
|
|
||||||
|
|
||||||
// Comment out the following to disable breakpoints (speed-up)
|
|
||||||
if (!Profiler::g_ProfileBlocks)
|
|
||||||
{
|
|
||||||
if (CPU::IsStepping())
|
|
||||||
{
|
|
||||||
blockSize = 1;
|
|
||||||
|
|
||||||
// Do not link this block to other blocks While single stepping
|
|
||||||
jo.enableBlocklink = false;
|
|
||||||
}
|
|
||||||
Trace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 = analyzer.Analyze(em_address, &code_block, &code_buffer, blockSize);
|
|
||||||
|
|
||||||
if (code_block.m_memory_exception)
|
|
||||||
{
|
|
||||||
// Address of instruction could not be translated
|
|
||||||
NPC = nextPC;
|
|
||||||
PowerPC::ppcState.Exceptions |= EXCEPTION_ISI;
|
|
||||||
PowerPC::CheckExceptions();
|
|
||||||
WARN_LOG(POWERPC, "ISI exception at 0x%08x", nextPC);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
JitBlock* b = blocks.AllocateBlock(em_address);
|
|
||||||
DoJit(em_address, &code_buffer, b, nextPC);
|
|
||||||
blocks.FinalizeBlock(*b, jo.enableBlocklink, code_block.m_physical_addresses);
|
|
||||||
}
|
|
||||||
|
|
||||||
const u8* JitIL::DoJit(u32 em_address, PPCAnalyst::CodeBuffer* code_buf, JitBlock* b, u32 nextPC)
|
|
||||||
{
|
|
||||||
js.isLastInstruction = false;
|
|
||||||
js.blockStart = em_address;
|
|
||||||
js.fifoBytesSinceCheck = 0;
|
|
||||||
js.curBlock = b;
|
|
||||||
js.numLoadStoreInst = 0;
|
|
||||||
js.numFloatingPointInst = 0;
|
|
||||||
|
|
||||||
PPCAnalyst::CodeOp* ops = code_buf->codebuffer;
|
|
||||||
|
|
||||||
const u8* start =
|
|
||||||
AlignCode4(); // TODO: Test if this or AlignCode16 make a difference from GetCodePtr
|
|
||||||
b->checkedEntry = start;
|
|
||||||
b->runCount = 0;
|
|
||||||
|
|
||||||
// Downcount flag check. The last block decremented downcounter, and the flag should still be
|
|
||||||
// available.
|
|
||||||
FixupBranch skip = J_CC(CC_NBE);
|
|
||||||
MOV(32, PPCSTATE(pc), Imm32(js.blockStart));
|
|
||||||
JMP(asm_routines.doTiming, true); // downcount hit zero - go doTiming.
|
|
||||||
SetJumpTarget(skip);
|
|
||||||
|
|
||||||
const u8* normalEntry = GetCodePtr();
|
|
||||||
b->normalEntry = normalEntry;
|
|
||||||
|
|
||||||
// Used to get a trace of the last few blocks before a crash, sometimes VERY useful.
|
|
||||||
if (ImHereDebug)
|
|
||||||
ABI_CallFunction(ImHere);
|
|
||||||
|
|
||||||
if (js.fpa.any)
|
|
||||||
{
|
|
||||||
// This block uses FPU - needs to add FP exception bailout
|
|
||||||
TEST(32, PPCSTATE(msr), Imm32(1 << 13)); // Test FP enabled bit
|
|
||||||
FixupBranch b1 = J_CC(CC_NZ);
|
|
||||||
|
|
||||||
// If a FPU exception occurs, the exception handler will read
|
|
||||||
// from PC. Update PC with the latest value in case that happens.
|
|
||||||
MOV(32, PPCSTATE(pc), Imm32(js.blockStart));
|
|
||||||
OR(32, PPCSTATE(Exceptions), Imm32(EXCEPTION_FPU_UNAVAILABLE));
|
|
||||||
WriteExceptionExit();
|
|
||||||
|
|
||||||
SetJumpTarget(b1);
|
|
||||||
}
|
|
||||||
|
|
||||||
js.rewriteStart = (u8*)GetCodePtr();
|
|
||||||
|
|
||||||
u64 codeHash = -1;
|
|
||||||
if (SConfig::GetInstance().bJITILTimeProfiling || SConfig::GetInstance().bJITILOutputIR)
|
|
||||||
{
|
|
||||||
// For profiling and IR Writer
|
|
||||||
for (u32 i = 0; i < code_block.m_num_instructions; i++)
|
|
||||||
{
|
|
||||||
const u64 inst = ops[i].inst.hex;
|
|
||||||
// Ported from boost::hash
|
|
||||||
codeHash ^= inst + (codeHash << 6) + (codeHash >> 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SConfig::GetInstance().bJITILTimeProfiling)
|
|
||||||
{
|
|
||||||
JitILProfiler::Block& block = JitILProfiler::Add(codeHash);
|
|
||||||
ABI_CallFunctionC(JitILProfiler::Begin, block.index);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start up IR builder (structure that collects the
|
|
||||||
// instruction processed by the JIT routines)
|
|
||||||
ibuild.Reset();
|
|
||||||
|
|
||||||
js.downcountAmount = 0;
|
|
||||||
|
|
||||||
// Translate instructions
|
|
||||||
for (u32 i = 0; i < code_block.m_num_instructions; i++)
|
|
||||||
{
|
|
||||||
js.compilerPC = ops[i].address;
|
|
||||||
js.op = &ops[i];
|
|
||||||
js.instructionNumber = i;
|
|
||||||
const GekkoOPInfo* opinfo = GetOpInfo(ops[i].inst);
|
|
||||||
js.downcountAmount += opinfo->numCycles;
|
|
||||||
|
|
||||||
if (!SConfig::GetInstance().bEnableDebugging)
|
|
||||||
js.downcountAmount += PatchEngine::GetSpeedhackCycles(js.compilerPC);
|
|
||||||
|
|
||||||
if (i == (code_block.m_num_instructions - 1))
|
|
||||||
js.isLastInstruction = true;
|
|
||||||
|
|
||||||
u32 function = HLE::GetFirstFunctionIndex(ops[i].address);
|
|
||||||
if (function != 0)
|
|
||||||
{
|
|
||||||
int type = HLE::GetFunctionTypeByIndex(function);
|
|
||||||
if (type == HLE::HLE_HOOK_START || type == HLE::HLE_HOOK_REPLACE)
|
|
||||||
{
|
|
||||||
int flags = HLE::GetFunctionFlagsByIndex(function);
|
|
||||||
if (HLE::IsEnabled(flags))
|
|
||||||
{
|
|
||||||
HLEFunction(function);
|
|
||||||
if (type == HLE::HLE_HOOK_REPLACE)
|
|
||||||
{
|
|
||||||
MOV(32, R(EAX), PPCSTATE(npc));
|
|
||||||
js.downcountAmount += js.st.numCycles;
|
|
||||||
WriteExitDestInOpArg(R(EAX));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ops[i].skip)
|
|
||||||
{
|
|
||||||
if (jo.memcheck && (opinfo->flags & FL_USE_FPU))
|
|
||||||
{
|
|
||||||
ibuild.EmitFPExceptionCheck(ibuild.EmitIntConst(ops[i].address));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (js.fifoWriteAddresses.find(js.compilerPC) != js.fifoWriteAddresses.end())
|
|
||||||
{
|
|
||||||
ibuild.EmitExtExceptionCheck(ibuild.EmitIntConst(ops[i].address));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SConfig::GetInstance().bEnableDebugging &&
|
|
||||||
breakpoints.IsAddressBreakPoint(ops[i].address) && !CPU::IsStepping())
|
|
||||||
{
|
|
||||||
// Turn off block linking if there are breakpoints so that the Step Over command does not
|
|
||||||
// link this block.
|
|
||||||
jo.enableBlocklink = false;
|
|
||||||
|
|
||||||
ibuild.EmitBreakPointCheck(ibuild.EmitIntConst(ops[i].address));
|
|
||||||
}
|
|
||||||
|
|
||||||
CompileInstruction(ops[i]);
|
|
||||||
|
|
||||||
if (jo.memcheck && (opinfo->flags & FL_LOADSTORE))
|
|
||||||
{
|
|
||||||
ibuild.EmitDSIExceptionCheck(ibuild.EmitIntConst(ops[i].address));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opinfo->flags & FL_LOADSTORE)
|
|
||||||
++js.numLoadStoreInst;
|
|
||||||
|
|
||||||
if (opinfo->flags & FL_USE_FPU)
|
|
||||||
++js.numFloatingPointInst;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Perform actual code generation
|
|
||||||
WriteCode(nextPC);
|
|
||||||
|
|
||||||
b->codeSize = (u32)(GetCodePtr() - start);
|
|
||||||
b->originalSize = code_block.m_num_instructions;
|
|
||||||
|
|
||||||
#ifdef JIT_LOG_X86
|
|
||||||
LogGeneratedX86(code_block.m_num_instructions, code_buf, normalEntry, b);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (SConfig::GetInstance().bJITILOutputIR)
|
|
||||||
{
|
|
||||||
ibuild.WriteToFile(codeHash);
|
|
||||||
}
|
|
||||||
|
|
||||||
return normalEntry;
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitIL::EnableBlockLink()
|
|
||||||
{
|
|
||||||
jo.enableBlocklink = true;
|
|
||||||
if (SConfig::GetInstance().bJITNoBlockLinking)
|
|
||||||
jo.enableBlocklink = false;
|
|
||||||
}
|
|
|
@ -1,83 +0,0 @@
|
||||||
// Copyright 2008 Dolphin Emulator Project
|
|
||||||
// Licensed under GPLv2+
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
// ========================
|
|
||||||
// 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.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Common/CommonTypes.h"
|
|
||||||
#include "Common/x64ABI.h"
|
|
||||||
#include "Common/x64Emitter.h"
|
|
||||||
#include "Core/PowerPC/Gekko.h"
|
|
||||||
#include "Core/PowerPC/Jit64/JitAsm.h"
|
|
||||||
#include "Core/PowerPC/JitCommon/JitCache.h"
|
|
||||||
#include "Core/PowerPC/JitILCommon/JitILBase.h"
|
|
||||||
#include "Core/PowerPC/PPCAnalyst.h"
|
|
||||||
|
|
||||||
class JitIL : public JitILBase
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Jit64AsmRoutineManager asm_routines;
|
|
||||||
|
|
||||||
JitIL() {}
|
|
||||||
~JitIL() {}
|
|
||||||
// Initialization, etc
|
|
||||||
|
|
||||||
void Init() override;
|
|
||||||
void Shutdown() override;
|
|
||||||
|
|
||||||
void EnableBlockLink();
|
|
||||||
|
|
||||||
// Jit!
|
|
||||||
|
|
||||||
void Jit(u32 em_address) override;
|
|
||||||
const u8* DoJit(u32 em_address, PPCAnalyst::CodeBuffer* code_buf, JitBlock* b, u32 nextPC);
|
|
||||||
|
|
||||||
void Trace();
|
|
||||||
|
|
||||||
JitBlockCache* GetBlockCache() override { return &blocks; }
|
|
||||||
void ClearCache() override;
|
|
||||||
|
|
||||||
const CommonAsmRoutines* GetAsmRoutines() override { return &asm_routines; }
|
|
||||||
const char* GetName() override { return "JIT64IL"; }
|
|
||||||
// Run!
|
|
||||||
void Run() override;
|
|
||||||
void SingleStep() override;
|
|
||||||
|
|
||||||
// Utilities for use by opcodes
|
|
||||||
|
|
||||||
void WriteExit(u32 destination);
|
|
||||||
void WriteExitDestInOpArg(const Gen::OpArg& arg);
|
|
||||||
void WriteExceptionExit();
|
|
||||||
void WriteRfiExitDestInOpArg(const Gen::OpArg& arg);
|
|
||||||
void Cleanup();
|
|
||||||
|
|
||||||
void WriteCode(u32 exitAddress);
|
|
||||||
|
|
||||||
// OPCODES
|
|
||||||
using Instruction = void (JitIL::*)(UGeckoInstruction instCode);
|
|
||||||
void FallBackToInterpreter(UGeckoInstruction _inst) override;
|
|
||||||
void DoNothing(UGeckoInstruction _inst) override;
|
|
||||||
void HLEFunction(UGeckoInstruction _inst) override;
|
|
||||||
|
|
||||||
void DynaRunTable4(UGeckoInstruction _inst) override;
|
|
||||||
void DynaRunTable19(UGeckoInstruction _inst) override;
|
|
||||||
void DynaRunTable31(UGeckoInstruction _inst) override;
|
|
||||||
void DynaRunTable59(UGeckoInstruction _inst) override;
|
|
||||||
void DynaRunTable63(UGeckoInstruction _inst) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
static void InitializeInstructionTables();
|
|
||||||
void CompileInstruction(PPCAnalyst::CodeOp& op);
|
|
||||||
};
|
|
|
@ -1,493 +0,0 @@
|
||||||
// Copyright 2010 Dolphin Emulator Project
|
|
||||||
// Licensed under GPLv2+
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#include "Core/PowerPC/Jit64IL/JitIL.h"
|
|
||||||
#include "Core/PowerPC/Gekko.h"
|
|
||||||
#include "Core/PowerPC/PPCTables.h"
|
|
||||||
|
|
||||||
static JitIL::Instruction dynaOpTable[64];
|
|
||||||
static JitIL::Instruction dynaOpTable4[1024];
|
|
||||||
static JitIL::Instruction dynaOpTable19[1024];
|
|
||||||
static JitIL::Instruction dynaOpTable31[1024];
|
|
||||||
static JitIL::Instruction dynaOpTable59[32];
|
|
||||||
static JitIL::Instruction dynaOpTable63[1024];
|
|
||||||
|
|
||||||
void JitIL::DynaRunTable4(UGeckoInstruction _inst)
|
|
||||||
{
|
|
||||||
(this->*dynaOpTable4[_inst.SUBOP10])(_inst);
|
|
||||||
}
|
|
||||||
void JitIL::DynaRunTable19(UGeckoInstruction _inst)
|
|
||||||
{
|
|
||||||
(this->*dynaOpTable19[_inst.SUBOP10])(_inst);
|
|
||||||
}
|
|
||||||
void JitIL::DynaRunTable31(UGeckoInstruction _inst)
|
|
||||||
{
|
|
||||||
(this->*dynaOpTable31[_inst.SUBOP10])(_inst);
|
|
||||||
}
|
|
||||||
void JitIL::DynaRunTable59(UGeckoInstruction _inst)
|
|
||||||
{
|
|
||||||
(this->*dynaOpTable59[_inst.SUBOP5])(_inst);
|
|
||||||
}
|
|
||||||
void JitIL::DynaRunTable63(UGeckoInstruction _inst)
|
|
||||||
{
|
|
||||||
(this->*dynaOpTable63[_inst.SUBOP10])(_inst);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct GekkoOPTemplate
|
|
||||||
{
|
|
||||||
int opcode;
|
|
||||||
JitIL::Instruction Inst;
|
|
||||||
};
|
|
||||||
|
|
||||||
const GekkoOPTemplate primarytable[] = {
|
|
||||||
{4, &JitIL::DynaRunTable4}, //"RunTable4", OPTYPE_SUBTABLE | (4<<24), 0}},
|
|
||||||
{19, &JitIL::DynaRunTable19}, //"RunTable19", OPTYPE_SUBTABLE | (19<<24), 0}},
|
|
||||||
{31, &JitIL::DynaRunTable31}, //"RunTable31", OPTYPE_SUBTABLE | (31<<24), 0}},
|
|
||||||
{59, &JitIL::DynaRunTable59}, //"RunTable59", OPTYPE_SUBTABLE | (59<<24), 0}},
|
|
||||||
{63, &JitIL::DynaRunTable63}, //"RunTable63", OPTYPE_SUBTABLE | (63<<24), 0}},
|
|
||||||
|
|
||||||
{16, &JitIL::bcx}, //"bcx", OPTYPE_SYSTEM, FL_ENDBLOCK}},
|
|
||||||
{18, &JitIL::bx}, //"bx", OPTYPE_SYSTEM, FL_ENDBLOCK}},
|
|
||||||
|
|
||||||
{3, &JitIL::FallBackToInterpreter}, //"twi", OPTYPE_SYSTEM, 0}},
|
|
||||||
{17, &JitIL::sc}, //"sc", OPTYPE_SYSTEM, FL_ENDBLOCK, 1}},
|
|
||||||
|
|
||||||
{7, &JitIL::mulli}, //"mulli", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_RC_BIT, 2}},
|
|
||||||
{8, &JitIL::subfic}, //"subfic", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_SET_CA}},
|
|
||||||
{10, &JitIL::cmpXX}, //"cmpli", OPTYPE_INTEGER, FL_IN_A | FL_SET_CRn}},
|
|
||||||
{11, &JitIL::cmpXX}, //"cmpi", OPTYPE_INTEGER, FL_IN_A | FL_SET_CRn}},
|
|
||||||
{12, &JitIL::reg_imm}, //"addic", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_SET_CA}},
|
|
||||||
{13, &JitIL::reg_imm}, //"addic_rc", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_SET_CR0}},
|
|
||||||
{14, &JitIL::reg_imm}, //"addi", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A0}},
|
|
||||||
{15, &JitIL::reg_imm}, //"addis", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A0}},
|
|
||||||
|
|
||||||
{20,
|
|
||||||
&JitIL::rlwimix}, //"rlwimix", OPTYPE_INTEGER, FL_OUT_A | FL_IN_A | FL_IN_S | FL_RC_BIT}},
|
|
||||||
{21, &JitIL::rlwinmx}, //"rlwinmx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_RC_BIT}},
|
|
||||||
{23, &JitIL::rlwnmx}, //"rlwnmx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_IN_B | FL_RC_BIT}},
|
|
||||||
|
|
||||||
{24, &JitIL::reg_imm}, //"ori", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S}},
|
|
||||||
{25, &JitIL::reg_imm}, //"oris", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S}},
|
|
||||||
{26, &JitIL::reg_imm}, //"xori", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S}},
|
|
||||||
{27, &JitIL::reg_imm}, //"xoris", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S}},
|
|
||||||
{28, &JitIL::reg_imm}, //"andi_rc", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_SET_CR0}},
|
|
||||||
{29, &JitIL::reg_imm}, //"andis_rc", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_SET_CR0}},
|
|
||||||
|
|
||||||
{32, &JitIL::lXz}, //"lwz", OPTYPE_LOAD, FL_OUT_D | FL_IN_A}},
|
|
||||||
{33, &JitIL::lXz}, //"lwzu", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A}},
|
|
||||||
{34, &JitIL::lXz}, //"lbz", OPTYPE_LOAD, FL_OUT_D | FL_IN_A}},
|
|
||||||
{35, &JitIL::lbzu}, //"lbzu", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A}},
|
|
||||||
{40, &JitIL::lXz}, //"lhz", OPTYPE_LOAD, FL_OUT_D | FL_IN_A}},
|
|
||||||
{41, &JitIL::lXz}, //"lhzu", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A}},
|
|
||||||
{42, &JitIL::lha}, //"lha", OPTYPE_LOAD, FL_OUT_D | FL_IN_A}},
|
|
||||||
{43, &JitIL::lhau}, //"lhau", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A}},
|
|
||||||
|
|
||||||
{44, &JitIL::stX}, //"sth", OPTYPE_STORE, FL_IN_A | FL_IN_S}},
|
|
||||||
{45, &JitIL::stX}, //"sthu", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_S}},
|
|
||||||
{36, &JitIL::stX}, //"stw", OPTYPE_STORE, FL_IN_A | FL_IN_S}},
|
|
||||||
{37, &JitIL::stX}, //"stwu", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_S}},
|
|
||||||
{38, &JitIL::stX}, //"stb", OPTYPE_STORE, FL_IN_A | FL_IN_S}},
|
|
||||||
{39, &JitIL::stX}, //"stbu", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_S}},
|
|
||||||
|
|
||||||
{46, &JitIL::lmw}, //"lmw", OPTYPE_SYSTEM, FL_EVIL, 10}},
|
|
||||||
{47, &JitIL::stmw}, //"stmw", OPTYPE_SYSTEM, FL_EVIL, 10}},
|
|
||||||
|
|
||||||
{48, &JitIL::lfs}, //"lfs", OPTYPE_LOADFP, FL_IN_A}},
|
|
||||||
{49, &JitIL::lfsu}, //"lfsu", OPTYPE_LOADFP, FL_OUT_A | FL_IN_A}},
|
|
||||||
{50, &JitIL::lfd}, //"lfd", OPTYPE_LOADFP, FL_IN_A}},
|
|
||||||
{51, &JitIL::lfdu}, //"lfdu", OPTYPE_LOADFP, FL_OUT_A | FL_IN_A}},
|
|
||||||
|
|
||||||
{52, &JitIL::stfs}, //"stfs", OPTYPE_STOREFP, FL_IN_A}},
|
|
||||||
{53, &JitIL::stfs}, //"stfsu", OPTYPE_STOREFP, FL_OUT_A | FL_IN_A}},
|
|
||||||
{54, &JitIL::stfd}, //"stfd", OPTYPE_STOREFP, FL_IN_A}},
|
|
||||||
{55, &JitIL::stfd}, //"stfdu", OPTYPE_STOREFP, FL_OUT_A | FL_IN_A}},
|
|
||||||
|
|
||||||
{56, &JitIL::psq_l}, //"psq_l", OPTYPE_PS, FL_IN_A}},
|
|
||||||
{57, &JitIL::psq_l}, //"psq_lu", OPTYPE_PS, FL_OUT_A | FL_IN_A}},
|
|
||||||
{60, &JitIL::psq_st}, //"psq_st", OPTYPE_PS, FL_IN_A}},
|
|
||||||
{61, &JitIL::psq_st}, //"psq_stu", OPTYPE_PS, FL_OUT_A | FL_IN_A}},
|
|
||||||
|
|
||||||
// missing: 0, 1, 2, 5, 6, 9, 22, 30, 62, 58
|
|
||||||
};
|
|
||||||
|
|
||||||
const GekkoOPTemplate table4[] = {
|
|
||||||
// SUBOP10
|
|
||||||
{0, &JitIL::FallBackToInterpreter}, //"ps_cmpu0", OPTYPE_PS, FL_SET_CRn}},
|
|
||||||
{32, &JitIL::FallBackToInterpreter}, //"ps_cmpo0", OPTYPE_PS, FL_SET_CRn}},
|
|
||||||
{40, &JitIL::FallBackToInterpreter}, //"ps_neg", OPTYPE_PS, FL_RC_BIT}},
|
|
||||||
{136, &JitIL::FallBackToInterpreter}, //"ps_nabs", OPTYPE_PS, FL_RC_BIT}},
|
|
||||||
{264, &JitIL::FallBackToInterpreter}, //"ps_abs", OPTYPE_PS, FL_RC_BIT}},
|
|
||||||
{64, &JitIL::FallBackToInterpreter}, //"ps_cmpu1", OPTYPE_PS, FL_RC_BIT}},
|
|
||||||
{72, &JitIL::FallBackToInterpreter}, //"ps_mr", OPTYPE_PS, FL_RC_BIT}},
|
|
||||||
{96, &JitIL::FallBackToInterpreter}, //"ps_cmpo1", OPTYPE_PS, FL_RC_BIT}},
|
|
||||||
{528, &JitIL::ps_mergeXX}, //"ps_merge00", OPTYPE_PS, FL_RC_BIT}},
|
|
||||||
{560, &JitIL::ps_mergeXX}, //"ps_merge01", OPTYPE_PS, FL_RC_BIT}},
|
|
||||||
{592, &JitIL::ps_mergeXX}, //"ps_merge10", OPTYPE_PS, FL_RC_BIT}},
|
|
||||||
{624, &JitIL::ps_mergeXX}, //"ps_merge11", OPTYPE_PS, FL_RC_BIT}},
|
|
||||||
|
|
||||||
{1014, &JitIL::FallBackToInterpreter}, //"dcbz_l", OPTYPE_SYSTEM, 0}},
|
|
||||||
};
|
|
||||||
|
|
||||||
const GekkoOPTemplate table4_2[] = {
|
|
||||||
{10, &JitIL::ps_sum}, //"ps_sum0", OPTYPE_PS, 0}},
|
|
||||||
{11, &JitIL::ps_sum}, //"ps_sum1", OPTYPE_PS, 0}},
|
|
||||||
{12, &JitIL::ps_muls}, //"ps_muls0", OPTYPE_PS, 0}},
|
|
||||||
{13, &JitIL::ps_muls}, //"ps_muls1", OPTYPE_PS, 0}},
|
|
||||||
{14, &JitIL::ps_maddXX}, //"ps_madds0", OPTYPE_PS, 0}},
|
|
||||||
{15, &JitIL::ps_maddXX}, //"ps_madds1", OPTYPE_PS, 0}},
|
|
||||||
{18, &JitIL::ps_arith}, //"ps_div", OPTYPE_PS, 0, 16}},
|
|
||||||
{20, &JitIL::ps_arith}, //"ps_sub", OPTYPE_PS, 0}},
|
|
||||||
{21, &JitIL::ps_arith}, //"ps_add", OPTYPE_PS, 0}},
|
|
||||||
{23, &JitIL::FallBackToInterpreter}, //"ps_sel", OPTYPE_PS, 0}},
|
|
||||||
{24, &JitIL::FallBackToInterpreter}, //"ps_res", OPTYPE_PS, 0}},
|
|
||||||
{25, &JitIL::ps_arith}, //"ps_mul", OPTYPE_PS, 0}},
|
|
||||||
{26, &JitIL::FallBackToInterpreter}, //"ps_rsqrte", OPTYPE_PS, 0, 1}},
|
|
||||||
{28, &JitIL::ps_maddXX}, //"ps_msub", OPTYPE_PS, 0}},
|
|
||||||
{29, &JitIL::ps_maddXX}, //"ps_madd", OPTYPE_PS, 0}},
|
|
||||||
{30, &JitIL::ps_maddXX}, //"ps_nmsub", OPTYPE_PS, 0}},
|
|
||||||
{31, &JitIL::ps_maddXX}, //"ps_nmadd", OPTYPE_PS, 0}},
|
|
||||||
};
|
|
||||||
|
|
||||||
const GekkoOPTemplate table4_3[] = {
|
|
||||||
{6, &JitIL::FallBackToInterpreter}, //"psq_lx", OPTYPE_PS, 0}},
|
|
||||||
{7, &JitIL::FallBackToInterpreter}, //"psq_stx", OPTYPE_PS, 0}},
|
|
||||||
{38, &JitIL::FallBackToInterpreter}, //"psq_lux", OPTYPE_PS, 0}},
|
|
||||||
{39, &JitIL::FallBackToInterpreter}, //"psq_stux", OPTYPE_PS, 0}},
|
|
||||||
};
|
|
||||||
|
|
||||||
const GekkoOPTemplate table19[] = {
|
|
||||||
{528, &JitIL::bcctrx}, //"bcctrx", OPTYPE_BRANCH, FL_ENDBLOCK}},
|
|
||||||
{16, &JitIL::bclrx}, //"bclrx", OPTYPE_BRANCH, FL_ENDBLOCK}},
|
|
||||||
{257, &JitIL::crXX}, //"crand", OPTYPE_CR, FL_EVIL}},
|
|
||||||
{129, &JitIL::crXX}, //"crandc", OPTYPE_CR, FL_EVIL}},
|
|
||||||
{289, &JitIL::crXX}, //"creqv", OPTYPE_CR, FL_EVIL}},
|
|
||||||
{225, &JitIL::crXX}, //"crnand", OPTYPE_CR, FL_EVIL}},
|
|
||||||
{33, &JitIL::crXX}, //"crnor", OPTYPE_CR, FL_EVIL}},
|
|
||||||
{449, &JitIL::crXX}, //"cror", OPTYPE_CR, FL_EVIL}},
|
|
||||||
{417, &JitIL::crXX}, //"crorc", OPTYPE_CR, FL_EVIL}},
|
|
||||||
{193, &JitIL::crXX}, //"crxor", OPTYPE_CR, FL_EVIL}},
|
|
||||||
|
|
||||||
{150, &JitIL::DoNothing}, //"isync", OPTYPE_ICACHE, FL_EVIL}},
|
|
||||||
{0, &JitIL::mcrf}, //"mcrf", OPTYPE_SYSTEM, FL_EVIL}},
|
|
||||||
|
|
||||||
{50, &JitIL::rfi}, //"rfi", OPTYPE_SYSTEM, FL_ENDBLOCK | FL_CHECKEXCEPTIONS, 1}},
|
|
||||||
};
|
|
||||||
|
|
||||||
const GekkoOPTemplate table31[] = {
|
|
||||||
{266, &JitIL::addx}, //"addx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT}},
|
|
||||||
{778, &JitIL::addx}, //"addox", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT}},
|
|
||||||
{10, &JitIL::FallBackToInterpreter}, //"addcx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB |
|
|
||||||
// FL_SET_CA | FL_RC_BIT}},
|
|
||||||
{522, &JitIL::FallBackToInterpreter}, //"addcox", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB |
|
|
||||||
// FL_SET_CA | FL_RC_BIT}},
|
|
||||||
{138, &JitIL::addex}, //"addex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA
|
|
||||||
//| FL_RC_BIT}},
|
|
||||||
{650, &JitIL::addex}, //"addeox", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA
|
|
||||||
//| FL_RC_BIT}},
|
|
||||||
{234, &JitIL::FallBackToInterpreter}, //"addmex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB |
|
|
||||||
// FL_READ_CA | FL_SET_CA | FL_RC_BIT}},
|
|
||||||
{746, &JitIL::FallBackToInterpreter}, //"addmeox"
|
|
||||||
{202, &JitIL::addzex}, //"addzex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA |
|
|
||||||
// FL_SET_CA | FL_RC_BIT}},
|
|
||||||
{714, &JitIL::addzex}, //"addzeox"
|
|
||||||
{491, &JitIL::FallBackToInterpreter}, //"divwx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB |
|
|
||||||
// FL_RC_BIT, 39}},
|
|
||||||
{1003, &JitIL::FallBackToInterpreter}, //"divwox", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB |
|
|
||||||
// FL_RC_BIT, 39}},
|
|
||||||
{459, &JitIL::divwux}, //"divwux", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 39}},
|
|
||||||
{971, &JitIL::divwux}, //"divwuox", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 39}},
|
|
||||||
{75, &JitIL::FallBackToInterpreter}, //"mulhwx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB |
|
|
||||||
// FL_RC_BIT, 4}},
|
|
||||||
{11, &JitIL::mulhwux}, //"mulhwux", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 4}},
|
|
||||||
{235, &JitIL::mullwx}, //"mullwx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 4}},
|
|
||||||
{747, &JitIL::mullwx}, //"mullwox", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 4}},
|
|
||||||
{104, &JitIL::negx}, //"negx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT}},
|
|
||||||
{616, &JitIL::negx}, //"negox"
|
|
||||||
{40, &JitIL::subfx}, //"subfx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT}},
|
|
||||||
{552, &JitIL::subfx}, //"subfox", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT}},
|
|
||||||
{8,
|
|
||||||
&JitIL::subfcx}, //"subfcx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_SET_CA | FL_RC_BIT}},
|
|
||||||
{520,
|
|
||||||
&JitIL::subfcx}, //"subfcox", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_SET_CA | FL_RC_BIT}},
|
|
||||||
{136, &JitIL::subfex}, //"subfex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA |
|
|
||||||
// FL_SET_CA | FL_RC_BIT}},
|
|
||||||
{648, &JitIL::subfex}, //"subfeox"
|
|
||||||
{232, &JitIL::FallBackToInterpreter}, //"subfmex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB |
|
|
||||||
// FL_READ_CA | FL_SET_CA | FL_RC_BIT}},
|
|
||||||
{744, &JitIL::FallBackToInterpreter}, //"subfmeox"
|
|
||||||
{200, &JitIL::FallBackToInterpreter}, //"subfzex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB |
|
|
||||||
// FL_READ_CA | FL_SET_CA | FL_RC_BIT}},
|
|
||||||
{712, &JitIL::FallBackToInterpreter}, //"subfzeox"
|
|
||||||
|
|
||||||
{28, &JitIL::boolX}, //"andx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}},
|
|
||||||
{60, &JitIL::boolX}, //"andcx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}},
|
|
||||||
{444, &JitIL::boolX}, //"orx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}},
|
|
||||||
{124, &JitIL::boolX}, //"norx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}},
|
|
||||||
{316, &JitIL::boolX}, //"xorx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}},
|
|
||||||
{412, &JitIL::boolX}, //"orcx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}},
|
|
||||||
{476, &JitIL::boolX}, //"nandx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}},
|
|
||||||
{284, &JitIL::boolX}, //"eqvx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}},
|
|
||||||
{0, &JitIL::cmpXX}, //"cmp", OPTYPE_INTEGER, FL_IN_AB | FL_SET_CRn}},
|
|
||||||
{32, &JitIL::cmpXX}, //"cmpl", OPTYPE_INTEGER, FL_IN_AB | FL_SET_CRn}},
|
|
||||||
{26, &JitIL::cntlzwx}, //"cntlzwx",OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_RC_BIT}},
|
|
||||||
{922, &JitIL::extshx}, //"extshx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_RC_BIT}},
|
|
||||||
{954, &JitIL::extsbx}, //"extsbx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_RC_BIT}},
|
|
||||||
{536, &JitIL::srwx}, //"srwx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_B | FL_IN_S | FL_RC_BIT}},
|
|
||||||
{792, &JitIL::srawx}, //"srawx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_B | FL_IN_S | FL_RC_BIT}},
|
|
||||||
{824, &JitIL::srawix}, //"srawix", OPTYPE_INTEGER, FL_OUT_A | FL_IN_B | FL_IN_S | FL_RC_BIT}},
|
|
||||||
{24, &JitIL::slwx}, //"slwx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_B | FL_IN_S | FL_RC_BIT}},
|
|
||||||
|
|
||||||
{54, &JitIL::dcbst}, //"dcbst", OPTYPE_DCACHE, 0, 4}},
|
|
||||||
{86, &JitIL::FallBackToInterpreter}, //"dcbf", OPTYPE_DCACHE, 0, 4}},
|
|
||||||
{246, &JitIL::DoNothing}, //"dcbtst", OPTYPE_DCACHE, 0, 1}},
|
|
||||||
{278, &JitIL::DoNothing}, //"dcbt", OPTYPE_DCACHE, 0, 1}},
|
|
||||||
{470, &JitIL::FallBackToInterpreter}, //"dcbi", OPTYPE_DCACHE, 0, 4}},
|
|
||||||
{758, &JitIL::DoNothing}, //"dcba", OPTYPE_DCACHE, 0, 4}},
|
|
||||||
{1014, &JitIL::dcbz}, //"dcbz", OPTYPE_DCACHE, 0, 4}},
|
|
||||||
|
|
||||||
// load word
|
|
||||||
{23, &JitIL::lXzx}, //"lwzx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}},
|
|
||||||
{55, &JitIL::lXzx}, //"lwzux", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A | FL_IN_B}},
|
|
||||||
|
|
||||||
// load halfword
|
|
||||||
{279, &JitIL::lXzx}, //"lhzx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}},
|
|
||||||
{311, &JitIL::lXzx}, //"lhzux", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A | FL_IN_B}},
|
|
||||||
|
|
||||||
// load halfword signextend
|
|
||||||
{343, &JitIL::lhax}, //"lhax", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}},
|
|
||||||
{375, &JitIL::lhaux}, //"lhaux", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A | FL_IN_B}},
|
|
||||||
|
|
||||||
// load byte
|
|
||||||
{87, &JitIL::lXzx}, //"lbzx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}},
|
|
||||||
{119, &JitIL::lXzx}, //"lbzux", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A | FL_IN_B}},
|
|
||||||
|
|
||||||
// load byte reverse
|
|
||||||
{534, &JitIL::FallBackToInterpreter}, //"lwbrx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}},
|
|
||||||
{790, &JitIL::FallBackToInterpreter}, //"lhbrx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}},
|
|
||||||
|
|
||||||
// Conditional load/store (Wii SMP)
|
|
||||||
{150, &JitIL::FallBackToInterpreter}, //"stwcxd", OPTYPE_STORE, FL_EVIL | FL_SET_CR0}},
|
|
||||||
{20, &JitIL::FallBackToInterpreter}, //"lwarx", OPTYPE_LOAD, FL_EVIL | FL_OUT_D | FL_IN_A0B |
|
|
||||||
// FL_SET_CR0}},
|
|
||||||
|
|
||||||
// load string (interpret these)
|
|
||||||
{533, &JitIL::FallBackToInterpreter}, //"lswx", OPTYPE_LOAD, FL_EVIL | FL_IN_A | FL_OUT_D}},
|
|
||||||
{597, &JitIL::FallBackToInterpreter}, //"lswi", OPTYPE_LOAD, FL_EVIL | FL_IN_AB | FL_OUT_D}},
|
|
||||||
|
|
||||||
// store word
|
|
||||||
{151, &JitIL::stXx}, //"stwx", OPTYPE_STORE, FL_IN_A0 | FL_IN_B}},
|
|
||||||
{183, &JitIL::stXx}, //"stwux", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_B}},
|
|
||||||
|
|
||||||
// store halfword
|
|
||||||
{407, &JitIL::stXx}, //"sthx", OPTYPE_STORE, FL_IN_A0 | FL_IN_B}},
|
|
||||||
{439, &JitIL::stXx}, //"sthux", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_B}},
|
|
||||||
|
|
||||||
// store byte
|
|
||||||
{215, &JitIL::stXx}, //"stbx", OPTYPE_STORE, FL_IN_A0 | FL_IN_B}},
|
|
||||||
{247, &JitIL::stXx}, //"stbux", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_B}},
|
|
||||||
|
|
||||||
// store bytereverse
|
|
||||||
{662, &JitIL::FallBackToInterpreter}, //"stwbrx", OPTYPE_STORE, FL_IN_A0 | FL_IN_B}},
|
|
||||||
{918, &JitIL::FallBackToInterpreter}, //"sthbrx", OPTYPE_STORE, FL_IN_A | FL_IN_B}},
|
|
||||||
|
|
||||||
{661, &JitIL::FallBackToInterpreter}, //"stswx", OPTYPE_STORE, FL_EVIL}},
|
|
||||||
{725, &JitIL::FallBackToInterpreter}, //"stswi", OPTYPE_STORE, FL_EVIL}},
|
|
||||||
|
|
||||||
// fp load/store
|
|
||||||
{535, &JitIL::lfsx}, //"lfsx", OPTYPE_LOADFP, FL_IN_A0 | FL_IN_B}},
|
|
||||||
{567, &JitIL::FallBackToInterpreter}, //"lfsux", OPTYPE_LOADFP, FL_IN_A | FL_IN_B}},
|
|
||||||
{599, &JitIL::FallBackToInterpreter}, //"lfdx", OPTYPE_LOADFP, FL_IN_A0 | FL_IN_B}},
|
|
||||||
{631, &JitIL::FallBackToInterpreter}, //"lfdux", OPTYPE_LOADFP, FL_IN_A | FL_IN_B}},
|
|
||||||
|
|
||||||
{663, &JitIL::stfsx}, //"stfsx", OPTYPE_STOREFP, FL_IN_A0 | FL_IN_B}},
|
|
||||||
{695, &JitIL::FallBackToInterpreter}, //"stfsux", OPTYPE_STOREFP, FL_IN_A | FL_IN_B}},
|
|
||||||
{727, &JitIL::FallBackToInterpreter}, //"stfdx", OPTYPE_STOREFP, FL_IN_A0 | FL_IN_B}},
|
|
||||||
{759, &JitIL::FallBackToInterpreter}, //"stfdux", OPTYPE_STOREFP, FL_IN_A | FL_IN_B}},
|
|
||||||
{983, &JitIL::FallBackToInterpreter}, //"stfiwx", OPTYPE_STOREFP, FL_IN_A0 | FL_IN_B}},
|
|
||||||
|
|
||||||
{19, &JitIL::mfcr}, //"mfcr", OPTYPE_SYSTEM, FL_OUT_D}},
|
|
||||||
{83, &JitIL::mfmsr}, //"mfmsr", OPTYPE_SYSTEM, FL_OUT_D}},
|
|
||||||
{144, &JitIL::mtcrf}, //"mtcrf", OPTYPE_SYSTEM, 0}},
|
|
||||||
{146, &JitIL::mtmsr}, //"mtmsr", OPTYPE_SYSTEM, FL_ENDBLOCK}},
|
|
||||||
{210, &JitIL::FallBackToInterpreter}, //"mtsr", OPTYPE_SYSTEM, 0}},
|
|
||||||
{242, &JitIL::FallBackToInterpreter}, //"mtsrin", OPTYPE_SYSTEM, 0}},
|
|
||||||
{339, &JitIL::mfspr}, //"mfspr", OPTYPE_SPR, FL_OUT_D}},
|
|
||||||
{467, &JitIL::mtspr}, //"mtspr", OPTYPE_SPR, 0, 2}},
|
|
||||||
{371, &JitIL::mftb}, //"mftb", OPTYPE_SYSTEM, FL_OUT_D | FL_TIMER}},
|
|
||||||
{512, &JitIL::FallBackToInterpreter}, //"mcrxr", OPTYPE_SYSTEM, 0}},
|
|
||||||
{595, &JitIL::FallBackToInterpreter}, //"mfsr", OPTYPE_SYSTEM, FL_OUT_D, 2}},
|
|
||||||
{659, &JitIL::FallBackToInterpreter}, //"mfsrin", OPTYPE_SYSTEM, FL_OUT_D, 2}},
|
|
||||||
|
|
||||||
{4, &JitIL::FallBackToInterpreter}, //"tw", OPTYPE_SYSTEM, 0, 1}},
|
|
||||||
{598, &JitIL::DoNothing}, //"sync", OPTYPE_SYSTEM, 0, 2}},
|
|
||||||
{982, &JitIL::icbi}, //"icbi", OPTYPE_SYSTEM, FL_ENDBLOCK, 3}},
|
|
||||||
|
|
||||||
// Unused instructions on GC
|
|
||||||
{310, &JitIL::FallBackToInterpreter}, //"eciwx", OPTYPE_INTEGER, FL_RC_BIT}},
|
|
||||||
{438, &JitIL::FallBackToInterpreter}, //"ecowx", OPTYPE_INTEGER, FL_RC_BIT}},
|
|
||||||
{854, &JitIL::DoNothing}, //"eieio", OPTYPE_INTEGER, FL_RC_BIT}},
|
|
||||||
{306, &JitIL::FallBackToInterpreter}, //"tlbie", OPTYPE_SYSTEM, 0}},
|
|
||||||
{566, &JitIL::DoNothing}, //"tlbsync", OPTYPE_SYSTEM, 0}},
|
|
||||||
};
|
|
||||||
|
|
||||||
const GekkoOPTemplate table59[] = {
|
|
||||||
{18, &JitIL::FallBackToInterpreter}, //{"fdivsx", OPTYPE_FPU, FL_RC_BIT_F, 16}},
|
|
||||||
{20, &JitIL::fp_arith_s}, //"fsubsx", OPTYPE_FPU, FL_RC_BIT_F}},
|
|
||||||
{21, &JitIL::fp_arith_s}, //"faddsx", OPTYPE_FPU, FL_RC_BIT_F}},
|
|
||||||
{24, &JitIL::FallBackToInterpreter}, //"fresx", OPTYPE_FPU, FL_RC_BIT_F}},
|
|
||||||
{25, &JitIL::fp_arith_s}, //"fmulsx", OPTYPE_FPU, FL_RC_BIT_F}},
|
|
||||||
{28, &JitIL::fmaddXX}, //"fmsubsx", OPTYPE_FPU, FL_RC_BIT_F}},
|
|
||||||
{29, &JitIL::fmaddXX}, //"fmaddsx", OPTYPE_FPU, FL_RC_BIT_F}},
|
|
||||||
{30, &JitIL::fmaddXX}, //"fnmsubsx", OPTYPE_FPU, FL_RC_BIT_F}},
|
|
||||||
{31, &JitIL::fmaddXX}, //"fnmaddsx", OPTYPE_FPU, FL_RC_BIT_F}},
|
|
||||||
};
|
|
||||||
|
|
||||||
const GekkoOPTemplate table63[] = {
|
|
||||||
{264, &JitIL::fsign}, //"fabsx", OPTYPE_FPU, FL_RC_BIT_F}},
|
|
||||||
{32, &JitIL::fcmpX}, //"fcmpo", OPTYPE_FPU, FL_RC_BIT_F}},
|
|
||||||
{0, &JitIL::fcmpX}, //"fcmpu", OPTYPE_FPU, FL_RC_BIT_F}},
|
|
||||||
{14, &JitIL::FallBackToInterpreter}, //"fctiwx", OPTYPE_FPU, FL_RC_BIT_F}},
|
|
||||||
{15, &JitIL::FallBackToInterpreter}, //"fctiwzx", OPTYPE_FPU, FL_RC_BIT_F}},
|
|
||||||
{72, &JitIL::fmrx}, //"fmrx", OPTYPE_FPU, FL_RC_BIT_F}},
|
|
||||||
{136, &JitIL::fsign}, //"fnabsx", OPTYPE_FPU, FL_RC_BIT_F}},
|
|
||||||
{40, &JitIL::fsign}, //"fnegx", OPTYPE_FPU, FL_RC_BIT_F}},
|
|
||||||
{12, &JitIL::FallBackToInterpreter}, //"frspx", OPTYPE_FPU, FL_RC_BIT_F}},
|
|
||||||
|
|
||||||
{64, &JitIL::FallBackToInterpreter}, //"mcrfs", OPTYPE_SYSTEMFP, 0}},
|
|
||||||
{583, &JitIL::FallBackToInterpreter}, //"mffsx", OPTYPE_SYSTEMFP, 0}},
|
|
||||||
{70, &JitIL::FallBackToInterpreter}, //"mtfsb0x", OPTYPE_SYSTEMFP, 0, 2}},
|
|
||||||
{38, &JitIL::FallBackToInterpreter}, //"mtfsb1x", OPTYPE_SYSTEMFP, 0, 2}},
|
|
||||||
{134, &JitIL::FallBackToInterpreter}, //"mtfsfix", OPTYPE_SYSTEMFP, 0, 2}},
|
|
||||||
{711, &JitIL::FallBackToInterpreter}, //"mtfsfx", OPTYPE_SYSTEMFP, 0, 2}},
|
|
||||||
};
|
|
||||||
|
|
||||||
const GekkoOPTemplate table63_2[] = {
|
|
||||||
{18, &JitIL::FallBackToInterpreter}, //"fdivx", OPTYPE_FPU, FL_RC_BIT_F, 30}},
|
|
||||||
{20, &JitIL::FallBackToInterpreter}, //"fsubx", OPTYPE_FPU, FL_RC_BIT_F}},
|
|
||||||
{21, &JitIL::FallBackToInterpreter}, //"faddx", OPTYPE_FPU, FL_RC_BIT_F}},
|
|
||||||
{23, &JitIL::FallBackToInterpreter}, //"fselx", OPTYPE_FPU, FL_RC_BIT_F}},
|
|
||||||
{25, &JitIL::fp_arith_s}, //"fmulx", OPTYPE_FPU, FL_RC_BIT_F}},
|
|
||||||
{26, &JitIL::FallBackToInterpreter}, //"frsqrtex", OPTYPE_FPU, FL_RC_BIT_F}},
|
|
||||||
{28, &JitIL::fmaddXX}, //"fmsubx", OPTYPE_FPU, FL_RC_BIT_F}},
|
|
||||||
{29, &JitIL::fmaddXX}, //"fmaddx", OPTYPE_FPU, FL_RC_BIT_F}},
|
|
||||||
{30, &JitIL::fmaddXX}, //"fnmsubx", OPTYPE_FPU, FL_RC_BIT_F}},
|
|
||||||
{31, &JitIL::fmaddXX}, //"fnmaddx", OPTYPE_FPU, FL_RC_BIT_F}},
|
|
||||||
};
|
|
||||||
|
|
||||||
void JitIL::CompileInstruction(PPCAnalyst::CodeOp& op)
|
|
||||||
{
|
|
||||||
(this->*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(js.compilerPC);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
info->compileCount++;
|
|
||||||
info->lastUse = js.compilerPC;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
PanicAlert("Tried to compile illegal (or unknown) instruction %08x, at %08x", op.inst.hex,
|
|
||||||
js.compilerPC);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitIL::InitializeInstructionTables()
|
|
||||||
{
|
|
||||||
// once initialized, tables are read-only
|
|
||||||
static bool initialized = false;
|
|
||||||
if (initialized)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// clear
|
|
||||||
for (auto& tpl : dynaOpTable)
|
|
||||||
{
|
|
||||||
tpl = &JitIL::FallBackToInterpreter;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto& tpl : dynaOpTable59)
|
|
||||||
{
|
|
||||||
tpl = &JitIL::FallBackToInterpreter;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < 1024; i++)
|
|
||||||
{
|
|
||||||
dynaOpTable4[i] = &JitIL::FallBackToInterpreter;
|
|
||||||
dynaOpTable19[i] = &JitIL::FallBackToInterpreter;
|
|
||||||
dynaOpTable31[i] = &JitIL::FallBackToInterpreter;
|
|
||||||
dynaOpTable63[i] = &JitIL::FallBackToInterpreter;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& tpl : primarytable)
|
|
||||||
{
|
|
||||||
dynaOpTable[tpl.opcode] = tpl.Inst;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < 32; i++)
|
|
||||||
{
|
|
||||||
int fill = i << 5;
|
|
||||||
for (const auto& tpl : table4_2)
|
|
||||||
{
|
|
||||||
int op = fill + tpl.opcode;
|
|
||||||
dynaOpTable4[op] = tpl.Inst;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < 16; i++)
|
|
||||||
{
|
|
||||||
int fill = i << 6;
|
|
||||||
for (const auto& tpl : table4_3)
|
|
||||||
{
|
|
||||||
int op = fill + tpl.opcode;
|
|
||||||
dynaOpTable4[op] = tpl.Inst;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& tpl : table4)
|
|
||||||
{
|
|
||||||
int op = tpl.opcode;
|
|
||||||
dynaOpTable4[op] = tpl.Inst;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& tpl : table31)
|
|
||||||
{
|
|
||||||
int op = tpl.opcode;
|
|
||||||
dynaOpTable31[op] = tpl.Inst;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& tpl : table19)
|
|
||||||
{
|
|
||||||
int op = tpl.opcode;
|
|
||||||
dynaOpTable19[op] = tpl.Inst;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& tpl : table59)
|
|
||||||
{
|
|
||||||
int op = tpl.opcode;
|
|
||||||
dynaOpTable59[op] = tpl.Inst;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& tpl : table63)
|
|
||||||
{
|
|
||||||
int op = tpl.opcode;
|
|
||||||
dynaOpTable63[op] = tpl.Inst;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < 32; i++)
|
|
||||||
{
|
|
||||||
int fill = i << 5;
|
|
||||||
for (const auto& tpl : table63_2)
|
|
||||||
{
|
|
||||||
int op = fill + tpl.opcode;
|
|
||||||
dynaOpTable63[op] = tpl.Inst;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
initialized = true;
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,443 +0,0 @@
|
||||||
// Copyright 2008 Dolphin Emulator Project
|
|
||||||
// Licensed under GPLv2+
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <cstddef>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "Common/CommonTypes.h"
|
|
||||||
#include "Common/NonCopyable.h"
|
|
||||||
|
|
||||||
namespace IREmitter
|
|
||||||
{
|
|
||||||
enum Opcode
|
|
||||||
{
|
|
||||||
Nop = 0,
|
|
||||||
|
|
||||||
// "Zero-operand" operators
|
|
||||||
// Register load operators
|
|
||||||
LoadGReg,
|
|
||||||
LoadLink,
|
|
||||||
LoadCR,
|
|
||||||
LoadCarry,
|
|
||||||
LoadCTR,
|
|
||||||
LoadMSR,
|
|
||||||
LoadGQR,
|
|
||||||
|
|
||||||
// Unary operators
|
|
||||||
// Integer unary operators
|
|
||||||
SExt8,
|
|
||||||
SExt16,
|
|
||||||
BSwap32,
|
|
||||||
BSwap16,
|
|
||||||
Cntlzw, // Count leading zeros
|
|
||||||
Not,
|
|
||||||
Load8, // These loads zext
|
|
||||||
Load16,
|
|
||||||
Load32,
|
|
||||||
// CR conversions
|
|
||||||
ConvertFromFastCR,
|
|
||||||
ConvertToFastCR,
|
|
||||||
// Branches
|
|
||||||
BranchUncond,
|
|
||||||
// Register store operators
|
|
||||||
StoreGReg,
|
|
||||||
StoreCR,
|
|
||||||
StoreLink,
|
|
||||||
StoreCarry,
|
|
||||||
StoreCTR,
|
|
||||||
StoreMSR,
|
|
||||||
StoreFPRF,
|
|
||||||
StoreGQR,
|
|
||||||
StoreSRR,
|
|
||||||
// Branch conditions
|
|
||||||
FastCRSOSet,
|
|
||||||
FastCREQSet,
|
|
||||||
FastCRGTSet,
|
|
||||||
FastCRLTSet,
|
|
||||||
// Arbitrary interpreter instruction
|
|
||||||
FallBackToInterpreter,
|
|
||||||
|
|
||||||
// Binary operators
|
|
||||||
// Commutative integer operators
|
|
||||||
Add,
|
|
||||||
Mul,
|
|
||||||
And,
|
|
||||||
Or,
|
|
||||||
Xor,
|
|
||||||
// Non-commutative integer operators
|
|
||||||
MulHighUnsigned,
|
|
||||||
Sub,
|
|
||||||
Shl, // Note that shifts ignore bits above the bottom 5
|
|
||||||
Shrl,
|
|
||||||
Sarl,
|
|
||||||
Rol,
|
|
||||||
ICmpCRSigned, // CR for signed int compare
|
|
||||||
ICmpCRUnsigned, // CR for unsigned int compare
|
|
||||||
ICmpEq, // One if equal, zero otherwise
|
|
||||||
ICmpNe,
|
|
||||||
ICmpUgt, // One if op1 > op2, zero otherwise
|
|
||||||
ICmpUlt,
|
|
||||||
ICmpUge,
|
|
||||||
ICmpUle,
|
|
||||||
ICmpSgt, // One if op1 > op2, zero otherwise
|
|
||||||
ICmpSlt,
|
|
||||||
ICmpSge,
|
|
||||||
ICmpSle, // Opposite of sgt
|
|
||||||
|
|
||||||
// Memory store operators
|
|
||||||
Store8,
|
|
||||||
Store16,
|
|
||||||
Store32,
|
|
||||||
BranchCond,
|
|
||||||
// Floating-point
|
|
||||||
// There are three floating-point formats: single, double,
|
|
||||||
// and packed. For any operation where the format of the
|
|
||||||
// operand isn't known, the ForceTo* operations are used;
|
|
||||||
// these are folded into the appropriate conversion
|
|
||||||
// (or no conversion) depending on the type of the operand.
|
|
||||||
// The "mreg" format is a pair of doubles; this is the
|
|
||||||
// most general possible represenation which is used
|
|
||||||
// in the register state.
|
|
||||||
// This might seem like overkill, but the semantics require
|
|
||||||
// having the different formats.
|
|
||||||
// FIXME: Check the accuracy of the mapping:
|
|
||||||
// 1. Is paired arithmetic always rounded to single-precision
|
|
||||||
// first, or does it do double-to-single like the
|
|
||||||
// single-precision instructions?
|
|
||||||
// 2. The implementation of madd is slightly off, and
|
|
||||||
// the implementation of fmuls is very slightly off;
|
|
||||||
// likely nothing cares, though.
|
|
||||||
FResult_Start,
|
|
||||||
LoadSingle,
|
|
||||||
LoadDouble,
|
|
||||||
LoadPaired, // This handles quantizers itself
|
|
||||||
DoubleToSingle,
|
|
||||||
DupSingleToMReg,
|
|
||||||
DupSingleToPacked,
|
|
||||||
InsertDoubleInMReg,
|
|
||||||
ExpandPackedToMReg,
|
|
||||||
CompactMRegToPacked,
|
|
||||||
LoadFReg,
|
|
||||||
LoadFRegDENToZero,
|
|
||||||
FSMul,
|
|
||||||
FSAdd,
|
|
||||||
FSSub,
|
|
||||||
FSNeg,
|
|
||||||
FPAdd,
|
|
||||||
FPMul,
|
|
||||||
FPSub,
|
|
||||||
FPNeg,
|
|
||||||
FDMul,
|
|
||||||
FDAdd,
|
|
||||||
FDSub,
|
|
||||||
FDNeg,
|
|
||||||
FPMerge00,
|
|
||||||
FPMerge01,
|
|
||||||
FPMerge10,
|
|
||||||
FPMerge11,
|
|
||||||
FPDup0,
|
|
||||||
FPDup1,
|
|
||||||
FResult_End,
|
|
||||||
StorePaired,
|
|
||||||
StoreSingle,
|
|
||||||
StoreDouble,
|
|
||||||
StoreFReg,
|
|
||||||
FDCmpCR,
|
|
||||||
|
|
||||||
// "Trinary" operators
|
|
||||||
// FIXME: Need to change representation!
|
|
||||||
// Select, // Equivalent to C "Op1 ? Op2 : Op3"
|
|
||||||
|
|
||||||
// Integer constants
|
|
||||||
CInt16,
|
|
||||||
CInt32,
|
|
||||||
|
|
||||||
// Funny PPC "branches"
|
|
||||||
SystemCall,
|
|
||||||
RFIExit,
|
|
||||||
InterpreterBranch,
|
|
||||||
|
|
||||||
IdleBranch, // branch operation belonging to idle loop
|
|
||||||
ShortIdleLoop, // Idle loop seen in homebrew like Wii mahjong,
|
|
||||||
// just a branch
|
|
||||||
|
|
||||||
// used for exception checking, at least until someone
|
|
||||||
// has a better idea of integrating it
|
|
||||||
FPExceptionCheck,
|
|
||||||
DSIExceptionCheck,
|
|
||||||
ExtExceptionCheck,
|
|
||||||
BreakPointCheck,
|
|
||||||
// "Opcode" representing a register too far away to
|
|
||||||
// reference directly; this is a size optimization
|
|
||||||
Tramp,
|
|
||||||
// "Opcode"s representing the start and end
|
|
||||||
BlockStart,
|
|
||||||
BlockEnd,
|
|
||||||
|
|
||||||
// used for debugging
|
|
||||||
Int3
|
|
||||||
};
|
|
||||||
|
|
||||||
using Inst = u32;
|
|
||||||
using InstLoc = Inst*;
|
|
||||||
|
|
||||||
constexpr u32 getOpcode(Inst i)
|
|
||||||
{
|
|
||||||
return i & 255;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr bool isImm(Inst i)
|
|
||||||
{
|
|
||||||
return getOpcode(i) >= CInt16 && getOpcode(i) <= CInt32;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr bool isICmp(Inst i)
|
|
||||||
{
|
|
||||||
return getOpcode(i) >= ICmpEq && getOpcode(i) <= ICmpSle;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr bool isFResult(Inst i)
|
|
||||||
{
|
|
||||||
return getOpcode(i) > FResult_Start && getOpcode(i) < FResult_End;
|
|
||||||
}
|
|
||||||
|
|
||||||
InstLoc inline getOp1(InstLoc i)
|
|
||||||
{
|
|
||||||
i = i - 1 - ((*i >> 8) & 255);
|
|
||||||
|
|
||||||
if (getOpcode(*i) == Tramp)
|
|
||||||
{
|
|
||||||
i = i - 1 - (*i >> 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
InstLoc inline getOp2(InstLoc i)
|
|
||||||
{
|
|
||||||
i = i - 1 - ((*i >> 16) & 255);
|
|
||||||
|
|
||||||
if (getOpcode(*i) == Tramp)
|
|
||||||
{
|
|
||||||
i = i - 1 - (*i >> 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
class IRBuilder final : private NonCopyable
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
IRBuilder();
|
|
||||||
|
|
||||||
void Reset();
|
|
||||||
|
|
||||||
InstLoc EmitIntConst(unsigned value) { return EmitIntConst64(value); }
|
|
||||||
InstLoc EmitIntConst64(u64 value);
|
|
||||||
|
|
||||||
InstLoc EmitStoreLink(InstLoc val) { return FoldUOp(StoreLink, val); }
|
|
||||||
InstLoc EmitBranchUncond(InstLoc val) { return FoldUOp(BranchUncond, val); }
|
|
||||||
InstLoc EmitBranchCond(InstLoc check, InstLoc dest) { return FoldBiOp(BranchCond, check, dest); }
|
|
||||||
InstLoc EmitIdleBranch(InstLoc check, InstLoc dest) { return FoldBiOp(IdleBranch, check, dest); }
|
|
||||||
InstLoc EmitLoadCR(unsigned crreg) { return FoldZeroOp(LoadCR, crreg); }
|
|
||||||
InstLoc EmitStoreCR(InstLoc value, unsigned crreg) { return FoldUOp(StoreCR, value, crreg); }
|
|
||||||
InstLoc EmitLoadLink() { return FoldZeroOp(LoadLink, 0); }
|
|
||||||
InstLoc EmitLoadMSR() { return FoldZeroOp(LoadMSR, 0); }
|
|
||||||
InstLoc EmitStoreMSR(InstLoc val, InstLoc pc) { return FoldBiOp(StoreMSR, val, pc); }
|
|
||||||
InstLoc EmitStoreFPRF(InstLoc value) { return FoldUOp(StoreFPRF, value); }
|
|
||||||
InstLoc EmitLoadGReg(unsigned reg) { return FoldZeroOp(LoadGReg, reg); }
|
|
||||||
InstLoc EmitStoreGReg(InstLoc value, unsigned reg) { return FoldUOp(StoreGReg, value, reg); }
|
|
||||||
InstLoc EmitNot(InstLoc op1) { return FoldUOp(Not, op1); }
|
|
||||||
InstLoc EmitAnd(InstLoc op1, InstLoc op2) { return FoldBiOp(And, op1, op2); }
|
|
||||||
InstLoc EmitXor(InstLoc op1, InstLoc op2) { return FoldBiOp(Xor, op1, op2); }
|
|
||||||
InstLoc EmitSub(InstLoc op1, InstLoc op2) { return FoldBiOp(Sub, op1, op2); }
|
|
||||||
InstLoc EmitOr(InstLoc op1, InstLoc op2) { return FoldBiOp(Or, op1, op2); }
|
|
||||||
InstLoc EmitAdd(InstLoc op1, InstLoc op2) { return FoldBiOp(Add, op1, op2); }
|
|
||||||
InstLoc EmitMul(InstLoc op1, InstLoc op2) { return FoldBiOp(Mul, op1, op2); }
|
|
||||||
InstLoc EmitMulHighUnsigned(InstLoc op1, InstLoc op2)
|
|
||||||
{
|
|
||||||
return FoldBiOp(MulHighUnsigned, op1, op2);
|
|
||||||
}
|
|
||||||
|
|
||||||
InstLoc EmitRol(InstLoc op1, InstLoc op2) { return FoldBiOp(Rol, op1, op2); }
|
|
||||||
InstLoc EmitShl(InstLoc op1, InstLoc op2) { return FoldBiOp(Shl, op1, op2); }
|
|
||||||
InstLoc EmitShrl(InstLoc op1, InstLoc op2) { return FoldBiOp(Shrl, op1, op2); }
|
|
||||||
InstLoc EmitSarl(InstLoc op1, InstLoc op2) { return FoldBiOp(Sarl, op1, op2); }
|
|
||||||
InstLoc EmitLoadCTR() { return FoldZeroOp(LoadCTR, 0); }
|
|
||||||
InstLoc EmitStoreCTR(InstLoc op1) { return FoldUOp(StoreCTR, op1); }
|
|
||||||
InstLoc EmitICmpEq(InstLoc op1, InstLoc op2) { return FoldBiOp(ICmpEq, op1, op2); }
|
|
||||||
InstLoc EmitICmpNe(InstLoc op1, InstLoc op2) { return FoldBiOp(ICmpNe, op1, op2); }
|
|
||||||
InstLoc EmitICmpUgt(InstLoc op1, InstLoc op2) { return FoldBiOp(ICmpUgt, op1, op2); }
|
|
||||||
InstLoc EmitICmpUlt(InstLoc op1, InstLoc op2) { return FoldBiOp(ICmpUlt, op1, op2); }
|
|
||||||
InstLoc EmitICmpSgt(InstLoc op1, InstLoc op2) { return FoldBiOp(ICmpSgt, op1, op2); }
|
|
||||||
InstLoc EmitICmpSlt(InstLoc op1, InstLoc op2) { return FoldBiOp(ICmpSlt, op1, op2); }
|
|
||||||
InstLoc EmitICmpSge(InstLoc op1, InstLoc op2) { return FoldBiOp(ICmpSge, op1, op2); }
|
|
||||||
InstLoc EmitICmpSle(InstLoc op1, InstLoc op2) { return FoldBiOp(ICmpSle, op1, op2); }
|
|
||||||
InstLoc EmitLoad8(InstLoc op1) { return FoldUOp(Load8, op1); }
|
|
||||||
InstLoc EmitLoad16(InstLoc op1) { return FoldUOp(Load16, op1); }
|
|
||||||
InstLoc EmitLoad32(InstLoc op1) { return FoldUOp(Load32, op1); }
|
|
||||||
InstLoc EmitStore8(InstLoc op1, InstLoc op2) { return FoldBiOp(Store8, op1, op2); }
|
|
||||||
InstLoc EmitStore16(InstLoc op1, InstLoc op2) { return FoldBiOp(Store16, op1, op2); }
|
|
||||||
InstLoc EmitStore32(InstLoc op1, InstLoc op2) { return FoldBiOp(Store32, op1, op2); }
|
|
||||||
InstLoc EmitSExt16(InstLoc op1) { return FoldUOp(SExt16, op1); }
|
|
||||||
InstLoc EmitSExt8(InstLoc op1) { return FoldUOp(SExt8, op1); }
|
|
||||||
InstLoc EmitCntlzw(InstLoc op1) { return FoldUOp(Cntlzw, op1); }
|
|
||||||
InstLoc EmitICmpCRSigned(InstLoc op1, InstLoc op2) { return FoldBiOp(ICmpCRSigned, op1, op2); }
|
|
||||||
InstLoc EmitICmpCRUnsigned(InstLoc op1, InstLoc op2)
|
|
||||||
{
|
|
||||||
return FoldBiOp(ICmpCRUnsigned, op1, op2);
|
|
||||||
}
|
|
||||||
|
|
||||||
InstLoc EmitConvertFromFastCR(InstLoc op1) { return FoldUOp(ConvertFromFastCR, op1); }
|
|
||||||
InstLoc EmitConvertToFastCR(InstLoc op1) { return FoldUOp(ConvertToFastCR, op1); }
|
|
||||||
InstLoc EmitFastCRSOSet(InstLoc op1) { return FoldUOp(FastCRSOSet, op1); }
|
|
||||||
InstLoc EmitFastCREQSet(InstLoc op1) { return FoldUOp(FastCREQSet, op1); }
|
|
||||||
InstLoc EmitFastCRLTSet(InstLoc op1) { return FoldUOp(FastCRLTSet, op1); }
|
|
||||||
InstLoc EmitFastCRGTSet(InstLoc op1) { return FoldUOp(FastCRGTSet, op1); }
|
|
||||||
InstLoc EmitFallBackToInterpreter(InstLoc op1, InstLoc op2)
|
|
||||||
{
|
|
||||||
return FoldBiOp(FallBackToInterpreter, op1, op2);
|
|
||||||
}
|
|
||||||
|
|
||||||
InstLoc EmitInterpreterBranch() { return FoldZeroOp(InterpreterBranch, 0); }
|
|
||||||
InstLoc EmitLoadCarry() { return FoldZeroOp(LoadCarry, 0); }
|
|
||||||
InstLoc EmitStoreCarry(InstLoc op1) { return FoldUOp(StoreCarry, op1); }
|
|
||||||
InstLoc EmitSystemCall(InstLoc pc) { return FoldUOp(SystemCall, pc); }
|
|
||||||
InstLoc EmitFPExceptionCheck(InstLoc pc) { return EmitUOp(FPExceptionCheck, pc); }
|
|
||||||
InstLoc EmitDSIExceptionCheck(InstLoc pc) { return EmitUOp(DSIExceptionCheck, pc); }
|
|
||||||
InstLoc EmitExtExceptionCheck(InstLoc pc) { return EmitUOp(ExtExceptionCheck, pc); }
|
|
||||||
InstLoc EmitBreakPointCheck(InstLoc pc) { return EmitUOp(BreakPointCheck, pc); }
|
|
||||||
InstLoc EmitRFIExit() { return FoldZeroOp(RFIExit, 0); }
|
|
||||||
InstLoc EmitShortIdleLoop(InstLoc pc) { return FoldUOp(ShortIdleLoop, pc); }
|
|
||||||
InstLoc EmitLoadSingle(InstLoc addr) { return FoldUOp(LoadSingle, addr); }
|
|
||||||
InstLoc EmitLoadDouble(InstLoc addr) { return FoldUOp(LoadDouble, addr); }
|
|
||||||
InstLoc EmitLoadPaired(InstLoc addr, unsigned quantReg)
|
|
||||||
{
|
|
||||||
return FoldUOp(LoadPaired, addr, quantReg);
|
|
||||||
}
|
|
||||||
|
|
||||||
InstLoc EmitStoreSingle(InstLoc value, InstLoc addr)
|
|
||||||
{
|
|
||||||
return FoldBiOp(StoreSingle, value, addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
InstLoc EmitStoreDouble(InstLoc value, InstLoc addr)
|
|
||||||
{
|
|
||||||
return FoldBiOp(StoreDouble, value, addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
InstLoc EmitStorePaired(InstLoc value, InstLoc addr, unsigned quantReg)
|
|
||||||
{
|
|
||||||
return FoldBiOp(StorePaired, value, addr, quantReg);
|
|
||||||
}
|
|
||||||
|
|
||||||
InstLoc EmitLoadFReg(unsigned freg) { return FoldZeroOp(LoadFReg, freg); }
|
|
||||||
InstLoc EmitLoadFRegDENToZero(unsigned freg) { return FoldZeroOp(LoadFRegDENToZero, freg); }
|
|
||||||
InstLoc EmitStoreFReg(InstLoc val, unsigned freg) { return FoldUOp(StoreFReg, val, freg); }
|
|
||||||
InstLoc EmitDupSingleToMReg(InstLoc val) { return FoldUOp(DupSingleToMReg, val); }
|
|
||||||
InstLoc EmitDupSingleToPacked(InstLoc val) { return FoldUOp(DupSingleToPacked, val); }
|
|
||||||
InstLoc EmitInsertDoubleInMReg(InstLoc val, InstLoc reg)
|
|
||||||
{
|
|
||||||
return FoldBiOp(InsertDoubleInMReg, val, reg);
|
|
||||||
}
|
|
||||||
|
|
||||||
InstLoc EmitExpandPackedToMReg(InstLoc val) { return FoldUOp(ExpandPackedToMReg, val); }
|
|
||||||
InstLoc EmitCompactMRegToPacked(InstLoc val) { return FoldUOp(CompactMRegToPacked, val); }
|
|
||||||
InstLoc EmitFSMul(InstLoc op1, InstLoc op2) { return FoldBiOp(FSMul, op1, op2); }
|
|
||||||
InstLoc EmitFSAdd(InstLoc op1, InstLoc op2) { return FoldBiOp(FSAdd, op1, op2); }
|
|
||||||
InstLoc EmitFSSub(InstLoc op1, InstLoc op2) { return FoldBiOp(FSSub, op1, op2); }
|
|
||||||
InstLoc EmitFSNeg(InstLoc op1) { return FoldUOp(FSNeg, op1); }
|
|
||||||
InstLoc EmitFDMul(InstLoc op1, InstLoc op2) { return FoldBiOp(FDMul, op1, op2); }
|
|
||||||
InstLoc EmitFDAdd(InstLoc op1, InstLoc op2) { return FoldBiOp(FDAdd, op1, op2); }
|
|
||||||
InstLoc EmitFDSub(InstLoc op1, InstLoc op2) { return FoldBiOp(FDSub, op1, op2); }
|
|
||||||
InstLoc EmitFDNeg(InstLoc op1) { return FoldUOp(FDNeg, op1); }
|
|
||||||
InstLoc EmitFPAdd(InstLoc op1, InstLoc op2) { return FoldBiOp(FPAdd, op1, op2); }
|
|
||||||
InstLoc EmitFPMul(InstLoc op1, InstLoc op2) { return FoldBiOp(FPMul, op1, op2); }
|
|
||||||
InstLoc EmitFPSub(InstLoc op1, InstLoc op2) { return FoldBiOp(FPSub, op1, op2); }
|
|
||||||
InstLoc EmitFPMerge00(InstLoc op1, InstLoc op2) { return FoldBiOp(FPMerge00, op1, op2); }
|
|
||||||
InstLoc EmitFPMerge01(InstLoc op1, InstLoc op2) { return FoldBiOp(FPMerge01, op1, op2); }
|
|
||||||
InstLoc EmitFPMerge10(InstLoc op1, InstLoc op2) { return FoldBiOp(FPMerge10, op1, op2); }
|
|
||||||
InstLoc EmitFPMerge11(InstLoc op1, InstLoc op2) { return FoldBiOp(FPMerge11, op1, op2); }
|
|
||||||
InstLoc EmitFPDup0(InstLoc op1) { return FoldUOp(FPDup0, op1); }
|
|
||||||
InstLoc EmitFPDup1(InstLoc op1) { return FoldUOp(FPDup1, op1); }
|
|
||||||
InstLoc EmitFPNeg(InstLoc op1) { return FoldUOp(FPNeg, op1); }
|
|
||||||
InstLoc EmitDoubleToSingle(InstLoc op1) { return FoldUOp(DoubleToSingle, op1); }
|
|
||||||
InstLoc EmitFDCmpCR(InstLoc op1, InstLoc op2, int ordered)
|
|
||||||
{
|
|
||||||
return FoldBiOp(FDCmpCR, op1, op2, ordered);
|
|
||||||
}
|
|
||||||
|
|
||||||
InstLoc EmitLoadGQR(unsigned gqr) { return FoldZeroOp(LoadGQR, gqr); }
|
|
||||||
InstLoc EmitStoreGQR(InstLoc op1, unsigned gqr) { return FoldUOp(StoreGQR, op1, gqr); }
|
|
||||||
InstLoc EmitStoreSRR(InstLoc op1, unsigned srr) { return FoldUOp(StoreSRR, op1, srr); }
|
|
||||||
InstLoc EmitINT3() { return FoldZeroOp(Int3, 0); }
|
|
||||||
void StartBackPass() { curReadPtr = InstList.data() + InstList.size(); }
|
|
||||||
void StartForwardPass() { curReadPtr = InstList.data(); }
|
|
||||||
InstLoc ReadForward() { return curReadPtr++; }
|
|
||||||
InstLoc ReadBackward() { return --curReadPtr; }
|
|
||||||
InstLoc getFirstInst() { return InstList.data(); }
|
|
||||||
size_t getNumInsts() const { return InstList.size(); }
|
|
||||||
unsigned int GetImmValue(InstLoc I) const { return (u32)GetImmValue64(I); }
|
|
||||||
u64 GetImmValue64(InstLoc I) const;
|
|
||||||
void SetMarkUsed(InstLoc I);
|
|
||||||
bool IsMarkUsed(InstLoc I) const;
|
|
||||||
void WriteToFile(u64 codeHash);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void InvalidateCaches();
|
|
||||||
|
|
||||||
InstLoc EmitZeroOp(unsigned Opcode, unsigned extra);
|
|
||||||
InstLoc EmitUOp(unsigned OpCode, InstLoc Op1, unsigned extra = 0);
|
|
||||||
InstLoc EmitBiOp(unsigned OpCode, InstLoc Op1, InstLoc Op2, unsigned extra = 0);
|
|
||||||
|
|
||||||
InstLoc FoldAdd(InstLoc Op1, InstLoc Op2);
|
|
||||||
InstLoc FoldSub(InstLoc Op1, InstLoc Op2);
|
|
||||||
InstLoc FoldMul(InstLoc Op1, InstLoc Op2);
|
|
||||||
InstLoc FoldMulHighUnsigned(InstLoc Op1, InstLoc Op2);
|
|
||||||
InstLoc FoldAnd(InstLoc Op1, InstLoc Op2);
|
|
||||||
InstLoc FoldOr(InstLoc Op1, InstLoc Op2);
|
|
||||||
InstLoc FoldRol(InstLoc Op1, InstLoc Op2);
|
|
||||||
InstLoc FoldShl(InstLoc Op1, InstLoc Op2);
|
|
||||||
InstLoc FoldShrl(InstLoc Op1, InstLoc Op2);
|
|
||||||
InstLoc FoldXor(InstLoc Op1, InstLoc Op2);
|
|
||||||
InstLoc FoldBranchCond(InstLoc Op1, InstLoc Op2);
|
|
||||||
InstLoc FoldICmp(unsigned Opcode, InstLoc Op1, InstLoc Op2);
|
|
||||||
InstLoc FoldICmpCRSigned(InstLoc Op1, InstLoc Op2);
|
|
||||||
InstLoc FoldICmpCRUnsigned(InstLoc Op1, InstLoc Op2);
|
|
||||||
InstLoc FoldDoubleBiOp(unsigned Opcode, InstLoc Op1, InstLoc Op2);
|
|
||||||
|
|
||||||
InstLoc FoldFallBackToInterpreter(InstLoc Op1, InstLoc Op2);
|
|
||||||
|
|
||||||
InstLoc FoldZeroOp(unsigned Opcode, unsigned extra);
|
|
||||||
InstLoc FoldUOp(unsigned OpCode, InstLoc Op1, unsigned extra = 0);
|
|
||||||
InstLoc FoldBiOp(unsigned OpCode, InstLoc Op1, InstLoc Op2, unsigned extra = 0);
|
|
||||||
|
|
||||||
unsigned ComputeKnownZeroBits(InstLoc I) const;
|
|
||||||
|
|
||||||
bool isSameValue(InstLoc Op1, InstLoc Op2) const;
|
|
||||||
unsigned getComplexity(InstLoc I) const;
|
|
||||||
unsigned getNumberOfOperands(InstLoc I) const;
|
|
||||||
void simplifyCommutative(unsigned Opcode, InstLoc& Op1, InstLoc& Op2);
|
|
||||||
bool maskedValueIsZero(InstLoc Op1, InstLoc Op2) const;
|
|
||||||
InstLoc isNeg(InstLoc I) const;
|
|
||||||
|
|
||||||
std::vector<Inst> InstList; // FIXME: We must ensure this is continuous!
|
|
||||||
std::vector<bool> MarkUsed; // Used for IRWriter
|
|
||||||
std::vector<u64> ConstList;
|
|
||||||
InstLoc curReadPtr;
|
|
||||||
std::array<InstLoc, 32> GRegCache;
|
|
||||||
std::array<InstLoc, 32> GRegCacheStore;
|
|
||||||
std::array<InstLoc, 32> FRegCache;
|
|
||||||
std::array<InstLoc, 32> FRegCacheStore;
|
|
||||||
InstLoc CarryCache;
|
|
||||||
InstLoc CarryCacheStore;
|
|
||||||
InstLoc CTRCache;
|
|
||||||
InstLoc CTRCacheStore;
|
|
||||||
std::array<InstLoc, 8> CRCache;
|
|
||||||
std::array<InstLoc, 8> CRCacheStore;
|
|
||||||
};
|
|
||||||
} // namespace IREmitter
|
|
|
@ -1,130 +0,0 @@
|
||||||
// Copyright 2008 Dolphin Emulator Project
|
|
||||||
// Licensed under GPLv2+
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Common/CommonTypes.h"
|
|
||||||
#include "Core/PowerPC/Gekko.h"
|
|
||||||
#include "Core/PowerPC/Jit64Common/Jit64Base.h"
|
|
||||||
#include "Core/PowerPC/JitILCommon/IR.h"
|
|
||||||
#include "Core/PowerPC/PPCAnalyst.h"
|
|
||||||
|
|
||||||
class JitILBase : public Jitx86Base
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
JitILBase() : code_buffer(32000) {}
|
|
||||||
~JitILBase() {}
|
|
||||||
virtual void Jit(u32 em_address) = 0;
|
|
||||||
|
|
||||||
virtual const CommonAsmRoutinesBase* GetAsmRoutines() = 0;
|
|
||||||
|
|
||||||
// OPCODES
|
|
||||||
virtual void FallBackToInterpreter(UGeckoInstruction inst) = 0;
|
|
||||||
virtual void DoNothing(UGeckoInstruction inst) = 0;
|
|
||||||
virtual void HLEFunction(UGeckoInstruction inst) = 0;
|
|
||||||
|
|
||||||
virtual void DynaRunTable4(UGeckoInstruction _inst) = 0;
|
|
||||||
virtual void DynaRunTable19(UGeckoInstruction _inst) = 0;
|
|
||||||
virtual void DynaRunTable31(UGeckoInstruction _inst) = 0;
|
|
||||||
virtual void DynaRunTable59(UGeckoInstruction _inst) = 0;
|
|
||||||
virtual void DynaRunTable63(UGeckoInstruction _inst) = 0;
|
|
||||||
|
|
||||||
// Branches
|
|
||||||
void sc(UGeckoInstruction inst);
|
|
||||||
void rfi(UGeckoInstruction inst);
|
|
||||||
void bx(UGeckoInstruction inst);
|
|
||||||
void bcx(UGeckoInstruction inst);
|
|
||||||
void bcctrx(UGeckoInstruction inst);
|
|
||||||
void bclrx(UGeckoInstruction inst);
|
|
||||||
|
|
||||||
// LoadStore
|
|
||||||
void lXzx(UGeckoInstruction inst);
|
|
||||||
void lhax(UGeckoInstruction inst);
|
|
||||||
void lhaux(UGeckoInstruction inst);
|
|
||||||
void stXx(UGeckoInstruction inst);
|
|
||||||
void lmw(UGeckoInstruction inst);
|
|
||||||
void stmw(UGeckoInstruction inst);
|
|
||||||
void stX(UGeckoInstruction inst); // stw sth stb
|
|
||||||
void lXz(UGeckoInstruction inst);
|
|
||||||
void lbzu(UGeckoInstruction inst);
|
|
||||||
void lha(UGeckoInstruction inst);
|
|
||||||
void lhau(UGeckoInstruction inst);
|
|
||||||
|
|
||||||
// System Registers
|
|
||||||
void mtspr(UGeckoInstruction inst);
|
|
||||||
void mfspr(UGeckoInstruction inst);
|
|
||||||
void mtmsr(UGeckoInstruction inst);
|
|
||||||
void mfmsr(UGeckoInstruction inst);
|
|
||||||
void mftb(UGeckoInstruction inst);
|
|
||||||
void mtcrf(UGeckoInstruction inst);
|
|
||||||
void mfcr(UGeckoInstruction inst);
|
|
||||||
void mcrf(UGeckoInstruction inst);
|
|
||||||
void crXX(UGeckoInstruction inst);
|
|
||||||
|
|
||||||
void dcbst(UGeckoInstruction inst);
|
|
||||||
void dcbz(UGeckoInstruction inst);
|
|
||||||
void icbi(UGeckoInstruction inst);
|
|
||||||
|
|
||||||
void addx(UGeckoInstruction inst);
|
|
||||||
void boolX(UGeckoInstruction inst);
|
|
||||||
void mulli(UGeckoInstruction inst);
|
|
||||||
void mulhwux(UGeckoInstruction inst);
|
|
||||||
void mullwx(UGeckoInstruction inst);
|
|
||||||
void divwux(UGeckoInstruction inst);
|
|
||||||
void srawix(UGeckoInstruction inst);
|
|
||||||
void srawx(UGeckoInstruction inst);
|
|
||||||
void addex(UGeckoInstruction inst);
|
|
||||||
void addzex(UGeckoInstruction inst);
|
|
||||||
|
|
||||||
void extsbx(UGeckoInstruction inst);
|
|
||||||
void extshx(UGeckoInstruction inst);
|
|
||||||
|
|
||||||
void reg_imm(UGeckoInstruction inst);
|
|
||||||
|
|
||||||
void ps_arith(UGeckoInstruction inst); // aggregate
|
|
||||||
void ps_mergeXX(UGeckoInstruction inst);
|
|
||||||
void ps_maddXX(UGeckoInstruction inst);
|
|
||||||
void ps_sum(UGeckoInstruction inst);
|
|
||||||
void ps_muls(UGeckoInstruction inst);
|
|
||||||
|
|
||||||
void fp_arith_s(UGeckoInstruction inst);
|
|
||||||
|
|
||||||
void fcmpX(UGeckoInstruction inst);
|
|
||||||
void fmrx(UGeckoInstruction inst);
|
|
||||||
|
|
||||||
void cmpXX(UGeckoInstruction inst);
|
|
||||||
|
|
||||||
void cntlzwx(UGeckoInstruction inst);
|
|
||||||
|
|
||||||
void lfs(UGeckoInstruction inst);
|
|
||||||
void lfsu(UGeckoInstruction inst);
|
|
||||||
void lfd(UGeckoInstruction inst);
|
|
||||||
void lfdu(UGeckoInstruction inst);
|
|
||||||
void stfd(UGeckoInstruction inst);
|
|
||||||
void stfs(UGeckoInstruction inst);
|
|
||||||
void stfsx(UGeckoInstruction inst);
|
|
||||||
void psq_l(UGeckoInstruction inst);
|
|
||||||
void psq_st(UGeckoInstruction inst);
|
|
||||||
|
|
||||||
void fmaddXX(UGeckoInstruction inst);
|
|
||||||
void fsign(UGeckoInstruction inst);
|
|
||||||
void rlwinmx(UGeckoInstruction inst);
|
|
||||||
void rlwimix(UGeckoInstruction inst);
|
|
||||||
void rlwnmx(UGeckoInstruction inst);
|
|
||||||
void negx(UGeckoInstruction inst);
|
|
||||||
void slwx(UGeckoInstruction inst);
|
|
||||||
void srwx(UGeckoInstruction inst);
|
|
||||||
void lfsx(UGeckoInstruction inst);
|
|
||||||
|
|
||||||
void subfic(UGeckoInstruction inst);
|
|
||||||
void subfcx(UGeckoInstruction inst);
|
|
||||||
void subfx(UGeckoInstruction inst);
|
|
||||||
void subfex(UGeckoInstruction inst);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
// The default code buffer. We keep it around to not have to alloc/dealloc a
|
|
||||||
// large chunk of memory for each recompiled block.
|
|
||||||
PPCAnalyst::CodeBuffer code_buffer;
|
|
||||||
IREmitter::IRBuilder ibuild;
|
|
||||||
};
|
|
|
@ -1,217 +0,0 @@
|
||||||
// Copyright 2008 Dolphin Emulator Project
|
|
||||||
// Licensed under GPLv2+
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#include "Core/PowerPC/JitILCommon/JitILBase.h"
|
|
||||||
#include "Common/CommonTypes.h"
|
|
||||||
#include "Core/ConfigManager.h"
|
|
||||||
#include "Core/PowerPC/Gekko.h"
|
|
||||||
#include "Core/PowerPC/PowerPC.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.
|
|
||||||
|
|
||||||
//#define NORMALBRANCH_START FallBackToInterpreter(inst); ibuild.EmitInterpreterBranch(); return;
|
|
||||||
#define NORMALBRANCH_START
|
|
||||||
|
|
||||||
void JitILBase::sc(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
ibuild.EmitSystemCall(ibuild.EmitIntConst(js.compilerPC));
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::rfi(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
ibuild.EmitRFIExit();
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::bx(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
NORMALBRANCH_START
|
|
||||||
INSTRUCTION_START;
|
|
||||||
|
|
||||||
// We must always process the following sentence
|
|
||||||
// even if the blocks are merged by PPCAnalyst::Flatten().
|
|
||||||
if (inst.LK)
|
|
||||||
ibuild.EmitStoreLink(ibuild.EmitIntConst(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;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 destination;
|
|
||||||
if (inst.AA)
|
|
||||||
destination = SignExt26(inst.LI << 2);
|
|
||||||
else
|
|
||||||
destination = js.compilerPC + SignExt26(inst.LI << 2);
|
|
||||||
|
|
||||||
if (destination == js.compilerPC)
|
|
||||||
{
|
|
||||||
ibuild.EmitShortIdleLoop(ibuild.EmitIntConst(js.compilerPC));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ibuild.EmitBranchUncond(ibuild.EmitIntConst(destination));
|
|
||||||
}
|
|
||||||
|
|
||||||
static IREmitter::InstLoc EmitCRTest(IREmitter::IRBuilder& ibuild, UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
IREmitter::InstLoc CRReg = ibuild.EmitLoadCR(inst.BI >> 2);
|
|
||||||
IREmitter::InstLoc CRTest = nullptr;
|
|
||||||
switch (3 - (inst.BI & 3))
|
|
||||||
{
|
|
||||||
case CR_SO_BIT:
|
|
||||||
CRTest = ibuild.EmitFastCRSOSet(CRReg);
|
|
||||||
break;
|
|
||||||
case CR_EQ_BIT:
|
|
||||||
CRTest = ibuild.EmitFastCREQSet(CRReg);
|
|
||||||
break;
|
|
||||||
case CR_GT_BIT:
|
|
||||||
CRTest = ibuild.EmitFastCRGTSet(CRReg);
|
|
||||||
break;
|
|
||||||
case CR_LT_BIT:
|
|
||||||
CRTest = ibuild.EmitFastCRLTSet(CRReg);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (!(inst.BO & 8))
|
|
||||||
CRTest = ibuild.EmitXor(CRTest, ibuild.EmitIntConst(1));
|
|
||||||
return CRTest;
|
|
||||||
}
|
|
||||||
|
|
||||||
static IREmitter::InstLoc TestBranch(IREmitter::IRBuilder& ibuild, UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
IREmitter::InstLoc CRTest = nullptr, CTRTest = nullptr;
|
|
||||||
if ((inst.BO & 16) == 0) // Test a CR bit
|
|
||||||
{
|
|
||||||
CRTest = EmitCRTest(ibuild, inst);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((inst.BO & 4) == 0)
|
|
||||||
{
|
|
||||||
IREmitter::InstLoc c = ibuild.EmitLoadCTR();
|
|
||||||
c = ibuild.EmitSub(c, ibuild.EmitIntConst(1));
|
|
||||||
ibuild.EmitStoreCTR(c);
|
|
||||||
|
|
||||||
if (inst.BO & 2)
|
|
||||||
CTRTest = ibuild.EmitICmpEq(c, ibuild.EmitIntConst(0));
|
|
||||||
else
|
|
||||||
CTRTest = c;
|
|
||||||
}
|
|
||||||
|
|
||||||
IREmitter::InstLoc Test = CRTest;
|
|
||||||
if (CTRTest)
|
|
||||||
{
|
|
||||||
if (Test)
|
|
||||||
Test = ibuild.EmitAnd(Test, CTRTest);
|
|
||||||
else
|
|
||||||
Test = CTRTest;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Test)
|
|
||||||
{
|
|
||||||
Test = ibuild.EmitIntConst(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Test;
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::bcx(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
NORMALBRANCH_START
|
|
||||||
if (inst.LK)
|
|
||||||
ibuild.EmitStoreLink(ibuild.EmitIntConst(js.compilerPC + 4));
|
|
||||||
|
|
||||||
IREmitter::InstLoc Test = TestBranch(ibuild, inst);
|
|
||||||
|
|
||||||
u32 destination;
|
|
||||||
if (inst.AA)
|
|
||||||
destination = SignExt16(inst.BD << 2);
|
|
||||||
else
|
|
||||||
destination = js.compilerPC + SignExt16(inst.BD << 2);
|
|
||||||
|
|
||||||
// Idle skipping:
|
|
||||||
// The main Idle skipping is done in the LoadStore code, but there is an optimization here.
|
|
||||||
// If idle skipping is enabled, then this branch will only be reached when the branch is not
|
|
||||||
// taken.
|
|
||||||
// TODO: We shouldn't use debug reads here.
|
|
||||||
if (inst.hex == 0x4182fff8 &&
|
|
||||||
(PowerPC::HostRead_U32(js.compilerPC - 8) & 0xFFFF0000) == 0x800D0000 &&
|
|
||||||
(PowerPC::HostRead_U32(js.compilerPC - 4) == 0x28000000 ||
|
|
||||||
(SConfig::GetInstance().bWii && PowerPC::HostRead_U32(js.compilerPC - 4) == 0x2C000000)))
|
|
||||||
{
|
|
||||||
// Uh, Do nothing.
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ibuild.EmitBranchCond(Test, ibuild.EmitIntConst(destination));
|
|
||||||
}
|
|
||||||
ibuild.EmitBranchUncond(ibuild.EmitIntConst(js.compilerPC + 4));
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::bcctrx(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
NORMALBRANCH_START
|
|
||||||
if ((inst.BO & 4) == 0)
|
|
||||||
{
|
|
||||||
IREmitter::InstLoc c = ibuild.EmitLoadCTR();
|
|
||||||
c = ibuild.EmitSub(c, ibuild.EmitIntConst(1));
|
|
||||||
ibuild.EmitStoreCTR(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
IREmitter::InstLoc test;
|
|
||||||
if ((inst.BO & 16) == 0) // Test a CR bit
|
|
||||||
{
|
|
||||||
test = EmitCRTest(ibuild, inst);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
test = ibuild.EmitIntConst(1);
|
|
||||||
}
|
|
||||||
test = ibuild.EmitICmpEq(test, ibuild.EmitIntConst(0));
|
|
||||||
ibuild.EmitBranchCond(test, ibuild.EmitIntConst(js.compilerPC + 4));
|
|
||||||
|
|
||||||
IREmitter::InstLoc destination = ibuild.EmitLoadCTR();
|
|
||||||
destination = ibuild.EmitAnd(destination, ibuild.EmitIntConst(-4));
|
|
||||||
if (inst.LK)
|
|
||||||
ibuild.EmitStoreLink(ibuild.EmitIntConst(js.compilerPC + 4));
|
|
||||||
ibuild.EmitBranchUncond(destination);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::bclrx(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
NORMALBRANCH_START
|
|
||||||
|
|
||||||
if (!js.isLastInstruction && (inst.BO & (1 << 4)) && (inst.BO & (1 << 2)))
|
|
||||||
{
|
|
||||||
if (inst.LK)
|
|
||||||
ibuild.EmitStoreLink(ibuild.EmitIntConst(js.compilerPC + 4));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inst.hex == 0x4e800020)
|
|
||||||
{
|
|
||||||
ibuild.EmitBranchUncond(ibuild.EmitLoadLink());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
IREmitter::InstLoc test = TestBranch(ibuild, inst);
|
|
||||||
test = ibuild.EmitICmpEq(test, ibuild.EmitIntConst(0));
|
|
||||||
ibuild.EmitBranchCond(test, ibuild.EmitIntConst(js.compilerPC + 4));
|
|
||||||
|
|
||||||
IREmitter::InstLoc destination = ibuild.EmitLoadLink();
|
|
||||||
destination = ibuild.EmitAnd(destination, ibuild.EmitIntConst(-4));
|
|
||||||
if (inst.LK)
|
|
||||||
ibuild.EmitStoreLink(ibuild.EmitIntConst(js.compilerPC + 4));
|
|
||||||
ibuild.EmitBranchUncond(destination);
|
|
||||||
}
|
|
|
@ -1,125 +0,0 @@
|
||||||
// Copyright 2008 Dolphin Emulator Project
|
|
||||||
// Licensed under GPLv2+
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#include "Core/PowerPC/JitILCommon/JitILBase.h"
|
|
||||||
#include "Common/Assert.h"
|
|
||||||
#include "Common/CommonTypes.h"
|
|
||||||
#include "Common/MsgHandler.h"
|
|
||||||
#include "Core/ConfigManager.h"
|
|
||||||
|
|
||||||
void JitILBase::fp_arith_s(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITFloatingPointOff);
|
|
||||||
FALLBACK_IF(inst.Rc || (inst.SUBOP5 != 25 && inst.SUBOP5 != 20 && inst.SUBOP5 != 21));
|
|
||||||
|
|
||||||
// Only the interpreter has "proper" support for (some) FP flags
|
|
||||||
FALLBACK_IF(inst.SUBOP5 == 25 && SConfig::GetInstance().bFPRF);
|
|
||||||
|
|
||||||
IREmitter::InstLoc val = ibuild.EmitLoadFReg(inst.FA);
|
|
||||||
switch (inst.SUBOP5)
|
|
||||||
{
|
|
||||||
case 20: // sub
|
|
||||||
val = ibuild.EmitFDSub(val, ibuild.EmitLoadFReg(inst.FB));
|
|
||||||
break;
|
|
||||||
case 21: // add
|
|
||||||
val = ibuild.EmitFDAdd(val, ibuild.EmitLoadFReg(inst.FB));
|
|
||||||
break;
|
|
||||||
case 25: // mul
|
|
||||||
val = ibuild.EmitFDMul(val, ibuild.EmitLoadFReg(inst.FC));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
_assert_msg_(DYNA_REC, 0, "fp_arith_s WTF!!!");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inst.OPCD == 59)
|
|
||||||
{
|
|
||||||
val = ibuild.EmitDoubleToSingle(val);
|
|
||||||
val = ibuild.EmitDupSingleToMReg(val);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
val = ibuild.EmitInsertDoubleInMReg(val, ibuild.EmitLoadFReg(inst.FD));
|
|
||||||
}
|
|
||||||
ibuild.EmitStoreFReg(val, inst.FD);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::fmaddXX(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITFloatingPointOff);
|
|
||||||
FALLBACK_IF(inst.Rc);
|
|
||||||
|
|
||||||
// Only the interpreter has "proper" support for (some) FP flags
|
|
||||||
FALLBACK_IF(inst.SUBOP5 == 29 && SConfig::GetInstance().bFPRF);
|
|
||||||
|
|
||||||
IREmitter::InstLoc val = ibuild.EmitLoadFReg(inst.FA);
|
|
||||||
val = ibuild.EmitFDMul(val, ibuild.EmitLoadFReg(inst.FC));
|
|
||||||
|
|
||||||
if (inst.SUBOP5 & 1)
|
|
||||||
val = ibuild.EmitFDAdd(val, ibuild.EmitLoadFReg(inst.FB));
|
|
||||||
else
|
|
||||||
val = ibuild.EmitFDSub(val, ibuild.EmitLoadFReg(inst.FB));
|
|
||||||
|
|
||||||
if (inst.SUBOP5 & 2)
|
|
||||||
val = ibuild.EmitFDNeg(val);
|
|
||||||
|
|
||||||
if (inst.OPCD == 59)
|
|
||||||
{
|
|
||||||
val = ibuild.EmitDoubleToSingle(val);
|
|
||||||
val = ibuild.EmitDupSingleToMReg(val);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
val = ibuild.EmitInsertDoubleInMReg(val, ibuild.EmitLoadFReg(inst.FD));
|
|
||||||
}
|
|
||||||
|
|
||||||
ibuild.EmitStoreFReg(val, inst.FD);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::fmrx(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITFloatingPointOff);
|
|
||||||
FALLBACK_IF(inst.Rc);
|
|
||||||
|
|
||||||
IREmitter::InstLoc val = ibuild.EmitLoadFReg(inst.FB);
|
|
||||||
val = ibuild.EmitInsertDoubleInMReg(val, ibuild.EmitLoadFReg(inst.FD));
|
|
||||||
ibuild.EmitStoreFReg(val, inst.FD);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::fcmpX(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITFloatingPointOff);
|
|
||||||
IREmitter::InstLoc lhs, rhs, res;
|
|
||||||
lhs = ibuild.EmitLoadFReg(inst.FA);
|
|
||||||
rhs = ibuild.EmitLoadFReg(inst.FB);
|
|
||||||
int ordered = (inst.SUBOP10 == 32) ? 1 : 0;
|
|
||||||
res = ibuild.EmitFDCmpCR(lhs, rhs, ordered);
|
|
||||||
ibuild.EmitStoreFPRF(res);
|
|
||||||
ibuild.EmitStoreCR(ibuild.EmitConvertToFastCR(res), inst.CRFD);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::fsign(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITFloatingPointOff);
|
|
||||||
|
|
||||||
FALLBACK_IF(true);
|
|
||||||
|
|
||||||
// TODO
|
|
||||||
switch (inst.SUBOP10)
|
|
||||||
{
|
|
||||||
case 40: // fnegx
|
|
||||||
break;
|
|
||||||
case 264: // fabsx
|
|
||||||
break;
|
|
||||||
case 136: // fnabs
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
PanicAlert("fsign bleh");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,559 +0,0 @@
|
||||||
// Copyright 2008 Dolphin Emulator Project
|
|
||||||
// Licensed under GPLv2+
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
#pragma warning( \
|
|
||||||
disable : 4146) // unary minus operator applied to unsigned type, result still unsigned
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "Core/PowerPC/JitILCommon/JitILBase.h"
|
|
||||||
#include "Common/CommonTypes.h"
|
|
||||||
|
|
||||||
static void ComputeRC(IREmitter::IRBuilder& ibuild, IREmitter::InstLoc val)
|
|
||||||
{
|
|
||||||
IREmitter::InstLoc res = ibuild.EmitICmpCRSigned(val, ibuild.EmitIntConst(0));
|
|
||||||
ibuild.EmitStoreCR(res, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::reg_imm(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITIntegerOff);
|
|
||||||
int d = inst.RD, a = inst.RA, s = inst.RS;
|
|
||||||
IREmitter::InstLoc val, test, c;
|
|
||||||
switch (inst.OPCD)
|
|
||||||
{
|
|
||||||
case 14: // addi
|
|
||||||
val = ibuild.EmitIntConst(inst.SIMM_16);
|
|
||||||
if (a)
|
|
||||||
val = ibuild.EmitAdd(ibuild.EmitLoadGReg(a), val);
|
|
||||||
ibuild.EmitStoreGReg(val, d);
|
|
||||||
break;
|
|
||||||
case 15: // addis
|
|
||||||
val = ibuild.EmitIntConst(inst.SIMM_16 << 16);
|
|
||||||
if (a)
|
|
||||||
val = ibuild.EmitAdd(ibuild.EmitLoadGReg(a), val);
|
|
||||||
ibuild.EmitStoreGReg(val, d);
|
|
||||||
break;
|
|
||||||
case 24: // ori
|
|
||||||
val = ibuild.EmitIntConst(inst.UIMM);
|
|
||||||
val = ibuild.EmitOr(ibuild.EmitLoadGReg(s), val);
|
|
||||||
ibuild.EmitStoreGReg(val, a);
|
|
||||||
break;
|
|
||||||
case 25: // oris
|
|
||||||
val = ibuild.EmitIntConst(inst.UIMM << 16);
|
|
||||||
val = ibuild.EmitOr(ibuild.EmitLoadGReg(s), val);
|
|
||||||
ibuild.EmitStoreGReg(val, a);
|
|
||||||
break;
|
|
||||||
case 28: // andi
|
|
||||||
val = ibuild.EmitIntConst(inst.UIMM);
|
|
||||||
val = ibuild.EmitAnd(ibuild.EmitLoadGReg(s), val);
|
|
||||||
ibuild.EmitStoreGReg(val, a);
|
|
||||||
ComputeRC(ibuild, val);
|
|
||||||
break;
|
|
||||||
case 29: // andis
|
|
||||||
val = ibuild.EmitIntConst(inst.UIMM << 16);
|
|
||||||
val = ibuild.EmitAnd(ibuild.EmitLoadGReg(s), val);
|
|
||||||
ibuild.EmitStoreGReg(val, a);
|
|
||||||
ComputeRC(ibuild, val);
|
|
||||||
break;
|
|
||||||
case 26: // xori
|
|
||||||
val = ibuild.EmitIntConst(inst.UIMM);
|
|
||||||
val = ibuild.EmitXor(ibuild.EmitLoadGReg(s), val);
|
|
||||||
ibuild.EmitStoreGReg(val, a);
|
|
||||||
break;
|
|
||||||
case 27: // xoris
|
|
||||||
val = ibuild.EmitIntConst(inst.UIMM << 16);
|
|
||||||
val = ibuild.EmitXor(ibuild.EmitLoadGReg(s), val);
|
|
||||||
ibuild.EmitStoreGReg(val, a);
|
|
||||||
break;
|
|
||||||
case 12: // addic
|
|
||||||
case 13: // addic_rc
|
|
||||||
c = ibuild.EmitIntConst(inst.SIMM_16);
|
|
||||||
val = ibuild.EmitAdd(ibuild.EmitLoadGReg(a), c);
|
|
||||||
ibuild.EmitStoreGReg(val, d);
|
|
||||||
test = ibuild.EmitICmpUgt(c, val);
|
|
||||||
ibuild.EmitStoreCarry(test);
|
|
||||||
if (inst.OPCD == 13)
|
|
||||||
ComputeRC(ibuild, val);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
FALLBACK_IF(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::cmpXX(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITIntegerOff);
|
|
||||||
IREmitter::InstLoc lhs, rhs, res;
|
|
||||||
lhs = ibuild.EmitLoadGReg(inst.RA);
|
|
||||||
|
|
||||||
if (inst.OPCD == 31)
|
|
||||||
{
|
|
||||||
rhs = ibuild.EmitLoadGReg(inst.RB);
|
|
||||||
if (inst.SUBOP10 == 32)
|
|
||||||
{
|
|
||||||
res = ibuild.EmitICmpCRUnsigned(lhs, rhs);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
res = ibuild.EmitICmpCRSigned(lhs, rhs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (inst.OPCD == 10)
|
|
||||||
{
|
|
||||||
rhs = ibuild.EmitIntConst(inst.UIMM);
|
|
||||||
res = ibuild.EmitICmpCRUnsigned(lhs, rhs);
|
|
||||||
}
|
|
||||||
else // inst.OPCD == 11
|
|
||||||
{
|
|
||||||
rhs = ibuild.EmitIntConst(inst.SIMM_16);
|
|
||||||
res = ibuild.EmitICmpCRSigned(lhs, rhs);
|
|
||||||
}
|
|
||||||
|
|
||||||
js.downcountAmount++; // TODO: should this be somewhere else?
|
|
||||||
|
|
||||||
ibuild.EmitStoreCR(res, inst.CRFD);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::boolX(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITIntegerOff);
|
|
||||||
|
|
||||||
IREmitter::InstLoc a = nullptr;
|
|
||||||
IREmitter::InstLoc s = ibuild.EmitLoadGReg(inst.RS);
|
|
||||||
IREmitter::InstLoc b = ibuild.EmitLoadGReg(inst.RB);
|
|
||||||
|
|
||||||
// FIXME: Some instructions does not work well in NSMBW, MP2, etc.
|
|
||||||
// Refer JitIL_Tables.cpp.
|
|
||||||
if (inst.SUBOP10 == 28) /* andx */
|
|
||||||
{
|
|
||||||
a = ibuild.EmitAnd(s, b);
|
|
||||||
}
|
|
||||||
else if (inst.SUBOP10 == 476) /* nandx */
|
|
||||||
{
|
|
||||||
a = ibuild.EmitNot(ibuild.EmitAnd(s, b));
|
|
||||||
}
|
|
||||||
else if (inst.SUBOP10 == 60) /* andcx */
|
|
||||||
{
|
|
||||||
a = ibuild.EmitAnd(s, ibuild.EmitNot(b));
|
|
||||||
}
|
|
||||||
else if (inst.SUBOP10 == 444) /* orx */
|
|
||||||
{
|
|
||||||
a = ibuild.EmitOr(s, b);
|
|
||||||
}
|
|
||||||
else if (inst.SUBOP10 == 124) /* norx */
|
|
||||||
{
|
|
||||||
a = ibuild.EmitNot(ibuild.EmitOr(s, b));
|
|
||||||
}
|
|
||||||
else if (inst.SUBOP10 == 412) /* orcx */
|
|
||||||
{
|
|
||||||
a = ibuild.EmitOr(s, ibuild.EmitNot(b));
|
|
||||||
}
|
|
||||||
else if (inst.SUBOP10 == 316) /* xorx */
|
|
||||||
{
|
|
||||||
a = ibuild.EmitXor(s, b);
|
|
||||||
}
|
|
||||||
else if (inst.SUBOP10 == 284) /* eqvx */
|
|
||||||
{
|
|
||||||
a = ibuild.EmitNot(ibuild.EmitXor(s, b));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
PanicAlert("WTF!");
|
|
||||||
}
|
|
||||||
|
|
||||||
ibuild.EmitStoreGReg(a, inst.RA);
|
|
||||||
if (inst.Rc)
|
|
||||||
ComputeRC(ibuild, a);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::extsbx(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITIntegerOff);
|
|
||||||
IREmitter::InstLoc val = ibuild.EmitLoadGReg(inst.RS);
|
|
||||||
val = ibuild.EmitSExt8(val);
|
|
||||||
ibuild.EmitStoreGReg(val, inst.RA);
|
|
||||||
if (inst.Rc)
|
|
||||||
ComputeRC(ibuild, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::extshx(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITIntegerOff);
|
|
||||||
IREmitter::InstLoc val = ibuild.EmitLoadGReg(inst.RS);
|
|
||||||
val = ibuild.EmitSExt16(val);
|
|
||||||
ibuild.EmitStoreGReg(val, inst.RA);
|
|
||||||
if (inst.Rc)
|
|
||||||
ComputeRC(ibuild, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::subfic(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITIntegerOff);
|
|
||||||
IREmitter::InstLoc nota, lhs, val, test;
|
|
||||||
nota = ibuild.EmitXor(ibuild.EmitLoadGReg(inst.RA), ibuild.EmitIntConst(-1));
|
|
||||||
|
|
||||||
if (inst.SIMM_16 == -1)
|
|
||||||
{
|
|
||||||
val = nota;
|
|
||||||
test = ibuild.EmitIntConst(1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
lhs = ibuild.EmitIntConst(inst.SIMM_16 + 1);
|
|
||||||
val = ibuild.EmitAdd(nota, lhs);
|
|
||||||
test = ibuild.EmitICmpUgt(lhs, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
ibuild.EmitStoreGReg(val, inst.RD);
|
|
||||||
ibuild.EmitStoreCarry(test);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::subfcx(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITIntegerOff);
|
|
||||||
|
|
||||||
if (inst.OE)
|
|
||||||
PanicAlert("OE: subfcx");
|
|
||||||
|
|
||||||
IREmitter::InstLoc val, test, lhs, rhs;
|
|
||||||
lhs = ibuild.EmitLoadGReg(inst.RB);
|
|
||||||
rhs = ibuild.EmitLoadGReg(inst.RA);
|
|
||||||
val = ibuild.EmitSub(lhs, rhs);
|
|
||||||
ibuild.EmitStoreGReg(val, inst.RD);
|
|
||||||
test = ibuild.EmitICmpEq(rhs, ibuild.EmitIntConst(0));
|
|
||||||
test = ibuild.EmitOr(test, ibuild.EmitICmpUgt(lhs, val));
|
|
||||||
ibuild.EmitStoreCarry(test);
|
|
||||||
|
|
||||||
if (inst.Rc)
|
|
||||||
ComputeRC(ibuild, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::subfex(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITIntegerOff);
|
|
||||||
|
|
||||||
if (inst.OE)
|
|
||||||
PanicAlert("OE: subfex");
|
|
||||||
|
|
||||||
IREmitter::InstLoc val, test, lhs, rhs, carry;
|
|
||||||
rhs = ibuild.EmitLoadGReg(inst.RA);
|
|
||||||
carry = ibuild.EmitLoadCarry();
|
|
||||||
rhs = ibuild.EmitXor(rhs, ibuild.EmitIntConst(-1));
|
|
||||||
rhs = ibuild.EmitAdd(rhs, carry);
|
|
||||||
test = ibuild.EmitICmpEq(rhs, ibuild.EmitIntConst(0));
|
|
||||||
test = ibuild.EmitAnd(test, carry);
|
|
||||||
lhs = ibuild.EmitLoadGReg(inst.RB);
|
|
||||||
val = ibuild.EmitAdd(lhs, rhs);
|
|
||||||
ibuild.EmitStoreGReg(val, inst.RD);
|
|
||||||
test = ibuild.EmitOr(test, ibuild.EmitICmpUgt(lhs, val));
|
|
||||||
ibuild.EmitStoreCarry(test);
|
|
||||||
|
|
||||||
if (inst.Rc)
|
|
||||||
ComputeRC(ibuild, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::subfx(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITIntegerOff);
|
|
||||||
|
|
||||||
if (inst.OE)
|
|
||||||
PanicAlert("OE: subfx");
|
|
||||||
|
|
||||||
IREmitter::InstLoc val = ibuild.EmitLoadGReg(inst.RB);
|
|
||||||
val = ibuild.EmitSub(val, ibuild.EmitLoadGReg(inst.RA));
|
|
||||||
ibuild.EmitStoreGReg(val, inst.RD);
|
|
||||||
|
|
||||||
if (inst.Rc)
|
|
||||||
ComputeRC(ibuild, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::mulli(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITIntegerOff);
|
|
||||||
IREmitter::InstLoc val = ibuild.EmitLoadGReg(inst.RA);
|
|
||||||
val = ibuild.EmitMul(val, ibuild.EmitIntConst(inst.SIMM_16));
|
|
||||||
ibuild.EmitStoreGReg(val, inst.RD);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::mullwx(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITIntegerOff);
|
|
||||||
IREmitter::InstLoc val = ibuild.EmitLoadGReg(inst.RB);
|
|
||||||
val = ibuild.EmitMul(ibuild.EmitLoadGReg(inst.RA), val);
|
|
||||||
ibuild.EmitStoreGReg(val, inst.RD);
|
|
||||||
|
|
||||||
if (inst.Rc)
|
|
||||||
ComputeRC(ibuild, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::mulhwux(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITIntegerOff);
|
|
||||||
|
|
||||||
IREmitter::InstLoc a = ibuild.EmitLoadGReg(inst.RA);
|
|
||||||
IREmitter::InstLoc b = ibuild.EmitLoadGReg(inst.RB);
|
|
||||||
IREmitter::InstLoc d = ibuild.EmitMulHighUnsigned(a, b);
|
|
||||||
ibuild.EmitStoreGReg(d, inst.RD);
|
|
||||||
|
|
||||||
if (inst.Rc)
|
|
||||||
ComputeRC(ibuild, d);
|
|
||||||
}
|
|
||||||
|
|
||||||
// skipped some of the special handling in here - if we get crashes, let the interpreter handle this
|
|
||||||
// op
|
|
||||||
void JitILBase::divwux(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
// FIXME
|
|
||||||
FALLBACK_IF(true);
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
int a = inst.RA, b = inst.RB, d = inst.RD;
|
|
||||||
gpr.FlushLockX(RSCRATCH1);
|
|
||||||
gpr.Lock(a, b, d);
|
|
||||||
|
|
||||||
if (d != a && d != b)
|
|
||||||
{
|
|
||||||
gpr.LoadToX64(d, false, true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
gpr.LoadToX64(d, true, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
MOV(32, R(RSCRATCH), gpr.R(a));
|
|
||||||
XOR(32, R(RSCRATCH2), R(RSCRATCH));
|
|
||||||
gpr.KillImmediate(b);
|
|
||||||
DIV(32, gpr.R(b));
|
|
||||||
MOV(32, gpr.R(d), R(RSCRATCH));
|
|
||||||
gpr.UnlockAll();
|
|
||||||
gpr.UnlockAllX();
|
|
||||||
|
|
||||||
if (inst.Rc)
|
|
||||||
{
|
|
||||||
CALL((u8*)asm_routines.computeRc);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::addx(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITIntegerOff);
|
|
||||||
IREmitter::InstLoc val = ibuild.EmitLoadGReg(inst.RB);
|
|
||||||
val = ibuild.EmitAdd(ibuild.EmitLoadGReg(inst.RA), val);
|
|
||||||
ibuild.EmitStoreGReg(val, inst.RD);
|
|
||||||
|
|
||||||
if (inst.Rc)
|
|
||||||
ComputeRC(ibuild, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::addzex(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITIntegerOff);
|
|
||||||
IREmitter::InstLoc lhs = ibuild.EmitLoadGReg(inst.RA), val, newcarry;
|
|
||||||
val = ibuild.EmitAdd(lhs, ibuild.EmitLoadCarry());
|
|
||||||
ibuild.EmitStoreGReg(val, inst.RD);
|
|
||||||
newcarry = ibuild.EmitICmpUlt(val, lhs);
|
|
||||||
ibuild.EmitStoreCarry(newcarry);
|
|
||||||
|
|
||||||
if (inst.Rc)
|
|
||||||
ComputeRC(ibuild, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::addex(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITIntegerOff);
|
|
||||||
|
|
||||||
IREmitter::InstLoc a = ibuild.EmitLoadGReg(inst.RA);
|
|
||||||
IREmitter::InstLoc b = ibuild.EmitLoadGReg(inst.RB);
|
|
||||||
|
|
||||||
IREmitter::InstLoc ab = ibuild.EmitAdd(a, b);
|
|
||||||
IREmitter::InstLoc new_carry = ibuild.EmitICmpUlt(ab, a);
|
|
||||||
|
|
||||||
IREmitter::InstLoc previous_carry = ibuild.EmitLoadCarry();
|
|
||||||
IREmitter::InstLoc abc = ibuild.EmitAdd(ab, previous_carry);
|
|
||||||
new_carry = ibuild.EmitOr(new_carry, ibuild.EmitICmpUlt(abc, ab));
|
|
||||||
|
|
||||||
ibuild.EmitStoreGReg(abc, inst.RD);
|
|
||||||
ibuild.EmitStoreCarry(new_carry);
|
|
||||||
|
|
||||||
if (inst.OE)
|
|
||||||
PanicAlert("OE: addex");
|
|
||||||
|
|
||||||
if (inst.Rc)
|
|
||||||
ComputeRC(ibuild, abc);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::rlwinmx(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITIntegerOff);
|
|
||||||
unsigned mask = Helper_Mask(inst.MB, inst.ME);
|
|
||||||
IREmitter::InstLoc val = ibuild.EmitLoadGReg(inst.RS);
|
|
||||||
val = ibuild.EmitRol(val, ibuild.EmitIntConst(inst.SH));
|
|
||||||
val = ibuild.EmitAnd(val, ibuild.EmitIntConst(mask));
|
|
||||||
ibuild.EmitStoreGReg(val, inst.RA);
|
|
||||||
|
|
||||||
if (inst.Rc)
|
|
||||||
ComputeRC(ibuild, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::rlwimix(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITIntegerOff);
|
|
||||||
unsigned mask = Helper_Mask(inst.MB, inst.ME);
|
|
||||||
IREmitter::InstLoc val = ibuild.EmitLoadGReg(inst.RS);
|
|
||||||
val = ibuild.EmitRol(val, ibuild.EmitIntConst(inst.SH));
|
|
||||||
val = ibuild.EmitAnd(val, ibuild.EmitIntConst(mask));
|
|
||||||
IREmitter::InstLoc ival = ibuild.EmitLoadGReg(inst.RA);
|
|
||||||
ival = ibuild.EmitAnd(ival, ibuild.EmitIntConst(~mask));
|
|
||||||
val = ibuild.EmitOr(ival, val);
|
|
||||||
ibuild.EmitStoreGReg(val, inst.RA);
|
|
||||||
|
|
||||||
if (inst.Rc)
|
|
||||||
ComputeRC(ibuild, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::rlwnmx(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITIntegerOff);
|
|
||||||
unsigned int mask = Helper_Mask(inst.MB, inst.ME);
|
|
||||||
IREmitter::InstLoc val = ibuild.EmitLoadGReg(inst.RS);
|
|
||||||
val = ibuild.EmitRol(val, ibuild.EmitLoadGReg(inst.RB));
|
|
||||||
val = ibuild.EmitAnd(val, ibuild.EmitIntConst(mask));
|
|
||||||
ibuild.EmitStoreGReg(val, inst.RA);
|
|
||||||
|
|
||||||
if (inst.Rc)
|
|
||||||
ComputeRC(ibuild, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::negx(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITIntegerOff);
|
|
||||||
IREmitter::InstLoc val = ibuild.EmitLoadGReg(inst.RA);
|
|
||||||
val = ibuild.EmitSub(ibuild.EmitIntConst(0), val);
|
|
||||||
ibuild.EmitStoreGReg(val, inst.RD);
|
|
||||||
|
|
||||||
if (inst.Rc)
|
|
||||||
ComputeRC(ibuild, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::srwx(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITIntegerOff);
|
|
||||||
|
|
||||||
IREmitter::InstLoc val = ibuild.EmitLoadGReg(inst.RS);
|
|
||||||
IREmitter::InstLoc samt = ibuild.EmitLoadGReg(inst.RB);
|
|
||||||
IREmitter::InstLoc corr;
|
|
||||||
|
|
||||||
// FIXME: We can do better with a cmov
|
|
||||||
// FIXME: We can do better on 64-bit
|
|
||||||
val = ibuild.EmitShrl(val, samt);
|
|
||||||
corr = ibuild.EmitShl(samt, ibuild.EmitIntConst(26));
|
|
||||||
corr = ibuild.EmitSarl(corr, ibuild.EmitIntConst(31));
|
|
||||||
corr = ibuild.EmitXor(corr, ibuild.EmitIntConst(-1));
|
|
||||||
val = ibuild.EmitAnd(corr, val);
|
|
||||||
ibuild.EmitStoreGReg(val, inst.RA);
|
|
||||||
|
|
||||||
if (inst.Rc)
|
|
||||||
ComputeRC(ibuild, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::slwx(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITIntegerOff);
|
|
||||||
|
|
||||||
IREmitter::InstLoc val = ibuild.EmitLoadGReg(inst.RS);
|
|
||||||
IREmitter::InstLoc samt = ibuild.EmitLoadGReg(inst.RB);
|
|
||||||
IREmitter::InstLoc corr;
|
|
||||||
|
|
||||||
// FIXME: We can do better with a cmov
|
|
||||||
// FIXME: We can do better on 64-bit
|
|
||||||
val = ibuild.EmitShl(val, samt);
|
|
||||||
corr = ibuild.EmitShl(samt, ibuild.EmitIntConst(26));
|
|
||||||
corr = ibuild.EmitSarl(corr, ibuild.EmitIntConst(31));
|
|
||||||
corr = ibuild.EmitXor(corr, ibuild.EmitIntConst(-1));
|
|
||||||
val = ibuild.EmitAnd(corr, val);
|
|
||||||
ibuild.EmitStoreGReg(val, inst.RA);
|
|
||||||
|
|
||||||
if (inst.Rc)
|
|
||||||
ComputeRC(ibuild, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::srawx(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITIntegerOff);
|
|
||||||
// FIXME: We can do a lot better on 64-bit
|
|
||||||
IREmitter::InstLoc val, samt, mask, mask2, test;
|
|
||||||
val = ibuild.EmitLoadGReg(inst.RS);
|
|
||||||
samt = ibuild.EmitLoadGReg(inst.RB);
|
|
||||||
mask = ibuild.EmitIntConst(-1);
|
|
||||||
val = ibuild.EmitSarl(val, samt);
|
|
||||||
mask = ibuild.EmitShl(mask, samt);
|
|
||||||
samt = ibuild.EmitShl(samt, ibuild.EmitIntConst(26));
|
|
||||||
samt = ibuild.EmitSarl(samt, ibuild.EmitIntConst(31));
|
|
||||||
samt = ibuild.EmitAnd(samt, ibuild.EmitIntConst(31));
|
|
||||||
val = ibuild.EmitSarl(val, samt);
|
|
||||||
ibuild.EmitStoreGReg(val, inst.RA);
|
|
||||||
mask = ibuild.EmitShl(mask, samt);
|
|
||||||
mask2 = ibuild.EmitAnd(mask, ibuild.EmitIntConst(0x7FFFFFFF));
|
|
||||||
test = ibuild.EmitOr(val, mask2);
|
|
||||||
test = ibuild.EmitICmpUgt(test, mask);
|
|
||||||
ibuild.EmitStoreCarry(test);
|
|
||||||
|
|
||||||
if (inst.Rc)
|
|
||||||
ComputeRC(ibuild, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::srawix(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITIntegerOff);
|
|
||||||
// Shift right by two
|
|
||||||
IREmitter::InstLoc input = ibuild.EmitLoadGReg(inst.RS);
|
|
||||||
IREmitter::InstLoc output = ibuild.EmitSarl(input, ibuild.EmitIntConst(inst.SH));
|
|
||||||
ibuild.EmitStoreGReg(output, inst.RA);
|
|
||||||
// Check whether the input is negative and any bits got shifted out.
|
|
||||||
unsigned int mask = -1u << inst.SH;
|
|
||||||
IREmitter::InstLoc test = ibuild.EmitOr(input, ibuild.EmitIntConst(mask & 0x7FFFFFFF));
|
|
||||||
test = ibuild.EmitICmpUgt(test, ibuild.EmitIntConst(mask));
|
|
||||||
|
|
||||||
ibuild.EmitStoreCarry(test);
|
|
||||||
if (inst.Rc)
|
|
||||||
ComputeRC(ibuild, output);
|
|
||||||
}
|
|
||||||
|
|
||||||
// count leading zeroes
|
|
||||||
void JitILBase::cntlzwx(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITIntegerOff);
|
|
||||||
IREmitter::InstLoc val = ibuild.EmitLoadGReg(inst.RS);
|
|
||||||
val = ibuild.EmitCntlzw(val);
|
|
||||||
ibuild.EmitStoreGReg(val, inst.RA);
|
|
||||||
|
|
||||||
if (inst.Rc)
|
|
||||||
ComputeRC(ibuild, val);
|
|
||||||
}
|
|
|
@ -1,310 +0,0 @@
|
||||||
// Copyright 2008 Dolphin Emulator Project
|
|
||||||
// Licensed under GPLv2+
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#include "Core/PowerPC/JitILCommon/JitILBase.h"
|
|
||||||
#include "Common/Assert.h"
|
|
||||||
#include "Common/CommonTypes.h"
|
|
||||||
#include "Core/ConfigManager.h"
|
|
||||||
#include "Core/HW/CPU.h"
|
|
||||||
#include "Core/PowerPC/PowerPC.h"
|
|
||||||
|
|
||||||
void JitILBase::lhax(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITLoadStoreOff);
|
|
||||||
FALLBACK_IF(jo.memcheck);
|
|
||||||
|
|
||||||
IREmitter::InstLoc addr = ibuild.EmitLoadGReg(inst.RB);
|
|
||||||
if (inst.RA)
|
|
||||||
addr = ibuild.EmitAdd(addr, ibuild.EmitLoadGReg(inst.RA));
|
|
||||||
|
|
||||||
IREmitter::InstLoc val = ibuild.EmitLoad16(addr);
|
|
||||||
val = ibuild.EmitSExt16(val);
|
|
||||||
ibuild.EmitStoreGReg(val, inst.RD);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::lhaux(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITLoadStoreOff);
|
|
||||||
FALLBACK_IF(jo.memcheck);
|
|
||||||
|
|
||||||
IREmitter::InstLoc addr = ibuild.EmitLoadGReg(inst.RB);
|
|
||||||
addr = ibuild.EmitAdd(addr, ibuild.EmitLoadGReg(inst.RA));
|
|
||||||
|
|
||||||
IREmitter::InstLoc val = ibuild.EmitLoad16(addr);
|
|
||||||
val = ibuild.EmitSExt16(val);
|
|
||||||
ibuild.EmitStoreGReg(val, inst.RD);
|
|
||||||
ibuild.EmitStoreGReg(addr, inst.RA);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::lXz(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITLoadStoreOff);
|
|
||||||
FALLBACK_IF(jo.memcheck);
|
|
||||||
|
|
||||||
IREmitter::InstLoc addr = ibuild.EmitIntConst(inst.SIMM_16);
|
|
||||||
if (inst.RA)
|
|
||||||
addr = ibuild.EmitAdd(addr, ibuild.EmitLoadGReg(inst.RA));
|
|
||||||
if (inst.OPCD & 1)
|
|
||||||
ibuild.EmitStoreGReg(addr, inst.RA);
|
|
||||||
|
|
||||||
IREmitter::InstLoc val;
|
|
||||||
|
|
||||||
// Idle Skipping.
|
|
||||||
// TODO: This really should be done somewhere else. Either lower in the IR
|
|
||||||
// or higher in PPCAnalyst
|
|
||||||
// TODO: We shouldn't use debug reads here.
|
|
||||||
if (!CPU::IsStepping() && inst.OPCD == 32 && // Lwx
|
|
||||||
(inst.hex & 0xFFFF0000) == 0x800D0000 &&
|
|
||||||
(PowerPC::HostRead_U32(js.compilerPC + 4) == 0x28000000 ||
|
|
||||||
(SConfig::GetInstance().bWii && PowerPC::HostRead_U32(js.compilerPC + 4) == 0x2C000000)) &&
|
|
||||||
PowerPC::HostRead_U32(js.compilerPC + 8) == 0x4182fff8)
|
|
||||||
{
|
|
||||||
val = ibuild.EmitLoad32(addr);
|
|
||||||
ibuild.EmitIdleBranch(val, ibuild.EmitIntConst(js.compilerPC));
|
|
||||||
ibuild.EmitStoreGReg(val, inst.RD);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (inst.OPCD & ~0x1)
|
|
||||||
{
|
|
||||||
case 32: // lwz
|
|
||||||
val = ibuild.EmitLoad32(addr);
|
|
||||||
break;
|
|
||||||
case 40: // lhz
|
|
||||||
val = ibuild.EmitLoad16(addr);
|
|
||||||
break;
|
|
||||||
case 34: // lbz
|
|
||||||
val = ibuild.EmitLoad8(addr);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
PanicAlert("lXz: invalid access size");
|
|
||||||
val = nullptr;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
ibuild.EmitStoreGReg(val, inst.RD);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::lbzu(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITLoadStoreOff);
|
|
||||||
const IREmitter::InstLoc uAddress =
|
|
||||||
ibuild.EmitAdd(ibuild.EmitLoadGReg(inst.RA), ibuild.EmitIntConst((int)inst.SIMM_16));
|
|
||||||
const IREmitter::InstLoc temp = ibuild.EmitLoad8(uAddress);
|
|
||||||
ibuild.EmitStoreGReg(temp, inst.RD);
|
|
||||||
ibuild.EmitStoreGReg(uAddress, inst.RA);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::lha(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITLoadStoreOff);
|
|
||||||
FALLBACK_IF(jo.memcheck);
|
|
||||||
|
|
||||||
IREmitter::InstLoc addr = ibuild.EmitIntConst((s32)(s16)inst.SIMM_16);
|
|
||||||
|
|
||||||
if (inst.RA)
|
|
||||||
addr = ibuild.EmitAdd(addr, ibuild.EmitLoadGReg(inst.RA));
|
|
||||||
|
|
||||||
IREmitter::InstLoc val = ibuild.EmitLoad16(addr);
|
|
||||||
val = ibuild.EmitSExt16(val);
|
|
||||||
ibuild.EmitStoreGReg(val, inst.RD);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::lhau(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITLoadStoreOff);
|
|
||||||
FALLBACK_IF(jo.memcheck);
|
|
||||||
|
|
||||||
IREmitter::InstLoc addr = ibuild.EmitIntConst((s32)inst.SIMM_16);
|
|
||||||
|
|
||||||
addr = ibuild.EmitAdd(addr, ibuild.EmitLoadGReg(inst.RA));
|
|
||||||
|
|
||||||
IREmitter::InstLoc val = ibuild.EmitLoad16(addr);
|
|
||||||
val = ibuild.EmitSExt16(val);
|
|
||||||
ibuild.EmitStoreGReg(val, inst.RD);
|
|
||||||
ibuild.EmitStoreGReg(addr, inst.RA);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::lXzx(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITLoadStoreOff);
|
|
||||||
FALLBACK_IF(jo.memcheck);
|
|
||||||
|
|
||||||
IREmitter::InstLoc addr = ibuild.EmitLoadGReg(inst.RB);
|
|
||||||
|
|
||||||
if (inst.RA)
|
|
||||||
{
|
|
||||||
addr = ibuild.EmitAdd(addr, ibuild.EmitLoadGReg(inst.RA));
|
|
||||||
if (inst.SUBOP10 & 32)
|
|
||||||
ibuild.EmitStoreGReg(addr, inst.RA);
|
|
||||||
}
|
|
||||||
|
|
||||||
IREmitter::InstLoc val;
|
|
||||||
switch (inst.SUBOP10 & ~32)
|
|
||||||
{
|
|
||||||
default:
|
|
||||||
PanicAlert("lXzx: invalid access size");
|
|
||||||
case 23: // lwzx
|
|
||||||
val = ibuild.EmitLoad32(addr);
|
|
||||||
break;
|
|
||||||
case 279: // lhzx
|
|
||||||
val = ibuild.EmitLoad16(addr);
|
|
||||||
break;
|
|
||||||
case 87: // lbzx
|
|
||||||
val = ibuild.EmitLoad8(addr);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
ibuild.EmitStoreGReg(val, inst.RD);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::dcbst(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITLoadStoreOff);
|
|
||||||
|
|
||||||
// If the dcbst instruction is preceded by dcbt, it is flushing a prefetched
|
|
||||||
// memory location. Do not invalidate the JIT cache in this case as the memory
|
|
||||||
// will be the same.
|
|
||||||
// dcbt = 0x7c00022c
|
|
||||||
// TODO: We shouldn't use a debug read here; it should be possible to get the
|
|
||||||
// previous instruction from the JIT state.
|
|
||||||
FALLBACK_IF((PowerPC::HostRead_U32(js.compilerPC - 4) & 0x7c00022c) != 0x7c00022c);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Zero cache line.
|
|
||||||
void JitILBase::dcbz(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
FALLBACK_IF(true);
|
|
||||||
|
|
||||||
// TODO!
|
|
||||||
#if 0
|
|
||||||
if (SConfig::GetInstance().bJITOff || SConfig::GetInstance().bJITLoadStoreOff)
|
|
||||||
{
|
|
||||||
Default(inst);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
INSTRUCTION_START;
|
|
||||||
MOV(32, R(RSCRATCH), gpr.R(inst.RB));
|
|
||||||
if (inst.RA)
|
|
||||||
ADD(32, R(RSCRATCH), gpr.R(inst.RA));
|
|
||||||
AND(32, R(RSCRATCH), Imm32(~31));
|
|
||||||
PXOR(XMM0, R(XMM0));
|
|
||||||
MOVAPS(MComplex(RMEM, RSCRATCH, SCALE_1, 0), XMM0);
|
|
||||||
MOVAPS(MComplex(RMEM, RSCRATCH, SCALE_1, 16), XMM0);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::stX(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITLoadStoreOff);
|
|
||||||
FALLBACK_IF(jo.memcheck);
|
|
||||||
|
|
||||||
IREmitter::InstLoc addr = ibuild.EmitIntConst(inst.SIMM_16);
|
|
||||||
IREmitter::InstLoc value = ibuild.EmitLoadGReg(inst.RS);
|
|
||||||
|
|
||||||
if (inst.RA)
|
|
||||||
addr = ibuild.EmitAdd(ibuild.EmitLoadGReg(inst.RA), addr);
|
|
||||||
if (inst.OPCD & 1)
|
|
||||||
ibuild.EmitStoreGReg(addr, inst.RA);
|
|
||||||
|
|
||||||
switch (inst.OPCD & ~1)
|
|
||||||
{
|
|
||||||
case 36: // stw
|
|
||||||
ibuild.EmitStore32(value, addr);
|
|
||||||
break;
|
|
||||||
case 44: // sth
|
|
||||||
ibuild.EmitStore16(value, addr);
|
|
||||||
break;
|
|
||||||
case 38: // stb
|
|
||||||
ibuild.EmitStore8(value, addr);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
_assert_msg_(DYNA_REC, 0, "stX: Invalid access size.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::stXx(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITLoadStoreOff);
|
|
||||||
FALLBACK_IF(jo.memcheck);
|
|
||||||
|
|
||||||
IREmitter::InstLoc addr = ibuild.EmitLoadGReg(inst.RB);
|
|
||||||
IREmitter::InstLoc value = ibuild.EmitLoadGReg(inst.RS);
|
|
||||||
|
|
||||||
addr = ibuild.EmitAdd(addr, ibuild.EmitLoadGReg(inst.RA));
|
|
||||||
|
|
||||||
if (inst.SUBOP10 & 32)
|
|
||||||
ibuild.EmitStoreGReg(addr, inst.RA);
|
|
||||||
|
|
||||||
switch (inst.SUBOP10 & ~32)
|
|
||||||
{
|
|
||||||
case 151: // stw
|
|
||||||
ibuild.EmitStore32(value, addr);
|
|
||||||
break;
|
|
||||||
case 407: // sth
|
|
||||||
ibuild.EmitStore16(value, addr);
|
|
||||||
break;
|
|
||||||
case 215: // stb
|
|
||||||
ibuild.EmitStore8(value, addr);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
_assert_msg_(DYNA_REC, 0, "stXx: Invalid store size.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// A few games use these heavily in video codecs. (GFZP01 @ 0x80020E18)
|
|
||||||
void JitILBase::lmw(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITLoadStoreOff);
|
|
||||||
FALLBACK_IF(jo.memcheck);
|
|
||||||
|
|
||||||
IREmitter::InstLoc addr = ibuild.EmitIntConst(inst.SIMM_16);
|
|
||||||
|
|
||||||
if (inst.RA)
|
|
||||||
addr = ibuild.EmitAdd(addr, ibuild.EmitLoadGReg(inst.RA));
|
|
||||||
|
|
||||||
for (int i = inst.RD; i < 32; i++)
|
|
||||||
{
|
|
||||||
IREmitter::InstLoc val = ibuild.EmitLoad32(addr);
|
|
||||||
ibuild.EmitStoreGReg(val, i);
|
|
||||||
addr = ibuild.EmitAdd(addr, ibuild.EmitIntConst(4));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::stmw(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITLoadStoreOff);
|
|
||||||
FALLBACK_IF(jo.memcheck);
|
|
||||||
|
|
||||||
IREmitter::InstLoc addr = ibuild.EmitIntConst(inst.SIMM_16);
|
|
||||||
|
|
||||||
if (inst.RA)
|
|
||||||
addr = ibuild.EmitAdd(addr, ibuild.EmitLoadGReg(inst.RA));
|
|
||||||
|
|
||||||
for (int i = inst.RD; i < 32; i++)
|
|
||||||
{
|
|
||||||
IREmitter::InstLoc val = ibuild.EmitLoadGReg(i);
|
|
||||||
ibuild.EmitStore32(val, addr);
|
|
||||||
addr = ibuild.EmitAdd(addr, ibuild.EmitIntConst(4));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::icbi(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
FallBackToInterpreter(inst);
|
|
||||||
ibuild.EmitBranchUncond(ibuild.EmitIntConst(js.compilerPC + 4));
|
|
||||||
}
|
|
|
@ -1,139 +0,0 @@
|
||||||
// Copyright 2008 Dolphin Emulator Project
|
|
||||||
// Licensed under GPLv2+
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#include "Core/PowerPC/JitILCommon/JitILBase.h"
|
|
||||||
#include "Common/CommonTypes.h"
|
|
||||||
|
|
||||||
// TODO: Add peephole optimizations for multiple consecutive lfd/lfs/stfd/stfs since they are so
|
|
||||||
// common,
|
|
||||||
// and pshufb could help a lot.
|
|
||||||
// Also add hacks for things like lfs/stfs the same reg consecutively, that is, simple memory moves.
|
|
||||||
|
|
||||||
void JitILBase::lfs(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITLoadStoreFloatingOff);
|
|
||||||
FALLBACK_IF(jo.memcheck);
|
|
||||||
|
|
||||||
IREmitter::InstLoc addr = ibuild.EmitIntConst(inst.SIMM_16);
|
|
||||||
|
|
||||||
if (inst.RA)
|
|
||||||
addr = ibuild.EmitAdd(addr, ibuild.EmitLoadGReg(inst.RA));
|
|
||||||
|
|
||||||
IREmitter::InstLoc val = ibuild.EmitDupSingleToMReg(ibuild.EmitLoadSingle(addr));
|
|
||||||
ibuild.EmitStoreFReg(val, inst.FD);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::lfsu(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITLoadStoreFloatingOff);
|
|
||||||
FALLBACK_IF(jo.memcheck);
|
|
||||||
|
|
||||||
IREmitter::InstLoc addr = ibuild.EmitIntConst(inst.SIMM_16);
|
|
||||||
|
|
||||||
addr = ibuild.EmitAdd(addr, ibuild.EmitLoadGReg(inst.RA));
|
|
||||||
|
|
||||||
IREmitter::InstLoc val = ibuild.EmitDupSingleToMReg(ibuild.EmitLoadSingle(addr));
|
|
||||||
ibuild.EmitStoreFReg(val, inst.FD);
|
|
||||||
ibuild.EmitStoreGReg(addr, inst.RA);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::lfd(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITLoadStoreFloatingOff);
|
|
||||||
FALLBACK_IF(jo.memcheck);
|
|
||||||
|
|
||||||
IREmitter::InstLoc addr = ibuild.EmitIntConst(inst.SIMM_16);
|
|
||||||
|
|
||||||
if (inst.RA)
|
|
||||||
addr = ibuild.EmitAdd(addr, ibuild.EmitLoadGReg(inst.RA));
|
|
||||||
|
|
||||||
IREmitter::InstLoc val = ibuild.EmitLoadFReg(inst.RD);
|
|
||||||
val = ibuild.EmitInsertDoubleInMReg(ibuild.EmitLoadDouble(addr), val);
|
|
||||||
ibuild.EmitStoreFReg(val, inst.RD);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::lfdu(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITLoadStoreFloatingOff);
|
|
||||||
FALLBACK_IF(jo.memcheck);
|
|
||||||
|
|
||||||
IREmitter::InstLoc addr = ibuild.EmitIntConst(inst.SIMM_16);
|
|
||||||
|
|
||||||
addr = ibuild.EmitAdd(addr, ibuild.EmitLoadGReg(inst.RA));
|
|
||||||
|
|
||||||
IREmitter::InstLoc val = ibuild.EmitLoadFReg(inst.FD);
|
|
||||||
val = ibuild.EmitInsertDoubleInMReg(ibuild.EmitLoadDouble(addr), val);
|
|
||||||
ibuild.EmitStoreFReg(val, inst.FD);
|
|
||||||
ibuild.EmitStoreGReg(addr, inst.RA);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::stfd(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITLoadStoreFloatingOff);
|
|
||||||
FALLBACK_IF(jo.memcheck);
|
|
||||||
|
|
||||||
IREmitter::InstLoc addr = ibuild.EmitIntConst(inst.SIMM_16);
|
|
||||||
IREmitter::InstLoc val = ibuild.EmitLoadFReg(inst.RS);
|
|
||||||
|
|
||||||
if (inst.RA)
|
|
||||||
addr = ibuild.EmitAdd(addr, ibuild.EmitLoadGReg(inst.RA));
|
|
||||||
if (inst.OPCD & 1)
|
|
||||||
ibuild.EmitStoreGReg(addr, inst.RA);
|
|
||||||
|
|
||||||
ibuild.EmitStoreDouble(val, addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::stfs(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITLoadStoreFloatingOff);
|
|
||||||
FALLBACK_IF(jo.memcheck);
|
|
||||||
|
|
||||||
IREmitter::InstLoc addr = ibuild.EmitIntConst(inst.SIMM_16);
|
|
||||||
IREmitter::InstLoc val = ibuild.EmitLoadFReg(inst.RS);
|
|
||||||
|
|
||||||
if (inst.RA)
|
|
||||||
addr = ibuild.EmitAdd(addr, ibuild.EmitLoadGReg(inst.RA));
|
|
||||||
if (inst.OPCD & 1)
|
|
||||||
ibuild.EmitStoreGReg(addr, inst.RA);
|
|
||||||
|
|
||||||
val = ibuild.EmitDoubleToSingle(val);
|
|
||||||
ibuild.EmitStoreSingle(val, addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::stfsx(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITLoadStoreFloatingOff);
|
|
||||||
FALLBACK_IF(jo.memcheck);
|
|
||||||
|
|
||||||
IREmitter::InstLoc addr = ibuild.EmitLoadGReg(inst.RB);
|
|
||||||
IREmitter::InstLoc val = ibuild.EmitLoadFReg(inst.RS);
|
|
||||||
|
|
||||||
if (inst.RA)
|
|
||||||
addr = ibuild.EmitAdd(addr, ibuild.EmitLoadGReg(inst.RA));
|
|
||||||
|
|
||||||
val = ibuild.EmitDoubleToSingle(val);
|
|
||||||
ibuild.EmitStoreSingle(val, addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::lfsx(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITLoadStoreFloatingOff);
|
|
||||||
FALLBACK_IF(jo.memcheck);
|
|
||||||
|
|
||||||
IREmitter::InstLoc addr = ibuild.EmitLoadGReg(inst.RB), val;
|
|
||||||
|
|
||||||
if (inst.RA)
|
|
||||||
addr = ibuild.EmitAdd(addr, ibuild.EmitLoadGReg(inst.RA));
|
|
||||||
|
|
||||||
val = ibuild.EmitDupSingleToMReg(ibuild.EmitLoadSingle(addr));
|
|
||||||
ibuild.EmitStoreFReg(val, inst.RD);
|
|
||||||
}
|
|
|
@ -1,56 +0,0 @@
|
||||||
// Copyright 2008 Dolphin Emulator Project
|
|
||||||
// Licensed under GPLv2+
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#include "Core/PowerPC/JitILCommon/JitILBase.h"
|
|
||||||
|
|
||||||
#include "Common/CommonTypes.h"
|
|
||||||
#include "Core/PowerPC/PowerPC.h"
|
|
||||||
|
|
||||||
void JitILBase::psq_st(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITLoadStorePairedOff);
|
|
||||||
FALLBACK_IF(jo.memcheck || inst.W);
|
|
||||||
|
|
||||||
// For performance, the AsmCommon routines assume address translation is on.
|
|
||||||
FALLBACK_IF(!UReg_MSR(MSR).DR);
|
|
||||||
|
|
||||||
IREmitter::InstLoc addr = ibuild.EmitIntConst(inst.SIMM_12);
|
|
||||||
IREmitter::InstLoc val;
|
|
||||||
|
|
||||||
if (inst.RA)
|
|
||||||
addr = ibuild.EmitAdd(addr, ibuild.EmitLoadGReg(inst.RA));
|
|
||||||
|
|
||||||
if (inst.OPCD == 61)
|
|
||||||
ibuild.EmitStoreGReg(addr, inst.RA);
|
|
||||||
|
|
||||||
val = ibuild.EmitLoadFReg(inst.RS);
|
|
||||||
val = ibuild.EmitCompactMRegToPacked(val);
|
|
||||||
ibuild.EmitStorePaired(val, addr, inst.I);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::psq_l(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITLoadStorePairedOff);
|
|
||||||
FALLBACK_IF(jo.memcheck || inst.W);
|
|
||||||
|
|
||||||
// For performance, the AsmCommon routines assume address translation is on.
|
|
||||||
FALLBACK_IF(!UReg_MSR(MSR).DR);
|
|
||||||
|
|
||||||
IREmitter::InstLoc addr = ibuild.EmitIntConst(inst.SIMM_12);
|
|
||||||
IREmitter::InstLoc val;
|
|
||||||
|
|
||||||
if (inst.RA)
|
|
||||||
addr = ibuild.EmitAdd(addr, ibuild.EmitLoadGReg(inst.RA));
|
|
||||||
|
|
||||||
if (inst.OPCD == 57)
|
|
||||||
ibuild.EmitStoreGReg(addr, inst.RA);
|
|
||||||
|
|
||||||
val = ibuild.EmitLoadPaired(
|
|
||||||
addr,
|
|
||||||
inst.I | (inst.W << 3)); // The lower 3 bits is for GQR index. The next 1 bit is for inst.W
|
|
||||||
val = ibuild.EmitExpandPackedToMReg(val);
|
|
||||||
ibuild.EmitStoreFReg(val, inst.RD);
|
|
||||||
}
|
|
|
@ -1,186 +0,0 @@
|
||||||
// Copyright 2008 Dolphin Emulator Project
|
|
||||||
// Licensed under GPLv2+
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#include "Core/PowerPC/JitILCommon/JitILBase.h"
|
|
||||||
#include "Common/Assert.h"
|
|
||||||
#include "Common/CommonTypes.h"
|
|
||||||
|
|
||||||
void JitILBase::ps_arith(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITPairedOff);
|
|
||||||
FALLBACK_IF(inst.Rc || (inst.SUBOP5 != 21 && inst.SUBOP5 != 20 && inst.SUBOP5 != 25));
|
|
||||||
|
|
||||||
IREmitter::InstLoc val = ibuild.EmitLoadFReg(inst.FA);
|
|
||||||
IREmitter::InstLoc rhs;
|
|
||||||
|
|
||||||
if (inst.SUBOP5 == 25)
|
|
||||||
rhs = ibuild.EmitCompactMRegToPacked(ibuild.EmitLoadFReg(inst.FC));
|
|
||||||
else
|
|
||||||
rhs = ibuild.EmitCompactMRegToPacked(ibuild.EmitLoadFReg(inst.FB));
|
|
||||||
|
|
||||||
val = ibuild.EmitCompactMRegToPacked(val);
|
|
||||||
|
|
||||||
switch (inst.SUBOP5)
|
|
||||||
{
|
|
||||||
case 20:
|
|
||||||
val = ibuild.EmitFPSub(val, rhs);
|
|
||||||
break;
|
|
||||||
case 21:
|
|
||||||
val = ibuild.EmitFPAdd(val, rhs);
|
|
||||||
break;
|
|
||||||
case 25:
|
|
||||||
val = ibuild.EmitFPMul(val, rhs);
|
|
||||||
}
|
|
||||||
|
|
||||||
val = ibuild.EmitExpandPackedToMReg(val);
|
|
||||||
ibuild.EmitStoreFReg(val, inst.FD);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::ps_sum(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
// TODO: This operation strikes me as a bit strange...
|
|
||||||
// perhaps we can optimize it depending on the users?
|
|
||||||
// TODO: ps_sum breaks Sonic Colours (black screen)
|
|
||||||
FALLBACK_IF(true);
|
|
||||||
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITPairedOff);
|
|
||||||
FALLBACK_IF(inst.Rc || inst.SUBOP5 != 10);
|
|
||||||
|
|
||||||
IREmitter::InstLoc val = ibuild.EmitLoadFReg(inst.FA);
|
|
||||||
IREmitter::InstLoc temp;
|
|
||||||
|
|
||||||
val = ibuild.EmitCompactMRegToPacked(val);
|
|
||||||
val = ibuild.EmitFPDup0(val);
|
|
||||||
temp = ibuild.EmitCompactMRegToPacked(ibuild.EmitLoadFReg(inst.FB));
|
|
||||||
val = ibuild.EmitFPAdd(val, temp);
|
|
||||||
temp = ibuild.EmitCompactMRegToPacked(ibuild.EmitLoadFReg(inst.FC));
|
|
||||||
val = ibuild.EmitFPMerge11(val, temp);
|
|
||||||
val = ibuild.EmitExpandPackedToMReg(val);
|
|
||||||
ibuild.EmitStoreFReg(val, inst.FD);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::ps_muls(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITPairedOff);
|
|
||||||
FALLBACK_IF(inst.Rc);
|
|
||||||
|
|
||||||
IREmitter::InstLoc val = ibuild.EmitLoadFReg(inst.FA);
|
|
||||||
IREmitter::InstLoc rhs = ibuild.EmitLoadFReg(inst.FC);
|
|
||||||
|
|
||||||
val = ibuild.EmitCompactMRegToPacked(val);
|
|
||||||
rhs = ibuild.EmitCompactMRegToPacked(rhs);
|
|
||||||
|
|
||||||
if (inst.SUBOP5 == 12)
|
|
||||||
rhs = ibuild.EmitFPDup0(rhs);
|
|
||||||
else
|
|
||||||
rhs = ibuild.EmitFPDup1(rhs);
|
|
||||||
|
|
||||||
val = ibuild.EmitFPMul(val, rhs);
|
|
||||||
val = ibuild.EmitExpandPackedToMReg(val);
|
|
||||||
ibuild.EmitStoreFReg(val, inst.FD);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: find easy cases and optimize them, do a breakout like ps_arith
|
|
||||||
void JitILBase::ps_mergeXX(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITPairedOff);
|
|
||||||
FALLBACK_IF(inst.Rc);
|
|
||||||
|
|
||||||
IREmitter::InstLoc val = ibuild.EmitCompactMRegToPacked(ibuild.EmitLoadFReg(inst.FA));
|
|
||||||
IREmitter::InstLoc rhs = ibuild.EmitCompactMRegToPacked(ibuild.EmitLoadFReg(inst.FB));
|
|
||||||
|
|
||||||
switch (inst.SUBOP10)
|
|
||||||
{
|
|
||||||
case 528:
|
|
||||||
val = ibuild.EmitFPMerge00(val, rhs);
|
|
||||||
break; // 00
|
|
||||||
case 560:
|
|
||||||
val = ibuild.EmitFPMerge01(val, rhs);
|
|
||||||
break; // 01
|
|
||||||
case 592:
|
|
||||||
val = ibuild.EmitFPMerge10(val, rhs);
|
|
||||||
break; // 10
|
|
||||||
case 624:
|
|
||||||
val = ibuild.EmitFPMerge11(val, rhs);
|
|
||||||
break; // 11
|
|
||||||
default:
|
|
||||||
_assert_msg_(DYNA_REC, 0, "ps_merge - invalid op");
|
|
||||||
}
|
|
||||||
|
|
||||||
val = ibuild.EmitExpandPackedToMReg(val);
|
|
||||||
ibuild.EmitStoreFReg(val, inst.FD);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::ps_maddXX(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITPairedOff);
|
|
||||||
FALLBACK_IF(inst.Rc);
|
|
||||||
|
|
||||||
IREmitter::InstLoc val = ibuild.EmitLoadFReg(inst.FA), op2, op3;
|
|
||||||
val = ibuild.EmitCompactMRegToPacked(val);
|
|
||||||
|
|
||||||
switch (inst.SUBOP5)
|
|
||||||
{
|
|
||||||
case 14: // madds0
|
|
||||||
{
|
|
||||||
op2 = ibuild.EmitCompactMRegToPacked(ibuild.EmitLoadFReg(inst.FC));
|
|
||||||
op2 = ibuild.EmitFPDup0(op2);
|
|
||||||
val = ibuild.EmitFPMul(val, op2);
|
|
||||||
op3 = ibuild.EmitCompactMRegToPacked(ibuild.EmitLoadFReg(inst.FB));
|
|
||||||
val = ibuild.EmitFPAdd(val, op3);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 15: // madds1
|
|
||||||
{
|
|
||||||
op2 = ibuild.EmitCompactMRegToPacked(ibuild.EmitLoadFReg(inst.FC));
|
|
||||||
op2 = ibuild.EmitFPDup1(op2);
|
|
||||||
val = ibuild.EmitFPMul(val, op2);
|
|
||||||
op3 = ibuild.EmitCompactMRegToPacked(ibuild.EmitLoadFReg(inst.FB));
|
|
||||||
val = ibuild.EmitFPAdd(val, op3);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 28: // msub
|
|
||||||
{
|
|
||||||
op2 = ibuild.EmitCompactMRegToPacked(ibuild.EmitLoadFReg(inst.FC));
|
|
||||||
val = ibuild.EmitFPMul(val, op2);
|
|
||||||
op3 = ibuild.EmitCompactMRegToPacked(ibuild.EmitLoadFReg(inst.FB));
|
|
||||||
val = ibuild.EmitFPSub(val, op3);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 29: // madd
|
|
||||||
{
|
|
||||||
op2 = ibuild.EmitCompactMRegToPacked(ibuild.EmitLoadFReg(inst.FC));
|
|
||||||
val = ibuild.EmitFPMul(val, op2);
|
|
||||||
op3 = ibuild.EmitCompactMRegToPacked(ibuild.EmitLoadFReg(inst.FB));
|
|
||||||
val = ibuild.EmitFPAdd(val, op3);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 30: // nmsub
|
|
||||||
{
|
|
||||||
op2 = ibuild.EmitCompactMRegToPacked(ibuild.EmitLoadFReg(inst.FC));
|
|
||||||
val = ibuild.EmitFPMul(val, op2);
|
|
||||||
op3 = ibuild.EmitCompactMRegToPacked(ibuild.EmitLoadFReg(inst.FB));
|
|
||||||
val = ibuild.EmitFPSub(val, op3);
|
|
||||||
val = ibuild.EmitFPNeg(val);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 31: // nmadd
|
|
||||||
{
|
|
||||||
op2 = ibuild.EmitCompactMRegToPacked(ibuild.EmitLoadFReg(inst.FC));
|
|
||||||
val = ibuild.EmitFPMul(val, op2);
|
|
||||||
op3 = ibuild.EmitCompactMRegToPacked(ibuild.EmitLoadFReg(inst.FB));
|
|
||||||
val = ibuild.EmitFPAdd(val, op3);
|
|
||||||
val = ibuild.EmitFPNeg(val);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val = ibuild.EmitExpandPackedToMReg(val);
|
|
||||||
ibuild.EmitStoreFReg(val, inst.FD);
|
|
||||||
}
|
|
|
@ -1,215 +0,0 @@
|
||||||
// Copyright 2008 Dolphin Emulator Project
|
|
||||||
// Licensed under GPLv2+
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#include "Core/PowerPC/JitILCommon/JitILBase.h"
|
|
||||||
#include "Common/CommonTypes.h"
|
|
||||||
#include "Common/MsgHandler.h"
|
|
||||||
|
|
||||||
void JitILBase::mtspr(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITSystemRegistersOff);
|
|
||||||
u32 iIndex = (inst.SPRU << 5) | (inst.SPRL & 0x1F);
|
|
||||||
|
|
||||||
switch (iIndex)
|
|
||||||
{
|
|
||||||
case SPR_TL:
|
|
||||||
case SPR_TU:
|
|
||||||
FALLBACK_IF(true);
|
|
||||||
case SPR_LR:
|
|
||||||
ibuild.EmitStoreLink(ibuild.EmitLoadGReg(inst.RD));
|
|
||||||
return;
|
|
||||||
case SPR_CTR:
|
|
||||||
ibuild.EmitStoreCTR(ibuild.EmitLoadGReg(inst.RD));
|
|
||||||
return;
|
|
||||||
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:
|
|
||||||
ibuild.EmitStoreGQR(ibuild.EmitLoadGReg(inst.RD), iIndex - SPR_GQR0);
|
|
||||||
return;
|
|
||||||
case SPR_SRR0:
|
|
||||||
case SPR_SRR1:
|
|
||||||
ibuild.EmitStoreSRR(ibuild.EmitLoadGReg(inst.RD), iIndex - SPR_SRR0);
|
|
||||||
return;
|
|
||||||
default:
|
|
||||||
FALLBACK_IF(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::mfspr(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITSystemRegistersOff);
|
|
||||||
u32 iIndex = (inst.SPRU << 5) | (inst.SPRL & 0x1F);
|
|
||||||
switch (iIndex)
|
|
||||||
{
|
|
||||||
case SPR_TL:
|
|
||||||
case SPR_TU:
|
|
||||||
FALLBACK_IF(true);
|
|
||||||
case SPR_LR:
|
|
||||||
ibuild.EmitStoreGReg(ibuild.EmitLoadLink(), inst.RD);
|
|
||||||
return;
|
|
||||||
case SPR_CTR:
|
|
||||||
ibuild.EmitStoreGReg(ibuild.EmitLoadCTR(), inst.RD);
|
|
||||||
return;
|
|
||||||
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:
|
|
||||||
ibuild.EmitStoreGReg(ibuild.EmitLoadGQR(iIndex - SPR_GQR0), inst.RD);
|
|
||||||
return;
|
|
||||||
default:
|
|
||||||
FALLBACK_IF(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// =======================================================================================
|
|
||||||
// Don't interpret this, if we do we get thrown out
|
|
||||||
// --------------
|
|
||||||
void JitILBase::mtmsr(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
ibuild.EmitStoreMSR(ibuild.EmitLoadGReg(inst.RS), ibuild.EmitIntConst(js.compilerPC));
|
|
||||||
ibuild.EmitBranchUncond(ibuild.EmitIntConst(js.compilerPC + 4));
|
|
||||||
}
|
|
||||||
// ==============
|
|
||||||
|
|
||||||
void JitILBase::mfmsr(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITSystemRegistersOff);
|
|
||||||
ibuild.EmitStoreGReg(ibuild.EmitLoadMSR(), inst.RD);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::mftb(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START;
|
|
||||||
JITDISABLE(bJITSystemRegistersOff);
|
|
||||||
mfspr(inst);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::mfcr(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START;
|
|
||||||
JITDISABLE(bJITSystemRegistersOff);
|
|
||||||
|
|
||||||
IREmitter::InstLoc d = ibuild.EmitIntConst(0);
|
|
||||||
for (int i = 0; i < 8; ++i)
|
|
||||||
{
|
|
||||||
IREmitter::InstLoc cr = ibuild.EmitLoadCR(i);
|
|
||||||
cr = ibuild.EmitConvertFromFastCR(cr);
|
|
||||||
cr = ibuild.EmitShl(cr, ibuild.EmitIntConst(28 - 4 * i));
|
|
||||||
d = ibuild.EmitOr(d, cr);
|
|
||||||
}
|
|
||||||
ibuild.EmitStoreGReg(d, inst.RD);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::mtcrf(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START;
|
|
||||||
JITDISABLE(bJITSystemRegistersOff);
|
|
||||||
|
|
||||||
IREmitter::InstLoc s = ibuild.EmitLoadGReg(inst.RS);
|
|
||||||
for (int i = 0; i < 8; ++i)
|
|
||||||
{
|
|
||||||
if (inst.CRM & (0x80 >> i))
|
|
||||||
{
|
|
||||||
IREmitter::InstLoc value;
|
|
||||||
value = ibuild.EmitShrl(s, ibuild.EmitIntConst(28 - i * 4));
|
|
||||||
value = ibuild.EmitAnd(value, ibuild.EmitIntConst(0xF));
|
|
||||||
value = ibuild.EmitConvertToFastCR(value);
|
|
||||||
ibuild.EmitStoreCR(value, i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::mcrf(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITSystemRegistersOff);
|
|
||||||
|
|
||||||
if (inst.CRFS != inst.CRFD)
|
|
||||||
{
|
|
||||||
ibuild.EmitStoreCR(ibuild.EmitLoadCR(inst.CRFS), inst.CRFD);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void JitILBase::crXX(UGeckoInstruction inst)
|
|
||||||
{
|
|
||||||
INSTRUCTION_START
|
|
||||||
JITDISABLE(bJITSystemRegistersOff);
|
|
||||||
|
|
||||||
// Get bit CRBA in EAX aligned with bit CRBD
|
|
||||||
const int shiftA = (inst.CRBD & 3) - (inst.CRBA & 3);
|
|
||||||
IREmitter::InstLoc eax = ibuild.EmitLoadCR(inst.CRBA >> 2);
|
|
||||||
eax = ibuild.EmitConvertFromFastCR(eax);
|
|
||||||
if (shiftA < 0)
|
|
||||||
eax = ibuild.EmitShl(eax, ibuild.EmitIntConst(-shiftA));
|
|
||||||
else if (shiftA > 0)
|
|
||||||
eax = ibuild.EmitShrl(eax, ibuild.EmitIntConst(shiftA));
|
|
||||||
|
|
||||||
// Get bit CRBB in ECX aligned with bit CRBD
|
|
||||||
const int shiftB = (inst.CRBD & 3) - (inst.CRBB & 3);
|
|
||||||
IREmitter::InstLoc ecx = ibuild.EmitLoadCR(inst.CRBB >> 2);
|
|
||||||
ecx = ibuild.EmitConvertFromFastCR(ecx);
|
|
||||||
if (shiftB < 0)
|
|
||||||
ecx = ibuild.EmitShl(ecx, ibuild.EmitIntConst(-shiftB));
|
|
||||||
else if (shiftB > 0)
|
|
||||||
ecx = ibuild.EmitShrl(ecx, ibuild.EmitIntConst(shiftB));
|
|
||||||
|
|
||||||
// Compute combined bit
|
|
||||||
const unsigned subop = inst.SUBOP10;
|
|
||||||
switch (subop)
|
|
||||||
{
|
|
||||||
case 257: // crand
|
|
||||||
eax = ibuild.EmitAnd(eax, ecx);
|
|
||||||
break;
|
|
||||||
case 129: // crandc
|
|
||||||
ecx = ibuild.EmitNot(ecx);
|
|
||||||
eax = ibuild.EmitAnd(eax, ecx);
|
|
||||||
break;
|
|
||||||
case 289: // creqv
|
|
||||||
eax = ibuild.EmitXor(eax, ecx);
|
|
||||||
eax = ibuild.EmitNot(eax);
|
|
||||||
break;
|
|
||||||
case 225: // crnand
|
|
||||||
eax = ibuild.EmitAnd(eax, ecx);
|
|
||||||
eax = ibuild.EmitNot(eax);
|
|
||||||
break;
|
|
||||||
case 33: // crnor
|
|
||||||
eax = ibuild.EmitOr(eax, ecx);
|
|
||||||
eax = ibuild.EmitNot(eax);
|
|
||||||
break;
|
|
||||||
case 449: // cror
|
|
||||||
eax = ibuild.EmitOr(eax, ecx);
|
|
||||||
break;
|
|
||||||
case 417: // crorc
|
|
||||||
ecx = ibuild.EmitNot(ecx);
|
|
||||||
eax = ibuild.EmitOr(eax, ecx);
|
|
||||||
break;
|
|
||||||
case 193: // crxor
|
|
||||||
eax = ibuild.EmitXor(eax, ecx);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
PanicAlert("crXX: invalid instruction");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store result bit in CRBD
|
|
||||||
eax = ibuild.EmitAnd(eax, ibuild.EmitIntConst(0x8 >> (inst.CRBD & 3)));
|
|
||||||
IREmitter::InstLoc bd = ibuild.EmitLoadCR(inst.CRBD >> 2);
|
|
||||||
bd = ibuild.EmitConvertFromFastCR(bd);
|
|
||||||
bd = ibuild.EmitAnd(bd, ibuild.EmitIntConst(~(0x8 >> (inst.CRBD & 3))));
|
|
||||||
bd = ibuild.EmitOr(bd, eax);
|
|
||||||
bd = ibuild.EmitConvertToFastCR(bd);
|
|
||||||
ibuild.EmitStoreCR(bd, inst.CRBD >> 2);
|
|
||||||
}
|
|
|
@ -30,7 +30,6 @@
|
||||||
|
|
||||||
#if _M_X86
|
#if _M_X86
|
||||||
#include "Core/PowerPC/Jit64/Jit.h"
|
#include "Core/PowerPC/Jit64/Jit.h"
|
||||||
#include "Core/PowerPC/Jit64IL/JitIL.h"
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if _M_ARM_64
|
#if _M_ARM_64
|
||||||
|
@ -53,9 +52,6 @@ CPUCoreBase* InitJitCore(int core)
|
||||||
case PowerPC::CORE_JIT64:
|
case PowerPC::CORE_JIT64:
|
||||||
ptr = new Jit64();
|
ptr = new Jit64();
|
||||||
break;
|
break;
|
||||||
case PowerPC::CORE_JITIL64:
|
|
||||||
ptr = new JitIL();
|
|
||||||
break;
|
|
||||||
#endif
|
#endif
|
||||||
#if _M_ARM_64
|
#if _M_ARM_64
|
||||||
case PowerPC::CORE_JITARM64:
|
case PowerPC::CORE_JITARM64:
|
||||||
|
|
|
@ -186,7 +186,7 @@ const std::vector<CPUCore>& AvailableCPUCores()
|
||||||
static const std::vector<CPUCore> cpu_cores = {
|
static const std::vector<CPUCore> cpu_cores = {
|
||||||
CORE_INTERPRETER, CORE_CACHEDINTERPRETER,
|
CORE_INTERPRETER, CORE_CACHEDINTERPRETER,
|
||||||
#ifdef _M_X86_64
|
#ifdef _M_X86_64
|
||||||
CORE_JIT64, CORE_JITIL64,
|
CORE_JIT64,
|
||||||
#elif defined(_M_ARM_64)
|
#elif defined(_M_ARM_64)
|
||||||
CORE_JITARM64,
|
CORE_JITARM64,
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -21,14 +21,14 @@ class PointerWrap;
|
||||||
|
|
||||||
namespace PowerPC
|
namespace PowerPC
|
||||||
{
|
{
|
||||||
|
// The gaps in the CPUCore numbering are from cores that only existed in the past.
|
||||||
|
// We avoid re-numbering cores so that settings will be compatible across versions.
|
||||||
enum CPUCore
|
enum CPUCore
|
||||||
{
|
{
|
||||||
CORE_INTERPRETER,
|
CORE_INTERPRETER = 0,
|
||||||
CORE_JIT64,
|
CORE_JIT64 = 1,
|
||||||
CORE_JITIL64,
|
CORE_JITARM64 = 4,
|
||||||
CORE_JITARM,
|
CORE_CACHEDINTERPRETER = 5,
|
||||||
CORE_JITARM64,
|
|
||||||
CORE_CACHEDINTERPRETER,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class CoreMode
|
enum class CoreMode
|
||||||
|
|
|
@ -29,7 +29,6 @@ static const std::map<PowerPC::CPUCore, std::string> CPU_CORE_NAMES = {
|
||||||
{PowerPC::CORE_INTERPRETER, _trans("Interpreter (slowest)")},
|
{PowerPC::CORE_INTERPRETER, _trans("Interpreter (slowest)")},
|
||||||
{PowerPC::CORE_CACHEDINTERPRETER, _trans("Cached Interpreter (slower)")},
|
{PowerPC::CORE_CACHEDINTERPRETER, _trans("Cached Interpreter (slower)")},
|
||||||
{PowerPC::CORE_JIT64, _trans("JIT Recompiler (recommended)")},
|
{PowerPC::CORE_JIT64, _trans("JIT Recompiler (recommended)")},
|
||||||
{PowerPC::CORE_JITIL64, _trans("JITIL Recompiler (slow, experimental)")},
|
|
||||||
{PowerPC::CORE_JITARM64, _trans("JIT Arm64 (experimental)")},
|
{PowerPC::CORE_JITARM64, _trans("JIT Arm64 (experimental)")},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue