view mc-inline.c @ 856:d9dd3e6e7c5f

macro compatibility
author Shinji KONO <kono@ie.u-ryukyu.ac.jp>
date Tue, 22 Nov 2011 05:44:49 +0900
parents 59ebc5cb2a99
children 5313ed059cee
line wrap: on
line source

/* Micro-C Partial Evaluator Part */

/************************************************************************
** Copyright (C) 2006 Shinji Kono
** 連絡先: 琉球大学情報工学科 河野 真治  
*)* (E-Mail Address: kono@ie.u-ryukyu.ac.jp)
**
**    このソースのいかなる複写,改変,修正も許諾します。ただし、
**    その際には、誰が貢献したを示すこの部分を残すこと。
**    再配布や雑誌の付録などの問い合わせも必要ありません。
**    営利利用も上記に反しない範囲で許可します。
**    バイナリの配布の際にはversion messageを保存することを条件とします。
**    このプログラムについては特に何の保証もしない、悪しからず。
**
**    Everyone is permitted to do anything on this program 
**    including copying, modifying, improving,
**    as long as you don't try to pretend that you wrote it.
**    i.e., the above copyright notice has to appear in all copies.  
**    Binary distribution requires original version messages.
**    You don't have to ask before copying, redistribution or publishing.
**    THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE.
***********************************************************************/

/*

    Inline handler

    inline code is stored as parse tree 
	expr already has parse tree
	statement part is handled here

        st_hoge()        code generator (called from gexpr())
	p_hoge()         partial evaluator called in gen_inline()
	    after p_hoge(), it contains no ST_* node.

    PVAR has an offset for pvartable, it can be
	constant, local variable or global variable
	other complex expression is evaluated before the code expansion

    We always perform inline expansion.
    Non static inline function can be referenced from other. Real
    function body is generated by pfdecl() in mc-parse.c.

 */

#include <stdio.h>
#include "mc.h"
#include "mc-parse.h"
#include "mc-codegen.h"
#include "mc-switch.h"
#include "mc-code.h"
#include "mc-inline.h"

static int pvartable;
static int pdisp;

static int ret_register,ret_reg_mode;

static int inline_lvars;

/*
    Basic code generator from parse tree
 */

extern void
st_decl(int e1){
    // NMTBL *n = (NMTBL *)caddr(e1);
    // int stmode = cadddr(e1);
}
 
extern void
st_if(int e1){
    int l1,l2,slfree;
    int e2=caddr(e1),e3;
    // conv->if_();
    slfree=lfree;
    checkret();
    l1 = bexpr(car(e2),0,fwdlabel());
    // conv->if_then_();
    g_expr_u(cadr(e2));
    checkret();
    if ((e3=caddr(e2))) {  // else
        // conv->if_else_();
	if ((l2 = control))
	    gen_jmp(l2=fwdlabel());
	fwddef(l1);
	g_expr_u(e3);
	checkret();
	if (l2) fwddef(l2);
    } else {
	fwddef(l1);
    }
    // conv->if_endif_();
}

 
extern void
st_do(int e1){
    int sbreak,scontinue,l;

    sbreak=blabel;
    scontinue=clabel;
    blabel=fwdlabel();
    clabel=fwdlabel();
    control=1;
    checkret();
    l=backdef();
    // conv->dowhile_();
    g_expr_u(cadddr(e1));
    checkret();
    // conv->dowhile_cond_();
    bexpr(caddr(e1),1,l);
    // conv->dowhile_end_();
    fwddef(blabel);
    clabel=scontinue;
    blabel=sbreak;
}

 
extern void
st_while(int e1){
    int sbreak,scontinue,e;

    sbreak=blabel;
    scontinue=clabel;
    blabel=fwdlabel();
    control=1;
    checkret();
    clabel=backdef();
    // conv->while_();
    // conv->while_body_();
    if(!(e=cadddr(e1))) {
	bexpr(caddr(e1),1,clabel);
        // conv->sm_();
    } else {
	bexpr(caddr(e1),0,blabel);
	g_expr_u(e);
	checkret();
	if(control)
	    gen_jmp(clabel);
    }
    // conv->while_end_();
    fwddef(blabel);
    clabel=scontinue;
    blabel=sbreak;
}

 
extern void
st_for(int e1){
    int p0,p1,p2,body;
    int l,e;
    int sbreak=blabel;
    int scontinue=clabel;

    e = caddr(e1); 
    p0 = car(e); p1 = cadr(e); p2 = caddr(e); body = cadddr(e);

    blabel=fwdlabel();
    // conv->for_();
    if (p0) {
	checkret();
	g_expr_u(p0);
    }
    // conv->for1_();
    control=1;
    checkret();
    l=backdef();
    if (p1) {
	bexpr(p1,0,blabel);
    }
    // conv->for2_();
    // conv->for_body_();
    if (!p2) {
        clabel=l;
	g_expr_u(body);
	checkret();
    } else {
        clabel=fwdlabel();
	g_expr_u(body);
	checkret();
	fwddef(clabel);
	g_expr_u(p2);
    }
    // conv->for_end_();
    gen_jmp(l);
    fwddef(blabel);
    clabel=scontinue;
    blabel=sbreak;
}

 
extern void
st_switch(int e1){
    int sbreak,scase,sdefault,slfree,svalue,slist;
    int cst,e;

    checkret();
    slist = cslist;
    cslist = 0;
    sbreak=blabel;      /* save parents break label */
    blabel=fwdlabel();
    sdefault=dlabel;    /* save parents default label */
    dlabel=0;
    scase=cslabel;      /* save parents next case label */
    // conv->switch_();
    slfree=lfree;
    svalue=csvalue1;      /* save parents switch value */
    e =caddr(e1);         /* switch value */
    if (car(e)==CONST) {
	cst = 1;
	csvalue1=glist2(CONST,cadr(e));
	gen_jmp( cslabel=fwdlabel());
    } else {
	cst = 0;
	g_expr(e);       /* switch value */
	csvalue1=csvalue() ;
	cslabel = control = 0;
    }
    // conv->switch_body_();
    g_expr_u(cadddr(e1));
    // conv->switch_end_();
    checkret();
#if CASE_CODE
    if (!cst) {
	if (control) gen_jmp(blabel);
	genswitch(cslist,cslabel);
    } else if (!cslist) {
	if(dlabel) df_label(cslabel,dlabel);
	else {
	    fwddef(cslabel);
	}
	cslist= 1;
    }
#else
    if (!(cst && cslist)) {
	if(dlabel) df_label(cslabel,dlabel);
	else fwddef(cslabel);
    }
#endif
    free_glist2(csvalue1);
    if (cst && !cslist) {
	if(pending_jmp!=cslabel)
	    fwddef(cslabel);
	else pending_jmp = 0; // cslabel is here
    }
    fwddef(blabel);
    csvalue1=svalue;
    cslabel=scase;
    dlabel=sdefault;
    blabel=sbreak;
    cslist = slist;
}

 
extern void
st_comp(int e1){
    g_expr_u(caddr(e1));
}

 
extern void
st_break(int e1){
    checkret();
    // conv->break_();
    if (control)
	gen_jmp(blabel);
}

 
extern void
st_continue(int e1){
    checkret();
    // conv->continue_();
    if (control) gen_jmp(clabel);
}

 
extern void
st_case(int e1){
#if CASE_CODE
    int l=0,clist=caddr(e1),c;
    int cst = (car(csvalue1)==CONST);
    if (cst) {
	c=cadr(csvalue1);
	for(;clist;clist=cadr(clist)) {
	    if (car(clist)==c) break;
	}
	if (!clist) return; // no match
    } else
	l = fwdlabel();
    if (retpending) {
        ret(); retpending=0;
    }
    if (cst) {
	if (cslist) { // may duplicat csvalue
	    return;
	}
	cslist=1;
	fwddef(cslabel);
	return;
    }
    if (!cslabel) {
        if (!control) {
            cmpdimm(car(clist),csvalue1,cslabel=fwdlabel(),1);
            caddr(clist)=0;
        } else {
            error(-1);
        }
    }
    while(clist) {
	caddr(clist) = l;
        clist = cadr(c=clist); cadr(c) = 0;  // insert destroy cadr of clist
        cslist=insert_ascend(cslist,c,docase_eq);
    }
    fwddef(l);
    control=1;
#else
    int c,clist,l;
    int cst = (car(csvalue1)==CONST);
    clist = caddr(e1);
    if (cst) {
	c=cadr(csvalue1);
	for(;clist;clist=cadr(clist)) {
	    if (car(clist)==c) break; // no match
	}
	if (!clist) return;
    }
    if (cst) {
	if (!cslist) {
	    if (retpending) {
		ret(); retpending=0;
	    }
	    fwddef(cslabel);
	    cslist = 1;
	} // else error(CSERR);
	return;
    }
    if (retpending) {
	ret(); retpending=0;
    }
    l=fwdlabel();
    if (control) {
        control=0;
        gen_jmp(l);
    }
    if (cslabel) fwddef(cslabel);
    while(cadr(clist)) {
        cmpdimm(car(clist),csvalue1,l,0);
        clist=cadr(clist);
    }
    cmpdimm(car(clist),csvalue1,cslabel=fwdlabel(),1);
    if (l) fwddef(l);
#endif
}

 
extern void
st_default(int e1){
    // int cst = (car(csvalue1)==CONST);
    control=1;
    checkret();
    if (dlabel) error(STERR);  // double default:
    dlabel = backdef();
    // conv->case_(0,1);
}

 
extern void
st_return(int e1){
    int e,t;

    if (!cslabel) gen_jmp(cslabel = fwdlabel());
    if(!(e=caddr(e1))) {
	// no return value
        retpending = 1;
        return;
    }
    t = type_value(cadr(fnptr->ty));
    if (t>0 && (car(t)==STRUCT || car(t)==UNION)) {
	// copy is included in e, pass the pointer
	t = list2(POINTER,type_value(cadr(fnptr->ty)));
    } 
    if (e) {
	g_expr(e);
    }
    if (e && t!=VOID) {
	if (ret_reg_mode==0) {
	    // return value register is not fixed
	    ret_reg_mode=1;
	    ret_register = code_get_fixed_creg(USE_CREG,t);
	} else {
	    code_set_fixed_creg(ret_register,1,t);
	}
    }
    // conv->return_end_();
    retpending = 1;
}

