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/ARM/ArmCompLoadStore.cpp
Views: 1401
1
// Copyright (c) 2012- 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 "ppsspp_config.h"
19
#if PPSSPP_ARCH(ARM)
20
21
#include "Core/MemMap.h"
22
#include "Core/Config.h"
23
#include "Core/MIPS/MIPS.h"
24
#include "Core/MIPS/MIPSAnalyst.h"
25
#include "Core/MIPS/MIPSCodeUtils.h"
26
#include "Core/MIPS/ARM/ArmJit.h"
27
#include "Core/MIPS/ARM/ArmRegCache.h"
28
29
#define _RS MIPS_GET_RS(op)
30
#define _RT MIPS_GET_RT(op)
31
#define _RD MIPS_GET_RD(op)
32
#define _FS MIPS_GET_FS(op)
33
#define _FT MIPS_GET_FT(op)
34
#define _FD MIPS_GET_FD(op)
35
#define _SA MIPS_GET_SA(op)
36
#define _POS ((op>> 6) & 0x1F)
37
#define _SIZE ((op>>11) & 0x1F)
38
#define _IMM16 (signed short)(op & 0xFFFF)
39
#define _IMM26 (op & 0x03FFFFFF)
40
41
// All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly.
42
// Currently known non working ones should have DISABLE.
43
44
// #define CONDITIONAL_DISABLE(flag) { Comp_Generic(op); return; }
45
#define CONDITIONAL_DISABLE(flag) if (jo.Disabled(JitDisable::flag)) { Comp_Generic(op); return; }
46
#define DISABLE { Comp_Generic(op); return; }
47
48
namespace MIPSComp
49
{
50
using namespace ArmGen;
51
using namespace ArmJitConstants;
52
53
void ArmJit::SetR0ToEffectiveAddress(MIPSGPReg rs, s16 offset) {
54
Operand2 op2;
55
if (offset) {
56
bool negated;
57
if (TryMakeOperand2_AllowNegation(offset, op2, &negated)) {
58
if (!negated)
59
ADD(R0, gpr.R(rs), op2);
60
else
61
SUB(R0, gpr.R(rs), op2);
62
} else {
63
// Try to avoid using MOVT
64
if (offset < 0) {
65
gpr.SetRegImm(R0, (u32)(-offset));
66
SUB(R0, gpr.R(rs), R0);
67
} else {
68
gpr.SetRegImm(R0, (u32)offset);
69
ADD(R0, gpr.R(rs), R0);
70
}
71
}
72
BIC(R0, R0, Operand2(0xC0, 4)); // &= 0x3FFFFFFF
73
} else {
74
BIC(R0, gpr.R(rs), Operand2(0xC0, 4)); // &= 0x3FFFFFFF
75
}
76
}
77
78
void ArmJit::SetCCAndR0ForSafeAddress(MIPSGPReg rs, s16 offset, ARMReg tempReg, bool reverse) {
79
SetR0ToEffectiveAddress(rs, offset);
80
81
// There are three valid ranges. Each one gets a bit.
82
const u32 BIT_SCRATCH = 1, BIT_RAM = 2, BIT_VRAM = 4;
83
MOVI2R(tempReg, BIT_SCRATCH | BIT_RAM | BIT_VRAM);
84
85
CMP(R0, AssumeMakeOperand2(PSP_GetScratchpadMemoryBase()));
86
SetCC(CC_LO);
87
BIC(tempReg, tempReg, BIT_SCRATCH);
88
SetCC(CC_HS);
89
CMP(R0, AssumeMakeOperand2(PSP_GetScratchpadMemoryEnd()));
90
BIC(tempReg, tempReg, BIT_SCRATCH);
91
92
// If it was in that range, later compares don't matter.
93
CMP(R0, AssumeMakeOperand2(PSP_GetVidMemBase()));
94
SetCC(CC_LO);
95
BIC(tempReg, tempReg, BIT_VRAM);
96
SetCC(CC_HS);
97
CMP(R0, AssumeMakeOperand2(PSP_GetVidMemEnd()));
98
BIC(tempReg, tempReg, BIT_VRAM);
99
100
CMP(R0, AssumeMakeOperand2(PSP_GetKernelMemoryBase()));
101
SetCC(CC_LO);
102
BIC(tempReg, tempReg, BIT_RAM);
103
SetCC(CC_HS);
104
CMP(R0, AssumeMakeOperand2(PSP_GetUserMemoryEnd()));
105
BIC(tempReg, tempReg, BIT_RAM);
106
107
// If we left any bit set, the address is OK.
108
SetCC(CC_AL);
109
CMP(tempReg, 0);
110
SetCC(reverse ? CC_EQ : CC_GT);
111
}
112
113
void ArmJit::Comp_ITypeMemLR(MIPSOpcode op, bool load) {
114
CONDITIONAL_DISABLE(LSU);
115
CheckMemoryBreakpoint();
116
int offset = SignExtend16ToS32(op & 0xFFFF);
117
MIPSGPReg rt = _RT;
118
MIPSGPReg rs = _RS;
119
int o = op >> 26;
120
121
if (!js.inDelaySlot && !jo.Disabled(JitDisable::LSU_UNALIGNED)) {
122
// Optimisation: Combine to single unaligned load/store
123
bool isLeft = (o == 34 || o == 42);
124
CheckMemoryBreakpoint(1);
125
MIPSOpcode nextOp = GetOffsetInstruction(1);
126
// Find a matching shift in opposite direction with opposite offset.
127
if (nextOp == (isLeft ? (op.encoding + (4<<26) - 3)
128
: (op.encoding - (4<<26) + 3)))
129
{
130
EatInstruction(nextOp);
131
nextOp = MIPSOpcode(((load ? 35 : 43) << 26) | ((isLeft ? nextOp : op) & 0x03FFFFFF)); //lw, sw
132
Comp_ITypeMem(nextOp);
133
return;
134
}
135
}
136
137
u32 iaddr = gpr.IsImm(rs) ? offset + gpr.GetImm(rs) : 0xFFFFFFFF;
138
bool doCheck = false;
139
FixupBranch skip;
140
141
if (gpr.IsImm(rs) && Memory::IsValidAddress(iaddr)) {
142
u32 addr = iaddr & 0x3FFFFFFF;
143
// Need to initialize since this only loads part of the register.
144
// But rs no longer matters (even if rs == rt) since we have the address.
145
gpr.MapReg(rt, load ? MAP_DIRTY : 0);
146
gpr.SetRegImm(R0, addr & ~3);
147
148
u8 shift = (addr & 3) * 8;
149
150
switch (o) {
151
case 34: // lwl
152
LDR(R0, MEMBASEREG, R0);
153
ANDI2R(gpr.R(rt), gpr.R(rt), 0x00ffffff >> shift, SCRATCHREG2);
154
ORR(gpr.R(rt), gpr.R(rt), Operand2(R0, ST_LSL, 24 - shift));
155
break;
156
157
case 38: // lwr
158
LDR(R0, MEMBASEREG, R0);
159
ANDI2R(gpr.R(rt), gpr.R(rt), 0xffffff00 << (24 - shift), SCRATCHREG2);
160
ORR(gpr.R(rt), gpr.R(rt), Operand2(R0, ST_LSR, shift));
161
break;
162
163
case 42: // swl
164
LDR(SCRATCHREG2, MEMBASEREG, R0);
165
// Don't worry, can't use temporary.
166
ANDI2R(SCRATCHREG2, SCRATCHREG2, 0xffffff00 << shift, R0);
167
ORR(SCRATCHREG2, SCRATCHREG2, Operand2(gpr.R(rt), ST_LSR, 24 - shift));
168
STR(SCRATCHREG2, MEMBASEREG, R0);
169
break;
170
171
case 46: // swr
172
LDR(SCRATCHREG2, MEMBASEREG, R0);
173
// Don't worry, can't use temporary.
174
ANDI2R(SCRATCHREG2, SCRATCHREG2, 0x00ffffff >> (24 - shift), R0);
175
ORR(SCRATCHREG2, SCRATCHREG2, Operand2(gpr.R(rt), ST_LSL, shift));
176
STR(SCRATCHREG2, MEMBASEREG, R0);
177
break;
178
}
179
return;
180
}
181
182
// This gets hit in a few games, as a result of never-taken delay slots (some branch types
183
// conditionally execute the delay slot instructions). Ignore in those cases.
184
if (!js.inDelaySlot) {
185
_dbg_assert_msg_(!gpr.IsImm(rs), "Invalid immediate address %08x? CPU bug?", iaddr);
186
}
187
188
if (load) {
189
gpr.MapDirtyIn(rt, rs, false);
190
} else {
191
gpr.MapInIn(rt, rs);
192
}
193
194
if (!g_Config.bFastMemory && rs != MIPS_REG_SP) {
195
SetCCAndR0ForSafeAddress(rs, offset, SCRATCHREG2, true);
196
doCheck = true;
197
} else {
198
SetR0ToEffectiveAddress(rs, offset);
199
}
200
if (doCheck) {
201
skip = B();
202
}
203
SetCC(CC_AL);
204
205
// Need temp regs. TODO: Get from the regcache?
206
static const ARMReg LR_SCRATCHREG3 = R9;
207
static const ARMReg LR_SCRATCHREG4 = R10;
208
if (load) {
209
PUSH(1, LR_SCRATCHREG3);
210
} else {
211
PUSH(2, LR_SCRATCHREG3, LR_SCRATCHREG4);
212
}
213
214
// Here's our shift amount.
215
AND(SCRATCHREG2, R0, 3);
216
LSL(SCRATCHREG2, SCRATCHREG2, 3);
217
218
// Now align the address for the actual read.
219
BIC(R0, R0, 3);
220
221
switch (o) {
222
case 34: // lwl
223
MOVI2R(LR_SCRATCHREG3, 0x00ffffff);
224
LDR(R0, MEMBASEREG, R0);
225
AND(gpr.R(rt), gpr.R(rt), Operand2(LR_SCRATCHREG3, ST_LSR, SCRATCHREG2));
226
RSB(SCRATCHREG2, SCRATCHREG2, 24);
227
ORR(gpr.R(rt), gpr.R(rt), Operand2(R0, ST_LSL, SCRATCHREG2));
228
break;
229
230
case 38: // lwr
231
MOVI2R(LR_SCRATCHREG3, 0xffffff00);
232
LDR(R0, MEMBASEREG, R0);
233
LSR(R0, R0, SCRATCHREG2);
234
RSB(SCRATCHREG2, SCRATCHREG2, 24);
235
AND(gpr.R(rt), gpr.R(rt), Operand2(LR_SCRATCHREG3, ST_LSL, SCRATCHREG2));
236
ORR(gpr.R(rt), gpr.R(rt), R0);
237
break;
238
239
case 42: // swl
240
MOVI2R(LR_SCRATCHREG3, 0xffffff00);
241
LDR(LR_SCRATCHREG4, MEMBASEREG, R0);
242
AND(LR_SCRATCHREG4, LR_SCRATCHREG4, Operand2(LR_SCRATCHREG3, ST_LSL, SCRATCHREG2));
243
RSB(SCRATCHREG2, SCRATCHREG2, 24);
244
ORR(LR_SCRATCHREG4, LR_SCRATCHREG4, Operand2(gpr.R(rt), ST_LSR, SCRATCHREG2));
245
STR(LR_SCRATCHREG4, MEMBASEREG, R0);
246
break;
247
248
case 46: // swr
249
MOVI2R(LR_SCRATCHREG3, 0x00ffffff);
250
LDR(LR_SCRATCHREG4, MEMBASEREG, R0);
251
RSB(SCRATCHREG2, SCRATCHREG2, 24);
252
AND(LR_SCRATCHREG4, LR_SCRATCHREG4, Operand2(LR_SCRATCHREG3, ST_LSR, SCRATCHREG2));
253
RSB(SCRATCHREG2, SCRATCHREG2, 24);
254
ORR(LR_SCRATCHREG4, LR_SCRATCHREG4, Operand2(gpr.R(rt), ST_LSL, SCRATCHREG2));
255
STR(LR_SCRATCHREG4, MEMBASEREG, R0);
256
break;
257
}
258
259
if (load) {
260
POP(1, LR_SCRATCHREG3);
261
} else {
262
POP(2, LR_SCRATCHREG3, LR_SCRATCHREG4);
263
}
264
265
if (doCheck) {
266
SetJumpTarget(skip);
267
}
268
}
269
270
void ArmJit::Comp_ITypeMem(MIPSOpcode op)
271
{
272
CONDITIONAL_DISABLE(LSU);
273
CheckMemoryBreakpoint();
274
int offset = (signed short)(op&0xFFFF);
275
bool load = false;
276
MIPSGPReg rt = _RT;
277
MIPSGPReg rs = _RS;
278
int o = op>>26;
279
if (((op >> 29) & 1) == 0 && rt == MIPS_REG_ZERO) {
280
// Don't load anything into $zr
281
return;
282
}
283
284
u32 iaddr = gpr.IsImm(rs) ? offset + gpr.GetImm(rs) : 0xFFFFFFFF;
285
bool doCheck = false;
286
ARMReg addrReg = R0;
287
288
switch (o) {
289
case 32: //lb
290
case 33: //lh
291
case 35: //lw
292
case 36: //lbu
293
case 37: //lhu
294
load = true;
295
case 40: //sb
296
case 41: //sh
297
case 43: //sw
298
// Map base register as pointer and go from there - if the displacement isn't too big.
299
// This is faster if there are multiple loads from the same pointer. Need to hook up the MIPS analyzer..
300
if (jo.cachePointers && g_Config.bFastMemory) {
301
// ARM has smaller load/store immediate displacements than MIPS, 12 bits - and some memory ops only have 8 bits.
302
int offsetRange = 0x3ff;
303
if (o == 41 || o == 33 || o == 37 || o == 32)
304
offsetRange = 0xff; // 8 bit offset only
305
if (!gpr.IsImm(rs) && rs != rt && (offset <= offsetRange) && offset >= -offsetRange) {
306
gpr.SpillLock(rs, rt);
307
gpr.MapRegAsPointer(rs);
308
gpr.MapReg(rt, load ? MAP_NOINIT : 0);
309
switch (o) {
310
case 35: LDR (gpr.R(rt), gpr.RPtr(rs), Operand2(offset, TYPE_IMM)); break;
311
case 37: LDRH (gpr.R(rt), gpr.RPtr(rs), Operand2(offset, TYPE_IMM)); break;
312
case 33: LDRSH(gpr.R(rt), gpr.RPtr(rs), Operand2(offset, TYPE_IMM)); break;
313
case 36: LDRB (gpr.R(rt), gpr.RPtr(rs), Operand2(offset, TYPE_IMM)); break;
314
case 32: LDRSB(gpr.R(rt), gpr.RPtr(rs), Operand2(offset, TYPE_IMM)); break;
315
// Store
316
case 43: STR (gpr.R(rt), gpr.RPtr(rs), Operand2(offset, TYPE_IMM)); break;
317
case 41: STRH (gpr.R(rt), gpr.RPtr(rs), Operand2(offset, TYPE_IMM)); break;
318
case 40: STRB (gpr.R(rt), gpr.RPtr(rs), Operand2(offset, TYPE_IMM)); break;
319
}
320
gpr.ReleaseSpillLocks();
321
break;
322
}
323
}
324
325
if (gpr.IsImm(rs) && Memory::IsValidAddress(iaddr)) {
326
// TODO: Avoid mapping a register for the "zero" register, use R0 instead.
327
328
// We can compute the full address at compile time. Kickass.
329
u32 addr = iaddr & 0x3FFFFFFF;
330
331
if (addr == iaddr && offset == 0) {
332
// It was already safe. Let's shove it into a reg and use it directly.
333
load ? gpr.MapDirtyIn(rt, rs) : gpr.MapInIn(rt, rs);
334
addrReg = gpr.R(rs);
335
} else {
336
// In this case, only map rt. rs+offset will be in R0.
337
gpr.MapReg(rt, load ? MAP_NOINIT : 0);
338
gpr.SetRegImm(R0, addr);
339
addrReg = R0;
340
}
341
} else {
342
_dbg_assert_msg_(!gpr.IsImm(rs), "Invalid immediate address? CPU bug?");
343
load ? gpr.MapDirtyIn(rt, rs) : gpr.MapInIn(rt, rs);
344
345
if (!g_Config.bFastMemory && rs != MIPS_REG_SP) {
346
SetCCAndR0ForSafeAddress(rs, offset, SCRATCHREG2);
347
doCheck = true;
348
} else {
349
SetR0ToEffectiveAddress(rs, offset);
350
}
351
addrReg = R0;
352
}
353
354
switch (o)
355
{
356
// Load
357
case 35: LDR (gpr.R(rt), MEMBASEREG, addrReg); break;
358
case 37: LDRH (gpr.R(rt), MEMBASEREG, addrReg); break;
359
case 33: LDRSH(gpr.R(rt), MEMBASEREG, addrReg); break;
360
case 36: LDRB (gpr.R(rt), MEMBASEREG, addrReg); break;
361
case 32: LDRSB(gpr.R(rt), MEMBASEREG, addrReg); break;
362
// Store
363
case 43: STR (gpr.R(rt), MEMBASEREG, addrReg); break;
364
case 41: STRH (gpr.R(rt), MEMBASEREG, addrReg); break;
365
case 40: STRB (gpr.R(rt), MEMBASEREG, addrReg); break;
366
}
367
if (doCheck) {
368
if (load) {
369
SetCC(CC_EQ);
370
MOVI2R(gpr.R(rt), 0);
371
}
372
SetCC(CC_AL);
373
}
374
break;
375
case 34: //lwl
376
case 38: //lwr
377
load = true;
378
case 42: //swl
379
case 46: //swr
380
Comp_ITypeMemLR(op, load);
381
break;
382
default:
383
Comp_Generic(op);
384
return;
385
}
386
}
387
388
void ArmJit::Comp_StoreSync(MIPSOpcode op) {
389
CONDITIONAL_DISABLE(LSU);
390
391
DISABLE;
392
}
393
394
void ArmJit::Comp_Cache(MIPSOpcode op) {
395
CONDITIONAL_DISABLE(LSU);
396
397
int func = (op >> 16) & 0x1F;
398
399
// See Int_Cache for the definitions.
400
switch (func) {
401
case 24: break;
402
case 25: break;
403
case 27: break;
404
case 30: break;
405
default:
406
// Fall back to the interpreter.
407
DISABLE;
408
}
409
}
410
}
411
412
#endif // PPSSPP_ARCH(ARM)
413
414