view mc-code-mips.c @ 283:230a3b98b843 mips-done

MIPS jump work.
author kono
date Mon, 24 May 2004 13:21:22 +0900
parents d61cf7a9b469
children ec1a68133f6a
line wrap: on
line source

/* Micro-C Code Generatation Part for Power PC (Mac OS X) */
/* $Id$ */

#define EXTERN extern
#include "mc.h"
#include "mc-code.h"
#include "mc-codegen.h"

char *l_include_path[] = {
    "/usr/include/",
    "/usr/lib/gcc-lib/mipsEEel-linux/2.95.2/include/",
    0
};

#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(int alignment);
static void init_ptr_cache();
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 void register_usage(char *s);

static int creg;

static int output_mode = TEXT_EMIT_MODE;
static int data_alignment = 0;

static FILE *asi;

static int cprestore_label;
static int fmask_label;
static int fmask_offset_label;
static int mask_label;
static int mask_offset_label;
static int register_save_return_label;
static int register_save_label;


static int r1_offset_label;
static int lvar_offset_label;
// static int cprestore_label;
static int    max_func_args = 0;


static int freg,ireg,lreg;
static int cmpreg;

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  0

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

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 21
#define REG_VAR_MIN  16
#define MIN_TMP_REG 4
#define MAX_TMP_REG 11

#define PTRC_REG 3

#define FREG_VAR_BASE 21
#define FREG_VAR_MIN  16
#define MIN_TMP_FREG 0
#define MAX_TMP_FREG 11

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)

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

#define LREG_V 3    /* for virtual long long/double register */
#define REGS_MAX (REAL_MAX_REGISTER+REAL_MAX_FREGISTER+REAL_MAX_LREGISTER+LREG_V)
static int mips_regs[REGS_MAX];
static int regv_h0[REAL_MAX_LREGISTER+LREG_V];
static int regv_l0[REAL_MAX_LREGISTER+LREG_V];
#define regv_h(i)  regv_h0[(i)-LREG_OFFSET]
#define regv_l(i)  regv_l0[(i)-LREG_OFFSET]

#define RET_REGISTER 2
#define REGISTER_OPERAND  4
#define RET_FREGISTER FREG_OFFSET
#define FREGISTER_OPERAND  (FREG_OFFSET +12)

#define RET_LREGISTER (LREG_OFFSET+REAL_MAX_LREGISTER)
#define LREGISTER_OPERAND  (LREG_OFFSET +REAL_MAX_LREGISTER +1)
#define LREGISTER_OPERAND_1  (LREG_OFFSET +REAL_MAX_LREGISTER +2)
#define RET_LREGISTER_L 2    /* low word */
#define RET_LREGISTER_H 3    /* high word */
#define LREGISTER_OPERAND_L  4    /* low word */
#define LREGISTER_OPERAND_H  5    /* high word */
#define LREGISTER_OPERAND_1_L  6    /* low word */
#define LREGISTER_OPERAND_1_H  7    /* high word */

#define RET_DREGISTER RET_LREGISTER 
#define DREGISTER_OPERAND LREGISTER_OPERAND
#define DREGISTER_OPERAND_1 LREGISTER_OPERAND_1
#define RET_DREGISTER_L RET_LREGISTER_L
#define RET_DREGISTER_H RET_LREGISTER_H
#define DREGISTER_OPERAND_H  LREGISTER_OPERAND_H  
#define DREGISTER_OPERAND_L  LREGISTER_OPERAND_L  
#define DREGISTER_OPERAND_1_L  LREGISTER_OPERAND_1_L  
#define DREGISTER_OPERAND_1_H  LREGISTER_OPERAND_1_H  

static int *regs  = mips_regs;

// #define CREG_REGISTER  (MAX_TMP_REG)
#define CREG_REGISTER  REGISTER_OPERAND
// #define FREG_FREGISTER (MAX_TMP_FREG+FREG_OFFSET)
#define FREG_FREGISTER FREGISTER_OPERAND
// #define LREG_LREGISTER (MAX_TMP_REG+LREG_OFFSET)
#define LREG_LREGISTER LREGISTER_OPERAND
#define DREG_DREGISTER LREG_LREGISTER
#define CMP_C1T (-1)


static int max_reg_var, max_freg_var;

static char *reg_name[] = {
    "$0","$1","$2","$3","$4","$5","$6","$7","$8","$9",
    "$10","$11","$12","$13","$14","$15","$16","$17","$18","$19",
    "$20","$21","$22","$23","$24","$25","$26","$27","$28","$29",
    "$30","$31",
    "$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 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+LREG_V)
#define is_double_reg(i)  is_longlong_reg(i)

#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) { 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;
}

static void lmove(int to,int from);
#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;
}

#define USING_DREG 5
#define INPUT_DREG 6

static
int use_double0() { 
    int i;
    use_longlong0();
    i = lreg;
    if (!regs[i]) regs[i]=USING_DREG;
    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


static int code_d1(double d);
static int code_d2(double d);
static void code_double_lib(char *lib,int to,int reg,int oreg);
static void code_double_lib_c(char *lib,int from,int to,double value);

#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();
static void clear_ptr_cache_reg(int r);
static void    set_ireg(int,int);
static void    set_dreg(int,int);
static void    set_freg(int,int);
static void    set_lreg(int,int);

static int max_func_args;
static int my_func_args;
static unsigned int code_mask();
static int code_mask_offset();
static unsigned int code_fmask();
static int code_fmask_offset();

#define ARG_LVAR_OFFSET 0x10000000

#define round16(i)   ((i+0xf)&~0xf)
#define round4(i)   ((i+3)&~3)

/*          

     Reorder is automatically done in assembler.
     delayed slot done within .set noreorder.

     r0    return value etc.
     $2,$3 return value. (dpcmp return value on $2)
     $0  special register
     $4-$7  input register
     r18-r24 saved register variable (input register for code segement)
     $25    jump register
     $31    stack pointer
     $fp    frame pointer

     $f0       return value etc.
     $f14,$f12 input register
     $f20-$f31 saved register variable

code segment stack frame

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


                      <-------r1_offset------------------------------>
                                      <------------lvar_offset------->
                                                              <-arg_offset->
 r+  +-----------+----+---------------+----------+-------------+----+
       callee arg xxx  register save   local      caller arg     xxx
                 ($r31)($fp) reg_save   disp          max_func_args*SIZE_OF_INT
        lvar>0                         lvar<0          lvar>0x1000 0000
                      prev $sp=$fp                                   $sp=$fp
 */

#define arg_offset 8
#define arg_offset1 0
#define disp_offset  0

#define func_disp_offset 8
#define code_disp_offset 0
#define jump_offset 0

#define CODE_LVAR(l) ((l)+code_disp_offset)
#define CODE_CALLER_ARG(l) ((l)+arg_offset1)
#define FUNC_LVAR(l) (l+disp_offset)
#define CALLER_ARG(l) ((l)+arg_offset1)
#define CALLEE_ARG(l) ((l)+arg_offset1)

static int
code_offset_set(NMTBL *fnptr)
{
    int lvar_offsetv,r1_offsetv;
    // int code_f = (fnptr->sc==CODE);

    disp &= -SIZE_OF_INT;
    lvar_offsetv = round16(-disp) +
	round16((max_func_args<2?2:max_func_args)*SIZE_OF_INT) +
	2*SIZE_OF_INT;
    r1_offsetv = lvar_offsetv + arg_offset + SIZE_OF_INT*2 +
	max_reg_var*SIZE_OF_INT+max_freg_var*SIZE_OF_FLOAT+2*SIZE_OF_INT ;
    lvar_offsetv += round16(r1_offsetv)-r1_offsetv;
    r1_offsetv = round16(r1_offsetv);

#if 1
printf("# vars= %d, regs= %d/%d, args= %d, extra= %d\n",
	round16(-disp),
	max_reg_var+2,
	max_freg_var,
	round16(max_func_args*SIZE_OF_INT),
	0
);
    printf("# mask_label $L_%d=0x%x\n",mask_label,code_mask());
    printf("# mask_offset$L_%d=%d\n",mask_offset_label,code_mask_offset());
    printf("# fmask_label $L_%d=0x%x\n",fmask_label,code_fmask());
    printf("# fmask_offset$L_%d=%d\n",fmask_offset_label,code_fmask_offset());
    printf("# cprestore  $L_%d=%d\n",cprestore_label ,round16(max_func_args*SIZE_OF_INT));
   printf("#\n");
   printf("# callee arg top=\t%d\n",CALLEE_ARG(0)+r1_offsetv);
   printf("# r1_offset=\t\t%d %d\n",r1_offsetv,r1_offsetv%16);
   printf("# reg_save_top=\t\t%d\n",r1_offsetv);
   printf("# reg_save_end=\t\t%d\n",
	-max_reg_var*SIZE_OF_INT-max_freg_var*SIZE_OF_FLOAT-2*SIZE_OF_INT+
	r1_offsetv);
   printf("# lvar_offset=\t\t%d %d\n",lvar_offsetv,lvar_offsetv%16);
   printf("# min local var=\t%d\n",FUNC_LVAR(0)+lvar_offsetv);
   printf("# max local var=\t%d\n",FUNC_LVAR(disp)+lvar_offsetv);
   printf("# min caller arg var=\t%d\n",
	CALLER_ARG(round16(max_func_args*SIZE_OF_INT)));
   printf("# max caller arg var=\t%d\n",CALLER_ARG(0));
   printf("#\n");
#endif
    fprintf(asi,"$L_%d=0x%x\n",mask_label,code_mask());
    fprintf(asi,"$L_%d=%d\n",mask_offset_label,code_mask_offset());
    fprintf(asi,"$L_%d=0x%x\n",fmask_label,code_fmask());
    fprintf(asi,"$L_%d=%d\n",fmask_offset_label,code_fmask_offset());
    fprintf(asi,"$L_%d=%d\n",cprestore_label ,
	round16((max_func_args>2?max_func_args:2)*SIZE_OF_INT));
    fprintf(asi,"$L_%d=%d\n",r1_offset_label,r1_offsetv);
    fprintf(asi,"$L_%d=%d\n",lvar_offset_label,lvar_offsetv);

    return r1_offsetv;
}

static void
lvar(int l)
{
    if (fnptr->sc==CODE) {
        if (l>=ARG_LVAR_OFFSET) {  /* caller's arguments */
            printf("%d($sp)\n",CODE_CALLER_ARG(l-ARG_LVAR_OFFSET));
        } else
            printf("%d($fp)\n",CODE_LVAR(l));
    } else if (l<0) {  /* local variable */
        printf("%d+$L_%d($fp)\n",FUNC_LVAR(l),lvar_offset_label);
    } else if (l>=ARG_LVAR_OFFSET) {  /* caller's arguments */
        printf("%d($sp)\n",CALLER_ARG(l-ARG_LVAR_OFFSET));
    } else { /* callee's arguments */
        printf("%d+$L_%d($fp)\n",CALLEE_ARG(l),r1_offset_label);
    }
}

static void
lvar_address(int l,int creg)
{
    if (fnptr->sc==CODE) {
        if (l>=ARG_LVAR_OFFSET) {  /* caller's arguments */
            printf("\taddu\t%s,$sp,%d\n",
		register_name(creg),CODE_CALLER_ARG(l-ARG_LVAR_OFFSET));
        } else
            printf("\taddu\t%s,$fp,%d\n",register_name(creg),CODE_LVAR(l));
    } else if (l<0) {  /* local variable */
        printf("\taddu\t%s,$fp,%d+$L_%d\n",register_name(creg),
		FUNC_LVAR(l),lvar_offset_label);
    } else if (l>=ARG_LVAR_OFFSET) {  /* caller's arguments */
        printf("\taddu\t%s,$sp,%d\n",
		register_name(creg),CALLER_ARG(l-ARG_LVAR_OFFSET));
    } else { /* callee's arguments */
        printf("\taddu\t%s,$fp,%d+$L_%d\n",
		register_name(creg),CALLEE_ARG(l),r1_offset_label);
    }
}


#define lvar_intro(e)  /* do nothing */

void
code_lvar(int e2,int reg) {
    use_int(reg);
    lvar_address(e2,reg);
}

void
code_init(void)
{
    int reg;
    macro_define("__STDC__ 1\n");
    macro_define("__SIZE_TYPE__ int\n");
    macro_define("__WCHAR_TYPE__ int\n");
    macro_define("__mips__ 1\n");
    macro_define("__LITTLE_ENDIAN__ 1\n");
    macro_define("__externsion__\n");
    macro_define("__flexarr\n");
    macro_define("__builtin_va_list int*\n");

    init_ptr_cache();
    reg=RET_LREGISTER;
    regv_l(reg) = RET_LREGISTER_L;
    regv_h(reg) = RET_LREGISTER_H;
    reg=LREGISTER_OPERAND;
    regv_l(reg) = LREGISTER_OPERAND_L;
    regv_h(reg) = LREGISTER_OPERAND_H;
    reg=LREGISTER_OPERAND_1;
    regv_l(reg) = LREGISTER_OPERAND_1_L;
    regv_h(reg) = LREGISTER_OPERAND_1_H;
}


void
gexpr_code_init(void){
    cmpreg = CMP_C1T ;
}

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


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

    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 = FREGISTER;
                n->dsp = cadr(reg);
                regs[n->dsp]= INPUT_REG;
                freg_var++;
                cadddr(args)=size(type);
            }
        } else if (type==DOUBLE) {
            if ((reg = get_input_lregister_var(reg_var,n,is_code0))) {
                n->sc = DREGISTER;
                n->dsp = cadr(reg);
                regs[i=n->dsp]= INPUT_DREG;
                regs[regv_l(i)]= INPUT_REG;
                regs[regv_h(i)]= INPUT_REG;
                reg_var+=2;
                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();
}


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 をつぶす */
    for(i=MAX_TMP_REG;i>MIN_TMP_REG;i--) {
	if (regs[i]==PTRC_REG) {
	    clear_ptr_cache_reg(i);
	} else 
	    continue;
	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||FLOAT_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+1>max_reg_var) max_reg_var=i+1;
	    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;
    if (d) {
      i = get_lregister();
      if (i!=-1) regs[i]=USING_DREG;
      return i;
    }
    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+1>max_freg_var) max_freg_var=i+1;
	    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;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) return -1;
    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+1>max_reg_var) max_reg_var=i+1;
		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+1>max_reg_var) max_reg_var=j+1;
			/* その場所を表す番号を返す */
			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;
    }
}