extern void
st_goto(int e){
    int e1;

    checkret();
    e1 = caddr(e);
    if (car(e1)==RINDIRECT) {
	gen_indirect_goto(cadr(e1));
	return ;
    } else if (car(e1)==RLVAR) {
	gen_indirect_goto(e1);
	return ;
    } else if (car(e1)==LVAR||car(e1)==FLABEL) {
	gen_jmp(cadr(e1));
        control=0;
        return ;
    } else if (car(e1)==JUMP) {
	/*   CbC continuation */
	// conv->jump_(env);
	// should be separate function
	jump(cadr(e1),caddr(e1));
	control=0;
	// conv->sm_();
	return ;
    } else {
	error(-1);
    }
}


#if ASM_CODE
extern void
st_asm(int e1){
    checkret();
    g_expr_u(list3(ASM,caddr(e1),cadddr(e1)));
}
#endif

 
extern void
st_label(int e1){
    int lb = caddr(e1);
    control=1;
    checkret();
    if (car(lb)==LVAR) {  // label variable case
    } else if (car(lb)!=FLABEL) error(-1);
    fwddef(cadr(lb));
}

static 
char *plinebuf;    // last comment in parse tree (for compiler debug)

static int plineno;

extern void
st_comment(int e1){
    glineno++;
    printf("## %d ",glineno);
    plineno = caddr(e1);
    gen_comment(plinebuf=(char *)ncadddr(e1));
}

/* 
   partial evaluator
 */

static int
p_vartable(int adisp,int ldisp)
{
    int i;
    int pvartable = getfree(adisp-ldisp); // have to be local heap
    pdisp = pvartable-ldisp;
    for(i=ldisp;i<0;i++) {
	heap[pdisp+i] = 0;
    }
    return pvartable;
}

extern int
p_lvar(int e1)
{
    int sz = is_memory(e1);
    int d = cadr(e1);
    int d1,e;
    if ((d1=(heap[pdisp+d]))) return d1;
fprintf(stderr,"## undeclared ivar %d\n",d);
    error(-1);
    e =  heap[pdisp+d]=list3(LVAR,new_lvar(sz),0);
    inline_lvars = glist2(e,inline_lvars);
    return e;
}

static int
pfunction(int e)
{
    // list4(INLINE,e1,arglist,ftype);
    // include code segement case
    int e1 = pexpr(cadr(e));
    int arglist = caddr(e);
    int newargs = 0;
    int ftype = cadddr(e);
    int e3;
    for (e3 = arglist; e3; e3 = cadr(e3)) {
	newargs = list3( pexpr(car(e3)), newargs, caddr(e3));
    }
    newargs = reverse0(newargs);
    return list4(car(e),e1,newargs,ftype);
}

