view mc-code-spu.c @ 935:7672a37e7545 default tip

Raspbery PI ARM support begin
author kono
date Sat, 24 Dec 2016 03:02:57 +0000
parents 35b339b36c88
children
line wrap: on
line source

/* Micro-C Code Generation Part for Cell SPU */
/* ************************************************************************
   ** Copyright (C) 2007 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"

char *l_include_path[]={
    "/usr/lib/gcc/spu/4.0.2/include",
        "/usr/spu/include",
        0};

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

static char *init_src0 = "\
#define __SPU__ 1\n\
#define __STDC_HOSTED__ 1\n\
#define __GNUC__ 4\n\
#define __FLT_MIN_EXP__ (-125)\n\
#define __DBL_MIN__ 2.2250738585072014e-308\n\
#define __NO_INLINE__ 1\n\
#define __ELF__ 1\n\
#define __FLT_RADIX__ 2\n\
#define __LDBL_EPSILON__ 2.2204460492503131e-16L\n\
#define __vector __attribute__((spu_vector))\n\
#define __WCHAR_TYPE__ int\n\
#define __DBL_EPSILON__ 2.2204460492503131e-16\n\
#define __INTMAX_MAX__ 9223372036854775807LL\n\
#define __FLT_DENORM_MIN__ 1.40129846e-45F\n\
#define __FLT_MAX__ 6.80564693e+38F\n\
#define __FLT_MIN_10_EXP__ (-37)\n\
#define __INTMAX_TYPE__ long long int\n\
#define __GNUC_MINOR__ 0\n\
#define __DBL_MAX_10_EXP__ 308\n\
#define __LDBL_DENORM_MIN__ 4.9406564584124654e-324L\n\
#define __PTRDIFF_TYPE__ long int\n\
#define __LDBL_MIN_10_EXP__ (-307)\n\
#define __LDBL_DIG__ 15\n\
#define __WINT_TYPE__ unsigned int\n\
";

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 cext(int sz, int sign, int from, int to);
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 void code_add(int reg,int offset,int r);
static void const_list_table();
//static int search_const(int tag,int value,int *label);
static char * cstore(int sz);
static void code_int_lib(char *lib,int reg,int oreg); 
static int caller_arg_offset_v(int arg);
static void pcond(int op, int r2,int r1,int r0,int cond,int l1,int mode);
#if FLOAT_CODE
static int code_d1(double d);
static int code_d2(double d);








#endif
static void use_input_reg(int reg,int mode);
static void ascii(char *s,int len);

static int creg;
static int output_mode = TEXT_EMIT_MODE;
static int register_save_return_label;

static int r1_offset_label;
static int lvar_offset_label;
static int    max_func_args = 0;
static int arg_on_register = 0;
static int max_func_arg_label = 0;

int code_lassop_p = 4;

#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 SIZE_OF_VECTOR  16
#define ENDIAN  0
#define ENDIAN_L  0
#define ENDIAN_D  1

int eval_order = NORMAL;

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


#define REG_ip   13
#define REG_fp   127
#define REG_sp   1
#define REG_VAR_BASE 3  
#define REG_VAR_MIN  3
#define REG_VAR_USER_MAX  (127-8)     /* at leat 6 tmp var */
#define REG_VAR_MAX (127-8)
#define MIN_TMP_REG 3
#define MAX_TMP_REG 126

#define FREG_VAR_BASE 3
#define FREG_VAR_MIN  3
#define FREG_VAR_MAX  REG_VAR_MAX
#define MIN_TMP_FREG 3
#define MAX_TMP_FREG 126

int MAX_REGISTER=127;             /* ARMのレジスタを10個まで使う*/
#define  REAL_MAX_REGISTER 128    /* ARMのレジスタが32ということ*/

#define FREG_OFFSET 3
#define LREG_OFFSET 3

int MAX_INPUT_REGISTER_VAR = 80;
int MAX_INPUT_DREGISTER_VAR = 80;
int MAX_INPUT_FREGISTER_VAR = 80;
int MAX_CODE_INPUT_REGISTER_VAR = 80;
int MAX_CODE_INPUT_DREGISTER_VAR = 80;
int MAX_CODE_INPUT_FREGISTER_VAR = 80;

#define LREG_V 3    /* mark for virtual long long/double register */
#define REGS_MAX (REAL_MAX_REGISTER)
static int spu_regs[REGS_MAX];
///   #define (i)  (i)
// #define (i)  (i)

#define RET_REGISTER 3
#define REGISTER_OPERAND  3     //this is first register for input
#define REGISTER_OPERAND_1  4
#define RET_FREGISTER FREG_OFFSET

static int *regs  = spu_regs;

#define CREG_REGISTER  REGISTER_OPERAND
#define CMP_C1T (-1)


static int max_reg_var;


static char *reg_name[] = {
        "$0", "$1", "$2", "$3", "$4", "$5", "$6", "$7", 
        "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", 
        "$16", "$17", "$18", "$19", "$20", "$21", "$22", "$23", 
        "$24", "$25", "$26", "$27", "$28", "$29", "$30", "$31", 
        "$32", "$33", "$34", "$35", "$36", "$37", "$38", "$39", 
        "$40", "$41", "$42", "$43", "$44", "$45", "$46", "$47", 
        "$48", "$49", "$50", "$51", "$52", "$53", "$54", "$55", 
        "$56", "$57", "$58", "$59", "$60", "$61", "$62", "$63", 
        "$64", "$65", "$66", "$67", "$68", "$69", "$70", "$71", 
        "$72", "$73", "$74", "$75", "$76", "$77", "$78", "$79", 
        "$80", "$81", "$82", "$83", "$84", "$85", "$86", "$87", 
        "$88", "$89", "$90", "$91", "$92", "$93", "$94", "$95", 
        "$96", "$97", "$98", "$99", "$100", "$101", "$102", "$103", 
        "$104", "$105", "$106", "$107", "$108", "$109", "$110", "$111", 
        "$112", "$113", "$114", "$115", "$116", "$117", "$118", "$119", 
        "$120", "$121", "$122", "$123", "$124", "$125", "$126", "$127"
};

#define register_name(i)  reg_name[i]

char *rn(int i) { return register_name(i); }

// input register mark

#define USING_DREG 5
#define INPUT_DREG 6

#if FLOAT_CODE

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

#define is_int_reg(reg)    (1)
#define is_float_reg(reg)    (1)
#define is_longlong_reg(reg)    (1)

#define  use_int(reg) if (reg==USE_CREG) reg=use_int0()
#define  use_float(d,reg) if (reg==USE_CREG) reg=use_int0()
#define  use_longlong(reg) if (reg==USE_CREG) reg=use_int0()

static int 
use_int0() {
    if (!creg) creg = get_register();
    return creg;
}

static void set_ireg(int reg,int mode);


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

static void code_save_input_registers(int dots);


static int max_func_args;
static int my_func_args;


static void jcond(int l, int cond);

#define ARG_LVAR_OFFSET 0x10000000

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

/*          

     r0    return value etc.
     r0,r1 return value. (dpcmp return value on $2)
     00  special register
     r0-r3  input register
     r7-r9 saved register variable (input register for code segment)
         jump register
     sp    stack pointer
     fp    frame pointer

     f0       return value etc.
     r0-r3    input register
     f20-f31 saved register variable

code segment stack frame

register_save is done by callee

                     (prev $sp)
                            <---lvar_offset-->
   #                       $fp <--------------------r1_offset---------> $sp
r+ +----------+-------------+--------+-------+-----------+---------------+
    cousin arg!  callee arg !reg_save!reg_arg!local       caller arg 
              (arg4..)lvar>0         (arg0.3) lvar<0      lvar>0x1000 000
               <--------------my_func_args--><--disp-----><-max_func_arg->
                              *SIZE_OF_INT                  *SIZE_OF_INT
                 prev $sp=$fp                 $fp                        $sp

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

#define arg_offset (16)
#define arg_offset1 (-64)
int disp_offset=0;    // fore mc-codegen.c
#define disp_offset  0

#define code_disp_offset 0

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

static int
code_offset_set(NMTBL *fnptr)
{
    int lvar_offsetv;
    int r1_offsetv;
    int code_f = is_code(fnptr);

    disp &= -SIZE_OF_INT;

    if (code_f) {
        r1_offsetv = disp-max_func_args*SIZE_OF_INT+code_disp_offset;
        printf("\t.set .LC%d, %d\n",r1_offset_label,r1_offsetv);
    } else {
        lvar_offsetv = 
            -(-32 - max_reg_var*SIZE_OF_INT-max_reg_var*SIZE_OF_DOUBLE);
        printf("\t.set .LC%d, %d\n",lvar_offset_label,lvar_offsetv);
    }
    if (max_func_arg_label) {
        printf("\t.set .LC%d, %d\n",max_func_arg_label,max_func_args*SIZE_OF_INT);
        max_func_arg_label = 0;
    }

#if 0
printf("## vars= %d, regs= %d/%d, args= %d, extra= %d\n",
        round16(-disp),
        max_reg_var+2,
        max_reg_var,
        round16(max_func_args*SIZE_OF_INT),
        0
);
   printf("## callee arg top=\t%d\n",CALLEE_ARG(0));
   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_reg_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

    return 0;
}

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

static void lvar_address(int l,int creg);

static int large_lvar;
static void
lvar_intro(int l)
{
    int large;
    if (is_code(fnptr)) {
        if (l>=ARG_LVAR_OFFSET) {
            large = LARGE_OFFSET(CODE_CALLER_ARG(l-ARG_LVAR_OFFSET));
        } else
            large = LARGE_OFFSET(CODE_LVAR(l));
    } else if (l<0) {  /* local variable */
            large = LARGE_OFFSET(FUNC_LVAR(l));
    } else if (l>=ARG_LVAR_OFFSET) {  /* caller's arguments */
            large = LARGE_OFFSET(CALLER_ARG(l-ARG_LVAR_OFFSET));
    } else { /* callee's arguments */
            large = LARGE_OFFSET(CALLEE_ARG(l));
    }
    if (large) {
        large_lvar = get_register();
        lvar_address(l,large_lvar);
    } else {
        large_lvar = 0;
    }
}

static void
lvar(int l,char *cext)
{
    if (large_lvar) {
        printf("0(%s)\n",register_name(large_lvar));
        free_register(large_lvar);
        return;
    }
    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($sp)\n",CODE_LVAR(l));
        }
   } else if (l<0) {  /* local variable */
        printf(".LC%d+%d($sp)\n",lvar_offset_label,FUNC_LVAR(l));
    } else if (l>=ARG_LVAR_OFFSET) {  /* caller's arguments */
        printf("%d($sp)%s\n",CALLER_ARG(l-ARG_LVAR_OFFSET),cext);
    } else { /* callee's arguments */
        printf("%d($sp)%s\n",CALLEE_ARG(l),cext);
    }
}

static void
lvar_address(int l,int creg)
{
    //int label,disp;

    int tmp = -1;
    char *trn;
    if (is_code(fnptr)) {
        if (l>=ARG_LVAR_OFFSET) {  /* caller's arguments */
            code_add(creg,CODE_CALLER_ARG(l-ARG_LVAR_OFFSET),REG_sp);
        } else
            code_add(creg,CODE_LVAR(l),REG_fp);
    } else if (l<0) {  /* local variable */
        trn = register_name(tmp = get_register());
        printf("\tlqd\t%s, 64($sp)\n",trn);
        printf("\ta\t%s, $sp, %s\n",register_name(creg),trn);
    } else if (l>=ARG_LVAR_OFFSET) {  /* caller's arguments */
        code_add(creg,CALLER_ARG(l-ARG_LVAR_OFFSET),REG_sp);
    } else { /* callee's arguments */
        code_add(creg,CALLEE_ARG(l),REG_fp);
    }
    if (tmp!=-1) free_register(tmp);
}


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


void
code_init(void)
{
    /* called only once */

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

}

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

    // SPU では ptr cache は使わない方が良い?
    init_ptr_cache();
    output_mode = -1;
}



void
gexpr_code_init(void){
}