int
get_input_dregister_var(int i,NMTBL *n,int is_code,int d)
{
    int j;
    if (d) {
      // if (i<0||i>=MAX_INPUT_REGISTER_VAR) return 0;
      j = get_input_lregister_var(i,n,is_code);
      if (car(j)==LREGISTER) {
	if (regs[cadr(j)]==INPUT_REG) regs[cadr(j)]=INPUT_DREG;
	car(j) = DREGISTER;
      }
      return j;
    }
    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=12+FREG_OFFSET;
	else if (i==1) i=14+FREG_OFFSET;
	else if (i==2) return list3(REGISTER,6,(int)n);
	else if (i==3) return list3(REGISTER,7,(int)n);
	else return 0;
    }
    return list3(FREGISTER,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 (i%2==1) i++;
	}
#if ENDIAN==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||FLOAT_CODE
    for(i=0;i<REAL_MAX_LREGISTER;i++) {
	regs[i+LREG_OFFSET]=0; 
    }
    lreg = 0;
    // set_lreg(LREG_LREGISTER,0);
#endif
    for(i=0;i<MAX_REGISTER;i++) { regs[i]=0; }
#if FLOAT_CODE
    for(i=0;i<MAX_FREGISTER;i++) { regs[i+FREG_OFFSET]=0; }
    freg = get_dregister(1);
    set_freg(FREG_FREGISTER,0);
#endif
    ireg = creg = get_register();
    set_ireg(CREG_REGISTER,0);
    return;
}

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


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


void

emit_init(void)
{
    free_all_register();
    max_reg_var=0; max_freg_var=0;
    reg_sp = 0;
    freg_sp = 0;
    text_mode(3);
}

#define reg_var_num(i) (REG_VAR_BASE-i)

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

#define freg_var_num(i) (FREG_VAR_BASE-i+FREG_OFFSET)

int
get_dregister_var(NMTBL *n,int d)
{
    int i,j;
    if (d) {
        i = get_lregister_var(n);
	if (car(i)==LREGISTER) {
	    car(i) = DREGISTER;
	    regs[cadr(i)] = USING_DREG;
	} 
	return i;
    }
	    
    for(i=0;i<FREG_VAR_BASE-FREG_VAR_MIN;i++) {
	j = freg_var_num(i);
        if (! regs[j]) {       /* 使われていないなら */
            regs[j]=USING_REG; /*そのレジスタを使うことを宣言し*/
	    if (i+1>max_freg_var) max_freg_var=i+1;
	    /* その場所を表す番号を返す */
	    return list3(FREGISTER,j,(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;
}

#define MAX_PTR_CACHE 10

int ptr_cache=0;

void
init_ptr_cache()
{
    int i;
    for(i=0;i<MAX_PTR_CACHE;i++) {
	ptr_cache=glist3(0,ptr_cache,0);
    }
}

void
clear_ptr_cache_reg(int r)
{
    int ptcptr=ptr_cache;
    while(ptcptr) {
	if(car(ptcptr)&&caddr(ptcptr)==r) {
	    car(ptcptr)=0;
	    caddr(ptcptr)=0;
	    free_register(r);
	    return;
	}
	ptcptr=cadr(ptcptr);
    }
}

void
clear_ptr_cache()
{
    int ptcptr=ptr_cache;
    while(ptcptr) {
	if(car(ptcptr))
	    free_register(caddr(ptcptr));
	car(ptcptr)=0;
	caddr(ptcptr)=0;
	ptcptr=cadr(ptcptr);
    }
}


int
get_ptr_cache(NMTBL *nptr)
{
    int r;
    int ptcptr=ptr_cache;
    int g = (int)nptr;
    int p,p1;
    char *rrn;

    p1 = ptcptr; p = cadr(p1); /* unnecesary, if ptcptr is initialized */
    while(ptcptr) {
	if(car(ptcptr)==g) return caddr(ptcptr);
	p1=p; p=ptcptr;
	ptcptr=cadr(ptcptr);
    }
    cadr(p1)=0;            /* remove the last one */
    cadr(p) = ptr_cache;   /* connect current queue to the last one */
    ptr_cache = p;         /* now the last one is the top */
    if (!caddr(p)) {
	if((r=get_register())) {
	    caddr(p)=r; regs[r]=PTRC_REG;
	} else {
	    error(-1);
	    r=creg; /* this can't happen */
	}
	car(p)=g;
    } else {
	r = caddr(p);
    }
    rrn = register_name(r);
    printf("\tla %s,%s\n",rrn,nptr->nm);
    return r;
}

static char *cload(int sz,int sign) { 
    if (sign) {
        return sz==1?"lb":sz==SIZE_OF_SHORT?"lh":"lw"; 
    } else {
        return sz==1?"lbu":sz==SIZE_OF_SHORT?"lhu":"lw"; 
    }
}

static char *cstore(int sz) { return sz==1?"sb":sz==SIZE_OF_SHORT?"sh":"sw"; }

#define cext(sign,sz,reg)  /* do nothing */


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

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

void
code_rgvar(int e1,int reg) {
    use_int(reg);
    printf("\tlw %s,0(%s)\n",register_name(reg),
                             register_name(get_ptr_cache((NMTBL*)cadr(e1))));
}

void
code_crgvar(int e1,int reg,int sign,int sz){
    char *crn;
    use_int(reg);
    crn = register_name(reg);
    printf("\t%s %s,0(%s)\n",cload(sz,sign),crn,
                             register_name(get_ptr_cache((NMTBL*)cadr(e1))));
    cext(sign,sz,reg);
}



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


void
code_rlvar(int e2,int reg) {
    use_int(reg);
    lvar_intro(e2);
    printf("\tlw %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,sign),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("\tmove %s,%s\n",register_name(reg),register_name(r));
    return;
}


void
code_const(int e2,int reg) {
    char *crn;
    use_int(reg);
    crn = register_name(reg);
    printf("\tli %s,%d\n",crn,e2);
}


void
code_neg(int creg) {
    use_int(creg);
    printf("\tsubu %s,$0,%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) {
    int dreg = get_register();
    use_int(creg);

    printf("\txori %s,%s,0x0\n", 
        register_name(dreg),register_name(creg));
    printf("\tsltu %s,%s,1\n", 
        register_name(creg),register_name(dreg));
    free_register(dreg);
}

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("\taddu %s,%s,%d\n", 
		register_name(cadr(e2)),register_name(cadr(e2)), dir);
	if (cadr(reg)!=e2)
	    printf("\tmove %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,sign),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("\tmove %s,%s\n",register_name(reg),register_name(cadr(e2)));
	printf("\taddu %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,sign),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("\tla %s,$L_%d\n",crn,retcont);
}

#define R1SAVE 0

void
code_environment(int creg) {
    /* save frame pointer */
#if R1SAVE
    use_int(creg);
    printf("\tlw %s,0($fp)\n",register_name(creg));
#else
    use_int(creg);
    printf("\taddu %s,",register_name(creg));
    printf("$fp,%d+$L_%d\n",FUNC_LVAR(0),lvar_offset_label);
#endif
}

void
code_bool(int e1,int reg) {
    char *xrn;
    int e2,e3;
    b_expr(e1,1,e2=fwdlabel(),1);  /* including > < ... */
    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);
}

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

char *
code_ugt(int cond) {
  return code_gt(cond);
}

char *
code_ge(int cond) {
  return code_gt(!cond);
}

char *
code_uge(int cond) {
  return code_gt(!cond);
}

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


void
code_cmp_crgvar(int e1,int reg,int sz) {
    int r;
    char *crn;
    use_int(reg);
    crn = register_name(reg);
    r = get_ptr_cache((NMTBL*)cadr(e1));
    printf("\t%s %s,0(%s)\n",cload(sz,0),crn,register_name(r));
    cext(0,sz,r);
    cmpreg = reg;
    // printf("\tcmpwi cr0,%s,0\n",crn);
}


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


void
code_cmp_rgvar(int e1,int reg) {
    int r;
    char *crn;
    use_int(reg);
    crn = register_name(reg);
    r = get_ptr_cache((NMTBL*)cadr(e1));
    printf("\tlw %s,0(%s)\n",crn,register_name(r));
    code_cmp_register(reg);
}


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


void
code_cmp_register(int e2) {
    use_int(e2);
    cmpreg = e2;  // used by jcond,  beq $reg,$0,L_xx
}


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

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

    use_int(creg);
    crn = register_name(creg);

    s=(char *)cadr(e1);
    printf("\t.rdata\n\t.align 2\n");
    lb=fwdlabel();
    printf("$L_%d:\n",lb);
    ascii(s);
    if (output_mode==TEXT_EMIT_MODE) {
	printf(".text\n");
    } else {
	text_mode(2);
    }
    printf("\tla %s,$L_%d\n",crn,lb);
}

#define MAX_COPY_LEN 20

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("\tlb %s,%d(%s)\n",drn,offset,frn);
	printf("\tsb %s,%d(%s)\n",drn,offset,trn);
	break;
    case 2: case -2:
	printf("\tlh %s,%d(%s)\n",drn,offset,frn);
	printf("\tsh %s,%d(%s)\n",drn,offset,trn);
	break;
    case 4: case -4:
	printf("\tlw %s,%d(%s)\n",drn,offset,frn);
	printf("\tsw %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 $6,%d\n",length);
	printf("\tmove $5,%s\n",frn);
	printf("\tmove $4,%s\n",trn);
        /* overrap must be allowed */
	printf("\tjal %s\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("\taddu %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("\tlw %s,%d(%s)\n",srn,count,crn);
            printf("\tsw %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;
}

static 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("\tmove %s,%s\n",register_name(reg),register_name(ireg));
	    }
	}
	free_register(creg);
	regs[reg]=USING_REG;
    }
    creg = ireg = reg;
}

static 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("\tmov.s %s,%s\n",fregister_name(reg),fregister_name(freg));
	    }
	}
	// if (creg!=ireg) free_register(creg);
	regs[reg]=USING_REG;
    }
    creg = freg = reg;
}

