view mc-code-powerpc.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 Power PC (Mac OS X) */
/* $Id$ */

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

char *l_include_path[] = {
    "/usr/include/",
    0
};

// __builtin_expect(a,t) branch prediction

static
char *init_src0 = "\
#define __ppc__ 1\n\
#define __BIG_ENDIAN__ 1\n\
#define __STDC__ 1\n\
#define __builtin_va_list int\n\
#define __builtin_va_start(ap,arg) ap=(((int)(&arg))+sizeof(arg))\n\
#define __builtin_va_arg(ap,type)  (*((type *)ap)++)\n\
#define alloca __builtin_alloca\n\
#define __builtin_expect(a,t) a\n\
";

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

static void data_mode(char *name);
static void text_mode();
static void ld_indexx(int byte, int n, int xreg,int reg,int sign);
static void local_table(void);
static void shift(char *op, int creg,int reg);
static int struct_push(int e4,int t,int arg);


static int creg;

static int output_mode = TEXT_EMIT_MODE;
int data_alignment = 0;

static int code_disp_label;
static int code_setup;
static int r1_offset_label;
static int lvar_offset_label;
static int max_func_arg_label;

static int reg_save;
static int freg_save;

static int freg,ireg,lreg;

int code_lassop_p = 1;

#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  1
#define ENDIAN_L  1
#define ENDIAN_D  1

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

/* floating point registers */

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

static int  lreg_sp;  /* longlong  REGister Stack-Pointer */
static int lreg_stack[MAX_MAX]; /* 実際のレジスタの領域 */

#define REG_fp   1
#define REG_sp   30
#define REG_VAR_BASE 29
#define REG_VAR_MIN  18
#define MIN_TMP_REG 3
#define MAX_TMP_REG 11

#define PTRC_REG 3

#define FREG_VAR_BASE 31
#define FREG_VAR_MIN  20
#define MIN_TMP_FREG 1
#define MAX_TMP_FREG 14

int MAX_REGISTER=30;             /* PowerPCのレジスタを10個まで使う*/
int MAX_FREGISTER=31;
#define  REAL_MAX_REGISTER 32    /* PowerPCのレジスタが32ということ*/
#define  REAL_MAX_FREGISTER 32    /* PowerPCのレジスタが32ということ*/
#define  REAL_MAX_LREGISTER 16

#define FREG_OFFSET REAL_MAX_REGISTER
#define LREG_OFFSET (REAL_MAX_REGISTER+REAL_MAX_FREGISTER)

#define RET_REGISTER 3
#define RET_FREGISTER (1+FREG_OFFSET)
#define RET_LREGISTER_H 3    /* high word */
#define RET_LREGISTER_L 4    /* low word */
#define RET_LREGISTER LREG_OFFSET

int MAX_INPUT_REGISTER_VAR = 11-MIN_TMP_REG;
int MAX_CODE_INPUT_REGISTER_VAR = 11-MIN_TMP_REG;
int MAX_INPUT_DREGISTER_VAR = 14-MIN_TMP_FREG;
int MAX_INPUT_FREGISTER_VAR = 14-MIN_TMP_FREG;
int MAX_CODE_INPUT_DREGISTER_VAR = 14-MIN_TMP_FREG;
int MAX_CODE_INPUT_FREGISTER_VAR = 14-MIN_TMP_FREG;

static int powerpc_regs[REAL_MAX_REGISTER+REAL_MAX_FREGISTER+
   REAL_MAX_LREGISTER];
static int regv_h0[REAL_MAX_LREGISTER];
static int regv_l0[REAL_MAX_LREGISTER];
#define regv_h(i)  regv_h0[(i)-LREG_OFFSET]
#define regv_l(i)  regv_l0[(i)-LREG_OFFSET]

static int *regs  = powerpc_regs;

#define CREG_REGISTER  (MAX_TMP_REG)
#define FREG_FREGISTER (MAX_TMP_FREG+FREG_OFFSET)
#define LREG_LREGISTER (MAX_TMP_REG+LREG_OFFSET)


static int max_reg_var, max_freg_var;

static char *reg_name[] = {
    "r0","r1","r2","r3","r4","r5","r6","r7","r8","r9",
    "r10","r11","r12","r13","r14","r15","r16","r17","r18","r19",
    "r20","r21","r22","r23","r24","r25","r26","r27","r28","r29",
    "r30","r31",
    "f0","f1","f2","f3","f4","f5","f6","f7","f8","f9",
    "f10","f11","f12","f13","f14","f15","f16","f17","f18","f19",
    "f20","f21","f22","f23","f24","f25","f26","f27","f28","f29",
    "f30","f31"
}; 

#define round4(i)   ((i+(SIZE_OF_INT-1))&~(SIZE_OF_INT-1))

#define register_name(i)  reg_name[i]
#define fregister_name(i) reg_name[i]
#define lregister_name_low(i) reg_name[regv_l(i)]
#define lregister_name_high(i) reg_name[regv_h(i)]

char *r(i) { return register_name(i); }
char *f(i) { return fregister_name(i); }
char *ll(i) { return lregister_name_low(i); }
char *lh(i) { return lregister_name_high(i); }

#define is_int_reg(i)  (0<=i&&i<REAL_MAX_REGISTER)
#define is_float_reg(i)  (REAL_MAX_REGISTER<=i&&i<REAL_MAX_FREGISTER+REAL_MAX_REGISTER)
#define is_longlong_reg(i)  (LREG_OFFSET<=i&&i<LREG_OFFSET+REAL_MAX_LREGISTER)

#define  use_int(reg) if (reg==USE_CREG) reg=use_int0()
static
int use_int0() { 
    int i = creg;
    if (!i||!ireg||!is_int_reg(i)) {
	if (lreg) { if (regs[lreg]) free_register(lreg); lreg = 0; }
	if (!ireg) ireg = get_register();
	// else if (ireg!=i) free_register(i);
	i = ireg;
    }
    if (!regs[i]) regs[i]=USING_REG;
    creg = i;
    return i;
}

#if LONGLONG_CODE
#define  use_longlong(reg) if (reg==USE_CREG) reg=use_longlong0()

static
int use_longlong0() {
    int i = creg;
    if (!is_longlong_reg(i)) {
	if (ireg) { free_register(ireg); ireg=0; }
	if (!lreg||!regs[lreg]) lreg = get_lregister();
	// else if (lreg!=i) free_register(i);
	i = lreg;
    }
    if (!regv_l(i)) regv_l(i) = get_register();
    if (!regv_h(i)) regv_h(i) = get_register();
    if (!regs[i]) regs[i]=USING_REG;
    if (!regs[regv_l(i)]) regs[regv_l(i)]=USING_REG;
    if (!regs[regv_h(i)]) regs[regv_h(i)]=USING_REG;
    creg = i;
    return i;
}
#endif


#if FLOAT_CODE
#define  use_float(d,reg) if (reg==USE_CREG) reg=d?use_double0():use_float0()
static
int use_float0() { 
    int i = creg;
    if (!is_float_reg(i)) {
	if (lreg) { free_register(lreg); lreg = 0; }
	if (!freg) freg = get_dregister(0);
	else if (freg!=i) free_register(i);
	i = freg;
    }
    if (!regs[i]) regs[i]=USING_REG;
    creg = i;
    return i;
}
static
int use_double0() { 
    int i = creg;
    if (!is_float_reg(i)) {
	if (lreg) { free_register(lreg); lreg = 0; }
	if (!freg) freg = get_dregister(1);
	else if (freg!=i) free_register(i);
	i = freg;
    }
    if (!regs[i]) regs[i]=USING_REG;
    creg = i;
    return i;
}
#endif


#if FLOAT_CODE
static
NMTBL float_zero = {"_float_zero",0,STATIC,FLOAT,0};
static
NMTBL float_one = {"_float_one",0,STATIC,FLOAT,0};


static char * fload(int d);
static int code_d1(double d);
static int code_d2(double d);
#endif
#if LONGLONG_CODE
static int code_l1(long long ll);
static int code_l2(long long ll);
#endif

static void code_save_stacks();
static void code_save_input_registers(int dots);
static void    set_ireg(int,int);
static void    set_freg(int,int);
static void    set_lreg(int,int);
static void jcond(int l, char cond);

static int max_func_args;
static int my_func_args;
#define ARG_LVAR_OFFSET 0x10000000

/*          

     r0    return value etc.
     r3-r10  input register
     r22-r29 saved register variable (input register for code segment)
     r30   stack pointer
     r31   0
     r1    frame pointer

     f0    return value etc.
     f1-r8  input register
     f24-f31 saved register variable

 function call stack frame
                      <-------r1_offset------------------------------>
                                      <------------lvar_offset0------>
                      <--lvar_offset-->
 r+  +------------+---+---------------+----------+--------------+----+    -
      callee arg   xx   register save   local      caller arg     xx
                          reg_save      disp       max_func_args*SIZE_OF_INT
        lvar>0                         lvar<0       lvar>0x1000 0000

code segment stack frame

                 * gotoを呼び出した関数のr1 ! r1(goto前のr1)
   #             *                           r30 <---r1_offset---------> r1
r+ +----------+--+----------+----------------+-----------+----------+----+
    cousin arg xx  reg save !callee arg      !code local  caller arg  xx
                   r20-r29     lvar>0         lvar<0      lvar>0x1000 000
                   f20-f31  <-my_func_args--><--disp-----><-max_func_arg->
                              *SIZE_OF_INT                  *SIZE_OF_INT

 */
int arg_offset = 24; int arg_offset1 = 24; int disp_offset = -12;
// #define func_disp_offset 60
#define func_disp_offset 68
#define r1_offset func_disp_offset+12 
int code_disp_offset = 0; int jump_offset = 0;
#define CODE_LVAR l+code_disp_offset
#define CODE_CALLER_ARG (l-ARG_LVAR_OFFSET)+arg_offset1
#define FUNC_LVAR l+disp_offset
#define CALLER_ARG (l-ARG_LVAR_OFFSET)+arg_offset1
#define CALLEE_ARG l+arg_offset

void
code_offset_set()
{
#if 0
    int l;
#endif
    int lvar_offsetv = -disp+max_func_args*SIZE_OF_INT+func_disp_offset;
    // int r1_offsetv = -disp+max_func_args*SIZE_OF_INT-reg_save+r1_offset;
    int r1_offsetv = lvar_offsetv-reg_save+12;
    printf(".set L_%d,%d\n",lvar_offset_label,r1_offsetv-lvar_offsetv);
    printf(".set L_%d,%d\n",r1_offset_label,r1_offsetv);
    if (max_func_arg_label) {
	printf(".set L_%d,%d\n",max_func_arg_label,max_func_args*SIZE_OF_INT+24);
	max_func_arg_label = 0;
    }

#if 0
printf("# reg_save %d\n",reg_save);
printf("# function %s\n",fnptr->nm);
    l = ARG_LVAR_OFFSET;
printf("# offset call0\t%d\n",CALLER_ARG);
    l = ARG_LVAR_OFFSET+max_func_args*SIZE_OF_INT;
printf("# offset calln\t%d %d\n",CALLER_ARG,max_func_args*SIZE_OF_INT);
    l = disp;
printf("# offset lvarn\t%d %d\n",FUNC_LVAR+lvar_offsetv,disp);
    l = 0;
printf("# offset lvar0\t%d\n",FUNC_LVAR+lvar_offsetv);
    l = -reg_save;
printf("# offset regs\t%d\n",FUNC_LVAR+lvar_offsetv);
printf("# offset r1off\t%d\n",r1_offsetv);
    l = 0;
printf("# offset carg0\t%d\n",CALLEE_ARG+r1_offsetv);
    l = my_func_args;
printf("# offset cargn\t%d %d\n",CALLEE_ARG+r1_offsetv,my_func_args);
#endif
}

static int large_offset_reg;

static void
lvar(int l)
{
    char *rn;
    if (!large_offset_reg) {
	if (fnptr->sc==CODE) {
	    if (l>=ARG_LVAR_OFFSET) {  /* caller's arguments */
		printf("lo16(%d)(r1)\n",CODE_CALLER_ARG);
	    } else
		printf("lo16(%d)(r30)\n",CODE_LVAR);
	} else if (l<0) {  /* local variable */
	    printf("lo16(%d)(r30)\n",FUNC_LVAR);
	} else if (l>=ARG_LVAR_OFFSET) {  /* caller's arguments */
	    printf("lo16(%d)(r1)\n",CALLER_ARG);
	} else { /* callee's arguments */
	    printf("lo16(%d+L_%d)(r30)\n",CALLEE_ARG,lvar_offset_label);
	}
    } else {
        rn = register_name(large_offset_reg);
        if (fnptr->sc==CODE) {
            if (l>=ARG_LVAR_OFFSET) {  /* caller's arguments */
                printf("lo16(%d)(%s)\n",CODE_CALLER_ARG,rn);
            } else
                printf("lo16(%d)(%s)\n",CODE_LVAR,rn);
        } else if (l<0) {  /* local variable */
            printf("lo16(%d)(%s)\n",FUNC_LVAR,rn);
        } else if (l>=ARG_LVAR_OFFSET) {  /* caller's arguments */
            printf("lo16(%d)(%s)\n",CALLER_ARG,rn);
        } else { /* callee's arguments */
            printf("lo16(%d+L_%d)(%s)\n",CALLEE_ARG,lvar_offset_label,rn);
        }
        free_register(large_offset_reg);
    }
}

/* if size of local variables / input variables is more then 64k,
   lo16 does not work. We have to use ha16 also. But we can't know
   the exact size in one path compile. We may safely use lvar16ha 
   if disp or max_func_args > 32k. Of course this is redundant for
   smaller offset. But who cares who use very large local variables?
 */

#define LARGE_OFFSET(l) (l<-32000||l>32000)

static void
lvar_intro(int l)
{
    char *rn;
    large_offset_reg=0;
    if (fnptr->sc==CODE) {
        if (l>=ARG_LVAR_OFFSET) {  /* caller's arguments */
            if (LARGE_OFFSET(CODE_CALLER_ARG)) {
                rn=register_name(large_offset_reg=get_register());
                printf("\taddis %s,r1,ha16(%d)\n",rn,CODE_CALLER_ARG);
            }
        } else {
            if (LARGE_OFFSET(CODE_LVAR)) {
                rn=register_name(large_offset_reg=get_register());
                printf("\taddis %s,r30,ha16(%d)\n",rn,CODE_LVAR);
            }
        }
    } else if (l<0) {  /* local variable */
        if (LARGE_OFFSET(FUNC_LVAR)) {
            rn=register_name(large_offset_reg=get_register());
            printf("\taddis %s,r30,ha16(%d)\n",rn,FUNC_LVAR);
        }
    } else if (l>=ARG_LVAR_OFFSET) {  /* caller's arguments */
        if (LARGE_OFFSET(CALLER_ARG)) {
            rn=register_name(large_offset_reg=get_register());
            printf("\taddis %s,r1,ha16(%d)\n",rn,CALLER_ARG);
        }
    } else { /* callee's arguments */
        if (LARGE_OFFSET(CALLEE_ARG)) {
            rn=register_name(large_offset_reg=get_register());
            printf("\taddis %s,r30,lo16(%d+L_%d)\n",
		rn,CALLEE_ARG,lvar_offset_label);
        }
    }
}


void
code_lvar(int e2,int reg) {
    use_int(reg);
    lvar_intro(e2);
    printf("\tla %s,",register_name(reg));
    lvar(e2);
}

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;

    regv_l(RET_LREGISTER) = RET_LREGISTER_L;
    regv_h(RET_LREGISTER) = RET_LREGISTER_H;
}

extern void
emit_reinit()
{
    /* called for each file */
    /* heap is initialized here, setup ptr cache free list */
    init_ptr_cache();
}

void
gexpr_code_init(void){
}

void
code_gexpr(int e){
//     if (is_int_reg(creg) && creg!=ireg) error(-1);
}


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 i;
    int is_code0 = is_code(fnptr);
    int dots;

    function_type(fnptr->ty,&dots);
    while (args) {
        /* process in reverse order */
        n = (NMTBL*)caddr(args);
        type = n->ty;
        if (scalar(type)) {
            if ((reg = get_input_register_var(reg_var,n,is_code0))) {
                n->sc = REGISTER;
                n->dsp = cadr(reg);
                regs[n->dsp]= INPUT_REG;
                reg_var++;
                cadddr(args)=SIZE_OF_INT;
            }
        } else if (type==FLOAT) {
            if ((reg = get_input_dregister_var(freg_var,n,is_code0,0))) {
                n->sc = DREGISTER;
                n->dsp = cadr(reg);
                regs[n->dsp]= INPUT_REG;
                freg_var++;
                cadddr(args)=size(type);
            }
        } else if (type==DOUBLE) {
            if ((reg = get_input_dregister_var(freg_var,n,is_code0,1))) {
                n->sc = DREGISTER;
                n->dsp = cadr(reg);
                regs[n->dsp]= INPUT_REG;
                freg_var++;
                cadddr(args)=size(type);
            }
        } else if (type==LONGLONG||type==ULONGLONG) {
            if ((reg = get_input_lregister_var(reg_var,n,is_code0))) {
                n->sc = LREGISTER;
                n->dsp = cadr(reg);
                regs[i=n->dsp]= INPUT_REG;
                regs[regv_l(i)]= INPUT_REG;
                regs[regv_h(i)]= INPUT_REG;
                reg_var+=2;
                cadddr(args)=size(type);
            }
        }
        args = cadr(args);
    }
    if (is_function(fnptr))
	code_save_input_registers(dots);
}


int 
get_register(void)
{    /* 使われていないレジスタを調べる */
    int i,j,reg;
    for(i=MAX_TMP_REG;i>MIN_TMP_REG;i--) {
	if (regs[i]) continue;  /* 使われている */
	regs[i]=USING_REG;      /* そのレジスタを使うことを宣言し */
	return i;   /* その場所を表す番号を返す */
    }
    /* PTR_CACHE をつぶす */
    if ((i=last_ptr_cache())) {
	clear_ptr_cache_reg(i);
	regs[i]=USING_REG;      /* そのレジスタを使うことを宣言し */
	return i;   /* その場所を表す番号を返す */
    }
    /* search register stack */
    for(i=0;i<reg_sp;i++) {
	if ((reg=reg_stack[i])>=0) {
            code_assign_lvar(
                (j=new_lvar(SIZE_OF_INT)),reg,0); 
            reg_stack[i]= j-REG_LVAR_OFFSET;
	    return reg;
	}
    }
#if LONGLONG_CODE
    /* search register stack */
    for(i=0;i<lreg_sp;i++) {
	if ((reg=lreg_stack[i])>=0) {
            code_lassign_lvar(
                (j=new_lvar(SIZE_OF_LONGLONG)),reg); 
            lreg_stack[i]= j-REG_LVAR_OFFSET;
	    free_register(reg);
	    return get_register();
	}
    }
#endif
    for(i=0;i<REG_VAR_BASE-REG_VAR_MIN;i++) {
        reg =REG_VAR_BASE-i;
        if (! regs[reg]) {       /* 使われていないなら */
            regs[reg]=USING_REG; /* そのレジスタを使うことを宣言し */
	    if (i>max_reg_var) max_reg_var=i;
	    return reg;   /* その場所を表す番号を返す */
        }
    }
    /* 空いている場所がないなら、エラー (いったい誰が使ってるの?) */
    error(HPERR); return creg;
}