void
code_gexpr(int e){
}


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++;
                caddr(args)=SIZE_OF_INT;
            }
        } else if (type==FLOAT) {
            if (is_function(fnptr)) {
                if ((reg = get_input_dregister_var(reg_var,n,is_code0,0))) {
                    n->sc = REGISTER;
                    n->dsp = cadr(reg);
                    regs[n->dsp]= INPUT_REG;
                    reg_var++;
                    caddr(args)=size(type);
                }
            } else {
                if ((reg = get_input_dregister_var(freg_var,n,is_code0,0))) {
                    n->sc = FREGISTER;
                    n->dsp = cadr(reg);
                    regs[n->dsp]= INPUT_REG;
                    freg_var++;
                    caddr(args)=size(type);
                }
            }
        } else if (type==DOUBLE) {
            if ((reg = get_input_dregister_var(reg_var,n,is_code0,1))) {
                n->sc = LREGISTER;
                n->dsp = cadr(reg);
                regs[i=n->dsp]= INPUT_DREG;
                regs[i]= INPUT_REG;
                //regs[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[i]= INPUT_REG;
                //regs[(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;   /* その場所を表す番号を返す */
    }
    for(i=0;i<REG_VAR_MAX-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;   /* その場所を表す番号を返す */
        }
    }
    /* 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)*4),reg,0); 
            reg_stack[i]= j-REG_LVAR_OFFSET;
                return reg;
        }
    }
    for(i=0;i<REG_VAR_MAX-REG_VAR_MIN;i++) {
        reg =REG_VAR_BASE+i;
    }
    /* あいている場所がないなら、エラー(いったい誰が使ってるの?) */
    error(RGERR); return creg;
}

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

#if FLOAT_CODE
int 
get_dregister(int d)
{    /* 使われていないレジスタを調べる */
    return get_register();
}


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

// static int lreg_count;
int
get_lregister0()
{
        return get_register();
}

int
get_lregister()
{
        return get_register();
}

int
get_lregister_var(NMTBL *n)
{
        return get_register_var(n);
}

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

void

free_register(int i) {    /* いらなくなったレジスタを解放 */
    regs[i]=0;
}

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

int
get_input_dregister_var(int i,NMTBL *n,int is_code,int d)
{
    int j;
    if (is_code) {
        if(i>MAX_CODE_INPUT_DREGISTER_VAR) return 0;
        i = FREG_VAR_BASE+i+FREG_OFFSET;
        use_input_reg(i,1);
        return list3n(FREGISTER,i,n);
    }
    if (d) {
        j = get_input_lregister_var(i,n,is_code);
        return j;
    } else {
        if (i==0) return list3n(REGISTER,1,n);
        else if (i==1) return list3n(REGISTER,2,n);
        else if (i==2) return list3n(REGISTER,3,n);
        else if (i==3) return list3n(REGISTER,4,n);
        else return 0;
    }
}

int
get_input_lregister_var(int i,NMTBL *n,int is_code)
{
        return get_input_register_var(i,n,is_code);
}

int
get_input_register_var(int i,NMTBL *n,int is_code)
{
    if (is_code) {
        if(!(i<REG_VAR_MAX-REG_VAR_MIN)) return 0;
        i = REG_VAR_BASE+i;
        use_input_reg(i,1);
    } else {
        if (i<0||i>=MAX_INPUT_REGISTER_VAR) 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_REGISTER;i++) {
        if (! regs[i+FREG_OFFSET]) fcount++;
    }
    printf("## free reg %d freg %d\n",count,fcount);
    return d?fcount:count;
}


void
free_all_register(void)
{
    int i;
    for(i=0;i<MAX_REGISTER;i++) { regs[i]=0; }
    creg = get_register();
    set_ireg(CREG_REGISTER,0);
    return;
}


extern int
code_register_overlap(int s,int t)
{
    switch(car(s)) {
    case REGISTER: case FREGISTER: case DREGISTER: case LREGISTER:
        switch(car(t)) {
            case REGISTER: case FREGISTER: case DREGISTER: case LREGISTER:
            return cadr(s)==cadr(t);
        }
    }
    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 1
    for(j=0,i=1;i<MAX_REGISTER;i++) if (regs[i]) j++;
    if (j>USAGE_MAX) {
        // printf("\n# regs:01234567890123456789012");
        printf("\n# regs:");
        for(i=1;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]);
        }
    }
#endif
    printf("\n");
}


void
gexpr_init(void)
{
    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_reg_var=0;
    reg_sp = 0;
    reg_sp = 0;
}

#define reg_var_num(i) (REG_VAR_BASE+i)

int
get_register_var(NMTBL *n)
{
    int i,j;
    int max = n?REG_VAR_USER_MAX-REG_VAR_BASE:REG_VAR_MAX-REG_VAR_BASE;
    for(i=0;i<max;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),n);
}

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

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

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 (new_reg==creg) error(-1);   /* who freed creg? */
    reg_stack[reg_sp++] = creg;     /* push するかわりにレジスタを使う */
    creg = new_reg;
    if (!regs[creg]) regs[creg]=USING_REG;
    return old;
}

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

static int const_list;
static int const_list_label;
static int prev_const_list;
static int prev_const_list_label;


extern void
code_ptr_cache_def(int r, NMTBL *nptr)
{
}

static int
is_stage1_const(int c,int mode)
{
    return mode?(0<=c && c<262143): (-32768<=c && c<32767);
}

extern void 
code_const(int e2,int reg) {
    char *crn;
        int r;
        //code_const0(e2,reg,"");
        //      printf("code_const\n");
        /************************************************************
         * ここでp1の値によって、命令が異なる......                 *
         * il命令は-32768~32767                                     *
         * ila命令は262143まで                                      *
         * それ以上はilhuとiohlを混合して使う。                     *
         * example:15728639                                         *
         *     15728639/16^4 = 239             ilhu  $4,239         *
         *     15728639%16^4 = 65535           iohl  $4,65535       *
         *負の場合も異なる                                          *
         * example : -99999  fffe7961                               *
         *     r = ~(-99999)   r = (r >> 16) & 0xffff; r += 1; r=-r;*       
         *     ilhu     $4,-2                                       *
         *     -99999 & 0xfff       iohl     $4,31073    -> 0x7961  *
         * example : -15728639  ff100001                            *
         ***********************************************************/
        use_int(reg);
        crn = register_name(reg);
        if (-32768<e2&&e2<32768)
                printf("\til %s,%d\n",crn,e2);
        else if(32767<e2&&e2<262144)
                printf("\tila %s,%d\n",crn,e2);
        else if(262143<e2) {
                printf("\t ilhu\t%s,%d\n",crn,e2/65536);
                printf("\t iohl %s,%d\n",crn,e2%65536);
        } else {
                r = (~e2 >> 16) & 0xffff;
                r += 1;
                r = -r;
                printf("\tilhu\t%s,%d\n",crn,r);
                if((e2&0xffff) > -262143)
                        printf("\tiohl %s,%d\n",crn, (e2&0xffff));
                else
                        printf("\tori %s,%s,%d\n",crn,crn,(e2&0xffff));
        }
}

static void 
code_add(int reg,int offset,int r)
{
    char *crn = register_name(reg);
    char *rrn = register_name(r);
        // char *rn2 = register_name(r+1);
        char *drn;
    int dreg;
    if (offset==0) {
        if(r!=reg)
            printf("\tori\t%s, %s, 0\n",crn,rrn);
    } else if (-32768<offset&&offset<32768)
        printf("\tai\t%s, %s, %d\n",crn,rrn,offset);
    } else {
        drn = register_name(dreg = get_register());
        code_const(offset, dreg);
        printf("\ta\t%s, %s, %s\n",crn,drn,rrn);
        free_register(dreg);
    }
}


static void 
code_ld(char *ld,int reg,int offset,int r)
{
    char *crn = register_name(reg);
    char *rrn = register_name(r);
    if (-1024<offset&&offset<1024) {
        printf("\t%s\t%s, %d(%s)\n",ld,crn,offset,rrn);
    } else {
        code_add(reg,offset,r);
        printf("\t%s\t%s, 0(%s)\n",ld,crn,crn);
    }
}

/*

store
        lqd     $2,176($sp)
        cbd     $3,0($sp)
        shufb   $2,$4,$2,$3
        stqd    $2,176($sp)
unsigned load
        rotqbyi $2,$2,13
        andi    $4,$2,0x00ff

 */



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

void
code_gvar(int e1,int reg) {
    use_int(reg);
    printf("\tila\t%s,%s",register_name(reg),ncaddr(e1)->nm);
}

void
code_rgvar(int e1,int reg) {
    code_crvar(e1,reg,0,SIZE_OF_INT);
}

void
code_crgvar(int e1,int reg,int sign,int sz){
    use_int(reg);
    printf("\tlqr\t%s,%s",register_name(reg),ncaddr(e1)->nm);
    cext(sz, sign, reg, reg);
}


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


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

extern void
code_i2c(int reg)
{
}

extern void
code_i2s(int reg)
{
}

extern void
code_u2uc(int reg)
{   
    use_int(reg);
    printf("\tandbi\t%s, %s, 255\n",register_name(reg),register_name(reg));
}

extern void
code_u2us(int reg)
{  
        //   &!  ということらしい、だけど16711680とかは使えない。よってilhuとかで16711680をレジスタに格納して
        //   and  rt,rs,ra   でandを行えば良いが、!はどうすんだ?
    use_int(reg);
    //printf("bic\t%s, %s, #16711680\n",register_name(reg),register_name(reg));
    printf("\tilhu\t%s,255\n", register_name(reg+1));
        printf("\tand\t%s,%s,%s\n",register_name(reg),register_name(reg),register_name(reg+1));
        //printf("bic\t%s, %s, #-16777216\n",register_name(reg),register_name(reg));
    printf("\tilhu\t%s,-256\n", register_name(reg+1));
    printf("\tand\t%s,%s,%s\n",register_name(reg),register_name(reg),register_name(reg+1));
}

static void
cext(int sz, int sign, int from, int to)
{
    if (sz==1 && !sign) {
// unsigned char
        printf("        rotqbyi %s,%s,13\n",fn,fn);
        printf("        andi    %s,%s,0x00ff\n",tn,fn);
    } else if (sz==1 && sign) {
// signed char
        printf("        rotqbyi %s,%s,13\n",fn,fn);
        printf("        xsbh    %s,%s\n",fn,fn);
        printf("        xshw    %s,%s\n",tn,fn);
    } else if (sz==2 && !sign) {
// unsigned short
        int t = get_register();
        char *tmp = register_name(t);
        printf("        rotqbyi %s,%s,14\n",fn,fn);
        printf("        ori     %s,%s,0\n",tmp,fn);
        printf("        ila     %s,65535\n",fn,fn);
        printf("        and     %s,%s,%s\n",tn,tmp,fn);
        free_register(t);
    } else if (sz==2 && !sign) {
// singed short
        printf("        rotqbyi %s,%s,14\n",fn,fn);
        printf("        xshw    %s,%s\n",tn,fn);
    }
}

static void
shuffle_offset(int sz, int offset, int base, int to)
{
    int tmp = get_register();
    char *bn = register_name(base);
    char *tn = register_name(tmp);

    if (sz==1) {
        printf("        cbd     %s %d(%s)\n",tn,offset, bn);
        printf("        shufb   %s,%s,%s,%s\n",tn,fn,tn);
    } else if (sz==1 && sign) {
// signed char
        printf("        rotqbyi %s,%s,13\n",fn,fn);
        printf("        xsbh    %s,%s\n",fn,fn);
        printf("        xshw    %s,%s\n",tn,fn);
    } else if (sz==2 && !sign) {
// unsigned short
        int t = get_register();
        char *tmp = register_name(t);
        printf("        rotqbyi %s,%s,14\n",fn,fn);
        printf("        ori     %s,%s,0\n",tmp,fn);
        printf("        ila     %s,65535\n",fn,fn);
        printf("        and     %s,%s,%s\n",tn,tmp,fn);
        free_register(t);
    } else if (sz==2 && !sign) {
// singed short
        printf("        rotqbyi %s,%s,14\n",fn,fn);
        printf("        xshw    %s,%s\n",tn,fn);
    }
    free_register(tmp);
}


void
code_crlvar(int e2,int reg,int sign,int sz) {
    use_int(reg);
    int tmp = 0;
    if (size<4)
       tmp = get_register();
    lvar_intro(e2);
    printf("\tlqd\t%s,",register_name(size<4?tmp:reg));
    lvar(e2);
    cext(sign,sz,reg,tmp);
    if (tmp) free_register(tmp);
}

