view mc-code-mips.c @ 794:032dc03be02e

i64 arg_register in inline mode
author Shinji KONO <kono@ie.u-ryukyu.ac.jp>
date Tue, 23 Nov 2010 09:42:08 +0900
parents 3bc6c34bfa2e
children 5313ed059cee
line wrap: on
line source

/* Micro-C Code Generation Part for MIPS (ps2linux) */

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


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

#if 0
char *l_include_path[] = {
    "/usr/include/",
    "/usr/lib/gcc-lib/mipsEEel-linux/2.95.2/include/",
    0
};
#else
#include "mc-include.c"
#endif

// va_start, va_arg is wrong, use va_mips.h

static char *init_src0 = "\
/* #define __builtin_va_list int */\n\
/* #define __builtin_va_start(ap,arg) ap=(((int)(&arg))+sizeof(arg)) */\n\
/* #define __builtin_va_arg(ap,type)  (*((type *)ap)++) */\n\
#define __builtin_next_arg(arg)  ap=((void *)(&arg)),va_arg(ap,typeof(arg))\n\
#define __mips__ 1\n\
#define __LITTLE_ENDIAN__ 1\n\
#define __inline__ inline\n\
#define __STDC__ 1\n\
#define __SIZE_TYPE__ int\n\
#define __extension__\n\
// #define __attribute__(a)\n\
#define __flexarr\n\
#define __WCHAR_TYPE__ int\n\
#define __alignof__(type) (sizeof(type)==1?1:sizeof(type)==2?2:sizeof(type)<=4?4:8)\n\
#define __PTRDIFF_TYPE__ int\n\
#define __GNUC__ 2\n\
#define __const const\n\
#define __signed__ signed\n\
#define alloca __builtin_alloca\n\
";

int eval_order = NORMAL;
int data_alignment = 0;

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

#define DOT_SIZE 1

// static void data_mode(char *name);
// static void text_mode(int alignment);
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 push_struct(int e4,int t,int arg);
static void register_usage(char *s);
static int get_input_dregister_var0(int i,int reg_var,NMTBL *n,int is_code,int d);

static void ascii(char *s);

static int creg;

static int output_mode = TEXT_EMIT_MODE;

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

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 FREG_VAR_BASE 21
#define FREG_VAR_MIN  16
#define MIN_TMP_FREG 0
#define MAX_TMP_FREG 11

int MAX_REGISTER=30;             /* MIPSのレジスタを10個まで使う*/
int MAX_FREGISTER=31;
#define  REAL_MAX_REGISTER 32    /* MIPSのレジスタが32ということ*/
#define  REAL_MAX_FREGISTER 32    /* MIPSのレジスタが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","$fp","$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(int i) { return register_name(i); }
char *f(int i) { return fregister_name(i); }
char *ll(int i) { return lregister_name_low(i); }
char *lh(int 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) { if (regs[lreg]) free_register(lreg); lreg = 0; }
	if (!ireg) ireg = get_register();
	// else if (ireg!=i) free_register(i);
	i = ireg;
    }
    if (!regs[i]) regs[i]=USING_REG;
    creg = i;
    return i;
}

#if LONGLONG_CODE||FLOAT_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

#define USING_DREG 5
#define INPUT_DREG 6


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

static
int use_double0() { 
    int i;
    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);

void code_lassign_lvar(int e2,int creg);
int code_lrindirect(int e1, int reg, int offset, int us);
void code_lregister(int e2,int reg);
void code_lassign_gvar(int e2,int creg);
void code_lassign(int e2,int creg);
void code_lassign_lregister(int e2,int reg);
void code_lrgvar(int e1,int creg);
void code_lrlvar(int e1,int creg);
void emit_lpop_free(int xreg);
void emit_lpush();
int emit_lpop();

#endif
#if LONGLONG_CODE
static int code_l1(long long ll);
static int code_l2(long long ll);
static void    set_lreg(int,int);
#endif

static void code_save_input_registers(int dots);
static void    set_ireg(int,int);
static void    set_dreg(int,int);
static void    set_freg(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();

static void jcond(int l, char cond);

#define ARG_LVAR_OFFSET 0x10000000

#define round16(i)   align(i,16)
#define round4(i)    align(i,4)

/*          

     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 segment)
     $25    jump register
     $31    return address
     $sp    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 $fp             prev $sp        $fp                           $sp
 */

#define arg_offset 8
#define arg_offset1 0
int disp_offset = 0;
#define disp_offset0  0


#define code_disp_offset0 0

#define CODE_LVAR(l) ((l)+code_disp_offset0)
#define CODE_CALLER_ARG(l) ((l)+arg_offset1)
#define FUNC_LVAR(l) (l+disp_offset0)
#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+16) +
	round16((max_func_args<2?2:max_func_args)*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 ;
    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
);
#endif
#if 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

#if 1
    printf("## $L_%d=%d\n",r1_offset_label,r1_offsetv);
    printf("## $L_%d=%d\n",lvar_offset_label,r1_offsetv-lvar_offsetv);
    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,r1_offsetv-lvar_offsetv);

    return r1_offsetv;
}

static void
lvar(int l)
{
    if (is_code(fnptr)) {
        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($fp)\n",FUNC_LVAR(l));
    } 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),lvar_offset_label);
    }
}

static void
lvar_address(int l,int creg)
{
    if (is_code(fnptr)) {
        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\n",register_name(creg),
		FUNC_LVAR(l));
    } 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),lvar_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;
    /* called only once */

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

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

extern void
emit_reinit()
{
    /* called for each file */
    init_ptr_cache();
    output_mode = -1;
}



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 in)
{
    int args = fnptr->dsp;
    NMTBL *n;
    int reg_var = 0;
    int freg_var = 0;
    int type;
    int reg;
    int i;
    int is_code0 = is_code(fnptr);
    int dots;
    if (in) return;

    function_type(fnptr->ty,&dots);
    while (args) {
        /* process in reverse order */
        n = ncadddr(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++;
                freg_var++;
                caddr(args)=SIZE_OF_INT;
            }
        } else if (type==FLOAT) {
            if ((reg = get_input_dregister_var0(freg_var,reg_var,n,is_code0,0))) {
                n->sc = FREGISTER;
                n->dsp = cadr(reg);
                regs[n->dsp]= INPUT_REG;
                reg_var++;
                freg_var++;
                caddr(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;
                caddr(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;
                caddr(args)=size(type);
            }
        }
        args = cadr(args);
    }
    if (is_function(fnptr))
	code_save_input_registers(dots);
}


int 
get_register(void)
{    /* 使われていないレジスタを調べる */
    int i,j,reg;
    for(i=MAX_TMP_REG;i>MIN_TMP_REG;i--) {
	if (regs[i]) continue;  /* 使われている */
	regs[i]=USING_REG;      /* そのレジスタを使うことを宣言し */
	return i;   /* その場所を表す番号を返す */
    }
    /* PTR_CACHE をつぶす */
    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(RGERR); 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_FLOAT)),reg,0); 
            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 list3n(LREGISTER,ll,n); 
		    }
		}
		/* ひとつしかなかった */
		regs[REG_VAR_BASE-i]=0; 
		max_reg_var=max_reg_var_save;
		goto not_found;
	    }
	}
    }
