/*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */

#ifndef lint
static char sccsid[] = "@(#)fhdr.c	5.2 (Berkeley) 7/26/85";
#endif not lint

#include "whoami.h"
#include "0.h"
#include "tree.h"
#include "opcode.h"
#include "objfmt.h"
#include "align.h"
#include "tree_ty.h"

/*
 * this array keeps the pxp counters associated with
 * functions and procedures, so that they can be output
 * when their bodies are encountered
 */
int	bodycnts[ DSPLYSZ ];

#ifdef PC
#   include "pc.h"
#endif PC

#ifdef OBJ
int	cntpatch;
int	nfppatch;
#endif OBJ

/*
 * Funchdr inserts
 * declaration of a the
 * prog/proc/func into the
 * namelist. It also handles
 * the arguments and puts out
 * a transfer which defines
 * the entry point of a procedure.
 */

struct nl *
funchdr(r)
	struct tnode *r;
{
	register struct nl *p;
	register struct tnode *rl;
	struct nl *cp, *dp, *temp;
	int o;

	if (inpflist(r->p_dec.id_ptr)) {
		opush('l');
		yyretrieve();	/* kludge */
	}
	pfcnt++;
	parts[ cbn ] |= RPRT;
	line = r->p_dec.line_no;
	if (r->p_dec.param_list == TR_NIL &&
		(p=lookup1(r->p_dec.id_ptr)) != NIL && bn == cbn) {
		/*
		 * Symbol already defined
		 * in this block. it is either
		 * a redeclared symbol (error)
		 * a forward declaration,
		 * or an external declaration.
		 * check that forwards are of the right kind:
		 *     if this fails, we are trying to redefine it
		 *     and enter() will complain.
		 */
		if (  ( ( p->nl_flags & NFORWD ) != 0 )
		   && (  ( p->class == FUNC && r->tag == T_FDEC )
		      || ( p->class == PROC && r->tag == T_PDEC ) ) ) {
			/*
			 * Grammar doesnt forbid
			 * types on a resolution
			 * of a forward function
			 * declaration.
			 */
			if (p->class == FUNC && r->p_dec.type)
				error("Function type should be given only in forward declaration");
			/*
			 * get another counter for the actual
			 */
			if ( monflg ) {
			    bodycnts[ cbn ] = getcnt();
			}
#			ifdef PC
			    enclosing[ cbn ] = p -> symbol;
#			endif PC
#			ifdef PTREE
				/*
				 *	mark this proc/func as forward
				 *	in the pTree.
				 */
			    pDEF( p -> inTree ).PorFForward = TRUE;
#			endif PTREE
			return (p);
		}
	}

	/* if a routine segment is being compiled,
	 * do level one processing.
	 */

	 if ((r->tag != T_PROG) && (!progseen))
		level1();