void
code_fname(NMTBL *n,int reg) {
    use_int(reg);
    printf("\tila\t%s, %s\n",register_name(reg),n->nm);
    return;
}

void
code_label_value(int label,int reg) {
    use_int(reg);
    printf("\tila\t%s,.LC%d\n",register_name(reg),label);
    return;
}

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


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


void
code_lnot(int creg) {
    use_int(creg);

    printf("\tceqi\t%s,%s,0\n",register_name(creg),register_name(creg));
}

void
code_preinc(int e1,int e2,int dir,int sign,int sz,int reg) {
    char *xrn,*drn;
    int xreg;
    if (car(e2)==REGISTER) {
        use_int(reg);
        code_add(cadr(e2),dir,cadr(e2));
        if (reg!=cadr(e2))
            code_register(cadr(e2),reg);
        return;
    } 
    g_expr(e2);
    if (!is_int_reg(creg)) error(-1);
    xrn = register_name(xreg = 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);
    }
    code_ld(cload(sz,sign),reg,0,xreg);
    code_add(reg,dir,reg);
    code_ld(cstore(sz),drn,0,xreg);
}


void
code_postinc(int e1,int e2,int dir,int sign,int sz,int reg) {
    char *xrn,*crn,*nrn;
    int nreg,xreg;
    if (car(e2)==REGISTER) {
        use_int(reg);
        code_register(cadr(e2),reg);
        code_add(cadr(e2),dir,cadr(e2));
        return;
    } 
    g_expr(e2);
    if (!is_int_reg(creg)) error(-1);
    crn = register_name(xreg=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);
    }
    code_ld(cload(sz,sign),reg,0,xreg);
    code_add(nreg,dir,reg);
    code_ldf(cstore(sz),nrn,0,xreg);

    free_register(nreg);
}


void
code_return(int creg) {
    char *crn;
    use_int(creg);
    crn = register_name(creg);

}

void
code_environment(int creg) {
    /* save frame pointer */
    if (is_code(fnptr)) {
        use_int(creg);
        printf("\tori\t%s, $sp, 0\n",register_name(creg),trn);
    } else {
        //int disp,label;
        use_int(creg);
        printf("\tori\t%s, $sp, 0\n",register_name(creg),trn);
    }
}

static int rexpr_bool(int e1, int reg);

void
code_bool(int e1,int reg) {
    int e2,e3;
    char *xrn;
    if (rexpr_bool(e1, reg)) return;
    if (use) {
        use_int(reg);
        xrn = register_name(reg);
        printf("\tceqi\t%s, %s, 0\n",xrn,xrn);
        printf("\tnor\t%s, %s, %s\n",xrn,xrn,xrn);
        printf("\tsfi\t%s, %s, 0\n",xrn,xrn);
    }
}


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);
    printf("\tceqi\t%s, %s, 0\n",register_name(reg), register_name(reg));
    jcond(label,cond);
}


void
code_cmp_crlvar(int e2,int reg, int sz,int label,int cond) {
    char *crn;
    use_int(reg);
    crn = register_name(reg);
    lvar_intro(e2);
    printf("\t%s\t%s,hoge2 ",cload(sz,0),crn);
    lvar(e2,cext_at(sz,0));
    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_crvar(e1,reg,0,SIZE_OF_INT);
    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("\tlqd\t%s, ",crn);
    lvar(e2,"");
    code_cmp_register(reg,label,cond);
}


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


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

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

    s=n->nm;
    lb = emit_string_label();
    ascii(s,n->dsp);
    text_mode(2);

    printf("\tila\t%s, .LC%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),car(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),car(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 dreg = REG_ip; 

    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("\tlqd\t%s, %d(%s)\n",drn,(offset*4),trn);
        printf("\tstqd\t%s,%d(%s)\n",drn,(offset*4),trn);
        break;
    case 2: case -2:
        printf("\tlqd\t%s, %d(%s)\n",drn,(offset*4),trn);
        printf("\tstqd\t%s,%d(%s)\n",drn,(offset*4),trn);
        break;
    case 4: case -4:
        printf("\tlqd\t%s, %d(%s)\n",drn,(offset*4),trn);
        printf("\tstqd\t%s,%d(%s)\n",drn,(offset*4),trn);
        break;
    default:
        if (length <0) {
            if (length > -MAX_COPY_LEN) {
                for(;length<=-4;length+=4,offset-=4)
                    emit_copy(from,to,-4,offset-4,0,det);
                for(;length<=-2;length+=2,offset-=2)
                    emit_copy(from,to,-2,offset-2,0,det);
                if(length<0)
                    emit_copy(from,to,length,offset-1,0,det);
                break;
            }
        } else if (length <=MAX_COPY_LEN) {
            for(;length>=4;length-=4,offset+=4)
                emit_copy(from,to,4,offset,0,det);
            for(;length>=2;length-=2,offset+=2)
                emit_copy(from,to,2,offset,0,det);
            if(length>0)
                emit_copy(from,to,length,offset,0,det);
            break;
        }
        clear_ptr_cache();
        code_save_stacks();
        parallel_rassign(list3(1,list3(2,0,from),to));
        code_const(length>0?length:-length,3);
        /* overlap must be allowed */
        // offset have to be ignored */
        //printf("\thbra\t%s\n",memmove);
        extern_define(memmove,0,FUNCTION,1);
        set_ireg(RET_REGISTER,0);
        if (creg!=to) {
            free_register(to); to = creg;
        }
        break;
    }
    if (value) {
    /* creg must point top of the destination data */
    /* this code is necessary for the value of assignment or function call */
    /* otherwise we don't need this */
        if(creg!=to) {
            free_register(creg); creg=to; 
        }
    }
    // free_register(dreg);
}

int
push_struct(int e4,int t,int arg)
{
    int length,len0,count;
    int dreg = -1,sreg; char *drn,*crn,*srn;
    int arg_disp = cadr(arg);
    int on_register,arg_reg;
    g_expr(e4);
    if (!is_int_reg(creg)) error(-1);
    len0=length=size(t);
    if(length%SIZE_OF_INT) {
        length += SIZE_OF_INT - (length%SIZE_OF_INT);
    }
    crn = register_name(creg);
    on_register = 0;
    arg_reg = (arg_disp-ARG_LVAR_OFFSET)/SIZE_OF_INT + 1;
    while (length>0 && 
             arg_disp>=ARG_LVAR_OFFSET && 
             CALLER_ARG(arg_disp-ARG_LVAR_OFFSET)<0) {
        /* some part will be on registers */
        on_register ++; 
        length-=SIZE_OF_INT; arg_disp+= SIZE_OF_INT;
    }
    if (length>0) {
        dreg = get_register(); if (!dreg) error(-1);
        drn = register_name(dreg);
        if (length<MAX_COPY_LEN) {
            sreg = get_register(); if (!sreg) error(-1);
            srn = register_name(sreg);
            code_lvar(arg_disp,dreg);
            for(count=0;count<length;count+=SIZE_OF_INT) {
                printf("\tlqd\t%s, %d(%s)\n",srn,count+on_register*SIZE_OF_INT,crn);
                printf("\tstqd\t%s, %d(%s)\n",srn,count,drn);
            }
            free_register(sreg);
            if (on_register) {
                if (creg<=MAX_INPUT_REGISTER_VAR) {
                    code_register(creg,REG_ip);
                    crn = register_name(REG_ip);
                }
            }
        } else {
            code_lvar(arg_disp,dreg);
            /* downward direction copy */
            if (on_register) {
                sreg = new_lvar(SIZE_OF_INT);
                code_assign_lvar(sreg*4,creg,0);
                code_add(creg,on_register*SIZE_OF_INT,creg);
                emit_copy(creg,dreg,length,0,0,1);
                code_rlvar(sreg,REG_ip);
                crn = register_name(REG_ip);
                free_lvar(sreg);
            } else {
                emit_copy(creg,dreg,length,0,0,1);
            }
        }
        if (dreg!=-1) free_register(dreg);
    }
    for (count=0,arg_reg; on_register-->0; arg_reg++,count+=SIZE_OF_INT) {
        // len0 = (len0>2)?0:len0;
        printf("\t%s\t%s, %d(%s)\n", cload(0,0),
                register_name(arg_reg), count,crn);
        use_input_reg(arg_reg,1);
    }
    return length/SIZE_OF_INT;
}

static void
set_ireg(int reg,int mode)
{
    if (reg!=creg) {
        clear_ptr_cache_reg(reg);
        if (creg && reg!=creg ) {
            if (mode) {
                printf("\tori\t%s, %s, 0\n",
                    register_name(reg),register_name(creg));
            }
        }
        free_register(creg);
        regs[reg]=USING_REG;
    }
    creg = reg;
}

void
use_reg(int arg)
{
    if (arg<0||arg> REGS_MAX)
        error(-1);
    clear_ptr_cache_reg(arg);
    regs[arg]=USING_REG;
}

void
code_save_input_registers(int dots)
{
    int args;
    NMTBL *n;
    int reg;
    int tag;
    /* fnptr->dsp=list4(type,fnptr->dsp,(int)n,0); */
    int offset = 0;
    int reg_var = 0;
    int len;
    arg_on_register = 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 (reg_var<MAX_INPUT_REGISTER_VAR) {
            n->dsp = offset;
            n->sc  = LVAR;
            len = size(n->ty); len = round4(len);
            for(;len>0 && reg_var<MAX_INPUT_REGISTER_VAR;len-=SIZE_OF_INT) {
                reg_var++;
                g_expr_u(assign_expr0(list3n(LVAR,offset,0),
                        list3(REGISTER,reg_var,0),INT,INT));
                arg_on_register += SIZE_OF_INT;
                free_register(reg);
                offset += SIZE_OF_INT;
            }
        }
    }
    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++;
            arg_on_register += SIZE_OF_INT;
        }
    }
    my_func_args = offset;
}

int
not_simple_p(int e3)
{
    switch(e3) {
        case FUNCTION: case CONV: case STASS: case ALLOCA:
        case DIV : case UDIV : case MOD : case UMOD :
        case LDIV: case LUDIV: case LMOD: case LUMOD:
        case LMUL: case LUMUL:
        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 RBIT_FIELD: case BASS: case BASSOP: case LCALL:
        case INLINE:
        return 1;
    }
    return 0;
}

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

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

/* 
     Mark argument register is used. This is too complex.
     There must be an easy way.
 */

static void
use_input_reg(int reg,int mode)
{
    if (creg&&reg == creg) {
        creg = 0;
    } 
    if (mode) use_reg(reg);
}

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

static void
increment_function_arg(int e3,int *pnargs,int *preg_arg,int *pfreg_arg) {
    int nargs=0,reg_arg=0;

    nargs ++ ; reg_arg++; 
    *pnargs += nargs;
    *preg_arg += reg_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 (mode==AS_SAVE) {
            return get_lregister_var(0);
        } else if (reg_arg>=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 (reg_arg>=MAX_INPUT_REGISTER_VAR) {
            return list3n(LVAR,caller_arg_offset_v(nargs),0);
        } else
            return get_input_dregister_var(freg_arg,0,0,0);
    } else if (t==DOUBLE) {
        if (mode==AS_SAVE) {
            return get_dregister_var(0,1);
        } else if (reg_arg>=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)) {
        // 16byte もあるんだから、ほとんどの構造体は、レジスタに
        // 乗ってしまうらしい。なので、レジスタに乗っている前提の
        // コードを書いた方が良いらしい...
        // でも、strop を書き直さないと出来ない
        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)
{
    if (car(e2) == FNAME) {
        printf("\tbrsl\t$lr,%s\n",fn->nm);
    } else {
        //printf("\tmov\tlr, pc\n");
        printf("\tori\t$sp,%s,0\n",register_name(cadr(jmp)));
        printf("\tori\t$sp, %s,0\n",register_name(cadr(jmp)));
    }
}

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

    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 = get_register_var(0);
        jmp = list2(REGISTER,REG_ip);
        if (!simple_arg(e2)) {
            e3=get_register_var(0);
            reg_arg_list = list2(e3,reg_arg_list);
            g_expr_u(assign_expr0(e3,e2,INT,INT));
            e2=e3;
        }
        reg_arg_list = list2(jmp,reg_arg_list);
        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;
    pnargs = preg_arg = pfreg_arg = 0;
    for (e3 = e1 = reverse0(caddr(e1)); e3; e3 = cadr(e3)) {    
        t=caddr(e3);
        if (reg_arg==3 && (t==DOUBLE||t==LONGLONG||t==ULONGLONG)) {
            half_register=1;
        }
        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);
            }
            // memorise last complex arg parameter
            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 (!simple_arg(cadr(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_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)   // wrong?
                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);
            if (car(arg)!=LVAR) use_input_reg(cadr(arg),1);
            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
    }
    if (half_register) {
        //  half register case writes *(sp-1) but it will be Ok.
        if (max_func_args<4) max_func_args=4;
        g_expr_u(assign_expr0(list3(REGISTER,4,0),
                list3n(LVAR,caller_arg_offset_v(3),0),INT,INT));
        use_input_reg(4,1);
    }
    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)==REGISTER) {
                        use_input_reg(cadr(arg),1);


            } else {
                g_expr_u(assign_expr0(arg,e4,t,t));
            }
        } else if (t==FLOAT) {
            reg_arg_list = list2(arg,reg_arg_list);
            if (car(arg)==REGISTER) {
                use_input_reg(cadr(arg),1);/* protect from input register free */

            } 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);
    for(i=1;i<MAX_INPUT_REGISTER_VAR;i++) {
        free_register(i);
    }
    if (ret_type!=VOID) {
        set_ireg(RET_REGISTER,0);
        use_reg(RET_REGISTER);
    } else {
    }
    return ret_type;
}