static int
prindirect(int e)
{
#if 0
    int offset = caddr(e);
    int e1 = pexpr(cadr(e));
    e1 = binop(ADD,e1,list2(CONST,offset),list2(POINTER,type),INT);
    type = cadddr(e);
    if (car(e1)==LVAR) return rvalue(e1);
    if (OP(car(e1))==REGISTER && car(cadr(e))==IVAR) return rvalue(e1);
    return list3(car(e),e1,0);
#else
    int lvar;
    int offset = caddr(e);
    int type0;
    if (offset!=0) error(-1);
    type = cadddr(e);
    type0 = type_value(type);
    if (type0>0 && car(type0)==POINTER) 
            type=set_type_with_attr(cadr(type),type);
    if (car(lvar=cadr(e))==IVAR) {
        lvar=p_lvar(cadr(e)); // can be anything....  
	switch(car(lvar)) {
        case LVAR: 
	    type = cadddr(e);
            return rvalue(lvar);
        case REGISTER: case DREGISTER:
        case FREGISTER: case LREGISTER:
        case CONST: case FCONST: case DCONST: case LCONST:
            // should do type check
            if (offset) error(-1);
            return lvar;
	default:
	    error(-1);
        }
    } 
    int e2 = pexpr(cadr(e));
    if (car(e2)==INDIRECT) e2 = cadr(e2);
    if (OP(car(e2))==ADD) {
	int c = caddr(e2);
	if (car(c)==CONST) { e2 = cadr(e2); offset = cadr(c); }
	else if (car(c)==LCONST) { e2 = cadr(e2); offset = lcadr(c); }
    }
    return list3(car(e),e2,offset);
#endif
}

static int
pindirect(int e)
{
    //int lvar;
    //if (car(lvar=cadr(e))==IVAR)
    //	lvar=p_lvar(cadr(e)); // can be anything....
#if 1
    return pexpr(cadr(e));
#else
    return list3(car(e),pexpr(cadr(e)),caddr(e));
#endif
}

static int
paddress(int e)
{
    // if (car(cadr(e))==INDIRECT) return pexpr(cadr(cadr(e)));
    return list2(car(e),pexpr(cadr(e)));
}

static int
p_conv(int e1,int e2)
{
    int t,stype = type;
    if (is_const(e2) && (t=type_of_conv(e1))) {
	type = INT;
	e1 =  correct_type(e2,t);
	type = stype;
	return e1;
    }
    return list3(CONV,pexpr(e2),e1);
}

static int
pbinop(int op,int e1,int e2)
{
    e1 = pexpr(e1);
    e2 = pexpr(e2);
    if (is_const(e1)||is_const(e2)) {
	int t;
	if((t= type_of_bop(op))) 
	    return binop(OP(op),e1,e2,t,t);
    }
    return list3(op,e1,e2);
}

static int
plor(int op,int e1,int e2)
{
    int e = pexpr(e1);
    return list3(op,e,pexpr(e2));
}

static int
pland(int op,int e1,int e2)
{
    int e = pexpr(e1);
    return list3(op,e,pexpr(e2));
}

static int
psassign(int e)
{
    int e1 = pexpr(cadr(e));
    int e2 = pexpr(caddr(e));
    return list4(car(e),e1,e2,cadddr(e));
}

static int
passign(int e)
{
    int e1 = pexpr(cadr(e));
    int e2 = pexpr(caddr(e));
    return list3(car(e),e1,e2);
}

static int
passop(int e)
{
    int e1 = pexpr(cadr(e));
    int e2 = pexpr(caddr(e));
    return list4(car(e),e1,e2,cadddr(e));
}

static int
pdassign(int e)
{
    int e1 = pexpr(cadr(e));
    int e2 = pexpr(caddr(e));
    return list3(car(e),e1,e2);
}

static int
pdassop(int e)
{
    int e1 = pexpr(cadr(e));
    int e2 = pexpr(caddr(e));
    return list4(car(e),e1,e2,cadddr(e));
}

static int
plassign(int e)
{
    int e1 = pexpr(cadr(e));
    int e2 = pexpr(caddr(e));
    return list3(car(e),e1,e2);
}

static int
plassop(int e)
{
    int e1 = pexpr(cadr(e));
    int e2 = pexpr(caddr(e));
    return list4(car(e),e1,e2,cadddr(e));
}

static int
palloc(int e)
{
    int e1 = pexpr(e);
    if (car(e1)==CONST) 
	return list2(ADDRESS,list3(LVAR,new_lvar_align(cadr(e1),16),0));
    return list2(ALLOCA,e1);
}

static int
pcomma(int e1,int e2)
{
    int e = pexpr(e1);
    return list3(COMMA,e,pexpr(e2));
}

static int
prbit_field(int e)
{
    int e1 =  list3(car(e),pexpr(cadr(e)),caddr(e));
    type = cadr(caddr(e));  // value type
    return e1;
}

static int
pbassign(int e)
{
    // list4(BASS,e1,e2,list2(BASS,t)));
    int e1=pexpr(caddr(e));
    return list4(car(e),pexpr(cadr(e)),e1,cadddr(e));
}

static int
pbassop(int e)
{
    int e1 = caddr(e);
    if (car(e)==BASSOP) e1=pexpr(e1);
    return list4(car(e),pexpr(cadr(e)),e1,cadddr(e));
}

/*
     variable initialization with offset
 */

static int
passign_data(int var,int target_type, int e,int t,int offset)
{
    int ass,sz,bfd;

#if STRUCT_ALIGN
    if (t!=EMPTY) {
        int strtype=0;
        if (t>0 && (car(t)==STRUCT||car(t)==UNION))
            strtype=1;
        sz = size(t);
        if (lp64 && (sz%size_of_longlong==0||strtype)) {
            offset = align(offset,size_of_longlong);
        } else if (sz%size_of_int==0||strtype) {
            offset = align(offset,size_of_int);
        }
    }
#endif
    if (car(e)==ADDRESS||car(e)==GVAR) {
        if (scalar(t)) {
            t = list2(POINTER,VOID); // fake
        } else if (t>0 && car(t)==ARRAY) {
        } else {
            error(TYERR);
        }
    }
    if (t==EMPTY) {
	/* empty space in partial initialization */
	return offset+cadr(e);
    }
    type = t;
    // e = rvalue_t(e,t);
    if (!scalar(type) && (type>0 && (car(type)!=ADDRESS && car(type)!=ARRAY)))
	e = correct_type(e,target_type);
    /* If this is a local declared constant, we don't have to assign.
       But some one may take it's address. We have to generate assign.
     */
    ass = assign_expr0(
	    offset?
		lp64?list3(LADD,var,llist2(LCONST,offset)):    // binop?
                     list3(ADD,var,list2(CONST,offset)):
		var,
	    e,target_type,t); // already correct_typed
    init_vars = list2(ass,init_vars);
    if (t>0&&car(t)==BIT_FIELD) {
        sz = 0; 
        bfd = cadr(caddr(t)); /* bit_field_disp */
#if BIT_FIELD_CODE
        code_bit_field_disp(t,&offset,&bfd,&sz);
#endif
        return offset+sz;
    }
    return offset+size(target_type);
}

