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