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"
24 struct native_register {
25 bool used, output, extend, extended,
26 zero_extend, zero_extended, locked;
29 enum reg_priority prio;
33 struct lightrec_state *state;
34 struct native_register lightrec_regs[NUM_REGS + NUM_TEMPS];
37 static const char * mips_regs[] = {
41 "a0", "a1", "a2", "a3",
42 "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7",
43 "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
46 "gp", "sp", "fp", "ra",
50 const char * lightrec_reg_name(u8 reg)
52 return mips_regs[reg];
55 static inline bool lightrec_reg_is_zero(u8 jit_reg)
57 #if defined(__mips__) || defined(__alpha__) || defined(__riscv)
64 static inline s8 lightrec_get_hardwired_reg(u8 reg)
66 #if defined(__mips__) || defined(__alpha__) || defined(__riscv)
73 static inline u8 lightrec_reg_number(const struct regcache *cache,
74 const struct native_register *nreg)
76 return (u8) (((uintptr_t) nreg - (uintptr_t) cache->lightrec_regs)
80 static inline u8 lightrec_reg_to_lightning(const struct regcache *cache,
81 const struct native_register *nreg)
83 u8 offset = lightrec_reg_number(cache, nreg);
85 if (offset < NUM_REGS)
86 return JIT_V(FIRST_REG + offset);
88 return JIT_R(FIRST_TEMP + offset - NUM_REGS);
91 static inline struct native_register * lightning_reg_to_lightrec(
92 struct regcache *cache, u8 reg)
94 if ((JIT_V0 > JIT_R0 && reg >= JIT_V0) ||
95 (JIT_V0 < JIT_R0 && reg < JIT_R0)) {
97 return &cache->lightrec_regs[reg - JIT_V(FIRST_REG)];
99 return &cache->lightrec_regs[JIT_V(FIRST_REG) - reg];
102 return &cache->lightrec_regs[NUM_REGS + reg - JIT_R(FIRST_TEMP)];
104 return &cache->lightrec_regs[NUM_REGS + JIT_R(FIRST_TEMP) - reg];
108 u8 lightrec_get_reg_in_flags(struct regcache *cache, u8 jit_reg)
110 struct native_register *reg;
113 if (lightrec_reg_is_zero(jit_reg))
114 return REG_EXT | REG_ZEXT;
116 reg = lightning_reg_to_lightrec(cache, jit_reg);
119 if (reg->zero_extended)
125 void lightrec_set_reg_out_flags(struct regcache *cache, u8 jit_reg, u8 flags)
127 struct native_register *reg;
129 if (!lightrec_reg_is_zero(jit_reg)) {
130 reg = lightning_reg_to_lightrec(cache, jit_reg);
131 reg->extend = flags & REG_EXT;
132 reg->zero_extend = flags & REG_ZEXT;
136 static struct native_register * alloc_temp(struct regcache *cache)
138 struct native_register *elm, *nreg = NULL;
139 enum reg_priority best = REG_NB_PRIORITIES;
142 /* We search the register list in reverse order. As temporaries are
143 * meant to be used only in the emitter functions, they can be mapped to
144 * caller-saved registers, as they won't have to be saved back to
146 for (i = ARRAY_SIZE(cache->lightrec_regs); i; i--) {
147 elm = &cache->lightrec_regs[i - 1];
149 if (!elm->used && elm->prio < best) {
153 if (best == REG_IS_TEMP)
161 static struct native_register * find_mapped_reg(struct regcache *cache,
166 for (i = 0; i < ARRAY_SIZE(cache->lightrec_regs); i++) {
167 struct native_register *nreg = &cache->lightrec_regs[i];
168 if ((nreg->prio >= REG_IS_ZERO) &&
169 nreg->emulated_register == reg &&
170 (!out || !nreg->locked))
177 static struct native_register * alloc_in_out(struct regcache *cache,
180 struct native_register *elm, *nreg = NULL;
181 enum reg_priority best = REG_NB_PRIORITIES;
184 /* Try to find if the register is already mapped somewhere */
185 nreg = find_mapped_reg(cache, reg, out);
191 for (i = 0; i < ARRAY_SIZE(cache->lightrec_regs); i++) {
192 elm = &cache->lightrec_regs[i];
194 if (!elm->used && elm->prio < best) {
198 if (best == REG_IS_TEMP)
206 static void lightrec_discard_nreg(struct native_register *nreg)
208 nreg->extended = false;
209 nreg->zero_extended = false;
210 nreg->output = false;
212 nreg->locked = false;
213 nreg->emulated_register = -1;
217 static void lightrec_unload_nreg(struct regcache *cache, jit_state_t *_jit,
218 struct native_register *nreg, u8 jit_reg)
220 /* If we get a dirty register, store back the old value */
221 if (nreg->prio == REG_IS_DIRTY) {
222 s16 offset = offsetof(struct lightrec_state, regs.gpr)
223 + (nreg->emulated_register << 2);
225 jit_stxi_i(offset, LIGHTREC_REG_STATE, jit_reg);
228 lightrec_discard_nreg(nreg);
231 void lightrec_unload_reg(struct regcache *cache, jit_state_t *_jit, u8 jit_reg)
233 if (lightrec_reg_is_zero(jit_reg))
236 lightrec_unload_nreg(cache, _jit,
237 lightning_reg_to_lightrec(cache, jit_reg), jit_reg);
240 /* lightrec_lock_reg: the register will be cleaned if dirty, then locked.
241 * A locked register cannot only be used as input, not output. */
242 void lightrec_lock_reg(struct regcache *cache, jit_state_t *_jit, u8 jit_reg)
244 struct native_register *reg;
246 if (lightrec_reg_is_zero(jit_reg))
249 reg = lightning_reg_to_lightrec(cache, jit_reg);
250 lightrec_clean_reg(cache, _jit, jit_reg);
255 u8 lightrec_alloc_reg(struct regcache *cache, jit_state_t *_jit, u8 jit_reg)
257 struct native_register *reg;
259 if (lightrec_reg_is_zero(jit_reg))
262 reg = lightning_reg_to_lightrec(cache, jit_reg);
263 lightrec_unload_nreg(cache, _jit, reg, jit_reg);
266 reg->prio = REG_IS_LOADED;
270 u8 lightrec_alloc_reg_temp(struct regcache *cache, jit_state_t *_jit)
273 struct native_register *nreg = alloc_temp(cache);
275 /* No free register, no dirty register to free. */
276 pr_err("No more registers! Abandon ship!\n");
280 jit_reg = lightrec_reg_to_lightning(cache, nreg);
281 lightrec_unload_nreg(cache, _jit, nreg, jit_reg);
283 nreg->prio = REG_IS_TEMP;
288 s8 lightrec_get_reg_with_value(struct regcache *cache, intptr_t value)
290 struct native_register *nreg;
293 for (i = 0; i < ARRAY_SIZE(cache->lightrec_regs); i++) {
294 nreg = &cache->lightrec_regs[i];
296 if (nreg->prio == REG_IS_TEMP_VALUE && nreg->value == value) {
298 return lightrec_reg_to_lightning(cache, nreg);
305 void lightrec_temp_set_value(struct regcache *cache, u8 jit_reg, intptr_t value)
307 struct native_register *nreg;
309 nreg = lightning_reg_to_lightrec(cache, jit_reg);
311 nreg->prio = REG_IS_TEMP_VALUE;
315 u8 lightrec_alloc_reg_out(struct regcache *cache, jit_state_t *_jit,
318 struct native_register *nreg;
322 hw_reg = lightrec_get_hardwired_reg(reg);
326 nreg = alloc_in_out(cache, reg, true);
328 /* No free register, no dirty register to free. */
329 pr_err("No more registers! Abandon ship!\n");
333 jit_reg = lightrec_reg_to_lightning(cache, nreg);
335 /* If we get a dirty register that doesn't correspond to the one
336 * we're requesting, store back the old value */
337 if (nreg->emulated_register != reg)
338 lightrec_unload_nreg(cache, _jit, nreg, jit_reg);
342 nreg->emulated_register = reg;
343 nreg->extend = flags & REG_EXT;
344 nreg->zero_extend = flags & REG_ZEXT;
345 nreg->prio = reg ? REG_IS_LOADED : REG_IS_ZERO;
349 u8 lightrec_alloc_reg_in(struct regcache *cache, jit_state_t *_jit,
352 struct native_register *nreg;
357 hw_reg = lightrec_get_hardwired_reg(reg);
361 nreg = alloc_in_out(cache, reg, false);
363 /* No free register, no dirty register to free. */
364 pr_err("No more registers! Abandon ship!\n");
368 jit_reg = lightrec_reg_to_lightning(cache, nreg);
370 /* If we get a dirty register that doesn't correspond to the one
371 * we're requesting, store back the old value */
372 reg_changed = nreg->emulated_register != reg;
374 lightrec_unload_nreg(cache, _jit, nreg, jit_reg);
376 if (nreg->prio < REG_IS_LOADED && reg != 0) {
377 s16 offset = offsetof(struct lightrec_state, regs.gpr)
380 nreg->zero_extended = flags & REG_ZEXT;
381 nreg->extended = !nreg->zero_extended;
383 /* Load previous value from register cache */
384 if (nreg->zero_extended)
385 jit_ldxi_ui(jit_reg, LIGHTREC_REG_STATE, offset);
387 jit_ldxi_i(jit_reg, LIGHTREC_REG_STATE, offset);
389 nreg->prio = REG_IS_LOADED;
392 /* Clear register r0 before use */
393 if (reg == 0 && nreg->prio != REG_IS_ZERO) {
394 jit_movi(jit_reg, 0);
395 nreg->extended = true;
396 nreg->zero_extended = true;
397 nreg->prio = REG_IS_ZERO;
401 nreg->output = false;
402 nreg->emulated_register = reg;
404 if ((flags & REG_EXT) && !nreg->extended &&
405 (!nreg->zero_extended || !(flags & REG_ZEXT))) {
406 nreg->extended = true;
407 nreg->zero_extended = false;
408 jit_extr_i(jit_reg, jit_reg);
409 } else if (!(flags & REG_EXT) && (flags & REG_ZEXT) &&
410 !nreg->zero_extended) {
411 nreg->zero_extended = true;
412 nreg->extended = false;
413 jit_extr_ui(jit_reg, jit_reg);
419 u8 lightrec_request_reg_in(struct regcache *cache, jit_state_t *_jit,
422 struct native_register *nreg;
425 nreg = find_mapped_reg(cache, reg, false);
427 jit_reg = lightrec_reg_to_lightning(cache, nreg);
432 nreg = lightning_reg_to_lightrec(cache, jit_reg);
433 lightrec_unload_nreg(cache, _jit, nreg, jit_reg);
435 /* Load previous value from register cache */
436 offset = offsetof(struct lightrec_state, regs.gpr) + (reg << 2);
437 jit_ldxi_i(jit_reg, LIGHTREC_REG_STATE, offset);
439 nreg->extended = true;
440 nreg->zero_extended = false;
442 nreg->emulated_register = reg;
443 nreg->prio = REG_IS_LOADED;
448 static void free_reg(struct native_register *nreg)
450 /* Set output registers as dirty */
451 if (nreg->used && nreg->output && nreg->emulated_register > 0)
452 nreg->prio = REG_IS_DIRTY;
454 nreg->extended = nreg->extend;
455 nreg->zero_extended = nreg->zero_extend;
460 void lightrec_free_reg(struct regcache *cache, u8 jit_reg)
462 if (!lightrec_reg_is_zero(jit_reg))
463 free_reg(lightning_reg_to_lightrec(cache, jit_reg));
466 void lightrec_free_regs(struct regcache *cache)
470 for (i = 0; i < ARRAY_SIZE(cache->lightrec_regs); i++)
471 free_reg(&cache->lightrec_regs[i]);
474 static void clean_reg(jit_state_t *_jit,
475 struct native_register *nreg, u8 jit_reg, bool clean)
477 if (nreg->prio == REG_IS_DIRTY) {
478 s16 offset = offsetof(struct lightrec_state, regs.gpr)
479 + (nreg->emulated_register << 2);
481 jit_stxi_i(offset, LIGHTREC_REG_STATE, jit_reg);
484 if (nreg->emulated_register == 0)
485 nreg->prio = REG_IS_ZERO;
487 nreg->prio = REG_IS_LOADED;
492 static void clean_regs(struct regcache *cache, jit_state_t *_jit, bool clean)
496 for (i = 0; i < NUM_REGS; i++) {
497 clean_reg(_jit, &cache->lightrec_regs[i],
498 JIT_V(FIRST_REG + i), clean);
500 for (i = 0; i < NUM_TEMPS; i++) {
501 clean_reg(_jit, &cache->lightrec_regs[i + NUM_REGS],
502 JIT_R(FIRST_TEMP + i), clean);
506 void lightrec_storeback_regs(struct regcache *cache, jit_state_t *_jit)
508 clean_regs(cache, _jit, false);
511 void lightrec_clean_regs(struct regcache *cache, jit_state_t *_jit)
513 clean_regs(cache, _jit, true);
516 bool lightrec_has_dirty_regs(struct regcache *cache)
520 for (i = 0; i < NUM_REGS + NUM_TEMPS; i++)
521 if (cache->lightrec_regs[i].prio == REG_IS_DIRTY)
527 void lightrec_clean_reg(struct regcache *cache, jit_state_t *_jit, u8 jit_reg)
529 struct native_register *reg;
531 if (!lightrec_reg_is_zero(jit_reg)) {
532 reg = lightning_reg_to_lightrec(cache, jit_reg);
533 clean_reg(_jit, reg, jit_reg, true);
537 void lightrec_clean_reg_if_loaded(struct regcache *cache, jit_state_t *_jit,
540 struct native_register *nreg;
543 nreg = find_mapped_reg(cache, reg, false);
545 jit_reg = lightrec_reg_to_lightning(cache, nreg);
548 lightrec_unload_nreg(cache, _jit, nreg, jit_reg);
550 clean_reg(_jit, nreg, jit_reg, true);
554 void lightrec_discard_reg_if_loaded(struct regcache *cache, u8 reg)
556 struct native_register *nreg;
558 nreg = find_mapped_reg(cache, reg, false);
560 lightrec_discard_nreg(nreg);
563 struct native_register * lightrec_regcache_enter_branch(struct regcache *cache)
565 struct native_register *backup;
567 backup = lightrec_malloc(cache->state, MEM_FOR_LIGHTREC,
568 sizeof(cache->lightrec_regs));
569 memcpy(backup, &cache->lightrec_regs, sizeof(cache->lightrec_regs));
574 void lightrec_regcache_leave_branch(struct regcache *cache,
575 struct native_register *regs)
577 memcpy(&cache->lightrec_regs, regs, sizeof(cache->lightrec_regs));
578 lightrec_free(cache->state, MEM_FOR_LIGHTREC,
579 sizeof(cache->lightrec_regs), regs);
582 void lightrec_regcache_reset(struct regcache *cache)
584 memset(&cache->lightrec_regs, 0, sizeof(cache->lightrec_regs));
587 struct regcache * lightrec_regcache_init(struct lightrec_state *state)
589 struct regcache *cache;
591 cache = lightrec_calloc(state, MEM_FOR_LIGHTREC, sizeof(*cache));
595 cache->state = state;
600 void lightrec_free_regcache(struct regcache *cache)
602 return lightrec_free(cache->state, MEM_FOR_LIGHTREC,
603 sizeof(*cache), cache);
606 void lightrec_regcache_mark_live(struct regcache *cache, jit_state_t *_jit)
608 struct native_register *nreg;
612 /* FIXME: GNU Lightning on Windows seems to use our mapped registers as
613 * temporaries. Until the actual bug is found and fixed, unconditionally
614 * mark our registers as live here. */
615 for (i = 0; i < NUM_REGS; i++) {
616 nreg = &cache->lightrec_regs[i];
618 if (nreg->used || nreg->prio > REG_IS_TEMP)
619 jit_live(JIT_V(FIRST_REG + i));
623 for (i = 0; i < NUM_TEMPS; i++) {
624 nreg = &cache->lightrec_regs[NUM_REGS + i];
626 if (nreg->used || nreg->prio > REG_IS_TEMP)
627 jit_live(JIT_R(FIRST_TEMP + i));
630 jit_live(LIGHTREC_REG_STATE);
631 jit_live(LIGHTREC_REG_CYCLE);