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/x86/RegCache.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(X86) || PPSSPP_ARCH(AMD64)1920#include <cstring>2122#include "Common/x64Emitter.h"23#include "Core/Reporting.h"24#include "Core/MIPS/MIPS.h"25#include "Core/MIPS/MIPSTables.h"26#include "Core/MIPS/MIPSAnalyst.h"27#include "Core/MIPS/x86/Jit.h"28#include "Core/MIPS/x86/RegCache.h"2930using namespace Gen;31using namespace X64JitConstants;3233static const X64Reg allocationOrder[] = {34// R12, when used as base register, for example in a LEA, can generate bad code! Need to look into this.35// On x64, RCX and RDX are the first args. CallProtectedFunction() assumes they're not regcached.36#if PPSSPP_ARCH(AMD64)37#ifdef _WIN3238RSI, RDI, R8, R9, R10, R11, R12, R13,39#else40RBP, R8, R9, R10, R11, R12, R13,41#endif42#elif PPSSPP_ARCH(X86)43ESI, EDI, EDX, ECX, EBX,44#endif45};4647#if PPSSPP_ARCH(AMD64)48static X64Reg allocationOrderR15[ARRAY_SIZE(allocationOrder) + 1] = {INVALID_REG};49#endif5051void GPRRegCache::FlushBeforeCall() {52// TODO: Only flush the non-preserved-by-callee registers.53Flush();54}5556GPRRegCache::GPRRegCache() {57}5859void GPRRegCache::Start(MIPSState *mipsState, MIPSComp::JitState *js, MIPSComp::JitOptions *jo, MIPSAnalyst::AnalysisResults &stats) {60#if PPSSPP_ARCH(AMD64)61if (allocationOrderR15[0] == INVALID_REG) {62memcpy(allocationOrderR15, allocationOrder, sizeof(allocationOrder));63allocationOrderR15[ARRAY_SIZE(allocationOrderR15) - 1] = R15;64}65#endif6667mips_ = mipsState;68for (int i = 0; i < NUM_X_REGS; i++) {69xregs[i].free = true;70xregs[i].dirty = false;71xregs[i].allocLocked = false;72}73memset(regs, 0, sizeof(regs));74OpArg base = GetDefaultLocation(MIPS_REG_ZERO);75for (int i = 0; i < 32; i++) {76regs[i].location = base;77base.IncreaseOffset(sizeof(u32));78}79for (int i = 32; i < NUM_MIPS_GPRS; i++) {80regs[i].location = GetDefaultLocation(MIPSGPReg(i));81}82SetImm(MIPS_REG_ZERO, 0);8384// todo: sort to find the most popular regs85/*86int maxPreload = 2;87for (int i = 0; i < NUM_MIPS_GPRS; i++)88{89if (stats.numReads[i] > 2 || stats.numWrites[i] >= 2)90{91LoadToX64(i, true, false); //stats.firstRead[i] <= stats.firstWrite[i], false);92maxPreload--;93if (!maxPreload)94break;95}96}*/97//Find top regs - preload them (load bursts ain't bad)98//But only preload IF written OR reads >= 399100js_ = js;101jo_ = jo;102}103104105// these are MIPS reg indices106void GPRRegCache::Lock(MIPSGPReg p1, MIPSGPReg p2, MIPSGPReg p3, MIPSGPReg p4) {107regs[p1].locked = true;108if (p2 != MIPS_REG_INVALID) regs[p2].locked = true;109if (p3 != MIPS_REG_INVALID) regs[p3].locked = true;110if (p4 != MIPS_REG_INVALID) regs[p4].locked = true;111}112113// these are x64 reg indices114void GPRRegCache::LockX(int x1, int x2, int x3, int x4) {115_assert_msg_(!xregs[x1].allocLocked, "RegCache: x %d already locked!", x1);116xregs[x1].allocLocked = true;117if (x2 != 0xFF) xregs[x2].allocLocked = true;118if (x3 != 0xFF) xregs[x3].allocLocked = true;119if (x4 != 0xFF) xregs[x4].allocLocked = true;120}121122void GPRRegCache::UnlockAll() {123for (int i = 0; i < NUM_MIPS_GPRS; i++)124regs[i].locked = false;125// In case it was stored, discard it now.126SetImm(MIPS_REG_ZERO, 0);127}128129void GPRRegCache::UnlockAllX() {130for (int i = 0; i < NUM_X_REGS; i++)131xregs[i].allocLocked = false;132}133134X64Reg GPRRegCache::FindBestToSpill(bool unusedOnly, bool *clobbered) {135int allocCount;136const X64Reg *allocOrder = GetAllocationOrder(allocCount);137138static const int UNUSED_LOOKAHEAD_OPS = 30;139140*clobbered = false;141for (int i = 0; i < allocCount; i++) {142X64Reg reg = allocOrder[i];143if (xregs[reg].allocLocked)144continue;145if (xregs[reg].mipsReg != MIPS_REG_INVALID && regs[xregs[reg].mipsReg].locked)146continue;147148// Awesome, a clobbered reg. Let's use it.149if (MIPSAnalyst::IsRegisterClobbered(xregs[reg].mipsReg, js_->compilerPC, UNUSED_LOOKAHEAD_OPS)) {150*clobbered = true;151return reg;152}153154// Not awesome. A used reg. Let's try to avoid spilling.155if (unusedOnly && MIPSAnalyst::IsRegisterUsed(xregs[reg].mipsReg, js_->compilerPC, UNUSED_LOOKAHEAD_OPS)) {156continue;157}158159return reg;160}161162return INVALID_REG;163}164165X64Reg GPRRegCache::GetFreeXReg()166{167int aCount;168const X64Reg *aOrder = GetAllocationOrder(aCount);169for (int i = 0; i < aCount; i++)170{171X64Reg xr = aOrder[i];172if (!xregs[xr].allocLocked && xregs[xr].free)173{174return xr;175}176}177178//Okay, not found :( Force grab one179bool clobbered;180X64Reg bestToSpill = FindBestToSpill(true, &clobbered);181if (bestToSpill == INVALID_REG) {182bestToSpill = FindBestToSpill(false, &clobbered);183}184185if (bestToSpill != INVALID_REG) {186// TODO: Broken somehow in Dante's Inferno, but most games work. Bad flags in MIPSTables somewhere?187if (clobbered) {188DiscardRegContentsIfCached(xregs[bestToSpill].mipsReg);189} else {190StoreFromRegister(xregs[bestToSpill].mipsReg);191}192return bestToSpill;193}194195// Still no dice? Give up.196_assert_msg_(false, "Regcache ran out of regs");197return (X64Reg)-1;198}199200void GPRRegCache::FlushR(X64Reg reg)201{202if (reg >= NUM_X_REGS) {203_assert_msg_(false, "Flushing non existent reg");204} else if (!xregs[reg].free) {205StoreFromRegister(xregs[reg].mipsReg);206}207}208209void GPRRegCache::FlushRemap(MIPSGPReg oldreg, MIPSGPReg newreg) {210OpArg oldLocation = regs[oldreg].location;211_assert_msg_(oldLocation.IsSimpleReg(), "FlushRemap: Must already be in an x86 register");212213X64Reg xr = oldLocation.GetSimpleReg();214215if (oldreg == newreg) {216xregs[xr].dirty = true;217return;218}219220StoreFromRegister(oldreg);221222// Now, if newreg already was mapped somewhere, get rid of that.223DiscardRegContentsIfCached(newreg);224225// Now, take over the old register.226regs[newreg].location = oldLocation;227regs[newreg].away = true;228regs[newreg].locked = true;229xregs[xr].mipsReg = newreg;230xregs[xr].dirty = true;231xregs[xr].free = false;232}233234int GPRRegCache::SanityCheck() const {235for (int i = 0; i < NUM_MIPS_GPRS; i++) {236const MIPSGPReg r = MIPSGPReg(i);237if (regs[i].away) {238if (regs[i].location.IsSimpleReg()) {239Gen::X64Reg simple = regs[i].location.GetSimpleReg();240if (xregs[simple].allocLocked)241return 1;242if (xregs[simple].mipsReg != r)243return 2;244}245else if (regs[i].location.IsImm())246return 3;247}248}249return 0;250}251252void GPRRegCache::DiscardRegContentsIfCached(MIPSGPReg preg) {253if (regs[preg].away && regs[preg].location.IsSimpleReg()) {254X64Reg xr = regs[preg].location.GetSimpleReg();255xregs[xr].free = true;256xregs[xr].dirty = false;257xregs[xr].mipsReg = MIPS_REG_INVALID;258regs[preg].away = false;259if (preg == MIPS_REG_ZERO) {260regs[preg].location = Imm32(0);261} else {262regs[preg].location = GetDefaultLocation(preg);263}264}265}266267void GPRRegCache::DiscardR(MIPSGPReg preg) {268if (regs[preg].away) {269if (regs[preg].location.IsSimpleReg()) {270DiscardRegContentsIfCached(preg);271} else {272regs[preg].away = false;273if (preg == MIPS_REG_ZERO) {274regs[preg].location = Imm32(0);275} else {276regs[preg].location = GetDefaultLocation(preg);277}278}279}280}281282283void GPRRegCache::SetImm(MIPSGPReg preg, u32 immValue) {284// ZERO is always zero. Let's just make sure.285if (preg == MIPS_REG_ZERO)286immValue = 0;287288DiscardRegContentsIfCached(preg);289regs[preg].away = true;290regs[preg].location = Imm32(immValue);291}292293bool GPRRegCache::IsImm(MIPSGPReg preg) const {294// Note that ZERO is generally always imm.295return regs[preg].location.IsImm();296}297298u32 GPRRegCache::GetImm(MIPSGPReg preg) const {299_dbg_assert_msg_(IsImm(preg), "Reg %d must be an immediate.", preg);300// Always 0 for ZERO.301if (preg == MIPS_REG_ZERO)302return 0;303return regs[preg].location.GetImmValue();304}305306const X64Reg *GPRRegCache::GetAllocationOrder(int &count) {307#if PPSSPP_ARCH(AMD64)308if (!jo_->reserveR15ForAsm) {309count = ARRAY_SIZE(allocationOrderR15);310return allocationOrderR15;311}312#endif313count = ARRAY_SIZE(allocationOrder);314return allocationOrder;315}316317318OpArg GPRRegCache::GetDefaultLocation(MIPSGPReg reg) const {319if (reg < 32) {320return MDisp(CTXREG, -128 + reg * 4);321}322switch (reg) {323case MIPS_REG_HI:324return MIPSSTATE_VAR(hi);325case MIPS_REG_LO:326return MIPSSTATE_VAR(lo);327case MIPS_REG_FPCOND:328return MIPSSTATE_VAR(fpcond);329case MIPS_REG_VFPUCC:330return MIPSSTATE_VAR(vfpuCtrl[VFPU_CTRL_CC]);331default:332ERROR_LOG_REPORT(Log::JIT, "Bad mips register %d", reg);333return MIPSSTATE_VAR(r[0]);334}335}336337338void GPRRegCache::KillImmediate(MIPSGPReg preg, bool doLoad, bool makeDirty) {339if (regs[preg].away) {340if (regs[preg].location.IsImm())341MapReg(preg, doLoad, makeDirty);342else if (regs[preg].location.IsSimpleReg())343xregs[RX(preg)].dirty |= makeDirty;344}345}346347void GPRRegCache::MapReg(MIPSGPReg i, bool doLoad, bool makeDirty) {348if (!regs[i].away && regs[i].location.IsImm()) {349_assert_msg_(false, "Bad immediate");350}351if (!regs[i].away || (regs[i].away && regs[i].location.IsImm())) {352X64Reg xr = GetFreeXReg();353_assert_msg_(!xregs[xr].dirty, "Xreg already dirty");354_assert_msg_(!xregs[xr].allocLocked, "GetFreeXReg returned locked register");355xregs[xr].free = false;356xregs[xr].mipsReg = i;357xregs[xr].dirty = makeDirty || regs[i].location.IsImm();358OpArg newloc = ::Gen::R(xr);359if (doLoad) {360// Force ZERO to be 0.361if (i == MIPS_REG_ZERO)362emit->MOV(32, newloc, Imm32(0));363else364emit->MOV(32, newloc, regs[i].location);365}366for (int j = 0; j < 32; j++) {367if (i != MIPSGPReg(j) && regs[j].location.IsSimpleReg(xr)) {368_assert_msg_(false, "BindToRegister: Strange condition");369}370}371regs[i].away = true;372regs[i].location = newloc;373} else {374// reg location must be simplereg; memory locations375// and immediates are taken care of above.376xregs[RX(i)].dirty |= makeDirty;377}378379_assert_msg_(!xregs[RX(i)].allocLocked, "This reg should have been flushed (r%d)", i);380}381382void GPRRegCache::StoreFromRegister(MIPSGPReg i) {383if (regs[i].away) {384bool doStore;385if (regs[i].location.IsSimpleReg()) {386X64Reg xr = RX(i);387xregs[xr].free = true;388xregs[xr].mipsReg = MIPS_REG_INVALID;389doStore = xregs[xr].dirty;390xregs[xr].dirty = false;391} else {392//must be immediate - do nothing393doStore = true;394}395OpArg newLoc = GetDefaultLocation(i);396// But never store to ZERO.397if (doStore && i != MIPS_REG_ZERO)398emit->MOV(32, newLoc, regs[i].location);399regs[i].location = newLoc;400regs[i].away = false;401}402}403404void GPRRegCache::Flush() {405for (int i = 0; i < NUM_X_REGS; i++) {406_assert_msg_(!xregs[i].allocLocked, "Someone forgot to unlock X64 reg %d.", i);407}408SetImm(MIPS_REG_ZERO, 0);409for (int i = 1; i < NUM_MIPS_GPRS; i++) {410const MIPSGPReg r = MIPSGPReg(i);411_assert_msg_(!regs[i].locked, "Somebody forgot to unlock MIPS reg %d.", i);412if (regs[i].away) {413if (regs[i].location.IsSimpleReg()) {414X64Reg xr = RX(r);415StoreFromRegister(r);416xregs[xr].dirty = false;417}418else if (regs[i].location.IsImm()) {419StoreFromRegister(r);420} else {421_assert_msg_(false, "Jit64 - Flush unhandled case, reg %d PC: %08x", i, mips_->pc);422}423}424}425}426427void GPRRegCache::GetState(GPRRegCacheState &state) const {428memcpy(state.regs, regs, sizeof(regs));429memcpy(state.xregs, xregs, sizeof(xregs));430}431432void GPRRegCache::RestoreState(const GPRRegCacheState& state) {433memcpy(regs, state.regs, sizeof(regs));434memcpy(xregs, state.xregs, sizeof(xregs));435}436437#endif // PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)438439440