home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Fresh Fish 6
/
FreshFish_September1994.bin
/
new
/
dev
/
c
/
hce
/
hcesource
/
top
/
source
/
reg.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-09-02
|
18KB
|
852 lines
/* Copyright (c) 1989,1991 by Sozobon, Limited. Author: Tony Andrews
*
* Permission is granted to anyone to use this software for any purpose
* on any computer system, and to redistribute it freely, with the
* following restrictions:
* 1) No charge may be made other than reasonable charges for reproduction.
* 2) Modified versions must be clearly marked as such.
* 3) The authors are not responsible for any harmful consequences
* of using this software, even if they result from defects in it.
*/
/*
* The code in this file deals with "registerizing" local variables and
* parameters. The general idea is to look for highly referenced local
* variables and parameters and effectively turn them into register
* variables automatically. Only the D registers are used, currently, so
* for pointer variables, a manual "register" declaration in the source
* code is actually better.
*
* We need to be certain of several things about a variable before placing
* it in a register. It's address must not be taken, and it must not be
* referred to through "aliases" (e.g. when casting to a shorter object).
* It must be able to fit in a register. And to keep things like printf from
* breaking, parameters can only be registerized if none of the parameters
* have their address taken.
*
* The compiler makes this all possible by placing "hints" within the
* generated assembly code. These hints appear as comments, but are parsed
* by the optimizer, and the information is stashed away by calling addvar().
* The hints give us the size and offset of each parameter and local variable.
* Their names are also given, although that information isn't needed here.
*
* There are tradeoffs to be wary of when registerizing. If no register
* variables exist yet, then "movem" instructions have to be added, requiring
* more references to make this worthwhile. In the case of parameters, the
* register has to be initialized from the stack. The four cases are:
*
* Locals w/ other regs: 1 reference required
* no other regs: 4 references required
* Parms w/ other regs: 2 references required
* no other regs: 6 references required
*
* The numbers above represent the break-even point based on a savings of
* 2 bytes per reference, and the incremental cost of adding "movem" or
* "move" instructions as needed.
*
* This optimizes for space only. To optimize for time, each reference would
* be weighted based on the loop nesting level at which it occurs.
*
* Modified by Detlef Wuerkner for AMIGA
* Changes marked with TETISOFT
*/
#include "top.h"
#define MAXLOCALS 100
static struct linfo {
long offset; /* offset from A6 */
int size; /* size of the object */
int ref; /* # of references to the local */
int reg; /* reg. we assigned it to */
int flags; /* length, etc. */
} locals[MAXLOCALS];
#define ALIASED 0x1 /* offset is aliased with another */
#define ADDR_TAKEN 0x2 /* address of the variable was taken */
#define IS_LOCAL(x) (locals[(x)].offset < 0)
#define IS_PARM(x) (locals[(x)].offset > 0)
static bool paddr; /* address of a parameter was taken */
static int lcnt; /* number of local variables we've seen */
static int rcnt; /* number of locals that got registerized */
static int omask, nmask; /* old and new register masks */
/*
* addvar(size, off) - add a variable entry for the current function
*
* These come from hints the compiler gives us about local variables.
* We use the size and offset here to make sure we don't have aliasing
* problems with the local variables we want to registerize.
*/
void
addvar(size, off)
int size;
int off;
{
locals[lcnt].offset = off;
locals[lcnt].size = size;
locals[lcnt].flags = 0;
locals[lcnt].ref = 0;
lcnt++;
}
/*
* clrvar() - clear the variable list
*/
void
clrvar()
{
register int i;
/*
* re-initialize the local information
*/
for (i=0; i < MAXLOCALS ;i++) {
locals[i].ref = 0;
locals[i].reg = -1;
locals[i].flags = 0;
locals[i].offset = 0;
locals[i].size = 0;
}
paddr = FALSE;
rcnt = lcnt = 0;
}
/*
* setreg() - try to "registerize" local variables in the given function
*/
void
setreg(bp)
BLOCK *bp;
{
void lcheck(), lassign(), lrewrite();
lcheck(bp);
lassign();
#ifdef DEBUG
if (debug)
dump_table();
#endif
if (rcnt > 0)
lrewrite(bp);
s_reg += rcnt; /* keep totals for accounting */
}
/*
* lcheck() - scan for local variable references in the given function
*/
static void
lcheck(bp)
BLOCK *bp;
{
void ckref();
register int i;
register BLOCK *cb;
register INST *ci;
for (cb = bp; cb != NULL ;cb = cb->next) {
for (ci = cb->first; ci != NULL ;ci = ci->next) {
ckref(ci, &ci->src);
ckref(ci, &ci->dst);
}
}
/*
* Now figure out which registers are currently used.
*/
ci = bp->first->next;
if (ci != NULL && ci->opcode == MOVEM) {
if (ci->src.amode == REG)
omask = RM(ci->src.areg);
else
omask = stomask(ci->src.astr);
} else
omask = 0;
}
/*
* ckref() - check for a local variable reference
*
* If a local variable reference is found, it's added to the table or
* (if already there) its reference count is incremented. If we're
* taking its address, note that too.
*/
static void
ckref(ip, op)
INST *ip;
struct opnd *op;
{
register int i;
register int sz;
/* CHANGED BY TETISOFT (see inst.h) */
/* if (op->amode != REGID || op->areg != A6) */
if (op->amode != REGID || op->areg != FRAMEP)
return;
switch (ip->flags) {
case LENL:
sz = 4;
break;
case LENW:
sz = 2;
break;
case LENB:
default: /* for LEA and PEA */
sz = 1;
break;
}
/*
* is the local variable already in the table?
*/
for (i=0; i < lcnt ;i++) {
if (locals[i].offset == op->disp && locals[i].size == sz) {
locals[i].ref++;
break;
}
}
/*
* If not in the table, add an entry for it. If we add an entry
* here, it must be an alias for one of the entries we got via
* the compiler hints.
*/
if (i == lcnt) {
locals[lcnt].offset = op->disp;
locals[lcnt].size = sz;
locals[lcnt].flags = 0;
locals[lcnt].ref = 1;
lcnt++;
}
if (ip->opcode == LEA || ip->opcode == PEA) {
locals[i].flags = ADDR_TAKEN;
/*
* If we took the address of a parameter, note that
* by setting 'paddr'.
*/
if (IS_PARM(i))
paddr = TRUE;
}
}
/*
* lassign() - assign local variable to registers
*
* Check for aliases, sort the table, and then decide how to assign
* the local variables to registers.
*/
static void
lassign()
{
void ck_aliases(), sort_table(), do_sort();
register int i;
register int r;
int minlref; /* min. required references for a local */
int minpref; /* min. required references for a parameter */
ck_aliases(); /* disqualify any "aliased" references */
sort_table(); /* and sort by reference count */
/*
* If there were already "movem" instructions, then we should
* convert as many locals as possible to registers. If we're
* going to have to add the movem's, then we need at least 4
* references for this to be worthwhile. The 2 movem instructions
* take 8 bytes, and each reference conversion saves 2 bytes.
* This analysis optimizes for size.
*/
minlref = (omask != 0) ? 1 : 4;
minpref = (omask != 0) ? 2 : 6;
nmask = omask;
/* CHANGED BY TETISOFT (see inst.h) */
/* for (i=0, r=D3; r <= D7 ;) { */
for (i=0, r=DRV_START; r <= D7 ;) {
/*
* If the register is already in use, skip it.
*/
if (omask & RM(r)) {
r++;
continue;
}
/*
* If no more eligible variables, then stop.
*/
if (locals[i].ref <= 0)
break;
/*
* If something meets the minimums, then assign it to
* the current register, and adjust the minimums.
*/
if ((IS_LOCAL(i) && locals[i].ref >= minlref) ||
(IS_PARM(i) && locals[i].ref >= minpref)) {
locals[i].reg = r;
nmask |= RM(r);
minlref = 1;
minpref = 2;
r++;
i++;
} else {
/*
* If we run into something that isn't referenced
* enough, disqualify it and re-sort. There might
* still be something else worth doing.
*/
locals[i].ref = -locals[i].ref;
do_sort();
}
}
rcnt = i;
}
/*
* ck_aliases() - check for aliases in the locals table
*
* An alias occurs when two differ