static int pdecl_data(int var, int target_type, int init,int offset);

static void
pflush_decl_data(int var,int sz)
{
    int offset;
    int offset0=0;
    int e;
    int t,offset1=0;
    int smode=mode;
    int init = decl_str_init;

    decl_str_init = 0;
    mode = STADECL;
    /*
         decl_str_init
            output delayed decl data
         list4(offset,next,expression,list2(type0,type1));
     */
    while (init) {
        offset= car(init);
        e=caddr(init);
        t=car(cadddr(init));    // type of source
        type=cadr(cadddr(init)); // destination type
        if (offset!=offset0) {
            // make space
	    passign_data(var,EMPTY,list2(CONST,offset-offset0),EMPTY,offset0);
        }
	e = pexpr(e);
	// offset0 = passign_data(var,type,e,t,offset);
	offset0 = pdecl_data(var,type,e,offset); 
        init = cadr(init);
    }
    offset = offset0;
    if ((sz=(offset1+sz-offset))>0)
	passign_data(var,EMPTY,list2(CONST,sz),EMPTY,offset0);
    local_nptr = 0;
    mode=smode;
}

static int
str_init_eq()
{
    // error(-1);  // duplicate struct field value
    return 2;      // allow override keep unique
}


static int
pdecl_data_array(int var,int init,int target_type,int offset)
{
    int type0 = cadr(target_type);  /* array item type */
    int e;

    for(; init; init = cadr(init)) {
	// unordered data with tag or array offset
	if (car(init)!=DECL_DATA_ARRAY) {
		error(-1);
	}
	e = pexpr(caddr(init));
	offset = pdecl_data(var,type0,e,offset);
    }
    return offset;
}

#if 0
static void
pzfill(int var, int offset, int sz)
{
    int c0 = list2(CONST,0);
    int l0 = llist2(LCONST,0L);
    while(sz>0) {
       int t,c;
       switch(sz) {
       case 1: t = UCHAR; c = c0; break;
       case 2: t = USHORT; c = c0; break;
       case 4: t = UNSIGNED; c = c0; break;
       default: t = ULONGLONG; c = l0; break;
       }
       int ass = assign_expr0(
               offset?
                   lp64?list3(LADD,var,llist2(LCONST,offset)):    // binop?
                        list3(ADD,var,list2(CONST,offset)):
                   var,
               c ,t,t); // already correct_typed
       init_vars = list2(ass,init_vars);
       sz -= size(t);
       offset += size(t);
    }
}
#endif

static int
pdecl_data_field(int var,int init,int target_type,int offset)
{
    int type0 = target_type;  /* list of fields */
    int e,t,type1,foffset;
    NMTBL *n;

    for(; init; init = cadr(init)) {
	// unordered data with tag or array offset
	if (car(init)!=DECL_DATA_FIELD) {
		error(-1);
	}
	n = ncadddr(init);
	type1 = search_struct_type(type0,n->nm,&foffset);
	e = caddr(init);
	if (car(e)!=DECL_DATA) error(-1);
	t = caddr(e);
	decl_str_init=insert_ascend(decl_str_init,
	    glist4(offset+foffset,0,e,glist2(type1,t)),str_init_eq);
    }
    return offset;
}

static int
pdecl_data_list(int var,int init,int target_type,int offset)
{
    int type0 = caddr(target_type);  /* list of fields */
    int e;

    for(; init; init = cadr(init),type0 = cadr(type0)) {
	if (car(init)==DECL_DATA) {
	    // casted initilizer ?
	    //error(-1);
	    e = cadr(init); // value
	    if (!e) continue; // {...,} case
	    e = pexpr(e);
	    offset = pdecl_data(var,caddr(init),e,offset);
	    continue;
	}
	// ordered data
	if (car(init)!=DECL_DATA_LIST) {
		error(-1);
	}
	e = caddr(init);
	if (!e) continue; // {...,} case
	e = pexpr(e);
	offset = pdecl_data(var,car(type0),e,offset);
    }
    return offset;
}

static int 
pdecl_data(int var, int target_type, int init,int offset)
{
    int t,e;
    int save_decl_str_init = decl_str_init;
    decl_str_init = 0;
    target_type = type_value(target_type);

    if (init==0) {
	// empty declaration     
	//   it can happen like a = (struct hoge){};
	return offset;
    }
    e = cadr(init);
    if (car(init)==DECL_DATA) {
	switch(car(e)) {
	case DECL_DATA_LIST: 
	    offset = pdecl_data_list(var,e,target_type,offset);
	    break;
	case DECL_DATA_FIELD:
	    offset = pdecl_data_field(var,e,target_type,offset);
	    break;
	case DECL_DATA_ARRAY:
	    offset = pdecl_data_array(var,e,target_type,offset);
	    break;
	default:
	    type = caddr(init);
	    e = rvalue(e);
	    e = pexpr(e);
	    t = caddr(init);       // type of source
	    offset = passign_data(var,target_type,e,t,offset);
	}
    } else {
	error(-1);
    }
    if (decl_str_init) {
	int sz = size(target_type);
	pflush_decl_data(var,sz);
    }
    decl_str_init = save_decl_str_init;
    return offset;
}

