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/Arm64RegCache.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/Log.h"21#include "Core/MemMap.h"22#include "Core/MIPS/ARM64/Arm64RegCache.h"23#include "Core/MIPS/ARM64/Arm64Jit.h"24#include "Core/MIPS/MIPSAnalyst.h"25#include "Core/Reporting.h"26#include "Common/Arm64Emitter.h"2728#ifndef offsetof29#include "stddef.h"30#endif3132using namespace Arm64Gen;33using namespace Arm64JitConstants;3435Arm64RegCache::Arm64RegCache(MIPSState *mipsState, MIPSComp::JitState *js, MIPSComp::JitOptions *jo) : mips_(mipsState), js_(js), jo_(jo) {36}3738void Arm64RegCache::Init(ARM64XEmitter *emitter) {39emit_ = emitter;40}4142void Arm64RegCache::Start(MIPSAnalyst::AnalysisResults &stats) {43for (int i = 0; i < NUM_ARMREG; i++) {44ar[i].mipsReg = MIPS_REG_INVALID;45ar[i].isDirty = false;46ar[i].pointerified = false;47ar[i].tempLocked = false;48}49for (int i = 0; i < NUM_MIPSREG; i++) {50mr[i].loc = ML_MEM;51mr[i].reg = INVALID_REG;52mr[i].imm = -1;53mr[i].spillLock = false;54mr[i].isStatic = false;55}56int numStatics;57const StaticAllocation *statics = GetStaticAllocations(numStatics);58for (int i = 0; i < numStatics; i++) {59ar[statics[i].ar].mipsReg = statics[i].mr;60ar[statics[i].ar].pointerified = statics[i].pointerified && jo_->enablePointerify;61mr[statics[i].mr].loc = ML_ARMREG;62mr[statics[i].mr].reg = statics[i].ar;63mr[statics[i].mr].isStatic = true;64mr[statics[i].mr].spillLock = true;65}66}6768const ARM64Reg *Arm64RegCache::GetMIPSAllocationOrder(int &count) {69// See register alloc remarks in Arm64Asm.cpp7071// W19-W23 are most suitable for static allocation. Those that are chosen for static allocation72// should be omitted here and added in GetStaticAllocations.73static const ARM64Reg allocationOrder[] = {74W19, W20, W21, W22, W23, W0, W1, W2, W3, W4, W5, W6, W7, W8, W9, W10, W11, W12, W13, W14, W15,75};76static const ARM64Reg allocationOrderStaticAlloc[] = {77W0, W1, W2, W3, W4, W5, W6, W7, W8, W9, W10, W11, W12, W13, W14, W15,78};7980if (jo_->useStaticAlloc) {81count = ARRAY_SIZE(allocationOrderStaticAlloc);82return allocationOrderStaticAlloc;83} else {84count = ARRAY_SIZE(allocationOrder);85return allocationOrder;86}87}8889const Arm64RegCache::StaticAllocation *Arm64RegCache::GetStaticAllocations(int &count) {90static const StaticAllocation allocs[] = {91{MIPS_REG_SP, W19, true},92{MIPS_REG_V0, W20},93{MIPS_REG_V1, W22},94{MIPS_REG_A0, W21},95{MIPS_REG_RA, W23},96};9798if (jo_->useStaticAlloc) {99count = ARRAY_SIZE(allocs);100return allocs;101} else {102count = 0;103return nullptr;104}105}106107void Arm64RegCache::EmitLoadStaticRegisters() {108int count;109const StaticAllocation *allocs = GetStaticAllocations(count);110// TODO: Use LDP when possible.111for (int i = 0; i < count; i++) {112int offset = GetMipsRegOffset(allocs[i].mr);113emit_->LDR(INDEX_UNSIGNED, allocs[i].ar, CTXREG, offset);114if (allocs[i].pointerified && jo_->enablePointerify) {115emit_->MOVK(EncodeRegTo64(allocs[i].ar), ((uint64_t)Memory::base) >> 32, SHIFT_32);116}117}118}119120void Arm64RegCache::EmitSaveStaticRegisters() {121int count;122const StaticAllocation *allocs = GetStaticAllocations(count);123// TODO: Use STP when possible.124// This only needs to run once (by Asm) so checks don't need to be fast.125for (int i = 0; i < count; i++) {126int offset = GetMipsRegOffset(allocs[i].mr);127emit_->STR(INDEX_UNSIGNED, allocs[i].ar, CTXREG, offset);128}129}130131void Arm64RegCache::FlushBeforeCall() {132// These registers are not preserved by function calls.133for (int i = 0; i < 19; ++i) {134FlushArmReg(ARM64Reg(W0 + i));135}136FlushArmReg(W30);137}138139bool Arm64RegCache::IsInRAM(MIPSGPReg reg) {140return mr[reg].loc == ML_MEM;141}142143bool Arm64RegCache::IsMapped(MIPSGPReg mipsReg) {144return mr[mipsReg].loc == ML_ARMREG || mr[mipsReg].loc == ML_ARMREG_IMM;145}146147bool Arm64RegCache::IsMappedAsPointer(MIPSGPReg mipsReg) {148if (mr[mipsReg].loc == ML_ARMREG) {149return ar[mr[mipsReg].reg].pointerified;150} else if (mr[mipsReg].loc == ML_ARMREG_IMM) {151if (ar[mr[mipsReg].reg].pointerified) {152ERROR_LOG(Log::JIT, "Really shouldn't be pointerified here");153}154} else if (mr[mipsReg].loc == ML_ARMREG_AS_PTR) {155return true;156}157return false;158}159160void Arm64RegCache::MarkDirty(ARM64Reg reg) {161ar[reg].isDirty = true;162}163164void Arm64RegCache::SetRegImm(ARM64Reg reg, u64 imm) {165if (reg == INVALID_REG) {166ERROR_LOG(Log::JIT, "SetRegImm to invalid register: at %08x", js_->compilerPC);167return;168}169// On ARM64, at least Cortex A57, good old MOVT/MOVW (MOVK in 64-bit) is really fast.170emit_->MOVI2R(reg, imm);171// ar[reg].pointerified = false;172}173174void Arm64RegCache::MapRegTo(ARM64Reg reg, MIPSGPReg mipsReg, int mapFlags) {175if (mr[mipsReg].isStatic) {176ERROR_LOG(Log::JIT, "Cannot MapRegTo static register %d", mipsReg);177return;178}179ar[reg].isDirty = (mapFlags & MAP_DIRTY) ? true : false;180if ((mapFlags & MAP_NOINIT) != MAP_NOINIT) {181if (mipsReg == MIPS_REG_ZERO) {182// If we get a request to map the zero register, at least we won't spend183// time on a memory access...184emit_->MOVI2R(reg, 0);185186// This way, if we SetImm() it, we'll keep it.187mr[mipsReg].loc = ML_ARMREG_IMM;188mr[mipsReg].imm = 0;189} else {190switch (mr[mipsReg].loc) {191case ML_MEM:192{193int offset = GetMipsRegOffset(mipsReg);194ARM64Reg loadReg = reg;195// INFO_LOG(Log::JIT, "MapRegTo %d mips: %d offset %d", (int)reg, mipsReg, offset);196if (mipsReg == MIPS_REG_LO) {197loadReg = EncodeRegTo64(loadReg);198}199// TODO: Scan ahead / hint when loading multiple regs?200// We could potentially LDP if mipsReg + 1 or mipsReg - 1 is needed.201emit_->LDR(INDEX_UNSIGNED, loadReg, CTXREG, offset);202mr[mipsReg].loc = ML_ARMREG;203break;204}205case ML_IMM:206SetRegImm(reg, mr[mipsReg].imm);207ar[reg].isDirty = true; // IMM is always dirty.208209// If we are mapping dirty, it means we're gonna overwrite.210// So the imm value is no longer valid.211if (mapFlags & MAP_DIRTY)212mr[mipsReg].loc = ML_ARMREG;213else214mr[mipsReg].loc = ML_ARMREG_IMM;215break;216default:217_assert_msg_(mr[mipsReg].loc != ML_ARMREG_AS_PTR, "MapRegTo with a pointer?");218mr[mipsReg].loc = ML_ARMREG;219break;220}221}222} else {223mr[mipsReg].loc = ML_ARMREG;224}225ar[reg].mipsReg = mipsReg;226ar[reg].pointerified = false;227mr[mipsReg].reg = reg;228}229230ARM64Reg Arm64RegCache::AllocateReg() {231int allocCount;232const ARM64Reg *allocOrder = GetMIPSAllocationOrder(allocCount);233234allocate:235for (int i = 0; i < allocCount; i++) {236ARM64Reg reg = allocOrder[i];237238if (ar[reg].mipsReg == MIPS_REG_INVALID && !ar[reg].tempLocked) {239return reg;240}241}242243// Still nothing. Let's spill a reg and goto 10.244// TODO: Use age or something to choose which register to spill?245// TODO: Spill dirty regs first? or opposite?246bool clobbered;247ARM64Reg bestToSpill = FindBestToSpill(true, &clobbered);248if (bestToSpill == INVALID_REG) {249bestToSpill = FindBestToSpill(false, &clobbered);250}251252if (bestToSpill != INVALID_REG) {253if (clobbered) {254DiscardR(ar[bestToSpill].mipsReg);255} else {256FlushArmReg(bestToSpill);257}258// Now one must be free.259goto allocate;260}261262// Uh oh, we have all of them spilllocked....263ERROR_LOG_REPORT(Log::JIT, "Out of spillable registers at PC %08x!!!", mips_->pc);264return INVALID_REG;265}266267ARM64Reg Arm64RegCache::FindBestToSpill(bool unusedOnly, bool *clobbered) {268int allocCount;269const ARM64Reg *allocOrder = GetMIPSAllocationOrder(allocCount);270271static const int UNUSED_LOOKAHEAD_OPS = 30;272273*clobbered = false;274for (int i = 0; i < allocCount; i++) {275ARM64Reg reg = allocOrder[i];276if (ar[reg].mipsReg != MIPS_REG_INVALID && mr[ar[reg].mipsReg].spillLock)277continue;278if (ar[reg].tempLocked)279continue;280281// As it's in alloc-order, we know it's not static so we don't need to check for that.282283// Awesome, a clobbered reg. Let's use it.284if (MIPSAnalyst::IsRegisterClobbered(ar[reg].mipsReg, compilerPC_, UNUSED_LOOKAHEAD_OPS)) {285bool canClobber = true;286// HI is stored inside the LO reg. They both have to clobber at the same time.287if (ar[reg].mipsReg == MIPS_REG_LO) {288canClobber = MIPSAnalyst::IsRegisterClobbered(MIPS_REG_HI, compilerPC_, UNUSED_LOOKAHEAD_OPS);289}290if (canClobber) {291*clobbered = true;292return reg;293}294}295296// Not awesome. A used reg. Let's try to avoid spilling.297if (unusedOnly && MIPSAnalyst::IsRegisterUsed(ar[reg].mipsReg, compilerPC_, UNUSED_LOOKAHEAD_OPS)) {298continue;299}300301return reg;302}303304return INVALID_REG;305}306307ARM64Reg Arm64RegCache::TryMapTempImm(MIPSGPReg r) {308// If already mapped, no need for a temporary.309if (IsMapped(r)) {310return R(r);311}312313if (mr[r].loc == ML_IMM) {314if (mr[r].imm == 0) {315return WZR;316}317318// Try our luck - check for an exact match in another armreg.319for (int i = 0; i < NUM_MIPSREG; ++i) {320if (mr[i].loc == ML_ARMREG_IMM && mr[i].imm == mr[r].imm) {321// Awesome, let's just use this reg.322return mr[i].reg;323}324}325}326327return INVALID_REG;328}329330ARM64Reg Arm64RegCache::GetAndLockTempR() {331ARM64Reg reg = AllocateReg();332if (reg != INVALID_REG) {333ar[reg].tempLocked = true;334}335return reg;336}337338// TODO: Somewhat smarter spilling - currently simply spills the first available, should do339// round robin or FIFO or something.340ARM64Reg Arm64RegCache::MapReg(MIPSGPReg mipsReg, int mapFlags) {341if (mipsReg == MIPS_REG_HI) {342ERROR_LOG_REPORT(Log::JIT, "Cannot map HI in Arm64RegCache");343return INVALID_REG;344}345346if (mipsReg == MIPS_REG_INVALID) {347ERROR_LOG(Log::JIT, "Cannot map invalid register");348return INVALID_REG;349}350351ARM64Reg armReg = mr[mipsReg].reg;352353if (mr[mipsReg].isStatic) {354if (armReg == INVALID_REG) {355ERROR_LOG(Log::JIT, "MapReg on statically mapped reg %d failed - armReg got lost", mipsReg);356}357if (mr[mipsReg].loc == ML_IMM) {358// Back into the register, with or without the imm value.359// If noinit, the MAP_DIRTY check below will take care of the rest.360if ((mapFlags & MAP_NOINIT) != MAP_NOINIT) {361SetRegImm(armReg, mr[mipsReg].imm);362mr[mipsReg].loc = ML_ARMREG_IMM;363ar[armReg].pointerified = false;364}365} else if (mr[mipsReg].loc == ML_ARMREG_AS_PTR) {366// Was mapped as pointer, now we want it mapped as a value, presumably to367// add or subtract stuff to it.368if ((mapFlags & MAP_NOINIT) != MAP_NOINIT) {369emit_->SUB(EncodeRegTo64(armReg), EncodeRegTo64(armReg), MEMBASEREG);370}371mr[mipsReg].loc = ML_ARMREG;372}373// Erasing the imm on dirty (necessary since otherwise we will still think it's ML_ARMREG_IMM and return374// true for IsImm and calculate crazily wrong things). /unknown375if (mapFlags & MAP_DIRTY) {376mr[mipsReg].loc = ML_ARMREG; // As we are dirty, can't keep ARMREG_IMM, we will quickly drift out of sync377ar[armReg].pointerified = false;378ar[armReg].isDirty = true; // Not that it matters379}380return mr[mipsReg].reg;381}382383// Let's see if it's already mapped. If so we just need to update the dirty flag.384// We don't need to check for ML_NOINIT because we assume that anyone who maps385// with that flag immediately writes a "known" value to the register.386if (mr[mipsReg].loc == ML_ARMREG || mr[mipsReg].loc == ML_ARMREG_IMM) {387if (ar[armReg].mipsReg != mipsReg) {388ERROR_LOG_REPORT(Log::JIT, "Register mapping out of sync! %i", mipsReg);389}390if (mapFlags & MAP_DIRTY) {391// Mapping dirty means the old imm value is invalid.392mr[mipsReg].loc = ML_ARMREG;393ar[armReg].isDirty = true;394// If reg is written to, pointerification is lost.395ar[armReg].pointerified = false;396}397398return mr[mipsReg].reg;399} else if (mr[mipsReg].loc == ML_ARMREG_AS_PTR) {400// Was mapped as pointer, now we want it mapped as a value, presumably to401// add or subtract stuff to it.402if ((mapFlags & MAP_NOINIT) != MAP_NOINIT) {403emit_->SUB(EncodeRegTo64(armReg), EncodeRegTo64(armReg), MEMBASEREG);404}405mr[mipsReg].loc = ML_ARMREG;406if (mapFlags & MAP_DIRTY) {407ar[armReg].isDirty = true;408}409return (ARM64Reg)mr[mipsReg].reg;410}411412// Okay, not mapped, so we need to allocate an ARM register.413ARM64Reg reg = AllocateReg();414if (reg != INVALID_REG) {415// Grab it, and load the value into it (if requested).416MapRegTo(reg, mipsReg, mapFlags);417}418419return reg;420}421422Arm64Gen::ARM64Reg Arm64RegCache::MapRegAsPointer(MIPSGPReg reg) {423// Already mapped.424if (mr[reg].loc == ML_ARMREG_AS_PTR) {425return mr[reg].reg;426}427428ARM64Reg retval = INVALID_REG;429if (mr[reg].loc != ML_ARMREG && mr[reg].loc != ML_ARMREG_IMM) {430retval = MapReg(reg);431} else {432retval = mr[reg].reg;433}434435if (mr[reg].loc == ML_ARMREG || mr[reg].loc == ML_ARMREG_IMM) {436// If there was an imm attached, discard it.437mr[reg].loc = ML_ARMREG;438ARM64Reg a = DecodeReg(mr[reg].reg);439if (!jo_->enablePointerify) {440// Convert to a pointer by adding the base and clearing off the top bits.441// If SP, we can probably avoid the top bit clear, let's play with that later.442#ifdef MASKED_PSP_MEMORY443emit_->ANDI2R(EncodeRegTo64(a), EncodeRegTo64(a), 0x3FFFFFFF);444#endif445emit_->ADD(EncodeRegTo64(a), EncodeRegTo64(a), MEMBASEREG);446mr[reg].loc = ML_ARMREG_AS_PTR;447} else if (!ar[a].pointerified) {448emit_->MOVK(EncodeRegTo64(a), ((uint64_t)Memory::base) >> 32, SHIFT_32);449ar[a].pointerified = true;450}451} else {452ERROR_LOG(Log::JIT, "MapRegAsPointer : MapReg failed to allocate a register?");453}454return retval;455}456457void Arm64RegCache::MapIn(MIPSGPReg rs) {458MapReg(rs);459}460461void Arm64RegCache::MapInIn(MIPSGPReg rd, MIPSGPReg rs) {462SpillLock(rd, rs);463MapReg(rd);464MapReg(rs);465ReleaseSpillLock(rd, rs);466}467468void Arm64RegCache::MapDirtyIn(MIPSGPReg rd, MIPSGPReg rs, bool avoidLoad) {469SpillLock(rd, rs);470bool load = !avoidLoad || rd == rs;471MapReg(rd, load ? MAP_DIRTY : MAP_NOINIT);472MapReg(rs);473ReleaseSpillLock(rd, rs);474}475476void Arm64RegCache::MapDirtyInIn(MIPSGPReg rd, MIPSGPReg rs, MIPSGPReg rt, bool avoidLoad) {477SpillLock(rd, rs, rt);478bool load = !avoidLoad || (rd == rs || rd == rt);479MapReg(rd, load ? MAP_DIRTY : MAP_NOINIT);480MapReg(rt);481MapReg(rs);482ReleaseSpillLock(rd, rs, rt);483}484485void Arm64RegCache::MapDirtyDirtyIn(MIPSGPReg rd1, MIPSGPReg rd2, MIPSGPReg rs, bool avoidLoad) {486SpillLock(rd1, rd2, rs);487bool load1 = !avoidLoad || rd1 == rs;488bool load2 = !avoidLoad || rd2 == rs;489MapReg(rd1, load1 ? MAP_DIRTY : MAP_NOINIT);490MapReg(rd2, load2 ? MAP_DIRTY : MAP_NOINIT);491MapReg(rs);492ReleaseSpillLock(rd1, rd2, rs);493}494495void Arm64RegCache::MapDirtyDirtyInIn(MIPSGPReg rd1, MIPSGPReg rd2, MIPSGPReg rs, MIPSGPReg rt, bool avoidLoad) {496SpillLock(rd1, rd2, rs, rt);497bool load1 = !avoidLoad || (rd1 == rs || rd1 == rt);498bool load2 = !avoidLoad || (rd2 == rs || rd2 == rt);499MapReg(rd1, load1 ? MAP_DIRTY : MAP_NOINIT);500MapReg(rd2, load2 ? MAP_DIRTY : MAP_NOINIT);501MapReg(rt);502MapReg(rs);503ReleaseSpillLock(rd1, rd2, rs, rt);504}505506void Arm64RegCache::FlushArmReg(ARM64Reg r) {507if (r == INVALID_REG) {508ERROR_LOG(Log::JIT, "FlushArmReg called on invalid register %d", r);509return;510}511if (ar[r].mipsReg == MIPS_REG_INVALID) {512// Nothing to do, reg not mapped.513if (ar[r].isDirty) {514ERROR_LOG_REPORT(Log::JIT, "Dirty but no mipsreg?");515}516return;517}518if (mr[ar[r].mipsReg].isStatic) {519ERROR_LOG(Log::JIT, "Cannot FlushArmReg a statically mapped register");520return;521}522auto &mreg = mr[ar[r].mipsReg];523if (mreg.loc == ML_ARMREG_IMM || ar[r].mipsReg == MIPS_REG_ZERO) {524// We know its immediate value, no need to STR now.525mreg.loc = ML_IMM;526mreg.reg = INVALID_REG;527} else {528if (mreg.loc == ML_IMM || ar[r].isDirty) {529if (mreg.loc == ML_ARMREG_AS_PTR) {530// Unpointerify, in case dirty.531emit_->SUB(EncodeRegTo64(r), EncodeRegTo64(r), MEMBASEREG);532mreg.loc = ML_ARMREG;533}534// Note: may be a 64-bit reg.535ARM64Reg storeReg = ARM64RegForFlush(ar[r].mipsReg);536if (storeReg != INVALID_REG)537emit_->STR(INDEX_UNSIGNED, storeReg, CTXREG, GetMipsRegOffset(ar[r].mipsReg));538}539mreg.loc = ML_MEM;540mreg.reg = INVALID_REG;541mreg.imm = 0;542}543ar[r].isDirty = false;544ar[r].mipsReg = MIPS_REG_INVALID;545ar[r].pointerified = false;546}547548void Arm64RegCache::DiscardR(MIPSGPReg mipsReg) {549if (mr[mipsReg].isStatic) {550// Simply do nothing unless it's an IMM/ARMREG_IMM/ARMREG_AS_PTR, in case we just switch it over to ARMREG, losing the value.551ARM64Reg armReg = mr[mipsReg].reg;552if (mr[mipsReg].loc == ML_ARMREG_IMM || mr[mipsReg].loc == ML_IMM || mr[mipsReg].loc == ML_ARMREG_AS_PTR) {553// Ignore the imm value, restore sanity554mr[mipsReg].loc = ML_ARMREG;555ar[armReg].pointerified = false;556ar[armReg].isDirty = false;557}558return;559}560const RegMIPSLoc prevLoc = mr[mipsReg].loc;561if (prevLoc == ML_ARMREG || prevLoc == ML_ARMREG_IMM || prevLoc == ML_ARMREG_AS_PTR) {562ARM64Reg armReg = mr[mipsReg].reg;563ar[armReg].isDirty = false;564ar[armReg].mipsReg = MIPS_REG_INVALID;565ar[armReg].pointerified = false;566mr[mipsReg].reg = INVALID_REG;567if (mipsReg == MIPS_REG_ZERO) {568mr[mipsReg].loc = ML_IMM;569} else {570mr[mipsReg].loc = ML_MEM;571}572mr[mipsReg].imm = 0;573}574if (prevLoc == ML_IMM && mipsReg != MIPS_REG_ZERO) {575mr[mipsReg].loc = ML_MEM;576mr[mipsReg].imm = 0;577}578}579580ARM64Reg Arm64RegCache::ARM64RegForFlush(MIPSGPReg r) {581if (mr[r].isStatic)582return INVALID_REG; // No flushing needed583584switch (mr[r].loc) {585case ML_IMM:586if (r == MIPS_REG_ZERO) {587return INVALID_REG;588}589// Zero is super easy.590if (mr[r].imm == 0) {591return WZR;592}593// Could we get lucky? Check for an exact match in another armreg.594for (int i = 0; i < NUM_MIPSREG; ++i) {595if (mr[i].loc == ML_ARMREG_IMM && mr[i].imm == mr[r].imm) {596// Awesome, let's just store this reg.597return mr[i].reg;598}599}600return INVALID_REG;601602case ML_ARMREG:603case ML_ARMREG_IMM:604if (mr[r].reg == INVALID_REG) {605ERROR_LOG_REPORT(Log::JIT, "ARM64RegForFlush: MipsReg %d had bad ArmReg", r);606return INVALID_REG;607}608// No need to flush if it's zero or not dirty.609if (r == MIPS_REG_ZERO || !ar[mr[r].reg].isDirty) {610return INVALID_REG;611}612if (r == MIPS_REG_LO) {613return EncodeRegTo64(mr[r].reg);614}615return mr[r].reg;616617case ML_ARMREG_AS_PTR:618return INVALID_REG;619620case ML_MEM:621return INVALID_REG;622623default:624ERROR_LOG_REPORT(Log::JIT, "ARM64RegForFlush: MipsReg %d with invalid location %d", r, mr[r].loc);625return INVALID_REG;626}627}628629void Arm64RegCache::FlushR(MIPSGPReg r) {630if (mr[r].isStatic) {631ERROR_LOG(Log::JIT, "Cannot flush static reg %d", r);632return;633}634635switch (mr[r].loc) {636case ML_IMM:637// IMM is always "dirty".638if (r == MIPS_REG_LO) {639SetRegImm(SCRATCH1_64, mr[r].imm);640emit_->STR(INDEX_UNSIGNED, SCRATCH1_64, CTXREG, GetMipsRegOffset(r));641} else if (r != MIPS_REG_ZERO) {642// Try to optimize using a different reg.643ARM64Reg storeReg = ARM64RegForFlush(r);644if (storeReg == INVALID_REG) {645SetRegImm(SCRATCH1, mr[r].imm);646storeReg = SCRATCH1;647}648emit_->STR(INDEX_UNSIGNED, storeReg, CTXREG, GetMipsRegOffset(r));649}650break;651652case ML_ARMREG:653case ML_ARMREG_IMM:654if (ar[mr[r].reg].isDirty) {655// Note: might be a 64-bit reg.656ARM64Reg storeReg = ARM64RegForFlush(r);657if (storeReg != INVALID_REG) {658emit_->STR(INDEX_UNSIGNED, storeReg, CTXREG, GetMipsRegOffset(r));659}660ar[mr[r].reg].isDirty = false;661}662ar[mr[r].reg].mipsReg = MIPS_REG_INVALID;663ar[mr[r].reg].pointerified = false;664break;665666case ML_ARMREG_AS_PTR:667if (ar[mr[r].reg].isDirty) {668emit_->SUB(EncodeRegTo64(mr[r].reg), EncodeRegTo64(mr[r].reg), MEMBASEREG);669// We set this so ARM64RegForFlush knows it's no longer a pointer.670mr[r].loc = ML_ARMREG;671ARM64Reg storeReg = ARM64RegForFlush(r);672if (storeReg != INVALID_REG) {673emit_->STR(INDEX_UNSIGNED, storeReg, CTXREG, GetMipsRegOffset(r));674}675ar[mr[r].reg].isDirty = false;676}677ar[mr[r].reg].mipsReg = MIPS_REG_INVALID;678break;679680case ML_MEM:681// Already there, nothing to do.682break;683684default:685ERROR_LOG_REPORT(Log::JIT, "FlushR: MipsReg %d with invalid location %d", r, mr[r].loc);686break;687}688if (r == MIPS_REG_ZERO) {689mr[r].loc = ML_IMM;690} else {691mr[r].loc = ML_MEM;692}693mr[r].reg = INVALID_REG;694mr[r].imm = 0;695}696697void Arm64RegCache::FlushAll() {698// Note: make sure not to change the registers when flushing:699// Branching code expects the armreg to retain its value.700701// LO can't be included in a 32-bit pair, since it's 64 bit.702// Flush it first so we don't get it confused.703FlushR(MIPS_REG_LO);704705// Try to flush in pairs when possible.706// 1 because MIPS_REG_ZERO isn't flushable anyway.707// 31 because 30 and 31 are the last possible pair - MIPS_REG_FPCOND, etc. are too far away.708for (int i = 1; i < 31; i++) {709MIPSGPReg mreg1 = MIPSGPReg(i);710MIPSGPReg mreg2 = MIPSGPReg(i + 1);711ARM64Reg areg1 = ARM64RegForFlush(mreg1);712ARM64Reg areg2 = ARM64RegForFlush(mreg2);713714// If either one doesn't have a reg yet, try flushing imms to scratch regs.715if (areg1 == INVALID_REG && IsPureImm(mreg1) && !mr[i].isStatic) {716areg1 = SCRATCH1;717}718if (areg2 == INVALID_REG && IsPureImm(mreg2) && !mr[i + 1].isStatic) {719areg2 = SCRATCH2;720}721722if (areg1 != INVALID_REG && areg2 != INVALID_REG) {723// Actually put the imms in place now that we know we can do the STP.724// We didn't do it before in case the other wouldn't work.725if (areg1 == SCRATCH1) {726SetRegImm(areg1, GetImm(mreg1));727}728if (areg2 == SCRATCH2) {729SetRegImm(areg2, GetImm(mreg2));730}731732// We can use a paired store, awesome.733emit_->STP(INDEX_SIGNED, areg1, areg2, CTXREG, GetMipsRegOffset(mreg1));734735// Now we mark them as stored by discarding.736DiscardR(mreg1);737DiscardR(mreg2);738}739}740741// Final pass to grab any that were left behind.742for (int i = 0; i < NUM_MIPSREG; i++) {743MIPSGPReg mipsReg = MIPSGPReg(i);744if (mr[i].isStatic) {745Arm64Gen::ARM64Reg armReg = mr[i].reg;746// Cannot leave any IMMs in registers, not even ML_ARMREG_IMM, can confuse the regalloc later if this flush is mid-block747// due to an interpreter fallback that changes the register.748if (mr[i].loc == ML_IMM) {749SetRegImm(mr[i].reg, mr[i].imm);750mr[i].loc = ML_ARMREG;751ar[armReg].pointerified = false;752} else if (mr[i].loc == ML_ARMREG_IMM) {753// The register already contains the immediate.754if (ar[armReg].pointerified) {755ERROR_LOG(Log::JIT, "ML_ARMREG_IMM but pointerified. Wrong.");756ar[armReg].pointerified = false;757}758mr[i].loc = ML_ARMREG;759} else if (mr[i].loc == ML_ARMREG_AS_PTR) {760emit_->SUB(EncodeRegTo64(armReg), EncodeRegTo64(armReg), MEMBASEREG);761mr[i].loc = ML_ARMREG;762}763if (i != MIPS_REG_ZERO && mr[i].reg == INVALID_REG) {764ERROR_LOG(Log::JIT, "ARM reg of static %i is invalid", i);765continue;766}767} else {768FlushR(mipsReg);769}770}771772int count = 0;773const StaticAllocation *allocs = GetStaticAllocations(count);774for (int i = 0; i < count; i++) {775if (allocs[i].pointerified && !ar[allocs[i].ar].pointerified && jo_->enablePointerify) {776// Re-pointerify777emit_->MOVK(EncodeRegTo64(allocs[i].ar), ((uint64_t)Memory::base) >> 32, SHIFT_32);778ar[allocs[i].ar].pointerified = true;779} else if (!allocs[i].pointerified) {780// If this register got pointerified on the way, mark it as not, so that after save/reload (like in an interpreter fallback), it won't be regarded as such, as it simply won't be.781ar[allocs[i].ar].pointerified = false;782}783}784// Sanity check785for (int i = 0; i < NUM_ARMREG; i++) {786if (ar[i].mipsReg != MIPS_REG_INVALID && mr[ar[i].mipsReg].isStatic == false) {787ERROR_LOG_REPORT(Log::JIT, "Flush fail: ar[%i].mipsReg=%i", i, ar[i].mipsReg);788}789}790}791792void Arm64RegCache::SetImm(MIPSGPReg r, u64 immVal) {793if (r == MIPS_REG_HI) {794ERROR_LOG_REPORT(Log::JIT, "Cannot set HI imm in Arm64RegCache");795return;796}797if (r == MIPS_REG_ZERO && immVal != 0) {798ERROR_LOG_REPORT(Log::JIT, "Trying to set immediate %08x to r0 at %08x", (u32)immVal, compilerPC_);799return;800}801802if (mr[r].loc == ML_ARMREG_IMM && mr[r].imm == immVal) {803// Already have that value, let's keep it in the reg.804return;805}806807if (r != MIPS_REG_LO) {808// All regs on the PSP are 32 bit, but LO we treat as HI:LO so is 64 full bits.809immVal = immVal & 0xFFFFFFFF;810}811812if (mr[r].isStatic) {813mr[r].loc = ML_IMM;814mr[r].imm = immVal;815ar[mr[r].reg].pointerified = false;816// We do not change reg to INVALID_REG for obvious reasons..817} else {818// Zap existing value if cached in a reg819if (mr[r].reg != INVALID_REG) {820ar[mr[r].reg].mipsReg = MIPS_REG_INVALID;821ar[mr[r].reg].isDirty = false;822ar[mr[r].reg].pointerified = false;823}824mr[r].loc = ML_IMM;825mr[r].imm = immVal;826mr[r].reg = INVALID_REG;827}828}829830bool Arm64RegCache::IsImm(MIPSGPReg r) const {831if (r == MIPS_REG_ZERO)832return true;833else834return mr[r].loc == ML_IMM || mr[r].loc == ML_ARMREG_IMM;835}836837bool Arm64RegCache::IsPureImm(MIPSGPReg r) const {838if (r == MIPS_REG_ZERO)839return true;840else841return mr[r].loc == ML_IMM;842}843844u64 Arm64RegCache::GetImm(MIPSGPReg r) const {845if (r == MIPS_REG_ZERO)846return 0;847if (mr[r].loc != ML_IMM && mr[r].loc != ML_ARMREG_IMM) {848ERROR_LOG_REPORT(Log::JIT, "Trying to get imm from non-imm register %i", r);849}850return mr[r].imm;851}852853int Arm64RegCache::GetMipsRegOffset(MIPSGPReg r) {854if (r < 32)855return r * 4;856switch (r) {857case MIPS_REG_HI:858return offsetof(MIPSState, hi);859case MIPS_REG_LO:860return offsetof(MIPSState, lo);861case MIPS_REG_FPCOND:862return offsetof(MIPSState, fpcond);863case MIPS_REG_VFPUCC:864return offsetof(MIPSState, vfpuCtrl[VFPU_CTRL_CC]);865default:866ERROR_LOG_REPORT(Log::JIT, "bad mips register %i", r);867return 0; // or what?868}869}870871void Arm64RegCache::SpillLock(MIPSGPReg r1, MIPSGPReg r2, MIPSGPReg r3, MIPSGPReg r4) {872mr[r1].spillLock = true;873if (r2 != MIPS_REG_INVALID) mr[r2].spillLock = true;874if (r3 != MIPS_REG_INVALID) mr[r3].spillLock = true;875if (r4 != MIPS_REG_INVALID) mr[r4].spillLock = true;876}877878void Arm64RegCache::ReleaseSpillLocksAndDiscardTemps() {879for (int i = 0; i < NUM_MIPSREG; i++) {880if (!mr[i].isStatic)881mr[i].spillLock = false;882}883for (int i = 0; i < NUM_ARMREG; i++) {884ar[i].tempLocked = false;885}886}887888void Arm64RegCache::ReleaseSpillLock(MIPSGPReg r1, MIPSGPReg r2, MIPSGPReg r3, MIPSGPReg r4) {889if (!mr[r1].isStatic)890mr[r1].spillLock = false;891if (r2 != MIPS_REG_INVALID && !mr[r2].isStatic)892mr[r2].spillLock = false;893if (r3 != MIPS_REG_INVALID && !mr[r3].isStatic)894mr[r3].spillLock = false;895if (r4 != MIPS_REG_INVALID && !mr[r4].isStatic)896mr[r4].spillLock = false;897}898899ARM64Reg Arm64RegCache::R(MIPSGPReg mipsReg) {900if (mr[mipsReg].loc == ML_ARMREG || mr[mipsReg].loc == ML_ARMREG_IMM) {901return mr[mipsReg].reg;902} else {903ERROR_LOG_REPORT(Log::JIT, "Reg %i not in arm reg. compilerPC = %08x", mipsReg, compilerPC_);904return INVALID_REG; // BAAAD905}906}907908ARM64Reg Arm64RegCache::RPtr(MIPSGPReg mipsReg) {909if (mr[mipsReg].loc == ML_ARMREG_AS_PTR) {910return (ARM64Reg)mr[mipsReg].reg;911} else if (mr[mipsReg].loc == ML_ARMREG || mr[mipsReg].loc == ML_ARMREG_IMM) {912int a = mr[mipsReg].reg;913if (ar[a].pointerified) {914return (ARM64Reg)mr[mipsReg].reg;915} else {916ERROR_LOG(Log::JIT, "Tried to use a non-pointer register as a pointer");917return INVALID_REG;918}919} else {920ERROR_LOG_REPORT(Log::JIT, "Reg %i not in arm reg. compilerPC = %08x", mipsReg, compilerPC_);921return INVALID_REG; // BAAAD922}923}924925#endif // PPSSPP_ARCH(ARM64)926927928