#if 0
int 
get_register(void)
{
    int i = get_register0();
    printf("# get_register %d\n",i);
    return i;
}
#endif

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

#if FLOAT_CODE
int 
get_dregister(int d)
{    /* 使われていないレジスタを調べる */
    int i,reg;
    for(i=MAX_TMP_FREG+FREG_OFFSET;i>MIN_TMP_FREG+FREG_OFFSET;i--) {
	if (regs[i]) continue;    /* 使われている */
	regs[i]=USING_REG;      /* そのレジスタを使うことを宣言し */
	return i;   /* その場所を表す番号を返す */
    }
    /* search register stack */
    for(i=0;i<freg_sp;i++) {
	if ((reg=freg_stack[i])>=0) {
            code_dassign_lvar(
                (freg_stack[i]=new_lvar(SIZE_OF_DOUBLE)),reg,1); 
            freg_stack[i]= freg_stack[i]-REG_LVAR_OFFSET;
	    return reg;
	}
    }
    for(i=0;i<FREG_VAR_BASE-REG_VAR_MIN;i++) {
        reg =FREG_VAR_BASE-i+FREG_OFFSET;
        if (! regs[reg]) {       /* 使われていないなら */
            regs[reg]=USING_REG; /* そのレジスタを使うことを宣言し */
	    if (i>max_freg_var) max_freg_var=i;
	    return reg;   /* その場所を表す番号を返す */
        }
    }
    /* 空いている場所がないなら、エラー (いったい誰が使ってるの?) */
    error(REG_ERR); return freg;
}

#if 0
int 
get_dregister(int d)
{
    int i = get_dregister0(d);
printf("# get_dregister %d\n",i);
    return i;
}
#endif

int
pop_fregister(void)
{     /* レジスタから値を取り出す */
    return freg_stack[--freg_sp];
}
#endif

int
get_lregister0()
{
    int i;
    for(i=LREG_OFFSET+1;i<REAL_MAX_LREGISTER+LREG_OFFSET;i++) {
	if (regs[i]==0) {
// printf("# get_lregister %d\n",i);
	    return i;
	}
    }
    return -1;
}

int
get_lregister()
{
    int h,l,i;
    i = get_lregister0();
    if (i==-1) return -1;
    h = get_register(); 
    if (h==-1) return -1;
    regv_h(i) = h;
    l = get_register(); 
    if (l==-1) { free_register(h); return -1; }
    regv_l(i) = l;
    regs[i]=USING_REG;
    return i;
}

int
get_lregister_var(NMTBL *n)
{
    int i,j,ll;
    int max_reg_var_save=max_reg_var;
    ll = get_lregister0();
    if (ll==-1) goto not_found;
    if (regs[ll]==0) {
	for(i=0;i<REG_VAR_BASE-REG_VAR_MIN;i++) {
	    if (! regs[REG_VAR_BASE-i]) {       /* 使われていないなら */
		/* そのレジスタを使うことを宣言し */
		regs[REG_VAR_BASE-i]=USING_REG; 
		if (i>max_reg_var) max_reg_var=i;
		for(j=0;j<REG_VAR_BASE-REG_VAR_MIN;j++) {
		    if (! regs[REG_VAR_BASE-j]) {       
			/* 使われていないなら */
			/* そのレジスタを使うことを宣言し */
			regs[REG_VAR_BASE-j]=USING_REG; 
			if (j>max_reg_var) max_reg_var=j;
			/* その場所を表す番号を返す */
			regs[ll]=USING_REG;
			regv_l(ll) = REG_VAR_BASE-j;
			regv_h(ll) = REG_VAR_BASE-i;
			return list3(LREGISTER,ll,(int)n); 
		    }
		}
		/* ひとつしかなかった */
		regs[REG_VAR_BASE-i]=0; 
		max_reg_var=max_reg_var_save;
		goto not_found;
	    }
	}
    }
not_found:
    return list2(LVAR,new_lvar(SIZE_OF_LONGLONG));
}

void
emit_pop_free(int xreg)
{
    if (xreg>=0)
	free_register(xreg);
}

void

free_register(int i) {    /* いらなくなったレジスタを開放 */
// printf("# free_register %d\n",i);
    regs[i]=0;
    if (is_longlong_reg(i)) {
	regs[regv_l(i)]=0;
	regs[regv_h(i)]=0;
	//regv_l(i)=0;
	//regv_h(i)=0;
    }
}

extern void
use_ptr_cache(int r)
{
    regs[r]=PTRC_REG;
}

int
get_input_dregister_var(int i,NMTBL *n,int is_code,int d)
{
    if (is_code) {
	if(!(i<FREG_VAR_BASE-FREG_VAR_MIN)) return 0;
	i = FREG_VAR_BASE-i+FREG_OFFSET;
    } else {
	if (i<0||i>=MAX_INPUT_DREGISTER_VAR) return 0;
	i = i+MIN_TMP_FREG+FREG_OFFSET;
    }
    return list3(DREGISTER,i,(int)n);
}

int
get_input_lregister_var(int i,NMTBL *n,int is_code)
{
    int ll;
    ll = get_lregister0();
    if (i!=-1) {
	if (is_code) {
	    if(!(i<REG_VAR_BASE-REG_VAR_MIN)) return 0;
	    i = REG_VAR_BASE-i;
	} else {
	    if (i<0||i>=MAX_INPUT_REGISTER_VAR) return 0;
	    i = i+MIN_TMP_REG;
	}
#if ENDIAN_L==0
	regv_l(ll)=i;
	regv_h(ll)=i+1;
#else
	regv_h(ll)=i;
	regv_l(ll)=i+1;
#endif
    } else { error(-1); ll=LREG_OFFSET+2; }
    return list3(LREGISTER,ll,(int)n);
}

int
get_input_register_var(int i,NMTBL *n,int is_code)
{
    if (is_code) {
	if(!(i<REG_VAR_BASE-REG_VAR_MIN)) return 0;
	i = REG_VAR_BASE-i;
    } else {
	if (i<0||i>=MAX_INPUT_REGISTER_VAR) return 0;
	i = i+MIN_TMP_REG;
    }
    return list3(REGISTER,i,(int)n);
}

/* double register case? */

int
get_input_register_var_1(int i,NMTBL *n,int is_code)
{
    if (is_code) {
	if(!(i<REG_VAR_BASE-REG_VAR_MIN)) return 0;
	i = REG_VAR_BASE-i;
    } else {
	if (i<0||i>=MAX_INPUT_REGISTER_VAR+1) return 0;
	i = i+MIN_TMP_REG;
    }
    return list3(REGISTER,i,(int)n);
}

int
free_register_count(int d)
{
    int i,count,fcount;
    fcount = count = 0;
    for(i=0;i<MAX_REGISTER;i++) {
        if (! regs[i]) count++;
    }
    for(i=0;i<MAX_FREGISTER;i++) {
        if (! regs[i+FREG_OFFSET]) fcount++;
    }
    printf("# free reg %d freg %d\n",count,fcount);
    return d?fcount:count;
}

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

void
free_all_register(void)
{
    int i;
// printf("# free_all register\n");
#if LONGLONG_CODE
    for(i=0;i<REAL_MAX_LREGISTER;i++) {
	regs[i+LREG_OFFSET]=0; 
	regv_l(i+LREG_OFFSET) = 0;
	regv_h(i+LREG_OFFSET) = 0;
    }
    lreg = 0;
    // set_lreg(LREG_LREGISTER,0);
#endif
    for(i=0;i<MAX_REGISTER;i++) { regs[i]=0; }
    for(i=0;i<MAX_FREGISTER;i++) { regs[i+FREG_OFFSET]=0; }
#if FLOAT_CODE
    freg = get_dregister(1);
    set_freg(FREG_FREGISTER,0);
#endif
    ireg = creg = get_register();
    set_ireg(CREG_REGISTER,0);
    return;
}

extern int
code_register_overlap(int s,int t)
{
    switch(car(s)) {
    case REGISTER:
	switch(car(t)) {
	case DREGISTER: case FREGISTER: break;
	case REGISTER:
	    if(cadr(s)==cadr(t)) return 1;
	    break;
	case LREGISTER:
	    if(cadr(s)==regv_l(cadr(t))) return 1;
	    if(cadr(s)==regv_h(cadr(t))) return 1;
	    break;
	}   
	break;
    case DREGISTER:
    case FREGISTER:
	switch(car(t)) {
	case REGISTER: case LREGISTER: break;
	case DREGISTER: case FREGISTER:
	    if(cadr(s)==cadr(t)) return 1;
	    break;
	}   
	break;
    case LREGISTER:
	switch(car(t)) {
	case DREGISTER: case FREGISTER: break;
	case REGISTER:
	    if(cadr(t)==regv_l(cadr(s))) return 1;
	    if(cadr(t)==regv_h(cadr(s))) return 1;
	    break;
	case LREGISTER:
	    if(regv_l(cadr(t))==regv_l(cadr(s))) return 1;
	    if(regv_l(cadr(t))==regv_h(cadr(s))) return 1;
	    if(regv_h(cadr(t))==regv_l(cadr(s))) return 1;
	    if(regv_h(cadr(t))==regv_h(cadr(s))) return 1;
	    break;
	}   
	break;
    }   
    return 0;
}

void
register_usage(char *s)
{
#if 1
    int i,j;
#endif
#define USAGE_MAX 4
    if (chk) return;
    if (!lsrc) return;
    printf("# %d: %s:",lineno,s);
    if (ireg) printf(" creg=%s",register_name(ireg));
    if (freg) printf(" freg=%s",fregister_name(freg));
    if (lreg) printf(" lreg=%s,%s",lregister_name_high(lreg),
	lregister_name_low(lreg));
#if 1
    for(j=0,i=0;i<MAX_REGISTER;i++) if (regs[i]) j++;
    if (j>USAGE_MAX) {
	printf("\n# regs:");
	for(i=0;i<MAX_REGISTER;i++) {  printf("%d",regs[i]); }
    }
    if (reg_sp>0) {
	printf(" stack ");
	for(i=reg_sp;i>0;i--) {
	    if(reg_stack[i-1]>=0) {
		printf(" %s",register_name(reg_stack[i-1]));
            } else 
		printf(",%d",reg_stack[i-1]);
	}
    }
    for(j=0,i=0;i<MAX_FREGISTER;i++) if (regs[i+FREG_OFFSET]) j++;
    if (j>USAGE_MAX) {
	printf("\n# freg:");
	for(i=0;i<MAX_FREGISTER;i++) {  printf("%d",regs[i+FREG_OFFSET]); }
    }
    if (freg_sp>0) {
	printf(" fstack ");
	for(i=freg_sp;i>0;i--) {
	    if(freg_stack[i-1]>=0) {
		printf(" %s",fregister_name(freg_stack[i-1]));
            } else 
		printf(",%d",freg_stack[i-1]);
	}
    }

    for(j=0,i=0;i<REAL_MAX_LREGISTER;i++) if (regs[i+LREG_OFFSET]) j++;
    if (j>USAGE_MAX) {
	printf("\n# lreg:");
	for(i=0;i<REAL_MAX_LREGISTER;i++) {  printf("%d",regs[i+LREG_OFFSET]); }
    }
    if (lreg_sp>0) {
	printf(" lstack ");
	for(i=lreg_sp;i>0;i--) {
	    if(lreg_stack[i-1]>=0) {
		printf(" %s",lregister_name_high(lreg_stack[i-1]));
		printf(",%s",lregister_name_low(lreg_stack[i-1]));
            } else 
		printf(",%d",lreg_stack[i-1]);
	}
    }
#endif
    printf("\n");
}

#if 0
void
register_usage(char *s)
{
#if 1
    int i;
#endif
    if (chk) return;
    if (!lsrc) return;
    printf("# %d: %s:",lineno,s);
    if (ireg) printf(" creg=%s",register_name(ireg));
    if (freg) printf(" freg=%s",fregister_name(freg));
    if (lreg) printf(" lreg=%s,%s",lregister_name_high(lreg),
	lregister_name_low(lreg));
#if 1
    printf("\n# regs:");
    for(i=0;i<MAX_REGISTER;i++) {  printf("%d",regs[i]); }
    printf(" stack ");
    for(i=reg_sp;i>0;i--) {
	if(reg_stack[i-1]>=0)
	    printf(" %s",register_name(reg_stack[i]));
    }
    printf("\n# freg:");
    for(i=0;i<MAX_FREGISTER;i++) {  printf("%d",regs[i+FREG_OFFSET]); }
    printf(" stack ");
    for(i=freg_sp;i>0;i--) {
	if(freg_stack[i-1]>=0)
	    printf(" %s",fregister_name(freg_stack[i]));
    }
    printf("\n# lreg:");
    for(i=0;i<REAL_MAX_LREGISTER;i++) {  printf("%d",regs[i+LREG_OFFSET]); }
    printf(" stack ");
    for(i=lreg_sp;i>0;i--) {
	if(lreg_stack[i-1]>=0)
	    printf(" %s",lregister_name_high(lreg_stack[i]));
	    printf(",%s",lregister_name_low(lreg_stack[i]));
    }
#endif
    printf("\n");
}
#endif

void

gexpr_init(void)
{
    while(reg_sp > 0) {
	error(-1);
	free_register(reg_stack[--reg_sp]);
    }
    while(freg_sp > 0) {
	error(-1);
	free_register(freg_stack[--freg_sp]);
    }
    while(lreg_sp > 0) {
	error(-1);
	free_register(lreg_stack[--lreg_sp]);
    }
    use_int0();
    text_mode();
    gexpr_code_init();
    register_usage("gexpr_init");
}


void

emit_init(void)
{
    /* called before each declaration */

    free_all_register();
    max_reg_var=-1; max_freg_var=-1;
    reg_sp = 0;
    freg_sp = 0;
}


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

int
get_dregister_var(NMTBL *n,int d)
{
    int i;
    for(i=0;i<FREG_VAR_BASE-FREG_VAR_MIN;i++) {
        if (! regs[FREG_VAR_BASE-i+FREG_OFFSET]) {       /* 使われていないなら */
            regs[FREG_VAR_BASE-i+FREG_OFFSET]=USING_REG; /*そのレジスタを使うことを宣言し*/
	    if (i>max_freg_var) max_freg_var=i;
	    /* その場所を表す番号を返す */
	    return list3(DREGISTER,
		FREG_VAR_BASE-i+FREG_OFFSET,(int)n); 
        }
    }
    return list2(LVAR,new_lvar(SIZE_OF_DOUBLE));
}

void

emit_push()
{
    int new_reg;
    if (!is_int_reg(creg)) error(-1);
    if (reg_sp>MAX_MAX) error(-1);
    new_reg = get_register();       /* 絶対に取れる */
    reg_stack[reg_sp++] = creg;     /* push するかわりにレジスタを使う */
    ireg = creg = new_reg;
}

int
emit_pop(int type)
{
    int xreg,reg;
    xreg=pop_register();
    if (xreg<= -REG_LVAR_OFFSET) {
	reg = get_register();
        code_rlvar(REG_LVAR_OFFSET+xreg,reg);
	free_lvar(REG_LVAR_OFFSET+xreg);
	xreg = reg;
    }
    return xreg;
}

static int code_base;

extern void
code_ptr_cache_def(int r,NMTBL *nptr)
{
    char *rrn = register_name(r);
    if (nptr->sc==STATIC) {
	printf("\taddis %s,r31,ha16(_%s-L_%d)\n",
		 rrn,nptr->nm,code_base);
	printf("\tla %s,lo16(_%s-L_%d)(%s)\n",
		 rrn,nptr->nm,code_base,rrn);
    } else {
	printf("\taddis %s,r31,ha16(L_%s$non_lazy_ptr-L_%d)\n",
		 rrn,nptr->nm,code_base);
	printf("\tlwz %s,lo16(L_%s$non_lazy_ptr-L_%d)(%s)\n",
		 rrn,nptr->nm,code_base,rrn);
    }
}

static char *cload(int sz) { return sz==1?"lbz":sz==SIZE_OF_SHORT?"lhz":"lwz"; }
static char *cstore(int sz) { return sz==1?"stb":sz==SIZE_OF_SHORT?"sth":"stw"; }

static void
cext(int sign,int sz,int reg)
{
    char *crn = register_name(reg);
    if (sign) {
	if (sz==1) 
	    printf("\textsb %s,%s\n",crn,crn);
	else if (sz==SIZE_OF_SHORT)
	    printf("\textsh %s,%s\n",crn,crn);
    } else {
	if (sz==1) 
	    printf("\trlwinm %s,%s,0,0xff\n",crn,crn);
	else if (sz==SIZE_OF_SHORT)
	    printf("\trlwinm %s,%s,0,0xffff\n",crn,crn);
    }
}


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

static void 
code_add(int reg,int offset,int r)
{
    char *crn = register_name(reg);
    char *rrn = register_name(r);
    if (offset==0) {
	if(r!=reg)
	    printf("\tmr %s,%s\n",crn,rrn);
    } else if (LARGE_OFFSET(offset)) {
	printf("\tla   %s,ha16(%d)(%s)\n",crn,offset,rrn);
	printf("\taddi %s,%s,lo16(%d)\n",crn,crn,offset);
    } else
	printf("\taddi %s,%s,%d\n",crn,rrn,offset);
}

static void 
code_ld(char *ld,int reg,int offset,int r)
{
    char *crn = register_name(reg);
    char *rrn = register_name(r);
    if (LARGE_OFFSET(offset)) {
	printf("\tla   %s,ha16(%d)(%s)\n",crn,offset,rrn);
	printf("\t%s %s,%s,lo16(%d)\n",ld,crn,crn,offset);
    } else
	printf("\t%s %s,%d(%s)\n",ld,crn,offset,rrn);
}

static void 
code_ldf(char *ld,char *crn,int offset,int r)
{
    char *rrn = register_name(r);
    int reg;
    char *lrn;
    if (offset<-32768||32767<offset) {
	lrn = register_name(reg = get_register());
	printf("\tla   %s,ha16(%d)(%s)\n",lrn,offset,rrn);
	printf("\t%s %s,%s,lo16(%d)\n",ld,crn,lrn,offset);
	free_register(reg);
    } else
	printf("\t%s %s,%d(%s)\n",ld,crn,offset,rrn);
}

void
code_gvar(int e1,int reg) {
    use_int(reg);
    code_add(reg,cadr(e1),get_ptr_cache((NMTBL*)caddr(e1)));
    return;
}

void
code_rgvar(int e1,int reg) {
    use_int(reg);
    code_ld("lwz",reg,cadr(e1),get_ptr_cache((NMTBL*)caddr(e1)));
}

void
code_crgvar(int e1,int reg,int sign,int sz){
    use_int(reg);
    code_ld(cload(sz),reg,cadr(e1),get_ptr_cache((NMTBL*)caddr(e1)));
    cext(sign,sz,reg);
}



void
code_register(int e2,int reg) {
    use_int(reg);
    if (reg!=e2)
	printf("\tmr %s,%s\n",register_name(reg),register_name(e2));
}

extern void
code_i2c(int reg)
{
    use_int(reg);
    cext(1,1,reg);
}

extern void
code_i2s(int reg)
{
    use_int(reg);
    cext(1,SIZE_OF_SHORT,reg);
}

extern void
code_u2uc(int reg)
{
    use_int(reg);
    cext(0,1,reg);
}

extern void
code_u2us(int reg)
{
    use_int(reg);
    cext(0,SIZE_OF_SHORT,reg);
}

