view mc-code-ia32.c @ 418:c73f013d07d7 arm-complete

ARM complete. PowerPC, MIPS, IA32 checked. emit_copy register save.
author kono
date Mon, 25 Oct 2004 19:16:17 +0900
parents 98888da30b35
children efbd420386c5
line wrap: on
line source

/* Micro-C Code Generation Part for intel386 */
/* $Id$ */

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

char *l_include_path[] = {
    "/usr/include/",
    "/usr/include/linux/",
    "/usr/include/diet/",
    "/usr/lib/gcc-lib/i386-linux/2.95.4/include/",
    "/usr/lib/gcc-lib/i386-redhat-linux/2.96/include/",
    "/usr/lib/dietlibc/include/",
    0
};

int data_alignment = 0;

#define    SIZE_OF_INT  4
#define    SIZE_OF_SHORT  2
#define    SIZE_OF_FLOAT  4
#define    SIZE_OF_DOUBLE  8
#define    SIZE_OF_LONGLONG  8
#define    ENDIAN  0
#define    ENDIAN_L  0
#define    ENDIAN_D  0

#define SAVE_STACKS 1

#define TEXT_EMIT_MODE 0
#define DATA_EMIT_MODE 1
#define RODATA_EMIT_MODE 2

#define DOT_SIZE 1

static int output_mode = TEXT_EMIT_MODE;

static int creg;
static int lreg;

int code_lassop_p = 0;

static int MAX_REGISTER=6;            /* intel386のレジスタを6つまで使う*/
#define REAL_MAX_REGISTER 8    /* intel386のレジスタが8つということ*/
static int MAX_DATA_REG=4;    
static int MAX_POINTER=3;    
int MAX_REGISTER_VAR=2;    
// static int MAX_FREGISTER=1;

#define MAX_FPU_STACK 7
#define REG_VAR 3

// static int MAX_INPUT_REGISTER_VAR = 0;
static int MAX_CODE_INPUT_REGISTER_VAR = 2;
// static int MAX_INPUT_DREGISTER_VAR = 0;
// static int MAX_INPUT_FREGISTER_VAR = 0;
// static int MAX_CODE_INPUT_DREGISTER_VAR = 0;

static int  reg_sp;   /* REGister Stack-Pointer */
static int reg_stack[MAX_MAX];  /* 実際のレジスタの領域 */
static int stack_depth = 0;

/* floating point registers */

static int  freg_sp;  /* floating point REGister Stack-Pointer */
static int freg_stack[MAX_MAX]; /* 実際のレジスタの領域 */

static int reg_var;
static int regvar[2];


/*
                                           -28  -8 local2
                                           -24  -4 local1
                                           -20  8  arg3
                                           -16  4  arg2
                                           -12  0  arg1
         local2     -20 4                   -8    (%edi)
         local1 <-- -16 0 local variable    -4    (%esi)
        %edi        -12  <- disp_offset          %ebp
        %esi         -8
        %ebx         -4
        %ebp = %esp   0
        %eip          4   <- arg_offset
          arg1        8 0
          arg2       12 4
            see enter/enter1/leave           see code_enter
 */
static int arg_offset;
int disp_offset = -12;
#define func_disp_offset -12
#define code_disp_offset 0
// static int jump_offset = 0;

static int code_disp_label;
static int func_disp_label;


static int
lvar(int l)
{
    if (is_code(fnptr)) {
	return l+code_disp_offset;
    } else if (l<0) {
	return l+disp_offset;
    } else {
	return l+arg_offset;
    }
}

/*
    creg   current virtual register
    dreg   spare virtual register

    rname[creg]   current real register
    rname[dreg]   spare real register

    regs[]        virtual register usage
    regv[]        value in virtual register flag

    reg_name[rname[creg]]

    freg    current floating point register
    fregv   value in floating point register
 */

static int dreg; /* general temporal register */

#define REAL_MAX_LREGISTER 2
static int ia32regs[REAL_MAX_REGISTER+REAL_MAX_LREGISTER];
static int ia32regv[REAL_MAX_REGISTER+REAL_MAX_LREGISTER];
static int ia32rname[REAL_MAX_REGISTER+REAL_MAX_LREGISTER];

static int *regv  = ia32regv;
static int *regs  = ia32regs;
static int *rname = ia32rname;

static int ia32fregs[1];
static int ia32fregv[1];

static int freg;
static int *fregv = ia32fregv;
static int *fregs = ia32fregs;


#define REG_EAX   0
#define REG_EBX   1
#define REG_ECX   2
#define REG_EDX   3
#define REG_ESI   4
#define REG_EDI   5
#define REG_EBP   6
#define REG_ESP   7
#define is_int_reg(reg) (reg<REG_EBP)
#define REG_LCREG     8
#define REG_L     9


#define DATA_REG 0    
#define POINTER_REG 3    
static char *reg_name[8]; 
static char *reg_name_l[4];
static char *reg_name_w[4];

static void use_register(int virt, int real, int move);
static int virtual(int real);
static void shift(char *op, int reg,int creg);
static void ld_indexx(int byte, int n, int xreg,int reg,int sign);
static void data_mode(char *name);
static void text_mode();
static int edx_setup(int rreg);
static void edx_cleanup();
static void local_table(void);
#if FLOAT_CODE
static char * fload(int d);
static int code_d1(double d);
static int code_d2(double d);
static void code_save_fstacks();
#endif
static void code_save_stacks();
static void jcond(int l, char cond);
#if LONGLONG_CODE
static int code_l1(long long d);
static int code_l2(long long d);
#endif

#define use_int(reg)   if (reg==-1) reg=use_int0()
static int use_int0() { lreg = 0; if (!is_int_reg(creg)) { creg = virtual(REG_EBX); regs[creg]=1;} return creg; }

#define use_longlong(reg)   reg=use_longlong0(reg)
static int
use_longlong0(int reg)
{
    int i;
    if (reg==USE_CREG)
	reg = REG_LCREG;
    if (!lreg) {
	code_save_stacks();
	// make edx,eax free
	use_register(creg,REG_EBX,regv[creg]);
	use_register(dreg,REG_ECX,regv[dreg]);
	for(i=0;i<reg_var;i++)
	    use_register(regvar[i],REG_ESI+i,1);
    } 
    creg = lreg = reg;
    return lreg;
}

char *
l_edx(int i) {
    return i==REG_L?"%edi":"%edx";
}
char *
l_eax(int i) {
    return i==REG_L?"%esi":"%eax";
}

static
char *init_src0 = "\
#define va_list int\n\
#define va_start(ap,arg) ap=(((int)(&arg))+sizeof(arg))\n\
#define va_arg(ap,type)  (*((type *)ap)++)\n\
#define va_end\n\
#define __i386__ 1\n\
#define __LITTLE_ENDIAN__ 1\n\
#define __STDC__ 1\n\
#define size_t int\n\
#define __externsion__\n\
#define __restrict\n\
#define __gnuc_va_list int\n\
#define __flexarr\n\
#define __const const\n\
#define __THORW\n\
#define __attribute__(a)\n\
#define __inline__\n\
#define wchar_t int\n\
#define __GNUC__ 2\n\
";

extern void
code_init(void)
{

    /* called only once */

    init_src = init_src0;
    size_of_int = SIZE_OF_INT;
    size_of_short = SIZE_OF_SHORT;
    size_of_float = SIZE_OF_FLOAT;
    size_of_double = SIZE_OF_DOUBLE;
    size_of_longlong = SIZE_OF_LONGLONG;
    endian = ENDIAN;

    arg_offset = 8;
    // func_disp_offset = -12;
    disp_offset = -12;
    MAX_REGISTER=6;
    MAX_DATA_REG=4;    
    MAX_POINTER=3;    
    MAX_REGISTER_VAR=2;    

    reg_name[REG_EAX] = "%eax";
    reg_name[REG_EBX] = "%ebx";
    reg_name[REG_ECX] = "%ecx";
    reg_name[REG_EDX] = "%edx";
    reg_name[REG_ESI] = "%esi";
    reg_name[REG_EDI] = "%edi";
    reg_name[REG_EBP] = "%ebp";
    reg_name[REG_ESP] = "%esp";
    reg_name_l[REG_EAX] = "%al";
    reg_name_l[REG_EBX] = "%bl";
    reg_name_l[REG_ECX] = "%cl";
    reg_name_l[REG_EDX] = "%dl";
    reg_name_w[REG_EAX] = "%ax";
    reg_name_w[REG_EBX] = "%bx";
    reg_name_w[REG_ECX] = "%cx";
    reg_name_w[REG_EDX] = "%dx";

}

extern void
emit_reinit()
{
    /* called for each file */
}


char *
register_name(int i,int byte)
{
    if (i<0) {
	error(REG_ERR);
	return "%eax";
    }
    if (byte==1 && rname[i] <= REG_EDX) {
	return reg_name_l[rname[i]];
    } else if (byte==SIZE_OF_SHORT && rname[i] <= REG_EDX) {
	return reg_name_w[rname[i]];
    } else {
	return reg_name[rname[i]]; /* 0 or 4 means int */
    }
}

/*
int use_int(int i) { return i;}
int use_float(int i) { return i;}
int use_double(int i) { return i;}
int use_longlong(int i) { return i; }
 */


void
gexpr_code_init(void){
    use_register(creg,REG_EAX,0);
    regv[creg]=0;
    regv[dreg]=0;
}

void
code_gexpr(int e){
}

int 
get_register(void)
{    /* 使われていないレジスタを調べる */
    int i;
    for(i=0;i<MAX_REGISTER;i++) {
	if (! regs[i]) {    /* 使われていないなら */
	    regs[i]=1;      /* そのレジスタを使うことを宣言し */
	    return i;       /* その場所を表す番号を返す */
	}
    }
    return -1;    /* 空いている場所がないなら、それを表す -1 を返す */
}

void 
free_register(int i) {    /* いらなくなったレジスタを開放 */
    regv[i]=regs[i]=0;
    if(i==REAL_MAX_REGISTER) {
	regv[virtual(REG_ESI)]=regv[virtual(REG_EDI)]=0;
    }
}

extern void
use_ptr_cache(int r)
{
    error(-1);
}

extern void
code_ptr_cache_def(int r,NMTBL *nptr)
{
    error(-1);
}

int 
get_input_register_var(int i,NMTBL *nptr,int is_code)
{
    if (is_code) {
	if (i>=MAX_CODE_INPUT_REGISTER_VAR) return 0;
	i =  virtual(i+REG_ESI);
	regs[i]=regv[i]=INPUT_REG;
	return list3(REGISTER,i,(int)nptr);
    } else {
	return 0;
    }
}

int 
get_input_dregister_var(int i,NMTBL *nptr,int is_code,int d)
{
    return 0;
}

int 
get_input_lregister_var(int i,NMTBL *nptr,int is_code)
{
    int h,l;
    if (is_code) {
	if (i+1>=MAX_CODE_INPUT_REGISTER_VAR) return 0;
	h = virtual(REG_ESI);
	l = virtual(REG_EDI);
	regs[h]=regs[l]=INPUT_REG;
	regv[h]=regv[l]=INPUT_REG;
	return list2(LREGISTER,REG_L);
    }
    return 0;
}

int 
get_dregister(int d)
{
    return -1;
}

int 
get_lregister_var(NMTBL *n)
{
    int h,l;
    h = virtual(REG_ESI);
    l = virtual(REG_EDI);
    if (regv[REAL_MAX_REGISTER]==0&&regs[h]==0&&regs[l]==0) {
	regs[h]=regs[l]=REG_VAR;
	regv[h]=regv[l]=REG_VAR;
	regv[REAL_MAX_REGISTER]=1;
	reg_var=2; regvar[0]=h; regvar[1]=l;
	return list2(LREGISTER,REG_L);
    }
    return list2(LVAR,new_lvar(SIZE_OF_LONGLONG));
}

int 
get_lregister()
{
    return -1;
}


int
register_full(void)
{
    int i;
    for(i=0;i<MAX_REGISTER;i++) {
	if (! regs[i]) { 
	    return 0;  
	}
    }
    return 1;    
}

int
free_register_count(int d)
{
    int i,count;
    count = 0;
    for(i=0;i<MAX_REGISTER;i++) {
	if (! regs[i] && ! regv[i]) count++;
    }
    return d?0:count;    
}

void
free_all_register(void)
{
    int i;
    for(i=0;i<MAX_REGISTER+REAL_MAX_LREGISTER;i++) {
	regs[i]=regv[i]=0;
    }
    creg = get_register();
    dreg = get_register();
    reg_var = 0;
    return;
}

extern int
code_register_overlap(int s,int t)
{
    if (car(s)==REGISTER) {
	if (car(t)==REGISTER) return cadr(s)==cadr(t);
	if (car(t)==LREGISTER)
	    return cadr(s)==virtual(REG_ESI)|| cadr(s)==virtual(REG_EDI);
    } else if (car(s)==LREGISTER) {
	if (car(t)==LREGISTER) return 1;
	if (car(t)==REGISTER)
	    return cadr(t)==virtual(REG_ESI)|| cadr(t)==virtual(REG_EDI);
    }
    return 0;
}

