CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/Core/MIPS/ARM/ArmCompBranch.cpp
Views: 1401
// Copyright (c) 2012- PPSSPP Project.12// This program is free software: you can redistribute it and/or modify3// it under the terms of the GNU General Public License as published by4// the Free Software Foundation, version 2.0 or later versions.56// This program is distributed in the hope that it will be useful,7// but WITHOUT ANY WARRANTY; without even the implied warranty of8// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the9// GNU General Public License 2.0 for more details.1011// A copy of the GPL 2.0 should have been included with the program.12// If not, see http://www.gnu.org/licenses/1314// Official git repository and contact information can be found at15// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.1617#include "ppsspp_config.h"18#if PPSSPP_ARCH(ARM)1920#include "Common/Data/Convert/SmallDataConvert.h"21#include "Common/Profiler/Profiler.h"2223#include "Core/Config.h"24#include "Core/Core.h"25#include "Core/Reporting.h"26#include "Core/MemMap.h"27#include "Core/HLE/HLE.h"28#include "Core/HLE/HLETables.h"2930#include "Core/MIPS/MIPS.h"31#include "Core/MIPS/MIPSCodeUtils.h"32#include "Core/MIPS/MIPSAnalyst.h"33#include "Core/MIPS/MIPSTables.h"3435#include "Core/MIPS/ARM/ArmJit.h"36#include "Core/MIPS/ARM/ArmRegCache.h"37#include "Core/MIPS/JitCommon/JitBlockCache.h"3839#include "Common/ArmEmitter.h"4041#define _RS MIPS_GET_RS(op)42#define _RT MIPS_GET_RT(op)43#define _RD MIPS_GET_RD(op)44#define _FS MIPS_GET_FS(op)45#define _FT MIPS_GET_FT(op)46#define _FD MIPS_GET_FD(op)47#define _SA MIPS_GET_SA(op)48#define _POS ((op>> 6) & 0x1F)49#define _SIZE ((op>>11) & 0x1F)50#define _IMM26 (op & 0x03FFFFFF)51#define TARGET16 ((int)(SignExtend16ToU32(op) << 2))52#define TARGET26 (_IMM26 << 2)5354#define LOOPOPTIMIZATION 05556// We can disable nice delay slots.57// #define CONDITIONAL_NICE_DELAYSLOT branchInfo.delaySlotIsNice = false;58#define CONDITIONAL_NICE_DELAYSLOT ;5960using namespace MIPSAnalyst;6162namespace MIPSComp63{64using namespace ArmGen;65using namespace ArmJitConstants;6667void ArmJit::BranchRSRTComp(MIPSOpcode op, CCFlags cc, bool likely)68{69if (js.inDelaySlot) {70ERROR_LOG_REPORT(Log::JIT, "Branch in RSRTComp delay slot at %08x in block starting at %08x", GetCompilerPC(), js.blockStart);71return;72}73int offset = TARGET16;74MIPSGPReg rt = _RT;75MIPSGPReg rs = _RS;76u32 targetAddr = GetCompilerPC() + offset + 4;7778BranchInfo branchInfo(GetCompilerPC(), op, GetOffsetInstruction(1), false, likely);79branchInfo.delaySlotIsNice = IsDelaySlotNiceReg(op, branchInfo.delaySlotOp, rt, rs);80CONDITIONAL_NICE_DELAYSLOT;8182bool immBranch = false;83bool immBranchTaken = false;84if (gpr.IsImm(rs) && gpr.IsImm(rt) && !branchInfo.delaySlotIsBranch) {85// The cc flags are opposites: when NOT to take the branch.86bool immBranchNotTaken;87s32 rsImm = (s32)gpr.GetImm(rs);88s32 rtImm = (s32)gpr.GetImm(rt);8990switch (cc)91{92case CC_EQ: immBranchNotTaken = rsImm == rtImm; break;93case CC_NEQ: immBranchNotTaken = rsImm != rtImm; break;94default: immBranchNotTaken = false; _dbg_assert_msg_(false, "Bad cc flag in BranchRSRTComp().");95}96immBranch = true;97immBranchTaken = !immBranchNotTaken;98}99100if (jo.immBranches && immBranch && js.numInstructions < jo.continueMaxInstructions) {101if (!immBranchTaken) {102// Skip the delay slot if likely, otherwise it'll be the next instruction.103if (likely)104js.compilerPC += 4;105return;106}107108// Branch taken. Always compile the delay slot, and then go to dest.109CompileDelaySlot(DELAYSLOT_NICE);110AddContinuedBlock(targetAddr);111// Account for the increment in the loop.112js.compilerPC = targetAddr - 4;113// In case the delay slot was a break or something.114js.compiling = true;115return;116}117118js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);119120u32 notTakenTarget = ResolveNotTakenTarget(branchInfo);121if (immBranch) {122// Continuing is handled above, this is just static jumping.123if (immBranchTaken || !likely)124CompileDelaySlot(DELAYSLOT_FLUSH);125else126FlushAll();127128const u32 destAddr = immBranchTaken ? targetAddr : notTakenTarget;129WriteExit(destAddr, js.nextExit++);130} else {131if (!likely && branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)132CompileDelaySlot(DELAYSLOT_NICE);133134// We might be able to flip the condition (EQ/NEQ are easy.)135const bool canFlip = cc == CC_EQ || cc == CC_NEQ;136137Operand2 op2;138bool negated;139if (gpr.IsImm(rt) && TryMakeOperand2_AllowNegation(gpr.GetImm(rt), op2, &negated)) {140gpr.MapReg(rs);141if (!negated)142CMP(gpr.R(rs), op2);143else144CMN(gpr.R(rs), op2);145} else {146if (gpr.IsImm(rs) && TryMakeOperand2_AllowNegation(gpr.GetImm(rs), op2, &negated) && canFlip) {147gpr.MapReg(rt);148if (!negated)149CMP(gpr.R(rt), op2);150else151CMN(gpr.R(rt), op2);152} else {153gpr.MapInIn(rs, rt);154CMP(gpr.R(rs), gpr.R(rt));155}156}157158ArmGen::FixupBranch ptr;159if (!likely) {160if (!branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)161CompileDelaySlot(DELAYSLOT_SAFE_FLUSH);162else163FlushAll();164ptr = B_CC(cc);165} else {166FlushAll();167ptr = B_CC(cc);168if (!branchInfo.delaySlotIsBranch)169CompileDelaySlot(DELAYSLOT_FLUSH);170}171172if (branchInfo.delaySlotIsBranch) {173// We still link when the branch is taken (targetAddr case.)174// Remember, it's from the perspective of the delay slot, so +12.175if ((branchInfo.delaySlotInfo & OUT_RA) != 0)176gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 12);177if ((branchInfo.delaySlotInfo & OUT_RD) != 0)178gpr.SetImm(MIPS_GET_RD(branchInfo.delaySlotOp), GetCompilerPC() + 12);179FlushAll();180}181182// Take the branch183WriteExit(targetAddr, js.nextExit++);184185SetJumpTarget(ptr);186// Not taken187WriteExit(notTakenTarget, js.nextExit++);188}189190js.compiling = false;191}192193194void ArmJit::BranchRSZeroComp(MIPSOpcode op, CCFlags cc, bool andLink, bool likely)195{196if (js.inDelaySlot) {197ERROR_LOG_REPORT(Log::JIT, "Branch in RSZeroComp delay slot at %08x in block starting at %08x", GetCompilerPC(), js.blockStart);198return;199}200int offset = TARGET16;201MIPSGPReg rs = _RS;202u32 targetAddr = GetCompilerPC() + offset + 4;203204BranchInfo branchInfo(GetCompilerPC(), op, GetOffsetInstruction(1), andLink, likely);205branchInfo.delaySlotIsNice = IsDelaySlotNiceReg(op, branchInfo.delaySlotOp, rs);206CONDITIONAL_NICE_DELAYSLOT;207208bool immBranch = false;209bool immBranchTaken = false;210if (gpr.IsImm(rs) && !branchInfo.delaySlotIsBranch) {211// The cc flags are opposites: when NOT to take the branch.212bool immBranchNotTaken;213s32 imm = (s32)gpr.GetImm(rs);214215switch (cc)216{217case CC_GT: immBranchNotTaken = imm > 0; break;218case CC_GE: immBranchNotTaken = imm >= 0; break;219case CC_LT: immBranchNotTaken = imm < 0; break;220case CC_LE: immBranchNotTaken = imm <= 0; break;221default: immBranchNotTaken = false; _dbg_assert_msg_(false, "Bad cc flag in BranchRSZeroComp().");222}223immBranch = true;224immBranchTaken = !immBranchNotTaken;225}226227if (jo.immBranches && immBranch && js.numInstructions < jo.continueMaxInstructions) {228if (!immBranchTaken) {229// Skip the delay slot if likely, otherwise it'll be the next instruction.230if (andLink)231gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 8);232if (likely)233js.compilerPC += 4;234return;235}236237// Branch taken. Always compile the delay slot, and then go to dest.238if (andLink)239gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 8);240CompileDelaySlot(DELAYSLOT_NICE);241242AddContinuedBlock(targetAddr);243// Account for the increment in the loop.244js.compilerPC = targetAddr - 4;245// In case the delay slot was a break or something.246js.compiling = true;247return;248}249250js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);251252u32 notTakenTarget = ResolveNotTakenTarget(branchInfo);253if (immBranch) {254// Continuing is handled above, this is just static jumping.255if (andLink)256gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 8);257if (immBranchTaken || !likely)258CompileDelaySlot(DELAYSLOT_FLUSH);259else260FlushAll();261262const u32 destAddr = immBranchTaken ? targetAddr : notTakenTarget;263WriteExit(destAddr, js.nextExit++);264} else {265if (!likely && branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)266CompileDelaySlot(DELAYSLOT_NICE);267268gpr.MapReg(rs);269CMP(gpr.R(rs), Operand2(0, TYPE_IMM));270271if (andLink)272gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 8);273274ArmGen::FixupBranch ptr;275if (!likely)276{277if (!branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)278CompileDelaySlot(DELAYSLOT_SAFE_FLUSH);279else280FlushAll();281ptr = B_CC(cc);282}283else284{285FlushAll();286ptr = B_CC(cc);287if (!branchInfo.delaySlotIsBranch)288CompileDelaySlot(DELAYSLOT_FLUSH);289}290291if (branchInfo.delaySlotIsBranch) {292// We still link when the branch is taken (targetAddr case.)293// Remember, it's from the perspective of the delay slot, so +12.294if ((branchInfo.delaySlotInfo & OUT_RA) != 0)295gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 12);296if ((branchInfo.delaySlotInfo & OUT_RD) != 0)297gpr.SetImm(MIPS_GET_RD(branchInfo.delaySlotOp), GetCompilerPC() + 12);298FlushAll();299}300301// Take the branch302WriteExit(targetAddr, js.nextExit++);303304SetJumpTarget(ptr);305// Not taken306WriteExit(notTakenTarget, js.nextExit++);307}308js.compiling = false;309}310311312void ArmJit::Comp_RelBranch(MIPSOpcode op)313{314// The CC flags here should be opposite of the actual branch becuase they skip the branching action.315switch (op >> 26)316{317case 4: BranchRSRTComp(op, CC_NEQ, false); break;//beq318case 5: BranchRSRTComp(op, CC_EQ, false); break;//bne319320case 6: BranchRSZeroComp(op, CC_GT, false, false); break;//blez321case 7: BranchRSZeroComp(op, CC_LE, false, false); break;//bgtz322323case 20: BranchRSRTComp(op, CC_NEQ, true); break;//beql324case 21: BranchRSRTComp(op, CC_EQ, true); break;//bnel325326case 22: BranchRSZeroComp(op, CC_GT, false, true); break;//blezl327case 23: BranchRSZeroComp(op, CC_LE, false, true); break;//bgtzl328329default:330_dbg_assert_msg_(false,"Trying to compile instruction that can't be compiled");331break;332}333}334335void ArmJit::Comp_RelBranchRI(MIPSOpcode op)336{337switch ((op >> 16) & 0x1F)338{339case 0: BranchRSZeroComp(op, CC_GE, false, false); break; //if ((s32)R(rs) < 0) DelayBranchTo(addr); else PC += 4; break;//bltz340case 1: BranchRSZeroComp(op, CC_LT, false, false); break; //if ((s32)R(rs) >= 0) DelayBranchTo(addr); else PC += 4; break;//bgez341case 2: BranchRSZeroComp(op, CC_GE, false, true); break; //if ((s32)R(rs) < 0) DelayBranchTo(addr); else PC += 8; break;//bltzl342case 3: BranchRSZeroComp(op, CC_LT, false, true); break; //if ((s32)R(rs) >= 0) DelayBranchTo(addr); else PC += 8; break;//bgezl343case 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;//bltzal344case 17: BranchRSZeroComp(op, CC_LT, true, false); break; //R(MIPS_REG_RA) = PC + 8; if ((s32)R(rs) >= 0) DelayBranchTo(addr); else PC += 4; break;//bgezal345case 18: BranchRSZeroComp(op, CC_GE, true, true); break; //R(MIPS_REG_RA) = PC + 8; if ((s32)R(rs) < 0) DelayBranchTo(addr); else SkipLikely(); break;//bltzall346case 19: BranchRSZeroComp(op, CC_LT, true, true); break; //R(MIPS_REG_RA) = PC + 8; if ((s32)R(rs) >= 0) DelayBranchTo(addr); else SkipLikely(); break;//bgezall347default:348_dbg_assert_msg_(false,"Trying to compile instruction that can't be compiled");349break;350}351}352353// If likely is set, discard the branch slot if NOT taken.354void ArmJit::BranchFPFlag(MIPSOpcode op, CCFlags cc, bool likely)355{356if (js.inDelaySlot) {357ERROR_LOG_REPORT(Log::JIT, "Branch in FPFlag delay slot at %08x in block starting at %08x", GetCompilerPC(), js.blockStart);358return;359}360int offset = TARGET16;361u32 targetAddr = GetCompilerPC() + offset + 4;362363BranchInfo branchInfo(GetCompilerPC(), op, GetOffsetInstruction(1), false, likely);364branchInfo.delaySlotIsNice = IsDelaySlotNiceFPU(op, branchInfo.delaySlotOp);365CONDITIONAL_NICE_DELAYSLOT;366367js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);368if (!likely && branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)369CompileDelaySlot(DELAYSLOT_NICE);370371gpr.MapReg(MIPS_REG_FPCOND);372TST(gpr.R(MIPS_REG_FPCOND), Operand2(1, TYPE_IMM));373374ArmGen::FixupBranch ptr;375if (!likely)376{377if (!branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)378CompileDelaySlot(DELAYSLOT_SAFE_FLUSH);379else380FlushAll();381ptr = B_CC(cc);382}383else384{385FlushAll();386ptr = B_CC(cc);387if (!branchInfo.delaySlotIsBranch)388CompileDelaySlot(DELAYSLOT_FLUSH);389}390391if (branchInfo.delaySlotIsBranch) {392// We still link when the branch is taken (targetAddr case.)393// Remember, it's from the perspective of the delay slot, so +12.394if ((branchInfo.delaySlotInfo & OUT_RA) != 0)395gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 12);396if ((branchInfo.delaySlotInfo & OUT_RD) != 0)397gpr.SetImm(MIPS_GET_RD(branchInfo.delaySlotOp), GetCompilerPC() + 12);398FlushAll();399}400401// Take the branch402WriteExit(targetAddr, js.nextExit++);403404SetJumpTarget(ptr);405// Not taken406WriteExit(ResolveNotTakenTarget(branchInfo), js.nextExit++);407js.compiling = false;408}409410void ArmJit::Comp_FPUBranch(MIPSOpcode op)411{412switch((op >> 16) & 0x1f)413{414case 0: BranchFPFlag(op, CC_NEQ, false); break; // bc1f415case 1: BranchFPFlag(op, CC_EQ, false); break; // bc1t416case 2: BranchFPFlag(op, CC_NEQ, true); break; // bc1fl417case 3: BranchFPFlag(op, CC_EQ, true); break; // bc1tl418default:419_dbg_assert_msg_(false,"Trying to interpret instruction that can't be interpreted");420break;421}422}423424// If likely is set, discard the branch slot if NOT taken.425void ArmJit::BranchVFPUFlag(MIPSOpcode op, CCFlags cc, bool likely)426{427if (js.inDelaySlot) {428ERROR_LOG_REPORT(Log::JIT, "Branch in VFPU delay slot at %08x in block starting at %08x", GetCompilerPC(), js.blockStart);429return;430}431int offset = TARGET16;432u32 targetAddr = GetCompilerPC() + offset + 4;433434BranchInfo branchInfo(GetCompilerPC(), op, GetOffsetInstruction(1), false, likely);435// Sometimes there's a VFPU branch in a delay slot (Disgaea 2: Dark Hero Days, Zettai Hero Project, La Pucelle)436// The behavior is undefined - the CPU may take the second branch even if the first one passes.437// However, it does consistently try each branch, which these games seem to expect.438branchInfo.delaySlotIsNice = IsDelaySlotNiceVFPU(op, branchInfo.delaySlotOp);439CONDITIONAL_NICE_DELAYSLOT;440441js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);442if (!likely && branchInfo.delaySlotIsNice)443CompileDelaySlot(DELAYSLOT_NICE);444445int imm3 = (op >> 18) & 7;446447gpr.MapReg(MIPS_REG_VFPUCC);448TST(gpr.R(MIPS_REG_VFPUCC), Operand2(1 << imm3, TYPE_IMM));449450ArmGen::FixupBranch ptr;451js.inDelaySlot = true;452if (!likely)453{454if (!branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)455CompileDelaySlot(DELAYSLOT_SAFE_FLUSH);456else457FlushAll();458ptr = B_CC(cc);459}460else461{462FlushAll();463ptr = B_CC(cc);464if (!branchInfo.delaySlotIsBranch)465CompileDelaySlot(DELAYSLOT_FLUSH);466}467js.inDelaySlot = false;468469if (branchInfo.delaySlotIsBranch) {470// We still link when the branch is taken (targetAddr case.)471// Remember, it's from the perspective of the delay slot, so +12.472if ((branchInfo.delaySlotInfo & OUT_RA) != 0)473gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 12);474if ((branchInfo.delaySlotInfo & OUT_RD) != 0)475gpr.SetImm(MIPS_GET_RD(branchInfo.delaySlotOp), GetCompilerPC() + 12);476FlushAll();477}478479// Take the branch480WriteExit(targetAddr, js.nextExit++);481482SetJumpTarget(ptr);483// Not taken484WriteExit(ResolveNotTakenTarget(branchInfo), js.nextExit++);485js.compiling = false;486}487488void ArmJit::Comp_VBranch(MIPSOpcode op)489{490switch ((op >> 16) & 3)491{492case 0: BranchVFPUFlag(op, CC_NEQ, false); break; // bvf493case 1: BranchVFPUFlag(op, CC_EQ, false); break; // bvt494case 2: BranchVFPUFlag(op, CC_NEQ, true); break; // bvfl495case 3: BranchVFPUFlag(op, CC_EQ, true); break; // bvtl496}497}498499static void HitInvalidJump(uint32_t dest) {500Core_ExecException(dest, currentMIPS->pc - 8, ExecExceptionType::JUMP);501}502503void ArmJit::Comp_Jump(MIPSOpcode op) {504if (js.inDelaySlot) {505ERROR_LOG_REPORT(Log::JIT, "Branch in Jump delay slot at %08x in block starting at %08x", GetCompilerPC(), js.blockStart);506return;507}508u32 off = TARGET26;509u32 targetAddr = (GetCompilerPC() & 0xF0000000) | off;510511// Might be a stubbed address or something?512if (!Memory::IsValidAddress(targetAddr) || (targetAddr & 3) != 0) {513if (js.nextExit == 0) {514ERROR_LOG_REPORT(Log::JIT, "Jump to invalid address: %08x", targetAddr);515} else {516js.compiling = false;517}518// TODO: Mark this block dirty or something? May be indication it will be changed by imports.519CompileDelaySlot(DELAYSLOT_NICE);520FlushAll();521gpr.SetRegImm(SCRATCHREG1, GetCompilerPC() + 8);522MovToPC(SCRATCHREG1);523MOVI2R(R0, targetAddr);524QuickCallFunction(SCRATCHREG2, &HitInvalidJump);525WriteSyscallExit();526return;527}528529switch (op >> 26) {530case 2: //j531CompileDelaySlot(DELAYSLOT_NICE);532if (jo.continueJumps && js.numInstructions < jo.continueMaxInstructions) {533AddContinuedBlock(targetAddr);534// Account for the increment in the loop.535js.compilerPC = targetAddr - 4;536// In case the delay slot was a break or something.537js.compiling = true;538return;539}540FlushAll();541WriteExit(targetAddr, js.nextExit++);542break;543544case 3: //jal545if (ReplaceJalTo(targetAddr))546return;547548gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 8);549CompileDelaySlot(DELAYSLOT_NICE);550if (jo.continueJumps && js.numInstructions < jo.continueMaxInstructions) {551AddContinuedBlock(targetAddr);552// Account for the increment in the loop.553js.compilerPC = targetAddr - 4;554// In case the delay slot was a break or something.555js.compiling = true;556return;557}558FlushAll();559WriteExit(targetAddr, js.nextExit++);560break;561562default:563_dbg_assert_msg_(false,"Trying to compile instruction that can't be compiled");564break;565}566js.compiling = false;567}568569void ArmJit::Comp_JumpReg(MIPSOpcode op)570{571if (js.inDelaySlot) {572ERROR_LOG_REPORT(Log::JIT, "Branch in JumpReg delay slot at %08x in block starting at %08x", GetCompilerPC(), js.blockStart);573return;574}575MIPSGPReg rs = _RS;576MIPSGPReg rd = _RD;577bool andLink = (op & 0x3f) == 9 && rd != MIPS_REG_ZERO;578579MIPSOpcode delaySlotOp = GetOffsetInstruction(1);580js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp);581bool delaySlotIsNice = IsDelaySlotNiceReg(op, delaySlotOp, rs);582if (andLink && rs == rd)583delaySlotIsNice = false;584CONDITIONAL_NICE_DELAYSLOT;585586ARMReg destReg = R8;587if (IsSyscall(delaySlotOp)) {588gpr.MapReg(rs);589MovToPC(gpr.R(rs)); // For syscall to be able to return.590if (andLink)591gpr.SetImm(rd, GetCompilerPC() + 8);592CompileDelaySlot(DELAYSLOT_FLUSH);593return; // Syscall wrote exit code.594} else if (delaySlotIsNice) {595if (andLink)596gpr.SetImm(rd, GetCompilerPC() + 8);597CompileDelaySlot(DELAYSLOT_NICE);598599if (!andLink && rs == MIPS_REG_RA && g_Config.bDiscardRegsOnJRRA) {600// According to the MIPS ABI, there are some regs we don't need to preserve.601// Let's discard them so we don't need to write them back.602// NOTE: Not all games follow the MIPS ABI! Tekken 6, for example, will crash603// with this enabled.604gpr.DiscardR(MIPS_REG_COMPILER_SCRATCH);605for (int i = MIPS_REG_A0; i <= MIPS_REG_T7; i++)606gpr.DiscardR((MIPSGPReg)i);607gpr.DiscardR(MIPS_REG_T8);608gpr.DiscardR(MIPS_REG_T9);609}610611if (jo.continueJumps && gpr.IsImm(rs) && js.numInstructions < jo.continueMaxInstructions) {612AddContinuedBlock(gpr.GetImm(rs));613// Account for the increment in the loop.614js.compilerPC = gpr.GetImm(rs) - 4;615// In case the delay slot was a break or something.616js.compiling = true;617return;618}619620gpr.MapReg(rs);621destReg = gpr.R(rs); // Safe because FlushAll doesn't change any regs622FlushAll();623} else {624// Delay slot - this case is very rare, might be able to free up R8.625gpr.MapReg(rs);626MOV(R8, gpr.R(rs));627if (andLink)628gpr.SetImm(rd, GetCompilerPC() + 8);629CompileDelaySlot(DELAYSLOT_NICE);630FlushAll();631}632633switch (op & 0x3f)634{635case 8: //jr636break;637case 9: //jalr638break;639default:640_dbg_assert_msg_(false,"Trying to compile instruction that can't be compiled");641break;642}643644WriteExitDestInR(destReg);645js.compiling = false;646}647648649void ArmJit::Comp_Syscall(MIPSOpcode op)650{651if (op.encoding == 0x03FFFFcc) {652WARN_LOG(Log::JIT, "Encountered bad syscall instruction at %08x (%08x)", js.compilerPC, op.encoding);653}654655if (!g_Config.bSkipDeadbeefFilling)656{657// All of these will be overwritten with DEADBEEF anyway.658gpr.DiscardR(MIPS_REG_COMPILER_SCRATCH);659// We need to keep A0 - T3, which are used for args.660gpr.DiscardR(MIPS_REG_T4);661gpr.DiscardR(MIPS_REG_T5);662gpr.DiscardR(MIPS_REG_T6);663gpr.DiscardR(MIPS_REG_T7);664gpr.DiscardR(MIPS_REG_T8);665gpr.DiscardR(MIPS_REG_T9);666667gpr.DiscardR(MIPS_REG_HI);668gpr.DiscardR(MIPS_REG_LO);669}670671// If we're in a delay slot, this is off by one.672const int offset = js.inDelaySlot ? -1 : 0;673WriteDownCount(offset);674RestoreRoundingMode();675js.downcountAmount = -offset;676677if (!js.inDelaySlot) {678gpr.SetRegImm(SCRATCHREG1, GetCompilerPC() + 4);679MovToPC(SCRATCHREG1);680}681682FlushAll();683684SaveDowncount();685#ifdef USE_PROFILER686// When profiling, we can't skip CallSyscall, since it times syscalls.687gpr.SetRegImm(R0, op.encoding);688QuickCallFunction(R1, (void *)&CallSyscall);689#else690// Skip the CallSyscall where possible.691void *quickFunc = GetQuickSyscallFunc(op);692if (quickFunc)693{694gpr.SetRegImm(R0, (u32)(intptr_t)GetSyscallFuncPointer(op));695// Already flushed, so R1 is safe.696QuickCallFunction(R1, quickFunc);697}698else699{700gpr.SetRegImm(R0, op.encoding);701QuickCallFunction(R1, (void *)&CallSyscall);702}703#endif704ApplyRoundingMode();705RestoreDowncount();706707WriteSyscallExit();708js.compiling = false;709}710711void ArmJit::Comp_Break(MIPSOpcode op)712{713Comp_Generic(op);714WriteSyscallExit();715js.compiling = false;716}717718} // namespace Mipscomp719720#endif // PPSSPP_ARCH(ARM)721722723