2883 lines
56 KiB
C
2883 lines
56 KiB
C
/****************************************************************
|
|
Copyright 1990, 1991 by AT&T Bell Laboratories and Bellcore.
|
|
|
|
Permission to use, copy, modify, and distribute this software
|
|
and its documentation for any purpose and without fee is hereby
|
|
granted, provided that the above copyright notice appear in all
|
|
copies and that both that the copyright notice and this
|
|
permission notice and warranty disclaimer appear in supporting
|
|
documentation, and that the names of AT&T Bell Laboratories or
|
|
Bellcore or any of their entities not be used in advertising or
|
|
publicity pertaining to distribution of the software without
|
|
specific, written prior permission.
|
|
|
|
AT&T and Bellcore disclaim all warranties with regard to this
|
|
software, including all implied warranties of merchantability
|
|
and fitness. In no event shall AT&T or Bellcore be liable for
|
|
any special, indirect or consequential damages or any damages
|
|
whatsoever resulting from loss of use, data or profits, whether
|
|
in an action of contract, negligence or other tortious action,
|
|
arising out of or in connection with the use or performance of
|
|
this software.
|
|
****************************************************************/
|
|
|
|
#include "defs.h"
|
|
#include "output.h"
|
|
#include "names.h"
|
|
|
|
LOCAL void conspower(), consbinop(), zdiv();
|
|
LOCAL expptr fold(), mkpower(), stfcall();
|
|
#ifndef stfcall_MAX
|
|
#define stfcall_MAX 144
|
|
#endif
|
|
|
|
typedef struct { double dreal, dimag; } dcomplex;
|
|
|
|
extern char dflttype[26];
|
|
|
|
/* little routines to create constant blocks */
|
|
|
|
Constp mkconst(t)
|
|
register int t;
|
|
{
|
|
register Constp p;
|
|
|
|
p = ALLOC(Constblock);
|
|
p->tag = TCONST;
|
|
p->vtype = t;
|
|
return(p);
|
|
}
|
|
|
|
|
|
/* mklogcon -- Make Logical Constant */
|
|
|
|
expptr mklogcon(l)
|
|
register int l;
|
|
{
|
|
register Constp p;
|
|
|
|
p = mkconst(TYLOGICAL);
|
|
p->Const.ci = l;
|
|
return( (expptr) p );
|
|
}
|
|
|
|
|
|
|
|
/* mkintcon -- Make Integer Constant */
|
|
|
|
expptr mkintcon(l)
|
|
ftnint l;
|
|
{
|
|
register Constp p;
|
|
|
|
p = mkconst(tyint);
|
|
p->Const.ci = l;
|
|
return( (expptr) p );
|
|
}
|
|
|
|
|
|
|
|
|
|
/* mkaddcon -- Make Address Constant, given integer value */
|
|
|
|
expptr mkaddcon(l)
|
|
register long l;
|
|
{
|
|
register Constp p;
|
|
|
|
p = mkconst(TYADDR);
|
|
p->Const.ci = l;
|
|
return( (expptr) p );
|
|
}
|
|
|
|
|
|
|
|
/* mkrealcon -- Make Real Constant. The type t is assumed
|
|
to be TYREAL or TYDREAL */
|
|
|
|
expptr mkrealcon(t, d)
|
|
register int t;
|
|
char *d;
|
|
{
|
|
register Constp p;
|
|
|
|
p = mkconst(t);
|
|
p->Const.cds[0] = cds(d,CNULL);
|
|
p->vstg = 1;
|
|
return( (expptr) p );
|
|
}
|
|
|
|
|
|
/* mkbitcon -- Make bit constant. Reads the input string, which is
|
|
assumed to correctly specify a number in base 2^shift (where shift
|
|
is the input parameter). shift may not exceed 4, i.e. only binary,
|
|
quad, octal and hex bases may be input. Constants may not exceed 32
|
|
bits, or whatever the size of (struct Constblock).ci may be. */
|
|
|
|
expptr mkbitcon(shift, leng, s)
|
|
int shift;
|
|
int leng;
|
|
char *s;
|
|
{
|
|
register Constp p;
|
|
register long x;
|
|
|
|
p = mkconst(TYLONG);
|
|
x = 0;
|
|
while(--leng >= 0)
|
|
if(*s != ' ')
|
|
x = (x << shift) | hextoi(*s++);
|
|
/* mwm wanted to change the type to short for short constants,
|
|
* but this is dangerous -- there is no syntax for long constants
|
|
* with small values.
|
|
*/
|
|
p->Const.ci = x;
|
|
return( (expptr) p );
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* mkstrcon -- Make string constant. Allocates storage and initializes
|
|
the memory for a copy of the input Fortran-string. */
|
|
|
|
expptr mkstrcon(l,v)
|
|
int l;
|
|
register char *v;
|
|
{
|
|
register Constp p;
|
|
register char *s;
|
|
|
|
p = mkconst(TYCHAR);
|
|
p->vleng = ICON(l);
|
|
p->Const.ccp = s = (char *) ckalloc(l+1);
|
|
p->Const.ccp1.blanks = 0;
|
|
while(--l >= 0)
|
|
*s++ = *v++;
|
|
*s = '\0';
|
|
return( (expptr) p );
|
|
}
|
|
|
|
|
|
|
|
/* mkcxcon -- Make complex contsant. A complex number is a pair of
|
|
values, each of which may be integer, real or double. */
|
|
|
|
expptr mkcxcon(realp,imagp)
|
|
register expptr realp, imagp;
|
|
{
|
|
int rtype, itype;
|
|
register Constp p;
|
|
expptr errnode();
|
|
|
|
rtype = realp->headblock.vtype;
|
|
itype = imagp->headblock.vtype;
|
|
|
|
if( ISCONST(realp) && ISNUMERIC(rtype) && ISCONST(imagp) && ISNUMERIC(itype) )
|
|
{
|
|
p = mkconst( (rtype==TYDREAL||itype==TYDREAL)
|
|
? TYDCOMPLEX : tycomplex);
|
|
if (realp->constblock.vstg || imagp->constblock.vstg) {
|
|
p->vstg = 1;
|
|
p->Const.cds[0] = ISINT(rtype)
|
|
? string_num("", realp->constblock.Const.ci)
|
|
: realp->constblock.vstg
|
|
? realp->constblock.Const.cds[0]
|
|
: dtos(realp->constblock.Const.cd[0]);
|
|
p->Const.cds[1] = ISINT(itype)
|
|
? string_num("", imagp->constblock.Const.ci)
|
|
: imagp->constblock.vstg
|
|
? imagp->constblock.Const.cds[0]
|
|
: dtos(imagp->constblock.Const.cd[0]);
|
|
}
|
|
else {
|
|
p->Const.cd[0] = ISINT(rtype)
|
|
? realp->constblock.Const.ci
|
|
: realp->constblock.Const.cd[0];
|
|
p->Const.cd[1] = ISINT(itype)
|
|
? imagp->constblock.Const.ci
|
|
: imagp->constblock.Const.cd[0];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
err("invalid complex constant");
|
|
p = (Constp)errnode();
|
|
}
|
|
|
|
frexpr(realp);
|
|
frexpr(imagp);
|
|
return( (expptr) p );
|
|
}
|
|
|
|
|
|
/* errnode -- Allocate a new error block */
|
|
|
|
expptr errnode()
|
|
{
|
|
struct Errorblock *p;
|
|
p = ALLOC(Errorblock);
|
|
p->tag = TERROR;
|
|
p->vtype = TYERROR;
|
|
return( (expptr) p );
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* mkconv -- Make type conversion. Cast expression p into type t.
|
|
Note that casting to a character copies only the first sizeof(char)
|
|
bytes. */
|
|
|
|
expptr mkconv(t, p)
|
|
register int t;
|
|
register expptr p;
|
|
{
|
|
register expptr q;
|
|
register int pt, charwarn = 1;
|
|
expptr opconv();
|
|
|
|
if (t >= 100) {
|
|
t -= 100;
|
|
charwarn = 0;
|
|
}
|
|
if(t==TYUNKNOWN || t==TYERROR)
|
|
badtype("mkconv", t);
|
|
pt = p->headblock.vtype;
|
|
|
|
/* Casting to the same type is a no-op */
|
|
|
|
if(t == pt)
|
|
return(p);
|
|
|
|
/* If we're casting a constant which is not in the literal table ... */
|
|
|
|
else if( ISCONST(p) && pt!=TYADDR && pt != TYCHAR)
|
|
{
|
|
if (ISINT(t) && ISINT(pt) || ISREAL(t) && ISREAL(pt)) {
|
|
/* avoid trouble with -i2 */
|
|
p->headblock.vtype = t;
|
|
return p;
|
|
}
|
|
q = (expptr) mkconst(t);
|
|
consconv(t, &q->constblock, &p->constblock );
|
|
frexpr(p);
|
|
}
|
|
else {
|
|
if (pt == TYCHAR && t != TYADDR && charwarn)
|
|
warn(
|
|
"ichar([first char. of] char. string) assumed for conversion to numeric");
|
|
q = opconv(p, t);
|
|
}
|
|
|
|
if(t == TYCHAR)
|
|
q->constblock.vleng = ICON(1);
|
|
return(q);
|
|
}
|
|
|
|
|
|
|
|
/* opconv -- Convert expression p to type t using the main
|
|
expression evaluator; returns an OPCONV expression, I think 14-jun-88 mwm */
|
|
|
|
expptr opconv(p, t)
|
|
expptr p;
|
|
int t;
|
|
{
|
|
register expptr q;
|
|
|
|
if (t == TYSUBR)
|
|
err("illegal use of subroutine name");
|
|
q = mkexpr(OPCONV, p, ENULL);
|
|
q->headblock.vtype = t;
|
|
return(q);
|
|
}
|
|
|
|
|
|
|
|
/* addrof -- Create an ADDR expression operation */
|
|
|
|
expptr addrof(p)
|
|
expptr p;
|
|
{
|
|
return( mkexpr(OPADDR, p, ENULL) );
|
|
}
|
|
|
|
|
|
|
|
/* cpexpr - Returns a new copy of input expression p */
|
|
|
|
tagptr cpexpr(p)
|
|
register tagptr p;
|
|
{
|
|
register tagptr e;
|
|
int tag;
|
|
register chainp ep, pp;
|
|
tagptr cpblock();
|
|
|
|
/* This table depends on the ordering of the T macros, e.g. TNAME */
|
|
|
|
static int blksize[ ] =
|
|
{
|
|
0,
|
|
sizeof(struct Nameblock),
|
|
sizeof(struct Constblock),
|
|
sizeof(struct Exprblock),
|
|
sizeof(struct Addrblock),
|
|
sizeof(struct Primblock),
|
|
sizeof(struct Listblock),
|
|
sizeof(struct Impldoblock),
|
|
sizeof(struct Errorblock)
|
|
};
|
|
|
|
if(p == NULL)
|
|
return(NULL);
|
|
|
|
/* TNAMEs are special, and don't get copied. Each name in the current
|
|
symbol table has a unique TNAME structure. */
|
|
|
|
if( (tag = p->tag) == TNAME)
|
|
return(p);
|
|
|
|
e = cpblock(blksize[p->tag], (char *)p);
|
|
|
|
switch(tag)
|
|
{
|
|
case TCONST:
|
|
if(e->constblock.vtype == TYCHAR)
|
|
{
|
|
e->constblock.Const.ccp =
|
|
copyn((int)e->constblock.vleng->constblock.Const.ci+1,
|
|
e->constblock.Const.ccp);
|
|
e->constblock.vleng =
|
|
(expptr) cpexpr(e->constblock.vleng);
|
|
}
|
|
case TERROR:
|
|
break;
|
|
|
|
case TEXPR:
|
|
e->exprblock.leftp = (expptr) cpexpr(p->exprblock.leftp);
|
|
e->exprblock.rightp = (expptr) cpexpr(p->exprblock.rightp);
|
|
break;
|
|
|
|
case TLIST:
|
|
if(pp = p->listblock.listp)
|
|
{
|
|
ep = e->listblock.listp =
|
|
mkchain((char *)cpexpr((tagptr)pp->datap), CHNULL);
|
|
for(pp = pp->nextp ; pp ; pp = pp->nextp)
|
|
ep = ep->nextp =
|
|
mkchain((char *)cpexpr((tagptr)pp->datap),
|
|
CHNULL);
|
|
}
|
|
break;
|
|
|
|
case TADDR:
|
|
e->addrblock.vleng = (expptr) cpexpr(e->addrblock.vleng);
|
|
e->addrblock.memoffset = (expptr)cpexpr(e->addrblock.memoffset);
|
|
e->addrblock.istemp = NO;
|
|
break;
|
|
|
|
case TPRIM:
|
|
e->primblock.argsp = (struct Listblock *)
|
|
cpexpr((expptr)e->primblock.argsp);
|
|
e->primblock.fcharp = (expptr) cpexpr(e->primblock.fcharp);
|
|
e->primblock.lcharp = (expptr) cpexpr(e->primblock.lcharp);
|
|
break;
|
|
|
|
default:
|
|
badtag("cpexpr", tag);
|
|
}
|
|
|
|
return(e);
|
|
}
|
|
|
|
/* frexpr -- Free expression -- frees up memory used by expression p */
|
|
|
|
frexpr(p)
|
|
register tagptr p;
|
|
{
|
|
register chainp q;
|
|
|
|
if(p == NULL)
|
|
return;
|
|
|
|
switch(p->tag)
|
|
{
|
|
case TCONST:
|
|
if( ISCHAR(p) )
|
|
{
|
|
free( (charptr) (p->constblock.Const.ccp) );
|
|
frexpr(p->constblock.vleng);
|
|
}
|
|
break;
|
|
|
|
case TADDR:
|
|
if (p->addrblock.vtype > TYERROR) /* i/o block */
|
|
break;
|
|
frexpr(p->addrblock.vleng);
|
|
frexpr(p->addrblock.memoffset);
|
|
break;
|
|
|
|
case TERROR:
|
|
break;
|
|
|
|
/* TNAME blocks don't get free'd - probably because they're pointed to in
|
|
the hash table. 14-Jun-88 -- mwm */
|
|
|
|
case TNAME:
|
|
return;
|
|
|
|
case TPRIM:
|
|
frexpr((expptr)p->primblock.argsp);
|
|
frexpr(p->primblock.fcharp);
|
|
frexpr(p->primblock.lcharp);
|
|
break;
|
|
|
|
case TEXPR:
|
|
frexpr(p->exprblock.leftp);
|
|
if(p->exprblock.rightp)
|
|
frexpr(p->exprblock.rightp);
|
|
break;
|
|
|
|
case TLIST:
|
|
for(q = p->listblock.listp ; q ; q = q->nextp)
|
|
frexpr((tagptr)q->datap);
|
|
frchain( &(p->listblock.listp) );
|
|
break;
|
|
|
|
default:
|
|
badtag("frexpr", p->tag);
|
|
}
|
|
|
|
free( (charptr) p );
|
|
}
|
|
|
|
void
|
|
wronginf(np)
|
|
Namep np;
|
|
{
|
|
int c, k;
|
|
warn1("fixing wrong type inferred for %.65s", np->fvarname);
|
|
np->vinftype = 0;
|
|
c = letter(np->fvarname[0]);
|
|
if ((np->vtype = impltype[c]) == TYCHAR
|
|
&& (k = implleng[c]))
|
|
np->vleng = ICON(k);
|
|
}
|
|
|
|
/* fix up types in expression; replace subtrees and convert
|
|
names to address blocks */
|
|
|
|
expptr fixtype(p)
|
|
register tagptr p;
|
|
{
|
|
|
|
if(p == 0)
|
|
return(0);
|
|
|
|
switch(p->tag)
|
|
{
|
|
case TCONST:
|
|
if(ONEOF(p->constblock.vtype,MSKINT|MSKLOGICAL|MSKADDR|
|
|
MSKREAL) )
|
|
return( (expptr) p);
|
|
|
|
return( (expptr) putconst((Constp)p) );
|
|
|
|
case TADDR:
|
|
p->addrblock.memoffset = fixtype(p->addrblock.memoffset);
|
|
return( (expptr) p);
|
|
|
|
case TERROR:
|
|
return( (expptr) p);
|
|
|
|
default:
|
|
badtag("fixtype", p->tag);
|
|
|
|
/* This case means that fixexpr can't call fixtype with any expr,
|
|
only a subexpr of its parameter. */
|
|
|
|
case TEXPR:
|
|
return( fixexpr((Exprp)p) );
|
|
|
|
case TLIST:
|
|
return( (expptr) p );
|
|
|
|
case TPRIM:
|
|
if(p->primblock.argsp && p->primblock.namep->vclass!=CLVAR)
|
|
{
|
|
if(p->primblock.namep->vtype == TYSUBR)
|
|
{
|
|
err("function invocation of subroutine");
|
|
return( errnode() );
|
|
}
|
|
else {
|
|
if (p->primblock.namep->vinftype)
|
|
wronginf(p->primblock.namep);
|
|
return( mkfunct(p) );
|
|
}
|
|
}
|
|
|
|
/* The lack of args makes p a function name, substring reference
|
|
or variable name. */
|
|
|
|
else return( mklhs((struct Primblock *) p) );
|
|
}
|
|
}
|
|
|
|
|
|
static expptr
|
|
cplenexpr(p)
|
|
expptr p;
|
|
{
|
|
expptr rv;
|
|
|
|
rv = cpexpr(p->headblock.vleng);
|
|
if (ISCONST(p) && p->constblock.vtype == TYCHAR)
|
|
rv->constblock.Const.ci += p->constblock.Const.ccp1.blanks;
|
|
return rv;
|
|
}
|
|
|
|
|
|
/* special case tree transformations and cleanups of expression trees.
|
|
Parameter p should have a TEXPR tag at its root, else an error is
|
|
returned */
|
|
|
|
expptr fixexpr(p)
|
|
register Exprp p;
|
|
{
|
|
expptr lp;
|
|
register expptr rp;
|
|
register expptr q;
|
|
int opcode, ltype, rtype, ptype, mtype;
|
|
|
|
if( ISERROR(p) )
|
|
return( (expptr) p );
|
|
else if(p->tag != TEXPR)
|
|
badtag("fixexpr", p->tag);
|
|
opcode = p->opcode;
|
|
|
|
/* First set the types of the left and right subexpressions */
|
|
|
|
lp = p->leftp;
|
|
if (!ISCONST(lp) || lp->constblock.vtype != TYCHAR)
|
|
lp = p->leftp = fixtype(lp);
|
|
ltype = lp->headblock.vtype;
|
|
|
|
if(opcode==OPASSIGN && lp->tag!=TADDR)
|
|
{
|
|
err("left side of assignment must be variable");
|
|
frexpr((expptr)p);
|
|
return( errnode() );
|
|
}
|
|
|
|
if(rp = p->rightp)
|
|
{
|
|
if (!ISCONST(rp) || rp->constblock.vtype != TYCHAR)
|
|
rp = p->rightp = fixtype(rp);
|
|
rtype = rp->headblock.vtype;
|
|
}
|
|
else
|
|
rtype = 0;
|
|
|
|
if(ltype==TYERROR || rtype==TYERROR)
|
|
{
|
|
frexpr((expptr)p);
|
|
return( errnode() );
|
|
}
|
|
|
|
/* Now work on the whole expression */
|
|
|
|
/* force folding if possible */
|
|
|
|
if( ISCONST(lp) && (rp==NULL || ISCONST(rp)) )
|
|
{
|
|
q = opcode == OPCONV && lp->constblock.vtype == p->vtype
|
|
? lp : mkexpr(opcode, lp, rp);
|
|
|
|
/* mkexpr is expected to reduce constant expressions */
|
|
|
|
if( ISCONST(q) ) {
|
|
p->leftp = p->rightp = 0;
|
|
frexpr(p);
|
|
return(q);
|
|
}
|
|
free( (charptr) q ); /* constants did not fold */
|
|
}
|
|
|
|
if( (ptype = cktype(opcode, ltype, rtype)) == TYERROR)
|
|
{
|
|
frexpr((expptr)p);
|
|
return( errnode() );
|
|
}
|
|
|
|
if (ltype == TYCHAR && ISCONST(lp))
|
|
p->leftp = lp = (expptr)putconst((Constp)lp);
|
|
if (rtype == TYCHAR && ISCONST(rp))
|
|
p->rightp = rp = (expptr)putconst((Constp)rp);
|
|
|
|
switch(opcode)
|
|
{
|
|
case OPCONCAT:
|
|
if(p->vleng == NULL)
|
|
p->vleng = mkexpr(OPPLUS, cplenexpr(lp),
|
|
cplenexpr(rp) );
|
|
break;
|
|
|
|
case OPASSIGN:
|
|
if (rtype == TYREAL)
|
|
break;
|
|
case OPPLUSEQ:
|
|
case OPSTAREQ:
|
|
if(ltype == rtype)
|
|
break;
|
|
if( ! ISCONST(rp) && ISREAL(ltype) && ISREAL(rtype) )
|
|
break;
|
|
if( ISCOMPLEX(ltype) || ISCOMPLEX(rtype) )
|
|
break;
|
|
if( ONEOF(ltype, MSKADDR|MSKINT) && ONEOF(rtype, MSKADDR|MSKINT)
|
|
&& typesize[ltype]>=typesize[rtype] )
|
|
break;
|
|
|
|
/* Cast the right hand side to match the type of the expression */
|
|
|
|
p->rightp = fixtype( mkconv(ptype, rp) );
|
|
break;
|
|
|
|
case OPSLASH:
|
|
if( ISCOMPLEX(rtype) )
|
|
{
|
|
p = (Exprp) call2(ptype,
|
|
|
|
/* Handle double precision complex variables */
|
|
|
|
ptype == TYCOMPLEX ? "c_div" : "z_div",
|
|
mkconv(ptype, lp), mkconv(ptype, rp) );
|
|
break;
|
|
}
|
|
case OPPLUS:
|
|
case OPMINUS:
|
|
case OPSTAR:
|
|
case OPMOD:
|
|
if(ptype==TYDREAL && ( (ltype==TYREAL && ! ISCONST(lp) ) ||
|
|
(rtype==TYREAL && ! ISCONST(rp) ) ))
|
|
break;
|
|
if( ISCOMPLEX(ptype) )
|
|
break;
|
|
|
|
/* Cast both sides of the expression to match the type of the whole
|
|
expression. */
|
|
|
|
if(ltype != ptype && (ltype < TYSHORT || ptype > TYDREAL))
|
|
p->leftp = fixtype(mkconv(ptype,lp));
|
|
if(rtype != ptype && (rtype < TYSHORT || ptype > TYDREAL))
|
|
p->rightp = fixtype(mkconv(ptype,rp));
|
|
break;
|
|
|
|
case OPPOWER:
|
|
return( mkpower((expptr)p) );
|
|
|
|
case OPLT:
|
|
case OPLE:
|
|
case OPGT:
|
|
case OPGE:
|
|
case OPEQ:
|
|
case OPNE:
|
|
if(ltype == rtype)
|
|
break;
|
|
mtype = cktype(OPMINUS, ltype, rtype);
|
|
if(mtype==TYDREAL && ( (ltype==TYREAL && ! ISCONST(lp)) ||
|
|
(rtype==TYREAL && ! ISCONST(rp)) ))
|
|
break;
|
|
if( ISCOMPLEX(mtype) )
|
|
break;
|
|
if(ltype != mtype)
|
|
p->leftp = fixtype(mkconv(mtype,lp));
|
|
if(rtype != mtype)
|
|
p->rightp = fixtype(mkconv(mtype,rp));
|
|
break;
|
|
|
|
case OPCONV:
|
|
ptype = cktype(OPCONV, p->vtype, ltype);
|
|
if(lp->tag==TEXPR && lp->exprblock.opcode==OPCOMMA)
|
|
{
|
|
lp->exprblock.rightp =
|
|
fixtype( mkconv(ptype, lp->exprblock.rightp) );
|
|
free( (charptr) p );
|
|
p = (Exprp) lp;
|
|
}
|
|
break;
|
|
|
|
case OPADDR:
|
|
if(lp->tag==TEXPR && lp->exprblock.opcode==OPADDR)
|
|
Fatal("addr of addr");
|
|
break;
|
|
|
|
case OPCOMMA:
|
|
case OPQUEST:
|
|
case OPCOLON:
|
|
break;
|
|
|
|
case OPMIN:
|
|
case OPMAX:
|
|
case OPMIN2:
|
|
case OPMAX2:
|
|
case OPDMIN:
|
|
case OPDMAX:
|
|
case OPABS:
|
|
case OPDABS:
|
|
ptype = p->vtype;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
p->vtype = ptype;
|
|
return((expptr) p);
|
|
}
|
|
|
|
|
|
/* fix an argument list, taking due care for special first level cases */
|
|
|
|
fixargs(doput, p0)
|
|
int doput; /* doput is true if constants need to be passed by reference */
|
|
struct Listblock *p0;
|
|
{
|
|
register chainp p;
|
|
register tagptr q, t;
|
|
register int qtag;
|
|
int nargs;
|
|
Addrp mkscalar();
|
|
|
|
nargs = 0;
|
|
if(p0)
|
|
for(p = p0->listp ; p ; p = p->nextp)
|
|
{
|
|
++nargs;
|
|
q = (tagptr)p->datap;
|
|
qtag = q->tag;
|
|
if(qtag == TCONST)
|
|
{
|
|
|
|
/* Call putconst() to store values in a constant table. Since even
|
|
constants must be passed by reference, this can optimize on the storage
|
|
required */
|
|
|
|
p->datap = doput ? (char *)putconst((Constp)q)
|
|
: (char *)q;
|
|
}
|
|
|
|
/* Take a function name and turn it into an Addr. This only happens when
|
|
nothing else has figured out the function beforehand */
|
|
|
|
else if(qtag==TPRIM && q->primblock.argsp==0 &&
|
|
q->primblock.namep->vclass==CLPROC &&
|
|
q->primblock.namep->vprocclass != PTHISPROC)
|
|
p->datap = (char *)mkaddr(q->primblock.namep);
|
|
|
|
else if(qtag==TPRIM && q->primblock.argsp==0 &&
|
|
q->primblock.namep->vdim!=NULL)
|
|
p->datap = (char *)mkscalar(q->primblock.namep);
|
|
|
|
else if(qtag==TPRIM && q->primblock.argsp==0 &&
|
|
q->primblock.namep->vdovar &&
|
|
(t = (tagptr) memversion(q->primblock.namep)) )
|
|
p->datap = (char *)fixtype(t);
|
|
else
|
|
p->datap = (char *)fixtype(q);
|
|
}
|
|
return(nargs);
|
|
}
|
|
|
|
|
|
|
|
/* mkscalar -- only called by fixargs above, and by some routines in
|
|
io.c */
|
|
|
|
Addrp mkscalar(np)
|
|
register Namep np;
|
|
{
|
|
register Addrp ap;
|
|
|
|
vardcl(np);
|
|
ap = mkaddr(np);
|
|
|
|
/* The prolog causes array arguments to point to the
|
|
* (0,...,0) element, unless subscript checking is on.
|
|
*/
|
|
if( !checksubs && np->vstg==STGARG)
|
|
{
|
|
register struct Dimblock *dp;
|
|
dp = np->vdim;
|
|
frexpr(ap->memoffset);
|
|
ap->memoffset = mkexpr(OPSTAR,
|
|
(np->vtype==TYCHAR ?
|
|
cpexpr(np->vleng) :
|
|
(tagptr)ICON(typesize[np->vtype]) ),
|
|
cpexpr(dp->baseoffset) );
|
|
}
|
|
return(ap);
|
|
}
|
|
|
|
|
|
static void
|
|
adjust_arginfo(np) /* adjust arginfo to omit the length arg for the
|
|
arg that we now know to be a character-valued
|
|
function */
|
|
register Namep np;
|
|
{
|
|
struct Entrypoint *ep;
|
|
register chainp args;
|
|
Argtypes *at;
|
|
|
|
for(ep = entries; ep; ep = ep->entnextp)
|
|
for(args = ep->arglist; args; args = args->nextp)
|
|
if (np == (Namep)args->datap
|
|
&& (at = ep->entryname->arginfo))
|
|
--at->nargs;
|
|
}
|
|
|
|
|
|
|
|
expptr mkfunct(p0)
|
|
expptr p0;
|
|
{
|
|
register struct Primblock *p = (struct Primblock *)p0;
|
|
struct Entrypoint *ep;
|
|
Addrp ap;
|
|
Extsym *extp;
|
|
register Namep np;
|
|
register expptr q;
|
|
expptr intrcall();
|
|
extern chainp new_procs;
|
|
int k, nargs;
|
|
int class;
|
|
|
|
if(p->tag != TPRIM)
|
|
return( errnode() );
|
|
|
|
np = p->namep;
|
|
class = np->vclass;
|
|
|
|
|
|
if(class == CLUNKNOWN)
|
|
{
|
|
np->vclass = class = CLPROC;
|
|
if(np->vstg == STGUNKNOWN)
|
|
{
|
|
if(np->vtype!=TYSUBR && (k = intrfunct(np->fvarname))
|
|
&& (zflag || !(*(struct Intrpacked *)&k).f4
|
|
|| dcomplex_seen))
|
|
{
|
|
np->vstg = STGINTR;
|
|
np->vardesc.varno = k;
|
|
np->vprocclass = PINTRINSIC;
|
|
}
|
|
else
|
|
{
|
|
extp = mkext(np->fvarname,
|
|
addunder(np->cvarname));
|
|
extp->extstg = STGEXT;
|
|
np->vstg = STGEXT;
|
|
np->vardesc.varno = extp - extsymtab;
|
|
np->vprocclass = PEXTERNAL;
|
|
}
|
|
}
|
|
else if(np->vstg==STGARG)
|
|
{
|
|
if(np->vtype == TYCHAR) {
|
|
adjust_arginfo(np);
|
|
if (np->vpassed) {
|
|
char wbuf[160], *who;
|
|
who = np->fvarname;
|
|
sprintf(wbuf, "%s%s%s\n\t%s%s%s",
|
|
"Character-valued dummy procedure ",
|
|
who, " not declared EXTERNAL.",
|
|
"Code may be wrong for previous function calls having ",
|
|
who, " as a parameter.");
|
|
warn(wbuf);
|
|
}
|
|
}
|
|
np->vprocclass = PEXTERNAL;
|
|
}
|
|
}
|
|
|
|
if(class != CLPROC)
|
|
fatali("invalid class code %d for function", class);
|
|
|
|
/* F77 doesn't allow subscripting of function calls */
|
|
|
|
if(p->fcharp || p->lcharp)
|
|
{
|
|
err("no substring of function call");
|
|
goto error;
|
|
}
|
|
impldcl(np);
|
|
np->vimpltype = 0; /* invoking as function ==> inferred type */
|
|
np->vcalled = 1;
|
|
nargs = fixargs( np->vprocclass!=PINTRINSIC, p->argsp);
|
|
|
|
switch(np->vprocclass)
|
|
{
|
|
case PEXTERNAL:
|
|
if(np->vtype == TYUNKNOWN)
|
|
{
|
|
dclerr("attempt to use untyped function", np);
|
|
np->vtype = dflttype[letter(np->fvarname[0])];
|
|
}
|
|
ap = mkaddr(np);
|
|
if (!extsymtab[np->vardesc.varno].extseen) {
|
|
new_procs = mkchain((char *)np, new_procs);
|
|
extsymtab[np->vardesc.varno].extseen = 1;
|
|
}
|
|
call:
|
|
q = mkexpr(OPCALL, (expptr)ap, (expptr)p->argsp);
|
|
q->exprblock.vtype = np->vtype;
|
|
if(np->vleng)
|
|
q->exprblock.vleng = (expptr) cpexpr(np->vleng);
|
|
break;
|
|
|
|
case PINTRINSIC:
|
|
q = intrcall(np, p->argsp, nargs);
|
|
break;
|
|
|
|
case PSTFUNCT:
|
|
q = stfcall(np, p->argsp);
|
|
break;
|
|
|
|
case PTHISPROC:
|
|
warn("recursive call");
|
|
|
|
/* entries is the list of multiple entry points */
|
|
|
|
for(ep = entries ; ep ; ep = ep->entnextp)
|
|
if(ep->enamep == np)
|
|
break;
|
|
if(ep == NULL)
|
|
Fatal("mkfunct: impossible recursion");
|
|
|
|
ap = builtin(np->vtype, ep->entryname->cextname, -2);
|
|
/* the negative last arg prevents adding */
|
|
/* this name to the list of used builtins */
|
|
goto call;
|
|
|
|
default:
|
|
fatali("mkfunct: impossible vprocclass %d",
|
|
(int) (np->vprocclass) );
|
|
}
|
|
free( (charptr) p );
|
|
return(q);
|
|
|
|
error:
|
|
frexpr((expptr)p);
|
|
return( errnode() );
|
|
}
|
|
|
|
|
|
|
|
LOCAL expptr stfcall(np, actlist)
|
|
Namep np;
|
|
struct Listblock *actlist;
|
|
{
|
|
register chainp actuals;
|
|
int nargs;
|
|
chainp oactp, formals;
|
|
int type;
|
|
expptr Ln, Lq, q, q1, rhs, ap;
|
|
Namep tnp;
|
|
register struct Rplblock *rp;
|
|
struct Rplblock *tlist;
|
|
static int inv_count;
|
|
|
|
if (++inv_count > stfcall_MAX)
|
|
Fatal("Loop invoking recursive statement function?");
|
|
if(actlist)
|
|
{
|
|
actuals = actlist->listp;
|
|
free( (charptr) actlist);
|
|
}
|
|
else
|
|
actuals = NULL;
|
|
oactp = actuals;
|
|
|
|
nargs = 0;
|
|
tlist = NULL;
|
|
if( (type = np->vtype) == TYUNKNOWN)
|
|
{
|
|
dclerr("attempt to use untyped statement function", np);
|
|
type = np->vtype = dflttype[letter(np->fvarname[0])];
|
|
}
|
|
formals = (chainp) np->varxptr.vstfdesc->datap;
|
|
rhs = (expptr) (np->varxptr.vstfdesc->nextp);
|
|
|
|
/* copy actual arguments into temporaries */
|
|
while(actuals!=NULL && formals!=NULL)
|
|
{
|
|
rp = ALLOC(Rplblock);
|
|
rp->rplnp = tnp = (Namep) formals->datap;
|
|
ap = fixtype((tagptr)actuals->datap);
|
|
if(tnp->vtype==ap->headblock.vtype && tnp->vtype!=TYCHAR
|
|
&& (ap->tag==TCONST || ap->tag==TADDR) )
|
|
{
|
|
|
|
/* If actuals are constants or variable names, no temporaries are required */
|
|
rp->rplvp = (expptr) ap;
|
|
rp->rplxp = NULL;
|
|
rp->rpltag = ap->tag;
|
|
}
|
|
else {
|
|
rp->rplvp = (expptr) mktmp(tnp->vtype, tnp->vleng);
|
|
rp -> rplxp = NULL;
|
|
putexpr ( mkexpr(OPASSIGN, cpexpr(rp->rplvp), ap));
|
|
if((rp->rpltag = rp->rplvp->tag) == TERROR)
|
|
err("disagreement of argument types in statement function call");
|
|
}
|
|
rp->rplnextp = tlist;
|
|
tlist = rp;
|
|
actuals = actuals->nextp;
|
|
formals = formals->nextp;
|
|
++nargs;
|
|
}
|
|
|
|
if(actuals!=NULL || formals!=NULL)
|
|
err("statement function definition and argument list differ");
|
|
|
|
/*
|
|
now push down names involved in formal argument list, then
|
|
evaluate rhs of statement function definition in this environment
|
|
*/
|
|
|
|
if(tlist) /* put tlist in front of the rpllist */
|
|
{
|
|
for(rp = tlist; rp->rplnextp; rp = rp->rplnextp)
|
|
;
|
|
rp->rplnextp = rpllist;
|
|
rpllist = tlist;
|
|
}
|
|
|
|
/* So when the expression finally gets evaled, that evaluator must read
|
|
from the globl rpllist 14-jun-88 mwm */
|
|
|
|
q = (expptr) mkconv(type, fixtype(cpexpr(rhs)) );
|
|
|
|
/* get length right of character-valued statement functions... */
|
|
if (type == TYCHAR
|
|
&& (Ln = np->vleng)
|
|
&& q->tag != TERROR
|
|
&& (Lq = q->exprblock.vleng)
|
|
&& (Lq->tag != TCONST
|
|
|| Ln->constblock.Const.ci != Lq->constblock.Const.ci)) {
|
|
q1 = (expptr) mktmp(type, Ln);
|
|
putexpr ( mkexpr(OPASSIGN, cpexpr(q1), q));
|
|
q = q1;
|
|
}
|
|
|
|
/* now generate the tree ( t1=a1, (t2=a2,... , f))))) */
|
|
while(--nargs >= 0)
|
|
{
|
|
if(rpllist->rplxp)
|
|
q = mkexpr(OPCOMMA, rpllist->rplxp, q);
|
|
rp = rpllist->rplnextp;
|
|
frexpr(rpllist->rplvp);
|
|
free((char *)rpllist);
|
|
rpllist = rp;
|
|
}
|
|
frchain( &oactp );
|
|
--inv_count;
|
|
return(q);
|
|
}
|
|
|
|
|
|
static int replaced;
|
|
|
|
/* mkplace -- Figure out the proper storage class for the input name and
|
|
return an addrp with the appropriate stuff */
|
|
|
|
Addrp mkplace(np)
|
|
register Namep np;
|
|
{
|
|
register Addrp s;
|
|
register struct Rplblock *rp;
|
|
int regn;
|
|
|
|
/* is name on the replace list? */
|
|
|
|
for(rp = rpllist ; rp ; rp = rp->rplnextp)
|
|
{
|
|
if(np == rp->rplnp)
|
|
{
|
|
replaced = 1;
|
|
if(rp->rpltag == TNAME)
|
|
{
|
|
np = (Namep) (rp->rplvp);
|
|
break;
|
|
}
|
|
else return( (Addrp) cpexpr(rp->rplvp) );
|
|
}
|
|
}
|
|
|
|
/* is variable a DO index in a register ? */
|
|
|
|
if(np->vdovar && ( (regn = inregister(np)) >= 0) )
|
|
if(np->vtype == TYERROR)
|
|
return((Addrp) errnode() );
|
|
else
|
|
{
|
|
s = ALLOC(Addrblock);
|
|
s->tag = TADDR;
|
|
s->vstg = STGREG;
|
|
s->vtype = TYIREG;
|
|
s->memno = regn;
|
|
s->memoffset = ICON(0);
|
|
s -> uname_tag = UNAM_NAME;
|
|
s -> user.name = np;
|
|
return(s);
|
|
}
|
|
|
|
vardcl(np);
|
|
return(mkaddr(np));
|
|
}
|
|
|
|
|
|
static int doing_vleng;
|
|
|
|
/* mklhs -- Compute the actual address of the given expression; account
|
|
for array subscripts, stack offset, and substring offsets. The f -> C
|
|
translator will need this only to worry about the subscript stuff */
|
|
|
|
expptr mklhs(p)
|
|
register struct Primblock *p;
|
|
{
|
|
expptr suboffset();
|
|
register Addrp s;
|
|
Namep np;
|
|
|
|
if(p->tag != TPRIM)
|
|
return( (expptr) p );
|
|
np = p->namep;
|
|
|
|
replaced = 0;
|
|
s = mkplace(np);
|
|
if(s->tag!=TADDR || s->vstg==STGREG)
|
|
{
|
|
free( (charptr) p );
|
|
return( (expptr) s );
|
|
}
|
|
|
|
/* compute the address modified by subscripts */
|
|
|
|
if (!replaced)
|
|
s->memoffset = mkexpr(OPPLUS, s->memoffset, suboffset(p) );
|
|
frexpr((expptr)p->argsp);
|
|
p->argsp = NULL;
|
|
|
|
/* now do substring part */
|
|
|
|
if(p->fcharp || p->lcharp)
|
|
{
|
|
if(np->vtype != TYCHAR)
|
|
errstr("substring of noncharacter %s", np->fvarname);
|
|
else {
|
|
if(p->lcharp == NULL)
|
|
p->lcharp = (expptr) cpexpr(s->vleng);
|
|
if(p->fcharp) {
|
|
doing_vleng = 1;
|
|
s->vleng = fixtype(mkexpr(OPMINUS,
|
|
p->lcharp,
|
|
mkexpr(OPMINUS, p->fcharp, ICON(1) )));
|
|
doing_vleng = 0;
|
|
}
|
|
else {
|
|
frexpr(s->vleng);
|
|
s->vleng = p->lcharp;
|
|
}
|
|
}
|
|
}
|
|
|
|
s->vleng = fixtype( s->vleng );
|
|
s->memoffset = fixtype( s->memoffset );
|
|
free( (charptr) p );
|
|
return( (expptr) s );
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* deregister -- remove a register allocation from the list; assumes that
|
|
names are deregistered in stack order (LIFO order - Last In First Out) */
|
|
|
|
deregister(np)
|
|
Namep np;
|
|
{
|
|
if(nregvar>0 && regnamep[nregvar-1]==np)
|
|
{
|
|
--nregvar;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
/* memversion -- moves a DO index REGISTER into a memory location; other
|
|
objects are passed through untouched */
|
|
|
|
Addrp memversion(np)
|
|
register Namep np;
|
|
{
|
|
register Addrp s;
|
|
|
|
if(np->vdovar==NO || (inregister(np)<0) )
|
|
return(NULL);
|
|
np->vdovar = NO;
|
|
s = mkplace(np);
|
|
np->vdovar = YES;
|
|
return(s);
|
|
}
|
|
|
|
|
|
|
|
/* inregister -- looks for the input name in the global list regnamep */
|
|
|
|
inregister(np)
|
|
register Namep np;
|
|
{
|
|
register int i;
|
|
|
|
for(i = 0 ; i < nregvar ; ++i)
|
|
if(regnamep[i] == np)
|
|
return( regnum[i] );
|
|
return(-1);
|
|
}
|
|
|
|
|
|
|
|
/* suboffset -- Compute the offset from the start of the array, given the
|
|
subscripts as arguments */
|
|
|
|
expptr suboffset(p)
|
|
register struct Primblock *p;
|
|
{
|
|
int n;
|
|
expptr si, size;
|
|
chainp cp;
|
|
expptr e, e1, offp, prod;
|
|
expptr subcheck();
|
|
struct Dimblock *dimp;
|
|
expptr sub[MAXDIM+1];
|
|
register Namep np;
|
|
|
|
np = p->namep;
|
|
offp = ICON(0);
|
|
n = 0;
|
|
if(p->argsp)
|
|
for(cp = p->argsp->listp ; cp ; cp = cp->nextp)
|
|
{
|
|
si = fixtype(cpexpr((tagptr)cp->datap));
|
|
if (!ISINT(si->headblock.vtype)) {
|
|
NOEXT("non-integer subscript");
|
|
si = mkconv(TYLONG, si);
|
|
}
|
|
sub[n++] = si;
|
|
if(n > maxdim)
|
|
{
|
|
erri("more than %d subscripts", maxdim);
|
|
break;
|
|
}
|
|
}
|
|
|
|
dimp = np->vdim;
|
|
if(n>0 && dimp==NULL)
|
|
errstr("subscripts on scalar variable %.68s", np->fvarname);
|
|
else if(dimp && dimp->ndim!=n)
|
|
errstr("wrong number of subscripts on %.68s", np->fvarname);
|
|
else if(n > 0)
|
|
{
|
|
prod = sub[--n];
|
|
while( --n >= 0)
|
|
prod = mkexpr(OPPLUS, sub[n],
|
|
mkexpr(OPSTAR, prod, cpexpr(dimp->dims[n].dimsize)) );
|
|
if(checksubs || np->vstg!=STGARG)
|
|
prod = mkexpr(OPMINUS, prod, cpexpr(dimp->baseoffset));
|
|
|
|
/* Add in the run-time bounds check */
|
|
|
|
if(checksubs)
|
|
prod = subcheck(np, prod);
|
|
size = np->vtype == TYCHAR ?
|
|
(expptr) cpexpr(np->vleng) : ICON(typesize[np->vtype]);
|
|
prod = mkexpr(OPSTAR, prod, size);
|
|
offp = mkexpr(OPPLUS, offp, prod);
|
|
}
|
|
|
|
/* Check for substring indicator */
|
|
|
|
if(p->fcharp && np->vtype==TYCHAR) {
|
|
e = p->fcharp;
|
|
e1 = mkexpr(OPMINUS, cpexpr(e), ICON(1));
|
|
if (!ISCONST(e) && (e->tag != TPRIM || e->primblock.argsp)) {
|
|
e = (expptr)mktmp(TYLONG, ENULL);
|
|
putout(putassign(cpexpr(e), e1));
|
|
p->fcharp = mkexpr(OPPLUS, cpexpr(e), ICON(1));
|
|
e1 = e;
|
|
}
|
|
offp = mkexpr(OPPLUS, offp, e1);
|
|
}
|
|
return(offp);
|
|
}
|
|
|
|
|
|
|
|
|
|
expptr subcheck(np, p)
|
|
Namep np;
|
|
register expptr p;
|
|
{
|
|
struct Dimblock *dimp;
|
|
expptr t, checkvar, checkcond, badcall;
|
|
|
|
dimp = np->vdim;
|
|
if(dimp->nelt == NULL)
|
|
return(p); /* don't check arrays with * bounds */
|
|
np->vlastdim = 0;
|
|
if( ISICON(p) )
|
|
{
|
|
|
|
/* check for negative (constant) offset */
|
|
|
|
if(p->constblock.Const.ci < 0)
|
|
goto badsub;
|
|
if( ISICON(dimp->nelt) )
|
|
|
|
/* see if constant offset exceeds the array declaration */
|
|
|
|
if(p->constblock.Const.ci < dimp->nelt->constblock.Const.ci)
|
|
return(p);
|
|
else
|
|
goto badsub;
|
|
}
|
|
|
|
/* We know that the subscript offset p or dimp -> nelt is not a constant.
|
|
Now find a register to use for run-time bounds checking */
|
|
|
|
if(p->tag==TADDR && p->addrblock.vstg==STGREG)
|
|
{
|
|
checkvar = (expptr) cpexpr(p);
|
|
t = p;
|
|
}
|
|
else {
|
|
checkvar = (expptr) mktmp(p->headblock.vtype, ENULL);
|
|
t = mkexpr(OPASSIGN, cpexpr(checkvar), p);
|
|
}
|
|
checkcond = mkexpr(OPLT, t, cpexpr(dimp->nelt) );
|
|
if( ! ISICON(p) )
|
|
checkcond = mkexpr(OPAND, checkcond,
|
|
mkexpr(OPLE, ICON(0), cpexpr(checkvar)) );
|
|
|
|
/* Construct the actual test */
|
|
|
|
badcall = call4(p->headblock.vtype, "s_rnge",
|
|
mkstrcon(strlen(np->fvarname), np->fvarname),
|
|
mkconv(TYLONG, cpexpr(checkvar)),
|
|
mkstrcon(strlen(procname), procname),
|
|
ICON(lineno) );
|
|
badcall->exprblock.opcode = OPCCALL;
|
|
p = mkexpr(OPQUEST, checkcond,
|
|
mkexpr(OPCOLON, checkvar, badcall));
|
|
|
|
return(p);
|
|
|
|
badsub:
|
|
frexpr(p);
|
|
errstr("subscript on variable %s out of range", np->fvarname);
|
|
return ( ICON(0) );
|
|
}
|
|
|
|
|
|
|
|
|
|
Addrp mkaddr(p)
|
|
register Namep p;
|
|
{
|
|
Extsym *extp;
|
|
register Addrp t;
|
|
Addrp intraddr();
|
|
int k;
|
|
|
|
switch( p->vstg)
|
|
{
|
|
case STGAUTO:
|
|
if(p->vclass == CLPROC && p->vprocclass == PTHISPROC)
|
|
return (Addrp) cpexpr((expptr)xretslot[p->vtype]);
|
|
goto other;
|
|
|
|
case STGUNKNOWN:
|
|
if(p->vclass != CLPROC)
|
|
break; /* Error */
|
|
extp = mkext(p->fvarname, addunder(p->cvarname));
|
|
extp->extstg = STGEXT;
|
|
p->vstg = STGEXT;
|
|
p->vardesc.varno = extp - extsymtab;
|
|
p->vprocclass = PEXTERNAL;
|
|
if ((extp->exproto || infertypes)
|
|
&& (p->vtype == TYUNKNOWN || p->vimpltype)
|
|
&& (k = extp->extype))
|
|
inferdcl(p, k);
|
|
|
|
|
|
case STGCOMMON:
|
|
case STGEXT:
|
|
case STGBSS:
|
|
case STGINIT:
|
|
case STGEQUIV:
|
|
case STGARG:
|
|
case STGLENG:
|
|
other:
|
|
t = ALLOC(Addrblock);
|
|
t->tag = TADDR;
|
|
|
|
t->vclass = p->vclass;
|
|
t->vtype = p->vtype;
|
|
t->vstg = p->vstg;
|
|
t->memno = p->vardesc.varno;
|
|
t->memoffset = ICON(p->voffset);
|
|
if (p->vdim)
|
|
t->isarray = 1;
|
|
if(p->vleng)
|
|
{
|
|
t->vleng = (expptr) cpexpr(p->vleng);
|
|
if( ISICON(t->vleng) )
|
|
t->varleng = t->vleng->constblock.Const.ci;
|
|
}
|
|
|
|
/* Keep the original name around for the C code generation */
|
|
|
|
t -> uname_tag = UNAM_NAME;
|
|
t -> user.name = p;
|
|
return(t);
|
|
|
|
case STGINTR:
|
|
|
|
return ( intraddr (p));
|
|
}
|
|
badstg("mkaddr", p->vstg);
|
|
/* NOT REACHED */ return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
/* mkarg -- create storage for a new parameter. This is called when a
|
|
function returns a string (for the return value, which is the first
|
|
parameter), or when a variable-length string is passed to a function. */
|
|
|
|
Addrp mkarg(type, argno)
|
|
int type, argno;
|
|
{
|
|
register Addrp p;
|
|
|
|
p = ALLOC(Addrblock);
|
|
p->tag = TADDR;
|
|
p->vtype = type;
|
|
p->vclass = CLVAR;
|
|
|
|
/* TYLENG is the type of the field holding the length of a character string */
|
|
|
|
p->vstg = (type==TYLENG ? STGLENG : STGARG);
|
|
p->memno = argno;
|
|
return(p);
|
|
}
|
|
|
|
|
|
|
|
|
|
/* mkprim -- Create a PRIM (primary/primitive) block consisting of a
|
|
Nameblock (or Paramblock), arguments (actual params or array
|
|
subscripts) and substring bounds. Requires that v have lots of
|
|
extra (uninitialized) storage, since it could be a paramblock or
|
|
nameblock */
|
|
|
|
expptr mkprim(v0, args, substr)
|
|
Namep v0;
|
|
struct Listblock *args;
|
|
chainp substr;
|
|
{
|
|
typedef union {
|
|
struct Paramblock paramblock;
|
|
struct Nameblock nameblock;
|
|
struct Headblock headblock;
|
|
} *Primu;
|
|
register Primu v = (Primu)v0;
|
|
register struct Primblock *p;
|
|
|
|
if(v->headblock.vclass == CLPARAM)
|
|
{
|
|
|
|
/* v is to be a Paramblock */
|
|
|
|
if(args || substr)
|
|
{
|
|
errstr("no qualifiers on parameter name %s",
|
|
v->paramblock.fvarname);
|
|
frexpr((expptr)args);
|
|
if(substr)
|
|
{
|
|
frexpr((tagptr)substr->datap);
|
|
frexpr((tagptr)substr->nextp->datap);
|
|
frchain(&substr);
|
|
}
|
|
frexpr((expptr)v);
|
|
return( errnode() );
|
|
}
|
|
return( (expptr) cpexpr(v->paramblock.paramval) );
|
|
}
|
|
|
|
p = ALLOC(Primblock);
|
|
p->tag = TPRIM;
|
|
p->vtype = v->nameblock.vtype;
|
|
|
|
/* v is to be a Nameblock */
|
|
|
|
p->namep = (Namep) v;
|
|
p->argsp = args;
|
|
if(substr)
|
|
{
|
|
p->fcharp = (expptr) substr->datap;
|
|
p->lcharp = (expptr) substr->nextp->datap;
|
|
frchain(&substr);
|
|
}
|
|
return( (expptr) p);
|
|
}
|
|
|
|
|
|
|
|
/* vardcl -- attempt to fill out the Name template for variable v.
|
|
This function is called on identifiers known to be variables or
|
|
recursive references to the same function */
|
|
|
|
vardcl(v)
|
|
register Namep v;
|
|
{
|
|
struct Dimblock *t;
|
|
expptr neltp;
|
|
extern int doing_stmtfcn;
|
|
|
|
if(v->vclass == CLUNKNOWN) {
|
|
v->vclass = CLVAR;
|
|
if (v->vinftype) {
|
|
v->vtype = TYUNKNOWN;
|
|
if (v->vdcldone) {
|
|
v->vdcldone = 0;
|
|
impldcl(v);
|
|
}
|
|
}
|
|
}
|
|
if(v->vdcldone)
|
|
return;
|
|
if(v->vclass == CLNAMELIST)
|
|
return;
|
|
|
|
if(v->vtype == TYUNKNOWN)
|
|
impldcl(v);
|
|
else if(v->vclass!=CLVAR && v->vprocclass!=PTHISPROC)
|
|
{
|
|
dclerr("used as variable", v);
|
|
return;
|
|
}
|
|
if(v->vstg==STGUNKNOWN) {
|
|
if (doing_stmtfcn) {
|
|
/* neither declare this variable if its only use */
|
|
/* is in defining a stmt function, nor complain */
|
|
/* that it is never used */
|
|
v->vimpldovar = 1;
|
|
return;
|
|
}
|
|
v->vstg = implstg[ letter(v->fvarname[0]) ];
|
|
v->vimplstg = 1;
|
|
}
|
|
|
|
/* Compute the actual storage location, i.e. offsets from base addresses,
|
|
possibly the stack pointer */
|
|
|
|
switch(v->vstg)
|
|
{
|
|
case STGBSS:
|
|
v->vardesc.varno = ++lastvarno;
|
|
break;
|
|
case STGAUTO:
|
|
if(v->vclass==CLPROC && v->vprocclass==PTHISPROC)
|
|
break;
|
|
if(t = v->vdim)
|
|
if( (neltp = t->nelt) && ISCONST(neltp) ) ;
|
|
else
|
|
dclerr("adjustable automatic array", v);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
v->vdcldone = YES;
|
|
}
|
|
|
|
|
|
|
|
/* Set the implicit type declaration of parameter p based on its first
|
|
letter */
|
|
|
|
impldcl(p)
|
|
register Namep p;
|
|
{
|
|
register int k;
|
|
int type;
|
|
ftnint leng;
|
|
|
|
if(p->vdcldone || (p->vclass==CLPROC && p->vprocclass==PINTRINSIC) )
|
|
return;
|
|
if(p->vtype == TYUNKNOWN)
|
|
{
|
|
k = letter(p->fvarname[0]);
|
|
type = impltype[ k ];
|
|
leng = implleng[ k ];
|
|
if(type == TYUNKNOWN)
|
|
{
|
|
if(p->vclass == CLPROC)
|
|
return;
|
|
dclerr("attempt to use undefined variable", p);
|
|
type = dflttype[k];
|
|
leng = 0;
|
|
}
|
|
settype(p, type, leng);
|
|
p->vimpltype = 1;
|
|
}
|
|
}
|
|
|
|
void
|
|
inferdcl(np,type)
|
|
Namep np;
|
|
int type;
|
|
{
|
|
int k = impltype[letter(np->fvarname[0])];
|
|
if (k != type) {
|
|
np->vinftype = 1;
|
|
np->vtype = type;
|
|
frexpr(np->vleng);
|
|
np->vleng = 0;
|
|
}
|
|
np->vimpltype = 0;
|
|
np->vinfproc = 1;
|
|
}
|
|
|
|
|
|
#define ICONEQ(z, c) (ISICON(z) && z->constblock.Const.ci==c)
|
|
#define COMMUTE { e = lp; lp = rp; rp = e; }
|
|
|
|
|
|
|
|
/* mkexpr -- Make expression, and simplify constant subcomponents (tree
|
|
order is not preserved). Assumes that lp is nonempty, and uses
|
|
fold() to simplify adjacent constants */
|
|
|
|
expptr mkexpr(opcode, lp, rp)
|
|
int opcode;
|
|
register expptr lp, rp;
|
|
{
|
|
register expptr e, e1;
|
|
int etype;
|
|
int ltype, rtype;
|
|
int ltag, rtag;
|
|
long L;
|
|
|
|
ltype = lp->headblock.vtype;
|
|
ltag = lp->tag;
|
|
if(rp && opcode!=OPCALL && opcode!=OPCCALL)
|
|
{
|
|
rtype = rp->headblock.vtype;
|
|
rtag = rp->tag;
|
|
}
|
|
else rtype = 0;
|
|
|
|
etype = cktype(opcode, ltype, rtype);
|
|
if(etype == TYERROR)
|
|
goto error;
|
|
|
|
switch(opcode)
|
|
{
|
|
/* check for multiplication by 0 and 1 and addition to 0 */
|
|
|
|
case OPSTAR:
|
|
if( ISCONST(lp) )
|
|
COMMUTE
|
|
|
|
if( ISICON(rp) )
|
|
{
|
|
if(rp->constblock.Const.ci == 0)
|
|
goto retright;
|
|
goto mulop;
|
|
}
|
|
break;
|
|
|
|
case OPSLASH:
|
|
case OPMOD:
|
|
if( ICONEQ(rp, 0) )
|
|
{
|
|
err("attempted division by zero");
|
|
rp = ICON(1);
|
|
break;
|
|
}
|
|
if(opcode == OPMOD)
|
|
break;
|
|
|
|
/* Handle multiplying or dividing by 1, -1 */
|
|
|
|
mulop:
|
|
if( ISICON(rp) )
|
|
{
|
|
if(rp->constblock.Const.ci == 1)
|
|
goto retleft;
|
|
|
|
if(rp->constblock.Const.ci == -1)
|
|
{
|
|
frexpr(rp);
|
|
return( mkexpr(OPNEG, lp, ENULL) );
|
|
}
|
|
}
|
|
|
|
/* Group all constants together. In particular,
|
|
|
|
(x * CONST1) * CONST2 ==> x * (CONST1 * CONST2)
|
|
(x * CONST1) / CONST2 ==> x * (CONST1 / CONST2)
|
|
*/
|
|
|
|
if (lp->tag != TEXPR || !lp->exprblock.rightp
|
|
|| !ISICON(lp->exprblock.rightp))
|
|
break;
|
|
|
|
if (lp->exprblock.opcode == OPLSHIFT) {
|
|
L = 1 << lp->exprblock.rightp->constblock.Const.ci;
|
|
if (opcode == OPSTAR || ISICON(rp) &&
|
|
!(L % rp->constblock.Const.ci)) {
|
|
lp->exprblock.opcode = OPSTAR;
|
|
lp->exprblock.rightp->constblock.Const.ci = L;
|
|
}
|
|
}
|
|
|
|
if (lp->exprblock.opcode == OPSTAR) {
|
|
if(opcode == OPSTAR)
|
|
e = mkexpr(OPSTAR, lp->exprblock.rightp, rp);
|
|
else if(ISICON(rp) &&
|
|
(lp->exprblock.rightp->constblock.Const.ci %
|
|
rp->constblock.Const.ci) == 0)
|
|
e = mkexpr(OPSLASH, lp->exprblock.rightp, rp);
|
|
else break;
|
|
|
|
e1 = lp->exprblock.leftp;
|
|
free( (charptr) lp );
|
|
return( mkexpr(OPSTAR, e1, e) );
|
|
}
|
|
break;
|
|
|
|
|
|
case OPPLUS:
|
|
if( ISCONST(lp) )
|
|
COMMUTE
|
|
goto addop;
|
|
|
|
case OPMINUS:
|
|
if( ICONEQ(lp, 0) )
|
|
{
|
|
frexpr(lp);
|
|
return( mkexpr(OPNEG, rp, ENULL) );
|
|
}
|
|
|
|
if( ISCONST(rp) && is_negatable((Constp)rp))
|
|
{
|
|
opcode = OPPLUS;
|
|
consnegop((Constp)rp);
|
|
}
|
|
|
|
/* Group constants in an addition expression (also subtraction, since the
|
|
subtracted value was negated above). In particular,
|
|
|
|
(x + CONST1) + CONST2 ==> x + (CONST1 + CONST2)
|
|
*/
|
|
|
|
addop:
|
|
if( ISICON(rp) )
|
|
{
|
|
if(rp->constblock.Const.ci == 0)
|
|
goto retleft;
|
|
if( ISPLUSOP(lp) && ISICON(lp->exprblock.rightp) )
|
|
{
|
|
e = mkexpr(OPPLUS, lp->exprblock.rightp, rp);
|
|
e1 = lp->exprblock.leftp;
|
|
free( (charptr) lp );
|
|
return( mkexpr(OPPLUS, e1, e) );
|
|
}
|
|
}
|
|
if (opcode == OPMINUS && (ISINT(etype) || doing_vleng)) {
|
|
/* check for (i [+const]) - (i [+const]) */
|
|
if (lp->tag == TPRIM)
|
|
e = lp;
|
|
else if (lp->tag == TEXPR && lp->exprblock.opcode == OPPLUS
|
|
&& lp->exprblock.rightp->tag == TCONST) {
|
|
e = lp->exprblock.leftp;
|
|
if (e->tag != TPRIM)
|
|
break;
|
|
}
|
|
else
|
|
break;
|
|
if (e->primblock.argsp)
|
|
break;
|
|
if (rp->tag == TPRIM)
|
|
e1 = rp;
|
|
else if (rp->tag == TEXPR && rp->exprblock.opcode == OPPLUS
|
|
&& rp->exprblock.rightp->tag == TCONST) {
|
|
e1 = rp->exprblock.leftp;
|
|
if (e1->tag != TPRIM)
|
|
break;
|
|
}
|
|
else
|
|
break;
|
|
if (e->primblock.namep != e1->primblock.namep
|
|
|| e1->primblock.argsp)
|
|
break;
|
|
L = e == lp ? 0 : lp->exprblock.rightp->constblock.Const.ci;
|
|
if (e1 != rp)
|
|
L -= rp->exprblock.rightp->constblock.Const.ci;
|
|
frexpr(lp);
|
|
frexpr(rp);
|
|
return ICON(L);
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
case OPPOWER:
|
|
break;
|
|
|
|
/* Eliminate outermost double negations */
|
|
|
|
case OPNEG:
|
|
case OPNEG1:
|
|
if(ltag==TEXPR && lp->exprblock.opcode==OPNEG)
|
|
{
|
|
e = lp->exprblock.leftp;
|
|
free( (charptr) lp );
|
|
return(e);
|
|
}
|
|
break;
|
|
|
|
/* Eliminate outermost double NOTs */
|
|
|
|
case OPNOT:
|
|
if(ltag==TEXPR && lp->exprblock.opcode==OPNOT)
|
|
{
|
|
e = lp->exprblock.leftp;
|
|
free( (charptr) lp );
|
|
return(e);
|
|
}
|
|
break;
|
|
|
|
case OPCALL:
|
|
case OPCCALL:
|
|
etype = ltype;
|
|
if(rp!=NULL && rp->listblock.listp==NULL)
|
|
{
|
|
free( (charptr) rp );
|
|
rp = NULL;
|
|
}
|
|
break;
|
|
|
|
case OPAND:
|
|
case OPOR:
|
|
if( ISCONST(lp) )
|
|
COMMUTE
|
|
|
|
if( ISCONST(rp) )
|
|
{
|
|
if(rp->constblock.Const.ci == 0)
|
|
if(opcode == OPOR)
|
|
goto retleft;
|
|
else
|
|
goto retright;
|
|
else if(opcode == OPOR)
|
|
goto retright;
|
|
else
|
|
goto retleft;
|
|
}
|
|
case OPEQV:
|
|
case OPNEQV:
|
|
|
|
case OPBITAND:
|
|
case OPBITOR:
|
|
case OPBITXOR:
|
|
case OPBITNOT:
|
|
case OPLSHIFT:
|
|
case OPRSHIFT:
|
|
|
|
case OPLT:
|
|
case OPGT:
|
|
case OPLE:
|
|
case OPGE:
|
|
case OPEQ:
|
|
case OPNE:
|
|
|
|
case OPCONCAT:
|
|
break;
|
|
case OPMIN:
|
|
case OPMAX:
|
|
case OPMIN2:
|
|
case OPMAX2:
|
|
case OPDMIN:
|
|
case OPDMAX:
|
|
|
|
case OPASSIGN:
|
|
case OPASSIGNI:
|
|
case OPPLUSEQ:
|
|
case OPSTAREQ:
|
|
case OPMINUSEQ:
|
|
case OPSLASHEQ:
|
|
case OPMODEQ:
|
|
case OPLSHIFTEQ:
|
|
case OPRSHIFTEQ:
|
|
case OPBITANDEQ:
|
|
case OPBITXOREQ:
|
|
case OPBITOREQ:
|
|
|
|
case OPCONV:
|
|
case OPADDR:
|
|
case OPWHATSIN:
|
|
|
|
case OPCOMMA:
|
|
case OPCOMMA_ARG:
|
|
case OPQUEST:
|
|
case OPCOLON:
|
|
case OPDOT:
|
|
case OPARROW:
|
|
case OPIDENTITY:
|
|
case OPCHARCAST:
|
|
case OPABS:
|
|
case OPDABS:
|
|
break;
|
|
|
|
default:
|
|
badop("mkexpr", opcode);
|
|
}
|
|
|
|
e = (expptr) ALLOC(Exprblock);
|
|
e->exprblock.tag = TEXPR;
|
|
e->exprblock.opcode = opcode;
|
|
e->exprblock.vtype = etype;
|
|
e->exprblock.leftp = lp;
|
|
e->exprblock.rightp = rp;
|
|
if(ltag==TCONST && (rp==0 || rtag==TCONST) )
|
|
e = fold(e);
|
|
return(e);
|
|
|
|
retleft:
|
|
frexpr(rp);
|
|
return(lp);
|
|
|
|
retright:
|
|
frexpr(lp);
|
|
return(rp);
|
|
|
|
error:
|
|
frexpr(lp);
|
|
if(rp && opcode!=OPCALL && opcode!=OPCCALL)
|
|
frexpr(rp);
|
|
return( errnode() );
|
|
}
|
|
|
|
#define ERR(s) { errs = s; goto error; }
|
|
|
|
/* cktype -- Check and return the type of the expression */
|
|
|
|
cktype(op, lt, rt)
|
|
register int op, lt, rt;
|
|
{
|
|
char *errs;
|
|
|
|
if(lt==TYERROR || rt==TYERROR)
|
|
goto error1;
|
|
|
|
if(lt==TYUNKNOWN)
|
|
return(TYUNKNOWN);
|
|
if(rt==TYUNKNOWN)
|
|
|
|
/* If not unary operation, return UNKNOWN */
|
|
|
|
if(!is_unary_op (op) && op != OPCALL && op != OPCCALL)
|
|
return(TYUNKNOWN);
|
|
|
|
switch(op)
|
|
{
|
|
case OPPLUS:
|
|
case OPMINUS:
|
|
case OPSTAR:
|
|
case OPSLASH:
|
|
case OPPOWER:
|
|
case OPMOD:
|
|
if( ISNUMERIC(lt) && ISNUMERIC(rt) )
|
|
return( maxtype(lt, rt) );
|
|
ERR("nonarithmetic operand of arithmetic operator")
|
|
|
|
case OPNEG:
|
|
case OPNEG1:
|
|
if( ISNUMERIC(lt) )
|
|
return(lt);
|
|
ERR("nonarithmetic operand of negation")
|
|
|
|
case OPNOT:
|
|
if(lt == TYLOGICAL)
|
|
return(TYLOGICAL);
|
|
ERR("NOT of nonlogical")
|
|
|
|
case OPAND:
|
|
case OPOR:
|
|
case OPEQV:
|
|
case OPNEQV:
|
|
if(lt==TYLOGICAL && rt==TYLOGICAL)
|
|
return(TYLOGICAL);
|
|
ERR("nonlogical operand of logical operator")
|
|
|
|
case OPLT:
|
|
case OPGT:
|
|
case OPLE:
|
|
case OPGE:
|
|
case OPEQ:
|
|
case OPNE:
|
|
if(lt==TYCHAR || rt==TYCHAR || lt==TYLOGICAL || rt==TYLOGICAL)
|
|
{
|
|
if(lt != rt)
|
|
ERR("illegal comparison")
|
|
}
|
|
|
|
else if( ISCOMPLEX(lt) || ISCOMPLEX(rt) )
|
|
{
|
|
if(op!=OPEQ && op!=OPNE)
|
|
ERR("order comparison of complex data")
|
|
}
|
|
|
|
else if( ! ISNUMERIC(lt) || ! ISNUMERIC(rt) )
|
|
ERR("comparison of nonarithmetic data")
|
|
return(TYLOGICAL);
|
|
|
|
case OPCONCAT:
|
|
if(lt==TYCHAR && rt==TYCHAR)
|
|
return(TYCHAR);
|
|
ERR("concatenation of nonchar data")
|
|
|
|
case OPCALL:
|
|
case OPCCALL:
|
|
case OPIDENTITY:
|
|
return(lt);
|
|
|
|
case OPADDR:
|
|
case OPCHARCAST:
|
|
return(TYADDR);
|
|
|
|
case OPCONV:
|
|
if(rt == 0)
|
|
return(0);
|
|
if(lt==TYCHAR && ISINT(rt) )
|
|
return(TYCHAR);
|
|
case OPASSIGN:
|
|
case OPASSIGNI:
|
|
case OPMINUSEQ:
|
|
case OPPLUSEQ:
|
|
case OPSTAREQ:
|
|
case OPSLASHEQ:
|
|
case OPMODEQ:
|
|
case OPLSHIFTEQ:
|
|
case OPRSHIFTEQ:
|
|
case OPBITANDEQ:
|
|
case OPBITXOREQ:
|
|
case OPBITOREQ:
|
|
if( ISINT(lt) && rt==TYCHAR)
|
|
return(lt);
|
|
if(lt==TYCHAR || rt==TYCHAR || lt==TYLOGICAL || rt==TYLOGICAL)
|
|
if((op!=OPASSIGN && op != OPPLUSEQ && op != OPMINUSEQ)
|
|
|| (lt!=rt))
|
|
{
|
|
ERR("impossible conversion")
|
|
}
|
|
return(lt);
|
|
|
|
case OPMIN:
|
|
case OPMAX:
|
|
case OPDMIN:
|
|
case OPDMAX:
|
|
case OPMIN2:
|
|
case OPMAX2:
|
|
case OPBITOR:
|
|
case OPBITAND:
|
|
case OPBITXOR:
|
|
case OPBITNOT:
|
|
case OPLSHIFT:
|
|
case OPRSHIFT:
|
|
case OPWHATSIN:
|
|
case OPABS:
|
|
case OPDABS:
|
|
return(lt);
|
|
|
|
case OPCOMMA:
|
|
case OPCOMMA_ARG:
|
|
case OPQUEST:
|
|
case OPCOLON: /* Only checks the rightmost type because
|
|
of C language definition (rightmost
|
|
comma-expr is the value of the expr) */
|
|
return(rt);
|
|
|
|
case OPDOT:
|
|
case OPARROW:
|
|
return (lt);
|
|
break;
|
|
default:
|
|
badop("cktype", op);
|
|
}
|
|
error:
|
|
err(errs);
|
|
error1:
|
|
return(TYERROR);
|
|
}
|
|
|
|
/* fold -- simplifies constant expressions; it assumes that e -> leftp and
|
|
e -> rightp are TCONST or NULL */
|
|
|
|
LOCAL expptr
|
|
fold(e)
|
|
register expptr e;
|
|
{
|
|
Constp p;
|
|
register expptr lp, rp;
|
|
int etype, mtype, ltype, rtype, opcode;
|
|
int i, bl, ll, lr;
|
|
char *q, *s;
|
|
struct Constblock lcon, rcon;
|
|
long L;
|
|
double d;
|
|
|
|
opcode = e->exprblock.opcode;
|
|
etype = e->exprblock.vtype;
|
|
|
|
lp = e->exprblock.leftp;
|
|
ltype = lp->headblock.vtype;
|
|
rp = e->exprblock.rightp;
|
|
|
|
if(rp == 0)
|
|
switch(opcode)
|
|
{
|
|
case OPNOT:
|
|
lp->constblock.Const.ci = ! lp->constblock.Const.ci;
|
|
retlp:
|
|
e->exprblock.leftp = 0;
|
|
frexpr(e);
|
|
return(lp);
|
|
|
|
case OPBITNOT:
|
|
lp->constblock.Const.ci = ~ lp->constblock.Const.ci;
|
|
goto retlp;
|
|
|
|
case OPNEG:
|
|
case OPNEG1:
|
|
consnegop((Constp)lp);
|
|
goto retlp;
|
|
|
|
case OPCONV:
|
|
case OPADDR:
|
|
return(e);
|
|
|
|
case OPABS:
|
|
case OPDABS:
|
|
switch(ltype) {
|
|
case TYSHORT:
|
|
case TYLONG:
|
|
if ((L = lp->constblock.Const.ci) < 0)
|
|
lp->constblock.Const.ci = -L;
|
|
goto retlp;
|
|
case TYREAL:
|
|
case TYDREAL:
|
|
if (lp->constblock.vstg) {
|
|
s = lp->constblock.Const.cds[0];
|
|
if (*s == '-')
|
|
lp->constblock.Const.cds[0] = s + 1;
|
|
goto retlp;
|
|
}
|
|
if ((d = lp->constblock.Const.cd[0]) < 0.)
|
|
lp->constblock.Const.cd[0] = -d;
|
|
case TYCOMPLEX:
|
|
case TYDCOMPLEX:
|
|
return e; /* lazy way out */
|
|
}
|
|
default:
|
|
badop("fold", opcode);
|
|
}
|
|
|
|
rtype = rp->headblock.vtype;
|
|
|
|
p = ALLOC(Constblock);
|
|
p->tag = TCONST;
|
|
p->vtype = etype;
|
|
p->vleng = e->exprblock.vleng;
|
|
|
|
switch(opcode)
|
|
{
|
|
case OPCOMMA:
|
|
case OPCOMMA_ARG:
|
|
case OPQUEST:
|
|
case OPCOLON:
|
|
return(e);
|
|
|
|
case OPAND:
|
|
p->Const.ci = lp->constblock.Const.ci &&
|
|
rp->constblock.Const.ci;
|
|
break;
|
|
|
|
case OPOR:
|
|
p->Const.ci = lp->constblock.Const.ci ||
|
|
rp->constblock.Const.ci;
|
|
break;
|
|
|
|
case OPEQV:
|
|
p->Const.ci = lp->constblock.Const.ci ==
|
|
rp->constblock.Const.ci;
|
|
break;
|
|
|
|
case OPNEQV:
|
|
p->Const.ci = lp->constblock.Const.ci !=
|
|
rp->constblock.Const.ci;
|
|
break;
|
|
|
|
case OPBITAND:
|
|
p->Const.ci = lp->constblock.Const.ci &
|
|
rp->constblock.Const.ci;
|
|
break;
|
|
|
|
case OPBITOR:
|
|
p->Const.ci = lp->constblock.Const.ci |
|
|
rp->constblock.Const.ci;
|
|
break;
|
|
|
|
case OPBITXOR:
|
|
p->Const.ci = lp->constblock.Const.ci ^
|
|
rp->constblock.Const.ci;
|
|
break;
|
|
|
|
case OPLSHIFT:
|
|
p->Const.ci = lp->constblock.Const.ci <<
|
|
rp->constblock.Const.ci;
|
|
break;
|
|
|
|
case OPRSHIFT:
|
|
p->Const.ci = lp->constblock.Const.ci >>
|
|
rp->constblock.Const.ci;
|
|
break;
|
|
|
|
case OPCONCAT:
|
|
ll = lp->constblock.vleng->constblock.Const.ci;
|
|
lr = rp->constblock.vleng->constblock.Const.ci;
|
|
bl = lp->constblock.Const.ccp1.blanks;
|
|
p->Const.ccp = q = (char *) ckalloc(ll+lr+bl);
|
|
p->Const.ccp1.blanks = rp->constblock.Const.ccp1.blanks;
|
|
p->vleng = ICON(ll+lr+bl);
|
|
s = lp->constblock.Const.ccp;
|
|
for(i = 0 ; i < ll ; ++i)
|
|
*q++ = *s++;
|
|
for(i = 0 ; i < bl ; i++)
|
|
*q++ = ' ';
|
|
s = rp->constblock.Const.ccp;
|
|
for(i = 0; i < lr; ++i)
|
|
*q++ = *s++;
|
|
break;
|
|
|
|
|
|
case OPPOWER:
|
|
if( ! ISINT(rtype) )
|
|
return(e);
|
|
conspower(p, (Constp)lp, rp->constblock.Const.ci);
|
|
break;
|
|
|
|
|
|
default:
|
|
if(ltype == TYCHAR)
|
|
{
|
|
lcon.Const.ci = cmpstr(lp->constblock.Const.ccp,
|
|
rp->constblock.Const.ccp,
|
|
lp->constblock.vleng->constblock.Const.ci,
|
|
rp->constblock.vleng->constblock.Const.ci);
|
|
rcon.Const.ci = 0;
|
|
mtype = tyint;
|
|
}
|
|
else {
|
|
mtype = maxtype(ltype, rtype);
|
|
consconv(mtype, &lcon, &lp->constblock);
|
|
consconv(mtype, &rcon, &rp->constblock);
|
|
}
|
|
consbinop(opcode, mtype, p, &lcon, &rcon);
|
|
break;
|
|
}
|
|
|
|
frexpr(e);
|
|
return( (expptr) p );
|
|
}
|
|
|
|
|
|
|
|
/* assign constant l = r , doing coercion */
|
|
|
|
consconv(lt, lc, rc)
|
|
int lt;
|
|
register Constp lc, rc;
|
|
{
|
|
int rt = rc->vtype;
|
|
register union Constant *lv = &lc->Const, *rv = &rc->Const;
|
|
|
|
lc->vtype = lt;
|
|
if (ONEOF(lt, MSKREAL|MSKCOMPLEX) && ONEOF(rt, MSKREAL|MSKCOMPLEX)) {
|
|
memcpy((char *)lv, (char *)rv, sizeof(union Constant));
|
|
lc->vstg = rc->vstg;
|
|
if (ISCOMPLEX(lt) && ISREAL(rt)) {
|
|
if (rc->vstg)
|
|
lv->cds[1] = cds("0",CNULL);
|
|
else
|
|
lv->cd[1] = 0.;
|
|
}
|
|
return;
|
|
}
|
|
lc->vstg = 0;
|
|
|
|
switch(lt)
|
|
{
|
|
|
|
/* Casting to character means just copying the first sizeof (character)
|
|
bytes into a new 1 character string. This is weird. */
|
|
|
|
case TYCHAR:
|
|
*(lv->ccp = (char *) ckalloc(1)) = rv->ci;
|
|
lv->ccp1.blanks = 0;
|
|
break;
|
|
|
|
case TYSHORT:
|
|
case TYLONG:
|
|
if(rt == TYCHAR)
|
|
lv->ci = rv->ccp[0];
|
|
else if( ISINT(rt) )
|
|
lv->ci = rv->ci;
|
|
else lv->ci = rc->vstg ? atof(rv->cds[0]) : rv->cd[0];
|
|
|
|
break;
|
|
|
|
case TYCOMPLEX:
|
|
case TYDCOMPLEX:
|
|
lv->cd[1] = 0.;
|
|
lv->cd[0] = rv->ci;
|
|
break;
|
|
|
|
case TYREAL:
|
|
case TYDREAL:
|
|
lv->cd[0] = rv->ci;
|
|
break;
|
|
|
|
case TYLOGICAL:
|
|
lv->ci = rv->ci;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* Negate constant value -- changes the input node's value */
|
|
|
|
consnegop(p)
|
|
register Constp p;
|
|
{
|
|
register char *s;
|
|
|
|
if (p->vstg) {
|
|
if (ISCOMPLEX(p->vtype)) {
|
|
s = p->Const.cds[1];
|
|
p->Const.cds[1] = *s == '-' ? s+1
|
|
: *s == '0' ? s : s-1;
|
|
}
|
|
s = p->Const.cds[0];
|
|
p->Const.cds[0] = *s == '-' ? s+1
|
|
: *s == '0' ? s : s-1;
|
|
return;
|
|
}
|
|
switch(p->vtype)
|
|
{
|
|
case TYSHORT:
|
|
case TYLONG:
|
|
p->Const.ci = - p->Const.ci;
|
|
break;
|
|
|
|
case TYCOMPLEX:
|
|
case TYDCOMPLEX:
|
|
p->Const.cd[1] = - p->Const.cd[1];
|
|
/* fall through and do the real parts */
|
|
case TYREAL:
|
|
case TYDREAL:
|
|
p->Const.cd[0] = - p->Const.cd[0];
|
|
break;
|
|
default:
|
|
badtype("consnegop", p->vtype);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* conspower -- Expand out an exponentiation */
|
|
|
|
LOCAL void
|
|
conspower(p, ap, n)
|
|
Constp p, ap;
|
|
ftnint n;
|
|
{
|
|
register union Constant *powp = &p->Const;
|
|
register int type;
|
|
struct Constblock x, x0;
|
|
|
|
if (n == 1) {
|
|
memcpy((char *)powp, (char *)&ap->Const, sizeof(ap->Const));
|
|
return;
|
|
}
|
|
|
|
switch(type = ap->vtype) /* pow = 1 */
|
|
{
|
|
case TYSHORT:
|
|
case TYLONG:
|
|
powp->ci = 1;
|
|
break;
|
|
case TYCOMPLEX:
|
|
case TYDCOMPLEX:
|
|
powp->cd[1] = 0;
|
|
case TYREAL:
|
|
case TYDREAL:
|
|
powp->cd[0] = 1;
|
|
break;
|
|
default:
|
|
badtype("conspower", type);
|
|
}
|
|
|
|
if(n == 0)
|
|
return;
|
|
switch(type) /* x0 = ap */
|
|
{
|
|
case TYSHORT:
|
|
case TYLONG:
|
|
x0.Const.ci = ap->Const.ci;
|
|
break;
|
|
case TYCOMPLEX:
|
|
case TYDCOMPLEX:
|
|
x0.Const.cd[1] =
|
|
ap->vstg ? atof(ap->Const.cds[1]) : ap->Const.cd[1];
|
|
case TYREAL:
|
|
case TYDREAL:
|
|
x0.Const.cd[0] =
|
|
ap->vstg ? atof(ap->Const.cds[0]) : ap->Const.cd[0];
|
|
break;
|
|
}
|
|
x0.vtype = type;
|
|
x0.vstg = 0;
|
|
if(n < 0)
|
|
{
|
|
if( ISINT(type) )
|
|
{
|
|
err("integer ** negative number");
|
|
return;
|
|
}
|
|
else if (!x0.Const.cd[0]
|
|
&& (!ISCOMPLEX(type) || !x0.Const.cd[1])) {
|
|
err("0.0 ** negative number");
|
|
return;
|
|
}
|
|
n = -n;
|
|
consbinop(OPSLASH, type, &x, p, &x0);
|
|
}
|
|
else
|
|
consbinop(OPSTAR, type, &x, p, &x0);
|
|
|
|
for( ; ; )
|
|
{
|
|
if(n & 01)
|
|
consbinop(OPSTAR, type, p, p, &x);
|
|
if(n >>= 1)
|
|
consbinop(OPSTAR, type, &x, &x, &x);
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* do constant operation cp = a op b -- assumes that ap and bp have data
|
|
matching the input type */
|
|
|
|
|
|
LOCAL void
|
|
consbinop(opcode, type, cpp, app, bpp)
|
|
int opcode, type;
|
|
Constp cpp, app, bpp;
|
|
{
|
|
register union Constant *ap = &app->Const,
|
|
*bp = &bpp->Const,
|
|
*cp = &cpp->Const;
|
|
int k;
|
|
double ad[2], bd[2], temp;
|
|
|
|
cpp->vstg = 0;
|
|
|
|
if (ONEOF(type, MSKREAL|MSKCOMPLEX)) {
|
|
ad[0] = app->vstg ? atof(ap->cds[0]) : ap->cd[0];
|
|
bd[0] = bpp->vstg ? atof(bp->cds[0]) : bp->cd[0];
|
|
if (ISCOMPLEX(type)) {
|
|
ad[1] = app->vstg ? atof(ap->cds[1]) : ap->cd[1];
|
|
bd[1] = bpp->vstg ? atof(bp->cds[1]) : bp->cd[1];
|
|
}
|
|
}
|
|
switch(opcode)
|
|
{
|
|
case OPPLUS:
|
|
switch(type)
|
|
{
|
|
case TYSHORT:
|
|
case TYLONG:
|
|
cp->ci = ap->ci + bp->ci;
|
|
break;
|
|
case TYCOMPLEX:
|
|
case TYDCOMPLEX:
|
|
cp->cd[1] = ad[1] + bd[1];
|
|
case TYREAL:
|
|
case TYDREAL:
|
|
cp->cd[0] = ad[0] + bd[0];
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case OPMINUS:
|
|
switch(type)
|
|
{
|
|
case TYSHORT:
|
|
case TYLONG:
|
|
cp->ci = ap->ci - bp->ci;
|
|
break;
|
|
case TYCOMPLEX:
|
|
case TYDCOMPLEX:
|
|
cp->cd[1] = ad[1] - bd[1];
|
|
case TYREAL:
|
|
case TYDREAL:
|
|
cp->cd[0] = ad[0] - bd[0];
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case OPSTAR:
|
|
switch(type)
|
|
{
|
|
case TYSHORT:
|
|
case TYLONG:
|
|
cp->ci = ap->ci * bp->ci;
|
|
break;
|
|
case TYREAL:
|
|
case TYDREAL:
|
|
cp->cd[0] = ad[0] * bd[0];
|
|
break;
|
|
case TYCOMPLEX:
|
|
case TYDCOMPLEX:
|
|
temp = ad[0] * bd[0] - ad[1] * bd[1] ;
|
|
cp->cd[1] = ad[0] * bd[1] + ad[1] * bd[0] ;
|
|
cp->cd[0] = temp;
|
|
break;
|
|
}
|
|
break;
|
|
case OPSLASH:
|
|
switch(type)
|
|
{
|
|
case TYSHORT:
|
|
case TYLONG:
|
|
cp->ci = ap->ci / bp->ci;
|
|
break;
|
|
case TYREAL:
|
|
case TYDREAL:
|
|
cp->cd[0] = ad[0] / bd[0];
|
|
break;
|
|
case TYCOMPLEX:
|
|
case TYDCOMPLEX:
|
|
zdiv((dcomplex*)cp, (dcomplex*)ad, (dcomplex*)bd);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case OPMOD:
|
|
if( ISINT(type) )
|
|
{
|
|
cp->ci = ap->ci % bp->ci;
|
|
break;
|
|
}
|
|
else
|
|
Fatal("inline mod of noninteger");
|
|
|
|
case OPMIN2:
|
|
case OPDMIN:
|
|
switch(type)
|
|
{
|
|
case TYSHORT:
|
|
case TYLONG:
|
|
cp->ci = ap->ci <= bp->ci ? ap->ci : bp->ci;
|
|
break;
|
|
case TYREAL:
|
|
case TYDREAL:
|
|
cp->cd[0] = ad[0] <= bd[0] ? ad[0] : bd[0];
|
|
break;
|
|
default:
|
|
Fatal("inline min of exected type");
|
|
}
|
|
break;
|
|
|
|
case OPMAX2:
|
|
case OPDMAX:
|
|
switch(type)
|
|
{
|
|
case TYSHORT:
|
|
case TYLONG:
|
|
cp->ci = ap->ci >= bp->ci ? ap->ci : bp->ci;
|
|
break;
|
|
case TYREAL:
|
|
case TYDREAL:
|
|
cp->cd[0] = ad[0] >= bd[0] ? ad[0] : bd[0];
|
|
break;
|
|
default:
|
|
Fatal("inline max of exected type");
|
|
}
|
|
break;
|
|
|
|
default: /* relational ops */
|
|
switch(type)
|
|
{
|
|
case TYSHORT:
|
|
case TYLONG:
|
|
if(ap->ci < bp->ci)
|
|
k = -1;
|
|
else if(ap->ci == bp->ci)
|
|
k = 0;
|
|
else k = 1;
|
|
break;
|
|
case TYREAL:
|
|
case TYDREAL:
|
|
if(ad[0] < bd[0])
|
|
k = -1;
|
|
else if(ad[0] == bd[0])
|
|
k = 0;
|
|
else k = 1;
|
|
break;
|
|
case TYCOMPLEX:
|
|
case TYDCOMPLEX:
|
|
if(ad[0] == bd[0] &&
|
|
ad[1] == bd[1] )
|
|
k = 0;
|
|
else k = 1;
|
|
break;
|
|
}
|
|
|
|
switch(opcode)
|
|
{
|
|
case OPEQ:
|
|
cp->ci = (k == 0);
|
|
break;
|
|
case OPNE:
|
|
cp->ci = (k != 0);
|
|
break;
|
|
case OPGT:
|
|
cp->ci = (k == 1);
|
|
break;
|
|
case OPLT:
|
|
cp->ci = (k == -1);
|
|
break;
|
|
case OPGE:
|
|
cp->ci = (k >= 0);
|
|
break;
|
|
case OPLE:
|
|
cp->ci = (k <= 0);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* conssgn - returns the sign of a Fortran constant */
|
|
|
|
conssgn(p)
|
|
register expptr p;
|
|
{
|
|
register char *s;
|
|
|
|
if( ! ISCONST(p) )
|
|
Fatal( "sgn(nonconstant)" );
|
|
|
|
switch(p->headblock.vtype)
|
|
{
|
|
case TYSHORT:
|
|
case TYLONG:
|
|
if(p->constblock.Const.ci > 0) return(1);
|
|
if(p->constblock.Const.ci < 0) return(-1);
|
|
return(0);
|
|
|
|
case TYREAL:
|
|
case TYDREAL:
|
|
if (p->constblock.vstg) {
|
|
s = p->constblock.Const.cds[0];
|
|
if (*s == '-')
|
|
return -1;
|
|
if (*s == '0')
|
|
return 0;
|
|
return 1;
|
|
}
|
|
if(p->constblock.Const.cd[0] > 0) return(1);
|
|
if(p->constblock.Const.cd[0] < 0) return(-1);
|
|
return(0);
|
|
|
|
|
|
/* The sign of a complex number is 0 iff the number is 0 + 0i, else it's 1 */
|
|
|
|
case TYCOMPLEX:
|
|
case TYDCOMPLEX:
|
|
if (p->constblock.vstg)
|
|
return *p->constblock.Const.cds[0] != '0'
|
|
&& *p->constblock.Const.cds[1] != '0';
|
|
return(p->constblock.Const.cd[0]!=0 || p->constblock.Const.cd[1]!=0);
|
|
|
|
default:
|
|
badtype( "conssgn", p->constblock.vtype);
|
|
}
|
|
/* NOT REACHED */ return 0;
|
|
}
|
|
|
|
char *powint[ ] = {
|
|
"pow_ii", "pow_ri", "pow_di", "pow_ci", "pow_zi" };
|
|
|
|
LOCAL expptr mkpower(p)
|
|
register expptr p;
|
|
{
|
|
register expptr q, lp, rp;
|
|
int ltype, rtype, mtype, tyi;
|
|
|
|
lp = p->exprblock.leftp;
|
|
rp = p->exprblock.rightp;
|
|
ltype = lp->headblock.vtype;
|
|
rtype = rp->headblock.vtype;
|
|
|
|
if(ISICON(rp))
|
|
{
|
|
if(rp->constblock.Const.ci == 0)
|
|
{
|
|
frexpr(p);
|
|
if( ISINT(ltype) )
|
|
return( ICON(1) );
|
|
else if (ISREAL (ltype))
|
|
return mkconv (ltype, ICON (1));
|
|
else
|
|
return( (expptr) putconst((Constp)
|
|
mkconv(ltype, ICON(1))) );
|
|
}
|
|
if(rp->constblock.Const.ci < 0)
|
|
{
|
|
if( ISINT(ltype) )
|
|
{
|
|
frexpr(p);
|
|
err("integer**negative");
|
|
return( errnode() );
|
|
}
|
|
rp->constblock.Const.ci = - rp->constblock.Const.ci;
|
|
p->exprblock.leftp = lp
|
|
= fixexpr((Exprp)mkexpr(OPSLASH, ICON(1), lp));
|
|
}
|
|
if(rp->constblock.Const.ci == 1)
|
|
{
|
|
frexpr(rp);
|
|
free( (charptr) p );
|
|
return(lp);
|
|
}
|
|
|
|
if( ONEOF(ltype, MSKINT|MSKREAL) && !doin_setbound) {
|
|
p->exprblock.vtype = ltype;
|
|
return(p);
|
|
}
|
|
}
|
|
if( ISINT(rtype) )
|
|
{
|
|
if(ltype==TYSHORT && rtype==TYSHORT && (!ISCONST(lp) || tyint==TYSHORT) )
|
|
q = call2(TYSHORT, "pow_hh", lp, rp);
|
|
else {
|
|
if(ltype == TYSHORT)
|
|
{
|
|
ltype = TYLONG;
|
|
lp = mkconv(TYLONG,lp);
|
|
}
|
|
rp = mkconv(TYLONG,rp);
|
|
if (ISCONST(rp)) {
|
|
tyi = tyint;
|
|
tyint = TYLONG;
|
|
rp = (expptr)putconst((Constp)rp);
|
|
tyint = tyi;
|
|
}
|
|
q = call2(ltype, powint[ltype-TYLONG], lp, rp);
|
|
}
|
|
}
|
|
else if( ISREAL( (mtype = maxtype(ltype,rtype)) )) {
|
|
extern int callk_kludge;
|
|
callk_kludge = TYDREAL;
|
|
q = call2(mtype, "pow_dd", mkconv(TYDREAL,lp), mkconv(TYDREAL,rp));
|
|
callk_kludge = 0;
|
|
}
|
|
else {
|
|
q = call2(TYDCOMPLEX, "pow_zz",
|
|
mkconv(TYDCOMPLEX,lp), mkconv(TYDCOMPLEX,rp));
|
|
if(mtype == TYCOMPLEX)
|
|
q = mkconv(TYCOMPLEX, q);
|
|
}
|
|
free( (charptr) p );
|
|
return(q);
|
|
}
|
|
|
|
|
|
/* Complex Division. Same code as in Runtime Library
|
|
*/
|
|
|
|
|
|
LOCAL void
|
|
zdiv(c, a, b)
|
|
register dcomplex *a, *b, *c;
|
|
{
|
|
double ratio, den;
|
|
double abr, abi;
|
|
|
|
if( (abr = b->dreal) < 0.)
|
|
abr = - abr;
|
|
if( (abi = b->dimag) < 0.)
|
|
abi = - abi;
|
|
if( abr <= abi )
|
|
{
|
|
if(abi == 0)
|
|
Fatal("complex division by zero");
|
|
ratio = b->dreal / b->dimag ;
|
|
den = b->dimag * (1 + ratio*ratio);
|
|
c->dreal = (a->dreal*ratio + a->dimag) / den;
|
|
c->dimag = (a->dimag*ratio - a->dreal) / den;
|
|
}
|
|
|
|
else
|
|
{
|
|
ratio = b->dimag / b->dreal ;
|
|
den = b->dreal * (1 + ratio*ratio);
|
|
c->dreal = (a->dreal + a->dimag*ratio) / den;
|
|
c->dimag = (a->dimag - a->dreal*ratio) / den;
|
|
}
|
|
}
|