// handle local variable declaration
//    initialization is accumrated in init argument
//    should consider  int k=some_compile_time_constant;
static int
p_decl(int e)
{
    // list4(ST_DECL,parse,(int)n,list3(mode,stmode,ctmode),init);
    int ctmode=caddr(e);
    NMTBL *n=ncaddddr(e);
    int dsp = n->dsp;
    int v=0;
    int sstmode = stmode;
    int smode = mode;
    int save_init_vars = init_vars;
    int init = cadddr(e); // variable initialization

    init_vars = 0;
    // in real partial evaluation, we have to check whether this variable
    // is used or not.
    if (ctmode) {
	mode = car(ctmode); stmode = cadr(ctmode); ctmode = caddr(ctmode);
    } else {
	mode = LDECL; stmode = 0; ctmode = 0;
    }
    switch(stmode) {
    case EXTRN: case EXTRN1: case STATIC:
	// def(n,ctmode);      we don't need this. already done.
	// stmode = sstmode;
	if (init) error(-1);
	stmode = sstmode;
	mode = smode;
	init_vars = save_init_vars;
	return pexpr(cadr(e));
#if 1
    case REGISTER:
	switch(n->ty) {
	case ULONGLONG: case LONGLONG:
	    v = get_lregister_var(n); break;
	case FLOAT: 
	    v = get_dregister_var(n,0); break;
	case DOUBLE: 
	    v = get_dregister_var(n,1); break;
	default:
	    if (scalar(n->ty)) 
		v = get_register_var(n);
	    else
		error(TYERR);
	}
	break;
#endif
//    case LLDECL:          LLDECL is mode, not stmode (bad design)
//	v = list2(FLABEL,fwdlabel()); break;
    default:
	if (mode==STADECL) {
	    if (init) {
		v = list3n(GVAR,0,n);
		gen_decl_data(init,v);
	    }
	    stmode = sstmode;
	    mode = smode;
	    init_vars = save_init_vars;
	    return pexpr(cadr(e));
	}
	if (n->sc==FLABEL)
	    v = list3n(LVAR,fwdlabel(),n);
	else {
	    NMTBL tn;
	    tn = *n;
	    code_lvar_alignment(disp,&tn,n->ty,size(n->ty));
	    v = list3n(LVAR,tn.dsp,n);
	}
    }
    if (n->sc!=FLABEL)
	inline_lvars = glist2(v,inline_lvars);
    if (heap[pdisp+dsp]) {
	fprintf(stderr,"## double p_decl %s %s\n",(ncaddr(heap[pdisp+dsp]))->nm,n->nm);
	error(-1);
    }
    heap[pdisp+dsp]=v;
    stmode = sstmode;
    mode = smode;
    if (init) {
	pdecl_data(v,n->ty,init,0);
	if (init_vars) {
	    int e1 = pexpr(cadr(e));
	    init_vars = reverse0(init_vars);
	    while (init_vars) {
		e1 = list3(ST_COMP,e1,car(init_vars));
		init_vars = cadr(init_vars);
	    }
	    init_vars = save_init_vars;
	    return e1;
	}
    }
    init_vars = save_init_vars;
    return pexpr(cadr(e));
}

static int
p_if(int e1)
{
    int cond,l1,l2;
    int e2=caddr(e1),e3;
    cond = pexpr(car(e2));
    // conv->if_then_();
    l1 = pexpr(cadr(e2));
    if ((e3=caddr(e2))) {  // else
	l2 = pexpr(e3);
    } else {
	l2 = 0;
    }
    return list3(ST_IF,pexpr(cadr(e1)),list3(cond,l1,l2));
}

static int
p_do(int e)
{
    int e2 = pexpr(caddr(e));
    int e3 = pexpr(cadddr(e));
    return list4(ST_DO,pexpr(cadr(e)),e2,e3);
}

static int
p_while(int e)
{
    int e2 = pexpr(caddr(e));
    int e3 = pexpr(cadddr(e));
    return list4(ST_WHILE,pexpr(cadr(e)),e2,e3);
}

static int
p_for(int e)
{
    int e1=caddr(e);
    int p0=pexpr(car(e1));
    int p1=pexpr(cadr(e1));
    int p2=pexpr(caddr(e1));
    int p3=pexpr(cadddr(e1));
    // unfolding for constant case?
    return list3(ST_FOR,pexpr(cadr(e)), list4(p0,p1,p2,p3));
}

static int
p_switch(int e)
{
    int e2 = pexpr(caddr(e));     // switch variable
    int e3 = pexpr(cadddr(e));    // a statement in switch 
    // if cadr(e) is a constant, we have to prune case statement.
    // No we cannot. A statement in case may contain labels or 
    // falling down entry.
    // case constants are need to be pexpred in p_case. 

    return list4(ST_SWITCH,pexpr(cadr(e)),e2,e3);
}

static int
p_comp(int e)
{
    int e1;
    e1=pexpr(caddr(e));
    return list3(ST_COMP,pexpr(cadr(e)),e1);
}

static int
p_break(int e)
{
    return list2(ST_BREAK,pexpr(cadr(e)));
}

static int
p_continue(int e)
{
    return list2(ST_CONTINUE,pexpr(cadr(e)));
}

static int
p_case(int e)
{
    int new=0,clist = caddr(e);
    // insert destory clist, we have to copy it now
    // car(clist) have to be expred, it may contain operators.
    for(;clist;clist=cadr(clist))
	// new=glist3(cexpr(pexpr(car(clist))),new,0);
	new=glist3(car(clist),new,0);
    return list3(ST_CASE,pexpr(cadr(e)),reverse0(new));
}

static int
p_default(int e)
{
    // should be removed if case value is a constant
    return list2(ST_DEFAULT,pexpr(cadr(e)));
}

static int
p_return(int e)
{
    int e1=pexpr(caddr(e));
    return list3(ST_RETURN,pexpr(cadr(e)),e1);
}

static int
p_goto(int e)
{
    int e1,lb,e2;
    if ((e1=caddr(e))) {
	switch(car(e1)) {
	case RINDIRECT: e1=pexpr(e1); break;
	case CODE: e2=pexpr(cadr(e1));
	    e1=list3(JUMP,e2,pexpr(caddr(e1))); break;
	case FLABEL: /* error(-1); */ break;
	case IVAR: 
	    lb = cadr(e1);
	    if (!(e1=heap[pdisp+lb])) {
		e1 = heap[pdisp+lb]=list2(FLABEL,fwdlabel());
	    }
	    break;
	case JUMP: 
	    e1 = list3(JUMP,pexpr(cadr(e1)),pexpr(caddr(e1)));
	    break;
	default:
	    error(-1);
	}
    }
    return list3(ST_GOTO,pexpr(cadr(e)),e1);
}

static int
p_list_expr(int e)
{
    int e3,new = 0;
    for (e3 = e; e3; e3 = cadr(e3)) {
	new= list2( pexpr(car(e3)), new);
    }
    return reverse0(new);
}

static int
p_asm(int e)
{
    int param=caddr(e);
    int e1 = p_list_expr(cadddr(e));
    return list4(ST_ASM,pexpr(cadr(e)),param,e1);
}

static int
p_label(int e)
{
    int e1,lb;
    if ((e1=caddr(e))) {
	switch(car(e1)) {
	case FLABEL: /* error(-1); */ break;
	case IVAR: 
	    lb = cadr(e1);
	    if (!(e1=heap[pdisp+lb])) {
		e1 = heap[pdisp+lb]=list2(FLABEL,fwdlabel());
	    }
	}
    }
    return list3(ST_LABEL,pexpr(cadr(e)),e1);
}

