ppsspp/Core/MIPS/IR/IRRegCache.h
2023-08-27 23:24:30 -07:00

256 lines
8.3 KiB
C++

// 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
// IR.
#include "Common/CommonTypes.h"
#include "Core/MIPS/MIPS.h"
#include "Core/MIPS/IR/IRInst.h"
// 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;
typedef int8_t IRNativeReg;
constexpr IRReg IRREG_INVALID = 255;
class IRWriter;
class MIPSState;
namespace MIPSComp {
class IRBlock;
struct JitOptions;
}
// Transient
class IRImmRegCache {
public:
IRImmRegCache(IRWriter *ir);
void SetImm(IRReg r, u32 immVal) {
isImm_[r] = true;
immVal_[r] = immVal;
}
bool IsImm(IRReg r) const { return isImm_[r]; }
u32 GetImm(IRReg r) const { return immVal_[r]; }
void FlushAll();
void MapDirty(IRReg rd);
void MapIn(IRReg rd);
void MapInIn(IRReg rs, IRReg rt);
void MapInInIn(IRReg rd, IRReg rs, IRReg rt);
void MapDirtyIn(IRReg rd, IRReg rs);
void MapDirtyInIn(IRReg rd, IRReg rs, IRReg rt);
private:
void Flush(IRReg rd);
void Discard(IRReg rd);
bool isImm_[TOTAL_MAPPABLE_IRREGS];
uint32_t immVal_[TOTAL_MAPPABLE_IRREGS];
IRWriter *ir_;
};
// Initing is the default so the flag is reversed.
// 8 bits - upper 4 are reserved for backend purposes.
enum class MIPSMap : uint8_t {
INIT = 0,
DIRTY = 1,
NOINIT = 2 | DIRTY,
BACKEND_MASK = 0xF0,
};
static inline MIPSMap operator |(const MIPSMap &lhs, const MIPSMap &rhs) {
return MIPSMap((uint8_t)lhs | (uint8_t)rhs);
}
static inline MIPSMap operator &(const MIPSMap &lhs, const MIPSMap &rhs) {
return MIPSMap((uint8_t)lhs & (uint8_t)rhs);
}
class IRNativeRegCacheBase {
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. Note: if FREGs and VREGS overlap, just use FREG.
VREG,
// Away in memory (in the mips context struct.)
MEM,
};
struct RegStatusMIPS {
// Where is this IR/MIPS register? Note: base reg if vector.
MIPSLoc loc = MIPSLoc::MEM;
// If in a register, what index (into nr array)?
IRNativeReg nReg = -1;
// If a known immediate value, what value?
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?
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;
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.
bool normalized32 = false;
};
public:
IRNativeRegCacheBase(MIPSComp::JitOptions *jo);
virtual ~IRNativeRegCacheBase() {}
virtual void Start(MIPSComp::IRBlock *irBlock);
void SetIRIndex(int index) {
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);
int GetFPRLane(IRReg fpr);
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);
void MarkGPRDirty(IRReg gpr, bool andNormalized32 = false);
void MarkGPRAsPointerDirty(IRReg gpr);
struct Mapping {
char type = '?';
IRReg reg = IRREG_INVALID;
uint8_t lanes = 1;
MIPSMap flags = MIPSMap::INIT;
};
void Map(const IRInst &inst);
void MapWithExtra(const IRInst &inst, std::vector<Mapping> extra);
virtual void FlushAll(bool gprs = true, bool fprs = true);
protected:
virtual void SetupInitialRegs();
virtual const int *GetAllocationOrder(MIPSLoc type, MIPSMap flags, int &count, int &base) const = 0;
virtual const StaticAllocation *GetStaticAllocations(int &count) const {
count = 0;
return nullptr;
}
IRNativeReg AllocateReg(MIPSLoc type, MIPSMap flags);
IRNativeReg FindFreeReg(MIPSLoc type, MIPSMap flags) const;
IRNativeReg FindBestToSpill(MIPSLoc type, MIPSMap flags, bool unusedOnly, bool *clobbered) const;
virtual bool IsNativeRegCompatible(IRNativeReg nreg, MIPSLoc type, MIPSMap flags);
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);
IRNativeReg MapNativeRegAsPointer(IRReg gpr);
IRNativeReg MapWithTemp(const IRInst &inst, MIPSLoc type);
void MappingFromInst(const IRInst &inst, Mapping mapping[3]);
void ApplyMapping(const Mapping *mapping, int count);
void CleanupMapping(const Mapping *mapping, int count);
// 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);
void SetSpillLockIRIndex(IRReg reg, int index);
int GetMipsRegOffset(IRReg r);
bool IsValidGPR(IRReg r) const;
bool IsValidGPRNoZero(IRReg r) const;
bool IsValidFPR(IRReg r) const;
MIPSComp::JitOptions *jo_;
const MIPSComp::IRBlock *irBlock_ = nullptr;
int irIndex_ = 0;
struct {
int totalNativeRegs = 0;
bool mapFPUSIMD = false;
bool mapUseVRegs = false;
} config_;
RegStatusNative nr[TOTAL_POSSIBLE_NATIVEREGS];
RegStatusMIPS mr[TOTAL_MAPPABLE_IRREGS];
RegStatusNative nrInitial_[TOTAL_POSSIBLE_NATIVEREGS];
RegStatusMIPS mrInitial_[TOTAL_MAPPABLE_IRREGS];
bool initialReady_ = false;
};