void
code_alloca(int e1,int reg)
{
    char *crn;
  
    g_expr(list3(BAND,list3(ADD,e1,list2(CONST,15)),list2(CONST,~15))); 
    use_int(reg);
    crn = register_name(reg);
    //printf("\tsfi\t$sp, %s, $sp\n",crn);
    if (!max_func_arg_label) max_func_arg_label = fwdlabel();
    code_label_value(max_func_arg_label ,REG_ip);
    printf("\ta\t%s, $sp, $sp\n",crn);
}

void
code_frame_pointer(int e3) {
    use_int(e3);
    printf("\tori\t$sp,%s\n",register_name(e3));
}

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

void
code_fix_frame_pointer(int env) {
    char *trn;
    if (is_function(fnptr) && ! env) {
        trn = register_name(REG_ip);
        printf("\ta\t$sp, $sp, %s\n",trn);
    }
}

static void
code_unfix_frame_pointer()
{
    char *trn;


    trn = register_name(REG_ip);
    printf("\tori\t$sp, $sp, %s\n",trn);
}


void
code_jmp(char *s) {
    // jump to continuation means use all register variable
    max_reg_var = REG_VAR_MAX-REG_VAR_MIN;
    max_reg_var = FREG_VAR_MAX-FREG_VAR_MIN;
    printf("\tbr\t%s\n",s);
    control=0;
}

void
code_indirect_jmp(int e2) {
    // jump to continuation means use all register variable
    max_reg_var = REG_VAR_MAX-REG_VAR_MIN;
    max_reg_var = FREG_VAR_MAX-FREG_VAR_MIN;
    use_int(e2);
    //printf("\tmov\tpc, %s @ indirect jump\n",register_name(e2)); // ?!
    printf("\tbi\t%s\n",register_name(e2)); // ?!
    control=0;
}

void
code_rindirect(int e1, int reg,int offset, int sign,int sz)
{
    int lreg;
    g_expr(e1);
    if (!is_int_reg(creg)) error(-1);
    lreg = creg;
    use_int(reg);
    code_ld(cload(sz,sign),reg,offset,lreg);
    cext(sign,sz,reg);
}

#if FLOAT_CODE
int
code_drindirect(int e1, int reg,int offset, int d)
{
    int xreg;
    g_expr(e1);
    if (!is_int_reg(creg)) error(-1);
    xreg = creg;
    use_float(d,reg);
    code_ld("lqd",reg,offset,xreg);
    return d?DOUBLE:FLOAT;
}
#endif

#if LONGLONG_CODE||FLOAT_CODE

static void
lload(int creg,int reg,int offset) 
{
    char *crn=register_name(creg);
    printf("\tlqd\t%s, %d(%s)\n",register_name(reg),offset,crn);
}


static void
lmove(int to,int from)
{
    printf("\tori\t%s, %s, 0\n",
        register_name(to),register_name(from));


}

static void
set_operands(int r0,int r1,int r2,int r3)
{






}


static void
lstore(int e2,int creg)
{
    char *drn = register_name(e2);
    printf("\tstqd\t%s, 0(%s)\n",register_name(creg),drn);
}

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

static char *
cstore(int sz)
{
    return "stqd";
}

void
code_assign_gvar(int e2,int creg,int byte) {
    use_int(creg);
    printf("\tstqr\t%s,%s\n",regsister_name(creg),ncaddr(e2)->nm);
}

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\t%s,",cstore(byte),crn);
    e2 *= 4;
        lvar(e2,"");
}

void
code_assign_register(int e2,int byte,int creg) {
    use_int(creg);
    if (e2!=creg) {
        printf("\tori\t%s, %s, 0\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\t%s, 0(%s)\n",cstore(byte),crn,drn);
}


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

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

    drn = register_name(edx=cadr(edx));
    use_int(creg);
    xrn = register_name(xreg = emit_pop(0));       /* pop e3 value */
    code_register(creg,edx);
    ld_indexx(byte,0,edx,creg,sign);
    tosop(op,creg,xreg);
    crn = register_name(creg);
    printf("\t%s\t%s, 0(%s)\n",cstore(byte),crn,drn);
    free_register(edx);
    free_register(creg);
    emit_pop_free(xreg);
}

int
tosop_operand_safe_p(int op)
{
    return 1;
}

void
tosop(int op,int creg,int oreg)
{
    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("asl",creg,oreg);
        if(ox!=-1) free_register(ox);
        return;
    case RSHIFT:
        shift("asr",creg,oreg);
        if(ox!=-1) free_register(ox);
        return;
    case URSHIFT:
        shift("lsr",creg,oreg);
        if(ox!=-1) free_register(ox);
        return;
    }
    orn = register_name(oreg);
    crn = register_name(creg);
    switch(op) {
    case ADD:
        printf("\ta\t%s, %s, %s\n",crn,crn,orn);
        break;
    case SUB:
        printf("\tsf\t%s, %s, %s\n",crn,crn,orn);
        break;
    case CMP:
        printf("\tceqp\t%s, %s\n",crn,orn);
        break;
    case BAND: 
        printf("\tand\t%s, %s, %s\n",crn,crn,orn);
        break;
    case EOR: 
        printf("\txor\t%s, %s, %s\n",crn,crn,orn);
        break;
    case BOR:
        printf("\tor\t%s, %s, %s\n",crn,crn,orn);
        break;
    case MUL:
    case UMUL:
        /* target!=source */
        printf("\tmpy\t%s, %s, %s\n",crn,orn,crn);
        break;
    case DIV:
        code_int_lib("__divsi3",creg,oreg); break;
    case UDIV:
        code_int_lib("__udivsi3",creg,oreg); break;
    case MOD:
        code_int_lib("__modsi3",creg,oreg); break;
    case UMOD:
        code_int_lib("__umodsi3",creg,oreg); break;
    default:
        error(-1);
    }
    if(ox!=-1) free_register(ox);
}

int 
code_const_op_p(int op,int v)
{
    if (car(v)!=CONST) return 0;
    v = cadr(v);
    switch(op) {
    case MUL: case UMUL: case DIV: case UDIV:
        return ilog(v);
    case ADD: case SUB:
        return 1;
    case LSHIFT: case ULSHIFT: case RSHIFT: case URSHIFT:
        return 0<v&&v<=32;
    case CMP:
        return is_stage1_const(v,CMP);
    case EOR: case BOR: case BAND:
        return (is_stage1_const(v,0)>0);
    default:
        return 0;
    }
}

void
oprtc(int op,int creg, int v)
{
    char *crn;
    use_int(creg);
    crn = register_name(creg);
    v = cadr(v);
    
    switch(op) {
    case MUL: case UMUL:
        v=ilog(v);
    case LSHIFT:
    case ULSHIFT:
        printf("\troti\t%s, %s,-%d\n",crn,crn,v);
        break;
    case DIV:
        v=ilog(v);
    case RSHIFT:
        printf("\troti\t%s, %s, -%d\n",crn,crn,v);
        break;
    case UDIV:
        v=ilog(v);
    case URSHIFT:
        printf("\tshli\t%s, %s,%d\n",crn,crn,v);
        break;
    case ADD:
        code_add(creg,v,creg);
        break;
    case SUB:
        code_add(creg,-v,creg);
        break;
    case CMP: printf("\tceq\t%s,%d\n",crn,v); break;
    case BOR: printf("\tori\t%s, %s, %d\n",crn,crn,v); break;
    case EOR: printf("\txori\t%s, %s, %d\n",crn,crn,v); break;
    case BAND: printf("\tandi\t%s, %s, %d\n",crn,crn,v); break;
    default:
        error(-1);
    }
}

void
shift(char *op, int creg, int reg)
{
    char *crn;
    char *rrn = register_name(reg);
    use_int(creg);
    crn = register_name(creg);
    if(crn == "asr") printf("\tshl\t%s, %s,%s\n",crn,crn,rrn);      //算術右シフト
        if(crn == "asl")  printf("\trotma\t%s, %s, -%s\n",crn,crn,rrn);  //算術左シフト
        if(crn == "lsr")  printf("\trot\t%s, %s, %s\n",crn,crn,rrn);  //左シフト
        //printf("\tmov\t%s, %s, %s  %s\n",crn,crn,op,rrn);
}

void
ld_indexx(int byte, int n, int xreg,int creg, int sign)
{       
    use_int(creg);
    code_ld(cload(byte,sign),creg,n,xreg,cext_at(byte,sign));
}

int
code_csvalue()
{
    return glist2(REGISTER,creg);
}

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

    if (!(sign=is_stage1_const(e,CMP))) {
        regsv = regs[csreg]; regs[csreg]=1;
        rn = register_name(reg= get_register());
        regs[csreg] = regsv;
        code_const(e,reg);
        printf("\tceq\t%s, %s, %s\n",crn,crn,rn);
    } else {
        if (sign>0) 
            printf("\tceqi\t%s, %s, %d\n",crn,crn,e);
        else
            //printf("\tcmn\t%s, #%d\n",crn,-e);
            printf("\tceqi\t%s, %s, %d\n",crn,crn,-e);
    }
    switch(cond) {
    case -1: break;
    case 1:
        printf("\tbr\t.LC%d\n",label); break;
    case 0:
        printf("\tbr\t.LC%d\n",label); break;
    case LT:
    printf("\tbra\t.LC%d\n",label); break;
        case GT:
        printf("\tbgt\t.LC%d\n",label); break;
    default: error(-1);
    }
    if (reg!=-1) free_register(reg);
}

void
code_opening(char *filename)
{
    /* this is called once per file */
    //printf("@ Generated by mc for ARM/elf\n");
    printf("\t.file \"%s\"\n",filename);
    // printf(".text\n");
}

// should have pcond_const

#define COND_BRANCH 1
#define COND_VALUE  2
#define COND_BRANCH_CONST 3
#define COND_VALUE_CONST  4

    /*
            if (r1 cmp r2) goto l1   COND_BRANCH
            r0 = (r1 cmp r2)         COND_VALUE
     */
