CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hrydgard

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: hrydgard/ppsspp
Path: blob/master/Core/MIPS/IR/IRRegCache.h
Views: 1401
1
// Copyright (c) 2023- PPSSPP Project.
2
3
// This program is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, version 2.0 or later versions.
6
7
// This program is distributed in the hope that it will be useful,
8
// but WITHOUT ANY WARRANTY; without even the implied warranty of
9
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
// GNU General Public License 2.0 for more details.
11
12
// A copy of the GPL 2.0 should have been included with the program.
13
// If not, see http://www.gnu.org/licenses/
14
15
// Official git repository and contact information can be found at
16
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17
18
#pragma once
19
20
// IRImmRegCache is only to perform pre-constant folding. This is worth it to get cleaner
21
// IR.
22
23
#include "Common/CommonTypes.h"
24
#include "Core/MIPS/MIPS.h"
25
#include "Core/MIPS/IR/IRAnalysis.h"
26
#include "Core/MIPS/IR/IRInst.h"
27
28
29
// Have to account for all of them due to temps, etc.
30
constexpr int TOTAL_MAPPABLE_IRREGS = 256;
31
// Arbitrary - increase if your backend has more.
32
constexpr int TOTAL_POSSIBLE_NATIVEREGS = 128;
33
34
typedef int8_t IRNativeReg; // invalid value is -1
35
36
constexpr IRReg IRREG_INVALID = 255;
37
38
class IRWriter;
39
class MIPSState;
40
41
namespace MIPSComp {
42
class IRBlock;
43
class IRBlockCache;
44
struct JitOptions;
45
}
46
47
// Transient
48
class IRImmRegCache {
49
public:
50
IRImmRegCache(IRWriter *ir);
51
52
void SetImm(IRReg r, u32 immVal) {
53
isImm_[r] = true;
54
immVal_[r] = immVal;
55
}
56
57
bool IsImm(IRReg r) const { return isImm_[r]; }
58
u32 GetImm(IRReg r) const { return immVal_[r]; }
59
60
void FlushAll();
61
62
void MapDirty(IRReg rd);
63
void MapIn(IRReg rd);
64
void MapInIn(IRReg rs, IRReg rt);
65
void MapInInIn(IRReg rd, IRReg rs, IRReg rt);
66
void MapDirtyIn(IRReg rd, IRReg rs);
67
void MapDirtyInIn(IRReg rd, IRReg rs, IRReg rt);
68
69
private:
70
void Flush(IRReg rd);
71
void Discard(IRReg rd);
72
73
bool isImm_[TOTAL_MAPPABLE_IRREGS];
74
uint32_t immVal_[TOTAL_MAPPABLE_IRREGS];
75
IRWriter *ir_;
76
};
77
78
// Initing is the default so the flag is reversed.
79
// 8 bits - upper 4 are reserved for backend purposes.
80
enum class MIPSMap : uint8_t {
81
INIT = 0,
82
DIRTY = 1,
83
NOINIT = 2 | DIRTY,
84
85
BACKEND_MASK = 0xF0,
86
};
87
static inline MIPSMap operator |(const MIPSMap &lhs, const MIPSMap &rhs) {
88
return MIPSMap((uint8_t)lhs | (uint8_t)rhs);
89
}
90
static inline MIPSMap operator &(const MIPSMap &lhs, const MIPSMap &rhs) {
91
return MIPSMap((uint8_t)lhs & (uint8_t)rhs);
92
}
93
94
class IRNativeRegCacheBase {
95
protected:
96
enum class MIPSLoc {
97
// Known immediate value (only in regcache.)
98
IMM,
99
// In a general reg.
100
REG,
101
// In a general reg, but an adjusted pointer (not pointerified - unaligned.)
102
REG_AS_PTR,
103
// In a general reg, but also has a known immediate value.
104
REG_IMM,
105
// In a native floating-point reg.
106
FREG,
107
// In a native vector reg. Note: if FREGs and VREGS overlap, just use FREG.
108
VREG,
109
// Away in memory (in the mips context struct.)
110
MEM,
111
};
112
113
struct RegStatusMIPS {
114
// Where is this IR/MIPS register? Note: base reg if vector.
115
MIPSLoc loc = MIPSLoc::MEM;
116
// If in a register, what index (into nr array)?
117
IRNativeReg nReg = -1;
118
// If a known immediate value, what value?
119
uint32_t imm = 0;
120
// Locked from spilling (i.e. used by current instruction) as of what IR instruction?
121
int spillLockIRIndex = -1;
122
// If in a multipart reg (vector or HI/LO), which lane?
123
int lane = -1;
124
// Whether this reg is statically allocated.
125
bool isStatic = false;
126
};
127
struct RegStatusNative {
128
// Which IR/MIPS reg is this currently holding?
129
IRReg mipsReg = IRREG_INVALID;
130
// Locked either as temp or direct reg as of what IR instruction?
131
int tempLockIRIndex = -1;
132
// Should the register be written back?
133
bool isDirty = false;
134
// Upper part of the register is used for "pointerification".
135
// Depending on backend, this may not be used or some/all operations may work on the lower 32 bits.
136
bool pointerified = false;
137
// Upper part of the register has a normalized form (i.e. zero or sign extend.)
138
// Which this means or if it matters depends on the backend.
139
bool normalized32 = false;
140
};
141
142
struct StaticAllocation {
143
IRReg mr;
144
IRNativeReg nr;
145
// Register type.
146
MIPSLoc loc;
147
// Whether the reg should be marked pointerified by default.
148
bool pointerified = false;
149
// Whether the reg should be considered always normalized at the start of a block.
150
bool normalized32 = false;
151
};
152
153
public:
154
IRNativeRegCacheBase(MIPSComp::JitOptions *jo);
155
virtual ~IRNativeRegCacheBase() {}
156
157
virtual void Start(MIPSComp::IRBlockCache *irBlockCache, int blockNum);
158
void SetIRIndex(int index) {
159
irIndex_ = index;
160
}
161
162
bool IsGPRInRAM(IRReg gpr);
163
bool IsFPRInRAM(IRReg fpr);
164
bool IsGPRMapped(IRReg gpr);
165
bool IsFPRMapped(IRReg fpr);
166
bool IsGPRMappedAsPointer(IRReg gpr);
167
bool IsGPRMappedAsStaticPointer(IRReg gpr);
168
int GetFPRLane(IRReg fpr);
169
int GetFPRLaneCount(IRReg fpr);
170
171
bool IsGPRImm(IRReg gpr);
172
bool IsGPR2Imm(IRReg base);
173
uint32_t GetGPRImm(IRReg gpr);
174
uint64_t GetGPR2Imm(IRReg first);
175
void SetGPRImm(IRReg gpr, uint32_t immval);
176
void SetGPR2Imm(IRReg first, uint64_t immval);
177
178
// Protect the native registers containing register froms spilling, to ensure that
179
// it's being kept allocated.
180
void SpillLockGPR(IRReg reg, IRReg reg2 = IRREG_INVALID, IRReg reg3 = IRREG_INVALID, IRReg reg4 = IRREG_INVALID);
181
void SpillLockFPR(IRReg reg, IRReg reg2 = IRREG_INVALID, IRReg reg3 = IRREG_INVALID, IRReg reg4 = IRREG_INVALID);
182
void ReleaseSpillLockGPR(IRReg reg, IRReg reg2 = IRREG_INVALID, IRReg reg3 = IRREG_INVALID, IRReg reg4 = IRREG_INVALID);
183
void ReleaseSpillLockFPR(IRReg reg, IRReg reg2 = IRREG_INVALID, IRReg reg3 = IRREG_INVALID, IRReg reg4 = IRREG_INVALID);
184
185
void MarkGPRDirty(IRReg gpr, bool andNormalized32 = false);
186
void MarkGPRAsPointerDirty(IRReg gpr);
187
188
bool IsGPRClobbered(IRReg gpr) const;
189
bool IsFPRClobbered(IRReg gpr) const;
190
191
struct Mapping {
192
char type = '?';
193
IRReg reg = IRREG_INVALID;
194
uint8_t lanes = 1;
195
MIPSMap flags = MIPSMap::INIT;
196
};
197
198
void Map(const IRInst &inst);
199
void MapWithExtra(const IRInst &inst, std::vector<Mapping> extra);
200
virtual void FlushAll(bool gprs = true, bool fprs = true);
201
202
protected:
203
virtual void SetupInitialRegs();
204
virtual const int *GetAllocationOrder(MIPSLoc type, MIPSMap flags, int &count, int &base) const = 0;
205
virtual const StaticAllocation *GetStaticAllocations(int &count) const {
206
count = 0;
207
return nullptr;
208
}
209
210
IRNativeReg AllocateReg(MIPSLoc type, MIPSMap flags);
211
IRNativeReg FindFreeReg(MIPSLoc type, MIPSMap flags) const;
212
IRNativeReg FindBestToSpill(MIPSLoc type, MIPSMap flags, bool unusedOnly, bool *clobbered) const;
213
virtual bool IsNativeRegCompatible(IRNativeReg nreg, MIPSLoc type, MIPSMap flags, int lanes);
214
virtual void DiscardNativeReg(IRNativeReg nreg);
215
virtual void FlushNativeReg(IRNativeReg nreg);
216
virtual void DiscardReg(IRReg mreg);
217
virtual void FlushReg(IRReg mreg);
218
virtual void AdjustNativeRegAsPtr(IRNativeReg nreg, bool state);
219
virtual void MapNativeReg(MIPSLoc type, IRNativeReg nreg, IRReg first, int lanes, MIPSMap flags);
220
virtual bool TransferNativeReg(IRNativeReg nreg, IRNativeReg dest, MIPSLoc type, IRReg first, int lanes, MIPSMap flags);
221
virtual IRNativeReg MapNativeReg(MIPSLoc type, IRReg first, int lanes, MIPSMap flags);
222
IRNativeReg MapNativeRegAsPointer(IRReg gpr);
223
224
IRNativeReg MapWithTemp(const IRInst &inst, MIPSLoc type);
225
226
void MappingFromInst(const IRInst &inst, Mapping mapping[3]);
227
void ApplyMapping(const Mapping *mapping, int count);
228
void CleanupMapping(const Mapping *mapping, int count);
229
230
// Load data from memory (possibly multiple lanes) into a native reg.
231
virtual void LoadNativeReg(IRNativeReg nreg, IRReg first, int lanes) = 0;
232
// Store data in a native reg back into memory.
233
virtual void StoreNativeReg(IRNativeReg nreg, IRReg first, int lanes) = 0;
234
// Set a native reg to a specific integer value.
235
virtual void SetNativeRegValue(IRNativeReg nreg, uint32_t imm) = 0;
236
// Store the imm value for a reg to memory (not currently in a native reg.)
237
virtual void StoreRegValue(IRReg mreg, uint32_t imm) = 0;
238
239
void SetSpillLockIRIndex(IRReg reg, IRReg reg2, IRReg reg3, IRReg reg4, int offset, int index);
240
void SetSpillLockIRIndex(IRReg reg, int index);
241
int GetMipsRegOffset(IRReg r);
242
243
bool IsRegClobbered(MIPSLoc type, IRReg r) const;
244
bool IsRegRead(MIPSLoc type, IRReg r) const;
245
IRUsage GetNextRegUsage(const IRSituation &info, MIPSLoc type, IRReg r) const;
246
247
bool IsValidGPR(IRReg r) const;
248
bool IsValidGPRNoZero(IRReg r) const;
249
bool IsValidFPR(IRReg r) const;
250
251
MIPSComp::JitOptions *jo_;
252
int irBlockNum_ = 0;
253
const MIPSComp::IRBlock *irBlock_ = nullptr;
254
const MIPSComp::IRBlockCache *irBlockCache_ = nullptr;
255
int irIndex_ = 0;
256
257
struct {
258
int totalNativeRegs = 0;
259
bool mapFPUSIMD = false;
260
bool mapUseVRegs = false;
261
} config_;
262
263
RegStatusNative nr[TOTAL_POSSIBLE_NATIVEREGS];
264
RegStatusMIPS mr[TOTAL_MAPPABLE_IRREGS];
265
RegStatusNative nrInitial_[TOTAL_POSSIBLE_NATIVEREGS];
266
RegStatusMIPS mrInitial_[TOTAL_MAPPABLE_IRREGS];
267
268
bool initialReady_ = false;
269
};
270
271