void
register_usage(char *s)
{
    int i;
    if (chk) return;
    printf("# %d: %s:",lineno,s);
    printf(" creg=%s dreg=%s ",register_name(creg,0),register_name(dreg,0));
    for(i=0;i<MAX_REGISTER;i++) {
	printf("%d",regs[i]);
    }
    printf(":");
    for(i=0;i<MAX_REGISTER;i++) {
	printf("%d",regv[i]);
    }
#if 0
    printf(" regs_stack",register_name(creg,0),register_name(dreg,0));
    for(i=reg_sp;i>=0;i--) {
	if(reg_stack[i]>=0)
	    printf(" %s",register_name(reg_stack[i],0));
    }
#endif
    printf(" f:%d",freg_sp);
    printf("\n");
}

void
code_arg_register(NMTBL *fnptr)
{
    int args = fnptr->dsp;
    NMTBL *n;
    int reg_var = 0;
    int freg_var = 0;
    int type;
    int reg;
    int is_code0 = is_code(fnptr);

    while (args) {
        /* process in reverse order */
        n = (NMTBL*)caddr(args);
        type = n->ty;
printf("#  %s %d %d\n",n->nm,n->dsp,n->ty);
        if (scalar(type)) {
            if ((reg = get_input_register_var(reg_var,n,is_code0))) {
                n->sc = REGISTER;
                n->dsp = cadr(reg);
                regv[n->dsp]= 1;
                regs[n->dsp]= INPUT_REG;
                reg_var++;
                cadddr(args)=SIZE_OF_INT; /* why we need this? */
            }
        } else if (type==FLOAT||type==DOUBLE) {
            if ((reg = get_input_dregister_var(freg_var,n,is_code0,1))) {
                n->sc = DREGISTER;
                n->dsp = cadr(reg);
                fregv[n->dsp]= 1;
                fregs[n->dsp]= INPUT_REG;
                freg_var++;
                cadddr(args)=size(type); /* why we need this? */
            }
        }
        args = cadr(args);
    }
}

void 
gexpr_init(void)
{
    if (reg_sp>0) error(-1);
    if (freg_sp>0) error(-1);
    reg_sp = 0;
    freg_sp = 0;
    stack_depth = 0;
    text_mode();
    gexpr_code_init();
    register_usage("gexpr_init");
}


void 
emit_init(void)
{
    int i;
    for(i=0;i<MAX_REGISTER;i++) { regs[i]=0; regv[i]=0;rname[i]=i;}
    free_all_register();
    reg_sp = 0;
    freg_sp = 0;
}

int
virtual(int real)
{
    int real_v,i;
    real_v = -1;
    for(i=0;i<MAX_REGISTER;i++) {
	if (rname[i]==real) {
	    real_v=i;
	    break;
	}
    }
    return real_v;
}

int 
pop_register(void)
{     /* レジスタから値を取り出す */
    return reg_stack[--reg_sp];
}

void
emit_pop_free(int xreg)
{
    if (xreg==dreg||xreg==creg) {
	regv[dreg]=0;
    } else if (xreg>=0) {
	free_register(xreg);
    }
}


int
get_register_var(NMTBL *nptr)
{
    int i;
    for(i=REG_ESI;i<REG_EBP;i++) {
        if (! regs[i]) {    /* 使われていないなら */
            regs[i]=REG_VAR;      /* そのレジスタを使うことを宣言し */
            regv[i]=0;
	    regvar[reg_var++]=i;
            return list3(REGISTER,i,(int)nptr); /* その場所を表す番号を返す */
        }
    }
    return list2(LVAR,new_lvar(SIZE_OF_INT));
}

int
get_dregister_var(NMTBL *nptr,int d)
{
    return list2(LVAR,new_lvar(d?SIZE_OF_DOUBLE:SIZE_OF_FLOAT));
}

void 
use_register(int virt, int real, int move)
{
    int real_v;
    char *move_op;
    if (rname[virt]==real)
	return;
    real_v = virtual(real);
    move_op = regs[real_v]?"\txchg %s,%s\n":"\tmovl %s,%s\n";
    if (move || (regv[real_v])) {
	printf(move_op,reg_name[rname[virt]],reg_name[real]);
    } 
    rname[real_v] = rname[virt];
    rname[virt] = real; 
}

void 
use_pointer(int virt, int move)
{
    int i;
    if (rname[virt]>=POINTER_REG)
	return;
    for(i=POINTER_REG;i<MAX_REGISTER;i++) {
	if (!regs[virtual(i)]) {
	    use_register(virt,i,move);
	    return;
	}
    }
    /* we prefer EBX */
    use_register(virt,REG_EBX,move);
}

void 
use_data_reg(int virt, int move)
{
    int i;
    if (rname[virt]<MAX_DATA_REG)
	return;
    for(i=0;i<MAX_DATA_REG;i++) {
	if (!regs[virtual(i)]) {
	    use_register(virt,i,move);
	    return;
	}
    }
    /* we prefer EBX */
    use_register(virt,REG_EBX,move);
}


void 
emit_push()
{
    int new_reg;
    new_reg = get_register();
    if (new_reg==creg) error(-1);
    if(new_reg<0) {                     /* もうレジスタがない */
	if (reg_sp>=MAX_MAX) error(-1);
	reg_stack[reg_sp++] =  -1;
	printf("\tpushl %s\n",register_name(creg,0));
	stack_depth += SIZE_OF_INT;
	/* creg is used soon, don't regv[creg]=0 */
    } else {
	reg_stack[reg_sp++] = creg;     /* push するかわりにレジスタを使う */
	creg = new_reg;
	regv[creg]=1;
    }
}

int
emit_pop(int type)
{
    int xreg;
    if ((xreg=pop_register())==-1) {
	if (type==POINTER_REG)
	    use_pointer(dreg,0);
	else if (type==DATA_REG)
	    use_data_reg(dreg,0);
if (regv[dreg]) {
    printf("# emit_pop dreg conflict\n");
    error(-1);
}
	printf("\tpopl %s\n",register_name(dreg,0));
	regv[dreg]=1;
	stack_depth -= SIZE_OF_INT;
	return dreg;
    } else if (xreg<= -REG_LVAR_OFFSET) {
	code_rlvar(xreg+REG_LVAR_OFFSET,dreg);
	free_lvar(xreg+REG_LVAR_OFFSET);
	regv[dreg]=1;
	return dreg;
    }
    return xreg;
}

int
stack_top(int type)
{
    int xreg;
    if (type==INT) {
        xreg = reg_stack[reg_sp];
        if (xreg<= -REG_LVAR_OFFSET) {
            return list2(LVAR,REG_LVAR_OFFSET+xreg);
        } else {
            return list2(REGISTER,xreg);
        }
    } else {
        xreg = freg_stack[freg_sp];
        if (xreg<= -REG_LVAR_OFFSET) {
            return list2(LVAR,REG_LVAR_OFFSET+xreg);
        } else {
            return list2(DREGISTER,xreg);
        }
    }
    return xreg;
}



void 
code_label(int labelno)
{
    printf("_%d:\n",labelno);
}

void
code_gvar(int e1,int creg) {
    use_int(creg);
    if (cadr(e1)) {
	printf("\tmovl $%s+%d,%s\n",((NMTBL*)caddr(e1))->nm,cadr(e1),
		register_name(creg,0));
    } else {
	printf("\tmovl $%s,%s\n",((NMTBL*)caddr(e1))->nm,register_name(creg,0));
    }
    regv[creg]=1;
}


void
code_rgvar(int e1,int creg) {
    use_int(creg);
    if (cadr(e1)) {
	printf("\tmovl %s+%d,%s\n",((NMTBL*)caddr(e1))->nm,cadr(e1),
		register_name(creg,0));
    } else
	printf("\tmovl %s,%s\n",((NMTBL*)caddr(e1))->nm,register_name(creg,0));
    regv[creg]=1;
}

static char *cload(int sign,int sz) { return sz==1?(sign?"movsbl":"movzbl"):sz==SIZE_OF_SHORT?(sign?"movswl":"movzwl"):"movl"; }

void
code_crgvar(int e1,int creg,int sign,int sz){
    use_int(creg);
    if (cadr(e1)) {
	printf("\t%s %s+%d,%s\n",cload(sign,sz),
		((NMTBL*)caddr(e1))->nm,cadr(e1),register_name(creg,0));
    } else
	printf("\t%s %s,%s\n",cload(sign,sz),
		((NMTBL*)caddr(e1))->nm,register_name(creg,0));
    regv[creg]=1;
}


void
code_lvar(int e2,int creg) {
    use_int(creg);
    printf("\tlea %d(%%ebp),%s\n",lvar(e2),register_name(creg,0));
    regv[creg]=1;
}


void
code_register(int e2,int creg) {
    use_int(creg);
    printf("\tmovl %s,%s\n",register_name(e2,0),register_name(creg,0));
    regv[creg]=1;
}


void
code_rlvar(int e2,int reg) {
    use_int(reg);
    printf("\tmovl %d(%%ebp),%s\n",lvar(e2),register_name(reg,0));
    regv[creg]=1;
}

extern void
code_i2c(int reg)
{
    use_int(reg);
    use_data_reg(reg,1);
    printf("\t%s %s,%s\n",cload(1,1),
	register_name(reg,1),register_name(reg,0));
}

extern void
code_i2s(int reg)
{
    use_int(reg);
    use_data_reg(reg,1);
    printf("\t%s %s,%s\n",cload(1,SIZE_OF_SHORT),
	register_name(reg,2),register_name(reg,0));
}

extern void
code_u2uc(int reg)
{   
    use_int(reg);
    use_data_reg(reg,1);
    printf("\t%s %s,%s\n",cload(0,1),
	register_name(reg,1),register_name(reg,0));
}

extern void
code_u2us(int reg)
{   
    use_int(reg);
    use_data_reg(reg,1);
    printf("\t%s %s,%s\n",cload(0,SIZE_OF_SHORT),
	register_name(reg,2),register_name(reg,0));
}

void
code_crlvar(int e2,int reg,int sign,int sz) {
    use_int(reg);
    printf("\t%s %d(%%ebp),%s\n",cload(sign,sz),lvar(e2),register_name(reg,0));
    regv[creg]=1;
}


void
code_fname(NMTBL *n,int creg) {
    use_int(creg);
    printf("\tmovl $%s,%s\n",n->nm,register_name(creg,0));
    regv[creg]=1;
}

void
code_label_value(int label,int reg) {
    use_int(reg);
    printf("\tleal _%d,%s\n",label,register_name(reg,0));
    regv[creg]=1;
}

void
code_const(int e2,int creg) {
    use_int(creg);
    printf("\tmovl $%d,%s\n",e2,register_name(creg,0));
    regv[creg]=1;
}

void
code_neg(int creg) {
    use_int(creg);
    printf("\tnegl %s\n", register_name(creg,0));
}


void
code_not(int creg) {
    use_int(creg);
    printf("\tnotl %s\n", register_name(creg,0));
}


void
code_lnot(int creg) {
    char *xrn;
    use_int(creg);
    use_data_reg(creg,1);
    xrn = register_name(creg,1);
    printf("\tcmpl $0,%s\n", register_name(creg,0));
    printf("\tsete %s\n", xrn);
    printf("\tmovzbl %s,%s\n", xrn,register_name(creg,0));
}

void
code_preinc(int e1,int e2,int dir,int sign,int sz,int reg) {
    char *xrn;
    if (car(e2)==REGISTER) {
	use_int(reg);
	printf("\taddl $%d,%s\n",dir,register_name(cadr(e2),0));
	printf("\tmovl %s,%s\n",register_name(cadr(e2),0),register_name(reg,0));
	regv[reg]=1;
	return;
    } 
    g_expr(e2);
    xrn = register_name(creg,0);
    use_int(reg);
    printf("\t%s $%d,(%s)\n",(sz==1)?"addb":(sz==SIZE_OF_SHORT)?"addw":"addl",dir,xrn);
    printf("\t%s (%s),%s\n",cload(sign,sz),xrn,register_name(reg,0));
}


void
code_postinc(int e1,int e2,int dir,int sign,int sz,int reg) {
    char *xrn;
    if (car(e2)==REGISTER) {
	use_int(reg);
	printf("\tmovl %s,%s\n",register_name(cadr(e2),0),register_name(reg,0));
	printf("\taddl $%d,%s\n",dir,register_name(cadr(e2),0));
	regv[reg]=1;
	return;
    } 
    g_expr(e2);
    emit_push();  
    xrn = register_name((e2=emit_pop(0)),0);
    use_int(reg);
    printf("\t%s (%s),%s\n",cload(sign,sz),xrn,register_name(reg,0));
    printf("\t%s $%d,(%s)\n",(sz==1)?"addb":(sz==SIZE_OF_SHORT)?"addw":"addl",dir,xrn);
    emit_pop_free(e2);
}



void
code_return(int creg) {
    use_int(creg);
    printf("\tleal _%d,%s\n",retcont,register_name(creg,0));
    regv[creg]=1;
}


void
code_environment(int creg) {
    use_int(creg);
    printf("\tmovl %%ebp,%s\n",register_name(creg,0));
    regv[creg]=1;
}

static int rexpr_bool(int e1,int reg);
static int drexpr_bool(int e1,int reg);

void
code_bool(int e1,int reg) {
    char *xrn;
    int e2,e3;
    if (rexpr_bool(e1,reg)) return;
#if FLOAT_CODE
    if (drexpr_bool(e1,reg)) return;
#endif
    b_expr(e1,1,e2=fwdlabel(),1);  /* including > < ... */
    if (use) {
	use_int(reg);
	xrn = register_name(reg,0);
	printf("\txorl %s,%s\n",xrn,xrn);
	jmp(e3=fwdlabel());
	fwddef(e2);
	printf("\tmovl $1,%s\n",xrn);
	fwddef(e3);
	regv[reg]=1;
    } else {
	fwddef(e2);
    }
}

