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/ARM64/Arm64IRCompLoadStore.cpp
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
#include "ppsspp_config.h"
19
// In other words, PPSSPP_ARCH(ARM64) || DISASM_ALL.
20
#if PPSSPP_ARCH(ARM64) || (PPSSPP_PLATFORM(WINDOWS) && !defined(__LIBRETRO__))
21
22
#include "Core/MemMap.h"
23
#include "Core/MIPS/ARM64/Arm64IRJit.h"
24
#include "Core/MIPS/ARM64/Arm64IRRegCache.h"
25
26
// This file contains compilation for load/store instructions.
27
//
28
// All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly.
29
// Currently known non working ones should have DISABLE. No flags because that's in IR already.
30
31
// #define CONDITIONAL_DISABLE { CompIR_Generic(inst); return; }
32
#define CONDITIONAL_DISABLE {}
33
#define DISABLE { CompIR_Generic(inst); return; }
34
#define INVALIDOP { _assert_msg_(false, "Invalid IR inst %d", (int)inst.op); CompIR_Generic(inst); return; }
35
36
namespace MIPSComp {
37
38
using namespace Arm64Gen;
39
using namespace Arm64IRJitConstants;
40
41
static int IROpToByteWidth(IROp op) {
42
switch (op) {
43
case IROp::Load8:
44
case IROp::Load8Ext:
45
case IROp::Store8:
46
return 1;
47
48
case IROp::Load16:
49
case IROp::Load16Ext:
50
case IROp::Store16:
51
return 2;
52
53
case IROp::Load32:
54
case IROp::Load32Linked:
55
case IROp::Load32Left:
56
case IROp::Load32Right:
57
case IROp::LoadFloat:
58
case IROp::Store32:
59
case IROp::Store32Conditional:
60
case IROp::Store32Left:
61
case IROp::Store32Right:
62
case IROp::StoreFloat:
63
return 4;
64
65
case IROp::LoadVec4:
66
case IROp::StoreVec4:
67
return 16;
68
69
default:
70
_assert_msg_(false, "Unexpected op: %s", GetIRMeta(op) ? GetIRMeta(op)->name : "?");
71
return -1;
72
}
73
}
74
75
Arm64JitBackend::LoadStoreArg Arm64JitBackend::PrepareSrc1Address(IRInst inst) {
76
const IRMeta *m = GetIRMeta(inst.op);
77
78
bool src1IsPointer = regs_.IsGPRMappedAsPointer(inst.src1);
79
bool readsFromSrc1 = inst.src1 == inst.src3 && (m->flags & (IRFLAG_SRC3 | IRFLAG_SRC3DST)) != 0;
80
// If it's about to be clobbered, don't waste time pointerifying. Use displacement.
81
bool clobbersSrc1 = !readsFromSrc1 && regs_.IsGPRClobbered(inst.src1);
82
83
int64_t imm = (int32_t)inst.constant;
84
// It can't be this negative, must be a constant address with the top bit set.
85
if ((imm & 0xC0000000) == 0x80000000) {
86
imm = (uint64_t)(uint32_t)inst.constant;
87
}
88
89
LoadStoreArg addrArg;
90
if (inst.src1 == MIPS_REG_ZERO) {
91
// The constant gets applied later.
92
addrArg.base = MEMBASEREG;
93
#ifdef MASKED_PSP_MEMORY
94
imm &= Memory::MEMVIEW32_MASK;
95
#endif
96
} else if (!jo.enablePointerify && readsFromSrc1) {
97
#ifndef MASKED_PSP_MEMORY
98
if (imm == 0) {
99
addrArg.base = MEMBASEREG;
100
addrArg.regOffset = regs_.MapGPR(inst.src1);
101
addrArg.useRegisterOffset = true;
102
addrArg.signExtendRegOffset = false;
103
}
104
#endif
105
106
// Since we can't modify src1, let's just use a temp reg while copying.
107
if (!addrArg.useRegisterOffset) {
108
ADDI2R(SCRATCH1, regs_.MapGPR(inst.src1), imm, SCRATCH2);
109
#ifdef MASKED_PSP_MEMORY
110
ANDI2R(SCRATCH1, SCRATCH1, Memory::MEMVIEW32_MASK, SCRATCH2);
111
#endif
112
113
addrArg.base = MEMBASEREG;
114
addrArg.regOffset = SCRATCH1;
115
addrArg.useRegisterOffset = true;
116
addrArg.signExtendRegOffset = false;
117
}
118
} else if ((jo.cachePointers && !clobbersSrc1) || src1IsPointer) {
119
// The offset gets set later.
120
addrArg.base = regs_.MapGPRAsPointer(inst.src1);
121
} else {
122
ADDI2R(SCRATCH1, regs_.MapGPR(inst.src1), imm, SCRATCH2);
123
#ifdef MASKED_PSP_MEMORY
124
ANDI2R(SCRATCH1, SCRATCH1, Memory::MEMVIEW32_MASK, SCRATCH2);
125
#endif
126
127
addrArg.base = MEMBASEREG;
128
addrArg.regOffset = SCRATCH1;
129
addrArg.useRegisterOffset = true;
130
addrArg.signExtendRegOffset = false;
131
}
132
133
// That's src1 taken care of, and possibly imm.
134
// If useRegisterOffset is false, imm still needs to be accounted for.
135
if (!addrArg.useRegisterOffset && imm != 0) {
136
#ifdef MASKED_PSP_MEMORY
137
// In case we have an address + offset reg.
138
if (imm > 0)
139
imm &= Memory::MEMVIEW32_MASK;
140
#endif
141
142
int scale = IROpToByteWidth(inst.op);
143
if (imm > 0 && (imm & (scale - 1)) == 0 && imm <= 0xFFF * scale) {
144
// Okay great, use the LDR/STR form.
145
addrArg.immOffset = (int)imm;
146
addrArg.useUnscaled = false;
147
} else if (imm >= -256 && imm < 256) {
148
// An unscaled offset (LDUR/STUR) should work fine for this range.
149
addrArg.immOffset = (int)imm;
150
addrArg.useUnscaled = true;
151
} else {
152
// No luck, we'll need to load into a register.
153
MOVI2R(SCRATCH1, imm);
154
addrArg.regOffset = SCRATCH1;
155
addrArg.useRegisterOffset = true;
156
addrArg.signExtendRegOffset = true;
157
}
158
}
159
160
return addrArg;
161
}
162
163
void Arm64JitBackend::CompIR_CondStore(IRInst inst) {
164
CONDITIONAL_DISABLE;
165
if (inst.op != IROp::Store32Conditional)
166
INVALIDOP;
167
168
regs_.SpillLockGPR(IRREG_LLBIT, inst.src3, inst.src1);
169
LoadStoreArg addrArg = PrepareSrc1Address(inst);
170
ARM64Reg valueReg = regs_.MapGPR(inst.src3, MIPSMap::INIT);
171
172
regs_.MapGPR(IRREG_LLBIT, MIPSMap::INIT);
173
174
// TODO: Safe memory? Or enough to have crash handler + validate?
175
176
FixupBranch condFailed = CBZ(regs_.R(IRREG_LLBIT));
177
178
if (addrArg.useRegisterOffset) {
179
STR(valueReg, addrArg.base, ArithOption(addrArg.regOffset, false, addrArg.signExtendRegOffset));
180
} else if (addrArg.useUnscaled) {
181
STUR(valueReg, addrArg.base, addrArg.immOffset);
182
} else {
183
STR(INDEX_UNSIGNED, valueReg, addrArg.base, addrArg.immOffset);
184
}
185
186
if (inst.dest != MIPS_REG_ZERO) {
187
MOVI2R(regs_.R(inst.dest), 1);
188
FixupBranch finish = B();
189
190
SetJumpTarget(condFailed);
191
MOVI2R(regs_.R(inst.dest), 0);
192
SetJumpTarget(finish);
193
} else {
194
SetJumpTarget(condFailed);
195
}
196
}
197
198
void Arm64JitBackend::CompIR_FLoad(IRInst inst) {
199
CONDITIONAL_DISABLE;
200
201
LoadStoreArg addrArg = PrepareSrc1Address(inst);
202
203
switch (inst.op) {
204
case IROp::LoadFloat:
205
regs_.MapFPR(inst.dest, MIPSMap::NOINIT);
206
if (addrArg.useRegisterOffset) {
207
fp_.LDR(32, regs_.F(inst.dest), addrArg.base, ArithOption(addrArg.regOffset, false, addrArg.signExtendRegOffset));
208
} else if (addrArg.useUnscaled) {
209
fp_.LDUR(32, regs_.F(inst.dest), addrArg.base, addrArg.immOffset);
210
} else {
211
fp_.LDR(32, INDEX_UNSIGNED, regs_.F(inst.dest), addrArg.base, addrArg.immOffset);
212
}
213
break;
214
215
default:
216
INVALIDOP;
217
break;
218
}
219
}
220
221
void Arm64JitBackend::CompIR_FStore(IRInst inst) {
222
CONDITIONAL_DISABLE;
223
224
LoadStoreArg addrArg = PrepareSrc1Address(inst);
225
226
switch (inst.op) {
227
case IROp::StoreFloat:
228
regs_.MapFPR(inst.src3);
229
if (addrArg.useRegisterOffset) {
230
fp_.STR(32, regs_.F(inst.src3), addrArg.base, ArithOption(addrArg.regOffset, false, addrArg.signExtendRegOffset));
231
} else if (addrArg.useUnscaled) {
232
fp_.STUR(32, regs_.F(inst.src3), addrArg.base, addrArg.immOffset);
233
} else {
234
fp_.STR(32, INDEX_UNSIGNED, regs_.F(inst.src3), addrArg.base, addrArg.immOffset);
235
}
236
break;
237
238
default:
239
INVALIDOP;
240
break;
241
}
242
}
243
244
void Arm64JitBackend::CompIR_Load(IRInst inst) {
245
CONDITIONAL_DISABLE;
246
247
regs_.SpillLockGPR(inst.dest, inst.src1);
248
LoadStoreArg addrArg = PrepareSrc1Address(inst);
249
// With NOINIT, MapReg won't subtract MEMBASEREG even if dest == src1.
250
regs_.MapGPR(inst.dest, MIPSMap::NOINIT);
251
252
// TODO: Safe memory? Or enough to have crash handler + validate?
253
254
switch (inst.op) {
255
case IROp::Load8:
256
if (addrArg.useRegisterOffset) {
257
LDRB(regs_.R(inst.dest), addrArg.base, ArithOption(addrArg.regOffset, false, addrArg.signExtendRegOffset));
258
} else if (addrArg.useUnscaled) {
259
LDURB(regs_.R(inst.dest), addrArg.base, addrArg.immOffset);
260
} else {
261
LDRB(INDEX_UNSIGNED, regs_.R(inst.dest), addrArg.base, addrArg.immOffset);
262
}
263
break;
264
265
case IROp::Load8Ext:
266
if (addrArg.useRegisterOffset) {
267
LDRSB(regs_.R(inst.dest), addrArg.base, ArithOption(addrArg.regOffset, false, addrArg.signExtendRegOffset));
268
} else if (addrArg.useUnscaled) {
269
LDURSB(regs_.R(inst.dest), addrArg.base, addrArg.immOffset);
270
} else {
271
LDRSB(INDEX_UNSIGNED, regs_.R(inst.dest), addrArg.base, addrArg.immOffset);
272
}
273
break;
274
275
case IROp::Load16:
276
if (addrArg.useRegisterOffset) {
277
LDRH(regs_.R(inst.dest), addrArg.base, ArithOption(addrArg.regOffset, false, addrArg.signExtendRegOffset));
278
} else if (addrArg.useUnscaled) {
279
LDURH(regs_.R(inst.dest), addrArg.base, addrArg.immOffset);
280
} else {
281
LDRH(INDEX_UNSIGNED, regs_.R(inst.dest), addrArg.base, addrArg.immOffset);
282
}
283
break;
284
285
case IROp::Load16Ext:
286
if (addrArg.useRegisterOffset) {
287
LDRSH(regs_.R(inst.dest), addrArg.base, ArithOption(addrArg.regOffset, false, addrArg.signExtendRegOffset));
288
} else if (addrArg.useUnscaled) {
289
LDURSH(regs_.R(inst.dest), addrArg.base, addrArg.immOffset);
290
} else {
291
LDRSH(INDEX_UNSIGNED, regs_.R(inst.dest), addrArg.base, addrArg.immOffset);
292
}
293
break;
294
295
case IROp::Load32:
296
if (addrArg.useRegisterOffset) {
297
LDR(regs_.R(inst.dest), addrArg.base, ArithOption(addrArg.regOffset, false, addrArg.signExtendRegOffset));
298
} else if (addrArg.useUnscaled) {
299
LDUR(regs_.R(inst.dest), addrArg.base, addrArg.immOffset);
300
} else {
301
LDR(INDEX_UNSIGNED, regs_.R(inst.dest), addrArg.base, addrArg.immOffset);
302
}
303
break;
304
305
case IROp::Load32Linked:
306
if (inst.dest != MIPS_REG_ZERO) {
307
if (addrArg.useRegisterOffset) {
308
LDR(regs_.R(inst.dest), addrArg.base, ArithOption(addrArg.regOffset, false, addrArg.signExtendRegOffset));
309
} else if (addrArg.useUnscaled) {
310
LDUR(regs_.R(inst.dest), addrArg.base, addrArg.immOffset);
311
} else {
312
LDR(INDEX_UNSIGNED, regs_.R(inst.dest), addrArg.base, addrArg.immOffset);
313
}
314
}
315
regs_.SetGPRImm(IRREG_LLBIT, 1);
316
break;
317
318
default:
319
INVALIDOP;
320
break;
321
}
322
}
323
324
void Arm64JitBackend::CompIR_LoadShift(IRInst inst) {
325
CONDITIONAL_DISABLE;
326
327
switch (inst.op) {
328
case IROp::Load32Left:
329
case IROp::Load32Right:
330
// Should not happen if the pass to split is active.
331
DISABLE;
332
break;
333
334
default:
335
INVALIDOP;
336
break;
337
}
338
}
339
340
void Arm64JitBackend::CompIR_Store(IRInst inst) {
341
CONDITIONAL_DISABLE;
342
343
regs_.SpillLockGPR(inst.src3, inst.src1);
344
LoadStoreArg addrArg = PrepareSrc1Address(inst);
345
346
ARM64Reg valueReg = regs_.TryMapTempImm(inst.src3);
347
if (valueReg == INVALID_REG)
348
valueReg = regs_.MapGPR(inst.src3);
349
350
// TODO: Safe memory? Or enough to have crash handler + validate?
351
352
switch (inst.op) {
353
case IROp::Store8:
354
if (addrArg.useRegisterOffset) {
355
STRB(valueReg, addrArg.base, ArithOption(addrArg.regOffset, false, addrArg.signExtendRegOffset));
356
} else if (addrArg.useUnscaled) {
357
STURB(valueReg, addrArg.base, addrArg.immOffset);
358
} else {
359
STRB(INDEX_UNSIGNED, valueReg, addrArg.base, addrArg.immOffset);
360
}
361
break;
362
363
case IROp::Store16:
364
if (addrArg.useRegisterOffset) {
365
STRH(valueReg, addrArg.base, ArithOption(addrArg.regOffset, false, addrArg.signExtendRegOffset));
366
} else if (addrArg.useUnscaled) {
367
STURH(valueReg, addrArg.base, addrArg.immOffset);
368
} else {
369
STRH(INDEX_UNSIGNED, valueReg, addrArg.base, addrArg.immOffset);
370
}
371
break;
372
373
case IROp::Store32:
374
if (addrArg.useRegisterOffset) {
375
STR(valueReg, addrArg.base, ArithOption(addrArg.regOffset, false, addrArg.signExtendRegOffset));
376
} else if (addrArg.useUnscaled) {
377
STUR(valueReg, addrArg.base, addrArg.immOffset);
378
} else {
379
STR(INDEX_UNSIGNED, valueReg, addrArg.base, addrArg.immOffset);
380
}
381
break;
382
383
default:
384
INVALIDOP;
385
break;
386
}
387
}
388
389
void Arm64JitBackend::CompIR_StoreShift(IRInst inst) {
390
CONDITIONAL_DISABLE;
391
392
switch (inst.op) {
393
case IROp::Store32Left:
394
case IROp::Store32Right:
395
// Should not happen if the pass to split is active.
396
DISABLE;
397
break;
398
399
default:
400
INVALIDOP;
401
break;
402
}
403
}
404
405
void Arm64JitBackend::CompIR_VecLoad(IRInst inst) {
406
CONDITIONAL_DISABLE;
407
408
LoadStoreArg addrArg = PrepareSrc1Address(inst);
409
410
switch (inst.op) {
411
case IROp::LoadVec4:
412
regs_.MapVec4(inst.dest, MIPSMap::NOINIT);
413
if (addrArg.useRegisterOffset) {
414
fp_.LDR(128, regs_.FQ(inst.dest), addrArg.base, ArithOption(addrArg.regOffset, false, addrArg.signExtendRegOffset));
415
} else if (addrArg.useUnscaled) {
416
fp_.LDUR(128, regs_.FQ(inst.dest), addrArg.base, addrArg.immOffset);
417
} else {
418
fp_.LDR(128, INDEX_UNSIGNED, regs_.FQ(inst.dest), addrArg.base, addrArg.immOffset);
419
}
420
break;
421
422
default:
423
INVALIDOP;
424
break;
425
}
426
}
427
428
void Arm64JitBackend::CompIR_VecStore(IRInst inst) {
429
CONDITIONAL_DISABLE;
430
431
LoadStoreArg addrArg = PrepareSrc1Address(inst);
432
433
switch (inst.op) {
434
case IROp::StoreVec4:
435
regs_.MapVec4(inst.src3);
436
if (addrArg.useRegisterOffset) {
437
fp_.STR(128, regs_.FQ(inst.src3), addrArg.base, ArithOption(addrArg.regOffset, false, addrArg.signExtendRegOffset));
438
} else if (addrArg.useUnscaled) {
439
fp_.STUR(128, regs_.FQ(inst.src3), addrArg.base, addrArg.immOffset);
440
} else {
441
fp_.STR(128, INDEX_UNSIGNED, regs_.FQ(inst.src3), addrArg.base, addrArg.immOffset);
442
}
443
break;
444
445
default:
446
INVALIDOP;
447
break;
448
}
449
}
450
451
} // namespace MIPSComp
452
453
#endif
454
455