Update lightrec 20220910 (#686)
[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 enum 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
24 struct native_register {
25         bool used, output, extend, extended,
26              zero_extend, zero_extended, locked;
27         s8 emulated_register;
28         intptr_t value;
29         enum reg_priority prio;
30 };
31
32 struct regcache {
33         struct lightrec_state *state;
34         struct native_register lightrec_regs[NUM_REGS + NUM_TEMPS];
35 };
36
37 static 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
50 const char * lightrec_reg_name(u8 reg)
51 {
52         return mips_regs[reg];
53 }
54
55 static 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
64 static 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
73 static 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
80 static 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);
84
85         if (offset < NUM_REGS)
86                 return JIT_V(FIRST_REG + offset);
87         else
88                 return JIT_R(FIRST_TEMP + offset - NUM_REGS);
89 }
90
91 static 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)
97                         return &cache->lightrec_regs[reg - JIT_V(FIRST_REG)];
98                 else
99                         return &cache->lightrec_regs[JIT_V(FIRST_REG) - reg];
100         } else {
101                 if (JIT_R1 > JIT_R0)
102                         return &cache->lightrec_regs[NUM_REGS + reg - JIT_R(FIRST_TEMP)];
103                 else
104                         return &cache->lightrec_regs[NUM_REGS + JIT_R(FIRST_TEMP) - reg];
105         }
106 }
107
108 u8 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
125 void 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
136 static struct native_register * alloc_temp(struct regcache *cache)
137 {
138         struct native_register *elm, *nreg = NULL;
139         enum reg_priority best = REG_NB_PRIORITIES;
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--) {
147                 elm = &cache->lightrec_regs[i - 1];
148
149                 if (!elm->used && elm->prio < best) {
150                         nreg = elm;
151                         best = elm->prio;
152
153                         if (best == REG_IS_TEMP)
154                                 break;
155                 }
156         }
157
158         return nreg;
159 }
160
161 static 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];
168                 if ((nreg->prio >= REG_IS_ZERO) &&
169                     nreg->emulated_register == reg &&
170                     (!out || !nreg->locked))
171                         return nreg;
172         }
173
174         return NULL;
175 }
176
177 static struct native_register * alloc_in_out(struct regcache *cache,
178                                              u8 reg, bool out)
179 {
180         struct native_register *elm, *nreg = NULL;
181         enum reg_priority best = REG_NB_PRIORITIES;
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
189         nreg = NULL;
190
191         for (i = 0; i < ARRAY_SIZE(cache->lightrec_regs); i++) {
192                 elm = &cache->lightrec_regs[i];
193
194                 if (!elm->used && elm->prio < best) {
195                         nreg = elm;
196                         best = elm->prio;
197
198                         if (best == REG_IS_TEMP)
199                                 break;
200                 }
201         }
202
203         return nreg;
204 }
205
206 static void lightrec_discard_nreg(struct native_register *nreg)
207 {
208         nreg->extended = false;
209         nreg->zero_extended = false;
210         nreg->output = false;
211         nreg->used = false;
212         nreg->locked = false;
213         nreg->emulated_register = -1;
214         nreg->prio = 0;
215 }
216
217 static 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 */
221         if (nreg->prio == REG_IS_DIRTY) {
222                 s16 offset = offsetof(struct lightrec_state, regs.gpr)
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
231 void lightrec_unload_reg(struct regcache *cache, jit_state_t *_jit, u8 jit_reg)
232 {
233         if (lightrec_reg_is_zero(jit_reg))
234                 return;
235
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. */
242 void lightrec_lock_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;
248
249         reg = lightning_reg_to_lightrec(cache, jit_reg);
250         lightrec_clean_reg(cache, _jit, jit_reg);
251
252         reg->locked = true;
253 }
254
255 u8 lightrec_alloc_reg(struct regcache *cache, jit_state_t *_jit, u8 jit_reg)
256 {
257         struct native_register *reg;
258
259         if (lightrec_reg_is_zero(jit_reg))
260                 return jit_reg;
261
262         reg = lightning_reg_to_lightrec(cache, jit_reg);
263         lightrec_unload_nreg(cache, _jit, reg, jit_reg);
264
265         reg->used = true;
266         reg->prio = REG_IS_LOADED;
267         return jit_reg;
268 }
269
270 u8 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
283         nreg->prio = REG_IS_TEMP;
284         nreg->used = true;
285         return jit_reg;
286 }
287
288 s8 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
305 void 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
315 u8 lightrec_alloc_reg_out(struct regcache *cache, jit_state_t *_jit,
316                           u8 reg, u8 flags)
317 {
318         struct native_register *nreg;
319         u8 jit_reg;
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);
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
340         nreg->used = true;
341         nreg->output = true;
342         nreg->emulated_register = reg;
343         nreg->extend = flags & REG_EXT;
344         nreg->zero_extend = flags & REG_ZEXT;
345         nreg->prio = reg ? REG_IS_LOADED : REG_IS_ZERO;
346         return jit_reg;
347 }
348
349 u8 lightrec_alloc_reg_in(struct regcache *cache, jit_state_t *_jit,
350                          u8 reg, u8 flags)
351 {
352         struct native_register *nreg;
353         u8 jit_reg;
354         bool reg_changed;
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);
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
376         if (nreg->prio < REG_IS_LOADED && reg != 0) {
377                 s16 offset = offsetof(struct lightrec_state, regs.gpr)
378                         + (reg << 2);
379
380                 nreg->zero_extended = flags & REG_ZEXT;
381                 nreg->extended = !nreg->zero_extended;
382
383                 /* Load previous value from register cache */
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
389                 nreg->prio = REG_IS_LOADED;
390         }
391
392         /* Clear register r0 before use */
393         if (reg == 0 && nreg->prio != REG_IS_ZERO) {
394                 jit_movi(jit_reg, 0);
395                 nreg->extended = true;
396                 nreg->zero_extended = true;
397                 nreg->prio = REG_IS_ZERO;
398         }
399
400         nreg->used = true;
401         nreg->output = false;
402         nreg->emulated_register = reg;
403
404         if ((flags & REG_EXT) && !nreg->extended &&
405             (!nreg->zero_extended || !(flags & REG_ZEXT))) {
406                 nreg->extended = true;
407                 nreg->zero_extended = false;
408                 jit_extr_i(jit_reg, jit_reg);
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);
414         }
415
416         return jit_reg;
417 }
418
419 u8 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 */
436         offset = offsetof(struct lightrec_state, regs.gpr) + (reg << 2);
437         jit_ldxi_i(jit_reg, LIGHTREC_REG_STATE, offset);
438
439         nreg->extended = true;
440         nreg->zero_extended = false;
441         nreg->used = true;
442         nreg->emulated_register = reg;
443         nreg->prio = REG_IS_LOADED;
444
445         return jit_reg;
446 }
447
448 static void free_reg(struct native_register *nreg)
449 {
450         /* Set output registers as dirty */
451         if (nreg->used && nreg->output && nreg->emulated_register > 0)
452                 nreg->prio = REG_IS_DIRTY;
453         if (nreg->output) {
454                 nreg->extended = nreg->extend;
455                 nreg->zero_extended = nreg->zero_extend;
456         }
457         nreg->used = false;
458 }
459
460 void lightrec_free_reg(struct regcache *cache, u8 jit_reg)
461 {
462         if (!lightrec_reg_is_zero(jit_reg))
463                 free_reg(lightning_reg_to_lightrec(cache, jit_reg));
464 }
465
466 void 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
474 static void clean_reg(jit_state_t *_jit,
475                 struct native_register *nreg, u8 jit_reg, bool clean)
476 {
477         if (nreg->prio == REG_IS_DIRTY) {
478                 s16 offset = offsetof(struct lightrec_state, regs.gpr)
479                         + (nreg->emulated_register << 2);
480
481                 jit_stxi_i(offset, LIGHTREC_REG_STATE, jit_reg);
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                 }
489         }
490 }
491
492 static void clean_regs(struct regcache *cache, jit_state_t *_jit, bool clean)
493 {
494         unsigned int i;
495
496         for (i = 0; i < NUM_REGS; i++) {
497                 clean_reg(_jit, &cache->lightrec_regs[i],
498                           JIT_V(FIRST_REG + i), clean);
499         }
500         for (i = 0; i < NUM_TEMPS; i++) {
501                 clean_reg(_jit, &cache->lightrec_regs[i + NUM_REGS],
502                                 JIT_R(FIRST_TEMP + i), clean);
503         }
504 }
505
506 void lightrec_storeback_regs(struct regcache *cache, jit_state_t *_jit)
507 {
508         clean_regs(cache, _jit, false);
509 }
510
511 void lightrec_clean_regs(struct regcache *cache, jit_state_t *_jit)
512 {
513         clean_regs(cache, _jit, true);
514 }
515
516 bool 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
527 void lightrec_clean_reg(struct regcache *cache, jit_state_t *_jit, u8 jit_reg)
528 {
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         }
535 }
536
537 void 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
554 void 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
563 struct 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
574 void 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
582 void lightrec_regcache_reset(struct regcache *cache)
583 {
584         memset(&cache->lightrec_regs, 0, sizeof(cache->lightrec_regs));
585 }
586
587 struct 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
600 void lightrec_free_regcache(struct regcache *cache)
601 {
602         return lightrec_free(cache->state, MEM_FOR_LIGHTREC,
603                              sizeof(*cache), cache);
604 }
605
606 void 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
618                 if (nreg->used || nreg->prio > REG_IS_TEMP)
619                         jit_live(JIT_V(FIRST_REG + i));
620         }
621 #endif
622
623         for (i = 0; i < NUM_TEMPS; i++) {
624                 nreg = &cache->lightrec_regs[NUM_REGS + i];
625
626                 if (nreg->used || nreg->prio > REG_IS_TEMP)
627                         jit_live(JIT_R(FIRST_TEMP + i));
628         }
629
630         jit_live(LIGHTREC_REG_STATE);
631         jit_live(LIGHTREC_REG_CYCLE);
632 }