	/*
	 * Declare the prog/proc/func
	 */
	switch (r->tag) {
	    case T_PROG:
		    progseen = TRUE;
		    if (opt('z'))
			    monflg = TRUE;
		    program = p = defnl(r->p_dec.id_ptr, PROG, NLNIL, 0);
		    p->value[3] = r->p_dec.line_no;
		    break;
	    case T_PDEC:
		    if (r->p_dec.type != TR_NIL)
			    error("Procedures do not have types, only functions do");
		    p = enter(defnl(r->p_dec.id_ptr, PROC, NLNIL, 0));
		    p->nl_flags |= NMOD;
#		    ifdef PC
			enclosing[ cbn ] = r->p_dec.id_ptr;
			p -> extra_flags |= NGLOBAL;
#		    endif PC
		    break;
	    case T_FDEC:
		    {
			register struct tnode *il;
		    il = r->p_dec.type;
		    if (il == TR_NIL) {
			    temp = NLNIL;
			    error("Function type must be specified");
		    } else if (il->tag != T_TYID) {
			    temp = NLNIL;
			    error("Function type can be specified only by using a type identifier");
		    } else
			    temp = gtype(il);
		    }
		    p = enter(defnl(r->p_dec.id_ptr, FUNC, temp, NIL));
		    p->nl_flags |= NMOD;
		    /*
		     * An arbitrary restriction
		     */
		    switch (o = classify(p->type)) {
			    case TFILE:
			    case TARY:
			    case TREC:
			    case TSET:
			    case TSTR:
				    warning();
				    if (opt('s')) {
					    standard();
				    }
				    error("Functions should not return %ss", clnames[o]);
		    }
#		    ifdef PC
			enclosing[ cbn ] = r->p_dec.id_ptr;
			p -> extra_flags |= NGLOBAL;
#		    endif PC
		    break;
	    default:
		    panic("funchdr");
	}
	if (r->tag != T_PROG) {
		/*
		 * Mark this proc/func as
		 * being forward declared
		 */
		p->nl_flags |= NFORWD;
		/*
		 * Enter the parameters
		 * in the next block for
		 * the time being
		 */
		if (++cbn >= DSPLYSZ) {
			error("Procedure/function nesting too deep");
			pexit(ERRS);
		}
		/*
		 * For functions, the function variable
		 */
		if (p->class == FUNC) {
#			ifdef OBJ
			    cp = defnl(r->p_dec.id_ptr, FVAR, p->type, 0);
#			endif OBJ
#			ifdef PC
				/*
				 * fvars used to be allocated and deallocated
				 * by the caller right before the arguments.
				 * the offset of the fvar was kept in
				 * value[NL_OFFS] of function (very wierd,
				 * but see asgnop).
				 * now, they are locals to the function
				 * with the offset kept in the fvar.
				 */

			    cp = defnl(r->p_dec.id_ptr, FVAR, p->type,
				(int)-leven(roundup(
			            (int)(DPOFF1+lwidth(p->type)),
				    (long)align(p->type))));
			    cp -> extra_flags |= NLOCAL;
#			endif PC
			cp->chain = p;
			p->ptr[NL_FVAR] = cp;
		}
		/*
		 * Enter the parameters
		 * and compute total size
		 */
	        p->value[NL_OFFS] = params(p, r->p_dec.param_list);
		/*
		 * because NL_LINENO field in the function 
		 * namelist entry has been used (as have all
		 * the other fields), the line number is
		 * stored in the NL_LINENO field of its fvar.
		 */
		if (p->class == FUNC)
		    p->ptr[NL_FVAR]->value[NL_LINENO] = r->p_dec.line_no;
		else
		    p->value[NL_LINENO] = r->p_dec.line_no;
		cbn--;
	} else { 
		/*
		 * The wonderful
		 * program statement!
		 */
#		ifdef OBJ
		    if (monflg) {
			    (void) put(1, O_PXPBUF);
			    cntpatch = put(2, O_CASE4, (long)0);
			    nfppatch = put(2, O_CASE4, (long)0);
		    }
#		endif OBJ
		cp = p;
		for (rl = r->p_dec.param_list; rl; rl = rl->list_node.next) {
			if (rl->list_node.list == TR_NIL)
				continue;
			dp = defnl((char *) rl->list_node.list, VAR, NLNIL, 0);
			cp->chain = dp;
			cp = dp;
		}
	}
	/*
	 * Define a branch at
	 * the "entry point" of
	 * the prog/proc/func.
	 */
	p->value[NL_ENTLOC] = (int) getlab();
	if (monflg) {
		bodycnts[ cbn ] = getcnt();
		p->value[ NL_CNTR ] = 0;
	}
#	ifdef OBJ
	    (void) put(2, O_TRA4, (long)p->value[NL_ENTLOC]);
#	endif OBJ
#	ifdef PTREE
	    {
		pPointer	PF = tCopy( r );

		pSeize( PorFHeader[ nesting ] );
		if ( r->tag != T_PROG ) {
			pPointer	*PFs;

			PFs = &( pDEF( PorFHeader[ nesting ] ).PorFPFs );
			*PFs = ListAppend( *PFs , PF );
		} else {
			pDEF( PorFHeader[ nesting ] ).GlobProg = PF;
		}
		pRelease( PorFHeader[ nesting ] );
	    }
#	endif PTREE
	return (p);
}

	/*
	 * deal with the parameter declaration for a routine.
	 * p is the namelist entry of the routine.
	 * formalist is the parse tree for the parameter declaration.
	 * formalist	[0]	T_LISTPP
	 *		[1]	pointer to a formal
	 *		[2]	pointer to next formal
	 * for by-value or by-reference formals, the formal is
	 * formal	[0]	T_PVAL or T_PVAR
	 *		[1]	pointer to id_list
	 *		[2]	pointer to type (error if not typeid)
	 * for function and procedure formals, the formal is
	 * formal	[0]	T_PFUNC or T_PPROC
	 *		[1]	pointer to id_list (error if more than one)
	 *		[2]	pointer to type (error if not typeid, or proc)
	 *		[3]	pointer to formalist for this routine.
	 */