static void
set_lreg0(int reg,int mode)
{
    if (reg!=creg) {
	if (lreg && reg!=lreg) {
	    if (mode) {
		printf("\tmove %s,%s\n",
		    lregister_name_low(reg),lregister_name_low(lreg));
		printf("\tmove %s,%s\n",
		    lregister_name_high(reg),lregister_name_high(lreg));
	    }
	    free_register(lreg);
	}
	if (creg!=lreg) {
	    free_register(creg);
	    if (creg==ireg) ireg = 0;
	}
	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;
}

static void
set_lreg(int reg,int mode)
{
    if (!is_longlong_reg(reg)) error(-1);
    set_lreg0(reg,mode);
}

static void
set_dreg(int reg,int mode)
{
    if (reg==RET_DREGISTER) {
	regv_l(reg) = RET_DREGISTER_L;
	regv_h(reg) = RET_DREGISTER_H;
    } else if (reg==DREGISTER_OPERAND) {
	regv_l(reg) = DREGISTER_OPERAND_L;
	regv_h(reg) = DREGISTER_OPERAND_H;
    } else if (reg==DREGISTER_OPERAND_1) {
	regv_l(reg) = DREGISTER_OPERAND_1_L;
	regv_h(reg) = DREGISTER_OPERAND_1_H;
    }
    set_lreg0(reg,mode);
    regs[regv_l(reg)]=USING_DREG;
    regs[regv_h(reg)]=USING_DREG;
}

static 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) {
	lmove(LREGISTER_OPERAND,reg);
    }
}

/*
static 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) {
	lmove(LREGISTER_OPERAND_1,reg);
    }
}
 */

static void
set_dreg_operand(int reg,int mode)
{
     set_lreg_operand(reg,mode);
}

/*
static void
set_dreg_operand1(int reg,int mode)
{
     set_lreg_operand1(reg,mode);
}
 */

void
use_reg(int arg)
{
// printf("# use reg %d\n",arg);
    if (arg<0||arg> REGS_MAX)
	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;
    } else if (is_double_reg(arg)) {
	clear_ptr_cache_reg(regv_l(arg));
	regs[regv_l(arg)]=USING_DREG;
	clear_ptr_cache_reg(regv_h(arg));
	regs[regv_h(arg)]=USING_DREG;
    }
}

void
code_save_input_registers()
{
    int args;
    NMTBL *n;
    int reg;
    int tag;
    int t;
    /* fnptr->dsp=list4(type,fnptr->dsp,(int)n,0); */
    int reg_offset = 0;
    int offset = 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;
	} else if (tag==FREGISTER) {
	    /* regs[reg]==INPUT_REG case should be considered */
	    n->dsp = offset;
	    t = n->ty;
	    if(t==FLOAT) {
		offset+=SIZE_OF_FLOAT; reg_offset+=1;
		if (reg==6||reg==7) {   // int register case
		    tag = REGISTER; t = INT;
		}
	    }
	    // else if(t==DOUBLE) { offset+=SIZE_OF_DOUBLE; reg_offset+=2; }
	    else error(-1);
	} else if (tag==DREGISTER) {
	    /* regs[reg]==INPUT_REG case should be considered */
	    n->dsp = offset;
	    t = n->ty;
	    offset+=SIZE_OF_DOUBLE; reg_offset+=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;
	} else {
	    offset += size(n->ty);
	    continue;
	}
	n->sc  = LVAR;
	g_expr_u(assign_expr0(list2(LVAR,n->dsp),list3(tag,reg,(int)n),t,t));
	if (tag==REGISTER||tag==DREGISTER||tag==FREGISTER||tag==LREGISTER) {
	    free_register(reg);
	}
    }
    my_func_args = offset;
}

static int
not_simple_p(int e3)
{
    return (e3==FUNCTION||e3==CONV||e3==RSTRUCT||e3==STASS||
	((e3/100==LOP/100)&&(e3==LDIV||e3==LUDIV||e3==LMOD||e3==LUMOD||
		e3==LLSHIFT||e3==LULSHIFT||e3==LRSHIFT||e3==LURSHIFT))||
	((e3/100==DOP/100)&&(e3==DDIV||e3==DADD||e3==DSUB||e3==DMUL||e3==DMINUS||
            e3== DPOSTINC || e3==DPREINC || e3==DASSOP ||
            e3== DOP+LT || e3== DOP+LE || e3== DOP+GT ||
            e3== DOP+GE || e3== DOP+EQ || e3== DOP+NEQ)));
}

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||
		     regs[regv_h(lreg)]==USING_DREG))
		  {
		    free_register(regv_h(lreg));
		}
		if (creg==lreg) creg = ireg;
		lreg = 0;
	    } else if (regv_h(lreg)==reg) {
		regs[lreg]=0;
		if (regv_h(lreg)>reg && (
		    (regs[regv_l(lreg)]==USING_DREG) ||
		    (regs[regv_l(lreg)]==USING_DREG) ))
		  {
		    free_register(regv_l(lreg));
		}
		if (creg==lreg) creg = ireg;
		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_double_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);
}

#define FASS_INPUT (FOP+199)

