From b30daa5760cce03a2878c6b2ef2875a416f1e4ec Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Tue, 15 Aug 2023 21:51:38 -0700 Subject: [PATCH 1/8] riscv: Centralize state of regcaches. --- Core/MIPS/IR/IRRegCache.cpp | 62 ++- Core/MIPS/IR/IRRegCache.h | 131 +++++- Core/MIPS/RiscV/RiscVCompFPU.cpp | 4 +- Core/MIPS/RiscV/RiscVCompVec.cpp | 3 - Core/MIPS/RiscV/RiscVJit.cpp | 6 +- Core/MIPS/RiscV/RiscVJit.h | 6 +- Core/MIPS/RiscV/RiscVRegCache.cpp | 589 ++++++++++++--------------- Core/MIPS/RiscV/RiscVRegCache.h | 136 ++----- Core/MIPS/RiscV/RiscVRegCacheFPU.cpp | 189 ++++----- Core/MIPS/RiscV/RiscVRegCacheFPU.h | 81 ++-- 10 files changed, 600 insertions(+), 607 deletions(-) diff --git a/Core/MIPS/IR/IRRegCache.cpp b/Core/MIPS/IR/IRRegCache.cpp index ddd3677df4..83604b41df 100644 --- a/Core/MIPS/IR/IRRegCache.cpp +++ b/Core/MIPS/IR/IRRegCache.cpp @@ -1,7 +1,30 @@ +// Copyright (c) 2023- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#ifndef offsetof +#include +#endif + #include #include "Common/Log.h" #include "Core/MIPS/IR/IRRegCache.h" #include "Core/MIPS/IR/IRInst.h" +#include "Core/MIPS/IR/IRJit.h" +#include "Core/MIPS/JitCommon/JitState.h" void IRImmRegCache::Flush(IRReg rd) { if (rd == 0) { @@ -28,7 +51,7 @@ IRImmRegCache::IRImmRegCache(IRWriter *ir) : ir_(ir) { } void IRImmRegCache::FlushAll() { - for (int i = 0; i < TOTAL_MAPPABLE_MIPSREGS; i++) { + for (int i = 0; i < TOTAL_MAPPABLE_IRREGS; i++) { Flush(i); } } @@ -67,3 +90,40 @@ void IRImmRegCache::MapDirtyInIn(IRReg rd, IRReg rs, IRReg rt) { Flush(rt); } +IRNativeRegCache::IRNativeRegCache(MIPSComp::JitOptions *jo) + : jo_(jo) {} + +void IRNativeRegCache::Start(MIPSComp::IRBlock *irBlock) { + if (!initialReady_) { + SetupInitialRegs(); + initialReady_ = true; + } + + memcpy(nr, nrInitial_, sizeof(nr[0]) * totalNativeRegs_); + memcpy(mr, mrInitial_, sizeof(mr)); + pendingFlush_ = false; + + int numStatics; + const StaticAllocation *statics = GetStaticAllocations(numStatics); + for (int i = 0; i < numStatics; i++) { + nr[statics[i].nr].mipsReg = statics[i].mr; + nr[statics[i].nr].pointerified = statics[i].pointerified && jo_->enablePointerify; + nr[statics[i].nr].normalized32 = statics[i].normalized32; + mr[statics[i].mr].loc = MIPSLoc::REG; + mr[statics[i].mr].nReg = statics[i].nr; + mr[statics[i].mr].isStatic = true; + // Lock it until the very end. + mr[statics[i].mr].spillLockIRIndex = irBlock->GetNumInstructions(); + } + + irBlock_ = irBlock; + irIndex_ = 0; +} + +void IRNativeRegCache::SetupInitialRegs() { + _assert_msg_(totalNativeRegs_ > 0, "totalNativeRegs_ was never set by backend"); + + // Everything else is initialized in the struct. + mrInitial_[MIPS_REG_ZERO].loc = MIPSLoc::IMM; + mrInitial_[MIPS_REG_ZERO].imm = 0; +} diff --git a/Core/MIPS/IR/IRRegCache.h b/Core/MIPS/IR/IRRegCache.h index 5ab5d51d0a..dd3d3ffbec 100644 --- a/Core/MIPS/IR/IRRegCache.h +++ b/Core/MIPS/IR/IRRegCache.h @@ -1,3 +1,20 @@ +// Copyright (c) 2023- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + #pragma once // IRImmRegCache is only to perform pre-constant folding. This is worth it to get cleaner @@ -7,16 +24,21 @@ #include "Core/MIPS/MIPS.h" #include "Core/MIPS/IR/IRInst.h" -enum { - TOTAL_MAPPABLE_MIPSREGS = 256, -}; -struct RegIR { - bool isImm; - u32 immVal; -}; +// Have to account for all of them due to temps, etc. +constexpr int TOTAL_MAPPABLE_IRREGS = 256; +// Arbitrary - increase if your backend has more. +constexpr int TOTAL_POSSIBLE_NATIVEREGS = 128; + +constexpr IRReg IRREG_INVALID = 255; class IRWriter; +class MIPSState; + +namespace MIPSComp { +class IRBlock; +struct JitOptions; +} // Transient class IRImmRegCache { @@ -43,6 +65,99 @@ public: private: void Flush(IRReg rd); void Discard(IRReg rd); - RegIR reg_[TOTAL_MAPPABLE_MIPSREGS]; + + struct RegIR { + bool isImm; + u32 immVal; + }; + + RegIR reg_[TOTAL_MAPPABLE_IRREGS]; IRWriter *ir_; }; + +class IRNativeRegCache { +protected: + enum class MIPSLoc { + // Known immediate value (only in regcache.) + IMM, + // In a general reg. + REG, + // In a general reg, but an adjusted pointer (not pointerified - unaligned.) + REG_AS_PTR, + // In a general reg, but also has a known immediate value. + REG_IMM, + // In a native floating-point reg. + FREG, + // In a native vector reg. + VREG, + // Away in memory (in the mips context struct.) + MEM, + }; + + struct RegStatusMIPS { + // Where is this IR/MIPS register? + MIPSLoc loc = MIPSLoc::MEM; + // If in a register, what index (into nr array)? + int8_t nReg = -1; + // If a known immediate value, what value? + uint64_t imm = 0; + // Locked from spilling (i.e. used by current instruction) as of what IR instruction? + int spillLockIRIndex = -1; + // If in a multipart reg (vector or HI/LO), which lane? + int lane = -1; + // Whether this reg is statically allocated. + bool isStatic = false; + }; + struct RegStatusNative { + // Which IR/MIPS reg is this currently holding? + IRReg mipsReg = IRREG_INVALID; + // Locked either as temp or direct reg as of what IR instruction? + int tempLockIRIndex = -1; + // Should the register be written back? + bool isDirty = false; + // Upper part of the register is used for "pointerification". + // Depending on backend, this may not be used or some/all operations may work on the lower 32 bits. + bool pointerified = false; + // Upper part of the register has a normalized form (i.e. zero or sign extend.) + // Which this means or if it matters depends on the backend. + bool normalized32 = false; + }; + + struct StaticAllocation { + IRReg mr; + int8_t nr; + // Whether the reg should be marked pointerified by default. + bool pointerified = false; + // Whether the reg should be considered always normalized at the start of a block. + bool normalized32 = false; + }; + +public: + IRNativeRegCache(MIPSComp::JitOptions *jo); + virtual ~IRNativeRegCache() {} + + virtual void Start(MIPSComp::IRBlock *irBlock); + void SetIRIndex(int index) { + irIndex_ = index; + } + +protected: + virtual void SetupInitialRegs(); + virtual const StaticAllocation *GetStaticAllocations(int &count) { + count = 0; + return nullptr; + } + + MIPSComp::JitOptions *jo_; + MIPSComp::IRBlock *irBlock_ = nullptr; + int irIndex_ = 0; + int totalNativeRegs_ = 0; + + RegStatusNative nr[TOTAL_POSSIBLE_NATIVEREGS]; + RegStatusMIPS mr[TOTAL_MAPPABLE_IRREGS]; + RegStatusNative nrInitial_[TOTAL_POSSIBLE_NATIVEREGS]; + RegStatusMIPS mrInitial_[TOTAL_MAPPABLE_IRREGS]; + + bool initialReady_ = false; + bool pendingFlush_ = false; +}; diff --git a/Core/MIPS/RiscV/RiscVCompFPU.cpp b/Core/MIPS/RiscV/RiscVCompFPU.cpp index 19655b8f65..bbad21f9cc 100644 --- a/Core/MIPS/RiscV/RiscVCompFPU.cpp +++ b/Core/MIPS/RiscV/RiscVCompFPU.cpp @@ -117,8 +117,6 @@ void RiscVJitBackend::CompIR_FCondAssign(IRInst inst) { SetJumpTarget(skipSwapCompare); } else { RiscVReg isSrc1LowerReg = gpr.GetAndLockTempR(); - gpr.ReleaseSpillLocksAndDiscardTemps(); - SLT(isSrc1LowerReg, SCRATCH1, SCRATCH2); // Flip the flag (to reverse the min/max) based on if both were negative. XOR(isSrc1LowerReg, isSrc1LowerReg, R_RA); @@ -342,7 +340,7 @@ void RiscVJitBackend::CompIR_FSat(IRInst inst) { void RiscVJitBackend::CompIR_FCompare(IRInst inst) { CONDITIONAL_DISABLE; - constexpr IRRegIndex IRREG_VFPUL_CC = IRREG_VFPU_CTRL_BASE + VFPU_CTRL_CC; + constexpr IRReg IRREG_VFPUL_CC = IRREG_VFPU_CTRL_BASE + VFPU_CTRL_CC; switch (inst.op) { case IROp::FCmp: diff --git a/Core/MIPS/RiscV/RiscVCompVec.cpp b/Core/MIPS/RiscV/RiscVCompVec.cpp index 9bb5321476..52e9e5d118 100644 --- a/Core/MIPS/RiscV/RiscVCompVec.cpp +++ b/Core/MIPS/RiscV/RiscVCompVec.cpp @@ -309,7 +309,6 @@ void RiscVJitBackend::CompIR_VecPack(IRInst inst) { fpr.MapReg(inst.src1); for (int i = 0; i < 4; ++i) fpr.MapReg(inst.dest + i, MIPSMap::NOINIT); - fpr.ReleaseSpillLocksAndDiscardTemps(); FMV(FMv::X, FMv::W, SCRATCH2, fpr.R(inst.src1)); for (int i = 0; i < 4; ++i) { @@ -331,7 +330,6 @@ void RiscVJitBackend::CompIR_VecPack(IRInst inst) { fpr.MapReg(inst.src1); for (int i = 0; i < 2; ++i) fpr.MapReg(inst.dest + i, MIPSMap::NOINIT); - fpr.ReleaseSpillLocksAndDiscardTemps(); FMV(FMv::X, FMv::W, SCRATCH2, fpr.R(inst.src1)); SLLI(SCRATCH1, SCRATCH2, 16); @@ -361,7 +359,6 @@ void RiscVJitBackend::CompIR_VecPack(IRInst inst) { fpr.MapReg(inst.src1 + i); } fpr.MapReg(inst.dest, MIPSMap::NOINIT); - fpr.ReleaseSpillLocksAndDiscardTemps(); for (int i = 0; i < 4; ++i) { FMV(FMv::X, FMv::W, SCRATCH1, fpr.R(inst.src1 + i)); diff --git a/Core/MIPS/RiscV/RiscVJit.cpp b/Core/MIPS/RiscV/RiscVJit.cpp index 1c71239fe8..d7663484b0 100644 --- a/Core/MIPS/RiscV/RiscVJit.cpp +++ b/Core/MIPS/RiscV/RiscVJit.cpp @@ -31,8 +31,8 @@ using namespace RiscVJitConstants; static constexpr int MIN_BLOCK_NORMAL_LEN = 16; static constexpr int MIN_BLOCK_EXIT_LEN = 8; -RiscVJitBackend::RiscVJitBackend(MIPSState *mipsState, JitOptions &jitopt, IRBlockCache &blocks) - : IRNativeBackend(blocks), jo(jitopt), gpr(mipsState, &jo), fpr(mipsState, &jo) { +RiscVJitBackend::RiscVJitBackend(JitOptions &jitopt, IRBlockCache &blocks) + : IRNativeBackend(blocks), jo(jitopt), gpr(&jo), fpr(&jo) { // Automatically disable incompatible options. if (((intptr_t)Memory::base & 0x00000000FFFFFFFFUL) != 0) { jo.enablePointerify = false; @@ -352,7 +352,7 @@ void RiscVJitBackend::NormalizeSrc12(IRInst inst, RiscVReg *lhs, RiscVReg *rhs, *rhs = NormalizeR(inst.src2, allowOverlap ? 0 : inst.dest, rhsTempReg); } -RiscVReg RiscVJitBackend::NormalizeR(IRRegIndex rs, IRRegIndex rd, RiscVReg tempReg) { +RiscVReg RiscVJitBackend::NormalizeR(IRReg rs, IRReg rd, RiscVReg tempReg) { // For proper compare, we must sign extend so they both match or don't match. // But don't change pointers, in case one is SP (happens in LittleBigPlanet.) if (gpr.IsImm(rs) && gpr.GetImm(rs) == 0) { diff --git a/Core/MIPS/RiscV/RiscVJit.h b/Core/MIPS/RiscV/RiscVJit.h index d8bba616ad..228fce48ab 100644 --- a/Core/MIPS/RiscV/RiscVJit.h +++ b/Core/MIPS/RiscV/RiscVJit.h @@ -31,7 +31,7 @@ namespace MIPSComp { class RiscVJitBackend : public RiscVGen::RiscVCodeBlock, public IRNativeBackend { public: - RiscVJitBackend(MIPSState *mipsState, JitOptions &jo, IRBlockCache &blocks); + RiscVJitBackend(JitOptions &jo, IRBlockCache &blocks); ~RiscVJitBackend(); bool DescribeCodePtr(const u8 *ptr, std::string &name) const override; @@ -109,7 +109,7 @@ private: int32_t AdjustForAddressOffset(RiscVGen::RiscVReg *reg, int32_t constant, int32_t range = 0); void NormalizeSrc1(IRInst inst, RiscVGen::RiscVReg *reg, RiscVGen::RiscVReg tempReg, bool allowOverlap); void NormalizeSrc12(IRInst inst, RiscVGen::RiscVReg *lhs, RiscVGen::RiscVReg *rhs, RiscVGen::RiscVReg lhsTempReg, RiscVGen::RiscVReg rhsTempReg, bool allowOverlap); - RiscVGen::RiscVReg NormalizeR(IRRegIndex rs, IRRegIndex rd, RiscVGen::RiscVReg tempReg); + RiscVGen::RiscVReg NormalizeR(IRReg rs, IRReg rd, RiscVGen::RiscVReg tempReg); JitOptions &jo; RiscVRegCache gpr; @@ -133,7 +133,7 @@ private: class RiscVJit : public IRNativeJit { public: RiscVJit(MIPSState *mipsState) - : IRNativeJit(mipsState), rvBackend_(mipsState, jo, blocks_) { + : IRNativeJit(mipsState), rvBackend_(jo, blocks_) { Init(rvBackend_); } diff --git a/Core/MIPS/RiscV/RiscVRegCache.cpp b/Core/MIPS/RiscV/RiscVRegCache.cpp index d37ce3eb23..a5e532e433 100644 --- a/Core/MIPS/RiscV/RiscVRegCache.cpp +++ b/Core/MIPS/RiscV/RiscVRegCache.cpp @@ -29,60 +29,26 @@ using namespace RiscVGen; using namespace RiscVJitConstants; -RiscVRegCache::RiscVRegCache(MIPSState *mipsState, MIPSComp::JitOptions *jo) - : mips_(mipsState), jo_(jo) { +RiscVRegCache::RiscVRegCache(MIPSComp::JitOptions *jo) + : IRNativeRegCache(jo) { + // TODO: Move to using for FPRs and VPRs too? + totalNativeRegs_ = NUM_RVREG; } void RiscVRegCache::Init(RiscVEmitter *emitter) { emit_ = emitter; } -void RiscVRegCache::Start(MIPSComp::IRBlock *irBlock) { - if (!initialReady_) { - SetupInitialRegs(); - initialReady_ = true; - } - - memcpy(ar, arInitial_, sizeof(ar)); - memcpy(mr, mrInitial_, sizeof(mr)); - - int numStatics; - const StaticAllocation *statics = GetStaticAllocations(numStatics); - for (int i = 0; i < numStatics; i++) { - ar[statics[i].ar].mipsReg = statics[i].mr; - ar[statics[i].ar].pointerified = statics[i].pointerified && jo_->enablePointerify; - ar[statics[i].ar].normalized32 = false; - mr[statics[i].mr].loc = MIPSLoc::RVREG; - mr[statics[i].mr].reg = statics[i].ar; - mr[statics[i].mr].isStatic = true; - mr[statics[i].mr].spillLock = true; - } - - irBlock_ = irBlock; - irIndex_ = 0; -} - void RiscVRegCache::SetupInitialRegs() { - for (int i = 0; i < NUM_RVREG; i++) { - arInitial_[i].mipsReg = IRREG_INVALID; - arInitial_[i].isDirty = false; - arInitial_[i].pointerified = false; - arInitial_[i].tempLocked = false; - arInitial_[i].normalized32 = false; - } - for (int i = 0; i < NUM_MIPSREG; i++) { - mrInitial_[i].loc = MIPSLoc::MEM; - mrInitial_[i].reg = INVALID_REG; - mrInitial_[i].imm = -1; - mrInitial_[i].spillLock = false; - mrInitial_[i].isStatic = false; - } + IRNativeRegCache::SetupInitialRegs(); // Treat R_ZERO a bit specially, but it's basically static alloc too. - arInitial_[R_ZERO].mipsReg = MIPS_REG_ZERO; - arInitial_[R_ZERO].normalized32 = true; - mrInitial_[MIPS_REG_ZERO].loc = MIPSLoc::RVREG_IMM; - mrInitial_[MIPS_REG_ZERO].reg = R_ZERO; + nrInitial_[R_ZERO].mipsReg = MIPS_REG_ZERO; + nrInitial_[R_ZERO].normalized32 = true; + + // Since we also have a fixed zero, mark it as a static allocation. + mrInitial_[MIPS_REG_ZERO].loc = MIPSLoc::REG_IMM; + mrInitial_[MIPS_REG_ZERO].nReg = R_ZERO; mrInitial_[MIPS_REG_ZERO].imm = 0; mrInitial_[MIPS_REG_ZERO].isStatic = true; } @@ -118,10 +84,8 @@ const RiscVRegCache::StaticAllocation *RiscVRegCache::GetStaticAllocations(int & if (jo_->useStaticAlloc) { count = ARRAY_SIZE(allocs); return allocs; - } else { - count = 0; - return nullptr; } + return IRNativeRegCache::GetStaticAllocations(count); } void RiscVRegCache::EmitLoadStaticRegisters() { @@ -130,10 +94,10 @@ void RiscVRegCache::EmitLoadStaticRegisters() { for (int i = 0; i < count; i++) { int offset = GetMipsRegOffset(allocs[i].mr); if (allocs[i].pointerified && jo_->enablePointerify) { - emit_->LWU(allocs[i].ar, CTXREG, offset); - emit_->ADD(allocs[i].ar, allocs[i].ar, MEMBASEREG); + emit_->LWU((RiscVReg)allocs[i].nr, CTXREG, offset); + emit_->ADD((RiscVReg)allocs[i].nr, (RiscVReg)allocs[i].nr, MEMBASEREG); } else { - emit_->LW(allocs[i].ar, CTXREG, offset); + emit_->LW((RiscVReg)allocs[i].nr, CTXREG, offset); } } } @@ -144,7 +108,7 @@ void RiscVRegCache::EmitSaveStaticRegisters() { // This only needs to run once (by Asm) so checks don't need to be fast. for (int i = 0; i < count; i++) { int offset = GetMipsRegOffset(allocs[i].mr); - emit_->SW(allocs[i].ar, CTXREG, offset); + emit_->SW((RiscVReg)allocs[i].nr, CTXREG, offset); } } @@ -161,43 +125,43 @@ void RiscVRegCache::FlushBeforeCall() { } } -bool RiscVRegCache::IsInRAM(IRRegIndex reg) { +bool RiscVRegCache::IsInRAM(IRReg reg) { _dbg_assert_(IsValidReg(reg)); return mr[reg].loc == MIPSLoc::MEM; } -bool RiscVRegCache::IsMapped(IRRegIndex mipsReg) { +bool RiscVRegCache::IsMapped(IRReg mipsReg) { _dbg_assert_(IsValidReg(mipsReg)); - return mr[mipsReg].loc == MIPSLoc::RVREG || mr[mipsReg].loc == MIPSLoc::RVREG_IMM; + return mr[mipsReg].loc == MIPSLoc::REG || mr[mipsReg].loc == MIPSLoc::REG_IMM; } -bool RiscVRegCache::IsMappedAsPointer(IRRegIndex mipsReg) { +bool RiscVRegCache::IsMappedAsPointer(IRReg mipsReg) { _dbg_assert_(IsValidReg(mipsReg)); - if (mr[mipsReg].loc == MIPSLoc::RVREG) { - return ar[mr[mipsReg].reg].pointerified; - } else if (mr[mipsReg].loc == MIPSLoc::RVREG_IMM) { - if (ar[mr[mipsReg].reg].pointerified) { + if (mr[mipsReg].loc == MIPSLoc::REG) { + return nr[mr[mipsReg].nReg].pointerified; + } else if (mr[mipsReg].loc == MIPSLoc::REG_IMM) { + if (nr[mr[mipsReg].nReg].pointerified) { ERROR_LOG(JIT, "Really shouldn't be pointerified here"); } - } else if (mr[mipsReg].loc == MIPSLoc::RVREG_AS_PTR) { + } else if (mr[mipsReg].loc == MIPSLoc::REG_AS_PTR) { return true; } return false; } -bool RiscVRegCache::IsMappedAsStaticPointer(IRRegIndex reg) { +bool RiscVRegCache::IsMappedAsStaticPointer(IRReg reg) { if (IsMappedAsPointer(reg)) { return mr[reg].isStatic; } return false; } -bool RiscVRegCache::IsNormalized32(IRRegIndex mipsReg) { +bool RiscVRegCache::IsNormalized32(IRReg mipsReg) { _dbg_assert_(IsValidReg(mipsReg)); if (XLEN == 32) return true; - if (mr[mipsReg].loc == MIPSLoc::RVREG || mr[mipsReg].loc == MIPSLoc::RVREG_IMM) { - return ar[mr[mipsReg].reg].normalized32; + if (mr[mipsReg].loc == MIPSLoc::REG || mr[mipsReg].loc == MIPSLoc::REG_IMM) { + return nr[mr[mipsReg].nReg].normalized32; } return false; } @@ -205,37 +169,37 @@ bool RiscVRegCache::IsNormalized32(IRRegIndex mipsReg) { void RiscVRegCache::MarkDirty(RiscVReg reg, bool andNormalized32) { // Can't mark X0 dirty. _dbg_assert_(reg > X0 && reg <= X31); - ar[reg].isDirty = true; - ar[reg].normalized32 = andNormalized32; + nr[reg].isDirty = true; + nr[reg].normalized32 = andNormalized32; // If reg is written to, pointerification is lost. - ar[reg].pointerified = false; - if (ar[reg].mipsReg != IRREG_INVALID) { - RegStatusMIPS &m = mr[ar[reg].mipsReg]; - if (m.loc == MIPSLoc::RVREG_AS_PTR || m.loc == MIPSLoc::RVREG_IMM) { - m.loc = MIPSLoc::RVREG; + nr[reg].pointerified = false; + if (nr[reg].mipsReg != IRREG_INVALID) { + RegStatusMIPS &m = mr[nr[reg].mipsReg]; + if (m.loc == MIPSLoc::REG_AS_PTR || m.loc == MIPSLoc::REG_IMM) { + m.loc = MIPSLoc::REG; m.imm = -1; } - _dbg_assert_(m.loc == MIPSLoc::RVREG); + _dbg_assert_(m.loc == MIPSLoc::REG); } } void RiscVRegCache::MarkPtrDirty(RiscVReg reg) { // Can't mark X0 dirty. _dbg_assert_(reg > X0 && reg <= X31); - _dbg_assert_(!ar[reg].normalized32); - ar[reg].isDirty = true; - if (ar[reg].mipsReg != IRREG_INVALID) { - _dbg_assert_(mr[ar[reg].mipsReg].loc == MIPSLoc::RVREG_AS_PTR); + _dbg_assert_(!nr[reg].normalized32); + nr[reg].isDirty = true; + if (nr[reg].mipsReg != IRREG_INVALID) { + _dbg_assert_(mr[nr[reg].mipsReg].loc == MIPSLoc::REG_AS_PTR); } else { - _dbg_assert_(ar[reg].pointerified); + _dbg_assert_(nr[reg].pointerified); } } -RiscVGen::RiscVReg RiscVRegCache::Normalize32(IRRegIndex mipsReg, RiscVGen::RiscVReg destReg) { +RiscVGen::RiscVReg RiscVRegCache::Normalize32(IRReg mipsReg, RiscVGen::RiscVReg destReg) { _dbg_assert_(IsValidReg(mipsReg)); _dbg_assert_(destReg == INVALID_REG || (destReg > X0 && destReg <= X31)); - RiscVReg reg = mr[mipsReg].reg; + RiscVReg reg = (RiscVReg)mr[mipsReg].nReg; if (XLEN == 32) return reg; @@ -245,36 +209,36 @@ RiscVGen::RiscVReg RiscVRegCache::Normalize32(IRRegIndex mipsReg, RiscVGen::Risc _assert_msg_(false, "Cannot normalize an imm or mem"); return INVALID_REG; - case MIPSLoc::RVREG: - case MIPSLoc::RVREG_IMM: - if (!ar[mr[mipsReg].reg].normalized32) { + case MIPSLoc::REG: + case MIPSLoc::REG_IMM: + if (!nr[mr[mipsReg].nReg].normalized32) { if (destReg == INVALID_REG) { - emit_->SEXT_W(mr[mipsReg].reg, mr[mipsReg].reg); - ar[mr[mipsReg].reg].normalized32 = true; - ar[mr[mipsReg].reg].pointerified = false; + emit_->SEXT_W((RiscVReg)mr[mipsReg].nReg, (RiscVReg)mr[mipsReg].nReg); + nr[mr[mipsReg].nReg].normalized32 = true; + nr[mr[mipsReg].nReg].pointerified = false; } else { - emit_->SEXT_W(destReg, mr[mipsReg].reg); + emit_->SEXT_W(destReg, (RiscVReg)mr[mipsReg].nReg); } } else if (destReg != INVALID_REG) { - emit_->SEXT_W(destReg, mr[mipsReg].reg); + emit_->SEXT_W(destReg, (RiscVReg)mr[mipsReg].nReg); } break; - case MIPSLoc::RVREG_AS_PTR: - _dbg_assert_(ar[mr[mipsReg].reg].normalized32 == false); + case MIPSLoc::REG_AS_PTR: + _dbg_assert_(nr[mr[mipsReg].nReg].normalized32 == false); if (destReg == INVALID_REG) { // If we can pointerify, SEXT_W will be enough. if (!jo_->enablePointerify) - emit_->SUB(mr[mipsReg].reg, mr[mipsReg].reg, MEMBASEREG); - emit_->SEXT_W(mr[mipsReg].reg, mr[mipsReg].reg); - mr[mipsReg].loc = MIPSLoc::RVREG; - ar[mr[mipsReg].reg].normalized32 = true; - ar[mr[mipsReg].reg].pointerified = false; + emit_->SUB((RiscVReg)mr[mipsReg].nReg, (RiscVReg)mr[mipsReg].nReg, MEMBASEREG); + emit_->SEXT_W((RiscVReg)mr[mipsReg].nReg, (RiscVReg)mr[mipsReg].nReg); + mr[mipsReg].loc = MIPSLoc::REG; + nr[mr[mipsReg].nReg].normalized32 = true; + nr[mr[mipsReg].nReg].pointerified = false; } else if (!jo_->enablePointerify) { - emit_->SUB(destReg, mr[mipsReg].reg, MEMBASEREG); + emit_->SUB(destReg, (RiscVReg)mr[mipsReg].nReg, MEMBASEREG); emit_->SEXT_W(destReg, destReg); } else { - emit_->SEXT_W(destReg, mr[mipsReg].reg); + emit_->SEXT_W(destReg, (RiscVReg)mr[mipsReg].nReg); } break; } @@ -287,11 +251,11 @@ void RiscVRegCache::SetRegImm(RiscVReg reg, u64 imm) { _dbg_assert_(reg >= X0 && reg <= X31); // TODO: Could optimize this more for > 32 bit constants. emit_->LI(reg, imm); - _dbg_assert_(!ar[reg].pointerified); - ar[reg].normalized32 = imm == (u64)(s64)(s32)imm; + _dbg_assert_(!nr[reg].pointerified); + nr[reg].normalized32 = imm == (u64)(s64)(s32)imm; } -void RiscVRegCache::MapRegTo(RiscVReg reg, IRRegIndex mipsReg, MIPSMap mapFlags) { +void RiscVRegCache::MapRegTo(RiscVReg reg, IRReg mipsReg, MIPSMap mapFlags) { _dbg_assert_(reg > X0 && reg <= X31); _dbg_assert_(IsValidReg(mipsReg)); _dbg_assert_(!mr[mipsReg].isStatic); @@ -299,7 +263,7 @@ void RiscVRegCache::MapRegTo(RiscVReg reg, IRRegIndex mipsReg, MIPSMap mapFlags) ERROR_LOG(JIT, "Cannot MapRegTo static register %d", mipsReg); return; } - ar[reg].isDirty = (mapFlags & MIPSMap::DIRTY) == MIPSMap::DIRTY; + nr[reg].isDirty = (mapFlags & MIPSMap::DIRTY) == MIPSMap::DIRTY; if ((mapFlags & MIPSMap::NOINIT) != MIPSMap::NOINIT) { if (mipsReg == MIPS_REG_ZERO) { // If we get a request to load the zero register, at least we won't spend @@ -307,49 +271,49 @@ void RiscVRegCache::MapRegTo(RiscVReg reg, IRRegIndex mipsReg, MIPSMap mapFlags) emit_->LI(reg, 0); // This way, if we SetImm() it, we'll keep it. - mr[mipsReg].loc = MIPSLoc::RVREG_IMM; + mr[mipsReg].loc = MIPSLoc::REG_IMM; mr[mipsReg].imm = 0; - ar[reg].normalized32 = true; + nr[reg].normalized32 = true; } else { switch (mr[mipsReg].loc) { case MIPSLoc::MEM: emit_->LW(reg, CTXREG, GetMipsRegOffset(mipsReg)); - mr[mipsReg].loc = MIPSLoc::RVREG; - ar[reg].normalized32 = true; + mr[mipsReg].loc = MIPSLoc::REG; + nr[reg].normalized32 = true; break; case MIPSLoc::IMM: SetRegImm(reg, mr[mipsReg].imm); // IMM is always dirty. - ar[reg].isDirty = true; + nr[reg].isDirty = true; // If we are mapping dirty, it means we're gonna overwrite. // So the imm value is no longer valid. if ((mapFlags & MIPSMap::DIRTY) == MIPSMap::DIRTY) - mr[mipsReg].loc = MIPSLoc::RVREG; + mr[mipsReg].loc = MIPSLoc::REG; else - mr[mipsReg].loc = MIPSLoc::RVREG_IMM; + mr[mipsReg].loc = MIPSLoc::REG_IMM; break; - case MIPSLoc::RVREG_IMM: + case MIPSLoc::REG_IMM: // If it's not dirty, we can keep it. - if (ar[reg].isDirty) - mr[mipsReg].loc = MIPSLoc::RVREG; + if (nr[reg].isDirty) + mr[mipsReg].loc = MIPSLoc::REG; break; default: - _assert_msg_(mr[mipsReg].loc != MIPSLoc::RVREG_AS_PTR, "MapRegTo with a pointer?"); - mr[mipsReg].loc = MIPSLoc::RVREG; + _assert_msg_(mr[mipsReg].loc != MIPSLoc::REG_AS_PTR, "MapRegTo with a pointer?"); + mr[mipsReg].loc = MIPSLoc::REG; break; } } } else { _dbg_assert_(mipsReg != MIPS_REG_ZERO); - _dbg_assert_(ar[reg].isDirty); - mr[mipsReg].loc = MIPSLoc::RVREG; + _dbg_assert_(nr[reg].isDirty); + mr[mipsReg].loc = MIPSLoc::REG; } - ar[reg].mipsReg = mipsReg; - ar[reg].pointerified = false; - if (ar[reg].isDirty) - ar[reg].normalized32 = (mapFlags & MIPSMap::MARK_NORM32) == MIPSMap::MARK_NORM32; - mr[mipsReg].reg = reg; + nr[reg].mipsReg = mipsReg; + nr[reg].pointerified = false; + if (nr[reg].isDirty) + nr[reg].normalized32 = (mapFlags & MIPSMap::MARK_NORM32) == MIPSMap::MARK_NORM32; + mr[mipsReg].nReg = reg; } RiscVReg RiscVRegCache::AllocateReg() { @@ -360,7 +324,7 @@ allocate: for (int i = 0; i < allocCount; i++) { RiscVReg reg = allocOrder[i]; - if (ar[reg].mipsReg == IRREG_INVALID && !ar[reg].tempLocked) { + if (nr[reg].mipsReg == IRREG_INVALID && nr[reg].tempLockIRIndex < irIndex_) { return reg; } } @@ -374,7 +338,7 @@ allocate: if (bestToSpill != INVALID_REG) { if (clobbered) { - DiscardR(ar[bestToSpill].mipsReg); + DiscardR(nr[bestToSpill].mipsReg); } else { FlushRiscVReg(bestToSpill); } @@ -383,7 +347,7 @@ allocate: } // Uh oh, we have all of them spilllocked.... - ERROR_LOG_REPORT(JIT, "Out of spillable registers near PC %08x", mips_->pc); + ERROR_LOG_REPORT(JIT, "Out of spillable registers in block PC %08x, index %d", irBlock_->GetOriginalStart(), irIndex_); _assert_(bestToSpill != INVALID_REG); return INVALID_REG; } @@ -403,13 +367,13 @@ RiscVReg RiscVRegCache::FindBestToSpill(bool unusedOnly, bool *clobbered) { *clobbered = false; for (int i = 0; i < allocCount; i++) { RiscVReg reg = allocOrder[i]; - if (ar[reg].mipsReg != IRREG_INVALID && mr[ar[reg].mipsReg].spillLock) + if (nr[reg].mipsReg != IRREG_INVALID && mr[nr[reg].mipsReg].spillLockIRIndex >= irIndex_) continue; - if (ar[reg].tempLocked) + if (nr[reg].tempLockIRIndex >= irIndex_) continue; // As it's in alloc-order, we know it's not static so we don't need to check for that. - IRUsage usage = IRNextGPRUsage(ar[reg].mipsReg, info); + IRUsage usage = IRNextGPRUsage(nr[reg].mipsReg, info); // Awesome, a clobbered reg. Let's use it. if (usage == IRUsage::CLOBBERED) { @@ -432,7 +396,7 @@ RiscVReg RiscVRegCache::FindBestToSpill(bool unusedOnly, bool *clobbered) { return INVALID_REG; } -RiscVReg RiscVRegCache::TryMapTempImm(IRRegIndex r) { +RiscVReg RiscVRegCache::TryMapTempImm(IRReg r) { _dbg_assert_(IsValidReg(r)); // If already mapped, no need for a temporary. if (IsMapped(r)) { @@ -445,10 +409,10 @@ RiscVReg RiscVRegCache::TryMapTempImm(IRRegIndex r) { } // Try our luck - check for an exact match in another rvreg. - for (int i = 0; i < NUM_MIPSREG; ++i) { - if (mr[i].loc == MIPSLoc::RVREG_IMM && mr[i].imm == mr[r].imm) { + for (int i = 0; i < TOTAL_MAPPABLE_IRREGS; ++i) { + if (mr[i].loc == MIPSLoc::REG_IMM && mr[i].imm == mr[r].imm) { // Awesome, let's just use this reg. - return mr[i].reg; + return (RiscVReg)mr[i].nReg; } } } @@ -459,13 +423,12 @@ RiscVReg RiscVRegCache::TryMapTempImm(IRRegIndex r) { RiscVReg RiscVRegCache::GetAndLockTempR() { RiscVReg reg = AllocateReg(); if (reg != INVALID_REG) { - ar[reg].tempLocked = true; - pendingUnlock_ = true; + nr[reg].tempLockIRIndex = irIndex_; } return reg; } -RiscVReg RiscVRegCache::MapReg(IRRegIndex mipsReg, MIPSMap mapFlags) { +RiscVReg RiscVRegCache::MapReg(IRReg mipsReg, MIPSMap mapFlags) { _dbg_assert_(IsValidReg(mipsReg)); // TODO: Optimization to force HI/LO to be combined? @@ -475,7 +438,7 @@ RiscVReg RiscVRegCache::MapReg(IRRegIndex mipsReg, MIPSMap mapFlags) { return INVALID_REG; } - RiscVReg riscvReg = mr[mipsReg].reg; + RiscVReg riscvReg = (RiscVReg)mr[mipsReg].nReg; if (mr[mipsReg].isStatic) { _dbg_assert_(riscvReg != INVALID_REG); @@ -488,73 +451,73 @@ RiscVReg RiscVRegCache::MapReg(IRRegIndex mipsReg, MIPSMap mapFlags) { if ((mapFlags & MIPSMap::NOINIT) != MIPSMap::NOINIT) { // This may set normalized32 to true. SetRegImm(riscvReg, mr[mipsReg].imm); - mr[mipsReg].loc = MIPSLoc::RVREG_IMM; - ar[riscvReg].pointerified = false; + mr[mipsReg].loc = MIPSLoc::REG_IMM; + nr[riscvReg].pointerified = false; } if ((mapFlags & MIPSMap::MARK_NORM32) == MIPSMap::MARK_NORM32) - ar[riscvReg].normalized32 = true; - } else if (mr[mipsReg].loc == MIPSLoc::RVREG_AS_PTR) { + nr[riscvReg].normalized32 = true; + } else if (mr[mipsReg].loc == MIPSLoc::REG_AS_PTR) { // Was mapped as pointer, now we want it mapped as a value, presumably to // add or subtract stuff to it. if ((mapFlags & MIPSMap::NOINIT) != MIPSMap::NOINIT) { #ifdef MASKED_PSP_MEMORY - _dbg_assert_(!ar[riscvReg].isDirty && (mapFlags & MIPSMap::DIRTY) != MIPSMap::DIRTY); + _dbg_assert_(!nr[riscvReg].isDirty && (mapFlags & MIPSMap::DIRTY) != MIPSMap::DIRTY); #endif emit_->SUB(riscvReg, riscvReg, MEMBASEREG); } - mr[mipsReg].loc = MIPSLoc::RVREG; - ar[riscvReg].normalized32 = false; + mr[mipsReg].loc = MIPSLoc::REG; + nr[riscvReg].normalized32 = false; } // Erasing the imm on dirty (necessary since otherwise we will still think it's ML_RVREG_IMM and return // true for IsImm and calculate crazily wrong things). /unknown if ((mapFlags & MIPSMap::DIRTY) == MIPSMap::DIRTY) { // As we are dirty, can't keep RVREG_IMM, we will quickly drift out of sync - mr[mipsReg].loc = MIPSLoc::RVREG; - ar[riscvReg].pointerified = false; - ar[riscvReg].isDirty = true; - ar[riscvReg].normalized32 = (mapFlags & MIPSMap::MARK_NORM32) == MIPSMap::MARK_NORM32; + mr[mipsReg].loc = MIPSLoc::REG; + nr[riscvReg].pointerified = false; + nr[riscvReg].isDirty = true; + nr[riscvReg].normalized32 = (mapFlags & MIPSMap::MARK_NORM32) == MIPSMap::MARK_NORM32; } else if ((mapFlags & MIPSMap::MARK_NORM32) == MIPSMap::MARK_NORM32) { - ar[riscvReg].normalized32 = true; + nr[riscvReg].normalized32 = true; } - return mr[mipsReg].reg; + return (RiscVReg)mr[mipsReg].nReg; } // Let's see if it's already mapped. If so we just need to update the dirty flag. // We don't need to check for ML_NOINIT because we assume that anyone who maps // with that flag immediately writes a "known" value to the register. - if (mr[mipsReg].loc == MIPSLoc::RVREG || mr[mipsReg].loc == MIPSLoc::RVREG_IMM) { - _dbg_assert_(riscvReg != INVALID_REG && ar[riscvReg].mipsReg == mipsReg); - if (ar[riscvReg].mipsReg != mipsReg) { + if (mr[mipsReg].loc == MIPSLoc::REG || mr[mipsReg].loc == MIPSLoc::REG_IMM) { + _dbg_assert_(riscvReg != INVALID_REG && nr[riscvReg].mipsReg == mipsReg); + if (nr[riscvReg].mipsReg != mipsReg) { ERROR_LOG_REPORT(JIT, "Register mapping out of sync! %i", mipsReg); } if ((mapFlags & MIPSMap::DIRTY) == MIPSMap::DIRTY) { // Mapping dirty means the old imm value is invalid. - mr[mipsReg].loc = MIPSLoc::RVREG; - ar[riscvReg].isDirty = true; + mr[mipsReg].loc = MIPSLoc::REG; + nr[riscvReg].isDirty = true; // If reg is written to, pointerification is lost. - ar[riscvReg].pointerified = false; - ar[riscvReg].normalized32 = (mapFlags & MIPSMap::MARK_NORM32) == MIPSMap::MARK_NORM32; + nr[riscvReg].pointerified = false; + nr[riscvReg].normalized32 = (mapFlags & MIPSMap::MARK_NORM32) == MIPSMap::MARK_NORM32; } else if ((mapFlags & MIPSMap::MARK_NORM32) == MIPSMap::MARK_NORM32) { - ar[riscvReg].normalized32 = true; + nr[riscvReg].normalized32 = true; } - return mr[mipsReg].reg; - } else if (mr[mipsReg].loc == MIPSLoc::RVREG_AS_PTR) { + return (RiscVReg)mr[mipsReg].nReg; + } else if (mr[mipsReg].loc == MIPSLoc::REG_AS_PTR) { // Was mapped as pointer, now we want it mapped as a value, presumably to // add or subtract stuff to it. if ((mapFlags & MIPSMap::NOINIT) != MIPSMap::NOINIT) { #ifdef MASKED_PSP_MEMORY - _dbg_assert_(!ar[riscvReg].isDirty && (mapFlags & MAP_DIRTY) == 0); + _dbg_assert_(!nr[riscvReg].isDirty && (mapFlags & MAP_DIRTY) == 0); #endif emit_->SUB(riscvReg, riscvReg, MEMBASEREG); } - mr[mipsReg].loc = MIPSLoc::RVREG; + mr[mipsReg].loc = MIPSLoc::REG; if ((mapFlags & MIPSMap::DIRTY) == MIPSMap::DIRTY) { - ar[riscvReg].isDirty = true; + nr[riscvReg].isDirty = true; } // Let's always set this false, the SUB won't normalize. - ar[riscvReg].normalized32 = false; - return mr[mipsReg].reg; + nr[riscvReg].normalized32 = false; + return (RiscVReg)mr[mipsReg].nReg; } // Okay, not mapped, so we need to allocate an RV register. @@ -567,34 +530,34 @@ RiscVReg RiscVRegCache::MapReg(IRRegIndex mipsReg, MIPSMap mapFlags) { return reg; } -RiscVReg RiscVRegCache::MapRegAsPointer(IRRegIndex reg) { +RiscVReg RiscVRegCache::MapRegAsPointer(IRReg reg) { _dbg_assert_(IsValidRegNoZero(reg)); // Already mapped. - if (mr[reg].loc == MIPSLoc::RVREG_AS_PTR) { - return mr[reg].reg; + if (mr[reg].loc == MIPSLoc::REG_AS_PTR) { + return (RiscVReg)mr[reg].nReg; } RiscVReg riscvReg = INVALID_REG; - if (mr[reg].loc != MIPSLoc::RVREG && mr[reg].loc != MIPSLoc::RVREG_IMM) { + if (mr[reg].loc != MIPSLoc::REG && mr[reg].loc != MIPSLoc::REG_IMM) { riscvReg = MapReg(reg); } else { - riscvReg = mr[reg].reg; + riscvReg = (RiscVReg)mr[reg].nReg; } - if (mr[reg].loc == MIPSLoc::RVREG || mr[reg].loc == MIPSLoc::RVREG_IMM) { + if (mr[reg].loc == MIPSLoc::REG || mr[reg].loc == MIPSLoc::REG_IMM) { // If there was an imm attached, discard it. - mr[reg].loc = MIPSLoc::RVREG; + mr[reg].loc = MIPSLoc::REG; if (!jo_->enablePointerify) { // Convert to a pointer by adding the base and clearing off the top bits. // If SP, we can probably avoid the top bit clear, let's play with that later. AddMemBase(riscvReg); - mr[reg].loc = MIPSLoc::RVREG_AS_PTR; - } else if (!ar[riscvReg].pointerified) { + mr[reg].loc = MIPSLoc::REG_AS_PTR; + } else if (!nr[riscvReg].pointerified) { AddMemBase(riscvReg); - ar[riscvReg].pointerified = true; + nr[riscvReg].pointerified = true; } - ar[riscvReg].normalized32 = false; + nr[riscvReg].normalized32 = false; } else { ERROR_LOG(JIT, "MapRegAsPointer : MapReg failed to allocate a register?"); } @@ -605,7 +568,7 @@ void RiscVRegCache::AddMemBase(RiscVGen::RiscVReg reg) { _assert_(reg >= X0 && reg <= X31); #ifdef MASKED_PSP_MEMORY // This destroys the value... - _dbg_assert_(!ar[reg].isDirty); + _dbg_assert_(!nr[reg].isDirty); emit_->SLLIW(reg, reg, 2); emit_->SRLIW(reg, reg, 2); emit_->ADD(reg, reg, MEMBASEREG); @@ -620,21 +583,21 @@ void RiscVRegCache::AddMemBase(RiscVGen::RiscVReg reg) { emit_->ADD(reg, reg, MEMBASEREG); } #endif - ar[reg].normalized32 = false; + nr[reg].normalized32 = false; } -void RiscVRegCache::MapIn(IRRegIndex rs) { +void RiscVRegCache::MapIn(IRReg rs) { MapReg(rs); } -void RiscVRegCache::MapInIn(IRRegIndex rd, IRRegIndex rs) { +void RiscVRegCache::MapInIn(IRReg rd, IRReg rs) { SpillLock(rd, rs); MapReg(rd); MapReg(rs); ReleaseSpillLock(rd, rs); } -void RiscVRegCache::MapDirtyIn(IRRegIndex rd, IRRegIndex rs, MapType type) { +void RiscVRegCache::MapDirtyIn(IRReg rd, IRReg rs, MapType type) { SpillLock(rd, rs); bool load = type == MapType::ALWAYS_LOAD || rd == rs; MIPSMap norm32 = type == MapType::AVOID_LOAD_MARK_NORM32 ? MIPSMap::MARK_NORM32 : MIPSMap::INIT; @@ -643,7 +606,7 @@ void RiscVRegCache::MapDirtyIn(IRRegIndex rd, IRRegIndex rs, MapType type) { ReleaseSpillLock(rd, rs); } -void RiscVRegCache::MapDirtyInIn(IRRegIndex rd, IRRegIndex rs, IRRegIndex rt, MapType type) { +void RiscVRegCache::MapDirtyInIn(IRReg rd, IRReg rs, IRReg rt, MapType type) { SpillLock(rd, rs, rt); bool load = type == MapType::ALWAYS_LOAD || (rd == rs || rd == rt); MIPSMap norm32 = type == MapType::AVOID_LOAD_MARK_NORM32 ? MIPSMap::MARK_NORM32 : MIPSMap::INIT; @@ -653,7 +616,7 @@ void RiscVRegCache::MapDirtyInIn(IRRegIndex rd, IRRegIndex rs, IRRegIndex rt, Ma ReleaseSpillLock(rd, rs, rt); } -void RiscVRegCache::MapDirtyDirtyIn(IRRegIndex rd1, IRRegIndex rd2, IRRegIndex rs, MapType type) { +void RiscVRegCache::MapDirtyDirtyIn(IRReg rd1, IRReg rd2, IRReg rs, MapType type) { SpillLock(rd1, rd2, rs); bool load1 = type == MapType::ALWAYS_LOAD || rd1 == rs; bool load2 = type == MapType::ALWAYS_LOAD || rd2 == rs; @@ -664,7 +627,7 @@ void RiscVRegCache::MapDirtyDirtyIn(IRRegIndex rd1, IRRegIndex rd2, IRRegIndex r ReleaseSpillLock(rd1, rd2, rs); } -void RiscVRegCache::MapDirtyDirtyInIn(IRRegIndex rd1, IRRegIndex rd2, IRRegIndex rs, IRRegIndex rt, MapType type) { +void RiscVRegCache::MapDirtyDirtyInIn(IRReg rd1, IRReg rd2, IRReg rs, IRReg rt, MapType type) { SpillLock(rd1, rd2, rs, rt); bool load1 = type == MapType::ALWAYS_LOAD || (rd1 == rs || rd1 == rt); bool load2 = type == MapType::ALWAYS_LOAD || (rd2 == rs || rd2 == rt); @@ -678,79 +641,79 @@ void RiscVRegCache::MapDirtyDirtyInIn(IRRegIndex rd1, IRRegIndex rd2, IRRegIndex void RiscVRegCache::FlushRiscVReg(RiscVReg r) { _dbg_assert_(r > X0 && r <= X31); - _dbg_assert_(ar[r].mipsReg != MIPS_REG_ZERO); + _dbg_assert_(nr[r].mipsReg != MIPS_REG_ZERO); if (r == INVALID_REG) { ERROR_LOG(JIT, "FlushRiscVReg called on invalid register %d", r); return; } - if (ar[r].mipsReg == IRREG_INVALID) { + if (nr[r].mipsReg == IRREG_INVALID) { // Nothing to do, reg not mapped. - _dbg_assert_(!ar[r].isDirty); + _dbg_assert_(!nr[r].isDirty); return; } - _dbg_assert_(!mr[ar[r].mipsReg].isStatic); - if (mr[ar[r].mipsReg].isStatic) { + _dbg_assert_(!mr[nr[r].mipsReg].isStatic); + if (mr[nr[r].mipsReg].isStatic) { ERROR_LOG(JIT, "Cannot FlushRiscVReg a statically mapped register"); return; } - auto &mreg = mr[ar[r].mipsReg]; - if (mreg.loc == MIPSLoc::RVREG_IMM || ar[r].mipsReg == MIPS_REG_ZERO) { + auto &mreg = mr[nr[r].mipsReg]; + if (mreg.loc == MIPSLoc::REG_IMM || nr[r].mipsReg == MIPS_REG_ZERO) { // We know its immediate value, no need to STR now. mreg.loc = MIPSLoc::IMM; - mreg.reg = INVALID_REG; + mreg.nReg = (int)INVALID_REG; } else { - if (mreg.loc == MIPSLoc::IMM || ar[r].isDirty) { - if (mreg.loc == MIPSLoc::RVREG_AS_PTR) { + if (mreg.loc == MIPSLoc::IMM || nr[r].isDirty) { + if (mreg.loc == MIPSLoc::REG_AS_PTR) { // Unpointerify, in case dirty. #ifdef MASKED_PSP_MEMORY - _dbg_assert_(!ar[r].isDirty); + _dbg_assert_(!nr[r].isDirty); #endif emit_->SUB(r, r, MEMBASEREG); - mreg.loc = MIPSLoc::RVREG; - ar[r].normalized32 = false; + mreg.loc = MIPSLoc::REG; + nr[r].normalized32 = false; } - RiscVReg storeReg = RiscVRegForFlush(ar[r].mipsReg); + RiscVReg storeReg = RiscVRegForFlush(nr[r].mipsReg); if (storeReg != INVALID_REG) - emit_->SW(storeReg, CTXREG, GetMipsRegOffset(ar[r].mipsReg)); + emit_->SW(storeReg, CTXREG, GetMipsRegOffset(nr[r].mipsReg)); } mreg.loc = MIPSLoc::MEM; - mreg.reg = INVALID_REG; + mreg.nReg = (int)INVALID_REG; mreg.imm = -1; } - ar[r].isDirty = false; - ar[r].mipsReg = IRREG_INVALID; - ar[r].pointerified = false; + nr[r].isDirty = false; + nr[r].mipsReg = IRREG_INVALID; + nr[r].pointerified = false; } -void RiscVRegCache::DiscardR(IRRegIndex mipsReg) { +void RiscVRegCache::DiscardR(IRReg mipsReg) { _dbg_assert_(IsValidRegNoZero(mipsReg)); if (mr[mipsReg].isStatic) { // Simply do nothing unless it's an IMM/RVREG_IMM/RVREG_AS_PTR, in case we just switch it over to RVREG, losing the value. - RiscVReg riscvReg = mr[mipsReg].reg; + RiscVReg riscvReg = (RiscVReg)mr[mipsReg].nReg; _dbg_assert_(riscvReg != INVALID_REG); if (mipsReg == MIPS_REG_ZERO) { // Shouldn't happen, but in case it does. - mr[mipsReg].loc = MIPSLoc::RVREG_IMM; - mr[mipsReg].reg = R_ZERO; + mr[mipsReg].loc = MIPSLoc::REG_IMM; + mr[mipsReg].nReg = R_ZERO; mr[mipsReg].imm = 0; - } else if (mr[mipsReg].loc == MIPSLoc::RVREG_IMM || mr[mipsReg].loc == MIPSLoc::IMM || mr[mipsReg].loc == MIPSLoc::RVREG_AS_PTR) { + } else if (mr[mipsReg].loc == MIPSLoc::REG_IMM || mr[mipsReg].loc == MIPSLoc::IMM || mr[mipsReg].loc == MIPSLoc::REG_AS_PTR) { // Ignore the imm value, restore sanity - mr[mipsReg].loc = MIPSLoc::RVREG; - ar[riscvReg].pointerified = false; - ar[riscvReg].isDirty = false; - ar[riscvReg].normalized32 = false; + mr[mipsReg].loc = MIPSLoc::REG; + nr[riscvReg].pointerified = false; + nr[riscvReg].isDirty = false; + nr[riscvReg].normalized32 = false; } return; } const MIPSLoc prevLoc = mr[mipsReg].loc; - if (prevLoc == MIPSLoc::RVREG || prevLoc == MIPSLoc::RVREG_IMM || prevLoc == MIPSLoc::RVREG_AS_PTR) { - RiscVReg riscvReg = mr[mipsReg].reg; + if (prevLoc == MIPSLoc::REG || prevLoc == MIPSLoc::REG_IMM || prevLoc == MIPSLoc::REG_AS_PTR) { + RiscVReg riscvReg = (RiscVReg)mr[mipsReg].nReg; _dbg_assert_(riscvReg != INVALID_REG); - ar[riscvReg].mipsReg = IRREG_INVALID; - ar[riscvReg].pointerified = false; - ar[riscvReg].isDirty = false; - ar[riscvReg].normalized32 = false; - mr[mipsReg].reg = INVALID_REG; + nr[riscvReg].mipsReg = IRREG_INVALID; + nr[riscvReg].pointerified = false; + nr[riscvReg].isDirty = false; + nr[riscvReg].normalized32 = false; + mr[mipsReg].nReg = (int)INVALID_REG; mr[mipsReg].loc = MIPSLoc::MEM; mr[mipsReg].imm = -1; } @@ -760,7 +723,7 @@ void RiscVRegCache::DiscardR(IRRegIndex mipsReg) { } } -RiscVReg RiscVRegCache::RiscVRegForFlush(IRRegIndex r) { +RiscVReg RiscVRegCache::RiscVRegForFlush(IRReg r) { _dbg_assert_(IsValidReg(r)); if (mr[r].isStatic) return INVALID_REG; // No flushing needed @@ -775,28 +738,28 @@ RiscVReg RiscVRegCache::RiscVRegForFlush(IRRegIndex r) { return R_ZERO; } // Could we get lucky? Check for an exact match in another rvreg. - for (int i = 0; i < NUM_MIPSREG; ++i) { - if (mr[i].loc == MIPSLoc::RVREG_IMM && mr[i].imm == mr[r].imm) { + for (int i = 0; i < TOTAL_MAPPABLE_IRREGS; ++i) { + if (mr[i].loc == MIPSLoc::REG_IMM && mr[i].imm == mr[r].imm) { // Awesome, let's just store this reg. - return mr[i].reg; + return (RiscVReg)mr[i].nReg; } } return INVALID_REG; - case MIPSLoc::RVREG: - case MIPSLoc::RVREG_IMM: - if (mr[r].reg == INVALID_REG) { + case MIPSLoc::REG: + case MIPSLoc::REG_IMM: + if (mr[r].nReg == INVALID_REG) { ERROR_LOG_REPORT(JIT, "RiscVRegForFlush: MipsReg %d had bad riscvReg", r); return INVALID_REG; } // No need to flush if it's zero or not dirty. - if (r == MIPS_REG_ZERO || !ar[mr[r].reg].isDirty) { + if (r == MIPS_REG_ZERO || !nr[mr[r].nReg].isDirty) { return INVALID_REG; } // TODO: Lo/hi optimization? - return mr[r].reg; + return (RiscVReg)mr[r].nReg; - case MIPSLoc::RVREG_AS_PTR: + case MIPSLoc::REG_AS_PTR: return INVALID_REG; case MIPSLoc::MEM: @@ -808,7 +771,7 @@ RiscVReg RiscVRegCache::RiscVRegForFlush(IRRegIndex r) { } } -void RiscVRegCache::FlushR(IRRegIndex r) { +void RiscVRegCache::FlushR(IRReg r) { _dbg_assert_(IsValidRegNoZero(r)); if (mr[r].isStatic) { ERROR_LOG(JIT, "Cannot flush static reg %d", r); @@ -830,35 +793,35 @@ void RiscVRegCache::FlushR(IRRegIndex r) { } break; - case MIPSLoc::RVREG: - case MIPSLoc::RVREG_IMM: - if (ar[mr[r].reg].isDirty) { + case MIPSLoc::REG: + case MIPSLoc::REG_IMM: + if (nr[mr[r].nReg].isDirty) { RiscVReg storeReg = RiscVRegForFlush(r); if (storeReg != INVALID_REG) { emit_->SW(storeReg, CTXREG, GetMipsRegOffset(r)); } - ar[mr[r].reg].isDirty = false; + nr[mr[r].nReg].isDirty = false; } - ar[mr[r].reg].mipsReg = IRREG_INVALID; - ar[mr[r].reg].pointerified = false; + nr[mr[r].nReg].mipsReg = IRREG_INVALID; + nr[mr[r].nReg].pointerified = false; break; - case MIPSLoc::RVREG_AS_PTR: - if (ar[mr[r].reg].isDirty) { + case MIPSLoc::REG_AS_PTR: + if (nr[mr[r].nReg].isDirty) { #ifdef MASKED_PSP_MEMORY // This is kinda bad, because we've cleared bits in it. - _dbg_assert_(!ar[mr[r].reg].isDirty); + _dbg_assert_(!nr[mr[r].nReg].isDirty); #endif - emit_->SUB(mr[r].reg, mr[r].reg, MEMBASEREG); + emit_->SUB((RiscVReg)mr[r].nReg, (RiscVReg)mr[r].nReg, MEMBASEREG); // We set this so RiscVRegForFlush knows it's no longer a pointer. - mr[r].loc = MIPSLoc::RVREG; + mr[r].loc = MIPSLoc::REG; RiscVReg storeReg = RiscVRegForFlush(r); if (storeReg != INVALID_REG) { emit_->SW(storeReg, CTXREG, GetMipsRegOffset(r)); } - ar[mr[r].reg].isDirty = false; + nr[mr[r].nReg].isDirty = false; } - ar[mr[r].reg].mipsReg = IRREG_INVALID; + nr[mr[r].nReg].mipsReg = IRREG_INVALID; break; case MIPSLoc::MEM: @@ -870,12 +833,12 @@ void RiscVRegCache::FlushR(IRRegIndex r) { break; } if (r == MIPS_REG_ZERO) { - mr[r].loc = MIPSLoc::RVREG_IMM; - mr[r].reg = R_ZERO; + mr[r].loc = MIPSLoc::REG_IMM; + mr[r].nReg = R_ZERO; mr[r].imm = 0; } else { mr[r].loc = MIPSLoc::MEM; - mr[r].reg = INVALID_REG; + mr[r].nReg = (int)INVALID_REG; mr[r].imm = -1; } } @@ -887,31 +850,31 @@ void RiscVRegCache::FlushAll() { // TODO: HI/LO optimization? // Final pass to grab any that were left behind. - for (int i = 1; i < NUM_MIPSREG; i++) { - IRRegIndex mipsReg = IRRegIndex(i); + for (int i = 1; i < TOTAL_MAPPABLE_IRREGS; i++) { + IRReg mipsReg = IRReg(i); if (mr[i].isStatic) { - RiscVReg riscvReg = mr[i].reg; + RiscVReg riscvReg = (RiscVReg)mr[i].nReg; // Cannot leave any IMMs in registers, not even ML_ARMREG_IMM, can confuse the regalloc later if this flush is mid-block // due to an interpreter fallback that changes the register. if (mr[i].loc == MIPSLoc::IMM) { - SetRegImm(mr[i].reg, mr[i].imm); - mr[i].loc = MIPSLoc::RVREG; - ar[riscvReg].pointerified = false; - } else if (mr[i].loc == MIPSLoc::RVREG_IMM) { + SetRegImm((RiscVReg)mr[i].nReg, mr[i].imm); + mr[i].loc = MIPSLoc::REG; + nr[riscvReg].pointerified = false; + } else if (mr[i].loc == MIPSLoc::REG_IMM) { // The register already contains the immediate. - if (ar[riscvReg].pointerified) { + if (nr[riscvReg].pointerified) { ERROR_LOG(JIT, "RVREG_IMM but pointerified. Wrong."); - ar[riscvReg].pointerified = false; + nr[riscvReg].pointerified = false; } - mr[i].loc = MIPSLoc::RVREG; - } else if (mr[i].loc == MIPSLoc::RVREG_AS_PTR) { + mr[i].loc = MIPSLoc::REG; + } else if (mr[i].loc == MIPSLoc::REG_AS_PTR) { #ifdef MASKED_PSP_MEMORY - _dbg_assert_(!ar[riscvReg].isDirty); + _dbg_assert_(!nr[riscvReg].isDirty); #endif emit_->SUB(riscvReg, riscvReg, MEMBASEREG); - mr[i].loc = MIPSLoc::RVREG; + mr[i].loc = MIPSLoc::REG; } - if (i != MIPS_REG_ZERO && mr[i].reg == INVALID_REG) { + if (i != MIPS_REG_ZERO && mr[i].nReg == INVALID_REG) { ERROR_LOG(JIT, "RV reg of static %i is invalid", i); continue; } @@ -923,34 +886,34 @@ void RiscVRegCache::FlushAll() { int count = 0; const StaticAllocation *allocs = GetStaticAllocations(count); for (int i = 0; i < count; i++) { - if (allocs[i].pointerified && !ar[allocs[i].ar].pointerified && jo_->enablePointerify) { + if (allocs[i].pointerified && !nr[allocs[i].nr].pointerified && jo_->enablePointerify) { // Re-pointerify - _dbg_assert_(mr[allocs[i].mr].loc == MIPSLoc::RVREG); - AddMemBase(allocs[i].ar); - ar[allocs[i].ar].pointerified = true; + _dbg_assert_(mr[allocs[i].mr].loc == MIPSLoc::REG); + AddMemBase((RiscVReg)allocs[i].nr); + nr[allocs[i].nr].pointerified = true; } else if (!allocs[i].pointerified) { // If this register got pointerified on the way, mark it as not. // This is so that after save/reload (like in an interpreter fallback), // it won't be regarded as such, as it may no longer be. - ar[allocs[i].ar].pointerified = false; + nr[allocs[i].nr].pointerified = false; } } // Sanity check for (int i = 0; i < NUM_RVREG; i++) { - if (ar[i].mipsReg != IRREG_INVALID && mr[ar[i].mipsReg].isStatic == false) { - ERROR_LOG_REPORT(JIT, "Flush fail: ar[%i].mipsReg=%i", i, ar[i].mipsReg); + if (nr[i].mipsReg != IRREG_INVALID && mr[nr[i].mipsReg].isStatic == false) { + ERROR_LOG_REPORT(JIT, "Flush fail: nr[%i].mipsReg=%i", i, nr[i].mipsReg); } } } -void RiscVRegCache::SetImm(IRRegIndex r, u64 immVal) { +void RiscVRegCache::SetImm(IRReg r, u64 immVal) { _dbg_assert_(IsValidReg(r)); if (r == MIPS_REG_ZERO && immVal != 0) { ERROR_LOG_REPORT(JIT, "Trying to set immediate %08x to r0", (u32)immVal); return; } - if (mr[r].loc == MIPSLoc::RVREG_IMM && mr[r].imm == immVal) { + if (mr[r].loc == MIPSLoc::REG_IMM && mr[r].imm == immVal) { // Already have that value, let's keep it in the reg. return; } @@ -962,48 +925,48 @@ void RiscVRegCache::SetImm(IRRegIndex r, u64 immVal) { if (mr[r].isStatic) { mr[r].loc = MIPSLoc::IMM; mr[r].imm = immVal; - ar[mr[r].reg].pointerified = false; - ar[mr[r].reg].normalized32 = false; + nr[mr[r].nReg].pointerified = false; + nr[mr[r].nReg].normalized32 = false; // We do not change reg to INVALID_REG for obvious reasons.. } else { // Zap existing value if cached in a reg - if (mr[r].reg != INVALID_REG) { - ar[mr[r].reg].mipsReg = IRREG_INVALID; - ar[mr[r].reg].isDirty = false; - ar[mr[r].reg].pointerified = false; - ar[mr[r].reg].normalized32 = false; + if (mr[r].nReg != INVALID_REG) { + nr[mr[r].nReg].mipsReg = IRREG_INVALID; + nr[mr[r].nReg].isDirty = false; + nr[mr[r].nReg].pointerified = false; + nr[mr[r].nReg].normalized32 = false; } mr[r].loc = MIPSLoc::IMM; mr[r].imm = immVal; - mr[r].reg = INVALID_REG; + mr[r].nReg = (int)INVALID_REG; } } -bool RiscVRegCache::IsImm(IRRegIndex r) const { +bool RiscVRegCache::IsImm(IRReg r) const { _dbg_assert_(IsValidReg(r)); if (r == MIPS_REG_ZERO) return true; else - return mr[r].loc == MIPSLoc::IMM || mr[r].loc == MIPSLoc::RVREG_IMM; + return mr[r].loc == MIPSLoc::IMM || mr[r].loc == MIPSLoc::REG_IMM; } -u64 RiscVRegCache::GetImm(IRRegIndex r) const { +u64 RiscVRegCache::GetImm(IRReg r) const { _dbg_assert_(IsValidReg(r)); if (r == MIPS_REG_ZERO) return 0; - if (mr[r].loc != MIPSLoc::IMM && mr[r].loc != MIPSLoc::RVREG_IMM) { + if (mr[r].loc != MIPSLoc::IMM && mr[r].loc != MIPSLoc::REG_IMM) { ERROR_LOG_REPORT(JIT, "Trying to get imm from non-imm register %i", r); } return mr[r].imm; } -int RiscVRegCache::GetMipsRegOffset(IRRegIndex r) { +int RiscVRegCache::GetMipsRegOffset(IRReg r) { _dbg_assert_(IsValidReg(r)); return r * 4; } -bool RiscVRegCache::IsValidReg(IRRegIndex r) const { - if (r < 0 || r >= NUM_MIPSREG) +bool RiscVRegCache::IsValidReg(IRReg r) const { + if (r < 0 || r >= TOTAL_MAPPABLE_IRREGS) return false; // See MIPSState for these offsets. @@ -1024,73 +987,57 @@ bool RiscVRegCache::IsValidReg(IRRegIndex r) const { return true; } -bool RiscVRegCache::IsValidRegNoZero(IRRegIndex r) const { +bool RiscVRegCache::IsValidRegNoZero(IRReg r) const { return IsValidReg(r) && r != MIPS_REG_ZERO; } -void RiscVRegCache::SpillLock(IRRegIndex r1, IRRegIndex r2, IRRegIndex r3, IRRegIndex r4) { +void RiscVRegCache::SpillLock(IRReg r1, IRReg r2, IRReg r3, IRReg r4) { _dbg_assert_(IsValidReg(r1)); _dbg_assert_(r2 == IRREG_INVALID || IsValidReg(r2)); _dbg_assert_(r3 == IRREG_INVALID || IsValidReg(r3)); _dbg_assert_(r4 == IRREG_INVALID || IsValidReg(r4)); - mr[r1].spillLock = true; - if (r2 != IRREG_INVALID) mr[r2].spillLock = true; - if (r3 != IRREG_INVALID) mr[r3].spillLock = true; - if (r4 != IRREG_INVALID) mr[r4].spillLock = true; - pendingUnlock_ = true; + mr[r1].spillLockIRIndex = irIndex_; + if (r2 != IRREG_INVALID) mr[r2].spillLockIRIndex = irIndex_; + if (r3 != IRREG_INVALID) mr[r3].spillLockIRIndex = irIndex_; + if (r4 != IRREG_INVALID) mr[r4].spillLockIRIndex = irIndex_; } -void RiscVRegCache::ReleaseSpillLocksAndDiscardTemps() { - if (!pendingUnlock_) - return; - - for (int i = 0; i < NUM_MIPSREG; i++) { - if (!mr[i].isStatic) - mr[i].spillLock = false; - } - for (int i = 0; i < NUM_RVREG; i++) { - ar[i].tempLocked = false; - } - - pendingUnlock_ = false; -} - -void RiscVRegCache::ReleaseSpillLock(IRRegIndex r1, IRRegIndex r2, IRRegIndex r3, IRRegIndex r4) { +void RiscVRegCache::ReleaseSpillLock(IRReg r1, IRReg r2, IRReg r3, IRReg r4) { _dbg_assert_(IsValidReg(r1)); _dbg_assert_(r2 == IRREG_INVALID || IsValidReg(r2)); _dbg_assert_(r3 == IRREG_INVALID || IsValidReg(r3)); _dbg_assert_(r4 == IRREG_INVALID || IsValidReg(r4)); if (!mr[r1].isStatic) - mr[r1].spillLock = false; + mr[r1].spillLockIRIndex = -1; if (r2 != IRREG_INVALID && !mr[r2].isStatic) - mr[r2].spillLock = false; + mr[r2].spillLockIRIndex = -1; if (r3 != IRREG_INVALID && !mr[r3].isStatic) - mr[r3].spillLock = false; + mr[r3].spillLockIRIndex = -1; if (r4 != IRREG_INVALID && !mr[r4].isStatic) - mr[r4].spillLock = false; + mr[r4].spillLockIRIndex = -1; } -RiscVReg RiscVRegCache::R(IRRegIndex mipsReg) { +RiscVReg RiscVRegCache::R(IRReg mipsReg) { _dbg_assert_(IsValidReg(mipsReg)); - _dbg_assert_(mr[mipsReg].loc == MIPSLoc::RVREG || mr[mipsReg].loc == MIPSLoc::RVREG_IMM); - if (mr[mipsReg].loc == MIPSLoc::RVREG || mr[mipsReg].loc == MIPSLoc::RVREG_IMM) { - return mr[mipsReg].reg; + _dbg_assert_(mr[mipsReg].loc == MIPSLoc::REG || mr[mipsReg].loc == MIPSLoc::REG_IMM); + if (mr[mipsReg].loc == MIPSLoc::REG || mr[mipsReg].loc == MIPSLoc::REG_IMM) { + return (RiscVReg)mr[mipsReg].nReg; } else { ERROR_LOG_REPORT(JIT, "Reg %i not in riscv reg", mipsReg); return INVALID_REG; // BAAAD } } -RiscVReg RiscVRegCache::RPtr(IRRegIndex mipsReg) { +RiscVReg RiscVRegCache::RPtr(IRReg mipsReg) { _dbg_assert_(IsValidReg(mipsReg)); - _dbg_assert_(mr[mipsReg].loc == MIPSLoc::RVREG || mr[mipsReg].loc == MIPSLoc::RVREG_IMM || mr[mipsReg].loc == MIPSLoc::RVREG_AS_PTR); - if (mr[mipsReg].loc == MIPSLoc::RVREG_AS_PTR) { - return mr[mipsReg].reg; - } else if (mr[mipsReg].loc == MIPSLoc::RVREG || mr[mipsReg].loc == MIPSLoc::RVREG_IMM) { - int rv = mr[mipsReg].reg; - _dbg_assert_(ar[rv].pointerified); - if (ar[rv].pointerified) { - return mr[mipsReg].reg; + _dbg_assert_(mr[mipsReg].loc == MIPSLoc::REG || mr[mipsReg].loc == MIPSLoc::REG_IMM || mr[mipsReg].loc == MIPSLoc::REG_AS_PTR); + if (mr[mipsReg].loc == MIPSLoc::REG_AS_PTR) { + return (RiscVReg)mr[mipsReg].nReg; + } else if (mr[mipsReg].loc == MIPSLoc::REG || mr[mipsReg].loc == MIPSLoc::REG_IMM) { + int rv = mr[mipsReg].nReg; + _dbg_assert_(nr[rv].pointerified); + if (nr[rv].pointerified) { + return (RiscVReg)mr[mipsReg].nReg; } else { ERROR_LOG(JIT, "Tried to use a non-pointer register as a pointer"); return INVALID_REG; diff --git a/Core/MIPS/RiscV/RiscVRegCache.h b/Core/MIPS/RiscV/RiscVRegCache.h index ffe373c639..2e84e3e6f4 100644 --- a/Core/MIPS/RiscV/RiscVRegCache.h +++ b/Core/MIPS/RiscV/RiscVRegCache.h @@ -20,6 +20,7 @@ #include "Common/RiscVEmitter.h" #include "Core/MIPS/MIPS.h" #include "Core/MIPS/IR/IRJit.h" +#include "Core/MIPS/IR/IRRegCache.h" namespace RiscVJitConstants { @@ -34,19 +35,6 @@ const RiscVGen::RiscVReg MEMBASEREG = RiscVGen::X27; const RiscVGen::RiscVReg SCRATCH1 = RiscVGen::X10; const RiscVGen::RiscVReg SCRATCH2 = RiscVGen::X11; -// Have to account for all of them due to temps, etc. -constexpr int TOTAL_MAPPABLE_MIPSREGS = 256; - -enum class MIPSLoc { - IMM, - RVREG, - // In a native reg, but an adjusted pointer (not pointerified - unaligned.) - RVREG_AS_PTR, - // In a native reg, but also has a known immediate value. - RVREG_IMM, - MEM, -}; - // Initing is the default so the flag is reversed. enum class MIPSMap { INIT = 0, @@ -69,129 +57,79 @@ enum class MapType { } // namespace RiscVJitConstants -namespace MIPSComp { -struct JitOptions; -} - -// Not using IRReg since this can be -1. -typedef int IRRegIndex; -constexpr IRRegIndex IRREG_INVALID = -1; - -struct RegStatusRiscV { - IRRegIndex mipsReg; // if -1, no mipsreg attached. - bool isDirty; // Should the register be written back? - bool pointerified; // Has added the memory base into the top part of the reg. Note - still usable as 32-bit reg (in only some cases.) - bool tempLocked; // Reserved for a temp register. - bool normalized32; // 32 bits sign extended to XLEN. RISC-V can't always address just the low 32-bits, so this matters. -}; - -struct RegStatusMIPS { - // Where is this MIPS register? - RiscVJitConstants::MIPSLoc loc; - // Data (both or only one may be used, depending on loc.) - u64 imm; - RiscVGen::RiscVReg reg; // reg index - bool spillLock; // if true, this register cannot be spilled. - bool isStatic; // if true, this register will not be written back to ram by the regcache - // If loc == ML_MEM, it's back in its location in the CPU context struct. -}; - -class RiscVRegCache { +class RiscVRegCache : public IRNativeRegCache { public: - RiscVRegCache(MIPSState *mipsState, MIPSComp::JitOptions *jo); - ~RiscVRegCache() {} + RiscVRegCache(MIPSComp::JitOptions *jo); void Init(RiscVGen::RiscVEmitter *emitter); - void Start(MIPSComp::IRBlock *irBlock); - void SetIRIndex(int index) { - irIndex_ = index; - } // Protect the arm register containing a MIPS register from spilling, to ensure that // it's being kept allocated. - void SpillLock(IRRegIndex reg, IRRegIndex reg2 = IRREG_INVALID, IRRegIndex reg3 = IRREG_INVALID, IRRegIndex reg4 = IRREG_INVALID); - void ReleaseSpillLock(IRRegIndex reg, IRRegIndex reg2 = IRREG_INVALID, IRRegIndex reg3 = IRREG_INVALID, IRRegIndex reg4 = IRREG_INVALID); - void ReleaseSpillLocksAndDiscardTemps(); + void SpillLock(IRReg reg, IRReg reg2 = IRREG_INVALID, IRReg reg3 = IRREG_INVALID, IRReg reg4 = IRREG_INVALID); + void ReleaseSpillLock(IRReg reg, IRReg reg2 = IRREG_INVALID, IRReg reg3 = IRREG_INVALID, IRReg reg4 = IRREG_INVALID); - void SetImm(IRRegIndex reg, u64 immVal); - bool IsImm(IRRegIndex reg) const; - u64 GetImm(IRRegIndex reg) const; + void SetImm(IRReg reg, u64 immVal); + bool IsImm(IRReg reg) const; + u64 GetImm(IRReg reg) const; // May fail and return INVALID_REG if it needs flushing. - RiscVGen::RiscVReg TryMapTempImm(IRRegIndex); + RiscVGen::RiscVReg TryMapTempImm(IRReg); // Returns an ARM register containing the requested MIPS register. - RiscVGen::RiscVReg MapReg(IRRegIndex reg, RiscVJitConstants::MIPSMap mapFlags = RiscVJitConstants::MIPSMap::INIT); - RiscVGen::RiscVReg MapRegAsPointer(IRRegIndex reg); + RiscVGen::RiscVReg MapReg(IRReg reg, RiscVJitConstants::MIPSMap mapFlags = RiscVJitConstants::MIPSMap::INIT); + RiscVGen::RiscVReg MapRegAsPointer(IRReg reg); - bool IsMapped(IRRegIndex reg); - bool IsMappedAsPointer(IRRegIndex reg); - bool IsMappedAsStaticPointer(IRRegIndex reg); - bool IsInRAM(IRRegIndex reg); - bool IsNormalized32(IRRegIndex reg); + bool IsMapped(IRReg reg); + bool IsMappedAsPointer(IRReg reg); + bool IsMappedAsStaticPointer(IRReg reg); + bool IsInRAM(IRReg reg); + bool IsNormalized32(IRReg reg); void MarkDirty(RiscVGen::RiscVReg reg, bool andNormalized32 = false); void MarkPtrDirty(RiscVGen::RiscVReg reg); // Copies to another reg if specified, otherwise same reg. - RiscVGen::RiscVReg Normalize32(IRRegIndex reg, RiscVGen::RiscVReg destReg = RiscVGen::INVALID_REG); - void MapIn(IRRegIndex rs); - void MapInIn(IRRegIndex rd, IRRegIndex rs); - void MapDirtyIn(IRRegIndex rd, IRRegIndex rs, RiscVJitConstants::MapType type = RiscVJitConstants::MapType::AVOID_LOAD); - void MapDirtyInIn(IRRegIndex rd, IRRegIndex rs, IRRegIndex rt, RiscVJitConstants::MapType type = RiscVJitConstants::MapType::AVOID_LOAD); - void MapDirtyDirtyIn(IRRegIndex rd1, IRRegIndex rd2, IRRegIndex rs, RiscVJitConstants::MapType type = RiscVJitConstants::MapType::AVOID_LOAD); - void MapDirtyDirtyInIn(IRRegIndex rd1, IRRegIndex rd2, IRRegIndex rs, IRRegIndex rt, RiscVJitConstants::MapType type = RiscVJitConstants::MapType::AVOID_LOAD); + RiscVGen::RiscVReg Normalize32(IRReg reg, RiscVGen::RiscVReg destReg = RiscVGen::INVALID_REG); + void MapIn(IRReg rs); + void MapInIn(IRReg rd, IRReg rs); + void MapDirtyIn(IRReg rd, IRReg rs, RiscVJitConstants::MapType type = RiscVJitConstants::MapType::AVOID_LOAD); + void MapDirtyInIn(IRReg rd, IRReg rs, IRReg rt, RiscVJitConstants::MapType type = RiscVJitConstants::MapType::AVOID_LOAD); + void MapDirtyDirtyIn(IRReg rd1, IRReg rd2, IRReg rs, RiscVJitConstants::MapType type = RiscVJitConstants::MapType::AVOID_LOAD); + void MapDirtyDirtyInIn(IRReg rd1, IRReg rd2, IRReg rs, IRReg rt, RiscVJitConstants::MapType type = RiscVJitConstants::MapType::AVOID_LOAD); void FlushBeforeCall(); void FlushAll(); - void FlushR(IRRegIndex r); + void FlushR(IRReg r); void FlushRiscVReg(RiscVGen::RiscVReg r); - void DiscardR(IRRegIndex r); + void DiscardR(IRReg r); RiscVGen::RiscVReg GetAndLockTempR(); - RiscVGen::RiscVReg R(IRRegIndex preg); // Returns a cached register, while checking that it's NOT mapped as a pointer - RiscVGen::RiscVReg RPtr(IRRegIndex preg); // Returns a cached register, if it has been mapped as a pointer + RiscVGen::RiscVReg R(IRReg preg); // Returns a cached register, while checking that it's NOT mapped as a pointer + RiscVGen::RiscVReg RPtr(IRReg preg); // Returns a cached register, if it has been mapped as a pointer // These are called once on startup to generate functions, that you should then call. void EmitLoadStaticRegisters(); void EmitSaveStaticRegisters(); +protected: + void SetupInitialRegs() override; + const StaticAllocation *GetStaticAllocations(int &count) override; + private: - struct StaticAllocation { - IRRegIndex mr; - RiscVGen::RiscVReg ar; - bool pointerified; - }; - const StaticAllocation *GetStaticAllocations(int &count); const RiscVGen::RiscVReg *GetMIPSAllocationOrder(int &count); - void MapRegTo(RiscVGen::RiscVReg reg, IRRegIndex mipsReg, RiscVJitConstants::MIPSMap mapFlags); + void MapRegTo(RiscVGen::RiscVReg reg, IRReg mipsReg, RiscVJitConstants::MIPSMap mapFlags); RiscVGen::RiscVReg AllocateReg(); RiscVGen::RiscVReg FindBestToSpill(bool unusedOnly, bool *clobbered); - RiscVGen::RiscVReg RiscVRegForFlush(IRRegIndex r); + RiscVGen::RiscVReg RiscVRegForFlush(IRReg r); void SetRegImm(RiscVGen::RiscVReg reg, u64 imm); void AddMemBase(RiscVGen::RiscVReg reg); - int GetMipsRegOffset(IRRegIndex r); + int GetMipsRegOffset(IRReg r); - bool IsValidReg(IRRegIndex r) const; - bool IsValidRegNoZero(IRRegIndex r) const; + bool IsValidReg(IRReg r) const; + bool IsValidRegNoZero(IRReg r) const; - void SetupInitialRegs(); - - MIPSState *mips_; RiscVGen::RiscVEmitter *emit_ = nullptr; - MIPSComp::JitOptions *jo_; - MIPSComp::IRBlock *irBlock_ = nullptr; - int irIndex_ = 0; enum { - NUM_RVREG = 32, // 31 actual registers, plus the zero/sp register which is not mappable. - NUM_MIPSREG = RiscVJitConstants::TOTAL_MAPPABLE_MIPSREGS, + NUM_RVREG = 32, }; - - RegStatusRiscV ar[NUM_RVREG]{}; - RegStatusMIPS mr[NUM_MIPSREG]{}; - - bool initialReady_ = false; - bool pendingUnlock_ = false; - RegStatusRiscV arInitial_[NUM_RVREG]; - RegStatusMIPS mrInitial_[NUM_MIPSREG]; }; diff --git a/Core/MIPS/RiscV/RiscVRegCacheFPU.cpp b/Core/MIPS/RiscV/RiscVRegCacheFPU.cpp index 1ecb7b4847..63f22a7abb 100644 --- a/Core/MIPS/RiscV/RiscVRegCacheFPU.cpp +++ b/Core/MIPS/RiscV/RiscVRegCacheFPU.cpp @@ -29,39 +29,15 @@ using namespace RiscVGen; using namespace RiscVJitConstants; -RiscVRegCacheFPU::RiscVRegCacheFPU(MIPSState *mipsState, MIPSComp::JitOptions *jo) - : mips_(mipsState), jo_(jo) {} +RiscVRegCacheFPU::RiscVRegCacheFPU(MIPSComp::JitOptions *jo) + : IRNativeRegCache(jo) { + totalNativeRegs_ = NUM_RVFPUREG; +} void RiscVRegCacheFPU::Init(RiscVEmitter *emitter) { emit_ = emitter; } -void RiscVRegCacheFPU::Start(MIPSComp::IRBlock *irBlock) { - if (!initialReady_) { - SetupInitialRegs(); - initialReady_ = true; - } - - memcpy(ar, arInitial_, sizeof(ar)); - memcpy(mr, mrInitial_, sizeof(mr)); - pendingFlush_ = false; - - irBlock_ = irBlock; - irIndex_ = 0; -} - -void RiscVRegCacheFPU::SetupInitialRegs() { - for (int i = 0; i < NUM_RVFPUREG; i++) { - arInitial_[i].mipsReg = IRREG_INVALID; - arInitial_[i].isDirty = false; - } - for (int i = 0; i < NUM_MIPSFPUREG; i++) { - mrInitial_[i].loc = MIPSLoc::MEM; - mrInitial_[i].reg = (int)INVALID_REG; - mrInitial_[i].spillLock = false; - } -} - const RiscVReg *RiscVRegCacheFPU::GetMIPSAllocationOrder(int &count) { // F8 through F15 are used for compression, so they are great. // TODO: Maybe we could remove some saved regs since we rarely need that many? Or maybe worth it? @@ -75,46 +51,46 @@ const RiscVReg *RiscVRegCacheFPU::GetMIPSAllocationOrder(int &count) { return allocationOrder; } -bool RiscVRegCacheFPU::IsInRAM(IRRegIndex reg) { +bool RiscVRegCacheFPU::IsInRAM(IRReg reg) { _dbg_assert_(IsValidReg(reg)); return mr[reg].loc == MIPSLoc::MEM; } -bool RiscVRegCacheFPU::IsMapped(IRRegIndex mipsReg) { +bool RiscVRegCacheFPU::IsMapped(IRReg mipsReg) { _dbg_assert_(IsValidReg(mipsReg)); - return mr[mipsReg].loc == MIPSLoc::RVREG; + return mr[mipsReg].loc == MIPSLoc::FREG; } -RiscVReg RiscVRegCacheFPU::MapReg(IRRegIndex mipsReg, MIPSMap mapFlags) { +RiscVReg RiscVRegCacheFPU::MapReg(IRReg mipsReg, MIPSMap mapFlags) { _dbg_assert_(IsValidReg(mipsReg)); - _dbg_assert_(mr[mipsReg].loc == MIPSLoc::MEM || mr[mipsReg].loc == MIPSLoc::RVREG); + _dbg_assert_(mr[mipsReg].loc == MIPSLoc::MEM || mr[mipsReg].loc == MIPSLoc::FREG); pendingFlush_ = true; // Let's see if it's already mapped. If so we just need to update the dirty flag. // We don't need to check for NOINIT because we assume that anyone who maps // with that flag immediately writes a "known" value to the register. - if (mr[mipsReg].loc == MIPSLoc::RVREG) { - _assert_msg_(ar[mr[mipsReg].reg].mipsReg == mipsReg, "GPU mapping out of sync, IR=%i", mipsReg); + if (mr[mipsReg].loc == MIPSLoc::FREG) { + _assert_msg_(nr[mr[mipsReg].nReg].mipsReg == mipsReg, "GPU mapping out of sync, IR=%i", mipsReg); if ((mapFlags & MIPSMap::DIRTY) == MIPSMap::DIRTY) { - ar[mr[mipsReg].reg].isDirty = true; + nr[mr[mipsReg].nReg].isDirty = true; } - return (RiscVReg)(mr[mipsReg].reg + F0); + return (RiscVReg)(mr[mipsReg].nReg + F0); } // Okay, not mapped, so we need to allocate an RV register. RiscVReg reg = AllocateReg(); if (reg != INVALID_REG) { // That means it's free. Grab it, and load the value into it (if requested). - ar[reg - F0].isDirty = (mapFlags & MIPSMap::DIRTY) == MIPSMap::DIRTY; + nr[reg - F0].isDirty = (mapFlags & MIPSMap::DIRTY) == MIPSMap::DIRTY; if ((mapFlags & MIPSMap::NOINIT) != MIPSMap::NOINIT) { if (mr[mipsReg].loc == MIPSLoc::MEM) { emit_->FL(32, reg, CTXREG, GetMipsRegOffset(mipsReg)); } } - ar[reg - F0].mipsReg = mipsReg; - mr[mipsReg].loc = MIPSLoc::RVREG; - mr[mipsReg].reg = reg - F0; + nr[reg - F0].mipsReg = mipsReg; + mr[mipsReg].loc = MIPSLoc::FREG; + mr[mipsReg].nReg = reg - F0; return reg; } @@ -129,7 +105,7 @@ allocate: for (int i = 0; i < allocCount; i++) { RiscVReg reg = allocOrder[i]; - if (ar[reg - F0].mipsReg == IRREG_INVALID) { + if (nr[reg - F0].mipsReg == IRREG_INVALID) { return reg; } } @@ -143,7 +119,7 @@ allocate: if (bestToSpill != INVALID_REG) { if (clobbered) { - DiscardR(ar[bestToSpill - F0].mipsReg); + DiscardR(nr[bestToSpill - F0].mipsReg); } else { FlushRiscVReg(bestToSpill); } @@ -152,7 +128,7 @@ allocate: } // Uh oh, we have all of them spilllocked.... - ERROR_LOG_REPORT(JIT, "Out of spillable registers near PC %08x", mips_->pc); + ERROR_LOG_REPORT(JIT, "Out of spillable registers in block PC %08x, index %d", irBlock_->GetOriginalStart(), irIndex_); _assert_(bestToSpill != INVALID_REG); return INVALID_REG; } @@ -172,11 +148,11 @@ RiscVReg RiscVRegCacheFPU::FindBestToSpill(bool unusedOnly, bool *clobbered) { *clobbered = false; for (int i = 0; i < allocCount; i++) { RiscVReg reg = allocOrder[i]; - if (ar[reg - F0].mipsReg != IRREG_INVALID && mr[ar[reg - F0].mipsReg].spillLock) + if (nr[reg - F0].mipsReg != IRREG_INVALID && mr[nr[reg - F0].mipsReg].spillLockIRIndex >= irIndex_) continue; // As it's in alloc-order, we know it's not static so we don't need to check for that. - IRUsage usage = IRNextFPRUsage(ar[reg - F0].mipsReg, info); + IRUsage usage = IRNextFPRUsage(nr[reg - F0].mipsReg, info); // Awesome, a clobbered reg. Let's use it. if (usage == IRUsage::CLOBBERED) { @@ -195,14 +171,14 @@ RiscVReg RiscVRegCacheFPU::FindBestToSpill(bool unusedOnly, bool *clobbered) { return INVALID_REG; } -void RiscVRegCacheFPU::MapInIn(IRRegIndex rd, IRRegIndex rs) { +void RiscVRegCacheFPU::MapInIn(IRReg rd, IRReg rs) { SpillLock(rd, rs); MapReg(rd); MapReg(rs); ReleaseSpillLock(rd, rs); } -void RiscVRegCacheFPU::MapDirtyIn(IRRegIndex rd, IRRegIndex rs, bool avoidLoad) { +void RiscVRegCacheFPU::MapDirtyIn(IRReg rd, IRReg rs, bool avoidLoad) { SpillLock(rd, rs); bool load = !avoidLoad || rd == rs; MapReg(rd, load ? MIPSMap::DIRTY : MIPSMap::NOINIT); @@ -210,7 +186,7 @@ void RiscVRegCacheFPU::MapDirtyIn(IRRegIndex rd, IRRegIndex rs, bool avoidLoad) ReleaseSpillLock(rd, rs); } -void RiscVRegCacheFPU::MapDirtyInIn(IRRegIndex rd, IRRegIndex rs, IRRegIndex rt, bool avoidLoad) { +void RiscVRegCacheFPU::MapDirtyInIn(IRReg rd, IRReg rs, IRReg rt, bool avoidLoad) { SpillLock(rd, rs, rt); bool load = !avoidLoad || (rd == rs || rd == rt); MapReg(rd, load ? MIPSMap::DIRTY : MIPSMap::NOINIT); @@ -219,7 +195,7 @@ void RiscVRegCacheFPU::MapDirtyInIn(IRRegIndex rd, IRRegIndex rs, IRRegIndex rt, ReleaseSpillLock(rd, rs, rt); } -RiscVReg RiscVRegCacheFPU::MapDirtyInTemp(IRRegIndex rd, IRRegIndex rs, bool avoidLoad) { +RiscVReg RiscVRegCacheFPU::MapDirtyInTemp(IRReg rd, IRReg rs, bool avoidLoad) { SpillLock(rd, rs); bool load = !avoidLoad || rd == rs; MapReg(rd, load ? MIPSMap::DIRTY : MIPSMap::NOINIT); @@ -229,7 +205,7 @@ RiscVReg RiscVRegCacheFPU::MapDirtyInTemp(IRRegIndex rd, IRRegIndex rs, bool avo return temp; } -void RiscVRegCacheFPU::Map4DirtyIn(IRRegIndex rdbase, IRRegIndex rsbase, bool avoidLoad) { +void RiscVRegCacheFPU::Map4DirtyIn(IRReg rdbase, IRReg rsbase, bool avoidLoad) { for (int i = 0; i < 4; ++i) SpillLock(rdbase + i, rsbase + i); bool load = !avoidLoad || (rdbase < rsbase + 4 && rdbase + 4 > rsbase); @@ -241,7 +217,7 @@ void RiscVRegCacheFPU::Map4DirtyIn(IRRegIndex rdbase, IRRegIndex rsbase, bool av ReleaseSpillLock(rdbase + i, rsbase + i); } -void RiscVRegCacheFPU::Map4DirtyInIn(IRRegIndex rdbase, IRRegIndex rsbase, IRRegIndex rtbase, bool avoidLoad) { +void RiscVRegCacheFPU::Map4DirtyInIn(IRReg rdbase, IRReg rsbase, IRReg rtbase, bool avoidLoad) { for (int i = 0; i < 4; ++i) SpillLock(rdbase + i, rsbase + i, rtbase + i); bool load = !avoidLoad || (rdbase < rsbase + 4 && rdbase + 4 > rsbase) || (rdbase < rtbase + 4 && rdbase + 4 > rtbase); @@ -255,7 +231,7 @@ void RiscVRegCacheFPU::Map4DirtyInIn(IRRegIndex rdbase, IRRegIndex rsbase, IRReg ReleaseSpillLock(rdbase + i, rsbase + i, rtbase + i); } -RiscVReg RiscVRegCacheFPU::Map4DirtyInTemp(IRRegIndex rdbase, IRRegIndex rsbase, bool avoidLoad) { +RiscVReg RiscVRegCacheFPU::Map4DirtyInTemp(IRReg rdbase, IRReg rsbase, bool avoidLoad) { for (int i = 0; i < 4; ++i) SpillLock(rdbase + i, rsbase + i); bool load = !avoidLoad || (rdbase < rsbase + 4 && rdbase + 4 > rsbase); @@ -272,35 +248,35 @@ RiscVReg RiscVRegCacheFPU::Map4DirtyInTemp(IRRegIndex rdbase, IRRegIndex rsbase, void RiscVRegCacheFPU::FlushRiscVReg(RiscVReg r) { _dbg_assert_(r >= F0 && r <= F31); int reg = r - F0; - if (ar[reg].mipsReg == IRREG_INVALID) { + if (nr[reg].mipsReg == IRREG_INVALID) { // Nothing to do, reg not mapped. return; } - if (ar[reg].isDirty && mr[ar[reg].mipsReg].loc == MIPSLoc::RVREG) { - emit_->FS(32, r, CTXREG, GetMipsRegOffset(ar[reg].mipsReg)); + if (nr[reg].isDirty && mr[nr[reg].mipsReg].loc == MIPSLoc::FREG) { + emit_->FS(32, r, CTXREG, GetMipsRegOffset(nr[reg].mipsReg)); } - mr[ar[reg].mipsReg].loc = MIPSLoc::MEM; - mr[ar[reg].mipsReg].reg = (int)INVALID_REG; - ar[reg].mipsReg = IRREG_INVALID; - ar[reg].isDirty = false; + mr[nr[reg].mipsReg].loc = MIPSLoc::MEM; + mr[nr[reg].mipsReg].nReg = (int)INVALID_REG; + nr[reg].mipsReg = IRREG_INVALID; + nr[reg].isDirty = false; } -void RiscVRegCacheFPU::FlushR(IRRegIndex r) { +void RiscVRegCacheFPU::FlushR(IRReg r) { _dbg_assert_(IsValidReg(r)); RiscVReg reg = RiscVRegForFlush(r); if (reg != INVALID_REG) FlushRiscVReg(reg); } -RiscVReg RiscVRegCacheFPU::RiscVRegForFlush(IRRegIndex r) { +RiscVReg RiscVRegCacheFPU::RiscVRegForFlush(IRReg r) { _dbg_assert_(IsValidReg(r)); switch (mr[r].loc) { - case MIPSLoc::RVREG: - _assert_msg_(mr[r].reg != INVALID_REG, "RiscVRegForFlush: IR %d had bad RiscVReg", r); - if (mr[r].reg == INVALID_REG) { + case MIPSLoc::FREG: + _assert_msg_(mr[r].nReg != INVALID_REG, "RiscVRegForFlush: IR %d had bad RiscVReg", r); + if (mr[r].nReg == INVALID_REG) { return INVALID_REG; } - return (RiscVReg)(F0 + mr[r].reg); + return (RiscVReg)(F0 + mr[r].nReg); case MIPSLoc::MEM: return INVALID_REG; @@ -340,37 +316,37 @@ void RiscVRegCacheFPU::FlushAll() { for (int i = 0; i < numRVRegs; i++) { int a = order[i] - F0; - int m = ar[a].mipsReg; + int m = nr[a].mipsReg; - if (ar[a].isDirty) { + if (nr[a].isDirty) { _assert_(m != MIPS_REG_INVALID); emit_->FS(32, order[i], CTXREG, GetMipsRegOffset(m)); mr[m].loc = MIPSLoc::MEM; - mr[m].reg = (int)INVALID_REG; - ar[a].mipsReg = IRREG_INVALID; - ar[a].isDirty = false; + mr[m].nReg = (int)INVALID_REG; + nr[a].mipsReg = IRREG_INVALID; + nr[a].isDirty = false; } else { if (m != IRREG_INVALID) { mr[m].loc = MIPSLoc::MEM; - mr[m].reg = (int)INVALID_REG; + mr[m].nReg = (int)INVALID_REG; } - ar[a].mipsReg = IRREG_INVALID; + nr[a].mipsReg = IRREG_INVALID; } } pendingFlush_ = false; } -void RiscVRegCacheFPU::DiscardR(IRRegIndex r) { +void RiscVRegCacheFPU::DiscardR(IRReg r) { _dbg_assert_(IsValidReg(r)); switch (mr[r].loc) { - case MIPSLoc::RVREG: - _assert_(mr[r].reg != INVALID_REG); - if (mr[r].reg != INVALID_REG) { + case MIPSLoc::FREG: + _assert_(mr[r].nReg != INVALID_REG); + if (mr[r].nReg != INVALID_REG) { // Note that we DO NOT write it back here. That's the whole point of Discard. - ar[mr[r].reg].isDirty = false; - ar[mr[r].reg].mipsReg = IRREG_INVALID; + nr[mr[r].nReg].isDirty = false; + nr[mr[r].nReg].mipsReg = IRREG_INVALID; } break; @@ -383,69 +359,57 @@ void RiscVRegCacheFPU::DiscardR(IRRegIndex r) { break; } mr[r].loc = MIPSLoc::MEM; - mr[r].reg = (int)INVALID_REG; - mr[r].spillLock = false; + mr[r].nReg = (int)INVALID_REG; + mr[r].spillLockIRIndex = -1; } -int RiscVRegCacheFPU::GetMipsRegOffset(IRRegIndex r) { +int RiscVRegCacheFPU::GetMipsRegOffset(IRReg r) { _assert_(IsValidReg(r)); // These are offsets within the MIPSState structure. // IR gives us an index that is already 32 after the state index (skipping GPRs.) return (32 + r) * 4; } -void RiscVRegCacheFPU::SpillLock(IRRegIndex r1, IRRegIndex r2, IRRegIndex r3, IRRegIndex r4) { +void RiscVRegCacheFPU::SpillLock(IRReg r1, IRReg r2, IRReg r3, IRReg r4) { _dbg_assert_(IsValidReg(r1)); _dbg_assert_(r2 == IRREG_INVALID || IsValidReg(r2)); _dbg_assert_(r3 == IRREG_INVALID || IsValidReg(r3)); _dbg_assert_(r4 == IRREG_INVALID || IsValidReg(r4)); - mr[r1].spillLock = true; + mr[r1].spillLockIRIndex = irIndex_; if (r2 != IRREG_INVALID) - mr[r2].spillLock = true; + mr[r2].spillLockIRIndex = irIndex_; if (r3 != IRREG_INVALID) - mr[r3].spillLock = true; + mr[r3].spillLockIRIndex = irIndex_; if (r4 != IRREG_INVALID) - mr[r4].spillLock = true; - pendingUnlock_ = true; + mr[r4].spillLockIRIndex = irIndex_; } -void RiscVRegCacheFPU::ReleaseSpillLocksAndDiscardTemps() { - if (!pendingUnlock_) - return; - - for (int i = 0; i < NUM_MIPSFPUREG; i++) { - mr[i].spillLock = false; - } - - pendingUnlock_ = false; -} - -void RiscVRegCacheFPU::ReleaseSpillLock(IRRegIndex r1, IRRegIndex r2, IRRegIndex r3, IRRegIndex r4) { +void RiscVRegCacheFPU::ReleaseSpillLock(IRReg r1, IRReg r2, IRReg r3, IRReg r4) { _dbg_assert_(IsValidReg(r1)); _dbg_assert_(r2 == IRREG_INVALID || IsValidReg(r2)); _dbg_assert_(r3 == IRREG_INVALID || IsValidReg(r3)); _dbg_assert_(r4 == IRREG_INVALID || IsValidReg(r4)); - mr[r1].spillLock = false; + mr[r1].spillLockIRIndex = -1; if (r2 != IRREG_INVALID) - mr[r2].spillLock = false; + mr[r2].spillLockIRIndex = -1; if (r3 != IRREG_INVALID) - mr[r3].spillLock = false; + mr[r3].spillLockIRIndex = -1; if (r4 != IRREG_INVALID) - mr[r4].spillLock = false; + mr[r4].spillLockIRIndex = -1; } -RiscVReg RiscVRegCacheFPU::R(IRRegIndex mipsReg) { +RiscVReg RiscVRegCacheFPU::R(IRReg mipsReg) { _dbg_assert_(IsValidReg(mipsReg)); - _dbg_assert_(mr[mipsReg].loc == MIPSLoc::RVREG); - if (mr[mipsReg].loc == MIPSLoc::RVREG) { - return (RiscVReg)(mr[mipsReg].reg + F0); + _dbg_assert_(mr[mipsReg].loc == MIPSLoc::FREG); + if (mr[mipsReg].loc == MIPSLoc::FREG) { + return (RiscVReg)(mr[mipsReg].nReg + F0); } else { ERROR_LOG_REPORT(JIT, "Reg %i not in riscv reg", mipsReg); return INVALID_REG; // BAAAD } } -bool RiscVRegCacheFPU::IsValidReg(IRRegIndex r) const { +bool RiscVRegCacheFPU::IsValidReg(IRReg r) const { if (r < 0 || r >= NUM_MIPSFPUREG) return false; @@ -462,3 +426,10 @@ bool RiscVRegCacheFPU::IsValidReg(IRRegIndex r) const { // Nothing else is allowed for the FPU side cache. return false; } + +void RiscVRegCacheFPU::SetupInitialRegs() { + IRNativeRegCache::SetupInitialRegs(); + + // TODO: Move to a shared cache? + mrInitial_[0].loc = MIPSLoc::MEM; +} diff --git a/Core/MIPS/RiscV/RiscVRegCacheFPU.h b/Core/MIPS/RiscV/RiscVRegCacheFPU.h index 2331a4c986..e4d5b0a489 100644 --- a/Core/MIPS/RiscV/RiscVRegCacheFPU.h +++ b/Core/MIPS/RiscV/RiscVRegCacheFPU.h @@ -21,92 +21,59 @@ #include "Core/MIPS/MIPS.h" #include "Core/MIPS/RiscV/RiscVRegCache.h" -struct FPURegStatusRiscV { - int mipsReg; // if -1, no mipsreg attached. - bool isDirty; // Should the register be written back? -}; - -struct FPURegStatusMIPS { - // Where is this MIPS register? - RiscVJitConstants::MIPSLoc loc; - // Index from F0. - int reg; - - bool spillLock; // if true, this register cannot be spilled. - // If loc == ML_MEM, it's back in its location in the CPU context struct. -}; - namespace MIPSComp { struct JitOptions; } -class RiscVRegCacheFPU { +class RiscVRegCacheFPU : public IRNativeRegCache { public: - RiscVRegCacheFPU(MIPSState *mipsState, MIPSComp::JitOptions *jo); - ~RiscVRegCacheFPU() {} + RiscVRegCacheFPU(MIPSComp::JitOptions *jo); void Init(RiscVGen::RiscVEmitter *emitter); - void Start(MIPSComp::IRBlock *irBlock); - void SetIRIndex(int index) { - irIndex_ = index; - } // Protect the RISC-V register containing a MIPS register from spilling, to ensure that // it's being kept allocated. - void SpillLock(IRRegIndex reg, IRRegIndex reg2 = IRREG_INVALID, IRRegIndex reg3 = IRREG_INVALID, IRRegIndex reg4 = IRREG_INVALID); - void ReleaseSpillLock(IRRegIndex reg, IRRegIndex reg2 = IRREG_INVALID, IRRegIndex reg3 = IRREG_INVALID, IRRegIndex reg4 = IRREG_INVALID); - void ReleaseSpillLocksAndDiscardTemps(); + void SpillLock(IRReg reg, IRReg reg2 = IRREG_INVALID, IRReg reg3 = IRREG_INVALID, IRReg reg4 = IRREG_INVALID); + void ReleaseSpillLock(IRReg reg, IRReg reg2 = IRREG_INVALID, IRReg reg3 = IRREG_INVALID, IRReg reg4 = IRREG_INVALID); // Returns a RISC-V register containing the requested MIPS register. - RiscVGen::RiscVReg MapReg(IRRegIndex reg, RiscVJitConstants::MIPSMap mapFlags = RiscVJitConstants::MIPSMap::INIT); + RiscVGen::RiscVReg MapReg(IRReg reg, RiscVJitConstants::MIPSMap mapFlags = RiscVJitConstants::MIPSMap::INIT); - bool IsMapped(IRRegIndex r); - bool IsInRAM(IRRegIndex r); + bool IsMapped(IRReg r); + bool IsInRAM(IRReg r); - void MapInIn(IRRegIndex rd, IRRegIndex rs); - void MapDirtyIn(IRRegIndex rd, IRRegIndex rs, bool avoidLoad = true); - void MapDirtyInIn(IRRegIndex rd, IRRegIndex rs, IRRegIndex rt, bool avoidLoad = true); - RiscVGen::RiscVReg MapDirtyInTemp(IRRegIndex rd, IRRegIndex rs, bool avoidLoad = true); - void Map4DirtyIn(IRRegIndex rdbase, IRRegIndex rsbase, bool avoidLoad = true); - void Map4DirtyInIn(IRRegIndex rdbase, IRRegIndex rsbase, IRRegIndex rtbase, bool avoidLoad = true); - RiscVGen::RiscVReg Map4DirtyInTemp(IRRegIndex rdbase, IRRegIndex rsbase, bool avoidLoad = true); + void MapInIn(IRReg rd, IRReg rs); + void MapDirtyIn(IRReg rd, IRReg rs, bool avoidLoad = true); + void MapDirtyInIn(IRReg rd, IRReg rs, IRReg rt, bool avoidLoad = true); + RiscVGen::RiscVReg MapDirtyInTemp(IRReg rd, IRReg rs, bool avoidLoad = true); + void Map4DirtyIn(IRReg rdbase, IRReg rsbase, bool avoidLoad = true); + void Map4DirtyInIn(IRReg rdbase, IRReg rsbase, IRReg rtbase, bool avoidLoad = true); + RiscVGen::RiscVReg Map4DirtyInTemp(IRReg rdbase, IRReg rsbase, bool avoidLoad = true); void FlushBeforeCall(); void FlushAll(); - void FlushR(IRRegIndex r); + void FlushR(IRReg r); void FlushRiscVReg(RiscVGen::RiscVReg r); - void DiscardR(IRRegIndex r); + void DiscardR(IRReg r); - RiscVGen::RiscVReg R(int preg); // Returns a cached register + RiscVGen::RiscVReg R(IRReg preg); // Returns a cached register + +protected: + void SetupInitialRegs() override; private: const RiscVGen::RiscVReg *GetMIPSAllocationOrder(int &count); RiscVGen::RiscVReg AllocateReg(); RiscVGen::RiscVReg FindBestToSpill(bool unusedOnly, bool *clobbered); - RiscVGen::RiscVReg RiscVRegForFlush(IRRegIndex r); - int GetMipsRegOffset(IRRegIndex r); + RiscVGen::RiscVReg RiscVRegForFlush(IRReg r); + int GetMipsRegOffset(IRReg r); - bool IsValidReg(IRRegIndex r) const; + bool IsValidReg(IRReg r) const; - void SetupInitialRegs(); - - MIPSState *mips_; RiscVGen::RiscVEmitter *emit_ = nullptr; - MIPSComp::JitOptions *jo_; - MIPSComp::IRBlock *irBlock_ = nullptr; - int irIndex_ = 0; enum { // On RiscV, each of the 32 registers are full 128-bit. No sharing of components! NUM_RVFPUREG = 32, - NUM_MIPSFPUREG = RiscVJitConstants::TOTAL_MAPPABLE_MIPSREGS - 32, + NUM_MIPSFPUREG = TOTAL_MAPPABLE_IRREGS - 32, }; - - FPURegStatusRiscV ar[NUM_RVFPUREG]; - FPURegStatusMIPS mr[NUM_MIPSFPUREG]; - - bool pendingFlush_ = false; - bool pendingUnlock_ = false; - bool initialReady_ = false; - FPURegStatusRiscV arInitial_[NUM_RVFPUREG]; - FPURegStatusMIPS mrInitial_[NUM_MIPSFPUREG]; }; From ebab0e15911a61b7c88997757aec87b744f88908 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 17 Aug 2023 18:50:33 -0700 Subject: [PATCH 2/8] riscv: Centralize reg allocation. --- Core/MIPS/IR/IRRegCache.cpp | 242 ++++++++++++++++++++- Core/MIPS/IR/IRRegCache.h | 33 ++- Core/MIPS/RiscV/RiscVRegCache.cpp | 300 +++++++-------------------- Core/MIPS/RiscV/RiscVRegCache.h | 14 +- Core/MIPS/RiscV/RiscVRegCacheFPU.cpp | 257 +++++++---------------- Core/MIPS/RiscV/RiscVRegCacheFPU.h | 10 +- 6 files changed, 421 insertions(+), 435 deletions(-) diff --git a/Core/MIPS/IR/IRRegCache.cpp b/Core/MIPS/IR/IRRegCache.cpp index 83604b41df..001ec77400 100644 --- a/Core/MIPS/IR/IRRegCache.cpp +++ b/Core/MIPS/IR/IRRegCache.cpp @@ -21,6 +21,8 @@ #include #include "Common/Log.h" +#include "Common/LogReporting.h" +#include "Core/MIPS/IR/IRAnalysis.h" #include "Core/MIPS/IR/IRRegCache.h" #include "Core/MIPS/IR/IRInst.h" #include "Core/MIPS/IR/IRJit.h" @@ -90,10 +92,10 @@ void IRImmRegCache::MapDirtyInIn(IRReg rd, IRReg rs, IRReg rt) { Flush(rt); } -IRNativeRegCache::IRNativeRegCache(MIPSComp::JitOptions *jo) +IRNativeRegCacheBase::IRNativeRegCacheBase(MIPSComp::JitOptions *jo) : jo_(jo) {} -void IRNativeRegCache::Start(MIPSComp::IRBlock *irBlock) { +void IRNativeRegCacheBase::Start(MIPSComp::IRBlock *irBlock) { if (!initialReady_) { SetupInitialRegs(); initialReady_ = true; @@ -109,7 +111,7 @@ void IRNativeRegCache::Start(MIPSComp::IRBlock *irBlock) { nr[statics[i].nr].mipsReg = statics[i].mr; nr[statics[i].nr].pointerified = statics[i].pointerified && jo_->enablePointerify; nr[statics[i].nr].normalized32 = statics[i].normalized32; - mr[statics[i].mr].loc = MIPSLoc::REG; + mr[statics[i].mr].loc = statics[i].loc; mr[statics[i].mr].nReg = statics[i].nr; mr[statics[i].mr].isStatic = true; // Lock it until the very end. @@ -120,10 +122,242 @@ void IRNativeRegCache::Start(MIPSComp::IRBlock *irBlock) { irIndex_ = 0; } -void IRNativeRegCache::SetupInitialRegs() { +void IRNativeRegCacheBase::SetupInitialRegs() { _assert_msg_(totalNativeRegs_ > 0, "totalNativeRegs_ was never set by backend"); // Everything else is initialized in the struct. mrInitial_[MIPS_REG_ZERO].loc = MIPSLoc::IMM; mrInitial_[MIPS_REG_ZERO].imm = 0; } + +IRNativeReg IRNativeRegCacheBase::AllocateReg(MIPSLoc type) { + _dbg_assert_(type == MIPSLoc::REG || type == MIPSLoc::FREG || type == MIPSLoc::VREG); + + IRNativeReg nreg = FindFreeReg(type); + if (nreg != -1) + return nreg; + + // Still nothing. Let's spill a reg and goto 10. + bool clobbered; + IRNativeReg bestToSpill = FindBestToSpill(type, true, &clobbered); + if (bestToSpill == -1) { + bestToSpill = FindBestToSpill(type, false, &clobbered); + } + + if (bestToSpill != -1) { + if (clobbered) { + DiscardNativeReg(bestToSpill); + } else { + FlushNativeReg(bestToSpill); + } + // Now one must be free. + return FindFreeReg(type); + } + + // Uh oh, we have all of them spilllocked.... + ERROR_LOG_REPORT(JIT, "Out of spillable registers in block PC %08x, index %d", irBlock_->GetOriginalStart(), irIndex_); + _assert_(bestToSpill != -1); + return -1; +} + +IRNativeReg IRNativeRegCacheBase::FindFreeReg(MIPSLoc type) const { + int allocCount = 0, base = 0; + const int *allocOrder = GetAllocationOrder(type, allocCount, base); + + for (int i = 0; i < allocCount; i++) { + IRNativeReg nreg = IRNativeReg(allocOrder[i] - base); + + if (nr[nreg].mipsReg == IRREG_INVALID) { + return nreg; + } + } + + return -1; +} + +IRNativeReg IRNativeRegCacheBase::FindBestToSpill(MIPSLoc type, bool unusedOnly, bool *clobbered) const { + int allocCount = 0, base = 0; + const int *allocOrder = GetAllocationOrder(type, allocCount, base); + + static const int UNUSED_LOOKAHEAD_OPS = 30; + + IRSituation info; + info.lookaheadCount = UNUSED_LOOKAHEAD_OPS; + info.currentIndex = irIndex_; + info.instructions = irBlock_->GetInstructions(); + info.numInstructions = irBlock_->GetNumInstructions(); + + auto getUsage = [type, &info](IRReg mipsReg) { + if (type == MIPSLoc::REG) + return IRNextGPRUsage(mipsReg, info); + else if (type == MIPSLoc::FREG || type == MIPSLoc::VREG) + return IRNextFPRUsage(mipsReg - 32, info); + _assert_msg_(false, "Unknown spill allocation type"); + return IRUsage::UNKNOWN; + }; + + *clobbered = false; + for (int i = 0; i < allocCount; i++) { + IRNativeReg nreg = IRNativeReg(allocOrder[i] - base); + if (nr[nreg].mipsReg != IRREG_INVALID && mr[nr[nreg].mipsReg].spillLockIRIndex >= irIndex_) + continue; + if (nr[nreg].tempLockIRIndex >= irIndex_) + continue; + + // As it's in alloc-order, we know it's not static so we don't need to check for that. + IRReg mipsReg = nr[nreg].mipsReg; + IRUsage usage = getUsage(mipsReg); + + // Awesome, a clobbered reg. Let's use it? + if (usage == IRUsage::CLOBBERED) { + // If multiple mips regs use this native reg (i.e. vector, HI/LO), check each. + // Note: mipsReg points to the lowest numbered IRReg. + bool canClobber = true; + for (IRReg m = mipsReg + 1; mr[m].nReg == nreg && m < IRREG_INVALID && canClobber; ++m) + canClobber = getUsage(mipsReg) == IRUsage::CLOBBERED; + + // Okay, if all can be clobbered, we're good to go. + if (canClobber) { + *clobbered = true; + return nreg; + } + } + + // Not awesome. A used reg. Let's try to avoid spilling. + if (!unusedOnly || usage == IRUsage::UNUSED) { + // TODO: Use age or something to choose which register to spill? + // TODO: Spill dirty regs first? or opposite? + return nreg; + } + } + + return -1; +} + +void IRNativeRegCacheBase::DiscardNativeReg(IRNativeReg nreg) { + _assert_msg_(nreg >= 0 && nreg < totalNativeRegs_, "DiscardNativeReg on invalid register %d", nreg); + if (nr[nreg].mipsReg != IRREG_INVALID) { + _assert_(nr[nreg].mipsReg != MIPS_REG_ZERO); + int8_t lanes = 0; + for (IRReg m = nr[nreg].mipsReg; mr[m].nReg == nreg && m < IRREG_INVALID; ++m) + lanes++; + + if (mr[nr[nreg].mipsReg].isStatic) { + int numStatics; + const StaticAllocation *statics = GetStaticAllocations(numStatics); + + // If it's not currently marked as in a reg, throw it away. + for (IRReg m = nr[nreg].mipsReg; m < nr[nreg].mipsReg + lanes; ++m) { + _assert_msg_(!mr[m].isStatic, "Reg in lane %d mismatched static status", m - nr[nreg].mipsReg); + for (int i = 0; i < numStatics; i++) { + if (m == statics[i].mr) + mr[m].loc = statics[i].loc; + } + } + } else { + for (IRReg m = nr[nreg].mipsReg; m < nr[nreg].mipsReg + lanes; ++m) { + mr[m].loc = MIPSLoc::MEM; + mr[m].nReg = -1; + mr[m].imm = 0; + _assert_msg_(!mr[m].isStatic, "Reg in lane %d mismatched static status", m - nr[nreg].mipsReg); + } + + nr[nreg].mipsReg = IRREG_INVALID; + } + } + + // Even for a static reg, we assume this means it's not pointerified anymore. + nr[nreg].pointerified = false; + nr[nreg].isDirty = false; + nr[nreg].normalized32 = false; +} + +void IRNativeRegCacheBase::FlushNativeReg(IRNativeReg nreg) { + _assert_msg_(nreg >= 0 && nreg < totalNativeRegs_, "FlushNativeReg on invalid register %d", nreg); + if (nr[nreg].mipsReg == IRREG_INVALID || nr[nreg].mipsReg == MIPS_REG_ZERO) { + // Nothing to do, reg not mapped or mapped to fixed zero. + _dbg_assert_(!nr[nreg].isDirty); + return; + } + _dbg_assert_(!mr[nr[nreg].mipsReg].isStatic); + if (mr[nr[nreg].mipsReg].isStatic) { + ERROR_LOG(JIT, "Cannot FlushNativeReg a statically mapped register"); + return; + } + + // Multiple mipsRegs may match this if a vector or HI/LO, etc. + bool isDirty = nr[nreg].isDirty; + int8_t lanes = 0; + for (IRReg m = nr[nreg].mipsReg; mr[m].nReg == nreg && m < IRREG_INVALID; ++m) { + _dbg_assert_(!mr[m].isStatic); + lanes++; + } + + if (isDirty) { + IRReg first = nr[nreg].mipsReg; + if (mr[first].loc == MIPSLoc::REG_AS_PTR) { + // We assume this can't be multiple lanes. Maybe some gather craziness? + _assert_(lanes == 1); + AdjustNativeRegAsPtr(nreg, false); + mr[first].loc = MIPSLoc::REG; + } + StoreNativeReg(nreg, first, lanes); + } + + for (int8_t i = 0; i < lanes; ++i) { + auto &mreg = mr[nr[nreg].mipsReg + i]; + mreg.nReg = -1; + // Note that it loses its imm status, because imms are always dirty. + mreg.loc = MIPSLoc::MEM; + mreg.imm = 0; + } + + nr[nreg].mipsReg = IRREG_INVALID; + nr[nreg].isDirty = false; + nr[nreg].pointerified = false; + nr[nreg].normalized32 = false; +} + +void IRNativeRegCacheBase::AdjustNativeRegAsPtr(IRNativeReg nreg, bool state) { + // This isn't necessary to implement if REG_AS_PTR is unsupported entirely. + _assert_msg_(false, "AdjustNativeRegAsPtr unimplemented"); +} + +bool IRNativeRegCacheBase::IsValidGPR(IRReg r) const { + // See MIPSState for these offsets. + + // Don't allow FPU regs, VFPU regs, or VFPU temps here. + if (r >= 32 && IsValidFPR(r - 32)) + return false; + // Don't allow nextPC, etc. since it's probably a mistake. + if (r > IRREG_FPCOND && r != IRREG_LLBIT) + return false; + // Don't allow PC either. + if (r == 241) + return false; + + return true; +} + +bool IRNativeRegCacheBase::IsValidGPRNoZero(IRReg r) const { + return IsValidGPR(r) && r != MIPS_REG_ZERO; +} + +bool IRNativeRegCacheBase::IsValidFPR(IRReg r) const { + // FPR parameters are off by 32 within the MIPSState object. + if (r >= TOTAL_MAPPABLE_IRREGS - 32) + return false; + + // See MIPSState for these offsets. + int index = r + 32; + + // Allow FPU or VFPU regs here. + if (index >= 32 && index < 32 + 32 + 128) + return true; + // Also allow VFPU temps. + if (index >= 224 && index < 224 + 16) + return true; + + // Nothing else is allowed for the FPU side. + return false; +} diff --git a/Core/MIPS/IR/IRRegCache.h b/Core/MIPS/IR/IRRegCache.h index dd3d3ffbec..82d79f418e 100644 --- a/Core/MIPS/IR/IRRegCache.h +++ b/Core/MIPS/IR/IRRegCache.h @@ -30,6 +30,8 @@ constexpr int TOTAL_MAPPABLE_IRREGS = 256; // Arbitrary - increase if your backend has more. constexpr int TOTAL_POSSIBLE_NATIVEREGS = 128; +typedef int8_t IRNativeReg; + constexpr IRReg IRREG_INVALID = 255; class IRWriter; @@ -75,7 +77,7 @@ private: IRWriter *ir_; }; -class IRNativeRegCache { +class IRNativeRegCacheBase { protected: enum class MIPSLoc { // Known immediate value (only in regcache.) @@ -95,10 +97,10 @@ protected: }; struct RegStatusMIPS { - // Where is this IR/MIPS register? + // Where is this IR/MIPS register? Note: base reg if vector. MIPSLoc loc = MIPSLoc::MEM; // If in a register, what index (into nr array)? - int8_t nReg = -1; + IRNativeReg nReg = -1; // If a known immediate value, what value? uint64_t imm = 0; // Locked from spilling (i.e. used by current instruction) as of what IR instruction? @@ -125,7 +127,9 @@ protected: struct StaticAllocation { IRReg mr; - int8_t nr; + IRNativeReg nr; + // Register type. + MIPSLoc loc; // Whether the reg should be marked pointerified by default. bool pointerified = false; // Whether the reg should be considered always normalized at the start of a block. @@ -133,8 +137,8 @@ protected: }; public: - IRNativeRegCache(MIPSComp::JitOptions *jo); - virtual ~IRNativeRegCache() {} + IRNativeRegCacheBase(MIPSComp::JitOptions *jo); + virtual ~IRNativeRegCacheBase() {} virtual void Start(MIPSComp::IRBlock *irBlock); void SetIRIndex(int index) { @@ -143,13 +147,26 @@ public: protected: virtual void SetupInitialRegs(); - virtual const StaticAllocation *GetStaticAllocations(int &count) { + virtual const int *GetAllocationOrder(MIPSLoc type, int &count, int &base) const = 0; + virtual const StaticAllocation *GetStaticAllocations(int &count) const { count = 0; return nullptr; } + IRNativeReg AllocateReg(MIPSLoc type); + IRNativeReg FindFreeReg(MIPSLoc type) const; + IRNativeReg FindBestToSpill(MIPSLoc type, bool unusedOnly, bool *clobbered) const; + virtual void DiscardNativeReg(IRNativeReg nreg); + virtual void FlushNativeReg(IRNativeReg nreg); + virtual void AdjustNativeRegAsPtr(IRNativeReg nreg, bool state); + virtual void StoreNativeReg(IRNativeReg nreg, IRReg first, int lanes) = 0; + + bool IsValidGPR(IRReg r) const; + bool IsValidGPRNoZero(IRReg r) const; + bool IsValidFPR(IRReg r) const; + MIPSComp::JitOptions *jo_; - MIPSComp::IRBlock *irBlock_ = nullptr; + const MIPSComp::IRBlock *irBlock_ = nullptr; int irIndex_ = 0; int totalNativeRegs_ = 0; diff --git a/Core/MIPS/RiscV/RiscVRegCache.cpp b/Core/MIPS/RiscV/RiscVRegCache.cpp index a5e532e433..913fdb8969 100644 --- a/Core/MIPS/RiscV/RiscVRegCache.cpp +++ b/Core/MIPS/RiscV/RiscVRegCache.cpp @@ -30,7 +30,7 @@ using namespace RiscVGen; using namespace RiscVJitConstants; RiscVRegCache::RiscVRegCache(MIPSComp::JitOptions *jo) - : IRNativeRegCache(jo) { + : IRNativeRegCacheBase(jo) { // TODO: Move to using for FPRs and VPRs too? totalNativeRegs_ = NUM_RVREG; } @@ -40,7 +40,7 @@ void RiscVRegCache::Init(RiscVEmitter *emitter) { } void RiscVRegCache::SetupInitialRegs() { - IRNativeRegCache::SetupInitialRegs(); + IRNativeRegCacheBase::SetupInitialRegs(); // Treat R_ZERO a bit specially, but it's basically static alloc too. nrInitial_[R_ZERO].mipsReg = MIPS_REG_ZERO; @@ -53,16 +53,18 @@ void RiscVRegCache::SetupInitialRegs() { mrInitial_[MIPS_REG_ZERO].isStatic = true; } -const RiscVReg *RiscVRegCache::GetMIPSAllocationOrder(int &count) { +const int *RiscVRegCache::GetAllocationOrder(MIPSLoc type, int &count, int &base) const { + _assert_(type == MIPSLoc::REG); // X8 and X9 are the most ideal for static alloc because they can be used with compression. // Otherwise we stick to saved regs - might not be necessary. - static const RiscVReg allocationOrder[] = { + static const int allocationOrder[] = { X8, X9, X12, X13, X14, X15, X5, X6, X7, X16, X17, X18, X19, X20, X21, X22, X23, X28, X29, X30, X31, }; - static const RiscVReg allocationOrderStaticAlloc[] = { + static const int allocationOrderStaticAlloc[] = { X12, X13, X14, X15, X5, X6, X7, X16, X17, X21, X22, X23, X28, X29, X30, X31, }; + base = X0; if (jo_->useStaticAlloc) { count = ARRAY_SIZE(allocationOrderStaticAlloc); return allocationOrderStaticAlloc; @@ -72,20 +74,20 @@ const RiscVReg *RiscVRegCache::GetMIPSAllocationOrder(int &count) { } } -const RiscVRegCache::StaticAllocation *RiscVRegCache::GetStaticAllocations(int &count) { +const RiscVRegCache::StaticAllocation *RiscVRegCache::GetStaticAllocations(int &count) const { static const StaticAllocation allocs[] = { - { MIPS_REG_SP, X8, true }, - { MIPS_REG_V0, X9 }, - { MIPS_REG_V1, X18 }, - { MIPS_REG_A0, X19 }, - { MIPS_REG_RA, X20 }, + { MIPS_REG_SP, X8, MIPSLoc::REG, true }, + { MIPS_REG_V0, X9, MIPSLoc::REG }, + { MIPS_REG_V1, X18, MIPSLoc::REG }, + { MIPS_REG_A0, X19, MIPSLoc::REG }, + { MIPS_REG_RA, X20, MIPSLoc::REG }, }; if (jo_->useStaticAlloc) { count = ARRAY_SIZE(allocs); return allocs; } - return IRNativeRegCache::GetStaticAllocations(count); + return IRNativeRegCacheBase::GetStaticAllocations(count); } void RiscVRegCache::EmitLoadStaticRegisters() { @@ -115,28 +117,28 @@ void RiscVRegCache::EmitSaveStaticRegisters() { void RiscVRegCache::FlushBeforeCall() { // These registers are not preserved by function calls. for (int i = 5; i <= 7; ++i) { - FlushRiscVReg(RiscVReg(X0 + i)); + FlushNativeReg(i); } for (int i = 10; i <= 17; ++i) { - FlushRiscVReg(RiscVReg(X0 + i)); + FlushNativeReg(i); } for (int i = 28; i <= 31; ++i) { - FlushRiscVReg(RiscVReg(X0 + i)); + FlushNativeReg(i); } } bool RiscVRegCache::IsInRAM(IRReg reg) { - _dbg_assert_(IsValidReg(reg)); + _dbg_assert_(IsValidGPR(reg)); return mr[reg].loc == MIPSLoc::MEM; } bool RiscVRegCache::IsMapped(IRReg mipsReg) { - _dbg_assert_(IsValidReg(mipsReg)); + _dbg_assert_(IsValidGPR(mipsReg)); return mr[mipsReg].loc == MIPSLoc::REG || mr[mipsReg].loc == MIPSLoc::REG_IMM; } bool RiscVRegCache::IsMappedAsPointer(IRReg mipsReg) { - _dbg_assert_(IsValidReg(mipsReg)); + _dbg_assert_(IsValidGPR(mipsReg)); if (mr[mipsReg].loc == MIPSLoc::REG) { return nr[mr[mipsReg].nReg].pointerified; } else if (mr[mipsReg].loc == MIPSLoc::REG_IMM) { @@ -157,7 +159,7 @@ bool RiscVRegCache::IsMappedAsStaticPointer(IRReg reg) { } bool RiscVRegCache::IsNormalized32(IRReg mipsReg) { - _dbg_assert_(IsValidReg(mipsReg)); + _dbg_assert_(IsValidGPR(mipsReg)); if (XLEN == 32) return true; if (mr[mipsReg].loc == MIPSLoc::REG || mr[mipsReg].loc == MIPSLoc::REG_IMM) { @@ -196,7 +198,7 @@ void RiscVRegCache::MarkPtrDirty(RiscVReg reg) { } RiscVGen::RiscVReg RiscVRegCache::Normalize32(IRReg mipsReg, RiscVGen::RiscVReg destReg) { - _dbg_assert_(IsValidReg(mipsReg)); + _dbg_assert_(IsValidGPR(mipsReg)); _dbg_assert_(destReg == INVALID_REG || (destReg > X0 && destReg <= X31)); RiscVReg reg = (RiscVReg)mr[mipsReg].nReg; @@ -257,7 +259,7 @@ void RiscVRegCache::SetRegImm(RiscVReg reg, u64 imm) { void RiscVRegCache::MapRegTo(RiscVReg reg, IRReg mipsReg, MIPSMap mapFlags) { _dbg_assert_(reg > X0 && reg <= X31); - _dbg_assert_(IsValidReg(mipsReg)); + _dbg_assert_(IsValidGPR(mipsReg)); _dbg_assert_(!mr[mipsReg].isStatic); if (mr[mipsReg].isStatic) { ERROR_LOG(JIT, "Cannot MapRegTo static register %d", mipsReg); @@ -316,88 +318,8 @@ void RiscVRegCache::MapRegTo(RiscVReg reg, IRReg mipsReg, MIPSMap mapFlags) { mr[mipsReg].nReg = reg; } -RiscVReg RiscVRegCache::AllocateReg() { - int allocCount; - const RiscVReg *allocOrder = GetMIPSAllocationOrder(allocCount); - -allocate: - for (int i = 0; i < allocCount; i++) { - RiscVReg reg = allocOrder[i]; - - if (nr[reg].mipsReg == IRREG_INVALID && nr[reg].tempLockIRIndex < irIndex_) { - return reg; - } - } - - // Still nothing. Let's spill a reg and goto 10. - bool clobbered; - RiscVReg bestToSpill = FindBestToSpill(true, &clobbered); - if (bestToSpill == INVALID_REG) { - bestToSpill = FindBestToSpill(false, &clobbered); - } - - if (bestToSpill != INVALID_REG) { - if (clobbered) { - DiscardR(nr[bestToSpill].mipsReg); - } else { - FlushRiscVReg(bestToSpill); - } - // Now one must be free. - goto allocate; - } - - // Uh oh, we have all of them spilllocked.... - ERROR_LOG_REPORT(JIT, "Out of spillable registers in block PC %08x, index %d", irBlock_->GetOriginalStart(), irIndex_); - _assert_(bestToSpill != INVALID_REG); - return INVALID_REG; -} - -RiscVReg RiscVRegCache::FindBestToSpill(bool unusedOnly, bool *clobbered) { - int allocCount; - const RiscVReg *allocOrder = GetMIPSAllocationOrder(allocCount); - - static const int UNUSED_LOOKAHEAD_OPS = 30; - - IRSituation info; - info.lookaheadCount = UNUSED_LOOKAHEAD_OPS; - info.currentIndex = irIndex_; - info.instructions = irBlock_->GetInstructions(); - info.numInstructions = irBlock_->GetNumInstructions(); - - *clobbered = false; - for (int i = 0; i < allocCount; i++) { - RiscVReg reg = allocOrder[i]; - if (nr[reg].mipsReg != IRREG_INVALID && mr[nr[reg].mipsReg].spillLockIRIndex >= irIndex_) - continue; - if (nr[reg].tempLockIRIndex >= irIndex_) - continue; - - // As it's in alloc-order, we know it's not static so we don't need to check for that. - IRUsage usage = IRNextGPRUsage(nr[reg].mipsReg, info); - - // Awesome, a clobbered reg. Let's use it. - if (usage == IRUsage::CLOBBERED) { - // TODO: Check HI/LO clobber together if we combine. - bool canClobber = true; - if (canClobber) { - *clobbered = true; - return reg; - } - } - - // Not awesome. A used reg. Let's try to avoid spilling. - if (!unusedOnly || usage == IRUsage::UNUSED) { - // TODO: Use age or something to choose which register to spill? - // TODO: Spill dirty regs first? or opposite? - return reg; - } - } - - return INVALID_REG; -} - RiscVReg RiscVRegCache::TryMapTempImm(IRReg r) { - _dbg_assert_(IsValidReg(r)); + _dbg_assert_(IsValidGPR(r)); // If already mapped, no need for a temporary. if (IsMapped(r)) { return R(r); @@ -421,7 +343,7 @@ RiscVReg RiscVRegCache::TryMapTempImm(IRReg r) { } RiscVReg RiscVRegCache::GetAndLockTempR() { - RiscVReg reg = AllocateReg(); + RiscVReg reg = (RiscVReg)AllocateReg(MIPSLoc::REG); if (reg != INVALID_REG) { nr[reg].tempLockIRIndex = irIndex_; } @@ -429,7 +351,7 @@ RiscVReg RiscVRegCache::GetAndLockTempR() { } RiscVReg RiscVRegCache::MapReg(IRReg mipsReg, MIPSMap mapFlags) { - _dbg_assert_(IsValidReg(mipsReg)); + _dbg_assert_(IsValidGPR(mipsReg)); // TODO: Optimization to force HI/LO to be combined? @@ -521,7 +443,7 @@ RiscVReg RiscVRegCache::MapReg(IRReg mipsReg, MIPSMap mapFlags) { } // Okay, not mapped, so we need to allocate an RV register. - RiscVReg reg = AllocateReg(); + RiscVReg reg = (RiscVReg)AllocateReg(MIPSLoc::REG); if (reg != INVALID_REG) { // Grab it, and load the value into it (if requested). MapRegTo(reg, mipsReg, mapFlags); @@ -531,7 +453,7 @@ RiscVReg RiscVRegCache::MapReg(IRReg mipsReg, MIPSMap mapFlags) { } RiscVReg RiscVRegCache::MapRegAsPointer(IRReg reg) { - _dbg_assert_(IsValidRegNoZero(reg)); + _dbg_assert_(IsValidGPRNoZero(reg)); // Already mapped. if (mr[reg].loc == MIPSLoc::REG_AS_PTR) { @@ -639,83 +561,38 @@ void RiscVRegCache::MapDirtyDirtyInIn(IRReg rd1, IRReg rd2, IRReg rs, IRReg rt, ReleaseSpillLock(rd1, rd2, rs, rt); } -void RiscVRegCache::FlushRiscVReg(RiscVReg r) { - _dbg_assert_(r > X0 && r <= X31); - _dbg_assert_(nr[r].mipsReg != MIPS_REG_ZERO); - if (r == INVALID_REG) { - ERROR_LOG(JIT, "FlushRiscVReg called on invalid register %d", r); - return; - } - if (nr[r].mipsReg == IRREG_INVALID) { - // Nothing to do, reg not mapped. - _dbg_assert_(!nr[r].isDirty); - return; - } - _dbg_assert_(!mr[nr[r].mipsReg].isStatic); - if (mr[nr[r].mipsReg].isStatic) { - ERROR_LOG(JIT, "Cannot FlushRiscVReg a statically mapped register"); - return; - } - auto &mreg = mr[nr[r].mipsReg]; - if (mreg.loc == MIPSLoc::REG_IMM || nr[r].mipsReg == MIPS_REG_ZERO) { - // We know its immediate value, no need to STR now. - mreg.loc = MIPSLoc::IMM; - mreg.nReg = (int)INVALID_REG; +void RiscVRegCache::AdjustNativeRegAsPtr(IRNativeReg nreg, bool state) { + RiscVReg r = (RiscVReg)(X0 + nreg); + if (state) { + AddMemBase(r); } else { - if (mreg.loc == MIPSLoc::IMM || nr[r].isDirty) { - if (mreg.loc == MIPSLoc::REG_AS_PTR) { - // Unpointerify, in case dirty. #ifdef MASKED_PSP_MEMORY - _dbg_assert_(!nr[r].isDirty); + _dbg_assert_(!nr[nreg].isDirty); #endif - emit_->SUB(r, r, MEMBASEREG); - mreg.loc = MIPSLoc::REG; - nr[r].normalized32 = false; - } - RiscVReg storeReg = RiscVRegForFlush(nr[r].mipsReg); - if (storeReg != INVALID_REG) - emit_->SW(storeReg, CTXREG, GetMipsRegOffset(nr[r].mipsReg)); - } - mreg.loc = MIPSLoc::MEM; - mreg.nReg = (int)INVALID_REG; - mreg.imm = -1; + emit_->SUB(r, r, MEMBASEREG); + nr[nreg].normalized32 = false; } - nr[r].isDirty = false; - nr[r].mipsReg = IRREG_INVALID; - nr[r].pointerified = false; +} + +void RiscVRegCache::StoreNativeReg(IRNativeReg nreg, IRReg first, int lanes) { + RiscVReg r = (RiscVReg)(X0 + nreg); + _dbg_assert_(r > X0 && r <= X31); + _dbg_assert_(first != MIPS_REG_ZERO); + // Multilane not yet supported. + _assert_(lanes == 1); + _assert_(mr[first].loc == MIPSLoc::REG || mr[first].loc == MIPSLoc::REG_IMM); + emit_->SW(r, CTXREG, GetMipsRegOffset(first)); } void RiscVRegCache::DiscardR(IRReg mipsReg) { - _dbg_assert_(IsValidRegNoZero(mipsReg)); + _dbg_assert_(IsValidGPRNoZero(mipsReg)); if (mr[mipsReg].isStatic) { - // Simply do nothing unless it's an IMM/RVREG_IMM/RVREG_AS_PTR, in case we just switch it over to RVREG, losing the value. - RiscVReg riscvReg = (RiscVReg)mr[mipsReg].nReg; - _dbg_assert_(riscvReg != INVALID_REG); - if (mipsReg == MIPS_REG_ZERO) { - // Shouldn't happen, but in case it does. - mr[mipsReg].loc = MIPSLoc::REG_IMM; - mr[mipsReg].nReg = R_ZERO; - mr[mipsReg].imm = 0; - } else if (mr[mipsReg].loc == MIPSLoc::REG_IMM || mr[mipsReg].loc == MIPSLoc::IMM || mr[mipsReg].loc == MIPSLoc::REG_AS_PTR) { - // Ignore the imm value, restore sanity - mr[mipsReg].loc = MIPSLoc::REG; - nr[riscvReg].pointerified = false; - nr[riscvReg].isDirty = false; - nr[riscvReg].normalized32 = false; - } + DiscardNativeReg(mr[mipsReg].nReg); return; } const MIPSLoc prevLoc = mr[mipsReg].loc; if (prevLoc == MIPSLoc::REG || prevLoc == MIPSLoc::REG_IMM || prevLoc == MIPSLoc::REG_AS_PTR) { - RiscVReg riscvReg = (RiscVReg)mr[mipsReg].nReg; - _dbg_assert_(riscvReg != INVALID_REG); - nr[riscvReg].mipsReg = IRREG_INVALID; - nr[riscvReg].pointerified = false; - nr[riscvReg].isDirty = false; - nr[riscvReg].normalized32 = false; - mr[mipsReg].nReg = (int)INVALID_REG; - mr[mipsReg].loc = MIPSLoc::MEM; - mr[mipsReg].imm = -1; + DiscardNativeReg(mr[mipsReg].nReg); } if (prevLoc == MIPSLoc::IMM && mipsReg != MIPS_REG_ZERO) { mr[mipsReg].loc = MIPSLoc::MEM; @@ -724,7 +601,7 @@ void RiscVRegCache::DiscardR(IRReg mipsReg) { } RiscVReg RiscVRegCache::RiscVRegForFlush(IRReg r) { - _dbg_assert_(IsValidReg(r)); + _dbg_assert_(IsValidGPR(r)); if (mr[r].isStatic) return INVALID_REG; // No flushing needed @@ -772,7 +649,7 @@ RiscVReg RiscVRegCache::RiscVRegForFlush(IRReg r) { } void RiscVRegCache::FlushR(IRReg r) { - _dbg_assert_(IsValidRegNoZero(r)); + _dbg_assert_(IsValidGPRNoZero(r)); if (mr[r].isStatic) { ERROR_LOG(JIT, "Cannot flush static reg %d", r); return; @@ -796,10 +673,7 @@ void RiscVRegCache::FlushR(IRReg r) { case MIPSLoc::REG: case MIPSLoc::REG_IMM: if (nr[mr[r].nReg].isDirty) { - RiscVReg storeReg = RiscVRegForFlush(r); - if (storeReg != INVALID_REG) { - emit_->SW(storeReg, CTXREG, GetMipsRegOffset(r)); - } + StoreNativeReg(mr[r].nReg, r, 1); nr[mr[r].nReg].isDirty = false; } nr[mr[r].nReg].mipsReg = IRREG_INVALID; @@ -808,17 +682,10 @@ void RiscVRegCache::FlushR(IRReg r) { case MIPSLoc::REG_AS_PTR: if (nr[mr[r].nReg].isDirty) { -#ifdef MASKED_PSP_MEMORY - // This is kinda bad, because we've cleared bits in it. - _dbg_assert_(!nr[mr[r].nReg].isDirty); -#endif - emit_->SUB((RiscVReg)mr[r].nReg, (RiscVReg)mr[r].nReg, MEMBASEREG); - // We set this so RiscVRegForFlush knows it's no longer a pointer. + AdjustNativeRegAsPtr(mr[r].nReg, false); + // We set this so StoreNativeReg knows it's no longer a pointer. mr[r].loc = MIPSLoc::REG; - RiscVReg storeReg = RiscVRegForFlush(r); - if (storeReg != INVALID_REG) { - emit_->SW(storeReg, CTXREG, GetMipsRegOffset(r)); - } + StoreNativeReg(mr[r].nReg, r, 1); nr[mr[r].nReg].isDirty = false; } nr[mr[r].nReg].mipsReg = IRREG_INVALID; @@ -845,11 +712,10 @@ void RiscVRegCache::FlushR(IRReg r) { void RiscVRegCache::FlushAll() { // Note: make sure not to change the registers when flushing: - // Branching code expects the armreg to retain its value. + // Branching code expects the native reg to retain its value. // TODO: HI/LO optimization? - // Final pass to grab any that were left behind. for (int i = 1; i < TOTAL_MAPPABLE_IRREGS; i++) { IRReg mipsReg = IRReg(i); if (mr[i].isStatic) { @@ -878,7 +744,7 @@ void RiscVRegCache::FlushAll() { ERROR_LOG(JIT, "RV reg of static %i is invalid", i); continue; } - } else if (IsValidRegNoZero(mipsReg)) { + } else if (IsValidGPRNoZero(mipsReg)) { FlushR(mipsReg); } } @@ -907,7 +773,7 @@ void RiscVRegCache::FlushAll() { } void RiscVRegCache::SetImm(IRReg r, u64 immVal) { - _dbg_assert_(IsValidReg(r)); + _dbg_assert_(IsValidGPR(r)); if (r == MIPS_REG_ZERO && immVal != 0) { ERROR_LOG_REPORT(JIT, "Trying to set immediate %08x to r0", (u32)immVal); return; @@ -943,7 +809,7 @@ void RiscVRegCache::SetImm(IRReg r, u64 immVal) { } bool RiscVRegCache::IsImm(IRReg r) const { - _dbg_assert_(IsValidReg(r)); + _dbg_assert_(IsValidGPR(r)); if (r == MIPS_REG_ZERO) return true; else @@ -951,7 +817,7 @@ bool RiscVRegCache::IsImm(IRReg r) const { } u64 RiscVRegCache::GetImm(IRReg r) const { - _dbg_assert_(IsValidReg(r)); + _dbg_assert_(IsValidGPR(r)); if (r == MIPS_REG_ZERO) return 0; if (mr[r].loc != MIPSLoc::IMM && mr[r].loc != MIPSLoc::REG_IMM) { @@ -961,41 +827,15 @@ u64 RiscVRegCache::GetImm(IRReg r) const { } int RiscVRegCache::GetMipsRegOffset(IRReg r) { - _dbg_assert_(IsValidReg(r)); + _dbg_assert_(IsValidGPR(r)); return r * 4; } -bool RiscVRegCache::IsValidReg(IRReg r) const { - if (r < 0 || r >= TOTAL_MAPPABLE_IRREGS) - return false; - - // See MIPSState for these offsets. - - // Don't allow FPU or VFPU regs here. - if (r >= 32 && r < 32 + 32 + 128) - return false; - // Also disallow VFPU temps. - if (r >= 224 && r < 224 + 16) - return false; - // Don't allow nextPC, etc. since it's probably a mistake. - if (r > IRREG_FPCOND && r != IRREG_LLBIT) - return false; - // Don't allow PC either. - if (r == 241) - return false; - - return true; -} - -bool RiscVRegCache::IsValidRegNoZero(IRReg r) const { - return IsValidReg(r) && r != MIPS_REG_ZERO; -} - void RiscVRegCache::SpillLock(IRReg r1, IRReg r2, IRReg r3, IRReg r4) { - _dbg_assert_(IsValidReg(r1)); - _dbg_assert_(r2 == IRREG_INVALID || IsValidReg(r2)); - _dbg_assert_(r3 == IRREG_INVALID || IsValidReg(r3)); - _dbg_assert_(r4 == IRREG_INVALID || IsValidReg(r4)); + _dbg_assert_(IsValidGPR(r1)); + _dbg_assert_(r2 == IRREG_INVALID || IsValidGPR(r2)); + _dbg_assert_(r3 == IRREG_INVALID || IsValidGPR(r3)); + _dbg_assert_(r4 == IRREG_INVALID || IsValidGPR(r4)); mr[r1].spillLockIRIndex = irIndex_; if (r2 != IRREG_INVALID) mr[r2].spillLockIRIndex = irIndex_; if (r3 != IRREG_INVALID) mr[r3].spillLockIRIndex = irIndex_; @@ -1003,10 +843,10 @@ void RiscVRegCache::SpillLock(IRReg r1, IRReg r2, IRReg r3, IRReg r4) { } void RiscVRegCache::ReleaseSpillLock(IRReg r1, IRReg r2, IRReg r3, IRReg r4) { - _dbg_assert_(IsValidReg(r1)); - _dbg_assert_(r2 == IRREG_INVALID || IsValidReg(r2)); - _dbg_assert_(r3 == IRREG_INVALID || IsValidReg(r3)); - _dbg_assert_(r4 == IRREG_INVALID || IsValidReg(r4)); + _dbg_assert_(IsValidGPR(r1)); + _dbg_assert_(r2 == IRREG_INVALID || IsValidGPR(r2)); + _dbg_assert_(r3 == IRREG_INVALID || IsValidGPR(r3)); + _dbg_assert_(r4 == IRREG_INVALID || IsValidGPR(r4)); if (!mr[r1].isStatic) mr[r1].spillLockIRIndex = -1; if (r2 != IRREG_INVALID && !mr[r2].isStatic) @@ -1018,7 +858,7 @@ void RiscVRegCache::ReleaseSpillLock(IRReg r1, IRReg r2, IRReg r3, IRReg r4) { } RiscVReg RiscVRegCache::R(IRReg mipsReg) { - _dbg_assert_(IsValidReg(mipsReg)); + _dbg_assert_(IsValidGPR(mipsReg)); _dbg_assert_(mr[mipsReg].loc == MIPSLoc::REG || mr[mipsReg].loc == MIPSLoc::REG_IMM); if (mr[mipsReg].loc == MIPSLoc::REG || mr[mipsReg].loc == MIPSLoc::REG_IMM) { return (RiscVReg)mr[mipsReg].nReg; @@ -1029,7 +869,7 @@ RiscVReg RiscVRegCache::R(IRReg mipsReg) { } RiscVReg RiscVRegCache::RPtr(IRReg mipsReg) { - _dbg_assert_(IsValidReg(mipsReg)); + _dbg_assert_(IsValidGPR(mipsReg)); _dbg_assert_(mr[mipsReg].loc == MIPSLoc::REG || mr[mipsReg].loc == MIPSLoc::REG_IMM || mr[mipsReg].loc == MIPSLoc::REG_AS_PTR); if (mr[mipsReg].loc == MIPSLoc::REG_AS_PTR) { return (RiscVReg)mr[mipsReg].nReg; diff --git a/Core/MIPS/RiscV/RiscVRegCache.h b/Core/MIPS/RiscV/RiscVRegCache.h index 2e84e3e6f4..8fd24bc4db 100644 --- a/Core/MIPS/RiscV/RiscVRegCache.h +++ b/Core/MIPS/RiscV/RiscVRegCache.h @@ -57,7 +57,7 @@ enum class MapType { } // namespace RiscVJitConstants -class RiscVRegCache : public IRNativeRegCache { +class RiscVRegCache : public IRNativeRegCacheBase { public: RiscVRegCache(MIPSComp::JitOptions *jo); @@ -98,7 +98,6 @@ public: void FlushBeforeCall(); void FlushAll(); void FlushR(IRReg r); - void FlushRiscVReg(RiscVGen::RiscVReg r); void DiscardR(IRReg r); RiscVGen::RiscVReg GetAndLockTempR(); @@ -112,21 +111,18 @@ public: protected: void SetupInitialRegs() override; - const StaticAllocation *GetStaticAllocations(int &count) override; + const StaticAllocation *GetStaticAllocations(int &count) const override; + const int *GetAllocationOrder(MIPSLoc type, int &count, int &base) const override; + void AdjustNativeRegAsPtr(IRNativeReg nreg, bool state) override; + void StoreNativeReg(IRNativeReg nreg, IRReg first, int lanes) override; private: - const RiscVGen::RiscVReg *GetMIPSAllocationOrder(int &count); void MapRegTo(RiscVGen::RiscVReg reg, IRReg mipsReg, RiscVJitConstants::MIPSMap mapFlags); - RiscVGen::RiscVReg AllocateReg(); - RiscVGen::RiscVReg FindBestToSpill(bool unusedOnly, bool *clobbered); RiscVGen::RiscVReg RiscVRegForFlush(IRReg r); void SetRegImm(RiscVGen::RiscVReg reg, u64 imm); void AddMemBase(RiscVGen::RiscVReg reg); int GetMipsRegOffset(IRReg r); - bool IsValidReg(IRReg r) const; - bool IsValidRegNoZero(IRReg r) const; - RiscVGen::RiscVEmitter *emit_ = nullptr; enum { diff --git a/Core/MIPS/RiscV/RiscVRegCacheFPU.cpp b/Core/MIPS/RiscV/RiscVRegCacheFPU.cpp index 63f22a7abb..c33a4f22d5 100644 --- a/Core/MIPS/RiscV/RiscVRegCacheFPU.cpp +++ b/Core/MIPS/RiscV/RiscVRegCacheFPU.cpp @@ -30,7 +30,7 @@ using namespace RiscVGen; using namespace RiscVJitConstants; RiscVRegCacheFPU::RiscVRegCacheFPU(MIPSComp::JitOptions *jo) - : IRNativeRegCache(jo) { + : IRNativeRegCacheBase(jo) { totalNativeRegs_ = NUM_RVFPUREG; } @@ -38,137 +38,65 @@ void RiscVRegCacheFPU::Init(RiscVEmitter *emitter) { emit_ = emitter; } -const RiscVReg *RiscVRegCacheFPU::GetMIPSAllocationOrder(int &count) { +const int *RiscVRegCacheFPU::GetAllocationOrder(MIPSLoc type, int &count, int &base) const { + _assert_(type == MIPSLoc::FREG); // F8 through F15 are used for compression, so they are great. // TODO: Maybe we could remove some saved regs since we rarely need that many? Or maybe worth it? - static const RiscVReg allocationOrder[] = { + static const int allocationOrder[] = { F8, F9, F10, F11, F12, F13, F14, F15, F0, F1, F2, F3, F4, F5, F6, F7, F16, F17, F18, F19, F20, F21, F22, F23, F24, F25, F26, F27, F28, F29, F30, F31, }; count = ARRAY_SIZE(allocationOrder); + base = F0; return allocationOrder; } bool RiscVRegCacheFPU::IsInRAM(IRReg reg) { - _dbg_assert_(IsValidReg(reg)); - return mr[reg].loc == MIPSLoc::MEM; + _dbg_assert_(IsValidFPR(reg)); + return mr[reg + 32].loc == MIPSLoc::MEM; } bool RiscVRegCacheFPU::IsMapped(IRReg mipsReg) { - _dbg_assert_(IsValidReg(mipsReg)); - return mr[mipsReg].loc == MIPSLoc::FREG; + _dbg_assert_(IsValidFPR(mipsReg)); + return mr[mipsReg + 32].loc == MIPSLoc::FREG; } RiscVReg RiscVRegCacheFPU::MapReg(IRReg mipsReg, MIPSMap mapFlags) { - _dbg_assert_(IsValidReg(mipsReg)); - _dbg_assert_(mr[mipsReg].loc == MIPSLoc::MEM || mr[mipsReg].loc == MIPSLoc::FREG); + _dbg_assert_(IsValidFPR(mipsReg)); + _dbg_assert_(mr[mipsReg + 32].loc == MIPSLoc::MEM || mr[mipsReg + 32].loc == MIPSLoc::FREG); pendingFlush_ = true; // Let's see if it's already mapped. If so we just need to update the dirty flag. // We don't need to check for NOINIT because we assume that anyone who maps // with that flag immediately writes a "known" value to the register. - if (mr[mipsReg].loc == MIPSLoc::FREG) { - _assert_msg_(nr[mr[mipsReg].nReg].mipsReg == mipsReg, "GPU mapping out of sync, IR=%i", mipsReg); + if (mr[mipsReg + 32].loc == MIPSLoc::FREG) { + _assert_msg_(nr[mr[mipsReg + 32].nReg].mipsReg == mipsReg + 32, "FPR mapping out of sync, IR=%i", mipsReg); if ((mapFlags & MIPSMap::DIRTY) == MIPSMap::DIRTY) { - nr[mr[mipsReg].nReg].isDirty = true; + nr[mr[mipsReg + 32].nReg].isDirty = true; } - return (RiscVReg)(mr[mipsReg].nReg + F0); + return (RiscVReg)(mr[mipsReg + 32].nReg + F0); } // Okay, not mapped, so we need to allocate an RV register. - RiscVReg reg = AllocateReg(); - if (reg != INVALID_REG) { + IRNativeReg nreg = AllocateReg(MIPSLoc::FREG); + if (nreg != -1) { // That means it's free. Grab it, and load the value into it (if requested). - nr[reg - F0].isDirty = (mapFlags & MIPSMap::DIRTY) == MIPSMap::DIRTY; + nr[nreg].isDirty = (mapFlags & MIPSMap::DIRTY) == MIPSMap::DIRTY; if ((mapFlags & MIPSMap::NOINIT) != MIPSMap::NOINIT) { - if (mr[mipsReg].loc == MIPSLoc::MEM) { - emit_->FL(32, reg, CTXREG, GetMipsRegOffset(mipsReg)); + if (mr[mipsReg + 32].loc == MIPSLoc::MEM) { + emit_->FL(32, (RiscVReg)(F0 + nreg), CTXREG, GetMipsRegOffset(mipsReg + 32)); } } - nr[reg - F0].mipsReg = mipsReg; - mr[mipsReg].loc = MIPSLoc::FREG; - mr[mipsReg].nReg = reg - F0; - return reg; + nr[nreg].mipsReg = mipsReg + 32; + mr[mipsReg + 32].loc = MIPSLoc::FREG; + mr[mipsReg + 32].nReg = nreg; + return (RiscVReg)(F0 + nreg); } - return reg; -} - -RiscVReg RiscVRegCacheFPU::AllocateReg() { - int allocCount = 0; - const RiscVReg *allocOrder = GetMIPSAllocationOrder(allocCount); - -allocate: - for (int i = 0; i < allocCount; i++) { - RiscVReg reg = allocOrder[i]; - - if (nr[reg - F0].mipsReg == IRREG_INVALID) { - return reg; - } - } - - // Still nothing. Let's spill a reg and goto 10. - bool clobbered; - RiscVReg bestToSpill = FindBestToSpill(true, &clobbered); - if (bestToSpill == INVALID_REG) { - bestToSpill = FindBestToSpill(false, &clobbered); - } - - if (bestToSpill != INVALID_REG) { - if (clobbered) { - DiscardR(nr[bestToSpill - F0].mipsReg); - } else { - FlushRiscVReg(bestToSpill); - } - // Now one must be free. - goto allocate; - } - - // Uh oh, we have all of them spilllocked.... - ERROR_LOG_REPORT(JIT, "Out of spillable registers in block PC %08x, index %d", irBlock_->GetOriginalStart(), irIndex_); - _assert_(bestToSpill != INVALID_REG); - return INVALID_REG; -} - -RiscVReg RiscVRegCacheFPU::FindBestToSpill(bool unusedOnly, bool *clobbered) { - int allocCount = 0; - const RiscVReg *allocOrder = GetMIPSAllocationOrder(allocCount); - - static const int UNUSED_LOOKAHEAD_OPS = 30; - - IRSituation info; - info.lookaheadCount = UNUSED_LOOKAHEAD_OPS; - info.currentIndex = irIndex_; - info.instructions = irBlock_->GetInstructions(); - info.numInstructions = irBlock_->GetNumInstructions(); - - *clobbered = false; - for (int i = 0; i < allocCount; i++) { - RiscVReg reg = allocOrder[i]; - if (nr[reg - F0].mipsReg != IRREG_INVALID && mr[nr[reg - F0].mipsReg].spillLockIRIndex >= irIndex_) - continue; - - // As it's in alloc-order, we know it's not static so we don't need to check for that. - IRUsage usage = IRNextFPRUsage(nr[reg - F0].mipsReg, info); - - // Awesome, a clobbered reg. Let's use it. - if (usage == IRUsage::CLOBBERED) { - *clobbered = true; - return reg; - } - - // Not awesome. A used reg. Let's try to avoid spilling. - if (!unusedOnly || usage == IRUsage::UNUSED) { - // TODO: Use age or something to choose which register to spill? - // TODO: Spill dirty regs first? or opposite? - return reg; - } - } - - return INVALID_REG; + return (RiscVReg)(F0 + nreg); } void RiscVRegCacheFPU::MapInIn(IRReg rd, IRReg rs) { @@ -200,7 +128,7 @@ RiscVReg RiscVRegCacheFPU::MapDirtyInTemp(IRReg rd, IRReg rs, bool avoidLoad) { bool load = !avoidLoad || rd == rs; MapReg(rd, load ? MIPSMap::DIRTY : MIPSMap::NOINIT); MapReg(rs); - RiscVReg temp = AllocateReg(); + RiscVReg temp = (RiscVReg)(F0 + AllocateReg(MIPSLoc::FREG)); ReleaseSpillLock(rd, rs); return temp; } @@ -239,44 +167,40 @@ RiscVReg RiscVRegCacheFPU::Map4DirtyInTemp(IRReg rdbase, IRReg rsbase, bool avoi MapReg(rdbase + i, load ? MIPSMap::DIRTY : MIPSMap::NOINIT); for (int i = 0; i < 4; ++i) MapReg(rsbase + i); - RiscVReg temp = AllocateReg(); + RiscVReg temp = (RiscVReg)(F0 + AllocateReg(MIPSLoc::FREG)); for (int i = 0; i < 4; ++i) ReleaseSpillLock(rdbase + i, rsbase + i); return temp; } -void RiscVRegCacheFPU::FlushRiscVReg(RiscVReg r) { +void RiscVRegCacheFPU::StoreNativeReg(IRNativeReg nreg, IRReg first, int lanes) { + RiscVReg r = (RiscVReg)(F0 + nreg); _dbg_assert_(r >= F0 && r <= F31); - int reg = r - F0; - if (nr[reg].mipsReg == IRREG_INVALID) { - // Nothing to do, reg not mapped. - return; + // Multilane not yet supported. + _assert_(lanes == 1); + if (mr[first].loc == MIPSLoc::FREG) { + emit_->FS(32, r, CTXREG, GetMipsRegOffset(first)); + } else { + _assert_msg_(mr[first].loc == MIPSLoc::FREG, "Cannot store this type: %d", (int)mr[first].loc); } - if (nr[reg].isDirty && mr[nr[reg].mipsReg].loc == MIPSLoc::FREG) { - emit_->FS(32, r, CTXREG, GetMipsRegOffset(nr[reg].mipsReg)); - } - mr[nr[reg].mipsReg].loc = MIPSLoc::MEM; - mr[nr[reg].mipsReg].nReg = (int)INVALID_REG; - nr[reg].mipsReg = IRREG_INVALID; - nr[reg].isDirty = false; } void RiscVRegCacheFPU::FlushR(IRReg r) { - _dbg_assert_(IsValidReg(r)); + _dbg_assert_(IsValidFPR(r)); RiscVReg reg = RiscVRegForFlush(r); if (reg != INVALID_REG) - FlushRiscVReg(reg); + FlushNativeReg((IRNativeReg)(reg - F0)); } RiscVReg RiscVRegCacheFPU::RiscVRegForFlush(IRReg r) { - _dbg_assert_(IsValidReg(r)); - switch (mr[r].loc) { + _dbg_assert_(IsValidFPR(r)); + switch (mr[r + 32].loc) { case MIPSLoc::FREG: - _assert_msg_(mr[r].nReg != INVALID_REG, "RiscVRegForFlush: IR %d had bad RiscVReg", r); - if (mr[r].nReg == INVALID_REG) { + _assert_msg_(mr[r + 32].nReg != INVALID_REG, "RiscVRegForFlush: IR %d had bad RiscVReg", r + 32); + if (mr[r + 32].nReg == INVALID_REG) { return INVALID_REG; } - return (RiscVReg)(F0 + mr[r].nReg); + return (RiscVReg)(F0 + mr[r + 32].nReg); case MIPSLoc::MEM: return INVALID_REG; @@ -295,13 +219,13 @@ void RiscVRegCacheFPU::FlushBeforeCall() { // These registers are not preserved by function calls. for (int i = 0; i <= 7; ++i) { - FlushRiscVReg(RiscVReg(F0 + i)); + FlushNativeReg(i); } for (int i = 10; i <= 17; ++i) { - FlushRiscVReg(RiscVReg(F0 + i)); + FlushNativeReg(i); } for (int i = 28; i <= 31; ++i) { - FlushRiscVReg(RiscVReg(F0 + i)); + FlushNativeReg(i); } } @@ -311,16 +235,16 @@ void RiscVRegCacheFPU::FlushAll() { return; } - int numRVRegs = 0; - const RiscVReg *order = GetMIPSAllocationOrder(numRVRegs); + int numRVRegs = 0, baseIndex = 0; + const int *order = GetAllocationOrder(MIPSLoc::FREG, numRVRegs, baseIndex); for (int i = 0; i < numRVRegs; i++) { - int a = order[i] - F0; + int a = order[i] - baseIndex; int m = nr[a].mipsReg; if (nr[a].isDirty) { _assert_(m != MIPS_REG_INVALID); - emit_->FS(32, order[i], CTXREG, GetMipsRegOffset(m)); + emit_->FS(32, (RiscVReg)(F0 + a), CTXREG, GetMipsRegOffset(m)); mr[m].loc = MIPSLoc::MEM; mr[m].nReg = (int)INVALID_REG; @@ -339,15 +263,12 @@ void RiscVRegCacheFPU::FlushAll() { } void RiscVRegCacheFPU::DiscardR(IRReg r) { - _dbg_assert_(IsValidReg(r)); - switch (mr[r].loc) { + _dbg_assert_(IsValidFPR(r)); + switch (mr[r + 32].loc) { case MIPSLoc::FREG: - _assert_(mr[r].nReg != INVALID_REG); - if (mr[r].nReg != INVALID_REG) { - // Note that we DO NOT write it back here. That's the whole point of Discard. - nr[mr[r].nReg].isDirty = false; - nr[mr[r].nReg].mipsReg = IRREG_INVALID; - } + _assert_(mr[r + 32].nReg != INVALID_REG); + // Note that we DO NOT write it back here. That's the whole point of Discard. + DiscardNativeReg(mr[r + 32].nReg); break; case MIPSLoc::MEM: @@ -358,77 +279,59 @@ void RiscVRegCacheFPU::DiscardR(IRReg r) { _assert_(false); break; } - mr[r].loc = MIPSLoc::MEM; - mr[r].nReg = (int)INVALID_REG; - mr[r].spillLockIRIndex = -1; + mr[r + 32].loc = MIPSLoc::MEM; + mr[r + 32].nReg = -1; + mr[r + 32].spillLockIRIndex = -1; } int RiscVRegCacheFPU::GetMipsRegOffset(IRReg r) { - _assert_(IsValidReg(r)); + _assert_(IsValidFPR(r - 32)); // These are offsets within the MIPSState structure. // IR gives us an index that is already 32 after the state index (skipping GPRs.) - return (32 + r) * 4; + return (r) * 4; } void RiscVRegCacheFPU::SpillLock(IRReg r1, IRReg r2, IRReg r3, IRReg r4) { - _dbg_assert_(IsValidReg(r1)); - _dbg_assert_(r2 == IRREG_INVALID || IsValidReg(r2)); - _dbg_assert_(r3 == IRREG_INVALID || IsValidReg(r3)); - _dbg_assert_(r4 == IRREG_INVALID || IsValidReg(r4)); - mr[r1].spillLockIRIndex = irIndex_; + _dbg_assert_(IsValidFPR(r1)); + _dbg_assert_(r2 == IRREG_INVALID || IsValidFPR(r2)); + _dbg_assert_(r3 == IRREG_INVALID || IsValidFPR(r3)); + _dbg_assert_(r4 == IRREG_INVALID || IsValidFPR(r4)); + mr[r1 + 32].spillLockIRIndex = irIndex_; if (r2 != IRREG_INVALID) - mr[r2].spillLockIRIndex = irIndex_; + mr[r2 + 32].spillLockIRIndex = irIndex_; if (r3 != IRREG_INVALID) - mr[r3].spillLockIRIndex = irIndex_; + mr[r3 + 32].spillLockIRIndex = irIndex_; if (r4 != IRREG_INVALID) - mr[r4].spillLockIRIndex = irIndex_; + mr[r4 + 32].spillLockIRIndex = irIndex_; } void RiscVRegCacheFPU::ReleaseSpillLock(IRReg r1, IRReg r2, IRReg r3, IRReg r4) { - _dbg_assert_(IsValidReg(r1)); - _dbg_assert_(r2 == IRREG_INVALID || IsValidReg(r2)); - _dbg_assert_(r3 == IRREG_INVALID || IsValidReg(r3)); - _dbg_assert_(r4 == IRREG_INVALID || IsValidReg(r4)); - mr[r1].spillLockIRIndex = -1; + _dbg_assert_(IsValidFPR(r1)); + _dbg_assert_(r2 == IRREG_INVALID || IsValidFPR(r2)); + _dbg_assert_(r3 == IRREG_INVALID || IsValidFPR(r3)); + _dbg_assert_(r4 == IRREG_INVALID || IsValidFPR(r4)); + mr[r1 + 32].spillLockIRIndex = -1; if (r2 != IRREG_INVALID) - mr[r2].spillLockIRIndex = -1; + mr[r2 + 32].spillLockIRIndex = -1; if (r3 != IRREG_INVALID) - mr[r3].spillLockIRIndex = -1; + mr[r3 + 32].spillLockIRIndex = -1; if (r4 != IRREG_INVALID) - mr[r4].spillLockIRIndex = -1; + mr[r4 + 32].spillLockIRIndex = -1; } RiscVReg RiscVRegCacheFPU::R(IRReg mipsReg) { - _dbg_assert_(IsValidReg(mipsReg)); - _dbg_assert_(mr[mipsReg].loc == MIPSLoc::FREG); - if (mr[mipsReg].loc == MIPSLoc::FREG) { - return (RiscVReg)(mr[mipsReg].nReg + F0); + _dbg_assert_(IsValidFPR(mipsReg)); + _dbg_assert_(mr[mipsReg + 32].loc == MIPSLoc::FREG); + if (mr[mipsReg + 32].loc == MIPSLoc::FREG) { + return (RiscVReg)(mr[mipsReg + 32].nReg + F0); } else { ERROR_LOG_REPORT(JIT, "Reg %i not in riscv reg", mipsReg); return INVALID_REG; // BAAAD } } -bool RiscVRegCacheFPU::IsValidReg(IRReg r) const { - if (r < 0 || r >= NUM_MIPSFPUREG) - return false; - - // See MIPSState for these offsets. - int index = r + 32; - - // Allow FPU or VFPU regs here. - if (index >= 32 && index < 32 + 32 + 128) - return true; - // Also allow VFPU temps. - if (index >= 224 && index < 224 + 16) - return true; - - // Nothing else is allowed for the FPU side cache. - return false; -} - void RiscVRegCacheFPU::SetupInitialRegs() { - IRNativeRegCache::SetupInitialRegs(); + IRNativeRegCacheBase::SetupInitialRegs(); // TODO: Move to a shared cache? mrInitial_[0].loc = MIPSLoc::MEM; diff --git a/Core/MIPS/RiscV/RiscVRegCacheFPU.h b/Core/MIPS/RiscV/RiscVRegCacheFPU.h index e4d5b0a489..3103101f94 100644 --- a/Core/MIPS/RiscV/RiscVRegCacheFPU.h +++ b/Core/MIPS/RiscV/RiscVRegCacheFPU.h @@ -25,7 +25,7 @@ namespace MIPSComp { struct JitOptions; } -class RiscVRegCacheFPU : public IRNativeRegCache { +class RiscVRegCacheFPU : public IRNativeRegCacheBase { public: RiscVRegCacheFPU(MIPSComp::JitOptions *jo); @@ -52,23 +52,19 @@ public: void FlushBeforeCall(); void FlushAll(); void FlushR(IRReg r); - void FlushRiscVReg(RiscVGen::RiscVReg r); void DiscardR(IRReg r); RiscVGen::RiscVReg R(IRReg preg); // Returns a cached register protected: void SetupInitialRegs() override; + const int *GetAllocationOrder(MIPSLoc type, int &count, int &base) const override; + void StoreNativeReg(IRNativeReg nreg, IRReg first, int lanes) override; private: - const RiscVGen::RiscVReg *GetMIPSAllocationOrder(int &count); - RiscVGen::RiscVReg AllocateReg(); - RiscVGen::RiscVReg FindBestToSpill(bool unusedOnly, bool *clobbered); RiscVGen::RiscVReg RiscVRegForFlush(IRReg r); int GetMipsRegOffset(IRReg r); - bool IsValidReg(IRReg r) const; - RiscVGen::RiscVEmitter *emit_ = nullptr; enum { From 4e41f83ecceb5d0e651b731aa5e5f8add3a28fb4 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 17 Aug 2023 22:37:43 -0700 Subject: [PATCH 3/8] riscv: Centralize IR reg cache metadata checks. These are all largely the same between backends. --- Core/MIPS/IR/IRRegCache.cpp | 160 ++++++++++++++++++++- Core/MIPS/IR/IRRegCache.h | 25 +++- Core/MIPS/RiscV/RiscVCompALU.cpp | 22 ++- Core/MIPS/RiscV/RiscVCompFPU.cpp | 4 +- Core/MIPS/RiscV/RiscVCompLoadStore.cpp | 25 ++-- Core/MIPS/RiscV/RiscVCompSystem.cpp | 6 +- Core/MIPS/RiscV/RiscVCompVec.cpp | 30 ++-- Core/MIPS/RiscV/RiscVJit.cpp | 4 +- Core/MIPS/RiscV/RiscVRegCache.cpp | 189 ++++--------------------- Core/MIPS/RiscV/RiscVRegCache.h | 13 -- Core/MIPS/RiscV/RiscVRegCacheFPU.cpp | 90 +++--------- Core/MIPS/RiscV/RiscVRegCacheFPU.h | 9 -- 12 files changed, 268 insertions(+), 309 deletions(-) diff --git a/Core/MIPS/IR/IRRegCache.cpp b/Core/MIPS/IR/IRRegCache.cpp index 001ec77400..90f677fc94 100644 --- a/Core/MIPS/IR/IRRegCache.cpp +++ b/Core/MIPS/IR/IRRegCache.cpp @@ -130,6 +130,158 @@ void IRNativeRegCacheBase::SetupInitialRegs() { mrInitial_[MIPS_REG_ZERO].imm = 0; } +bool IRNativeRegCacheBase::IsGPRInRAM(IRReg gpr) { + _dbg_assert_(IsValidGPR(gpr)); + return mr[gpr].loc == MIPSLoc::MEM; +} + +bool IRNativeRegCacheBase::IsFPRInRAM(IRReg fpr) { + _dbg_assert_(IsValidFPR(fpr)); + return mr[fpr + 32].loc == MIPSLoc::MEM; +} + +bool IRNativeRegCacheBase::IsGPRMapped(IRReg gpr) { + _dbg_assert_(IsValidGPR(gpr)); + return mr[gpr].loc == MIPSLoc::REG || mr[gpr].loc == MIPSLoc::REG_IMM; +} + +bool IRNativeRegCacheBase::IsFPRMapped(IRReg fpr) { + _dbg_assert_(IsValidFPR(fpr)); + return mr[fpr + 32].loc == MIPSLoc::FREG || mr[fpr + 32].loc == MIPSLoc::VREG; +} + +bool IRNativeRegCacheBase::IsGPRMappedAsPointer(IRReg gpr) { + _dbg_assert_(IsValidGPR(gpr)); + if (mr[gpr].loc == MIPSLoc::REG) { + return nr[mr[gpr].nReg].pointerified; + } else if (mr[gpr].loc == MIPSLoc::REG_IMM) { + _assert_msg_(!nr[mr[gpr].nReg].pointerified, "Really shouldn't be pointerified here"); + } else if (mr[gpr].loc == MIPSLoc::REG_AS_PTR) { + return true; + } + return false; +} + +bool IRNativeRegCacheBase::IsGPRMappedAsStaticPointer(IRReg gpr) { + if (IsGPRMappedAsPointer(gpr)) { + return mr[gpr].isStatic; + } + return false; +} + +bool IRNativeRegCacheBase::IsGPRImm(IRReg gpr) { + _dbg_assert_(IsValidGPR(gpr)); + if (gpr == MIPS_REG_ZERO) + return true; + return mr[gpr].loc == MIPSLoc::IMM || mr[gpr].loc == MIPSLoc::REG_IMM; +} + +bool IRNativeRegCacheBase::IsGPR2Imm(IRReg base) { + return IsGPRImm(base) && IsGPRImm(base + 1); +} + +uint32_t IRNativeRegCacheBase::GetGPRImm(IRReg gpr) { + _dbg_assert_(IsValidGPR(gpr)); + if (gpr == MIPS_REG_ZERO) + return 0; + if (mr[gpr].loc != MIPSLoc::IMM && mr[gpr].loc != MIPSLoc::REG_IMM) { + _assert_msg_(mr[gpr].loc == MIPSLoc::IMM || mr[gpr].loc == MIPSLoc::REG_IMM, "GPR %d not in an imm", gpr); + } + return mr[gpr].imm; +} + +uint64_t IRNativeRegCacheBase::GetGPR2Imm(IRReg base) { + return (uint64_t)GetGPRImm(base) | ((uint64_t)GetGPRImm(base + 1) << 32); +} + +void IRNativeRegCacheBase::SetGPRImm(IRReg gpr, uint32_t immVal) { + _dbg_assert_(IsValidGPR(gpr)); + if (gpr == MIPS_REG_ZERO && immVal != 0) { + ERROR_LOG_REPORT(JIT, "Trying to set immediate %08x to r0", immVal); + return; + } + + if (mr[gpr].loc == MIPSLoc::REG_IMM && mr[gpr].imm == immVal) { + // Already have that value, let's keep it in the reg. + return; + } + + if (mr[gpr].nReg != -1) { + // Zap existing value if cached in a reg. + _assert_msg_(mr[gpr].lane == -1, "Should not be a multilane reg"); + DiscardNativeReg(mr[gpr].nReg); + } + + mr[gpr].loc = MIPSLoc::IMM; + mr[gpr].imm = immVal; +} + +void IRNativeRegCacheBase::SetGPR2Imm(IRReg base, uint64_t immVal) { + _dbg_assert_(IsValidGPRNoZero(base)); + uint32_t imm0 = (uint32_t)(immVal & 0xFFFFFFFF); + uint32_t imm1 = (uint32_t)(immVal >> 32); + + if (IsGPRImm(base) && IsGPRImm(base + 1) && GetGPRImm(base) == imm0 && GetGPRImm(base + 1) == imm1) { + // Already set to this, don't bother. + return; + } + + if (mr[base].nReg != -1) { + // Zap existing value if cached in a reg. + DiscardNativeReg(mr[base].nReg); + if (mr[base + 1].nReg != -1) + DiscardNativeReg(mr[base + 1].nReg); + } + + mr[base].loc = MIPSLoc::IMM; + mr[base].imm = imm0; + mr[base + 1].loc = MIPSLoc::IMM; + mr[base + 1].imm = imm1; +} + +void IRNativeRegCacheBase::SpillLockGPR(IRReg r1, IRReg r2, IRReg r3, IRReg r4) { + _dbg_assert_(IsValidGPR(r1)); + _dbg_assert_(r2 == IRREG_INVALID || IsValidGPR(r2)); + _dbg_assert_(r3 == IRREG_INVALID || IsValidGPR(r3)); + _dbg_assert_(r4 == IRREG_INVALID || IsValidGPR(r4)); + SetSpillLockIRIndex(r1, r2, r3, r4, 0, irIndex_); +} + +void IRNativeRegCacheBase::SpillLockFPR(IRReg r1, IRReg r2, IRReg r3, IRReg r4) { + _dbg_assert_(IsValidFPR(r1)); + _dbg_assert_(r2 == IRREG_INVALID || IsValidFPR(r2)); + _dbg_assert_(r3 == IRREG_INVALID || IsValidFPR(r3)); + _dbg_assert_(r4 == IRREG_INVALID || IsValidFPR(r4)); + SetSpillLockIRIndex(r1, r2, r3, r4, 32, irIndex_); +} + +void IRNativeRegCacheBase::ReleaseSpillLockGPR(IRReg r1, IRReg r2, IRReg r3, IRReg r4) { + _dbg_assert_(IsValidGPR(r1)); + _dbg_assert_(r2 == IRREG_INVALID || IsValidGPR(r2)); + _dbg_assert_(r3 == IRREG_INVALID || IsValidGPR(r3)); + _dbg_assert_(r4 == IRREG_INVALID || IsValidGPR(r4)); + SetSpillLockIRIndex(r1, r2, r3, r4, 0, -1); +} + +void IRNativeRegCacheBase::ReleaseSpillLockFPR(IRReg r1, IRReg r2, IRReg r3, IRReg r4) { + _dbg_assert_(IsValidFPR(r1)); + _dbg_assert_(r2 == IRREG_INVALID || IsValidFPR(r2)); + _dbg_assert_(r3 == IRREG_INVALID || IsValidFPR(r3)); + _dbg_assert_(r4 == IRREG_INVALID || IsValidFPR(r4)); + SetSpillLockIRIndex(r1, r2, r3, r4, 32, -1); +} + +void IRNativeRegCacheBase::SetSpillLockIRIndex(IRReg r1, IRReg r2, IRReg r3, IRReg r4, int offset, int index) { + if (!mr[r1 + offset].isStatic) + mr[r1 + offset].spillLockIRIndex = index; + if (r2 != IRREG_INVALID && !mr[r2 + offset].isStatic) + mr[r2 + offset].spillLockIRIndex = index; + if (r3 != IRREG_INVALID && !mr[r3 + offset].isStatic) + mr[r3 + offset].spillLockIRIndex = index; + if (r4 != IRREG_INVALID && !mr[r4 + offset].isStatic) + mr[r4 + offset].spillLockIRIndex = index; +} + IRNativeReg IRNativeRegCacheBase::AllocateReg(MIPSLoc type) { _dbg_assert_(type == MIPSLoc::REG || type == MIPSLoc::FREG || type == MIPSLoc::VREG); @@ -248,7 +400,7 @@ void IRNativeRegCacheBase::DiscardNativeReg(IRNativeReg nreg) { // If it's not currently marked as in a reg, throw it away. for (IRReg m = nr[nreg].mipsReg; m < nr[nreg].mipsReg + lanes; ++m) { - _assert_msg_(!mr[m].isStatic, "Reg in lane %d mismatched static status", m - nr[nreg].mipsReg); + _assert_msg_(mr[m].isStatic, "Reg in lane %d mismatched static status", m - nr[nreg].mipsReg); for (int i = 0; i < numStatics; i++) { if (m == statics[i].mr) mr[m].loc = statics[i].loc; @@ -259,6 +411,7 @@ void IRNativeRegCacheBase::DiscardNativeReg(IRNativeReg nreg) { mr[m].loc = MIPSLoc::MEM; mr[m].nReg = -1; mr[m].imm = 0; + mr[m].lane = -1; _assert_msg_(!mr[m].isStatic, "Reg in lane %d mismatched static status", m - nr[nreg].mipsReg); } @@ -289,7 +442,9 @@ void IRNativeRegCacheBase::FlushNativeReg(IRNativeReg nreg) { bool isDirty = nr[nreg].isDirty; int8_t lanes = 0; for (IRReg m = nr[nreg].mipsReg; mr[m].nReg == nreg && m < IRREG_INVALID; ++m) { - _dbg_assert_(!mr[m].isStatic); + _assert_(!mr[m].isStatic); + // If we're flushing a native reg, better not be partially in mem or an imm. + _assert_(mr[m].loc != MIPSLoc::MEM && mr[m].loc != MIPSLoc::IMM); lanes++; } @@ -310,6 +465,7 @@ void IRNativeRegCacheBase::FlushNativeReg(IRNativeReg nreg) { // Note that it loses its imm status, because imms are always dirty. mreg.loc = MIPSLoc::MEM; mreg.imm = 0; + mreg.lane = -1; } nr[nreg].mipsReg = IRREG_INVALID; diff --git a/Core/MIPS/IR/IRRegCache.h b/Core/MIPS/IR/IRRegCache.h index 82d79f418e..0e9e41acf9 100644 --- a/Core/MIPS/IR/IRRegCache.h +++ b/Core/MIPS/IR/IRRegCache.h @@ -102,7 +102,7 @@ protected: // If in a register, what index (into nr array)? IRNativeReg nReg = -1; // If a known immediate value, what value? - uint64_t imm = 0; + uint32_t imm = 0; // Locked from spilling (i.e. used by current instruction) as of what IR instruction? int spillLockIRIndex = -1; // If in a multipart reg (vector or HI/LO), which lane? @@ -145,6 +145,27 @@ public: irIndex_ = index; } + bool IsGPRInRAM(IRReg gpr); + bool IsFPRInRAM(IRReg fpr); + bool IsGPRMapped(IRReg gpr); + bool IsFPRMapped(IRReg fpr); + bool IsGPRMappedAsPointer(IRReg gpr); + bool IsGPRMappedAsStaticPointer(IRReg gpr); + + bool IsGPRImm(IRReg gpr); + bool IsGPR2Imm(IRReg base); + uint32_t GetGPRImm(IRReg gpr); + uint64_t GetGPR2Imm(IRReg first); + void SetGPRImm(IRReg gpr, uint32_t immval); + void SetGPR2Imm(IRReg first, uint64_t immval); + + // Protect the native registers containing register froms spilling, to ensure that + // it's being kept allocated. + void SpillLockGPR(IRReg reg, IRReg reg2 = IRREG_INVALID, IRReg reg3 = IRREG_INVALID, IRReg reg4 = IRREG_INVALID); + void SpillLockFPR(IRReg reg, IRReg reg2 = IRREG_INVALID, IRReg reg3 = IRREG_INVALID, IRReg reg4 = IRREG_INVALID); + void ReleaseSpillLockGPR(IRReg reg, IRReg reg2 = IRREG_INVALID, IRReg reg3 = IRREG_INVALID, IRReg reg4 = IRREG_INVALID); + void ReleaseSpillLockFPR(IRReg reg, IRReg reg2 = IRREG_INVALID, IRReg reg3 = IRREG_INVALID, IRReg reg4 = IRREG_INVALID); + protected: virtual void SetupInitialRegs(); virtual const int *GetAllocationOrder(MIPSLoc type, int &count, int &base) const = 0; @@ -161,6 +182,8 @@ protected: virtual void AdjustNativeRegAsPtr(IRNativeReg nreg, bool state); virtual void StoreNativeReg(IRNativeReg nreg, IRReg first, int lanes) = 0; + void SetSpillLockIRIndex(IRReg reg, IRReg reg2, IRReg reg3, IRReg reg4, int offset, int index); + bool IsValidGPR(IRReg r) const; bool IsValidGPRNoZero(IRReg r) const; bool IsValidFPR(IRReg r) const; diff --git a/Core/MIPS/RiscV/RiscVCompALU.cpp b/Core/MIPS/RiscV/RiscVCompALU.cpp index c1939e9867..e00beab181 100644 --- a/Core/MIPS/RiscV/RiscVCompALU.cpp +++ b/Core/MIPS/RiscV/RiscVCompALU.cpp @@ -65,7 +65,7 @@ void RiscVJitBackend::CompIR_Arith(IRInst inst) { case IROp::AddConst: if ((int32_t)inst.constant >= -2048 && (int32_t)inst.constant <= 2047) { // Typical of stack pointer updates. - if (gpr.IsMappedAsPointer(inst.src1) && inst.dest == inst.src1 && allowPtrMath) { + if (gpr.IsGPRMappedAsPointer(inst.src1) && inst.dest == inst.src1 && allowPtrMath) { gpr.MarkPtrDirty(gpr.RPtr(inst.dest)); ADDI(gpr.RPtr(inst.dest), gpr.RPtr(inst.dest), inst.constant); } else { @@ -127,7 +127,7 @@ void RiscVJitBackend::CompIR_Logic(IRInst inst) { case IROp::Xor: if (inst.src1 == inst.src2) { - gpr.SetImm(inst.dest, 0); + gpr.SetGPRImm(inst.dest, 0); } else { gpr.MapDirtyInIn(inst.dest, inst.src1, inst.src2); XOR(gpr.R(inst.dest), gpr.R(inst.src1), gpr.R(inst.src2)); @@ -334,7 +334,7 @@ void RiscVJitBackend::CompIR_Shift(IRInst inst) { case IROp::ShlImm: // Shouldn't happen, but let's be safe of any passes that modify the ops. if (inst.src2 >= 32) { - gpr.SetImm(inst.dest, 0); + gpr.SetGPRImm(inst.dest, 0); } else if (inst.src2 == 0) { if (inst.dest != inst.src1) { gpr.MapDirtyIn(inst.dest, inst.src1); @@ -350,7 +350,7 @@ void RiscVJitBackend::CompIR_Shift(IRInst inst) { case IROp::ShrImm: // Shouldn't happen, but let's be safe of any passes that modify the ops. if (inst.src2 >= 32) { - gpr.SetImm(inst.dest, 0); + gpr.SetGPRImm(inst.dest, 0); } else if (inst.src2 == 0) { if (inst.dest != inst.src1) { gpr.MapDirtyIn(inst.dest, inst.src1); @@ -408,12 +408,11 @@ void RiscVJitBackend::CompIR_Compare(IRInst inst) { RiscVReg rhs = INVALID_REG; switch (inst.op) { case IROp::Slt: - gpr.SpillLock(inst.dest, inst.src1, inst.src2); + gpr.SpillLockGPR(inst.dest, inst.src1, inst.src2); gpr.MapReg(inst.src1); gpr.MapReg(inst.src2); NormalizeSrc12(inst, &lhs, &rhs, SCRATCH1, SCRATCH2, true); gpr.MapReg(inst.dest, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); - gpr.ReleaseSpillLock(inst.dest, inst.src1, inst.src2); SLT(gpr.R(inst.dest), lhs, rhs); break; @@ -424,11 +423,10 @@ void RiscVJitBackend::CompIR_Compare(IRInst inst) { gpr.MapDirtyIn(inst.dest, inst.src1, MapType::AVOID_LOAD_MARK_NORM32); SRLIW(gpr.R(inst.dest), gpr.R(inst.src1), 31); } else { - gpr.SpillLock(inst.dest, inst.src1); + gpr.SpillLockGPR(inst.dest, inst.src1); gpr.MapReg(inst.src1); NormalizeSrc1(inst, &lhs, SCRATCH1, false); gpr.MapReg(inst.dest, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); - gpr.ReleaseSpillLock(inst.dest, inst.src1); if ((int32_t)inst.constant >= -2048 && (int32_t)inst.constant <= 2047) { SLTI(gpr.R(inst.dest), lhs, (int32_t)inst.constant); @@ -441,26 +439,24 @@ void RiscVJitBackend::CompIR_Compare(IRInst inst) { break; case IROp::SltU: - gpr.SpillLock(inst.dest, inst.src1, inst.src2); + gpr.SpillLockGPR(inst.dest, inst.src1, inst.src2); gpr.MapReg(inst.src1); gpr.MapReg(inst.src2); // It's still fine to sign extend, the biggest just get even bigger. NormalizeSrc12(inst, &lhs, &rhs, SCRATCH1, SCRATCH2, true); gpr.MapReg(inst.dest, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); - gpr.ReleaseSpillLock(inst.dest, inst.src1, inst.src2); SLTU(gpr.R(inst.dest), lhs, rhs); break; case IROp::SltUConst: if (inst.constant == 0) { - gpr.SetImm(inst.dest, 0); + gpr.SetGPRImm(inst.dest, 0); } else { - gpr.SpillLock(inst.dest, inst.src1); + gpr.SpillLockGPR(inst.dest, inst.src1); gpr.MapReg(inst.src1); NormalizeSrc1(inst, &lhs, SCRATCH1, false); gpr.MapReg(inst.dest, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); - gpr.ReleaseSpillLock(inst.dest, inst.src1); // We sign extend because we're comparing against something normalized. // It's also the most efficient to set. diff --git a/Core/MIPS/RiscV/RiscVCompFPU.cpp b/Core/MIPS/RiscV/RiscVCompFPU.cpp index bbad21f9cc..7b92468713 100644 --- a/Core/MIPS/RiscV/RiscVCompFPU.cpp +++ b/Core/MIPS/RiscV/RiscVCompFPU.cpp @@ -346,7 +346,7 @@ void RiscVJitBackend::CompIR_FCompare(IRInst inst) { case IROp::FCmp: switch (inst.dest) { case IRFpCompareMode::False: - gpr.SetImm(IRREG_FPCOND, 0); + gpr.SetGPRImm(IRREG_FPCOND, 0); break; case IRFpCompareMode::EitherUnordered: @@ -584,7 +584,7 @@ void RiscVJitBackend::CompIR_FSpecial(IRInst inst) { gpr.FlushBeforeCall(); fpr.FlushBeforeCall(); // It might be in a non-volatile register. - if (fpr.IsMapped(inst.src1)) { + if (fpr.IsFPRMapped(inst.src1)) { FMV(32, F10, fpr.R(inst.src1)); } else { int offset = offsetof(MIPSState, f) + inst.src1 * 4; diff --git a/Core/MIPS/RiscV/RiscVCompLoadStore.cpp b/Core/MIPS/RiscV/RiscVCompLoadStore.cpp index 09ac714519..348221ef4b 100644 --- a/Core/MIPS/RiscV/RiscVCompLoadStore.cpp +++ b/Core/MIPS/RiscV/RiscVCompLoadStore.cpp @@ -66,7 +66,7 @@ int32_t RiscVJitBackend::AdjustForAddressOffset(RiscVGen::RiscVReg *reg, int32_t void RiscVJitBackend::CompIR_Load(IRInst inst) { CONDITIONAL_DISABLE; - gpr.SpillLock(inst.dest, inst.src1); + gpr.SpillLockGPR(inst.dest, inst.src1); RiscVReg addrReg = INVALID_REG; if (inst.src1 == MIPS_REG_ZERO) { // This will get changed by AdjustForAddressOffset. @@ -74,7 +74,7 @@ void RiscVJitBackend::CompIR_Load(IRInst inst) { #ifdef MASKED_PSP_MEMORY inst.constant &= Memory::MEMVIEW32_MASK; #endif - } else if (jo.cachePointers || gpr.IsMappedAsPointer(inst.src1)) { + } else if (jo.cachePointers || gpr.IsGPRMappedAsPointer(inst.src1)) { addrReg = gpr.MapRegAsPointer(inst.src1); } else { SetScratch1ToSrc1Address(inst.src1); @@ -82,7 +82,6 @@ void RiscVJitBackend::CompIR_Load(IRInst inst) { } // With NOINIT, MapReg won't subtract MEMBASEREG even if dest == src1. gpr.MapReg(inst.dest, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); - gpr.ReleaseSpillLock(inst.dest, inst.src1); s32 imm = AdjustForAddressOffset(&addrReg, inst.constant); @@ -112,7 +111,7 @@ void RiscVJitBackend::CompIR_Load(IRInst inst) { case IROp::Load32Linked: if (inst.dest != MIPS_REG_ZERO) LW(gpr.R(inst.dest), addrReg, imm); - gpr.SetImm(IRREG_LLBIT, 1); + gpr.SetGPRImm(IRREG_LLBIT, 1); break; default: @@ -147,7 +146,7 @@ void RiscVJitBackend::CompIR_FLoad(IRInst inst) { #ifdef MASKED_PSP_MEMORY inst.constant &= Memory::MEMVIEW32_MASK; #endif - } else if (jo.cachePointers || gpr.IsMappedAsPointer(inst.src1)) { + } else if (jo.cachePointers || gpr.IsGPRMappedAsPointer(inst.src1)) { addrReg = gpr.MapRegAsPointer(inst.src1); } else { SetScratch1ToSrc1Address(inst.src1); @@ -180,7 +179,7 @@ void RiscVJitBackend::CompIR_VecLoad(IRInst inst) { #ifdef MASKED_PSP_MEMORY inst.constant &= Memory::MEMVIEW32_MASK; #endif - } else if (jo.cachePointers || gpr.IsMappedAsPointer(inst.src1)) { + } else if (jo.cachePointers || gpr.IsGPRMappedAsPointer(inst.src1)) { addrReg = gpr.MapRegAsPointer(inst.src1); } else { SetScratch1ToSrc1Address(inst.src1); @@ -210,7 +209,7 @@ void RiscVJitBackend::CompIR_VecLoad(IRInst inst) { void RiscVJitBackend::CompIR_Store(IRInst inst) { CONDITIONAL_DISABLE; - gpr.SpillLock(inst.src3, inst.src1); + gpr.SpillLockGPR(inst.src3, inst.src1); RiscVReg addrReg = INVALID_REG; if (inst.src1 == MIPS_REG_ZERO) { // This will get changed by AdjustForAddressOffset. @@ -218,7 +217,7 @@ void RiscVJitBackend::CompIR_Store(IRInst inst) { #ifdef MASKED_PSP_MEMORY inst.constant &= Memory::MEMVIEW32_MASK; #endif - } else if ((jo.cachePointers || gpr.IsMappedAsPointer(inst.src1)) && inst.src3 != inst.src1) { + } else if ((jo.cachePointers || gpr.IsGPRMappedAsPointer(inst.src1)) && inst.src3 != inst.src1) { addrReg = gpr.MapRegAsPointer(inst.src1); } else { SetScratch1ToSrc1Address(inst.src1); @@ -227,7 +226,6 @@ void RiscVJitBackend::CompIR_Store(IRInst inst) { RiscVReg valueReg = gpr.TryMapTempImm(inst.src3); if (valueReg == INVALID_REG) valueReg = gpr.MapReg(inst.src3); - gpr.ReleaseSpillLock(inst.src3, inst.src1); s32 imm = AdjustForAddressOffset(&addrReg, inst.constant); @@ -257,7 +255,7 @@ void RiscVJitBackend::CompIR_CondStore(IRInst inst) { if (inst.op != IROp::Store32Conditional) INVALIDOP; - gpr.SpillLock(IRREG_LLBIT, inst.src3, inst.src1); + gpr.SpillLockGPR(IRREG_LLBIT, inst.src3, inst.src1); RiscVReg addrReg = INVALID_REG; if (inst.src1 == MIPS_REG_ZERO) { // This will get changed by AdjustForAddressOffset. @@ -265,7 +263,7 @@ void RiscVJitBackend::CompIR_CondStore(IRInst inst) { #ifdef MASKED_PSP_MEMORY inst.constant &= Memory::MEMVIEW32_MASK; #endif - } else if ((jo.cachePointers || gpr.IsMappedAsPointer(inst.src1)) && inst.src3 != inst.src1) { + } else if ((jo.cachePointers || gpr.IsGPRMappedAsPointer(inst.src1)) && inst.src3 != inst.src1) { addrReg = gpr.MapRegAsPointer(inst.src1); } else { SetScratch1ToSrc1Address(inst.src1); @@ -273,7 +271,6 @@ void RiscVJitBackend::CompIR_CondStore(IRInst inst) { } gpr.MapReg(inst.src3, inst.dest == MIPS_REG_ZERO ? MIPSMap::INIT : MIPSMap::DIRTY); gpr.MapReg(IRREG_LLBIT); - gpr.ReleaseSpillLock(IRREG_LLBIT, inst.src3, inst.src1); s32 imm = AdjustForAddressOffset(&addrReg, inst.constant); @@ -320,7 +317,7 @@ void RiscVJitBackend::CompIR_FStore(IRInst inst) { #ifdef MASKED_PSP_MEMORY inst.constant &= Memory::MEMVIEW32_MASK; #endif - } else if (jo.cachePointers || gpr.IsMappedAsPointer(inst.src1)) { + } else if (jo.cachePointers || gpr.IsGPRMappedAsPointer(inst.src1)) { addrReg = gpr.MapRegAsPointer(inst.src1); } else { SetScratch1ToSrc1Address(inst.src1); @@ -353,7 +350,7 @@ void RiscVJitBackend::CompIR_VecStore(IRInst inst) { #ifdef MASKED_PSP_MEMORY inst.constant &= Memory::MEMVIEW32_MASK; #endif - } else if (jo.cachePointers || gpr.IsMappedAsPointer(inst.src1)) { + } else if (jo.cachePointers || gpr.IsGPRMappedAsPointer(inst.src1)) { addrReg = gpr.MapRegAsPointer(inst.src1); } else { SetScratch1ToSrc1Address(inst.src1); diff --git a/Core/MIPS/RiscV/RiscVCompSystem.cpp b/Core/MIPS/RiscV/RiscVCompSystem.cpp index dbfa392a2b..f727979b53 100644 --- a/Core/MIPS/RiscV/RiscVCompSystem.cpp +++ b/Core/MIPS/RiscV/RiscVCompSystem.cpp @@ -45,7 +45,7 @@ void RiscVJitBackend::CompIR_Basic(IRInst inst) { case IROp::SetConst: // Sign extend all constants. We get 0xFFFFFFFF sometimes, and it's more work to truncate. // The register only holds 32 bits in the end anyway. - gpr.SetImm(inst.dest, (int32_t)inst.constant); + gpr.SetGPRImm(inst.dest, (int32_t)inst.constant); break; case IROp::SetConstF: @@ -90,7 +90,7 @@ void RiscVJitBackend::CompIR_Transfer(IRInst inst) { switch (inst.op) { case IROp::SetCtrlVFPU: - gpr.SetImm(IRREG_VFPU_CTRL_BASE + inst.dest, (int32_t)inst.constant); + gpr.SetGPRImm(IRREG_VFPU_CTRL_BASE + inst.dest, (int32_t)inst.constant); break; case IROp::SetCtrlVFPUReg: @@ -158,7 +158,7 @@ void RiscVJitBackend::CompIR_Transfer(IRInst inst) { case IROp::FMovFromGPR: fpr.MapReg(inst.dest, MIPSMap::NOINIT); - if (gpr.IsImm(inst.src1) && gpr.GetImm(inst.src1) == 0) { + if (gpr.IsGPRImm(inst.src1) && gpr.GetGPRImm(inst.src1) == 0) { FCVT(FConv::S, FConv::W, fpr.R(inst.dest), R_ZERO); } else { gpr.MapReg(inst.src1); diff --git a/Core/MIPS/RiscV/RiscVCompVec.cpp b/Core/MIPS/RiscV/RiscVCompVec.cpp index 52e9e5d118..3e0f5c1d34 100644 --- a/Core/MIPS/RiscV/RiscVCompVec.cpp +++ b/Core/MIPS/RiscV/RiscVCompVec.cpp @@ -41,11 +41,9 @@ void RiscVJitBackend::CompIR_VecAssign(IRInst inst) { switch (inst.op) { case IROp::Vec4Init: for (int i = 0; i < 4; ++i) - fpr.SpillLock(inst.dest + i); + fpr.SpillLockFPR(inst.dest + i); for (int i = 0; i < 4; ++i) fpr.MapReg(inst.dest + i, MIPSMap::NOINIT); - for (int i = 0; i < 4; ++i) - fpr.ReleaseSpillLock(inst.dest + i); // TODO: Check if FCVT/FMV/FL is better. switch ((Vec4Init)inst.src1) { @@ -220,10 +218,9 @@ void RiscVJitBackend::CompIR_VecArith(IRInst inst) { break; case IROp::Vec4Scale: - fpr.SpillLock(inst.src2); + fpr.SpillLockFPR(inst.src2); fpr.MapReg(inst.src2); fpr.Map4DirtyIn(inst.dest, inst.src1); - fpr.ReleaseSpillLock(inst.src2); for (int i = 0; i < 4; ++i) FMUL(32, fpr.R(inst.dest + i), fpr.R(inst.src1 + i), fpr.R(inst.src2)); break; @@ -252,21 +249,16 @@ void RiscVJitBackend::CompIR_VecHoriz(IRInst inst) { switch (inst.op) { case IROp::Vec4Dot: // TODO: Maybe some option to call the slow accurate mode? - fpr.SpillLock(inst.dest); + fpr.SpillLockFPR(inst.dest); for (int i = 0; i < 4; ++i) { - fpr.SpillLock(inst.src1 + i); - fpr.SpillLock(inst.src2 + i); + fpr.SpillLockFPR(inst.src1 + i); + fpr.SpillLockFPR(inst.src2 + i); } for (int i = 0; i < 4; ++i) { fpr.MapReg(inst.src1 + i); fpr.MapReg(inst.src2 + i); } fpr.MapReg(inst.dest, MIPSMap::NOINIT); - for (int i = 0; i < 4; ++i) { - fpr.ReleaseSpillLock(inst.src1 + i); - fpr.ReleaseSpillLock(inst.src2 + i); - } - fpr.ReleaseSpillLock(inst.dest); if ((inst.dest < inst.src1 + 4 && inst.dest >= inst.src1) || (inst.dest < inst.src2 + 4 && inst.dest >= inst.src2)) { // This means inst.dest overlaps one of src1 or src2. We have to do that one first. @@ -303,9 +295,9 @@ void RiscVJitBackend::CompIR_VecPack(IRInst inst) { break; case IROp::Vec4Unpack8To32: - fpr.SpillLock(inst.src1); + fpr.SpillLockFPR(inst.src1); for (int i = 0; i < 4; ++i) - fpr.SpillLock(inst.dest + i); + fpr.SpillLockFPR(inst.dest + i); fpr.MapReg(inst.src1); for (int i = 0; i < 4; ++i) fpr.MapReg(inst.dest + i, MIPSMap::NOINIT); @@ -324,9 +316,9 @@ void RiscVJitBackend::CompIR_VecPack(IRInst inst) { break; case IROp::Vec2Unpack16To32: - fpr.SpillLock(inst.src1); + fpr.SpillLockFPR(inst.src1); for (int i = 0; i < 2; ++i) - fpr.SpillLock(inst.dest + i); + fpr.SpillLockFPR(inst.dest + i); fpr.MapReg(inst.src1); for (int i = 0; i < 2; ++i) fpr.MapReg(inst.dest + i, MIPSMap::NOINIT); @@ -353,9 +345,9 @@ void RiscVJitBackend::CompIR_VecPack(IRInst inst) { break; case IROp::Vec4Pack31To8: - fpr.SpillLock(inst.dest); + fpr.SpillLockFPR(inst.dest); for (int i = 0; i < 4; ++i) { - fpr.SpillLock(inst.src1 + i); + fpr.SpillLockFPR(inst.src1 + i); fpr.MapReg(inst.src1 + i); } fpr.MapReg(inst.dest, MIPSMap::NOINIT); diff --git a/Core/MIPS/RiscV/RiscVJit.cpp b/Core/MIPS/RiscV/RiscVJit.cpp index d7663484b0..b31e762d39 100644 --- a/Core/MIPS/RiscV/RiscVJit.cpp +++ b/Core/MIPS/RiscV/RiscVJit.cpp @@ -355,9 +355,9 @@ void RiscVJitBackend::NormalizeSrc12(IRInst inst, RiscVReg *lhs, RiscVReg *rhs, RiscVReg RiscVJitBackend::NormalizeR(IRReg rs, IRReg rd, RiscVReg tempReg) { // For proper compare, we must sign extend so they both match or don't match. // But don't change pointers, in case one is SP (happens in LittleBigPlanet.) - if (gpr.IsImm(rs) && gpr.GetImm(rs) == 0) { + if (gpr.IsGPRImm(rs) && gpr.GetGPRImm(rs) == 0) { return R_ZERO; - } else if (gpr.IsMappedAsPointer(rs) || rs == rd) { + } else if (gpr.IsGPRMappedAsPointer(rs) || rs == rd) { return gpr.Normalize32(rs, tempReg); } else { return gpr.Normalize32(rs); diff --git a/Core/MIPS/RiscV/RiscVRegCache.cpp b/Core/MIPS/RiscV/RiscVRegCache.cpp index 913fdb8969..b836e1dd81 100644 --- a/Core/MIPS/RiscV/RiscVRegCache.cpp +++ b/Core/MIPS/RiscV/RiscVRegCache.cpp @@ -127,37 +127,6 @@ void RiscVRegCache::FlushBeforeCall() { } } -bool RiscVRegCache::IsInRAM(IRReg reg) { - _dbg_assert_(IsValidGPR(reg)); - return mr[reg].loc == MIPSLoc::MEM; -} - -bool RiscVRegCache::IsMapped(IRReg mipsReg) { - _dbg_assert_(IsValidGPR(mipsReg)); - return mr[mipsReg].loc == MIPSLoc::REG || mr[mipsReg].loc == MIPSLoc::REG_IMM; -} - -bool RiscVRegCache::IsMappedAsPointer(IRReg mipsReg) { - _dbg_assert_(IsValidGPR(mipsReg)); - if (mr[mipsReg].loc == MIPSLoc::REG) { - return nr[mr[mipsReg].nReg].pointerified; - } else if (mr[mipsReg].loc == MIPSLoc::REG_IMM) { - if (nr[mr[mipsReg].nReg].pointerified) { - ERROR_LOG(JIT, "Really shouldn't be pointerified here"); - } - } else if (mr[mipsReg].loc == MIPSLoc::REG_AS_PTR) { - return true; - } - return false; -} - -bool RiscVRegCache::IsMappedAsStaticPointer(IRReg reg) { - if (IsMappedAsPointer(reg)) { - return mr[reg].isStatic; - } - return false; -} - bool RiscVRegCache::IsNormalized32(IRReg mipsReg) { _dbg_assert_(IsValidGPR(mipsReg)); if (XLEN == 32) @@ -321,7 +290,7 @@ void RiscVRegCache::MapRegTo(RiscVReg reg, IRReg mipsReg, MIPSMap mapFlags) { RiscVReg RiscVRegCache::TryMapTempImm(IRReg r) { _dbg_assert_(IsValidGPR(r)); // If already mapped, no need for a temporary. - if (IsMapped(r)) { + if (IsGPRMapped(r)) { return R(r); } @@ -513,44 +482,44 @@ void RiscVRegCache::MapIn(IRReg rs) { } void RiscVRegCache::MapInIn(IRReg rd, IRReg rs) { - SpillLock(rd, rs); + SpillLockGPR(rd, rs); MapReg(rd); MapReg(rs); - ReleaseSpillLock(rd, rs); + ReleaseSpillLockGPR(rd, rs); } void RiscVRegCache::MapDirtyIn(IRReg rd, IRReg rs, MapType type) { - SpillLock(rd, rs); + SpillLockGPR(rd, rs); bool load = type == MapType::ALWAYS_LOAD || rd == rs; MIPSMap norm32 = type == MapType::AVOID_LOAD_MARK_NORM32 ? MIPSMap::MARK_NORM32 : MIPSMap::INIT; MapReg(rd, (load ? MIPSMap::DIRTY : MIPSMap::NOINIT) | norm32); MapReg(rs); - ReleaseSpillLock(rd, rs); + ReleaseSpillLockGPR(rd, rs); } void RiscVRegCache::MapDirtyInIn(IRReg rd, IRReg rs, IRReg rt, MapType type) { - SpillLock(rd, rs, rt); + SpillLockGPR(rd, rs, rt); bool load = type == MapType::ALWAYS_LOAD || (rd == rs || rd == rt); MIPSMap norm32 = type == MapType::AVOID_LOAD_MARK_NORM32 ? MIPSMap::MARK_NORM32 : MIPSMap::INIT; MapReg(rd, (load ? MIPSMap::DIRTY : MIPSMap::NOINIT) | norm32); MapReg(rt); MapReg(rs); - ReleaseSpillLock(rd, rs, rt); + ReleaseSpillLockGPR(rd, rs, rt); } void RiscVRegCache::MapDirtyDirtyIn(IRReg rd1, IRReg rd2, IRReg rs, MapType type) { - SpillLock(rd1, rd2, rs); + SpillLockGPR(rd1, rd2, rs); bool load1 = type == MapType::ALWAYS_LOAD || rd1 == rs; bool load2 = type == MapType::ALWAYS_LOAD || rd2 == rs; MIPSMap norm32 = type == MapType::AVOID_LOAD_MARK_NORM32 ? MIPSMap::MARK_NORM32 : MIPSMap::INIT; MapReg(rd1, (load1 ? MIPSMap::DIRTY : MIPSMap::NOINIT) | norm32); MapReg(rd2, (load2 ? MIPSMap::DIRTY : MIPSMap::NOINIT) | norm32); MapReg(rs); - ReleaseSpillLock(rd1, rd2, rs); + ReleaseSpillLockGPR(rd1, rd2, rs); } void RiscVRegCache::MapDirtyDirtyInIn(IRReg rd1, IRReg rd2, IRReg rs, IRReg rt, MapType type) { - SpillLock(rd1, rd2, rs, rt); + SpillLockGPR(rd1, rd2, rs, rt); bool load1 = type == MapType::ALWAYS_LOAD || (rd1 == rs || rd1 == rt); bool load2 = type == MapType::ALWAYS_LOAD || (rd2 == rs || rd2 == rt); MIPSMap norm32 = type == MapType::AVOID_LOAD_MARK_NORM32 ? MIPSMap::MARK_NORM32 : MIPSMap::INIT; @@ -558,7 +527,7 @@ void RiscVRegCache::MapDirtyDirtyInIn(IRReg rd1, IRReg rd2, IRReg rs, IRReg rt, MapReg(rd2, (load2 ? MIPSMap::DIRTY : MIPSMap::NOINIT) | norm32); MapReg(rt); MapReg(rs); - ReleaseSpillLock(rd1, rd2, rs, rt); + ReleaseSpillLockGPR(rd1, rd2, rs, rt); } void RiscVRegCache::AdjustNativeRegAsPtr(IRNativeReg nreg, bool state) { @@ -579,9 +548,14 @@ void RiscVRegCache::StoreNativeReg(IRNativeReg nreg, IRReg first, int lanes) { _dbg_assert_(r > X0 && r <= X31); _dbg_assert_(first != MIPS_REG_ZERO); // Multilane not yet supported. - _assert_(lanes == 1); + _assert_(lanes == 1 || (lanes == 2 && first == IRREG_LO)); _assert_(mr[first].loc == MIPSLoc::REG || mr[first].loc == MIPSLoc::REG_IMM); - emit_->SW(r, CTXREG, GetMipsRegOffset(first)); + if (lanes == 1) + emit_->SW(r, CTXREG, GetMipsRegOffset(first)); + else if (lanes == 2) + emit_->SD(r, CTXREG, GetMipsRegOffset(first)); + else + _assert_(false); } void RiscVRegCache::DiscardR(IRReg mipsReg) { @@ -625,20 +599,7 @@ RiscVReg RiscVRegCache::RiscVRegForFlush(IRReg r) { case MIPSLoc::REG: case MIPSLoc::REG_IMM: - if (mr[r].nReg == INVALID_REG) { - ERROR_LOG_REPORT(JIT, "RiscVRegForFlush: MipsReg %d had bad riscvReg", r); - return INVALID_REG; - } - // No need to flush if it's zero or not dirty. - if (r == MIPS_REG_ZERO || !nr[mr[r].nReg].isDirty) { - return INVALID_REG; - } - // TODO: Lo/hi optimization? - return (RiscVReg)mr[r].nReg; - case MIPSLoc::REG_AS_PTR: - return INVALID_REG; - case MIPSLoc::MEM: return INVALID_REG; @@ -667,28 +628,21 @@ void RiscVRegCache::FlushR(IRReg r) { storeReg = SCRATCH1; } emit_->SW(storeReg, CTXREG, GetMipsRegOffset(r)); + mr[r].loc = MIPSLoc::MEM; + mr[r].nReg = (int)INVALID_REG; + mr[r].imm = -1; + } else { + mr[r].loc = MIPSLoc::REG_IMM; + mr[r].nReg = R_ZERO; + mr[r].imm = 0; } break; case MIPSLoc::REG: case MIPSLoc::REG_IMM: - if (nr[mr[r].nReg].isDirty) { - StoreNativeReg(mr[r].nReg, r, 1); - nr[mr[r].nReg].isDirty = false; - } - nr[mr[r].nReg].mipsReg = IRREG_INVALID; - nr[mr[r].nReg].pointerified = false; - break; - case MIPSLoc::REG_AS_PTR: - if (nr[mr[r].nReg].isDirty) { - AdjustNativeRegAsPtr(mr[r].nReg, false); - // We set this so StoreNativeReg knows it's no longer a pointer. - mr[r].loc = MIPSLoc::REG; - StoreNativeReg(mr[r].nReg, r, 1); - nr[mr[r].nReg].isDirty = false; - } - nr[mr[r].nReg].mipsReg = IRREG_INVALID; + // Might be in a native reg with multiple IR regs, flush together. + FlushNativeReg(mr[r].nReg); break; case MIPSLoc::MEM: @@ -699,15 +653,6 @@ void RiscVRegCache::FlushR(IRReg r) { ERROR_LOG_REPORT(JIT, "FlushR: MipsReg %d with invalid location %d", r, (int)mr[r].loc); break; } - if (r == MIPS_REG_ZERO) { - mr[r].loc = MIPSLoc::REG_IMM; - mr[r].nReg = R_ZERO; - mr[r].imm = 0; - } else { - mr[r].loc = MIPSLoc::MEM; - mr[r].nReg = (int)INVALID_REG; - mr[r].imm = -1; - } } void RiscVRegCache::FlushAll() { @@ -772,91 +717,11 @@ void RiscVRegCache::FlushAll() { } } -void RiscVRegCache::SetImm(IRReg r, u64 immVal) { - _dbg_assert_(IsValidGPR(r)); - if (r == MIPS_REG_ZERO && immVal != 0) { - ERROR_LOG_REPORT(JIT, "Trying to set immediate %08x to r0", (u32)immVal); - return; - } - - if (mr[r].loc == MIPSLoc::REG_IMM && mr[r].imm == immVal) { - // Already have that value, let's keep it in the reg. - return; - } - - // TODO: HI/LO optimization? - // All regs on the PSP are 32 bit, but LO we treat as HI:LO so is 64 full bits. - immVal = immVal & 0xFFFFFFFF; - - if (mr[r].isStatic) { - mr[r].loc = MIPSLoc::IMM; - mr[r].imm = immVal; - nr[mr[r].nReg].pointerified = false; - nr[mr[r].nReg].normalized32 = false; - // We do not change reg to INVALID_REG for obvious reasons.. - } else { - // Zap existing value if cached in a reg - if (mr[r].nReg != INVALID_REG) { - nr[mr[r].nReg].mipsReg = IRREG_INVALID; - nr[mr[r].nReg].isDirty = false; - nr[mr[r].nReg].pointerified = false; - nr[mr[r].nReg].normalized32 = false; - } - mr[r].loc = MIPSLoc::IMM; - mr[r].imm = immVal; - mr[r].nReg = (int)INVALID_REG; - } -} - -bool RiscVRegCache::IsImm(IRReg r) const { - _dbg_assert_(IsValidGPR(r)); - if (r == MIPS_REG_ZERO) - return true; - else - return mr[r].loc == MIPSLoc::IMM || mr[r].loc == MIPSLoc::REG_IMM; -} - -u64 RiscVRegCache::GetImm(IRReg r) const { - _dbg_assert_(IsValidGPR(r)); - if (r == MIPS_REG_ZERO) - return 0; - if (mr[r].loc != MIPSLoc::IMM && mr[r].loc != MIPSLoc::REG_IMM) { - ERROR_LOG_REPORT(JIT, "Trying to get imm from non-imm register %i", r); - } - return mr[r].imm; -} - int RiscVRegCache::GetMipsRegOffset(IRReg r) { _dbg_assert_(IsValidGPR(r)); return r * 4; } -void RiscVRegCache::SpillLock(IRReg r1, IRReg r2, IRReg r3, IRReg r4) { - _dbg_assert_(IsValidGPR(r1)); - _dbg_assert_(r2 == IRREG_INVALID || IsValidGPR(r2)); - _dbg_assert_(r3 == IRREG_INVALID || IsValidGPR(r3)); - _dbg_assert_(r4 == IRREG_INVALID || IsValidGPR(r4)); - mr[r1].spillLockIRIndex = irIndex_; - if (r2 != IRREG_INVALID) mr[r2].spillLockIRIndex = irIndex_; - if (r3 != IRREG_INVALID) mr[r3].spillLockIRIndex = irIndex_; - if (r4 != IRREG_INVALID) mr[r4].spillLockIRIndex = irIndex_; -} - -void RiscVRegCache::ReleaseSpillLock(IRReg r1, IRReg r2, IRReg r3, IRReg r4) { - _dbg_assert_(IsValidGPR(r1)); - _dbg_assert_(r2 == IRREG_INVALID || IsValidGPR(r2)); - _dbg_assert_(r3 == IRREG_INVALID || IsValidGPR(r3)); - _dbg_assert_(r4 == IRREG_INVALID || IsValidGPR(r4)); - if (!mr[r1].isStatic) - mr[r1].spillLockIRIndex = -1; - if (r2 != IRREG_INVALID && !mr[r2].isStatic) - mr[r2].spillLockIRIndex = -1; - if (r3 != IRREG_INVALID && !mr[r3].isStatic) - mr[r3].spillLockIRIndex = -1; - if (r4 != IRREG_INVALID && !mr[r4].isStatic) - mr[r4].spillLockIRIndex = -1; -} - RiscVReg RiscVRegCache::R(IRReg mipsReg) { _dbg_assert_(IsValidGPR(mipsReg)); _dbg_assert_(mr[mipsReg].loc == MIPSLoc::REG || mr[mipsReg].loc == MIPSLoc::REG_IMM); diff --git a/Core/MIPS/RiscV/RiscVRegCache.h b/Core/MIPS/RiscV/RiscVRegCache.h index 8fd24bc4db..28618e040d 100644 --- a/Core/MIPS/RiscV/RiscVRegCache.h +++ b/Core/MIPS/RiscV/RiscVRegCache.h @@ -63,15 +63,6 @@ public: void Init(RiscVGen::RiscVEmitter *emitter); - // Protect the arm register containing a MIPS register from spilling, to ensure that - // it's being kept allocated. - void SpillLock(IRReg reg, IRReg reg2 = IRREG_INVALID, IRReg reg3 = IRREG_INVALID, IRReg reg4 = IRREG_INVALID); - void ReleaseSpillLock(IRReg reg, IRReg reg2 = IRREG_INVALID, IRReg reg3 = IRREG_INVALID, IRReg reg4 = IRREG_INVALID); - - void SetImm(IRReg reg, u64 immVal); - bool IsImm(IRReg reg) const; - u64 GetImm(IRReg reg) const; - // May fail and return INVALID_REG if it needs flushing. RiscVGen::RiscVReg TryMapTempImm(IRReg); @@ -79,10 +70,6 @@ public: RiscVGen::RiscVReg MapReg(IRReg reg, RiscVJitConstants::MIPSMap mapFlags = RiscVJitConstants::MIPSMap::INIT); RiscVGen::RiscVReg MapRegAsPointer(IRReg reg); - bool IsMapped(IRReg reg); - bool IsMappedAsPointer(IRReg reg); - bool IsMappedAsStaticPointer(IRReg reg); - bool IsInRAM(IRReg reg); bool IsNormalized32(IRReg reg); void MarkDirty(RiscVGen::RiscVReg reg, bool andNormalized32 = false); diff --git a/Core/MIPS/RiscV/RiscVRegCacheFPU.cpp b/Core/MIPS/RiscV/RiscVRegCacheFPU.cpp index c33a4f22d5..48653f9fcf 100644 --- a/Core/MIPS/RiscV/RiscVRegCacheFPU.cpp +++ b/Core/MIPS/RiscV/RiscVRegCacheFPU.cpp @@ -53,16 +53,6 @@ const int *RiscVRegCacheFPU::GetAllocationOrder(MIPSLoc type, int &count, int &b return allocationOrder; } -bool RiscVRegCacheFPU::IsInRAM(IRReg reg) { - _dbg_assert_(IsValidFPR(reg)); - return mr[reg + 32].loc == MIPSLoc::MEM; -} - -bool RiscVRegCacheFPU::IsMapped(IRReg mipsReg) { - _dbg_assert_(IsValidFPR(mipsReg)); - return mr[mipsReg + 32].loc == MIPSLoc::FREG; -} - RiscVReg RiscVRegCacheFPU::MapReg(IRReg mipsReg, MIPSMap mapFlags) { _dbg_assert_(IsValidFPR(mipsReg)); _dbg_assert_(mr[mipsReg + 32].loc == MIPSLoc::MEM || mr[mipsReg + 32].loc == MIPSLoc::FREG); @@ -100,54 +90,54 @@ RiscVReg RiscVRegCacheFPU::MapReg(IRReg mipsReg, MIPSMap mapFlags) { } void RiscVRegCacheFPU::MapInIn(IRReg rd, IRReg rs) { - SpillLock(rd, rs); + SpillLockFPR(rd, rs); MapReg(rd); MapReg(rs); - ReleaseSpillLock(rd, rs); + ReleaseSpillLockFPR(rd, rs); } void RiscVRegCacheFPU::MapDirtyIn(IRReg rd, IRReg rs, bool avoidLoad) { - SpillLock(rd, rs); + SpillLockFPR(rd, rs); bool load = !avoidLoad || rd == rs; MapReg(rd, load ? MIPSMap::DIRTY : MIPSMap::NOINIT); MapReg(rs); - ReleaseSpillLock(rd, rs); + ReleaseSpillLockFPR(rd, rs); } void RiscVRegCacheFPU::MapDirtyInIn(IRReg rd, IRReg rs, IRReg rt, bool avoidLoad) { - SpillLock(rd, rs, rt); + SpillLockFPR(rd, rs, rt); bool load = !avoidLoad || (rd == rs || rd == rt); MapReg(rd, load ? MIPSMap::DIRTY : MIPSMap::NOINIT); MapReg(rt); MapReg(rs); - ReleaseSpillLock(rd, rs, rt); + ReleaseSpillLockFPR(rd, rs, rt); } RiscVReg RiscVRegCacheFPU::MapDirtyInTemp(IRReg rd, IRReg rs, bool avoidLoad) { - SpillLock(rd, rs); + SpillLockFPR(rd, rs); bool load = !avoidLoad || rd == rs; MapReg(rd, load ? MIPSMap::DIRTY : MIPSMap::NOINIT); MapReg(rs); RiscVReg temp = (RiscVReg)(F0 + AllocateReg(MIPSLoc::FREG)); - ReleaseSpillLock(rd, rs); + ReleaseSpillLockFPR(rd, rs); return temp; } void RiscVRegCacheFPU::Map4DirtyIn(IRReg rdbase, IRReg rsbase, bool avoidLoad) { for (int i = 0; i < 4; ++i) - SpillLock(rdbase + i, rsbase + i); + SpillLockFPR(rdbase + i, rsbase + i); bool load = !avoidLoad || (rdbase < rsbase + 4 && rdbase + 4 > rsbase); for (int i = 0; i < 4; ++i) MapReg(rdbase + i, load ? MIPSMap::DIRTY : MIPSMap::NOINIT); for (int i = 0; i < 4; ++i) MapReg(rsbase + i); for (int i = 0; i < 4; ++i) - ReleaseSpillLock(rdbase + i, rsbase + i); + ReleaseSpillLockFPR(rdbase + i, rsbase + i); } void RiscVRegCacheFPU::Map4DirtyInIn(IRReg rdbase, IRReg rsbase, IRReg rtbase, bool avoidLoad) { for (int i = 0; i < 4; ++i) - SpillLock(rdbase + i, rsbase + i, rtbase + i); + SpillLockFPR(rdbase + i, rsbase + i, rtbase + i); bool load = !avoidLoad || (rdbase < rsbase + 4 && rdbase + 4 > rsbase) || (rdbase < rtbase + 4 && rdbase + 4 > rtbase); for (int i = 0; i < 4; ++i) MapReg(rdbase + i, load ? MIPSMap::DIRTY : MIPSMap::NOINIT); @@ -156,12 +146,12 @@ void RiscVRegCacheFPU::Map4DirtyInIn(IRReg rdbase, IRReg rsbase, IRReg rtbase, b for (int i = 0; i < 4; ++i) MapReg(rtbase + i); for (int i = 0; i < 4; ++i) - ReleaseSpillLock(rdbase + i, rsbase + i, rtbase + i); + ReleaseSpillLockFPR(rdbase + i, rsbase + i, rtbase + i); } RiscVReg RiscVRegCacheFPU::Map4DirtyInTemp(IRReg rdbase, IRReg rsbase, bool avoidLoad) { for (int i = 0; i < 4; ++i) - SpillLock(rdbase + i, rsbase + i); + SpillLockFPR(rdbase + i, rsbase + i); bool load = !avoidLoad || (rdbase < rsbase + 4 && rdbase + 4 > rsbase); for (int i = 0; i < 4; ++i) MapReg(rdbase + i, load ? MIPSMap::DIRTY : MIPSMap::NOINIT); @@ -169,7 +159,7 @@ RiscVReg RiscVRegCacheFPU::Map4DirtyInTemp(IRReg rdbase, IRReg rsbase, bool avoi MapReg(rsbase + i); RiscVReg temp = (RiscVReg)(F0 + AllocateReg(MIPSLoc::FREG)); for (int i = 0; i < 4; ++i) - ReleaseSpillLock(rdbase + i, rsbase + i); + ReleaseSpillLockFPR(rdbase + i, rsbase + i); return temp; } @@ -186,28 +176,20 @@ void RiscVRegCacheFPU::StoreNativeReg(IRNativeReg nreg, IRReg first, int lanes) } void RiscVRegCacheFPU::FlushR(IRReg r) { - _dbg_assert_(IsValidFPR(r)); - RiscVReg reg = RiscVRegForFlush(r); - if (reg != INVALID_REG) - FlushNativeReg((IRNativeReg)(reg - F0)); -} - -RiscVReg RiscVRegCacheFPU::RiscVRegForFlush(IRReg r) { _dbg_assert_(IsValidFPR(r)); switch (mr[r + 32].loc) { case MIPSLoc::FREG: - _assert_msg_(mr[r + 32].nReg != INVALID_REG, "RiscVRegForFlush: IR %d had bad RiscVReg", r + 32); - if (mr[r + 32].nReg == INVALID_REG) { - return INVALID_REG; - } - return (RiscVReg)(F0 + mr[r + 32].nReg); + case MIPSLoc::VREG: + _assert_msg_(mr[r + 32].nReg != INVALID_REG, "FlushR: IR %d had bad RiscVReg", r + 32); + FlushNativeReg(mr[r + 32].nReg); + break; case MIPSLoc::MEM: - return INVALID_REG; + break; default: - _assert_(false); - return INVALID_REG; + _assert_msg_(false, "Unexpected register location %d", (int)mr[r + 32].loc); + break; } } @@ -279,8 +261,6 @@ void RiscVRegCacheFPU::DiscardR(IRReg r) { _assert_(false); break; } - mr[r + 32].loc = MIPSLoc::MEM; - mr[r + 32].nReg = -1; mr[r + 32].spillLockIRIndex = -1; } @@ -291,34 +271,6 @@ int RiscVRegCacheFPU::GetMipsRegOffset(IRReg r) { return (r) * 4; } -void RiscVRegCacheFPU::SpillLock(IRReg r1, IRReg r2, IRReg r3, IRReg r4) { - _dbg_assert_(IsValidFPR(r1)); - _dbg_assert_(r2 == IRREG_INVALID || IsValidFPR(r2)); - _dbg_assert_(r3 == IRREG_INVALID || IsValidFPR(r3)); - _dbg_assert_(r4 == IRREG_INVALID || IsValidFPR(r4)); - mr[r1 + 32].spillLockIRIndex = irIndex_; - if (r2 != IRREG_INVALID) - mr[r2 + 32].spillLockIRIndex = irIndex_; - if (r3 != IRREG_INVALID) - mr[r3 + 32].spillLockIRIndex = irIndex_; - if (r4 != IRREG_INVALID) - mr[r4 + 32].spillLockIRIndex = irIndex_; -} - -void RiscVRegCacheFPU::ReleaseSpillLock(IRReg r1, IRReg r2, IRReg r3, IRReg r4) { - _dbg_assert_(IsValidFPR(r1)); - _dbg_assert_(r2 == IRREG_INVALID || IsValidFPR(r2)); - _dbg_assert_(r3 == IRREG_INVALID || IsValidFPR(r3)); - _dbg_assert_(r4 == IRREG_INVALID || IsValidFPR(r4)); - mr[r1 + 32].spillLockIRIndex = -1; - if (r2 != IRREG_INVALID) - mr[r2 + 32].spillLockIRIndex = -1; - if (r3 != IRREG_INVALID) - mr[r3 + 32].spillLockIRIndex = -1; - if (r4 != IRREG_INVALID) - mr[r4 + 32].spillLockIRIndex = -1; -} - RiscVReg RiscVRegCacheFPU::R(IRReg mipsReg) { _dbg_assert_(IsValidFPR(mipsReg)); _dbg_assert_(mr[mipsReg + 32].loc == MIPSLoc::FREG); diff --git a/Core/MIPS/RiscV/RiscVRegCacheFPU.h b/Core/MIPS/RiscV/RiscVRegCacheFPU.h index 3103101f94..1c0b18ccfd 100644 --- a/Core/MIPS/RiscV/RiscVRegCacheFPU.h +++ b/Core/MIPS/RiscV/RiscVRegCacheFPU.h @@ -31,17 +31,9 @@ public: void Init(RiscVGen::RiscVEmitter *emitter); - // Protect the RISC-V register containing a MIPS register from spilling, to ensure that - // it's being kept allocated. - void SpillLock(IRReg reg, IRReg reg2 = IRREG_INVALID, IRReg reg3 = IRREG_INVALID, IRReg reg4 = IRREG_INVALID); - void ReleaseSpillLock(IRReg reg, IRReg reg2 = IRREG_INVALID, IRReg reg3 = IRREG_INVALID, IRReg reg4 = IRREG_INVALID); - // Returns a RISC-V register containing the requested MIPS register. RiscVGen::RiscVReg MapReg(IRReg reg, RiscVJitConstants::MIPSMap mapFlags = RiscVJitConstants::MIPSMap::INIT); - bool IsMapped(IRReg r); - bool IsInRAM(IRReg r); - void MapInIn(IRReg rd, IRReg rs); void MapDirtyIn(IRReg rd, IRReg rs, bool avoidLoad = true); void MapDirtyInIn(IRReg rd, IRReg rs, IRReg rt, bool avoidLoad = true); @@ -62,7 +54,6 @@ protected: void StoreNativeReg(IRNativeReg nreg, IRReg first, int lanes) override; private: - RiscVGen::RiscVReg RiscVRegForFlush(IRReg r); int GetMipsRegOffset(IRReg r); RiscVGen::RiscVEmitter *emit_ = nullptr; From 718a1b3944130bdfb19fb2da97d7e12d45b8fb4f Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 17 Aug 2023 22:59:38 -0700 Subject: [PATCH 4/8] riscv: Centralize MarkDirty flagging. --- Core/MIPS/IR/IRRegCache.cpp | 35 +++++++++++++++++++++ Core/MIPS/IR/IRRegCache.h | 3 ++ Core/MIPS/RiscV/RiscVCompALU.cpp | 48 ++++++++++++++--------------- Core/MIPS/RiscV/RiscVCompSystem.cpp | 6 ++-- Core/MIPS/RiscV/RiscVRegCache.cpp | 29 ----------------- Core/MIPS/RiscV/RiscVRegCache.h | 2 -- 6 files changed, 65 insertions(+), 58 deletions(-) diff --git a/Core/MIPS/IR/IRRegCache.cpp b/Core/MIPS/IR/IRRegCache.cpp index 90f677fc94..8d9a0a74d0 100644 --- a/Core/MIPS/IR/IRRegCache.cpp +++ b/Core/MIPS/IR/IRRegCache.cpp @@ -22,6 +22,7 @@ #include #include "Common/Log.h" #include "Common/LogReporting.h" +#include "Core/MemMap.h" #include "Core/MIPS/IR/IRAnalysis.h" #include "Core/MIPS/IR/IRRegCache.h" #include "Core/MIPS/IR/IRInst.h" @@ -282,6 +283,40 @@ void IRNativeRegCacheBase::SetSpillLockIRIndex(IRReg r1, IRReg r2, IRReg r3, IRR mr[r4 + offset].spillLockIRIndex = index; } +void IRNativeRegCacheBase::MarkGPRDirty(IRReg gpr, bool andNormalized32) { + _assert_(IsGPRMapped(gpr)); + if (!IsGPRMapped(gpr)) + return; + + IRNativeReg nreg = mr[gpr].nReg; + nr[nreg].isDirty = true; + nr[nreg].normalized32 = andNormalized32; + // If reg is written to, pointerification is assumed lost. + nr[nreg].pointerified = false; + if (mr[gpr].loc == MIPSLoc::REG_AS_PTR || mr[gpr].loc == MIPSLoc::REG_IMM) { + mr[gpr].loc = MIPSLoc::REG; + mr[gpr].imm = -1; + } + _dbg_assert_(mr[gpr].loc == MIPSLoc::REG); +} + +void IRNativeRegCacheBase::MarkGPRAsPointerDirty(IRReg gpr) { + _assert_(IsGPRMappedAsPointer(gpr)); + if (!IsGPRMappedAsPointer(gpr)) + return; + +#ifdef MASKED_PSP_MEMORY + if (mr[gpr].loc == MIPSLoc::REG_AS_PTR) { + _assert_msg_(false, "MarkGPRAsPointerDirty is not possible when using MASKED_PSP_MEMORY"); + } +#endif + + IRNativeReg nreg = mr[gpr].nReg; + _dbg_assert_(!nr[nreg].normalized32); + nr[nreg].isDirty = true; + // Stays pointerified or REG_AS_PTR. +} + IRNativeReg IRNativeRegCacheBase::AllocateReg(MIPSLoc type) { _dbg_assert_(type == MIPSLoc::REG || type == MIPSLoc::FREG || type == MIPSLoc::VREG); diff --git a/Core/MIPS/IR/IRRegCache.h b/Core/MIPS/IR/IRRegCache.h index 0e9e41acf9..f5301f8197 100644 --- a/Core/MIPS/IR/IRRegCache.h +++ b/Core/MIPS/IR/IRRegCache.h @@ -166,6 +166,9 @@ public: void ReleaseSpillLockGPR(IRReg reg, IRReg reg2 = IRREG_INVALID, IRReg reg3 = IRREG_INVALID, IRReg reg4 = IRREG_INVALID); void ReleaseSpillLockFPR(IRReg reg, IRReg reg2 = IRREG_INVALID, IRReg reg3 = IRREG_INVALID, IRReg reg4 = IRREG_INVALID); + void MarkGPRDirty(IRReg gpr, bool andNormalized32 = false); + void MarkGPRAsPointerDirty(IRReg gpr); + protected: virtual void SetupInitialRegs(); virtual const int *GetAllocationOrder(MIPSLoc type, int &count, int &base) const = 0; diff --git a/Core/MIPS/RiscV/RiscVCompALU.cpp b/Core/MIPS/RiscV/RiscVCompALU.cpp index e00beab181..69e4a47ee5 100644 --- a/Core/MIPS/RiscV/RiscVCompALU.cpp +++ b/Core/MIPS/RiscV/RiscVCompALU.cpp @@ -65,8 +65,8 @@ void RiscVJitBackend::CompIR_Arith(IRInst inst) { case IROp::AddConst: if ((int32_t)inst.constant >= -2048 && (int32_t)inst.constant <= 2047) { // Typical of stack pointer updates. - if (gpr.IsGPRMappedAsPointer(inst.src1) && inst.dest == inst.src1 && allowPtrMath) { - gpr.MarkPtrDirty(gpr.RPtr(inst.dest)); + if (gpr.IsGPRMappedAsPointer(inst.dest) && inst.dest == inst.src1 && allowPtrMath) { + gpr.MarkGPRAsPointerDirty(inst.dest); ADDI(gpr.RPtr(inst.dest), gpr.RPtr(inst.dest), inst.constant); } else { gpr.MapDirtyIn(inst.dest, inst.src1, MapType::AVOID_LOAD_MARK_NORM32); @@ -107,7 +107,7 @@ void RiscVJitBackend::CompIR_Logic(IRInst inst) { } else if (inst.src1 != inst.dest) { gpr.MapDirtyIn(inst.dest, inst.src1); MV(gpr.R(inst.dest), gpr.R(inst.src1)); - gpr.MarkDirty(gpr.R(inst.dest), gpr.IsNormalized32(inst.src1)); + gpr.MarkGPRDirty(inst.dest, gpr.IsNormalized32(inst.src1)); } break; @@ -117,11 +117,11 @@ void RiscVJitBackend::CompIR_Logic(IRInst inst) { OR(gpr.R(inst.dest), gpr.R(inst.src1), gpr.R(inst.src2)); // If both were normalized before, the result is normalized. if (gpr.IsNormalized32(inst.src1) && gpr.IsNormalized32(inst.src2)) - gpr.MarkDirty(gpr.R(inst.dest), true); + gpr.MarkGPRDirty(inst.dest, true); } else if (inst.src1 != inst.dest) { gpr.MapDirtyIn(inst.dest, inst.src1); MV(gpr.R(inst.dest), gpr.R(inst.src1)); - gpr.MarkDirty(gpr.R(inst.dest), gpr.IsNormalized32(inst.src1)); + gpr.MarkGPRDirty(inst.dest, gpr.IsNormalized32(inst.src1)); } break; @@ -145,10 +145,10 @@ void RiscVJitBackend::CompIR_Logic(IRInst inst) { } // If the sign bits aren't cleared, and it was normalized before - it still is. if ((inst.constant & 0x80000000) != 0 && gpr.IsNormalized32(inst.src1)) - gpr.MarkDirty(gpr.R(inst.dest), true); + gpr.MarkGPRDirty(inst.dest, true); // Otherwise, if we cleared the sign bits, it's naturally normalized. else if ((inst.constant & 0x80000000) == 0) - gpr.MarkDirty(gpr.R(inst.dest), true); + gpr.MarkGPRDirty(inst.dest, true); break; case IROp::OrConst: @@ -162,7 +162,7 @@ void RiscVJitBackend::CompIR_Logic(IRInst inst) { } // Since our constant is normalized, oring its bits in won't hurt normalization. if (gpr.IsNormalized32(inst.src1)) - gpr.MarkDirty(gpr.R(inst.dest), true); + gpr.MarkGPRDirty(inst.dest, true); break; case IROp::XorConst: @@ -195,7 +195,7 @@ void RiscVJitBackend::CompIR_Assign(IRInst inst) { if (inst.dest != inst.src1) { gpr.MapDirtyIn(inst.dest, inst.src1); MV(gpr.R(inst.dest), gpr.R(inst.src1)); - gpr.MarkDirty(gpr.R(inst.dest), gpr.IsNormalized32(inst.src1)); + gpr.MarkGPRDirty(inst.dest, gpr.IsNormalized32(inst.src1)); } break; @@ -280,7 +280,7 @@ void RiscVJitBackend::CompIR_Bits(IRInst inst) { if (XLEN >= 64) { // REV8 swaps the entire register, so get the 32 highest bits. SRAI(gpr.R(inst.dest), gpr.R(inst.dest), XLEN - 32); - gpr.MarkDirty(gpr.R(inst.dest), true); + gpr.MarkGPRDirty(inst.dest, true); } } else { CompIR_Generic(inst); @@ -339,7 +339,7 @@ void RiscVJitBackend::CompIR_Shift(IRInst inst) { if (inst.dest != inst.src1) { gpr.MapDirtyIn(inst.dest, inst.src1); MV(gpr.R(inst.dest), gpr.R(inst.src1)); - gpr.MarkDirty(gpr.R(inst.dest), gpr.IsNormalized32(inst.src1)); + gpr.MarkGPRDirty(inst.dest, gpr.IsNormalized32(inst.src1)); } } else { gpr.MapDirtyIn(inst.dest, inst.src1, MapType::AVOID_LOAD_MARK_NORM32); @@ -355,7 +355,7 @@ void RiscVJitBackend::CompIR_Shift(IRInst inst) { if (inst.dest != inst.src1) { gpr.MapDirtyIn(inst.dest, inst.src1); MV(gpr.R(inst.dest), gpr.R(inst.src1)); - gpr.MarkDirty(gpr.R(inst.dest), gpr.IsNormalized32(inst.src1)); + gpr.MarkGPRDirty(inst.dest, gpr.IsNormalized32(inst.src1)); } } else { gpr.MapDirtyIn(inst.dest, inst.src1, MapType::AVOID_LOAD_MARK_NORM32); @@ -372,7 +372,7 @@ void RiscVJitBackend::CompIR_Shift(IRInst inst) { if (inst.dest != inst.src1) { gpr.MapDirtyIn(inst.dest, inst.src1); MV(gpr.R(inst.dest), gpr.R(inst.src1)); - gpr.MarkDirty(gpr.R(inst.dest), gpr.IsNormalized32(inst.src1)); + gpr.MarkGPRDirty(inst.dest, gpr.IsNormalized32(inst.src1)); } } else { gpr.MapDirtyIn(inst.dest, inst.src1, MapType::AVOID_LOAD_MARK_NORM32); @@ -385,7 +385,7 @@ void RiscVJitBackend::CompIR_Shift(IRInst inst) { if (inst.dest != inst.src1) { gpr.MapDirtyIn(inst.dest, inst.src1); MV(gpr.R(inst.dest), gpr.R(inst.src1)); - gpr.MarkDirty(gpr.R(inst.dest), gpr.IsNormalized32(inst.src1)); + gpr.MarkGPRDirty(inst.dest, gpr.IsNormalized32(inst.src1)); } } else if (cpu_info.RiscV_Zbb) { gpr.MapDirtyIn(inst.dest, inst.src1, MapType::AVOID_LOAD_MARK_NORM32); @@ -434,7 +434,7 @@ void RiscVJitBackend::CompIR_Compare(IRInst inst) { LI(SCRATCH2, (int32_t)inst.constant); SLT(gpr.R(inst.dest), lhs, SCRATCH2); } - gpr.MarkDirty(gpr.R(inst.dest), true); + gpr.MarkGPRDirty(inst.dest, true); } break; @@ -514,14 +514,14 @@ void RiscVJitBackend::CompIR_CondAssign(IRInst inst) { NormalizeSrc12(inst, &lhs, &rhs, SCRATCH1, SCRATCH2, true); MAX(gpr.R(inst.dest), lhs, rhs); // Because we had to normalize the inputs, the output is normalized. - gpr.MarkDirty(gpr.R(inst.dest), true); + gpr.MarkGPRDirty(inst.dest, true); } else { CompIR_Generic(inst); } } else if (inst.dest != inst.src1) { gpr.MapDirtyIn(inst.dest, inst.src1); MV(gpr.R(inst.dest), gpr.R(inst.src1)); - gpr.MarkDirty(gpr.R(inst.dest), gpr.IsNormalized32(inst.src1)); + gpr.MarkGPRDirty(inst.dest, gpr.IsNormalized32(inst.src1)); } break; @@ -532,14 +532,14 @@ void RiscVJitBackend::CompIR_CondAssign(IRInst inst) { NormalizeSrc12(inst, &lhs, &rhs, SCRATCH1, SCRATCH2, true); MIN(gpr.R(inst.dest), lhs, rhs); // Because we had to normalize the inputs, the output is normalized. - gpr.MarkDirty(gpr.R(inst.dest), true); + gpr.MarkGPRDirty(inst.dest, true); } else { CompIR_Generic(inst); } } else if (inst.dest != inst.src1) { gpr.MapDirtyIn(inst.dest, inst.src1); MV(gpr.R(inst.dest), gpr.R(inst.src1)); - gpr.MarkDirty(gpr.R(inst.dest), gpr.IsNormalized32(inst.src1)); + gpr.MarkGPRDirty(inst.dest, gpr.IsNormalized32(inst.src1)); } break; @@ -556,25 +556,25 @@ void RiscVJitBackend::CompIR_HiLo(IRInst inst) { case IROp::MtLo: gpr.MapDirtyIn(IRREG_LO, inst.src1); MV(gpr.R(IRREG_LO), gpr.R(inst.src1)); - gpr.MarkDirty(gpr.R(IRREG_LO), gpr.IsNormalized32(inst.src1)); + gpr.MarkGPRDirty(IRREG_LO, gpr.IsNormalized32(inst.src1)); break; case IROp::MtHi: gpr.MapDirtyIn(IRREG_HI, inst.src1); MV(gpr.R(IRREG_HI), gpr.R(inst.src1)); - gpr.MarkDirty(gpr.R(IRREG_HI), gpr.IsNormalized32(inst.src1)); + gpr.MarkGPRDirty(IRREG_HI, gpr.IsNormalized32(inst.src1)); break; case IROp::MfLo: gpr.MapDirtyIn(inst.dest, IRREG_LO); MV(gpr.R(inst.dest), gpr.R(IRREG_LO)); - gpr.MarkDirty(gpr.R(inst.dest), gpr.IsNormalized32(IRREG_LO)); + gpr.MarkGPRDirty(inst.dest, gpr.IsNormalized32(IRREG_LO)); break; case IROp::MfHi: gpr.MapDirtyIn(inst.dest, IRREG_HI); MV(gpr.R(inst.dest), gpr.R(IRREG_HI)); - gpr.MarkDirty(gpr.R(inst.dest), gpr.IsNormalized32(IRREG_HI)); + gpr.MarkGPRDirty(inst.dest, gpr.IsNormalized32(IRREG_HI)); break; default: @@ -612,7 +612,7 @@ void RiscVJitBackend::CompIR_Mult(IRInst inst) { }; auto splitMulResult = [&] { SRAI(gpr.R(IRREG_HI), gpr.R(IRREG_LO), 32); - gpr.MarkDirty(gpr.R(IRREG_HI), true); + gpr.MarkGPRDirty(IRREG_HI, true); }; RiscVReg lhs = INVALID_REG; diff --git a/Core/MIPS/RiscV/RiscVCompSystem.cpp b/Core/MIPS/RiscV/RiscVCompSystem.cpp index f727979b53..ce41e6faf0 100644 --- a/Core/MIPS/RiscV/RiscVCompSystem.cpp +++ b/Core/MIPS/RiscV/RiscVCompSystem.cpp @@ -96,7 +96,7 @@ void RiscVJitBackend::CompIR_Transfer(IRInst inst) { case IROp::SetCtrlVFPUReg: gpr.MapDirtyIn(IRREG_VFPU_CTRL_BASE + inst.dest, inst.src1); MV(gpr.R(IRREG_VFPU_CTRL_BASE + inst.dest), gpr.R(inst.src1)); - gpr.MarkDirty(gpr.R(IRREG_VFPU_CTRL_BASE + inst.dest), gpr.IsNormalized32(inst.src1)); + gpr.MarkGPRDirty(IRREG_VFPU_CTRL_BASE + inst.dest, gpr.IsNormalized32(inst.src1)); break; case IROp::SetCtrlVFPUFReg: @@ -113,7 +113,7 @@ void RiscVJitBackend::CompIR_Transfer(IRInst inst) { case IROp::FpCondToReg: gpr.MapDirtyIn(inst.dest, IRREG_FPCOND); MV(gpr.R(inst.dest), gpr.R(IRREG_FPCOND)); - gpr.MarkDirty(gpr.R(inst.dest), gpr.IsNormalized32(IRREG_FPCOND)); + gpr.MarkGPRDirty(inst.dest, gpr.IsNormalized32(IRREG_FPCOND)); break; case IROp::FpCtrlFromReg: @@ -153,7 +153,7 @@ void RiscVJitBackend::CompIR_Transfer(IRInst inst) { case IROp::VfpuCtrlToReg: gpr.MapDirtyIn(inst.dest, IRREG_VFPU_CTRL_BASE + inst.src1); MV(gpr.R(inst.dest), gpr.R(IRREG_VFPU_CTRL_BASE + inst.src1)); - gpr.MarkDirty(gpr.R(inst.dest), gpr.IsNormalized32(IRREG_VFPU_CTRL_BASE + inst.src1)); + gpr.MarkGPRDirty(inst.dest, gpr.IsNormalized32(IRREG_VFPU_CTRL_BASE + inst.src1)); break; case IROp::FMovFromGPR: diff --git a/Core/MIPS/RiscV/RiscVRegCache.cpp b/Core/MIPS/RiscV/RiscVRegCache.cpp index b836e1dd81..484628e84d 100644 --- a/Core/MIPS/RiscV/RiscVRegCache.cpp +++ b/Core/MIPS/RiscV/RiscVRegCache.cpp @@ -137,35 +137,6 @@ bool RiscVRegCache::IsNormalized32(IRReg mipsReg) { return false; } -void RiscVRegCache::MarkDirty(RiscVReg reg, bool andNormalized32) { - // Can't mark X0 dirty. - _dbg_assert_(reg > X0 && reg <= X31); - nr[reg].isDirty = true; - nr[reg].normalized32 = andNormalized32; - // If reg is written to, pointerification is lost. - nr[reg].pointerified = false; - if (nr[reg].mipsReg != IRREG_INVALID) { - RegStatusMIPS &m = mr[nr[reg].mipsReg]; - if (m.loc == MIPSLoc::REG_AS_PTR || m.loc == MIPSLoc::REG_IMM) { - m.loc = MIPSLoc::REG; - m.imm = -1; - } - _dbg_assert_(m.loc == MIPSLoc::REG); - } -} - -void RiscVRegCache::MarkPtrDirty(RiscVReg reg) { - // Can't mark X0 dirty. - _dbg_assert_(reg > X0 && reg <= X31); - _dbg_assert_(!nr[reg].normalized32); - nr[reg].isDirty = true; - if (nr[reg].mipsReg != IRREG_INVALID) { - _dbg_assert_(mr[nr[reg].mipsReg].loc == MIPSLoc::REG_AS_PTR); - } else { - _dbg_assert_(nr[reg].pointerified); - } -} - RiscVGen::RiscVReg RiscVRegCache::Normalize32(IRReg mipsReg, RiscVGen::RiscVReg destReg) { _dbg_assert_(IsValidGPR(mipsReg)); _dbg_assert_(destReg == INVALID_REG || (destReg > X0 && destReg <= X31)); diff --git a/Core/MIPS/RiscV/RiscVRegCache.h b/Core/MIPS/RiscV/RiscVRegCache.h index 28618e040d..960bd607aa 100644 --- a/Core/MIPS/RiscV/RiscVRegCache.h +++ b/Core/MIPS/RiscV/RiscVRegCache.h @@ -72,8 +72,6 @@ public: bool IsNormalized32(IRReg reg); - void MarkDirty(RiscVGen::RiscVReg reg, bool andNormalized32 = false); - void MarkPtrDirty(RiscVGen::RiscVReg reg); // Copies to another reg if specified, otherwise same reg. RiscVGen::RiscVReg Normalize32(IRReg reg, RiscVGen::RiscVReg destReg = RiscVGen::INVALID_REG); void MapIn(IRReg rs); From 83946eeef800b4063d6c190f280d555c74ef9477 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 17 Aug 2023 23:02:59 -0700 Subject: [PATCH 5/8] Core: Reduce log spam on exit. This doesn't need to be INFO. --- Core/HLE/sceKernel.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Core/HLE/sceKernel.cpp b/Core/HLE/sceKernel.cpp index 60317157d9..40fc161a01 100644 --- a/Core/HLE/sceKernel.cpp +++ b/Core/HLE/sceKernel.cpp @@ -523,9 +523,9 @@ void KernelObjectPool::List() { char buffer[256]; if (pool[i]) { pool[i]->GetQuickInfo(buffer, sizeof(buffer)); - INFO_LOG(SCEKERNEL, "KO %i: %s \"%s\": %s", i + handleOffset, pool[i]->GetTypeName(), pool[i]->GetName(), buffer); + DEBUG_LOG(SCEKERNEL, "KO %i: %s \"%s\": %s", i + handleOffset, pool[i]->GetTypeName(), pool[i]->GetName(), buffer); } else { - strcpy(buffer, "WTF? Zero Pointer"); + ERROR_LOG(SCEKERNEL, "KO %i: bad object", i + handleOffset); } } } From 92f7374c898ef8cd913b0f792a7b3779e45e5aec Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 19 Aug 2023 13:42:43 -0700 Subject: [PATCH 6/8] riscv: Centralize reg mapping itself. --- Core/MIPS/IR/IRRegCache.cpp | 194 ++++++++++++++++ Core/MIPS/IR/IRRegCache.h | 26 ++- Core/MIPS/RiscV/RiscVRegCache.cpp | 319 +++++++-------------------- Core/MIPS/RiscV/RiscVRegCache.h | 24 +- Core/MIPS/RiscV/RiscVRegCacheFPU.cpp | 54 ++--- Core/MIPS/RiscV/RiscVRegCacheFPU.h | 6 +- 6 files changed, 335 insertions(+), 288 deletions(-) diff --git a/Core/MIPS/IR/IRRegCache.cpp b/Core/MIPS/IR/IRRegCache.cpp index 8d9a0a74d0..da79de1c62 100644 --- a/Core/MIPS/IR/IRRegCache.cpp +++ b/Core/MIPS/IR/IRRegCache.cpp @@ -509,6 +509,200 @@ void IRNativeRegCacheBase::FlushNativeReg(IRNativeReg nreg) { nr[nreg].normalized32 = false; } +IRNativeReg IRNativeRegCacheBase::MapNativeReg(MIPSLoc type, IRReg first, int lanes, MIPSMap flags) { + _assert_msg_(first != IRREG_INVALID, "Cannot map invalid register"); + _assert_msg_(lanes >= 1 && lanes <= 4, "Cannot map %d lanes", lanes); + if (first == IRREG_INVALID || lanes < 0) + return -1; + + // Let's see if it's already mapped or we need a new reg. + IRNativeReg nreg = mr[first].nReg; + if (mr[first].isStatic) { + _assert_msg_(nreg != -1, "MapIRReg on static without an nReg?"); + } else { + switch (mr[first].loc) { + case MIPSLoc::REG_IMM: + case MIPSLoc::REG_AS_PTR: + case MIPSLoc::REG: + if (type != MIPSLoc::REG) + nreg = AllocateReg(type); + break; + + case MIPSLoc::FREG: + if (type != MIPSLoc::FREG) + nreg = AllocateReg(type); + break; + + case MIPSLoc::VREG: + if (type != MIPSLoc::VREG) + nreg = AllocateReg(type); + break; + + case MIPSLoc::IMM: + case MIPSLoc::MEM: + nreg = AllocateReg(type); + break; + } + } + + if (nreg != -1) { + // This will handle already mapped and new mappings. + MapNativeReg(type, nreg, first, lanes, flags); + } + + return nreg; +} + +void IRNativeRegCacheBase::MapNativeReg(MIPSLoc type, IRNativeReg nreg, IRReg first, int lanes, MIPSMap flags) { + pendingFlush_ = true; + + // First, try to clean up any lane mismatches. + // It must either be in the same nreg and lane count, or not in an nreg. + for (int i = 0; i < lanes; ++i) { + auto &mreg = mr[first + i]; + if (mreg.nReg != -1) { + // How many lanes is it currently in? + int oldlanes = 0; + for (IRReg m = nr[mreg.nReg].mipsReg; mr[m].nReg == mreg.nReg && m < IRREG_INVALID; ++m) + oldlanes++; + + // We may need to flush if it goes outside or we're initing. + bool mismatch = oldlanes != lanes || mreg.lane != (lanes == 1 ? -1 : i); + if (mismatch) { + _assert_msg_(!mreg.isStatic, "Cannot MapNativeReg a static reg mismatch"); + if ((flags & MIPSMap::NOINIT) != MIPSMap::NOINIT) { + // If we need init, we have to flush mismatches. + // TODO: Do a shuffle if interior only? + // TODO: We may also be motivated to have multiple read-only "views" or an IRReg. + // For example Vec4Scale v0..v3, v0..v3, v3 + FlushNativeReg(mreg.nReg); + } else if (oldlanes != 1) { + // Even if we don't care about the current contents, we can't discard outside. + bool extendsBefore = mreg.lane > i; + bool extendsAfter = i + oldlanes - mreg.lane > lanes; + if (extendsBefore || extendsAfter) + FlushNativeReg(mreg.nReg); + } + } + + // If it's still in a different reg, either discard or possibly transfer. + if (mreg.nReg != -1 && mreg.nReg != nreg) { + _assert_msg_(!mreg.isStatic, "Cannot MapNativeReg a static reg to a new reg"); + if ((flags & MIPSMap::NOINIT) != MIPSMap::NOINIT) { + // We better not be trying to map to a different nreg if it's in one now. + // This might happen on some sort of transfer... + // TODO: Make a direct transfer, i.e. FREG -> VREG? + FlushNativeReg(mreg.nReg); + } else { + DiscardNativeReg(mreg.nReg); + } + } + } + + // If somehow this is an imm and mapping to a multilane native reg (HI/LO?), we store it. + // TODO: Could check the others are imm and be smarter, but seems an unlikely case. + if (mreg.loc == MIPSLoc::IMM && lanes > 1) { + if ((flags & MIPSMap::NOINIT) != MIPSMap::NOINIT) + StoreRegValue(first + i, mreg.imm); + mreg.loc = MIPSLoc::MEM; + if (!mreg.isStatic) + mreg.nReg = -1; + mreg.imm = 0; + } + } + + // Double check: everything should be in the same loc for multilane now. + for (int i = 1; i < lanes; ++i) { + _assert_(mr[first + i].loc == mr[first].loc); + } + + bool markDirty = (flags & MIPSMap::DIRTY) == MIPSMap::DIRTY; + if (mr[first].nReg != nreg) { + nr[nreg].isDirty = markDirty; + nr[nreg].pointerified = false; + nr[nreg].normalized32 = (flags & MIPSMap::MARK_NORM32) == MIPSMap::MARK_NORM32; + } + + // Alright, now to actually map. + if ((flags & MIPSMap::NOINIT) != MIPSMap::NOINIT) { + if (first == MIPS_REG_ZERO) { + _assert_msg_(lanes == 1, "Cannot use MIPS_REG_ZERO in multilane"); + SetNativeRegValue(nreg, 0); + mr[first].loc = MIPSLoc::REG_IMM; + mr[first].imm = 0; + } else { + // Note: we checked above, everything is in the same loc if multilane. + switch (mr[first].loc) { + case MIPSLoc::IMM: + _assert_msg_(lanes == 1, "Not handling multilane imm here"); + SetNativeRegValue(nreg, mr[first].imm); + // IMM is always dirty unless static. + if (!mr[first].isStatic) + nr[nreg].isDirty = true; + + // If we are mapping dirty, it means we're gonna overwrite. + // So the imm value is no longer valid. + if ((flags & MIPSMap::DIRTY) == MIPSMap::DIRTY) + mr[first].loc = MIPSLoc::REG; + else + mr[first].loc = MIPSLoc::REG_IMM; + break; + + case MIPSLoc::REG_IMM: + // If it's not dirty, we can keep it. + _assert_msg_(type == MIPSLoc::REG, "Should have flushed this reg already"); + if ((flags & MIPSMap::DIRTY) == MIPSMap::DIRTY || lanes != 1) + mr[first].loc = MIPSLoc::REG; + for (int i = 1; i < lanes; ++i) + mr[first + i].loc = type; + break; + + case MIPSLoc::REG_AS_PTR: + _assert_msg_(lanes == 1, "Should have flushed before getting here"); + _assert_msg_(type == MIPSLoc::REG, "Should have flushed this reg already"); + AdjustNativeRegAsPtr(nreg, false); + for (int i = 0; i < lanes; ++i) + mr[first + i].loc = type; + break; + + case MIPSLoc::REG: + case MIPSLoc::FREG: + case MIPSLoc::VREG: + // Might be flipping from FREG -> VREG or something. + _assert_msg_(type == mr[first].loc, "Should have flushed this reg already"); + for (int i = 0; i < lanes; ++i) + mr[first + i].loc = type; + break; + + case MIPSLoc::MEM: + for (int i = 0; i < lanes; ++i) + mr[first + i].loc = type; + LoadNativeReg(nreg, first, lanes); + break; + } + } + } else { + for (int i = 0; i < lanes; ++i) + mr[first + i].loc = type; + } + + for (int i = 0; i < lanes; ++i) { + mr[first + i].nReg = nreg; + mr[first + i].lane = lanes == 1 ? -1 : i; + } + + nr[nreg].mipsReg = first; + + if (markDirty) { + nr[nreg].isDirty = true; + nr[nreg].pointerified = false; + nr[nreg].normalized32 = (flags & MIPSMap::MARK_NORM32) == MIPSMap::MARK_NORM32; + _assert_(first != MIPS_REG_ZERO); + } else if ((flags & MIPSMap::MARK_NORM32) == MIPSMap::MARK_NORM32) { + nr[nreg].normalized32 = true; + } +} + void IRNativeRegCacheBase::AdjustNativeRegAsPtr(IRNativeReg nreg, bool state) { // This isn't necessary to implement if REG_AS_PTR is unsupported entirely. _assert_msg_(false, "AdjustNativeRegAsPtr unimplemented"); diff --git a/Core/MIPS/IR/IRRegCache.h b/Core/MIPS/IR/IRRegCache.h index f5301f8197..a333b300ef 100644 --- a/Core/MIPS/IR/IRRegCache.h +++ b/Core/MIPS/IR/IRRegCache.h @@ -77,6 +77,20 @@ private: IRWriter *ir_; }; +// Initing is the default so the flag is reversed. +enum class MIPSMap { + INIT = 0, + DIRTY = 1, + NOINIT = 2 | DIRTY, + MARK_NORM32 = 4, +}; +static inline MIPSMap operator |(const MIPSMap &lhs, const MIPSMap &rhs) { + return MIPSMap((int)lhs | (int)rhs); +} +static inline MIPSMap operator &(const MIPSMap &lhs, const MIPSMap &rhs) { + return MIPSMap((int)lhs & (int)rhs); +} + class IRNativeRegCacheBase { protected: enum class MIPSLoc { @@ -90,7 +104,7 @@ protected: REG_IMM, // In a native floating-point reg. FREG, - // In a native vector reg. + // In a native vector reg. Note: if FREGs and VREGS overlap, just use FREG. VREG, // Away in memory (in the mips context struct.) MEM, @@ -183,7 +197,17 @@ protected: virtual void DiscardNativeReg(IRNativeReg nreg); virtual void FlushNativeReg(IRNativeReg nreg); virtual void AdjustNativeRegAsPtr(IRNativeReg nreg, bool state); + virtual void MapNativeReg(MIPSLoc type, IRNativeReg nreg, IRReg first, int lanes, MIPSMap flags); + virtual IRNativeReg MapNativeReg(MIPSLoc type, IRReg first, int lanes, MIPSMap flags); + + // Load data from memory (possibly multiple lanes) into a native reg. + virtual void LoadNativeReg(IRNativeReg nreg, IRReg first, int lanes) = 0; + // Store data in a native reg back into memory. virtual void StoreNativeReg(IRNativeReg nreg, IRReg first, int lanes) = 0; + // Set a native reg to a specific integer value. + virtual void SetNativeRegValue(IRNativeReg nreg, uint32_t imm) = 0; + // Store the imm value for a reg to memory (not currently in a native reg.) + virtual void StoreRegValue(IRReg mreg, uint32_t imm) = 0; void SetSpillLockIRIndex(IRReg reg, IRReg reg2, IRReg reg3, IRReg reg4, int offset, int index); diff --git a/Core/MIPS/RiscV/RiscVRegCache.cpp b/Core/MIPS/RiscV/RiscVRegCache.cpp index 484628e84d..67f133db0a 100644 --- a/Core/MIPS/RiscV/RiscVRegCache.cpp +++ b/Core/MIPS/RiscV/RiscVRegCache.cpp @@ -171,7 +171,7 @@ RiscVGen::RiscVReg RiscVRegCache::Normalize32(IRReg mipsReg, RiscVGen::RiscVReg if (destReg == INVALID_REG) { // If we can pointerify, SEXT_W will be enough. if (!jo_->enablePointerify) - emit_->SUB((RiscVReg)mr[mipsReg].nReg, (RiscVReg)mr[mipsReg].nReg, MEMBASEREG); + AdjustNativeRegAsPtr(mr[mipsReg].nReg, false); emit_->SEXT_W((RiscVReg)mr[mipsReg].nReg, (RiscVReg)mr[mipsReg].nReg); mr[mipsReg].loc = MIPSLoc::REG; nr[mr[mipsReg].nReg].normalized32 = true; @@ -188,76 +188,6 @@ RiscVGen::RiscVReg RiscVRegCache::Normalize32(IRReg mipsReg, RiscVGen::RiscVReg return destReg == INVALID_REG ? reg : destReg; } -void RiscVRegCache::SetRegImm(RiscVReg reg, u64 imm) { - _dbg_assert_(reg != R_ZERO || imm == 0); - _dbg_assert_(reg >= X0 && reg <= X31); - // TODO: Could optimize this more for > 32 bit constants. - emit_->LI(reg, imm); - _dbg_assert_(!nr[reg].pointerified); - nr[reg].normalized32 = imm == (u64)(s64)(s32)imm; -} - -void RiscVRegCache::MapRegTo(RiscVReg reg, IRReg mipsReg, MIPSMap mapFlags) { - _dbg_assert_(reg > X0 && reg <= X31); - _dbg_assert_(IsValidGPR(mipsReg)); - _dbg_assert_(!mr[mipsReg].isStatic); - if (mr[mipsReg].isStatic) { - ERROR_LOG(JIT, "Cannot MapRegTo static register %d", mipsReg); - return; - } - nr[reg].isDirty = (mapFlags & MIPSMap::DIRTY) == MIPSMap::DIRTY; - if ((mapFlags & MIPSMap::NOINIT) != MIPSMap::NOINIT) { - if (mipsReg == MIPS_REG_ZERO) { - // If we get a request to load the zero register, at least we won't spend - // time on a memory access... - emit_->LI(reg, 0); - - // This way, if we SetImm() it, we'll keep it. - mr[mipsReg].loc = MIPSLoc::REG_IMM; - mr[mipsReg].imm = 0; - nr[reg].normalized32 = true; - } else { - switch (mr[mipsReg].loc) { - case MIPSLoc::MEM: - emit_->LW(reg, CTXREG, GetMipsRegOffset(mipsReg)); - mr[mipsReg].loc = MIPSLoc::REG; - nr[reg].normalized32 = true; - break; - case MIPSLoc::IMM: - SetRegImm(reg, mr[mipsReg].imm); - // IMM is always dirty. - nr[reg].isDirty = true; - - // If we are mapping dirty, it means we're gonna overwrite. - // So the imm value is no longer valid. - if ((mapFlags & MIPSMap::DIRTY) == MIPSMap::DIRTY) - mr[mipsReg].loc = MIPSLoc::REG; - else - mr[mipsReg].loc = MIPSLoc::REG_IMM; - break; - case MIPSLoc::REG_IMM: - // If it's not dirty, we can keep it. - if (nr[reg].isDirty) - mr[mipsReg].loc = MIPSLoc::REG; - break; - default: - _assert_msg_(mr[mipsReg].loc != MIPSLoc::REG_AS_PTR, "MapRegTo with a pointer?"); - mr[mipsReg].loc = MIPSLoc::REG; - break; - } - } - } else { - _dbg_assert_(mipsReg != MIPS_REG_ZERO); - _dbg_assert_(nr[reg].isDirty); - mr[mipsReg].loc = MIPSLoc::REG; - } - nr[reg].mipsReg = mipsReg; - nr[reg].pointerified = false; - if (nr[reg].isDirty) - nr[reg].normalized32 = (mapFlags & MIPSMap::MARK_NORM32) == MIPSMap::MARK_NORM32; - mr[mipsReg].nReg = reg; -} - RiscVReg RiscVRegCache::TryMapTempImm(IRReg r) { _dbg_assert_(IsValidGPR(r)); // If already mapped, no need for a temporary. @@ -293,103 +223,14 @@ RiscVReg RiscVRegCache::GetAndLockTempR() { RiscVReg RiscVRegCache::MapReg(IRReg mipsReg, MIPSMap mapFlags) { _dbg_assert_(IsValidGPR(mipsReg)); - // TODO: Optimization to force HI/LO to be combined? - if (mipsReg == IRREG_INVALID) { ERROR_LOG(JIT, "Cannot map invalid register"); return INVALID_REG; } - RiscVReg riscvReg = (RiscVReg)mr[mipsReg].nReg; - - if (mr[mipsReg].isStatic) { - _dbg_assert_(riscvReg != INVALID_REG); - if (riscvReg == INVALID_REG) { - ERROR_LOG(JIT, "MapReg on statically mapped reg %d failed - riscvReg got lost", mipsReg); - } - if (mr[mipsReg].loc == MIPSLoc::IMM) { - // Back into the register, with or without the imm value. - // If noinit, the MAP_DIRTY check below will take care of the rest. - if ((mapFlags & MIPSMap::NOINIT) != MIPSMap::NOINIT) { - // This may set normalized32 to true. - SetRegImm(riscvReg, mr[mipsReg].imm); - mr[mipsReg].loc = MIPSLoc::REG_IMM; - nr[riscvReg].pointerified = false; - } - if ((mapFlags & MIPSMap::MARK_NORM32) == MIPSMap::MARK_NORM32) - nr[riscvReg].normalized32 = true; - } else if (mr[mipsReg].loc == MIPSLoc::REG_AS_PTR) { - // Was mapped as pointer, now we want it mapped as a value, presumably to - // add or subtract stuff to it. - if ((mapFlags & MIPSMap::NOINIT) != MIPSMap::NOINIT) { -#ifdef MASKED_PSP_MEMORY - _dbg_assert_(!nr[riscvReg].isDirty && (mapFlags & MIPSMap::DIRTY) != MIPSMap::DIRTY); -#endif - emit_->SUB(riscvReg, riscvReg, MEMBASEREG); - } - mr[mipsReg].loc = MIPSLoc::REG; - nr[riscvReg].normalized32 = false; - } - // Erasing the imm on dirty (necessary since otherwise we will still think it's ML_RVREG_IMM and return - // true for IsImm and calculate crazily wrong things). /unknown - if ((mapFlags & MIPSMap::DIRTY) == MIPSMap::DIRTY) { - // As we are dirty, can't keep RVREG_IMM, we will quickly drift out of sync - mr[mipsReg].loc = MIPSLoc::REG; - nr[riscvReg].pointerified = false; - nr[riscvReg].isDirty = true; - nr[riscvReg].normalized32 = (mapFlags & MIPSMap::MARK_NORM32) == MIPSMap::MARK_NORM32; - } else if ((mapFlags & MIPSMap::MARK_NORM32) == MIPSMap::MARK_NORM32) { - nr[riscvReg].normalized32 = true; - } - return (RiscVReg)mr[mipsReg].nReg; - } - - // Let's see if it's already mapped. If so we just need to update the dirty flag. - // We don't need to check for ML_NOINIT because we assume that anyone who maps - // with that flag immediately writes a "known" value to the register. - if (mr[mipsReg].loc == MIPSLoc::REG || mr[mipsReg].loc == MIPSLoc::REG_IMM) { - _dbg_assert_(riscvReg != INVALID_REG && nr[riscvReg].mipsReg == mipsReg); - if (nr[riscvReg].mipsReg != mipsReg) { - ERROR_LOG_REPORT(JIT, "Register mapping out of sync! %i", mipsReg); - } - if ((mapFlags & MIPSMap::DIRTY) == MIPSMap::DIRTY) { - // Mapping dirty means the old imm value is invalid. - mr[mipsReg].loc = MIPSLoc::REG; - nr[riscvReg].isDirty = true; - // If reg is written to, pointerification is lost. - nr[riscvReg].pointerified = false; - nr[riscvReg].normalized32 = (mapFlags & MIPSMap::MARK_NORM32) == MIPSMap::MARK_NORM32; - } else if ((mapFlags & MIPSMap::MARK_NORM32) == MIPSMap::MARK_NORM32) { - nr[riscvReg].normalized32 = true; - } - - return (RiscVReg)mr[mipsReg].nReg; - } else if (mr[mipsReg].loc == MIPSLoc::REG_AS_PTR) { - // Was mapped as pointer, now we want it mapped as a value, presumably to - // add or subtract stuff to it. - if ((mapFlags & MIPSMap::NOINIT) != MIPSMap::NOINIT) { -#ifdef MASKED_PSP_MEMORY - _dbg_assert_(!nr[riscvReg].isDirty && (mapFlags & MAP_DIRTY) == 0); -#endif - emit_->SUB(riscvReg, riscvReg, MEMBASEREG); - } - mr[mipsReg].loc = MIPSLoc::REG; - if ((mapFlags & MIPSMap::DIRTY) == MIPSMap::DIRTY) { - nr[riscvReg].isDirty = true; - } - // Let's always set this false, the SUB won't normalize. - nr[riscvReg].normalized32 = false; - return (RiscVReg)mr[mipsReg].nReg; - } - // Okay, not mapped, so we need to allocate an RV register. - RiscVReg reg = (RiscVReg)AllocateReg(MIPSLoc::REG); - if (reg != INVALID_REG) { - // Grab it, and load the value into it (if requested). - MapRegTo(reg, mipsReg, mapFlags); - } - - return reg; + IRNativeReg nreg = MapNativeReg(MIPSLoc::REG, mipsReg, 1, mapFlags); + return (RiscVReg)nreg; } RiscVReg RiscVRegCache::MapRegAsPointer(IRReg reg) { @@ -413,10 +254,10 @@ RiscVReg RiscVRegCache::MapRegAsPointer(IRReg reg) { if (!jo_->enablePointerify) { // Convert to a pointer by adding the base and clearing off the top bits. // If SP, we can probably avoid the top bit clear, let's play with that later. - AddMemBase(riscvReg); + AdjustNativeRegAsPtr(mr[reg].nReg, true); mr[reg].loc = MIPSLoc::REG_AS_PTR; } else if (!nr[riscvReg].pointerified) { - AddMemBase(riscvReg); + AdjustNativeRegAsPtr(mr[reg].nReg, true); nr[riscvReg].pointerified = true; } nr[riscvReg].normalized32 = false; @@ -426,28 +267,6 @@ RiscVReg RiscVRegCache::MapRegAsPointer(IRReg reg) { return riscvReg; } -void RiscVRegCache::AddMemBase(RiscVGen::RiscVReg reg) { - _assert_(reg >= X0 && reg <= X31); -#ifdef MASKED_PSP_MEMORY - // This destroys the value... - _dbg_assert_(!nr[reg].isDirty); - emit_->SLLIW(reg, reg, 2); - emit_->SRLIW(reg, reg, 2); - emit_->ADD(reg, reg, MEMBASEREG); -#else - // Clear the top bits to be safe. - if (cpu_info.RiscV_Zba) { - emit_->ADD_UW(reg, reg, MEMBASEREG); - } else { - _assert_(XLEN == 64); - emit_->SLLI(reg, reg, 32); - emit_->SRLI(reg, reg, 32); - emit_->ADD(reg, reg, MEMBASEREG); - } -#endif - nr[reg].normalized32 = false; -} - void RiscVRegCache::MapIn(IRReg rs) { MapReg(rs); } @@ -503,8 +322,26 @@ void RiscVRegCache::MapDirtyDirtyInIn(IRReg rd1, IRReg rd2, IRReg rs, IRReg rt, void RiscVRegCache::AdjustNativeRegAsPtr(IRNativeReg nreg, bool state) { RiscVReg r = (RiscVReg)(X0 + nreg); + _assert_(r >= X0 && r <= X31); if (state) { - AddMemBase(r); +#ifdef MASKED_PSP_MEMORY + // This destroys the value... + _dbg_assert_(!nr[nreg].isDirty); + emit_->SLLIW(r, r, 2); + emit_->SRLIW(r, r, 2); + emit_->ADD(r, r, MEMBASEREG); +#else + // Clear the top bits to be safe. + if (cpu_info.RiscV_Zba) { + emit_->ADD_UW(r, r, MEMBASEREG); + } else { + _assert_(XLEN == 64); + emit_->SLLI(r, r, 32); + emit_->SRLI(r, r, 32); + emit_->ADD(r, r, MEMBASEREG); + } +#endif + nr[nreg].normalized32 = false; } else { #ifdef MASKED_PSP_MEMORY _dbg_assert_(!nr[nreg].isDirty); @@ -514,6 +351,21 @@ void RiscVRegCache::AdjustNativeRegAsPtr(IRNativeReg nreg, bool state) { } } +void RiscVRegCache::LoadNativeReg(IRNativeReg nreg, IRReg first, int lanes) { + RiscVReg r = (RiscVReg)(X0 + nreg); + _dbg_assert_(r > X0 && r <= X31); + _dbg_assert_(first != MIPS_REG_ZERO); + // Multilane not yet supported. + _assert_(lanes == 1 || (lanes == 2 && first == IRREG_LO)); + if (lanes == 1) + emit_->LW(r, CTXREG, GetMipsRegOffset(first)); + else if (lanes == 2) + emit_->LD(r, CTXREG, GetMipsRegOffset(first)); + else + _assert_(false); + nr[nreg].normalized32 = true; +} + void RiscVRegCache::StoreNativeReg(IRNativeReg nreg, IRReg first, int lanes) { RiscVReg r = (RiscVReg)(X0 + nreg); _dbg_assert_(r > X0 && r <= X31); @@ -529,6 +381,44 @@ void RiscVRegCache::StoreNativeReg(IRNativeReg nreg, IRReg first, int lanes) { _assert_(false); } +void RiscVRegCache::SetNativeRegValue(IRNativeReg nreg, uint32_t imm) { + RiscVReg r = (RiscVReg)(X0 + nreg); + if (r == R_ZERO && imm == 0) + return; + _dbg_assert_(r > X0 && r <= X31); + emit_->LI(r, (int32_t)imm); + + // We always use 32-bit immediates, so this is normalized now. + nr[nreg].normalized32 = true; +} + +void RiscVRegCache::StoreRegValue(IRReg mreg, uint32_t imm) { + _assert_(mreg != MIPS_REG_ZERO); + // Try to optimize using a different reg. + RiscVReg storeReg = INVALID_REG; + + // Zero is super easy. + if (imm == 0) { + storeReg = R_ZERO; + } else { + // Could we get lucky? Check for an exact match in another rvreg. + for (int i = 0; i < TOTAL_MAPPABLE_IRREGS; ++i) { + if (mr[i].loc == MIPSLoc::REG_IMM && mr[i].imm == imm) { + // Awesome, let's just store this reg. + storeReg = (RiscVReg)mr[i].nReg; + break; + } + } + + if (storeReg == INVALID_REG) { + emit_->LI(SCRATCH1, imm); + storeReg = SCRATCH1; + } + } + + emit_->SW(storeReg, CTXREG, GetMipsRegOffset(mreg)); +} + void RiscVRegCache::DiscardR(IRReg mipsReg) { _dbg_assert_(IsValidGPRNoZero(mipsReg)); if (mr[mipsReg].isStatic) { @@ -545,41 +435,6 @@ void RiscVRegCache::DiscardR(IRReg mipsReg) { } } -RiscVReg RiscVRegCache::RiscVRegForFlush(IRReg r) { - _dbg_assert_(IsValidGPR(r)); - if (mr[r].isStatic) - return INVALID_REG; // No flushing needed - - switch (mr[r].loc) { - case MIPSLoc::IMM: - if (r == MIPS_REG_ZERO) { - return INVALID_REG; - } - // Zero is super easy. - if (mr[r].imm == 0) { - return R_ZERO; - } - // Could we get lucky? Check for an exact match in another rvreg. - for (int i = 0; i < TOTAL_MAPPABLE_IRREGS; ++i) { - if (mr[i].loc == MIPSLoc::REG_IMM && mr[i].imm == mr[r].imm) { - // Awesome, let's just store this reg. - return (RiscVReg)mr[i].nReg; - } - } - return INVALID_REG; - - case MIPSLoc::REG: - case MIPSLoc::REG_IMM: - case MIPSLoc::REG_AS_PTR: - case MIPSLoc::MEM: - return INVALID_REG; - - default: - ERROR_LOG_REPORT(JIT, "RiscVRegForFlush: MipsReg %d with invalid location %d", r, (int)mr[r].loc); - return INVALID_REG; - } -} - void RiscVRegCache::FlushR(IRReg r) { _dbg_assert_(IsValidGPRNoZero(r)); if (mr[r].isStatic) { @@ -590,15 +445,8 @@ void RiscVRegCache::FlushR(IRReg r) { switch (mr[r].loc) { case MIPSLoc::IMM: // IMM is always "dirty". - // TODO: HI/LO optimization? if (r != MIPS_REG_ZERO) { - // Try to optimize using a different reg. - RiscVReg storeReg = RiscVRegForFlush(r); - if (storeReg == INVALID_REG) { - SetRegImm(SCRATCH1, mr[r].imm); - storeReg = SCRATCH1; - } - emit_->SW(storeReg, CTXREG, GetMipsRegOffset(r)); + StoreRegValue(r, mr[r].imm); mr[r].loc = MIPSLoc::MEM; mr[r].nReg = (int)INVALID_REG; mr[r].imm = -1; @@ -636,10 +484,10 @@ void RiscVRegCache::FlushAll() { IRReg mipsReg = IRReg(i); if (mr[i].isStatic) { RiscVReg riscvReg = (RiscVReg)mr[i].nReg; - // Cannot leave any IMMs in registers, not even ML_ARMREG_IMM, can confuse the regalloc later if this flush is mid-block + // Cannot leave any IMMs in registers, not even MIPSLoc::REG_IMM, can confuse the regalloc later if this flush is mid-block // due to an interpreter fallback that changes the register. if (mr[i].loc == MIPSLoc::IMM) { - SetRegImm((RiscVReg)mr[i].nReg, mr[i].imm); + SetNativeRegValue(mr[i].nReg, mr[i].imm); mr[i].loc = MIPSLoc::REG; nr[riscvReg].pointerified = false; } else if (mr[i].loc == MIPSLoc::REG_IMM) { @@ -650,10 +498,7 @@ void RiscVRegCache::FlushAll() { } mr[i].loc = MIPSLoc::REG; } else if (mr[i].loc == MIPSLoc::REG_AS_PTR) { -#ifdef MASKED_PSP_MEMORY - _dbg_assert_(!nr[riscvReg].isDirty); -#endif - emit_->SUB(riscvReg, riscvReg, MEMBASEREG); + AdjustNativeRegAsPtr(mr[i].nReg, false); mr[i].loc = MIPSLoc::REG; } if (i != MIPS_REG_ZERO && mr[i].nReg == INVALID_REG) { @@ -671,7 +516,7 @@ void RiscVRegCache::FlushAll() { if (allocs[i].pointerified && !nr[allocs[i].nr].pointerified && jo_->enablePointerify) { // Re-pointerify _dbg_assert_(mr[allocs[i].mr].loc == MIPSLoc::REG); - AddMemBase((RiscVReg)allocs[i].nr); + AdjustNativeRegAsPtr(allocs[i].nr, true); nr[allocs[i].nr].pointerified = true; } else if (!allocs[i].pointerified) { // If this register got pointerified on the way, mark it as not. diff --git a/Core/MIPS/RiscV/RiscVRegCache.h b/Core/MIPS/RiscV/RiscVRegCache.h index 960bd607aa..00b788c69e 100644 --- a/Core/MIPS/RiscV/RiscVRegCache.h +++ b/Core/MIPS/RiscV/RiscVRegCache.h @@ -35,20 +35,6 @@ const RiscVGen::RiscVReg MEMBASEREG = RiscVGen::X27; const RiscVGen::RiscVReg SCRATCH1 = RiscVGen::X10; const RiscVGen::RiscVReg SCRATCH2 = RiscVGen::X11; -// Initing is the default so the flag is reversed. -enum class MIPSMap { - INIT = 0, - DIRTY = 1, - NOINIT = 2 | DIRTY, - MARK_NORM32 = 4, -}; -static inline MIPSMap operator |(const MIPSMap &lhs, const MIPSMap &rhs) { - return MIPSMap((int)lhs | (int)rhs); -} -static inline MIPSMap operator &(const MIPSMap &lhs, const MIPSMap &rhs) { - return MIPSMap((int)lhs & (int)rhs); -} - enum class MapType { AVOID_LOAD, AVOID_LOAD_MARK_NORM32, @@ -67,7 +53,7 @@ public: RiscVGen::RiscVReg TryMapTempImm(IRReg); // Returns an ARM register containing the requested MIPS register. - RiscVGen::RiscVReg MapReg(IRReg reg, RiscVJitConstants::MIPSMap mapFlags = RiscVJitConstants::MIPSMap::INIT); + RiscVGen::RiscVReg MapReg(IRReg reg, MIPSMap mapFlags = MIPSMap::INIT); RiscVGen::RiscVReg MapRegAsPointer(IRReg reg); bool IsNormalized32(IRReg reg); @@ -99,13 +85,13 @@ protected: const StaticAllocation *GetStaticAllocations(int &count) const override; const int *GetAllocationOrder(MIPSLoc type, int &count, int &base) const override; void AdjustNativeRegAsPtr(IRNativeReg nreg, bool state) override; + + void LoadNativeReg(IRNativeReg nreg, IRReg first, int lanes) override; void StoreNativeReg(IRNativeReg nreg, IRReg first, int lanes) override; + void SetNativeRegValue(IRNativeReg nreg, uint32_t imm) override; + void StoreRegValue(IRReg mreg, uint32_t imm) override; private: - void MapRegTo(RiscVGen::RiscVReg reg, IRReg mipsReg, RiscVJitConstants::MIPSMap mapFlags); - RiscVGen::RiscVReg RiscVRegForFlush(IRReg r); - void SetRegImm(RiscVGen::RiscVReg reg, u64 imm); - void AddMemBase(RiscVGen::RiscVReg reg); int GetMipsRegOffset(IRReg r); RiscVGen::RiscVEmitter *emit_ = nullptr; diff --git a/Core/MIPS/RiscV/RiscVRegCacheFPU.cpp b/Core/MIPS/RiscV/RiscVRegCacheFPU.cpp index 48653f9fcf..f594cf4c03 100644 --- a/Core/MIPS/RiscV/RiscVRegCacheFPU.cpp +++ b/Core/MIPS/RiscV/RiscVRegCacheFPU.cpp @@ -57,36 +57,10 @@ RiscVReg RiscVRegCacheFPU::MapReg(IRReg mipsReg, MIPSMap mapFlags) { _dbg_assert_(IsValidFPR(mipsReg)); _dbg_assert_(mr[mipsReg + 32].loc == MIPSLoc::MEM || mr[mipsReg + 32].loc == MIPSLoc::FREG); - pendingFlush_ = true; - - // Let's see if it's already mapped. If so we just need to update the dirty flag. - // We don't need to check for NOINIT because we assume that anyone who maps - // with that flag immediately writes a "known" value to the register. - if (mr[mipsReg + 32].loc == MIPSLoc::FREG) { - _assert_msg_(nr[mr[mipsReg + 32].nReg].mipsReg == mipsReg + 32, "FPR mapping out of sync, IR=%i", mipsReg); - if ((mapFlags & MIPSMap::DIRTY) == MIPSMap::DIRTY) { - nr[mr[mipsReg + 32].nReg].isDirty = true; - } - return (RiscVReg)(mr[mipsReg + 32].nReg + F0); - } - - // Okay, not mapped, so we need to allocate an RV register. - IRNativeReg nreg = AllocateReg(MIPSLoc::FREG); - if (nreg != -1) { - // That means it's free. Grab it, and load the value into it (if requested). - nr[nreg].isDirty = (mapFlags & MIPSMap::DIRTY) == MIPSMap::DIRTY; - if ((mapFlags & MIPSMap::NOINIT) != MIPSMap::NOINIT) { - if (mr[mipsReg + 32].loc == MIPSLoc::MEM) { - emit_->FL(32, (RiscVReg)(F0 + nreg), CTXREG, GetMipsRegOffset(mipsReg + 32)); - } - } - nr[nreg].mipsReg = mipsReg + 32; - mr[mipsReg + 32].loc = MIPSLoc::FREG; - mr[mipsReg + 32].nReg = nreg; + IRNativeReg nreg = MapNativeReg(MIPSLoc::FREG, mipsReg + 32, 1, mapFlags); + if (nreg != -1) return (RiscVReg)(F0 + nreg); - } - - return (RiscVReg)(F0 + nreg); + return INVALID_REG; } void RiscVRegCacheFPU::MapInIn(IRReg rd, IRReg rs) { @@ -163,6 +137,18 @@ RiscVReg RiscVRegCacheFPU::Map4DirtyInTemp(IRReg rdbase, IRReg rsbase, bool avoi return temp; } +void RiscVRegCacheFPU::LoadNativeReg(IRNativeReg nreg, IRReg first, int lanes) { + RiscVReg r = (RiscVReg)(F0 + nreg); + _dbg_assert_(r >= F0 && r <= F31); + // Multilane not yet supported. + _assert_(lanes == 1); + if (mr[first].loc == MIPSLoc::FREG) { + emit_->FL(32, r, CTXREG, GetMipsRegOffset(first)); + } else { + _assert_msg_(mr[first].loc == MIPSLoc::FREG, "Cannot store this type: %d", (int)mr[first].loc); + } +} + void RiscVRegCacheFPU::StoreNativeReg(IRNativeReg nreg, IRReg first, int lanes) { RiscVReg r = (RiscVReg)(F0 + nreg); _dbg_assert_(r >= F0 && r <= F31); @@ -175,6 +161,14 @@ void RiscVRegCacheFPU::StoreNativeReg(IRNativeReg nreg, IRReg first, int lanes) } } +void RiscVRegCacheFPU::SetNativeRegValue(IRNativeReg nreg, uint32_t imm) { + _assert_msg_(false, "Set float to imm is unsupported"); +} + +void RiscVRegCacheFPU::StoreRegValue(IRReg mreg, uint32_t imm) { + _assert_msg_(false, "Storing imms to floats is unsupported"); +} + void RiscVRegCacheFPU::FlushR(IRReg r) { _dbg_assert_(IsValidFPR(r)); switch (mr[r + 32].loc) { @@ -226,7 +220,7 @@ void RiscVRegCacheFPU::FlushAll() { if (nr[a].isDirty) { _assert_(m != MIPS_REG_INVALID); - emit_->FS(32, (RiscVReg)(F0 + a), CTXREG, GetMipsRegOffset(m)); + StoreNativeReg(a, m, 1); mr[m].loc = MIPSLoc::MEM; mr[m].nReg = (int)INVALID_REG; diff --git a/Core/MIPS/RiscV/RiscVRegCacheFPU.h b/Core/MIPS/RiscV/RiscVRegCacheFPU.h index 1c0b18ccfd..e672295c47 100644 --- a/Core/MIPS/RiscV/RiscVRegCacheFPU.h +++ b/Core/MIPS/RiscV/RiscVRegCacheFPU.h @@ -32,7 +32,7 @@ public: void Init(RiscVGen::RiscVEmitter *emitter); // Returns a RISC-V register containing the requested MIPS register. - RiscVGen::RiscVReg MapReg(IRReg reg, RiscVJitConstants::MIPSMap mapFlags = RiscVJitConstants::MIPSMap::INIT); + RiscVGen::RiscVReg MapReg(IRReg reg, MIPSMap mapFlags = MIPSMap::INIT); void MapInIn(IRReg rd, IRReg rs); void MapDirtyIn(IRReg rd, IRReg rs, bool avoidLoad = true); @@ -51,7 +51,11 @@ public: protected: void SetupInitialRegs() override; const int *GetAllocationOrder(MIPSLoc type, int &count, int &base) const override; + + void LoadNativeReg(IRNativeReg nreg, IRReg first, int lanes) override; void StoreNativeReg(IRNativeReg nreg, IRReg first, int lanes) override; + void SetNativeRegValue(IRNativeReg nreg, uint32_t imm) override; + void StoreRegValue(IRReg mreg, uint32_t imm) override; private: int GetMipsRegOffset(IRReg r); From f3d4bd8c119d5bab35a93ac776de38c93f14fabf Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 19 Aug 2023 15:11:47 -0700 Subject: [PATCH 7/8] riscv: Centralize reg-as-pointer. --- Core/MIPS/IR/IRRegCache.cpp | 31 ++++++++++++++++++++++++++ Core/MIPS/IR/IRRegCache.h | 1 + Core/MIPS/RiscV/RiscVRegCache.cpp | 37 +------------------------------ 3 files changed, 33 insertions(+), 36 deletions(-) diff --git a/Core/MIPS/IR/IRRegCache.cpp b/Core/MIPS/IR/IRRegCache.cpp index da79de1c62..fbabfa6d19 100644 --- a/Core/MIPS/IR/IRRegCache.cpp +++ b/Core/MIPS/IR/IRRegCache.cpp @@ -703,6 +703,37 @@ void IRNativeRegCacheBase::MapNativeReg(MIPSLoc type, IRNativeReg nreg, IRReg fi } } +IRNativeReg IRNativeRegCacheBase::MapNativeRegAsPointer(IRReg gpr) { + _dbg_assert_(IsValidGPRNoZero(gpr)); + + // Already mapped. + if (mr[gpr].loc == MIPSLoc::REG_AS_PTR) { + return mr[gpr].nReg; + } + + IRNativeReg nreg = mr[gpr].nReg; + if (mr[gpr].loc != MIPSLoc::REG && mr[gpr].loc != MIPSLoc::REG_IMM) { + nreg = MapNativeReg(MIPSLoc::REG, gpr, 1, MIPSMap::INIT); + } + + if (mr[gpr].loc == MIPSLoc::REG || mr[gpr].loc == MIPSLoc::REG_IMM) { + // If there was an imm attached, discard it. + mr[gpr].loc = MIPSLoc::REG; + mr[gpr].imm = 0; + + if (!jo_->enablePointerify) { + AdjustNativeRegAsPtr(nreg, true); + mr[gpr].loc = MIPSLoc::REG_AS_PTR; + } else if (!nr[nreg].pointerified) { + AdjustNativeRegAsPtr(nreg, true); + nr[nreg].pointerified = true; + } + } else { + ERROR_LOG(JIT, "MapNativeRegAsPointer: MapNativeReg failed to allocate a register?"); + } + return nreg; +} + void IRNativeRegCacheBase::AdjustNativeRegAsPtr(IRNativeReg nreg, bool state) { // This isn't necessary to implement if REG_AS_PTR is unsupported entirely. _assert_msg_(false, "AdjustNativeRegAsPtr unimplemented"); diff --git a/Core/MIPS/IR/IRRegCache.h b/Core/MIPS/IR/IRRegCache.h index a333b300ef..4ef53a5a94 100644 --- a/Core/MIPS/IR/IRRegCache.h +++ b/Core/MIPS/IR/IRRegCache.h @@ -199,6 +199,7 @@ protected: virtual void AdjustNativeRegAsPtr(IRNativeReg nreg, bool state); virtual void MapNativeReg(MIPSLoc type, IRNativeReg nreg, IRReg first, int lanes, MIPSMap flags); virtual IRNativeReg MapNativeReg(MIPSLoc type, IRReg first, int lanes, MIPSMap flags); + IRNativeReg MapNativeRegAsPointer(IRReg gpr); // Load data from memory (possibly multiple lanes) into a native reg. virtual void LoadNativeReg(IRNativeReg nreg, IRReg first, int lanes) = 0; diff --git a/Core/MIPS/RiscV/RiscVRegCache.cpp b/Core/MIPS/RiscV/RiscVRegCache.cpp index 67f133db0a..b7fa865074 100644 --- a/Core/MIPS/RiscV/RiscVRegCache.cpp +++ b/Core/MIPS/RiscV/RiscVRegCache.cpp @@ -223,48 +223,13 @@ RiscVReg RiscVRegCache::GetAndLockTempR() { RiscVReg RiscVRegCache::MapReg(IRReg mipsReg, MIPSMap mapFlags) { _dbg_assert_(IsValidGPR(mipsReg)); - if (mipsReg == IRREG_INVALID) { - ERROR_LOG(JIT, "Cannot map invalid register"); - return INVALID_REG; - } - // Okay, not mapped, so we need to allocate an RV register. IRNativeReg nreg = MapNativeReg(MIPSLoc::REG, mipsReg, 1, mapFlags); return (RiscVReg)nreg; } RiscVReg RiscVRegCache::MapRegAsPointer(IRReg reg) { - _dbg_assert_(IsValidGPRNoZero(reg)); - - // Already mapped. - if (mr[reg].loc == MIPSLoc::REG_AS_PTR) { - return (RiscVReg)mr[reg].nReg; - } - - RiscVReg riscvReg = INVALID_REG; - if (mr[reg].loc != MIPSLoc::REG && mr[reg].loc != MIPSLoc::REG_IMM) { - riscvReg = MapReg(reg); - } else { - riscvReg = (RiscVReg)mr[reg].nReg; - } - - if (mr[reg].loc == MIPSLoc::REG || mr[reg].loc == MIPSLoc::REG_IMM) { - // If there was an imm attached, discard it. - mr[reg].loc = MIPSLoc::REG; - if (!jo_->enablePointerify) { - // Convert to a pointer by adding the base and clearing off the top bits. - // If SP, we can probably avoid the top bit clear, let's play with that later. - AdjustNativeRegAsPtr(mr[reg].nReg, true); - mr[reg].loc = MIPSLoc::REG_AS_PTR; - } else if (!nr[riscvReg].pointerified) { - AdjustNativeRegAsPtr(mr[reg].nReg, true); - nr[riscvReg].pointerified = true; - } - nr[riscvReg].normalized32 = false; - } else { - ERROR_LOG(JIT, "MapRegAsPointer : MapReg failed to allocate a register?"); - } - return riscvReg; + return (RiscVReg)MapNativeRegAsPointer(reg); } void RiscVRegCache::MapIn(IRReg rs) { From 161465ab6690a8e70b956c75fc96f5952cfef728 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 19 Aug 2023 15:38:31 -0700 Subject: [PATCH 8/8] riscv: Centralize register FlushAll(). --- Core/MIPS/IR/IRRegCache.cpp | 121 ++++++++++++++++++++++++++- Core/MIPS/IR/IRRegCache.h | 6 +- Core/MIPS/RiscV/RiscVRegCache.cpp | 113 +------------------------ Core/MIPS/RiscV/RiscVRegCache.h | 3 - Core/MIPS/RiscV/RiscVRegCacheFPU.cpp | 77 +---------------- Core/MIPS/RiscV/RiscVRegCacheFPU.h | 5 -- 6 files changed, 127 insertions(+), 198 deletions(-) diff --git a/Core/MIPS/IR/IRRegCache.cpp b/Core/MIPS/IR/IRRegCache.cpp index fbabfa6d19..f3b45bef71 100644 --- a/Core/MIPS/IR/IRRegCache.cpp +++ b/Core/MIPS/IR/IRRegCache.cpp @@ -104,7 +104,6 @@ void IRNativeRegCacheBase::Start(MIPSComp::IRBlock *irBlock) { memcpy(nr, nrInitial_, sizeof(nr[0]) * totalNativeRegs_); memcpy(mr, mrInitial_, sizeof(mr)); - pendingFlush_ = false; int numStatics; const StaticAllocation *statics = GetStaticAllocations(numStatics); @@ -509,6 +508,119 @@ void IRNativeRegCacheBase::FlushNativeReg(IRNativeReg nreg) { nr[nreg].normalized32 = false; } +void IRNativeRegCacheBase::DiscardReg(IRReg mreg) { + if (mr[mreg].isStatic) { + DiscardNativeReg(mr[mreg].nReg); + return; + } + switch (mr[mreg].loc) { + case MIPSLoc::IMM: + if (mreg != MIPS_REG_ZERO) { + mr[mreg].loc = MIPSLoc::MEM; + mr[mreg].imm = 0; + } + break; + + case MIPSLoc::REG: + case MIPSLoc::REG_AS_PTR: + case MIPSLoc::REG_IMM: + case MIPSLoc::FREG: + case MIPSLoc::VREG: + DiscardNativeReg(mr[mreg].nReg); + break; + + case MIPSLoc::MEM: + // Already discarded. + break; + } + mr[mreg].spillLockIRIndex = -1; +} + +void IRNativeRegCacheBase::FlushReg(IRReg mreg) { + _assert_msg_(!mr[mreg].isStatic, "Cannot flush static reg %d", mreg); + + switch (mr[mreg].loc) { + case MIPSLoc::IMM: + // IMM is always "dirty". + StoreRegValue(mreg, mr[mreg].imm); + mr[mreg].loc = MIPSLoc::MEM; + mr[mreg].nReg = -1; + mr[mreg].imm = 0; + break; + + case MIPSLoc::REG: + case MIPSLoc::REG_IMM: + case MIPSLoc::REG_AS_PTR: + case MIPSLoc::FREG: + case MIPSLoc::VREG: + // Might be in a native reg with multiple IR regs, flush together. + FlushNativeReg(mr[mreg].nReg); + break; + + case MIPSLoc::MEM: + // Already there, nothing to do. + break; + } +} + +void IRNativeRegCacheBase::FlushAll() { + // Note: make sure not to change the registers when flushing. + // Branching code may expect the native reg to retain its value. + + for (int i = 1; i < TOTAL_MAPPABLE_IRREGS; i++) { + IRReg mipsReg = (IRReg)i; + if (mr[i].isStatic) { + IRNativeReg nreg = mr[i].nReg; + // Cannot leave any IMMs in registers, not even MIPSLoc::REG_IMM. + // Can confuse the regalloc later if this flush is mid-block + // due to an interpreter fallback that changes the register. + if (mr[i].loc == MIPSLoc::IMM) { + SetNativeRegValue(mr[i].nReg, mr[i].imm); + _assert_(IsValidGPR(mipsReg)); + mr[i].loc = MIPSLoc::REG; + nr[nreg].pointerified = false; + } else if (mr[i].loc == MIPSLoc::REG_IMM) { + // The register already contains the immediate. + if (nr[nreg].pointerified) { + ERROR_LOG(JIT, "RVREG_IMM but pointerified. Wrong."); + nr[nreg].pointerified = false; + } + mr[i].loc = MIPSLoc::REG; + } else if (mr[i].loc == MIPSLoc::REG_AS_PTR) { + AdjustNativeRegAsPtr(mr[i].nReg, false); + mr[i].loc = MIPSLoc::REG; + } + _assert_(mr[i].nReg != -1); + } else if (mr[i].loc != MIPSLoc::MEM) { + FlushReg(mipsReg); + } + } + + int count = 0; + const StaticAllocation *allocs = GetStaticAllocations(count); + for (int i = 0; i < count; i++) { + if (allocs[i].pointerified && !nr[allocs[i].nr].pointerified && jo_->enablePointerify) { + // Re-pointerify + if (mr[allocs[i].mr].loc == MIPSLoc::REG_IMM) + mr[allocs[i].mr].loc = MIPSLoc::REG; + _dbg_assert_(mr[allocs[i].mr].loc == MIPSLoc::REG); + AdjustNativeRegAsPtr(allocs[i].nr, true); + nr[allocs[i].nr].pointerified = true; + } else if (!allocs[i].pointerified) { + // If this register got pointerified on the way, mark it as not. + // This is so that after save/reload (like in an interpreter fallback), + // it won't be regarded as such, as it may no longer be. + nr[allocs[i].nr].pointerified = false; + } + } + // Sanity check + for (int i = 0; i < totalNativeRegs_; i++) { + if (nr[i].mipsReg != IRREG_INVALID && !mr[nr[i].mipsReg].isStatic) { + ERROR_LOG_REPORT(JIT, "Flush fail: nr[%i].mipsReg=%i", i, nr[i].mipsReg); + } + } +} + IRNativeReg IRNativeRegCacheBase::MapNativeReg(MIPSLoc type, IRReg first, int lanes, MIPSMap flags) { _assert_msg_(first != IRREG_INVALID, "Cannot map invalid register"); _assert_msg_(lanes >= 1 && lanes <= 4, "Cannot map %d lanes", lanes); @@ -554,8 +666,6 @@ IRNativeReg IRNativeRegCacheBase::MapNativeReg(MIPSLoc type, IRReg first, int la } void IRNativeRegCacheBase::MapNativeReg(MIPSLoc type, IRNativeReg nreg, IRReg first, int lanes, MIPSMap flags) { - pendingFlush_ = true; - // First, try to clean up any lane mismatches. // It must either be in the same nreg and lane count, or not in an nreg. for (int i = 0; i < lanes; ++i) { @@ -739,6 +849,11 @@ void IRNativeRegCacheBase::AdjustNativeRegAsPtr(IRNativeReg nreg, bool state) { _assert_msg_(false, "AdjustNativeRegAsPtr unimplemented"); } +int IRNativeRegCacheBase::GetMipsRegOffset(IRReg r) { + _dbg_assert_(IsValidGPR(r) || (r >= 32 && IsValidFPR(r - 32))); + return r * 4; +} + bool IRNativeRegCacheBase::IsValidGPR(IRReg r) const { // See MIPSState for these offsets. diff --git a/Core/MIPS/IR/IRRegCache.h b/Core/MIPS/IR/IRRegCache.h index 4ef53a5a94..33779b701a 100644 --- a/Core/MIPS/IR/IRRegCache.h +++ b/Core/MIPS/IR/IRRegCache.h @@ -183,6 +183,8 @@ public: void MarkGPRDirty(IRReg gpr, bool andNormalized32 = false); void MarkGPRAsPointerDirty(IRReg gpr); + virtual void FlushAll(); + protected: virtual void SetupInitialRegs(); virtual const int *GetAllocationOrder(MIPSLoc type, int &count, int &base) const = 0; @@ -196,6 +198,8 @@ protected: IRNativeReg FindBestToSpill(MIPSLoc type, bool unusedOnly, bool *clobbered) const; virtual void DiscardNativeReg(IRNativeReg nreg); virtual void FlushNativeReg(IRNativeReg nreg); + virtual void DiscardReg(IRReg mreg); + virtual void FlushReg(IRReg mreg); virtual void AdjustNativeRegAsPtr(IRNativeReg nreg, bool state); virtual void MapNativeReg(MIPSLoc type, IRNativeReg nreg, IRReg first, int lanes, MIPSMap flags); virtual IRNativeReg MapNativeReg(MIPSLoc type, IRReg first, int lanes, MIPSMap flags); @@ -211,6 +215,7 @@ protected: virtual void StoreRegValue(IRReg mreg, uint32_t imm) = 0; void SetSpillLockIRIndex(IRReg reg, IRReg reg2, IRReg reg3, IRReg reg4, int offset, int index); + int GetMipsRegOffset(IRReg r); bool IsValidGPR(IRReg r) const; bool IsValidGPRNoZero(IRReg r) const; @@ -227,5 +232,4 @@ protected: RegStatusMIPS mrInitial_[TOTAL_MAPPABLE_IRREGS]; bool initialReady_ = false; - bool pendingFlush_ = false; }; diff --git a/Core/MIPS/RiscV/RiscVRegCache.cpp b/Core/MIPS/RiscV/RiscVRegCache.cpp index b7fa865074..e364a2ab97 100644 --- a/Core/MIPS/RiscV/RiscVRegCache.cpp +++ b/Core/MIPS/RiscV/RiscVRegCache.cpp @@ -386,121 +386,12 @@ void RiscVRegCache::StoreRegValue(IRReg mreg, uint32_t imm) { void RiscVRegCache::DiscardR(IRReg mipsReg) { _dbg_assert_(IsValidGPRNoZero(mipsReg)); - if (mr[mipsReg].isStatic) { - DiscardNativeReg(mr[mipsReg].nReg); - return; - } - const MIPSLoc prevLoc = mr[mipsReg].loc; - if (prevLoc == MIPSLoc::REG || prevLoc == MIPSLoc::REG_IMM || prevLoc == MIPSLoc::REG_AS_PTR) { - DiscardNativeReg(mr[mipsReg].nReg); - } - if (prevLoc == MIPSLoc::IMM && mipsReg != MIPS_REG_ZERO) { - mr[mipsReg].loc = MIPSLoc::MEM; - mr[mipsReg].imm = -1; - } + DiscardReg(mipsReg); } void RiscVRegCache::FlushR(IRReg r) { _dbg_assert_(IsValidGPRNoZero(r)); - if (mr[r].isStatic) { - ERROR_LOG(JIT, "Cannot flush static reg %d", r); - return; - } - - switch (mr[r].loc) { - case MIPSLoc::IMM: - // IMM is always "dirty". - if (r != MIPS_REG_ZERO) { - StoreRegValue(r, mr[r].imm); - mr[r].loc = MIPSLoc::MEM; - mr[r].nReg = (int)INVALID_REG; - mr[r].imm = -1; - } else { - mr[r].loc = MIPSLoc::REG_IMM; - mr[r].nReg = R_ZERO; - mr[r].imm = 0; - } - break; - - case MIPSLoc::REG: - case MIPSLoc::REG_IMM: - case MIPSLoc::REG_AS_PTR: - // Might be in a native reg with multiple IR regs, flush together. - FlushNativeReg(mr[r].nReg); - break; - - case MIPSLoc::MEM: - // Already there, nothing to do. - break; - - default: - ERROR_LOG_REPORT(JIT, "FlushR: MipsReg %d with invalid location %d", r, (int)mr[r].loc); - break; - } -} - -void RiscVRegCache::FlushAll() { - // Note: make sure not to change the registers when flushing: - // Branching code expects the native reg to retain its value. - - // TODO: HI/LO optimization? - - for (int i = 1; i < TOTAL_MAPPABLE_IRREGS; i++) { - IRReg mipsReg = IRReg(i); - if (mr[i].isStatic) { - RiscVReg riscvReg = (RiscVReg)mr[i].nReg; - // Cannot leave any IMMs in registers, not even MIPSLoc::REG_IMM, can confuse the regalloc later if this flush is mid-block - // due to an interpreter fallback that changes the register. - if (mr[i].loc == MIPSLoc::IMM) { - SetNativeRegValue(mr[i].nReg, mr[i].imm); - mr[i].loc = MIPSLoc::REG; - nr[riscvReg].pointerified = false; - } else if (mr[i].loc == MIPSLoc::REG_IMM) { - // The register already contains the immediate. - if (nr[riscvReg].pointerified) { - ERROR_LOG(JIT, "RVREG_IMM but pointerified. Wrong."); - nr[riscvReg].pointerified = false; - } - mr[i].loc = MIPSLoc::REG; - } else if (mr[i].loc == MIPSLoc::REG_AS_PTR) { - AdjustNativeRegAsPtr(mr[i].nReg, false); - mr[i].loc = MIPSLoc::REG; - } - if (i != MIPS_REG_ZERO && mr[i].nReg == INVALID_REG) { - ERROR_LOG(JIT, "RV reg of static %i is invalid", i); - continue; - } - } else if (IsValidGPRNoZero(mipsReg)) { - FlushR(mipsReg); - } - } - - int count = 0; - const StaticAllocation *allocs = GetStaticAllocations(count); - for (int i = 0; i < count; i++) { - if (allocs[i].pointerified && !nr[allocs[i].nr].pointerified && jo_->enablePointerify) { - // Re-pointerify - _dbg_assert_(mr[allocs[i].mr].loc == MIPSLoc::REG); - AdjustNativeRegAsPtr(allocs[i].nr, true); - nr[allocs[i].nr].pointerified = true; - } else if (!allocs[i].pointerified) { - // If this register got pointerified on the way, mark it as not. - // This is so that after save/reload (like in an interpreter fallback), - // it won't be regarded as such, as it may no longer be. - nr[allocs[i].nr].pointerified = false; - } - } - // Sanity check - for (int i = 0; i < NUM_RVREG; i++) { - if (nr[i].mipsReg != IRREG_INVALID && mr[nr[i].mipsReg].isStatic == false) { - ERROR_LOG_REPORT(JIT, "Flush fail: nr[%i].mipsReg=%i", i, nr[i].mipsReg); - } - } -} - -int RiscVRegCache::GetMipsRegOffset(IRReg r) { - _dbg_assert_(IsValidGPR(r)); - return r * 4; + FlushReg(r); } RiscVReg RiscVRegCache::R(IRReg mipsReg) { diff --git a/Core/MIPS/RiscV/RiscVRegCache.h b/Core/MIPS/RiscV/RiscVRegCache.h index 00b788c69e..9080c4c783 100644 --- a/Core/MIPS/RiscV/RiscVRegCache.h +++ b/Core/MIPS/RiscV/RiscVRegCache.h @@ -67,7 +67,6 @@ public: void MapDirtyDirtyIn(IRReg rd1, IRReg rd2, IRReg rs, RiscVJitConstants::MapType type = RiscVJitConstants::MapType::AVOID_LOAD); void MapDirtyDirtyInIn(IRReg rd1, IRReg rd2, IRReg rs, IRReg rt, RiscVJitConstants::MapType type = RiscVJitConstants::MapType::AVOID_LOAD); void FlushBeforeCall(); - void FlushAll(); void FlushR(IRReg r); void DiscardR(IRReg r); @@ -92,8 +91,6 @@ protected: void StoreRegValue(IRReg mreg, uint32_t imm) override; private: - int GetMipsRegOffset(IRReg r); - RiscVGen::RiscVEmitter *emit_ = nullptr; enum { diff --git a/Core/MIPS/RiscV/RiscVRegCacheFPU.cpp b/Core/MIPS/RiscV/RiscVRegCacheFPU.cpp index f594cf4c03..604cebfdfe 100644 --- a/Core/MIPS/RiscV/RiscVRegCacheFPU.cpp +++ b/Core/MIPS/RiscV/RiscVRegCacheFPU.cpp @@ -171,28 +171,10 @@ void RiscVRegCacheFPU::StoreRegValue(IRReg mreg, uint32_t imm) { void RiscVRegCacheFPU::FlushR(IRReg r) { _dbg_assert_(IsValidFPR(r)); - switch (mr[r + 32].loc) { - case MIPSLoc::FREG: - case MIPSLoc::VREG: - _assert_msg_(mr[r + 32].nReg != INVALID_REG, "FlushR: IR %d had bad RiscVReg", r + 32); - FlushNativeReg(mr[r + 32].nReg); - break; - - case MIPSLoc::MEM: - break; - - default: - _assert_msg_(false, "Unexpected register location %d", (int)mr[r + 32].loc); - break; - } + FlushReg(r + 32); } void RiscVRegCacheFPU::FlushBeforeCall() { - // Note: don't set this false at the end, since we don't flush everything. - if (!pendingFlush_) { - return; - } - // These registers are not preserved by function calls. for (int i = 0; i <= 7; ++i) { FlushNativeReg(i); @@ -205,64 +187,9 @@ void RiscVRegCacheFPU::FlushBeforeCall() { } } -void RiscVRegCacheFPU::FlushAll() { - if (!pendingFlush_) { - // Nothing allocated. FPU regs are not nearly as common as GPR. - return; - } - - int numRVRegs = 0, baseIndex = 0; - const int *order = GetAllocationOrder(MIPSLoc::FREG, numRVRegs, baseIndex); - - for (int i = 0; i < numRVRegs; i++) { - int a = order[i] - baseIndex; - int m = nr[a].mipsReg; - - if (nr[a].isDirty) { - _assert_(m != MIPS_REG_INVALID); - StoreNativeReg(a, m, 1); - - mr[m].loc = MIPSLoc::MEM; - mr[m].nReg = (int)INVALID_REG; - nr[a].mipsReg = IRREG_INVALID; - nr[a].isDirty = false; - } else { - if (m != IRREG_INVALID) { - mr[m].loc = MIPSLoc::MEM; - mr[m].nReg = (int)INVALID_REG; - } - nr[a].mipsReg = IRREG_INVALID; - } - } - - pendingFlush_ = false; -} - void RiscVRegCacheFPU::DiscardR(IRReg r) { _dbg_assert_(IsValidFPR(r)); - switch (mr[r + 32].loc) { - case MIPSLoc::FREG: - _assert_(mr[r + 32].nReg != INVALID_REG); - // Note that we DO NOT write it back here. That's the whole point of Discard. - DiscardNativeReg(mr[r + 32].nReg); - break; - - case MIPSLoc::MEM: - // Already there, nothing to do. - break; - - default: - _assert_(false); - break; - } - mr[r + 32].spillLockIRIndex = -1; -} - -int RiscVRegCacheFPU::GetMipsRegOffset(IRReg r) { - _assert_(IsValidFPR(r - 32)); - // These are offsets within the MIPSState structure. - // IR gives us an index that is already 32 after the state index (skipping GPRs.) - return (r) * 4; + DiscardReg(r + 32); } RiscVReg RiscVRegCacheFPU::R(IRReg mipsReg) { diff --git a/Core/MIPS/RiscV/RiscVRegCacheFPU.h b/Core/MIPS/RiscV/RiscVRegCacheFPU.h index e672295c47..6b612053e3 100644 --- a/Core/MIPS/RiscV/RiscVRegCacheFPU.h +++ b/Core/MIPS/RiscV/RiscVRegCacheFPU.h @@ -42,7 +42,6 @@ public: void Map4DirtyInIn(IRReg rdbase, IRReg rsbase, IRReg rtbase, bool avoidLoad = true); RiscVGen::RiscVReg Map4DirtyInTemp(IRReg rdbase, IRReg rsbase, bool avoidLoad = true); void FlushBeforeCall(); - void FlushAll(); void FlushR(IRReg r); void DiscardR(IRReg r); @@ -58,13 +57,9 @@ protected: void StoreRegValue(IRReg mreg, uint32_t imm) override; private: - int GetMipsRegOffset(IRReg r); - RiscVGen::RiscVEmitter *emit_ = nullptr; enum { - // On RiscV, each of the 32 registers are full 128-bit. No sharing of components! NUM_RVFPUREG = 32, - NUM_MIPSFPUREG = TOTAL_MAPPABLE_IRREGS - 32, }; };