not_found:
    return list3n(LVAR,new_lvar(SIZE_OF_LONGLONG),0);
}

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

void

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

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

static int
get_input_dregister_var0(int i,int reg_var,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 {
	/* 
	    f50(float,float,float...) => f12,f14,$6,$7
	    test3(int,float,float,float...) => $4,$5,$6,$7
	*/
	if (i==0) i=12+FREG_OFFSET;
	else if (i==1 && reg_var==0) i=14+FREG_OFFSET;
	else if (i==1) return list3n(REGISTER,5,n);
	else if (i==2) return list3n(REGISTER,6,n);
	else if (i==3) return list3n(REGISTER,7,n);
	else return 0;
    }
    return list3n(FREGISTER,i,n);
}

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

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_L==0
	regv_l(ll)=i;
	regv_h(ll)=i+1;
#else
	regv_h(ll)=i;
	regv_l(ll)=i+1;
#endif
    } else { error(-1); ll=LREG_OFFSET+2; }
    return list3n(LREGISTER,ll,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 list3n(REGISTER,i,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 list3n(REGISTER,i,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;
}


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


void
register_usage(char *s)
{
#if 1
    int i,j;
#endif
#define USAGE_MAX 4
    if (!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:01234567890123456789012");
	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) {
	error(-1);
	free_register(reg_stack[--reg_sp]);
    }
    while(freg_sp > 0) {
	error(-1);
	free_register(freg_stack[--freg_sp]);
    }
    while(lreg_sp > 0) {
	error(-1);
	free_register(lreg_stack[--lreg_sp]);
    }
    use_int0();
    text_mode(2);
    gexpr_code_init();
    register_usage("");
}


void

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

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

#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 list3n(REGISTER,j,n); 
	}
    }
    return list3n(LVAR,new_lvar(SIZE_OF_INT),0);
}

#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 list3n(FREGISTER,j,n); 
        }
    }
    return list3n(LVAR,new_lvar(SIZE_OF_DOUBLE),0);
}

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

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

extern void
code_ptr_cache_def(int r, NMTBL *nptr)
{
    char *rrn = register_name(r);
    printf("\tla %s,%s\n",rrn,nptr->nm);
}

static void 
code_add(int reg,int offset,int r)
{
    char *crn = register_name(reg);
    char *rrn = register_name(r);
    if (offset==0) {
        if(r!=reg)
            printf("\tmove %s,%s\n",crn,rrn);
    } else
        printf("\taddu %s,%s,%d\n",crn,rrn,offset);
}


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

static void 
code_ldf(char *ld,char *crn,int offset,int r)
{
    char *rrn = register_name(r);
    printf("\t%s %s,%d(%s)\n",ld,crn,offset,rrn);
}


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) {
    use_int(reg);
    code_add(reg,cadr(e1),get_ptr_cache(ncaddr(e1)));
    return;
}

void
code_rgvar(int e1,int reg) {
    use_int(reg);
    code_ld("lw",reg,cadr(e1),get_ptr_cache(ncaddr(e1)));
}

void
code_crgvar(int e1,int reg,int sign,int sz){
    use_int(reg);
    code_ld(cload(sz,sign),reg,cadr(e1),get_ptr_cache(ncaddr(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);
}

extern void
code_i2c(int reg)
{
    int reg1;
    use_int(reg);
    reg1 = get_register();
    printf("sll %s,%s,24\n",register_name(reg1),register_name(reg));
    printf("sra %s,%s,24\n",register_name(reg),register_name(reg1));
    free_register(reg1);
}

extern void
code_i2s(int reg)
{
    int reg1;
    use_int(reg);
    reg1 = get_register();
    printf("sll %s,%s,16\n",register_name(reg1),register_name(reg));
    printf("sra %s,%s,16\n",register_name(reg),register_name(reg1));
    free_register(reg1);
}

extern void
code_u2uc(int reg)
{   
    use_int(reg);
    printf("andi %s,%s,0xff\n",register_name(reg),register_name(reg));
}

extern void
code_u2us(int reg)
{   
    use_int(reg);
    printf("andi %s,%s,0xffff\n",register_name(reg),register_name(reg));
}

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_label_value(int label,int reg) {
    use_int(reg);
    printf("\tla %s,$L_%d\n",register_name(reg),label);
    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 (use && cadr(e2)!=reg)
	    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);
	if (use)
	    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);
#if 0
    printf("\taddu %s,",register_name(creg));
    printf("$fp,%d+$L_%d\n",FUNC_LVAR(0),lvar_offset_label);
#else
    printf("\tmove %s,$fp\n",register_name(creg));
#endif
#endif
}

static int rexpr_bool(int e1, int reg);

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


void
code_cmp_crgvar(int e1,int reg,int sz,int label,int cond) {
    use_int(reg);
    code_ld(cload(sz,0),reg,cadr(e1),get_ptr_cache(ncaddr(e1)));
    cext(0,sz,r);
    cmpreg = reg;
    // printf("\tcmpwi cr0,%s,0\n",crn);
    jcond(label,cond);
}


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


void
code_cmp_rgvar(int e1,int reg,int label,int cond) {
    use_int(reg);
    code_ld("lw",reg,cadr(e1),get_ptr_cache(ncaddr(e1)));
    code_cmp_register(reg,label,cond);
}


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


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


void
code_string(int e1,int creg)
{
    char *s,*crn;
    int lb;
    NMTBL *n = ncaddr(e1);
    if ((lb=attr_value(n,LABEL))) {
        // already defined
        return code_label_value(lb,creg) ;
    }

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

    s=n->nm;
    lb = emit_string_label();
    ascii(s);
    text_mode(2);
    printf("\tla %s,$L_%d\n",crn,lb);
    set_attr(n,LABEL,lb);
}

void
emit_strings(NMTBL *n)
{
    int l = emit_string_label();
    int i;
    for(i = n->dsp; i; i = cadr(i)) {
        ascii(scaddr(i));
    }
    emit_label(l);
}