static void
code_assign_input_float_int(int e0) {
    int e1 = cadr(e0);
    int e2 = caddr(e0);
    int r;
    double value;
    char *frn;
    //  e2 = e3;
    if (car(e1)!=REGISTER) { error(-1); return; }
    frn = register_name(cadr(e1));
    switch(car(e2)) {
    case FCONST:
	value = dcadr(e2);
        printf("\tli.s %s,%12.12g\n",frn,value);
	break;
    case FRGVAR:
	r = get_ptr_cache((NMTBL*)cadr(e2));
	printf("\tlw %s,0(%s)\n",frn,register_name(r));
	break;
    case FRLVAR:
	lvar_intro(cadr(e2));
	printf("\tlw %s,",frn); lvar(cadr(e2));
    default:
	g_expr(e2);
    case FREGISTER:
	printf("\tmfc1 %s,%s\n",frn,fregister_name(freg));
    }
}

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);
    if (car(arg)==REGISTER||car(arg)==DREGISTER||
	car(arg)==FREGISTER||car(arg)==LREGISTER)
	use_input_reg(cadr(arg),1);
    g_expr_u(assign_expr0(arg,e4,t,t));
    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(scalar(t)) {
	nargs ++ ; reg_arg++;
    } else if (t==LONGLONG||t==ULONGLONG||t==DOUBLE) {
	if (*preg_arg%2==1) reg_arg++;  // alignment
        if (*pnargs%2==1) nargs++;  // alignment
	nargs ++ ; reg_arg++;
	nargs ++ ; reg_arg++;
    } else if (t==FLOAT) {
	reg_arg ++ ; 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(scalar(t)) {
	if (mode==AS_SAVE) {
	    return get_register_var(0);
	} else if (reg_arg+1>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 (reg_arg%2==1) reg_arg++;  // alignment
        if (nargs%2==1) nargs++;  // alignment
	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 (reg_arg%2==1) reg_arg++;  // alignment
        if (nargs%2==1) nargs++;  // alignment
	if (mode==AS_SAVE) {
	    return get_dregister_var(0,1);
	} else if (reg_arg+1>=MAX_INPUT_DREGISTER_VAR) {
	    return list2(LVAR,caller_arg_offset_v(nargs));
	} else
	    return get_input_dregister_var(reg_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);
    }
}

static void
code_call(int e2,NMTBL *fn,int jmp)
{
    char *jrn;
    if (fnptr->sc==CODE) {
	if (car(e2) == FNAME) {
	    printf("\tla\t$25,%s\n",fn->nm);
	} else {
	    jrn = register_name(cadr(jmp));
	    printf("\tmove\t$25,%s\n",jrn);
	}
	printf("\tjalr\t$25\n");
	printf("\tlw\t$gp,$L_%d($sp)\n",cprestore_label);
    } else {
	if (car(e2) == FNAME) {
	    printf("\tjal\t%s\n",fn->nm);
	} else {
	    jrn = register_name(cadr(jmp));
	    printf("\tmove $25,%s\n",jrn);
	    printf("\tjal\t$31,$25\n");
	}
    }
}

int
function(int e1)
{
    int e2,e3,e4,e5,nargs,t;
    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;
    int complex_;
    int pnargs,preg_arg,pfreg_arg;
    int stargs;

    special_lvar = -1;
    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;
	}
    }

    e2 = cadr(e1);
    if (car(e2) == FNAME) {	
	fn=(NMTBL *)cadr(e2);
    } else {	
	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 */
    }
    /* First we execute complex argument to avoid interaction with
       input variables. Remain the last complex argument in complex_. */
    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);
		}
	    }
	    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 calcurate it here.
    //  We cannot do this in the previous loop, because the copied struct may be
    //  override by other complex arguments. But bofore 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);
	    car(complex_) = 0; // done.
	}
    }

    nargs = reg_arg = freg_arg = arg_assign = 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));
	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 (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) {
	    reg_arg_list = list2(arg,reg_arg_list);
	    if (car(arg)==DREGISTER)
		use_input_reg(cadr(arg),1);
	    g_expr_u(assign_expr0(arg,e4,t,t));
	} else if (t==FLOAT) {
	    reg_arg_list = list2(arg,reg_arg_list);
            if (car(arg)==FREGISTER) {
                use_input_reg(cadr(arg),1);/* protect from input register free */
                g_expr_u(assign_expr0(arg,e4,t,t)); /* XXX */
            } else if (car(arg)==REGISTER) {
		use_input_reg(cadr(arg),1);/* protect from input register free */
                code_assign_input_float_int(list3(FASS_INPUT, arg, e4));
            } else {
                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();
    code_call(e2,fn,jmp);
    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) {
        set_dreg(RET_DREGISTER,0);
        use_reg(RET_DREGISTER);
    } else if (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_frame_pointer(int e3) {
    use_int(e3);
#if R1SAVE
    printf("\tmove $fp,%s\n",register_name(e3));
#else
    printf("\tmove $fp,%s\n",register_name(e3));
#endif
}


void
code_fix_frame_pointer(int offset) {
    printf("\tla $fp,");
    printf("%d+$L_%d($sp)\n",FUNC_LVAR(0),lvar_offset_label);
}

//  MIPS $25 (=$jp) contains calling function address.
//  It is used in cpload $25 to get global address table $gp.

void
code_jmp(char *s) {
    // jump to continuation means use all register variable
    max_reg_var = REG_VAR_BASE-REG_VAR_MIN;
    max_freg_var = FREG_VAR_BASE-FREG_VAR_MIN;
    printf("\tla $25,%s\n",s);
    printf("\tj\t$25\n");
    control=0;
}

void
code_indirect_jmp(int e2) {
    // jump to continuation means use all register variable
    max_reg_var = REG_VAR_BASE-REG_VAR_MIN;
    max_freg_var = FREG_VAR_BASE-FREG_VAR_MIN;
    use_int(e2);
    printf("\tmove $25,%s\n",register_name(e2));
    printf("\tj\t$25\n");
    control=0;
}

int
code_rindirect(int e1, int reg,int offset, int us)
{
    char *crn,*rrn;
    g_expr(e1);
    if (!is_int_reg(creg)) error(-1);
    crn=register_name(creg);
    use_int(reg);
    rrn=register_name(reg);
    printf("\tlw %s,%d(%s)\n",rrn,offset,crn);
    return us?UNSIGNED:INT;
}

int
code_crindirect(int e1, int reg,int offset, int sign)
{
    char *crn,*rrn;
    g_expr(e1);
    if (!is_int_reg(creg)) error(-1);
    crn=register_name(creg);
    use_int(reg);
    rrn=register_name(reg);
    printf("\t%s %s,%d(%s)\n",cload(1,sign),rrn,offset,crn);
    if (sign) {
	cext(sign,1,reg);
	return CHAR;
    }
    return UCHAR;
}

int
code_srindirect(int e1, int reg,int offset, int sign)
{
    char *crn,*rrn;
    g_expr(e1);
    if (!is_int_reg(creg)) error(-1);
    crn=register_name(creg);
    use_int(reg);
    rrn = register_name(reg);
    printf("\t%s %s,%d(%s)\n",cload(SIZE_OF_SHORT,sign),rrn,offset,crn);
    if (sign) {
	cext(sign,SIZE_OF_SHORT,reg);
	return SHORT;
    }
    return USHORT;
}

#if FLOAT_CODE
int
code_drindirect(int e1, int reg,int offset, int d)
{
     char *crn;
    if (d) {
        code_lrindirect(e1,reg,offset,1);
	use_float(d,reg);
	// regs[reg==USE_CREG?lreg:reg]=USING_DREG;
	return DOUBLE;
    }
    g_expr(e1);
    if (!is_int_reg(creg)) error(-1);
    crn=register_name(creg);
    use_float(d,reg);
    printf("\tl.s %s,%d(%s)\n",
	fregister_name(reg),offset,crn);
    return FLOAT;
}
#endif

#if LONGLONG_CODE

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


static void
lmove(int to,int from)
{
    int l;
    l = list3(regv_l(to),0,regv_l(from));
    l = list3(regv_h(to),l,regv_h(from));
    parallel_rassign(l);
}

static void
set_operands(int r0,int r1,int r2,int r3)
{
    int l;
    l = list3(DREGISTER_OPERAND_L,0,r0);
    l = list3(DREGISTER_OPERAND_H,l,r1);
    l = list3(DREGISTER_OPERAND_1_L,l,r2);
    l = list3(DREGISTER_OPERAND_1_H,l,r3);
    parallel_rassign(l);
}


static void
lstore(int e2,int creg)
{
    char *drn = register_name(e2);
    char *crn_h;
    char *crn_l;
    crn_h = lregister_name_high(creg);
    crn_l = lregister_name_low(creg);
#if ENDIAN==0
    printf("\tsw %s,0(%s)\n",crn_l,drn);
    printf("\tsw %s,%d(%s)\n",crn_h,SIZE_OF_INT,drn);
#else
    printf("\tsw %s,0(%s)\n",crn_h,drn);
    printf("\tsw %s,%d(%s)\n",crn_l,SIZE_OF_INT,drn);
#endif
}

int
code_lrindirect(int e1, int reg, int offset, int us)
{
    int creg0;

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


void
code_assign_gvar(int e2,int creg,int byte) {
    int r;
    use_int(creg);
    r = get_ptr_cache((NMTBL*)cadr(e2));
    code_assign(r,byte,creg);
}

void
code_assign_lvar(int e2,int creg,int byte) {
    char *crn;
    use_int(creg);
    crn=register_name(creg);
    lvar_intro(e2);
    if (byte==1) {
	printf("\tsb %s,",crn);
    } else if (byte==SIZE_OF_SHORT) {
	printf("\tsh %s,",crn);
    } else {
	printf("\tsw %s,",crn);
    }
    lvar(e2);
}

void
code_assign_register(int e2,int byte,int creg) {
    use_int(creg);
    if (e2!=creg)
	printf("\tmove %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);

    if (byte==1) {
	printf("\tsb %s,0(%s)\n",crn,drn);
    } else if (byte==SIZE_OF_SHORT) {
	printf("\tsh %s,0(%s)\n",crn,drn);
    } else {
	printf("\tsw %s,0(%s)\n",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 */
    printf("# assop\n\tmove %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("\tsb %s,0(%s)\n",crn,drn);
    } else if (byte==SIZE_OF_SHORT) {
	printf("\tsh %s,0(%s)\n",crn,drn);
    } else {
	printf("\tsw %s,0(%s)\n",crn,drn);
    }
    free_register(edx);
    emit_pop_free(xreg);
}


void
tosop(int op,int creg,int oreg)
{
    int dx = -1;
    int ox = -1;
    char *orn,*crn;
    // 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("sll",creg,oreg);
	if(ox!=-1) free_register(ox);
	return;
    case RSHIFT:
	shift("sra",creg,oreg);
	if(ox!=-1) free_register(ox);
	return;
    case URSHIFT:
	shift("srl",creg,oreg);
	if(ox!=-1) free_register(ox);
	return;
    }
    orn = register_name(oreg);
    crn = register_name(creg);
    switch(op) {
    case ADD:
	printf("\taddu %s,%s,%s\n",crn,crn,orn);
	break;
    case SUB:
	printf("\tsubu %s,%s,%s\n",crn,crn,orn);
	break;
    case CMPGE:
        printf("\tslt %s,%s,%s\n",crn,crn,orn);
	cmpreg = creg;
	break;
    case CMP:
        printf("\tslt %s,%s,%s\n",crn,orn,crn);
	cmpreg = creg;
	break;
    case UCMPGE:
        printf("\tsltu %s,%s,%s\n",crn,crn,orn);
	cmpreg = creg;
	break;
    case UCMP:
        printf("\tsltu %s,%s,%s\n",crn,orn,crn);
	cmpreg = creg;
	break;
    case CMPEQ:
        printf("\tbeq %s,%s",crn,orn);  // beq  $2,$3,L1
	break;
    case CMPNEQ:
	printf("\tbne %s,%s",crn,orn);  // beq  $2,$3,L1
	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("\tmult %s,%s,%s\n",crn,crn,orn);
	break;
    case UMUL:
	printf("\tmultu %s,%s,%s\n",crn,crn,orn);
	break;
    case DIV: case UDIV: case MOD: case UMOD:
        printf("\t%s $0,%s,%s\n",(op==UDIV||op==UMOD)?"divu":"div",crn,orn);
        printf("\t%s %s\n",(op==MOD||op==UMOD)?"mfhi":"mflo",crn);
        printf("\t.set    noreorder\n");
        printf("\tbeql    %s,$0,1f\n",orn);
        printf("\tbreak   7\n");
        printf("1:\n");
        printf("\t.set    reorder\n");
        break;
    default:
	error(-1);
    }
    if(dx!=-1) free_register(dx);
    if(ox!=-1) free_register(ox);
}

static int
ilog(int i)
{
    int j,k;
    for(k=1,j=0;j<8;j++,k+=k) {
	if (i==k)
	    return j;
    }
    return 0;
}

int 
code_const_op_p(int op,int v)
{
    if (car(v)!=CONST) return 0;
    v = cadr(v);
    if (op==MUL||op==UMUL) return ilog(v) ;
    if (op==DIV||op==UDIV) return ilog(v) ;
    if (!(op==LSHIFT|| op==ULSHIFT|| op==RSHIFT|| op==URSHIFT|| op==ADD|| op==SUB|| op==CMP|| op==BOR)) return 0;
    return (-32766<v&&v<32767);
}

void
oprtc(int op,int creg, int v)
{
    char *crn;
    use_int(creg);
    crn = register_name(creg);
    v = cadr(v);
    
    switch(op) {
    case LSHIFT:
    case ULSHIFT:
	printf("\tsll %s,%s,%d\n",crn,crn,v);
	return;
    case RSHIFT:
	printf("\tsra %s,%s,%d\n",crn,crn,v);
	return;
    case URSHIFT:
	printf("\tsrl %s,%s,%d\n",crn,crn,v);
	return;
    case ADD:
	printf("\taddu %s,%s,%d\n",crn,crn,v);
	break;
    case SUB:
	printf("\taddu %s,%s,-%d\n",crn,crn,v);
	break;
    case CMP:
        printf("\tslt %s,%s,%d\n",crn,crn,v);
        cmpreg = creg;
	break;
    case BOR:
	printf("\tori %s,%s,%d\n",crn,crn,v);
	break;
    case MUL: case UMUL:
	printf("\tsll %s,%s,%d\n",crn,crn,ilog(v));
	break;
    case UDIV:
	printf("\tsrl %s,%s,%d\n",crn,crn,ilog(v));
	break;
    case DIV:
	printf("\tsra %s,%s,%d\n",crn,crn,ilog(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,sign),register_name(creg),n,
	    register_name(xreg));
}

int
code_csvalue()
{
    return creg;
}

#define CMP_IMM (-2)
static char * cmpreg_1;
static char * cmpreg_2;

void
code_cmpdimm(int e, int csreg)
{
    /* used in dosiwtch() */
    int reg;
    char *rn;
    if(chk) return;
    use_reg(csreg);
    rn = register_name(reg = get_register());
    printf("\tli %s,%d\n",rn,e);
    cmpreg_1 = rn;
    cmpreg_2 = register_name(csreg);
    cmpreg=CMP_IMM;
    free_register(reg);
}

void
code_opening(char *filename)
{
    static int count=0;
    /* this is called once per file */
    char *p=cheapp;
    char *s,*t;

    printf("\t.file %d \"%s\"\n",count++,filename);
    printf(".abicalls\n");
    printf(".text\n");

    if (asi) {
        fclose(asi);
        asi = 0;
    }
    for (t=0,s=filename;(*cheapp++ = *s++);) {
        if (*s=='.') {
	    t=cheapp;
        }
    }
    if (!t) {
	cheapp--;
	*cheapp++ = '.';
	*cheapp++ = 'i';
	*cheapp++ = 0;
    } else {
	t[1]='i'; 
	t[2]=0; 
    }
    asi = fopen(p,"w");
    cheapp = p;
    printf(".include \"%s\"\n",p);
    if (!asi) error(-1);
}

static void
pcond(int op, int r2,int r1,int r0,int cond,int l1)
{
    char *slt  = "slt";
    char *sltu = "sltu";
    char *eq = "eq";
    char *ne = "ne";
    int t;
// printf("# pcond %d cond %d\n",op,cond);
    switch(op+(!cond)*BNOT) {
    case GT:  case LE+BNOT:
		    t=r1;r1=r0;r0=t;
    case LT:  case GE+BNOT:
		    eq = ne;
		    break;
    case UGT: case ULE+BNOT:
		    t=r1;r1=r0;r0=t;
    case ULT: case UGE+BNOT:
		    eq = ne; slt = sltu;
		    break;
    case ULE: case UGT+BNOT:
		    t=r1;r1=r0;r0=t;
    case UGE: case ULT+BNOT:
		    slt = sltu;
		    break;
    case LE:  case GT+BNOT:
		    t=r1;r1=r0;r0=t;
    case GE:  case LT+BNOT:
    case EQ:  case NEQ+BNOT:
		    break;
    case NEQ: case EQ+BNOT:   
		    eq = ne;
		    break;
    default:        error(-1);
    }

    if (op==EQ||op==NEQ) {
	printf("\tb%s\t%s,%s,$L_%d\n",eq,
	    register_name(r0),
	    register_name(r1),
	    l1);
    } else {
	printf("\t%s\t%s,%s,%s\n",slt,
	    register_name(r2),
	    register_name(r1),
	    register_name(r0));
	printf("\tb%s %s,$0,$L_%d\n",eq,register_name(r2),l1);
    }
}

void
rexpr(int e1, int l1, int cond,int t)
{       
    int e2;
    int op = car(e1);

    g_expr(cadr(e1));
    emit_push();
    g_expr(caddr(e1));
    e2 = emit_pop(1);
    pcond(op, e2,e2,ireg,cond,l1);
    emit_pop_free(e2);
}

#define CMP_C1T (-1)

void
jcond(int l, char cond)
{       
    if (chk) return;
    if (cmpreg==CMP_C1T)  {
      printf("\tbc1%s $L_%d\n",cond?"f":"t",l);
    } else if (cmpreg==CMP_IMM)  {
      printf("\tb%s %s,%s,$L_%d\n",cond?"ne":"eq",cmpreg_1,cmpreg_2,l);
    } else
      printf("\tb%s %s,$0,$L_%d\n",cond?"ne":"eq",register_name(cmpreg),l);
}

void
jmp(int l)
{       
    control=0;
    if (chk) return;
    printf("\tj\t$L_%d\n",l);
}

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

static int
code_mask_offset()
{
    /* used regsister var */
    int i;
    int offset=0;
    int min = reg_var_num(max_reg_var);
    int max = reg_var_num(0);
    for(i=0;i<32;i++) {
	if (i==28||i==31||(max>i&&i>=min)) {
	    offset++;
	}
    }
    if (offset>2) offset-=1;
    return -offset*SIZE_OF_INT;
}

static unsigned int
code_mask()
{
    /* used regsister var */
    int i;
    unsigned int mask=0;
    int min = reg_var_num(max_reg_var);
    int max = reg_var_num(0);
    for(i=0;i<32;i++) {
	if (i==28||i==31||(max>i&&i>=min)) {
	    mask |= (1<<i);
	}
    }
    return mask;
}

static int
code_register_save(int reg_save,int freg_save,int disp)
{
    int i;
    for (i=reg_var_num(0);i>reg_var_num(reg_save);i--) {
	printf("\tsw    %s,$L_%d-%d($sp)\n",register_name(i),
	    r1_offset_label,-disp);
	disp -= SIZE_OF_INT;
    }
    for (i=freg_var_num(0);i>freg_var_num(freg_save);i--) {
	printf("\ts.s    %s,$L_%d-%d($sp)\n",register_name(i),
	    r1_offset_label,-disp);
	disp -= SIZE_OF_FLOAT;
    }
    return disp;
}

static int
code_register_restore(int reg_save,int freg_save,int disp)
{
    int i;
    for (i=reg_var_num(0);i>reg_var_num(reg_save);i--) {
	printf("\tlw    %s,$L_%d-%d($sp)\n",register_name(i),
	    r1_offset_label,-disp);
	disp -= SIZE_OF_INT;
    }
    for (i=freg_var_num(0);i>freg_var_num(freg_save);i--) {
	printf("\tl.s    %s,$L_%d-%d($sp)\n",register_name(i),
	    r1_offset_label,-disp);
	disp -= SIZE_OF_FLOAT;
    }
    return disp;
}

static int
code_fmask_offset()
{
    int i;
    int offset=0;
    int min = freg_var_num(max_reg_var);
    int max = freg_var_num(0);
    for(i=0;i<32;i++) {
	if (i==28||i==31||(max>i&&i>=min)) {
	    offset++;
	}
    }
    if (offset>2) offset-=1;
    return -offset*SIZE_OF_FLOAT;
}

static unsigned int
code_fmask()
{
    int i;
    unsigned int mask=0;
    int min = freg_var_num(max_reg_var);
    int max = freg_var_num(0);
    for(i=0;i<32;i++) {
	if (i==28||i==31||(max>i&&i>=min)) {
	    mask |= (1<<i);
	}
    }
    return mask;
}

void
code_enter(char *name)
{
    if (output_mode!=TEXT_EMIT_MODE) 
	text_mode(3);
    else
	printf("\t.align 3\n");
    if (stmode!=STATIC)
    printf("\t.globl\t%s\n",name);
#ifdef DOT_SIZE
    printf("\t.type\t%s,@function\n",name);
#endif
    printf(".ent %s\n",name);
    printf("%s:\n",name);
    printf("\t.frame $fp,$L_%d,$31\n",r1_offset_label=fwdlabel());
    printf("\t.mask  $L_%d,$L_%d\n",mask_label=fwdlabel(),
	mask_offset_label=fwdlabel());
    printf("\t.fmask  $L_%d,$L_%d\n",fmask_label=fwdlabel(),
	fmask_offset_label=fwdlabel());
    printf("\t.set noreorder\n");
    printf("\t.cpload $25\n");
    printf("\t.set reorder\n");
    printf("\tsubu $sp,$fp,$L_%d\n",r1_offset_label);
    printf("\t.cprestore $L_%d\n",cprestore_label=fwdlabel());
    // printf("\tmove  $fp,$sp\n");
    lvar_offset_label = fwdlabel();
    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)
{
    code_offset_set(fnptr);
    local_table();
    printf("\t.end    %s\n",name);
}

void
enter(char *name)
{
    if (output_mode!=TEXT_EMIT_MODE) 
	text_mode(3);
    else
	printf("\t.align 3\n");
    
    max_func_args = 0;

    lvar_offset_label = fwdlabel();

    if (stmode!=STATIC)
	printf("\t.globl\t%s\n",name);
    printf(".ent %s\n",name);
    printf("%s:\n",name);
    printf("\t.frame $fp,$L_%d,$31\n",r1_offset_label=fwdlabel());
    printf("\t.mask  $L_%d,$L_%d\n",mask_label=fwdlabel(),
	mask_offset_label=fwdlabel());
    printf("\t.fmask  $L_%d,$L_%d\n",fmask_label=fwdlabel(),
	fmask_offset_label=fwdlabel());
    printf("\t.set noreorder\n");
    printf("\t.cpload $25\n");
    printf("\t.set reorder\n");
    printf("\tsubu $sp,$sp,$L_%d\n",r1_offset_label);
    printf("\t.cprestore $L_%d\n",cprestore_label=fwdlabel());
    printf("\tsw      $31,$L_%d-%d($sp)\n",r1_offset_label,arg_offset);
    printf("\tsw      $fp,$L_%d-%d($sp)\n",r1_offset_label,arg_offset+SIZE_OF_INT);
    printf("\tj $L_%d\n",register_save_label=fwdlabel());
    register_save_return_label = backdef();
    printf("\tmove  $fp,$sp\n");
}

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


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

    if (control) {
	code_set_return_register(1);
    } else
	text_mode(2);
    if (retcont) { 
	/* return from CbC segement */
	if (control) jmp(retlabel);
	retcont1 = fwdlabel();
	fwddef(retcont);
	if (cadr(fnptr->ty)==FLOAT) {
	    creg = freg = cadr(get_input_dregister_var(0,0,1,0));
	    set_freg(RET_FREGISTER,1);
	} else if (cadr(fnptr->ty)==DOUBLE) {
	    creg = lreg = cadr(get_input_dregister_var(0,0,1,1));
	    set_dreg(RET_DREGISTER,1);
	} else if (cadr(fnptr->ty)>0&&(
	    car(cadr(fnptr->ty))==STRUCT ||
	    car(cadr(fnptr->ty))==UNION)) {
	    sz = size(cadr(fnptr->ty));
	    printf("\tli $4,%d\n",sz);
	    printf("\tsubl $5,$4,$fp\n");
	    printf("\tlw $3,(%d)($fp)\n",(my_func_args-1)*SIZE_OF_INT);
	    emit_copy(6,3,sz,0,1,1);
	} else if (cadr(fnptr->ty)!=VOID) {
	    creg = ireg = cadr(get_input_register_var(0,0,1));
	    if (creg!=RET_REGISTER)
		set_ireg(RET_REGISTER,1);
	}
#if R1SAVE
#else
	printf("\tsubu $fp,");
	printf("$fp,%d+$L_%d\n",FUNC_LVAR(0),lvar_offset_label);
	// printf("\tj $L_%d\n",retcont1);
#endif
    }
    fwddef(retlabel);
    // if (retcont) {
// 	fwddef(retcont1);
    // }

    r1_offsetv = code_offset_set(fnptr);

    printf("\tmove    $sp,$fp\n");
    printf("\tlw      $31,$L_%d-%d($sp)\n",r1_offset_label,arg_offset);
    printf("\tlw      $fp,$L_%d-%d($sp)\n",r1_offset_label,arg_offset+SIZE_OF_INT);
    if (max_reg_var+max_freg_var)
	code_register_restore(max_reg_var,max_freg_var,-arg_offset-SIZE_OF_INT*2);
    printf("\taddu    $sp,$sp,%d\n",r1_offsetv);
    printf("\tj       $31\n");

//  leave part end

//  entry part  (save register)

    if (max_reg_var+max_freg_var==0) {
	fprintf(asi,"$L_%d=$L_%d\n",
		register_save_label,register_save_return_label);
    } else {
	code_label(register_save_label);
	code_register_save(max_reg_var,max_freg_var,-arg_offset-SIZE_OF_INT*2);
	jmp(register_save_return_label);
    }

    local_table();
    printf("\t.end    %s\n",name);

    labelno++;
    free_all_register();
}


void
code_set_return_register(int mode) {
    if (cadr(fnptr->ty)==FLOAT) {
	set_freg(RET_FREGISTER,mode);
    } else if (cadr(fnptr->ty)==DOUBLE) {
	set_dreg(RET_DREGISTER,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) {
	set_freg(reg,mode);
    } else if (type==DOUBLE) {
	set_dreg(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); 
     */
}

void

align(int t)
{
    if (t!=CHAR) {
	if (data_alignment & 1)
	    printf("\t.align 2\n");
	data_alignment = 0;
    }
}

void
emit_data(int e, int t, NMTBL *n)
{
    int l;
#if FLOAT_CODE
    double d;
    float f;
#endif
#if LONGLONG_CODE
    long long ll;
#endif
    char *name;
    name = n->nm; 
    if(mode!=GDECL && mode!=STADECL)  { 
	error(-1); return;
    }
    if (chk) return;
    if (n->dsp != -1) {
	n->dsp = -1;   /* initiallized flag */
	if (n->sc!=STATIC)
	    printf(".globl\t%s\n",name);
	data_mode(name);
	align(t);
	printf("%s:\n",name); 
    } else {
	data_mode(0);
    }
    if(car(e)==CONST) {       
	if (t==CHAR||t==UCHAR) {
	    printf("\t.byte %d\n",cadr(e));
	    if (data_alignment>0)
		data_alignment++;
	    gpc += 1;
	} else if (t==SHORT||t==USHORT) {
	    printf("\t.short %d\n",cadr(e));
	    if (data_alignment>0) data_alignment++;
	    gpc += SIZE_OF_SHORT;
	} else {
	    printf("\t.long %d\n",cadr(e));
	    gpc += SIZE_OF_INT;
	}
#if LONGLONG_CODE
    } else if(t==LONGLONG||t==ULONGLONG) {       
	ll = lcadr(e);
#if (ENDIAN==0)
	printf("\t.long\t0x%x,0x%x\n",code_l1(ll),code_l2(ll));
#else
	printf("\t.long\t0x%x,0x%x\n",code_l2(ll),code_l1(ll));
#endif
#endif
#if FLOAT_CODE
    } else if(t==DOUBLE) {       
	d = dcadr(e);
#if (ENDIAN==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
    } else if(t==FLOAT) {       
	f = dcadr(e);
	printf("\t.long\t0x%x\n",*(int *)&f);
#endif
    } else if(t!=CHAR) {       
	gpc += SIZE_OF_INT;
	if(car(e)==ADDRESS&&car(cadr(e))==GVAR) {
	    printf("\t.long %s\n",((NMTBL *)cadr(cadr(e)))->nm);
	} else if(car(e)==FNAME) {
	    printf("\t.long %s\n",((NMTBL *)cadr(e))->nm);
	} else if(car(e)==GVAR) {
	    printf("\t.long %s\n",((NMTBL *)cadr(e))->nm);
	} else if(car(e)==STRING) {       
	    if (car(n->ty)!=ARRAY || cadr(n->ty)!=CHAR) {
		l = fwdlabel();
		printf("\t.long $L_%d\n",l);
		// should put on diffirent segement
		printf("\t.rdata\n\t.align 2\n");
		printf("$L_%d:\n",l);
		output_mode = RODATA_EMIT_MODE;
	    }
	    ascii((char *)cadr(e));
	} else error(TYERR);
    } else error(TYERR);
}

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

static void
comm(NMTBL *n)
{
    printf(".comm %s,%d,%d\n",n->nm,size(n->ty),
	(n->ty==DOUBLE||n->ty==LONGLONG||n->ty==ULONGLONG)?8:4
    );
}

void
global_table(void)
{
    NMTBL *n;
    int init;
    init=0;
    for(n=ntable;n < &ntable[GSYMS];n++) {
	if ((n->sc == GVAR) && n->dsp != -1) {
	    /* n->dsp = -1 means initialized global */
	    if (init==0) {
		data_mode(0);
		init=1;
	    }
	    comm(n);
	} else if ((n->sc==STATIC) && n->dsp != -1) {
	    /* n->dsp = -1 means initialized global */
	    if (init==0) {
		data_mode(0);
		init=1;
	    }
	    printf(".local %s\n",n->nm);
	    comm(n);
	}
    }
}

void
local_table(void)
{
    NMTBL *n;
    int init;
    init=0;
    /* static local variables */
    for(n=ntable+GSYMS;n < &ntable[GSYMS+LSYMS];n++) {
	if (n->sc == GVAR) {
	    if (n->dsp != -1) { /* initialized static */
		if (init==0) {
		    data_mode(0);
		    init=1;
		}
		comm(n);
	    }
	}
    }
    text_mode(2);
}

void
text_mode(int align)
{
    if (output_mode!=TEXT_EMIT_MODE) {
	printf(".text\n");
	if (align) printf("\t.align %d\n",align);
	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);
}

#define lib_args(max) if (max_func_args<max) max_func_args=max

static void
extern_conv(char *conv)
{
     code_save_stacks();
     clear_ptr_cache();
     extern_define(conv,0,FUNCTION,1);
     printf("\tjal %s\n",conv);
     lib_args(16);
}

#if FLOAT_CODE

/* floating point */

#define  set_double(freg) if (regs[freg]) {regs[freg]=USING_DREG;}

static void dconst(int l,int h,double value);

void
code_cmp_dregister(int e2,int d)
{
    char *grn,*frn;
    int greg;
    use_float(d,e2);

    if (d) {
        code_save_stacks();
	clear_ptr_cache();
	set_dreg(DREGISTER_OPERAND,0);
	dconst(DREGISTER_OPERAND_1_L,DREGISTER_OPERAND_1_H,0.0);
        extern_conv("dpcmp");
	set_dreg(RET_DREGISTER,0);
	cmpreg = 2;
	return;
    }
    grn =  register_name(greg = get_dregister(d));
    frn = register_name(e2);
    printf("\tmtc1 $0,%s\n",grn);
    printf("\tc.eq.s %s,%s\n",grn,frn);
    free_register(greg);
    cmpreg = CMP_C1T;
    return;
}

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

void
code_dassign_gvar(int e2,int freg,int d)
{ 
    int r;
    use_float(d,freg);
    if (d) {
         code_lassign_gvar(e2,freg);  set_double(freg); return;
    }
    r = get_ptr_cache((NMTBL*)cadr(e2));
    printf("\ts.s %s,0(%s)\n",fregister_name(freg),register_name(r));
}

void
code_dassign_lvar(int e2,int freg,int d)
{ 
    use_float(d,freg);
    if (d) {
        code_lassign_lvar(e2,freg); set_double(freg); return;
    }
    lvar_intro(e2);
    printf("\ts.s %s,",fregister_name(freg));
    lvar(e2);
}

void
code_dassign(int e2,int freg,int d)
{ 
    use_float(d,freg);
    if (d) {
        code_lassign(e2,freg); set_double(freg); return;
    }
    printf("\ts.s %s,0(%s)\n",fregister_name(freg),register_name(e2));
}

void
code_dassign_dregister(int e2,int d,int freg) {
    use_float(d,freg);
    if (d) {
        code_lassign_lregister(e2,freg); set_double(freg); return;
    }
    if (e2!=freg) {
	printf("\tmov.s %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;
}

static void
dconst(int l,int h,double value)
{
#if ENDIAN==0
	printf("\tli %s,0x%x\n",register_name(l),code_d1(value));
	printf("\tli %s,0x%x\n",register_name(h),code_d2(value));
#else
	printf("\tli %s,0x%x\n",register_name(h),code_d1(value));
	printf("\tli %s,0x%x\n",register_name(l),code_d2(value));
#endif
}

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

    use_float(d,freg);
    if (d) {
	dconst(regv_l(freg),regv_h(freg),value);
    } else {
        frn = fregister_name(freg);
        printf("\tli.s %s,%10.10g\n",frn,value);
    }
}


void
code_dneg(int freg,int d)
{ 
    char *frn;
    use_float(d,freg);
    if (d) {
        code_save_stacks();
	clear_ptr_cache();
	set_dreg(DREGISTER_OPERAND_1,1);
        printf("\tmove $4,$0\n"); 
        printf("\tmove $5,$0\n"); 
	/// set_dreg_operand(oreg,1);
        extern_conv("dpsub");
	set_dreg(RET_DREGISTER,0);
	return;
    }
    frn = fregister_name(freg);
    printf("\tneg.s %s,%s\n",frn,frn);
}

void
code_d2i(int reg)
{ 
    use_float(1,reg);
    code_save_stacks();
    clear_ptr_cache();
    set_dreg(DREGISTER_OPERAND,1);
    extern_conv("dptoli");
    set_ireg(RET_REGISTER,0);
    return;
}

void
code_i2d(int reg)
{ 
    set_ireg(REGISTER_OPERAND,1);
    code_save_stacks();
    clear_ptr_cache();
    extern_conv("litodp");
    set_dreg(RET_DREGISTER,0);
    use_float(1,reg);
    return;
}

void
code_d2u(int reg)
{ 
    use_float(1,reg);
    code_save_stacks();
    clear_ptr_cache();
    set_dreg(DREGISTER_OPERAND,1);
    extern_conv("dptoul");
    set_ireg(RET_REGISTER,0);
    return;
}

void
code_u2d(int reg)
{ 
    int tmp=new_lvar(SIZE_OF_INT);
    set_ireg(REGISTER_OPERAND,1);
    code_assign_lvar(tmp,REGISTER_OPERAND,0);
    code_save_stacks();
    clear_ptr_cache();
    extern_conv("litodp");
    code_rlvar(tmp,REGISTER_OPERAND);
    printf("\tbgez\t%s,1f\n",register_name(REGISTER_OPERAND));
    code_double_lib_c("dpadd",RET_DREGISTER,RET_DREGISTER,4.29496729600000000000e9);
    printf("1:\n");
    set_dreg(RET_DREGISTER,0);
    if (reg!=USE_CREG) {
	use_float(1,reg);
	if (reg!=RET_DREGISTER) {
	    lmove(reg,RET_DREGISTER);
	}
    }
    free_lvar(tmp);
    return;
}

void
code_d2f(int reg) { 
    set_dreg(DREGISTER_OPERAND,1);
    code_save_stacks();
    clear_ptr_cache();
    extern_conv("dptofp");
    set_freg(RET_FREGISTER,0);
    use_float(0,reg);
    return;
}

void
code_f2d(int reg) { 
    set_freg(FREGISTER_OPERAND,1);
    code_save_stacks();
    clear_ptr_cache();
    extern_conv("fptodp");
    set_dreg(RET_DREGISTER,0);
    use_float(1,reg);
    return;
}

void
code_f2i(int reg) {
#if 0
    int tmp=new_lvar(SIZE_OF_INT);
    use_int(reg);
    printf("\ttrunc.w.s %s,%s,%s\n",register_name(freg),
	register_name(freg),register_name(ireg));
    code_dassign_lvar(tmp,freg,1);
    code_rlvar(tmp,reg);
    free_lvar(tmp);
#else
    use_int(reg);
    printf("\ttrunc.w.s %s,%s,%s\n",fregister_name(freg),
	fregister_name(freg),register_name(ireg));
    printf("\tmfc1    %s,%s\n",register_name(reg),fregister_name(freg));
#endif
}

void
code_f2u(int reg) {
    int freg0 = freg;
    int freg1 = get_dregister(0);
    int freg2 = get_dregister(0);
    int ireg1 = get_register();
    char *fr0 = fregister_name(freg0);
    char *fr1 = fregister_name(freg1);
    char *fr2 = fregister_name(freg2);
    char *r1 = register_name(ireg1);
    char *r0;
    int lb1,lb2;

    use_int(reg);
    r0 = register_name(ireg);
    
    printf("\tli.s    %s,2.14748364800000000000e9\n",fr1);
    printf("\tc.le.s  %s,%s\n",fr1,fr0);
    printf("\tbc1t    $L_%d\n",lb1=fwdlabel());
    printf("\ttrunc.w.s %s,%s,%s\n",fr2,fr0,r0);
    printf("\tmfc1    %s,%s\n",r0,fr2);
    printf("\tj       $L_%d\n",lb2=fwdlabel());
    printf("\t.p2align 2\n");
    fwddef(lb1);
    printf("\tsub.s   %s,%s,%s\n",fr0,fr0,fr1);
    printf("\tli      $3,-2147483648                  # 0x80000000\n");
    printf("\ttrunc.w.s %s,%s,%s\n",fr1,fr0,r1);
    printf("\tmfc1    %s,%s\n",r1,fr1);
    printf("\tor      %s,%s,%s\n",r1,r1,r0);
    fwddef(lb2);
    free_register(freg1);
    free_register(freg2);
    free_register(ireg1);
}

void
code_i2f(int reg) {
     int n = new_lvar(SIZE_OF_FLOAT);
     use_int(reg);
     code_assign_lvar(n,reg,0);
     reg = USE_CREG;
     use_float(0,reg);
     code_drlvar(n,0,reg);
     printf("\tcvt.s.w %s,%s\n",register_name(freg),register_name(freg));
     free_lvar(n);
}

void
code_u2f(int reg) {
     int n = new_lvar(SIZE_OF_FLOAT);
     int reg0,reg1;
     int lb1,lb2;
     char *frn,*r0,*r1;
     code_assign_lvar(n,ireg,0);
     printf("\tbltz    %s,$L_%d\n",r0=register_name(ireg),lb1=fwdlabel());
     use_float(0,reg);
     code_drlvar(n,0,reg);
     r0= register_name(reg0 = get_register());
     r1= register_name(reg1 = get_register());
     frn = fregister_name(reg);
     printf("\tcvt.s.w %s,%s\n",frn,frn);
     printf("\tj       $L_%d\n",lb2=fwdlabel());
    printf("\t.p2align 2\n");
    fwddef(lb1);
    printf("\tandi    %s,%s,0x1\n",r1,r0);
    printf("\tsrl     %s,%s,1\n",r0,r0);
    printf("\tor      %s,%s,%s\n",r1,r1,r0);
    printf("\tmtc1    %s,%s\n",r1,frn);
    printf("\tcvt.s.w %s,%s\n",frn,frn);
    printf("\tadd.s   %s,%s,%s\n",frn,frn,frn);
    fwddef(lb2);
    free_register(reg0);
    free_register(reg1);
}

void
code_drgvar(int e2,int d,int freg)
{ 
    int r;
    if (d) {
         code_lrgvar(e2,freg);
         set_double(freg);
	 return;
    }
    r = get_ptr_cache((NMTBL*)cadr(e2));
    use_float(d,freg);
    printf("\tl.s %s,0(%s)\n",fregister_name(freg),register_name(r));
}


void
code_drlvar(int e2,int d,int freg)
{ 
    if (d) {
         code_lrlvar(e2,freg);
         set_double(freg);
	 return;
    }
    use_float(d,freg);
    lvar_intro(e2);
    printf("\tl.s %s,",fregister_name(freg)); lvar(e2);
}

void
code_cmp_drgvar(int e2,int reg,int d)
{ 
    int r;
    char *frn,*fr1;
    int g;
    use_float(d,reg);

    r = get_ptr_cache((NMTBL*)cadr(e2));

    if (d) {
	code_save_stacks();
	set_dreg(RET_DREGISTER,0);
        code_drgvar(e2,d,RET_DREGISTER);
	clear_ptr_cache();
	printf("\tmove  $6,$0\n");
	printf("\tmove  $7,$0\n");
	extern_conv("dcmp");
	cmpreg = 2;
    } else {
        code_drgvar(e2,d,USE_CREG);
	frn = fregister_name(freg);
	fr1=fregister_name(g = get_dregister(0));
	printf("\tmtc1   $0,%s\n",fr1);
        printf("\tfc.eq.s %s,%s\n",frn,fr1);
	cmpreg = CMP_C1T;
	free_register(g);
    }
}

void
code_cmp_drlvar(int e2,int reg,int d)
{ 
    char *frn,*fr1;
    int g;
    use_float(d,reg);

    if (d) {
	code_save_stacks();
	set_dreg(RET_DREGISTER,0);
        code_drlvar(e2,d,RET_DREGISTER);
	clear_ptr_cache();
	printf("\tmove  $6,$0\n");
	printf("\tmove  $7,$0\n");
	extern_conv("dcmp");
	cmpreg = 2;
    } else {
        code_drlvar(e2,d,USE_CREG);
	frn = fregister_name(freg);
	fr1=fregister_name(g = get_dregister(0));
	printf("\tmtc1   $0,%s\n",fr1);
        printf("\tfc.eq.s %s,%s\n",frn,fr1);
	cmpreg = CMP_C1T;
	free_register(g);
    }
}

static void
code_double_lib(char *lib,int to,int reg,int oreg)
{
    code_save_stacks();
    clear_ptr_cache();
    set_operands(regv_l(reg),regv_h(reg),regv_l(oreg),regv_h(oreg));
    extern_conv(lib);
    set_dreg(RET_DREGISTER,0);
    if (to!=RET_DREGISTER) {
	lmove(to,RET_DREGISTER);
    }
}

static void
code_double_lib_c(char *lib,int from,int to,double value)
{
    code_save_stacks();
    clear_ptr_cache();
    set_dreg_operand(from,1);
    dconst(DREGISTER_OPERAND_1_L,DREGISTER_OPERAND_1_H,value);
    extern_conv(lib);
    set_dreg(RET_DREGISTER,0);
    if (to!=RET_DREGISTER) {
	lmove(to,RET_DREGISTER);
    }
}


void
dtosop(int op,int reg,int e1)
{ 
    char *opn="";
    char *opc="";
    char *grn,*frn;
    int d;
    int cmp=0;
    int reg0=reg;

    d=(op<FOP);
    use_float(d,reg);
    if (d) {
	switch(op) {
	case DADD: opc="dpadd"; break;
	case DSUB: opc="dpsub"; break;
	case DDIV: opc="dpdiv"; break;
	case DMUL: opc="dpmul"; break;
	case DCMPGE:
	case DCMP:  opc="dpcmp"; break;
	default:
	    error(-1); return;
	}
        code_double_lib(opc,reg0==USE_CREG?RET_DREGISTER:reg,reg,e1);
    } else {
	switch(op) {
	case FADD: opn="add.s"; break;
	case FSUB: opn="sub.s"; break;
	case FDIV: opn="div.s"; break;
	case FMUL: opn="mul.s"; break;
	case FCMP: opn="c.lt.s"; cmp=1; break;
	case FCMPGE: opn="c.le.s"; cmp=1; break;
	case FCMPEQ: opn="c.eq.s"; cmp=1; break;
	default:
	    error(-1); return;
	}
        grn = fregister_name(e1);
        frn = fregister_name(reg);
        if (cmp) {
	  cmpreg=CMP_C1T;
	  printf("\t%s %s,%s\n",opn,frn,grn);
        } else {
	  printf("\t%s %s,%s,%s\n",opn,frn,frn,grn);
        }
    }
}

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;
    char *crn;
    char *frn;
    int edx,edx0=-1;
    int reg0=reg;

    if (!d) {
      xreg=emit_dpop(d);
      crn=register_name(ireg);
      use_float(d,reg);
      frn  =fregister_name(reg);

      printf("\tl.s %s,0(%s)\n",frn,crn);
      dtosop(op,reg,xreg);
      printf("\ts.s %s,0(%s)\n",frn,crn);
      emit_dpop_free(xreg,d);
    } else {
      xreg = emit_lpop(0);       /* pop e3 value */
      if (!is_int_reg(creg)) error(-1);
      edx = ireg;
      emit_push();
      use_float(d,reg);
      if (regv_l(lreg)==edx || regv_h(lreg)==edx) {
	edx0 = get_register(); if(!edx0) error(-1);
	printf("# dassop\n\tmove %s,%s\n",register_name(edx0),register_name(edx));
	edx = edx0;
      }
      lload(edx,reg,0);
      dtosop(op,USE_CREG,xreg);
      if (lreg!=RET_DREGISTER) error(-1);
      use_reg(lreg);
      edx = emit_pop(0);
      code_lassign(edx,RET_DREGISTER);
      if (edx0!=-1)
	free_register(edx0);
      emit_pop_free(edx);
      emit_lpop_free(xreg);
      if (reg0!=USE_CREG && reg!=RET_DREGISTER)
	lmove(reg,RET_DREGISTER);
      set_double(reg);
    }
}

void
code_register_dassop(int reg,int op,int d) {
    // reg op= dpop()
    int  xreg;
    if (!d) {
      xreg=emit_dpop(d);
      dtosop(op,reg,xreg);
      emit_dpop_free(xreg,d);
    } else {
       xreg=emit_lpop();
       dtosop(op,reg,xreg);
       emit_lpop_free(xreg);
       set_double(lreg);
    }
}   

static int
code_dload_1(int d)
{
    int g = get_dregister(d);
    if (d)
	error(-1);
    else
	printf("\tli.s %s,1.0\n",fregister_name(g));
    return g;
}

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

    if (!d) {
	if (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",dir>0?"add.s":"sub.s",crn,crn,grn);
	    if (use && reg!=cadr(e2))
		printf("\tmov.s %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("\tl.s %s,0(%s)\n",frn,crn);
	    printf("\t%s %s,%s,%s\n",dir>0?"add.s":"sub.s",frn,frn,grn);
	    printf("\ts.s %s,0(%s)\n",frn,crn);
	}
	free_register(g);
    } else {
	if (car(e2)==DREGISTER) {
	    use_float(d,reg);
	    code_save_stacks();
	    code_double_lib_c("dpadd",cadr(e2),cadr(e2),dir);
	    if (reg!=cadr(e2)) lmove(reg,cadr(e2));
	    return;
	} 
	g_expr(e2);
	if(!is_int_reg(creg)) error(-1);
	xreg = ireg;
	emit_push();
	code_save_stacks();
	lload(xreg,DREGISTER_OPERAND,0);
	code_double_lib_c("dpadd",DREGISTER_OPERAND,RET_DREGISTER,dir);
	xreg = emit_pop(0);
	lstore(xreg,RET_DREGISTER);
	if (use) {
	    if (reg==USE_CREG)
		set_dreg(RET_DREGISTER,0);
	    else
		lmove(reg,RET_DREGISTER);
	}
	emit_pop_free(xreg);
    }
}

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

    if (!d) {
	if (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("\tmov.s %s,%s\n",frn,crn);
	    printf("\t%s %s,%s,%s\n",dir>0?"add.s":"sub.s",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("\tl.s %s,0(%s)\n",frn,crn);
	    printf("\t%s %s,%s,%s\n",dir>0?"add.s":"sub.s",grn,frn,grn);
	    printf("\ts.s %s,0(%s)\n",grn,crn);
	}
	free_register(g);
    } else {
	if (car(e2)==DREGISTER) {
	    xreg = cadr(e2);
	    code_double_lib_c("dpadd",xreg,RET_DREGISTER,dir);
	    //    xreg は increment する
            //    USE_CREG だと increment する前の値を creg にセット
	    //    reg が指定されていれば、それに前の値をセット
	    if (reg==USE_CREG) {
		use_float(d,reg);
		if (reg==RET_DREGISTER) {
		    reg = get_dregister(d);
		}
		set_dreg(reg,0);
	    }
	    g = list3(regv_l(reg),0,regv_l(xreg));
	    g = list3(regv_h(reg),g,regv_h(xreg));
	    g = list3(regv_l(xreg),g,regv_l(RET_DREGISTER));
	    g = list3(regv_h(xreg),g,regv_h(RET_DREGISTER));
	    parallel_rassign(g);
	    return;
	} 
	g_expr(e2);
	if(!is_int_reg(creg)) error(-1);
	xreg = ireg;
	emit_push();
	code_save_stacks();
	use_float(1,reg);
	lload(xreg,DREGISTER_OPERAND,0);
	code_double_lib_c("dpadd",DREGISTER_OPERAND,RET_DREGISTER,dir);
	xreg = emit_pop(0);
	emit_lpush();
	if (use) {
	    use_longlong(reg);
	    lload(xreg,reg,0);
	}
	reg = emit_lpop();
	lstore(xreg,reg);
	emit_lpop_free(reg);
	emit_pop_free(xreg);
    }
}

void
drexpr(int e1, int e2,int l1, int op,int cond)
{
    int op1;
    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;
	}
    }
    switch(op) {
    case FOP+GT:  op1=FOP+CMP;   break;
    case FOP+GE:  op1=FOP+CMPGE; break;
    case FOP+EQ:  op1=FOP+CMPEQ; break;
    case FOP+NEQ: op1=FOP+CMPEQ; break;
    case DOP+GT:  op1=DOP+CMP;   break;
    case DOP+GE:  op1=DOP+CMPGE; break;
    case DOP+EQ:  op1=DOP+CMP;   break;
    case DOP+NEQ: op1=DOP+CMP;   break;
    default: error(-1);
    }
    g_expr(list3(op1,e2,e1));
    switch(op) {
	case DOP+GT:	printf("\tbltz\t$2,$L_%d\n",l1);break;
	case DOP+GE:	printf("\tblez\t$2,$L_%d\n",l1);break;
	case DOP+EQ:	printf("\tbeq\t$2,$0,$L_%d\n",l1);break;
	case DOP+NEQ:	printf("\tbne\t$2,$0,$L_%d\n",l1);break;
	case FOP+GT:	printf("\tbc1t\t$L_%d\n",l1);break;
	case FOP+GE:	printf("\tbc1t\t$L_%d\n",l1);break;
	case FOP+EQ:	printf("\tbc1t\t$L_%d\n",l1);break;
        case FOP+NEQ:	printf("\tbc1f\t$L_%d\n",l1);break;
    }
}

int emit_dpop(int d)
{ 
    int xreg,reg;
    if (d) {
	return emit_lpop(); 
    }
    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;
}

#if 0
static int emit_lpop_regvar();

static
int emit_dpop_regvar(int d)
{ 
    int xreg,reg;
    if (d) {
	reg =  emit_lpop_regvar();
	regs[reg] = USING_DREG;
	return  reg;
    }
    xreg=pop_fregister();
    reg = cadr(get_dregister_var(0,d));
    if (xreg<= -REG_LVAR_OFFSET) {
        code_drlvar(REG_LVAR_OFFSET+xreg,1,reg);
	free_lvar(REG_LVAR_OFFSET+xreg);
	xreg=reg;
    } else {
        code_dassign_dregister(reg,d,xreg);
    }
    return xreg;
}
#endif

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

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

#endif

#if LONGLONG_CODE


/* 64bit int part */

#if 0
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 CMPGE;
    } else {
	return CMP;
    }
}
#endif

void
lrexpr(int e1, int e2,int l1, int op,int cond)
{
    int reg,regh,regl,e3h,e3l;
    int e3,l2,cr0=-1;
    g_expr(e1);
    emit_lpush();
    g_expr(e2);
    e3 = emit_lpop();
    if (!is_longlong_reg(creg)) error(-1);
    reg = lreg;
    l2 = fwdlabel();
    if (!(op==LOP+EQ||op==LOP+NEQ))
	cr0 = get_register();
    regh = regv_h(reg); regl = regv_l(reg);
    e3h =  regv_h(e3);  e3l = regv_l(e3);
    switch(op) {
    case LOP+GT: case LOP+GE:
	pcond(GT,  cr0,e3h,regh,1,cond?l1:l2);
	pcond(NEQ, cr0,e3h,regh,1,cond?l2:l1);
        break;
    case LOP+UGT: case LOP+UGE:
	pcond(UGT, cr0,e3h,regh,1,cond?l1:l2);
	pcond(NEQ, cr0,e3h,regh,1,cond?l2:l1);
        break;
    case LOP+EQ:
	pcond(EQ, cr0,regh,e3h,0,cond?l2:l1);
        break;
    case LOP+NEQ:
	pcond(EQ, cr0,regh,e3h,0,cond?l1:l2);
        break;
    default:
        error(-1);
    }
    pcond(op%LOP,cr0,e3l,regl,cond,l1);
    fwddef(l2);  
    if (cr0!=-1) free_register(cr0);
    emit_lpop_free(e3);
}

#if 0
static
int emit_lpop_regvar()
{ 
    int xreg,reg;
    xreg=lreg_stack[--lreg_sp];
    reg = cadr(get_lregister_var(0));
    if (xreg<= -REG_LVAR_OFFSET) {
        code_lrlvar(REG_LVAR_OFFSET+xreg,reg);
        free_lvar(REG_LVAR_OFFSET+xreg);
        xreg = reg;
    } else {
        code_lassign_lregister(reg,xreg);
    }
    return xreg;
}
#endif

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)
{
    use_longlong(reg);
    printf("\tor %s,%s,%s\n",
		lregister_name_low(reg),
		lregister_name_low(reg),
		lregister_name_high(reg));
    code_cmp_register(regv_l(reg));
}

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

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

void
code_lassign(int e2,int creg)
{
    use_longlong(creg);
    lstore(e2,creg);
}

void
code_lassign_gvar(int e2,int creg)
{
    int r;
    r = get_ptr_cache((NMTBL*)cadr(e2));
    code_lassign(r,creg);
}

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==0
    printf("\tsw %s,",crn_l);lvar(e2);
    printf("\tsw %s,",crn_h);lvar(e2+SIZE_OF_INT);
#else
    printf("\tsw %s,",crn_h);lvar(e2);
    printf("\tsw %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==0
    code_const(code_l1(lcadr(e1)),regv_l(creg));
    code_const(code_l2(lcadr(e1)),regv_h(creg));
#else
    code_const(code_l1(lcadr(e1)),regv_h(creg));
    code_const(code_l2(lcadr(e1)),regv_l(creg));
#endif
}

void
code_lneg(int creg)
{
    int dreg;
    char *rh,*rl,*dh,*dl;
    use_longlong(creg);
    rl=lregister_name_low(creg);
    rh=lregister_name_high(creg);
    dreg = get_lregister();
    dl=lregister_name_low(dreg);
    dh=lregister_name_high(dreg);
    printf("\tsubu %s,$0,%s\n",dl,rl);
    printf("\tsubu %s,$0,%s\n",dh,rh);
    printf("\tsltu %s,$0,%s\n",rl,dl);
    printf("\tsubu %s,%s,%s\n",dh,dh,rl);
    free_register(lreg);
    set_lreg(dreg,0);
}

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*)cadr(e1));
#if ENDIAN==0
    printf("\tlw %s,%d(%s)\n",crn_h,SIZE_OF_INT,register_name(r));
    printf("\tlw %s,0(%s)\n",crn_l,register_name(r));