static void
pcond(int op, int r2,int r1,int r0,int cond,int l1,int mode)
{
    char *rn2;
    char *rn1;
    char *rn0;
    char *cc=0,*ncc=0;

    if (mode==COND_BRANCH_CONST||mode==COND_VALUE_CONST) {
        rn1 =           register_name(r1);
        if (r2>=0) 
            printf("\tceqi\t%s, %s, %d\n",rn1,rn1,r2);
        else
            printf("\tceqi\t%s, %s, -%d\n",rn1,rn1,-r2);
    } else {
        rn1 =           register_name(r1);
        rn2 =           register_name(r2);
        printf("\tceq\t%s, %s, %s\n",rn1,rn1,rn2);
    }

    switch(op+(!cond)*BNOT) {
    case GT:  case LE+BNOT:     cc="r"; ncc="r"; break;
    case LE:  case GT+BNOT:     cc="r"; ncc="r"; break;
    case GE:  case LT+BNOT:     cc="r"; ncc="r"; break;
    case LT:  case GE+BNOT:     cc="r"; ncc="r"; break;
    case UGT: case ULE+BNOT:    cc="r"; ncc="r"; break;
    case ULE: case UGT+BNOT:    cc="r"; ncc="r"; break;
    case UGE: case ULT+BNOT:    cc="r"; ncc="r"; break;
    case ULT: case UGE+BNOT:    cc="r"; ncc="r"; break;
    case EQ:  case NEQ+BNOT:    cc="r"; ncc="r"; break;
    //case NEQ: case EQ+BNOT:           cc="rnz"; ncc="r"; break;
    case NEQ: case EQ+BNOT:     cc="r"; ncc="r"; break;
    default:        error(-1);
    }

    if (mode==COND_BRANCH||mode==COND_BRANCH_CONST) {
                printf("\tb%s\t.LC%d\n",cc,l1);
        //printf("\tb%s\t%s, .LC%d\n",cc,rn1,l1);
    } else if (mode==COND_VALUE||mode==COND_VALUE_CONST) {
        rn0 =           register_name(r0);
        //   ここはnccで分岐させなきゃダメだな  
        printf("#####pcond##########\n");
        //printf("\tmovhoge%s\t%s, #0\n",ncc,rn0);
        //printf("\tmovfuho%s\t%s, #1\n",cc,rn0);
    } 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;
    if (car(caddr(e1))==CONST  && is_stage1_const(cadr(caddr(e1)),CMP)) {
        g_expr(cadr(e1));
        reg0 = creg;
        use_int(reg);
        pcond(op, cadr(caddr(e1)),reg0,reg,1,0,COND_VALUE_CONST);
    } else {
        g_expr(cadr(e1));
        emit_push();
        g_expr(caddr(e1));
        e2 = emit_pop(1);
        reg0 = creg;
        use_int(reg);
        pcond(op, reg0,e2,reg,1,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);

    if (car(caddr(e1))==CONST  && is_stage1_const(cadr(caddr(e1)),CMP)) {
        g_expr(cadr(e1));
        pcond(op, cadr(caddr(e1)),creg,0,cond,l1,COND_BRANCH_CONST);
    } else {
        g_expr(cadr(e1));
        emit_push();
        g_expr(caddr(e1));
        e2 = emit_pop(1);
        pcond(op, creg,e2,0,cond,l1,COND_BRANCH);
        emit_pop_free(e2);
    }
    return l1;
}

#define CMP_C1T (-1)

static void
jcond(int l, int cond)
{       
    printf("\tb%s\t.LC%d\n",cond?"r":"r",l);
}

void
jmp(int l)
{       
    printf("\tbr\t.LC%d\n",l);
    if (inst_count>CONST_TBL_COUNT/2) {
        const_list_table();
    } 
}

void
code_comment(char *s)
{
    printf("## %s",s);
}

static int
code_register_restore(int reg_save,int freg_save,int disp)
{
    int i;
    if (freg_save>0) {  
            i=reg_save*SIZE_OF_INT+
                freg_save*SIZE_OF_DOUBLE + 20;
            printf("\tlqd\t$sp,%d($sp)\n",i);
        }
        for (i=reg_var_num(0);i<reg_var_num(reg_save);i++) {
            printf("\tlqd\t%s,-%d($sp)\n",register_name(i),-disp);
                disp -= SIZE_OF_FLOAT*4;
        }
    return disp;
}

static int entry_label;

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);
    printf("\t.type\t%s,function\n",name);
    r1_offset_label = fwdlabel();
    max_func_args = 0;
    printf("%s:\n",name);
    code_label_value(r1_offset_label,REG_ip);
    printf("\ta\t$sp, $sp, $fp\n");
    clear_ptr_cache();
}


void
code_enter1(int args)
{
    set_ireg(CREG_REGISTER,0);
}

void
code_leave(char *name)
{
    code_offset_set(fnptr);
    local_table();
    printf("\t.size\t%s,.LC%d-%s\n",name, backdef(),name);
}

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

    lvar_offset_label = fwdlabel();
    // r1_offset_label = fwdlabel();
    printf("\t.type\t%s,function\n",name);
    if (stmode!=STATIC)
        printf("\t.globl\t%s\n",name);
    printf("%s:\n",name);
    //printf("\tmov\t$sp, $lr,0\n");
    printf("\tstqd\t$lr,16($sp)\n");
    printf("\tstqd\t$sp,-32($sp)\n");
    printf("\tai\t$sp,$sp,-32\n");      
    //gen_jmp(entry_label = fwdlabel());
    //register_save_return_label = backdef();
    clear_ptr_cache();
}

void
enter1()
{
    text_mode(0);
    set_ireg(CREG_REGISTER,0);
}

void
code_label_call(int l)
{
        printf("\tbr\tL_%d\n",l);
}

void
code_ret()
{
        //printf("\tmov\tpc, lr\n");
        control=0;
}

