1 // SPDX-License-Identifier: LGPL-2.1-or-later
3 * Copyright (C) 2014-2021 Paul Cercueil <paul@crapouillou.net>
7 #include "memmanager.h"
8 #include "lightning-wrapper.h"
14 struct native_register {
15 bool used, loaded, dirty, output, extend, extended,
16 zero_extend, zero_extended, locked;
21 struct lightrec_state *state;
22 struct native_register lightrec_regs[NUM_REGS + NUM_TEMPS];
25 static const char * mips_regs[] = {
29 "a0", "a1", "a2", "a3",
30 "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7",
31 "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
34 "gp", "sp", "fp", "ra",
38 const char * lightrec_reg_name(u8 reg)
40 return mips_regs[reg];
43 static inline bool lightrec_reg_is_zero(u8 jit_reg)
45 #if defined(__mips__) || defined(__alpha__) || defined(__riscv)
52 static inline s8 lightrec_get_hardwired_reg(u8 reg)
54 #if defined(__mips__) || defined(__alpha__) || defined(__riscv)
61 static inline u8 lightrec_reg_number(const struct regcache *cache,
62 const struct native_register *nreg)
64 return (u8) (((uintptr_t) nreg - (uintptr_t) cache->lightrec_regs)
68 static inline u8 lightrec_reg_to_lightning(const struct regcache *cache,
69 const struct native_register *nreg)
71 u8 offset = lightrec_reg_number(cache, nreg);
72 return offset < NUM_REGS ? JIT_V(offset) : JIT_R(offset - NUM_REGS);
75 static inline struct native_register * lightning_reg_to_lightrec(
76 struct regcache *cache, u8 reg)
78 if ((JIT_V0 > JIT_R0 && reg >= JIT_V0) ||
79 (JIT_V0 < JIT_R0 && reg < JIT_R0)) {
81 return &cache->lightrec_regs[reg - JIT_V0];
83 return &cache->lightrec_regs[JIT_V0 - reg];
86 return &cache->lightrec_regs[NUM_REGS + reg - JIT_R0];
88 return &cache->lightrec_regs[NUM_REGS + JIT_R0 - reg];
92 u8 lightrec_get_reg_in_flags(struct regcache *cache, u8 jit_reg)
94 struct native_register *reg;
97 if (lightrec_reg_is_zero(jit_reg))
98 return REG_EXT | REG_ZEXT;
100 reg = lightning_reg_to_lightrec(cache, jit_reg);
103 if (reg->zero_extended)
109 void lightrec_set_reg_out_flags(struct regcache *cache, u8 jit_reg, u8 flags)
111 struct native_register *reg;
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;
120 static struct native_register * alloc_temp(struct regcache *cache)
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
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)
134 for (i = ARRAY_SIZE(cache->lightrec_regs); i; i--) {
135 struct native_register *nreg = &cache->lightrec_regs[i - 1];
143 static struct native_register * find_mapped_reg(struct regcache *cache,
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))
159 static struct native_register * alloc_in_out(struct regcache *cache,
162 struct native_register *nreg;
165 /* Try to find if the register is already mapped somewhere */
166 nreg = find_mapped_reg(cache, reg, out);
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)
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)
186 for (i = 0; i < ARRAY_SIZE(cache->lightrec_regs); i++) {
187 nreg = &cache->lightrec_regs[i];
195 static void lightrec_discard_nreg(struct native_register *nreg)
197 nreg->extended = false;
198 nreg->zero_extended = false;
199 nreg->loaded = false;
200 nreg->output = false;
203 nreg->locked = false;
204 nreg->emulated_register = -1;
207 static void lightrec_unload_nreg(struct regcache *cache, jit_state_t *_jit,
208 struct native_register *nreg, u8 jit_reg)
210 /* If we get a dirty register, store back the old value */
212 s16 offset = offsetof(struct lightrec_state, regs.gpr)
213 + (nreg->emulated_register << 2);
215 jit_stxi_i(offset, LIGHTREC_REG_STATE, jit_reg);
218 lightrec_discard_nreg(nreg);
221 void lightrec_unload_reg(struct regcache *cache, jit_state_t *_jit, u8 jit_reg)
223 if (lightrec_reg_is_zero(jit_reg))
226 lightrec_unload_nreg(cache, _jit,
227 lightning_reg_to_lightrec(cache, jit_reg), jit_reg);
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)
234 struct native_register *reg;
236 if (lightrec_reg_is_zero(jit_reg))
239 reg = lightning_reg_to_lightrec(cache, jit_reg);
240 lightrec_clean_reg(cache, _jit, jit_reg);
245 u8 lightrec_alloc_reg(struct regcache *cache, jit_state_t *_jit, u8 jit_reg)
247 struct native_register *reg;
249 if (lightrec_reg_is_zero(jit_reg))
252 reg = lightning_reg_to_lightrec(cache, jit_reg);
253 lightrec_unload_nreg(cache, _jit, reg, jit_reg);
259 u8 lightrec_alloc_reg_temp(struct regcache *cache, jit_state_t *_jit)
262 struct native_register *nreg = alloc_temp(cache);
264 /* No free register, no dirty register to free. */
265 pr_err("No more registers! Abandon ship!\n");
269 jit_reg = lightrec_reg_to_lightning(cache, nreg);
270 lightrec_unload_nreg(cache, _jit, nreg, jit_reg);
276 u8 lightrec_alloc_reg_out(struct regcache *cache, jit_state_t *_jit,
279 struct native_register *nreg;
283 hw_reg = lightrec_get_hardwired_reg(reg);
287 nreg = alloc_in_out(cache, reg, true);
289 /* No free register, no dirty register to free. */
290 pr_err("No more registers! Abandon ship!\n");
294 jit_reg = lightrec_reg_to_lightning(cache, nreg);
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);
303 nreg->emulated_register = reg;
304 nreg->extend = flags & REG_EXT;
305 nreg->zero_extend = flags & REG_ZEXT;
309 u8 lightrec_alloc_reg_in(struct regcache *cache, jit_state_t *_jit,
312 struct native_register *nreg;
317 hw_reg = lightrec_get_hardwired_reg(reg);
321 nreg = alloc_in_out(cache, reg, false);
323 /* No free register, no dirty register to free. */
324 pr_err("No more registers! Abandon ship!\n");
328 jit_reg = lightrec_reg_to_lightning(cache, nreg);
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;
334 lightrec_unload_nreg(cache, _jit, nreg, jit_reg);
336 if (!nreg->loaded && !nreg->dirty && reg != 0) {
337 s16 offset = offsetof(struct lightrec_state, regs.gpr)
340 nreg->zero_extended = flags & REG_ZEXT;
341 nreg->extended = !nreg->zero_extended;
343 /* Load previous value from register cache */
344 if (nreg->zero_extended)
345 jit_ldxi_ui(jit_reg, LIGHTREC_REG_STATE, offset);
347 jit_ldxi_i(jit_reg, LIGHTREC_REG_STATE, offset);
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;
361 nreg->output = false;
362 nreg->emulated_register = reg;
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);
379 u8 lightrec_request_reg_in(struct regcache *cache, jit_state_t *_jit,
382 struct native_register *nreg;
385 nreg = find_mapped_reg(cache, reg, false);
387 jit_reg = lightrec_reg_to_lightning(cache, nreg);
392 nreg = lightning_reg_to_lightrec(cache, jit_reg);
393 lightrec_unload_nreg(cache, _jit, nreg, jit_reg);
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);
399 nreg->extended = true;
400 nreg->zero_extended = false;
403 nreg->emulated_register = reg;
408 static void free_reg(struct native_register *nreg)
410 /* Set output registers as dirty */
411 if (nreg->used && nreg->output && nreg->emulated_register > 0)
414 nreg->extended = nreg->extend;
415 nreg->zero_extended = nreg->zero_extend;
420 void lightrec_free_reg(struct regcache *cache, u8 jit_reg)
422 if (!lightrec_reg_is_zero(jit_reg))
423 free_reg(lightning_reg_to_lightrec(cache, jit_reg));
426 void lightrec_free_regs(struct regcache *cache)
430 for (i = 0; i < ARRAY_SIZE(cache->lightrec_regs); i++)
431 free_reg(&cache->lightrec_regs[i]);
434 static void clean_reg(jit_state_t *_jit,
435 struct native_register *nreg, u8 jit_reg, bool clean)
438 s16 offset = offsetof(struct lightrec_state, regs.gpr)
439 + (nreg->emulated_register << 2);
441 jit_stxi_i(offset, LIGHTREC_REG_STATE, jit_reg);
442 nreg->loaded |= nreg->dirty;
443 nreg->dirty ^= clean;
447 static void clean_regs(struct regcache *cache, jit_state_t *_jit, bool clean)
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],
459 void lightrec_storeback_regs(struct regcache *cache, jit_state_t *_jit)
461 clean_regs(cache, _jit, false);
464 void lightrec_clean_regs(struct regcache *cache, jit_state_t *_jit)
466 clean_regs(cache, _jit, true);
469 void lightrec_clean_reg(struct regcache *cache, jit_state_t *_jit, u8 jit_reg)
471 struct native_register *reg;
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);
479 void lightrec_clean_reg_if_loaded(struct regcache *cache, jit_state_t *_jit,
482 struct native_register *nreg;
485 nreg = find_mapped_reg(cache, reg, false);
487 jit_reg = lightrec_reg_to_lightning(cache, nreg);
490 lightrec_unload_nreg(cache, _jit, nreg, jit_reg);
492 clean_reg(_jit, nreg, jit_reg, true);
496 void lightrec_discard_reg_if_loaded(struct regcache *cache, u8 reg)
498 struct native_register *nreg;
500 nreg = find_mapped_reg(cache, reg, false);
502 lightrec_discard_nreg(nreg);
505 struct native_register * lightrec_regcache_enter_branch(struct regcache *cache)
507 struct native_register *backup;
509 backup = lightrec_malloc(cache->state, MEM_FOR_LIGHTREC,
510 sizeof(cache->lightrec_regs));
511 memcpy(backup, &cache->lightrec_regs, sizeof(cache->lightrec_regs));
516 void lightrec_regcache_leave_branch(struct regcache *cache,
517 struct native_register *regs)
519 memcpy(&cache->lightrec_regs, regs, sizeof(cache->lightrec_regs));
520 lightrec_free(cache->state, MEM_FOR_LIGHTREC,
521 sizeof(cache->lightrec_regs), regs);
524 void lightrec_regcache_reset(struct regcache *cache)
526 memset(&cache->lightrec_regs, 0, sizeof(cache->lightrec_regs));
529 struct regcache * lightrec_regcache_init(struct lightrec_state *state)
531 struct regcache *cache;
533 cache = lightrec_calloc(state, MEM_FOR_LIGHTREC, sizeof(*cache));
537 cache->state = state;
542 void lightrec_free_regcache(struct regcache *cache)
544 return lightrec_free(cache->state, MEM_FOR_LIGHTREC,
545 sizeof(*cache), cache);
548 void lightrec_regcache_mark_live(struct regcache *cache, jit_state_t *_jit)
550 struct native_register *nreg;
554 /* FIXME: GNU Lightning on Windows seems to use our mapped registers as
555 * temporaries. Until the actual bug is found and fixed, unconditionally
556 * mark our registers as live here. */
557 for (i = 0; i < NUM_REGS; i++) {
558 nreg = &cache->lightrec_regs[i];
560 if (nreg->used || nreg->loaded || nreg->dirty)
565 for (i = 0; i < NUM_TEMPS; i++) {
566 nreg = &cache->lightrec_regs[NUM_REGS + i];
568 if (nreg->used || nreg->loaded || nreg->dirty)