void
code_strings(int e2,int reg)
{
    int l = emit_string_label();
    int i;
    for(i = e2; i; i = cadr(i)) {
        ascii(scaddr(i));
    }
    text_mode(2);
    code_label_value(l,reg);
}


#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;
    char *memmove = "memmove";
    int l;
    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) {
		free_register(dreg); dreg = 0;
                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) {
	    free_register(dreg); dreg = 0;
	    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();

	l = list3(4,0,to);
	l = list3(5,l,from);
	parallel_rassign(l);
	printf("\tli $6,%d\n",length>0?length:-length);
        /* overrap must be allowed */
	if (is_code(fnptr)) {
	    printf("\tla\t$25,%s\n",memmove);
	    printf("\tjalr\t$25\n");
	    printf("\tlw\t$gp,$L_%d($sp)\n",cprestore_label);
	} else {
	    printf("\tjal %s\n",memmove);
	}
	extern_define(memmove,0,FUNCTION,1);
	set_ireg(RET_REGISTER,0);
	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(creg!=to) {
	    free_register(to);
	    // set_ireg(to,1);
	}
    }
    if (dreg) free_register(dreg);
}

int
push_struct(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);
	if (creg==lreg) lreg = 0;
	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;
}

#if FLOAT_CODE

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_dreg_operand(int reg,int mode)
{
     set_lreg_operand(reg,mode);
}

#endif

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 dots)
{
    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;
    int reg_var = 0;

    for(args = fnptr->dsp;args;args = cadr(args)) {
	n = ncadddr(args);
	tag = n->sc;
	reg = n->dsp;
	if (!n||n==&null_nptr) error(REG_ERR);
	if (tag==REGISTER) {
	    /* regs[reg]==INPUT_REG case should be considered */
	    n->dsp = offset;
	    offset+=SIZE_OF_INT;
	    t = INT;
	    reg_var++;
	} 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==5||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);
	    reg_var++;
	} 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;
	    reg_var += 2;
	} else if (tag==LREGISTER) {
	    /* regs[reg]==INPUT_REG case should be considered */
	    n->dsp = offset;
	    t = n->ty;
	    offset+=SIZE_OF_LONGLONG; reg_offset+=2;
	    reg_var += 2;
	} else {
	    offset += size(n->ty);
	    continue;
	}
	n->sc  = LVAR;
	g_expr_u(assign_expr0(list3n(LVAR,n->dsp,0),list3n(tag,reg,n),t,t));
	if (tag==REGISTER||tag==DREGISTER||tag==FREGISTER||tag==LREGISTER) {
	    free_register(reg);
	}
    }
    if (dots) {
        while ((reg = get_input_register_var(reg_var,0,0))) {
            g_expr_u(assign_expr0(
                list3n(LVAR,offset,0),reg,INT,INT));
            offset+=SIZE_OF_INT;
            reg_var++;
        }
    }
    my_func_args = offset;
}

int
not_simple_p(int e3)
{
    switch(e3) {
	case FUNCTION: case CONV: case RSTRUCT: case STASS: case ALLOCA: 
	case LDIV: case LUDIV: case LMOD: case LUMOD: 
	case LLSHIFT: case LULSHIFT: case LRSHIFT: case LURSHIFT: 
	case DDIV: case DADD: case DSUB: case DMUL: case DMINUS: 
	case  DPOSTINC :  case DPREINC :  case DASSOP : 
	case  DOP+LT :  case  DOP+LE :  case  DOP+GT : 
	case  DOP+GE :  case  DOP+EQ :  case  DOP+NEQ:
	case INLINE:
    return 1;
    }
    return 0;
}

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

#define FASS_INPUT (FOP+199)

static void
code_assign_input_float_int(int e0) {
#if FLOAT_CODE
    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(ncaddr(e2));
	printf("\tlw %s,%d(%s)\n",frn,cadr(e2),register_name(r));
	break;
    case FRLVAR:
	lvar_intro(cadr(e2));
	printf("\tlw %s,",frn); lvar(cadr(e2));
    default:
	g_expr(e2);
	printf("\tmfc1 %s,%s\n",frn,fregister_name(freg));
	break;
    case FREGISTER:
	printf("\tmfc1 %s,%s\n",frn,fregister_name(cadr(e2)));
    }
#endif
}

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);
    if (t==FLOAT&&car(arg)==REGISTER)
	code_assign_input_float_int(list3(FASS_INPUT, arg, e4));
    else
	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++;
	freg_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 list3n(LVAR,caller_arg_offset_v(nargs),0);
	} 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 list3n(LVAR,caller_arg_offset_v(nargs),0);
	} 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 list3n(LVAR,caller_arg_offset_v(nargs),0);
	} else
	    return get_input_dregister_var0(freg_arg,reg_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 list3n(LVAR,caller_arg_offset_v(nargs),0);
	} 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 list3n(LVAR,caller_arg_offset_v(nargs),0);
    } else {
	error(-1);
	return get_register_var(0);
    }
}

