2012-11-01 15:19:01 +00: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 22:01:49 +00:00
// the Free Software Foundation, version 2.0 or later versions.
2012-11-01 15:19:01 +00: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-27 03:30:14 +00:00
# include "Core/Reporting.h"
2013-08-15 08:35:17 +00:00
# include "Core/HLE/HLE.h"
# include "Core/HLE/HLETables.h"
# include "Core/Host.h"
2012-11-01 15:19:01 +00:00
2013-08-15 08:35:17 +00:00
# include "Core/MIPS/MIPS.h"
# include "Core/MIPS/MIPSCodeUtils.h"
# include "Core/MIPS/MIPSAnalyst.h"
# include "Core/MIPS/MIPSTables.h"
2012-11-01 15:19:01 +00:00
2013-08-15 08:35:17 +00:00
# include "Core/MIPS/x86/Jit.h"
# include "Core/MIPS/x86/RegCache.h"
2013-04-26 21:58:20 +00:00
# include "Core/MIPS/JitCommon/JitBlockCache.h"
2012-11-01 15:19:01 +00:00
2013-08-25 02:31:12 +00:00
# define _RS MIPS_GET_RS(op)
# define _RT MIPS_GET_RT(op)
# define _RD MIPS_GET_RD(op)
# define _FS MIPS_GET_FS(op)
# define _FT MIPS_GET_FT(op)
# define _FD MIPS_GET_FD(op)
# define _SA MIPS_GET_SA(op)
# define _POS ((op>> 6) & 0x1F)
# define _SIZE ((op>>11) & 0x1F)
# define _IMM16 (signed short)(op & 0xFFFF)
2013-08-16 09:02:56 +00:00
# define _IMM26 (op & 0x03FFFFFF)
2012-11-01 15:19:01 +00:00
# define LOOPOPTIMIZATION 0
using namespace MIPSAnalyst ;
2012-11-12 13:35:10 +00: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-21 02:48:54 +00:00
// But we can at least log and compare.
// #define DO_CONDITIONAL_LOG 1
# define DO_CONDITIONAL_LOG 0
2013-01-25 03:11:03 +00:00
// We can also disable nice delay slots.
// #define CONDITIONAL_NICE_DELAYSLOT delaySlotIsNice = false;
# define CONDITIONAL_NICE_DELAYSLOT ;
2013-01-21 02:48:54 +00: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 15:19:01 +00:00
namespace MIPSComp
{
2013-01-21 02:48:54 +00:00
static u32 intBranchExit ;
static u32 jitBranchExit ;
2013-08-24 21:43:49 +00:00
static void JitBranchLog ( MIPSOpcode op , u32 pc )
2013-01-21 02:48:54 +00:00
{
currentMIPS - > pc = pc ;
currentMIPS - > inDelaySlot = false ;
MIPSInterpretFunc func = MIPSGetInterpretFunc ( op ) ;
2013-08-24 20:22:10 +00:00
MIPSInfo info = MIPSGetInfo ( op ) ;
2013-01-21 02:48:54 +00:00
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-08-24 21:43:49 +00:00
static void JitBranchLogMismatch ( MIPSOpcode op , u32 pc )
2013-01-21 02:48:54 +00: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 ) ;
}
2013-08-24 21:43:49 +00:00
void Jit : : BranchLog ( MIPSOpcode op )
2013-01-21 02:48:54 +00:00
{
FlushAll ( ) ;
2013-08-24 21:43:49 +00:00
ABI_CallFunctionCC ( thunks . ProtectFunction ( ( void * ) & JitBranchLog , 2 ) , op . encoding , js . compilerPC ) ;
2013-01-21 02:48:54 +00:00
}
2013-08-24 21:43:49 +00:00
void Jit : : BranchLogExit ( MIPSOpcode op , u32 dest , bool useEAX )
2013-01-21 02:48:54 +00:00
{
2013-01-21 03:29:06 +00:00
OpArg destArg = useEAX ? R ( EAX ) : Imm32 ( dest ) ;
2013-01-21 02:48:54 +00:00
2013-01-21 03:29:06 +00:00
CMP ( 32 , M ( ( void * ) & intBranchExit ) , destArg ) ;
FixupBranch skip = J_CC ( CC_E ) ;
2013-01-21 02:48:54 +00:00
2013-01-21 03:29:06 +00:00
MOV ( 32 , M ( ( void * ) & jitBranchExit ) , destArg ) ;
2013-08-24 21:43:49 +00:00
ABI_CallFunctionCC ( thunks . ProtectFunction ( ( void * ) & JitBranchLogMismatch , 2 ) , op . encoding , js . compilerPC ) ;
2013-01-21 03:29:06 +00:00
// Restore EAX, we probably ruined it.
2013-01-21 02:48:54 +00:00
if ( useEAX )
MOV ( 32 , R ( EAX ) , M ( ( void * ) & jitBranchExit ) ) ;
2013-01-21 03:29:06 +00:00
SetJumpTarget ( skip ) ;
2013-01-21 02:48:54 +00:00
}
2013-08-24 21:43:49 +00:00
void Jit : : BranchRSRTComp ( MIPSOpcode op , Gen : : CCFlags cc , bool likely )
2012-11-01 15:19:01 +00:00
{
2013-01-21 02:48:54 +00:00
CONDITIONAL_LOG ;
2012-12-26 07:37:53 +00:00
if ( js . inDelaySlot ) {
2013-06-30 20:19:27 +00:00
ERROR_LOG_REPORT ( JIT , " Branch in RSRTComp delay slot at %08x in block starting at %08x " , js . compilerPC , js . blockStart ) ;
2012-12-26 07:37:53 +00:00
return ;
}
2013-08-16 07:44:23 +00:00
int offset = _IMM16 < < 2 ;
2013-08-25 02:31:12 +00:00
MIPSGPReg rt = _RT ;
MIPSGPReg rs = _RS ;
2012-11-01 15:19:01 +00:00
u32 targetAddr = js . compilerPC + offset + 4 ;
2013-08-24 21:43:49 +00:00
MIPSOpcode delaySlotOp = Memory : : Read_Instruction ( js . compilerPC + 4 ) ;
2013-01-24 16:29:32 +00:00
bool delaySlotIsNice = IsDelaySlotNiceReg ( op , delaySlotOp , rt , rs ) ;
2013-01-25 03:11:03 +00:00
CONDITIONAL_NICE_DELAYSLOT ;
2013-08-16 08:05:52 +00:00
2013-08-25 00:25:50 +00:00
if ( jo . immBranches & & gpr . IsImmediate ( rs ) & & gpr . IsImmediate ( rt ) & & js . numInstructions < jo . continueMaxInstructions )
2013-08-16 08:05:52 +00:00
{
// The cc flags are opposites: when NOT to take the branch.
bool skipBranch ;
s32 rsImm = ( s32 ) gpr . GetImmediate32 ( rs ) ;
s32 rtImm = ( s32 ) gpr . GetImmediate32 ( rt ) ;
switch ( cc )
{
case CC_E : skipBranch = rsImm = = rtImm ; break ;
case CC_NE : skipBranch = rsImm ! = rtImm ; break ;
default : _dbg_assert_msg_ ( JIT , false , " Bad cc flag in BranchRSRTComp(). " ) ;
}
if ( skipBranch )
{
// Skip the delay slot if likely, otherwise it'll be the next instruction.
if ( likely )
js . compilerPC + = 4 ;
return ;
}
// Branch taken. Always compile the delay slot, and then go to dest.
CompileDelaySlot ( DELAYSLOT_NICE ) ;
// Account for the increment in the loop.
js . compilerPC = targetAddr - 4 ;
2013-08-25 00:25:50 +00:00
// In case the delay slot was a break or something.
js . compiling = true ;
2013-08-16 08:05:52 +00:00
return ;
}
2013-01-24 09:56:47 +00:00
if ( ! likely & & delaySlotIsNice )
CompileDelaySlot ( DELAYSLOT_NICE ) ;
2013-08-17 05:58:18 +00:00
if ( gpr . IsImmediate ( rt ) & & gpr . GetImmediate32 ( rt ) = = 0 )
2012-11-01 15:19:01 +00:00
{
2013-02-21 08:00:30 +00:00
gpr . KillImmediate ( rs , true , false ) ;
2013-01-08 18:30:28 +00:00
CMP ( 32 , gpr . R ( rs ) , Imm32 ( 0 ) ) ;
2012-11-01 15:19:01 +00:00
}
else
{
gpr . BindToRegister ( rs , true , false ) ;
2013-08-25 02:31:12 +00:00
CMP ( 32 , gpr . R ( rs ) , rt = = MIPS_REG_ZERO ? Imm32 ( 0 ) : gpr . R ( rt ) ) ;
2012-11-01 15:19:01 +00:00
}
Gen : : FixupBranch ptr ;
2013-08-16 08:07:11 +00:00
RegCacheState state ;
2012-11-01 15:19:01 +00:00
if ( ! likely )
{
2013-01-24 09:56:47 +00:00
if ( ! delaySlotIsNice )
2013-08-16 08:07:11 +00:00
CompileDelaySlot ( DELAYSLOT_SAFE_FLUSH , state ) ;
2013-01-30 19:01:26 +00:00
else
2013-08-16 08:07:11 +00:00
GetStateAndFlushAll ( state ) ;
2012-11-01 15:19:01 +00:00
ptr = J_CC ( cc , true ) ;
}
else
{
2013-08-16 08:07:11 +00:00
GetStateAndFlushAll ( state ) ;
2012-11-01 15:19:01 +00:00
ptr = J_CC ( cc , true ) ;
2013-01-24 09:56:47 +00:00
CompileDelaySlot ( DELAYSLOT_FLUSH ) ;
2012-11-01 15:19:01 +00:00
}
// Take the branch
2013-01-21 02:48:54 +00:00
CONDITIONAL_LOG_EXIT ( targetAddr ) ;
2013-08-16 07:44:23 +00:00
WriteExit ( targetAddr , js . nextExit + + ) ;
2012-11-01 15:19:01 +00:00
SetJumpTarget ( ptr ) ;
// Not taken
2013-01-21 02:48:54 +00:00
CONDITIONAL_LOG_EXIT ( js . compilerPC + 8 ) ;
2012-11-01 15:19:01 +00:00
2013-08-16 08:07:11 +00:00
if ( CanContinueBranch ( ) )
{
// Account for the delay slot.
js . compilerPC + = 4 ;
RestoreState ( state ) ;
2013-08-25 00:25:50 +00:00
// In case the delay slot was a break or something.
js . compiling = true ;
2013-08-16 08:07:11 +00:00
}
else
{
WriteExit ( js . compilerPC + 8 , js . nextExit + + ) ;
js . compiling = false ;
}
2012-11-01 15:19:01 +00:00
}
2013-08-24 21:43:49 +00:00
void Jit : : BranchRSZeroComp ( MIPSOpcode op , Gen : : CCFlags cc , bool andLink , bool likely )
2012-11-01 15:19:01 +00:00
{
2013-01-21 02:48:54 +00:00
CONDITIONAL_LOG ;
2012-12-26 07:37:53 +00:00
if ( js . inDelaySlot ) {
2013-06-30 20:19:27 +00:00
ERROR_LOG_REPORT ( JIT , " Branch in RSZeroComp delay slot at %08x in block starting at %08x " , js . compilerPC , js . blockStart ) ;
2012-12-26 07:37:53 +00:00
return ;
}
2013-08-16 07:44:23 +00:00
int offset = _IMM16 < < 2 ;
2013-08-25 02:31:12 +00:00
MIPSGPReg rs = _RS ;
2012-11-01 15:19:01 +00:00
u32 targetAddr = js . compilerPC + offset + 4 ;
2013-08-24 21:43:49 +00:00
MIPSOpcode delaySlotOp = Memory : : Read_Instruction ( js . compilerPC + 4 ) ;
2013-01-24 16:29:32 +00:00
bool delaySlotIsNice = IsDelaySlotNiceReg ( op , delaySlotOp , rs ) ;
2013-01-25 03:11:03 +00:00
CONDITIONAL_NICE_DELAYSLOT ;
2013-08-16 08:05:52 +00:00
2013-08-25 00:25:50 +00:00
if ( jo . immBranches & & gpr . IsImmediate ( rs ) & & js . numInstructions < jo . continueMaxInstructions )
2013-08-16 08:05:52 +00:00
{
// The cc flags are opposites: when NOT to take the branch.
bool skipBranch ;
s32 imm = ( s32 ) gpr . GetImmediate32 ( rs ) ;
switch ( cc )
{
case CC_G : skipBranch = imm > 0 ; break ;
case CC_GE : skipBranch = imm > = 0 ; break ;
case CC_L : skipBranch = imm < 0 ; break ;
case CC_LE : skipBranch = imm < = 0 ; break ;
default : _dbg_assert_msg_ ( JIT , false , " Bad cc flag in BranchRSZeroComp(). " ) ;
}
if ( skipBranch )
{
// Skip the delay slot if likely, otherwise it'll be the next instruction.
if ( likely )
js . compilerPC + = 4 ;
return ;
}
// Branch taken. Always compile the delay slot, and then go to dest.
CompileDelaySlot ( DELAYSLOT_NICE ) ;
if ( andLink )
{
gpr . BindToRegister ( MIPS_REG_RA , false , true ) ;
MOV ( 32 , gpr . R ( MIPS_REG_RA ) , Imm32 ( js . compilerPC + 8 ) ) ;
}
// Account for the increment in the loop.
js . compilerPC = targetAddr - 4 ;
2013-08-25 00:25:50 +00:00
// In case the delay slot was a break or something.
js . compiling = true ;
2013-08-16 08:05:52 +00:00
return ;
}
2013-01-24 09:56:47 +00:00
if ( ! likely & & delaySlotIsNice )
CompileDelaySlot ( DELAYSLOT_NICE ) ;
2012-11-01 15:19:01 +00:00
gpr . BindToRegister ( rs , true , false ) ;
CMP ( 32 , gpr . R ( rs ) , Imm32 ( 0 ) ) ;
Gen : : FixupBranch ptr ;
2013-08-16 08:07:11 +00:00
RegCacheState state ;
2012-11-01 15:19:01 +00:00
if ( ! likely )
{
2013-01-24 09:56:47 +00:00
if ( ! delaySlotIsNice )
2013-08-16 08:07:11 +00:00
CompileDelaySlot ( DELAYSLOT_SAFE_FLUSH , state ) ;
2013-01-30 19:01:26 +00:00
else
2013-08-16 08:07:11 +00:00
GetStateAndFlushAll ( state ) ;
2012-11-01 15:19:01 +00:00
ptr = J_CC ( cc , true ) ;
}
else
{
2013-08-16 08:07:11 +00:00
GetStateAndFlushAll ( state ) ;
2012-11-01 15:19:01 +00:00
ptr = J_CC ( cc , true ) ;
2013-01-24 09:56:47 +00:00
CompileDelaySlot ( DELAYSLOT_FLUSH ) ;
2012-11-01 15:19:01 +00:00
}
// Take the branch
2013-01-22 16:04:01 +00:00
if ( andLink )
MOV ( 32 , M ( & mips_ - > r [ MIPS_REG_RA ] ) , Imm32 ( js . compilerPC + 8 ) ) ;
2013-01-21 02:48:54 +00:00
CONDITIONAL_LOG_EXIT ( targetAddr ) ;
2013-08-16 07:44:23 +00:00
WriteExit ( targetAddr , js . nextExit + + ) ;
2012-11-01 15:19:01 +00:00
SetJumpTarget ( ptr ) ;
// Not taken
2013-01-21 02:48:54 +00:00
CONDITIONAL_LOG_EXIT ( js . compilerPC + 8 ) ;
2013-08-16 08:07:11 +00:00
if ( CanContinueBranch ( ) )
{
// Account for the delay slot.
js . compilerPC + = 4 ;
RestoreState ( state ) ;
2013-08-25 00:25:50 +00:00
// In case the delay slot was a break or something.
js . compiling = true ;
2013-08-16 08:07:11 +00:00
}
else
{
WriteExit ( js . compilerPC + 8 , js . nextExit + + ) ;
js . compiling = false ;
}
2012-11-01 15:19:01 +00:00
}
2013-08-24 21:43:49 +00:00
void Jit : : Comp_RelBranch ( MIPSOpcode op )
2012-11-01 15:19:01 +00:00
{
switch ( op > > 26 )
{
case 4 : BranchRSRTComp ( op , CC_NZ , false ) ; break ; //beq
2013-01-22 16:04:01 +00:00
case 5 : BranchRSRTComp ( op , CC_Z , false ) ; break ; //bne
2012-11-01 15:19:01 +00:00
2013-01-22 16:04:01 +00:00
case 6 : BranchRSZeroComp ( op , CC_G , false , false ) ; break ; //blez
case 7 : BranchRSZeroComp ( op , CC_LE , false , false ) ; break ; //bgtz
2012-11-01 15:19:01 +00:00
case 20 : BranchRSRTComp ( op , CC_NZ , true ) ; break ; //beql
2013-01-22 16:04:01 +00:00
case 21 : BranchRSRTComp ( op , CC_Z , true ) ; break ; //bnel
2012-11-01 15:19:01 +00:00
2013-01-22 16:04:01 +00:00
case 22 : BranchRSZeroComp ( op , CC_G , false , true ) ; break ; //blezl
case 23 : BranchRSZeroComp ( op , CC_LE , false , true ) ; break ; //bgtzl
2012-11-01 15:19:01 +00:00
default :
_dbg_assert_msg_ ( CPU , 0 , " Trying to compile instruction that can't be compiled " ) ;
break ;
}
}
2013-08-24 21:43:49 +00:00
void Jit : : Comp_RelBranchRI ( MIPSOpcode op )
2012-11-01 15:19:01 +00:00
{
switch ( ( op > > 16 ) & 0x1F )
{
2013-01-22 16:04:01 +00: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 15:19:01 +00:00
default :
_dbg_assert_msg_ ( CPU , 0 , " Trying to compile instruction that can't be compiled " ) ;
break ;
}
}
// If likely is set, discard the branch slot if NOT taken.
2013-08-24 21:43:49 +00:00
void Jit : : BranchFPFlag ( MIPSOpcode op , Gen : : CCFlags cc , bool likely )
2012-11-01 15:19:01 +00:00
{
2013-01-21 02:48:54 +00:00
CONDITIONAL_LOG ;
2012-12-26 07:37:53 +00:00
if ( js . inDelaySlot ) {
2013-06-30 20:19:27 +00:00
ERROR_LOG_REPORT ( JIT , " Branch in FPFlag delay slot at %08x in block starting at %08x " , js . compilerPC , js . blockStart ) ;
2012-12-26 07:37:53 +00:00
return ;
}
2013-08-16 07:44:23 +00:00
int offset = _IMM16 < < 2 ;
2012-11-01 15:19:01 +00:00
u32 targetAddr = js . compilerPC + offset + 4 ;
2013-08-24 21:43:49 +00:00
MIPSOpcode delaySlotOp = Memory : : Read_Instruction ( js . compilerPC + 4 ) ;
2013-01-24 16:29:32 +00:00
bool delaySlotIsNice = IsDelaySlotNiceFPU ( op , delaySlotOp ) ;
2013-01-25 03:11:03 +00:00
CONDITIONAL_NICE_DELAYSLOT ;
2013-01-24 09:56:47 +00:00
if ( ! likely & & delaySlotIsNice )
CompileDelaySlot ( DELAYSLOT_NICE ) ;
2012-11-01 15:19:01 +00:00
TEST ( 32 , M ( ( void * ) & ( mips_ - > fpcond ) ) , Imm32 ( 1 ) ) ;
Gen : : FixupBranch ptr ;
2013-08-16 08:07:11 +00:00
RegCacheState state ;
2012-11-01 15:19:01 +00:00
if ( ! likely )
{
2013-01-24 09:56:47 +00:00
if ( ! delaySlotIsNice )
2013-08-16 08:07:11 +00:00
CompileDelaySlot ( DELAYSLOT_SAFE_FLUSH , state ) ;
2013-08-16 07:44:23 +00:00
else
2013-08-16 08:07:11 +00:00
GetStateAndFlushAll ( state ) ;
2012-11-01 15:19:01 +00:00
ptr = J_CC ( cc , true ) ;
}
else
{
2013-08-16 08:07:11 +00:00
GetStateAndFlushAll ( state ) ;
2012-11-01 15:19:01 +00:00
ptr = J_CC ( cc , true ) ;
2013-01-24 09:56:47 +00:00
CompileDelaySlot ( DELAYSLOT_FLUSH ) ;
2012-11-01 15:19:01 +00:00
}
// Take the branch
2013-01-21 02:48:54 +00:00
CONDITIONAL_LOG_EXIT ( targetAddr ) ;
2013-08-16 07:44:23 +00:00
WriteExit ( targetAddr , js . nextExit + + ) ;
2012-11-01 15:19:01 +00:00
SetJumpTarget ( ptr ) ;
// Not taken
2013-01-21 02:48:54 +00:00
CONDITIONAL_LOG_EXIT ( js . compilerPC + 8 ) ;
2012-11-01 15:19:01 +00:00
2013-08-16 08:07:11 +00:00
if ( CanContinueBranch ( ) )
{
// Account for the delay slot.
js . compilerPC + = 4 ;
RestoreState ( state ) ;
2013-08-25 00:25:50 +00:00
// In case the delay slot was a break or something.
js . compiling = true ;
2013-08-16 08:07:11 +00:00
}
else
{
WriteExit ( js . compilerPC + 8 , js . nextExit + + ) ;
js . compiling = false ;
}
2012-11-01 15:19:01 +00:00
}
2013-08-24 21:43:49 +00:00
void Jit : : Comp_FPUBranch ( MIPSOpcode op )
2012-11-01 15:19:01 +00:00
{
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 ;
}
}
// If likely is set, discard the branch slot if NOT taken.
2013-08-24 21:43:49 +00:00
void Jit : : BranchVFPUFlag ( MIPSOpcode op , Gen : : CCFlags cc , bool likely )
2012-11-01 15:19:01 +00:00
{
2013-01-21 02:48:54 +00:00
CONDITIONAL_LOG ;
2012-12-26 07:37:53 +00:00
if ( js . inDelaySlot ) {
2013-06-30 20:19:27 +00:00
ERROR_LOG_REPORT ( JIT , " Branch in VFPU delay slot at %08x in block starting at %08x " , js . compilerPC , js . blockStart ) ;
2012-12-26 07:37:53 +00:00
return ;
}
2013-08-16 07:44:23 +00:00
int offset = _IMM16 < < 2 ;
2012-11-01 15:19:01 +00:00
u32 targetAddr = js . compilerPC + offset + 4 ;
2013-08-24 21:43:49 +00:00
MIPSOpcode delaySlotOp = Memory : : Read_Instruction ( js . compilerPC + 4 ) ;
2013-08-15 05:34:32 +00: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-25 03:11:03 +00:00
CONDITIONAL_NICE_DELAYSLOT ;
2013-01-24 09:56:47 +00:00
if ( ! likely & & delaySlotIsNice )
CompileDelaySlot ( DELAYSLOT_NICE ) ;
2013-08-15 06:14:25 +00: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 09:56:47 +00:00
2012-11-01 15:19:01 +00:00
// 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 ;
2013-08-16 08:07:11 +00:00
RegCacheState state ;
2012-11-01 15:19:01 +00:00
if ( ! likely )
{
2013-08-15 05:34:32 +00:00
if ( ! delaySlotIsNice & & ! delaySlotIsBranch )
2013-08-16 08:07:11 +00:00
CompileDelaySlot ( DELAYSLOT_SAFE_FLUSH , state ) ;
2013-08-16 07:44:23 +00:00
else
2013-08-16 08:07:11 +00:00
GetStateAndFlushAll ( state ) ;
2012-11-01 15:19:01 +00:00
ptr = J_CC ( cc , true ) ;
}
else
{
2013-08-16 08:07:11 +00:00
GetStateAndFlushAll ( state ) ;
2012-11-01 15:19:01 +00:00
ptr = J_CC ( cc , true ) ;
2013-08-15 05:34:32 +00:00
if ( ! delaySlotIsBranch )
CompileDelaySlot ( DELAYSLOT_FLUSH ) ;
2012-11-01 15:19:01 +00:00
}
// Take the branch
2013-01-21 02:48:54 +00:00
CONDITIONAL_LOG_EXIT ( targetAddr ) ;
2013-08-16 07:44:23 +00:00
WriteExit ( targetAddr , js . nextExit + + ) ;
2012-11-01 15:19:01 +00:00
SetJumpTarget ( ptr ) ;
// Not taken
2013-08-15 05:34:32 +00:00
u32 notTakenTarget = js . compilerPC + ( delaySlotIsBranch ? 4 : 8 ) ;
CONDITIONAL_LOG_EXIT ( notTakenTarget ) ;
2012-11-01 15:19:01 +00:00
2013-08-16 08:07:11 +00:00
if ( CanContinueBranch ( ) & & ! delaySlotIsBranch )
{
// Account for the delay slot.
js . compilerPC + = 4 ;
RestoreState ( state ) ;
2013-08-25 00:25:50 +00:00
// In case the delay slot was a break or something.
js . compiling = true ;
2013-08-16 08:07:11 +00:00
}
else
{
WriteExit ( notTakenTarget , js . nextExit + + ) ;
js . compiling = false ;
}
2012-11-01 15:19:01 +00:00
}
2013-08-24 21:43:49 +00:00
void Jit : : Comp_VBranch ( MIPSOpcode op )
2012-11-01 15:19:01 +00:00
{
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 ;
}
}
2013-08-24 21:43:49 +00:00
void Jit : : Comp_Jump ( MIPSOpcode op )
2012-11-01 15:19:01 +00:00
{
2013-01-21 02:48:54 +00:00
CONDITIONAL_LOG ;
2012-12-26 07:37:53 +00:00
if ( js . inDelaySlot ) {
2013-06-30 20:19:27 +00:00
ERROR_LOG_REPORT ( JIT , " Branch in Jump delay slot at %08x in block starting at %08x " , js . compilerPC , js . blockStart ) ;
2012-12-26 07:37:53 +00:00
return ;
}
2013-08-16 07:44:23 +00:00
u32 off = _IMM26 < < 2 ;
2012-11-01 15:19:01 +00:00
u32 targetAddr = ( js . compilerPC & 0xF0000000 ) | off ;
2013-03-11 09:04:15 +00:00
2012-11-01 15:19:01 +00:00
switch ( op > > 26 )
{
case 2 : //j
2013-03-11 09:18:27 +00:00
CompileDelaySlot ( DELAYSLOT_NICE ) ;
FlushAll ( ) ;
2013-01-21 02:48:54 +00:00
CONDITIONAL_LOG_EXIT ( targetAddr ) ;
2013-08-16 07:44:23 +00:00
WriteExit ( targetAddr , js . nextExit + + ) ;
2013-03-11 09:04:15 +00:00
break ;
2012-11-01 15:19:01 +00:00
case 3 : //jal
2013-03-11 09:18:27 +00: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-21 02:48:54 +00:00
CONDITIONAL_LOG_EXIT ( targetAddr ) ;
2013-08-16 07:44:23 +00:00
WriteExit ( targetAddr , js . nextExit + + ) ;
2012-11-01 15:19:01 +00:00
break ;
default :
_dbg_assert_msg_ ( CPU , 0 , " Trying to compile instruction that can't be compiled " ) ;
break ;
}
js . compiling = false ;
}
static u32 savedPC ;
2013-08-24 21:43:49 +00:00
void Jit : : Comp_JumpReg ( MIPSOpcode op )
2012-11-01 15:19:01 +00:00
{
2013-01-21 02:48:54 +00:00
CONDITIONAL_LOG ;
2012-12-26 07:37:53 +00:00
if ( js . inDelaySlot ) {
2013-06-30 20:19:27 +00:00
ERROR_LOG_REPORT ( JIT , " Branch in JumpReg delay slot at %08x in block starting at %08x " , js . compilerPC , js . blockStart ) ;
2012-12-26 07:37:53 +00:00
return ;
}
2013-08-25 02:31:12 +00:00
MIPSGPReg rs = _RS ;
2012-11-01 15:19:01 +00:00
2013-08-24 21:43:49 +00:00
MIPSOpcode delaySlotOp = Memory : : Read_Instruction ( js . compilerPC + 4 ) ;
2013-01-24 16:29:32 +00:00
bool delaySlotIsNice = IsDelaySlotNiceReg ( op , delaySlotOp , rs ) ;
2013-01-25 03:11:03 +00:00
CONDITIONAL_NICE_DELAYSLOT ;
2012-11-01 15:19:01 +00:00
2013-01-24 08:53:05 +00: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 09:56:47 +00:00
CompileDelaySlot ( DELAYSLOT_FLUSH ) ;
2013-01-24 08:53:05 +00: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 15:19:01 +00:00
{
2013-01-24 09:56:47 +00:00
CompileDelaySlot ( DELAYSLOT_NICE ) ;
2012-11-01 15:19:01 +00:00
MOV ( 32 , R ( EAX ) , gpr . R ( rs ) ) ;
FlushAll ( ) ;
}
else
{
2013-01-24 08:53:05 +00:00
// Latch destination now - save it in memory.
2012-11-01 15:19:01 +00:00
gpr . BindToRegister ( rs , true , false ) ;
MOV ( 32 , M ( & savedPC ) , gpr . R ( rs ) ) ;
2013-01-24 09:56:47 +00:00
CompileDelaySlot ( DELAYSLOT_NICE ) ;
2012-11-01 15:19:01 +00:00
MOV ( 32 , R ( EAX ) , M ( & savedPC ) ) ;
2013-01-24 09:56:47 +00:00
FlushAll ( ) ;
2012-11-01 15:19:01 +00: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-21 02:48:54 +00:00
CONDITIONAL_LOG_EXIT_EAX ( ) ;
2012-11-01 15:19:01 +00:00
WriteExitDestInEAX ( ) ;
js . compiling = false ;
}
2013-08-24 21:43:49 +00:00
void Jit : : Comp_Syscall ( MIPSOpcode op )
2012-11-01 15:19:01 +00:00
{
FlushAll ( ) ;
2013-01-08 18:30:28 +00:00
2013-01-22 06:57:53 +00: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 08:35:17 +00: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
2013-08-24 21:43:49 +00:00
ABI_CallFunctionC ( ( void * ) & CallSyscall , op . encoding ) ;
2012-11-01 15:19:01 +00:00
WriteSyscallExit ( ) ;
js . compiling = false ;
}
2013-08-24 21:43:49 +00:00
void Jit : : Comp_Break ( MIPSOpcode op )
2013-02-01 08:49:14 +00:00
{
Comp_Generic ( op ) ;
WriteSyscallExit ( ) ;
js . compiling = false ;
}
2012-11-01 15:19:01 +00:00
} // namespace Mipscomp