#else
    printf("\tlw %s,0(%s)\n",crn_h,register_name(r));
    printf("\tlw %s,%d(%s)\n",crn_l,SIZE_OF_INT,register_name(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);
    printf("\tlw %s,",crn_l); lvar(e1);
    printf("\tlw %s,",crn_h); lvar(e1+SIZE_OF_INT);
}



static void
code_asld_lib(int reg,int oreg)
{
    char *ch,*cl,*oh,*ol,*dh,*dl;
    //    5   4   7   3   2   6
    int dreg = get_lregister();
    ch = lregister_name_high(reg);
    cl = lregister_name_low(reg);
    oh = lregister_name_high(oreg);
    ol = lregister_name_low(oreg);
    dh = lregister_name_high(dreg);
    dl = lregister_name_low(dreg);
    
    printf("\tsll     %s,%s,26\n",dh,ol);
    printf("\tbgez    %s,1f\n",dh);
    printf("\tsll     %s,%s,%s\n",oh,cl,ol);
    printf("\t.set    noreorder\n");
    printf("\tb       3f\n");
    printf("\tmove    %s,$0\n",dl);
    printf("\t.set    reorder\n");
    printf("\t1:\n");
    printf("\t.set    noreorder\n");
    printf("\tbeq     %s,$0,2f\n",dh);
    printf("\tsll     %s,%s,%s\n",oh,ch,ol);
    printf("\t.set    reorder\n");
    printf("\tsubu    %s,$0,%s\n",dh,ol);
    printf("\tsrl     %s,%s,%s\n",dh,cl,dh);
    printf("\tor      %s,%s,%s\n",oh,oh,dh);
    printf("\t2:\n");
    printf("\tsll     %s,%s,%s\n",dl,cl,ol);
    printf("\t3:\n");
    // printf("\tmove    %s,%s\n",cl,dl);
    // printf("\tmove    %s,%s\n",ch,oh);
    printf("\tmove    %s,%s\n",dh,oh);
    set_lreg(dreg,0);
    // free_register(dreg);
}