void
code_rlvar(int e2,int reg) {
    use_int(reg);
    lvar_intro(e2);
    printf("\tlwz %s,",register_name(reg));
    lvar(e2);
}

void
code_crlvar(int e2,int reg,int sign,int sz) {
    use_int(reg);
    lvar_intro(e2);
    printf("\t%s %s,",cload(sz),register_name(reg));
    lvar(e2);
    cext(sign,sz,reg);
}

void
code_fname(NMTBL *n,int reg) {
    int r;
    use_int(reg);
    r = get_ptr_cache(n);
    if(r!=reg)
	printf("\tmr %s,%s\n",register_name(reg),register_name(r));
    return;
}

void
code_label_value(int label,int reg) {
    char *crn;
    use_int(reg);
    crn = register_name(reg);
    printf("\taddis %s,r31,ha16(L_%d-L_%d)\n",crn,label,code_base);
    printf("\tla %s,lo16(L_%d-L_%d)(%s)\n",crn,label,code_base,crn);
    return;
}

void
code_const(int e2,int reg) {
    char *crn;
    use_int(reg);
    crn = register_name(reg);
    // printf("# 0x%08x\n",e2);
    if (-32768<e2&&e2<32768)
	printf("\tli %s,%d\n",crn,e2);
    else {
	printf("\tlis %s,ha16(%d)\n",crn,e2);
	printf("\taddi %s,%s,lo16(%d)\n",crn,crn,e2);
    }
}


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


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


void
code_lnot(int creg) {
    use_int(creg);
    printf("\tsubfic r0,%s,0\n", register_name(creg));
    printf("\tadde %s,r0,%s\n", register_name(creg),register_name(creg));
}

void
code_preinc(int e1,int e2,int dir,int sign,int sz,int reg) {
    char *xrn,*drn;
    if (car(e2)==REGISTER) {
	use_int(reg);
	printf("\taddi %s,%s,%d\n", 
		register_name(cadr(e2)),register_name(cadr(e2)), dir);
	if (cadr(e2)!=reg)
	    printf("\tmr %s,%s\n",register_name(reg),register_name(cadr(e2)));
	return;
    } 
    g_expr(e2);
    if (!is_int_reg(creg)) error(-1);
    xrn = register_name(creg);
    if (reg==USE_CREG) {
	reg=get_register(); if (!reg) error(-1);
	drn = register_name(reg);
	set_ireg(reg,0);
    } else {
	drn = register_name(reg);
    }
    printf("\t%s %s,0(%s)\n",cload(sz),drn,xrn);
    if (use) cext(sign,sz,reg);
    printf("\taddi %s,%s,%d\n",drn,drn,dir);
    printf("\t%s %s,0(%s)\n",cstore(sz),drn,xrn);
}


void
code_postinc(int e1,int e2,int dir,int sign,int sz,int reg) {
    char *xrn,*crn,*nrn;
    int nreg;
    if (car(e2)==REGISTER) {
	use_int(reg);
	printf("\tmr %s,%s\n",register_name(reg),register_name(cadr(e2)));
	printf("\taddi %s,%s,%d\n", 
	    register_name(cadr(e2)),register_name(cadr(e2)),dir);
	return;
    } 
    g_expr(e2);
    if (!is_int_reg(creg)) error(-1);
    crn = register_name(creg);
    nreg=get_register(); if (!nreg) error(-1);
    nrn = register_name(nreg);
    if (reg==USE_CREG) {
	reg=get_register(); if (!reg) error(-1);
	xrn = register_name(reg);
	set_ireg(reg,0);
    } else {
	xrn = register_name(reg);
    }
    printf("\t%s %s,0(%s)\n",cload(sz),xrn,crn);
    if (use) cext(sign,sz,reg);
    printf("\taddi %s,%s,%d\n",nrn,xrn,dir);
    printf("\t%s %s,0(%s)\n",cstore(sz),nrn,crn);
    free_register(nreg);
}


void
code_return(int creg) {
    char *crn;
    use_int(creg);
    crn = register_name(creg);
    printf("\taddis %s,r31,ha16(L_%d-L_%d)\n",crn,retcont,code_base);
    printf("\tla %s,lo16(L_%d-L_%d)(%s)\n",crn,retcont,code_base,crn);
}

#define R1SAVE 1

void
code_environment(int creg) {
    /* save frame pointer */
    use_int(creg);
#if R1SAVE
    printf("\tlwz %s,0(r1)\n",register_name(creg));
#else
    int l = 0;
    printf("\tla %s,",register_name(creg));
    printf("lo16(%d+L_%d)(r30)\n",FUNC_LVAR,lvar_offset_label);
#endif
}

static int rexpr_bool(int e1,int reg);

#if FLOAT_CODE
static int drexpr_bool(int e1,int reg);
#endif

#if LONGLONG_CODE
static int lrexpr_bool(int e1,int reg)
{
    return 0;
}
#endif



void
code_bool(int e1,int reg) {
    char *xrn;
    int e2,e3;

    if (rexpr_bool(e1,reg)) return;
#if FLOAT_CODE
    else if (drexpr_bool(e1,reg)) return;
#endif
#if LONGLONG_CODE
    else if (lrexpr_bool(e1,reg)) return;
#endif

    b_expr(e1,1,e2=fwdlabel(),1);  /* including > < ... */
    if (use) {
	use_int(reg);
	xrn = register_name(reg);
	printf("\tli %s,0\n",xrn);
	jmp(e3=fwdlabel());
	fwddef(e2);
	printf("\tli %s,1\n",xrn);
	fwddef(e3);
    } else {
	fwddef(e2);
    }
}

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

char *
code_ugt(int cond) {
    return (cond?"gt":"le");
}

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

char *
code_uge(int cond) {
    return (cond?"ge":"lt");
}

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

static int cmpflag = 0;

static void
inc_cmpflag()
{
    cmpflag = (cmpflag+1)%8;
}

void
code_cmp_crgvar(int e1,int reg,int sz,int label,int cond) {
    int r;
    use_int(reg);
    code_ld(cload(sz),reg,cadr(e1),get_ptr_cache((NMTBL*)caddr(e1)));
    cext(0,sz,r);
    inc_cmpflag();
    printf("\tcmpwi cr%d,%s,0\n",cmpflag,register_name(reg));
    jcond(label,cond);
}


void
code_cmp_crlvar(int e2,int reg, int sz,int label,int cond) {
    char *crn;
    use_int(reg);
    crn = register_name(reg);
    lvar_intro(e2);
    printf("\t%s %s,",cload(sz),crn);
    lvar(e2);
    cext(0,sz,reg);
    code_cmp_register(reg,label,cond);
}


void
code_cmp_rgvar(int e1,int reg,int label,int cond) {
    use_int(reg);
    code_ld("lwz",reg,cadr(e1),get_ptr_cache((NMTBL*)caddr(e1)));
    code_cmp_register(reg,label,cond);
}


void
code_cmp_rlvar(int e2,int reg,int label,int cond) {
    char *crn;
    use_int(reg);
    crn = register_name(reg);
    lvar_intro(e2);
    printf("\tlwz %s,",crn);
    lvar(e2);
    code_cmp_register(reg,label,cond);
}


void
code_cmp_register(int e2,int label,int cond) {
    use_int(e2);
    inc_cmpflag();
    printf("\tcmpwi cr%d,%s,0\n",cmpflag,register_name(e2));
    jcond(label,cond);
}


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

    use_int(creg);
    crn = register_name(creg);
    lb = emit_string_label();
    ascii((char *)cadr(e1));
    if (output_mode==TEXT_EMIT_MODE) {
	printf(".text\n");
    } else {
	text_mode();
    }
    printf("\taddis %s,r31,ha16(L_%d-L_%d)\n",crn,lb,code_base);
    printf("\tla %s,lo16(L_%d-L_%d)(%s)\n",crn,lb,code_base,crn);
}

#define MAX_COPY_LEN 20
// #define MAX_COPY_LEN 10

void

emit_copy(int from,int  to,int length,int offset,int value,int det)
{
    char *frn;
    char *trn;
    char *drn;
    int fix = 0;
    char *memmove = "memmove";
    int dreg = get_register(); if (!dreg) error(-1);

    drn	 = register_name(dreg);
    use_int(from);
    use_int(to);
    frn =	register_name(from);
    trn =	register_name(to);

    /* length <0 means upward direction copy */
    switch (length) {
    case 0:	break;
    case 1: case -1:
	printf("\tlbz %s,%d(%s)\n",drn,offset,frn);
	printf("\tstb %s,%d(%s)\n",drn,offset,trn);
	break;
    case 2: case -2:
	printf("\tlhz %s,%d(%s)\n",drn,offset,frn);
	printf("\tsth %s,%d(%s)\n",drn,offset,trn);
	break;
    case 4: case -4:
	printf("\tlwz %s,%d(%s)\n",drn,offset,frn);
	printf("\tstw %s,%d(%s)\n",drn,offset,trn);
	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;
	}
	clear_ptr_cache();
	code_save_stacks();
	printf("\tli r5,%d\n",length>0?length:-length);
	printf("\tmr r4,%s\n",frn);
	printf("\tmr r3,%s\n",trn);
	/* offset should be ignored */
        /* overrap must be allowed */
	printf("\tbl L_%s$stub\n",memmove);
	extern_define(memmove,0,FUNCTION,1);
        fix=0;
	set_ireg(RET_REGISTER,0);
	if (creg!=to) {
	    free_register(to); to = creg;
	}
	break;
    }
    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 (fix) printf("\taddi %s,%s,%d\n",trn,trn,fix);
	if(creg!=to) {
	    free_register(creg); creg=to; ireg=to;
	}
    }
    free_register(dreg);
}

int
struct_push(int e4,int t,int arg) 
{
    int length,count;
    int dreg,sreg; char *drn,*crn,*srn;
    g_expr(e4);
    if (!is_int_reg(creg)) error(-1);
    length=size(t); 
    if(length%SIZE_OF_INT) {
	length += SIZE_OF_INT - (length%SIZE_OF_INT);
    }
    dreg = get_register(); if (!dreg) error(-1);
    drn = register_name(dreg);
    crn = register_name(creg);
    if (length<MAX_COPY_LEN) {
	sreg = get_register(); if (!sreg) error(-1);
	srn = register_name(sreg);
	code_lvar(cadr(arg),dreg);
	for(count=0;count<length;count+=SIZE_OF_INT) {
	    printf("\tlwz %s,%d(%s)\n",srn,count,crn);
	    printf("\tstwu %s,%d(%s)\n",srn,count,drn);
	}
	free_register(sreg);
	free_register(dreg);
	return length/SIZE_OF_INT;
    } else {
	code_lvar(cadr(arg),dreg);
	/* downward direction copy */
	emit_copy(creg,dreg,length,0,0,1);
    }
    free_register(dreg);
    return length/SIZE_OF_INT;
}

void
set_ireg(int reg,int mode)
{
    if (!is_int_reg(reg)) error(-1);
    if (reg!=creg) {
	clear_ptr_cache_reg(reg);
	if (ireg && reg!=ireg ) {
	    free_register(ireg);
	    if (mode) {
		printf("\tmr %s,%s\n",register_name(reg),register_name(ireg));
	    }
	}
	free_register(creg);
	regs[reg]=USING_REG;
    }
    creg = ireg = reg;
}

void
set_freg(int reg,int mode)
{
    if (!is_float_reg(reg)) error(-1);
    if (reg!=creg) {
	if (freg && reg!=freg) {
	    free_register(freg);
	    if (mode) {
		printf("\tfmr %s,%s\n",fregister_name(reg),fregister_name(freg));
	    }
	}
	// if (creg!=ireg) free_register(creg);
	regs[reg]=USING_REG;
    }
    creg = freg = reg;
}

void
set_lreg(int reg,int mode)
{
    if (reg==RET_LREGISTER) {
	regv_l(reg) = RET_LREGISTER_L;
	regv_h(reg) = RET_LREGISTER_H;
    }
    if (!is_longlong_reg(reg)) error(-1);
    if (reg!=creg) {
	if (lreg && reg!=lreg) {
	    if (mode) {
		printf("\tmr %s,%s\n",
		    lregister_name_low(reg),lregister_name_low(lreg));
		printf("\tmr %s,%s\n",
		    lregister_name_high(reg),lregister_name_high(lreg));
	    }
	    free_register(lreg);
	}
	free_register(creg);
	regs[reg]=USING_REG;
	clear_ptr_cache_reg(regv_l(reg));
	regs[regv_l(reg)]=USING_REG;
	clear_ptr_cache_reg(regv_h(reg));
	regs[regv_h(reg)]=USING_REG;
    }
    creg = lreg = reg;
}

void
set_lreg_operand(int reg,int mode)
{
    // save_stack,clear_ptr_cache is assumed    
    if (!is_longlong_reg(reg)) { error(-1); return; }
    if (mode) {
	if (regv_l(reg)!=3)
	    printf("\tmr r3,%s\n", lregister_name_high(reg));
	if (regv_l(reg)!=4)
	    printf("\tmr r4,%s\n", lregister_name_low(reg));
    }
}

void
set_lreg_operand1(int reg,int mode)
{
    // save_stack,clear_ptr_cache is assumed    
    if (!is_longlong_reg(reg)) { error(-1); return; }
    if (mode) {
	if (regv_l(reg)!=5)
	    printf("\tmr r5,%s\n", lregister_name_high(reg));
	if (regv_l(reg)!=6)
	    printf("\tmr r6,%s\n", lregister_name_low(reg));
    }
}

void
use_reg(int arg)
{
// printf("# use reg %d\n",arg);
    if (arg<0||arg> REAL_MAX_REGISTER+REAL_MAX_FREGISTER+ REAL_MAX_LREGISTER)
	error(-1);
    clear_ptr_cache_reg(arg);
    regs[arg]=USING_REG;
    if (is_longlong_reg(arg)) {
	clear_ptr_cache_reg(regv_l(arg));
	regs[regv_l(arg)]=USING_REG;
	clear_ptr_cache_reg(regv_h(arg));
	regs[regv_h(arg)]=USING_REG;
    }
}

void
code_save_input_registers(int dots)
{
    int args;
    NMTBL *n;
    int reg;
    int tag;
    int lvar;
    int t;
    /* fnptr->dsp=list4(type,fnptr->dsp,(int)n,0); */
    int reg_offset = 0;
    int offset = 0;
    int reg_var = 0;

    for(args = fnptr->dsp;args;args = cadr(args)) {
	n = (NMTBL *)caddr(args);
	tag = n->sc;
	reg = n->dsp;
	if (!n||n==&null_nptr) error(REG_ERR);
	if (tag==REGISTER) {
	    /* regs[reg]==INPUT_REG case should be considered */
	    n->dsp = offset;
	    offset+=SIZE_OF_INT;
	    t = INT;
	    reg += reg_offset; /* for duplicated floating point argument */
	    reg_var++;
	} else if (tag==DREGISTER) {
	    /* regs[reg]==INPUT_REG case should be considered */
	    n->dsp = offset;
	    t = n->ty;
	    if(t==FLOAT) { offset+=SIZE_OF_FLOAT; reg_offset+=1; }
	    else if(t==DOUBLE) { offset+=SIZE_OF_DOUBLE; reg_offset+=2; }
	    else error(-1);
	    reg_var += 2;
	} else if (tag==LREGISTER) {
	    /* regs[reg]==INPUT_REG case should be considered */
	    n->dsp = offset;
	    t = n->ty;
	    offset+=SIZE_OF_LONGLONG; reg_offset+=2;
	    reg_var += 2;
	} else {
	    offset += size(n->ty);
	    continue;
	}
	n->sc  = LVAR;
	lvar = list2(LVAR,n->dsp);
	g_expr_u(assign_expr0(
	    list2(LVAR,n->dsp),list3(tag,reg,(int)n),n->ty,t));
	if (tag==REGISTER||tag==DREGISTER||tag==FREGISTER||tag==LREGISTER) {
	    free_register(reg);
	}
    }
    if (dots) {
	while ((reg = get_input_register_var(reg_var,0,0))) {
	    g_expr_u(assign_expr0(
		list2(LVAR,offset),reg,INT,INT));
	    offset+=SIZE_OF_INT;
	    reg_var++;
	}
    }
    my_func_args = offset;
}

static int
not_simple_p(int e3)
{
    return e3==FUNCTION||e3==CONV/*||e3==RSTRUCT*/||e3==STASS|| 
	e3==LLSHIFT||e3==LULSHIFT||e3==LRSHIFT||e3==LURSHIFT||
	e3==LDIV||e3==LUDIV||e3==LMOD||e3==LUMOD||e3==LASSOP||e3==ALLOCA;
}

static int
simple_arg(int e3)
{
    return !contains_p(e3,not_simple_p);
}

int
caller_arg_offset_v(int arg)
{
    return ARG_LVAR_OFFSET+arg*SIZE_OF_INT;
}

void
use_input_reg(int reg,int mode)
{
    if (is_int_reg(reg)) {
	if (ireg&&reg == ireg) {
	    if (creg==ireg) creg = 0;
	    ireg = 0;
	} 
	if (lreg) {
	    if (regv_l(lreg)==reg) {
		regs[lreg]=0;
		if (regv_h(lreg)>reg&&regs[regv_h(lreg)]==USING_REG) {
		    free_register(regv_h(lreg));
		}
		if (creg==lreg) creg = 0;
		lreg = 0;
	    } else if (regv_h(lreg)==reg) {
		regs[lreg]=0;
		if (regv_h(lreg)>reg&&regs[regv_l(lreg)]==USING_REG) {
		    free_register(regv_l(lreg));
		}
		if (creg==lreg) creg = 0;
		lreg = 0;
	    }
	}
    } else if (is_longlong_reg(reg)) {
	use_input_reg(regv_h(reg),0);
	use_input_reg(regv_l(reg),0);
    } else if (is_float_reg(reg)) {
	if (freg&&reg == freg) {
	    if (creg==freg) creg = 0;
	    freg = 0;
	} 
    }
    if (mode) use_reg(reg);
}

static int
compute_complex_arg(int e3,int reg_arg_list,int arg) {
    int t=caddr(e3);
    int e4 = car(e3);
    reg_arg_list = list2(arg,reg_arg_list);
    g_expr_u(assign_expr0(arg,e4,t,t));
    if (car(arg)==REGISTER||car(arg)==DREGISTER||
	car(arg)==FREGISTER||car(arg)==LREGISTER)
	use_input_reg(cadr(arg),1);
    car(e3) = arg;
    return reg_arg_list;
}

static void
increment_function_arg(int e3,int *pnargs,int *preg_arg,int *pfreg_arg) {
    int nargs=0,reg_arg=0,freg_arg=0;
    int t=caddr(e3);
    if (t>=0&&(car(t)==BIT_FIELD)) {
	t = cadr(t);
    }
    if(scalar(t)) {
	nargs ++ ; reg_arg++;
    } else if (t==LONGLONG||t==ULONGLONG) {
	nargs ++ ; reg_arg++;
	nargs ++ ; reg_arg++;
    } else if (t==DOUBLE||t==FLOAT) {
	if (*preg_arg<MAX_INPUT_REGISTER_VAR) {
	    reg_arg += 2;
	}
	freg_arg++;
	nargs += size(t)/SIZE_OF_INT;
    } else if (t>=0&&(car(t)==STRUCT||car(t)==UNION)) {
	nargs += round4(size(t))/SIZE_OF_INT;
    } else {
	error(TYERR);
	nargs ++ ;
    }
    *pnargs += nargs;
    *preg_arg += reg_arg;
    *pfreg_arg += freg_arg;
}