char *
code_gt(int cond) {
    return (cond?"g":"le");
}

char *
code_ugt(int cond) {
    return (cond?"a":"be");
}

char *
code_ge(int cond) {
    return (cond?"ge":"l");
}

char *
code_uge(int cond) {
    return (cond?"ae":"b");
}

char *
code_eq(int cond) {
    return (cond?"e":"ne");
}

void
code_cmp_crgvar(int e1,int reg,int sz,int label,int cond) {
    use_int(reg);
    if (cadr(e1)) {
	if (sz==1)
	    printf("\tcmpb $0,%s+%d\n",((NMTBL*)caddr(e1))->nm,cadr(e1));
	else if (sz==SIZE_OF_SHORT)
	    printf("\tcmpw $0,%s+%d\n",((NMTBL*)caddr(e1))->nm,cadr(e1));
    } else {
	if (sz==1)
	    printf("\tcmpb $0,%s\n",((NMTBL*)caddr(e1))->nm);
	else if (sz==SIZE_OF_SHORT)
	    printf("\tcmpw $0,%s\n",((NMTBL*)caddr(e1))->nm);
    }
    jcond(label,cond);
}


void
code_cmp_crlvar(int e1,int reg,int sz,int label,int cond) {
    use_int(reg);
    if (sz==1)
	printf("\tcmpb $0,%d(%%ebp)\n",lvar(e1));
    else if (sz==SIZE_OF_SHORT)
	printf("\tcmpw $0,%d(%%ebp)\n",lvar(e1));
    jcond(label,cond);
}


void
code_cmp_rgvar(int e1,int reg,int label,int cond) {
    use_int(reg);
    if (cadr(e1))
	printf("\tcmpl $0,%s+%d\n",((NMTBL*)caddr(e1))->nm,cadr(e1));
    else
	printf("\tcmpl $0,%s\n",((NMTBL*)caddr(e1))->nm);
    jcond(label,cond);
}


void
code_cmp_rlvar(int e1,int reg,int label,int cond) {
    use_int(reg);
    printf("\tcmpl $0,%d(%%ebp)\n",lvar(e1));
    jcond(label,cond);
}


void
code_cmp_register(int e2,int label,int cond) {
    use_int(e2);
    printf("\tcmpl $0,%s\n",register_name(e2,0));
    jcond(label,cond);
}


void
code_string(int e1,int creg)
{
    char *s;
    int lb;

    use_int(creg);
    s=(char *)cadr(e1);
    lb = emit_string_label();
    ascii(s);
    if (output_mode==TEXT_EMIT_MODE) {
	printf(".text\n");
    } else {
	text_mode();
    }
    printf("\tlea _%d,%s\n",lb,register_name(creg,0));
}

#define MAX_COPY_LEN 20

void 
emit_copy(int from,int  to,int length,int offset,int value,int det)
{
    /* length <0 means upward direction copy */
    use_int(from);
    use_int(to);
    switch (length) {
    case 0:	break;
    case 1: case -1:
	printf("\tmovb %d(%s),%s\n",offset,
	    register_name(from,0), reg_name_l[rname[dreg]] );
	printf("\tmovb %s,%d(%s)\n",reg_name_l[rname[dreg]] ,offset,
	    register_name(to,0));
	break;
    case 2: case -2:
	printf("\tmovw %d(%s),%s\n",offset,
	    register_name(from,0), reg_name_w[rname[dreg]] );
	printf("\tmovw %s,%d(%s)\n",reg_name_w[rname[dreg]] ,offset,
	    register_name(to,0));
	break;
    case 4: case -4:
	printf("\tmovl %d(%s),%s\n",offset,
	    register_name(from,0), register_name(dreg,0));
	printf("\tmovl %s,%d(%s)\n",register_name(dreg,0), offset,
	    register_name(to,0));
	break;
    default:
        if (length <0) {
            if (length > -MAX_COPY_LEN) {
                for(;length<=-4;length+=4,offset-=4)
                    emit_copy(from,to,-4,offset-4,0,det);
                for(;length<=-2;length+=2,offset-=2)
                    emit_copy(from,to,-2,offset-2,0,det);
                if(length<0)
                    emit_copy(from,to,length,offset-1,0,det);
                break;
            }
        } else if (length <=MAX_COPY_LEN) {
            for(;length>=4;length-=4,offset+=4)
                emit_copy(from,to,4,offset,0,det);
            for(;length>=2;length-=2,offset+=2)
                emit_copy(from,to,2,offset,0,det);
            if(length>0)
                emit_copy(from,to,length,offset,0,det);
            break;
        }
	printf("\tpushl %%esi\n");
	printf("\tpushl %%edi\n");
	printf("\tpushl %%ecx\n");
	printf("\tpushl %s\n",register_name(from,0));
	printf("\tpushl %s\n",register_name(to,0));
	printf("\tpushl %s\n",register_name(dreg,0));
	printf("\tpopl %%ecx\n");
	printf("\tpopl %%edi\n");
	printf("\tpopl %%esi\n");
	if (length<0) {
	    printf("\tmovl $%d,%%ecx\n",-length/4);
	    printf("\taddl $%d,%%esi\n",-length-4);
	    printf("\taddl $%d,%%edi\n",-length-4);
	    printf("\tstd\n\trep\n\tmovsl\n");
	    printf("\tpopl %%ecx\n");
	    printf("\tpopl %%edi\n");
	    printf("\tpopl %%esi\n");
	    if(length%4) {
		emit_copy(from,to,length,offset+length/SIZE_OF_INT,0,det);
	    }
	} else {
	    printf("\tmovl $%d,%%ecx\n",length/4);
	    printf("\tcld\n\trep\n\tmovsl\n");
	    printf("\tpopl %%ecx\n");
	    printf("\tpopl %%edi\n");
	    printf("\tpopl %%esi\n");
	    if(length%4) {
		emit_copy(from,to,length,offset+length/SIZE_OF_INT,0,det);
	    }
	}
    }
    if (value) {
    /* creg must point top of the destination data */
    /* this code is necessary for the value of assignment or function call */
    /* otherwise we don't need this */
	if(creg!=to) {
	    if (to==dreg) 
		printf("\tmovl %s,%s\n",register_name(to,0),register_name(creg,0));
	    else {
		free_register(creg); creg=to;
	    }
	}
    }
    regv[from]=regv[to]=regv[dreg]=0;
    regv[creg]=1;
}

int
struct_push(int e4,int t) 
{
    int length,xreg,save,lreg,count;
    g_expr(e4);
    length=size(t); 
    if(length%SIZE_OF_INT) {
	length += SIZE_OF_INT - (length%SIZE_OF_INT);
    }
    for(count=0;length<MAX_COPY_LEN;count++,length-=SIZE_OF_INT) {
	if (length==0) return count;
	else {
	    printf("\tpushl %d(%s)\n",
		length-SIZE_OF_INT,register_name(creg,0));
	}
    }
    printf("\tsubl $%d,%%esp\n",length);
    if (register_full()) {
	save = 1;
	for(lreg=0;lreg==creg||lreg==dreg;lreg++);
	printf("\tpushl %s\n",register_name(lreg,0));
	xreg = lreg; regv[xreg]=0;
    } else {
	save=0;
	xreg = get_register();
    }
    if (save) 
	printf("\tlea %d(%%esp),%s\n",SIZE_OF_INT,register_name(xreg,0));
    else
	printf("\tmovl %%esp,%s\n",register_name(xreg,0));
    regv[xreg]=1;
    /* downward direction copy */
    emit_copy(creg,xreg,length,0,0,1);
    /* we have value in creg, it may be changed */
    if (save) {
	if(creg==xreg) {
	    creg = get_register();   /* creg is freed in emit_copy */
	}
	printf("\tpopl %s\n",register_name(xreg,0));
	regv[xreg]=1;
    } else
	free_register(xreg);
    stack_depth += length*SIZE_OF_INT;
    return length/SIZE_OF_INT;
}

int
function(int e1)
{
    int e2,e3,e4,nargs,t,ret_type;
    NMTBL *n=0;
    int save,saved;
    int stack_depth_save = stack_depth;

    ret_type = cadr(cadddr(e1));
    if (ret_type==CHAR) ret_type=INT;

#ifdef SAVE_STACKS
    code_save_stacks();
#if FLOAT_CODE
    code_save_fstacks();
#endif
#endif
    if (free_register_count(0)<1) {
        for(save = 0;save==dreg||save==creg;save++);
	printf("\tpushl %s\n",register_name(save,0));
        saved = 1;
    } else {
        save = get_register();
        saved = 0;
    }
    regv[save]=0;
    e2 = cadr(e1);
    nargs = 0;
    for (e3 = caddr(e1); e3; e3 = cadr(e3)) {	
	t=caddr(e3);
	e4 = car(e3);
	if(scalar(t)) {
	    if (car(e4)==REGISTER) {
		printf("\tpushl %s\n",register_name(cadr(e4),0));
	    } else {
		g_expr(e4);
		printf("\tpushl %s\n",register_name(creg,0));
	    }
	    stack_depth += SIZE_OF_INT;
	} else if (t==LONGLONG||t==ULONGLONG) {
	    if (car(e4)==LREGISTER) {
		printf("\tpushl %s\n\tpushl %s\n",l_edx(cadr(e4)),l_eax(cadr(e4)));
	    } else {
		g_expr(e4);
		printf("\tpushl %%edx\n\tpushl %%eax\n");
	    }
	    ++nargs;
	    stack_depth += SIZE_OF_INT*2;
	} else if (t==DOUBLE) {
	    g_expr(e4);
	    printf("\tleal\t-8(%%esp),%%esp\n\tfstpl\t(%%esp)\n");
	    nargs += SIZE_OF_DOUBLE/SIZE_OF_INT;
	    fregv[freg]=0;
	    stack_depth += SIZE_OF_DOUBLE;
	    continue;
	} else if (t==FLOAT) {
	    g_expr(e4);
	    printf("\tleal\t-4(%%esp),%%esp\n\tfstps\t(%%esp)\n");
	    nargs += SIZE_OF_FLOAT/SIZE_OF_INT;
	    fregv[freg]=0;
	    stack_depth += SIZE_OF_FLOAT;
	    continue;
	} else if (car(t)==STRUCT||car(t)==UNION) {
	    nargs += struct_push(e4,t);
	    continue;
	} else {
	    error(TYERR);
	}
	++nargs;
    }
    if (car(e2) == FNAME) {	
	n=(NMTBL *)cadr(e2);
	regv[creg]=0;
	use_register(creg,REG_EAX,0);  /* will be destroyed */
    } else {	
	g_expr(e2);
	regv[creg]=1;
	use_register(creg,REG_EAX,1);  /* will be destroyed */
    }

    /* we don't have to save creg nor dreg */
    regs[creg]=0; regs[dreg]=0;
    regv[dreg]= regv[save]= 0;
    use_register(dreg,REG_EBX,0);  /* will be destroyed */
    use_register(save,REG_ECX,0);  /* will be destroyed */
    regs[creg]=1; regs[dreg]=1;

    if (car(e2) == FNAME) {	
	printf("\tcall\t%s\n",n->nm);
    } else {
	printf("\tcall\t*%s\n",register_name(creg,0));
    }
    if (nargs) printf("\taddl $%d,%%esp\n",SIZE_OF_INT*nargs);
    if (saved) {
	printf("\tpopl %s\n",register_name(save,0));
    } else {
        free_register(save);
    }
    regv[save]=0;
    if (ret_type==DOUBLE||ret_type==FLOAT) {
    } else if (ret_type==LONGLONG||ret_type==ULONGLONG) {
	use_longlong0(USE_CREG);
	regv[creg]=1;
    } else if (ret_type==VOID) {
	regv[freg]=0; regv[creg]=0;
    } else {
	if (!is_int_reg(creg)) {
	    lreg=0; creg=virtual(REG_EAX);
	}
	use_register(creg,REG_EAX,0);
	fregv[freg]=0; regv[creg]=1;
    }
    stack_depth = stack_depth_save;
    return ret_type;
}

void
code_alloca(int e1,int reg)
{
    char *crn,*drn;
    int edx;
  
    g_expr(list3(BAND,list3(ADD,e1,list2(CONST,15)),list2(CONST,~15))); 
    use_int(reg);
    if (stack_depth>0) {
	edx = edx_setup(-1);
	crn = register_name(reg,0);
	drn = register_name(edx,0);
	printf("\tmovl %%esp,%s\n",drn);
	printf("\tsubl %s,%%esp\n",crn);
	printf("\tmovl %%esp,%s\n",crn);
	emit_copy(edx,creg,stack_depth,0,1,1);
	printf("\taddl $%d,%s\n",stack_depth,register_name(creg,0));
	edx_cleanup();
    } else {
	crn = register_name(reg,0);
	printf("\tsubl %s,%%esp\n",crn);
	printf("\tmovl %%esp,%s\n",crn);
    }
}

void
code_frame_pointer(int e3) {
    use_int(e3);
    printf("\tmovl %s,%%ebp\n",register_name(e3,0));
}


void
code_fix_frame_pointer(int disp_offset) {
    printf("\tlea %d(%%ebp),%%ebp\n",disp_offset);
}


void
code_jmp(char *s) {
    printf("\tjmp %s\n",s);
}


void
code_indirect_jmp(int e2) {
    use_int(e2);
    printf("\tjmp *%s\n",register_name(e2,0));
}