static void
code_call(int e2,NMTBL *fn,int jmp)
{
    // char *jrn;
    if (is_code(fnptr)) {
	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=0,preg_arg=0,pfreg_arg=0;
    int stargs;

    special_lvar = -1;
    ret_type = function_type(cadddr(e1),&dots);
    if (caddr(cadddr(e1))==0) dots=1;

    arg_assign = 0;
    e2 = cadr(e1);
    if (car(e2) == FNAME) {	
	fn=ncaddr(e2);
    } else {	
	if (car(e2)==INDIRECT) e2=cadr(e2); // (*func)(i) case
	jmp = list2(REGISTER,25);
	if (!simple_arg(e2)) {
	    e3=get_register_var(0);
	    g_expr_u(assign_expr0(e3,e2,INT,INT));
	    e2=e3;
	}
	arg_assign = list2(assign_expr0(jmp,e2,INT,INT),arg_assign);
    }
    /* 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) e4 = cadr(e4);
		else if (car(e4)!=INDIRECT) e4 = cadr(e4);
		if (!simple_arg(e4)) {
		    // Calculate complex struct address here.
		    // If simple, leave it.
		    arg = get_register_var(0);
		    g_expr_u(assign_expr0(arg,e4,INT,INT));
		    car(e3)=arg;
		    reg_arg_list = list2(arg,reg_arg_list);
		    if (car(arg)==REGISTER) use_input_reg(cadr(arg),1);
                    else car(e3) = rvalue_t(arg,INT);
		}
	    }
	    stargs = list4(e3,stargs,nargs,reg_arg);
	}
	increment_function_arg(e3,&nargs,&reg_arg,&freg_arg);
    }

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

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

    if (stargs) {
	if (complex_) {
	    arg = get_input_arg(caddr(complex_),AS_SAVE,
				    pnargs,preg_arg,pfreg_arg);
	    reg_arg_list = compute_complex_arg(complex_,reg_arg_list,arg);
	}
	for(stargs=reverse0(stargs);stargs;stargs = cadr(stargs)) {
	    e3 = car(stargs);
	    e4 = car(e3);
	    t  = caddr(e3);
	    arg = get_input_arg(t,AS_ARG,caddr(stargs),cadddr(stargs),0);
	    push_struct(e4,t,arg);
	    car(e3)=0;  // done
	    if (car(arg)==REGISTER)
		use_input_reg(cadr(arg),1);
	}
    } 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.
	    if (car(arg)==REGISTER)
		use_input_reg(cadr(arg),1);
	}
    }

    nargs = reg_arg = freg_arg = 0;
    // calc stack arguments first, it may requires extra registers,
    // and we can still use input registers now.
    for (e3 = e1; e3; 
		increment_function_arg(e3,&nargs,&reg_arg,&freg_arg),
		e3 = cadr(e3)) {	
	if (!(e4=car(e3))) continue;
	t=caddr(e3);
	arg = get_input_arg(t,AS_ARG,nargs,reg_arg,freg_arg);
	if (car(arg)!=LVAR) continue;
	g_expr_u(assign_expr0(arg,e4,t,t));
	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);
    free_register_var(reg_arg_list);
    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_alloca(int e1,int reg)
{
    char *crn;
  
    g_expr(list3(BAND,list3(ADD,e1,list2(CONST,15+4)),list2(CONST,~15))); 
    use_int(reg);
    crn = register_name(reg);
    printf("\tsubu $sp,$sp,%s\n",crn);
    printf("\taddu %s,$sp,$L_%d+4\n",crn,cprestore_label);
    if (is_code(fnptr)) {
	printf("\tsw\t$gp,$L_%d($sp)\n",cprestore_label);
    }
}

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
}

int
code_frame_pointer_register()
{
    return list2(REGISTER,REG_fp);
}

void
code_fix_frame_pointer(int offset) {
}

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

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

#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||FLOAT_CODE

static void
lload(int creg,int reg,int offset) 
{
    char *crn=register_name(creg);
#if ENDIAN_L==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_L==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) {
    use_int(creg);
    code_ldf(cstore(byte),register_name(creg),cadr(e2),
        get_ptr_cache(ncaddr(e2)));
}

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

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

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


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

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

    use_int(creg);
    xreg = emit_pop(0);       /* pop e3 value */

    set_ireg(edx,0);
    ld_indexx(byte,0,creg,ireg,sign);
    tosop(op,ireg,xreg);
    crn = register_name(ireg);
    drn = register_name(creg);
    printf("\t%s %s,0(%s)\n",cstore(byte),crn,drn);
    free_register(edx);
    emit_pop_free(xreg);
}

int
tosop_operand_safe_p(int op)
{
    switch(op) {
    case CMPGE: case CMP: case UCMPGE:
	return CMP;  // creg is not safe
    default:
    return 1;
    }
}

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);
        free_lvar(oreg+REG_LVAR_OFFSET);
	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);
}

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 glist2(REGISTER,creg);
}

void
code_cmpdimm(int e, int csreg,int label,int cond)
{
    /* used in dosiwtch() */
    int reg=-1,regsv;
    char *rn,*crn;
    crn = register_name(csreg);

    if (e<-32767||32766<e) {
	regsv=regs[csreg]; use_reg(csreg);
	rn = register_name(reg= get_register());
	regs[csreg]=regsv;
	code_const(e,reg);
	switch(cond) {
	case 1:
	    printf("\tbne\t%s,%s,$L_%d\n",crn,rn,label); break;
	case 0:
	    printf("\tbeq\t%s,%s,$L_%d\n",crn,rn,label); break;
	case LT:
	    printf("\tslt\t%s,%s,%s\n",rn,crn,rn);
	    printf("\tbne\t%s,$0,$L_%d\n",rn,label); break;
	default: error(-1);
	}
	free_register(reg);
    }
    switch(cond) {
    case 1:
	printf("\tbne\t%s,%d,$L_%d\n",crn,e,label); break;
    case 0:
	printf("\tbeq\t%s,%d,$L_%d\n",crn,e,label); break;
    case LT:
	regsv=regs[csreg]; use_reg(csreg);
	rn = register_name(reg= get_register());
	regs[csreg]=regsv;
	printf("\tslt\t%s,%s,%d\n",rn,crn,e);
	printf("\tbne\t%s,$0,$L_%d\n",rn,label); break;
    default: error(-1);
    }
    if (reg!=-1) free_register(reg);
}

void
code_opening(char *filename)
{
    static int count=0;
    char *asi_name;
    /* this is called once per file */
    printf("\t.file %d \"%s\"\n",count++,filename);
    printf(".abicalls\n");
    // printf(".text\n");

    if (asi) {
        fclose(asi);
        asi = 0;
    }
    asi_name = make_filename_with_extension(filename,"i");
    if ( (asi = fopen(asi_name,"w")) == NULL ) error(FILERR);
    if (!asi) error(-1);
    printf(".include \"%s\"\n",asi_name);
}

// should have pcond_const

#define COND_BRANCH 1
#define COND_VALUE  2

static void
pcond(int op, int r2,int r1,int r0,int cond,int l1,int mode)
{
    char *slt  = "slt";
    char *sltu = "sltu";
    char *eq = "eq";
    char *ne = "ne";
    int t;
    char *rn2;
    char *rn1;
    char *rn0;

// 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);
    }
    rn2 =		register_name(r2);
    rn1 =		register_name(r1);
    rn0 =		register_name(r0);


    if (mode==COND_BRANCH) {
	if (op==EQ||op==NEQ) {
	    printf("\tb%s\t%s,%s,$L_%d\n",eq,rn0,rn1, l1);
	} else {
	    printf("\t%s\t%s,%s,%s\n",slt,rn2,rn1,rn0);
	    printf("\tb%s %s,$0,$L_%d\n",eq,rn2,l1);
	}
    } else if (mode==COND_VALUE) {
	if (op==EQ) {
	    printf("\txor\t%s,%s,%s\n",rn2,rn1,rn0);
	    printf("\tsltu\t%s,%s,1\n",rn2,rn2);
	} else if (op==NEQ) {
	    printf("\txor\t%s,%s,%s\n",rn2,rn1,rn0);
	    printf("\tsltu\t%s,$0,%s\n",rn2,rn2);
	} else {
	    printf("\t%s\t%s,%s,%s\n",slt,rn2,rn1,rn0);
	    if (eq==ne) {
		printf("\txor\t%s,%s,1\n",rn2,rn2);
	    }
	}
    } else error(-1);
}

