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