1781 lines
		
	
	
	
		
			38 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1781 lines
		
	
	
	
		
			38 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.
 | |
| ****************************************************************/
 | |
| 
 | |
| /* INTERMEDIATE CODE GENERATION FOR S. C. JOHNSON C COMPILERS */
 | |
| /* NEW VERSION USING BINARY POLISH POSTFIX INTERMEDIATE */
 | |
| 
 | |
| #include "defs.h"
 | |
| #include "pccdefs.h"
 | |
| #include "output.h"		/* for nice_printf */
 | |
| #include "names.h"
 | |
| #include "p1defs.h"
 | |
| 
 | |
| Addrp realpart();
 | |
| LOCAL Addrp intdouble(), putcx1(), putcxeq (), putch1 ();
 | |
| LOCAL putct1 ();
 | |
| 
 | |
| expptr putcxop();
 | |
| LOCAL expptr putcall (), putmnmx (), putcheq(), putcat ();
 | |
| LOCAL expptr putaddr(), putchcmp (), putpower(), putop();
 | |
| LOCAL expptr putcxcmp ();
 | |
| expptr imagpart();
 | |
| ftnint lencat();
 | |
| 
 | |
| #define FOUR 4
 | |
| extern int ops2[];
 | |
| extern int types2[];
 | |
| extern int proc_argchanges, proc_protochanges;
 | |
| extern int krparens;
 | |
| 
 | |
| #define P2BUFFMAX 128
 | |
| 
 | |
| /* Puthead -- output the header information about subroutines, functions
 | |
|    and entry points */
 | |
| 
 | |
| puthead(s, class)
 | |
| char *s;
 | |
| int class;
 | |
| {
 | |
| 	if (headerdone == NO) {
 | |
| 		if (class == CLMAIN)
 | |
| 			s = "MAIN__";
 | |
| 		p1_head (class, s);
 | |
| 		headerdone = YES;
 | |
| 		}
 | |
| }
 | |
| 
 | |
| putif(p, else_if_p)
 | |
|  register expptr p;
 | |
|  int else_if_p;
 | |