void
code_rindirect(int e1, int reg,int offset, int sign,int byte)
{
    char *crn,*op;
    g_expr(e1);
    op=cload(sign,byte);
    crn = register_name(creg,0);
    use_int(reg);
    printf("\t%s %d(%s),%s\n",op,offset,crn,register_name(reg,0));
}

#if FLOAT_CODE
int
code_drindirect(int e1, int reg,int offset, int d)
{
    g_expr(e1);
    printf("\t%s (%s)\n",fload(d),register_name(creg,0));
    return DOUBLE;
}
#endif

#if LONGLONG_CODE

static void
lload(int creg,int offset,int reg)
{
    char *crn = register_name(creg,0);
    use_longlong(reg);
    if((reg==REG_L&&rname[creg]==REG_ESI)||(rname[creg]==REG_EAX)) {
	printf("\tmovl %d(%s),%s\n",offset+SIZE_OF_INT,crn,l_edx(reg));
	printf("\tmovl %d(%s),%s\n",offset,crn,l_eax(reg));
    } else {
	printf("\tmovl %d(%s),%s\n",offset,crn,l_eax(reg));
	printf("\tmovl %d(%s),%s\n",offset+SIZE_OF_INT,crn,l_edx(reg));
    }
}

int
code_lrindirect(int e1, int reg, int offset, int us)
{
    int reg0;
    g_expr(e1);
    regv[reg0=creg]=1;
    use_longlong(reg);
    lload(reg0,offset,reg);
    return LONGLONG;
}
#endif

char *
move(int byte)
{
    return byte==1?"movb":byte==SIZE_OF_SHORT?"movw":"movl";
}

void
code_assign_gvar(int e2,int creg,int byte) {
    use_int(creg);
    if (byte) use_data_reg(creg,1);
    if (cadr(e2)) 
	printf("\t%s %s,%s+%d\n",move(byte),register_name(creg,byte),((NMTBL*)caddr(e2))->nm,cadr(e2));
    else
	printf("\t%s %s,%s\n",move(byte),register_name(creg,byte),((NMTBL*)caddr(e2))->nm);
}

void
code_assign_lvar(int e2,int creg,int byte) {
    use_int(creg);
    if (byte) use_data_reg(creg,1);
    printf("\t%s %s,%d(%%ebp)\n",move(byte),register_name(creg,byte),lvar(e2));
}

void
code_assign_register(int e2,int byte,int creg) {
    use_int(creg);
    if (creg!=e2)
	printf("\tmovl %s,%s\n",register_name(creg,0),register_name(e2,0));
}

void
code_assign(int e2,int byte,int creg) {
    use_int(e2);
    use_int(creg);
    if (byte) use_data_reg(creg,1);
    printf("\t%s %s,(%s)\n",move(byte),register_name(creg,byte),register_name(e2,0));
    regv[creg]=1;
}

void
code_register_assop(int e2,int reg0,int op,int byte) {
    int reg;
    int xreg = creg;
    creg = reg = e2;
    tosop(op,reg,xreg);
    creg = xreg;
    // printf("\tmovl %s,%s\n",register_name(reg,0),register_name(creg,0));
    regs[creg]=regv[creg]=1;
}


void
code_assop(int op,int reg,int byte,int sign) {
    char *xrn;
    int xreg;
    int edx = edx_setup(-1);
    use_int(reg);
    xrn = register_name(xreg = emit_pop(0),0);       /* pop e3 value */
    regv[xreg]=regs[xreg]=1;
    printf("\tmovl %s,%s  # assop \n",register_name(reg,0),register_name(edx,0));
    regv[edx]=1;
    ld_indexx(byte,0,edx,reg,sign);
    tosop(op,reg,xreg);
    printf("\t%s %s,(%s)\n",move(byte),register_name(reg,byte),register_name(edx,0));
    edx_cleanup();
    emit_pop_free(xreg);
    regv[creg]=1;
}


void
tosop(int op,int reg,int oreg)
{
    int dx;
    char *orn,*crn;
    use_int(reg);

    switch(op) {
    case LSHIFT:
    case ULSHIFT:
	shift("sall",oreg,reg);
	regv[creg]=1;
	return;
    case RSHIFT:
	shift("sarl",oreg,reg);
	regv[creg]=1;
	return;
    case URSHIFT:
	shift("shrl",oreg,reg);
	regv[creg]=1;
	return;
    }
    if(oreg==-1) {
        printf("\tpopl %s\n",register_name(dreg,0));
	oreg = dreg;
	regv[dreg]=1;
    } else if (oreg<= -REG_LVAR_OFFSET) {
	code_rlvar(oreg+REG_LVAR_OFFSET,dreg);
	free_lvar(oreg+REG_LVAR_OFFSET);
	oreg = dreg;
	regv[dreg]=1;
    }
    regv[oreg]=1; regs[oreg]=1;
    orn = register_name(oreg,0);
    crn = register_name(creg,0);
    switch(op) {
    case ADD:
	printf("\taddl %s,%s\n",orn,crn);
	break;
    case SUB: case CMP:
	printf("\tsubl %s,%s\n",orn,crn);
	break;
    case BAND: 
	printf("\tandl %s,%s\n",orn,crn);
	break;
    case EOR: 
	printf("\txorl %s,%s\n",orn,crn);
	break;
    case BOR:
	printf("\torl %s,%s\n",orn,crn);
	break;
    case MUL:
    case UMUL:
	printf("\t%s %s,%s\n","imull",orn,crn);
	break;
    case DIV:
    case UDIV:
	use_register(creg,REG_EAX,1);
	edx_setup(REG_EDX);
	orn = register_name(oreg,0);
	if (op==DIV)
	    printf("\tcltd\n\tidivl %s\n",orn);
	else 
	    printf("\txor %%edx,%%edx\n\tdivl %s\n",orn);
	edx_cleanup();
	break;
    case MOD:
    case UMOD:
	use_register(creg,REG_EAX,1);
	edx_setup(REG_EDX);
	orn = register_name(oreg,0);
	if (op==MOD)
	    printf("\tcltd\n\tidivl %s\n",orn);
	else 
	    printf("\txor %%edx,%%edx\n\tdivl %s\n",orn);
        dx = virtual(REG_EDX);	
	if (dx!=creg) {
	    rname[dx]=rname[creg];
	    rname[creg]=REG_EDX;
	}
	edx_cleanup();
	break;
    }
    if (oreg!=dreg&&oreg!=creg&&oreg>=0)
	free_register(oreg);
    else if (oreg==dreg) regv[dreg]=0;
    regv[creg]=1;
}

int
code_const_op_p(int op,int e)
{
    if (car(e)!=CONST) return 0;
    if (op==DIV||op==UDIV) return ilog(cadr(e));
    if (op==MOD||op==UMOD) return 0;
    else return 1;
}

void
oprtc(int op,int reg,int orn)
{
    char *crn;
    int datareg;
    use_int(reg);
    crn = register_name(reg,0);
    orn = cadr(orn);
    datareg=(rname[reg]<MAX_DATA_REG);

    switch(op) {
    case LSHIFT:
    case ULSHIFT:
	printf("\tsall $%d,%s\n",orn,crn);
	return;
    case DIV:
	orn = ilog(orn);
    case RSHIFT:
	printf("\tsarl $%d,%s\n",orn,crn);
	return;
    case UDIV:
	orn = ilog(orn);
    case URSHIFT:
	printf("\tshrl $%d,%s\n",orn,crn);
	return;
    case ADD:
	printf("\taddl $%d,%s\n",orn,crn);
	break;
    case SUB: case CMP:
	printf("\tsubl $%d,%s\n",orn,crn);
	break;
    case BAND: 
	if (datareg&&(orn & ~255)==~255)
	    printf("\tandb $%d,%s\n",orn,register_name(reg,1));
	else if (datareg&&(orn & ~65535)==~65535)
	    printf("\tandw $%d,%s\n",orn,register_name(reg,2));
	else
	    printf("\tandl $%d,%s\n",orn,crn);
	break;
    case EOR: 
	printf("\txorl $%d,%s\n",orn,crn);
	break;
    case BOR:
	if (datareg&&(orn & ~255)==0)
	    printf("\tor $%d,%s\n",orn,register_name(reg,1));
	else if (datareg&&(orn & ~65535)==0)
	    printf("\tor $%d,%s\n",orn,register_name(reg,2));
	else
	    printf("\torl $%d,%s\n",orn,crn);
	break;
    case MUL:
    case UMUL:
	if (ilog(orn)) {
	    printf("\tsall $%d,%s\n",ilog(orn),crn);
	} else
	    printf("\t%s $%d,%s\n","imull",orn,crn);
	break;
    default:
	error(-1);
    }
}


static int edx_stack=0;

int
edx_setup(int rreg)
{
    int edx_save;
    /* make real EDX (or rreg) register empty */
    if (free_register_count(0)<1) {
        for(edx_save = 0;edx_save==dreg||edx_save==creg;edx_save++);
	printf("\tpushl %s\n",register_name(edx_save,0));
        edx_stack = list3(edx_save,edx_stack,0);
    } else {
        edx_save = get_register();
        edx_stack = list3(edx_save,edx_stack,1);
    }
    regv[edx_save]=0;
    if (rreg!=-1)
	use_register(edx_save,rreg,0);
    return edx_save;
}


void
edx_cleanup()
{
    if (caddr(edx_stack)==0) {
	printf("\tpopl %s\n",register_name(car(edx_stack),0));
    } else
	free_register(car(edx_stack));
    edx_stack = cadr(edx_stack);
}

void
shift(char *op, int reg,int creg)
{
    use_int(creg);
    if (reg>=0) {
	use_register(reg,REG_ECX,1);
    } else if (reg<= -REG_LVAR_OFFSET) {
	use_register(dreg,REG_ECX,0);
        code_rlvar(reg+REG_LVAR_OFFSET,dreg);
        reg = dreg;
        regv[dreg]=0;
    } else {
	use_register(dreg,REG_ECX,0);
	printf("\tpopl %%ecx\n");
        regv[dreg]=0;
    }
    printf("\t%s %%cl,%s\n",op,register_name(creg,0));
}

void
ld_indexx(int byte, int n, int xreg,int reg,int sign)
{	
    use_int(reg);
    if (n) 
	    printf("\t%s %d(%s),%s\n",cload(sign,byte),n,
		register_name(xreg,0),register_name(reg,0));
    else
	    printf("\t%s (%s),%s\n",cload(sign,byte),
		register_name(xreg,0),register_name(reg,0));
}

int
code_csvalue()
{
    return rname[creg]; /* for switch value */
}

void
code_cmpdimm(int e, int csreg,int label,int cond)
{
    /* used in dosiwtch() */
    if(chk) return;
    use_register(creg,csreg,0);
    printf("\tcmpl $%d,%s\n",e,register_name(creg,0));
    jcond(label,cond);
}

void
code_opening(char *filename)
{
    printf("\t.file \"%s\"\n",filename);
    printf("\t.version\t\"01.01\"\n");
    /* printf("gcc2_compiled.:\n"); */
    printf(".text\n");
}

void
code_closing()
{
    global_table();
    printf("\t.ident \"Micro-C compiled\"\n");
}

static char *
code_cond(int op,int cond)
{
    switch(op) {
    case GT:  return code_gt(cond);
    case UGT: return code_ugt(cond);
    case GE:  return code_ge(cond);
    case UGE: return code_uge(cond);
    case LT:  return code_ge(!cond);
    case ULT: return code_uge(!cond);
    case LE:  return code_gt(!cond);
    case ULE: return code_ugt(!cond);
    case EQ:  return code_eq(cond);
    case NEQ: return code_eq(!cond);
    default: return 0;
    }
}

static int
rexpr_bool(int e1,int reg)
{
    char *s;
    if (!(s=code_cond(car(e1),1))) return 0;
    g_expr(list3(CMP,cadr(e1),caddr(e1)));
    use_int(reg);
    use_data_reg(reg,0);
    printf("\tset%s\t%s\n",s,register_name(reg,1));
    printf("\tmovzbl %s,%s\n",register_name(reg,1),register_name(reg,0));
    return 1;
}

void
rexpr(int e1, int l1, int cond,int t)
{
    g_expr(list3(CMP,cadr(e1),caddr(e1)));
    printf("\tj%s\t_%d\n",code_cond(car(e1),cond),l1);
}


static void
jcond(int l, char cond)
{       
    if (chk) return;
    printf("\tj%s\t_%d\n",cond==LT?code_ge(0):cond?"ne":"e",l);
}

void
jmp(int l)
{       
    control=0;
    if (chk) return;
    printf("\tjmp\t_%d\n",l);
    /* align? */
    /*
      this is not allowed because of ? operator
    regv[creg]=regv[dreg]=0; 
    use_register(creg,REG_EAX,0);
    use_register(dreg,REG_EBX,0);
     */
}

void
gen_comment(char *s)
{
    if (chk) return;
    printf("## %s",s);
}


void
code_enter(char *name)
{
    text_mode();
    printf("\t.align 4\n");
    if (stmode!=STATIC)
	printf(".globl %s\n",name);
    printf("\t.type\t%s,@function\n",name);
    printf("%s:\n",name);
}


void
code_enter1(int args)
{
    code_disp_label=fwdlabel();
    printf("\tlea _%d(%%ebp),%%esp\n",code_disp_label);

    printf("## args %d disp %d  code_disp_offset=%d\n",args,disp,code_disp_offset); 
}