#define AS_SAVE 1
#define AS_ARG  0

static int
get_input_arg(int t,int mode,int nargs,int reg_arg,int freg_arg) 
{
    if (t>=0&&(car(t)==BIT_FIELD)) {
	t = cadr(t);
    }
    if(scalar(t)) {
	if (mode==AS_SAVE) {
	    return get_register_var(0);
	} else if (reg_arg>=MAX_INPUT_REGISTER_VAR) {
	    return list2(LVAR,caller_arg_offset_v(nargs));
	} else 
	    return get_input_register_var(reg_arg,0,0);
    } else if (t==LONGLONG||t==ULONGLONG) {
	if (mode==AS_SAVE) {
	    return get_lregister_var(0);
	} else if (reg_arg+1>=MAX_INPUT_REGISTER_VAR)  {
	    return list2(LVAR,caller_arg_offset_v(nargs));
	} else
	    return get_input_lregister_var(reg_arg,0,0);
    } else if (t==FLOAT) {
	if (mode==AS_SAVE) {
	    return get_dregister_var(0,0);
	} else if (freg_arg>=MAX_INPUT_DREGISTER_VAR) {
	    return list2(LVAR,caller_arg_offset_v(nargs));
	} else
	    return get_input_dregister_var(freg_arg,0,0,0);
    } else if (t==DOUBLE) {
	if (mode==AS_SAVE) {
	    return get_dregister_var(0,1);
	} else if (freg_arg>=MAX_INPUT_DREGISTER_VAR) {
	    return list2(LVAR,caller_arg_offset_v(nargs));
	} else
	    return get_input_dregister_var(freg_arg,0,0,1);
    } else if (t>=0&&(car(t)==STRUCT||car(t)==UNION)) {
	if (mode==AS_SAVE) {
	    return get_register_var(0);
	} else
	    return list2(LVAR,caller_arg_offset_v(nargs));
    } else {
	error(-1);
	return get_register_var(0);
    }
}

int
function(int e1)
{
    int e2,e3,e4,e5,nargs,t,r0,r1;
    int arg,reg_arg,freg_arg,arg_assign;
    int dots;
    int reg_arg_list=0,ret_type,special_lvar;
    NMTBL *fn = 0;
    int jmp = 0;
    char *jrn;
    int complex_;
    int pnargs,preg_arg,pfreg_arg;
    int stargs;

    special_lvar = -1;
#if 0
    ret_type = cadr(cadddr(e1));
    if (ret_type==CHAR) ret_type=INT;

    /* check argments type is DOTS? */
    t = caddr(cadddr(e1));
    if (t==0 || t==DOTS) dots = 1;
    else {
	dots = 0;
	for(t = caddr(cadddr(e1));t;t = cadr(t)) {
	    if (car(t)==DOTS) dots = 1;
	}
    }
#else
    ret_type = function_type(cadddr(e1),&dots);
    if (caddr(cadddr(e1))==0) dots=1;
#endif

    arg_assign = 0;
    e2 = cadr(e1);
    if (car(e2) == FNAME) {	
	fn=(NMTBL *)cadr(e2);
    } else {	
#if 0
	jmp = get_register_var(0);
	if (car(jmp)!=REGISTER) error(-1);
	reg_arg_list = list2(jmp,reg_arg_list);
	g_expr(e2);
	if (!is_int_reg(creg)) error(-1);
	code_register(creg,cadr(jmp));
        /* g_expr(assign_expr0(jmp,e2,INT,INT)); functions are lvalue */
#else
        if (car(e2)==INDIRECT) e2=cadr(e2); // (*func)(i) case
	jmp = get_register_var(0);
	if (car(jmp)!=REGISTER) error(-1);
	reg_arg_list = list2(jmp,reg_arg_list);
        if (!simple_arg(e2)) {
            g_expr_u(assign_expr0(jmp,e2,INT,INT));
        } else
	    arg_assign = list2(assign_expr0(jmp,e2,INT,INT),arg_assign);
#endif
    }
    /* first we execute complex argument to avoid interaction with
       input variables */
    stargs = 0;
    complex_ = 0;
    nargs = reg_arg = freg_arg = 0;
    for (e3 = e1 = reverse0(caddr(e1)); e3; e3 = cadr(e3)) {	
	t=caddr(e3);
	if ((e5= !simple_arg(car(e3)))) {
	    if (complex_) {
		arg = get_input_arg(caddr(complex_),AS_SAVE,
					pnargs,preg_arg,pfreg_arg);
		reg_arg_list = compute_complex_arg(complex_,reg_arg_list,arg);
	    }
	    pnargs=nargs;preg_arg=reg_arg;pfreg_arg=freg_arg;
	    complex_ = e3;
	}
	if (t>=0&&(car(t)==STRUCT||car(t)==UNION)) {
	    // The struct should be pushed after complex arguments.
	    if (e5) { // compute address only, complex_ is me now. Clear it.
		complex_ = 0;
		e4 = car(e3);
		if (car(e4)!=RSTRUCT) error(-1);
		if (!simple_arg(cadr(e4))) {
		    // Calculate complex struct address here.
		    // If simple, leave it.
		    arg = get_register_var(0);
		    g_expr_u(assign_expr0(arg,list2(ADDRESS,car(e3)),INT,INT));
		    car(e3)=arg;
		    reg_arg_list = list2(arg,reg_arg_list);
		    if (car(arg)==REGISTER) use_input_reg(cadr(arg),1);
                    else car(e3) = rvalue_t(arg,INT);
		}
	    }
	    stargs = list4(e3,stargs,nargs,reg_arg);
	}
	increment_function_arg(e3,&nargs,&reg_arg,&freg_arg);
    }

    /* now all input register vars are free */
    code_save_stacks();
    // set_lreg(LREG_LREGISTER,0);
    set_freg(FREG_FREGISTER,0);
    set_ireg(CREG_REGISTER,0);

    //  Struct arguments need emit_copy. it destructs 3 input registers.
    //  But it returns no value on a register. So calculate it here.
    //  We cannot do this in the previous loop, because the copied struct may be
    //  override by other complex arguments. But before this we have to check
    //  complex_.

    if (stargs) {
        if (complex_) {
            arg = get_input_arg(caddr(complex_),AS_SAVE,
                                    pnargs,preg_arg,pfreg_arg);
            reg_arg_list = compute_complex_arg(complex_,reg_arg_list,arg);
        }   
        for(stargs=reverse0(stargs);stargs;stargs = cadr(stargs)) {
            e3 = car(stargs);
            e4 = car(e3);
            t  = caddr(e3);
            arg = get_input_arg(t,AS_ARG,caddr(stargs),cadddr(stargs),0);
            struct_push(e4,t,arg);
            car(e3)=0;  // done
        }
    } else {
	//  last complex argument can use input register
	if (complex_) {
	    arg = get_input_arg(caddr(complex_),AS_ARG,pnargs,preg_arg,pfreg_arg);
	    reg_arg_list = compute_complex_arg(complex_,reg_arg_list,arg);
	}
    }

    nargs = reg_arg = freg_arg = 0;
    // calc stack arguments first, it may requires extra registers,
    // and we can still use input registers now.
    for (e3 = e1; e3; 
		increment_function_arg(e3,&nargs,&reg_arg,&freg_arg),
		e3 = cadr(e3)) {	
	if (!(e4=car(e3))) continue;
	t=caddr(e3);
	arg = get_input_arg(t,AS_ARG,nargs,reg_arg,freg_arg);
	if (car(arg)!=LVAR) continue;
	g_expr_u(assign_expr0(arg,e4,t,t));
	if (t==LONGLONG||t==ULONGLONG) {
	    if (reg_arg+1==MAX_INPUT_REGISTER_VAR) {
		// half register, half memory case
		arg_assign = list2(
		    assign_expr0(r0=get_input_register_var(reg_arg,0,0),
			arg,INT,INT),
		    arg_assign);
		use_input_reg(cadr(r0),1);
	    }
	}
	car(e3)=0;  // done
    }
    nargs = reg_arg = freg_arg = 0;
    for (e3 = e1; e3; 
		increment_function_arg(e3,&nargs,&reg_arg,&freg_arg),
		e3 = cadr(e3)) {	
	if (!(e4=car(e3))) continue;
	t=caddr(e3);
	arg = get_input_arg(t,AS_ARG,nargs,reg_arg,freg_arg);
	if(scalar(t)) {
	    reg_arg_list = list2(arg,reg_arg_list);
	    /* protect from input register free */
	    if (car(arg)==REGISTER)
		use_input_reg(cadr(arg),1);
	    g_expr_u(assign_expr0(arg,e4,t,t));
	} else if (t==LONGLONG||t==ULONGLONG) {
	    if (reg_arg+1==MAX_INPUT_REGISTER_VAR) { 
		// half register, half memory case
		// put whole long long anyway
		arg_assign = list2(
		    assign_expr0(r0=get_input_register_var(reg_arg,0,0),
			arg,INT,INT),
		    arg_assign);
		use_input_reg(cadr(r0),1);
	    } else {
		if (car(arg)==LREGISTER)
		    use_input_reg(cadr(arg),1);
	    }
	    reg_arg_list = list2(arg,reg_arg_list);
	    g_expr_u(assign_expr0(arg,e4,t,t));
	} else if (t==DOUBLE||t==FLOAT) {
	    if (reg_arg<MAX_INPUT_REGISTER_VAR) {
		/* sigh... 
                   printf requires floating value in integer registers
                 */
		if (dots) {
		    if (car(e4)==DRLVAR) {
			special_lvar = cadr(e4);
			e5 = list2(LVAR,special_lvar);
		    } else {
			special_lvar = new_lvar(SIZE_OF_DOUBLE);
			g_expr(assign_expr0(
			    (e5=list2(LVAR,special_lvar)),e4,DOUBLE,t));
			reg_arg_list = list2(e5,reg_arg_list);
			e4 = list2(DREGISTER,freg);
			/* freg should not change until XXX */
		    }
		    r0=get_input_register_var(reg_arg,0,0);
		    r1 = reg_arg+1+MIN_TMP_REG;
		    if (regs[r1]==PTRC_REG) 
			clear_ptr_cache_reg(list2(REGISTER,r1));
		    /* else if (regs[r1]) error(-1); */
		    r1=get_input_register_var_1(reg_arg+1,0,0);
		    use_input_reg(cadr(r0),1); /* protect from input register free */
		    use_input_reg(cadr(r1),1); /* protect from input register free */
		    reg_arg_list = list2(r0,reg_arg_list);
		    reg_arg_list = list2(r1,reg_arg_list);
		    arg_assign = list2( assign_expr0(r0,e5,INT,INT), arg_assign);
		    arg_assign = list2( assign_expr0(r1,
			    list2(LVAR,special_lvar+SIZE_OF_INT),
			    INT,INT), arg_assign);
		}
	    }
	    if (dots && freg_arg>=4 && freg_arg<MAX_INPUT_DREGISTER_VAR) { 
		/* oh my god! 
                   it requires integer register and floating register and
                   stack value. You are crazy.
                 */
		arg_assign = list2(
		    assign_expr0(list2(LVAR,caller_arg_offset_v(nargs)),
			    get_input_dregister_var(freg_arg,0,0,1),t,t),
		    arg_assign);
	    }
	    reg_arg_list = list2(arg,reg_arg_list);
	    if (car(arg)==DREGISTER)
		use_input_reg(cadr(arg),1); /* protect from input register free */
	    g_expr_u(assign_expr0(arg,e4,t,t)); /* XXX */
	}
	// structs are finished
    }
    if (max_func_args<nargs) max_func_args=nargs;
    for(;arg_assign;arg_assign=cadr(arg_assign)) {
	g_expr_u(car(arg_assign));
    }
    clear_ptr_cache();
    if (car(e2) == FNAME) {	
	printf("\tbl\tL_%s$stub\n",fn->nm);
    } else {
        jrn = register_name(cadr(jmp));
        printf("\tmtctr %s\n",jrn);
        printf("\tbctrl\n");
    }
    for(;reg_arg_list;reg_arg_list=cadr(reg_arg_list)) {
	arg = car(reg_arg_list);
	if (car(arg)==REGISTER||car(arg)==DREGISTER||car(arg)==FREGISTER
		||car(arg)==LREGISTER) 
	    free_register(cadr(arg));
	else if (car(arg)==LVAR&&cadr(arg)<0) free_lvar(cadr(arg));
    }
    if (ret_type==DOUBLE||ret_type==FLOAT) {
	set_freg(RET_FREGISTER,0);
    } else if (ret_type==ULONGLONG||ret_type==LONGLONG) {
	set_lreg(RET_LREGISTER,0);
	use_reg(RET_LREGISTER);
    } else if (ret_type==VOID) {
    } else {
	set_ireg(RET_REGISTER,0);
    }
    return ret_type;
}

void
code_alloca(int e1,int reg)
{
    char *crn,*grn;
    int g;
  
    g_expr(list3(BAND,list3(ADD,e1,list2(CONST,30)),list2(CONST,~15))); 
    use_int(reg);
    g = get_register();
    crn = register_name(reg);
    grn = register_name(g);
//    printf("\trlwinm r0,%s,0,0,27\n",crn);
    printf("\tlwz %s,0(r1)\n",grn);
    printf("\tneg %s,%s\n",crn,crn);
    printf("\tstwux %s,r1,%s\n",grn,crn);
//    printf("\tstw %s,0(r1)\n",grn);
    if (!max_func_arg_label) max_func_arg_label = fwdlabel();
    printf("\taddis r1,r1,ha16(L_%d)\n",max_func_arg_label);
    printf("\taddi %s,r1,lo16(L_%d)\n",crn,max_func_arg_label);
    free_register(g);
}

void
code_frame_pointer(int e3) {
    use_int(e3);
#if R1SAVE
    printf("\tmr r1,%s\n",register_name(e3));
#else
    printf("\tmr r30,%s\n",register_name(e3));
#endif
}


void
code_fix_frame_pointer(int disp_offset) {
    int l = 0;
    printf("\tla r30,");
    printf("lo16(%d)(r30)\n",FUNC_LVAR);
}

void
code_jmp(char *s) {
    max_reg_var = REG_VAR_BASE-REG_VAR_MIN;
    max_freg_var = FREG_VAR_BASE-FREG_VAR_MIN;
    printf("\tb L_%s$stub\n",s);
    control=0;
}


void
code_indirect_jmp(int e2) {
    max_reg_var = REG_VAR_BASE-REG_VAR_MIN;
    max_freg_var = FREG_VAR_BASE-FREG_VAR_MIN;
    use_int(e2);
    printf("\tmtctr %s\n",register_name(e2));
    printf("\tbctr\n");
    control=0;
}

void
code_rindirect(int e1, int reg,int offset, int sign,int sz)
{
    char *crn,*rrn;
    g_expr(e1);
    if (!is_int_reg(creg)) error(-1);
    crn=register_name(creg);
    use_int(reg);
    rrn=register_name(reg);
    if (LARGE_OFFSET(offset)) {
        printf("\taddis %s,%s,ha16(%d)\n",crn,crn,offset);
    }
    printf("\t%s %s,lo16(%d)(%s)\n",cload(sz),rrn,offset,crn);
    cext(sign,sz,reg);
}

#if FLOAT_CODE
int
code_drindirect(int e1, int reg,int offset, int d)
{
    char *crn;
    g_expr(e1);
    if (!is_int_reg(creg)) error(-1);
    crn=register_name(creg);
    use_float(d,reg);
    if (LARGE_OFFSET(offset)) {
        printf("\taddis %s,%s,ha16(%d)\n",crn,crn,offset);
    }
    printf("\t%s %s,lo16(%d)(%s)\n",fload(d),
	fregister_name(reg),offset,crn);

    return d?DOUBLE:FLOAT;
}
#endif

static void
lload(int creg,int reg,int offset)
{
    char *crn = register_name(creg);
    if (LARGE_OFFSET(offset)) {
	printf("\taddis %s,%s,ha16(%d)\n",crn,crn,offset);
    }
#if ENDIAN_L==0
    if (creg!=regv_l(reg)) {
	printf("\tlwz %s,lo16(%d)(%s)\n",lregister_name_low(reg),offset,crn);
	printf("\tlwz %s,lo16(%d)(%s)\n",lregister_name_high(reg),offset+SIZE_OF_INT,crn);
    } else {
	printf("\tlwz %s,lo16(%d)(%s)\n",lregister_name_high(reg),offset+SIZE_OF_INT,crn);
	printf("\tlwz %s,lo16(%d)(%s)\n",lregister_name_low(reg),offset,crn);
    }
#else
    if (creg!=regv_h(reg)) {
	printf("\tlwz %s,lo16(%d)(%s)\n",lregister_name_high(reg),offset,crn);
	printf("\tlwz %s,lo16(%d)(%s)\n",lregister_name_low(reg),offset+SIZE_OF_INT,crn);
    } else {
	printf("\tlwz %s,lo16(%d)(%s)\n",lregister_name_low(reg),offset+SIZE_OF_INT,crn);
	printf("\tlwz %s,lo16(%d)(%s)\n",lregister_name_high(reg),offset,crn);
    }
#endif
}

#if LONGLONG_CODE
int
code_lrindirect(int e1, int reg, int offset, int us)
{
    char *crn;
    int creg0;

    g_expr(e1);
    if (!is_int_reg(creg)) error(-1);
    crn=register_name(creg0=creg);
    use_longlong(reg);
    lload(creg0,reg,offset);
    return us?ULONGLONG:LONGLONG;
}
#endif

void
code_assign_gvar(int e2,int creg,int byte) {
    use_int(creg);
    code_ldf(cstore(byte),register_name(creg),cadr(e2),
	get_ptr_cache((NMTBL*)caddr(e2)));
}

void
code_assign_lvar(int e2,int creg,int byte) {
    char *crn;
    use_int(creg);
    crn=register_name(creg);
    lvar_intro(e2);
    printf("\t%s  %s,",cstore(byte),crn);
    lvar(e2);
}

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

void
code_assign(int e2,int byte,int creg) {
    char *drn;
    char *crn;
    use_int(e2);
    drn=register_name(e2);
    use_int(creg);
    crn=register_name(creg);

    printf("\t%s %s,0(%s)\n",cstore(byte),crn,drn);
}


void
code_register_assop(int e2,int reg, int op,int byte) {
    //  reg <= reg(e2) op=reg
    use_int(reg);
    tosop(op,e2,reg);
}

void
code_assop(int op,int creg, int byte,int sign) {
    char *xrn,*crn,*drn;
    int xreg;
    int edx = get_register(); if(!edx) error(-1);
    //  (*creg) op = pop()

    use_int(creg);
    xrn = register_name(xreg = emit_pop(0));       /* pop e3 value */
#if 1
    set_ireg(edx,0);
    ld_indexx(byte,0,creg,ireg,sign);
    use_reg(creg); // to clear ptr cache
    tosop(op,ireg,xreg);
    crn = register_name(ireg);
    drn = register_name(creg);
    if (byte==1) {
	printf("\tstb %s,0(%s)\n",crn,drn);
    } else if (byte==SIZE_OF_SHORT) {
	printf("\tsth %s,0(%s)\n",crn,drn);
    } else {
	printf("\tstw %s,0(%s)\n",crn,drn);
    }
    free_register(creg);
    emit_pop_free(xreg);
#else
    printf("# assop\n\tmr %s,%s\n",register_name(edx),register_name(creg));
    ld_indexx(byte,0,edx,creg,sign);
    tosop(op,creg,xreg);
    crn = register_name(creg);
    drn = register_name(edx);
    if (byte==1) {
	printf("\tstb %s,0(%s)\n",crn,drn);
    } else if (byte==SIZE_OF_SHORT) {
	printf("\tsth %s,0(%s)\n",crn,drn);
    } else {
	printf("\tstw %s,0(%s)\n",crn,drn);
    }
    free_register(edx);
    emit_pop_free(xreg);
#endif
}