static int
p_label_var(int e)
{
    int d,e1;
    switch(car(e1=e)) {
    case LVAR: /* error(-1) */ break;
    case IVAR: 
	// should this done in p_decl? (in __label__?)
	d = cadr(e);
	if (!(e1=(heap[pdisp+d])))  {
	    // error(-1);
	    e1 = heap[pdisp+d]=list3(LVAR,fwdlabel(),caddr(e));
	}
    }
    return list2(LABEL,e1);
}

static int
p_bool(int e)
{
    error(-1);
    return e;
}

static int
p_comment(int e)
{
    glineno++;
    plineno = caddr(e);
    return list4n(ST_COMMENT,pexpr(cadr(e)),caddr(e),ncadddr(e));
}

static int
p_inline(int e)
{
    int e3;
    int narg;

    /* inline function arguments */
    narg = 0;
    for (e3 = caddr(e); e3; e3 = cadr(e3)) {
	narg=list3(pexpr(car(e3)),narg,caddr(e3));
    }
    return list4(INLINE,cadr(e),reverse0(narg),cadddr(e));
}

extern int
pexpr(int e1)
{
    int e2,e3;

    // if (inmode) error(-1);
    if (e1==0) return 0;
    e2 = cadr(e1);
    switch (car(e1)){
    case GVAR: case RGVAR: case CRGVAR: case CURGVAR: case SRGVAR:
    case URGVAR:
    case SURGVAR: case REGISTER:
    case DREGISTER: case FREGISTER:
    case FRGVAR: case  DRGVAR:
    case LREGISTER:
    case LRGVAR: case LURGVAR:
    case CONST: 
    case DCONST: case FCONST:
    case LCONST:
    case STRING:
    case STRINGS:
    case FNAME:
    case FLABEL:
    case BUILTIN_INF:
    case BUILTIN_INFF:
    case BUILTIN_INFL:
	return e1;
    case RSTRUCT: 
	// list3(RSTRUCT,e,size)
	return pexpr(e2);
    case LABEL: 
	return p_label_var(e2);
    case LVAR: case URLVAR:
    case RLVAR: case CRLVAR: case CURLVAR: case SRLVAR: case SURLVAR:
    case FRLVAR: case DRLVAR: 
    case LRLVAR: case LURLVAR: 
	return e1;
    case IVAR:
	return p_lvar(e1);
    case CODE:
    case FUNCTION:
	return pfunction(e1);
    case JUMP:
	e2 = pexpr(e2);
	return list3(car(e1),e2,pexpr(cadddr(e1)));
    case ARRAY:
	e2 = pexpr(e2);
	e1 =  binop(ADD,e2,pexpr(caddr(e1)),cadddr(e1),caddddr(e1));
	return indop(e1);
    case PERIOD:
	e2 = pexpr(e2);
        nptr = ncadddr(e1);
	type = caddr(e1);
        return strop(e2,0); 
    case ARROW:
	type = caddr(e1);
	e2 = rvalue(e2);
	e2 = pexpr(e2);
        nptr = ncadddr(e1);
	type = caddr(e1);
        return strop(e2,1); 
    case INLINE:
	return p_inline(e1);
    case INDIRECT:
	return pindirect(e1);
    case RINDIRECT:  case URINDIRECT:  
    case CRINDIRECT: case CURINDIRECT:
    case SRINDIRECT: case SURINDIRECT:
    case FRINDIRECT: case DRINDIRECT: 
    case LRINDIRECT: case LURINDIRECT:
	return prindirect(e1);
    case ADDRESS:
	return paddress(e1);
    case ST_OP:
	e3=caddr(e1);
	e1=pexpr(car(e3));
	return binop0(e2,e1,pexpr(cadr(e3)),caddr(e3),cadddr(e3));
    case MINUS:
	if ((e3 = pexpr(e2))==e2) return e1;
	if (car(e3)==CONST) return list2(CONST,-cadr(e3));
	return list2(car(e1),e3);
#if LONGLONG_CODE
    case LMINUS: 
	if ((e3 = pexpr(e2))==e2) return e1;
	if (car(e3)==LCONST) return llist2(LCONST,-lcadr(e3));
	return list2(car(e1),e3);
#endif
#if FLOAT_CODE
    case DMINUS: 
	if ((e3 = pexpr(e2))==e2) return e1;
	if (car(e3)==DCONST) return dlist2(DCONST,-dcadr(e3));
	if (car(e3)==FCONST) return dlist2(FCONST,-dcadr(e3));
	return list2(car(e1),e3);
    case FMINUS: 
	if ((e3 = pexpr(e2))==e2) return e1;
	if (car(e3)==DCONST) return dlist2(DCONST,-dcadr(e3));
	if (car(e3)==FCONST) return dlist2(FCONST,-dcadr(e3));
	return list2(car(e1),e3);
#endif
    case CONV: 
	return p_conv(caddr(e1),e2);
    case BNOT:   /* ~ */
	if ((e3 = pexpr(e2))==e2) return e1;
	if (car(e3)==CONST) return list2(CONST,~cadr(e3));
	return list2(BNOT,e3);
    case LNOT:   /* !  */
	if ((e3 = pexpr(e2))==e2) return e1;
	if (car(e3)==CONST) return list2(CONST,!cadr(e3));
	return list2(LNOT,e3);
    case PREINC:
    case UPREINC:
	if ((e3 = pexpr(e2))==e2) return e1;
	if (car(e3)==CONST) return list2(CONST,cadr(e3)+caddr(e1));
	return list4(car(e1),e3,caddr(e1),cadddr(e1));
    case POSTINC:
    case UPOSTINC:
	if ((e3 = pexpr(e2))==e2) return e1;
	if (car(e3)==CONST) return e3;
	return list4(car(e1),e3,caddr(e1),cadddr(e1));
#if FLOAT_CODE
    case DPREINC:   /* ++d */
	if ((e3 = pexpr(e2))==e2) return e1;
	if (car(e3)==FCONST) return dlist2(FCONST,dcadr(e3)+cadr(e2));
	if (car(e3)==DCONST) return dlist2(DCONST,dcadr(e3)+cadr(e2));
	return list4(car(e1),e3,caddr(e1),cadddr(e1));
    case DPOSTINC:  /* d++ */
	if ((e3 = pexpr(e2))==e2) return e1;
	if (car(e3)==FCONST||car(e3)==DCONST) return e3;
	return list4(car(e1),e3,caddr(e1),cadddr(e1));
    case FPREINC:   /* ++f */
	if ((e3 = pexpr(e2))==e2) return e1;
	if (car(e3)==FCONST) return dlist2(FCONST,dcadr(e3)+cadr(e2));
	if (car(e3)==DCONST) return dlist2(DCONST,dcadr(e3)+cadr(e2));
	return list4(car(e1),e3,caddr(e1),cadddr(e1));
    case FPOSTINC:  /* f++ */
	if ((e3 = pexpr(e2))==e2) return e1;
	if (car(e3)==FCONST||car(e3)==DCONST) return e3;
	return list4(car(e1),e3,caddr(e1),cadddr(e1));
#endif
#if LONGLONG_CODE
    case LPREINC:   /* ++d */
    case LUPREINC:   /* ++d */
	if ((e3 = pexpr(e2))==e2) return e1;
	if (car(e3)==LCONST) return llist2(LCONST,lcadr(e3)+cadr(e2));
	return list4(car(e1),e3,caddr(e1),cadddr(e1));
    case LPOSTINC:  /* d++ */
    case LUPOSTINC:  /* d++ */
	if ((e3 = pexpr(e2))==e2) return e1;
	if (car(e3)==LCONST) return e3;
	return list4(car(e1),e3,caddr(e1),cadddr(e1));
#endif
    case MUL: case UMUL:
    case DIV: case UDIV:	   
    case MOD: case UMOD:
    case LSHIFT: case ULSHIFT: case RSHIFT: case URSHIFT:
    case ADD: case SUB: case BAND: case EOR: case BOR: case CMP: case CMPGE:
    case UCMP: case CMPEQ: case CMPNEQ: case UCMPGE:
#if FLOAT_CODE
    case DMUL: case DDIV:
    case DADD: case DSUB:
    case DCMP: case DCMPGE: case DCMPEQ: case DCMPNEQ:
    case FMUL: case FDIV:
    case FADD: case FSUB:
    case FCMP: case FCMPGE: case FCMPEQ: case FCMPNEQ:
#endif
#if LONGLONG_CODE
    case LMUL: case LUMUL:
    case LDIV: case LUDIV:	   
    case LMOD: case LUMOD:
    case LLSHIFT: case LULSHIFT: case LRSHIFT: case LURSHIFT:
    case LADD: case LSUB: case LBAND: case LEOR: case LBOR: case LCMP:
#endif
	return pbinop(car(e1),e2,caddr(e1));
// relational operator
    case GT: case UGT: case GE: case UGE: case LT:
    case ULT: case LE: case ULE:
    case LOP+GT: case LOP+UGT: case LOP+GE: case LOP+UGE: case LOP+LT:
    case LOP+ULT: case LOP+LE: case LOP+ULE:
    case DOP+GT: case DOP+GE: case DOP+LT: case DOP+LE:
    case FOP+GT: case FOP+GE: case FOP+LT: case FOP+LE:
    case FOP+EQ: case FOP+NEQ:
    case EQ: case NEQ: case DOP+EQ: case DOP+NEQ:
    case LOP+EQ: case LOP+NEQ:
	return pbinop(car(e1),e2,caddr(e1));
    case LAND:
	return pland(car(e1),cadr(e1),caddr(e1));
    case LOR:
	return plor(car(e1),cadr(e1),caddr(e1));
    case LCOND: case DCOND: case FCOND: case COND: case UCOND: case LUCOND:
	e2 = pexpr(e2);
	if (car(e2)==CONST) return 
	    caddr(e1)? pexpr(cadr(e2)?caddr(e1):cadddr(e1)) :
		pexpr(cadr(e2)?e2:cadddr(e1)); // GNU extension h?:g
	e3=pexpr(caddr(e1));
	return list4(car(e1),e2,e3,pexpr(cadddr(e1)));
    case STASS: 
	return psassign(e1);
    case ASS: case CASS: case SASS:
	return passign(e1);
    case SASSOP: case SUASSOP:
    case ASSOP: case CASSOP: case CUASSOP:
	return passop(e1);
#if FLOAT_CODE
    case FASS: case DASS: 
	return pdassign(e1);
    case DASSOP: case FASSOP:
	return pdassop(e1);

    case BUILTIN_FABS:
    case BUILTIN_FABSF:
    case BUILTIN_FABSL:
	e2 = pexpr(e2);
	if (is_const(e2)) {
	    if (dcadr(e2) >= 0.0 )  return e2;
	    return  pexpr(list2(car(e1)==BUILTIN_FABSF?
		FMINUS:DMINUS,e2));
	}
	return list2(car(e1),e2);
#endif
#if LONGLONG_CODE
    case LASS: 
	return plassign(e1);
    case LASSOP: case LUASSOP:
	return plassop(e1);
#endif
    case ALLOCA:
	return palloc(e2);
    case BUILTINP:
	return list2(CONST,is_const(pexpr(e2)));
    case COMMA:
	return pcomma(e2,caddr(e1));
    case RETURN:
    case ENVIRONMENT:
    case LCALL:
	return e1;
#if BIT_FIELD_CODE
    case RBIT_FIELD:
	return prbit_field(e1);
    case BIT_FIELD:
        return list3(BIT_FIELD,pexpr(e2),caddr(e1));
    case BASS:
	return pbassign(e1);
    case BPREINC:
    case BPOSTINC:
    case BASSOP:
	return pbassop(e1);
#endif
#if ASM_CODE
    case ASM:
	return list3(ASM,list4(
	    car(e2),cadr(e2),caddr(e2),cadddr(e2)),
		caddr(e1));
#endif
    case CAST:
	if (e2==0) {
	    // casted empty structure  (struct hoge){}
	    // I think we can skip it. It means nothing in declaration
	    //        but it make zero fill, we have to handle it later
	    return 0;
	} else if (car(e2)==DECL_DATA) {
	    // casted initialized structure  (struct hoge){...}
	    return list3(DECL_DATA,pexpr(cadr(e2)),caddr(e2));
	}
	if (type_compatible(caddr(e1),cadddr(e1))) {
	    return pexpr(e2); // rvalue ?
	} else {
#if 1
	    e2 = pexpr(e2); // will override type
            type = cadddr(e1);
            return correct_type(e2,caddr(e1));
#else
	    e2 = pexpr(e2); // will override type
	    e2 =  correct_type(e2,caddr(e1));
	    type = caddr(e1);
	    return e2;
#endif
	    // return list4(CAST,pexpr(e2),caddr(e1),cadddr(e1));
	}
    case DECL_DATA:
	return list3(DECL_DATA,pexpr(e2),caddr(e1));
    case DECL_DATA_LIST:
    case DECL_DATA_ARRAY:
	return list4(car(e1),pexpr(e2),pexpr(caddr(e1)),cadddr(e1));
    case DECL_DATA_FIELD:
	return list4n(car(e1),pexpr(e2),pexpr(caddr(e1)),ncadddr(e1));
    case ST_DECL:         return p_decl(e1);
    case ST_IF:           return p_if(e1);
    case ST_DO:           return p_do(e1);
    case ST_WHILE:        return p_while(e1);
    case ST_FOR:          return p_for(e1);
    case ST_SWITCH:       return p_switch(e1);
    case ST_COMP:         return p_comp(e1);
    case ST_BREAK:        return p_break(e1);
    case ST_CONTINUE:     return p_continue(e1);
    case ST_CASE:         return p_case(e1);
    case ST_DEFAULT:      return p_default(e1);
    case ST_RETURN:       return p_return(e1);
    case ST_GOTO:         return p_goto(e1);
    case ST_ASM:          return p_asm(e1);
    case ST_LABEL:        return p_label(e1);
    case ST_COMMENT:      return p_comment(e1);
    default:
	error(-1);
	return p_bool(e1);
  }
  return VOID;
}

