home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The C Users' Group Library 1994 August
/
wc-cdrom-cusersgrouplibrary-1994-08.iso
/
vol_300
/
357_01
/
cstar1.exe
/
X2.C
< prev
next >
Wrap
C/C++ Source or Header
|
1991-06-18
|
41KB
|
1,527 lines
/*
C* -- Code generation -- Support routines.
source: x2.c
started: January 21, 1986
version:
February 20, 1987
March 7, 1989
PUBLIC DOMAIN SOFTWARE
The CSTAR program was placed in the public domain on June 15, 1991,
by its author and sole owner,
Edward K. Ream
1617 Monroe Street
Madison, WI 53711
(608) 257-0802
CSTAR may be used for any commercial or non-commercial purpose.
See cstar.h or cstar.c for a DISCLAIMER OF WARRANTIES.
*/
#include "cstar.h"
struct node * x_addpsi(register struct node *locp,
register struct node *loci,
register int x_op);
int x_shift (register unsigned long scale);
struct node * x_scale (register struct node * loc1, unsigned long scale);
struct node * x_subp (register struct node *loc1, struct node *loci,
unsigned long scale);
struct node * x_cast (register struct node *loc1,
int len1, int imod1, int len3, bool dloc_ok);
struct node * resolve (register struct node *loc1);
void x_lookat(register struct node *loc1);
struct node * x_sspush(register struct node *loc1);
void gen_pp (struct node *p);
/*
CAUTION: the following assumptions are made in the arithmetical
generators:
1. "A" temporaries are assumed to be valid to all 32 bits. All
"A" word operations produce sign-extended results, so the
only way to violate this is to EXG in a D register that has
trash in the upper word. Generators that do this are to undo
it before they exit.
2. A couple of routines depend explicitly on receiving integer
loc_node arguments that are already set up to be int_type
or long_type. Beware.
3. Pointers are everywhere implicitly and explicitly assumed to
be long (32 bits); there is no short pointer type. Let's
hope MacIntosh doesn't have functions that pass a short pointer
by reference, and intend it to be 16 bits. (Since the 68000
is bass-ackwards, in order to slavishly imitate DEC minis,
the memory address of the 16-bit lower portion of a pointer is
NOT the same as the memory address of the entire 32-bit
pointer. I wonder if the 68020 has to spend extra nanoseconds
to flip the halves of a word on a long store; I suppose either
it does or else the memory hardware does.
4. If a loc_node contains two register entries, the first one
is to be long. Such nodes are at present pointer nodes
created by x_addpsi(); so the potential violation is via
sop_cast().
*/
/*
Access to register parameter, for unsigned extend in casts and
in pointer addition
*/
extern int na_free;
extern int nd_free;
/*
L O W L E V E L L O C _ N O D E R O U T I N E S
*/
/*
add/subtract <<< int_type or long_type only >>> to/from pointer
loci is resolved prior to entry except when used for subtraction
neither operands nor result are necessarily resolved
loci is typically obtained from an arbitrary int using x_scale;
use x_scale(loci, 1L) on unknown ints before calling this
loci should NOT be used by the
caller afterward (its temps will be freed anyhow)
this code attempts to add an integer (already made signed-short
or signed-long elsewhere) to a pointer, and prefers to do it
by combination with the already existing node if possible. the
sizes of constants are NOT checked here against the size limits
imposed by the processor; that is resolve's job. the addition
or subtraction of two GLOBALS is checked, however, since there
is no representation for an address containing two independent
globals, either here or in any typical linker.
this code consists basically of a multitude of special cases
*/
struct node *
x_addpsi( register struct node *locp, register struct node *loci,
register int x_op)
{
register struct node *loct, *locx;
register struct st_node *id;
register int reg;
register long i_cnst, p_cnst;
int gi_symbol, gp_symbol;
TRACEPB("x_addpsi",
printf("(%p, %p, %d)\n", locp, loci, x_op);
pr_loc(locp); printf("; ");
pr_loc(loci); printf(";\n"));
/* for addition to zero, do absolutely nothing */
if (is_zloc(loci)) {
TRACEP("x_addpsi", printf("does nothing\n"));
RETURN_PTR("x_addpsi", locp);
}
x_op = (x_op == MINUS_TOK)? X_SUB : X_ADD;
/* we do intend to represent the actual sum, so we must resolve
pointer EA loc_node. */
if (is_ea(locp -> n_mode)) {
locp = resolve(locp); /* we have to get it */
if (reg = has_atreg(locp)) {
loct = locn_reg(reg);
free_xtemp(locp, loct);
}
else if (x_op == X_ADD &&
(is_atloc(loci) ||
(is_dtloc(loci) && loci -> n_cltype == long_type))
) {
/* SHORTCUT */
/* add the EA pointer directly to the xreg int */
/* areg assumption: all 32 areg bits valid--true if
EXG instruction is never used to insert them */
g_2l1(x_op, locp, loci);
loci -> n_cltype = locp -> n_cltype;
RETURN_PTR("x_addpsi", loci);
}
else {
loct = get_atemp();
free_temp(locp);
/* ss_push serves no purpose here since it has no atemp */
}
loct -> n_cltype = locp -> n_cltype;
g_2l1(X_MOVEA, locp, loct);
locp = locn_dupl(loct);
}
else {
locp = locn_xdupl(locp);
}
/* the upshot of the above is that locp is now a VALUE */
if (is_ea(loci -> n_mode) || x_op == X_SUB) {
loci = resolve(loci);
}
loci = locn_xdupl(loci);
/* now attempt combination of constants */
if (loci -> n_mode == VALUE_MODE) {
gi_symbol = gp_symbol = FALSE;
p_cnst = locp -> n_const;
i_cnst = loci -> n_const;
/* check whether any of the symbols are global */
if (id = locp -> n_cid) {
if (is_rstack(id -> st_sclass) ||
id -> st_sclass == SUE_CLASS) {
p_cnst += id -> st_offset;
}
else {
gp_symbol = TRUE;
}
}
if (id = loci -> n_cid) {
if (is_rstack(id -> st_sclass) ||
id -> st_sclass == SUE_CLASS) {
i_cnst += id -> st_offset;
}
else {
gi_symbol = TRUE;
}
}
if (x_op == X_SUB) {
i_cnst = -i_cnst;
}
/* at this point non-global components are evaluated */
/* NOTE:
global symbols are not known until link time; they
can't be combined here, and must be presumed to be longare generally assigned by the linker, and
*/
/* transfer constants from i to p except for two globals */
locp -> n_const += i_cnst;
loci -> n_const = 0;
if (gi_symbol) {
if (gp_symbol) {
}
else {
locp -> n_const = i_cnst + p_cnst;
locp -> n_cid = loci -> n_cid;
}
}
else {
loci -> n_cid = NULL;
}
/* transfer register from i to p as space permits */
if (x_op == X_ADD) {
TRACEP("x_addpsi",
printf("addition--transfer regs\n"));
#ifdef DEBUG
if (loci -> n_reg2) {
g_error(NULL, "x_addpsi: internal: two i regs");
}
#endif /* DEBUG */
if (loci -> n_cltype == int_type) {
if (locp -> n_reg1 && !locp -> n_reg2) {
locp -> n_reg2 = loci -> n_reg1;
loci -> n_reg1 = NULL;
locp -> n_scflag = X2_WORD;
}
}
else {
if (!locp -> n_reg1) {
locp -> n_reg1 = loci -> n_reg1;
loci -> n_reg1 = NULL;
}
else if (!locp -> n_reg2) {
locp -> n_reg2 = loci -> n_reg1;
locp -> n_scflag = X2_LONG;
loci -> n_reg1 = NULL;
}
}
}
}
TRACEP("x_addpsi",
printf("transfers done: ");
pr_loc(locp); printf("; ");
pr_loc(loci); printf(";\n"));
if (is_zloc(loci)) {
/* SHORTCUT */
RETURN_PTR("x_addpsi", locp);
}
/* get it all added up; there is a reg or const that didn't move */
/* now try to add to a register already part of the pointer node */
if (
(reg = has_atreg(locp)) ||
(is_dtreg(reg = locp -> n_reg1) && loci -> n_cltype == long_type) ||
(is_dtreg(reg = locp -> n_reg2) &&
((locp->n_scflag == X2_LONG)? long_type : int_type) == loci->n_cltype)
) {
TRACEP("x_addpsi", printf("locp temporary\n"));
/* do it in the temporary, no cast needed */
loct = locn_reg(reg);
if (loci -> n_mode == VALUE_MODE) {
loct -> n_cltype = loci -> n_cltype;
if (loci -> n_reg1) {
g_2l2(x_op, locn_reg(loci -> n_reg1), loct);
}
loci -> n_reg1 = 0;
if (loci -> n_cid) {
g_2l2(x_op, loci, loct);
}
}
else {
g_2l1(x_op, loci, loct);
}
/* all added up */
}
else if (x_op == X_ADD && is_atreg(reg = loci -> n_reg1)) {
/* do it in the integer's areg */
TRACEP("x_addpsi", printf("loci temporary\n"));
loct = locn_reg(reg);
loct -> n_cltype = long_type;
if (is_ea(loci -> n_mode)) {
g_2l1(X_MOVEA, loci, loct);
free_reg(loci -> n_reg2);
loci = loct;
}
else if (loci -> n_cid) {
loci -> n_reg1 = 0;
g_2l1(X_ADDA, loci, loct);
}
if (locp -> n_reg1) {
g_2l2(X_ADDA, locn_reg(locp -> n_reg1), loct);
}
locp -> n_reg1 = reg;
/* all added up */
}
else {
/*
loci and locp are both arbitrary combinations of
registers and constants; loci may not have a
second register, nor an n_const; it may have
one register and/or an n_cid
locp is now in VALUE mode but not necessarily
resolved (usable as an address mode). if it
involves a register, that register will be
substituted with the temporary and the integer
components added to it or subtracted from it.
otherwise, the temporary will be inserted, and
the integer components inserted and added or
subtracted. for subtraction, a D temporary may
be used.
*/
TRACEP("x_addpsi", printf("temporary required\n"));
if (x_op == X_SUB && !locp -> n_reg2) {
loct = get_dtemp();
}
else {
loct = get_atemp();
}
loct -> n_cltype = locp -> n_cltype;
if (is_zloc(locp)) {
locp = loct;
}
else {
if (locp -> n_reg1) {
/* substitute the temporary for it */
g_2l2(X_MOVE, locn_reg(locp -> n_reg1), loct);
free_reg(locp -> n_reg1);
locp -> n_reg1 = loct -> n_reg1;
}
else {
g_2l1(X_MOVE, locp, loct);
locp = loct;
}
if (is_ea(loci -> n_mode)) {
g_2l1(x_op, loci, loct);
free_temp(loci);
}
else {
if (loci -> n_cid) {
/* loci -> n_const should have been
transferred */
locx = locn_dupl(loci);
locx -> n_reg1 = 0;
loci -> n_cid = 0;
g_2l1(x_op, locx, loct);
}
if (loci -> n_reg1) {
g_2l1(x_op, loci ,loct);
}
}
if (is_dreg(locp -> n_reg1) && !is_dloc(locp)) {
/* don't return a composite D-value node */
/* note that the conditions above for drawing
a D temp restrict the combinations
possible here */
locp -> n_reg1 = 0;
g_2l1(x_op, locp, loct);
locp = loct;
}
}
/* all added up */
if (x_op == X_SUB) {
locp -> n_cltype = long_type;
}
}
TRACEP("x_addpsi",
printf("returns: ");
pr_loc(locp); printf("; ");
pr_loc(loci); printf(";\n"));
RETURN_PTR("x_addpsi", locp);
}
/*
return a shift counter if a particular scaling can be done as a shift
*/
int
x_shift(register unsigned long scale)
{
register int count;
register unsigned long x;
TRACEPB("x_shift", printf("(%lu)\n", scale));
x = 1;
count = 0;
while (x && x < scale) {
count++;
x <<= 1;
}
if (x == scale) {
RETURN_INT("x_shift", count);
}
else {
RETURN_INT("x_shift", -1);
}
}
/*
Given a location containing an unscaled integer of any sort,
return a location containing the integer multiplied by
the scale argument, and which is always either int_type
(signed word) or else long_type (signed vs. unsigned makes no
difference).
The returned node is ALWAYS modifiable; that is, it is never
a node which is already attached to something in the code list.
CAUTION: the postponement of scaling, if any, is someone else's
problem.
This code contains a multitude of special cases; the scaling
process does not take quite so long as the size of this function
suggests.
*/
struct node *
x_scale(register struct node * loc1, unsigned long scale)
{
register int imod, in_place, shift, dloc_ok;
register struct node *loc3, *loc4;
int cast;
TRACEPB("x_scale",
printf("(%p by %lu)\n", loc1, scale);
pr_loc(loc1); printf(";\n"));
/* ------ if it's a constant, life is very simple ------ */
if (loc3 = fix_cloc(loc1)) {
/* loc3 is a copy if necessary (locn_xdupl) */
loc3 -> n_const *= (long) scale;
if (loc3 -> n_const <= 32767 && loc3 -> n_const >= -32768L) {
loc3 -> n_cltype = int_type;
}
else {
loc3 -> n_cltype = long_type;
}
TRACEP("x_scale",
printf("returns (const): ");
pr_loc(loc3); printf(";\n"));
RETURN_PTR("x_scale", loc3);
}
/* ------ decide whether a cast is necessary ------ */
shift = x_shift(scale);
imod = loc1->n_cltype->t_mclass & (LONG_MOD | CHAR_MOD | UNSIGNED_MOD);
if (imod & LONG_MOD) {
imod &= ~UNSIGNED_MOD; /* ignore sign of long */
}
cast = imod & (CHAR_MOD | UNSIGNED_MOD);
/* chars and non-long unsigneds have to be exteneded */
/* ------ perform special areg cast and scaling by 2 or 4 ------ */
if (shift && na_free && !cast) { if (
(shift == 1) ||
(shift == 2 && (!is_dtloc(loc1) || !(imod & LONG_MOD)) )
) {
TRACEP("x_scale", printf("special scale\n"));
loc1 = resolve(loc1);
/* SHORTCUT */
if (!is_atloc(loc1)) {
ss_push(loc1);
loc3 = get_atemp();
loc1 = ss_pop();
g_2l1(X_MOVEA, loc1, loc3);
free_temp(loc1);
}
else {
loc3 = locn_xdupl(loc1);
}
loc3 -> n_cltype = long_type;
while (shift--) {
g_2l1(X_ADDA, loc3, loc3);
}
TRACEP("x_scale",
printf("returns (areg): ");
pr_loc(loc3); printf(";\n"));
RETURN_PTR("x_scale", loc3);
} }
/* ------ cast index and scale as necessary ------ */
if (cast || scale != 1) {
TRACEP("x_scale", printf("cast or scale\n"));
dloc_ok = (scale == 1 && EXCESSLENOK);
/* perform the appropriate cast */
if (imod & LONG_MOD) {
loc3 = x_cast(loc1, 4, imod, 4, dloc_ok);
}
else if (imod & CHAR_MOD) {
/* char to int or long in dreg */
if (shift >= 8 ||
( (~imod & UNSIGNED_MOD) && scale > 32767) ||
scale > 65535L) {
loc3 = x_cast(loc1, 1, imod, 4, dloc_ok);
}
else {
loc3 = x_cast(loc1, 1, imod, 2, dloc_ok);
}
}
else if (shift > 0 || ((imod & UNSIGNED_MOD) && scale == 1) ||
scale > 32767) {
loc3 = x_cast(loc1, 2, imod, 4, dloc_ok);
}
else {
loc3 = x_cast(loc1, 2, imod, 2, dloc_ok);
}
/* the index is now loc3, and loc1 is discarded */
/* now carry out the scaling */
if (scale != 1) {
TRACEP("x_scale",
printf("scaling required: %lx\n", scale));
if (shift > 0) {
if (shift == 1) {
g_2l1(X_ADD, loc3, loc3);
}
else if (nd_free) {
loc4 = get_dtemp();
loc4 -> n_cltype = int_type;
g_2l2(X_MOVE, locn_xconst((long)shift),
loc4);
g_2l2(X_ASL, loc4, loc3);
free_temp(loc4);
}
else {
while (shift > 8) {
g_2l2(X_ASL, locn_xconst(8L),
loc3);
shift -= 8;
}
if (shift) {
g_2l2(X_ASL, locn_xconst((long)shift),
loc3);
}
}
}
/* NOTE: in CHAR_MOD with scale < 256, these
multiplies really ought to return a sort-of
long-or-int type */
/* SIDE EFFECT: */
else if (loc4 = locn_xconst(scale),
(imod & UNSIGNED_MOD) && scale < 65536L) {
g_2(X_MULU, loc4, loc3);
loc3 -> n_cltype = long_type;
}
else if ((~imod & LONG_MOD) && scale <= 32767L) {
g_2(X_MULS, loc4, loc3);
loc3 -> n_cltype = long_type;
}
else {
g_2call(loc4, loc3,
((imod & UNSIGNED_MOD) ||
scale >= (unsigned long) 0x80000000L) ?
"lmulu" : "lmul" );
loc3 = d0_loc;
loc3 -> n_cltype = long_type;
}
}
}
else {
/* no cast and no scaling */
loc3 = locn_xdupl(loc1);
loc3 -> n_cltype = (imod & LONG_MOD)? long_type : int_type;
}
TRACEP("x_scale",
printf("returns: ");
pr_loc(loc3); printf(";\n"));
RETURN_PTR("x_scale", loc3);
}
/*
Subtract two pointers and descale according to given scale (do not
take scale from either pointer)
This function generates code for a LONG WORD.
*/
struct node *
x_subp(register struct node *loc1, struct node *loci, unsigned long scale)
{
register int imod, in_place, shift;
register struct node *loc3, *loc4;
int cast;
/* this subtraction always gets done at runtime and always gets
done in a Dreg and nothing gets moved from one node to the other */
TRACEPB("x_subp", printf("(%p, %p, %lu)\n", loc1, loci, scale));
loci = resolve(loci);
loc1 = resolve(loc1);
if (!is_dtloc(loc1)) {
loc3 = get_dtemp();
loc3 -> n_cltype = loc1 -> n_cltype;
g_2l1(X_MOVE, loc1, loc3);
loc1 = loc3;
}
loc1 = locn_xdupl(loc1);
loc1 -> n_cltype = long_type;
/* the following subtraction yields a 33-bit result */
/* see lint.doc on folded variables */
g_2l2(X_SUB, loci, loc1);
if (scale <= 1) {
/* SHORTCUT */
if (scale == 1) {
RETURN_PTR("x_subp", loc1); /* and the context better know bit 32 */
}
else {
g_error(NULL, "subtraction of pointers to void");
RETURN_PTR("x_subp", zero_loc);
}
}
/* ------ do a shift if possible ------ */
shift = x_shift(scale);
if (shift > 0) {
/* SHORTCUT */
TRACEP("x_subp", printf("arithmetic shift"));
g_2l2(X_ROXR, one_loc, loc1);
shift--;
while (shift > 8) {
g_2l2(X_ASR, locn_xconst(8L), loc1);
shift -= 8;
}
if (shift) {
g_2l2(X_ASR, locn_xconst((long)shift), loc1);
}
RETURN_PTR("x_subp", loc1);
}
/* WARNING: this does not deal with the 33rd bit! */
loci = locn_xconst(scale);
loci -> n_cltype = long_type;
g_2call(loc1, loci, "ldiv");
d0_loc -> n_cltype = long_type;
RETURN_PTR("x_subp", d0_loc);
}
/*
Extend something and leave the result in a D temporary
If dloc_ok, leave the result in-place even if the D
location is not a temporary; this must be used with caution!
Result type is set to int_type or long_type, for use in the
pointer scaling routines. For sop_cast and other uses, the
result type should be set to the cast operator type.
If the extension is nil (len1 == len3), it still gets loaded.
*/
struct node *
x_cast(register struct node *loc1, int len1, int imod1, int len3, bool dloc_ok)
{
register int reg, in_place;
register struct node *loc3;
TRACEPB("x_cast", printf("(%p, %d, %d, %d, %s)\n",
loc1, len1, imod1, len3, sl_sbout(dloc_ok)));
loc1 = resolve(loc1);
if (((reg = has_dtreg(loc1)) || (dloc_ok && is_dloc(loc1))) &&
!(nd_free && len3 == 4 && (imod1 & UNSIGNED_MOD)) ) {
/* dloc or dtloc and do in place */
in_place = TRUE;
if (is_dloc(loc1)) {
loc3 = locn_xdupl(loc1);
}
else {
loc3 = locn_reg(reg);
g_2l1(X_MOVE, loc1, loc3);
}
}
else {
/* draw a D temp if none in node, or if unsigned-to-long */
/* unsigned-to-16-bit is just as fast in_place */
ss_push(loc1);
loc3 = get_dtemp();
loc1 = ss_pop();
in_place = FALSE;
}
loc3 -> n_cltype = (len3 > 2)? long_type : int_type;
/* perform actual extension in D register */
if (len3 <= len1) {
/* no extension */
if (!in_place) {
/*
for a shrink, this moves more than needed,
but it gets the address alignment correct
and this is not the shrink routine anyhow;
see sop_cast
*/
g_2l1(X_MOVE, loc1, loc3);
}
}
/* various extension operations in each case */
/* caller is responsible for special areg casts */
else if (imod1 & UNSIGNED_MOD) {
if (in_place) {
/* in place */
if (len1 == 1) {
g_2l2(X_AND, locn_xconst(255L), loc3);
if (len3 > 2) {
g_1l(X_EXT, loc3);
}
}
else {
/* len1 == 2 */
/* andi long takes 16 clocks; this takes 12 */
/* clearly a 68000 botch! */
g_1(X_SWAP, loc3);
loc3 -> n_cltype = int_type;
g_1l(X_CLR, loc3);
loc3 -> n_cltype = long_type;
g_1(X_SWAP, loc3);
}
}
else {
g_1l(X_CLR, loc3);
g_2l1(X_MOVE, loc1, loc3);
}
}
else {
/* signed extension */
if (!in_place) {
g_2l1(X_MOVE, loc1, loc3);
}
if (len1 == 1 && len3 > 2) {
/* then: will need two extends */
loc3 -> n_cltype = int_type;
g_1l(X_EXT, loc3);
loc3 -> n_cltype = long_type;
}
g_1l(X_EXT, loc3);
}
RETURN_PTR("x_cast", loc3);
}
/*
Evaluate a node to the point where it could be used as
an <EA> mode in a 68000 machine instruction. For EA mode
nodes, this will typically mean adding the constant to
one or another of the registers, or adding into a temporary,
if the constant is out of range. For VAL mode, it means
adding everything up, into a register if a register is
already involved.
The second register in a loc_node is never to be set up except
by x_addpsi() and is to be X2_WORD or X2_LONG.
This code is a multitude of special cases.
*/
struct node *
resolve(register struct node *loc1)
{
register struct node *locp, *locc, *loc3;
register struct st_node *id;
register int reg, g_symbol;
register long i_cnst;
TRACEPB("resolve",
printf("(%p)\n", loc1);
pr_loc(loc1); printf("\n"));
g_symbol = FALSE;
i_cnst = loc1 -> n_const;
if (id = loc1 -> n_cid) {
if (is_rstack(id -> st_sclass) || id->st_sclass == SUE_CLASS) {
/* i_cnst is the true value of the const if known */
i_cnst += id -> st_offset;
}
else {
/* the true value is unknown and long */
g_symbol = TRUE;
}
}
if (!loc1 -> n_reg1) {
/* constant */
TRACEP("resolve",
printf("returns (constant/constant EA): ");
pr_loc(loc1); printf("\n"));
RETURN_PTR("resolve", loc1);
}
/* from here on the node must represent a variable involving a reg */
if (!g_symbol && i_cnst == 0 && loc1 -> n_cid) {
loc1 = locn_xdupl(loc1);
loc1 -> n_cid = NULL;
loc1 -> n_const = 0;
TRACEP("resolve", printf("consolidate zero\n"));
}
/* for dual-reg node: rearrange to put the "A" register first */
/* the node was to have been set up by x_addpsi and was pointer at
that time */
reg = loc1 -> n_reg2;
if ( (is_atreg(reg) && !is_atreg(loc1 -> n_reg1)) ||
(is_areg(reg) && !is_areg(loc1 -> n_reg1)) ) {
/* switch */
loc1 = locn_xdupl(loc1);
loc1 -> n_reg2 = loc1 -> n_reg1;
loc1 -> n_reg1 = reg; /* the areg can be taken as long */
loc1 -> n_scflag = X2_LONG; /* first item always long */
}
if (is_ea(loc1 -> n_mode)) {
/* force an areg into the mode; it is otherwise unusable */
if (!is_areg(loc1 -> n_reg1)) {
loc3 = get_atemp();
loc3 -> n_cltype = long_type;
g_2l2(X_MOVEA, locn_reg(loc1 -> n_reg1), loc3);
loc1 = locn_xdupl(loc1);
loc1 -> n_reg1 = loc3 -> n_reg1;
}
/* now see if it is OK as is */
if (!g_symbol &&
((!loc1->n_reg2 && i_cnst<=32767 && i_cnst >= -32768L)
||
(i_cnst <= 127 && i_cnst >= -128))
) {
/* it is a valid mode as is */
TRACEP("resolve",
printf("returns ");
pr_loc(loc1); printf("\n"));
RETURN_PTR("resolve", loc1);
}
/* areg node doesn't work as is; must start adding */
/* the constant must be too big */
locc = locn_xdupl(loc1);
locc -> n_reg1 = locc -> n_reg2 = 0;
locc -> n_mode = 0;
if (g_symbol ||
locc -> n_const <= -32768L || locc -> n_const >= 32767) {
locc -> n_cltype = long_type;
}
else {
locc -> n_cltype = int_type;
}
/* we WILL now add, so we either shortcut or else get an
atreg in which to add */
if (!is_atreg(loc1 -> n_reg1)) {
/* first reg is therefore a DESIGNATED areg */
/* WARNING:
after we have postponement of scaling,
we will probably want to do an ext.l
on the D temporary in the X2_WORD case
instead of skipping to the get-atemp
code. For now, however, doing that produces
silly cascades of extends and additions into
the D register.
*/
if (is_dtreg(loc1 -> n_reg2) &&
loc1 -> n_scflag == X2_LONG) {
/* SHORTCUT */
/* add the constant to the already long dreg */
locc -> n_cltype = long_type;
g_2l1(X_ADD, locc, locn_reg(loc1 -> n_reg2));
loc1 = locn_xdupl(loc1);
loc1 -> n_cid = NULL;
loc1 -> n_const = 0L;
TRACEP("resolve",
printf("returns (dreg): ");
pr_loc(loc1);printf("\n"));
RETURN_PTR("resolve", loc1);
}
loc3 = get_atemp();
loc3 -> n_cltype = long_type;
g_2l2(X_MOVEA, locn_reg(loc1 -> n_reg1), loc3);
loc1 = locn_xdupl(loc1);
loc1 -> n_reg1 = loc3 -> n_reg1;
}
/* now we have an atreg node, so all we need do is add const */
/* 3/6/89 !loc1 was !locp */
if (!loc1 -> n_reg2 || g_symbol ||
i_cnst < -32768L || i_cnst > 32767) {
/* one or two registers including an atemp */
/* add the constant to the atemp */
g_2l1(X_ADDA, locc, locn_reg(loc1 -> n_reg1));
loc1 = locn_xdupl(loc1);
loc1 -> n_cid = NULL;
loc1 -> n_const = 0L;
}
else {
/* two registers with atemp, and word constant */
/* add the other register to the atemp */
/* this is a judgment call; the register-to-register
add in a sense wastes two clock cycles in some
cases. we could add the constant to the atemp;
in many cases that would produce an 0(a,x) mode
that would have nothing more added to the constant,
or tend to trigger more of the same since it's
almost full-up; that may also in effect waste
a couple of cycles
*/
loc3 = locn_reg(loc1 -> n_reg2);
loc3 -> n_cltype = (loc1 -> n_scflag == X2_WORD)?
int_type : long_type;
g_2l1(X_ADDA, loc3, locn_reg(loc1 -> n_reg1));
loc1 = locn_xdupl(loc1);
loc1 -> n_reg2 = 0;
free_xtemp(loc3, loc1);
}
TRACEP("resolve",
printf("returns: ");
pr_loc(loc1); printf("\n"));
RETURN_PTR("resolve", loc1);
}
else {
/* the node is required as a value, so it has to be
added up to yield an actual number in a single register */
if (i_cnst == 0 && !g_symbol && !loc1 -> n_reg2) {
TRACEP("resolve",
printf("returns (as is): ");
pr_loc(loc1); printf("\n"));
RETURN_PTR("resolve", loc1);
}
#ifdef DEBUG
/* the shortening cast is to resolve its target, so this
is not to happen */
if (mlen(loc1) < 4) {
g_error(loc1, "resolve: internal: must not happen");
TRACEP("resolve",
printf("short combined node: ");
pr_loc(loc1); printf("\n");
pr_type(loc1 -> n_cltype); printf("\n"));
}
#endif /* DEBUG */
/* find a destination */
if (reg = has_atreg(loc1)) {
loc3 = locn_reg(reg);
}
else if (is_dtreg(reg = loc1 -> n_reg1)) {
loc3 = locn_reg(reg);
}
else if (loc1 -> n_scflag == X2_LONG &&
is_dtreg(reg = loc1 -> n_reg2)) {
loc3 = locn_reg(reg);
}
else {
loc3 = get_atemp();
/* try to pick it up with LEA in one fell swoop */
if (!g_symbol && is_areg(loc1 -> n_reg1) &&
(
(!loc1->n_reg2 && i_cnst<=32767 && i_cnst >= -32768L) ||
(i_cnst <= 127 && i_cnst >= -128) )
) {
/* it is a valid mode */
g_2(X_LEA, locn_chmod(loc1, EA_MODE), loc3);
TRACEP("resolve",
printf("returns (LEA): ");
pr_loc(loc3);printf("\n"));
free_temp(loc1);
loc3 -> n_cltype = loc1 -> n_cltype;
RETURN_PTR("resolve", loc3);
}
/* otherwise move and add */
/* LEA of the two registers into the temp is not
any faster, so we won't try it */
loc3 -> n_cltype = loc1 -> n_cltype;
g_2l2(X_MOVEA, locn_reg(loc1 -> n_reg1), loc3);
loc3 -> n_cltype = long_type;
free_reg(loc1 -> n_reg1);
loc1 = locn_xdupl(loc1);
loc1 -> n_reg1 = reg = loc3 -> n_reg1;
}
/* see if it can be obtained with LEA as is */
/* NOTE: double-check timing & make sure this never harms */
if (!g_symbol && is_areg(reg) &&
(
(!loc1->n_reg2 && i_cnst<=32767 && i_cnst >= -32768L) ||
(i_cnst <= 127 && i_cnst >= -128) )
) {
/* it is a valid mode */
g_2(X_LEA, locn_chmod(loc1, EA_MODE), loc3);
free_xtemp(loc1, loc3);
TRACEP("resolve",
printf("returns (LEA): ");
pr_loc(loc3);printf("\n"));
loc3 -> n_cltype = loc1 -> n_cltype;
RETURN_PTR("resolve", loc3);
}
/* must add the other register and the constant into loc3 */
locc = locn_xdupl(loc1);
locc -> n_reg1 = locc -> n_reg2 = 0;
if (g_symbol || is_dreg(reg) ||
locc -> n_const <= -32768L || locc -> n_const >= 32767) {
locc -> n_cltype = long_type;
}
else {
locc -> n_cltype = int_type;
}
/* add the "other" register to the result */
if (reg == loc1 -> n_reg2) {
loc3 -> n_cltype = long_type;
g_2l2(X_ADD, locn_reg(loc1 -> n_reg1), loc3);
}
else {
if (loc1 -> n_reg2) {
loc3->n_cltype = (loc1 -> n_scflag == X2_WORD)?
int_type : long_type;
g_2l2(X_ADD, locn_reg(loc1 -> n_reg2), loc3);
}
}
/* now we have an atreg node, so all we need do is add const */
if (g_symbol || i_cnst) {
g_2l1(X_ADD, locc, loc3);
}
TRACEP("resolve",
printf("returns (add): ");
pr_loc(loc3); printf("\n"));
loc3 -> n_cltype = loc1 -> n_cltype;
free_xtemp(loc1, loc3);
RETURN_PTR("resolve", loc3);
}
}
/*
Touch a loc_node in order to make sure the side effects get
done. Typically what this does is increment the areg involved
in a dangling EA_PRD or EA_PSI node, as when the programmer
writes
*x++;
as a complete statement. This is called by the expression entry
point routines on the item which always dangles on the stack when they
are about to return.
*/
void
x_lookat(register struct node *loc1)
{
register struct node *loc3;
long x;
TRACEPB("x_lookat", printf("(%p)\n", loc1));
switch (loc1 -> n_mode) {
case EAPRD_MODE:
x = (long) mlen(loc1);
if (x == 1 && loc1 -> n_reg1 == R_A7) {
x = 2;
}
loc3 = locn_reg(loc1 -> n_reg1);
loc3 -> n_cltype = int_type;
g_2l2(X_SUBQ, locn_xconst(x), loc3);
break;
case EAPSI_MODE:
x = (long) mlen(loc1);
if (x == 1 && loc1 -> n_reg1 == R_A7) {
x = 2;
}
loc3 = locn_reg(loc1 -> n_reg1);
loc3 -> n_cltype = int_type;
g_2l2(X_ADDQ, locn_xconst(x), loc3);
break;
}
TICKX("x_lookat");
}
/*
Conditionally push a node onto the physical stack and return
an adjusted node which will work later when popped from the
SIMULATION stack. Unlike resolve(), this is not to draw a temporary.
Any node containing no temporary returns NULL.
Any node containing a temporary gets resolved and pushed,
possibly by PEA. The temporary is then freed, and the caller
may inspect nd_free/na_free to see what it is.
The second register is never to be set up except
by x_addpsi() and is signed: X2_WORD or X2_LONG.
*/
struct node *
x_sspush(register struct node *loc1)
{
register struct node *locx, *locp, *locc, *loc3;
register struct st_node *id;
register int reg, g_symbol;
register long i_cnst;
struct type_node *type1;
TRACEPB("x_sspush",
printf("(%p)\n", loc1);
pr_loc(loc1); printf("\n"));
if (!has_atreg(loc1) && !has_dtreg(loc1)) {
TRACEP("x_sspush", printf("no temp to free\n"));
RETURN_PTR("x_sspush", NULL);
}
/* node contains at least one temporary */
loc1 = locn_dupl(loc1);
/* this routine might be called again on loc1, so
it should not alter loc1 ever */
locx = NULL; /* for exit adjustment !!! */
locp = NULL; /* for addition */
/* check symbols */
g_symbol = FALSE;
i_cnst = loc1 -> n_const;
if (id = loc1 -> n_cid) {
if (is_rstack(id -> st_sclass) || id->st_sclass == SUE_CLASS) {
/* i_cnst is the true value of the const if known */
i_cnst += id -> st_offset;
}
else {
/* the true value is unknown and long */
g_symbol = TRUE;
}
}
if (!g_symbol && i_cnst == 0 && loc1 -> n_cid) {
loc1 -> n_cid = NULL;
loc1 -> n_const = 0;
TRACEP("x_sspush", printf("consolidate zero\n"));
}
/* pure register push */
if (loc1 -> n_mode == VALUE_MODE && !loc1 -> n_cid &&
!loc1 -> n_const && !loc1 -> n_reg2) {
g_2l1(X_MOVE, loc1, push_loc);
goto push_adj;
}
/* at this point, it is shown to be a combined or EA node and
reg1 must be long */
#ifdef DEBUG
if (loc1 -> n_mode == VALUE_MODE && mlen(loc1) < 4) {
g_error(loc1, "x_sspush: internal: must not happen");
TRACEP("x_sspush",
printf("short combined node: ");
pr_loc(loc1); printf("\n");
pr_type(loc1 -> n_cltype); printf("\n"));
}
#endif /* DEBUG */
/* if dual-reg node, put the temporary first; put the areg first
if two temporaries */
if ( !(is_atreg(loc1 -> n_reg1) || is_dtreg(loc1 -> n_reg1)) ||
(is_atreg(loc1 -> n_reg2) && !is_atreg(loc1 -> n_reg1))
) {
/* switch */
reg = loc1 -> n_reg2;
loc1 -> n_reg2 = loc1 -> n_reg1;
loc1 -> n_reg1 = reg; /* the areg can be taken as long */
if (loc1 -> n_scflag != X2_LONG) {
loc1 -> n_scflag = X2_LONG;
if (is_dreg(reg)) {
locp = locn_reg(reg);
locp -> n_cltype = long_type;
g_1l2(X_EXT, locp); /* 3/5/89: was g_2l1() */
}
}
}
/* a temporary is always first, and is always long */
/* if it is EA, we MUST have an areg */
/* if second register is short, we must have areg since we may
not be able to cast the second register */
if (is_dreg(loc1 -> n_reg1) &&
(loc1 -> n_scflag != X2_LONG || is_ea(loc1 -> n_mode)) ) {
locx = locn_reg(reg);
a0_loc -> n_cltype = long_type;
g_2(X_EXG, locx, a0_loc);
loc1 -> n_reg1 = R_A0;
}
/* try to pick it up with EA/PEA in one fell swoop */
if (!g_symbol && is_areg(loc1 -> n_reg1) &&
( (!loc1->n_reg2 && i_cnst<=32767 && i_cnst >= -32768L) ||
(i_cnst <= 127 && i_cnst >= -128) )
) {
if (is_ea(loc1 -> n_mode)) {
g_2l1(X_MOVE, loc1, push_loc);
}
else {
loc1 -> n_mode = EA_MODE;
g_2(X_PEA, loc1, push_loc);
}
goto push_adj;
}
/* otherwise add it up */
if (locp == 0) {
locp = locn_reg(loc1 -> n_reg1); /* destination */
}
if (reg = loc1 -> n_reg2) {
locc = locn_reg(reg);
locc -> n_cltype = (loc1 -> n_scflag == X2_WORD)?
int_type : long_type;
g_2l1(X_ADD, locc, locp);
}
if (g_symbol || i_cnst) {
locc = locn_dupl(loc1);
locc -> n_reg1 = locc -> n_reg2 = 0;
if (is_areg(locp -> n_reg1) && !g_symbol &&
i_cnst <= 32767 && i_cnst >= -32768L) {
locc -> n_cltype = int_type;
}
else {
locc -> n_cltype = long_type;
}
g_2l1(X_ADD, locc, locp);
}
locp -> n_cltype = long_type;
g_2l1(X_MOVE, locp, push_loc);
push_adj:
if (locx) {
g_2(X_EXG, locx, a0_loc);
free_reg(locx -> n_reg1);
}
else {
free_reg(loc1 -> n_reg1);
}
TRACEP("x_sspush",
printf("frees %p: ", loc1);
pr_loc(loc1); printf("\n"));
free_reg(loc1 -> n_reg2);
loc1 = locn_xdupl(loc1);
loc1 -> n_reg2 = 0;
loc1 -> n_reg1 = R_A7;
loc1 -> n_mode = EAPSI_MODE;
loc1 -> n_cid = NULL;
loc1 -> n_const = 0;
RETURN_PTR("x_sspush", loc1);
}
/*
Make markings on the tree as follows:
process array into * and + with possible reassociation
the tree may be null
*/
void
gen_pp(struct node *p)
{
register struct node *q, *r, *r1;
register struct type_node *qt;
register int op, class;
SL_DISABLE();
if (p == NULL) {
return;
}
op = p -> n_type;
TRACEPB("gen_pp",
printf("node = %p->%p: %d %s: ",
p, p -> n_cltype, op, ps_tok(op));
pr_type(p -> n_cltype); printf("\n"));
switch(op = p -> n_type) {
case CALL_TOK:
TRACEP("gen_pp", printf("call\n"));
gen_pp(p -> n_arg1);
gen_pp(p -> n_arg2);
break;
case SEPARATOR_TOK:
TRACEP("gen_pp", printf("separator\n"));
gen_pp(p -> n_car);
gen_pp(p -> n_next); /* the separator */
break;
case ID_TOK:
/*
place the correct address mode into the id node
according to the storage class; the symbol table
must of course be correct by now!
if there is no ID, the item ought to be a c-language
numeric constant, and must be of VALUE_MODE with
no registers, and is left unaltered
*/
TRACEP("gen_pp", printf("id: "); pr_loc(p); printf("\n"));
if (p -> n_cid) {
class = p -> n_cid -> st_sclass;
if (p -> n_reg1 == 0) switch(class) {
case SUE_CLASS:
/* no register content */
break;
case FORMREG_CLASS:
TRACEP("gen_pp", printf("formal register class\n"));
p -> n_mode = VALUE_MODE;
if (p -> n_cltype &&
p -> n_cltype -> t_typtok != INT_TYPE &&
p -> n_cltype -> t_typtok != POINTER_TYPE) {
g_error(p, "register declared aggregate");
}
else {
if (p -> n_cid) {
p -> n_reg1 =
p -> n_cid -> st_misc & ST_REG;
p -> n_cid = 0;
}
break;
}
/* FALL_THROUGH */
case FORMAL_CLASS:
/*
all FORMALS including arrays are literally
effective addresses; a formal array is
literally an lvalue pointer to a[0], while
a local or global array is a virtual pointer
having no lvalue.
*/
p -> n_mode = EA_MODE;
p -> n_reg1 = R_A6;
break;
case REGISTER_CLASS:
TRACEP("gen_pp", printf("register class\n"));
p -> n_mode = VALUE_MODE;
if (p -> n_cltype &&
p -> n_cltype -> t_typtok != INT_TYPE &&
p -> n_cltype -> t_typtok != POINTER_TYPE
) {
g_error(p, "register declared aggregate");
}
else {
if (p -> n_cid) {
p -> n_reg1 =
p -> n_cid -> st_misc & ST_REG;
p -> n_cid = 0;
}
break;
}
/* FALL_THROUGH */
case AUTO_CLASS:
p -> n_reg1 = R_A6;
/* FALL_THROUGH */
default:
if (p -> n_cltype &&
p -> n_cltype -> t_typtok == ARRAY_TYPE) {
p -> n_mode = VALUE_MODE;
}
else {
p -> n_mode = EA_MODE;
}
}
if ( no_local && p -> n_cid &&
(is_rstack(class) || class == SUE_CLASS)
) {
TRACEP("gen_pp",printf("fix local/element\n"));
p -> n_const += p -> n_cid -> st_offset;
p -> n_cid = NULL;
}
}
else if (p -> n_reg1) {
TRACEP("gen_pp", printf("manifest register\n"));
}
TRACEP("gen_pp", printf("-> "); pr_loc(p); printf("\n"));
break;
case ARRAY_TOK:
gen_pp(p -> n_arg1);
gen_pp(p -> n_arg2);
TRACEP("gen_pp", printf("replace ARRAY with * and +\n"));
q = node_dupl( (byte *) p, sizeof(struct binop_node));
q -> n_type = PLUS_TOK;
/* EXISTING node gets CHANGED to * to avoid dangling ptrs */
p -> n_type = USTAR_TOK;
q -> n_cltype = qt = q -> n_arg1 -> n_cltype;
/*
CAUTION: there is a tacit assumption here that
a unop_node is the same as a binop_node, except
for the second arg!!!
NOTE: K & R are silent as to which of the following
two associations for this operation is correct.
They differ in the case that the i + j sum overflows,
and the differences depend on one's interpretation
of the automatic casting rules. The DRI compiler
does not follow a consistent interpretation.
*/
if (array_opt) {
/* association: p[i + j] = *(p + (i + j)) */
TRACEP("gen_pp", printf("array strict\n"));
p -> n_arg1 = q;
}
else {
/* association: p[i + j] = *(p + i + j) */
TRACEP("gen_pp", printf("array reassociation\n"));
r1 = NULL;
r = p -> n_arg2; /* the int tree */
while (r -> n_type == PLUS_TOK ||
(r -> n_type == MINUS_TOK &&
r -> n_arg1 -> n_cltype -> t_typtok == INT_TYPE)) {
r1 = r;
r = r1 -> n_arg1;
}
if (r1) {
q -> n_arg2 = r;
r1 -> n_arg1 = q;
/* propagate type */
for (r = p -> n_arg2; r != q; r = r -> n_arg1) {
r -> n_cltype = qt;
}
/* set argument of USTAR */
p -> n_arg1 = p -> n_arg2;
}
else {
p -> n_arg1 = q;
}
}
break;
case QUESTION_TOK:
TRACEP("gen_pp", printf("ternop\n"));
gen_pp(p -> n_arg1);
gen_pp(p -> n_arg2);
gen_pp(p -> n_arg3);
TRACEP("gen_pp",
printf("node = %p again: %d %s\n", p, op, ps_tok(op));
pr_expr(p);printf("\n"));
default:
if (is_unop(op)) {
TRACEP("gen_pp", printf("unop\n"));
gen_pp(p -> n_arg1);
TRACEP("gen_pp",
printf("node = %p again: %d %s\n", p, op, ps_tok(op));
pr_expr(p);printf("\n"));
}
else if (is_binop(op)) {
TRACEP("gen_pp", printf("binop\n"));
gen_pp(p -> n_arg1);
gen_pp(p -> n_arg2);
TRACEP("gen_pp",
printf("node = %p again: %d %s\n", p, op, ps_tok(op));
pr_expr(p); printf("\n");
pr_type(p -> n_cltype); printf("\n"));
}
else {
TRACEP("gen_pp", printf("unknown op"));
}
break;
} /* End switch. */
TICKX("gen_pp");
}