2 * Copyright (C) 2014-2020 Paul Cercueil <paul@crapouillou.net>
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.
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.
16 #include "memmanager.h"
19 #include <lightning.h>
23 struct native_register {
24 bool used, loaded, dirty, output, extend, extended, locked;
29 struct lightrec_state *state;
30 struct native_register lightrec_regs[NUM_REGS + NUM_TEMPS];
33 static const char * mips_regs[] = {
37 "a0", "a1", "a2", "a3",
38 "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7",
39 "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
42 "gp", "sp", "fp", "ra",
46 const char * lightrec_reg_name(u8 reg)
48 return mips_regs[reg];
51 static inline u8 lightrec_reg_number(const struct regcache *cache,
52 const struct native_register *nreg)
54 return (u8) (((uintptr_t) nreg - (uintptr_t) cache->lightrec_regs)
58 static inline u8 lightrec_reg_to_lightning(const struct regcache *cache,
59 const struct native_register *nreg)
61 u8 offset = lightrec_reg_number(cache, nreg);
62 return offset < NUM_REGS ? JIT_V(offset) : JIT_R(offset - NUM_REGS);
65 static inline struct native_register * lightning_reg_to_lightrec(
66 struct regcache *cache, u8 reg)
68 if ((JIT_V0 > JIT_R0 && reg >= JIT_V0) ||
69 (JIT_V0 < JIT_R0 && reg < JIT_R0)) {
71 return &cache->lightrec_regs[reg - JIT_V0];
73 return &cache->lightrec_regs[JIT_V0 - reg];
76 return &cache->lightrec_regs[NUM_REGS + reg - JIT_R0];
78 return &cache->lightrec_regs[NUM_REGS + JIT_R0 - reg];
82 static struct native_register * alloc_temp(struct regcache *cache)
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
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)
96 for (i = ARRAY_SIZE(cache->lightrec_regs); i; i--) {
97 struct native_register *nreg = &cache->lightrec_regs[i - 1];
105 static struct native_register * find_mapped_reg(struct regcache *cache,
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))
121 static struct native_register * alloc_in_out(struct regcache *cache,
124 struct native_register *nreg;
127 /* Try to find if the register is already mapped somewhere */
128 nreg = find_mapped_reg(cache, reg, out);
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)
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)
148 for (i = 0; i < ARRAY_SIZE(cache->lightrec_regs); i++) {
149 nreg = &cache->lightrec_regs[i];
157 static void lightrec_discard_nreg(struct native_register *nreg)
159 nreg->extended = false;
160 nreg->loaded = false;
161 nreg->output = false;
164 nreg->locked = false;
165 nreg->emulated_register = -1;
168 static void lightrec_unload_nreg(struct regcache *cache, jit_state_t *_jit,
169 struct native_register *nreg, u8 jit_reg)
171 /* If we get a dirty register, store back the old value */
173 s16 offset = offsetof(struct lightrec_state, native_reg_cache)
174 + (nreg->emulated_register << 2);
176 jit_stxi_i(offset, LIGHTREC_REG_STATE, jit_reg);
179 lightrec_discard_nreg(nreg);
182 void lightrec_unload_reg(struct regcache *cache, jit_state_t *_jit, u8 jit_reg)
184 lightrec_unload_nreg(cache, _jit,
185 lightning_reg_to_lightrec(cache, jit_reg), jit_reg);
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)
192 struct native_register *reg = lightning_reg_to_lightrec(cache, jit_reg);
194 lightrec_clean_reg(cache, _jit, jit_reg);
199 u8 lightrec_alloc_reg(struct regcache *cache, jit_state_t *_jit, u8 jit_reg)
201 struct native_register *reg = lightning_reg_to_lightrec(cache, jit_reg);
203 lightrec_unload_nreg(cache, _jit, reg, jit_reg);
209 u8 lightrec_alloc_reg_temp(struct regcache *cache, jit_state_t *_jit)
212 struct native_register *nreg = alloc_temp(cache);
214 /* No free register, no dirty register to free. */
215 pr_err("No more registers! Abandon ship!\n");
219 jit_reg = lightrec_reg_to_lightning(cache, nreg);
220 lightrec_unload_nreg(cache, _jit, nreg, jit_reg);
226 u8 lightrec_alloc_reg_out(struct regcache *cache, jit_state_t *_jit, u8 reg)
229 struct native_register *nreg = alloc_in_out(cache, reg, true);
231 /* No free register, no dirty register to free. */
232 pr_err("No more registers! Abandon ship!\n");
236 jit_reg = lightrec_reg_to_lightning(cache, nreg);
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);
243 nreg->extend = false;
246 nreg->emulated_register = reg;
250 u8 lightrec_alloc_reg_in(struct regcache *cache, jit_state_t *_jit, u8 reg)
254 struct native_register *nreg = alloc_in_out(cache, reg, false);
256 /* No free register, no dirty register to free. */
257 pr_err("No more registers! Abandon ship!\n");
261 jit_reg = lightrec_reg_to_lightning(cache, nreg);
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;
267 lightrec_unload_nreg(cache, _jit, nreg, jit_reg);
269 if (!nreg->loaded && !nreg->dirty && reg != 0) {
270 s16 offset = offsetof(struct lightrec_state, native_reg_cache)
273 /* Load previous value from register cache */
274 jit_ldxi_i(jit_reg, LIGHTREC_REG_STATE, offset);
276 nreg->extended = true;
279 /* Clear register r0 before use */
280 if (reg == 0 && (!nreg->loaded || nreg->dirty)) {
281 jit_movi(jit_reg, 0);
282 nreg->extended = true;
287 nreg->output = false;
288 nreg->emulated_register = reg;
292 u8 lightrec_alloc_reg_out_ext(struct regcache *cache, jit_state_t *_jit, u8 reg)
294 struct native_register *nreg;
297 jit_reg = lightrec_alloc_reg_out(cache, _jit, reg);
298 nreg = lightning_reg_to_lightrec(cache, jit_reg);
305 u8 lightrec_alloc_reg_in_ext(struct regcache *cache, jit_state_t *_jit, u8 reg)
307 struct native_register *nreg;
310 jit_reg = lightrec_alloc_reg_in(cache, _jit, reg);
311 nreg = lightning_reg_to_lightrec(cache, jit_reg);
314 if (!nreg->extended) {
315 nreg->extended = true;
316 jit_extr_i(jit_reg, jit_reg);
323 u8 lightrec_request_reg_in(struct regcache *cache, jit_state_t *_jit,
326 struct native_register *nreg;
329 nreg = find_mapped_reg(cache, reg, false);
331 jit_reg = lightrec_reg_to_lightning(cache, nreg);
336 nreg = lightning_reg_to_lightrec(cache, jit_reg);
337 lightrec_unload_nreg(cache, _jit, nreg, jit_reg);
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);
343 nreg->extended = true;
346 nreg->emulated_register = reg;
351 static void free_reg(struct native_register *nreg)
353 /* Set output registers as dirty */
354 if (nreg->used && nreg->output && nreg->emulated_register > 0)
357 nreg->extended = nreg->extend;
361 void lightrec_free_reg(struct regcache *cache, u8 jit_reg)
363 free_reg(lightning_reg_to_lightrec(cache, jit_reg));
366 void lightrec_free_regs(struct regcache *cache)
370 for (i = 0; i < ARRAY_SIZE(cache->lightrec_regs); i++)
371 free_reg(&cache->lightrec_regs[i]);
374 static void clean_reg(jit_state_t *_jit,
375 struct native_register *nreg, u8 jit_reg, bool clean)
378 s16 offset = offsetof(struct lightrec_state, native_reg_cache)
379 + (nreg->emulated_register << 2);
381 jit_stxi_i(offset, LIGHTREC_REG_STATE, jit_reg);
382 nreg->loaded |= nreg->dirty;
383 nreg->dirty ^= clean;
387 static void clean_regs(struct regcache *cache, jit_state_t *_jit, bool clean)
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],
399 void lightrec_storeback_regs(struct regcache *cache, jit_state_t *_jit)
401 clean_regs(cache, _jit, false);
404 void lightrec_clean_regs(struct regcache *cache, jit_state_t *_jit)
406 clean_regs(cache, _jit, true);
409 void lightrec_clean_reg(struct regcache *cache, jit_state_t *_jit, u8 jit_reg)
411 struct native_register *reg = lightning_reg_to_lightrec(cache, jit_reg);
412 clean_reg(_jit, reg, jit_reg, true);
415 void lightrec_clean_reg_if_loaded(struct regcache *cache, jit_state_t *_jit,
418 struct native_register *nreg;
421 nreg = find_mapped_reg(cache, reg, false);
423 jit_reg = lightrec_reg_to_lightning(cache, nreg);
426 lightrec_unload_nreg(cache, _jit, nreg, jit_reg);
428 clean_reg(_jit, nreg, jit_reg, true);
432 struct native_register * lightrec_regcache_enter_branch(struct regcache *cache)
434 struct native_register *backup;
436 backup = lightrec_malloc(cache->state, MEM_FOR_LIGHTREC,
437 sizeof(cache->lightrec_regs));
438 memcpy(backup, &cache->lightrec_regs, sizeof(cache->lightrec_regs));
443 void lightrec_regcache_leave_branch(struct regcache *cache,
444 struct native_register *regs)
446 memcpy(&cache->lightrec_regs, regs, sizeof(cache->lightrec_regs));
447 lightrec_free(cache->state, MEM_FOR_LIGHTREC,
448 sizeof(cache->lightrec_regs), regs);
451 void lightrec_regcache_reset(struct regcache *cache)
453 memset(&cache->lightrec_regs, 0, sizeof(cache->lightrec_regs));
456 struct regcache * lightrec_regcache_init(struct lightrec_state *state)
458 struct regcache *cache;
460 cache = lightrec_calloc(state, MEM_FOR_LIGHTREC, sizeof(*cache));
464 cache->state = state;
469 void lightrec_free_regcache(struct regcache *cache)
471 return lightrec_free(cache->state, MEM_FOR_LIGHTREC,
472 sizeof(*cache), cache);
475 void lightrec_regcache_mark_live(struct regcache *cache, jit_state_t *_jit)
477 struct native_register *nreg;
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];
487 if (nreg->used || nreg->loaded || nreg->dirty)
492 for (i = 0; i < NUM_TEMPS; i++) {
493 nreg = &cache->lightrec_regs[NUM_REGS + i];
495 if (nreg->used || nreg->loaded || nreg->dirty)