static void
code_asrd_lib(int reg,int oreg) // ___ashrdi3$stub
{
    char *ch,*cl,*oh,*ol,*dh,*dl;
    //    5   4   2   3   9   8
    int dreg = get_lregister();
    ch = lregister_name_high(creg);
    cl = lregister_name_low(creg);
    oh = lregister_name_high(oreg);
    ol = lregister_name_low(oreg);
    dh = lregister_name_high(dreg);
    dl = lregister_name_low(dreg);

    printf("\tsll     %s,%s,26\n",oh,ol);
    printf("\tbgez    %s,1f\n",oh);
    printf("\tsra     %s,%s,%s\n",dl,ch,ol);
    printf("\t.set    noreorder\n");
    printf("\tb       3f\n");
    printf("\tsra     %s,%s,31\n",dh,ch);
    printf("\t.set    reorder\n");
    printf("\t1:\n");
    printf("\t.set    noreorder\n");
    printf("\tbeq     %s,$0,2f\n",oh);
    printf("\tsrl     %s,%s,%s\n",dl,cl,ol);
    printf("\t.set    reorder\n");
    printf("\tsubu    %s,$0,%s\n",oh,ol);
    printf("\tsll     %s,%s,%s\n",oh,ch,oh);
    printf("\tor      %s,%s,%s\n",dl,dl,oh);
    printf("\t2:\n");
    printf("\tsra     %s,%s,%s\n",dh,ch,ol);
    printf("\t3:\n");
    // printf("\tmove    %s,%s\n",cl,dl);
    // printf("\tmove    %s,%s\n",ch,dh);
    set_lreg(dreg,0);
    // free_register(dreg);
}