void
tosop(int op,int creg,int oreg)
{
    int dx = -1;
    int ox = -1;
    char *orn,*crn,*drn;
    // creg = creg op oreg

    use_int(creg);
    if(oreg==-1) {
	error(-1);
    } else if (oreg<= -REG_LVAR_OFFSET) {
	ox = get_register(); if (ox<0) error(-1);
        code_rlvar(oreg+REG_LVAR_OFFSET,ox);
	oreg = ox;
    }

    switch(op) {
    case LSHIFT:
    case ULSHIFT:
	shift("slw",creg,oreg);
	if(ox!=-1) free_register(ox);
	return;
    case RSHIFT:
	shift("sraw",creg,oreg);
	if(ox!=-1) free_register(ox);
	return;
    case URSHIFT:
	shift("srw",creg,oreg);
	if(ox!=-1) free_register(ox);
	return;
    }
    orn = register_name(oreg);
    crn = register_name(creg);
    switch(op) {
    case ADD:
	printf("\tadd %s,%s,%s\n",crn,crn,orn);
	break;
    case SUB:
	printf("\tsub %s,%s,%s\n",crn,crn,orn);
	break;
    case CMP:
	inc_cmpflag();
	printf("\tcmpw cr%d,%s,%s\n",cmpflag,crn,orn);
	break;
    case UCMP:
	inc_cmpflag();
	printf("\tcmplw cr%d,%s,%s\n",cmpflag,crn,orn);
	break;
    case BAND: 
	printf("\tand %s,%s,%s\n",crn,crn,orn);
	break;
    case EOR: 
	printf("\txor %s,%s,%s\n",crn,crn,orn);
	break;
    case BOR:
	printf("\tor %s,%s,%s\n",crn,crn,orn);
	break;
    case MUL:
	printf("\tmullw %s,%s,%s\n",crn,crn,orn);
	break;
    case UMUL:
	printf("\tmullw %s,%s,%s\n",crn,crn,orn);
	break;
    case DIV:
	printf("\tdivw %s,%s,%s\n",crn,crn,orn);
	break;
    case UDIV:
	printf("\tdivwu %s,%s,%s\n",crn,crn,orn);
	break;
    case MOD:
	dx=get_register();
	drn = register_name(dx);
	printf("\tdivw %s,%s,%s\n",drn,crn,orn);
	printf("\tmullw %s,%s,%s\n",drn,drn,orn);
	printf("\tsubf %s,%s,%s\n",crn,drn,crn);
	break;
    case UMOD:
	dx=get_register();
	drn = register_name(dx);
	printf("\tdivwu %s,%s,%s\n",drn,crn,orn);
	printf("\tmullw %s,%s,%s\n",drn,drn,orn);
	printf("\tsubf %s,%s,%s\n",crn,drn,crn);
	break;
    default:
	error(-1);
    }
    if(dx!=-1) free_register(dx);
    if(ox!=-1) free_register(ox);
}

int 
code_const_op_p(int op,int v)
{
    if (car(v)!=CONST) return 0;
    if (op==DIV||op==UDIV) return ilog(v);
    if (op==BAND||op==MOD||op==UMOD) return 0;
    v = cadr(v);
    return (-32766<v&&v<32767);
}

void
oprtc(int op,int creg, int v)
{
    char *crn;
    int l;
    use_int(creg);
    crn = register_name(creg);
    v = cadr(v);
    
    switch(op) {
    case LSHIFT:
    case ULSHIFT:
	printf("\tslwi %s,%s,%d\n",crn,crn,v);
	return;
    case DIV:
	v = ilog(v);
    case RSHIFT:
	printf("\tsrawi %s,%s,%d\n",crn,crn,v);
	return;
    case UDIV:
	v = ilog(v);
    case URSHIFT:
	printf("\tsrwi %s,%s,%d\n",crn,crn,v);
	return;
    case ADD:
	printf("\taddi %s,%s,lo16(%d)\n",crn,crn,v);
	break;
    case SUB:
	printf("\taddi %s,%s,lo16(-%d)\n",crn,crn,v);
	break;
    case CMP:
	inc_cmpflag();
	printf("\tcmpwi cr%d,%s,lo16(%d)\n",cmpflag,crn,v);
	break;
    case UCMP:
	inc_cmpflag();
	printf("\tcmplwi cr%d,%s,lo16(%d)\n",cmpflag,crn,v);
	break;
    case EOR: 
	printf("\txori %s,%s,lo16(%d)\n",crn,crn,v);
	break;
    case BOR:
	printf("\tori %s,%s,lo16(%d)\n",crn,crn,v);
	break;
    case MUL:
    case UMUL:
	if ((l=ilog(v))) {
	    printf("\tslwi %s,%s,%d\n",crn,crn,l);
	} else
	    printf("\tmulli %s,%s,lo16(%d)\n",crn,crn,v);
	break;
    default:
	error(-1);
    }
}

void
shift(char *op, int creg, int reg)
{
    char *crn;
    char *rrn = register_name(reg);
    use_int(creg);
    crn = register_name(creg);
    printf("\t%s %s,%s,%s\n",op,crn,crn,rrn);
}

void
ld_indexx(int byte, int n, int xreg,int creg, int sign)
{	
    char *crn;
    use_int(creg);
    crn = register_name(creg);
    printf("\t%s %s,%d(%s)\n",cload(byte),register_name(creg),n,
	    register_name(xreg));
    cext(sign,byte,creg);
}

int
code_csvalue()
{
    return creg;
}

void
code_cmpdimm(int e, int csreg,int label,int cond)
{
    int reg,regsv;
    /* used in dosiwtch() */
    if(chk) return;
    inc_cmpflag();
    if (-32767<e&&e<32767) {
	printf("\tcmpwi cr%d,%s,%d\n",cmpflag,register_name(csreg),e);
	jcond(label,cond);
    } else {
	regsv = regs[csreg]; regs[csreg]=USING_REG;
	reg = get_register();
	regs[csreg]= regsv;
	code_const(e,reg);
	printf("\tcmpw cr%d,%s,%s\n",cmpflag,register_name(csreg),register_name(reg));
	jcond(label,cond);
	free_register(reg);
    }
}

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");
}

#define CRBITSIZ 4

static int
rexpr_bool(int e1,int reg)
{
    int t,flag=-1,eq=-1,neg=-1;
    char *rn;
    switch(car(e1)) {
    case GT:  t=INT; flag = 2; break;
    case UGT: t=0;   flag = 2; break;
    case GE:  t=INT; flag = 2; eq=3; break;
    case UGE: t=0;   flag = 2; eq=3; break;
    case LT:  t=INT; flag = 1; break;
    case ULT: t=0;   flag = 1; break;
    case LE:  t=INT; flag = 1; eq=3; break;
    case ULE: t=0;   flag = 1; eq=3; break;
    case EQ:  t=INT; flag = 3; break;
    case NEQ: t=INT; flag = 3; neg=3; break;
    default: return 0;
    }
    g_expr(list3((t==INT?CMP:UCMP),cadr(e1),caddr(e1)));
    use_int(reg);
    rn = register_name(reg);
    t = CRBITSIZ*cmpflag;
    if (eq>0) {
	printf("\tcror %d,%d,%d\n",t+flag-1,t+eq-1,t+flag-1);
    }
    if (neg>0) {
	neg = t+neg-1,
	printf("\tcrnor %d,%d,%d\n",neg,neg,neg);
    }
    printf("\tmfcr %s\n",rn);
    printf("\trlwinm %s,%s,%d,1\n",rn,rn,t+flag);
    return 1;
}

void
rexpr(int e1, int l1, int cond,int t)
{       
    char *s;
    switch(car(e1)) {
    case GT:  s=code_gt(cond);  break;
    case UGT: s=code_ugt(cond); break;
    case GE:  s=code_ge(cond);  break;
    case UGE: s=code_uge(cond); break;
    case LT:  s=code_ge(!cond); break;
    case ULT: s=code_uge(!cond);break;
    case LE:  s=code_gt(!cond); break;
    case ULE: s=code_ugt(!cond);break;
    case EQ:  s=code_eq(cond);  break;
    case NEQ: s=code_eq(!cond); break;
    default: error(-1);
    }
    g_expr(list3((t==INT?CMP:UCMP),cadr(e1),caddr(e1)));
    printf("\tb%s cr%d,L_%d\n",s,cmpflag,l1);
}

static void
jcond(int l, char cond)
{       
    if (chk) return;
    if (cond==LT) {
	printf("\tb%s cr%d,L_%d\n",code_ge(0),cmpflag,l);
    } else if (cond==1||cond==0) {
	printf("\tb%s cr%d,L_%d\n",cond?"ne":"eq",cmpflag,l);
    } else error(-1);
}

void
jmp(int l)
{       
    control=0;
    if (chk) return;
    printf("\tb\tL_%d\n",l);
}

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

void
code_enter(char *name)
{
    if (output_mode!=TEXT_EMIT_MODE) 
	text_mode();
    else
	printf("\t.align 2\n");
    if (stmode!=STATIC)
	printf(".globl _%s\n",name);
#ifdef DOT_SIZE
    printf("\t.type\t%s,@function\n",name);
#endif
    printf("_%s:\n",name);
    code_disp_label=fwdlabel();
#if 0
    printf("\tla r1,lo16(L_%d)(r30)\n",code_disp_label);
#else
    printf("\tla r1,lo16(L_%d)(r30)\n",code_disp_label);
    printf("\taddis r1,r1,ha16(L_%d)\n",code_disp_label);
#endif
    printf("\tbcl 20,31,L_%d\n",code_base = fwdlabel());
    fwddef(code_base);
    printf("\tmflr r31\n");
    max_func_args = 0;
}


void
code_enter1(int args)
{
    // set_lreg(LREG_LREGISTER,0);
    set_ireg(CREG_REGISTER,0);
    set_freg(FREG_FREGISTER,0);
}

void
code_leave(char *name)
{
    int r1_offsetv;
    disp&= -SIZE_OF_INT;
    r1_offsetv = -disp+max_func_args*SIZE_OF_INT+code_disp_offset+8;

    printf(".set L_%d,%d\n",code_disp_label,-r1_offsetv);
    if (max_func_arg_label) {
	printf(".set L_%d,%d\n",max_func_arg_label,max_func_args*SIZE_OF_INT+24);
	max_func_arg_label = 0;
    }
    local_table();
    // free_all_register();
}

void
enter(char *name)
{
    if (output_mode!=TEXT_EMIT_MODE) 
	text_mode();
    else
	printf("\t.align 2\n");
    if (stmode!=STATIC)
	printf(".globl _%s\n",name);
/*
    printf("\t.type\t%s,@function\n",name);
 */
    printf("_%s:\n",name);
    code_setup=fwdlabel();
    printf("\tmflr r0\n");
    printf("\tbl L_%d\n",code_setup);
    code_base=fwdlabel();
    fwddef(code_base);
    r1_offset_label = fwdlabel();
    lvar_offset_label = fwdlabel();
#if 0
    printf("\taddi r30,r1,lo16(-L_%d)\n",lvar_offset_label);
    printf("\tstwu r1,lo16(-L_%d)(r1)\n",r1_offset_label);
    // printf("\tmr r30,r1\n");
#else
    printf("\taddi r30,r1,lo16(-L_%d)\n",lvar_offset_label);
    printf("\tlis r31,ha16(-L_%d)\n",r1_offset_label);
    printf("\taddi r31,r31,lo16(-L_%d)\n",r1_offset_label);
    printf("\tstwux r1,r1,r31\n");
#endif
    printf("\tmflr r31\n");
    max_func_args = 0;
}

void
enter1()
{
    text_mode();
    // set_lreg(LREG_LREGISTER,0);
    set_ireg(CREG_REGISTER,0);
    set_freg(FREG_FREGISTER,0);
}

int
reg_save_offset()
{
    return -(
	(REAL_MAX_REGISTER-(REG_VAR_BASE-max_reg_var))*SIZE_OF_INT+
	(REAL_MAX_FREGISTER-(FREG_VAR_BASE-max_freg_var))*SIZE_OF_DOUBLE
	);
}

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

    if (max_freg_var>=0 && max_freg_var<=3) max_freg_var=3; 
    reg_save = reg_save_offset();

    if (control) {
	code_set_return_register(1);
    }
    if (retcont) { 
	if (control) jmp(retlabel);
	retcont1 = fwdlabel();
	fwddef(retcont);
	if (cadr(fnptr->ty)==FLOAT||cadr(fnptr->ty)==DOUBLE) {
	    printf("\tfmr f1,f31\n");
	} else if (cadr(fnptr->ty)>0&&(
	    car(cadr(fnptr->ty))==STRUCT ||
	    car(cadr(fnptr->ty))==UNION)) {
	    sz = size(cadr(fnptr->ty));
	    printf("\tli r7,%d\n",sz);
	    printf("\tsubl r6,r7,r30\n");
	    printf("\tlwz r3,lo16(%d)(r30)\n",(my_func_args-1)*SIZE_OF_INT);
	    emit_copy(6,3,sz,0,1,1);
	} else if (cadr(fnptr->ty)!=VOID) {
	    printf("\tmr r3,r29\n");
	}
#if !R1SAVE
	printf("\tla r1,lo16(%d)(r30)\n",
	    -reg_save+my_func_args*SIZE_OF_INT);
#endif
	printf("\tb L_%d\n",retcont1);
    }
    fwddef(retlabel);
    printf("\tlwz r1,0(r1)\n");
    if (retcont) {
	fwddef(retcont1);
    }
    if (max_freg_var>=0) {
	printf("\tlmw r%d,%d(r1)\n",
			REG_VAR_BASE-max_reg_var,reg_save);
	freg_save = 72-(REAL_MAX_FREGISTER-(FREG_VAR_BASE-max_freg_var))*4;
	printf("\tb restFP+%d ; restore f%d-f31\n",
			freg_save,
			FREG_VAR_BASE-max_freg_var);
    } else {
	printf("\tlwz r0,8(r1)\n");
	printf("\tmtlr r0\n");
	printf("\tlmw r%d,%d(r1)\n",
		    REG_VAR_BASE-max_reg_var,reg_save);
	printf("\tblr\n");
    }

    disp &= -SIZE_OF_INT;
    fwddef(code_setup);
    printf("\tstmw r%d,%d(r1)\n",
		    REG_VAR_BASE-max_reg_var,reg_save);
    printf("\tstw r0,8(r1)\n");
    if (max_freg_var>=0)
	printf("\tb saveFP+%d ; save f%d-f31\n",
			freg_save,
			FREG_VAR_BASE-max_freg_var);
    else {
	printf("\tblr\n");
    }

    code_offset_set();
    local_table();
    labelno++;
    // free_all_register();
}


void
code_set_return_register(int mode) {
    if (cadr(fnptr->ty)==DOUBLE||cadr(fnptr->ty)==FLOAT) {
	set_freg(RET_FREGISTER,mode);
    } else if (cadr(fnptr->ty)==LONGLONG||cadr(fnptr->ty)==ULONGLONG) {
	set_lreg(RET_LREGISTER,mode);
    } else if (cadr(fnptr->ty)==VOID) {
    } else {
	set_ireg(RET_REGISTER,mode);
    }
}

int
code_get_fixed_creg(int reg,int type) {
    return creg;
}

void
code_set_fixed_creg(int reg,int mode,int type) {
    if (type==FLOAT||type==DOUBLE) {
	set_freg(reg,mode);
    } else if (type==LONGLONG||type==ULONGLONG) {
	set_lreg(reg,mode);
	// use_reg(reg);
    } else {
	set_ireg(reg,mode);
    }
}

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

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

extern int
emit_string_label() {
    int lb;

    printf(".data\t\n.cstring\n\t.align 2\n");
    lb=fwdlabel();
    printf("L_%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 d = lcadr(e);
    data_mode(0);
#if ENDIAN_L==0
    printf("\t.long\t0x%x,0x%x\n",code_l1(d),code_l2(d));
#else
    printf("\t.long\t0x%x,0x%x\n",code_l2(d),code_l1(d));
#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 L_%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("L_%d:\n",lb);
	printf("\t.size\t%s,L_%d-%s\n",n->nm,lb,n->nm);
#endif
    }
}

void
global_table(void)
{
    NMTBL *n;
    int init; char *extrn;
    init=0;
    for(n = global_list;n!=&null_nptr;n = n->next) {
	if ((n->sc == GVAR) && 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));
	} else if ((n->sc==STATIC) && n->dsp != -1) {
	    /* n->dsp = -1 means initialized global */
	    if (init==0) {
		data_mode(0);
		init=1;
	    }
	    printf(".lcomm _%s,%d\n",n->nm,size(n->ty));
	}
    }
    for(n = global_list;n!=&null_nptr;n = n->next) {
	if (is_code(n)||is_function(n)) {
	    extrn = n->nm;
	    if (n->sc==EXTRN1) {
		data_mode(0);
printf(".picsymbol_stub\n");
printf("L_%s$stub:\n",extrn);
printf("\t.indirect_symbol _%s\n",extrn);
printf("\tmflr r0\n");
printf("\tbcl 20,31,L0$_%s\n",extrn);
printf("L0$_%s:\n",extrn);
printf("\tmflr r11\n");
printf("\taddis r11,r11,ha16(L_%s$lazy_ptr-L0$_%s)\n",extrn,extrn);
printf("\tmtlr r0\n");
printf("\tlwz r12,lo16(L_%s$lazy_ptr-L0$_%s)(r11)\n",extrn,extrn);
printf("\tmtctr r12\n");
printf("\taddi r11,r11,lo16(L_%s$lazy_ptr-L0$_%s)\n",extrn,extrn);
printf("\tbctr\n");
printf(".data\n");
printf(".lazy_symbol_pointer\n");
printf("L_%s$lazy_ptr:\n",extrn);
printf("\t.indirect_symbol _%s\n",extrn);
printf("\t.long dyld_stub_binding_helper\n");
	    } else if (n->sc==FUNCTION||n->sc==CODE) {
		text_mode();
printf("\t.set L_%s$stub,_%s\n",extrn,extrn);
		data_mode(0);
printf("L_%s$non_lazy_ptr:\n\t.long\t_%s\n",extrn,extrn);
	    } 
	}
    }
    init=0;
    for(n = global_list;n!=&null_nptr;n = n->next) {
	if (n->sc == GVAR) {
	    if (init==0) {
		printf(".data\n");
		init=1;
	    }
printf("L_%s$non_lazy_ptr:\n\t.long\t_%s\n",n->nm,n->nm);
	}
    }
    init = 0;
    for(n = global_list;n!=&null_nptr;n = n->next) {
	if (is_code(n)||is_function(n)) continue;
	if (n->sc==EXTRN1) {
	    if(init==0) {
		printf(".data\n");
printf(".non_lazy_symbol_pointer\n");
		init=1;
	    }
printf("L_%s$non_lazy_ptr:\n",n->nm);
printf("\t.indirect_symbol _%s\n",n->nm);
printf("\t.long\t0\n");
	}
    }
}