/*
     Prepare pvariable table
 */

static int
replace_inline_parameter(NMTBL *anptr,int t,int e4,int narg,int evals)
{
    int arg;
    if (has_attr(anptr,KONST) && !has_attr(anptr,HAS_ADDRESS)) {
	// replacable const variable 
	if (is_memory(e4)) {
	    heap[pdisp+narg]=reference(e4);
	    return evals;
	} else if (is_const(e4)) {
	    heap[pdisp+narg]=e4;
	    return evals;
	} 
    }
    // we need real local variable for this inline
    arg = heap[pdisp+narg]=list3n(LVAR,new_lvar(size(t)),anptr);
    inline_lvars = glist2(arg,inline_lvars);
    evals=list2(assign_expr0(arg,e4,anptr->ty,t),evals);
    return evals;
}

/*
    setup parameter replacement of inline call
 */

static void
enter_inline(NMTBL *n, int e,int toplevel)
{
    int e1 = attr_value(n,INLINE);
    int arg_disp = cadr(e1);  // number of arguments
    int narg,e3,e4, e5, fargtype;
    int evals = 0;
    int t, replace;
    NMTBL *anptr;

    fnptr = n;   // st_return see this
    pvartable = p_vartable(arg_disp,    /* number of arg */
			    caddr(e1)   /* number of local parameter  */);

    replace =  (is_code(fnptr)||toplevel);

    /* function arguments type */
    fargtype = n->dsp;   // we cannot do destruct reverse here

    /* inline function arguments */
    narg = 0;
    if (!fargtype) {
	goto no_args;  // wrong number of arguments
    }

    for (e3 = e5 = reverse0(caddr(e)); e3; e3 = cadr(e3)) {
	anptr = ncadddr(fargtype);
	if (!anptr) break; // should not happen?
        t=caddr(e3);  // type
	e4 = car(e3);
	if (replace) {
	    heap[pdisp+narg]=reference(e4);
	} else {
	    evals = replace_inline_parameter(anptr,t,e4,narg,evals);
	}
	narg ++;
	fargtype = cadr(fargtype);
    }
    caddr(e) = reverse0(e5);  // make it normal
    if (eval_order==NORMAL) evals = reverse0(evals);
    for(;evals;evals=cadr(evals)) {
	g_expr_u(car(evals));
    }
no_args:
    return;
}

