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