1 /* $Id: ra_xform.c,v 1.7 1995/11/08 11:08:09 ceriel Exp $ */
3 * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
4 * See the copyright notice in the ACK home directory, in the file "Copyright".
6 /* R E G I S T E R A L L O C A T I O N
17 #include "../share/types.h"
18 #include "../share/debug.h"
19 #include "../share/def.h"
20 #include "../share/global.h"
21 #include "../share/lset.h"
22 #include "../share/aux.h"
23 #include "../share/alloc.h"
25 #include "ra_interv.h"
30 /* The replacement table is used to transform instructions that reference
31 * items other than local variables (i.e. the address of a local or global
32 * variable or a single/double constant; the transformation of an instruction
33 * that references a local variable is very simple).
34 * The generated code depends on the word and pointer size of the target
40 short r_instr; /* instruction */
41 short r_op; /* operand */
44 /* REGNR,NO and STOP should not equal the wordsize or pointer size
55 #define LOAD_POINTER op_nop
56 #define BLANK {0, STOP}
58 #define NRREPLACEMENTS 13
61 struct repl repl_tab[NRREPLACEMENTS][REPL_LENGTH] = {
62 /* 0 */ {{op_lil, REGNR}, BLANK, BLANK},
63 /* 1 */ {{LOAD_POINTER,REGNR}, {op_loi,PS}, {op_loi,WS}},
64 /* 2 */ {{LOAD_POINTER,REGNR}, BLANK, BLANK},
65 /* 3 */ {{LOAD_POINTER,REGNR}, {op_loi,WS2}, BLANK},
66 /* 4 */ {{op_sil,REGNR}, BLANK, BLANK},
67 /* 5 */ {{LOAD_POINTER,REGNR}, {op_loi,PS}, {op_sti,WS}},
68 /* 6 */ {{LOAD_POINTER,REGNR}, {op_sti,WS2}, BLANK},
69 /* 7 */ {{op_lil,REGNR}, {op_inc,NO}, {op_sil,REGNR}},
70 /* 8 */ {{op_lil,REGNR}, {op_dec,NO}, {op_sil,REGNR}},
71 /* 9 */ {{op_zer,WS}, {op_sil,REGNR}, BLANK},
72 /*10 */ {{op_lol,REGNR}, BLANK, BLANK},
73 /*11 */ {{op_ldl,REGNR}, BLANK, BLANK},
74 /*12 */ {{LOAD_POINTER,REGNR}, {op_cai,NO}, BLANK},
80 init_replacements(psize,wsize)
83 /* The replacement code to be generated depends on the
84 * wordsize and pointer size of the target machine.
85 * The replacement table is initialized with a description
86 * of which sizes to use. This routine inserts the real sizes.
87 * It also inserts the actual EM instruction to be used
88 * as a 'Load pointer' instruction.
95 assert (psize == wsize || psize == 2*wsize);
96 load_pointer = (psize == wsize ? op_lol : op_ldl);
97 for (i = 0; i < NRREPLACEMENTS; i++) {
98 for (j = 0; j < REPL_LENGTH; j++) {
100 if (r->r_op == STOP) break;
101 if (r->r_instr == LOAD_POINTER) {
102 r->r_instr = load_pointer;
105 /* initially r_op describes how to compute
106 * the real operand of the instruction. */
120 case REGNR: /* use offset of dummy local,
121 * will be filled in later.
124 default: assert(FALSE);
132 STATIC int repl_index(l)
135 return itemtab[INSTR(l) - sp_fmnem].id_replindex;
140 STATIC bool is_current(alloc,t)
144 /* Is time t part of alloc's timespan? */
146 return contains(t,alloc->al_timespan);
150 STATIC match_item(item,l)
154 /* See if the item used by l is the same one as 'item' */
155 struct item thisitem;
157 fill_item(&thisitem,l);
158 if (item->it_type == LOCAL_ADDR && thisitem.it_type == LOCALVAR) {
159 /* The usage of a local variable is also considered to
160 * be the usage of the address of that variable.
162 thisitem.it_type = LOCAL_ADDR;
164 return item->it_type == thisitem.it_type && same_item(item,&thisitem);
169 STATIC alloc_p find_alloc(alloclist,l,t)
174 /* See if any of the allocations of the list applies to instruction
178 register alloc_p alloc,m;
180 for (alloc = alloclist; alloc != (alloc_p) 0; alloc = alloc->al_next) {
181 for (m = alloc; m != (alloc_p) 0; m = m->al_mates) {
182 if (is_current(m,t) && match_item(m->al_item,l)) {
191 STATIC replace_line(l,b,list)
195 if (b->b_start == l) {
198 PREV(l)->l_next = list;
200 PREV(list) = PREV(l);
201 while (list->l_next != (line_p) 0) {
204 list->l_next = l->l_next;
205 if (l->l_next != (line_p) 0) {
206 PREV(l->l_next) = list;
212 STATIC line_p repl_code(lnp,regnr)
216 line_p head,*q,l,prev = (line_p) 0;
221 index = repl_index(lnp);
222 for (i = 0; i < REPL_LENGTH; i++) {
223 r = &repl_tab[index][i];
224 if (r->r_op == STOP) break; /* replacement < REPL_LENGTH */
233 l = newline(OPSHORT);
238 l->l_instr = r->r_instr;
248 STATIC apply_alloc(b,l,alloc)
253 /* 'l' is an EM instruction using an item that will be put in
254 * a register. Generate new code that uses the register instead
256 * If the item is a local variable the new code is the same as
257 * the old code, except for the fact that the offset of the
258 * local is changed (it now uses the dummy local that will be
259 * put in a register by the code generator).
260 * If the item is a constant, the new code is a LOL or LDL.
261 * If the item is the address of a local or global variable, things
262 * get more complicated. The new code depends on the instruction
263 * that uses the item (i.e. l). The new code, which may consist of
264 * several instructions, is obtained by consulting a replacement
270 if (alloc->al_item->it_type == LOCALVAR) {
271 if ((short) (alloc->al_dummy) == alloc->al_dummy) {
273 SHORT(l) = alloc->al_dummy;
277 OFFSET(l) = alloc->al_dummy;
280 newcode = repl_code(l,alloc->al_dummy);
281 replace_line(l,b,newcode);
287 STATIC int loaditem_tab[NRITEMTYPES][2] =
289 /*LOCALVAR*/ op_lol, op_ldl,
290 /*LOCAL_ADDR*/ op_lal, op_lal,
291 /*GLOBL_ADDR*/ op_lae, op_lae,
292 /*PROC_ADDR*/ op_lpi, op_lpi,
293 /*CONST*/ op_loc, op_nop,
294 /*DCONST*/ op_nop, op_ldc
298 STATIC line_p load_item(item)
301 /* Generate an EM instruction that loads the item on the stack */
305 switch (item->it_type) {
307 l = newline(OPOBJECT);
308 OBJ(l) = item->i_t.it_obj;
312 PROC(l) = item->i_t.it_proc;
315 l = int_line(item->i_t.it_off);
317 l->l_instr = loaditem_tab[item->it_type][item->it_size == ws ? 0 : 1];
318 assert(l->l_instr != op_nop);
323 STATIC line_p store_local(size,off)
327 line_p l = int_line(off);
329 l->l_instr = (size == ws ? op_stl : op_sdl);
335 STATIC line_p init_place(b)
339 register line_p l,prev;
342 for (l = b->b_start; l != (line_p) 0; l = l->l_next) {
358 STATIC append_code(l1,l2,b)
362 /* Append instruction l1 and l2 at begin of block b */
368 if (l == (line_p) 0) {
369 l2->l_next = b->b_start;
371 PREV(l1) = (line_p) 0;
373 l2->l_next = l->l_next;
376 if (l2->l_next != (line_p) 0) {
377 PREV(l2->l_next) = l2;
383 STATIC emit_init_code(list)
386 /* Emit initialization code for all packed allocations.
387 * This code looks like "dummy_local := item", e.g.
388 * "LOC 25 ; STL -10" in EM terminology.
391 register alloc_p alloc,m;
395 for (alloc = list; alloc != (alloc_p) 0; alloc = alloc->al_next) {
396 for (m = alloc; m != (alloc_p) 0; m = m->al_mates) {
397 for (bi = Lfirst(m->al_inits); bi != (Lindex) 0;
398 bi = Lnext(bi,m->al_inits)) {
399 /* "inits" contains all initialization points */
400 b = (bblock_p) Lelem(bi);
401 append_code(load_item(m->al_item),
402 store_local(m->al_item->it_size,
412 STATIC emit_mesregs(p,alloclist)
420 l = p->p_start->b_start;
422 for (alloc = alloclist; alloc != (alloc_p) 0; alloc = alloc->al_next) {
423 m = reg_mes(alloc->al_dummy,alloc->al_item->it_size,
424 alloc->al_regtype,INFINITE);
428 if (x != (line_p) 0) DLINK(l,x);
431 #define is_mesreg(l) (INSTR(l) == ps_mes && aoff(ARG(l),0) == ms_reg)
439 register line_p l,next;
442 for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
443 for (l = b->b_start; l != (line_p) 0; l = next) {
445 if (INSTR(l) == ps_mes
446 && aoff(ARG(l),0) == ms_ego
447 && ((m = aoff(ARG(l),1)) == ego_live
449 /* remove live/dead messages */
458 xform_proc(p,alloclist,nrinstrs,instrmap)
464 /* Transform every instruction of procedure p that uses an item
465 * at a point where the item is kept in a register.
468 register short now = 0;
469 register line_p l,next;
473 for (b = p->p_start; b != (bblock_p) 0; b = b->b_next) {
474 for (l = b->b_start; l != (line_p) 0; l = next) {
476 if (is_mesreg(l) && ARG(l)->a_next != (arg_p) 0 &&
477 aoff(ARG(l),4) != INFINITE) {
478 /* All register messages for local variables
479 * that were not assigned a register get
480 * their 'count' fields* set to 0.
482 ARG(l)->a_next->a_next->a_next
483 ->a_next->a_a.a_offset = 0;
486 (alloc = find_alloc(alloclist,l,now))
488 apply_alloc(b,l,alloc);
493 emit_init_code(alloclist);
494 emit_mesregs(p,alloclist);
501 bool always_in_reg(off,allocs,size_out)
506 /* See if the local variable with the given offset is stored
507 * in a register during its entire lifetime. As a side effect,
508 * return the size of the local.
514 for (alloc = allocs; alloc != (alloc_p) 0; alloc = alloc->al_next) {
515 for (m = alloc; m != (alloc_p) 0; m = m->al_mates) {
517 if (m->al_iswholeproc &&
518 item->it_type == LOCALVAR &&
519 item->i_t.it_off == off) {
520 *size_out = item->it_size;
533 /* Try to decrease the number of locals of procedure p, by
534 * looking at which locals are always stored in a register.
537 offset nrlocals = p->p_localbytes;
540 while (nrlocals > 0) {
541 /* A local can only be removed if all locals with
542 * higher offsets are removed too.
544 if (always_in_reg(-nrlocals,allocs,&size)) {
545 OUTVERBOSE("local %d removed from proc %d\n",
552 p->p_localbytes = nrlocals;
554 rem_formals(p,allocs)
558 /* Try to decrease the number of formals of procedure p, by
559 * looking at which formals are always stored in a register.
562 offset nrformals = p->p_nrformals;
566 if (nrformals == UNKNOWN_SIZE) return;
567 while (off < nrformals) {
568 if (always_in_reg(off,allocs,&size)) {
569 OUTVERBOSE("formal %d removed from proc %d\n",
576 if (nrformals == off) {
577 OUTVERBOSE("all formals of procedure %d removed\n",p->p_id,0);