void
code_leave(char *name)
{
    disp &= -SIZE_OF_INT;
    printf("\t.set _%d,%d\n",code_disp_label,disp+code_disp_offset-8);
    printf("_%d:\n",labelno);
    printf("\t.size\t%s,_%d-%s\n",name,labelno,name);
    local_table();
    labelno++;
    free_all_register();
}

void
enter(char *name)
{
    text_mode();
    printf("\t.align 2\n");
    if (stmode!=STATIC)
	printf(".globl %s\n",name);
    printf("%s:\n",name);
    printf("\t.type\t%s,@function\n",name);
    printf("\tpushl %%ebp\n");
    printf("\tmovl %%esp,%%ebp\n");
    printf("\tpushl %%ebx\n");
    printf("\tpushl %%esi\n");
    printf("\tpushl %%edi\n");
}

void
enter1()
{
    text_mode();
    func_disp_label=fwdlabel();
    printf("\tlea _%d(%%ebp),%%esp\n",func_disp_label); 
    /* if(disp) printf("\tsubl $%d,%%esp\n",-disp); */
}

void
leave(int control, char *name)
{
    int sz;

    disp &= -SIZE_OF_INT;
    if (control)
        code_set_return_register(1);
    if (retcont) {
	if (control)
	    jmp(retlabel);
	fwddef(retcont);
        if (cadr(fnptr->ty)==FLOAT||cadr(fnptr->ty)==DOUBLE) {
	    printf("\tfldl %d(%%ebp)\n",-SIZE_OF_DOUBLE);
        } else if (cadr(fnptr->ty)>0&&(
            car(cadr(fnptr->ty))==STRUCT ||
            car(cadr(fnptr->ty))==UNION)) {
            sz = size(cadr(fnptr->ty));
            printf("\tlea %d(%%ebp),%s\n",-sz,register_name(dreg,0));
            printf("\tmovl %d(%%ebp),%s\n",disp-SIZE_OF_INT,
		register_name(creg,0));
            emit_copy(dreg,creg,sz,0,1,1);
        } else if (cadr(fnptr->ty)!=VOID) {
	    use_register(creg,REG_EAX,0);
	    printf("\tmovl %s,%s\n",reg_name[REG_ESI],register_name(creg,0));
        }
    }
    fwddef(retlabel);

    printf("\tlea %d(%%ebp),%%esp\n",disp_offset);
    printf("\tpopl %%edi\n");
    printf("\tpopl %%esi\n");
    printf("\tpopl %%ebx\n");
    printf("\tleave\n");
    printf("\tret\n");
    printf("\t.set _%d,%d\n",func_disp_label,disp+disp_offset);
    printf("_%d:\n",labelno);
    printf("\t.size\t%s,_%d-%s\n",name,labelno,name);
    local_table();
    labelno++;
    free_all_register();
}

int
code_get_fixed_creg(int reg,int type) {
    if (type==FLOAT||type==DOUBLE) {
	return 0;
    } else if (type==LONGLONG||type==ULONGLONG) {
	use_longlong(reg);
	return reg;
    } else {
	use_int(reg);
	return rname[reg];
    }
}

void
code_set_fixed_creg(int reg,int mode,int type) {
    if (type==FLOAT||type==DOUBLE) {
    } else if (type==LONGLONG||type==ULONGLONG) {
    } else {
	use_register(creg,reg,mode);
    }
}

void
code_set_return_register(int mode) {
    if (fnptr->ty==DOUBLE||fnptr->ty==FLOAT) {
    } else if (fnptr->ty==LONGLONG||fnptr->ty==ULONGLONG) {
	use_longlong0(USE_CREG);
    } else {
	use_register(creg,REG_EAX,mode);
    }
}

void
gen_gdecl(char *n, int gpc)
{
    /*
    if (stmode!=STATIC)
	printf(".globl %s\n",n); 
     */
}

extern void
ascii(char *s)
{
    printf("\t.string \"");
    while(*s) {
	if (*s=='\n')
	    printf("%cn",92);
	else if (*s<' ')
	    printf("%c%03o",92,*s);
	else if (*s==34)
	    printf("%c%c",92,34);
	else 
	    printf("%c",*s);
	s++;
    }
    printf("%c\n",34);
}

extern int
emit_string_label()
{
    int lb;
    printf(".section\t.rodata\n");
    lb=fwdlabel();
    printf("_%d:\n",lb);
    output_mode = RODATA_EMIT_MODE;
    return lb;
}

void
align(int t)
{
    int d;
    switch(t) {
    case CHAR: case UCHAR: return;
    case SHORT: case USHORT: d = data_alignment & 1; break;
    default: d = data_alignment & 3;
    }
    if (d) {
        printf("\t.align 2\n");
        data_alignment = 0;
    }
}

extern void
emit_global(char *name,int t)
{
    data_mode(name);
    printf(".globl\t%s\n",name);
    align(t);
    printf("%s:\n",name); 
}

extern void
emit_space(int sp)
{
    data_mode(0);
    printf("\t.space\t%d\n",sp);
}

extern void
emit_char(int d)
{
    data_mode(0);
    printf("\t.byte %d\n",d);
}

extern void
emit_short(int d)
{
    data_mode(0);
    printf("\t.short %d\n",d);
}

extern void
emit_int(int d)
{
    data_mode(0);
    printf("\t.long %d\n",d);
}

extern void
emit_longlong(int e)
{
#if LONGLONG_CODE
    long long ll = lcadr(e);
    data_mode(0);
#if (ENDIAN_L==0)
        printf("\t.long\t0x%x,0x%x\n",code_l1(ll),code_l2(ll));
#else
        printf("\t.long\t0x%x,0x%x\n",code_l2(ll),code_l1(ll));
#endif
#endif
}

extern void
emit_double(int e)
{
#if FLOAT_CODE
    double d = dcadr(e);
    data_mode(0);
#if (ENDIAN_D==0)
        printf("\t.long\t0x%x,0x%x\n",code_d1(d),code_d2(d));
#else
        printf("\t.long\t0x%x,0x%x\n",code_d2(d),code_d1(d));
#endif
#endif
}

extern void
emit_float(int e)
{
#if FLOAT_CODE
    float f = dcadr(e);
    data_mode(0);
    printf("\t.long\t0x%x\n",*(int *)&f);
#endif
}

extern void
emit_address(char *s,int offset)
{
    data_mode(0);
    if (offset)
	printf("\t.long %s+%d\n",s,offset);
    else
	printf("\t.long %s\n",s);
}

extern void
emit_label(int labelno)
{
    data_mode(0);
    printf("\t.long _%d\n",labelno);
}

extern void
emit_data_closing(NMTBL *n)
{
#ifdef DOT_SIZE
    int lb;
#endif
    if (chk) return;
    if (mode==GDECL) {
	data_mode(0);
#ifdef DOT_SIZE
	lb=fwdlabel();
	printf("_%d:\n",lb);
	printf("\t.size\t%s,_%d-%s\n",n->nm,lb,n->nm);
#endif
    }
}

#if LONGLONG_CODE
static long long ll0 = 1LL;

static int 
code_l1(long long d)
{
    int *i = (int *)&ll0; int *j = (int *)&d;
    return (i[1] == 1)?j[1]:j[0];
}

static int 
code_l2(long long d)
{
    int *i = (int *)&ll0; int *j = (int *)&d;
    return (i[1] == 1)?j[0]:j[1];
}
#endif

void
global_table(void)
{
    NMTBL *n;
    int init;
    init=0;
    for(n=global_list;n;n = n->next) {
	if ((n->sc == GVAR||n->sc == STATIC) && n->dsp != -1) {
	    /* n->dsp = -1 means initialized global */
	    if (init==0) {
		data_mode(0);
		init=1;
	    }
	    printf(".comm %s,%d\n",n->nm,size(n->ty));
	}
    }
}

void
local_table(void)
{
    NMTBL *n;
    int init;
    init=0;
    /* static local variables */
    for(n=local_static_list;n;n = n->next) {
	if (n->sc == GVAR) {
	    if (init==0) {
		data_mode(0);
		init=1;
	    }
	    if (n->dsp!= -1) /* -1 means initialized global */
		printf(".lcomm %s,%d\n",n->nm,size(n->ty));
	}
    }
}

void
text_mode(void)
{
    if (output_mode!=TEXT_EMIT_MODE) {
	printf(".text\n");
	printf("\t.align 2\n");
	output_mode = TEXT_EMIT_MODE;
    }
}

void
data_mode(char *name)
{
    if (output_mode!=DATA_EMIT_MODE) {
	printf(".data\n");
	output_mode = DATA_EMIT_MODE;
    }
    if (name)
	printf("\t.type\t%s,@object\n",name);
}

#if FLOAT_CODE

/* floating point */


char *
fstore(int d)
{
    return use?
	(d?"fstl":"fsts"):
	(d?"fstpl":"fstps")
    ;
}

char *
fstore_u(int d)
{
    return d?"fstpl":"fstps";
}

char *
fload(int d)
{
    return d?"fldl":"flds";
}


void code_dassign_gvar(int e2,int freg,int d)
{ 
    if (cadr(e2)) 
	printf("\t%s %s+%d\n",fstore(d),((NMTBL*)caddr(e2))->nm,cadr(e2));
    else
	printf("\t%s %s\n",fstore(d),((NMTBL*)caddr(e2))->nm);
}

void code_dassign_lvar(int e2,int freg,int d)
{ 
    printf("\t%s %d(%%ebp)\n",fstore(d),lvar(e2));
}

void code_dassign_dregister(int e,int d,int freg)
{
    error(-1);
}

void code_dassign(int e2,int freg,int d)
{ 
    printf("\t%s (%s)\n",fstore(d),register_name(e2,0));
}

static double d0 = 1.0;

static int
code_d1(double d)
{
    int *i = (int *)&d0; int *j = (int *)&d;
    return (i[1] == 0x3ff00000)?j[0]:j[1];
}

static int
code_d2(double d)
{
    int *i = (int *)&d0; int *j = (int *)&d;
    return (i[1] == 0x3ff00000)?j[1]:j[0];
}

void code_dconst(int e2,int freg,int d)
{ 
    int lb;
    double value = dcadr(e2);

    if (value==0.0) {
	printf("\tfldz\n"); return;
    }
    if (value==1.0) {
	printf("\tfld1\n"); return;
    }
    printf(" \t.section\t.rodata\n\t.align 8\n");
    lb=fwdlabel();
    printf("_%d:\n",lb);
#if ENDIAN_D==0
    printf("\t.long\t0x%x,0x%x\n",code_d1(value),code_d2(value));
#endif
    if (output_mode==TEXT_EMIT_MODE) {
	printf(".text\n");
    } else {
	text_mode();
    }
    printf("\tfldl _%d\n",lb);
}

void code_dneg(int freg,int d)
{ 
    printf("\tfchs\n");
}

void code_d2i(int reg)
{ 
    use_int(reg);
    printf("\tlea -%d(%%esp),%%esp\n",SIZE_OF_INT*2);
    printf("\tfnstcw  (%%esp)\n");
    printf("\tmovl    (%%esp), %s\n",register_name(creg,0));
    printf("\tmovb    $12, 1(%%esp)\n");
    printf("\tfldcw   (%%esp)\n");
    printf("\tfistpl  %d(%%esp)\n",SIZE_OF_INT);
    printf("\tmovl    %s, (%%esp)\n",register_name(creg,0));
    printf("\tfldcw   (%%esp)\n");
    printf("\tpopl    %s\n",register_name(creg,0));
    printf("\tpopl    %s\n",register_name(creg,0));
}

void code_i2d(int reg)
{ 
    printf("\tpushl %s\n",register_name(creg,0));
    printf("\tfildl (%%esp)\n");
    printf("\tlea %d(%%esp),%%esp\n",SIZE_OF_INT);
}

void code_d2u(int reg)
{ 
    use_int(reg);
    printf("\tlea -%d(%%esp),%%esp\n",SIZE_OF_INT*3);
    printf("\tfnstcw  (%%esp)\n");
    printf("\tmovl    (%%esp), %s\n",register_name(reg,0));
    printf("\tmovb    $12, 1(%%esp)\n");
    printf("\tfldcw   (%%esp)\n");
    printf("\tmovl    %s, (%%esp)\n",register_name(reg,0));
    printf("\tfistpll %d(%%esp)\n",SIZE_OF_INT);
    printf("\tfldcw   (%%esp)\n");
    printf("\tmovl    %d(%%esp),%s\n",SIZE_OF_INT,register_name(reg,0));
    printf("\tlea %d(%%esp),%%esp\n",SIZE_OF_INT*3);
}

void code_u2d(int reg)
{ 
    printf("\tpushl  %s\n",register_name(creg,0));
    printf("\tpushl  %s\n",register_name(creg,0));
    printf("\tmovl   $0, %d(%%esp)\n",SIZE_OF_INT);
    printf("\tfildll (%%esp)\n");
    printf("\tlea %d(%%esp),%%esp\n",SIZE_OF_INT*2);
}

void code_d2f(int reg) { }
void code_f2d(int reg) { }
void code_f2i(int reg) { code_d2i(reg); }
void code_f2u(int reg) { code_d2u(reg); }
void code_i2f(int reg) { code_i2d(reg); }
void code_u2f(int reg) { code_u2d(reg); }

void code_drgvar(int e2,int d,int freg)
{ 
    if (cadr(e2))
	printf("\t%s %s+%d\n",fload(d),((NMTBL*)caddr(e2))->nm,cadr(e2));
    else
	printf("\t%s %s\n",fload(d),((NMTBL*)caddr(e2))->nm);
}