static void 
make_return_continuation()
{
    fwddef(retcont);
    if (cadr(fnptr->ty)==FLOAT) {
#if FLOAT_CODE
        creg = cadr(get_input_dregister_var(0,0,1,0));
        set_ireg(RET_REGISTER,1);
#endif
        printf("\t%s\t%s, %s, 0\n",movef(0),
            register_name(REG_fp),register_name(
                cadr(get_input_register_var(1,0,1,0)));
    } else if (cadr(fnptr->ty)==DOUBLE) {
#if FLOAT_CODE
        creg = cadr(get_input_dregister_var(0,0,1,1));
        set_ireg(RET_REGISTER,1);
        printf("\t%s\t%s, %s, 0\n",movef(0),
            register_name(REG_fp),register_name(
                cadr(get_input_register_var(1,0,1,0)));
#endif
    } else if (cadr(fnptr->ty)>0&&(
        car(cadr(fnptr->ty))==STRUCT ||
        car(cadr(fnptr->ty))==UNION)) {
        sz = size(cadr(fnptr->ty));
        code_const(sz,REGISTER_OPERAND);
        printf("\tsf\t$1, $2, $sp\n");
        printf("\tlqd\t$1, %d($sp)\n",(my_func_args-1)*SIZE_OF_INT);
        // emit_copy(6,3,sz,0,1,1);
        printf("\t%s\t%s, %s, 0\n",movef(0),
            register_name(REG_fp),register_name(
                cadr(get_input_register_var(1,0,1,0)));
    } else if (cadr(fnptr->ty)!=VOID) {
        creg = cadr(get_input_register_var(0,0,1));
        if (creg!=RET_REGISTER)
            set_ireg(RET_REGISTER,1);
        printf("\t%s\t%s, %s, 0\n",movef(0),
            register_name(REG_fp),register_name(
                cadr(get_input_register_var(1,0,1,0)));
    }
    code_unfix_frame_pointer();
}

void
leave(int control0, char *name)
{
    if (control0) {
        code_set_return_register(1);
    } else
        text_mode(2);
    if (retcont) { 
        /* return from CbC segement */
        if (control0) gen_jmp(retlabel);
        make_return_continuation();
    }
    fwddef(retlabel);

    code_offset_set(fnptr);
    code_register_restore(max_reg_var,max_reg_var,0);

//  leave part end

//  entry part  (save register)

    fwddef(entry_label);
    if (arg_on_register>0)
        printf("\tsfi\t$sp, $sp, %d\n",arg_on_register);
    // code_register_save(max_reg_var,max_reg_var,0);

    code_add(REG_sp,disp-max_func_args*SIZE_OF_INT,REG_sp);
    gen_jmp(register_save_return_label);
    local_table();

    printf("\t.size\t%s,.LC%d-%s\n",name, backdef(),name);

    free_all_register();
}


int
code_set_return_register(int mode) {
    set_ireg(RET_REGISTER,mode);
    return creg;
}

int
code_get_fixed_creg(int reg,int type) {
    if (reg==USE_CREG) {
        if (regs[CREG_REGISTER]==0||regs[CREG_REGISTER]==PTRC_REG) {
            set_ireg(CREG_REGISTER,regs[creg]==USING_REG);
            return CREG_REGISTER;
        }
    }
    use_int(reg);
    return reg;
}

void
code_set_fixed_creg(int reg,int mode,int type) {
    set_ireg(reg,mode);
}

void
gen_gdecl(char *n, int gpc)
{
}

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

extern void
ascii(char *s,int len)
{
    if (s[len-1]==0) 
        printf("\t.string \"");
    else
        printf("\t.ascii \"");
    while(len-->0) {
        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("%c\n",34);
}


int
emit_string_label() {
    int lb;

    lb=fwdlabel();
    // should put on different segement
    cstring_mode();
    printf(".LC%d:\n",lb);
    return lb;
}

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

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

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

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

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

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

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
}

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
}

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

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

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

void
emit_data_closing(NMTBL *n)
{
#ifdef DOT_SIZE
    int lb;
#endif
    if (mode==GDECL) {
        data_mode(0);
#ifdef DOT_SIZE
        lb=fwdlabel();
        printf(".LC%d:\n",lb);
        printf("\t.size\t%s,.LC%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) {
            /* n->dsp = -1 means initialized global */
            if (is_code(n)||is_function(n)) continue;
            if (init==0) {
                data_mode(0);
                init=1;
            }
            // printf(".local %s\n",n->nm);
            comm(n);
        }
    }
}

void
local_table(void)
{
    NMTBL *n;
    int init;
    free_glist3_a(prev_const_list); prev_const_list = 0;
    const_list_table();

    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 LONGLONG_CODE||FLOAT_CODE
static void
extern_conv(char *conv)
{
     code_save_stacks();
     clear_ptr_cache();
     extern_define(conv,0,FUNCTION,1);
     printf("\tbr\t%s\n",conv);
     lib_args(16);
}
#endif

static void
code_int_lib(char *lib,int reg,int oreg)
{
    int g;
    code_save_stacks();
    clear_ptr_cache();
    g = list3(REGISTER_OPERAND,0,reg);
    g = list3(REGISTER_OPERAND_1,g,oreg);
    parallel_rassign(g);
    extern_define(lib,0,FUNCTION,1);
    printf("\tbr\t%s\n",lib);
    lib_args(16);
    set_ireg(RET_REGISTER,0);
    if (reg!=RET_REGISTER) {
        code_register(RET_REGISTER,reg);
    }
}

#if FLOAT_CODE

/* floating point */

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

void
code_cmp_dregister(int e2,int d,int label,int cond)
{
    char *frn;
    use_float(d,e2);

    frn = register_name(e2);
    printf("\tcmf\t%s, #0\n",frn);
    jcond(label,cond);
    return;
}

static char *
movef(int d)
{
    return d?"ori":"ori";
}

static char *
fload(int d)  { return d?"lqd":"lqd"; }

static char *
fstore(int d) { return d?"stqd":"stqd"; }


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

void
code_dassign_gvar(int e2,int freg,int d)
{ 
        use_float(d,freg);
        code_ldf(fstore(d),register_name(freg),cadr(e2),
            get_ptr_cache(ncaddr(e2)),"");
}

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

void
code_dassign(int e2,int freg,int d)
{ 
    use_float(d,freg);
    //printf("\t%s\t%s, [%s, #0] @ float\n",fstore(d),
    printf("\t%s\t%s, 0(%s)\n",fstore(d),
        register_name(freg),register_name(e2));
}

void
code_dassign_dregister(int e2,int d,int freg) {
    use_float(d,freg);
    if (e2!=freg) {
        printf("\t%s\t%s, %s, 0\n",movef(d),
                register_name(e2),register_name(freg));
    }
}

static double d0 = 1.0;

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

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

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


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

    char *frn;
    int label,disp;

    use_float(d,freg);
        frn = register_name(freg);
        if (value==0 || value==1 || value==10) {
            printf("\t%s\t%s, %s, %d\n",movef(d),frn,frn,(int)value);
        } else if (value==-1 || value==-10) {
            printf("\t%s\t%s, %d\n",d?"mnfd":"mnfs",frn,(int)-value);
        } else if (d) {
#if ENDIAN_D==0
            disp = search_double_const(DCONST,
                code_d1(value),code_d2(value),&label);
#else
            disp = search_double_const(DCONST,
                code_d2(value),code_d1(value),&label);
#endif
            printf("\tlqd\t%s, .LC%d+%d($sp)\n",frn,label,disp);
        } else {
        }
}


void
code_builtin_fabsf(int e)
{
}
void
code_builtin_fabs(int e)
{
}
void
code_builtin_inff()
{
}
void
code_builtin_inf(int op)
{
}

void
code_dneg(int freg,int d)
{ 
    char *frn;
    use_float(d,freg);
        frn = register_name(freg);
        printf("\t%s\t%s, %s\n",d?"mnfd":"mnfs",frn,frn);
}

void
code_d2i0(int reg,int d)
{ 
    int lreg;
        use_float(d,reg);
        lreg = get_register();
        //printf("\tfixz\t%s, %s\n",register_name(lreg),register_name(reg));
        set_ireg(lreg,0);
    return;
}

void
code_i2d0(int reg,int d)
{ 
    int lreg;
        use_int(reg);
        lreg = get_dregister(1);
        printf("\tflt\t%s, %s\n",
                register_name(lreg),register_name(reg));
        set_ireg(lreg,0);
    return;
}

void
code_d2u0(int reg,int d)
{ 
    int lreg,reg0;
    char *lrn,*frn,*crn;
        //   u = (d>2.1e9)?((int)(d-2.1e9)^2147483648):(int)d
        use_float(1,reg);
        frn = register_name(reg);
        if (!d) printf("\tori\t%s, %s, 0\n",frn,frn);
        emit_dpush(1);
        code_dconst(dlist2(DCONST,2.147483648e9),USE_CREG,1);
        lrn = register_name(lreg = emit_dpop(d));
        frn = register_name(creg);
        set_ireg(reg0=get_register(),0);
        crn = register_name(reg0);
        printf("\tcmfe\t%s, %s\n",lrn,frn);
        printf("\tbr\t1f\n");
        //printf("\tfixz\t%s, %s\n",crn,lrn);
        printf("\tb\t2f\n");
        printf("1:\n");
        printf("\tsufd\t%s, %s, %s\n",lrn,lrn,frn);
        //printf("\tfixz\t%s, %s\n",crn,lrn);
        printf("\teor\t%s, %s, #-2147483648\n",crn,crn);
        printf("2:\n");
        emit_dpop_free(lreg,d);
    return;
}

void
code_u2d0(int reg,int d)
{ 
    int freg1;
    char *crn,*frn,*lrn;
        use_int(reg);
        crn = register_name(reg);
        set_ireg(reg=get_dregister(1),0);
        frn = register_name(reg);
        printf("\tfltd\t%s, %s\n",frn,crn);
        printf("\tceqi\t%s, %s, 0\n",crn,crn);
        printf("\tbr\t1f\n");
        freg1 = get_dregister(1);
        code_dconst(dlist2(DCONST,4.29496729600000000000e9),freg1,1);
        frn = register_name(creg);
        lrn = register_name(freg1);
        printf("\tadfd\t%s, %s, %s\n",frn,frn,lrn);
        printf("1:\n");
        if (!d)
            printf("\tmvfs\t%s, %s\n",frn,frn);
        free_register(freg1);
    return;
}

void
code_d2f(int reg) { 
    char *frn;
        frn = register_name(creg);
        printf("\tmvfs\t%s,%s\n",frn,frn);
    return;
}

void
code_f2d(int reg) { 
    char *frn;
        frn = register_name(creg);
        printf("\tori\t%s, %s, 0\n",frn,frn);
    return;
}

void
code_d2i(int reg) {
    code_d2i0(reg,1);
}

void
code_d2u(int reg) {
    code_d2u0(reg,1);
}

void
code_f2i(int reg) {
    code_d2i0(reg,0);
}

void
code_f2u(int reg) {
    code_d2u0(reg,0);
}

void
code_i2d(int reg) {
    code_i2d0(reg,1);
}

void
code_i2f(int reg) {
    code_i2d0(reg,0);
}

void
code_u2d(int reg) {
    code_u2d0(reg,1);
}

void
code_u2f(int reg) {
    code_u2d0(reg,0);
}

void
code_drgvar(int e2,int d,int freg)
{ 
        use_float(d,freg);
        code_ldf(fload(d),register_name(freg),cadr(e2),
            get_ptr_cache(ncaddr(e2)),"");
}


void
code_drlvar(int e2,int d,int freg)
{ 
        use_float(d,freg);
        lvar_intro(e2);
        printf("\t%s\t%s, ",fload(d),register_name(freg)); 
        lvar(e2,d?"@ double":"@ float");
}

void
code_cmp_drgvar(int e2,int reg,int d,int label,int cond)
{ 
    use_float(d,reg);
    code_drgvar(e2,d,reg);
    code_cmp_dregister(reg,d,label,cond);
}

void
code_cmp_drlvar(int e2,int reg,int d,int label,int cond)
{ 
    use_float(d,reg);
    code_drlvar(e2,d,reg);
    code_cmp_dregister(reg,d,label,cond);
}



void
dtosop(int op,int reg,int e1)
{ 
    char *opn="";

    char *grn,*frn;
    int d;
    int cmp=0;


    d=(op<FOP);
    use_float(d,reg);
    switch(op) {
    case DADD: opn="adfd"; break;
    case DSUB: opn="sufd"; break;
    case DDIV: opn="fdvd"; break;
    case DMUL: opn="fmld"; break;
    case DCMPGE:
    case DCMP:  opn="cmfe"; cmp=1; break;
    case FADD: opn="adfs"; break;
    case FSUB: opn="sufs"; break;
    case FDIV: opn="fdvs"; break;
    case FMUL: opn="fmls"; break;
    case FCMPGE:
    case FCMP:  opn="cmfe"; cmp=1; break;
    default:
        error(-1); return;
    }
    grn = register_name(e1);
    frn = register_name(reg);
    if (cmp) {
      printf("\t%s\t%s, %s\n",opn,grn,frn);
    } else {
      printf("\t%s\t%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;



    xreg=emit_dpop(d);
    crn=register_name(creg);
    use_float(d,reg);
    frn  =register_name(reg);

    code_ldf(fload(d),register_name(creg),0,creg,"");
    dtosop(op,reg,xreg);
    code_ldf(fstore(d),register_name(creg),0,creg,"");
    emit_dpop_free(xreg,d);
}

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

static int
code_dload_1(int d)
{
    int g = get_dregister(d);
    printf("\tmvf\t%s,#1\n",register_name(g));
    return g;
}


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

    if (car(e2)==DREGISTER||car(e2)==FREGISTER) {
        crn=register_name(cadr(e2));
        grn=register_name(g=code_dload_1(d));
        if (reg==USE_CREG) {
            reg=get_dregister(d); if (!reg) error(-1);
            set_ireg(reg,0); 
        }
        frn=register_name(reg);
        printf("\t%s\t%s, %s, %s\n",
            dir>0?"adf":"suf",
            crn,crn,grn);
        if (use && reg!=cadr(e2)) {
            printf("\tmvf\t%s, %s\n",frn,crn);
        }
    } else {
        g_expr(e2);
        if (!is_int_reg(creg)) error(-1);
        reg0 = creg;
        if (reg==USE_CREG) {
            reg=get_dregister(d); if (!reg) error(-1);
            set_ireg(reg,0); 
        }
        grn = register_name(g = code_dload_1(d));
        frn=register_name(reg);
        code_ldf(fload(d),frn,0,reg0,"");
        printf("\t%s\t%s, %s, %s\n",
            dir>0?"adf":"suf",
            frn,frn,grn);
        code_ldf(fstore(d),frn,0,reg0,"");
    }
    free_register(g);
}

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

    if (car(e2)==DREGISTER||car(e2)==FREGISTER) {
        crn=register_name(cadr(e2));
        grn=register_name(g=code_dload_1(d));
        if (reg==USE_CREG) {
            reg=get_dregister(d); if (!reg) error(-1);
            set_ireg(reg,0);
        }
        frn=register_name(reg);
        if (use && reg!=cadr(e2)) {
            printf("\tmvf\t%s, %s\n",frn,crn);
        }
        printf("\t%s\t%s,%s,%s\n",dir>0?"adf":"suf",
            crn,crn,grn);
    } else {
        g_expr(e2);
        if (!is_int_reg(creg)) error(-1);
        crn=register_name(reg0=creg);
        if (reg==USE_CREG) {
            reg=get_dregister(d); if (!reg) error(-1);
            set_ireg(reg,0);
        }
        frn=register_name(reg);
        grn = register_name(g = code_dload_1(d));
        code_ldf(fload(d),frn,0,reg0,"");
        printf("\t%s\t%s, %s, %s\n",
            dir>0?"adf":"suf",
            grn,frn,grn);
        code_ldf(fstore(d),grn,0,reg0,"");
    }
    free_register(g);
}


int
drexpr(int e1, int e2,int l1, int op,int cond)
{
    int op1=0;
    char *opn=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;   opn = "br"; break;
    case FOP+GE:  op1=FOP+CMPGE; opn = "br"; break;
    case FOP+EQ:  op1=FOP+CMP;   opn = "br"; break;
    case FOP+NEQ: op1=FOP+CMP;   opn = "br"; break;
    case DOP+GT:  op1=DOP+CMP;   opn = "br"; break;
    case DOP+GE:  op1=DOP+CMPGE; opn = "br"; break;
    case DOP+EQ:  op1=DOP+CMP;   opn = "br"; break;
    case DOP+NEQ: op1=DOP+CMP;   opn = "br"; break;
    default: error(-1);
    }
    g_expr(list3(op1,e2,e1));
    printf("\t%s\t.L%d\n",opn,l1);
    return l1;
}

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


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

void
emit_dpush(int d)
{ 
    int new_reg;
    if (!is_float_reg(creg)) error(-1);
    if (reg_sp>MAX_MAX) error(-1);
    new_reg = get_dregister(d);       /* ?????????? */
    reg_stack[reg_sp++] = creg;     /* push ???????????????? */
    creg = 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;
    g_expr(e1);
    emit_lpush();
    g_expr(e2);
    e3 = emit_lpop();

    reg = creg;
    l2 = fwdlabel();
    regh = reg; regl = reg;
    e3h =  e3;  e3l = e3;
    switch(op) {
    case LOP+GT: case LOP+GE:
        pcond(GT,  regh,e3h,0,1,cond?l1:l2,COND_BRANCH);
        pcond(NEQ, regh,e3h,0,1,cond?l2:l1,COND_BRANCH);
        break;
    case LOP+UGT: case LOP+UGE:
        pcond(UGT, regh,e3h,0,1,cond?l1:l2,COND_BRANCH);
        pcond(NEQ, regh,e3h,0,1,cond?l2:l1,COND_BRANCH);
        break;
    case LOP+EQ:
        pcond(EQ, regh,e3h,0,0,cond?l2:l1,COND_BRANCH);
        break;
    case LOP+NEQ:
        pcond(EQ, regh,e3h,0,0,cond?l1:l2,COND_BRANCH);
        break;
    default:
        error(-1);
    }
    pcond(op%LOP,regl,e3l,0,cond,l1,COND_BRANCH);
    fwddef(l2);  
    emit_lpop_free(e3);
    return l1;
}

void
code_cmp_lregister(int reg,int label,int cond)
{
    use_longlong(reg);




    code_cmp_register((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

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

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


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

    code_ldf(cstore(0),register_name(creg),cadr(e2),r,"");





}

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


    use_longlong(creg);
    crn = register_name(creg);

    lvar_intro(e2);

    printf("\tstqd\t%s, ",crn);lvar(e2,"");





}

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

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 (reg_sp>MAX_MAX) error(-1);
    new_reg = get_lregister();        /* ??????????(?) */
    reg_stack[reg_sp++] = creg;     /* push ???????????????? */
    creg = new_reg;
}

void
code_lrgvar(int e1,int creg)
{
    int r;
    char *crn;

    use_longlong(creg);
    crn = register_name(creg);

    r = get_ptr_cache(ncaddr(e1));

    code_ldf("lqd",crn,cadr(e1),r,"");





}

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

    use_longlong(creg);
    crn = register_name(creg);

    lvar_intro(e1);
    printf("\tlqd\t%s, ",crn); lvar(e1,"");

}

#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);
    code_const(code_l2(lcadr(e1)),(creg));
}

void
code_lneg(int creg)
{

    char *rh;
    use_longlong(creg);
    rh=register_name(creg);


    printf("\trsc\t%s, %s, #0\n",rh,rh);
}

