psxmem: Add support for Lightrec's custom mem init sequence
[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 struct native_register {
15         bool used, loaded, dirty, output, extend, extended,
16              zero_extend, zero_extended, locked;
17         s8 emulated_register;
18 };
19
20 struct regcache {
21         struct lightrec_state *state;
22         struct native_register lightrec_regs[NUM_REGS + NUM_TEMPS];
23 };
24
25 static 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
38 const char * lightrec_reg_name(u8 reg)
39 {
40         return mips_regs[reg];
41 }
42
43 static 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
52 static 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
61 static 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
68 static 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
75 static 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
92 u8 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
109 void 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
120 static 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
143 static 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
159 static 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
195 static void lightrec_discard_nreg(struct native_register *nreg)
196 {
197         nreg->extended = false;
198         nreg->zero_extended = false;
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
207 static 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) {
212                 s16 offset = offsetof(struct lightrec_state, regs.gpr)
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
221 void lightrec_unload_reg(struct regcache *cache, jit_state_t *_jit, u8 jit_reg)
222 {
223         if (lightrec_reg_is_zero(jit_reg))
224                 return;
225
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. */
232 void lightrec_lock_reg(struct regcache *cache, jit_state_t *_jit, u8 jit_reg)
233 {
234         struct native_register *reg;
235
236         if (lightrec_reg_is_zero(jit_reg))
237                 return;
238
239         reg = lightning_reg_to_lightrec(cache, jit_reg);
240         lightrec_clean_reg(cache, _jit, jit_reg);
241
242         reg->locked = true;
243 }
244
245 u8 lightrec_alloc_reg(struct regcache *cache, jit_state_t *_jit, u8 jit_reg)
246 {
247         struct native_register *reg;
248
249         if (lightrec_reg_is_zero(jit_reg))
250                 return jit_reg;
251
252         reg = lightning_reg_to_lightrec(cache, jit_reg);
253         lightrec_unload_nreg(cache, _jit, reg, jit_reg);
254
255         reg->used = true;
256         return jit_reg;
257 }
258
259 u8 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
276 u8 lightrec_alloc_reg_out(struct regcache *cache, jit_state_t *_jit,
277                           u8 reg, u8 flags)
278 {
279         struct native_register *nreg;
280         u8 jit_reg;
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);
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
301         nreg->used = true;
302         nreg->output = true;
303         nreg->emulated_register = reg;
304         nreg->extend = flags & REG_EXT;
305         nreg->zero_extend = flags & REG_ZEXT;
306         return jit_reg;
307 }
308
309 u8 lightrec_alloc_reg_in(struct regcache *cache, jit_state_t *_jit,
310                          u8 reg, u8 flags)
311 {
312         struct native_register *nreg;
313         u8 jit_reg;
314         bool reg_changed;
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);
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) {
337                 s16 offset = offsetof(struct lightrec_state, regs.gpr)
338                         + (reg << 2);
339
340                 nreg->zero_extended = flags & REG_ZEXT;
341                 nreg->extended = !nreg->zero_extended;
342
343                 /* Load previous value from register cache */
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
349                 nreg->loaded = true;
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;
356                 nreg->zero_extended = true;
357                 nreg->loaded = true;
358         }
359
360         nreg->used = true;
361         nreg->output = false;
362         nreg->emulated_register = reg;
363
364         if ((flags & REG_EXT) && !nreg->extended &&
365             (!nreg->zero_extended || !(flags & REG_ZEXT))) {
366                 nreg->extended = true;
367                 nreg->zero_extended = false;
368                 jit_extr_i(jit_reg, jit_reg);
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);
374         }
375
376         return jit_reg;
377 }
378
379 u8 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 */
396         offset = offsetof(struct lightrec_state, regs.gpr) + (reg << 2);
397         jit_ldxi_i(jit_reg, LIGHTREC_REG_STATE, offset);
398
399         nreg->extended = true;
400         nreg->zero_extended = false;
401         nreg->used = true;
402         nreg->loaded = true;
403         nreg->emulated_register = reg;
404
405         return jit_reg;
406 }
407
408 static 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;
413         if (nreg->output) {
414                 nreg->extended = nreg->extend;
415                 nreg->zero_extended = nreg->zero_extend;
416         }
417         nreg->used = false;
418 }
419
420 void lightrec_free_reg(struct regcache *cache, u8 jit_reg)
421 {
422         if (!lightrec_reg_is_zero(jit_reg))
423                 free_reg(lightning_reg_to_lightrec(cache, jit_reg));
424 }
425
426 void 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
434 static void clean_reg(jit_state_t *_jit,
435                 struct native_register *nreg, u8 jit_reg, bool clean)
436 {
437         if (nreg->dirty) {
438                 s16 offset = offsetof(struct lightrec_state, regs.gpr)
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
447 static 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
459 void lightrec_storeback_regs(struct regcache *cache, jit_state_t *_jit)
460 {
461         clean_regs(cache, _jit, false);
462 }
463
464 void lightrec_clean_regs(struct regcache *cache, jit_state_t *_jit)
465 {
466         clean_regs(cache, _jit, true);
467 }
468
469 void lightrec_clean_reg(struct regcache *cache, jit_state_t *_jit, u8 jit_reg)
470 {
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         }
477 }
478
479 void 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
496 struct 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
507 void 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
515 void lightrec_regcache_reset(struct regcache *cache)
516 {
517         memset(&cache->lightrec_regs, 0, sizeof(cache->lightrec_regs));
518 }
519
520 struct 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
533 void lightrec_free_regcache(struct regcache *cache)
534 {
535         return lightrec_free(cache->state, MEM_FOR_LIGHTREC,
536                              sizeof(*cache), cache);
537 }
538
539 void 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 }