/*
    pass local static variable list to our parents
    clean up and free used inline parameters
 */

static void
leave_inline(int e1,int toplevel)
{
    NMTBL *n;
    NMTBL *local_statics = ncadddr(e1);  // local static list

    if (retcont && !toplevel) error(STERR); 
    // inline can't handle return/environment except top level

    if (local_statics && local_statics != &null_nptr) {
	// append our local static variables to the parent list
	n = local_statics;
	while(n->next != &null_nptr) n=n->next;
	n->next = local_static_list; local_static_list = local_statics;
	ncadddr(e1) = 0;  // prevent duplicate initialize
    }

    // free used local variables or registers
    while(inline_lvars) {
	int e;
	int l = car(inline_lvars);
	switch(car(l)) {
	case LVAR: free_lvar(cadr(l)); break;
	case REGISTER: case DREGISTER: 
	case FREGISTER: case LREGISTER: 
	    free_register(cadr(l));
	}
	e = cadr(inline_lvars); 
	free_glist2(inline_lvars);
	inline_lvars = e; 
    }
}

extern int
gen_inline(int e, int toplevel)
{
    // these saved value should be some struct
    int sretlabel;
    NMTBL *sfnptr;
    int svartable;

    int scslabel = cslabel;
    int sdisp = pdisp;
    int sret_register;
    int sret_reg_mode;
    int sinline_lvars;
    int slfree;
    // int slreg_count=lreg_count;

    NMTBL *n = (NMTBL*)ncaddr(cadr(e));
    int e1 = attr_value(n,INLINE);
    int parse = car(e1);      // inline parse tree
    int dots;
    int ret_type = function_type(cadddr(e),&dots);

    checkret();
    checkjmp(-1);

    svartable = pvartable;

    scslabel = cslabel;
    sdisp = pdisp;
    sret_register = ret_register;
    sret_reg_mode = ret_reg_mode;
    sinline_lvars = inline_lvars;
    slfree=lfree;
    // int slreg_count=lreg_count;

    sretlabel = retlabel;
    sfnptr = fnptr;

    cslabel = -1;
    retpending = 0;
    inline_lvars = 0;
    if (!toplevel) {
	retlabel = fwdlabel();
    }

    enter_inline(n,e,toplevel);

    if (toplevel) {
	ret_register = code_set_return_register(0); // fnptr required
	ret_reg_mode = 1;
    } else {
	ret_register = 555;
	ret_reg_mode = 0;
    }

    // partial evaluation of parse tree
    //     constant propergation, etc.
    parse = pexpr(parse);
    pdisp = sdisp;
    pvartable = svartable;

    // generate code if necessary
    if (ret_type!=VOID)
	g_expr0(parse);
    else
	g_expr_u(parse);

    if (!toplevel) {
	checkret();
	fwddef(retlabel);
	control=1;
    }

    leave_inline(e1,toplevel);

    fnptr = sfnptr;
    retlabel = sretlabel;

    cslabel = scslabel;
    ret_register = sret_register;
    ret_reg_mode = sret_reg_mode;
    inline_lvars = sinline_lvars;
    lfree=slfree;

    return ret_type;
}

/* end */