void
local_table(void)
{
    NMTBL *n;
    int init;
    init=0;
    /* static local variables */
    for(n = local_static_list;n!=&null_nptr;n = n->next) {
	if (n->sc == GVAR) {
	    if (init==0) {
		data_mode(0);
		init=1;
	    }
	    if (n->dsp != -1) /* initialized static */
		printf(".lcomm _%s,%d\n",n->nm,size(n->ty));
	    printf("L_%s$non_lazy_ptr:\n\t.long\t_%s\n",n->nm,n->nm);
	}
    }
}

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 */

static int float_one_lib_used=0;
static char *float_one_lib[] = {
".data",
/* ".literal8", */
"        .align 3",
"__float_one:",
"        .long   1065353216",
".text",
/* ".set L__float_one$non_lazy_ptr,__float_one", */
0
};
static int float_zero_lib_used=0;
static char *float_zero_lib[] = {
".data",
/* ".literal8", */
"        .align 3",
"__float_zero:",
"        .long   0",
".text",
/* ".set L__float_zero$non_lazy_ptr,__float_zero", */
0
};

char *
fstore(int d)
{
    return (d?"stfd":"stfs");
}

char *
fload(int d)
{
    return d?"lfd":"lfs";
}

void
code_cmp_dregister(int e2,int d,int label,int cond)
{
    char *frn,*rrn,*grn;
    int greg,r;
    grn =  register_name(greg = get_dregister(d));
    use_float(d,e2);
    frn = register_name(e2);
    float_zero_lib_used=1;
    r = get_ptr_cache(&float_zero);
    rrn = register_name(r);
    printf("\tlfs %s,0(%s)\n",grn,rrn);
    inc_cmpflag();
    printf("\tfcmpu cr%d,%s,%s\n",cmpflag,grn,frn);
    free_register(greg);
    jcond(label,cond);
    return;
}

void
code_dregister(int e2,int freg,int d)
{
    use_float(d,freg);
    if (freg!=e2) {
	if (is_int_reg(e2)) error(-1);
	printf("\tfmr %s,%s\n",fregister_name(freg),fregister_name(e2));
    }
}

void
code_dassign_gvar(int e2,int freg,int d)
{ 
    use_float(d,freg);
    code_ldf(fstore(d),fregister_name(freg),cadr(e2),
	get_ptr_cache((NMTBL*)caddr(e2)));
}

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

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

void
code_dassign_dregister(int e2,int d,int freg) {
    use_float(d,freg);
    if (e2!=freg) {
	printf("\tfmr %s,%s\n",fregister_name(e2),fregister_name(freg));
    }
}

static double d0 = 1.0;

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

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

int
code_f(double d)
{
    float f = d;
    int *j = (int *)&f;
    return *j;
}

void

code_dconst(int e2,int freg,int d)
{ 
    int lb;
    double value = dcadr(e2);
    int r;
    char *rrn,*frn;

    use_float(d,freg);
    frn = fregister_name(freg);
    if (value==0.0) {
	float_zero_lib_used=1;
	r = get_ptr_cache(&float_zero);
	rrn = register_name(r);
	printf("\tlfs %s,0(%s)\n",frn,rrn);
	return;
    }
    if (value==1.0) {
	float_one_lib_used=1;
	r = get_ptr_cache(&float_one);
	rrn = register_name(r);
	printf("\tlfs %s,0(%s)\n",frn,rrn);
	return;
    }
    rrn = register_name((r=get_register()));
    use_reg(r); // to clear ptr cache
    printf(" \t.data\n\t.align 3\n");
    lb=fwdlabel();
    printf("L_%d:\n",lb);
    if (d) {
#if ENDIAN_D==0
	printf("\t.long\t0x%x,0x%x\n",code_d1(value),code_d2(value));
#else
	printf("\t.long\t0x%x,0x%x\n",code_d2(value),code_d1(value));
#endif
    } else {
	printf("\t.long\t0x%x\n",code_f(value));
    }
    if (output_mode==TEXT_EMIT_MODE) {
	printf(".text\n");
    } else {
	text_mode();
    }
    printf("\taddis %s,r31,ha16(L_%d-L_%d)\n",rrn,lb,code_base);
    printf("\tla %s,lo16(L_%d-L_%d)(%s)\n",rrn,lb,code_base,rrn);
    if (d) {
	printf("\tlfd %s,0(%s)\n",frn,rrn);
    } else {
	printf("\tlfs %s,0(%s)\n",frn,rrn);
    }
    free_register(r);
}


void
code_dneg(int freg,int d)
{ 
    char *frn;
    use_float(d,freg);
    frn = fregister_name(freg);
    printf("\tfneg %s,%s\n",frn,frn);
}

void
code_d2i()
{ 
    char *frn;
    char *crn;
    int e2 = new_lvar(SIZE_OF_DOUBLE);

    use_double0();
    frn = fregister_name(freg);
    use_int0();
    crn = register_name(creg);

    free_lvar(e2);
    printf("\tfctiwz  %s,%s\n",frn,frn);
    lvar_intro(e2);
    printf("\tstfd  %s,",frn); lvar(e2);
    lvar_intro(e2+SIZE_OF_DOUBLE-SIZE_OF_INT);
    printf("\tlwz %s,",crn); lvar(e2+SIZE_OF_DOUBLE-SIZE_OF_INT);
}

static int i2d_lib_used=0;
static char *i2d_lib[] = {
".data",
/* ".literal8", */
"        .align 3",
"__i2dLC0:",
"        .long   1127219200",
"        .long   -2147483648",
".text",
"        .align 2",
"i2d_:",
"        mflr r0",
"        bcl 20,31,__i2dL1$pb",
"__i2dL1$pb:",
"        mflr r10",
"        mtlr r0",
"        xoris r3,r3,0x8000",
"        stw r3,-28(r1)",
"        lis r0,0x4330",
"        stw r0,-32(r1)",
"        lfd f0,-32(r1)",
"        addis r9,r10,ha16(__i2dLC0-__i2dL1$pb)",
"        lfd f1,lo16(__i2dLC0-__i2dL1$pb)(r9)",
"        fsub f1,f0,f1",
"        blr",
0
};

void
code_i2d()
{ 
    i2d_lib_used = 1;
    clear_ptr_cache();
    code_save_stacks();
    set_ireg(RET_REGISTER,1);
    printf("\tbl i2d_\n");
    set_freg(RET_FREGISTER,0);
}

static int d2u_lib_used=0;
static char *d2u_lib[] = {
/* ".literal8", */
"        .align 3",
"__d2uLC0:",
"        .long   1105199104",
"        .long   0",
".text",
"        .align 2",
"d2u_:",
"        mflr r0",
"        bcl 20,31,__d2uL1$pb",
"__d2uL1$pb:",
"        mflr r10",
"        mtlr r0",
"        addis r9,r10,ha16(__d2uLC0-__d2uL1$pb)",
"        lfd f0,lo16(__d2uLC0-__d2uL1$pb)(r9)",
"        fcmpu cr0,f1,f0",
"        cror 2,1,2",
"        beq- cr0,__d2uL2",
"        fctiwz f0,f1",
"        stfd f0,-32(r1)",
"        lwz r3,-28(r1)",
"        blr",
"__d2uL2:",
"        addis r9,r10,ha16(__d2uLC0-__d2uL1$pb)",
"        lfd f0,lo16(__d2uLC0-__d2uL1$pb)(r9)",
"        fsub f0,f1,f0",
"        fctiwz f0,f0",
"        stfd f0,-24(r1)",
"        lwz r3,-20(r1)",
"        xoris r3,r3,0x8000",
"        blr",
0
};

void
code_d2u()
{ 
    code_save_stacks();
    clear_ptr_cache();
    d2u_lib_used=1;
    set_freg(RET_FREGISTER,1);
    printf("\tbl d2u_\n");
    set_ireg(RET_REGISTER,0);
}

static int u2d_lib_used=0;
static char *u2d_lib[] = {
".data",
/* ".literal8", */
"        .align 3",
"__u2dLC1:",
"        .long   1127219200",
"        .long   0",
".text",
"        .align 2",
"u2d_:",
"        mflr r0",
"        bcl 20,31,__u2dL2$pb",
"__u2dL2$pb:",
"        mflr r10",
"        mtlr r0",
"        stw r3,-28(r1)",
"        lis r0,0x4330",
"        stw r0,-32(r1)",
"        lfd f0,-32(r1)",
"        addis r9,r10,ha16(__u2dLC1-__u2dL2$pb)",
"        lfd f1,lo16(__u2dLC1-__u2dL2$pb)(r9)",
"        fsub f1,f0,f1",
"        blr",
0
};

void
code_u2d()
{ 
    code_save_stacks();
    clear_ptr_cache();
    u2d_lib_used = 1;
    set_ireg(RET_REGISTER,1);
    printf("\tbl u2d_\n");
    set_freg(RET_FREGISTER,0);
}

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

void
code_drgvar(int e2,int d,int freg)
{ 
    use_float(d,freg);
    code_ldf(fload(d),fregister_name(freg),cadr(e2),
	get_ptr_cache((NMTBL*)caddr(e2)));
}


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

void
code_cmp_drgvar(int e2,int reg,int d,int label,int cond)
{ 
    char *frn;
    int g=get_dregister(d);
    char *grn=fregister_name(g);

    use_float(d,reg);
    frn=fregister_name(reg);

    code_ldf(fload(1),grn,cadr(e2),
	get_ptr_cache((NMTBL*)caddr(e2)));
    inc_cmpflag();
    printf("\tfcmpu cr%d,%s,%s\n",cmpflag,frn,grn);
    free_register(g);
    jcond(label,cond);
}

void
code_cmp_drlvar(int e2,int reg,int d,int label,int cond)
{ 
    char *frn;
    int g=get_dregister(d);
    char *grn=fregister_name(g);

    use_float(d,reg);
    frn=fregister_name(reg);

    lvar_intro(e2);
    printf("\t%s %s,",fload(1),grn); lvar(e2);
    inc_cmpflag();
    printf("\tfcmpu cr%d,%s,%s\n",cmpflag,frn,grn);
    free_register(g);
    jcond(label,cond);
}

void
dtosop(int op,int reg,int oreg)
{ 
    char *opn="";
    char *frn;
    char *grn;
    int ox = -1;

    use_float(1,reg);
    if(oreg==-1) {
        error(-1);
    } else if (oreg<= -REG_LVAR_OFFSET) {
        ox = get_dregister(1); if (ox<0) error(-1);
        use_reg(ox);
        code_drlvar(oreg+REG_LVAR_OFFSET,1,ox);
        oreg = ox;
    }
    grn=fregister_name(oreg);
    frn=fregister_name(reg);
    switch(op) {
    case FADD:
    case DADD: opn="fadd"; break;
    case FSUB:
    case DSUB: opn="fsub"; break;
    case FDIV:
    case DDIV: opn="fdiv"; break;
    case FMUL:
    case DMUL: opn="fmul"; break;
    case FCMP:
    case DCMP: 
	inc_cmpflag();
	printf("\tfcmpu cr%d,%s,%s\n",cmpflag,frn,grn);
	if (ox!=-1) free_register(ox);
	return;
    case FCMPGE: 
    case DCMPGE: 
	inc_cmpflag();
	printf("\tfcmpu cr%d,%s,%s\n",cmpflag,frn,grn);
	if (ox!=-1) free_register(ox);
	return;
    default:
	error(-1); return;
    }
    printf("\t%s %s,%s,%s\n",opn,frn,frn,grn);
    if (ox!=-1) free_register(ox);
}

void
code_dassop(int op,int reg,int d) {
    /* we have lvalue in creg, applied floating value is in freg */
    //  (*creg) op = pop()
    int  xreg=emit_dpop(d);
    char *crn;
    char *frn;

    crn=register_name(ireg);
    use_float(d,reg);
    frn  =fregister_name(reg);

    printf("\t%s %s,0(%s)\n",fload(d),frn,crn);
    dtosop(op,reg,xreg);
    printf("\t%s %s,0(%s)\n",fstore(d),frn,crn);
    emit_dpop_free(xreg,d);
}

void
code_register_dassop(int reg,int op,int d) {
    // reg op= dpop()
    int  xreg=emit_dpop(d);
    dtosop(op,reg,xreg);
    emit_dpop_free(xreg,d);
}   

static int
code_dload_1(int d)
{
    int r,g;
    char *drn,*grn;
    // load 1
    float_one_lib_used=1;
    r = get_ptr_cache(&float_one);
    drn=register_name(r);
    grn=fregister_name(g=get_dregister(d));
    printf("\tlfs %s,0(%s)\n",grn,drn);
    return g;
}

void
code_dpreinc(int e1,int e2,int d,int reg) {
    char *frn,*crn,*grn;
    int  g;

    if (car(e2)==DREGISTER||car(e2)==FREGISTER) {
	crn=register_name(cadr(e2));
        grn = fregister_name(g = code_dload_1(d));
	if (reg==USE_CREG) {
	    reg=get_dregister(d); if (!reg) error(-1);
	    set_freg(reg,0);
	}
	frn=fregister_name(reg);
	printf("\t%s %s,%s,%s\n",(caddr(e1)>0)?"fadd":"fsub",crn,crn,grn);
	if (use && reg!=cadr(e2))
	    printf("\tfmr %s,%s\n",frn,crn);
    } else {
	g_expr(e2);
	if (!is_int_reg(creg)) error(-1);
	crn=register_name(ireg);
	if (reg==USE_CREG) {
	    reg=get_dregister(d); if (!reg) error(-1);
	    set_freg(reg,0);
	}
	frn=fregister_name(reg);
        grn = fregister_name(g = code_dload_1(d));
	printf("\t%s %s,0(%s)\n",fload(d),frn,crn);
	printf("\t%s %s,%s,%s\n",(caddr(e1)>0)?"fadd":"fsub",frn,frn,grn);
	printf("\t%s %s,0(%s)\n",fstore(d),frn,crn);
    }
    free_register(g);
}

void
code_dpostinc(int e1,int e2,int d,int reg) {
    char *frn,*crn,*grn;
    int  g;

    if (car(e2)==DREGISTER||car(e2)==FREGISTER) {
	crn=register_name(cadr(e2));
        grn = fregister_name(g = code_dload_1(d));
	if (reg==USE_CREG) {
	    reg=get_dregister(d); if (!reg) error(-1);
	    set_freg(reg,0);
	}
	frn=fregister_name(reg);
	if (use && reg!=cadr(e2))
	    printf("\tfmr %s,%s\n",frn,crn);
	printf("\t%s %s,%s,%s\n",(caddr(e1)>0)?"fadd":"fsub",crn,crn,grn);
    } else {
	g_expr(e2);
	if (!is_int_reg(creg)) error(-1);
	crn=register_name(ireg);
	if (reg==USE_CREG) {
	    reg=get_dregister(d); if (!reg) error(-1);
	    set_freg(reg,0);
	}
	frn=fregister_name(reg);
        grn = fregister_name(g = code_dload_1(d));
	printf("\t%s %s,0(%s)\n",fload(d),frn,crn);
	printf("\t%s %s,%s,%s\n",(caddr(e1)>0)?"fadd":"fsub",grn,frn,grn);
	printf("\t%s %s,0(%s)\n",fstore(d),grn,crn);
    }
    free_register(g);
}

static int
drexpr_bool(int e1,int reg)
{
    int flag=-1,eq=-1,neg=-1,t;
    char *rn;
    switch(car(e1)) {
    case DOP+GT: case FOP+GT:  flag = 2; break;
    case DOP+GE: case FOP+GE:  flag = 2; eq=3; break;
    case DOP+LT: case FOP+LT:  flag = 1; break;
    case DOP+LE: case FOP+LE:  flag = 1; eq=3; break;
    case DOP+EQ: case FOP+EQ:  flag = 3; break;
    case DOP+NEQ: case FOP+NEQ: flag = 3; neg=3; break;
    default: return 0;
    }
    g_expr(list3(DCMP,cadr(e1),caddr(e1)));
    use_int(reg);
    rn = register_name(reg);
    t = CRBITSIZ*cmpflag;
    if (eq>0) {
	printf("\tcror %d,%d,%d\n",t+flag-1,t+eq-1,t+flag-1);
    }
    if (neg>0) {
	neg = t+neg-1,
	printf("\tcrnor %d,%d,%d\n",neg,neg,neg);
    }
    printf("\tmfcr %s\n",rn);
    printf("\trlwinm %s,%s,%d,1\n",rn,rn,t+flag);
    return 1;
}

void
drexpr(int e1, int e2,int l1, int op,int cond)
{
    if (!cond) {
	switch(op) {
	    case FOP+GT:
		drexpr(e2,e1,l1,FOP+GE,1); return;
	    case FOP+GE:
		drexpr(e2,e1,l1,FOP+GT,1); return;
	    case FOP+EQ:
		op=FOP+NEQ; break;
	    case FOP+NEQ:
		op=FOP+EQ; break;
	    case DOP+GT:
		drexpr(e2,e1,l1,DOP+GE,1); return;
	    case DOP+GE:
		drexpr(e2,e1,l1,DOP+GT,1); return;
	    case DOP+EQ:
		op=DOP+NEQ; break;
	    case DOP+NEQ:
		op=DOP+EQ; break;
	}
    }
    g_expr(list3(DCMP, e1,e2));
    switch(op) {
	case DOP+GT: case FOP+GT:
	    printf("\tbgt\tcr%d,L_%d\n",cmpflag,l1);
	    break;
	case DOP+GE: case FOP+GE:
	    printf("\tbge\tcr%d,L_%d\n",cmpflag,l1);
	    break;
	case DOP+EQ: case FOP+EQ:
	    printf("\tbeq\tcr%d,L_%d\n",cmpflag,l1);
	    break;
	case DOP+NEQ: case FOP+NEQ:
	    printf("\tbne\tcr%d,L_%d\n",cmpflag,l1);
	    break;
    }
}

int emit_dpop(int d)
{ 
    int xreg,reg;
    xreg=pop_fregister();
    if (xreg<= -REG_LVAR_OFFSET) {
	reg = get_dregister(d);
        code_drlvar(REG_LVAR_OFFSET+xreg,1,reg);
	free_lvar(REG_LVAR_OFFSET+xreg);
	xreg=reg;
    }
    return xreg;
}

void
emit_dpop_free(int e1,int d)
{ 
    free_register(e1);
}

void
emit_dpush(int d)
{ 
    int new_reg;
    if (!is_float_reg(creg)) error(-1);
    if (freg_sp>MAX_MAX) error(-1);
    new_reg = get_dregister(1);       /* 絶対に取れる */
    freg_stack[freg_sp++] = freg;     /* push するかわりにレジスタを使う */
    creg = freg = new_reg;
}

#endif

#if LONGLONG_CODE


/* 64bit int part */
static void
lmove(int to,int from)   //   to = from
{
    int tmp;
    if (regv_h(to)==regv_l(from)&&(regv_l(to)==regv_h(from))) {
	tmp = get_register();
	printf("\tmr %s,%s\n",register_name(tmp),lregister_name_low(from));
	printf("\tmr %s,%s\n",lregister_name_high(to),lregister_name_high(from));
	printf("\tmr %s,%s\n",lregister_name_low(to),register_name(tmp));
	free_register(tmp);
    } else if (regv_h(to)==regv_l(from)) {
	printf("\tmr %s,%s\n",lregister_name_low(to),lregister_name_low(from));
	printf("\tmr %s,%s\n",lregister_name_high(to),lregister_name_high(from));
    } else {
	printf("\tmr %s,%s\n",lregister_name_high(to),lregister_name_high(from));
	printf("\tmr %s,%s\n",lregister_name_low(to),lregister_name_low(from));
    }
}

