git subrepo pull --force deps/lightning
[pcsx_rearmed.git] / deps / lightrec / regcache.c
CommitLineData
d16005f8
PC
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
23struct native_register {
24 bool used, loaded, dirty, output, extend, extended, locked;
25 s8 emulated_register;
26};
27
28struct regcache {
29 struct lightrec_state *state;
30 struct native_register lightrec_regs[NUM_REGS + NUM_TEMPS];
31};
32
33static 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
46const char * lightrec_reg_name(u8 reg)
47{
48 return mips_regs[reg];
49}
50
51static 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
58static 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
65static 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
82static 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
105static 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
121static 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
157static 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
168static 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
182void 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. */
190void 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
199u8 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
209u8 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
226u8 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
250u8 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
292u8 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
305u8 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
323u8 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
351static 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
361void lightrec_free_reg(struct regcache *cache, u8 jit_reg)
362{
363 free_reg(lightning_reg_to_lightrec(cache, jit_reg));
364}
365
366void 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
374static 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
387static 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
399void lightrec_storeback_regs(struct regcache *cache, jit_state_t *_jit)
400{
401 clean_regs(cache, _jit, false);
402}
403
404void lightrec_clean_regs(struct regcache *cache, jit_state_t *_jit)
405{
406 clean_regs(cache, _jit, true);
407}
408
409void 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
415void 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
432struct 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
443void 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
451void lightrec_regcache_reset(struct regcache *cache)
452{
453 memset(&cache->lightrec_regs, 0, sizeof(cache->lightrec_regs));
454}
455
456struct 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
469void lightrec_free_regcache(struct regcache *cache)
470{
471 return lightrec_free(cache->state, MEM_FOR_LIGHTREC,
472 sizeof(*cache), cache);
473}
474
475void 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}