fparams(p, formal)
	register struct nl *p;
	struct tnode *formal;		/* T_PFUNC or T_PPROC */
{
	(void) params(p, formal->pfunc_node.param_list);
	p -> value[ NL_LINENO ] = formal->pfunc_node.line_no;
	p -> ptr[ NL_FCHAIN ] = p -> chain;
	p -> chain = NIL;
}

params(p, formalist)
	register struct nl *p;
	struct tnode *formalist;	/* T_LISTPP */
{
	struct nl *chainp, *savedp;
	struct nl *dp;
	register struct tnode *formalp;	/* an element of the formal list */
	register struct tnode *formal;	/* a formal */
	struct tnode *r, *s, *t, *typ, *idlist;
	int w, o;

	/*
	 * Enter the parameters
	 * and compute total size
	 */
	chainp = savedp = p;

#	ifdef OBJ
	    o = 0;
#	endif OBJ
#	ifdef PC
		/*
		 * parameters used to be allocated backwards,
		 * then fixed.  for pc, they are allocated correctly.
		 * also, they are aligned.
		 */
	    o = DPOFF2;
#	endif PC
	for (formalp = formalist; formalp != TR_NIL;
			formalp = formalp->list_node.next) {
		formal = formalp->list_node.list;
		if (formal == TR_NIL)
			continue;
		/*
		 * Parametric procedures
		 * don't have types !?!
		 */
		typ = formal->pfunc_node.type;
		p = NLNIL;
		if ( typ == TR_NIL ) {
		    if ( formal->tag != T_PPROC ) {
			error("Types must be specified for arguments");
		    }
		} else {
		    if ( formal->tag == T_PPROC ) {
			error("Procedures cannot have types");
		    } else {
			p = gtype(typ);
		    }
		}
		for (idlist = formal->param.id_list; idlist != TR_NIL;
				idlist = idlist->list_node.next) {
			switch (formal->tag) {
			    default:
				    panic("funchdr2");
			    case T_PVAL:
				    if (p != NLNIL) {
					    if (p->class == FILET)
						    error("Files cannot be passed by value");
					    else if (p->nl_flags & NFILES)
						    error("Files cannot be a component of %ss passed by value",
							    nameof(p));
				    }
#				    ifdef OBJ
					w = lwidth(p);
					o -= even(w);
#					ifdef DEC11
					    dp = defnl((char *) idlist->list_node.list,
								VAR, p, o);
#					else
					    dp = defnl((char *) idlist->list_node.list,
						    VAR,p, (w < 2) ? o + 1 : o);
#					endif DEC11
#				    endif OBJ
#				    ifdef PC
					o = roundup(o, (long) A_STACK);
					w = lwidth(p);
#					ifndef DEC11
					    if (w <= sizeof(int)) {
						o += sizeof(int) - w;
					    }
#					endif not DEC11
					dp = defnl((char *) idlist->list_node.list,VAR,
							p, o);
					o += w;
#				    endif PC
				    dp->nl_flags |= NMOD;
				    break;
			    case T_PVAR:
#				    ifdef OBJ
					dp = defnl((char *) idlist->list_node.list, REF,
						    p, o -= sizeof ( int * ) );
#				    endif OBJ
#				    ifdef PC
					dp = defnl( (char *) idlist->list_node.list, REF,
						    p , 
					    o = roundup( o , (long)A_STACK ) );
					o += sizeof(char *);
#				    endif PC
				    break;
			    case T_PFUNC:
				    if (idlist->list_node.next != TR_NIL) {
					error("Each function argument must be declared separately");
					idlist->list_node.next = TR_NIL;
				    }
#				    ifdef OBJ
					dp = defnl((char *) idlist->list_node.list,FFUNC,
						p, o -= sizeof ( int * ) );
#				    endif OBJ
#				    ifdef PC
					dp = defnl( (char *) idlist->list_node.list , 
						FFUNC , p ,
						o = roundup( o , (long)A_STACK ) );
					o += sizeof(char *);
#				    endif PC
				    dp -> nl_flags |= NMOD;
				    fparams(dp, formal);
				    break;
			    case T_PPROC:
				    if (idlist->list_node.next != TR_NIL) {
					error("Each procedure argument must be declared separately");
					idlist->list_node.next = TR_NIL;
				    }
#				    ifdef OBJ
					dp = defnl((char *) idlist->list_node.list,
					    FPROC, p, o -= sizeof ( int * ) );
#				    endif OBJ
#				    ifdef PC
					dp = defnl( (char *) idlist->list_node.list ,
						FPROC , p,
						o = roundup( o , (long)A_STACK ) );
					o += sizeof(char *);
#				    endif PC
				    dp -> nl_flags |= NMOD;
				    fparams(dp, formal);
				    break;
			    }
			if (dp != NLNIL) {
#				ifdef PC
				    dp -> extra_flags |= NPARAM;
#				endif PC
				chainp->chain = dp;
				chainp = dp;
			}
		}
		if (typ != TR_NIL && typ->tag == T_TYCARY) {
#		    ifdef OBJ
			w = -even(lwidth(p->chain));
#			ifndef DEC11
			    w = (w > -2)? w + 1 : w;
#			endif
#		    endif OBJ
#		    ifdef PC
			w = lwidth(p->chain);
			o = roundup(o, (long)A_STACK);
#		    endif PC
		    /*
		     * Allocate space for upper and
		     * lower bounds and width.
		     */
		    for (s=typ; s->tag == T_TYCARY; s = s->ary_ty.type) {
			for (r=s->ary_ty.type_list; r != TR_NIL;
						r = r->list_node.next) {
			    t = r->list_node.list;
			    p = p->chain;
#			    ifdef OBJ
				o += w;
#			    endif OBJ
			    chainp->chain = defnl(t->crang_ty.lwb_var,
								VAR, p, o);
			    chainp = chainp->chain;
			    chainp->nl_flags |= (NMOD | NUSED);
			    p->nptr[0] = chainp;
			    o += w;
			    chainp->chain = defnl(t->crang_ty.upb_var,
								VAR, p, o);
			    chainp = chainp->chain;
			    chainp->nl_flags |= (NMOD | NUSED);
			    p->nptr[1] = chainp;
			    o += w;
			    chainp->chain  = defnl(0, VAR, p, o);
			    chainp = chainp->chain;
			    chainp->nl_flags |= (NMOD | NUSED);
			    p->nptr[2] = chainp;
#			    ifdef PC
				o += w;
#			    endif PC
			}
		    }
		}
	}
	p = savedp;
#	ifdef OBJ
		/*
		 * Correct the naivete (naivety)
		 * of our above code to
		 * calculate offsets
		 */
	    for (dp = p->chain; dp != NLNIL; dp = dp->chain)
		    dp->value[NL_OFFS] += -o + DPOFF2;
	    return (-o + DPOFF2);
#	endif OBJ
#	ifdef PC
	    return roundup( o , (long)A_STACK );
#	endif PC
}
