Path: blob/master/Core/MIPS/LoongArch64/LoongArch64CompSystem.cpp
3188 views
// Copyright (c) 2023- 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 "Common/Profiler/Profiler.h"18#include "Core/Core.h"19#include "Core/HLE/HLE.h"20#include "Core/HLE/ReplaceTables.h"21#include "Core/MemMap.h"22#include "Core/MIPS/LoongArch64/LoongArch64Jit.h"23#include "Core/MIPS/LoongArch64/LoongArch64RegCache.h"2425// This file contains compilation for basic PC/downcount accounting, syscalls, debug funcs, etc.26//27// All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly.28// Currently known non working ones should have DISABLE. No flags because that's in IR already.2930// #define CONDITIONAL_DISABLE { CompIR_Generic(inst); return; }31#define CONDITIONAL_DISABLE {}32#define DISABLE { CompIR_Generic(inst); return; }33#define INVALIDOP { _assert_msg_(false, "Invalid IR inst %d", (int)inst.op); CompIR_Generic(inst); return; }3435namespace MIPSComp {3637using namespace LoongArch64Gen;38using namespace LoongArch64JitConstants;3940void LoongArch64JitBackend::CompIR_Basic(IRInst inst) {41CONDITIONAL_DISABLE;4243switch (inst.op) {44case IROp::SetConst:45// Sign extend all constants. We get 0xFFFFFFFF sometimes, and it's more work to truncate.46// The register only holds 32 bits in the end anyway.47regs_.SetGPRImm(inst.dest, (int32_t)inst.constant);48break;4950case IROp::SetConstF:51regs_.Map(inst);52if (inst.constant == 0)53MOVGR2FR_W(regs_.F(inst.dest), R_ZERO);54else55QuickFLI(32, regs_.F(inst.dest), inst.constant, SCRATCH1);56break;5758case IROp::Downcount:59if (inst.constant <= 2048) {60ADDI_D(DOWNCOUNTREG, DOWNCOUNTREG, -(s32)inst.constant);61} else {62LI(SCRATCH1, inst.constant);63SUB_D(DOWNCOUNTREG, DOWNCOUNTREG, SCRATCH1);64}65break;6667case IROp::SetPC:68regs_.Map(inst);69MovToPC(regs_.R(inst.src1));70break;7172case IROp::SetPCConst:73LI(SCRATCH1, inst.constant);74MovToPC(SCRATCH1);75break;7677default:78INVALIDOP;79break;80}81}8283void LoongArch64JitBackend::CompIR_Transfer(IRInst inst) {84CONDITIONAL_DISABLE;8586switch (inst.op) {87case IROp::SetCtrlVFPU:88regs_.SetGPRImm(IRREG_VFPU_CTRL_BASE + inst.dest, inst.constant);89break;9091case IROp::SetCtrlVFPUReg:92regs_.Map(inst);93MOVE(regs_.R(IRREG_VFPU_CTRL_BASE + inst.dest), regs_.R(inst.src1));94regs_.MarkGPRDirty(IRREG_VFPU_CTRL_BASE + inst.dest, regs_.IsNormalized32(inst.src1));95break;9697case IROp::SetCtrlVFPUFReg:98regs_.Map(inst);99MOVFR2GR_S(regs_.R(IRREG_VFPU_CTRL_BASE + inst.dest), regs_.F(inst.src1));100regs_.MarkGPRDirty(IRREG_VFPU_CTRL_BASE + inst.dest, true);101break;102103case IROp::FpCondFromReg:104regs_.MapWithExtra(inst, { { 'G', IRREG_FPCOND, 1, MIPSMap::NOINIT } });105MOVE(regs_.R(IRREG_FPCOND), regs_.R(inst.src1));106break;107108case IROp::FpCondToReg:109regs_.MapWithExtra(inst, { { 'G', IRREG_FPCOND, 1, MIPSMap::INIT } });110MOVE(regs_.R(inst.dest), regs_.R(IRREG_FPCOND));111regs_.MarkGPRDirty(inst.dest, regs_.IsNormalized32(IRREG_FPCOND));112break;113114case IROp::FpCtrlFromReg:115regs_.MapWithExtra(inst, { { 'G', IRREG_FPCOND, 1, MIPSMap::NOINIT } });116LI(SCRATCH1, 0x0181FFFF);117AND(SCRATCH1, regs_.R(inst.src1), SCRATCH1);118// Extract the new fpcond value.119SRLI_D(regs_.R(IRREG_FPCOND), SCRATCH1, 23);120ANDI(regs_.R(IRREG_FPCOND), regs_.R(IRREG_FPCOND), 1);121ST_W(SCRATCH1, CTXREG, IRREG_FCR31 * 4);122regs_.MarkGPRDirty(IRREG_FPCOND, true);123break;124125case IROp::FpCtrlToReg:126regs_.MapWithExtra(inst, { { 'G', IRREG_FPCOND, 1, MIPSMap::INIT } });127// Load fcr31 and clear the fpcond bit.128LD_W(SCRATCH1, CTXREG, IRREG_FCR31 * 4);129LI(SCRATCH2, ~(1 << 23));130AND(SCRATCH1, SCRATCH1, SCRATCH2);131132// Now get the correct fpcond bit.133ANDI(SCRATCH2, regs_.R(IRREG_FPCOND), 1);134SLLI_D(SCRATCH2, SCRATCH2, 23);135OR(regs_.R(inst.dest), SCRATCH1, SCRATCH2);136137// Also update mips->fcr31 while we're here.138ST_W(regs_.R(inst.dest), CTXREG, IRREG_FCR31 * 4);139regs_.MarkGPRDirty(inst.dest, true);140break;141142case IROp::VfpuCtrlToReg:143regs_.Map(inst);144MOVE(regs_.R(inst.dest), regs_.R(IRREG_VFPU_CTRL_BASE + inst.src1));145regs_.MarkGPRDirty(inst.dest, regs_.IsNormalized32(IRREG_VFPU_CTRL_BASE + inst.src1));146break;147148case IROp::FMovFromGPR:149if (regs_.IsGPRImm(inst.src1) && regs_.GetGPRImm(inst.src1) == 0) {150regs_.MapFPR(inst.dest, MIPSMap::NOINIT);151MOVGR2FR_W(regs_.F(inst.dest), R_ZERO);152} else {153regs_.Map(inst);154MOVGR2FR_W(regs_.F(inst.dest), regs_.R(inst.src1));155}156break;157158case IROp::FMovToGPR:159regs_.Map(inst);160MOVFR2GR_S(regs_.R(inst.dest), regs_.F(inst.src1));161regs_.MarkGPRDirty(inst.dest, true);162break;163164default:165INVALIDOP;166break;167}168}169170void LoongArch64JitBackend::CompIR_System(IRInst inst) {171CONDITIONAL_DISABLE;172173switch (inst.op) {174case IROp::Syscall:175FlushAll();176SaveStaticRegisters();177178WriteDebugProfilerStatus(IRProfilerStatus::SYSCALL);179#ifdef USE_PROFILER180// When profiling, we can't skip CallSyscall, since it times syscalls.181LI(R4, (int32_t)inst.constant);182QuickCallFunction(&CallSyscall, SCRATCH2);183#else184// Skip the CallSyscall where possible.185{186MIPSOpcode op(inst.constant);187void *quickFunc = GetQuickSyscallFunc(op);188if (quickFunc) {189LI(R4, (uintptr_t)GetSyscallFuncPointer(op));190QuickCallFunction((const u8 *)quickFunc, SCRATCH2);191} else {192LI(R4, (int32_t)inst.constant);193QuickCallFunction(&CallSyscall, SCRATCH2);194}195}196#endif197198WriteDebugProfilerStatus(IRProfilerStatus::IN_JIT);199LoadStaticRegisters();200// This is always followed by an ExitToPC, where we check coreState.201break;202203case IROp::CallReplacement:204FlushAll();205SaveStaticRegisters();206WriteDebugProfilerStatus(IRProfilerStatus::REPLACEMENT);207QuickCallFunction(GetReplacementFunc(inst.constant)->replaceFunc, SCRATCH2);208WriteDebugProfilerStatus(IRProfilerStatus::IN_JIT);209LoadStaticRegisters();210211// Do not violate value in R4212MOVE(SCRATCH1, R4);213SRAI_W(SCRATCH2, R4, 31);214// Absolute value trick: if neg, abs(x) == (x ^ -1) + 1.215XOR(SCRATCH1, SCRATCH1, SCRATCH2);216SUB_W(SCRATCH1, SCRATCH1, SCRATCH2);217SUB_D(DOWNCOUNTREG, DOWNCOUNTREG, SCRATCH1);218219// R4 might be the mapped reg, but there's only one.220// Set dest reg to the sign of the result.221regs_.Map(inst);222MOVE(regs_.R(inst.dest), SCRATCH2);223break;224225case IROp::Break:226FlushAll();227// This doesn't naturally have restore/apply around it.228RestoreRoundingMode(true);229SaveStaticRegisters();230MovFromPC(R4);231QuickCallFunction(&Core_BreakException, SCRATCH2);232LoadStaticRegisters();233ApplyRoundingMode(true);234MovFromPC(SCRATCH1);235ADDI_D(SCRATCH1, SCRATCH1, 4);236QuickJ(R_RA, dispatcherPCInSCRATCH1_);237break;238239default:240INVALIDOP;241break;242}243}244245void LoongArch64JitBackend::CompIR_Breakpoint(IRInst inst) {246CONDITIONAL_DISABLE;247248switch (inst.op) {249case IROp::Breakpoint:250case IROp::MemoryCheck:251CompIR_Generic(inst);252break;253254default:255INVALIDOP;256break;257}258}259260void LoongArch64JitBackend::CompIR_ValidateAddress(IRInst inst) {261CONDITIONAL_DISABLE;262263switch (inst.op) {264case IROp::ValidateAddress8:265case IROp::ValidateAddress16:266case IROp::ValidateAddress32:267case IROp::ValidateAddress128:268CompIR_Generic(inst);269break;270271default:272INVALIDOP;273break;274}275}276277} // namespace MIPSComp278279