/* $Id$ */ /* * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. * See the copyright notice in the ACK home directory, in the file "Copyright". */ /* R E G I S T E R A L L O C A T I O N * * R A _ P A C K . C */ #include #include #include "../share/types.h" #include "../share/debug.h" #include "../share/def.h" #include "../share/global.h" #include "../share/lset.h" #include "../share/cset.h" #include "../share/alloc.h" #include "../share/utils.h" #include "ra.h" #include "ra_aux.h" #include "ra_interv.h" #include "ra_pack.h" #include "ra_profits.h" short regs_occupied[NRREGTYPES]; /* #occupied registers for reg_pointer, * reg_any etc. */ #define reg_available(t) (regs_available[t] > regs_occupied[t]) STATIC void initregcount() { int t; for (t = 0; t < NRREGTYPES; t++) { regs_occupied[t] = 0; } } STATIC alloc_p make_dummy() { alloc_p x; x = newalloc(); /* x->al_profits = 0; */ return x; } STATIC bool fits_in(alloc_p a, alloc_p b, bool *cont_item) { /* See if allocation a can be assigned the same register as b. * Both allocations should be of the same register-type. * Note that there may be several other allocations (mates) assigned to * the same register as b. A new candidate (i.e. 'a') is only * allowed to join them if it is not the rival of any resident * allocation. */ *cont_item = FALSE; if (a->al_regtype == b->al_regtype) { while (b != (alloc_p) 0) { if (Cis_elem(a->al_id,b->al_rivals)) break; b = b->al_mates; if (b != (alloc_p) 0 && a->al_item == b->al_item) { *cont_item = TRUE; } } } return b == (alloc_p) 0; } STATIC alloc_p find_fitting_alloc(alloc,packed) alloc_p alloc,packed; { /* Try to find and already packed allocation that is assigned * a register that may also be used for alloc. * We prefer allocations that have the same item as alloc. */ register alloc_p x; alloc_p cand = (alloc_p) 0; bool cont_item; for (x = packed->al_next; x != (alloc_p) 0; x = x->al_next) { if (fits_in(alloc,x,&cont_item)) { cand = x; if (cont_item) break; } } return cand; } STATIC bool room_for(alloc,packed) alloc_p alloc,packed; { /* See if there is any register available for alloc */ return reg_available(alloc->al_regtype) || (find_fitting_alloc(alloc,packed) != (alloc_p) 0); } STATIC alloc_p best_alloc(unpacked,packed,time_opt) alloc_p unpacked,packed; bool time_opt; /* now unused */ { /* Find the next best candidate */ register alloc_p x,best; best = unpacked; /* dummy */ for (x = unpacked->al_next; x != (alloc_p) 0; x = x->al_next) { if (x->al_profits > best->al_profits && room_for(x,packed)) { best = x; } } return (best == unpacked ? (alloc_p) 0 : best); } STATIC alloc_p choose_location(alloc,packed,p) alloc_p alloc,packed; proc_p p; { /* Decide in which register to put alloc */ alloc_p fit; offset dum; fit = find_fitting_alloc(alloc,packed); if (fit == (alloc_p) 0) { /* Take a brand new register; allocate a dummy local for it */ alloc->al_regnr = regs_occupied[alloc->al_regtype]++; dum = tmplocal(p,(offset) alloc->al_item->it_size); alloc->al_dummy = dum; } else { alloc->al_regnr = fit->al_regnr; alloc->al_dummy = fit->al_dummy; } return fit; } STATIC void update_lists(alloc,unpacked,packed,fit) alloc_p alloc,unpacked,packed,fit; { /* 'alloc' has been granted a register; move it from the 'unpacked' * list to the 'packed' list. Also remove any allocation from 'unpacked' * having: * 1. the same item as 'alloc' and * 2. a timespan that overlaps the timespan of alloc. */ register alloc_p x,q,next; q = unpacked; /* dummy element at head of list */ for (x = unpacked->al_next; x != (alloc_p) 0; x = next) { next = x->al_next; if (x->al_item == alloc->al_item && not_disjoint(x->al_timespan, alloc->al_timespan)) { /* this code kills two birds with one stone; * x is either an overlapping allocation or * alloc itself! */ q->al_next = x->al_next; if (x == alloc) { if (fit == (alloc_p) 0) { x->al_next = packed->al_next; packed->al_next = x; } else { x->al_mates = fit->al_mates; fit->al_mates = x; x->al_next = (alloc_p) 0; } } } else { q = x; } } } STATIC short cum_profits(alloc) alloc_p alloc; { /* Add the profits of all allocations packed in the same * register as alloc (i.e. alloc and all its 'mates'). */ alloc_p m; short sum = 0; for (m = alloc; m != (alloc_p) 0; m = m->al_mates) { sum += m->al_profits; } return sum; } STATIC alloc_p best_cumprofits(list,x_out,prev_out) alloc_p list, *x_out, *prev_out; { /* Find the allocation with the best cummulative profits */ register alloc_p x,prev,best_prev; short best = 0, cum; prev = list; for (x = list->al_next; x != (alloc_p) 0; x = x->al_next) { cum = cum_profits(x); if (cum > best) { best = cum; best_prev = prev; } prev = x; } if (best == 0) { *x_out = (alloc_p) 0; } else { *x_out = best_prev->al_next; *prev_out = best_prev; } } STATIC void account_regsave(packed,unpacked) alloc_p packed,unpacked; { /* After all packing has been done, we check for every allocated * register whether it is really advantageous to use this * register. It may be possible that the cost of saving * and restoring the register are higher than the profits of all * allocations packed in the register. If so, we simply remove * all these allocations. * The cost of saving/restoring one extra register may depend on * the number of registers already saved. */ alloc_p x,prev,checked; short time,space; short tot_cost = 0,diff; initregcount(); checked = make_dummy(); while (TRUE) { best_cumprofits(packed,&x,&prev); if (x == (alloc_p) 0) break; regs_occupied[x->al_regtype]++; regsave_cost(regs_occupied,&time,&space); diff = add_timespace(time,space) - tot_cost; if (diff < cum_profits(x)) { /* x is o.k. */ prev->al_next = x->al_next; x->al_next = checked->al_next; checked->al_next = x; tot_cost += diff; } else { break; } } /* Now every allocation in 'packed' does not pay off, so * it is moved to unpacked, indicating it will not be assigned * a register. */ for (x = unpacked; x->al_next != (alloc_p) 0; x = x->al_next); x->al_next = packed->al_next; packed->al_next = checked->al_next; oldalloc(checked); } STATIC bool in_single_reg(item,packed) item_p item; alloc_p packed; { /* See if item is allocated in only one register (i.e. not in * several different registers during several parts of its lifetime. */ register alloc_p x,m; bool seen = FALSE; for (x = packed->al_next; x != (alloc_p) 0; x = x->al_next) { for ( m = x; m != (alloc_p) 0; m = m->al_mates) { if (m->al_item == item) { if (seen) return FALSE; seen = TRUE; break; } } } return TRUE; } STATIC alloc_p find_prev(alloc,list) alloc_p alloc,list; { register alloc_p x; assert ( alloc != (alloc_p) 0); for (x = list; x->al_next != alloc ; x = x->al_next) assert(x != (alloc_p) 0); return x; } /* If an item is always put in the same register during different loops, * we try to put it in that register during the whole procedure. * The profits of the whole-procedure allocation are updated to prevent * account_regsave from rejecting it. */ STATIC void repl_allocs(new,old,packed) alloc_p new,old,packed; { alloc_p x,next,prev,*p; short prof = 0; new->al_regnr = old->al_regnr; new->al_dummy = old->al_dummy; prev = find_prev(old,packed); new->al_next = old->al_next; old->al_next = (alloc_p) 0; prev->al_next = new; new->al_mates = old; p = &new->al_mates; for (x = old; x != (alloc_p) 0; x = next) { next = x->al_mates; if (x->al_item == new->al_item) { prof += x->al_profits; *p = next; oldalloc(x); } else { p = &x->al_mates; } } new->al_profits = prof; } STATIC void assemble_allocs(packed) alloc_p packed; { register alloc_p x,m,next; alloc_p e; bool voidb; for (x = packed->al_next; x != (alloc_p) 0; x = next) { next = x->al_next; for ( m = x; m != (alloc_p) 0; m = m->al_mates) { if (in_single_reg(m->al_item,packed) && (e = m->al_wholeproc) != (alloc_p) 0 && e->al_profits > 0 && fits_in(e,x,&voidb)) { repl_allocs(e,x,packed); break; } } } } void pack(alloc_p alloclist, bool time_opt, alloc_p *packed_out, alloc_p *not_packed_out, proc_p p) { /* This is the packing system. It decides which allations * to grant a register. * We use two lists: packed (for allocations that are assigned a * register) and unpacked (allocations not yet assigned a register). * The packed list is in fact '2-dimensional': the al_next field is * used to link allations that are assigned different registers; * the al_mates field links allocations that are assigned to * the same registers (i.e. these allocations fit together). */ register alloc_p x; alloc_p packed,unpacked,fit; initregcount(); packed = make_dummy(); unpacked = make_dummy(); unpacked->al_next = alloclist; while ((x = best_alloc(unpacked,packed,time_opt)) != (alloc_p) 0) { fit = choose_location(x,packed,p); update_lists(x,unpacked,packed,fit); } assemble_allocs(packed); account_regsave(packed,unpacked); /* remove allocations that don't pay off against register * save/restore costs. */ *packed_out = packed->al_next; *not_packed_out = unpacked->al_next; oldalloc(packed); oldalloc(unpacked); }