static void
pcond(char *s,int cmpflag,int l1)
{
    printf("\tb%s cr%d,L_%d\n",s,cmpflag,l1);
}

static int
lcmp(int op,int cond)
{
    if (op==LOP+UGT||op==LOP+UGE) {
        return UCMP;
    } else if (op==LOP+LE||op==LOP+GE) {
        return CMP;
    } else {
        return CMP;
    }
}

void
lrexpr(int e1, int e2,int l1, int op,int cond)
{
    int reg;
    int e3;
    int l2;
    int op1,cr0,cr1;

    g_expr(e1);
    emit_lpush();
    g_expr(e2);
    e3 = emit_lpop();
    if (!is_longlong_reg(creg)) error(-1);
    reg = lreg;
    op1 = lcmp(op,cond);
    tosop(op1,regv_h(e3),regv_h(reg));
    cr0 = cmpflag;
    tosop(op1,regv_l(e3),regv_l(reg));
    cr1 = cmpflag;

    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),cr0,cond?l1:l2);
	pcond(code_eq(0),cr0,cond?l2:l1);
	break;
    case LOP+UGT:
    case LOP+UGE:
	pcond(code_ugt(1),cr0,cond?l1:l2);
	pcond(code_eq(0), cr0,cond?l2:l1);
	break;
    case LOP+EQ:
	pcond(code_eq(0),cr0,(cond?l2:l1));
	pcond(code_eq(cond),cr1,l1);
	break;
    case LOP+NEQ:
	pcond(code_eq(0),cr0,(cond?l1:l2));
	pcond(code_eq(!cond),cr1,l1);
	break;
    default:
	error(-1);
    }
    switch(op) {
    case LOP+GT:  pcond(code_gt(cond),  cr1,l1); break;
    case LOP+GE:  pcond(code_ge(cond),  cr1,l1); break;
    case LOP+UGT: pcond(code_ugt(cond), cr1,l1); break;  
    case LOP+UGE: pcond(code_uge(cond), cr1,l1); break;  
    }  
    fwddef(l2);  
    emit_lpop_free(e3); 
} 

int
emit_lpop()
{
    int xreg,reg;
    xreg=lreg_stack[--lreg_sp];
    if (xreg<= -REG_LVAR_OFFSET) {
        reg = get_lregister();
        code_lrlvar(REG_LVAR_OFFSET+xreg,reg);
        free_lvar(REG_LVAR_OFFSET+xreg);
        xreg = reg;
    }
    return xreg;
}

void
code_lregister(int e2,int reg)
{
    use_longlong(reg);
    if (reg!=e2) {
	lmove(reg,e2);
    }
}

void
code_cmp_lregister(int reg,int label,int cond)
{
    use_longlong(reg);
    printf("\tor %s,%s,%s\n",
		lregister_name_low(reg),
		lregister_name_low(reg),
		lregister_name_high(reg));
    inc_cmpflag();
    printf("\tcmpwi cr%d,%s,0\n",cmpflag,lregister_name_low(reg));
    jcond(label,cond);
}

void
code_cmp_lrgvar(int e1,int creg,int label,int cond)
{
    use_longlong(creg);
    code_lrgvar(e1,creg);
    code_cmp_lregister(creg,label,cond);
}

void
code_cmp_lrlvar(int e1,int creg,int label,int cond)
{
    use_longlong(creg);
    code_lrlvar(e1,creg);
    code_cmp_lregister(creg,label,cond);
}

void
code_lassign(int e2,int creg)
{
    char *drn = register_name(e2);
    char *crn_h;
    char *crn_l;

    use_longlong(creg);
    crn_h = lregister_name_high(creg);
    crn_l = lregister_name_low(creg);
#if ENDIAN_L==0
    printf("\tstw %s,0(%s)\n",crn_l,drn);
    printf("\tstw %s,%d(%s)\n",crn_h,SIZE_OF_INT,drn);
#else
    printf("\tstw %s,0(%s)\n",crn_h,drn);
    printf("\tstw %s,%d(%s)\n",crn_l,SIZE_OF_INT,drn);
#endif
}

void
code_lassign_gvar(int e2,int creg)
{
    int r;
    use_longlong(creg);
    r = get_ptr_cache((NMTBL*)caddr(e2));
#if ENDIAN_L==0
    code_ldf(cstore(0),lregister_name_low(creg),cadr(e2),r);
    code_ldf(cstore(0),lregister_name_high(creg),cadr(e2)+SIZE_OF_INT,r);
#else
    code_ldf(cstore(0),lregister_name_high(creg),cadr(e2),r);
    code_ldf(cstore(0),lregister_name_low(creg),cadr(e2)+SIZE_OF_INT,r);
#endif
}

void
code_lassign_lvar(int e2,int creg)
{
    char *crn_h;
    char *crn_l;

    use_longlong(creg);
    crn_h = lregister_name_high(creg);
    crn_l = lregister_name_low(creg);
    lvar_intro(e2);
#if ENDIAN_L==0
    printf("\tstw %s,",crn_l);lvar(e2);
    printf("\tstw %s,",crn_h);lvar(e2+SIZE_OF_INT);
#else
    printf("\tstw %s,",crn_h);lvar(e2);
    printf("\tstw %s,",crn_l);lvar(e2+SIZE_OF_INT);
#endif
}

void
code_lassign_lregister(int e2,int reg)
{
    use_longlong(reg);
    if (e2!=reg) {
	lmove(e2,reg);
    }
}

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];
}

void
code_lconst(int e1,int creg)
{
    use_longlong(creg);
#if ENDIAN_L==0
    code_const(code_l1(lcadr(e1)),regv_h(creg));
    code_const(code_l2(lcadr(e1)),regv_l(creg));
#else
    code_const(code_l1(lcadr(e1)),regv_l(creg));
    code_const(code_l2(lcadr(e1)),regv_h(creg));
#endif
}

void
code_lneg(int creg)
{
    use_longlong(creg);
    printf("\tsubfic %s,%s,0\n",
	lregister_name_low(creg),lregister_name_low(creg));
    printf("\tsubfze %s,%s\n",
	lregister_name_high(creg),lregister_name_high(creg));
}

void
code_lrgvar(int e1,int creg)
{
    int r;
    char *crn_h;
    char *crn_l;
    use_longlong(creg);
    crn_h = lregister_name_high(creg);
    crn_l = lregister_name_low(creg);
    r = get_ptr_cache((NMTBL*)caddr(e1));
#if ENDIAN_L==0
    code_ldf(cload(0),crn_l,cadr(e1),r);
    code_ldf(cload(0),crn_h,cadr(e1)+SIZE_OF_INT,r);
#else
    code_ldf(cload(0),crn_h,cadr(e1),r);
    code_ldf(cload(0),crn_l,cadr(e1)+SIZE_OF_INT,r);
#endif
}

void
code_lrlvar(int e1,int creg)
{
    char *crn_h;
    char *crn_l;
    use_longlong(creg);
    crn_h = lregister_name_high(creg);
    crn_l = lregister_name_low(creg);
    lvar_intro(e1);
#if ENDIAN_L==0
    printf("\tlwz %s,",crn_l); lvar(e1);
    printf("\tlwz %s,",crn_h); lvar(e1+SIZE_OF_INT);
#else
    printf("\tlwz %s,",crn_h); lvar(e1);
    printf("\tlwz %s,",crn_l); lvar(e1+SIZE_OF_INT);
#endif
}

#if 0
static int lumod_lib_used=0;
static char *lumod_lib[] = {
};

static int lmod_lib_used=0;
static char *lmod_lib[] = {
};

static int ludiv_lib_used=0;
static char *ludiv_lib[] = {
};

static int ldiv_lib_used=0;
static char *ldiv_lib[] = {
};
#endif

static int lsrd_lib_used=0;
static char *lsrd_lib[] = {
".text",
"        .align 2",
"lsrd__:",
"       mr.     r5,r5",
"       beqlr",
"       subfic  r2,r5,32",
"      stw     r3,-32(r1)",
"      stw     r4,-28(r1)",
"      cmpwi   cr7,r2,0",
"      bgt+    cr7,L__lsrd1",
"      neg     r0,r2",
"      lwz     r2,-32(r1)",
"      li      r9,0",
"      srw     r2,r2,r0",
"      stw     r9,-48(r1)",
"      b       L__lsrd2",
"L__lsrd1:      lwz     r0,-32(r1)",
"      slw     r9,r0,r2",
"      lwz     r2,-28(r1)",
"      srw     r0,r0,r5",
"      srw     r2,r2,r5",
"      stw     r0,-48(r1)",
"      or      r2,r2,r9",
"L__lsrd2:      stw     r2,-44(r1)",
"      lwz     r3,-48(r1)",
"      lwz     r4,-44(r1)",
"      blr",
0
};

static int asld_lib_used=0;
static char *asld_lib[] = {
".text",
"        .align 2",
"asld__:",
"       mr.     r5,r5",
"       beqlr",
"       subfic  r2,r5,32",
"      stw     r3,-32(r1)",
"      stw     r4,-28(r1)",
"      cmpwi   cr7,r2,0",
"      bgt+    cr7,L__asld1",
"      neg     r0,r2",
"      lwz     r2,-28(r1)",
"      li      r9,0",
"      slw     r2,r2,r0",
"      stw     r9,-44(r1)",
"      b       L__asld2",
"L__asld1:      lwz     r0,-28(r1)",
"      srw     r9,r0,r2",
"      lwz     r2,-32(r1)",
"      slw     r0,r0,r5",
"      slw     r2,r2,r5",
"      stw     r0,-44(r1)",
"      or      r2,r2,r9",
"L__asld2:      stw     r2,-48(r1)",
"      lwz     r3,-48(r1)",
"      lwz     r4,-44(r1)",
"      blr",
0
};

static int asrd_lib_used=0;
static char *asrd_lib[] = {
".text",
"        .align 2",
"asrd__:",
"       mr.     r5,r5",
"       beqlr",
"       subfic  r2,r5,32",
"      stw     r3,-32(r1)",
"      stw     r4,-28(r1)",
"      cmpwi   cr7,r2,0",
"      bgt+    cr7,L__asrd1",
"      lwz     r0,-32(r1)",
"      neg     r2,r2",
"      sraw    r2,r0,r2",
"      srawi   r0,r0,31",
"      b       L__asrd2",
"L__asrd1:      lwz     r0,-32(r1)",
"      slw     r9,r0,r2",
"      lwz     r2,-28(r1)",
"      sraw    r0,r0,r5",
"      srw     r2,r2,r5",
"      or      r2,r2,r9",
"L__asrd2:      stw     r0,-48(r1)",
"      stw     r2,-44(r1)",
"      lwz     r3,-48(r1)",
"      lwz     r4,-44(r1)",
"      blr",
0
};

static void
extern_conv(char *conv)
{
    code_save_stacks();
    clear_ptr_cache();
    extern_define(conv,0,FUNCTION,1);
    printf("\tbl L_%s$stub\n",conv);
}

#if FLOAT_CODE
#if 0
static int d2ll_lib_used=0;
static char *d2ll_lib[] = {
};

static int d2ull_lib_used=0;
static char *d2ull_lib[] = {
};

static int f2ll_lib_used=0;
static char *f2ll_lib[] = {
};

static int f2ull_lib_used=0;
static char *f2ull_lib[] = {
};

static int ll2d_lib_used=0;
static char *ll2d_lib[] = {
};

static int ll2f_lib_used=0;
static char *ll2f_lib[] = {
};
#endif

#endif

static void
code_asld_lib(int reg,int oreg)
{ 
    code_save_stacks();
    clear_ptr_cache();
    asld_lib_used = 1;
    set_lreg_operand(reg,1);
    set_lreg(RET_LREGISTER,0);
    if (regv_l(oreg)!=5) {
	printf("\tmr r5,%s\n", lregister_name_low(oreg));
    }
    printf("\tbl asld__\n");
}

static void
code_asrd_lib(int reg,int oreg) // ___ashrdi3$stub
{
    code_save_stacks();
    clear_ptr_cache();
    asrd_lib_used = 1;
    set_lreg_operand(reg,1);
    set_lreg(RET_LREGISTER,0);
    if (regv_l(oreg)!=5) {
	printf("\tmr r5,%s\n", lregister_name_low(oreg));
    }
    printf("\tbl asrd__\n");
}

static void
code_lsrd_lib(int reg,int oreg) // ___lshrdi3$stub
{
    code_save_stacks();
    clear_ptr_cache();
    lsrd_lib_used = 1;
    set_lreg_operand(reg,1);
    set_lreg(RET_LREGISTER,0);
    if (regv_l(oreg)!=5) {
	printf("\tmr r5,%s\n", lregister_name_low(oreg));
    }
    printf("\tbl lsrd__\n");
}

static void
code_ldiv_lib(int reg,int oreg) // ___divdi3$stub
{
    code_save_stacks();
    clear_ptr_cache();
    set_lreg_operand(reg,1);
    set_lreg(RET_LREGISTER,0);
    set_lreg_operand1(oreg,1);
    extern_conv("__divdi3");
}

static void
code_ludiv_lib(int reg,int oreg) // ___udivdi3$stub
{
    code_save_stacks();
    clear_ptr_cache();
    set_lreg_operand(reg,1);
    set_lreg(RET_LREGISTER,0);
    set_lreg_operand1(oreg,1);
    extern_conv("__udivdi3");
}

static void
code_lmod_lib(int reg,int oreg) // ___moddi3$stub
{
    code_save_stacks();
    clear_ptr_cache();
    set_lreg_operand(reg,1);
    set_lreg(RET_LREGISTER,0);
    set_lreg_operand1(oreg,1);
    extern_conv("__moddi3");
}

static void
code_lumod_lib(int reg,int oreg) // ___umoddi3$stub
{
    code_save_stacks();
    clear_ptr_cache();
    set_lreg(RET_LREGISTER,0);
    set_lreg_operand(reg,1);
    set_lreg_operand1(oreg,1);
    extern_conv("__umoddi3");
}

#define check_lreg(reg) if (reg!=lreg) { lmove(reg,lreg); }

void
ltosop(int op,int reg,int oreg)
{
    int dx = -1;
    int ox = -1;
    char *orn_h,*crn_h,*drn_h;
    char *orn_l,*crn_l,*drn_l;
    // creg = creg op oreg

    use_longlong(reg);
    if(oreg==-1) {
	error(-1);
    } else if (oreg<= -REG_LVAR_OFFSET) {
	ox = get_lregister(); if (ox<0) error(-1);
	use_reg(ox);
        code_lrlvar(oreg+REG_LVAR_OFFSET,ox);
	oreg = ox;
    }

    switch(op) {
    case LLSHIFT:
    case LULSHIFT:
	code_asld_lib(reg,oreg); // ___ashldi3$stub
	check_lreg(reg);
	if(ox!=-1) free_register(ox);
	return;
    case LRSHIFT:
	code_asrd_lib(reg,oreg); // ___ashrdi3$stub
	check_lreg(reg);
	if(ox!=-1) free_register(ox);
	return;
    case LURSHIFT:
	code_lsrd_lib(reg,oreg); // ___lshrdi3$stub
	check_lreg(reg);
	if(ox!=-1) free_register(ox);
	return;
    }
    orn_h = lregister_name_high(oreg);
    orn_l = lregister_name_low(oreg);
    crn_h = lregister_name_high(reg);
    crn_l = lregister_name_low(reg);
    switch(op) {
    case LADD:
	printf("\taddc %s,%s,%s\n",crn_l,crn_l,orn_l);
	printf("\tadde %s,%s,%s\n",crn_h,crn_h,orn_h);
	break;
    case LSUB:
	printf("\tsubfc %s,%s,%s\n",crn_l,orn_l,crn_l);
	printf("\tsubfe %s,%s,%s\n",crn_h,orn_h,crn_h);
	break;
    case LCMP:
	error(-1);
	break;
    case LBAND: 
	printf("\tand %s,%s,%s\n",crn_l,crn_l,orn_l);
	printf("\tand %s,%s,%s\n",crn_h,crn_h,orn_h);
	break;
    case LEOR: 
	printf("\txor %s,%s,%s\n",crn_l,crn_l,orn_l);
	printf("\txor %s,%s,%s\n",crn_h,crn_h,orn_h);
	break;
    case LBOR:
	printf("\tor %s,%s,%s\n",crn_l,crn_l,orn_l);
	printf("\tor %s,%s,%s\n",crn_h,crn_h,orn_h);
	break;
    case LMUL:
    case LUMUL:
	// code_save_stacks();
	// clear_ptr_cache();
	dx=get_lregister();
	use_reg(dx);
	drn_l = lregister_name_low(dx);
	drn_h = lregister_name_high(dx);
        /*
            drn_l = l32( crn_l * orn_l);
            drn_h = h32( crn_l * orn_l);
            orn_l = l32( crn_h * orn_l);
	    drn_h = drn_h + orn_l;
            crn_l = l32( crn_l * orn_h);
	    crn_h = drn_h + crn_l;
	    crn_l = drn_l;
        */
	printf("\tmulhwu %s,%s,%s\n",drn_h,crn_l,orn_l);
	printf("\tmullw %s,%s,%s\n", drn_l,crn_l,orn_l);
	printf("\tmullw %s,%s,%s\n", orn_l,crn_h,orn_l);
	printf("\tadd %s,%s,%s\n",   drn_h,drn_h,orn_l);
	printf("\tmullw %s,%s,%s\n", crn_l,orn_h,crn_l);
	printf("\tadd %s,%s,%s\n",   crn_h,drn_h,crn_l);
	printf("\tmr %s,%s\n",       crn_l,drn_l);
	break;
    case LDIV:
	code_ldiv_lib(reg,oreg); // ___divdi3$stub
	check_lreg(reg);
	break;
    case LUDIV:
	code_ludiv_lib(reg,oreg); // ___udivdi3$stub
	check_lreg(reg);
	break;
    case LMOD:
	code_lmod_lib(reg,oreg); // ___moddi3$stub
	check_lreg(reg);
	break;
    case LUMOD:
	code_lumod_lib(reg,oreg); // ___umoddi3$stub
	check_lreg(reg);
	break;
    default:
	error(-1);
    }
    if(ox!=-1) free_register(dx);
    if(dx!=-1) free_register(dx);
}

int
code_lconst_op_p(int op,int e)
{
    int v;
    if (car(e)==LCONST) {
	if (!(-32766<lcadr(e)&&lcadr(e)<32767)) return 0;
	v = lcadr(e);
    } else if (car(e)==CONST) {
	if (!(-32766<cadr(e)&&cadr(e)<32767)) return 0;
	v = cadr(e);
    } else return 0;
    
    switch(op) {
    case LLSHIFT:
    case LULSHIFT:
    case LRSHIFT:
    case LURSHIFT:
	return  (0<v&&v<31);
    case LADD:
    case LSUB:
	return 1;
    case LBOR:
	return  (v>0);
    default:
	return 0;
    }
}