static void
code_longlong_lib(char *lib,int reg,int oreg)
{
    code_save_stacks();
    clear_ptr_cache();
    set_operands((reg),(reg),(oreg),(oreg));
    extern_conv(lib);
}

#define check_lreg(reg) lmove(reg,RET_REGISTER)

void
ltosop(int op,int reg,int oreg)
{
    int dx = -1;
    int ox = -1;
    char *orn, *crn;


    // 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_longlong_lib("__ashldi3",reg,oreg);
        check_lreg(reg);
        if(ox!=-1) free_register(ox);
        return;
    case LRSHIFT:
        code_longlong_lib("__ashrdi3",reg,oreg);
        check_lreg(reg);
        if(ox!=-1) free_register(ox);
        return;
    case LURSHIFT:
        code_longlong_lib("__lshrdi3",reg,oreg);
        check_lreg(reg);
        if(ox!=-1) free_register(ox);
        return;
    }
    orn = register_name(oreg);

    crn = register_name(reg);

    switch(op) {
    case LADD:
        printf("\tadds\t%s, %s, %s\n",crn,crn,orn);

        break;
    case LSUB:
        printf("\tsubs\t%s, %s, %s\n",crn,crn,orn);

        break;
    case LCMP:
        error(-1);
        break;
    case LBAND: 
        printf("\tand\t%s, %s, %s\n",crn,crn,orn);

        break;
    case LEOR: 
        printf("\teor\t%s, %s, %s\n",crn,crn,orn);

        break;
    case LBOR:
        printf("\tor\t%s, %s, %s\n",crn,crn,orn);

        break;
    case LMUL:
    case LUMUL:
        code_longlong_lib("__muldi3",reg,oreg);
        check_lreg(reg);
        break;
    case LDIV:
        code_longlong_lib("__divdi3",reg,oreg);
        check_lreg(reg);
        break;
    case LUDIV:
        code_longlong_lib("__udivdi3",reg,oreg);
        check_lreg(reg);
        break;
    case LMOD:
        code_longlong_lib("__moddi3",reg,oreg);
        check_lreg(reg);
        break;
    case LUMOD:
        code_longlong_lib("__umoddi3",reg,oreg);
        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)
{
    long long v;
    if (car(e)==LCONST) {
        v = lcadr(e);
    } else if (car(e)==CONST) {
        v = cadr(e);
    } else return 0;
    
    switch(op) {
    case LMUL: case LUMUL: /* case LDIV: */ case LUDIV:
        return ilog(v);
    case LLSHIFT: case LULSHIFT: case LRSHIFT: case LURSHIFT:
        return 0<v&&v<=64;
    case LADD:
    case LSUB:
    case LBOR:
    case LEOR: case LBAND:
        return (is_stage1_const(v,0)>0)
            && (is_stage1_const(v>>32,0)>0);
    default:
        return 0;
    }
}

void
loprtc(int op,int creg,int e)
{
    char *crn;

    char *grn;
    int v=0;
    int vh=0;
    int greg,dx=-1;

    use_longlong(creg);
    crn = register_name(creg);


    if (car(e)==LCONST) { v = lcadr(e); vh = lcadr(e)>>32; }
    else if (car(e)==CONST) {
        v = cadr(e);
        vh = (v<0)?-1:0;
    }

    switch(op) {
    case LMUL: case LUMUL:
        v=ilog(v);
    case LLSHIFT:
    case LULSHIFT:
        if (v==0) return;
        if (v==32) {
            code_register((creg),(creg));
            code_const(0,(creg));
            return;
        } else if (v>31) {

            // 右シフト
                        printf("\troti\t%s,%s,%d\n",crn,crn,96-v);
                        code_const(0,(creg));
            return;
        }
        greg = get_register();
        grn = register_name(greg);
        printf("\tshli\t%s, %s, %d\n",crn,crn,v);
        printf("\troti\t%s, %s, -%d\n",grn,crn,32-v);
        printf("\torc\t%s, %s,%s\n",crn,crn,grn);
        printf("\tshli\t%s, %s,%d\n",crn,crn,v);
        free_register(greg);
        return;
    case LDIV:
        v=ilog(v);
    case LRSHIFT:
        if (v==0) return;
        if (v==32) {
            code_register((creg),(creg));
            printf("\tshli\t%s, %s, 31\n",crn,crn);
            return;
        } else if (v>31) {
            printf("\t\t%s, %s, asr #%d\n",crn,crn,v-32);
            printf("\tshl\t%s, %s, 31\n",crn,crn);
            return;
        }
        greg = get_register();
        grn = register_name(greg);
        printf("\troti \t%s, %s, %d\n",crn,crn,v);
        printf("\troti\t%s, %s, %d\n",grn,crn,160-v);
        printf("\tor\t%s, %s,%s\n",crn,crn,grn);
        printf("\tshli\t%s, %s, %d\n",crn,crn,v);
        free_register(greg);
        return;
    case LUDIV:
        v=ilog(v);
    case LURSHIFT:
        if (v==0) return;
        if (v==32) {
            code_register((creg),(creg));
            code_const(0,(creg));
            return;
        } else if (v>31) {
            printf("\tmov\t%s, %s, lsr #%d\n",crn,crn,v-32);
            code_const(0,(creg));
            return;
        }
        greg = get_register();
        grn = register_name(greg);
        printf("\troti\t%s, %s, %d\n",grn,crn,96-v);
        printf("\troti\t%s, %s, %d\n",crn,crn,v);
        printf("\tor\t%s, %s,%s\n",crn,grn,crn);
        printf("\troti\t%s, %s, %d\n",crn,crn,v);
        free_register(greg);
        return;
    case LADD:
        printf("\tadds\t%s, %s, #%d\n",crn,crn,v);
        printf("\tadc\t%s, %s, #%d\n",crn,crn,vh);
        break;
    case LSUB:
        printf("\tsubs\t%s, %s, #%d\n",crn,crn,v);
        printf("\tsbc\t%s, %s, #%d\n",crn,crn,vh);
        break;
    case LBAND: 
        printf("\tandi\t%s, %s, %d\n",crn,crn,v);
        printf("\tandi\t%s, %s, %d\n",crn,crn,vh);
        break;
    case LEOR: 
        printf("\teor\t%s, %s, #%d\n",crn,crn,v);
        printf("\teor\t%s, %s, #%d\n",crn,crn,vh);
        break;
    case LBOR:
        printf("\tori\t%s, %s, %d\n",crn,crn,v);
        printf("\tori\t%s, %s, %d\n",crn,crn,vh);
        break;
    default:
        error(-1);
    }
    if (dx!=-1) free_register(dx);
}


void
code_i2ll(int reg)
{
    char *crn;
    int reg0;
    crn = register_name(reg0 = creg);
    use_longlong(reg);


    if (reg0!=(creg))
        printf("\tori\t%s, %s, 0\n",crn,crn);
    printf("\tshli\t%s, %s, 31\n",crn,crn);
}

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

void
code_u2ll(int reg)
{
    char *crn;
    int reg0;
    crn = register_name(reg0 = creg);
    use_longlong(reg);


    if (reg0!=(creg))
        printf("\tori\t%s, %s, 0\n",crn,crn);
    printf("\tmov\t%s, #0\n",crn);
}

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

void
code_ll2i(int reg)
{
    char *crn;
    int reg0;
    crn = register_name(reg0=creg);
    use_int(reg);
    if (creg!=(reg0)) {
        printf("\tori\t%s, %s, 0\n",register_name(creg),crn);
    }
}

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
    extern_conv("__fixdfdi");
    set_ireg(RET_REGISTER,0);
    if (reg!=USE_CREG&&reg!=RET_REGISTER) {
        use_longlong(reg);
    }
}

void
code_d2ull(int reg)
{
    extern_conv("__fixunsdfdi");
    set_ireg(RET_REGISTER,0);
    if (reg!=USE_CREG&&reg!=RET_REGISTER) {
        use_longlong(reg);
    }
}

void
code_f2ll(int reg)
{
    extern_conv("__fixsfdi");
    set_ireg(RET_REGISTER,0);
    if (reg!=USE_CREG&&reg!=RET_REGISTER) {
        use_longlong(reg);
    }
}

void
code_f2ull(int reg)
{
    extern_conv("__fixunssfdi");
    set_ireg(RET_REGISTER,0);
    if (reg!=USE_CREG&&reg!=RET_REGISTER) {
        use_longlong(reg);
    }
}

void
code_ll2d(int reg)
{
    set_ireg(REGISTER_OPERAND,1);
    extern_conv("__floatdidf");
    set_ireg(RET_REGISTER,0);
    if (reg!=USE_CREG&&reg!=RET_REGISTER)
        use_float(1,reg);
}


void
code_ll2f(int reg)
{
    set_ireg(REGISTER_OPERAND,1);
    extern_conv("__floatdisf");
    set_ireg(RET_REGISTER,0);
    if (reg!=USE_CREG&&reg!=RET_REGISTER)
        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
{
    // v should be 8bit const with 2's shift
    if (v>0) {
        printf("\taddq\t%s, %s, #%d\n", 
                register_name(rreg),register_name(dreg), v);
    } else {
        printf("\tsubq\t%s, %s, #%d\n", 
                register_name(rreg),register_name(dreg), -v);
    }
}

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

void
code_lpostinc(int e1,int e2,int reg)
{
    int dreg,nreg,xreg;
    int dir=caddr(e1);
    if (car(e2)==LREGISTER) {
        use_longlong(reg);
        lmove(reg,cadr(e2));
        ladd(cadr(e2),cadr(e2),dir);
        return;
    } 
    g_expr(e2);
    if(!is_int_reg(creg)) error(-1);
    emit_push();
    nreg=get_lregister(); if (!nreg) error(-1);
    if (reg==USE_CREG) {
        dreg=get_lregister(); if (!dreg) error(-1);
        set_ireg(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 = creg;
    emit_push();
    use_longlong(reg);
    if ((creg)==edx) {
        edx0 = get_register(); if(!edx0) error(-1);
        printf("## lassop\n\tori\t%s, %s, 0\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!=creg)
        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_VECTOR)),reg,0); 
            reg_stack[i]= reg_stack[i]-REG_LVAR_OFFSET;
            free_register(reg);
        }
    }
}

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

void
code_closing()
{
    global_table();
}

#if CASE_CODE

int
code_table_jump_p(int delta) { return delta==1||delta==2||delta==4; }

void
code_table_jump(int l,int csvalue,int delta,int max,int min,int dlabel)
{
    int t,regsv;
    char *trn;

    regsv = regs[csvalue]; regs[csvalue] = 1;
    trn = register_name(t=get_register());
    regs[csvalue] = regsv;

    code_add(t,-min,csvalue);
    switch(delta) {
    case 1: 
        code_cmpdimm(max-min,t,dlabel,-1);
        printf("\tldrls\tpc, [pc, %s, asl #2]\n",trn);
        break;
    case 2:
        printf("\ttst\t%s, #1\n",trn);
        printf("\tbr\t.L%d\n",dlabel);
        code_cmpdimm(max-min,t,dlabel,-1);
        printf("\tldrls\tpc, [pc, %s, asl #1]\n",trn); break;
        break;
    case 4:
        printf("\ttst\t%s, #3\n",trn);
        printf("\tbr\t.L%d\n",dlabel);
        code_cmpdimm(max-min,t,dlabel,-1);
        printf("\tldrls\tpc, [pc, %s]\n",trn); break;
        break;
    default:
        error(-1);
    }
    printf("\thbr\t.L%d\n",dlabel);

    free_register(t);
}

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

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

void
code_table_close()
{
}

#endif


#if ASM_CODE

/*
    print an operand  
 */