int
rexpr_bool(int e1, int reg)
{       
    int e2,reg0;
    int op = car(e1);
    if (!(
	op== GT || op== LT || op== UGT || op== ULT ||
	op== ULE || op== UGE || op== LE || op== GE ||
	op== EQ || op== NEQ
    )) return 0;
    g_expr(cadr(e1));
    emit_push();
    g_expr(caddr(e1));
    e2 = emit_pop(1);
    reg0 = ireg;
    use_int(reg);
    pcond(op, reg,e2,reg0,0,0,COND_VALUE);
    emit_pop_free(e2);
    return 1;
}

int
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,COND_BRANCH);
    emit_pop_free(e2);
    return l1;
}

#define CMP_C1T (-1)

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

void
jmp(int l)
{       
    printf("\tj\t$L_%d\n",l);
}

void
code_comment(char *s)
{
    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,-%d($13)\n",register_name(i),
	    -disp);
	disp -= SIZE_OF_INT;
    }
    for (i=freg_var_num(0);i>freg_var_num(freg_save);i--) {
	printf("\ts.s    %s,-%d($13)\n",register_name(i),
	    -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,-%d($13)\n",register_name(i),
	    -disp);
	disp -= SIZE_OF_INT;
    }
    for (i=freg_var_num(0);i>freg_var_num(freg_save);i--) {
	printf("\tl.s    %s,-%d($13)\n",register_name(i),
	    -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;
    clear_ptr_cache();
}


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 $sp,$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("\tmove $13,$sp\n");
    printf("\tsubu $sp,$sp,$L_%d\n",r1_offset_label);
    printf("\t.cprestore $L_%d\n",cprestore_label=fwdlabel());
    printf("\tsw      $31,-%d($13)\n",arg_offset);
    printf("\tsw      $fp,-%d($13)\n",arg_offset+SIZE_OF_INT);
    printf("\tj $L_%d\n",register_save_label=fwdlabel());
    register_save_return_label = backdef();
    printf("\tsubu  $fp,$13,$L_%d\n",lvar_offset_label);
    clear_ptr_cache();
}

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

void
code_label_call(int l)
{
    if (is_code(fnptr)) {
	printf("\tla\t$25,L_%d\n",l);
	printf("\tjalr\t$25\n");
	printf("\tlw\t$gp,$L_%d($sp)\n",cprestore_label);
    } else
	printf("\tjal       L_%d\n",l);
}

void
code_ret()
{
    printf("\tj       $31\n");
    control=1;
}

static void 
make_return_continuation()
{
    // 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);
	printf("\tmove    $fp,$21\n");
    } else if (cadr(fnptr->ty)==DOUBLE) {
	creg = lreg = cadr(get_input_dregister_var(0,0,1,1));
	set_dreg(RET_DREGISTER,1);
	printf("\tmove    $fp,$19\n");
    } else if (cadr(fnptr->ty)>0&&(
	car(cadr(fnptr->ty))==STRUCT ||
	car(cadr(fnptr->ty))==UNION)) {
	int 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);
	printf("\tmove    $fp,$20\n");
	// 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);
	printf("\tmove    $fp,$20\n");
    }
#if R1SAVE
#else
#if 0
    printf("\tsubu $fp,");
    printf("$fp,%d+$L_%d\n",FUNC_LVAR(0),lvar_offset_label);
    // printf("\tj $L_%d\n",retcont1);
#endif
#endif
}

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

    if (control) {
	code_set_return_register(1);
    } else
	text_mode(2);
    if (retcont) { 
	/* return from CbC segement */
	if (control) jmp(retlabel);
	make_return_continuation();
    }
    fwddef(retlabel);
    // if (retcont) {
// 	fwddef(retcont1);
    // }

    r1_offsetv = code_offset_set(fnptr);

    printf("\taddu    $13,$fp,$L_%d\n",lvar_offset_label);
    printf("\tlw      $31,-%d($13)\n",arg_offset);
    printf("\tlw      $fp,-%d($13)\n",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("\tmove    $sp,$13\n");
    code_ret();

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


int
code_set_return_register(int mode) {
    if (cadr(fnptr->ty)==FLOAT) {
	set_freg(RET_FREGISTER,mode);
	return freg;
    } else if (cadr(fnptr->ty)==DOUBLE) {
	set_dreg(RET_DREGISTER,mode);
	return lreg;
    } else if (cadr(fnptr->ty)==LONGLONG||cadr(fnptr->ty)==ULONGLONG) {
	set_lreg(RET_LREGISTER,mode);
	return lreg;
    } else if (cadr(fnptr->ty)==VOID) {
	return 0;
    } else {
	set_ireg(RET_REGISTER,mode);
	return ireg;
    }
}

int
code_get_fixed_creg(int reg,int type) {
    switch(type) {
#if FLOAT_CODE
    case DOUBLE: use_float(1,reg); break;
    case FLOAT:  use_float(0,reg); break;
#endif
#if LONGLONG_CODE
    case LONGLONG:  case ULONGLONG:  use_longlong(reg); break;
#endif
    default: 
	if (reg==USE_CREG) {
	    if (regs[CREG_REGISTER]==0||regs[CREG_REGISTER]==PTRC_REG) {
                set_ireg(CREG_REGISTER,is_int_reg(creg)&&regs[creg]==USING_REG);
		return CREG_REGISTER;
	    }
	}
	use_int(reg); 
	break;
    }
    return reg;
}

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
code_align(int t)
{
    int d;
    switch(t) {
    case CHAR: case UCHAR: return;
    case SHORT: case USHORT: d = data_alignment & 1; break;
    default: d = data_alignment & 3;
    }
    if (d) {
        printf("\t.align 2\n");
        data_alignment = 0;
    }
}

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

extern int
emit_string_label() {
    int lb;

    lb=fwdlabel();
    cstring_mode();
    printf("$L_%d:\n",lb);
    return lb;
}

extern void 
emit_string(char *s,int t)
{
    t = type_value(t);
    if (car(t)==ARRAY &&  
            (type_value(cadr(t))==CHAR||type_value(cadr(t))==UCHAR)) {
        ascii(s);
    } else {
        int l = emit_string_label();
        ascii(s);
        emit_label(l);
    }
    return;
}

extern void
emit_global(NMTBL *n,int a,int e)
{
    int t = type_value(n->ty);
    if (e>0 && car(e)==STRING && t>0 && car(t)==ARRAY &&  
            (type_value(cadr(t))==CHAR||type_value(cadr(t))==UCHAR)) {
        cstring_mode();
    } else
	data_mode(n->nm);
    code_align(a);
    if (n && n->sc!=STATIC)
	printf("\t.globl\t%s\n",n->nm);
    printf("%s:\n",n->nm);
}

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

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

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

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

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

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

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

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

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

extern void
emit_data_closing(NMTBL *n)
{
#ifdef DOT_SIZE
    int lb;
#endif
    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=global_list;n;n=n->next) {
	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) {
	    // is this really happen? why don't we use local static list?
	    if (is_function(n)||is_code(n)) continue;
	    /* 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=local_static_list;n;n=n->next) {
	if (n->sc == STATIC) {
	    if (n->dsp != -1) { /* initialized static */
		if (init==0) {
		    data_mode(0);
		    init=1;
		}
		comm(n);
	    }
	}
    }
    text_mode(2);
}

