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/IRAnalysis.cpp
Views: 1401
1
// Copyright (c) 2016- 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
#include "Core/MIPS/IR/IRAnalysis.h"
19
20
// For std::min
21
#include <algorithm>
22
23
24
static bool IRReadsFrom(const IRInstMeta &inst, int reg, char type, bool *directly) {
25
if (inst.m.types[1] == type && inst.src1 == reg) {
26
if (directly)
27
*directly = true;
28
return true;
29
}
30
if (inst.m.types[2] == type && inst.src2 == reg) {
31
if (directly)
32
*directly = true;
33
return true;
34
}
35
if ((inst.m.flags & (IRFLAG_SRC3 | IRFLAG_SRC3DST)) != 0 && inst.m.types[0] == type && inst.src3 == reg) {
36
if (directly)
37
*directly = true;
38
return true;
39
}
40
41
if (directly)
42
*directly = false;
43
if ((inst.m.flags & (IRFLAG_EXIT | IRFLAG_BARRIER)) != 0)
44
return true;
45
return false;
46
}
47
48
bool IRReadsFromFPR(const IRInstMeta &inst, int reg, bool *directly) {
49
if (IRReadsFrom(inst, reg, 'F', directly))
50
return true;
51
52
// We also need to check V and 2. Indirect reads already checked, don't check again.
53
if (inst.m.types[1] == 'V' && reg >= inst.src1 && reg < inst.src1 + 4)
54
return true;
55
if (inst.m.types[1] == '2' && reg >= inst.src1 && reg < inst.src1 + 2)
56
return true;
57
if (inst.m.types[2] == 'V' && reg >= inst.src2 && reg < inst.src2 + 4)
58
return true;
59
if (inst.m.types[2] == '2' && reg >= inst.src2 && reg < inst.src2 + 2)
60
return true;
61
if ((inst.m.flags & (IRFLAG_SRC3 | IRFLAG_SRC3DST)) != 0) {
62
if (inst.m.types[0] == 'V' && reg >= inst.src3 && reg <= inst.src3 + 4)
63
return true;
64
if (inst.m.types[0] == '2' && reg >= inst.src3 && reg <= inst.src3 + 2)
65
return true;
66
}
67
return false;
68
}
69
70
static int IRReadsFromList(const IRInstMeta &inst, IRReg regs[4], char type) {
71
int c = 0;
72
73
if (inst.m.types[1] == type)
74
regs[c++] = inst.src1;
75
if (inst.m.types[2] == type)
76
regs[c++] = inst.src2;
77
if ((inst.m.flags & (IRFLAG_SRC3 | IRFLAG_SRC3DST)) != 0 && inst.m.types[0] == type)
78
regs[c++] = inst.src3;
79
80
if (inst.op == IROp::Interpret || inst.op == IROp::CallReplacement || inst.op == IROp::Syscall || inst.op == IROp::Break)
81
return -1;
82
if (inst.op == IROp::Breakpoint || inst.op == IROp::MemoryCheck)
83
return -1;
84
85
return c;
86
}
87
88
bool IRReadsFromGPR(const IRInstMeta &inst, int reg, bool *directly) {
89
return IRReadsFrom(inst, reg, 'G', directly);
90
}
91
92
int IRDestGPR(const IRInstMeta &inst) {
93
if ((inst.m.flags & IRFLAG_SRC3) == 0 && inst.m.types[0] == 'G') {
94
return inst.dest;
95
}
96
return -1;
97
}
98
99
bool IRWritesToGPR(const IRInstMeta &inst, int reg) {
100
return IRDestGPR(inst) == reg;
101
}
102
103
bool IRWritesToFPR(const IRInstMeta &inst, int reg) {
104
// Doesn't write to anything.
105
if ((inst.m.flags & IRFLAG_SRC3) != 0)
106
return false;
107
108
if (inst.m.types[0] == 'F' && reg == inst.dest)
109
return true;
110
if (inst.m.types[0] == 'V' && reg >= inst.dest && reg < inst.dest + 4)
111
return true;
112
if (inst.m.types[0] == '2' && reg >= inst.dest && reg < inst.dest + 2)
113
return true;
114
return false;
115
}
116
117
int IRDestFPRs(const IRInstMeta &inst, IRReg regs[4]) {
118
// Doesn't write to anything.
119
if ((inst.m.flags & IRFLAG_SRC3) != 0)
120
return 0;
121
122
if (inst.m.types[0] == 'F') {
123
regs[0] = inst.dest;
124
return 1;
125
}
126
if (inst.m.types[0] == 'V') {
127
for (int i = 0; i < 4; ++i)
128
regs[i] = inst.dest + i;
129
return 4;
130
}
131
if (inst.m.types[0] == '2') {
132
for (int i = 0; i < 2; ++i)
133
regs[i] = inst.dest + i;
134
return 2;
135
}
136
return 0;
137
}
138
139
int IRReadsFromGPRs(const IRInstMeta &inst, IRReg regs[4]) {
140
return IRReadsFromList(inst, regs, 'G');
141
}
142
143
int IRReadsFromFPRs(const IRInstMeta &inst, IRReg regs[16]) {
144
int c = IRReadsFromList(inst, regs, 'F');
145
if (c != 0)
146
return c;
147
148
// We also need to check V and 2. Indirect reads already checked, don't check again.
149
if (inst.m.types[1] == 'V' || inst.m.types[1] == '2') {
150
for (int i = 0; i < (inst.m.types[1] == 'V' ? 4 : 2); ++i)
151
regs[c++] = inst.src1 + i;
152
}
153
if (inst.m.types[2] == 'V' || inst.m.types[2] == '2') {
154
for (int i = 0; i < (inst.m.types[2] == 'V' ? 4 : 2); ++i)
155
regs[c++] = inst.src2 + i;
156
}
157
if ((inst.m.flags & (IRFLAG_SRC3 | IRFLAG_SRC3DST)) != 0) {
158
if (inst.m.types[0] == 'V' || inst.m.types[0] == '2') {
159
for (int i = 0; i < (inst.m.types[0] == 'V' ? 4 : 2); ++i)
160
regs[c++] = inst.src3 + i;
161
}
162
}
163
return c;
164
}
165
166
IRUsage IRNextGPRUsage(int gpr, const IRSituation &info) {
167
// Exclude any "special" regs from this logic for now.
168
if (gpr >= 32)
169
return IRUsage::UNKNOWN;
170
171
int count = std::min(info.numInstructions - info.currentIndex, info.lookaheadCount);
172
for (int i = 0; i < count; ++i) {
173
const IRInstMeta inst = GetIRMeta(info.instructions[info.currentIndex + i]);
174
if (IRReadsFromGPR(inst, gpr))
175
return IRUsage::READ;
176
// We say WRITE when the current instruction writes. It's not useful for spilling.
177
if (IRDestGPR(inst) == gpr)
178
return i == 0 ? IRUsage::WRITE : IRUsage::CLOBBERED;
179
}
180
181
return IRUsage::UNUSED;
182
}
183
184
IRUsage IRNextFPRUsage(int fpr, const IRSituation &info) {
185
// Let's only pay attention to standard FP regs and temps.
186
// See MIPS.h for these offsets.
187
if (fpr < 0 || (fpr >= 160 && fpr < 192) || fpr >= 208)
188
return IRUsage::UNKNOWN;
189
190
int count = std::min(info.numInstructions - info.currentIndex, info.lookaheadCount);
191
for (int i = 0; i < count; ++i) {
192
const IRInstMeta inst = GetIRMeta(info.instructions[info.currentIndex + i]);
193
194
if (IRReadsFromFPR(inst, fpr)) {
195
// Special case a broadcast that clobbers it.
196
if (inst.op == IROp::Vec4Shuffle && inst.src2 == 0 && inst.dest == inst.src1)
197
return inst.src1 == fpr ? IRUsage::READ : IRUsage::CLOBBERED;
198
199
// If this is an exit reading a temp, ignore it.
200
if (fpr < IRVTEMP_PFX_S || (GetIRMeta(inst.op)->flags & IRFLAG_EXIT) == 0)
201
return IRUsage::READ;
202
}
203
// We say WRITE when the current instruction writes. It's not useful for spilling.
204
if (IRWritesToFPR(inst, fpr)) {
205
return i == 0 ? IRUsage::WRITE : IRUsage::CLOBBERED;
206
}
207
}
208
209
// This means we only had exits and hit the end.
210
if (fpr >= IRVTEMP_PFX_S && count == info.numInstructions - info.currentIndex)
211
return IRUsage::CLOBBERED;
212
213
return IRUsage::UNUSED;
214
}
215
216