void code_drlvar(int e2,int d,int freg)
{ 
    printf("\t%s %d(%%ebp)\n",fload(d),lvar(e2));
}

void code_cmp_drgvar(int e2,int reg,int d,int label,int cond)
{ 
    if (cadr(e2))
	printf("\tfcomp %s+%d\n",((NMTBL*)caddr(e2))->nm,cadr(e2));
    else
	printf("\tfcomp %s\n",((NMTBL*)caddr(e2))->nm);
    jcond(label,cond);
}

void code_cmp_drlvar(int e2,int reg,int d,int label,int cond)
{ 
    printf("\tfcomp %d(%%ebp)\n",lvar(e2));
    jcond(label,cond);
}

void dtosop(int op,int reg,int e1)
{
    switch(op) {
    case FADD:
    case DADD: printf("\tfaddp %%st,%%st(1)\n"); break;
    case FSUB:
    case DSUB: printf("\tfsubp %%st,%%st(1)\n"); break;
    case FDIV:
    case DDIV: printf("\tfdivp %%st,%%st(1)\n"); break;
    case FMUL:
    case DMUL: printf("\tfmulp %%st,%%st(1)\n"); break;
    case FCMP:
    case DCMP: 
	printf("\tfucompp\n");
	printf("\tfnstsw\t%%ax\n");
	break;
    }
}

void
code_dassop(int op,int reg,int d) {
    /* we have lvalue in creg, applied floating value is in %st(0) */
    emit_dpop(d);                            /* do nothing for 387 */
    printf("\t%s (%s)\n",fload(d),register_name(creg,0));
    dtosop(op,reg,0);
    printf("\t%s (%s)\n",fstore(d),register_name(creg,0));
}

void
code_register_dassop(int reg,int op,int d) {
    error(-1);
}

void
code_dpreinc(int e1,int e2,int d,int freg) {
    g_expr(e2);
    printf("\t%s (%s)\n",fload(d),register_name(creg,0));
    printf("\tfld1\n");
    if (caddr(e1)>0)
	printf("\tfaddp %%st,%%st(1)\n");
    else
	printf("\tfsubrp %%st,%%st(1)\n");
    printf("\t%s (%s)\n",fstore(d),register_name(creg,0));
}

void
code_dpostinc(int e1,int e2,int d,int freg) {
    g_expr(e2);
    printf("\t%s (%s)\n",fload(d),register_name(creg,0));
    if (use)
	printf("\t%s (%s)\n",fload(d),register_name(creg,0));
    printf("\tfld1\n");
    if (caddr(e1)>0)
	printf("\tfaddp %%st,%%st(1)\n");
    else
	printf("\tfsubrp %%st,%%st(1)\n");
    printf("\t%s (%s)\n",(use?fstore_u(d):fstore(d)),register_name(creg,0));
}

#define COND_BRANCH 1
#define COND_VALUE  2

int
drexpr0(int e1, int e2,int l1, int op,int cond,int reg,int mode)
{       
    char *s;
    if (!cond) {
	switch(op) {
	case FOP+GT:
	    return drexpr0(e2,e1,l1,FOP+GE,1,reg,mode);
	case FOP+GE:
	    return drexpr0(e2,e1,l1,FOP+GT,1,reg,mode);
	case FOP+EQ:
	    op=FOP+NEQ; break;
	case FOP+NEQ:
	    op=FOP+EQ; break;
	case DOP+GT:
	    return drexpr0(e2,e1,l1,DOP+GE,1,reg,mode);
	case DOP+GE:
	    return drexpr0(e2,e1,l1,DOP+GT,1,reg,mode);
	case DOP+EQ:
	    op=DOP+NEQ; break;
	case DOP+NEQ:
	    op=DOP+EQ; break;
	default: return 0;
	}
    }
    s = "e";
    switch(op) {
	case DOP+GE:
	case FOP+GE:
	    g_expr(list3(DCMP,e1,e2));
	    printf("\ttestb\t$5,%%ah\n");
	    break;
	case DOP+GT:
	case FOP+GT:
	    g_expr(list3(DCMP,e1,e2));
	    printf("\ttestb\t$69,%%ah\n");
	    break;
	case DOP+EQ:
	case FOP+EQ:
	    g_expr(list3(DCMP,e1,e2));
	    printf("\tandb\t$69,%%ah\n");
	    printf("\txorb\t$64,%%ah\n");
	    break;
	case DOP+NEQ:
	case FOP+NEQ:
	    g_expr(list3(DCMP,e1,e2));
	    printf("\tandb\t$69,%%ah\n");
	    printf("\txorb\t$64,%%ah\n");
	    s = "ne";
	    break;
	default:
	    return 0;
    }
    if (mode==COND_BRANCH) {
	printf("\tj%s\t_%d\n",s,l1);
    } else {
	use_int(reg); use_data_reg(reg,0);
	printf("\tset%s\t%s\n",s,register_name(reg,1));
	printf("\tmovzbl\t%s,%s\n",
	    register_name(reg,1),register_name(reg,0));
    }
    return 1;
}

void
drexpr(int e1, int e2,int l1, int op,int cond)
{
    drexpr0(e1, e2,l1, op,cond,USE_CREG,COND_BRANCH);
}

static int
drexpr_bool(int e1, int reg)
{
    return drexpr0(cadr(e1), caddr(e1),0, car(e1),1,reg,COND_VALUE);
}


void 
code_dregister(int e2,int freg,int d)
{
    error(-1);
}

void
code_cmp_dregister(int e2,int d,int label,int cond)
{
    if (e2!=USE_CREG)
	error(-1);
    printf("\tfldz\n"); 
    printf("\tfucompp\n");
    printf("\tfnstsw\t%%ax\n");
    printf("\tandb\t$69,%%ah\n");
    printf("\txorb\t$64,%%ah\n");
    jcond(label,cond);
}

int pop_fregister()
{ 
    if (freg_sp<0) { error(-1); return -1;}
    printf("# fpop: %d\n",freg_sp-1);
    return freg_stack[--freg_sp];
}

int
emit_dpop(int d)
{
    int xreg;
    if ((xreg=pop_fregister())==-1) {
    } else if (xreg<= -REG_LVAR_OFFSET) {
	code_drlvar(REG_LVAR_OFFSET+xreg,1,freg);
	free_lvar(xreg+REG_LVAR_OFFSET);
	/* pushed order is reversed.   We don't need this for commutable 
	    operator, but it is ok to do this.  */
        printf("\tfxch\t%%st(1)\n");
    } 
    return xreg;
}


void emit_dpop_free(int e1,int d)
{ 
}

void emit_dpush(int type)
{ 
    if (freg_sp>=MAX_FPU_STACK) code_save_fstacks();
    if (freg_sp>MAX_MAX) error(-1);
    freg_stack[freg_sp++]=-1;
    printf("# fpush:%d\n",freg_sp);
}

#endif

void
code_save_stacks()
{
    /* temporal registers or stacks in fpu are saved in local variable */
    int xreg,sp,screg;
    sp=reg_sp;
    while(sp-->0) {
	if ((xreg=reg_stack[sp])>=0) {
	    screg=creg;
	    if(creg!=xreg) {
		if (xreg!=dreg) free_register(xreg); 
		creg = xreg;
	    }
	    code_assign_lvar(
		(reg_stack[sp]=new_lvar(SIZE_OF_INT)),creg,0); 
	    reg_stack[sp]= reg_stack[sp]-REG_LVAR_OFFSET;
	    regv[xreg]=0;
	    creg=screg;
	}
    }
}

#if FLOAT_CODE
void
code_save_fstacks()
{
    int xreg,sp,uses;
    uses = use; use = 0;
    sp=freg_sp;
    while(sp-->0) {
	if ((xreg=freg_stack[sp])==-1) {
	    code_dassign_lvar(
		(freg_stack[sp]=new_lvar(SIZE_OF_DOUBLE)),freg,1); 
	    freg_stack[sp]= freg_stack[sp]-REG_LVAR_OFFSET;
	}
    }
    use = uses;
}
#endif



#if LONGLONG_CODE


/* 64bit int part */

static void
pcond(char *s,int l1)
{
    printf("\tj%s\t_%d\n",s,l1);
}

void
lrexpr(int e1, int e2,int l1, int op,int cond)
{
    int l2;
    code_save_stacks();
    g_expr(e1);
    emit_lpush();
    g_expr(e2);
    // we are sure %ecx,%ebx is free
    printf("\tpopl %%ecx\n");   // LSW
    printf("\tpopl %%ebx\n");   // MSW
    printf("\tsubl %%edx,%%ebx\n");
    l2 = fwdlabel();
    // cond==0 jump on false condtion   ( if(x) => rexpr(..  cond=0 ...) )
    switch(op) {
    case LOP+GT:
    case LOP+GE:
        pcond(code_gt(1),cond?l1:l2);
        pcond(code_eq(0),cond?l2:l1);
        break;
    case LOP+UGT:
    case LOP+UGE:
        pcond(code_ugt(1),cond?l1:l2);
        pcond(code_eq(0), cond?l2:l1);
        break;
    case LOP+EQ:
        pcond(code_eq(0),(cond?l2:l1));
        pcond(code_eq(cond),l1);
        break;
    case LOP+NEQ:
        pcond(code_eq(0),(cond?l1:l2));
        pcond(code_eq(!cond),l1);
        break;
    default:
        error(-1);
    }
    printf("\tsubl %%eax,%%ecx\n");
    switch(op) {
    case LOP+GT:  pcond(code_gt(cond),  l1); break;
    case LOP+GE:  pcond(code_ge(cond),  l1); break;
    case LOP+UGT: pcond(code_ugt(cond), l1); break;  
    case LOP+UGE: pcond(code_uge(cond), l1); break;  
    }  
    fwddef(l2); 
}

int emit_lpop()
{
    return 0;
}

void code_lregister(int e2,int reg)
{
    use_longlong(reg);
    if (reg!=REG_L) {
	printf("\tmovl %%esi,%s\n",l_eax(reg));
	printf("\tmovl %%edi,%s\n",l_edx(reg));
    }
}

void code_cmp_lregister(int reg,int label,int cond)
{
    char *crn;
    use_int(reg);
    crn = register_name(reg,0);
    printf("\tmovl %%esi,%s\n",crn);
    printf("\torl %%edi,%s\n",crn);
    printf("\ttestl %s,%s\n",crn,crn);
    jcond(label,cond);
}

void code_cmp_lrgvar(int e1,int e2,int label,int cond)
{
    char *n,*crn;
    n = ((NMTBL*)caddr(e1))->nm;
    use_int(e2);
    crn = register_name(e2,0);
    if (cadr(e1)) {
	printf("\tmovl %s+%d,%s\n",n,cadr(e1),crn);
	printf("\torl  %s+%d,%s\n",n,cadr(e1)+4,crn);
    } else {
	printf("\tmovl %s,%s\n",n,crn);
	printf("\torl  %s+4,%s\n",n,crn);
    }
    printf("\ttestl %s,%s\n",crn,crn);
    jcond(label,cond);
}

void code_cmp_lrlvar(int e1,int e2,int label,int cond)
{
    char *crn;
    use_int(e2);
    crn = register_name(e2,0);
    printf("\tmovl %d(%%ebp),%s\n",lvar(e1),crn);
    printf("\torl  %d(%%ebp),%s\n",lvar(e1)+4,crn);
    printf("\ttestl %s,%s\n",crn,crn);
    jcond(label,cond);
}

void code_lassign(int e1,int e2)
{
    char *rn;
    // e1 = e2
    use_longlong(e2);
    rn = register_name(e1,0);
#if ENDIAN_L==0
    printf("\tmovl %s,(%s)\n",l_eax(e2),rn);
    printf("\tmovl %s,4(%s)\n",l_edx(e2),rn);
#endif
}

void code_lassign_gvar(int e1,int e2)
{
    char *n;
    n = ((NMTBL*)caddr(e1))->nm;
    use_longlong(e2);
#if ENDIAN_L==0
    if (cadr(e1)) {
	printf("\tmovl %s,%s+%d\n",l_eax(e2),n,cadr(e1));
	printf("\tmovl %s,%s+%d\n",l_edx(e2),n,cadr(e1)+4);
    } else {
	printf("\tmovl %s,%s\n",l_eax(e2),n);
	printf("\tmovl %s,%s+4\n",l_edx(e2),n);
    }
#endif
}

void code_lassign_lvar(int e1,int e2)
{
    use_longlong(e2);
#if ENDIAN_L==0
    printf("\tmovl %s,%d(%%ebp)\n",l_eax(e2),lvar(e1));
    printf("\tmovl %s,%d(%%ebp)\n",l_edx(e2),lvar(e1)+4);
#endif
}

void code_lassign_lregister(int e2,int reg)
{
    // e2 = reg
    use_longlong(reg);
    if (e2!=reg) {
	printf("\tmovl %s,%s\n",l_eax(reg),l_eax(e2));
	printf("\tmovl %s,%s\n",l_edx(reg),l_edx(e2));
    }
}

void
code_lconst(int e1,int creg)
{
    use_longlong(creg);
#if ENDIAN_L==0
    printf("\tmovl $%d,%s\n",code_l1(lcadr(e1)),l_eax(creg));
    printf("\tmovl $%d,%s\n",code_l2(lcadr(e1)),l_edx(creg));
#endif
}

void code_lneg(int e1)
{
    use_longlong(e1);
    printf("\tnegl %s\n",l_eax(e1));
    printf("\tadcl $0,%s\n",l_edx(e1));
    printf("\tnegl %s\n",l_edx(e1));
}