void
cstring_mode(int align)
{
    if (output_mode!=RODATA_EMIT_MODE) {
        printf(".section\t.rodata\n\t.align 2\n");
        output_mode = RODATA_EMIT_MODE;
    }
}

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

#if FLOAT_CODE||LONGLONG_CODE
static void
extern_conv(char *conv)
{
     code_save_stacks();
     clear_ptr_cache();
     extern_define(conv,0,FUNCTION,1);
     if (is_code(fnptr)) {
	printf("\tla\t$25,%s\n",conv);
	printf("\tjalr\t$25\n");
	printf("\tlw\t$gp,$L_%d($sp)\n",cprestore_label);
     } else
	printf("\tjal %s\n",conv);
     lib_args(16);
}
#endif

#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,int label,int cond)
{
    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;
    } else {
	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;
    }
    jcond(label,cond);
    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)
{ 
    if (d) {
         code_lassign_gvar(e2,freg);  set_double(freg); return;
    }
    use_float(d,freg);
    code_ldf("s.s",fregister_name(freg),cadr(e2),
        get_ptr_cache(ncaddr(e2)));
}

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

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

void
code_dassign_dregister(int e2,int d,int freg) {
    if (d) {
        code_lassign_lregister(e2,freg); set_double(freg); return;
    }
    use_float(d,freg);
    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_D==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_builtin_fabsf(int e)
{
}
void
code_builtin_fabs(int e)
{
}
void
code_builtin_inff()
{
}
void
code_builtin_inf()
{
}

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      %s,-2147483648                  # 0x80000000\n",
	r0);
    printf("\ttrunc.w.s %s,%s,%s\n",fr1,fr0,r1);
    printf("\tmfc1    %s,%s\n",r1,fr1);
    printf("\tor      %s,%s,%s\n",r0,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("\tmtc1     %s,%s\n",r0,frn);
     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)
{ 
    if (d) {
         code_lrgvar(e2,freg);
         set_double(freg);
	 return;
    }
    use_float(d,freg);
    code_ldf("l.s",fregister_name(freg),cadr(e2),
        get_ptr_cache(ncaddr(e2)));
}


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 label,int cond)
{ 
    char *frn,*fr1;
    int g;
    use_float(d,reg);

    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);
    }
    jcond(label,cond);
}

void
code_cmp_drlvar(int e2,int reg,int d,int label,int cond)
{ 
    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);
    }
    jcond(label,cond);
}

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

int
drexpr(int e1, int e2,int l1, int op,int cond)
{
    int op1=0;
    if (!cond) {
	switch(op) {
	    case FOP+GT:
		return drexpr(e2,e1,l1,FOP+GE,1);
	    case FOP+GE:
		return drexpr(e2,e1,l1,FOP+GT,1);
	    case FOP+EQ:
		op=FOP+NEQ; break;
	    case FOP+NEQ:
		op=FOP+EQ; break;
	    case DOP+GT:
		return drexpr(e2,e1,l1,DOP+GE,1);
	    case DOP+GE:
		return drexpr(e2,e1,l1,DOP+GT,1);
	    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;
    }
    return l1;
}

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

int
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,COND_BRANCH);
	pcond(NEQ, cr0,e3h,regh,1,cond?l2:l1,COND_BRANCH);
        break;
    case LOP+UGT: case LOP+UGE:
	pcond(UGT, cr0,e3h,regh,1,cond?l1:l2,COND_BRANCH);
	pcond(NEQ, cr0,e3h,regh,1,cond?l2:l1,COND_BRANCH);
        break;
    case LOP+EQ:
	pcond(EQ, cr0,regh,e3h,0,cond?l2:l1,COND_BRANCH);
        break;
    case LOP+NEQ:
	pcond(EQ, cr0,regh,e3h,0,cond?l1:l2,COND_BRANCH);
        break;
    default:
        error(-1);
    }
    pcond(op%LOP,cr0,e3l,regl,cond,l1,COND_BRANCH);
    fwddef(l2);  
    if (cr0!=-1) free_register(cr0);
    emit_lpop_free(e3);
    return l1;
}

#endif
#if LONGLONG_CODE||FLOAT_CODE

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

#endif
#if LONGLONG_CODE

void
code_cmp_lregister(int reg,int label,int cond)
{
    use_longlong(reg);
    printf("\tor %s,%s,%s\n",
		lregister_name_low(reg),
		lregister_name_low(reg),
		lregister_name_high(reg));
    code_cmp_register(regv_l(reg),label,cond);
}

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

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

#endif
#if LONGLONG_CODE||FLOAT_CODE
void
code_lassign(int e2,int creg)
{
    use_longlong(creg);
    lstore(e2,creg);
}

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

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

    use_longlong(creg);
    crn_h = lregister_name_high(creg);
    crn_l = lregister_name_low(creg);
    lvar_intro(e2);
#if ENDIAN_L==0
    printf("\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);
    }
}

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(ncaddr(e1));
#if ENDIAN_L==0
    code_ldf("lw",crn_l,cadr(e1),r);
    code_ldf("lw",crn_h,cadr(e1)+SIZE_OF_INT,r);
#else
    code_ldf("lw",crn_h,cadr(e1),r);
    code_ldf("lw",crn_l,cadr(e1)+SIZE_OF_INT,r);
#endif
}

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

#endif
#if LONGLONG_CODE

static long long ll0 = 1LL;

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

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

