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 
lightrec_reg_name(u8 reg)46 const char * lightrec_reg_name(u8 reg)
47 {
48 	return mips_regs[reg];
49 }
50 
lightrec_reg_number(const struct regcache * cache,const struct native_register * nreg)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 
lightrec_reg_to_lightning(const struct regcache * cache,const struct native_register * nreg)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 
lightning_reg_to_lightrec(struct regcache * cache,u8 reg)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 
alloc_temp(struct regcache * cache)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 
find_mapped_reg(struct regcache * cache,u8 reg,bool out)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 
alloc_in_out(struct regcache * cache,u8 reg,bool out)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 
lightrec_discard_nreg(struct native_register * nreg)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 
lightrec_unload_nreg(struct regcache * cache,jit_state_t * _jit,struct native_register * nreg,u8 jit_reg)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 
lightrec_unload_reg(struct regcache * cache,jit_state_t * _jit,u8 jit_reg)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. */
lightrec_lock_reg(struct regcache * cache,jit_state_t * _jit,u8 jit_reg)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 
lightrec_alloc_reg(struct regcache * cache,jit_state_t * _jit,u8 jit_reg)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 
lightrec_alloc_reg_temp(struct regcache * cache,jit_state_t * _jit)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 
lightrec_alloc_reg_out(struct regcache * cache,jit_state_t * _jit,u8 reg)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 
lightrec_alloc_reg_in(struct regcache * cache,jit_state_t * _jit,u8 reg)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 
lightrec_alloc_reg_out_ext(struct regcache * cache,jit_state_t * _jit,u8 reg)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 
lightrec_alloc_reg_in_ext(struct regcache * cache,jit_state_t * _jit,u8 reg)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 
lightrec_request_reg_in(struct regcache * cache,jit_state_t * _jit,u8 reg,u8 jit_reg)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 
free_reg(struct native_register * nreg)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 
lightrec_free_reg(struct regcache * cache,u8 jit_reg)361 void lightrec_free_reg(struct regcache *cache, u8 jit_reg)
362 {
363 	free_reg(lightning_reg_to_lightrec(cache, jit_reg));
364 }
365 
lightrec_free_regs(struct regcache * cache)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 
clean_reg(jit_state_t * _jit,struct native_register * nreg,u8 jit_reg,bool clean)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 
clean_regs(struct regcache * cache,jit_state_t * _jit,bool clean)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 
lightrec_storeback_regs(struct regcache * cache,jit_state_t * _jit)399 void lightrec_storeback_regs(struct regcache *cache, jit_state_t *_jit)
400 {
401 	clean_regs(cache, _jit, false);
402 }
403 
lightrec_clean_regs(struct regcache * cache,jit_state_t * _jit)404 void lightrec_clean_regs(struct regcache *cache, jit_state_t *_jit)
405 {
406 	clean_regs(cache, _jit, true);
407 }
408 
lightrec_clean_reg(struct regcache * cache,jit_state_t * _jit,u8 jit_reg)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 
lightrec_clean_reg_if_loaded(struct regcache * cache,jit_state_t * _jit,u8 reg,bool unload)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 
lightrec_regcache_enter_branch(struct regcache * cache)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 
lightrec_regcache_leave_branch(struct regcache * cache,struct native_register * regs)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 
lightrec_regcache_reset(struct regcache * cache)451 void lightrec_regcache_reset(struct regcache *cache)
452 {
453 	memset(&cache->lightrec_regs, 0, sizeof(cache->lightrec_regs));
454 }
455 
lightrec_regcache_init(struct lightrec_state * state)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 
lightrec_free_regcache(struct regcache * cache)469 void lightrec_free_regcache(struct regcache *cache)
470 {
471 	return lightrec_free(cache->state, MEM_FOR_LIGHTREC,
472 			     sizeof(*cache), cache);
473 }
474 
lightrec_regcache_mark_live(struct regcache * cache,jit_state_t * _jit)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 }
499