| {
 | |
| 	register int k;
 | |
| 	int n;
 | |
| 	long where;
 | |
| 
 | |
| 	if (else_if_p) {
 | |
| 		p1put(P1_ELSEIFSTART);
 | |
| 		where = ftell(pass1_file);
 | |
| 		}
 | |
| 	if( ( k = (p = fixtype(p))->headblock.vtype) != TYLOGICAL)
 | |
| 	{
 | |
| 		if(k != TYERROR)
 | |
| 			err("non-logical expression in IF statement");
 | |
| 		}
 | |
| 	else {
 | |
| 		if (else_if_p) {
 | |
| 			if (ei_next >= ei_last)
 | |
| 				{
 | |
| 				k = ei_last - ei_first;
 | |
| 				n = k + 100;
 | |
| 				ei_next = mem(n,0);
 | |
| 				ei_last = ei_first + n;
 | |
| 				if (k)
 | |
| 					memcpy(ei_next, ei_first, k);
 | |
| 				ei_first =  ei_next;
 | |
| 				ei_next += k;
 | |
| 				ei_last = ei_first + n;
 | |
| 				}
 | |
| 			p = putx(p);
 | |
| 			if (*ei_next++ = ftell(pass1_file) > where) {
 | |
| 				p1_if(p);
 | |
| 				new_endif();
 | |
| 				}
 | |
| 			else
 | |
| 				p1_elif(p);
 | |
| 			}
 | |
| 		else {
 | |
| 			p = putx(p);
 | |
| 			p1_if(p);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 
 | |
| putexpr(p)
 | |
| expptr p;
 | |
| {
 | |
| 	putex1(p);
 | |
| }
 | |
| 
 | |
| 
 | |
| putout(p)
 | |
| expptr p;
 | |
| {
 | |
| 	p1_expr (p);
 | |
| 
 | |
| /* Used to make temporaries in holdtemps available here, but they */
 | |
| /* may be reused too soon (e.g. when multiple **'s are involved). */
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| putcmgo(index, nlab, labs)
 | |
| expptr index;
 | |
| int nlab;
 | |
| struct Labelblock *labs[];
 | |
| {
 | |
| 	if(! ISINT(index->headblock.vtype) )
 | |
| 	{
 | |
| 		execerr("computed goto index must be integer", CNULL);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	p1comp_goto (index, nlab, labs);
 | |
| }
 | |
| 
 | |
|  static expptr
 | |
| krput(p)
 | |
|  register expptr p;
 | |
| {
 | |
| 	register expptr e, e1;
 | |
| 	register unsigned op;
 | |
| 	int t = krparens == 2 ? TYDREAL : p->exprblock.vtype;
 | |
| 
 | |
| 	op = p->exprblock.opcode;
 | |
| 	e = p->exprblock.leftp;
 | |
| 	if (e->tag == TEXPR && e->exprblock.opcode == op) {
 | |
| 		e1 = (expptr)mktmp(t, ENULL);
 | |
| 		putout(putassign(cpexpr(e1), e));
 | |
| 		p->exprblock.leftp = e1;
 | |
| 		}
 | |
| 	else
 | |
| 		p->exprblock.leftp = putx(e);
 | |
| 
 | |
| 	e = p->exprblock.rightp;
 | |
| 	if (e->tag == TEXPR && e->exprblock.opcode == op) {
 | |
| 		e1 = (expptr)mktmp(t, ENULL);
 | |
| 		putout(putassign(cpexpr(e1), e));
 | |
| 		p->exprblock.rightp = e1;
 | |
| 		}
 | |
| 	else
 | |
| 		p->exprblock.rightp = putx(e);
 | |
| 	return p;
 | |
| 	}
 | |
| 
 | |
| expptr putx(p)
 | |
|  register expptr p;
 | |
| {
 | |
| 	int opc;
 | |
| 	int k;
 | |
| 
 | |
| 	if (p)
 | |
| 	  switch(p->tag)
 | |
| 	{
 | |
| 	case TERROR:
 | |
| 		break;
 | |
| 
 | |
| 	case TCONST:
 | |
| 		switch(p->constblock.vtype)
 | |
| 		{
 | |
| 		case TYLOGICAL:
 | |
| 		case TYLONG:
 | |
| 		case TYSHORT:
 | |
| 			break;
 | |
| 
 | |
| 		case TYADDR:
 | |
| 			break;
 | |
| 		case TYREAL:
 | |
| 		case TYDREAL:
 | |
| 
 | |
| /* Don't write it out to the p2 file, since you'd need to call putconst,
 | |
|    which is just what we need to avoid in the translator */
 | |
| 
 | |
| 			break;
 | |
| 		default:
 | |
| 			p = putx( (expptr)putconst((Constp)p) );
 | |
| 			break;
 | |
| 		}
 | |
| 		break;
 | |
| 
 | |
| 	case TEXPR:
 | |
| 		switch(opc = p->exprblock.opcode)
 | |
| 		{
 | |
| 		case OPCALL:
 | |
| 		case OPCCALL:
 | |
| 			if( ISCOMPLEX(p->exprblock.vtype) )
 | |
| 				p = putcxop(p);
 | |
| 			else	p = putcall(p, (Addrp *)NULL);
 | |
| 			break;
 | |
| 
 | |
| 		case OPMIN:
 | |
| 		case OPMAX:
 | |
| 			p = putmnmx(p);
 | |
| 			break;
 | |
| 
 | |
| 
 | |
| 		case OPASSIGN:
 | |
| 			if(ISCOMPLEX(p->exprblock.leftp->headblock.vtype)
 | |
| 			    || ISCOMPLEX(p->exprblock.rightp->headblock.vtype)) {
 | |
| 				(void) putcxeq(p);
 | |
| 				p = ENULL;
 | |
| 			} else if( ISCHAR(p) )
 | |
| 				p = putcheq(p);
 | |
| 			else
 | |
| 				goto putopp;
 | |
| 			break;
 | |
| 
 | |
| 		case OPEQ:
 | |
| 		case OPNE:
 | |
| 			if( ISCOMPLEX(p->exprblock.leftp->headblock.vtype) ||
 | |
| 			    ISCOMPLEX(p->exprblock.rightp->headblock.vtype) )
 | |
| 			{
 | |
| 				p = putcxcmp(p);
 | |
| 				break;
 | |
| 			}
 | |
| 		case OPLT:
 | |
| 		case OPLE:
 | |
| 		case OPGT:
 | |
| 		case OPGE:
 | |
| 			if(ISCHAR(p->exprblock.leftp))
 | |
| 			{
 | |
| 				p = putchcmp(p);
 | |
| 				break;
 | |
| 			}
 | |
| 			goto putopp;
 | |
| 
 | |
| 		case OPPOWER:
 | |
| 			p = putpower(p);
 | |
| 			break;
 | |
| 
 | |
| 		case OPSTAR:
 | |
| 			/*   m * (2**k) -> m<<k   */
 | |
| 			if(INT(p->exprblock.leftp->headblock.vtype) &&
 | |
| 			    ISICON(p->exprblock.rightp) &&
 | |
| 			    ( (k = log_2(p->exprblock.rightp->constblock.Const.ci))>0) )
 | |
| 			{
 | |
| 				p->exprblock.opcode = OPLSHIFT;
 | |
| 				frexpr(p->exprblock.rightp);
 | |
| 				p->exprblock.rightp = ICON(k);
 | |
| 				goto putopp;
 | |
| 			}
 | |
| 			if (krparens && ISREAL(p->exprblock.vtype))
 | |
| 				return krput(p);
 | |
| 
 | |
| 		case OPMOD:
 | |
| 			goto putopp;
 | |
| 		case OPPLUS:
 | |
| 			if (krparens && ISREAL(p->exprblock.vtype))
 | |
| 				return krput(p);
 | |
| 		case OPMINUS:
 | |
| 		case OPSLASH:
 | |
| 		case OPNEG:
 | |
| 		case OPNEG1:
 | |
| 		case OPABS:
 | |
| 		case OPDABS:
 | |
| 			if( ISCOMPLEX(p->exprblock.vtype) )
 | |
| 				p = putcxop(p);
 | |
| 			else	goto putopp;
 | |
| 			break;
 | |
| 
 | |
| 		case OPCONV:
 | |
| 			if( ISCOMPLEX(p->exprblock.vtype) )
 | |
| 				p = putcxop(p);
 | |
| 			else if( ISCOMPLEX(p->exprblock.leftp->headblock.vtype) )
 | |
| 			{
 | |
| 				p = putx( mkconv(p->exprblock.vtype,
 | |
| 				    (expptr)realpart(putcx1(p->exprblock.leftp))));
 | |
| 			}
 | |
| 			else	goto putopp;
 | |
| 			break;
 | |
| 
 | |
| 		case OPNOT:
 | |
| 		case OPOR:
 | |
| 		case OPAND:
 | |
| 		case OPEQV:
 | |
| 		case OPNEQV:
 | |
| 		case OPADDR:
 | |
| 		case OPPLUSEQ:
 | |
| 		case OPSTAREQ:
 | |
| 		case OPCOMMA:
 | |
| 		case OPQUEST:
 | |
| 		case OPCOLON:
 | |
| 		case OPBITOR:
 | |
| 		case OPBITAND:
 | |
| 		case OPBITXOR:
 | |
| 		case OPBITNOT:
 | |
| 		case OPLSHIFT:
 | |
| 		case OPRSHIFT:
 | |
| 		case OPASSIGNI:
 | |
| 		case OPIDENTITY:
 | |
| 		case OPCHARCAST:
 | |
| 		case OPMIN2:
 | |
| 		case OPMAX2:
 | |
| 		case OPDMIN:
 | |
| 		case OPDMAX:
 | |
| putopp:
 | |
| 			p = putop(p);
 | |
| 			break;
 | |
| 
 | |
| 		default:
 | |
| 			badop("putx", opc);
 | |
| 			p = errnode ();
 | |
| 		}
 | |
| 		break;
 | |
| 
 | |
| 	case TADDR:
 | |
| 		p = putaddr(p);
 | |
| 		break;
 | |
| 
 | |
| 	default:
 | |
| 		badtag("putx", p->tag);
 | |
| 		p = errnode ();
 | |
| 	}
 | |
| 
 | |
| 	return p;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| LOCAL expptr putop(p)
 | |
| expptr p;
 | |
| {
 | |
| 	expptr lp, tp;
 | |
| 	int pt, lt, lt1;
 | |
| 	int comma;
 | |
| 
 | |
| 	switch(p->exprblock.opcode)	/* check for special cases and rewrite */
 | |
| 	{
 | |
| 	case OPCONV:
 | |
| 		pt = p->exprblock.vtype;
 | |
| 		lp = p->exprblock.leftp;
 | |
| 		lt = lp->headblock.vtype;
 | |
| 
 | |
| /* Simplify nested type casts */
 | |
| 
 | |
| 		while(p->tag==TEXPR && p->exprblock.opcode==OPCONV &&
 | |
| 		    ( (ISREAL(pt)&&ONEOF(lt,MSKREAL|MSKCOMPLEX)) ||
 | |
| 		    (INT(pt)&&(ONEOF(lt,MSKINT|MSKADDR|MSKCHAR|M(TYSUBR)))) ))
 | |
| 		{
 | |
| 			if(pt==TYDREAL && lt==TYREAL)
 | |
| 			{
 | |
| 				if(lp->tag==TEXPR
 | |
| 				&& lp->exprblock.opcode == OPCONV) {
 | |
| 				    lt1 = lp->exprblock.leftp->headblock.vtype;
 | |
| 				    if (lt1 == TYDREAL) {
 | |
| 					lp->exprblock.leftp =
 | |
| 						putx(lp->exprblock.leftp);
 | |
| 					return p;
 | |
| 					}
 | |
| 				    if (lt1 == TYDCOMPLEX) {
 | |
| 					lp->exprblock.leftp = putx(
 | |
| 						(expptr)realpart(
 | |
| 						putcx1(lp->exprblock.leftp)));
 | |
| 					return p;
 | |
| 					}
 | |
| 				    }
 | |
| 				break;
 | |
| 			}
 | |
| 			else if (ISREAL(pt) && ISCOMPLEX(lt)) {
 | |
| 				p->exprblock.leftp = putx(mkconv(pt,
 | |
| 					(expptr)realpart(
 | |
| 						putcx1(p->exprblock.leftp))));
 | |
| 				break;
 | |
| 				}
 | |
| 			if(lt==TYCHAR && lp->tag==TEXPR &&
 | |
| 			    lp->exprblock.opcode==OPCALL)
 | |
| 			{
 | |
| 
 | |
| /* May want to make a comma expression here instead.  I had one, but took
 | |
|    it out for my convenience, not for the convenience of the end user */
 | |
| 
 | |
| 				putout (putcall (lp, (Addrp *) &(p ->
 | |
| 				    exprblock.leftp)));
 | |
| 				return putop (p);
 | |
| 			}
 | |
| 			if (lt == TYCHAR) {
 | |
| 				p->exprblock.leftp = putx(p->exprblock.leftp);
 | |
| 				return p;
 | |
| 				}
 | |
| 			frexpr(p->exprblock.vleng);
 | |
| 			free( (charptr) p );
 | |
| 			p = lp;
 | |
| 			if (p->tag != TEXPR)
 | |
| 				goto retputx;
 | |
| 			pt = lt;
 | |
| 			lp = p->exprblock.leftp;
 | |
| 			lt = lp->headblock.vtype;
 | |
| 		} /* while */
 | |
| 		if(p->tag==TEXPR && p->exprblock.opcode==OPCONV)
 | |
| 			break;
 | |
|  retputx:
 | |
| 		return putx(p);
 | |
| 
 | |
| 	case OPADDR:
 | |
| 		comma = NO;
 | |
| 		lp = p->exprblock.leftp;
 | |
| 		free( (charptr) p );
 | |
| 		if(lp->tag != TADDR)
 | |
| 		{
 | |
| 			tp = (expptr)
 | |
| 			    mktmp(lp->headblock.vtype,lp->headblock.vleng);
 | |
| 			p = putx( mkexpr(OPASSIGN, cpexpr(tp), lp) );
 | |
| 			lp = tp;
 | |
| 			comma = YES;
 | |
| 		}
 | |
| 		if(comma)
 | |
| 			p = mkexpr(OPCOMMA, p, putaddr(lp));
 | |
| 		else
 | |
| 			p = (expptr)putaddr(lp);
 | |
| 		return p;
 | |
| 
 | |
| 	case OPASSIGN:
 | |
| 	case OPASSIGNI:
 | |
| 	case OPLT:
 | |
| 	case OPLE:
 | |
| 	case OPGT:
 | |
| 	case OPGE:
 | |
| 	case OPEQ:
 | |
| 	case OPNE:
 | |
| 	    ;
 | |
| 	}
 | |
| 
 | |
| 	if( ops2[p->exprblock.opcode] <= 0)
 | |
| 		badop("putop", p->exprblock.opcode);
 | |
| 	p -> exprblock.leftp = putx (p -> exprblock.leftp);
 | |
| 	if (p -> exprblock.rightp)
 | |
| 	    p -> exprblock.rightp = putx (p -> exprblock.rightp);
 | |
| 	return p;
 | |
| }
 | |
| 
 | |
| LOCAL expptr putpower(p)
 | |
| expptr p;
 | |
| {
 | |
| 	expptr base;
 | |
| 	Addrp t1, t2;
 | |
| 	ftnint k;
 | |
| 	int type;
 | |
| 	char buf[80];			/* buffer for text of comment */
 | |
| 
 | |
| 	if(!ISICON(p->exprblock.rightp) ||
 | |
| 	    (k = p->exprblock.rightp->constblock.Const.ci)<2)
 | |
| 		Fatal("putpower: bad call");
 | |
| 	base = p->exprblock.leftp;
 | |
| 	type = base->headblock.vtype;
 | |
| 	t1 = mktmp(type, ENULL);
 | |
| 	t2 = NULL;
 | |
| 
 | |
| 	free ((charptr) p);
 | |
| 	p = putassign (cpexpr((expptr) t1), base);
 | |
| 
 | |
| 	sprintf (buf, "Computing %ld%s power", k,
 | |
| 		k == 2 ? "nd" : k == 3 ? "rd" : "th");
 | |
| 	p1_comment (buf);
 | |
| 
 | |
| 	for( ; (k&1)==0 && k>2 ; k>>=1 )
 | |
| 	{
 | |
| 		p = mkexpr (OPCOMMA, p, putsteq(t1, t1));
 | |
| 	}
 | |
| 
 | |
| 	if(k == 2) {
 | |
| 
 | |
| /* Write the power computation out immediately */
 | |
| 		putout (p);
 | |
| 		p = putx( mkexpr(OPSTAR, cpexpr((expptr)t1), cpexpr((expptr)t1)));
 | |
| 	} else {
 | |
| 		t2 = mktmp(type, ENULL);
 | |
| 		p = mkexpr (OPCOMMA, p, putassign(cpexpr((expptr)t2),
 | |
| 						cpexpr((expptr)t1)));
 | |
| 
 | |
| 		for(k>>=1 ; k>1 ; k>>=1)
 | |
| 		{
 | |
| 			p = mkexpr (OPCOMMA, p, putsteq(t1, t1));
 | |
| 			if(k & 1)
 | |
| 			{
 | |
| 				p = mkexpr (OPCOMMA, p, putsteq(t2, t1));
 | |
| 			}
 | |
| 		}
 | |
| /* Write the power computation out immediately */
 | |
| 		putout (p);
 | |
| 		p = putx( mkexpr(OPSTAR, cpexpr((expptr)t2),
 | |
| 		    mkexpr(OPSTAR, cpexpr((expptr)t1), cpexpr((expptr)t1))));
 | |
| 	}
 | |
| 	frexpr((expptr)t1);
 | |
| 	if(t2)
 | |
| 		frexpr((expptr)t2);
 | |
| 	return p;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| LOCAL Addrp intdouble(p)
 | |
| Addrp p;
 | |
| {
 | |
| 	register Addrp t;
 | |
| 
 | |
| 	t = mktmp(TYDREAL, ENULL);
 | |
| 	putout (putassign(cpexpr((expptr)t), (expptr)p));
 | |
| 	return(t);
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| /* Complex-type variable assignment */
 | |
| 
 | |
| LOCAL Addrp putcxeq(p)
 | |
| register expptr p;
 | |
| {
 | |
| 	register Addrp lp, rp;
 | |
| 	expptr code;
 | |
| 
 | |
| 	if(p->tag != TEXPR)
 | |
| 		badtag("putcxeq", p->tag);
 | |
| 
 | |
| 	lp = putcx1(p->exprblock.leftp);
 | |
| 	rp = putcx1(p->exprblock.rightp);
 | |
| 	code = putassign ( (expptr)realpart(lp), (expptr)realpart(rp));
 | |
| 
 | |
| 	if( ISCOMPLEX(p->exprblock.vtype) )
 | |
| 	{
 | |
| 		code = mkexpr (OPCOMMA, code, putassign
 | |
| 			(imagpart(lp), imagpart(rp)));
 | |
| 	}
 | |
| 	putout (code);
 | |
| 	frexpr((expptr)rp);
 | |
| 	free ((charptr) p);
 | |
| 	return lp;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /* putcxop -- used to write out embedded calls to complex functions, and
 | |
|    complex arguments to procedures */
 | |
| 
 | |
| expptr putcxop(p)
 | |
| expptr p;
 | |
| {
 | |
| 	return (expptr)putaddr((expptr)putcx1(p));
 | |
| }
 | |
| 
 | |
| #define PAIR(x,y) mkexpr (OPCOMMA, (x), (y))
 | |
| 
 | |
| LOCAL Addrp putcx1(p)
 | |
| register expptr p;
 | |
| {
 | |
| 	expptr q;
 | |
| 	Addrp lp, rp;
 | |
| 	register Addrp resp;
 | |
| 	int opcode;
 | |
| 	int ltype, rtype;
 | |
| 	long ts;
 | |
| 	expptr mkrealcon();
 | |
| 
 | |
| 	if(p == NULL)
 | |
| 		return(NULL);
 | |
| 
 | |
| 	switch(p->tag)
 | |
| 	{
 | |
| 	case TCONST:
 | |
| 		if( ISCOMPLEX(p->constblock.vtype) )
 | |
| 			p = (expptr) putconst((Constp)p);
 | |
| 		return( (Addrp) p );
 | |
| 
 | |
| 	case TADDR:
 | |
| 		resp = &p->addrblock;
 | |
| 		if (addressable(p))
 | |
| 			return (Addrp) p;
 | |
| 		if ((q = resp->memoffset) && resp->isarray
 | |
| 					  && resp->vtype != TYCHAR) {
 | |
| 			if (ONEOF(resp->vstg, M(STGCOMMON)|M(STGEQUIV))
 | |
| 					&& resp->uname_tag == UNAM_NAME)
 | |
| 				q = mkexpr(OPMINUS, q,
 | |
| 					mkintcon(resp->user.name->voffset));
 | |
| 			ts = typesize[resp->vtype]
 | |
| 					* (resp->Field ? 2 : 1);
 | |
| 			q = resp->memoffset = mkexpr(OPSLASH, q, ICON(ts));
 | |
| 			}
 | |
| 		else
 | |
| 			ts = 0;
 | |
| 		resp = mktmp(tyint, ENULL);
 | |
| 		putout(putassign(cpexpr((expptr)resp), q));
 | |
| 		p->addrblock.memoffset = (expptr)resp;
 | |
| 		if (ts) {
 | |
| 			resp = &p->addrblock;
 | |
| 			q = mkexpr(OPSTAR, resp->memoffset, ICON(ts));
 | |
| 			if (ONEOF(resp->vstg, M(STGCOMMON)|M(STGEQUIV))
 | |
| 				&& resp->uname_tag == UNAM_NAME)
 | |
| 				q = mkexpr(OPPLUS, q,
 | |
| 				    mkintcon(resp->user.name->voffset));
 | |
| 			resp->memoffset = q;
 | |
| 			}
 | |
| 		return (Addrp) p;
 | |
| 
 | |
| 	case TEXPR:
 | |
| 		if( ISCOMPLEX(p->exprblock.vtype) )
 | |
| 			break;
 | |
| 		resp = mktmp(TYDREAL, ENULL);
 | |
| 		putout (putassign( cpexpr((expptr)resp), p));
 | |
| 		return(resp);
 | |
| 
 | |
| 	default:
 | |
| 		badtag("putcx1", p->tag);
 | |
| 	}
 | |
| 
 | |
| 	opcode = p->exprblock.opcode;
 | |
| 	if(opcode==OPCALL || opcode==OPCCALL)
 | |
| 	{
 | |
| 		Addrp t;
 | |
| 		p = putcall(p, &t);
 | |
| 		putout(p);
 | |
| 		return t;
 | |
| 	}
 | |
| 	else if(opcode == OPASSIGN)
 | |
| 	{
 | |
| 		return putcxeq (p);
 | |
| 	}
 | |
| 
 | |
| /* BUG  (inefficient)  Generates too many temporary variables */
 | |
| 
 | |
| 	resp = mktmp(p->exprblock.vtype, ENULL);
 | |
| 	if(lp = putcx1(p->exprblock.leftp) )
 | |
| 		ltype = lp->vtype;
 | |
| 	if(rp = putcx1(p->exprblock.rightp) )
 | |
| 		rtype = rp->vtype;
 | |
| 
 | |
| 	switch(opcode)
 | |
| 	{
 | |
| 	case OPCOMMA:
 | |
| 		frexpr((expptr)resp);
 | |
| 		resp = rp;
 | |
| 		rp = NULL;
 | |
| 		break;
 | |
| 
 | |
| 	case OPNEG:
 | |
| 	case OPNEG1:
 | |
| 		putout (PAIR (
 | |
| 			putassign( (expptr)realpart(resp),
 | |
| 				mkexpr(OPNEG, (expptr)realpart(lp), ENULL)),
 | |
| 			putassign( imagpart(resp),
 | |
| 				mkexpr(OPNEG, imagpart(lp), ENULL))));
 | |
| 		break;
 | |
| 
 | |
| 	case OPPLUS:
 | |
| 	case OPMINUS: { expptr r;
 | |
| 		r = putassign( (expptr)realpart(resp),
 | |
| 		    mkexpr(opcode, (expptr)realpart(lp), (expptr)realpart(rp) ));
 | |
| 		if(rtype < TYCOMPLEX)
 | |
| 			q = putassign( imagpart(resp), imagpart(lp) );
 | |
| 		else if(ltype < TYCOMPLEX)
 | |
| 		{
 | |
| 			if(opcode == OPPLUS)
 | |
| 				q = putassign( imagpart(resp), imagpart(rp) );
 | |
| 			else
 | |
| 				q = putassign( imagpart(resp),
 | |
| 				    mkexpr(OPNEG, imagpart(rp), ENULL) );
 | |
| 		}
 | |
| 		else
 | |
| 			q = putassign( imagpart(resp),
 | |
| 			    mkexpr(opcode, imagpart(lp), imagpart(rp) ));
 | |
| 		r = PAIR (r, q);
 | |
| 		putout (r);
 | |
| 		break;
 | |
| 	    } /* case OPPLUS, OPMINUS: */
 | |
| 	case OPSTAR:
 | |
| 		if(ltype < TYCOMPLEX)
 | |
| 		{
 | |
| 			if( ISINT(ltype) )
 | |
| 				lp = intdouble(lp);
 | |
| 			putout (PAIR (
 | |
| 				putassign( (expptr)realpart(resp),
 | |
| 				    mkexpr(OPSTAR, cpexpr((expptr)lp),
 | |
| 					(expptr)realpart(rp))),
 | |
| 				putassign( imagpart(resp),
 | |
| 				    mkexpr(OPSTAR, cpexpr((expptr)lp), imagpart(rp)))));
 | |
| 		}
 | |
| 		else if(rtype < TYCOMPLEX)
 | |
| 		{
 | |
| 			if( ISINT(rtype) )
 | |
| 				rp = intdouble(rp);
 | |
| 			putout (PAIR (
 | |
| 				putassign( (expptr)realpart(resp),
 | |
| 				    mkexpr(OPSTAR, cpexpr((expptr)rp),
 | |
| 					(expptr)realpart(lp))),
 | |
| 				putassign( imagpart(resp),
 | |
| 				    mkexpr(OPSTAR, cpexpr((expptr)rp), imagpart(lp)))));
 | |
| 		}
 | |
| 		else	{
 | |
| 			putout (PAIR (
 | |
| 				putassign( (expptr)realpart(resp), mkexpr(OPMINUS,
 | |
| 				    mkexpr(OPSTAR, (expptr)realpart(lp),
 | |
| 					(expptr)realpart(rp)),
 | |
| 				    mkexpr(OPSTAR, imagpart(lp), imagpart(rp)))),
 | |
| 				putassign( imagpart(resp), mkexpr(OPPLUS,
 | |
| 				    mkexpr(OPSTAR, (expptr)realpart(lp), imagpart(rp)),
 | |
| 				    mkexpr(OPSTAR, imagpart(lp),
 | |
| 					(expptr)realpart(rp))))));
 | |
| 		}
 | |
| 		break;
 | |
| 
 | |
| 	case OPSLASH:
 | |
| 		/* fixexpr has already replaced all divisions
 | |
| 		 * by a complex by a function call
 | |
| 		 */
 | |
| 		if( ISINT(rtype) )
 | |
| 			rp = intdouble(rp);
 | |
| 		putout (PAIR (
 | |
| 			putassign( (expptr)realpart(resp),
 | |
| 			    mkexpr(OPSLASH, (expptr)realpart(lp), cpexpr((expptr)rp))),
 | |
| 			putassign( imagpart(resp),
 | |
| 			    mkexpr(OPSLASH, imagpart(lp), cpexpr((expptr)rp)))));
 | |
| 		break;
 | |
| 
 | |
| 	case OPCONV:
 | |
| 		if( ISCOMPLEX(lp->vtype) )
 | |
| 			q = imagpart(lp);
 | |
| 		else if(rp != NULL)
 | |
| 			q = (expptr) realpart(rp);
 | |
| 		else
 | |
| 			q = mkrealcon(TYDREAL, "0");
 | |
| 		putout (PAIR (
 | |
| 			putassign( (expptr)realpart(resp), (expptr)realpart(lp)),
 | |
| 			putassign( imagpart(resp), q)));
 | |
| 		break;
 | |
| 
 | |
| 	default:
 | |
| 		badop("putcx1", opcode);
 | |
| 	}
 | |
| 
 | |
| 	frexpr((expptr)lp);
 | |
| 	frexpr((expptr)rp);
 | |
| 	free( (charptr) p );
 | |
| 	return(resp);
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| /* Only .EQ. and .NE. may be performed on COMPLEX data, other relations
 | |
|    are not defined */
 | |
| 
 | |
| LOCAL expptr putcxcmp(p)
 | |
| register expptr p;
 | |
| {
 | |
| 	int opcode;
 | |
| 	register Addrp lp, rp;
 | |
| 	expptr q;
 | |
| 
 | |
| 	if(p->tag != TEXPR)
 | |
| 		badtag("putcxcmp", p->tag);
 | |
| 
 | |
| 	opcode = p->exprblock.opcode;
 | |
| 	lp = putcx1(p->exprblock.leftp);
 | |
| 	rp = putcx1(p->exprblock.rightp);
 | |
| 
 | |
| 	q = mkexpr( opcode==OPEQ ? OPAND : OPOR ,
 | |
| 	    mkexpr(opcode, (expptr)realpart(lp), (expptr)realpart(rp)),
 | |
| 	    mkexpr(opcode, imagpart(lp), imagpart(rp)) );
 | |
| 
 | |
| 	free( (charptr) lp);
 | |
| 	free( (charptr) rp);
 | |
| 	free( (charptr) p );
 | |
| 	return 	putx( fixexpr((Exprp)q) );
 | |
| }
 | |
| 
 | |
| /* putch1 -- Forces constants into the literal pool, among other things */
 | |
| 
 | |
| LOCAL Addrp putch1(p)
 | |
| register expptr p;
 | |
| {
 | |
| 	Addrp t;
 | |
| 	expptr e;
 | |
| 
 | |
| 	switch(p->tag)
 | |
| 	{
 | |
| 	case TCONST:
 | |
| 		return( putconst((Constp)p) );
 | |
| 
 | |
| 	case TADDR:
 | |
| 		return( (Addrp) p );
 | |
| 
 | |
| 	case TEXPR:
 | |
| 		switch(p->exprblock.opcode)
 | |
| 		{
 | |
| 			expptr q;
 | |
| 
 | |
| 		case OPCALL:
 | |
| 		case OPCCALL:
 | |
| 
 | |
| 			p = putcall(p, &t);
 | |
| 			putout (p);
 | |
| 			break;
 | |
| 
 | |
| 		case OPCONCAT:
 | |
| 			t = mktmp(TYCHAR, ICON(lencat(p)));
 | |
| 			q = (expptr) cpexpr(p->headblock.vleng);
 | |
| 			p = putcat( cpexpr((expptr)t), p );
 | |
| 			/* put the correct length on the block */
 | |
| 			frexpr(t->vleng);
 | |
| 			t->vleng = q;
 | |
| 			putout (p);
 | |
| 			break;
 | |
| 
 | |
| 		case OPCONV:
 | |
| 			if(!ISICON(p->exprblock.vleng)
 | |
| 			    || p->exprblock.vleng->constblock.Const.ci!=1
 | |
| 			    || ! INT(p->exprblock.leftp->headblock.vtype) )
 | |
| 				Fatal("putch1: bad character conversion");
 | |
| 			t = mktmp(TYCHAR, ICON(1));
 | |
| 			e = mkexpr(OPCONV, (expptr)t, ENULL);
 | |
| 			e->headblock.vtype = tyint;
 | |
| 			p = putop( mkexpr(OPASSIGN, cpexpr(e), p));
 | |
| 			putout (p);
 | |
| 			break;
 | |
| 		default:
 | |
| 			badop("putch1", p->exprblock.opcode);
 | |
| 		}
 | |
| 		return(t);
 | |
| 
 | |
| 	default:
 | |
| 		badtag("putch1", p->tag);
 | |
| 	}
 | |
| 	/* NOT REACHED */ return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* putchop -- Write out a character actual parameter; that is, this is
 | |
|    part of a procedure invocation */
 | |
| 
 | |
| Addrp putchop(p)
 | |
| expptr p;
 | |
| {
 | |
| 	p = putaddr((expptr)putch1(p));
 | |
| 	return (Addrp)p;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| LOCAL expptr putcheq(p)
 | |
| register expptr p;
 | |
| {
 | |
| 	expptr lp, rp;
 | |
| 
 | |
| 	if(p->tag != TEXPR)
 | |
| 		badtag("putcheq", p->tag);
 | |
| 
 | |
| 	lp = p->exprblock.leftp;
 | |
| 	rp = p->exprblock.rightp;
 | |
| 	frexpr(p->exprblock.vleng);
 | |
| 	free( (charptr) p );
 | |
| 
 | |
| /* If s = t // u, don't bother copying the result, write it directly into
 | |
|    this buffer */
 | |
| 
 | |
| 	if( rp->tag==TEXPR && rp->exprblock.opcode==OPCONCAT )
 | |
| 		p = putcat(lp, rp);
 | |
| 	else if( ISONE(lp->headblock.vleng) && ISONE(rp->headblock.vleng) ) {
 | |
| 		lp = mkexpr(OPCONV, lp, ENULL);
 | |
| 		rp = mkexpr(OPCONV, rp, ENULL);
 | |
| 		lp->headblock.vtype = rp->headblock.vtype = tyint;
 | |
| 		p = putop(mkexpr(OPASSIGN, lp, rp));
 | |
| 		}
 | |
| 	else
 | |
| 		p = putx( call2(TYSUBR, "s_copy", lp, rp) );
 | |
| 	return p;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| LOCAL expptr putchcmp(p)
 | |
| register expptr p;
 | |
| {
 | |
| 	expptr lp, rp;
 | |
| 
 | |
| 	if(p->tag != TEXPR)
 | |
| 		badtag("putchcmp", p->tag);
 | |
| 
 | |
| 	lp = p->exprblock.leftp;
 | |
| 	rp = p->exprblock.rightp;
 | |
| 
 | |
| 	if(ISONE(lp->headblock.vleng) && ISONE(rp->headblock.vleng) ) {
 | |
| 		lp = mkexpr(OPCONV, lp, ENULL);
 | |
| 		rp = mkexpr(OPCONV, rp, ENULL);
 | |
| 		lp->headblock.vtype = rp->headblock.vtype = tyint;
 | |
| 		}
 | |
| 	else {
 | |
| 		lp = call2(TYINT,"s_cmp", lp, rp);
 | |
| 		rp = ICON(0);
 | |
| 		}
 | |
| 	p->exprblock.leftp = lp;
 | |
| 	p->exprblock.rightp = rp;
 | |
| 	p = putop(p);
 | |
| 	return p;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| /* putcat -- Writes out a concatenation operation.  Two temporary arrays
 | |
|    are allocated,   putct1()   is called to initialize them, and then a
 | |
|    call to runtime library routine   s_cat()   is inserted.
 | |
| 
 | |
| 	This routine generates code which will perform an  (nconc lhs rhs)
 | |
|    at runtime.  The runtime funciton does not return a value, the routine
 | |
|    that calls this   putcat   must remember the name of   lhs.
 | |
| */
 | |
| 
 | |
| 
 | |
| LOCAL expptr putcat(lhs0, rhs)
 | |
|  expptr lhs0;
 | |
|  register expptr rhs;
 | |
| {
 | |
| 	register Addrp lhs = (Addrp)lhs0;
 | |
| 	int n, tyi;
 | |
| 	Addrp length_var, string_var;
 | |
| 	expptr p;
 | |
| 	static char Writing_concatenation[] = "Writing concatenation";
 | |
| 
 | |
| /* Create the temporary arrays */
 | |
| 
 | |
| 	n = ncat(rhs);
 | |
| 	length_var = mktmpn(n, tyioint, ENULL);
 | |
| 	string_var = mktmpn(n, TYADDR, ENULL);
 | |
| 	frtemp((Addrp)cpexpr((expptr)length_var));
 | |
| 	frtemp((Addrp)cpexpr((expptr)string_var));
 | |
| 
 | |
| /* Initialize the arrays */
 | |
| 
 | |
| 	n = 0;
 | |
| 	/* p1_comment scribbles on its argument, so we
 | |
| 	 * cannot safely pass a string literal here. */
 | |
| 	p1_comment(Writing_concatenation);
 | |
| 	putct1(rhs, length_var, string_var, &n);
 | |
| 
 | |
| /* Create the invocation */
 | |
| 
 | |
| 	tyi = tyint;
 | |
| 	tyint = tyioint;	/* for -I2 */
 | |
| 	p = putx (call4 (TYSUBR, "s_cat",
 | |
| 				(expptr)lhs,
 | |
| 				(expptr)string_var,
 | |
| 				(expptr)length_var,
 | |
| 				(expptr)putconst((Constp)ICON(n))));
 | |
| 	tyint = tyi;
 | |
| 
 | |
| 	return p;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| LOCAL putct1(q, length_var, string_var, ip)
 | |
| register expptr q;
 | |
| register Addrp length_var, string_var;
 | |
| int *ip;
 | |
| {
 | |
| 	int i;
 | |
| 	Addrp length_copy, string_copy;
 | |
| 	expptr e;
 | |
| 	extern int szleng;
 | |
| 
 | |
| 	if(q->tag==TEXPR && q->exprblock.opcode==OPCONCAT)
 | |
| 	{
 | |
| 		putct1(q->exprblock.leftp, length_var, string_var,
 | |
| 		    ip);
 | |
| 		putct1(q->exprblock.rightp, length_var, string_var,
 | |
| 		    ip);
 | |
| 		frexpr (q -> exprblock.vleng);
 | |
| 		free ((charptr) q);
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		i = (*ip)++;
 | |
| 		length_copy = (Addrp) cpexpr((expptr)length_var);
 | |
| 		length_copy->memoffset =
 | |
| 		    mkexpr(OPPLUS,length_copy->memoffset, ICON(i*szleng));
 | |
| 		string_copy = (Addrp) cpexpr((expptr)string_var);
 | |
| 		string_copy->memoffset =
 | |
| 		    mkexpr(OPPLUS, string_copy->memoffset,
 | |
| 			ICON(i*typesize[TYLONG]));
 | |
| 		e = cpexpr(q->headblock.vleng);
 | |
| 		putout (PAIR (putassign((expptr)length_copy, e),
 | |
| 			putassign((expptr)string_copy, addrof((expptr)putch1(q)))));
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /* putaddr -- seems to write out function invocation actual parameters */
 | |
| 
 | |
| LOCAL expptr putaddr(p0)
 | |
|  expptr p0;
 | |
| {
 | |
| 	register Addrp p;
 | |
| 
 | |
| 	if (!(p = (Addrp)p0))
 | |
| 		return ENULL;
 | |
| 
 | |
| 	if( p->tag==TERROR || (p->memoffset!=NULL && ISERROR(p->memoffset)) )
 | |
| 	{
 | |
| 		frexpr((expptr)p);
 | |
| 		return ENULL;
 | |
| 	}
 | |
| 	if (p->isarray && p->memoffset)
 | |
| 		p->memoffset = putx(p->memoffset);
 | |
| 	return (expptr) p;
 | |
| }
 | |
| 
 | |
|  LOCAL expptr
 | |
| addrfix(e)		/* fudge character string length if it's a TADDR */
 | |
|  expptr e;
 | |
| {
 | |
| 	return e->tag == TADDR ? mkexpr(OPIDENTITY, e, ENULL) : e;
 | |
| 	}
 | |
| 
 | |
|  LOCAL int
 | |
| typekludge(ccall, q, at, j)
 | |
|  int ccall;
 | |
|  register expptr q;
 | |
|  Atype *at;
 | |
|  int j;	/* alternate type */
 | |
| {
 | |
| 	register int i, k;
 | |
| 	extern int iocalladdr;
 | |
| 	register Namep np;
 | |
| 
 | |
| 	/* Return value classes:
 | |
| 	 *	< 100 ==> Fortran arg (pointer to type)
 | |
| 	 *	< 200 ==> C arg
 | |
| 	 *	< 300 ==> procedure arg
 | |
| 	 *	< 400 ==> external, no explicit type
 | |
| 	 *	< 500 ==> arg that may turn out to be
 | |
| 	 *		  either a variable or a procedure
 | |
| 	 */
 | |
| 
 | |
| 	k = q->headblock.vtype;
 | |
| 	if (ccall) {
 | |
| 		if (k == TYREAL)
 | |
| 			k = TYDREAL;	/* force double for library routines */
 | |
| 		return k + 100;
 | |
| 		}
 | |
| 	if (k == TYADDR)
 | |
| 		return iocalladdr;
 | |
| 	i = q->tag;
 | |
| 	if ((i == TEXPR && q->exprblock.opcode != OPCOMMA_ARG)
 | |
| 	||  (i == TADDR && q->addrblock.charleng)
 | |
| 	||   i == TCONST)
 | |
| 		k = TYFTNLEN + 100;
 | |
| 	else if (i == TADDR)
 | |
| 	    switch(q->addrblock.vclass) {
 | |
| 		case CLPROC:
 | |
| 			if (q->addrblock.uname_tag != UNAM_NAME)
 | |
| 				k += 200;
 | |
| 			else if ((np = q->addrblock.user.name)->vprocclass
 | |
| 					!= PTHISPROC) {
 | |
| 				if (k && !np->vimpltype)
 | |
| 					k += 200;
 | |
| 				else {
 | |
| 					if (j > 200 && infertypes && j < 300) {
 | |
| 						k = j;
 | |
| 						inferdcl(np, j-200);
 | |
| 						}
 | |
| 					else k = (np->vstg == STGEXT
 | |
| 						? extsymtab[np->vardesc.varno].extype
 | |
| 						: 0) + 200;
 | |
| 					at->cp = mkchain((char *)np, at->cp);
 | |
| 					}
 | |
| 				}
 | |
| 			else if (k == TYSUBR)
 | |
| 				k += 200;
 | |
| 			break;
 | |
| 
 | |
| 		case CLUNKNOWN:
 | |
| 			if (q->addrblock.vstg == STGARG
 | |
| 			 && q->addrblock.uname_tag == UNAM_NAME) {
 | |
| 				k += 400;
 | |
| 				at->cp = mkchain((char *)q->addrblock.user.name,
 | |
| 						at->cp);
 | |
| 				}
 | |
| 		}
 | |
| 	else if (i == TNAME && q->nameblock.vstg == STGARG) {
 | |
| 		np = &q->nameblock;
 | |
| 		switch(np->vclass) {
 | |
| 		    case CLPROC:
 | |
| 			if (!np->vimpltype)
 | |
| 				k += 200;
 | |
| 			else if (j <= 200 || !infertypes || j >= 300)
 | |
| 				k += 300;
 | |
| 			else {
 | |
| 				k = j;
 | |
| 				inferdcl(np, j-200);
 | |
| 				}
 | |
| 			goto add2chain;
 | |
| 
 | |
| 		    case CLUNKNOWN:
 | |
| 			/* argument may be a scalar variable or a function */
 | |
| 			if (np->vimpltype && j && infertypes
 | |
| 			&& j < 300) {
 | |
| 				inferdcl(np, j % 100);
 | |
| 				k = j;
 | |
| 				}
 | |
| 			else
 | |
| 				k += 400;
 | |
| 
 | |
| 			/* to handle procedure args only so far known to be
 | |
| 			 * external, save a pointer to the symbol table entry...
 | |
| 		 	 */
 | |
|  add2chain:
 | |
| 			at->cp = mkchain((char *)np, at->cp);
 | |
| 		    }
 | |
| 		}
 | |
| 	return k;
 | |
| 	}
 | |
| 
 | |
|  char *
 | |
| Argtype(k, buf)
 | |
|  int k;
 | |
|  char *buf;
 | |
| {
 | |
| 	if (k < 100) {
 | |
| 		sprintf(buf, "%s variable", ftn_types[k]);
 | |
| 		return buf;
 | |
| 		}
 | |
| 	if (k < 200) {
 | |
| 		k -= 100;
 | |
| 		return ftn_types[k];
 | |
| 		}
 | |
| 	if (k < 300) {
 | |
| 		k -= 200;
 | |
| 		if (k == TYSUBR)
 | |
| 			return ftn_types[TYSUBR];
 | |
| 		sprintf(buf, "%s function", ftn_types[k]);
 | |
| 		return buf;
 | |
| 		}
 | |
| 	if (k < 400)
 | |
| 		return "external argument";
 | |
| 	k -= 400;
 | |
| 	sprintf(buf, "%s argument", ftn_types[k]);
 | |
| 	return buf;
 | |
| 	}
 | |
| 
 | |
|  static void
 | |
| atype_squawk(at, msg)
 | |
|  Argtypes *at;
 | |
|  char *msg;
 | |
| {
 | |
| 	register Atype *a, *ae;
 | |
| 	warn(msg);
 | |
| 	for(a = at->atypes, ae = a + at->nargs; a < ae; a++)
 | |
| 		frchain(&a->cp);
 | |
| 	at->nargs = -1;
 | |
| 	if (at->changes & 2)
 | |
| 		proc_protochanges++;
 | |
| 	}
 | |
| 
 | |
|  static char inconsist[] = "inconsistent calling sequences for ";
 | |
| 
 | |
|  void
 | |
| bad_atypes(at, fname, i, j, k, here, prev)
 | |
|  Argtypes *at;
 | |
|  char *fname, *here, *prev;
 | |
|  int i, j, k;
 | |
| {
 | |
| 	char buf[208], buf1[32], buf2[32];
 | |
| 
 | |
| 	sprintf(buf, "%s%.90s,\n\targ %d: %s%s%s %s.",
 | |
| 		inconsist, fname, i, here, Argtype(k, buf1),
 | |
| 		prev, Argtype(j, buf2));
 | |
| 	atype_squawk(at, buf);
 | |
| 	}
 | |
| 
 | |
|  int
 | |
| type_fixup(at,a,k)
 | |
|  Argtypes *at;
 | |
|  Atype *a;
 | |
|  int k;
 | |
| {
 | |
| 	register struct Entrypoint *ep;
 | |
| 	if (!infertypes)
 | |
| 		return 0;
 | |
| 	for(ep = entries; ep; ep = ep->entnextp)
 | |
| 		if (at == ep->entryname->arginfo) {
 | |
| 			a->type = k % 100;
 | |
| 			return proc_argchanges = 1;
 | |
| 			}
 | |
| 	return 0;
 | |
| 	}
 | |
| 
 | |
| 
 | |
|  void
 | |
| save_argtypes(arglist, at0, at1, ccall, fname, stg, nchargs, type, zap)
 | |
|  chainp arglist;
 | |
|  Argtypes **at0, **at1;
 | |
|  int ccall, stg, nchargs, type, zap;
 | |
|  char *fname;
 | |
| {
 | |
| 	Argtypes *at;
 | |
| 	chainp cp;
 | |
| 	int i, i0, j, k, nargs, *t, *te;
 | |
| 	Atype *atypes;
 | |
| 	expptr q;
 | |
| 	char buf[208];
 | |
| 	static int initargs[4] = {TYCOMPLEX, TYDCOMPLEX, TYCHAR, TYFTNLEN+100};
 | |
| 	static int *init_ap[TYSUBR+1] = {0,0,0,0,0,0,
 | |
| 				initargs, initargs+1,0,initargs+2};
 | |
| 	extern int init_ac[TYSUBR+1];
 | |
| 
 | |
| 	i0 = init_ac[type];
 | |
| 	t = init_ap[type];
 | |
| 	te = t + i0;
 | |
| 	if (at = *at0) {
 | |
| 		*at1 = at;
 | |
| 		nargs = at->nargs;
 | |
| 		if (nargs < 0) { /* inconsistent usage seen */
 | |
| 			if (type) {
 | |
| 				if (at->changes & 2)
 | |
| 					--proc_protochanges;
 | |
| 				goto newlist;
 | |
| 				}
 | |
| 			return;
 | |
| 			}
 | |
| 		atypes = at->atypes;
 | |
| 		i = nchargs;
 | |
| 		for(; t < te; atypes++) {
 | |
| 			if (++i > nargs) {
 | |
|  toomany:
 | |
| 				i = nchargs + i0;
 | |
| 				for(cp = arglist; cp; cp = cp->nextp)
 | |
| 					i++;
 | |
|  toofew:
 | |
| 				sprintf(buf,
 | |
| 		"%s%.90s:\n\there %d, previously %d args and string lengths.",
 | |
| 					inconsist, fname, i, nargs);
 | |
| 				atype_squawk(at, buf);
 | |
|  retn:
 | |
| 				if (type)
 | |
| 					goto newlist;
 | |
| 				return;
 | |
| 				}
 | |
| 			j = atypes->type;
 | |
| 			k = *t++;
 | |
| 			if (j != k)
 | |
| 				goto badtypes;
 | |
| 			}
 | |
| 		for(cp = arglist; cp; atypes++, cp = cp->nextp) {
 | |
| 			if (++i > nargs)
 | |
| 				goto toomany;
 | |
| 			j = atypes->type;
 | |
| 			if (!(q = (expptr)cp->datap))
 | |
| 				continue;
 | |
| 			k = typekludge(ccall, q, atypes, j);
 | |
| 			if (k >= 300 || k == j)
 | |
| 				continue;
 | |
| 			if (j >= 300) {
 | |
| 				if (k >= 200) {
 | |
| 					if (k == TYUNKNOWN + 200)
 | |
| 						continue;
 | |
| 					if (j % 100 != k - 200
 | |
| 					 && k != TYSUBR + 200
 | |
| 					 && j != TYUNKNOWN + 300
 | |
| 					 && !type_fixup(at,atypes,k))
 | |
| 						goto badtypes;
 | |
| 					}
 | |
| 				else if (j % 100 % TYSUBR != k % TYSUBR
 | |
| 						&& !type_fixup(at,atypes,k))
 | |
| 					goto badtypes;
 | |
| 				}
 | |
| 			else if (k < 200 || j < 200)
 | |
| 				if (j)
 | |
| 					goto badtypes;
 | |
| 				else ; /* fall through to update */
 | |
| 			else if (k == TYUNKNOWN+200)
 | |
| 				continue;
 | |
| 			else if (j != TYUNKNOWN+200)
 | |
| 				{
 | |
|  badtypes:
 | |
| 				bad_atypes(at, fname, i, j, k, "here ",
 | |
| 						", previously");
 | |
| 				if (type) {
 | |
| 					/* we're defining the procedure */
 | |
| 					t = init_ap[type];
 | |
| 					te = t + i0;
 | |
| 					proc_argchanges = 1;
 | |
| 					goto newlist;
 | |
| 					}
 | |
| 				goto retn;
 | |
| 				}
 | |
| 			/* We've subsequently learned the right type,
 | |
| 			   as in the call on zoo below...
 | |
| 
 | |
| 				subroutine foo(x, zap)
 | |
| 				external zap
 | |
| 				call goo(zap)
 | |
| 				x = zap(3)
 | |
| 				call zoo(zap)
 | |
| 				end
 | |
| 			 */
 | |
| 			atypes->type = k;
 | |
| 			at->changes |= 1;
 | |
| 			}
 | |
| 		if (i < nargs)
 | |
| 			goto toofew;
 | |
| 		if (zap && (at->changes & 5) != 5)
 | |
| 			at->changes = 0;
 | |
| 		return;
 | |
| 		}
 | |
|  newlist:
 | |
| 	i = i0 + nchargs;
 | |
| 	for(cp = arglist; cp; cp = cp->nextp)
 | |
| 		i++;
 | |
| 	k = sizeof(Argtypes) + (i-1)*sizeof(Atype);
 | |
| 	*at0 = *at1 = at = stg == STGEXT ? (Argtypes *)gmem(k,1)
 | |
| 					 : (Argtypes *) mem(k,1);
 | |
| 	at->nargs = i;
 | |
| 	at->changes = type ? 0 : 4;
 | |
| 	atypes = at->atypes;
 | |
| 	for(; t < te; atypes++) {
 | |
| 		atypes->type = *t++;
 | |
| 		atypes->cp = 0;
 | |
| 		}
 | |
| 	for(cp = arglist; cp; atypes++, cp = cp->nextp) {
 | |
| 		atypes->cp = 0;
 | |
| 		atypes->type = (q = (expptr)cp->datap)
 | |
| 			? typekludge(ccall, q, atypes, 0)
 | |
| 			: 0;
 | |
| 		}
 | |
| 	for(; --nchargs >= 0; atypes++) {
 | |
| 		atypes->type = TYFTNLEN + 100;
 | |
| 		atypes->cp = 0;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
|  void
 | |
| saveargtypes(p)		/* for writing prototypes */
 | |
|  register Exprp p;
 | |
| {
 | |
| 	Addrp a;
 | |
| 	Argtypes **at0, **at1;
 | |
| 	Namep np;
 | |
| 	chainp arglist;
 | |
| 	expptr rp;
 | |
| 	Extsym *e;
 | |
| 	char *fname;
 | |
| 
 | |
| 	a = (Addrp)p->leftp;
 | |
| 	switch(a->vstg) {
 | |
| 		case STGEXT:
 | |
| 			switch(a->uname_tag) {
 | |
| 				case UNAM_EXTERN:	/* e.g., sqrt() */
 | |
| 					e = extsymtab + a->memno;
 | |
| 					at0 = at1 = &e->arginfo;
 | |
| 					fname = e->fextname;
 | |
| 					break;
 | |
| 				case UNAM_NAME:
 | |
| 					np = a->user.name;
 | |
| 					at0 = &extsymtab[np->vardesc.varno].arginfo;
 | |
| 					at1 = &np->arginfo;
 | |
| 					fname = np->fvarname;
 | |
| 					break;
 | |
| 				default:
 | |
| 					goto bug;
 | |
| 				}
 | |
| 			break;
 | |
| 		case STGARG:
 | |
| 			if (a->uname_tag != UNAM_NAME)
 | |
| 				goto bug;
 | |
| 			np = a->user.name;
 | |
| 			at0 = at1 = &np->arginfo;
 | |
| 			fname = np->fvarname;
 | |
| 			break;
 | |
| 		default:
 | |
| 	 bug:
 | |
| 			Fatal("Confusion in saveargtypes");
 | |
| 		}
 | |
| 	rp = p->rightp;
 | |
| 	arglist = rp && rp->tag == TLIST ? rp->listblock.listp : 0;
 | |
| 	save_argtypes(arglist, at0, at1, p->opcode == OPCCALL,
 | |
| 		fname, a->vstg, 0, 0, 0);
 | |
| 	}
 | |
| 
 | |
| /* putcall - fix up the argument list, and write out the invocation.   p
 | |
|    is expected to be initialized and point to an OPCALL or OPCCALL
 | |
|    expression.  The return value is a pointer to a temporary holding the
 | |
|    result of a COMPLEX or CHARACTER operation, or NULL. */
 | |
| 
 | |
| LOCAL expptr putcall(p0, temp)
 | |
|  expptr p0;
 | |
|  Addrp *temp;
 | |
| {
 | |
|     register Exprp p = (Exprp)p0;
 | |
|     chainp arglist;		/* Pointer to actual arguments, if any */
 | |
|     chainp charsp;		/* List of copies of the variables which
 | |
| 				   hold the lengths of character
 | |
| 				   parameters (other than procedure
 | |
| 				   parameters) */
 | |
|     chainp cp;			/* Iterator over argument lists */
 | |
|     register expptr q;		/* Pointer to the current argument */
 | |
|     Addrp fval;			/* Function return value */
 | |
|     int type;			/* type of the call - presumably this was
 | |
| 				   set elsewhere */
 | |
|     int byvalue;		/* True iff we don't want to massage the
 | |
| 				   parameter list, since we're calling a C
 | |
| 				   library routine */
 | |
|     extern int Castargs;
 | |
|     char *s;
 | |
|     extern struct Listblock *mklist();
 | |
| 
 | |
|     type = p -> vtype;
 | |
|     charsp = NULL;
 | |
|     byvalue =  (p->opcode == OPCCALL);
 | |
| 
 | |
| /* Verify the actual parameters */
 | |
| 
 | |
|     if (p == (Exprp) NULL)
 | |
| 	err ("putcall:  NULL call expression");
 | |
|     else if (p -> tag != TEXPR)
 | |
| 	erri ("putcall:  expected TEXPR, got '%d'", p -> tag);
 | |
| 
 | |
| /* Find the argument list */
 | |
| 
 | |
|     if(p->rightp && p -> rightp -> tag == TLIST)
 | |
| 	arglist = p->rightp->listblock.listp;
 | |
|     else
 | |
| 	arglist = NULL;
 | |
| 
 | |
| /* Count the number of explicit arguments, including lengths of character
 | |
|    variables */
 | |
| 
 | |
|     for(cp = arglist ; cp ; cp = cp->nextp)
 | |
| 	if(!byvalue) {
 | |
| 	    q = (expptr) cp->datap;
 | |
| 	    if( ISCONST(q) )
 | |
| 	    {
 | |
| 
 | |
| /* Even constants are passed by reference, so we need to put them in the
 | |
|    literal table */
 | |
| 
 | |
| 		q = (expptr) putconst((Constp)q);
 | |
| 		cp->datap = (char *) q;
 | |
| 	    }
 | |
| 
 | |
| /* Save the length expression of character variables (NOT character
 | |
|    procedures) for the end of the argument list */
 | |
| 
 | |
| 	    if( ISCHAR(q) &&
 | |
| 		(q->headblock.vclass != CLPROC
 | |
| 		|| q->headblock.vstg == STGARG
 | |
| 			&& q->tag == TADDR
 | |
| 			&& q->addrblock.uname_tag == UNAM_NAME
 | |
| 			&& q->addrblock.user.name->vprocclass == PTHISPROC))
 | |
| 	    {
 | |
| 		p0 = cpexpr(q->headblock.vleng);
 | |
| 		charsp = mkchain((char *)p0, charsp);
 | |
| 		if (q->headblock.vclass == CLUNKNOWN
 | |
| 		 && q->headblock.vstg == STGARG)
 | |
| 			q->addrblock.user.name->vpassed = 1;
 | |
| 		else if (q->tag == TADDR
 | |
| 				&& q->addrblock.uname_tag == UNAM_CONST)
 | |
| 			p0->constblock.Const.ci
 | |
| 				+= q->addrblock.user.Const.ccp1.blanks;
 | |
| 	    }
 | |
| 	}
 | |
|     charsp = revchain(charsp);
 | |
| 
 | |
| /* If the routine is a CHARACTER function ... */
 | |
| 
 | |
|     if(type == TYCHAR)
 | |
|     {
 | |
| 	if( ISICON(p->vleng) )
 | |
| 	{
 | |
| 
 | |
| /* Allocate a temporary to hold the return value of the function */
 | |
| 
 | |
| 	    fval = mktmp(TYCHAR, p->vleng);
 | |
| 	}
 | |
| 	else    {
 | |
| 		err("adjustable character function");
 | |
| 		if (temp)
 | |
| 			*temp = 0;
 | |
| 		return 0;
 | |
| 		}
 | |
|     }
 | |
| 
 | |
| /* If the routine is a COMPLEX function ... */
 | |
| 
 | |
|     else if( ISCOMPLEX(type) )
 | |
| 	fval = mktmp(type, ENULL);
 | |
|     else
 | |
| 	fval = NULL;
 | |
| 
 | |
| /* Write the function name, without taking its address */
 | |
| 
 | |
|     p -> leftp = putx(fixtype(putaddr(p->leftp)));
 | |
| 
 | |
|     if(fval)
 | |
|     {
 | |
| 	chainp prepend;
 | |
| 
 | |
| /* Prepend a copy of the function return value buffer out as the first
 | |
|    argument. */
 | |
| 
 | |
| 	prepend = mkchain((char *)putx(putaddr(cpexpr((expptr)fval))), arglist);
 | |
| 
 | |
| /* If it's a character function, also prepend the length of the result */
 | |
| 
 | |
| 	if(type==TYCHAR)
 | |
| 	{
 | |
| 
 | |
| 	    prepend->nextp = mkchain((char *)putx(mkconv(TYLENG,
 | |
| 					p->vleng)), arglist);
 | |
| 	}
 | |
| 	if (!(q = p->rightp))
 | |
| 		p->rightp = q = (expptr)mklist(CHNULL);
 | |
| 	q->listblock.listp = prepend;
 | |
|     }
 | |
| 
 | |
| /* Scan through the fortran argument list */
 | |
| 
 | |
|     for(cp = arglist ; cp ; cp = cp->nextp)
 | |
|     {
 | |
| 	q = (expptr) (cp->datap);
 | |
| 	if (q == ENULL)
 | |
| 	    err ("putcall:  NULL argument");
 | |
| 
 | |
| /* call putaddr only when we've got a parameter for a C routine or a
 | |
|    memory resident parameter */
 | |
| 
 | |
| 	if (q -> tag == TCONST && !byvalue)
 | |
| 	    q = (expptr) putconst ((Constp)q);
 | |
| 
 | |
| 	if(q->tag==TADDR && (byvalue || q->addrblock.vstg!=STGREG) )
 | |
| 		cp->datap = (char *)putaddr(q);
 | |
| 	else if( ISCOMPLEX(q->headblock.vtype) )
 | |
| 	    cp -> datap = (char *) putx (fixtype(putcxop(q)));
 | |
| 	else if (ISCHAR(q) )
 | |
| 	    cp -> datap = (char *) putx (fixtype((expptr)putchop(q)));
 | |
| 	else if( ! ISERROR(q) )
 | |
| 	{
 | |
| 	    if(byvalue
 | |
| 	    || q->tag == TEXPR && q->exprblock.opcode == OPCHARCAST)
 | |
| 		cp -> datap = (char *) putx(q);
 | |
| 	    else {
 | |
| 		expptr t, t1;
 | |
| 
 | |
| /* If we've got a register parameter, or (maybe?) a constant, save it in a
 | |
|    temporary first */
 | |
| 
 | |
| 		t = (expptr) mktmp(q->headblock.vtype, q->headblock.vleng);
 | |
| 
 | |
| /* Assign to temporary variables before invoking the subroutine or
 | |
|    function */
 | |
| 
 | |
| 		t1 = putassign( cpexpr(t), q );
 | |
| 		if (doin_setbound)
 | |
| 			t = mkexpr(OPCOMMA_ARG, t1, t);
 | |
| 		else
 | |
| 			putout(t1);
 | |
| 		cp -> datap = (char *) t;
 | |
| 	    } /* else */
 | |
| 	} /* if !ISERROR(q) */
 | |
|     }
 | |
| 
 | |
| /* Now adjust the lengths of the CHARACTER parameters */
 | |
| 
 | |
|     for(cp = charsp ; cp ; cp = cp->nextp)
 | |
| 	cp->datap = (char *)addrfix(putx(
 | |
| 			/* in case MAIN has a character*(*)... */
 | |
| 			(s = cp->datap) ? mkconv(TYLENG,(expptr)s)
 | |
| 					 : ICON(0)));
 | |
| 
 | |
| /* ... and add them to the end of the argument list */
 | |
| 
 | |
|     hookup (arglist, charsp);
 | |
| 
 | |
| /* Return the name of the temporary used to hold the results, if any was
 | |
|    necessary. */
 | |
| 
 | |
|     if (temp) *temp = fval;
 | |
|     else frexpr ((expptr)fval);
 | |
| 
 | |
|     saveargtypes(p);
 | |
| 
 | |
|     return (expptr) p;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /* putmnmx -- Put min or max.   p   must point to an EXPR, not just a
 | |
|    CONST */
 | |
| 
 | |
| LOCAL expptr putmnmx(p)
 | |
| register expptr p;
 | |
| {
 | |
| 	int op, op2, type;
 | |
| 	expptr arg, qp, temp;
 | |
| 	chainp p0, p1;
 | |
| 	Addrp sp, tp;
 | |
| 	char comment_buf[80];
 | |
| 	char *what;
 | |
| 
 | |
| 	if(p->tag != TEXPR)
 | |
| 		badtag("putmnmx", p->tag);
 | |
| 
 | |
| 	type = p->exprblock.vtype;
 | |
| 	op = p->exprblock.opcode;
 | |
| 	op2 = op == OPMIN ? OPMIN2 : OPMAX2;
 | |
| 	p0 = p->exprblock.leftp->listblock.listp;
 | |
| 	free( (charptr) (p->exprblock.leftp) );
 | |
| 	free( (charptr) p );
 | |
| 
 | |
| 	/* special case for two addressable operands */
 | |
| 
 | |
| 	if (addressable((expptr)p0->datap)
 | |
| 	 && (p1 = p0->nextp)
 | |
| 	 && addressable((expptr)p1->datap)
 | |
| 	 && !p1->nextp) {
 | |
| 		if (type == TYREAL && forcedouble)
 | |
| 			op2 = op == OPMIN ? OPDMIN : OPDMAX;
 | |
| 		p = mkexpr(op2, mkconv(type, cpexpr((expptr)p0->datap)),
 | |
| 				mkconv(type, cpexpr((expptr)p1->datap)));
 | |
| 		frchain(&p0);
 | |
| 		return p;
 | |
| 		}
 | |
| 
 | |
| 	/* general case */
 | |
| 
 | |
| 	sp = mktmp(type, ENULL);
 | |
| 
 | |
| /* We only need a second temporary if the arg list has an unaddressable
 | |
|    value */
 | |
| 
 | |
| 	tp = (Addrp) NULL;
 | |
| 	qp = ENULL;
 | |
| 	for (p1 = p0 -> nextp; p1; p1 = p1 -> nextp)
 | |
| 		if (!addressable ((expptr) p1 -> datap)) {
 | |
| 			tp = mktmp(type, ENULL);
 | |
| 			qp = mkexpr(op2, cpexpr((expptr)sp), cpexpr((expptr)tp));
 | |
| 			qp = fixexpr((Exprp)qp);
 | |
| 			break;
 | |
| 		} /* if */
 | |
| 
 | |
| /* Now output the appropriate number of assignments and comparisons.  Min
 | |
|    and max are implemented by the simple O(n) algorithm:
 | |
| 
 | |
| 	min (a, b, c, d) ==>
 | |
| 	{ <type> t1, t2;
 | |
| 
 | |
| 	    t1 = a;
 | |
| 	    t2 = b; t1 = (t1 < t2) ? t1 : t2;
 | |
| 	    t2 = c; t1 = (t1 < t2) ? t1 : t2;
 | |
| 	    t2 = d; t1 = (t1 < t2) ? t1 : t2;
 | |
| 	}
 | |
| */
 | |
| 
 | |
| 	if (!doin_setbound) {
 | |
| 		switch(op) {
 | |
| 			case OPLT:
 | |
| 			case OPMIN:
 | |
| 			case OPDMIN:
 | |
| 			case OPMIN2:
 | |
| 				what = "IN";
 | |
| 				break;
 | |
| 			default:
 | |
| 				what = "AX";
 | |
| 			}
 | |
| 		sprintf (comment_buf, "Computing M%s", what);
 | |
| 		p1_comment (comment_buf);
 | |
| 		}
 | |
| 
 | |
| 	p1 = p0->nextp;
 | |
| 	temp = (expptr)p0->datap;
 | |
| 	if (addressable(temp) && addressable((expptr)p1->datap)) {
 | |
| 		p = mkconv(type, cpexpr(temp));
 | |
| 		arg = mkconv(type, cpexpr((expptr)p1->datap));
 | |
| 		temp = mkexpr(op2, p, arg);
 | |
| 		if (!ISCONST(temp))
 | |
| 			temp = fixexpr((Exprp)temp);
 | |
| 		p1 = p1->nextp;
 | |
| 		}
 | |
| 	p = putassign (cpexpr((expptr)sp), temp);
 | |
| 
 | |
| 	for(; p1 ; p1 = p1->nextp)
 | |
| 	{
 | |
| 		if (addressable ((expptr) p1 -> datap)) {
 | |
| 			arg = mkconv(type, cpexpr((expptr)p1->datap));
 | |
| 			temp = mkexpr(op2, cpexpr((expptr)sp), arg);
 | |
| 			temp = fixexpr((Exprp)temp);
 | |
| 		} else {
 | |
| 			temp = (expptr) cpexpr (qp);
 | |
| 			p = mkexpr(OPCOMMA, p,
 | |
| 				putassign(cpexpr((expptr)tp), (expptr)p1->datap));
 | |
| 		} /* else */
 | |
| 
 | |
| 		if(p1->nextp)
 | |
| 			p = mkexpr(OPCOMMA, p,
 | |
| 				putassign(cpexpr((expptr)sp), temp));
 | |
| 		else {
 | |
| 			if (type == TYREAL && forcedouble)
 | |
| 				temp->exprblock.opcode =
 | |
| 					op == OPMIN ? OPDMIN : OPDMAX;
 | |
| 			if (doin_setbound)
 | |
| 				p = mkexpr(OPCOMMA, p, temp);
 | |
| 			else {
 | |
| 				putout (p);
 | |
| 				p = putx(temp);
 | |
| 				}
 | |
| 			if (qp)
 | |
| 				frexpr (qp);
 | |
| 		} /* else */
 | |
| 	} /* for */
 | |
| 
 | |
| 	frchain( &p0 );
 | |
| 	return p;
 | |
| }
 | |
| 
 | |
| 
 | |
|  void
 | |
| putwhile(p)
 | |
|  expptr p;
 | |
| {
 | |
| 	long where;
 | |
| 	int k, n;
 | |
| 
 | |
| 	if (wh_next >= wh_last)
 | |
| 		{
 | |
| 		k = wh_last - wh_first;
 | |
| 		n = k + 100;
 | |
| 		wh_next = mem(n,0);
 | |
| 		wh_last = wh_first + n;
 | |
| 		if (k)
 | |
| 			memcpy(wh_next, wh_first, k);
 | |
| 		wh_first =  wh_next;
 | |
| 		wh_next += k;
 | |
| 		wh_last = wh_first + n;
 | |
| 		}
 | |
| 	if( ( k = (p = fixtype(p))->headblock.vtype) != TYLOGICAL)
 | |
| 		{
 | |
| 		if(k != TYERROR)
 | |
| 			err("non-logical expression in DO WHILE statement");
 | |
| 		}
 | |
| 	else	{
 | |
| 		p1put(P1_WHILE1START);
 | |
| 		where = ftell(pass1_file);
 | |
| 		p = putx(p);
 | |
| 		*wh_next++ = ftell(pass1_file) > where;
 | |
| 		p1put(P1_WHILE2START);
 | |
| 		p1_expr(p);
 | |
| 		}
 | |
| 	}
 |