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/ARM64/Arm64CompBranch.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(ARM64)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/ARM64/Arm64Jit.h"36#include "Core/MIPS/ARM64/Arm64RegCache.h"37#include "Core/MIPS/JitCommon/JitBlockCache.h"3839#include "Common/Arm64Emitter.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 Arm64Gen;65using namespace Arm64JitConstants;6667void Arm64Jit::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;136const bool rsIsZero = gpr.IsImm(rs) && gpr.GetImm(rs) == 0;137const bool rtIsZero = gpr.IsImm(rt) && gpr.GetImm(rt) == 0;138139Arm64Gen::FixupBranch ptr;140if ((likely || branchInfo.delaySlotIsNice) && (rsIsZero || rtIsZero) && canFlip) {141// Special case, we can just use CBZ/CBNZ directly.142MIPSGPReg r = rsIsZero ? rt : rs;143gpr.MapReg(r);144// Flush should keep r in the same armreg.145ARM64Reg ar = gpr.R(r);146FlushAll();147if (cc == CC_EQ) {148ptr = CBZ(ar);149} else {150ptr = CBNZ(ar);151}152} else {153u32 val;154bool shift;155if (gpr.IsImm(rt) && IsImmArithmetic(gpr.GetImm(rt), &val, &shift)) {156gpr.MapReg(rs);157CMP(gpr.R(rs), val, shift);158} else if (gpr.IsImm(rt) && IsImmArithmetic((u64)(s64)-(s32)gpr.GetImm(rt), &val, &shift)) {159gpr.MapReg(rs);160CMN(gpr.R(rs), val, shift);161} else if (gpr.IsImm(rs) && IsImmArithmetic(gpr.GetImm(rs), &val, &shift) && canFlip) {162gpr.MapReg(rt);163CMP(gpr.R(rt), val, shift);164} else if (gpr.IsImm(rs) && IsImmArithmetic((u64)(s64)-(s32)gpr.GetImm(rs), &val, &shift) && canFlip) {165gpr.MapReg(rt);166CMN(gpr.R(rt), val, shift);167} else {168gpr.MapInIn(rs, rt);169CMP(gpr.R(rs), gpr.R(rt));170}171172if (!likely) {173if (!branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)174CompileDelaySlot(DELAYSLOT_SAFE_FLUSH);175else176FlushAll();177ptr = B(cc);178} else {179FlushAll();180ptr = B(cc);181}182}183184if (likely && !branchInfo.delaySlotIsBranch) {185// Only executed when taking the branch.186CompileDelaySlot(DELAYSLOT_FLUSH);187}188189if (branchInfo.delaySlotIsBranch) {190// We still link when the branch is taken (targetAddr case.)191// Remember, it's from the perspective of the delay slot, so +12.192if ((branchInfo.delaySlotInfo & OUT_RA) != 0)193gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 12);194if ((branchInfo.delaySlotInfo & OUT_RD) != 0)195gpr.SetImm(MIPS_GET_RD(branchInfo.delaySlotOp), GetCompilerPC() + 12);196FlushAll();197}198199// Take the branch200WriteExit(targetAddr, js.nextExit++);201202SetJumpTarget(ptr);203// Not taken204WriteExit(notTakenTarget, js.nextExit++);205}206207js.compiling = false;208}209210211void Arm64Jit::BranchRSZeroComp(MIPSOpcode op, CCFlags cc, bool andLink, bool likely)212{213if (js.inDelaySlot) {214ERROR_LOG_REPORT(Log::JIT, "Branch in RSZeroComp delay slot at %08x in block starting at %08x", GetCompilerPC(), js.blockStart);215return;216}217int offset = TARGET16;218MIPSGPReg rs = _RS;219u32 targetAddr = GetCompilerPC() + offset + 4;220221BranchInfo branchInfo(GetCompilerPC(), op, GetOffsetInstruction(1), andLink, likely);222branchInfo.delaySlotIsNice = IsDelaySlotNiceReg(op, branchInfo.delaySlotOp, rs);223CONDITIONAL_NICE_DELAYSLOT;224225bool immBranch = false;226bool immBranchTaken = false;227if (gpr.IsImm(rs) && !branchInfo.delaySlotIsBranch) {228// The cc flags are opposites: when NOT to take the branch.229bool immBranchNotTaken;230s32 imm = (s32)gpr.GetImm(rs);231232switch (cc)233{234case CC_GT: immBranchNotTaken = imm > 0; break;235case CC_GE: immBranchNotTaken = imm >= 0; break;236case CC_LT: immBranchNotTaken = imm < 0; break;237case CC_LE: immBranchNotTaken = imm <= 0; break;238default: immBranchNotTaken = false; _dbg_assert_msg_(false, "Bad cc flag in BranchRSZeroComp().");239}240immBranch = true;241immBranchTaken = !immBranchNotTaken;242}243244if (jo.immBranches && immBranch && js.numInstructions < jo.continueMaxInstructions) {245if (!immBranchTaken) {246// Skip the delay slot if likely, otherwise it'll be the next instruction.247if (andLink)248gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 8);249if (likely)250js.compilerPC += 4;251return;252}253254// Branch taken. Always compile the delay slot, and then go to dest.255if (andLink)256gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 8);257CompileDelaySlot(DELAYSLOT_NICE);258259AddContinuedBlock(targetAddr);260// Account for the increment in the loop.261js.compilerPC = targetAddr - 4;262// In case the delay slot was a break or something.263js.compiling = true;264return;265}266267js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);268269u32 notTakenTarget = ResolveNotTakenTarget(branchInfo);270if (immBranch) {271// Continuing is handled above, this is just static jumping.272if (andLink)273gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 8);274if (immBranchTaken || !likely)275CompileDelaySlot(DELAYSLOT_FLUSH);276else277FlushAll();278279const u32 destAddr = immBranchTaken ? targetAddr : notTakenTarget;280WriteExit(destAddr, js.nextExit++);281} else {282if (!likely && branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)283CompileDelaySlot(DELAYSLOT_NICE);284285gpr.MapReg(rs);286CMP(gpr.R(rs), 0);287288if (andLink)289gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 8);290291Arm64Gen::FixupBranch ptr;292if (!likely)293{294if (!branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)295CompileDelaySlot(DELAYSLOT_SAFE_FLUSH);296else297FlushAll();298ptr = B(cc);299}300else301{302FlushAll();303ptr = B(cc);304if (!branchInfo.delaySlotIsBranch)305CompileDelaySlot(DELAYSLOT_FLUSH);306}307308if (branchInfo.delaySlotIsBranch) {309// We still link when the branch is taken (targetAddr case.)310// Remember, it's from the perspective of the delay slot, so +12.311if ((branchInfo.delaySlotInfo & OUT_RA) != 0)312gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 12);313if ((branchInfo.delaySlotInfo & OUT_RD) != 0)314gpr.SetImm(MIPS_GET_RD(branchInfo.delaySlotOp), GetCompilerPC() + 12);315FlushAll();316}317318// Take the branch319WriteExit(targetAddr, js.nextExit++);320321SetJumpTarget(ptr);322// Not taken323WriteExit(notTakenTarget, js.nextExit++);324}325js.compiling = false;326}327328329void Arm64Jit::Comp_RelBranch(MIPSOpcode op)330{331// The CC flags here should be opposite of the actual branch becuase they skip the branching action.332switch (op >> 26)333{334case 4: BranchRSRTComp(op, CC_NEQ, false); break;//beq335case 5: BranchRSRTComp(op, CC_EQ, false); break;//bne336337case 6: BranchRSZeroComp(op, CC_GT, false, false); break;//blez338case 7: BranchRSZeroComp(op, CC_LE, false, false); break;//bgtz339340case 20: BranchRSRTComp(op, CC_NEQ, true); break;//beql341case 21: BranchRSRTComp(op, CC_EQ, true); break;//bnel342343case 22: BranchRSZeroComp(op, CC_GT, false, true); break;//blezl344case 23: BranchRSZeroComp(op, CC_LE, false, true); break;//bgtzl345346default:347_dbg_assert_msg_(false,"Trying to compile instruction that can't be compiled");348break;349}350}351352void Arm64Jit::Comp_RelBranchRI(MIPSOpcode op)353{354switch ((op >> 16) & 0x1F)355{356case 0: BranchRSZeroComp(op, CC_GE, false, false); break; //if ((s32)R(rs) < 0) DelayBranchTo(addr); else PC += 4; break;//bltz357case 1: BranchRSZeroComp(op, CC_LT, false, false); break; //if ((s32)R(rs) >= 0) DelayBranchTo(addr); else PC += 4; break;//bgez358case 2: BranchRSZeroComp(op, CC_GE, false, true); break; //if ((s32)R(rs) < 0) DelayBranchTo(addr); else PC += 8; break;//bltzl359case 3: BranchRSZeroComp(op, CC_LT, false, true); break; //if ((s32)R(rs) >= 0) DelayBranchTo(addr); else PC += 8; break;//bgezl360case 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;//bltzal361case 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;//bgezal362case 18: BranchRSZeroComp(op, CC_GE, true, true); break; //R(MIPS_REG_RA) = PC + 8; if ((s32)R(rs) < 0) DelayBranchTo(addr); else SkipLikely(); break;//bltzall363case 19: BranchRSZeroComp(op, CC_LT, true, true); break; //R(MIPS_REG_RA) = PC + 8; if ((s32)R(rs) >= 0) DelayBranchTo(addr); else SkipLikely(); break;//bgezall364default:365_dbg_assert_msg_(false,"Trying to compile instruction that can't be compiled");366break;367}368}369370// If likely is set, discard the branch slot if NOT taken.371void Arm64Jit::BranchFPFlag(MIPSOpcode op, CCFlags cc, bool likely) {372if (js.inDelaySlot) {373ERROR_LOG_REPORT(Log::JIT, "Branch in FPFlag delay slot at %08x in block starting at %08x", GetCompilerPC(), js.blockStart);374return;375}376int offset = TARGET16;377u32 targetAddr = GetCompilerPC() + offset + 4;378379BranchInfo branchInfo(GetCompilerPC(), op, GetOffsetInstruction(1), false, likely);380branchInfo.delaySlotIsNice = IsDelaySlotNiceFPU(op, branchInfo.delaySlotOp);381CONDITIONAL_NICE_DELAYSLOT;382383js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);384if (!likely && branchInfo.delaySlotIsNice && !branchInfo.delaySlotIsBranch)385CompileDelaySlot(DELAYSLOT_NICE);386387gpr.MapReg(MIPS_REG_FPCOND);388Arm64Gen::FixupBranch ptr;389if (likely || branchInfo.delaySlotIsNice) {390// FlushAll() won't actually change the reg.391ARM64Reg ar = gpr.R(MIPS_REG_FPCOND);392FlushAll();393if (cc == CC_EQ) {394ptr = TBZ(ar, 0);395} else {396ptr = TBNZ(ar, 0);397}398} else {399TSTI2R(gpr.R(MIPS_REG_FPCOND), 1, SCRATCH1);400if (!branchInfo.delaySlotIsBranch)401CompileDelaySlot(DELAYSLOT_SAFE_FLUSH);402ptr = B(cc);403}404405if (likely && !branchInfo.delaySlotIsBranch) {406CompileDelaySlot(DELAYSLOT_FLUSH);407}408409if (branchInfo.delaySlotIsBranch) {410// We still link when the branch is taken (targetAddr case.)411// Remember, it's from the perspective of the delay slot, so +12.412if ((branchInfo.delaySlotInfo & OUT_RA) != 0)413gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 12);414if ((branchInfo.delaySlotInfo & OUT_RD) != 0)415gpr.SetImm(MIPS_GET_RD(branchInfo.delaySlotOp), GetCompilerPC() + 12);416FlushAll();417}418419// Take the branch420WriteExit(targetAddr, js.nextExit++);421422SetJumpTarget(ptr);423// Not taken424WriteExit(ResolveNotTakenTarget(branchInfo), js.nextExit++);425js.compiling = false;426}427428void Arm64Jit::Comp_FPUBranch(MIPSOpcode op) {429switch((op >> 16) & 0x1f) {430case 0: BranchFPFlag(op, CC_NEQ, false); break; // bc1f431case 1: BranchFPFlag(op, CC_EQ, false); break; // bc1t432case 2: BranchFPFlag(op, CC_NEQ, true); break; // bc1fl433case 3: BranchFPFlag(op, CC_EQ, true); break; // bc1tl434default:435_dbg_assert_msg_( 0, "Trying to interpret instruction that can't be interpreted");436break;437}438}439440// If likely is set, discard the branch slot if NOT taken.441void Arm64Jit::BranchVFPUFlag(MIPSOpcode op, CCFlags cc, bool likely) {442if (js.inDelaySlot) {443ERROR_LOG_REPORT(Log::JIT, "Branch in VFPU delay slot at %08x in block starting at %08x", GetCompilerPC(), js.blockStart);444return;445}446int offset = TARGET16;447u32 targetAddr = GetCompilerPC() + offset + 4;448449BranchInfo branchInfo(GetCompilerPC(), op, GetOffsetInstruction(1), false, likely);450// Sometimes there's a VFPU branch in a delay slot (Disgaea 2: Dark Hero Days, Zettai Hero Project, La Pucelle)451// The behavior is undefined - the CPU may take the second branch even if the first one passes.452// However, it does consistently try each branch, which these games seem to expect.453branchInfo.delaySlotIsNice = IsDelaySlotNiceVFPU(op, branchInfo.delaySlotOp);454CONDITIONAL_NICE_DELAYSLOT;455456js.downcountAmount += MIPSGetInstructionCycleEstimate(branchInfo.delaySlotOp);457if (!likely && branchInfo.delaySlotIsNice)458CompileDelaySlot(DELAYSLOT_NICE);459460int imm3 = (op >> 18) & 7;461462gpr.MapReg(MIPS_REG_VFPUCC);463Arm64Gen::FixupBranch ptr;464if (likely || branchInfo.delaySlotIsNice || branchInfo.delaySlotIsBranch) {465// FlushAll() won't actually change the reg.466ARM64Reg ar = gpr.R(MIPS_REG_VFPUCC);467FlushAll();468if (cc == CC_EQ) {469ptr = TBZ(ar, imm3);470} else {471ptr = TBNZ(ar, imm3);472}473} else {474TSTI2R(gpr.R(MIPS_REG_VFPUCC), 1ULL << imm3, SCRATCH1);475if (!branchInfo.delaySlotIsBranch)476CompileDelaySlot(DELAYSLOT_SAFE_FLUSH);477ptr = B(cc);478}479480if (likely && !branchInfo.delaySlotIsBranch) {481CompileDelaySlot(DELAYSLOT_FLUSH);482}483484if (branchInfo.delaySlotIsBranch) {485// We still link when the branch is taken (targetAddr case.)486// Remember, it's from the perspective of the delay slot, so +12.487if ((branchInfo.delaySlotInfo & OUT_RA) != 0)488gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 12);489if ((branchInfo.delaySlotInfo & OUT_RD) != 0)490gpr.SetImm(MIPS_GET_RD(branchInfo.delaySlotOp), GetCompilerPC() + 12);491FlushAll();492}493494// Take the branch495WriteExit(targetAddr, js.nextExit++);496497SetJumpTarget(ptr);498// Not taken499WriteExit(ResolveNotTakenTarget(branchInfo), js.nextExit++);500js.compiling = false;501}502503void Arm64Jit::Comp_VBranch(MIPSOpcode op)504{505switch ((op >> 16) & 3)506{507case 0: BranchVFPUFlag(op, CC_NEQ, false); break; // bvf508case 1: BranchVFPUFlag(op, CC_EQ, false); break; // bvt509case 2: BranchVFPUFlag(op, CC_NEQ, true); break; // bvfl510case 3: BranchVFPUFlag(op, CC_EQ, true); break; // bvtl511}512}513514static void HitInvalidJump(uint32_t dest) {515Core_ExecException(dest, currentMIPS->pc - 8, ExecExceptionType::JUMP);516}517518void Arm64Jit::Comp_Jump(MIPSOpcode op) {519if (js.inDelaySlot) {520ERROR_LOG_REPORT(Log::JIT, "Branch in Jump delay slot at %08x in block starting at %08x", GetCompilerPC(), js.blockStart);521return;522}523u32 off = TARGET26;524u32 targetAddr = (GetCompilerPC() & 0xF0000000) | off;525526// Might be a stubbed address or something?527if (!Memory::IsValidAddress(targetAddr) || (targetAddr & 3) != 0) {528if (js.nextExit == 0) {529ERROR_LOG_REPORT(Log::JIT, "Jump to invalid address: %08x", targetAddr);530} else {531js.compiling = false;532}533// TODO: Mark this block dirty or something? May be indication it will be changed by imports.534CompileDelaySlot(DELAYSLOT_NICE);535FlushAll();536gpr.SetRegImm(SCRATCH1, GetCompilerPC() + 8);537MovToPC(SCRATCH1);538MOVI2R(W0, targetAddr);539QuickCallFunction(SCRATCH1, (const void *)&HitInvalidJump);540WriteSyscallExit();541return;542}543544switch (op >> 26) {545case 2: //j546CompileDelaySlot(DELAYSLOT_NICE);547if (jo.continueJumps && js.numInstructions < jo.continueMaxInstructions) {548AddContinuedBlock(targetAddr);549// Account for the increment in the loop.550js.compilerPC = targetAddr - 4;551// In case the delay slot was a break or something.552js.compiling = true;553return;554}555FlushAll();556WriteExit(targetAddr, js.nextExit++);557break;558559case 3: //jal560if (ReplaceJalTo(targetAddr))561return;562563gpr.SetImm(MIPS_REG_RA, GetCompilerPC() + 8);564CompileDelaySlot(DELAYSLOT_NICE);565if (jo.continueJumps && js.numInstructions < jo.continueMaxInstructions) {566AddContinuedBlock(targetAddr);567// Account for the increment in the loop.568js.compilerPC = targetAddr - 4;569// In case the delay slot was a break or something.570js.compiling = true;571return;572}573FlushAll();574WriteExit(targetAddr, js.nextExit++);575break;576577default:578_dbg_assert_msg_(false,"Trying to compile instruction that can't be compiled");579break;580}581js.compiling = false;582}583584void Arm64Jit::Comp_JumpReg(MIPSOpcode op)585{586if (js.inDelaySlot) {587ERROR_LOG_REPORT(Log::JIT, "Branch in JumpReg delay slot at %08x in block starting at %08x", GetCompilerPC(), js.blockStart);588return;589}590MIPSGPReg rs = _RS;591MIPSGPReg rd = _RD;592bool andLink = (op & 0x3f) == 9 && rd != MIPS_REG_ZERO;593594MIPSOpcode delaySlotOp = GetOffsetInstruction(1);595js.downcountAmount += MIPSGetInstructionCycleEstimate(delaySlotOp);596bool delaySlotIsNice = IsDelaySlotNiceReg(op, delaySlotOp, rs);597if (andLink && rs == rd)598delaySlotIsNice = false;599CONDITIONAL_NICE_DELAYSLOT;600601ARM64Reg destReg = INVALID_REG;602if (IsSyscall(delaySlotOp)) {603gpr.MapReg(rs);604MovToPC(gpr.R(rs)); // For syscall to be able to return.605if (andLink)606gpr.SetImm(rd, GetCompilerPC() + 8);607CompileDelaySlot(DELAYSLOT_FLUSH);608return; // Syscall (delay slot) wrote exit code.609} else if (delaySlotIsNice) {610if (andLink)611gpr.SetImm(rd, GetCompilerPC() + 8);612CompileDelaySlot(DELAYSLOT_NICE);613614if (!andLink && rs == MIPS_REG_RA && g_Config.bDiscardRegsOnJRRA) {615// According to the MIPS ABI, there are some regs we don't need to preserve.616// Let's discard them so we don't need to write them back.617// NOTE: Not all games follow the MIPS ABI! Tekken 6, for example, will crash618// with this enabled.619gpr.DiscardR(MIPS_REG_COMPILER_SCRATCH);620for (int i = MIPS_REG_A0; i <= MIPS_REG_T7; i++)621gpr.DiscardR((MIPSGPReg)i);622gpr.DiscardR(MIPS_REG_T8);623gpr.DiscardR(MIPS_REG_T9);624}625626if (jo.continueJumps && gpr.IsImm(rs) && js.numInstructions < jo.continueMaxInstructions) {627AddContinuedBlock(gpr.GetImm(rs));628// Account for the increment in the loop.629js.compilerPC = gpr.GetImm(rs) - 4;630// In case the delay slot was a break or something.631js.compiling = true;632return;633}634635gpr.MapReg(rs);636destReg = gpr.R(rs); // Safe because FlushAll doesn't change any regs637FlushAll();638} else {639// Since we can't be in a delay slot, should be safe to steal FLAGTEMPREG for a temp reg.640// It will be saved, even if a function is called.641destReg = DecodeReg(FLAGTEMPREG);642gpr.MapReg(rs);643MOV(destReg, gpr.R(rs));644if (andLink)645gpr.SetImm(rd, GetCompilerPC() + 8);646CompileDelaySlot(DELAYSLOT_NICE);647FlushAll();648}649650switch (op & 0x3f)651{652case 8: //jr653break;654case 9: //jalr655break;656default:657_dbg_assert_msg_(false,"Trying to compile instruction that can't be compiled");658break;659}660661WriteExitDestInR(destReg);662js.compiling = false;663}664665666void Arm64Jit::Comp_Syscall(MIPSOpcode op)667{668if (op.encoding == 0x03FFFFcc) {669WARN_LOG(Log::JIT, "Encountered bad syscall instruction at %08x (%08x)", js.compilerPC, op.encoding);670}671if (!g_Config.bSkipDeadbeefFilling)672{673// All of these will be overwritten with DEADBEEF anyway.674gpr.DiscardR(MIPS_REG_COMPILER_SCRATCH);675// We need to keep A0 - T3, which are used for args.676gpr.DiscardR(MIPS_REG_T4);677gpr.DiscardR(MIPS_REG_T5);678gpr.DiscardR(MIPS_REG_T6);679gpr.DiscardR(MIPS_REG_T7);680gpr.DiscardR(MIPS_REG_T8);681gpr.DiscardR(MIPS_REG_T9);682683gpr.DiscardR(MIPS_REG_HI);684gpr.DiscardR(MIPS_REG_LO);685}686687// If we're in a delay slot, this is off by one.688const int offset = js.inDelaySlot ? -1 : 0;689WriteDownCount(offset, false);690RestoreRoundingMode();691js.downcountAmount = -offset;692693if (!js.inDelaySlot) {694gpr.SetRegImm(SCRATCH1, GetCompilerPC() + 4);695MovToPC(SCRATCH1);696}697698FlushAll();699700SaveStaticRegisters();701#ifdef USE_PROFILER702// When profiling, we can't skip CallSyscall, since it times syscalls.703MOVI2R(W0, op.encoding);704QuickCallFunction(X1, (void *)&CallSyscall);705#else706// Skip the CallSyscall where possible.707void *quickFunc = GetQuickSyscallFunc(op);708if (quickFunc) {709MOVI2R(X0, (uintptr_t)GetSyscallFuncPointer(op));710// Already flushed, so X1 is safe.711QuickCallFunction(X1, quickFunc);712} else {713MOVI2R(W0, op.encoding);714QuickCallFunction(X1, (void *)&CallSyscall);715}716#endif717LoadStaticRegisters();718ApplyRoundingMode();719720WriteSyscallExit();721js.compiling = false;722}723724void Arm64Jit::Comp_Break(MIPSOpcode op)725{726Comp_Generic(op);727WriteSyscallExit();728js.compiling = false;729}730731} // namespace Mipscomp732733#endif // PPSSPP_ARCH(ARM64)734735736