static void
code_lsrd_lib(int reg,int oreg) // ___lshrdi3$stub
{
    char *ch,*cl,*oh,*ol,*dh,*dl;
    //    5   4   2   3   9   8
    int dreg = get_lregister();
    ch = lregister_name_high(reg);
    cl = lregister_name_low(reg);
    oh = lregister_name_high(oreg);
    ol = lregister_name_low(oreg);
    dh = lregister_name_high(dreg);
    dl = lregister_name_low(dreg);
    
    printf("\tsll     %s,%s,26\n",oh,ol);
    printf("\tbgez    %s,1f\n",oh);
    printf("\tsrl     %s,%s,%s\n",dl,ch,ol);
    printf("\t.set    noreorder\n");
    printf("\tb       3f\n");
    printf("\tmove    %s,$0\n",dh);
    printf("\t.set    reorder\n");
    printf("\t\n");
    printf("\t1:\n");
    printf("\t.set    noreorder\n");
    printf("\tbeq     %s,$0,2f\n",oh);
    printf("\tsrl     %s,%s,%s\n",dl,cl,ol);
    printf("\t.set    reorder\n");
    printf("\t\n");
    printf("\tsubu    %s,$0,%s\n",oh,ol);
    printf("\tsll     %s,%s,%s\n",oh,ch,oh);
    printf("\tor      %s,%s,%s\n",dl,dl,oh);
    printf("\t2:\n");
    printf("\tsrl     %s,%s,%s\n",dh,ch,ol);
    printf("\t3:\n");
    // printf("\tmove    %s,%s\n",cl,dl);
    // printf("\tmove    %s,%s\n",ch,dh);
    set_lreg(dreg,0);
    // free_register(dreg);
}