void
code_lconst(int e1,int creg)
{
    use_longlong(creg);
#if ENDIAN_L==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);
}



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();
    int sreg = get_register();
    ch = lregister_name_high(reg);
    cl = lregister_name_low(reg);
    oh = register_name(sreg);
    ol = lregister_name_low(oreg);
    dh = lregister_name_high(dreg);
    dl = lregister_name_low(dreg);
    printf("\tmove %s,%s\n",oh,lregister_name_high(oreg));
    
    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(sreg);
    // 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();
    int sreg = get_register();
    ch = lregister_name_high(creg);
    cl = lregister_name_low(creg);
    oh = register_name(sreg);
    ol = lregister_name_low(oreg);
    dh = lregister_name_high(dreg);
    dl = lregister_name_low(dreg);
    printf("\tmove %s,%s\n",oh,lregister_name_high(oreg));

    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);
    free_register(sreg);
    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();
    int sreg = get_register();
    ch = lregister_name_high(reg);
    cl = lregister_name_low(reg);
    oh = register_name(sreg);
    ol = lregister_name_low(oreg);
    dh = lregister_name_high(dreg);
    dl = lregister_name_low(dreg);
    printf("\tmove %s,%s\n",oh,lregister_name_high(oreg));
    
    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);
    free_register(sreg);
    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);
            crn_h 7 = l32( crn_h * orn_l);  7, 3
	    drn_h 5 = drn_h + crn_h;  5, 7
            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",crn_h,crn_h,orn_l);
	printf("\taddu    %s,%s,%s\n",drn_h,drn_h,crn_h);
	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 = lcadr(e);
    } else if (car(e)==CONST) {
	if (!(-32766<cadr(e)&&cadr(e)<32767)) return 0;
	v = cadr(e);
    } else return 0;
    
    switch(op) {
    case LLSHIFT:
    case LULSHIFT:
    case LRSHIFT:
    case LURSHIFT:
	return  (0<v&&v<31);
    case LADD:
    case LSUB:
	return 1;
    case LBOR:
	return  (v>0);
    case LMUL:
    case LUMUL:
    case LUDIV:
    /* case LDIV: */
        return ilog(v);
    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);
    else { v=0; error(-1); }

    switch(op) {
    case LMUL: case LUMUL:
        v=ilog(v);
    case LLSHIFT:
    case LULSHIFT:
        if (v==0) return;
        if (v==32) {
            code_register(regv_l(creg),regv_h(creg));
            code_const(0,regv_l(creg));
            return;
        } else if (v>31) {
            printf("\tsll %s,%s,%d\n",crn_h,crn_l,v-32);
            code_const(0,regv_l(creg));
            return;
        }
	greg = get_register();
	grn = register_name(greg);
	printf("\tsll %s,%s,%d\n",crn_h,crn_h,v);
	printf("\tsrl %s,%s,%d\n",grn,crn_l,32-v);
	printf("\tor %s,%s,%s\n",crn_h,crn_h,grn);
	printf("\tsll %s,%s,%d\n",crn_l,crn_l,v);
	free_register(greg);
	return;
    case LRSHIFT:
        if (v==0) return;
        if (v==32) {
            code_register(regv_h(creg),regv_l(creg));
            printf("\tsra %s,%s,31\n",crn_h,crn_l);
            return;
        } else if (v>31) {
            printf("\tsra %s,%s,%d\n",crn_l,crn_h,v-32);
            printf("\tsra %s,%s,31\n",crn_h,crn_l);
            return;
        }
	greg = get_register();
	grn = register_name(greg);
	printf("\tsrl %s,%s,%d\n",crn_l,crn_l,v);
	printf("\tsll %s,%s,%d\n",grn,crn_h,32-v);
	printf("\tor  %s,%s,%s\n",crn_l,crn_l,grn);
	printf("\tsra %s,%s,%d\n",crn_h,crn_h,v);
	free_register(greg);
	return;
    case LUDIV:
        v=ilog(v);
    case LURSHIFT:
        if (v==0) return;
        if (v==32) {
            code_register(regv_h(creg),regv_l(creg));
            code_const(0,regv_h(creg));
            return;
        } else if (v>31) {
            printf("\tsrl %s,%s,%d\n",crn_l,crn_h,v-32);
            code_const(0,regv_h(creg));
            return;
        }
	greg = get_register();
	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,%d\n",crn_l,crn_l,v);
	break;
    default:
	error(-1);
    }
    if (dx!=-1) free_register(dx);
}

#endif
#if LONGLONG_CODE||FLOAT_CODE

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

#endif
#if LONGLONG_CODE

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 (use && 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);
	if (use) 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);
    if (reg!=lreg) free_register(reg);
}

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


#endif

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

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

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

#if CASE_CODE

int
code_table_jump_p(int delta) { return 1; }

void
code_table_jump(int l,int csvalue,int delta,int max,int min,int dlabel)
{
    int t,s;
    char *crn = register_name(csvalue);  // can be t or s
    char *trn = register_name(t=get_register());
    char *srn = register_name(s=get_register());

    printf("\taddu\t%s,%s,%d\n",trn,crn,-min);
    printf("\tsltu\t%s,%s,%d\n",srn,trn,max-min+1);
    printf("\tbeq\t%s,$0,$L_%d\n",srn,dlabel);
    switch(delta) {
    case 1: printf("\tsll %s,%s,2\n",trn,trn); break;
    case 2:
	printf("\tli\t%s,1\n",srn);
	printf("\tand\t%s,%s,%s\n",srn,trn,srn);
	printf("\tbne\t%s,$0,$L_%d\n",srn,dlabel);
	printf("\tsll %s,%s,1\n",trn,trn);
	break;
    case 4:
	printf("\tli\t%s,3\n",srn);
	printf("\tand\t%s,%s,%s\n",srn,trn,srn);
	printf("\tbne\t%s,$0,$L_%d\n",srn,dlabel);
	break;
    default:
	printf("\tdivu %s,%s,%d\n",trn,trn,delta);
	printf("\tmfhi %s\n",srn);
	printf("\tbne\t%s,$0,$L_%d\n",srn,dlabel);
	printf("\tsll %s,%s,2\n",trn,trn); 
    }
    printf("\tlw\t%s,$L_%d(%s)\n",trn,l,trn);
    printf("\t.cpadd %s\n",trn);
    printf("\tj %s\n",trn);

    free_register(s);
    free_register(t);
}

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

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

void
code_table_close()
{
    text_mode(4);
}

#endif


#if ASM_CODE

/*
    print an operand  
 */