static void
emit_asm_operand(int rstr)
{
    if (car(rstr)==REGISTER) {
        printf("%s",register_name(cadr(rstr)));
    } else if (car(rstr)==CONST) {
        printf("%d",cadr(rstr));
    } else if (car(rstr)==FNAME) {
        printf("%s",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,ncaddr(e1)->dsp);
            e1=list2(LABEL,val);
        } 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=0,l=0;
    switch(cadr(type)) {   /* value type */
    case INT:           sign=1; break; 
    case UNSIGNED:      break; 
    case CHAR:          sign=1; break; 
    case UCHAR:         break; 
    case SHORT:         sign=1; break; 
    case USHORT:        sign=1; break; 
    case LONGLONG:      sign=1; l=1; break; 
    case ULONGLONG:     l=1; break; 
    default: error(-1);
    }
    if (car(caddr(type))>0) { /* store type */
        if (car(car(caddr(type)))==STRUCT) {
            bitsz=64+32; align=4; l=2;
        } else error(-1);
    } else {
        switch(car(caddr(type))) { 
        case INT:           bitsz=32; align=4; break; 
        case UNSIGNED:      bitsz=32; align=4; break; 
        case CHAR:          bitsz= 8; align=1; break; 
        case UCHAR:         bitsz= 8; align=1; break; 
        case SHORT:         bitsz=16; align=2; break; 
        case USHORT:        bitsz=16; align=2; break; 
        case LONGLONG:      bitsz=64; align=4; l=1; break; 
        case ULONGLONG:     bitsz=64; align=4; l=1; break; 
        default: error(-1);
        }
    }
    *pbitpos = cadr(caddr(type));
    *pbitsize = caddr(caddr(type));
    *psign = sign;
    *pbitsz = bitsz;
    *palign = align;
    *pl = l;
}

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

#define NON_ALIGNED_BFD 1

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 */
        offset-=(bitpos+7)/8;

#ifdef NON_ALIGNED_BFD
        /* code for non-aligned non-hole bit-field */

        /* remove extra previous offset from bitpos */
        while(bitpos>align*8) {
            i = ((offset+(align))&~(align-1))-offset;
            offset+=i; bitpos-=i*8;
        }
        if (bitpos+bitsize > bitsz) {
            int stype=UNSIGNED;

            /* rewind extra previous offset */
            bitpos = *bfd; offset=*poffset;
            offset-=(bitpos+7)/8;

            /* extend store type to allow |---===|===---| */
            switch(car(caddr(type))) { 
            case INT:   case UNSIGNED:  
                stype=ULONGLONG; align=4; break;
            case CHAR:   case UCHAR:    
                stype=USHORT; align=2; break;
            case SHORT:   case USHORT:          
                stype=UNSIGNED; align=4; break;
            case ULONGLONG: case LONGLONG:      
                /* dummy struct type. fields are never used */
                stype=list4(STRUCT,12,0,0);align=4;break;
            default: error(-1);
            }
            /* remove extra previous offset from bitpos again */
            while(bitpos>align*8) {
                i = ((offset+(align))&~(align-1))-offset;
                offset+=i; bitpos-=i*8;
            }
            bitsz = size(stype)*8;
            /* fix store type */
            car(caddr(type)) = stype;
        }
#endif

        /* previous remaining bit space can contain this type? */
        i= offset;
        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;
#ifdef BITPOS_DEBUG
    printf("## %d: bitpos=%d bitsize=%d bitsz=%d offset=%d\n",lineno,bitpos,bitsize,bitsz,*poffset);
#endif
                return l;
            } 
        }
    }

    /* first bit-field or no remaining bits */

    if ((i=(offset & (align-1)))) {
        *poffset = (offset += (align-i));
    }
    bitpos = 0;
    *bfd = (bitsize==bitsz)?0:bitsize;
    *sz = (bitsize+7)/8;
#ifdef BITPOS_DEBUG
    printf("## %d: bitpos=%d bitsize=%d bitsz=%d offset=%d\n",lineno,bitpos,bitsize,bitsz,*poffset);
#endif
    return bitpos;
}

/* bit field value */

/* reg contains container value, result should be in reg */
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));
    } else if (l==2) {   /* three int container */
        int lvalue;
        // use_int(adr);
        use_longlong(reg);
        lvalue = get_register(); 
        code_register(adr,lvalue); adr = lvalue;
   /*

                        <-----bitsize--------------->
                        hhhhhh  mmmmmmmmmmmm  lllllll
   lllll  00000000000  mmmmmmmmmmmm  0000000  hhhhhhh 
  |-----||-----------||------------||-------||-------|
         <-bitpos--->
  <----------------bitsz----------------------------->
  <-----32----------> <---32------> <----32---------->
      (r0:r1) <<= bitsz-bitsize-bitbos
      rest >> =  b;
      rest << =  a;   (b>a)   ==>    rest >> (b-a)
                  (64+32-bitsize -bitpos - (bitsz-bitsize))
                 = 64+32 -bitsz -bitbpos
   */
        /* load hhhhh */
        code_ld(cload(0,0),(reg),SIZE_OF_INT*2,adr,cext_at(0,0));
        /* load mmmmmm */
        code_ld(cload(0,0),(reg),SIZE_OF_INT,adr,cext_at(0,0));
        i = 64-(bitsize-(32-bitpos));
        loprtc(LLSHIFT,reg,list2(CONST,i));
        if (i<0||64<=i) error(-1);
        /* load lllll */
        code_ld(cload(0,0),adr,0,adr,cext_at(0,0));
        i = (bitsize-(32-bitpos))-32;
        oprtc(URSHIFT,adr,list2(CONST,i));
        if (i<0||32<=i) error(-1);
        printf("\tadd\t%s,%s,%s\n",
                            register_name((reg)),
                            register_name((reg)),
                            register_name(adr));
        i = 64-bitsize;
        loprtc(sign?LRSHIFT:LURSHIFT,reg,list2(CONST,i));
        if (i<0||64<=i) error(-1);
        free_register(adr);
#endif
    } else {
        // use_int(adr);
        use_int(reg);
        code_ld(cload(size,sign),reg,0,adr,cext_at(0,0));
        /* 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\t%s, %s, %s\n",trn,crn,trn);
        /* do conjunction  */
        printf("\tand\t%s, %s, %s\n",lrn,trn,lrn);
        /* make or-mask  */
        code_const(mask,tmp);
        printf("\tand\t%s, %s, %s\n",trn,crn,trn);
        /* do disjunction  */
        printf("\tor\t%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==1) {
#if LONGLONG_CODE
        int tmp2;
        // use_int(adr);
        lvalue = get_lregister();
        tmp2 = get_register();
        code_register(adr,tmp2); adr = tmp2;
        lload(adr,lvalue,0);
        use_longlong(value);
        crn = register_name(value);
        lrn = register_name(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 = register_name(value);
        lrn = register_name(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);
        free_register(lvalue);
        free_register(adr);
    } else if (l==2) {
        int i;
        int tmpvar;
        // use_int(adr);
        use_longlong(value);
        lvalue = get_register();
        code_register(adr,lvalue); adr = lvalue;
        tmpvar=new_lvar(SIZE_OF_LONGLONG);
        code_lassign_lvar(tmpvar,value);
        trn = register_name(tmp = get_register());

        /* make and-mask upper */
        i=bitpos;
        if (i) 
            oprtc(LSHIFT,(value),list2(CONST,i));
        if (i<0||32<=i) error(-1);
        crn = register_name(value);
        code_ld(cload(0,0),(value),0,adr,cext_at(0,0));
        lrn = register_name(value);
        mask = make_mask(0,31-bitpos);
        make_mask_and_or(mask,tmp,trn,crn,lrn);
        printf("\t%s\t%s, 0(%s)\n",cstore(0),crn,register_name(adr));
/*
                        <-----bitsize--------------->
                        hhhhhh  mmmmmmmmmmmm  lllllll
   lllll  00000000000  mmmmmmmmmmmm  0000000  hhhhhhh 
  |-----||-----------||------------||-------||-------|
         <-bitpos--->
  <----------------bitsz----------------------------->
  <-----32----------> <---32------> <----32---------->
 */
        /* store middle */
        code_lrlvar(tmpvar,value);
        i=32-bitpos;
        if (i)
            loprtc(LRSHIFT,value,list2(CONST,i));
        if (i<0||64<=i) error(-1);
        printf("\t%s\t%s, 16(%s)\n",cstore(0),crn,register_name(adr));

        /* make and-mask lower */
        code_ld(cload(0,0),(value),8,adr,cext_at(0,0));
        if (i<0||64<=i) error(-1);
        mask = make_mask(bitsz-bitpos-bitsize,31);
        make_mask_and_or(mask,tmp,trn,lrn,crn);
        printf("\t%s\t%s, 32(%s)\n",cstore(0),lrn,register_name(adr));
        free_lvar(tmpvar);
        free_register(adr);
#endif
    } else {
        // use_int(adr);
        use_int(value);
        lvalue = get_register();
        code_ld(cload(size,sign),lvalue,0,adr,cext_at(0,0));
        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;
    int m;
// printf("## mask 0x%08x ~0x%08x\n",mask,~mask);
    if ((m=(~mask|c))!=-1) {
        if (is_stage1_const(m,0)>0) {
            printf("\tandi\t%s, %s, %d\n",crn,crn,m);
        } else {
            trn = register_name(tmp=get_register());
            code_const((~mask|c),tmp);
            /* do conjunction  */
            printf("\tand\t%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 (is_stage1_const(c,0)>0) {
            printf("\tori\t%s, %s, %d\n",crn,crn,c);
        } else {
            trn = register_name(tmp=get_register());
            code_const(c,tmp);
            printf("\tor\t%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;
    int tmp;
#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==1) {
#if LONGLONG_CODE
        use_int(adr);
        lvalue = get_lregister();
        lload(adr,lvalue,0);
        crn = register_name(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 = register_name(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);
        free_register(adr);
        free_register(lvalue);
    } else if (l==2) {  // three int container
        char *trn;
/*
                        hhhhhh  mmmmmmmmmmmm  lllllll
   lllll  00000000000  mmmmmmmmmmmm  0000000  hhhhhhh 
  |-----||-----------||------------||-------||-------|
 */
        use_int(adr);
        crn = register_name(adr);
        trn = register_name(tmp = get_register());
        /* shift right */
        lc = lcadr(value);
        /* make and-mask upper */
        code_ld(cload(0,0),tmp,0,adr,cext_at(0,0));
        mask = make_mask(0,31-bitpos);
        make_mask_and_or_const(mask,trn,(int)(lc<<bitpos));
        printf("\t%s\t%s, 0(%s)\n",cstore(0),trn,crn);
        /* store middle */
        code_const((int)(lc>>(32-bitpos)),tmp);
        printf("\t%s\t%s, 16(%s)\n",cstore(0),trn,crn);
        /* make and-mask lower */
        code_ld(cload(0,0),tmp,8,adr,cext_at(0,0));
        mask = make_mask(bitsz-bitpos-bitsize,31);
        make_mask_and_or_const(mask,trn,(int)(lc>>(64-bitpos)));
        printf("\t%s\t%s, 32(%s)\n",cstore(0),trn,crn);
        free_register(tmp);
#endif
    } else {
        use_int(adr);
        lvalue = get_register();
        code_ld(cload(size,sign),lvalue,0,adr,cext_at(0,0));
        crn = register_name(lvalue);
        /* shift left */
        c = cadr(value);
        c <<= bitpos;
        /* make and-mask */
        mask = make_mask(32-bitpos-bitsize,31-bitpos);
        make_mask_and_or_const(mask,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)
{
    if(type0==CHAR||type0==UCHAR) {
        if (n->dsp==0) {
            n->dsp = args;
            if (endian) n->dsp += size_of_int-1;
        }
        args += size_of_int;
    } else if(type0==SHORT||type0==USHORT) {
        if (n->dsp==0) {
            n->dsp = args;
            if (endian) n->dsp += size_of_int-size_of_short;
        }
        args += size_of_int;
    } else if(type0>0&&(car(type0)==UNION||car(type0)==STRUCT)) {
        /* alignment in struct in argument */
        /* should be GCD of member alignment */
        /* __attribute(alignment(16)) is ignored in argments */
        n->dsp = args;
        args += ((sz+(size_of_int-1))&~(size_of_int-1));
    } else {
        /* if (n->dsp==0) (argument list in ADECL is useless, type
           list can be found in type ) */
        n->dsp = args;
        args += sz;
    }
    return args;
}

extern int
code_lvar_alignment(int disp0,NMTBL *n,int type0,int sz) {
    int align;

    /* local variable alignment is done by new_lvar */
    if ((align=attr_value(n,ALIGNED))) {
        if (car(align)!=CONST) error(-1);
        n->sc = LVAR;
        n->dsp = new_lvar_align(sz,caddr(align));
    } else {
        n->sc = LVAR;
        n->dsp = new_lvar(sz);
    }
    return disp;
}



/* end */