static void
code_longlong_lib(char *lib,int reg,int oreg)
{
    code_save_stacks();
    clear_ptr_cache();
    set_operands(regv_l(reg),regv_h(reg),regv_l(oreg),regv_h(oreg));
    extern_conv(lib);
    set_lreg(RET_LREGISTER,0);
}

#define code_ldiv_lib(reg,oreg)  code_longlong_lib("__divdi3",reg,oreg)
#define code_ludiv_lib(reg,oreg) code_longlong_lib("__udivdi3",reg,oreg)
#define code_lmod_lib(reg,oreg)  code_longlong_lib("__moddi3",reg,oreg)
#define code_lumod_lib(reg,oreg) code_longlong_lib("__umoddi3",reg,oreg)

#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;
    char *drn;
    // reg = reg 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_rlvar(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:
        drn = register_name(dx = get_register());
	printf("\taddu %s,%s,%s\n",crn_l,crn_l,orn_l);
	printf("\tsltu %s,%s,%s\n",drn,crn_l,orn_l);
	printf("\taddu %s,%s,%s\n",crn_h,crn_h,orn_h);
	printf("\taddu %s,%s,%s\n",crn_h,crn_h,drn);
	break;
    case LSUB:
        drn = register_name(dx = get_register());
	printf("\tsltu %s,%s,%s\n",drn,crn_l,orn_l);
	printf("\tsubu %s,%s,%s\n",crn_l,crn_l,orn_l);
	printf("\tsubu %s,%s,%s\n",crn_h,crn_h,orn_h);
	printf("\tsubu %s,%s,%s\n",crn_h,crn_h,drn);
	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:
	dx=get_lregister();
	use_reg(dx);
	drn_l = lregister_name_low(dx);
	drn_h = lregister_name_high(dx);
        /*
            drn_l 4 = l32( crn_l * orn_l);  6, 2
            drn_h 5 = h32( crn_l * orn_l);
            orn_l 6 = l32( crn_h * orn_l);  7, 3
	    drn_h 5 = drn_h + orn_l;  5, 6
            crn_l 2 = l32( crn_l * orn_h);  2, 6
	    crn_h 5 = drn_h + crn_l;  5, 2
	    crn_l = drn_l;
        */
	printf("\tsra     %s,%s,31\n",drn_l,orn_l);
	printf("\tmultu   %s,%s\n",crn_l,orn_l);
	printf("\tmfhi    %s\n",drn_h);
	printf("\tmflo    %s\n",drn_l);
	printf("\tmult    %s,%s,%s\n",orn_l,crn_h,orn_l);
	printf("\taddu    %s,%s,%s\n",drn_h,drn_h,orn_l);
	printf("\tmult    %s,%s,%s\n",crn_l,crn_l,orn_h);
	printf("\taddu    %s,%s,%s\n",crn_h,drn_h,crn_l);
	printf("\tmove    %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(ox);
    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 = lcaddr(e);
    } else if (car(e)==CONST) {
	if (!(-32766<cadr(e)&&cadr(e)<32767)) return 0;
	v = caddr(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,*drn;
    int v;
    int greg,dx=-1;

    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("\tsll %s,%s,%d\n",grn,crn_l,32-v);
	printf("\tsrl %s,%s,%d\n",crn_h,crn_h,v);
	printf("\tor %s,%s,%s\n",crn_h,grn,crn_h);
	printf("\tsll %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("\tsrl %s,%s,%d\n",grn,crn_l,v);
	printf("\tsll %s,%s,%d\n",grn,crn_h,32-v);
	printf("\tor  %s,%s,%s\n",grn,grn,grn);
	printf("\tsra %s,%s,%d\n",crn_h,crn_h,v);
	free_register(greg);
	return;
    case LURSHIFT:
	greg = get_register();
	use_reg(greg);
	grn = register_name(greg);
	printf("\tsll %s,%s,%d\n",grn,crn_h,32-v);
	printf("\tsrl %s,%s,%d\n",crn_l,crn_l,v);
	printf("\tor %s,%s,%s\n",crn_l,grn,crn_l);
	printf("\tsrl %s,%s,%d\n",crn_h,crn_h,v);
	free_register(greg);
	return;
    case LSUB:
	v = -v;
    case LADD:
	drn = register_name(dx = get_register());
	if (v<0) {
	    printf("\tsubu %s,%s,%d\n",crn_l,crn_l,-v);
	    printf("\tsltu %s,%s,%d\n",drn,crn_l,v);
	    printf("\tsubu %s,%s,1\n",crn_h,crn_h);
	    printf("\taddu %s,%s,%s\n",crn_h,crn_h,drn);
	} else {
	    printf("\tsltu %s,%s,%d\n",drn,crn_l,v);
	    printf("\taddu %s,%s,%d\n",crn_l,crn_l,v);
	    printf("\taddu %s,%s,%s\n",crn_h,crn_h,drn);
	}
	break;
    case LBOR:
	printf("\tori %s,%s,lo16(%d)\n",crn_l,crn_l,v);
	break;
    default:
	error(-1);
    }
    if (dx!=-1) free_register(dx);
}


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("\tmove %s,%s\n",crn_l,crn);
    printf("\tsra %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("\tmove %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("\tmove %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_dreg(DREGISTER_OPERAND,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_dreg(DREGISTER_OPERAND,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(FREGISTER_OPERAND,1);
    extern_conv("__fixsfdi");
    set_lreg(RET_LREGISTER,0);
    if (reg!=USE_CREG&&reg!=RET_LREGISTER)
	use_longlong(reg);
}

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

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


void
code_ll2f(int reg)
{
    set_lreg(LREGISTER_OPERAND,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 dreg,int rreg,int v)   //   rreg = dreg + v
{
  int dx;
  char *crn_l=lregister_name_low(dreg);
  char *crn_h=lregister_name_high(dreg);
  char *rrn_l=lregister_name_low(rreg);
  char *rrn_h=lregister_name_high(rreg);
  char *drn = register_name(dx = get_register());
    if (v<0) {
        printf("\tsubu %s,%s,%d\n",rrn_l,crn_l,-v);
        printf("\tsltu %s,%s,%d\n",drn,rrn_l,v);
        printf("\tsubu %s,%s,1\n",rrn_h,crn_h);
        printf("\taddu %s,%s,%s\n",rrn_h,rrn_h,drn);
    } else {
        printf("\taddu %s,%s,%d\n",rrn_l,crn_l,v);
        printf("\tsltu %s,%s,%d\n",drn,rrn_l,v);
        printf("\taddu %s,%s,%s\n",rrn_h,crn_h,drn);
    }
    free_register(dx);
}

void
code_lpreinc(int e1,int e2,int reg)
{
    int dreg=-1,xreg=-1;
    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);
    if (dreg!=-1) free_register(dreg);
}

void
code_lpostinc(int e1,int e2,int reg)
{
    int dreg,nreg,xreg;
    int dir=caddr(e1);
    if (car(e2)==LREGISTER) {
	use_longlong(reg);
	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(dreg,nreg,dir);
    lstore(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) {
	edx0 = get_register(); if(!edx0) error(-1);
	printf("# lassop\n\tmove %s,%s\n",register_name(edx0),
	       register_name(edx));
	edx = edx0;
    }
    lload(edx,reg,0);
    // free_register(edx); don't do this, it will free pushed register
    ltosop(op,reg,xreg);    // loprtc?
    emit_lpop_free(xreg);
    use_reg(reg);
    edx = emit_pop(0);
    code_lassign(edx,reg);
    emit_pop_free(edx);
    if (edx0!=-1)
	free_register(edx0);
}

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;
        }
    }
#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;
        }
    }
#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;
        }
    }
#endif
}

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

void
code_closing()
{
    global_table();
    /* printf("\t.ident \"Micro-C compiled\"\n"); */
    fclose(asi); asi=0;
}

/* end */