void code_lrgvar(int e1,int e2)
{
    char *n;
    n = ((NMTBL*)caddr(e1))->nm;
    use_longlong(e2);
#if ENDIAN_L==0
    if (cadr(e1)) {
	printf("\tmovl %s+%d,%s\n",n,cadr(e1),l_eax(e2));
	printf("\tmovl %s+%d,%s\n",n,cadr(e1)+4,l_edx(e2));
    } else {
	printf("\tmovl %s,%s\n",n,l_eax(e2));
	printf("\tmovl %s+4,%s\n",n,l_edx(e2));
    }
#endif
}

void code_lrlvar(int e1,int e2)
{
    use_longlong(e2);
#if ENDIAN_L==0
    printf("\tmovl %d(%%ebp),%s\n",lvar(e1),l_eax(e2));
    printf("\tmovl %d(%%ebp),%s\n",lvar(e1)+4,l_edx(e2));
#endif
}

#define check_lreg(reg) if (reg==REG_L) code_lassign_lregister(reg,REG_LCREG)

void
ltosop(int op,int reg,int e2)
{
    char *opl,*oph,*call;
    int lb;

    // e2 (operand) is on the top of the stack
    use_longlong(reg);
    opl = 0; call=0;
    stack_depth -= SIZE_OF_INT * 2;

    switch(op) {
    case LLSHIFT:
    case LULSHIFT:
	printf("\tmovl %%ecx,4(%%esp)\n");
	printf("\tpopl %%ecx\n");
        printf("\tshldl %%eax,%%edx\n");
        printf("\tsall %%cl,%%eax\n");
        printf("\ttestb $32,%%cl\n");
	printf("\tje\t_%d\n",(lb=fwdlabel()));
        printf("\tmovl %%eax,%%edx\n");
        printf("\txorl %%eax,%%eax\n");
	fwddef(lb);
	printf("\tpopl %%ecx\n");
	check_lreg(reg);
	return;
    case LRSHIFT:
	printf("\tmovl %%ecx,4(%%esp)\n");
	printf("\tpopl %%ecx\n");
        printf("\tshrdl %%edx,%%eax\n");
        printf("\tsarl %%cl,%%eax\n");
        printf("\ttestb $32,%%cl\n");
	printf("\tje\t_%d\n",(lb=fwdlabel()));
        printf("\tmovl %%edx,%%eax\n");
        printf("\tsarl $31,%%edx\n");
	fwddef(lb);
	printf("\tpopl %%ecx\n");
	check_lreg(reg);
	return;
    case LURSHIFT:
	printf("\tmovl %%ecx,4(%%esp)\n");
	printf("\tpopl %%ecx\n");
        printf("\tshrdl %%edx,%%eax\n");
        printf("\tshrl %%cl,%%eax\n");
        printf("\ttestb $32,%%cl\n");
	printf("\tje\t_%d\n",(lb=fwdlabel()));
        printf("\tmovl %%edx,%%eax\n");
        printf("\txorl %%edx,%%edx\n");
	fwddef(lb);
	printf("\tpopl %%ecx\n");
	check_lreg(reg);
	return;
    }
    switch(op) {
    case LADD:  opl="addl";oph="adcl"; break;
    case LSUB:  opl="subl";oph="sbbl"; break;
    case LBAND: opl=oph="andl"; break;
    case LEOR:  opl=oph="xorl"; break;
    case LBOR:  opl=oph="orl"; break;
    case LMUL:
    case LUMUL:
	printf("\tpushl %%edx\n");
	printf("\tpushl %%eax\n");
	printf("\tpushl %%ecx\n");
	//       0   saved ecx
	//       4   c_l
	//       8   c_h
	//      12   o_l
	//      16   o_h
        printf("\tmull 12(%%esp)\n");          //  c_l*o_l -> %edx,%eax
        printf("\tmovl 4(%%esp),%%ecx\n");     //  c_l->%ecx
        printf("\timull 16(%%esp),%%ecx\n");   //  c_l*o_h->%ecx
        printf("\taddl %%ecx,%%edx\n");        //  %edx+%ecx->%edx
        printf("\tmovl 8(%%esp),%%ecx\n");     //  c_h->%ecx
        printf("\timull 12(%%esp),%%ecx\n");   //  c_h*o_l->%ecx
        printf("\taddl %%ecx,%%edx\n");        //  %edx+%ecx->%edx
	printf("\tpopl %%ecx\n");
	// printf("\taddl $8,%%esp\n");
	printf("\tlea 16(%%esp),%%esp\n");
	return;
    case LDIV:  call="__divdi3"; break;
    case LUDIV: call="__udivdi3"; break;
    case LMOD:  call="__moddi3"; break;
    case LUMOD: call="__umoddi3"; break;
    default: error(-1);
    }
    if (opl) {
	printf("\t%s (%%esp),%%eax\n\t%s 4(%%esp),%%edx\n",opl,oph);
	printf("\tlea 8(%%esp),%%esp\n");
	check_lreg(reg);
    } else if (call) {
	printf("\tpushl %%edx\n");
	printf("\tpushl %%eax\n");
	printf("\tcall %s\n",call);
	// printf("\taddl $8,%%esp\n");
	printf("\tlea 16(%%esp),%%esp\n");
	check_lreg(reg);
    } else {
	error(-1);
    }
}

int code_lconst_op_p(int op,int e) {
    long long l;
    if (car(e)==CONST) l = cadr(e);
    else if (car(e)==LCONST) l = lcadr(e);
    else return 0;

    switch(op) {
    case LLSHIFT:
    case LULSHIFT:
    case LRSHIFT:
    case LURSHIFT:
	return (0<=l&&l<=32);
    case LADD:
    case LSUB:
    case LBAND:
    case LEOR:
    case LBOR:
	return 1;
    default:
	return 0;
    }
}

void loprtc(int op,int reg,int e) {
    char *opl,*oph;
    int vl;
    int vh;
    long long l;

    if (car(e)==CONST) l = cadr(e);
    else if (car(e)==LCONST) l = lcadr(e);
    else error(-1);

    vl = code_l1(l);
    vh = code_l2(l);

    use_longlong(reg);
    opl = 0;

    switch(op) {
    case LLSHIFT:
    case LULSHIFT:
        printf("\tshldl $%d,%s,%s\n",vl,l_eax(reg),l_edx(reg));
        printf("\tsall $%d,%s\n",vl,l_eax(reg));
	return;
    case LRSHIFT:
        printf("\tshrdl $%d,%s,%s\n",vl,l_edx(reg),l_eax(reg));
        printf("\tsarl $%d,%s\n",vl,l_edx(reg));
	return;
    case LURSHIFT:
        printf("\tshrdl $%d,%s,%s\n",vl,l_edx(reg),l_eax(reg));
        printf("\tshrl $%d,%s\n",vl,l_edx(reg));
	return;
    }
    switch(op) {
    case LADD: opl="addl";oph="adcl"; break;
    case LSUB: opl="subl";oph="sbbl"; break;
    case LEOR:  opl=oph="xorl"; break;
    case LBOR:  opl=oph="orl"; break;
    case LBAND: opl=oph="andl"; break;
    default: error(-1);
    }
    printf("\t%s $%d,%s\n\t%s $%d,%s\n",opl,vl,l_eax(reg),oph,vh,l_edx(reg));
}

void emit_lpop_free(int e1)
{
//     printf("\taddl $8,%%esp\n");
}

void emit_lpush()
{
    stack_depth += SIZE_OF_INT * 2;
    printf("\tpushl %%edx\n\tpushl %%eax\n");
}

void code_i2ll(int reg)
{
    int reg0 = USE_CREG;
    use_register(creg,REG_EAX,1);
    regv[creg]=0;
    use_longlong(reg0);
    printf("\tcltd\n");
    check_lreg(reg);
    lreg = creg = reg0;
}

void code_i2ull(int reg)
{
    code_i2ll(reg);
}

void code_u2ll(int reg)
{
    int reg0 = USE_CREG;
    use_register(creg,REG_EAX,1);
    regv[creg]=0;
    use_longlong(reg0);
    printf("\txorl %%edx,%%edx\n");
    check_lreg(reg);
    lreg = creg = reg0;
}

void code_u2ull(int reg)
{
    code_u2ll(reg);
}

void code_ll2i(int reg)
{
    use_int(reg);
    if (virtual(REG_EAX)!=reg)
	printf("\tmovl %%eax,%s\n",register_name(creg,0));
}

void code_ll2u(int reg)
{
    code_ll2i(reg);
}

void code_ull2i(int reg)
{
    code_ll2i(reg);
}

void code_ull2u(int reg)
{
    code_ll2i(reg);
}

#if FLOAT_CODE
void code_d2ll(int reg)
{
    use_longlong(reg);
        printf("\tsubl $40,%%esp\n");
        printf("\tfnstcw 2(%%esp)\n");
        printf("\tmovw 2(%%esp),%%ax\n");
        printf("\torw $3072,%%ax\n");
        printf("\tmovw %%ax,0(%%esp)\n");
        printf("\tfldcw 0(%%esp)\n");
        printf("\tfistpll 12(%%esp)\n");
        printf("\tfldcw 2(%%esp)\n");
        printf("\tmovl 12(%%esp),%%eax\n");
        printf("\tmovl 16(%%esp),%%edx\n");
        printf("\taddl $40,%%esp\n");
}

void code_d2ull(int reg)
{
    use_longlong(reg);
        printf("\tsubl $16,%%esp\n");
        printf("\tfstpl (%%esp)\n");
        printf("\tcall __fixunsdfdi\n");
        printf("\taddl $16,%%esp\n");
}

void code_f2ll(int reg)
{
    code_d2ll(reg);
}

void code_f2ull(int reg)
{
    use_longlong(reg);
        printf("\tsubl $16,%%esp\n");
        printf("\tfstps (%%esp)\n");
        printf("\tcall __fixunssfdi\n");
        printf("\taddl $16,%%esp\n");
}

void code_ll2d(int reg)
{
        printf("\tsubl $8,%%esp\n");
        printf("\tmovl %%eax,(%%esp)\n");
        printf("\tmovl %%edx,4(%%esp)\n");
        printf("\tfildll (%%esp)\n");
        printf("\taddl $8,%%esp\n");
}

void code_ll2f(int reg)
{
    code_ll2d(reg);
}

void code_ull2d(int reg)
{
    code_ll2d(reg);
}

void code_ull2f(int reg)
{
    code_ll2d(reg);
}

#endif


void code_lpreinc(int e1,int e2,int reg)
{
    int dir = caddr(e1);
    int creg0;
    char *crn;
    if (car(e2)==LREGISTER) {
        use_longlong(reg);
        printf("\taddl $%d,%%esi\n",dir);
	printf("\tadcl $%d,%%edi\n",dir>0?0:-1);
	if (reg!=REG_L) {
	    code_lregister(REG_L,reg);
	}
        return;
    } 
    g_expr(e2);
    crn = register_name(creg0=creg,0);
    printf("\taddl $%d,(%s)\n",dir,crn);
    printf("\tadcl $%d,4(%s)\n",dir>0?0:-1,crn);
    use_longlong(reg);
    lload(creg0,0,reg);
}

void code_lpostinc(int e1,int e2,int reg)
{
    int dir = caddr(e1);
    int creg0;
    char *crn;
    if (car(e2)==LREGISTER) {
        use_longlong(reg);
	if (reg!=REG_L) {
	    code_lregister(REG_L,reg);
	}
        printf("\taddl $%d,%%esi\n",dir);
	printf("\tadcl $%d,%%edi\n",dir>0?0:-1);
        return;
    } 
    g_expr(e2);
    crn = register_name(creg0=creg,0);
    printf("\taddl $%d,(%s)\n",dir,crn);
    printf("\tadcl $%d,4(%s)\n",dir>0?0:-1,crn);
    use_longlong(reg);
    lload(creg0,0,reg);
    printf("\taddl $%d,%s\n",-dir,l_eax(reg));
    printf("\tadcl $%d,%s\n",-dir>0?0:-1,l_edx(reg));
}

void code_lassop(int op,int reg)
{
    error(-1);
}

void
code_register_lassop(int reg,int op) {
    error(-1);
}


#endif





#if CASE_CODE

int
code_table_jump_p(int delta) { return 1; }

void
code_table_jump(int l,int csvalue,int delta,int max,int min,int dlabel)
{
    char *crn;
    use_register(creg,csvalue,0);
    crn = register_name(creg,0);
    printf("\tsubl\t$%d,%s\n",min,crn);
    printf("\tcmpl\t$%d,%s\n",max-min,crn);
    printf("\tja\t_%d\n",dlabel);
    if (delta==1)  {
	printf("\tjmp\t*_%d(,%s,4)\n",l,crn);
	return;
    }
    use_register(creg,REG_EAX,1);
    edx_setup(-1);
    switch(delta) {
    case 2:
	printf("\tmovl\t$1,%%edx\n");
	printf("\tandl\t%%eax,%%edx\n");
	printf("\tjne\t_%d\n",dlabel);
	printf("\tjmp\t*_%d(,%%eax,2)\n",l); break;
    case 4:
	printf("\tmovl\t$3,%%edx\n");
	printf("\tandl\t%%eax,%%edx\n");
	printf("\tjne\t_%d\n",dlabel);
	printf("\tjmp\t*_%d(%%eax)\n",l); break;
    default:
	printf("\tmovl $%d,%%ecx\n",delta);
	printf("\txor %%edx,%%edx\n\tdivl %%ecx\n");
	printf("\tandl\t%%edx,%%edx\n");
	printf("\tjne\t_%d\n",dlabel);
	printf("\tjmp\t*_%d(,%%eax,4)\n",l); break;
    }
    edx_cleanup();
}

