2012-11-01 16:19:01 +01:00
// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
2012-11-04 23:01:49 +01:00
// the Free Software Foundation, version 2.0 or later versions.
2012-11-01 16:19:01 +01:00
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
2013-05-26 20:30:14 -07:00
# include "Core/Reporting.h"
2013-08-15 01:35:17 -07:00
# include "Core/HLE/HLE.h"
# include "Core/HLE/HLETables.h"
# include "Core/Host.h"
2012-11-01 16:19:01 +01:00
2013-08-15 01:35:17 -07:00
# include "Core/MIPS/MIPS.h"
# include "Core/MIPS/MIPSCodeUtils.h"
# include "Core/MIPS/MIPSAnalyst.h"
# include "Core/MIPS/MIPSTables.h"
2012-11-01 16:19:01 +01:00
2013-08-15 01:35:17 -07:00
# include "Core/MIPS/x86/Jit.h"
# include "Core/MIPS/x86/RegCache.h"
2013-04-26 23:58:20 +02:00
# include "Core/MIPS/JitCommon/JitBlockCache.h"
2012-11-01 16:19:01 +01:00
# define _RS ((op>>21) & 0x1F)
# define _RT ((op>>16) & 0x1F)
# define _RD ((op>>11) & 0x1F)
# define _FS ((op>>11) & 0x1F)
# define _FT ((op>>16) & 0x1F)
# define _FD ((op>>6 ) & 0x1F)
# define _POS ((op>>6 ) & 0x1F)
# define _SIZE ((op>>11 ) & 0x1F)
# define LOOPOPTIMIZATION 0
using namespace MIPSAnalyst ;
2012-11-12 14:35:10 +01:00
// NOTE: Can't use CONDITIONAL_DISABLE in this file, branches are so special
// that they cannot be interpreted in the context of the Jit.
2013-01-20 18:48:54 -08:00
// But we can at least log and compare.
// #define DO_CONDITIONAL_LOG 1
# define DO_CONDITIONAL_LOG 0
2013-01-24 19:11:03 -08:00
// We can also disable nice delay slots.
// #define CONDITIONAL_NICE_DELAYSLOT delaySlotIsNice = false;
# define CONDITIONAL_NICE_DELAYSLOT ;
2013-01-20 18:48:54 -08:00
# if DO_CONDITIONAL_LOG
# define CONDITIONAL_LOG BranchLog(op);
# define CONDITIONAL_LOG_EXIT(addr) BranchLogExit(op, addr, false);
# define CONDITIONAL_LOG_EXIT_EAX() BranchLogExit(op, 0, true);
# else
# define CONDITIONAL_LOG ;
# define CONDITIONAL_LOG_EXIT(addr) ;
# define CONDITIONAL_LOG_EXIT_EAX() ;
# endif
2012-11-01 16:19:01 +01:00
namespace MIPSComp
{
2013-01-20 18:48:54 -08:00
static u32 intBranchExit ;
static u32 jitBranchExit ;
static void JitBranchLog ( u32 op , u32 pc )
{
currentMIPS - > pc = pc ;
currentMIPS - > inDelaySlot = false ;
MIPSInterpretFunc func = MIPSGetInterpretFunc ( op ) ;
u32 info = MIPSGetInfo ( op ) ;
func ( op ) ;
// Branch taken, use nextPC.
if ( currentMIPS - > inDelaySlot )
intBranchExit = currentMIPS - > nextPC ;
else
{
// Branch not taken, likely delay slot skipped.
if ( info & LIKELY )
intBranchExit = currentMIPS - > pc ;
// Branch not taken, so increment over delay slot.
else
intBranchExit = currentMIPS - > pc + 4 ;
}
currentMIPS - > pc = pc ;
currentMIPS - > inDelaySlot = false ;
}
2013-01-20 19:29:06 -08:00
static void JitBranchLogMismatch ( u32 op , u32 pc )
2013-01-20 18:48:54 -08:00
{
char temp [ 256 ] ;
MIPSDisAsm ( op , pc , temp , true ) ;
ERROR_LOG ( JIT , " Bad jump: %s - int:%08x jit:%08x " , temp , intBranchExit , jitBranchExit ) ;
host - > SetDebugMode ( true ) ;
}
void Jit : : BranchLog ( u32 op )
{
FlushAll ( ) ;
ABI_CallFunctionCC ( thunks . ProtectFunction ( ( void * ) & JitBranchLog , 2 ) , op , js . compilerPC ) ;
}
void Jit : : BranchLogExit ( u32 op , u32 dest , bool useEAX )
{
2013-01-20 19:29:06 -08:00
OpArg destArg = useEAX ? R ( EAX ) : Imm32 ( dest ) ;
2013-01-20 18:48:54 -08:00
2013-01-20 19:29:06 -08:00
CMP ( 32 , M ( ( void * ) & intBranchExit ) , destArg ) ;
FixupBranch skip = J_CC ( CC_E ) ;
2013-01-20 18:48:54 -08:00
2013-01-20 19:29:06 -08:00
MOV ( 32 , M ( ( void * ) & jitBranchExit ) , destArg ) ;
ABI_CallFunctionCC ( thunks . ProtectFunction ( ( void * ) & JitBranchLogMismatch , 2 ) , op , js . compilerPC ) ;
// Restore EAX, we probably ruined it.
2013-01-20 18:48:54 -08:00
if ( useEAX )
MOV ( 32 , R ( EAX ) , M ( ( void * ) & jitBranchExit ) ) ;
2013-01-20 19:29:06 -08:00
SetJumpTarget ( skip ) ;
2013-01-20 18:48:54 -08:00
}
2012-11-01 16:19:01 +01:00
void Jit : : BranchRSRTComp ( u32 op , Gen : : CCFlags cc , bool likely )
{
2013-01-20 18:48:54 -08:00
CONDITIONAL_LOG ;
2012-12-26 08:37:53 +01:00
if ( js . inDelaySlot ) {
2013-06-30 13:19:27 -07:00
ERROR_LOG_REPORT ( JIT , " Branch in RSRTComp delay slot at %08x in block starting at %08x " , js . compilerPC , js . blockStart ) ;
2012-12-26 08:37:53 +01:00
return ;
}
2012-11-01 16:19:01 +01:00
int offset = ( signed short ) ( op & 0xFFFF ) < < 2 ;
int rt = _RT ;
int rs = _RS ;
u32 targetAddr = js . compilerPC + offset + 4 ;
2013-02-02 11:46:35 -08:00
u32 delaySlotOp = Memory : : Read_Instruction ( js . compilerPC + 4 ) ;
2013-01-24 08:29:32 -08:00
bool delaySlotIsNice = IsDelaySlotNiceReg ( op , delaySlotOp , rt , rs ) ;
2013-01-24 19:11:03 -08:00
CONDITIONAL_NICE_DELAYSLOT ;
2013-01-24 01:56:47 -08:00
if ( ! likely & & delaySlotIsNice )
CompileDelaySlot ( DELAYSLOT_NICE ) ;
2013-01-08 19:30:28 +01:00
if ( rt = = 0 )
2012-11-01 16:19:01 +01:00
{
2013-02-21 00:00:30 -08:00
gpr . KillImmediate ( rs , true , false ) ;
2013-01-08 19:30:28 +01:00
CMP ( 32 , gpr . R ( rs ) , Imm32 ( 0 ) ) ;
2012-11-01 16:19:01 +01:00
}
else
{
gpr . BindToRegister ( rs , true , false ) ;
CMP ( 32 , gpr . R ( rs ) , rt = = 0 ? Imm32 ( 0 ) : gpr . R ( rt ) ) ;
}
Gen : : FixupBranch ptr ;
if ( ! likely )
{
2013-01-24 01:56:47 -08:00
if ( ! delaySlotIsNice )
CompileDelaySlot ( DELAYSLOT_SAFE_FLUSH ) ;
2013-01-30 20:01:26 +01:00
else
FlushAll ( ) ;
2012-11-01 16:19:01 +01:00
ptr = J_CC ( cc , true ) ;
}
else
{
2013-01-30 20:01:26 +01:00
FlushAll ( ) ;
2012-11-01 16:19:01 +01:00
ptr = J_CC ( cc , true ) ;
2013-01-24 01:56:47 -08:00
CompileDelaySlot ( DELAYSLOT_FLUSH ) ;
2012-11-01 16:19:01 +01:00
}
// Take the branch
2013-01-20 18:48:54 -08:00
CONDITIONAL_LOG_EXIT ( targetAddr ) ;
2012-11-01 16:19:01 +01:00
WriteExit ( targetAddr , 0 ) ;
SetJumpTarget ( ptr ) ;
// Not taken
2013-01-20 18:48:54 -08:00
CONDITIONAL_LOG_EXIT ( js . compilerPC + 8 ) ;
WriteExit ( js . compilerPC + 8 , 1 ) ;
2012-11-01 16:19:01 +01:00
js . compiling = false ;
}
2013-01-22 08:04:01 -08:00
void Jit : : BranchRSZeroComp ( u32 op , Gen : : CCFlags cc , bool andLink , bool likely )
2012-11-01 16:19:01 +01:00
{
2013-01-20 18:48:54 -08:00
CONDITIONAL_LOG ;
2012-12-26 08:37:53 +01:00
if ( js . inDelaySlot ) {
2013-06-30 13:19:27 -07:00
ERROR_LOG_REPORT ( JIT , " Branch in RSZeroComp delay slot at %08x in block starting at %08x " , js . compilerPC , js . blockStart ) ;
2012-12-26 08:37:53 +01:00
return ;
}
2012-11-01 16:19:01 +01:00
int offset = ( signed short ) ( op & 0xFFFF ) < < 2 ;
int rs = _RS ;
u32 targetAddr = js . compilerPC + offset + 4 ;
2013-02-02 11:46:35 -08:00
u32 delaySlotOp = Memory : : Read_Instruction ( js . compilerPC + 4 ) ;
2013-01-24 08:29:32 -08:00
bool delaySlotIsNice = IsDelaySlotNiceReg ( op , delaySlotOp , rs ) ;
2013-01-24 19:11:03 -08:00
CONDITIONAL_NICE_DELAYSLOT ;
2013-01-24 01:56:47 -08:00
if ( ! likely & & delaySlotIsNice )
CompileDelaySlot ( DELAYSLOT_NICE ) ;
2012-11-01 16:19:01 +01:00
gpr . BindToRegister ( rs , true , false ) ;
CMP ( 32 , gpr . R ( rs ) , Imm32 ( 0 ) ) ;
Gen : : FixupBranch ptr ;
if ( ! likely )
{
2013-01-24 01:56:47 -08:00
if ( ! delaySlotIsNice )
CompileDelaySlot ( DELAYSLOT_SAFE_FLUSH ) ;
2013-01-30 20:01:26 +01:00
else
FlushAll ( ) ;
2012-11-01 16:19:01 +01:00
ptr = J_CC ( cc , true ) ;
}
else
{
2013-01-30 20:01:26 +01:00
FlushAll ( ) ;
2012-11-01 16:19:01 +01:00
ptr = J_CC ( cc , true ) ;
2013-01-24 01:56:47 -08:00
CompileDelaySlot ( DELAYSLOT_FLUSH ) ;
2012-11-01 16:19:01 +01:00
}
// Take the branch
2013-01-22 08:04:01 -08:00
if ( andLink )
MOV ( 32 , M ( & mips_ - > r [ MIPS_REG_RA ] ) , Imm32 ( js . compilerPC + 8 ) ) ;
2013-01-20 18:48:54 -08:00
CONDITIONAL_LOG_EXIT ( targetAddr ) ;
2012-11-01 16:19:01 +01:00
WriteExit ( targetAddr , 0 ) ;
SetJumpTarget ( ptr ) ;
// Not taken
2013-01-20 18:48:54 -08:00
CONDITIONAL_LOG_EXIT ( js . compilerPC + 8 ) ;
2012-11-01 16:19:01 +01:00
WriteExit ( js . compilerPC + 8 , 1 ) ;
2013-01-20 18:48:54 -08:00
2012-11-01 16:19:01 +01:00
js . compiling = false ;
}
void Jit : : Comp_RelBranch ( u32 op )
{
switch ( op > > 26 )
{
case 4 : BranchRSRTComp ( op , CC_NZ , false ) ; break ; //beq
2013-01-22 08:04:01 -08:00
case 5 : BranchRSRTComp ( op , CC_Z , false ) ; break ; //bne
2012-11-01 16:19:01 +01:00
2013-01-22 08:04:01 -08:00
case 6 : BranchRSZeroComp ( op , CC_G , false , false ) ; break ; //blez
case 7 : BranchRSZeroComp ( op , CC_LE , false , false ) ; break ; //bgtz
2012-11-01 16:19:01 +01:00
case 20 : BranchRSRTComp ( op , CC_NZ , true ) ; break ; //beql
2013-01-22 08:04:01 -08:00
case 21 : BranchRSRTComp ( op , CC_Z , true ) ; break ; //bnel
2012-11-01 16:19:01 +01:00
2013-01-22 08:04:01 -08:00
case 22 : BranchRSZeroComp ( op , CC_G , false , true ) ; break ; //blezl
case 23 : BranchRSZeroComp ( op , CC_LE , false , true ) ; break ; //bgtzl
2012-11-01 16:19:01 +01:00
default :
_dbg_assert_msg_ ( CPU , 0 , " Trying to compile instruction that can't be compiled " ) ;
break ;
}
js . compiling = false ;
}
void Jit : : Comp_RelBranchRI ( u32 op )
{
switch ( ( op > > 16 ) & 0x1F )
{
2013-01-22 08:04:01 -08:00
case 0 : BranchRSZeroComp ( op , CC_GE , false , false ) ; break ; //if ((s32)R(rs) < 0) DelayBranchTo(addr); else PC += 4; break;//bltz
case 1 : BranchRSZeroComp ( op , CC_L , false , false ) ; break ; //if ((s32)R(rs) >= 0) DelayBranchTo(addr); else PC += 4; break;//bgez
case 2 : BranchRSZeroComp ( op , CC_GE , false , true ) ; break ; //if ((s32)R(rs) < 0) DelayBranchTo(addr); else PC += 8; break;//bltzl
case 3 : BranchRSZeroComp ( op , CC_L , false , true ) ; break ; //if ((s32)R(rs) >= 0) DelayBranchTo(addr); else PC += 8; break;//bgezl
case 16 : BranchRSZeroComp ( op , CC_GE , true , false ) ; break ; //R(MIPS_REG_RA) = PC + 8; if ((s32)R(rs) < 0) DelayBranchTo(addr); else PC += 4; break;//bltzal
case 17 : BranchRSZeroComp ( op , CC_L , true , false ) ; break ; //R(MIPS_REG_RA) = PC + 8; if ((s32)R(rs) >= 0) DelayBranchTo(addr); else PC += 4; break;//bgezal
case 18 : BranchRSZeroComp ( op , CC_GE , true , true ) ; break ; //R(MIPS_REG_RA) = PC + 8; if ((s32)R(rs) < 0) DelayBranchTo(addr); else SkipLikely(); break;//bltzall
case 19 : BranchRSZeroComp ( op , CC_L , true , true ) ; break ; //R(MIPS_REG_RA) = PC + 8; if ((s32)R(rs) >= 0) DelayBranchTo(addr); else SkipLikely(); break;//bgezall
2012-11-01 16:19:01 +01:00
default :
_dbg_assert_msg_ ( CPU , 0 , " Trying to compile instruction that can't be compiled " ) ;
break ;
}
js . compiling = false ;
}
// If likely is set, discard the branch slot if NOT taken.
void Jit : : BranchFPFlag ( u32 op , Gen : : CCFlags cc , bool likely )
{
2013-01-20 18:48:54 -08:00
CONDITIONAL_LOG ;
2012-12-26 08:37:53 +01:00
if ( js . inDelaySlot ) {
2013-06-30 13:19:27 -07:00
ERROR_LOG_REPORT ( JIT , " Branch in FPFlag delay slot at %08x in block starting at %08x " , js . compilerPC , js . blockStart ) ;
2012-12-26 08:37:53 +01:00
return ;
}
2012-11-01 16:19:01 +01:00
int offset = ( signed short ) ( op & 0xFFFF ) < < 2 ;
u32 targetAddr = js . compilerPC + offset + 4 ;
2013-02-02 11:46:35 -08:00
u32 delaySlotOp = Memory : : Read_Instruction ( js . compilerPC + 4 ) ;
2013-01-24 08:29:32 -08:00
bool delaySlotIsNice = IsDelaySlotNiceFPU ( op , delaySlotOp ) ;
2013-01-24 19:11:03 -08:00
CONDITIONAL_NICE_DELAYSLOT ;
2013-01-24 01:56:47 -08:00
if ( ! likely & & delaySlotIsNice )
CompileDelaySlot ( DELAYSLOT_NICE ) ;
2012-11-01 16:19:01 +01:00
FlushAll ( ) ;
TEST ( 32 , M ( ( void * ) & ( mips_ - > fpcond ) ) , Imm32 ( 1 ) ) ;
Gen : : FixupBranch ptr ;
if ( ! likely )
{
2013-01-24 01:56:47 -08:00
if ( ! delaySlotIsNice )
CompileDelaySlot ( DELAYSLOT_SAFE_FLUSH ) ;
2012-11-01 16:19:01 +01:00
ptr = J_CC ( cc , true ) ;
}
else
{
ptr = J_CC ( cc , true ) ;
2013-01-24 01:56:47 -08:00
CompileDelaySlot ( DELAYSLOT_FLUSH ) ;
2012-11-01 16:19:01 +01:00
}
// Take the branch
2013-01-20 18:48:54 -08:00
CONDITIONAL_LOG_EXIT ( targetAddr ) ;
2012-11-01 16:19:01 +01:00
WriteExit ( targetAddr , 0 ) ;
SetJumpTarget ( ptr ) ;
// Not taken
2013-01-20 18:48:54 -08:00
CONDITIONAL_LOG_EXIT ( js . compilerPC + 8 ) ;
2012-11-01 16:19:01 +01:00
WriteExit ( js . compilerPC + 8 , 1 ) ;
js . compiling = false ;
}
void Jit : : Comp_FPUBranch ( u32 op )
{
switch ( ( op > > 16 ) & 0x1f )
{
case 0 : BranchFPFlag ( op , CC_NZ , false ) ; break ; //bc1f
case 1 : BranchFPFlag ( op , CC_Z , false ) ; break ; //bc1t
case 2 : BranchFPFlag ( op , CC_NZ , true ) ; break ; //bc1fl
case 3 : BranchFPFlag ( op , CC_Z , true ) ; break ; //bc1tl
default :
_dbg_assert_msg_ ( CPU , 0 , " Trying to interpret instruction that can't be interpreted " ) ;
break ;
}
js . compiling = false ;
}
// If likely is set, discard the branch slot if NOT taken.
void Jit : : BranchVFPUFlag ( u32 op , Gen : : CCFlags cc , bool likely )
{
2013-01-20 18:48:54 -08:00
CONDITIONAL_LOG ;
2012-12-26 08:37:53 +01:00
if ( js . inDelaySlot ) {
2013-06-30 13:19:27 -07:00
ERROR_LOG_REPORT ( JIT , " Branch in VFPU delay slot at %08x in block starting at %08x " , js . compilerPC , js . blockStart ) ;
2012-12-26 08:37:53 +01:00
return ;
}
2012-11-01 16:19:01 +01:00
int offset = ( signed short ) ( op & 0xFFFF ) < < 2 ;
u32 targetAddr = js . compilerPC + offset + 4 ;
2013-02-02 11:46:35 -08:00
u32 delaySlotOp = Memory : : Read_Instruction ( js . compilerPC + 4 ) ;
2013-08-14 22:34:32 -07:00
// Sometimes there's a VFPU branch in a delay slot (Disgaea 2: Dark Hero Days, Zettai Hero Project, La Pucelle)
// The behavior is undefined - the CPU may take the second branch even if the first one passes.
// However, it does consistently try each branch, which these games seem to expect.
bool delaySlotIsBranch = MIPSCodeUtils : : IsVFPUBranch ( delaySlotOp ) ;
bool delaySlotIsNice = ! delaySlotIsBranch & & IsDelaySlotNiceVFPU ( op , delaySlotOp ) ;
2013-01-24 19:11:03 -08:00
CONDITIONAL_NICE_DELAYSLOT ;
2013-01-24 01:56:47 -08:00
if ( ! likely & & delaySlotIsNice )
CompileDelaySlot ( DELAYSLOT_NICE ) ;
2013-08-14 23:14:25 -07:00
if ( delaySlotIsBranch & & ( signed short ) ( delaySlotOp & 0xFFFF ) ! = ( signed short ) ( op & 0xFFFF ) - 1 )
ERROR_LOG ( JIT , " VFPU branch in VFPU delay slot at %08x with different target %d / %d " , js . compilerPC , ( signed short ) ( delaySlotOp & 0xFFFF ) , ( signed short ) ( op & 0xFFFF ) - 1 ) ;
2013-01-24 01:56:47 -08:00
2012-11-01 16:19:01 +01:00
FlushAll ( ) ;
// THE CONDITION
int imm3 = ( op > > 18 ) & 7 ;
//int val = (mips_->vfpuCtrl[VFPU_CTRL_CC] >> imm3) & 1;
TEST ( 32 , M ( ( void * ) & ( mips_ - > vfpuCtrl [ VFPU_CTRL_CC ] ) ) , Imm32 ( 1 < < imm3 ) ) ;
Gen : : FixupBranch ptr ;
if ( ! likely )
{
2013-08-14 22:34:32 -07:00
if ( ! delaySlotIsNice & & ! delaySlotIsBranch )
2013-01-24 01:56:47 -08:00
CompileDelaySlot ( DELAYSLOT_SAFE_FLUSH ) ;
2012-11-01 16:19:01 +01:00
ptr = J_CC ( cc , true ) ;
}
else
{
ptr = J_CC ( cc , true ) ;
2013-08-14 22:34:32 -07:00
if ( ! delaySlotIsBranch )
CompileDelaySlot ( DELAYSLOT_FLUSH ) ;
2012-11-01 16:19:01 +01:00
}
// Take the branch
2013-01-20 18:48:54 -08:00
CONDITIONAL_LOG_EXIT ( targetAddr ) ;
2012-11-01 16:19:01 +01:00
WriteExit ( targetAddr , 0 ) ;
SetJumpTarget ( ptr ) ;
// Not taken
2013-08-14 22:34:32 -07:00
u32 notTakenTarget = js . compilerPC + ( delaySlotIsBranch ? 4 : 8 ) ;
CONDITIONAL_LOG_EXIT ( notTakenTarget ) ;
WriteExit ( notTakenTarget , 1 ) ;
2012-11-01 16:19:01 +01:00
js . compiling = false ;
}
void Jit : : Comp_VBranch ( u32 op )
{
switch ( ( op > > 16 ) & 3 )
{
case 0 : BranchVFPUFlag ( op , CC_NZ , false ) ; break ; //bvf
case 1 : BranchVFPUFlag ( op , CC_Z , false ) ; break ; //bvt
case 2 : BranchVFPUFlag ( op , CC_NZ , true ) ; break ; //bvfl
case 3 : BranchVFPUFlag ( op , CC_Z , true ) ; break ; //bvtl
default :
_dbg_assert_msg_ ( CPU , 0 , " Comp_VBranch: Invalid instruction " ) ;
break ;
}
js . compiling = false ;
}
void Jit : : Comp_Jump ( u32 op )
{
2013-01-20 18:48:54 -08:00
CONDITIONAL_LOG ;
2012-12-26 08:37:53 +01:00
if ( js . inDelaySlot ) {
2013-06-30 13:19:27 -07:00
ERROR_LOG_REPORT ( JIT , " Branch in Jump delay slot at %08x in block starting at %08x " , js . compilerPC , js . blockStart ) ;
2012-12-26 08:37:53 +01:00
return ;
}
2012-11-01 16:19:01 +01:00
u32 off = ( ( op & 0x3FFFFFF ) < < 2 ) ;
u32 targetAddr = ( js . compilerPC & 0xF0000000 ) | off ;
2013-03-11 02:04:15 -07:00
2012-11-01 16:19:01 +01:00
switch ( op > > 26 )
{
case 2 : //j
2013-03-11 02:18:27 -07:00
CompileDelaySlot ( DELAYSLOT_NICE ) ;
FlushAll ( ) ;
2013-01-20 18:48:54 -08:00
CONDITIONAL_LOG_EXIT ( targetAddr ) ;
2012-11-01 16:19:01 +01:00
WriteExit ( targetAddr , 0 ) ;
2013-03-11 02:04:15 -07:00
break ;
2012-11-01 16:19:01 +01:00
case 3 : //jal
2013-03-11 02:18:27 -07:00
gpr . BindToRegister ( MIPS_REG_RA , false , true ) ;
MOV ( 32 , gpr . R ( MIPS_REG_RA ) , Imm32 ( js . compilerPC + 8 ) ) ; // Save return address
CompileDelaySlot ( DELAYSLOT_NICE ) ;
FlushAll ( ) ;
2013-01-20 18:48:54 -08:00
CONDITIONAL_LOG_EXIT ( targetAddr ) ;
2012-11-01 16:19:01 +01:00
WriteExit ( targetAddr , 0 ) ;
break ;
default :
_dbg_assert_msg_ ( CPU , 0 , " Trying to compile instruction that can't be compiled " ) ;
break ;
}
js . compiling = false ;
}
static u32 savedPC ;
void Jit : : Comp_JumpReg ( u32 op )
{
2013-01-20 18:48:54 -08:00
CONDITIONAL_LOG ;
2012-12-26 08:37:53 +01:00
if ( js . inDelaySlot ) {
2013-06-30 13:19:27 -07:00
ERROR_LOG_REPORT ( JIT , " Branch in JumpReg delay slot at %08x in block starting at %08x " , js . compilerPC , js . blockStart ) ;
2012-12-26 08:37:53 +01:00
return ;
}
2012-11-01 16:19:01 +01:00
int rs = _RS ;
2013-02-02 11:46:35 -08:00
u32 delaySlotOp = Memory : : Read_Instruction ( js . compilerPC + 4 ) ;
2013-01-24 08:29:32 -08:00
bool delaySlotIsNice = IsDelaySlotNiceReg ( op , delaySlotOp , rs ) ;
2013-01-24 19:11:03 -08:00
CONDITIONAL_NICE_DELAYSLOT ;
2012-11-01 16:19:01 +01:00
2013-01-24 00:53:05 -08:00
if ( IsSyscall ( delaySlotOp ) )
{
// If this is a syscall, write the pc (for thread switching and other good reasons.)
gpr . BindToRegister ( rs , true , false ) ;
MOV ( 32 , M ( & currentMIPS - > pc ) , gpr . R ( rs ) ) ;
2013-01-24 01:56:47 -08:00
CompileDelaySlot ( DELAYSLOT_FLUSH ) ;
2013-01-24 00:53:05 -08:00
// Syscalls write the exit code for us.
_dbg_assert_msg_ ( JIT , ! js . compiling , " Expected syscall to write an exit code. " ) ;
return ;
}
else if ( delaySlotIsNice )
2012-11-01 16:19:01 +01:00
{
2013-01-24 01:56:47 -08:00
CompileDelaySlot ( DELAYSLOT_NICE ) ;
2012-11-01 16:19:01 +01:00
MOV ( 32 , R ( EAX ) , gpr . R ( rs ) ) ;
FlushAll ( ) ;
}
else
{
2013-01-24 00:53:05 -08:00
// Latch destination now - save it in memory.
2012-11-01 16:19:01 +01:00
gpr . BindToRegister ( rs , true , false ) ;
MOV ( 32 , M ( & savedPC ) , gpr . R ( rs ) ) ;
2013-01-24 01:56:47 -08:00
CompileDelaySlot ( DELAYSLOT_NICE ) ;
2012-11-01 16:19:01 +01:00
MOV ( 32 , R ( EAX ) , M ( & savedPC ) ) ;
2013-01-24 01:56:47 -08:00
FlushAll ( ) ;
2012-11-01 16:19:01 +01:00
}
switch ( op & 0x3f )
{
case 8 : //jr
break ;
case 9 : //jalr
MOV ( 32 , M ( & mips_ - > r [ MIPS_REG_RA ] ) , Imm32 ( js . compilerPC + 8 ) ) ;
break ;
default :
_dbg_assert_msg_ ( CPU , 0 , " Trying to compile instruction that can't be compiled " ) ;
break ;
}
2013-01-20 18:48:54 -08:00
CONDITIONAL_LOG_EXIT_EAX ( ) ;
2012-11-01 16:19:01 +01:00
WriteExitDestInEAX ( ) ;
js . compiling = false ;
}
void Jit : : Comp_Syscall ( u32 op )
{
FlushAll ( ) ;
2013-01-08 19:30:28 +01:00
2013-01-21 22:57:53 -08:00
// If we're in a delay slot, this is off by one.
const int offset = js . inDelaySlot ? - 1 : 0 ;
WriteDowncount ( offset ) ;
js . downcountAmount = - offset ;
2013-08-15 01:35:17 -07:00
// Skip the CallSyscall overhead for __KernelIdle, which is called a lot.
if ( op = = GetSyscallOp ( " FakeSysCalls " , NID_IDLE ) )
ABI_CallFunction ( ( void * ) GetFunc ( " FakeSysCalls " , NID_IDLE ) - > func ) ;
else
ABI_CallFunctionC ( ( void * ) & CallSyscall , op ) ;
2012-11-01 16:19:01 +01:00
WriteSyscallExit ( ) ;
js . compiling = false ;
}
2013-02-01 00:49:14 -08:00
void Jit : : Comp_Break ( u32 op )
{
Comp_Generic ( op ) ;
WriteSyscallExit ( ) ;
js . compiling = false ;
}
2012-11-01 16:19:01 +01:00
} // namespace Mipscomp