2016-05-06 23:45:37 +02: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
// the Free Software Foundation, version 2.0 or later versions.
// 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/.
2021-01-31 01:25:52 -08:00
# include "Common/Data/Convert/SmallDataConvert.h"
2020-10-04 10:04:01 +02:00
# include "Common/Profiler/Profiler.h"
2016-05-06 23:45:37 +02:00
# include "Core/Reporting.h"
# include "Core/Config.h"
# include "Core/MemMap.h"
# include "Core/HLE/HLE.h"
# include "Core/HLE/HLETables.h"
# include "Core/MIPS/MIPS.h"
# include "Core/MIPS/MIPSCodeUtils.h"
# include "Core/MIPS/MIPSAnalyst.h"
# include "Core/MIPS/MIPSTables.h"
2016-05-09 23:47:56 +02:00
# include "Core/MIPS/IR/IRFrontend.h"
2016-05-06 23:45:37 +02:00
# include "Core/MIPS/JitCommon/JitBlockCache.h"
# 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 _IMM26 (op & 0x03FFFFFF)
2021-01-31 08:39:21 -08:00
# define TARGET16 ((int)(SignExtend16ToU32(op) << 2))
2021-01-29 20:53:41 -08:00
# define TARGET26 (_IMM26 << 2)
2016-05-06 23:45:37 +02:00
# define LOOPOPTIMIZATION 0
2016-05-13 07:59:39 -07:00
# define MIPS_IS_BREAK(op) (((op) & 0xFC00003F) == 13)
2016-05-06 23:45:37 +02:00
using namespace MIPSAnalyst ;
namespace MIPSComp
{
2016-05-09 19:57:18 +02:00
void IRFrontend : : BranchRSRTComp ( MIPSOpcode op , IRComparison cc , bool likely ) {
2016-05-06 23:45:37 +02:00
if ( js . inDelaySlot ) {
2024-07-14 14:42:59 +02:00
ERROR_LOG_REPORT ( Log : : JIT , " Branch in RSRTComp delay slot at %08x in block starting at %08x " , GetCompilerPC ( ) , js . blockStart ) ;
2016-05-06 23:45:37 +02:00
return ;
}
2021-01-29 20:53:41 -08:00
int offset = TARGET16 ;
2016-05-06 23:45:37 +02:00
MIPSGPReg rt = _RT ;
MIPSGPReg rs = _RS ;
u32 targetAddr = GetCompilerPC ( ) + offset + 4 ;
2022-09-03 19:58:46 -07:00
BranchInfo branchInfo ( GetCompilerPC ( ) , op , GetOffsetInstruction ( 1 ) , false , likely ) ;
branchInfo . delaySlotIsNice = IsDelaySlotNiceReg ( op , branchInfo . delaySlotOp , rt , rs ) ;
js . downcountAmount + = MIPSGetInstructionCycleEstimate ( branchInfo . delaySlotOp ) ;
2016-05-06 23:45:37 +02:00
2016-05-13 07:59:39 -07:00
// Often, div/divu are followed by a likely "break" if the divisor was zero.
// Stalling is not really useful for us, so we optimize this out.
2022-09-03 19:58:46 -07:00
if ( likely & & offset = = 4 & & MIPS_IS_BREAK ( branchInfo . delaySlotOp ) ) {
2016-05-13 07:59:39 -07:00
// Okay, let's not actually branch at all. We're done here.
2022-09-03 19:58:46 -07:00
EatInstruction ( branchInfo . delaySlotOp ) ;
2016-05-13 07:59:39 -07:00
// Let's not double-count the downcount, though.
js . downcountAmount - - ;
return ;
}
2016-05-06 23:45:37 +02:00
MIPSGPReg lhs = rs ;
MIPSGPReg rhs = rt ;
2022-09-03 19:58:46 -07:00
if ( ! branchInfo . delaySlotIsNice & & ! likely ) { // if likely, we don't need this
2016-05-07 17:37:19 +02:00
if ( rs ! = 0 ) {
2016-05-08 10:36:37 +02:00
ir . Write ( IROp : : Mov , IRTEMP_LHS , rs ) ;
lhs = ( MIPSGPReg ) IRTEMP_LHS ;
2016-05-07 17:37:19 +02:00
}
if ( rt ! = 0 ) {
2016-05-08 10:36:37 +02:00
ir . Write ( IROp : : Mov , IRTEMP_RHS , rt ) ;
rhs = ( MIPSGPReg ) IRTEMP_RHS ;
2016-05-07 17:37:19 +02:00
}
2016-05-06 23:45:37 +02:00
}
2022-09-03 19:58:46 -07:00
if ( ! likely & & ! branchInfo . delaySlotIsBranch )
2016-05-06 23:45:37 +02:00
CompileDelaySlot ( ) ;
2016-07-02 16:35:13 -07:00
int dcAmount = js . downcountAmount ;
2018-01-03 23:32:31 -08:00
ir . Write ( IROp : : Downcount , 0 , ir . AddConstant ( dcAmount ) ) ;
2016-07-02 16:35:13 -07:00
js . downcountAmount = 0 ;
2016-05-07 17:37:19 +02:00
FlushAll ( ) ;
2022-09-03 19:58:46 -07:00
ir . Write ( ComparisonToExit ( cc ) , ir . AddConstant ( ResolveNotTakenTarget ( branchInfo ) ) , lhs , rhs ) ;
2016-05-06 23:45:37 +02:00
// This makes the block "impure" :(
2022-09-03 19:58:46 -07:00
if ( likely & & ! branchInfo . delaySlotIsBranch )
2016-05-06 23:45:37 +02:00
CompileDelaySlot ( ) ;
2022-09-03 19:58:46 -07:00
if ( branchInfo . delaySlotIsBranch ) {
2022-09-03 19:05:31 -07:00
// We still link when the branch is taken (targetAddr case.)
// Remember, it's from the perspective of the delay slot, so +12.
2022-09-03 19:58:46 -07:00
if ( ( branchInfo . delaySlotInfo & OUT_RA ) ! = 0 )
2022-09-03 19:05:31 -07:00
ir . WriteSetConstant ( MIPS_REG_RA , GetCompilerPC ( ) + 12 ) ;
2022-09-03 19:58:46 -07:00
if ( ( branchInfo . delaySlotInfo & OUT_RD ) ! = 0 )
ir . WriteSetConstant ( MIPS_GET_RD ( branchInfo . delaySlotOp ) , GetCompilerPC ( ) + 12 ) ;
2022-09-03 19:05:31 -07:00
}
2016-05-06 23:45:37 +02:00
2016-05-07 17:37:19 +02:00
FlushAll ( ) ;
2016-05-06 23:45:37 +02:00
ir . Write ( IROp : : ExitToConst , ir . AddConstant ( targetAddr ) ) ;
2018-01-01 22:51:03 -08:00
// Account for the delay slot.
js . compilerPC + = 4 ;
2016-05-06 23:45:37 +02:00
js . compiling = false ;
}
2016-05-09 19:57:18 +02:00
void IRFrontend : : BranchRSZeroComp ( MIPSOpcode op , IRComparison cc , bool andLink , bool likely ) {
2016-05-06 23:45:37 +02:00
if ( js . inDelaySlot ) {
2024-07-14 14:42:59 +02:00
ERROR_LOG_REPORT ( Log : : JIT , " Branch in RSZeroComp delay slot at %08x in block starting at %08x " , GetCompilerPC ( ) , js . blockStart ) ;
2016-05-06 23:45:37 +02:00
return ;
}
2021-01-29 20:53:41 -08:00
int offset = TARGET16 ;
2016-05-06 23:45:37 +02:00
MIPSGPReg rs = _RS ;
u32 targetAddr = GetCompilerPC ( ) + offset + 4 ;
2022-09-03 19:58:46 -07:00
BranchInfo branchInfo ( GetCompilerPC ( ) , op , GetOffsetInstruction ( 1 ) , andLink , likely ) ;
branchInfo . delaySlotIsNice = IsDelaySlotNiceReg ( op , branchInfo . delaySlotOp , rs ) ;
js . downcountAmount + = MIPSGetInstructionCycleEstimate ( branchInfo . delaySlotOp ) ;
2016-05-06 23:45:37 +02:00
2016-05-07 21:00:30 +02:00
MIPSGPReg lhs = rs ;
2022-09-03 19:58:46 -07:00
if ( ! branchInfo . delaySlotIsNice ) { // if likely, we don't need this
2016-05-08 10:36:37 +02:00
ir . Write ( IROp : : Mov , IRTEMP_LHS , rs ) ;
lhs = ( MIPSGPReg ) IRTEMP_LHS ;
2016-05-06 23:45:37 +02:00
}
2016-05-07 17:37:19 +02:00
if ( andLink )
2016-05-07 23:12:53 +02:00
ir . WriteSetConstant ( MIPS_REG_RA , GetCompilerPC ( ) + 8 ) ;
2016-05-07 21:00:30 +02:00
2022-09-03 19:58:46 -07:00
if ( ! likely & & ! branchInfo . delaySlotIsBranch )
2016-05-07 21:00:30 +02:00
CompileDelaySlot ( ) ;
2016-07-02 16:35:13 -07:00
int dcAmount = js . downcountAmount ;
2018-01-03 23:32:31 -08:00
ir . Write ( IROp : : Downcount , 0 , ir . AddConstant ( dcAmount ) ) ;
2016-07-02 16:35:13 -07:00
js . downcountAmount = 0 ;
2016-05-07 17:37:19 +02:00
FlushAll ( ) ;
2022-09-03 19:58:46 -07:00
ir . Write ( ComparisonToExit ( cc ) , ir . AddConstant ( ResolveNotTakenTarget ( branchInfo ) ) , lhs ) ;
if ( likely & & ! branchInfo . delaySlotIsBranch )
2016-05-06 23:45:37 +02:00
CompileDelaySlot ( ) ;
2022-09-03 19:58:46 -07:00
if ( branchInfo . delaySlotIsBranch ) {
2022-09-03 19:05:31 -07:00
// We still link when the branch is taken (targetAddr case.)
// Remember, it's from the perspective of the delay slot, so +12.
2022-09-03 19:58:46 -07:00
if ( ( branchInfo . delaySlotInfo & OUT_RA ) ! = 0 )
2022-09-03 19:05:31 -07:00
ir . WriteSetConstant ( MIPS_REG_RA , GetCompilerPC ( ) + 12 ) ;
2022-09-03 19:58:46 -07:00
if ( ( branchInfo . delaySlotInfo & OUT_RD ) ! = 0 )
ir . WriteSetConstant ( MIPS_GET_RD ( branchInfo . delaySlotOp ) , GetCompilerPC ( ) + 12 ) ;
2022-09-03 19:05:31 -07:00
}
2016-05-06 23:45:37 +02:00
// Taken
2016-05-07 17:37:19 +02:00
FlushAll ( ) ;
2016-05-06 23:45:37 +02:00
ir . Write ( IROp : : ExitToConst , ir . AddConstant ( targetAddr ) ) ;
2018-01-01 22:51:03 -08:00
// Account for the delay slot.
js . compilerPC + = 4 ;
2016-05-06 23:45:37 +02:00
js . compiling = false ;
}
2016-05-09 19:57:18 +02:00
void IRFrontend : : Comp_RelBranch ( MIPSOpcode op ) {
2016-05-13 07:59:39 -07:00
// The CC flags here should be opposite of the actual branch because they skip the branching action.
2016-05-06 23:45:37 +02:00
switch ( op > > 26 ) {
case 4 : BranchRSRTComp ( op , IRComparison : : NotEqual , false ) ; break ; //beq
2016-05-08 21:38:03 +02:00
case 5 : BranchRSRTComp ( op , IRComparison : : Equal , false ) ; break ; //bne
2016-05-06 23:45:37 +02:00
case 6 : BranchRSZeroComp ( op , IRComparison : : Greater , false , false ) ; break ; //blez
case 7 : BranchRSZeroComp ( op , IRComparison : : LessEqual , false , false ) ; break ; //bgtz
case 20 : BranchRSRTComp ( op , IRComparison : : NotEqual , true ) ; break ; //beql
2016-05-08 21:38:03 +02:00
case 21 : BranchRSRTComp ( op , IRComparison : : Equal , true ) ; break ; //bnel
2016-05-06 23:45:37 +02:00
case 22 : BranchRSZeroComp ( op , IRComparison : : Greater , false , true ) ; break ; //blezl
case 23 : BranchRSZeroComp ( op , IRComparison : : LessEqual , false , true ) ; break ; //bgtzl
default :
2020-07-19 17:47:02 +02:00
_dbg_assert_msg_ ( false , " Trying to compile instruction that can't be compiled " ) ;
2016-05-06 23:45:37 +02:00
break ;
}
}
2016-05-09 19:57:18 +02:00
void IRFrontend : : Comp_RelBranchRI ( MIPSOpcode op ) {
2016-05-06 23:45:37 +02:00
switch ( ( op > > 16 ) & 0x1F ) {
case 0 : BranchRSZeroComp ( op , IRComparison : : GreaterEqual , false , false ) ; break ; //if ((s32)R(rs) < 0) DelayBranchTo(addr); else PC += 4; break;//bltz
case 1 : BranchRSZeroComp ( op , IRComparison : : Less , false , false ) ; break ; //if ((s32)R(rs) >= 0) DelayBranchTo(addr); else PC += 4; break;//bgez
case 2 : BranchRSZeroComp ( op , IRComparison : : GreaterEqual , false , true ) ; break ; //if ((s32)R(rs) < 0) DelayBranchTo(addr); else PC += 8; break;//bltzl
case 3 : BranchRSZeroComp ( op , IRComparison : : Less , false , true ) ; break ; //if ((s32)R(rs) >= 0) DelayBranchTo(addr); else PC += 8; break;//bgezl
case 16 : BranchRSZeroComp ( op , IRComparison : : GreaterEqual , 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 , IRComparison : : Less , 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 , IRComparison : : GreaterEqual , true , true ) ; break ; //R(MIPS_REG_RA) = PC + 8; if ((s32)R(rs) < 0) DelayBranchTo(addr); else SkipLikely(); break;//bltzall
case 19 : BranchRSZeroComp ( op , IRComparison : : Less , true , true ) ; break ; //R(MIPS_REG_RA) = PC + 8; if ((s32)R(rs) >= 0) DelayBranchTo(addr); else SkipLikely(); break;//bgezall
default :
2020-07-19 17:47:02 +02:00
_dbg_assert_msg_ ( false , " Trying to compile instruction that can't be compiled " ) ;
2016-05-06 23:45:37 +02:00
break ;
}
}
// If likely is set, discard the branch slot if NOT taken.
2016-05-09 19:57:18 +02:00
void IRFrontend : : BranchFPFlag ( MIPSOpcode op , IRComparison cc , bool likely ) {
2016-05-06 23:45:37 +02:00
if ( js . inDelaySlot ) {
2024-07-14 14:42:59 +02:00
ERROR_LOG_REPORT ( Log : : JIT , " Branch in FPFlag delay slot at %08x in block starting at %08x " , GetCompilerPC ( ) , js . blockStart ) ;
2016-05-06 23:45:37 +02:00
return ;
}
2021-01-29 20:53:41 -08:00
int offset = TARGET16 ;
2016-05-06 23:45:37 +02:00
u32 targetAddr = GetCompilerPC ( ) + offset + 4 ;
2022-09-03 19:58:46 -07:00
BranchInfo branchInfo ( GetCompilerPC ( ) , op , GetOffsetInstruction ( 1 ) , false , likely ) ;
2022-09-03 19:05:31 -07:00
2016-05-08 10:36:37 +02:00
ir . Write ( IROp : : FpCondToReg , IRTEMP_LHS ) ;
2022-09-03 19:58:46 -07:00
if ( ! likely & & ! branchInfo . delaySlotIsBranch )
2016-05-06 23:45:37 +02:00
CompileDelaySlot ( ) ;
2016-05-09 01:10:04 -07:00
int dcAmount = js . downcountAmount ;
2018-01-03 23:32:31 -08:00
ir . Write ( IROp : : Downcount , 0 , ir . AddConstant ( dcAmount ) ) ;
2016-05-09 01:10:04 -07:00
js . downcountAmount = 0 ;
2016-05-07 17:37:19 +02:00
2016-05-06 23:45:37 +02:00
FlushAll ( ) ;
// Not taken
2022-09-03 19:58:46 -07:00
ir . Write ( ComparisonToExit ( cc ) , ir . AddConstant ( ResolveNotTakenTarget ( branchInfo ) ) , IRTEMP_LHS , 0 ) ;
2016-05-06 23:45:37 +02:00
// Taken
2022-09-03 19:58:46 -07:00
if ( likely & & ! branchInfo . delaySlotIsBranch )
2016-05-06 23:45:37 +02:00
CompileDelaySlot ( ) ;
2022-09-03 19:58:46 -07:00
if ( branchInfo . delaySlotIsBranch ) {
2022-09-03 19:05:31 -07:00
// We still link when the branch is taken (targetAddr case.)
// Remember, it's from the perspective of the delay slot, so +12.
2022-09-03 19:58:46 -07:00
if ( ( branchInfo . delaySlotInfo & OUT_RA ) ! = 0 )
2022-09-03 19:05:31 -07:00
ir . WriteSetConstant ( MIPS_REG_RA , GetCompilerPC ( ) + 12 ) ;
2022-09-03 19:58:46 -07:00
if ( ( branchInfo . delaySlotInfo & OUT_RD ) ! = 0 )
ir . WriteSetConstant ( MIPS_GET_RD ( branchInfo . delaySlotOp ) , GetCompilerPC ( ) + 12 ) ;
2022-09-03 19:05:31 -07:00
}
2016-05-07 17:37:19 +02:00
FlushAll ( ) ;
2016-05-06 23:45:37 +02:00
ir . Write ( IROp : : ExitToConst , ir . AddConstant ( targetAddr ) ) ;
2018-01-01 22:51:03 -08:00
// Account for the delay slot.
js . compilerPC + = 4 ;
2016-05-06 23:45:37 +02:00
js . compiling = false ;
}
2016-05-09 19:57:18 +02:00
void IRFrontend : : Comp_FPUBranch ( MIPSOpcode op ) {
2016-05-06 23:45:37 +02:00
switch ( ( op > > 16 ) & 0x1f ) {
case 0 : BranchFPFlag ( op , IRComparison : : NotEqual , false ) ; break ; // bc1f
case 1 : BranchFPFlag ( op , IRComparison : : Equal , false ) ; break ; // bc1t
case 2 : BranchFPFlag ( op , IRComparison : : NotEqual , true ) ; break ; // bc1fl
case 3 : BranchFPFlag ( op , IRComparison : : Equal , true ) ; break ; // bc1tl
default :
2020-07-19 17:47:02 +02:00
_dbg_assert_msg_ ( 0 , " Trying to interpret instruction that can't be interpreted " ) ;
2016-05-06 23:45:37 +02:00
break ;
}
}
// If likely is set, discard the branch slot if NOT taken.
2016-05-09 19:57:18 +02:00
void IRFrontend : : BranchVFPUFlag ( MIPSOpcode op , IRComparison cc , bool likely ) {
2016-05-06 23:45:37 +02:00
if ( js . inDelaySlot ) {
2024-07-14 14:42:59 +02:00
ERROR_LOG_REPORT ( Log : : JIT , " Branch in VFPU delay slot at %08x in block starting at %08x " , GetCompilerPC ( ) , js . blockStart ) ;
2016-05-06 23:45:37 +02:00
return ;
}
2021-01-29 20:53:41 -08:00
int offset = TARGET16 ;
2016-05-06 23:45:37 +02:00
u32 targetAddr = GetCompilerPC ( ) + offset + 4 ;
2022-09-03 19:58:46 -07:00
BranchInfo branchInfo ( GetCompilerPC ( ) , op , GetOffsetInstruction ( 1 ) , false , likely ) ;
js . downcountAmount + = MIPSGetInstructionCycleEstimate ( branchInfo . delaySlotOp ) ;
2016-05-08 10:36:37 +02:00
ir . Write ( IROp : : VfpuCtrlToReg , IRTEMP_LHS , VFPU_CTRL_CC ) ;
2016-05-06 23:45:37 +02: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.
2022-09-03 19:58:46 -07:00
if ( ! likely & & ! branchInfo . delaySlotIsBranch )
2016-05-06 23:45:37 +02:00
CompileDelaySlot ( ) ;
2016-07-02 16:35:13 -07:00
int dcAmount = js . downcountAmount ;
2018-01-03 23:32:31 -08:00
ir . Write ( IROp : : Downcount , 0 , ir . AddConstant ( dcAmount ) ) ;
2016-07-02 16:35:13 -07:00
js . downcountAmount = 0 ;
2016-05-06 23:45:37 +02:00
int imm3 = ( op > > 18 ) & 7 ;
2016-07-02 16:35:13 -07:00
2016-05-08 10:36:37 +02:00
ir . Write ( IROp : : AndConst , IRTEMP_LHS , IRTEMP_LHS , ir . AddConstant ( 1 < < imm3 ) ) ;
2016-05-07 17:37:19 +02:00
FlushAll ( ) ;
2022-09-03 19:58:46 -07:00
ir . Write ( ComparisonToExit ( cc ) , ir . AddConstant ( ResolveNotTakenTarget ( branchInfo ) ) , IRTEMP_LHS , 0 ) ;
2016-05-06 23:45:37 +02:00
2022-09-03 19:58:46 -07:00
if ( likely & & ! branchInfo . delaySlotIsBranch )
2016-05-06 23:45:37 +02:00
CompileDelaySlot ( ) ;
2022-09-03 19:58:46 -07:00
if ( branchInfo . delaySlotIsBranch ) {
2022-09-03 19:05:31 -07:00
// We still link when the branch is taken (targetAddr case.)
// Remember, it's from the perspective of the delay slot, so +12.
2022-09-03 19:58:46 -07:00
if ( ( branchInfo . delaySlotInfo & OUT_RA ) ! = 0 )
2022-09-03 19:05:31 -07:00
ir . WriteSetConstant ( MIPS_REG_RA , GetCompilerPC ( ) + 12 ) ;
2022-09-03 19:58:46 -07:00
if ( ( branchInfo . delaySlotInfo & OUT_RD ) ! = 0 )
ir . WriteSetConstant ( MIPS_GET_RD ( branchInfo . delaySlotOp ) , GetCompilerPC ( ) + 12 ) ;
2022-09-03 19:05:31 -07:00
}
2016-05-06 23:45:37 +02:00
// Taken
2016-05-07 17:37:19 +02:00
FlushAll ( ) ;
2016-05-06 23:45:37 +02:00
ir . Write ( IROp : : ExitToConst , ir . AddConstant ( targetAddr ) ) ;
2018-01-01 22:51:03 -08:00
// Account for the delay slot.
js . compilerPC + = 4 ;
2016-05-06 23:45:37 +02:00
js . compiling = false ;
}
2016-05-09 19:57:18 +02:00
void IRFrontend : : Comp_VBranch ( MIPSOpcode op ) {
2016-05-06 23:45:37 +02:00
switch ( ( op > > 16 ) & 3 ) {
case 0 : BranchVFPUFlag ( op , IRComparison : : NotEqual , false ) ; break ; // bvf
case 1 : BranchVFPUFlag ( op , IRComparison : : Equal , false ) ; break ; // bvt
case 2 : BranchVFPUFlag ( op , IRComparison : : NotEqual , true ) ; break ; // bvfl
case 3 : BranchVFPUFlag ( op , IRComparison : : Equal , true ) ; break ; // bvtl
}
}
2016-05-09 19:57:18 +02:00
void IRFrontend : : Comp_Jump ( MIPSOpcode op ) {
2016-05-06 23:45:37 +02:00
if ( js . inDelaySlot ) {
2024-07-14 14:42:59 +02:00
ERROR_LOG_REPORT ( Log : : JIT , " Branch in Jump delay slot at %08x in block starting at %08x " , GetCompilerPC ( ) , js . blockStart ) ;
2016-05-06 23:45:37 +02:00
return ;
}
2021-01-29 20:53:41 -08:00
u32 off = TARGET26 ;
2016-05-06 23:45:37 +02:00
u32 targetAddr = ( GetCompilerPC ( ) & 0xF0000000 ) | off ;
// Might be a stubbed address or something?
if ( ! Memory : : IsValidAddress ( targetAddr ) ) {
2018-01-06 17:23:53 -08:00
// If preloading, flush - this block will likely be fixed later.
if ( js . preloading )
js . cancel = true ;
else
2024-07-14 14:42:59 +02:00
ERROR_LOG_REPORT ( Log : : JIT , " Jump to invalid address: %08x " , targetAddr ) ;
2016-05-06 23:45:37 +02:00
// TODO: Mark this block dirty or something? May be indication it will be changed by imports.
2020-07-12 22:17:36 -07:00
// Continue so the block gets completed and crashes properly.
2016-05-06 23:45:37 +02:00
}
switch ( op > > 26 ) {
case 2 : //j
CompileDelaySlot ( ) ;
break ;
case 3 : //jal
2016-05-07 23:12:53 +02:00
ir . WriteSetConstant ( MIPS_REG_RA , GetCompilerPC ( ) + 8 ) ;
2016-05-06 23:45:37 +02:00
CompileDelaySlot ( ) ;
break ;
default :
2020-07-19 17:47:02 +02:00
_dbg_assert_msg_ ( false , " Trying to compile instruction that can't be compiled " ) ;
2016-05-06 23:45:37 +02:00
break ;
}
2016-07-02 16:35:13 -07:00
int dcAmount = js . downcountAmount ;
2018-01-03 23:32:31 -08:00
ir . Write ( IROp : : Downcount , 0 , ir . AddConstant ( dcAmount ) ) ;
2016-07-02 16:35:13 -07:00
js . downcountAmount = 0 ;
FlushAll ( ) ;
ir . Write ( IROp : : ExitToConst , ir . AddConstant ( targetAddr ) ) ;
2018-01-01 22:51:03 -08:00
// Account for the delay slot.
js . compilerPC + = 4 ;
2016-05-06 23:45:37 +02:00
js . compiling = false ;
}
2016-05-09 19:57:18 +02:00
void IRFrontend : : Comp_JumpReg ( MIPSOpcode op ) {
2016-05-06 23:45:37 +02:00
if ( js . inDelaySlot ) {
2024-07-14 14:42:59 +02:00
ERROR_LOG_REPORT ( Log : : JIT , " Branch in JumpReg delay slot at %08x in block starting at %08x " , GetCompilerPC ( ) , js . blockStart ) ;
2016-05-06 23:45:37 +02:00
return ;
}
MIPSGPReg rs = _RS ;
MIPSGPReg rd = _RD ;
bool andLink = ( op & 0x3f ) = = 9 & & rd ! = MIPS_REG_ZERO ;
MIPSOpcode delaySlotOp = GetOffsetInstruction ( 1 ) ;
2021-04-10 09:20:06 -07:00
js . downcountAmount + = MIPSGetInstructionCycleEstimate ( delaySlotOp ) ;
2016-05-06 23:45:37 +02:00
bool delaySlotIsNice = IsDelaySlotNiceReg ( op , delaySlotOp , rs ) ;
if ( andLink & & rs = = rd )
delaySlotIsNice = false ;
int destReg ;
if ( IsSyscall ( delaySlotOp ) ) {
ir . Write ( IROp : : SetPC , 0 , rs ) ;
if ( andLink )
2016-05-07 23:12:53 +02:00
ir . WriteSetConstant ( rd , GetCompilerPC ( ) + 8 ) ;
2016-05-06 23:45:37 +02:00
CompileDelaySlot ( ) ;
2016-05-07 21:00:30 +02:00
// Syscall (the delay slot) does FlushAll.
2018-01-01 22:51:03 -08:00
// Account for the delay slot itself in total bytes.
js . compilerPC + = 4 ;
2016-05-06 23:45:37 +02:00
return ; // Syscall (delay slot) wrote exit code.
} else if ( delaySlotIsNice ) {
if ( andLink )
2016-05-07 23:12:53 +02:00
ir . WriteSetConstant ( rd , GetCompilerPC ( ) + 8 ) ;
2016-05-06 23:45:37 +02:00
CompileDelaySlot ( ) ;
destReg = rs ; // Safe because FlushAll doesn't change any regs
FlushAll ( ) ;
} else {
// Bad delay slot.
2016-05-08 10:36:37 +02:00
ir . Write ( IROp : : Mov , IRTEMP_LHS , rs ) ;
destReg = IRTEMP_LHS ;
2016-05-06 23:45:37 +02:00
if ( andLink )
2016-05-07 23:12:53 +02:00
ir . WriteSetConstant ( rd , GetCompilerPC ( ) + 8 ) ;
2016-05-06 23:45:37 +02:00
CompileDelaySlot ( ) ;
FlushAll ( ) ;
}
2016-07-02 16:35:13 -07:00
switch ( op & 0x3f )
2016-05-06 23:45:37 +02:00
{
case 8 : //jr
break ;
case 9 : //jalr
break ;
default :
2020-07-19 17:47:02 +02:00
_dbg_assert_msg_ ( false , " Trying to compile instruction that can't be compiled " ) ;
2016-05-06 23:45:37 +02:00
break ;
}
2016-07-02 16:35:13 -07:00
int dcAmount = js . downcountAmount ;
2018-01-03 23:32:31 -08:00
ir . Write ( IROp : : Downcount , 0 , ir . AddConstant ( dcAmount ) ) ;
2016-07-02 16:35:13 -07:00
js . downcountAmount = 0 ;
2016-05-12 18:34:27 -07:00
ir . Write ( IROp : : ExitToReg , 0 , destReg , 0 ) ;
2018-01-01 22:51:03 -08:00
// Account for the delay slot.
js . compilerPC + = 4 ;
2016-05-06 23:45:37 +02:00
js . compiling = false ;
}
2016-05-09 19:57:18 +02:00
void IRFrontend : : Comp_Syscall ( MIPSOpcode op ) {
2016-05-09 01:10:04 -07:00
// Note: If we're in a delay slot, this is off by one compared to the interpreter.
2016-07-02 16:35:13 -07:00
int dcAmount = js . downcountAmount + ( js . inDelaySlot ? - 1 : 0 ) ;
2018-01-03 23:32:31 -08:00
ir . Write ( IROp : : Downcount , 0 , ir . AddConstant ( dcAmount ) ) ;
2016-05-09 01:10:04 -07:00
js . downcountAmount = 0 ;
2016-05-08 21:38:03 +02:00
2017-06-04 09:15:13 -07:00
// If not in a delay slot, we need to update PC.
if ( ! js . inDelaySlot ) {
ir . Write ( IROp : : SetPCConst , 0 , ir . AddConstant ( GetCompilerPC ( ) + 4 ) ) ;
}
2016-05-06 23:45:37 +02:00
FlushAll ( ) ;
2016-05-14 15:26:43 +02:00
RestoreRoundingMode ( ) ;
2016-05-06 23:45:37 +02:00
ir . Write ( IROp : : Syscall , 0 , ir . AddConstant ( op . encoding ) ) ;
ApplyRoundingMode ( ) ;
2016-05-14 14:31:38 +02:00
ir . Write ( IROp : : ExitToPC ) ;
2016-05-06 23:45:37 +02:00
js . compiling = false ;
}
2016-05-09 19:57:18 +02:00
void IRFrontend : : Comp_Break ( MIPSOpcode op ) {
2023-07-23 23:52:50 -07:00
ir . Write ( IROp : : SetPCConst , 0 , ir . AddConstant ( GetCompilerPC ( ) ) ) ;
2016-05-07 22:27:58 +02:00
ir . Write ( IROp : : Break ) ;
2016-05-06 23:45:37 +02:00
js . compiling = false ;
}
} // namespace Mipscomp