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/Arm64IRAsm.cpp
Views: 1401
// 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 "ppsspp_config.h"18// In other words, PPSSPP_ARCH(ARM64) || DISASM_ALL.19#if PPSSPP_ARCH(ARM64) || (PPSSPP_PLATFORM(WINDOWS) && !defined(__LIBRETRO__))2021#include "Common/Log.h"22#include "Core/CoreTiming.h"23#include "Core/MemMap.h"24#include "Core/MIPS/ARM64/Arm64IRJit.h"25#include "Core/MIPS/ARM64/Arm64IRRegCache.h"26#include "Core/MIPS/JitCommon/JitCommon.h"27#include "Core/MIPS/JitCommon/JitState.h"28#include "Core/System.h"2930namespace MIPSComp {3132using namespace Arm64Gen;33using namespace Arm64IRJitConstants;3435static const bool enableDebug = false;36static const bool enableDisasm = false;3738static void ShowPC(void *membase, void *jitbase) {39static int count = 0;40if (currentMIPS) {41u32 downcount = currentMIPS->downcount;42ERROR_LOG(Log::JIT, "[%08x] ShowPC Downcount : %08x %d %p %p", currentMIPS->pc, downcount, count, membase, jitbase);43} else {44ERROR_LOG(Log::JIT, "Universe corrupt?");45}46//if (count > 2000)47// exit(0);48count++;49}5051void Arm64JitBackend::GenerateFixedCode(MIPSState *mipsState) {52// This will be used as a writable scratch area, always 32-bit accessible.53const u8 *start = AlignCodePage();54if (DebugProfilerEnabled()) {55ProtectMemoryPages(start, GetMemoryProtectPageSize(), MEM_PROT_READ | MEM_PROT_WRITE);56hooks_.profilerPC = (uint32_t *)GetWritableCodePtr();57Write32(0);58hooks_.profilerStatus = (IRProfilerStatus *)GetWritableCodePtr();59Write32(0);60}6162const u8 *disasmStart = AlignCodePage();63BeginWrite(GetMemoryProtectPageSize());6465if (jo.useStaticAlloc) {66saveStaticRegisters_ = AlignCode16();67STR(INDEX_UNSIGNED, DOWNCOUNTREG, CTXREG, offsetof(MIPSState, downcount));68regs_.EmitSaveStaticRegisters();69RET();7071loadStaticRegisters_ = AlignCode16();72regs_.EmitLoadStaticRegisters();73LDR(INDEX_UNSIGNED, DOWNCOUNTREG, CTXREG, offsetof(MIPSState, downcount));74RET();75} else {76saveStaticRegisters_ = nullptr;77loadStaticRegisters_ = nullptr;78}7980restoreRoundingMode_ = AlignCode16();81{82MRS(SCRATCH2_64, FIELD_FPCR);83// We are not in flush-to-zero mode outside the JIT, so let's turn it off.84uint32_t mask = ~(4 << 22);85// Assume we're always in round-to-nearest mode beforehand.86mask &= ~(3 << 22);87ANDI2R(SCRATCH2, SCRATCH2, mask);88_MSR(FIELD_FPCR, SCRATCH2_64);89RET();90}9192applyRoundingMode_ = AlignCode16();93{94LDR(INDEX_UNSIGNED, SCRATCH1, CTXREG, offsetof(MIPSState, fcr31));95ANDI2R(SCRATCH2, SCRATCH1, 3);96FixupBranch skip1 = TBZ(SCRATCH1, 24);97ADDI2R(SCRATCH2, SCRATCH2, 4);98SetJumpTarget(skip1);99100// We can skip if the rounding mode is nearest (0) and flush is not set.101// (as restoreRoundingMode cleared it out anyway)102FixupBranch skip = CBZ(SCRATCH2);103104// MIPS Rounding Mode: ARM Rounding Mode105// 0: Round nearest 0106// 1: Round to zero 3107// 2: Round up (ceil) 1108// 3: Round down (floor) 2109ANDI2R(SCRATCH1, SCRATCH2, 3);110CMPI2R(SCRATCH1, 1);111112FixupBranch skipadd = B(CC_NEQ);113ADDI2R(SCRATCH2, SCRATCH2, 2);114SetJumpTarget(skipadd);115FixupBranch skipsub = B(CC_LE);116SUBI2R(SCRATCH2, SCRATCH2, 1);117SetJumpTarget(skipsub);118119// Actually change the system FPCR register120MRS(SCRATCH1_64, FIELD_FPCR);121// Clear both flush-to-zero and rounding before re-setting them.122ANDI2R(SCRATCH1, SCRATCH1, ~((4 | 3) << 22));123ORR(SCRATCH1, SCRATCH1, SCRATCH2, ArithOption(SCRATCH2, ST_LSL, 22));124_MSR(FIELD_FPCR, SCRATCH1_64);125126SetJumpTarget(skip);127RET();128}129130updateRoundingMode_ = AlignCode16();131{132LDR(INDEX_UNSIGNED, SCRATCH1, CTXREG, offsetof(MIPSState, fcr31));133134// Set SCRATCH2 to FZ:RM (FZ is bit 24, and RM are lowest 2 bits.)135ANDI2R(SCRATCH2, SCRATCH1, 3);136FixupBranch skip = TBZ(SCRATCH1, 24);137ADDI2R(SCRATCH2, SCRATCH2, 4);138SetJumpTarget(skip);139140// Update currentRoundingFunc_ with the right convertS0ToSCRATCH1_ func.141MOVP2R(SCRATCH1_64, convertS0ToSCRATCH1_);142LSL(SCRATCH2, SCRATCH2, 3);143LDR(SCRATCH2_64, SCRATCH1_64, SCRATCH2);144MOVP2R(SCRATCH1_64, ¤tRoundingFunc_);145STR(INDEX_UNSIGNED, SCRATCH2_64, SCRATCH1_64, 0);146RET();147}148149hooks_.enterDispatcher = (IRNativeFuncNoArg)AlignCode16();150151uint32_t regs_to_save = Arm64Gen::ALL_CALLEE_SAVED;152uint32_t regs_to_save_fp = Arm64Gen::ALL_CALLEE_SAVED_FP;153fp_.ABI_PushRegisters(regs_to_save, regs_to_save_fp);154155// Fixed registers, these are always kept when in Jit context.156MOVP2R(MEMBASEREG, Memory::base);157MOVP2R(CTXREG, mipsState);158// Pre-subtract this to save time later.159MOVI2R(JITBASEREG, (intptr_t)GetBasePtr() - MIPS_EMUHACK_OPCODE);160161LoadStaticRegisters();162WriteDebugProfilerStatus(IRProfilerStatus::IN_JIT);163MovFromPC(SCRATCH1);164WriteDebugPC(SCRATCH1);165outerLoopPCInSCRATCH1_ = GetCodePtr();166MovToPC(SCRATCH1);167outerLoop_ = GetCodePtr();168SaveStaticRegisters(); // Advance can change the downcount, so must save/restore169RestoreRoundingMode(true);170WriteDebugProfilerStatus(IRProfilerStatus::TIMER_ADVANCE);171QuickCallFunction(SCRATCH1_64, &CoreTiming::Advance);172WriteDebugProfilerStatus(IRProfilerStatus::IN_JIT);173ApplyRoundingMode(true);174LoadStaticRegisters();175176dispatcherCheckCoreState_ = GetCodePtr();177178MOVP2R(SCRATCH1_64, &coreState);179LDR(INDEX_UNSIGNED, SCRATCH1, SCRATCH1_64, 0);180FixupBranch badCoreState = CBNZ(SCRATCH1);181182// Check downcount.183TBNZ(DOWNCOUNTREG, 31, outerLoop_);184FixupBranch skipToRealDispatch = B();185186dispatcherPCInSCRATCH1_ = GetCodePtr();187MovToPC(SCRATCH1);188189hooks_.dispatcher = GetCodePtr();190191FixupBranch bail = TBNZ(DOWNCOUNTREG, 31);192SetJumpTarget(skipToRealDispatch);193194dispatcherNoCheck_ = GetCodePtr();195196// Debug197if (enableDebug) {198MOV(W0, DOWNCOUNTREG);199MOV(X1, MEMBASEREG);200MOV(X2, JITBASEREG);201QuickCallFunction(SCRATCH1_64, &ShowPC);202}203204MovFromPC(SCRATCH1);205WriteDebugPC(SCRATCH1);206#ifdef MASKED_PSP_MEMORY207ANDI2R(SCRATCH1, SCRATCH1, Memory::MEMVIEW32_MASK);208#endif209hooks_.dispatchFetch = GetCodePtr();210LDR(SCRATCH1, MEMBASEREG, SCRATCH1_64);211LSR(SCRATCH2, SCRATCH1, 24); // or UBFX(SCRATCH2, SCRATCH1, 24, 8)212// We don't mask SCRATCH1 as that's already baked into JITBASEREG.213CMP(SCRATCH2, MIPS_EMUHACK_OPCODE >> 24);214FixupBranch skipJump = B(CC_NEQ);215ADD(SCRATCH1_64, JITBASEREG, SCRATCH1_64);216BR(SCRATCH1_64);217SetJumpTarget(skipJump);218219// No block found, let's jit. We don't need to save static regs, they're all callee saved.220RestoreRoundingMode(true);221WriteDebugProfilerStatus(IRProfilerStatus::COMPILING);222QuickCallFunction(SCRATCH1_64, &MIPSComp::JitAt);223WriteDebugProfilerStatus(IRProfilerStatus::IN_JIT);224ApplyRoundingMode(true);225226// Let's just dispatch again, we'll enter the block since we know it's there.227B(dispatcherNoCheck_);228229SetJumpTarget(bail);230231MOVP2R(SCRATCH1_64, &coreState);232LDR(INDEX_UNSIGNED, SCRATCH1, SCRATCH1_64, 0);233CBZ(SCRATCH1, outerLoop_);234235const uint8_t *quitLoop = GetCodePtr();236SetJumpTarget(badCoreState);237238WriteDebugProfilerStatus(IRProfilerStatus::NOT_RUNNING);239SaveStaticRegisters();240RestoreRoundingMode(true);241242fp_.ABI_PopRegisters(regs_to_save, regs_to_save_fp);243244RET();245246hooks_.crashHandler = GetCodePtr();247MOVP2R(SCRATCH1_64, &coreState);248MOVI2R(SCRATCH2, CORE_RUNTIME_ERROR);249STR(INDEX_UNSIGNED, SCRATCH2, SCRATCH1_64, 0);250B(quitLoop);251252// Generate some integer conversion funcs.253// MIPS order!254static const RoundingMode roundModes[8] = { ROUND_N, ROUND_Z, ROUND_P, ROUND_M, ROUND_N, ROUND_Z, ROUND_P, ROUND_M };255for (size_t i = 0; i < ARRAY_SIZE(roundModes); ++i) {256convertS0ToSCRATCH1_[i] = AlignCode16();257258// Invert 0x80000000 -> 0x7FFFFFFF for the NAN result.259fp_.MVNI(32, EncodeRegToDouble(SCRATCHF2), 0x80, 24);260fp_.FCMP(S0, S0); // Detect NaN261fp_.FCVTS(S0, S0, roundModes[i]);262fp_.FCSEL(S0, S0, SCRATCHF2, CC_VC);263264RET();265}266267// Leave this at the end, add more stuff above.268if (enableDisasm) {269std::vector<std::string> lines = DisassembleArm64(disasmStart, (int)(GetCodePtr() - disasmStart));270for (auto s : lines) {271INFO_LOG(Log::JIT, "%s", s.c_str());272}273}274275// Let's spare the pre-generated code from unprotect-reprotect.276AlignCodePage();277jitStartOffset_ = (int)(GetCodePtr() - start);278// Don't forget to zap the instruction cache! This must stay at the end of this function.279FlushIcache();280EndWrite();281282// Update our current cached rounding mode func, too.283UpdateFCR31(mipsState);284}285286} // namespace MIPSComp287288#endif289290291