void
loprtc(int op,int creg,int e)
{
    char *crn_h;
    char *crn_l;
    char *grn;
    int v;
    int greg;

    use_longlong(creg);
    crn_h = lregister_name_high(creg);
    crn_l = lregister_name_low(creg);

    if (car(e)==LCONST) v = lcadr(e);
    else if (car(e)==CONST) v = cadr(e);

    switch(op) {
    case LLSHIFT:
    case LULSHIFT:
	greg = get_register();
	use_reg(greg);
	grn = register_name(greg);
	printf("\tsrwi %s,%s,%d\n",grn,crn_l,32-v);
	printf("\tslwi %s,%s,%d\n",crn_h,crn_h,v);
	printf("\tor %s,%s,%s\n",crn_h,grn,crn_h);
	printf("\tslwi %s,%s,%d\n",crn_l,crn_l,v);
	free_register(greg);
	return;
    case LRSHIFT:
	greg = get_register();
	use_reg(greg);
	grn = register_name(greg);
	printf("\tsrwi %s,%s,%d\n",grn,crn_l,v);
	printf("\tinsrwi %s,%s,%d,0\n",grn,crn_h,v);
	printf("\tsrawi %s,%s,%d\n",crn_h,crn_h,v);
	printf("\tmr %s,%s\n",crn_l,grn);
	free_register(greg);
	return;
    case LURSHIFT:
	greg = get_register();
	use_reg(greg);
	grn = register_name(greg);
	printf("\tslwi %s,%s,%d\n",grn,crn_h,32-v);
	printf("\tsrwi %s,%s,%d\n",crn_l,crn_l,v);
	printf("\tor %s,%s,%s\n",crn_l,grn,crn_l);
	printf("\tsrwi %s,%s,%d\n",crn_h,crn_h,v);
	free_register(greg);
	return;
    case LSUB:
	v = -v;
    case LADD:
	printf("\taddic %s,%s,%d\n",crn_l,crn_l,v);
	if (v<0)
	    printf("\taddme %s,%s\n",crn_h,crn_h);
	else
	    printf("\taddze %s,%s\n",crn_h,crn_h);
	break;
    case LBOR:
	printf("\tori %s,%s,lo16(%d)\n",crn_l,crn_l,v);
	break;
    default:
	error(-1);
    }
}


void
emit_lpop_free(int xreg)
{
    if (xreg>=0)
        free_register(xreg);
}

void
emit_lpush()
{
    int new_reg;
    if (!is_longlong_reg(creg)) error(-1);
    if (lreg_sp>MAX_MAX) error(-1);
    new_reg = get_lregister();        /* 絶対に取れる(?) */
    lreg_stack[lreg_sp++] = creg;     /* push するかわりにレジスタを使う */
    lreg = creg = new_reg;
}

void
code_i2ll(int reg)
{
    char *crn,*crn_h,*crn_l;
    int reg0;
    crn = register_name(reg0 = ireg);
    use_longlong(reg);
    crn_h = lregister_name_high(lreg);
    crn_l = lregister_name_low(lreg);
    if (reg0!=regv_l(lreg))
	printf("\tmr %s,%s\n",crn_l,crn);
    printf("\tsrawi %s,%s,31\n",crn_h,crn_l);
}

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

void
code_u2ll(int reg)
{
    char *crn,*crn_h,*crn_l;
    int reg0;
    crn = register_name(reg0 = ireg);
    use_longlong(reg);
    crn_h = lregister_name_high(lreg);
    crn_l = lregister_name_low(lreg);
    if (reg0!=regv_l(lreg))
	printf("\tmr %s,%s\n",crn_l,crn);
    printf("\tli %s,0\n",crn_h);
}

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

void
code_ll2i(int reg)
{
    char *crn_l;
    int reg0;
    crn_l = lregister_name_low(reg0=lreg);
    use_int(reg);
    if (ireg!=regv_l(reg0))
	printf("\tmr %s,%s\n",register_name(ireg),crn_l);
}

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

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

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

#if FLOAT_CODE

void
code_d2ll(int reg)
{
    // fixdfdi$stub
    set_freg(RET_FREGISTER,1);
    extern_conv("__fixdfdi");
    set_lreg(RET_LREGISTER,0);
    if (reg!=USE_CREG&&reg!=RET_LREGISTER)
	use_longlong(reg);
}

void
code_d2ull(int reg)
{
    set_freg(RET_FREGISTER,1);
    extern_conv("__fixunsdfdi");
    set_lreg(RET_LREGISTER,0);
    if (reg!=USE_CREG&&reg!=RET_LREGISTER)
	use_longlong(reg);
}

void
code_f2ll(int reg)
{
    set_freg(RET_FREGISTER,1);
    extern_conv("__fixdfdi");
    set_lreg(RET_LREGISTER,0);
    if (reg!=USE_CREG&&reg!=RET_LREGISTER)
	use_longlong(reg);
}

void
code_f2ull(int reg)
{
    set_freg(RET_FREGISTER,1);
    extern_conv("__fixsfdi");
    set_lreg(RET_LREGISTER,0);
    if (reg!=USE_CREG&&reg!=RET_LREGISTER)
	use_longlong(reg);
}

void
code_ll2d(int reg)
{
    set_lreg(RET_LREGISTER,1);
    extern_conv("__floatdidf");
    set_freg(RET_FREGISTER,0);
    if (reg!=USE_CREG&&reg!=RET_FREGISTER)
	use_float(1,reg);
}


void
code_ll2f(int reg)
{
    set_lreg(RET_LREGISTER,1);
    extern_conv("__floatdisf");
    set_freg(RET_FREGISTER,0);
    if (reg!=USE_CREG&&reg!=RET_FREGISTER)
	use_float(0,reg);
}

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

void
code_ull2f(int creg)
{
    code_ll2f(creg);
}

#endif

static void
ladd(int creg,int reg,int dir)   // creg=reg+dir
{
    printf("\taddic %s,%s,%d\n", 
	    lregister_name_low(creg),lregister_name_low(reg), dir);
    printf("\tadd%s %s,%s\n", dir>0?"ze":"me",
	    lregister_name_high(creg),lregister_name_high(reg));
}

void
code_lpreinc(int e1,int e2,int reg)
{
    int dreg,xreg;
    int dir=caddr(e1);
    if (car(e2)==LREGISTER) {
	use_longlong(reg);
	ladd(cadr(e2),cadr(e2),dir);
        if (reg!=cadr(e2)) {
	    lmove(reg,cadr(e2));
	}
        return;
    } 
    g_expr(e2);
    if(!is_int_reg(creg)) error(-1);
    emit_push();
    if (reg==USE_CREG) {
	dreg=get_lregister(); if (!dreg) error(-1);
	set_lreg(dreg,0);  // free old lreg==creg
    } else {
        dreg = reg;
    }
    xreg = emit_pop(0);
    lload(xreg,dreg,0);
    ladd(dreg,dreg,dir);
    code_lassign(xreg,dreg);
    emit_pop_free(xreg);
}

void
code_lpostinc(int e1,int e2,int reg)
{
    int dreg,nreg,xreg;
    int dir=caddr(e1);
    if (car(e2)==LREGISTER) {
	use_longlong(reg);
	if (reg!=cadr(e2))
	    lmove(reg,cadr(e2));
	ladd(cadr(e2),cadr(e2),dir);
        return;
    } 
    g_expr(e2);
    if(!is_int_reg(creg)) error(-1);
    emit_push();
    nreg=get_lregister(); if (!nreg) error(-1);
    if (reg==USE_CREG) {
	dreg=get_lregister(); if (!dreg) error(-1);
	set_lreg(dreg,0);  // free old lreg==creg
    } else {
        dreg = reg;
    }
    xreg = emit_pop(0);
    lload(xreg,dreg,0);
    ladd(nreg,dreg,dir);
    code_lassign(xreg,nreg);
    emit_pop_free(xreg);
    free_register(nreg);
}

void
code_lassop(int op,int reg)
{
    int xreg;
    int edx,edx0=-1;

    // (*creg) op = pop()
    xreg = emit_lpop(0);       /* pop e3 value */
    if (!is_int_reg(creg)) error(-1);
    edx = ireg;
    emit_push();
    use_longlong(reg);
    if (regv_l(lreg)==edx || regv_h(lreg)==edx) {
	// this can't happen
	edx0 = get_register(); if(!edx0) error(-1);
	printf("# lassop\n\tmr %s,%s\n",register_name(edx0),register_name(edx));
	edx = edx0;
    }
    lload(edx0=edx,reg,0);
    ltosop(op,reg,xreg);
    use_reg(reg);
    edx = emit_pop(0);
    code_lassign(edx,reg);
    if (edx0!=-1)
	free_register(edx0);
    emit_pop_free(edx);
    emit_lpop_free(xreg);
}

void
code_register_lassop(int reg,int op) {
    // reg op = pop()
    int  xreg=emit_lpop();
    ltosop(op,reg,xreg);
    emit_lpop_free(xreg);
}   


#endif

void
code_save_stacks()
{
    int i,reg;
    for(i=0;i<reg_sp;i++) {
        if ((reg=reg_stack[i])>=0) {
            code_assign_lvar(
                (reg_stack[i]=new_lvar(SIZE_OF_INT)),reg,0); 
            reg_stack[i]= reg_stack[i]-REG_LVAR_OFFSET;
	    free_register(reg);
        }
    }
#if FLOAT_CODE
    for(i=0;i<freg_sp;i++) {
        if ((reg=freg_stack[i])>=0) {
            code_dassign_lvar(
                (freg_stack[i]=new_lvar(SIZE_OF_DOUBLE)),reg,1); 
            freg_stack[i]= freg_stack[i]-REG_LVAR_OFFSET;
	    free_register(reg);
        }
    }
#endif
#if LONGLONG_CODE
    for(i=0;i<lreg_sp;i++) {
        if ((reg=lreg_stack[i])>=0) {
            code_lassign_lvar(
                (lreg_stack[i]=new_lvar(SIZE_OF_LONGLONG)),reg); 
            lreg_stack[i]= lreg_stack[i]-REG_LVAR_OFFSET;
	    free_register(reg);
        }
    }
#endif
}

void
emit_lib(char *p[])
{
    while(*p) {
	printf("%s\n",*p++);
    }
}

void
code_closing()
{
#if FLOAT_CODE
    if (d2u_lib_used) emit_lib(d2u_lib);
    if (u2d_lib_used) emit_lib(u2d_lib);
    if (float_one_lib_used) emit_lib(float_one_lib);
    if (float_zero_lib_used) emit_lib(float_zero_lib);
    if (i2d_lib_used) emit_lib(i2d_lib);
#endif
#if LONGLONG_CODE
    if (asld_lib_used) emit_lib(asld_lib);
    if (lsrd_lib_used) emit_lib(lsrd_lib);
    if (asrd_lib_used) emit_lib(asrd_lib);
#if 0
    if (lumod_lib_used) emit_lib(lumod_lib);
    if (lmod_lib_used) emit_lib(lmod_lib);
    if (ludiv_lib_used) emit_lib(ludiv_lib);
    if (ldiv_lib_used) emit_lib(ldiv_lib);
#endif
#if FLOAT_CODE
#if 0
    if (dl2ll_lib_used) emit_lib(dl2ll_lib);
    if (d2ull_lib_used) emit_lib(d2ull_lib);
    if (f2ll_lib_used) emit_lib(f2ll_lib);
    if (f2ull_lib_used) emit_lib(f2ull_lib);
    if (ll2d_lib_used) emit_lib(ll2d_lib);
    if (ll2f_lib_used) emit_lib(ll2f_lib);
#endif
#endif
#endif
    global_table();
    /* printf("\t.ident \"Micro-C compiled\"\n"); */
}

#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)
{
    int t,s,u=-1;
    char *crn = register_name(csvalue); // can be t,s,u
    char *trn = register_name(t=get_register());
    char *srn = register_name(s=get_register());
    char *urn;

    inc_cmpflag();
    if (min>32767||min<-32765) {
	if (t==csvalue) {
	    code_const(min,s);
	    printf("\tsub\t%s,%s,%s\n",trn,crn,srn);
	} else {
	    code_const(min,t);
	    printf("\tsub\t%s,%s,%s\n",trn,crn,trn);
	}
    } else {
	printf("\taddi\t%s,%s,lo16(%d)\n",trn,crn,-min);
    }
    printf("\tcmplwi   cr%d,%s,%d\n",cmpflag,trn,max-min);
    printf("\tbgt-\tcr%d,L_%d\n",cmpflag,dlabel);
    inc_cmpflag();
    switch(delta) {
    case 1: printf("\tslwi %s,%s,2\n",trn,trn); break;
    case 2: 
	printf("\tli %s,1\n",srn);
	printf("\tand %s,%s,%s\n",srn,srn,trn);
	printf("\tcmplwi   cr%d,%s,0\n",cmpflag,srn);
	printf("\tbne\tcr%d,L_%d\n",cmpflag,dlabel);
	printf("\tslwi %s,%s,1\n",trn,trn);
	break;
    case 4: 
	printf("\tli %s,3\n",srn);
	printf("\tand %s,%s,%s\n",srn,srn,trn);
	printf("\tcmplwi   cr%d,%s,0\n",cmpflag,srn);
	printf("\tbne\tcr%d,L_%d\n",cmpflag,dlabel);
	break;
    default:
	urn = register_name(u=get_register());
	printf("\tli %s,%d\n",srn,delta);
	printf("\tdivwu %s,%s,%s\n",urn,trn,srn);
	printf("\tmullw %s,%s,%s\n",srn,urn,srn);
	printf("\tsubf %s,%s,%s\n",srn,trn,srn);
	printf("\tcmplwi   cr%d,%s,0\n",cmpflag,srn);
	printf("\tbne\tcr%d,L_%d\n",cmpflag,dlabel);
	printf("\tslwi %s,%s,2\n",trn,urn);
    }
    printf("\taddis %s,r31,ha16(L_%d-L_%d)\n",
	     srn,l,code_base);
    printf("\tla %s,lo16(L_%d-L_%d)(%s)\n",
	     srn,l,code_base,srn);
    printf("\tadd %s,%s,%s\n",trn,srn,trn);
    printf("\tlwz r0,0(%s)\n",trn);
    printf("\tadd r0,r0,%s\n",srn);
    printf("\tmtctr r0\n");
    printf("\tbctr\n");

    free_register(s);
    free_register(t);
    if (u!=-1) free_register(u);
}

void
code_table_open(int l)
{
    printf("\t.p2align 2\n");
    fwddef(l);
}

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

void
code_table_close()
{
}

#endif

#if ASM_CODE

/*
    print an operand  
 */

static void
emit_asm_operand(int rstr)
{
    if (car(rstr)==REGISTER) {
	printf("%s",register_name(cadr(rstr)));
    } 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("L_%d",cadr(rstr));
    } else {
	error(-1);
    }
}

/*
     prepare asm operands

     char *constraints sgtring
     int  oeprand 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
 */

extern 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 (bitpos) 
	    loprtc(LLSHIFT,reg,list2(CONST,bitpos));
	/* shift right */
	if ((i=bitsz-bitsize)) 
	    loprtc(sign?LRSHIFT:LURSHIFT,reg,list2(CONST,i));
    } else {
	use_int(reg);
	/* shift left */
	if ((i=bitpos+(32-bitsz))) 
	    oprtc(LSHIFT,reg,list2(CONST,i));
	/* shift right */
	if ((i=bitsz-bitsize+(32-bitsz))) 
	    oprtc(sign?RSHIFT:URSHIFT,reg,list2(CONST,i));
    }
}

/* bit field replacement */

static void
make_mask_and_or(int mask,int tmp,char *trn,char *crn,char *lrn)
{
// printf("# mask 0x%08x ~0x%08x\n",mask,~mask);
	code_const(~mask,tmp);
	printf("\tor %s,%s,%s\n",trn,crn,trn);
	/* do conjunction  */
	printf("\tand %s,%s,%s\n",lrn,trn,lrn);
	/* make or-mask  */
	code_const(mask,tmp);
	printf("\tand %s,%s,%s\n",trn,crn,trn);
	/* do disjunction  */
	printf("\tor %s,%s,%s\n",crn,trn,lrn);
}

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;
    int tmp = -1;
    int i;
    char *crn,*lrn,*trn;
    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);
	crn = lregister_name_low(value);
	lrn = lregister_name_low(lvalue);
	/* shift left */
	if ((i=bitsz-bitsize-bitpos)) 
	    loprtc(LLSHIFT,value,list2(CONST,i));
	trn = register_name(tmp = get_register());
	if (bitpos+bitsize>=32) {
	    /* make and-mask lower */
	    mask = make_mask(bitpos>=32?bitpos-32:0,bitpos+bitsize-32-1);
	    make_mask_and_or(mask,tmp,trn,crn,lrn);
	}
	crn = lregister_name_high(value);
	lrn = lregister_name_high(lvalue);
	if (bitpos<32) {
	    /* make and-mask upper */
	    mask = make_mask(bitpos,bitpos+bitsize>=32?31:bitpos+bitsize-1);
	    make_mask_and_or(mask,tmp,trn,crn,lrn);
	}
    } else {
	use_int(value);
	crn = register_name(value);
	lrn = register_name(lvalue);
	/* shift left */
	if ((i=bitsz-bitsize-bitpos)) 
	    oprtc(LSHIFT,value,list2(CONST,i));
	trn = register_name(tmp = get_register());
	/* make and-mask */
	mask = make_mask(bitpos+(32-bitsz),bitpos+bitsize-1+(32-bitsz));
	make_mask_and_or(mask,tmp,trn,crn,lrn);
    }
    if (tmp!=-1) free_register(tmp);
}

static void
make_mask_and_or_const(int mask,char *crn,int c)
{
    char *trn;
    int tmp = -1;
// printf("# mask 0x%08x ~0x%08x\n",mask,~mask);
    if ((~mask|c)!=-1) {
	trn = register_name(tmp=get_register());
	code_const((~mask|c),tmp);
	/* do conjunction  */
	printf("\tand %s,%s,%s\n",crn,trn,crn);
    }
    /* make or-mask  */
    c = mask&c;
    if (c!=0) {
	/* do disjunction  */
	if (!((mask&c)&0xffff0000)) {
	    printf("\tori %s,%s,lo16(%d)\n",crn,crn,c);
	} else {
	    trn = register_name(tmp=get_register());
	    code_const(c,tmp);
	    printf("\tor %s,%s,%s\n",crn,trn,crn);
	}
    }
    if (tmp!=-1) free_register(tmp);
}

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 i;
    int c;
#if LONGLONG_CODE
    long long lc;
#endif
    char *crn;
    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);
	crn = lregister_name_low(lvalue);
	/* shift left */
	lc = lcadr(value);
	if ((i=bitsz-bitsize-bitpos)) 
	    lc <<= i;
	if (bitpos+bitsize>=32) {
	    /* make and-mask lower */
	    mask = make_mask(bitpos>=32?bitpos-32:0,bitpos+bitsize-32-1);
	    make_mask_and_or_const(mask,crn,(int)lc);
	}
	crn = lregister_name_high(lvalue);
	if (bitpos<32) {
	    /* make and-mask upper */
	    mask = make_mask(bitpos,bitpos+bitsize>=32?31:bitpos+bitsize-1);
	    make_mask_and_or_const(mask,crn,(int)(lc>>32));
	}
#endif
    } else {
	use_int(lvalue);
	crn = register_name(lvalue);
	/* shift left */
	c = cadr(value);
	if ((i=bitsz-bitsize-bitpos)) 
	    c <<= i;
	/* make and-mask */
	mask = make_mask(bitpos+(32-bitsz),bitpos+bitsize-1+(32-bitsz));
	make_mask_and_or_const(mask,crn,c);
    }
}

#endif

/* end */