static void
emit_asm_operand(int rstr)
{
    if (car(rstr)==REGISTER) {
	printf("%s",register_name(cadr(rstr)));
    } else if (car(rstr)==CONST) {
	printf("%d",cadr(rstr));
    } else if (car(rstr)==FNAME) {
	printf("%s",ncaddr(rstr)->nm);
    } else if (car(rstr)==LABEL) {
	printf("$L_%d",cadr(rstr));
    } else {
	error(-1);
    }
}

/*
     prepare asm operands

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

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

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

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

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


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

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

#endif


#if BIT_FIELD_CODE

/* bit field alignment calcuration */

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

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

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

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

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

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

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

    /* first bit-field */

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

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

/* bit field value */

extern void
code_bit_field(int type,int adr,int reg)
{
    int sign,bitsz,l,align;
    int bitsize,bitpos;
    int i,size;
    set_bitsz(type,&bitpos,&bitsize,&sign,&bitsz,&align,&l);
    size = bitsz/8;
// printf("## %d: bitpos=%d bitsize=%d bitsz=%d\n",lineno,bitpos,bitsize,bitsz);
    /* this implementation returns -1 for int i:1; */
    if (l==1) {
#if LONGLONG_CODE
	use_int(adr);
	use_longlong(reg);
        lload(adr,reg,0);
	/* shift left */
	if ((i=bitsz-bitsize-bitpos)) 
	    loprtc(LLSHIFT,reg,list2(CONST,i));
	/* shift right */
	if ((i=bitsz-bitsize)) 
	    loprtc(sign?LRSHIFT:LURSHIFT,reg,list2(CONST,i));
#endif
    } else {
	use_int(adr);
	use_int(reg);
	ld_indexx(size, 0, adr, reg, sign);
	/* shift left */
	if ((i=32-bitsize-bitpos)) 
	    oprtc(LSHIFT,reg,list2(CONST,i));
	/* shift right */
	if ((i=32-bitsize)) 
	    oprtc(sign?RSHIFT:URSHIFT,reg,list2(CONST,i));
    }
}

/* bit field replacement */

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

extern void
code_bit_replace(int adr,int value,int type)
{
    int sign,bitsz,l,align;
    int bitsize,bitpos;
    int mask = 0;
    int tmp = -1,lvalue,size;
    char *crn,*lrn,*trn;
    set_bitsz(type,&bitpos,&bitsize,&sign,&bitsz,&align,&l);
    size = bitsz/8;
// printf("## %d: bitpos=%d bitsize=%d bitsz=%d\n",lineno,bitpos,bitsize,bitsz);
    if (l) {
#if LONGLONG_CODE
	use_int(adr);
	lvalue = get_lregister();
        lload(adr,lvalue,0);
	use_longlong(value);
	crn = lregister_name_high(value);
	lrn = lregister_name_high(lvalue);
	/* shift left */
	if (bitpos) 
	    loprtc(LLSHIFT,value,list2(CONST,bitpos));
	trn = register_name(tmp = get_register());
	if (bitpos+bitsize>=32) {
	    /* make and-mask upper */
	    mask = make_mask(64-bitpos-bitsize,bitpos>=32?63-bitpos:31);
	    make_mask_and_or(mask,tmp,trn,crn,lrn);
	}
	crn = lregister_name_low(value);
	lrn = lregister_name_low(lvalue);
	if (bitpos<32) {
	    /* make and-mask lower */
	    mask = make_mask(bitpos+bitsize>=32?0:32-bitpos-bitsize,31-bitpos);
	    make_mask_and_or(mask,tmp,trn,crn,lrn);
	}
        code_lassign(adr,value);
#endif
    } else {
	use_int(adr);
	use_int(value);
	lvalue = get_register();
	ld_indexx(size, 0, adr, lvalue, sign);
	crn = register_name(value);
	lrn = register_name(lvalue);
	/* shift left */
	if (bitpos) 
	    oprtc(LSHIFT,value,list2(CONST,bitpos));
	trn = register_name(tmp = get_register());
	/* make and-mask */
	mask = make_mask(32-bitpos-bitsize,31-bitpos);
	make_mask_and_or(mask,tmp,trn,crn,lrn);
        code_assign(adr,size,value);
    }
    free_register(lvalue);
    if (tmp!=-1) free_register(tmp);
    if (use) {
       code_bit_field(type,adr,USE_CREG);
    }
}


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

extern void
code_bit_replace_const(int value,int adr,int type)
{
    int sign,bitsz,l,align;
    int bitsize,bitpos,size;
    int mask = 0;
    int c;
    int lvalue;
#if LONGLONG_CODE
    long long lc;
#endif
    char *crn;
    set_bitsz(type,&bitpos,&bitsize,&sign,&bitsz,&align,&l);
    size = bitsz/8;
// printf("## %d: bitpos=%d bitsize=%d bitsz=%d\n",lineno,bitpos,bitsize,bitsz);
    if (l) {
#if LONGLONG_CODE
        use_int(adr);
        lvalue = get_lregister();
        lload(adr,lvalue,0);
	crn = lregister_name_high(lvalue);
	/* shift left */
	lc = lcadr(value);
	lc <<= bitpos;
	if (bitpos+bitsize>=32) {
	    /* make and-mask upper */
	    mask = make_mask(64-bitpos-bitsize,bitpos>=32?63-bitpos:31);
	    make_mask_and_or_const(mask,crn,(int)(lc>>32));
	}
	crn = lregister_name_low(lvalue);
	if (bitpos<32) {
	    /* make and-mask lower */
	    mask = make_mask(bitpos+bitsize>=32?0:32-bitpos-bitsize,31-bitpos);
	    make_mask_and_or_const(mask,crn,(int)lc);
	}
        code_lassign(adr,lvalue);
#endif
    } else {
        use_int(adr);
        lvalue = get_register();
	crn = register_name(lvalue);
	ld_indexx(size, 0, adr, lvalue, sign);
	/* shift left */
	c = cadr(value);
	c <<= bitpos;
	/* make and-mask */
        mask = make_mask(32-bitpos-bitsize,31-bitpos);
	make_mask_and_or_const(mask,crn,c);
        code_assign(adr,size,lvalue);
      }
      free_register(lvalue);
      if (use) {
        code_bit_field(type,adr,USE_CREG);
      }
}


#endif

extern int 
code_arg_alignment(int args,NMTBL *n, int type0,int sz, int is_code)
{
    return code_arg_alignment0(args,n, type0,sz, is_code);
}


extern int 
code_lvar_alignment(int args,NMTBL *n, int type0,int sz)
{
    return code_lvar_alignment0(args,n, type0,sz);
}


/* end */