void
code_table_open(int l)
{
    output_mode=DATA_EMIT_MODE;
    printf(" \t.section\t.rodata\n\t.align 4\n");
    fwddef(l);
}

void
code_table_value(int label,int table_top)
{
    printf("\t.long _%d\n",label);
}

void
code_table_close()
{
    text_mode();
}

#endif


#if ASM_CODE

/*
    print an operand  
 */

static void
emit_asm_operand(int rstr)
{
    if (car(rstr)==REGISTER) {
	printf("%s",register_name(cadr(rstr),0));
    } else if (car(rstr)==CONST) {
	printf("%d",cadr(rstr));
    } else if (car(rstr)==FNAME) {
	printf("%s",(char*)cadr(rstr));
    } else if (car(rstr)==STRING) {
	printf("_%d",cadr(rstr));
    } else {
	error(-1);
    }
}

/*
     prepare asm operands

     char *constraints string
     int  operand expr
     int  mode          (ASM_INPUT,ASM_OUTPUT)
     int  replacement list
     int  output operands count
     int  output operands replacement list

     retrun replacement list
        list3( operands, next, clobber )
                               0    can be shared in input/output
                               1    can't be used in input
 */

int
code_asm_operand(char *p,int e1,int mode,int repl,int n,int repl0)
{
    int r;
    int c;
    int val;
    int clobber = 0;

    printf("# constraint %s\n",p);
    if (*p=='=') {
	// output register
	p++;
    }
    if (*p=='&') {
	// earlyclobber
	p++;
	clobber = 1;
    }
    c = *p;
    if (c=='r') {
	if (mode==ASM_INPUT) {
	    for(;repl0;repl0 = cadr(repl0)) {
		if (car(car(repl0))==REGISTER && caddr(repl0)==0) {
		    r = cadr(car(repl0));
		    caddr(repl0) = ASM_USED;
		    break;
		}
            }  
	    r = get_register();
	} else {
	    r = get_register();
	}
	repl = list3(list2(REGISTER,r),repl,clobber);
    } else if (c=='m') {
	repl = list3(list2(0,0),repl,clobber);
    } else if (c=='i') {
	if (car(e1)==GVAR) {
	    e1=list3(FNAME,(int)(((NMTBL *)caddr(e1))->nm),0);
	} else if (car(e1)==FNAME) {
	    e1=list3(FNAME,(int)(((NMTBL *)cadr(e1))->nm),0);
	} else if (car(e1)==STRING) {
	    val = emit_string_label();
	    ascii((char*)cadr(e1));
	    e1=list3(STRING,val,0);
	} else if (car(e1)==CONST) {
	} else error(-1);
	repl = list3(e1,repl,clobber);
    } else if (digit(c)) {
	val = 0;
	do { val = val*10 + c-'0'; } while (digit(c=*p++));
	if (val>MAX_ASM_REG) error(-1); // too large register
	if (n-val<0) error(-1);
	repl = list3(car(nth(n-val-1,repl0)),repl,clobber);
    } else error(-1);
    return repl;
}

void
code_free_asm_operand(int repl)
{
    for(;repl;repl=cadr(repl)) {
	if (car(car(repl))==REGISTER)
	    free_register(cadr(car(repl)));
    }
}


extern void
code_asm(char *asm_str,int repl)
{
    int c,i,rstr,val;
    char *p;
    int reg[MAX_ASM_REG];

    text_mode();
    c = *asm_str;
    if (c!='\t'&&c!=' ') printf("\t");
    for(i=0;repl && i<MAX_ASM_REG;i++) {
	reg[i] = car(repl);
	repl = cadr(repl);
    }
    p = asm_str;
    while((c = *p++)) {
	if (c=='%') {
	    c = *p++;
	    if (!c) { break;
	    } else if (c=='%') {
		printf("%%"); continue;
	    } else if (!digit(c)) {
		printf("%%%c",c); continue;
	    }
	    val = 0;
	    do { val = val*10 + c-'0'; } while (digit(c=*p++)) ;
	    p--;
	    if (val>MAX_ASM_REG) error(-1); // too large register
	    rstr = reg[val];
	    emit_asm_operand(rstr);
	} else {
	    printf("%c",c);
	}
    }
    printf("\n");
}

#endif


#if BIT_FIELD_CODE

/* bit field alignment calcuration */

static void
set_bitsz(int type,int *psign,int *pbitsz,int *palign,int *pl)
{ 
    int sign=0,bitsz; 
    int align,l=0;
    switch(cadr(type)) { 
    case INT:		sign=1; bitsz=32; align=4;break; 
    case UNSIGNED:		bitsz=32; align=4;break; 
    case CHAR:          sign=1; bitsz= 8; align=1;break; 
    case UCHAR: 		bitsz= 8; align=1;break; 
    case SHORT:         sign=1; bitsz=16; align=2;break; 
    case USHORT:        sign=1; bitsz=16; align=2;break; 
    case LONGLONG:      sign=1; bitsz=64; align=4;l=1; break; 
    case ULONGLONG:            	bitsz=64; align=4;l=1; break; 
    default: error(-1);
    }
    *psign = sign;
    *pbitsz = bitsz;
    *palign = align;
    *pl = l;
}

/*
      bit field alignment calcuration
        this is architecture depenedent
 */

extern int
code_bit_field_disp(int type,int *poffset,int *bfd,int *sz)
{
    int sign,bitsz,align;
    int i;
    int bitpos = *bfd;
    int offset = *poffset;
    int l;
    int bitsize = cadddr(type);
    set_bitsz(type,&sign,&bitsz,&align,&l);

    if (bitsize>bitsz) { error(BTERR); bitsize = i; }

    /* bfd means previous bit field bit offset */
    if (bitpos) {
	/* previous field is bit field and spaces may remain */
	/* calc previsous offset */

	i= offset-(bitpos+7)/8;

	for(l = bitpos;l>0;l -= 8,i++) {
	    if ((i & (align-1))==0 && l+bitsize <= bitsz) {
		/* alignment is correct and space remains */
		*poffset=offset=i;
		i = l+bitsize;
		*bfd = i;
		*sz = (i+7)/8;
// printf("# bitpos=%d bitsize=%d bitsz=%d offset=%d\n",l,bitsize,bitsz,*poffset);
		return l;
	    } 
	}
    }

    /* first bit-field */

    if ((i=(offset & (align-1)))) {
	*poffset = (offset += (align-i));
    }
    bitpos = 0;
    *bfd = bitsize;
    *sz = (bitsize+7)/8;

// printf("# bitpos=%d bitsize=%d bitsz=%d offset=%d\n",bitpos,bitsize,bitsz,*poffset);
    return bitpos;
}

/* bit field value */

/* reg contains container value, result should be in reg */
extern void
code_bit_field(int type,int bitpos,int reg)
{
    int sign,bitsz,l,align;
    int bitsize = cadddr(type);
    int i;
    set_bitsz(type,&sign,&bitsz,&align,&l);
// printf("# %d: bitpos=%d bitsize=%d bitsz=%d\n",lineno,bitpos,bitsize,bitsz);
    /* this implementation returns -1 for int i:1; */
    if (l==1) {
	use_longlong(reg);
	/* shift left */
	if ((i=bitsz-bitsize-bitpos)) 
	    loprtc(LLSHIFT,reg,list2(CONST,i));
	/* shift right */
	if ((i=bitsz-bitsize)) 
	    loprtc(sign?LRSHIFT:LURSHIFT,reg,list2(CONST,i));
    } else {
	use_int(reg);
	/* shift left */
	if ((i=32-bitsize-bitpos)) 
	    oprtc(LSHIFT,reg,list2(CONST,i));
	/* shift right */
	if ((i=32-bitsize)) 
	    oprtc(sign?RSHIFT:URSHIFT,reg,list2(CONST,i));
    }
}

/* bit field replacement */

static void
make_mask_and_or(int mask,int reg,int lreg)
{
printf("# mask 0x%08x ~0x%08x\n",mask,~mask);
	printf("\tpushl %s\n",register_name(reg,0));
	/* make and-mask  */
	oprtc(BOR,reg,list2(CONST,~mask));
	/* do conjunction  */
	if (lreg==-1) {
	    printf("\tandl %s,4(%%esp)\n",register_name(reg,0));
	} else if (lreg==-2) {
	    printf("\tandl %s,8(%%esp)\n",register_name(reg,0));
	} else {
	    printf("\tandl %s,%s\n",register_name(reg,0),register_name(lreg,0));
	}
	/* make or-mask  */
	printf("\tpopl %s\n",register_name(reg,0));
	oprtc(BAND,reg,list2(CONST,mask));
	/* do disjunction  */
	if (lreg==-1) {
	    printf("\torl %s,0(%%esp)\n",register_name(reg,0));
	} else if (lreg==-2) {
	    printf("\torl %s,4(%%esp)\n",register_name(reg,0));
	} else {
	    printf("\torl %s,%s\n",register_name(reg,0),register_name(lreg,0));
	    printf("\txchg %s,%s\n",register_name(reg,0),register_name(lreg,0));
	}
}

extern void
code_bit_replace(int value,int lvalue,int type,int bitpos)
{
    int sign,bitsz,l,align;
    int bitsize = cadddr(type);
    int mask = 0;
    set_bitsz(type,&sign,&bitsz,&align,&l);
// printf("# %d: bitpos=%d bitsize=%d bitsz=%d\n",lineno,bitpos,bitsize,bitsz);
    if (l) {
	use_longlong(value);
	/* shift left */
	if (bitpos) 
	    loprtc(LLSHIFT,value,list2(CONST,bitpos));
	if (bitpos+bitsize>=32) {
	    /* make and-mask upper */
	    mask = make_mask(64-bitpos-bitsize,bitpos>=32?63-bitpos:31);
	    make_mask_and_or(mask,virtual(value==REG_L?REG_EDI:REG_EDX),-2);
	}
	if (bitpos<32) {
	    /* make and-mask lower */
	    mask = make_mask(bitpos+bitsize>=32?0:32-bitpos-bitsize,31-bitpos);
	    make_mask_and_or(mask,virtual(value==REG_L?REG_ESI:REG_EAX),-1);
	}
	printf("\tpopl %s\n",l_eax(value));
	printf("\tpopl %s\n",l_edx(value));
    } else {
	use_int(value);
	/* shift left */
	if (bitpos) 
	    oprtc(LSHIFT,value,list2(CONST,bitpos));
	/* make and-mask */
	mask = make_mask(32-bitpos-bitsize,31-bitpos);
	make_mask_and_or(mask,value,lvalue);
    }
}


static void
make_mask_and_or_const(int mask,int reg,int c)
{
    int a;
// printf("# mask 0x%08x ~0x%08x\n",mask,~mask);
    a = ~mask|c;
    if (a!=-1) {
	/* do conjunction  */
	if (rname[reg]<MAX_DATA_REG && ((a& ~0xffff)==~0xffff)) {
	    if ((a& ~0xff)==~0xff)
		printf("\tandb $%d,%s\n",a&0xff,register_name(reg,1));
	    else
		printf("\tandw $%d,%s\n",a&0xffff,register_name(reg,2));
	} else
	    printf("\tandl $%d,%s\n",a,register_name(reg,0));
    }
    /* make or-mask  */
    c = mask&c;
    if (c!=0) {
	/* do disjunction  */
	if (rname[reg]<MAX_DATA_REG && (!(c& ~0xffff))) {
	    if (!(c& ~0xff))
		printf("\torb $%d,%s\n",c&0xff,register_name(reg,1));
	    else
		printf("\torw $%d,%s\n",c&0xffff,register_name(reg,2));
	} else
	    printf("\torl $%d,%s\n",c,register_name(reg,0));
    }
}

extern void
code_bit_replace_const(int value,int lvalue,int type,int bitpos)
{
    int sign,bitsz,l,align;
    int bitsize = cadddr(type);
    int mask = 0;
    int c;
#if LONGLONG_CODE
    long long lc;
#endif
    set_bitsz(type,&sign,&bitsz,&align,&l);
// printf("# %d: bitpos=%d bitsize=%d bitsz=%d\n",lineno,bitpos,bitsize,bitsz);
    if (l) {
#if LONGLONG_CODE
	use_longlong(lvalue);
	/* shift left */
	lc = lcadr(value);
	lc <<= bitpos;
	if (bitpos+bitsize>=32) {
	    /* make and-mask upper */
	    mask = make_mask(64-bitpos-bitsize,bitpos>=32?63-bitpos:31);
	    make_mask_and_or_const(mask,
		virtual(lvalue==REG_L?REG_EDI:REG_EDX),(int)(lc>>32));
	}
	if (bitpos<32) {
	    /* make and-mask lower */
	    mask = make_mask(bitpos+bitsize>=32?0:32-bitpos-bitsize,31-bitpos);
	    make_mask_and_or_const(mask,
		virtual(lvalue==REG_L?REG_ESI:REG_EAX),(int)(lc));
	}
#endif
    } else {
	use_int(lvalue);
	/* shift left */
	c = cadr(value);
	c <<= bitpos;
	/* make and-mask */
	mask = make_mask(32-bitpos-bitsize,31-bitpos);
	make_mask_and_or_const(mask,lvalue,c);
    }
}


#endif

/* end */