psxmem: Add support for Lightrec's custom mem init sequence
[pcsx_rearmed.git] / deps / lightrec / regcache.c
CommitLineData
98fa08a5 1// SPDX-License-Identifier: LGPL-2.1-or-later
d16005f8 2/*
98fa08a5 3 * Copyright (C) 2014-2021 Paul Cercueil <paul@crapouillou.net>
d16005f8
PC
4 */
5
6#include "debug.h"
7#include "memmanager.h"
98fa08a5 8#include "lightning-wrapper.h"
d16005f8
PC
9#include "regcache.h"
10
d16005f8
PC
11#include <stdbool.h>
12#include <stddef.h>
13
14struct native_register {
98fa08a5
PC
15 bool used, loaded, dirty, output, extend, extended,
16 zero_extend, zero_extended, locked;
d16005f8
PC
17 s8 emulated_register;
18};
19
20struct regcache {
21 struct lightrec_state *state;
22 struct native_register lightrec_regs[NUM_REGS + NUM_TEMPS];
23};
24
25static const char * mips_regs[] = {
26 "zero",
27 "at",
28 "v0", "v1",
29 "a0", "a1", "a2", "a3",
30 "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7",
31 "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
32 "t8", "t9",
33 "k0", "k1",
34 "gp", "sp", "fp", "ra",
35 "lo", "hi",
36};
37
38const char * lightrec_reg_name(u8 reg)
39{
40 return mips_regs[reg];
41}
42
98fa08a5
PC
43static inline bool lightrec_reg_is_zero(u8 jit_reg)
44{
45#if defined(__mips__) || defined(__alpha__) || defined(__riscv)
46 if (jit_reg == _ZERO)
47 return true;
48#endif
49 return false;
50}
51
52static inline s8 lightrec_get_hardwired_reg(u8 reg)
53{
54#if defined(__mips__) || defined(__alpha__) || defined(__riscv)
55 if (reg == 0)
56 return _ZERO;
57#endif
58 return -1;
59}
60
d16005f8
PC
61static inline u8 lightrec_reg_number(const struct regcache *cache,
62 const struct native_register *nreg)
63{
64 return (u8) (((uintptr_t) nreg - (uintptr_t) cache->lightrec_regs)
65 / sizeof(*nreg));
66}
67
68static inline u8 lightrec_reg_to_lightning(const struct regcache *cache,
69 const struct native_register *nreg)
70{
71 u8 offset = lightrec_reg_number(cache, nreg);
72 return offset < NUM_REGS ? JIT_V(offset) : JIT_R(offset - NUM_REGS);
73}
74
75static inline struct native_register * lightning_reg_to_lightrec(
76 struct regcache *cache, u8 reg)
77{
78 if ((JIT_V0 > JIT_R0 && reg >= JIT_V0) ||
79 (JIT_V0 < JIT_R0 && reg < JIT_R0)) {
80 if (JIT_V1 > JIT_V0)
81 return &cache->lightrec_regs[reg - JIT_V0];
82 else
83 return &cache->lightrec_regs[JIT_V0 - reg];
84 } else {
85 if (JIT_R1 > JIT_R0)
86 return &cache->lightrec_regs[NUM_REGS + reg - JIT_R0];
87 else
88 return &cache->lightrec_regs[NUM_REGS + JIT_R0 - reg];
89 }
90}
91
98fa08a5
PC
92u8 lightrec_get_reg_in_flags(struct regcache *cache, u8 jit_reg)
93{
94 struct native_register *reg;
95 u8 flags = 0;
96
97 if (lightrec_reg_is_zero(jit_reg))
98 return REG_EXT | REG_ZEXT;
99
100 reg = lightning_reg_to_lightrec(cache, jit_reg);
101 if (reg->extended)
102 flags |= REG_EXT;
103 if (reg->zero_extended)
104 flags |= REG_ZEXT;
105
106 return flags;
107}
108
109void lightrec_set_reg_out_flags(struct regcache *cache, u8 jit_reg, u8 flags)
110{
111 struct native_register *reg;
112
113 if (!lightrec_reg_is_zero(jit_reg)) {
114 reg = lightning_reg_to_lightrec(cache, jit_reg);
115 reg->extend = flags & REG_EXT;
116 reg->zero_extend = flags & REG_ZEXT;
117 }
118}
119
d16005f8
PC
120static struct native_register * alloc_temp(struct regcache *cache)
121{
122 unsigned int i;
123
124 /* We search the register list in reverse order. As temporaries are
125 * meant to be used only in the emitter functions, they can be mapped to
126 * caller-saved registers, as they won't have to be saved back to
127 * memory. */
128 for (i = ARRAY_SIZE(cache->lightrec_regs); i; i--) {
129 struct native_register *nreg = &cache->lightrec_regs[i - 1];
130 if (!nreg->used && !nreg->loaded && !nreg->dirty)
131 return nreg;
132 }
133
134 for (i = ARRAY_SIZE(cache->lightrec_regs); i; i--) {
135 struct native_register *nreg = &cache->lightrec_regs[i - 1];
136 if (!nreg->used)
137 return nreg;
138 }
139
140 return NULL;
141}
142
143static struct native_register * find_mapped_reg(struct regcache *cache,
144 u8 reg, bool out)
145{
146 unsigned int i;
147
148 for (i = 0; i < ARRAY_SIZE(cache->lightrec_regs); i++) {
149 struct native_register *nreg = &cache->lightrec_regs[i];
150 if ((!reg || nreg->loaded || nreg->dirty) &&
151 nreg->emulated_register == reg &&
152 (!out || !nreg->locked))
153 return nreg;
154 }
155
156 return NULL;
157}
158
159static struct native_register * alloc_in_out(struct regcache *cache,
160 u8 reg, bool out)
161{
162 struct native_register *nreg;
163 unsigned int i;
164
165 /* Try to find if the register is already mapped somewhere */
166 nreg = find_mapped_reg(cache, reg, out);
167 if (nreg)
168 return nreg;
169
170 /* Try to allocate a non-dirty, non-loaded register.
171 * Loaded registers may be re-used later, so it's better to avoid
172 * re-using one if possible. */
173 for (i = 0; i < ARRAY_SIZE(cache->lightrec_regs); i++) {
174 nreg = &cache->lightrec_regs[i];
175 if (!nreg->used && !nreg->dirty && !nreg->loaded)
176 return nreg;
177 }
178
179 /* Try to allocate a non-dirty register */
180 for (i = 0; i < ARRAY_SIZE(cache->lightrec_regs); i++) {
181 nreg = &cache->lightrec_regs[i];
182 if (!nreg->used && !nreg->dirty)
183 return nreg;
184 }
185
186 for (i = 0; i < ARRAY_SIZE(cache->lightrec_regs); i++) {
187 nreg = &cache->lightrec_regs[i];
188 if (!nreg->used)
189 return nreg;
190 }
191
192 return NULL;
193}
194
195static void lightrec_discard_nreg(struct native_register *nreg)
196{
197 nreg->extended = false;
98fa08a5 198 nreg->zero_extended = false;
d16005f8
PC
199 nreg->loaded = false;
200 nreg->output = false;
201 nreg->dirty = false;
202 nreg->used = false;
203 nreg->locked = false;
204 nreg->emulated_register = -1;
205}
206
207static void lightrec_unload_nreg(struct regcache *cache, jit_state_t *_jit,
208 struct native_register *nreg, u8 jit_reg)
209{
210 /* If we get a dirty register, store back the old value */
211 if (nreg->dirty) {
98fa08a5 212 s16 offset = offsetof(struct lightrec_state, regs.gpr)
d16005f8
PC
213 + (nreg->emulated_register << 2);
214
215 jit_stxi_i(offset, LIGHTREC_REG_STATE, jit_reg);
216 }
217
218 lightrec_discard_nreg(nreg);
219}
220
221void lightrec_unload_reg(struct regcache *cache, jit_state_t *_jit, u8 jit_reg)
222{
98fa08a5
PC
223 if (lightrec_reg_is_zero(jit_reg))
224 return;
225
d16005f8
PC
226 lightrec_unload_nreg(cache, _jit,
227 lightning_reg_to_lightrec(cache, jit_reg), jit_reg);
228}
229
230/* lightrec_lock_reg: the register will be cleaned if dirty, then locked.
231 * A locked register cannot only be used as input, not output. */
232void lightrec_lock_reg(struct regcache *cache, jit_state_t *_jit, u8 jit_reg)
233{
98fa08a5
PC
234 struct native_register *reg;
235
236 if (lightrec_reg_is_zero(jit_reg))
237 return;
d16005f8 238
98fa08a5 239 reg = lightning_reg_to_lightrec(cache, jit_reg);
d16005f8
PC
240 lightrec_clean_reg(cache, _jit, jit_reg);
241
242 reg->locked = true;
243}
244
245u8 lightrec_alloc_reg(struct regcache *cache, jit_state_t *_jit, u8 jit_reg)
246{
98fa08a5 247 struct native_register *reg;
d16005f8 248
98fa08a5
PC
249 if (lightrec_reg_is_zero(jit_reg))
250 return jit_reg;
251
252 reg = lightning_reg_to_lightrec(cache, jit_reg);
d16005f8
PC
253 lightrec_unload_nreg(cache, _jit, reg, jit_reg);
254
255 reg->used = true;
256 return jit_reg;
257}
258
259u8 lightrec_alloc_reg_temp(struct regcache *cache, jit_state_t *_jit)
260{
261 u8 jit_reg;
262 struct native_register *nreg = alloc_temp(cache);
263 if (!nreg) {
264 /* No free register, no dirty register to free. */
265 pr_err("No more registers! Abandon ship!\n");
266 return 0;
267 }
268
269 jit_reg = lightrec_reg_to_lightning(cache, nreg);
270 lightrec_unload_nreg(cache, _jit, nreg, jit_reg);
271
272 nreg->used = true;
273 return jit_reg;
274}
275
98fa08a5
PC
276u8 lightrec_alloc_reg_out(struct regcache *cache, jit_state_t *_jit,
277 u8 reg, u8 flags)
d16005f8 278{
98fa08a5 279 struct native_register *nreg;
d16005f8 280 u8 jit_reg;
98fa08a5
PC
281 s8 hw_reg;
282
283 hw_reg = lightrec_get_hardwired_reg(reg);
284 if (hw_reg >= 0)
285 return (u8) hw_reg;
286
287 nreg = alloc_in_out(cache, reg, true);
d16005f8
PC
288 if (!nreg) {
289 /* No free register, no dirty register to free. */
290 pr_err("No more registers! Abandon ship!\n");
291 return 0;
292 }
293
294 jit_reg = lightrec_reg_to_lightning(cache, nreg);
295
296 /* If we get a dirty register that doesn't correspond to the one
297 * we're requesting, store back the old value */
298 if (nreg->emulated_register != reg)
299 lightrec_unload_nreg(cache, _jit, nreg, jit_reg);
300
d16005f8
PC
301 nreg->used = true;
302 nreg->output = true;
303 nreg->emulated_register = reg;
98fa08a5
PC
304 nreg->extend = flags & REG_EXT;
305 nreg->zero_extend = flags & REG_ZEXT;
d16005f8
PC
306 return jit_reg;
307}
308
98fa08a5
PC
309u8 lightrec_alloc_reg_in(struct regcache *cache, jit_state_t *_jit,
310 u8 reg, u8 flags)
d16005f8 311{
98fa08a5 312 struct native_register *nreg;
d16005f8
PC
313 u8 jit_reg;
314 bool reg_changed;
98fa08a5
PC
315 s8 hw_reg;
316
317 hw_reg = lightrec_get_hardwired_reg(reg);
318 if (hw_reg >= 0)
319 return (u8) hw_reg;
320
321 nreg = alloc_in_out(cache, reg, false);
d16005f8
PC
322 if (!nreg) {
323 /* No free register, no dirty register to free. */
324 pr_err("No more registers! Abandon ship!\n");
325 return 0;
326 }
327
328 jit_reg = lightrec_reg_to_lightning(cache, nreg);
329
330 /* If we get a dirty register that doesn't correspond to the one
331 * we're requesting, store back the old value */
332 reg_changed = nreg->emulated_register != reg;
333 if (reg_changed)
334 lightrec_unload_nreg(cache, _jit, nreg, jit_reg);
335
336 if (!nreg->loaded && !nreg->dirty && reg != 0) {
98fa08a5 337 s16 offset = offsetof(struct lightrec_state, regs.gpr)
d16005f8
PC
338 + (reg << 2);
339
98fa08a5
PC
340 nreg->zero_extended = flags & REG_ZEXT;
341 nreg->extended = !nreg->zero_extended;
342
d16005f8 343 /* Load previous value from register cache */
98fa08a5
PC
344 if (nreg->zero_extended)
345 jit_ldxi_ui(jit_reg, LIGHTREC_REG_STATE, offset);
346 else
347 jit_ldxi_i(jit_reg, LIGHTREC_REG_STATE, offset);
348
d16005f8 349 nreg->loaded = true;
d16005f8
PC
350 }
351
352 /* Clear register r0 before use */
353 if (reg == 0 && (!nreg->loaded || nreg->dirty)) {
354 jit_movi(jit_reg, 0);
355 nreg->extended = true;
98fa08a5 356 nreg->zero_extended = true;
d16005f8
PC
357 nreg->loaded = true;
358 }
359
360 nreg->used = true;
361 nreg->output = false;
362 nreg->emulated_register = reg;
d16005f8 363
98fa08a5
PC
364 if ((flags & REG_EXT) && !nreg->extended &&
365 (!nreg->zero_extended || !(flags & REG_ZEXT))) {
d16005f8 366 nreg->extended = true;
98fa08a5 367 nreg->zero_extended = false;
d16005f8 368 jit_extr_i(jit_reg, jit_reg);
98fa08a5
PC
369 } else if (!(flags & REG_EXT) && (flags & REG_ZEXT) &&
370 !nreg->zero_extended) {
371 nreg->zero_extended = true;
372 nreg->extended = false;
373 jit_extr_ui(jit_reg, jit_reg);
d16005f8 374 }
d16005f8
PC
375
376 return jit_reg;
377}
378
379u8 lightrec_request_reg_in(struct regcache *cache, jit_state_t *_jit,
380 u8 reg, u8 jit_reg)
381{
382 struct native_register *nreg;
383 u16 offset;
384
385 nreg = find_mapped_reg(cache, reg, false);
386 if (nreg) {
387 jit_reg = lightrec_reg_to_lightning(cache, nreg);
388 nreg->used = true;
389 return jit_reg;
390 }
391
392 nreg = lightning_reg_to_lightrec(cache, jit_reg);
393 lightrec_unload_nreg(cache, _jit, nreg, jit_reg);
394
395 /* Load previous value from register cache */
98fa08a5 396 offset = offsetof(struct lightrec_state, regs.gpr) + (reg << 2);
d16005f8
PC
397 jit_ldxi_i(jit_reg, LIGHTREC_REG_STATE, offset);
398
399 nreg->extended = true;
98fa08a5 400 nreg->zero_extended = false;
d16005f8
PC
401 nreg->used = true;
402 nreg->loaded = true;
403 nreg->emulated_register = reg;
404
405 return jit_reg;
406}
407
408static void free_reg(struct native_register *nreg)
409{
410 /* Set output registers as dirty */
411 if (nreg->used && nreg->output && nreg->emulated_register > 0)
412 nreg->dirty = true;
98fa08a5 413 if (nreg->output) {
d16005f8 414 nreg->extended = nreg->extend;
98fa08a5
PC
415 nreg->zero_extended = nreg->zero_extend;
416 }
d16005f8
PC
417 nreg->used = false;
418}
419
420void lightrec_free_reg(struct regcache *cache, u8 jit_reg)
421{
98fa08a5
PC
422 if (!lightrec_reg_is_zero(jit_reg))
423 free_reg(lightning_reg_to_lightrec(cache, jit_reg));
d16005f8
PC
424}
425
426void lightrec_free_regs(struct regcache *cache)
427{
428 unsigned int i;
429
430 for (i = 0; i < ARRAY_SIZE(cache->lightrec_regs); i++)
431 free_reg(&cache->lightrec_regs[i]);
432}
433
434static void clean_reg(jit_state_t *_jit,
435 struct native_register *nreg, u8 jit_reg, bool clean)
436{
437 if (nreg->dirty) {
98fa08a5 438 s16 offset = offsetof(struct lightrec_state, regs.gpr)
d16005f8
PC
439 + (nreg->emulated_register << 2);
440
441 jit_stxi_i(offset, LIGHTREC_REG_STATE, jit_reg);
442 nreg->loaded |= nreg->dirty;
443 nreg->dirty ^= clean;
444 }
445}
446
447static void clean_regs(struct regcache *cache, jit_state_t *_jit, bool clean)
448{
449 unsigned int i;
450
451 for (i = 0; i < NUM_REGS; i++)
452 clean_reg(_jit, &cache->lightrec_regs[i], JIT_V(i), clean);
453 for (i = 0; i < NUM_TEMPS; i++) {
454 clean_reg(_jit, &cache->lightrec_regs[i + NUM_REGS],
455 JIT_R(i), clean);
456 }
457}
458
459void lightrec_storeback_regs(struct regcache *cache, jit_state_t *_jit)
460{
461 clean_regs(cache, _jit, false);
462}
463
464void lightrec_clean_regs(struct regcache *cache, jit_state_t *_jit)
465{
466 clean_regs(cache, _jit, true);
467}
468
469void lightrec_clean_reg(struct regcache *cache, jit_state_t *_jit, u8 jit_reg)
470{
98fa08a5
PC
471 struct native_register *reg;
472
473 if (!lightrec_reg_is_zero(jit_reg)) {
474 reg = lightning_reg_to_lightrec(cache, jit_reg);
475 clean_reg(_jit, reg, jit_reg, true);
476 }
d16005f8
PC
477}
478
479void lightrec_clean_reg_if_loaded(struct regcache *cache, jit_state_t *_jit,
480 u8 reg, bool unload)
481{
482 struct native_register *nreg;
483 u8 jit_reg;
484
485 nreg = find_mapped_reg(cache, reg, false);
486 if (nreg) {
487 jit_reg = lightrec_reg_to_lightning(cache, nreg);
488
489 if (unload)
490 lightrec_unload_nreg(cache, _jit, nreg, jit_reg);
491 else
492 clean_reg(_jit, nreg, jit_reg, true);
493 }
494}
495
496struct native_register * lightrec_regcache_enter_branch(struct regcache *cache)
497{
498 struct native_register *backup;
499
500 backup = lightrec_malloc(cache->state, MEM_FOR_LIGHTREC,
501 sizeof(cache->lightrec_regs));
502 memcpy(backup, &cache->lightrec_regs, sizeof(cache->lightrec_regs));
503
504 return backup;
505}
506
507void lightrec_regcache_leave_branch(struct regcache *cache,
508 struct native_register *regs)
509{
510 memcpy(&cache->lightrec_regs, regs, sizeof(cache->lightrec_regs));
511 lightrec_free(cache->state, MEM_FOR_LIGHTREC,
512 sizeof(cache->lightrec_regs), regs);
513}
514
515void lightrec_regcache_reset(struct regcache *cache)
516{
517 memset(&cache->lightrec_regs, 0, sizeof(cache->lightrec_regs));
518}
519
520struct regcache * lightrec_regcache_init(struct lightrec_state *state)
521{
522 struct regcache *cache;
523
524 cache = lightrec_calloc(state, MEM_FOR_LIGHTREC, sizeof(*cache));
525 if (!cache)
526 return NULL;
527
528 cache->state = state;
529
530 return cache;
531}
532
533void lightrec_free_regcache(struct regcache *cache)
534{
535 return lightrec_free(cache->state, MEM_FOR_LIGHTREC,
536 sizeof(*cache), cache);
537}
538
539void lightrec_regcache_mark_live(struct regcache *cache, jit_state_t *_jit)
540{
541 struct native_register *nreg;
542 unsigned int i;
543
544#ifdef _WIN32
545 /* FIXME: GNU Lightning on Windows seems to use our mapped registers as
546 * temporaries. Until the actual bug is found and fixed, unconditionally
547 * mark our registers as live here. */
548 for (i = 0; i < NUM_REGS; i++) {
549 nreg = &cache->lightrec_regs[i];
550
551 if (nreg->used || nreg->loaded || nreg->dirty)
552 jit_live(JIT_V(i));
553 }
554#endif
555
556 for (i = 0; i < NUM_TEMPS; i++) {
557 nreg = &cache->lightrec_regs[NUM_REGS + i];
558
559 if (nreg->used || nreg->loaded || nreg->dirty)
560 jit_live(JIT_R(i));
561 }
562}