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


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

#define UseFPP 1

int arch_mode = UseFPP; 

char *l_include_path[]={
    "/Developer/Zaurus-X-gcc/opt/Embedix/tools/arm-linux/include",
    "/Developer/Zaurus-X-gcc/opt/Embedix/tools/arm-linux/include/linux",
    "/home/zaurus/develop/include",
    "/home/zaurus/develop/include/linux",
    "/home/zaurus/develop/lib/gcc-lib/armv4l-redhat-linux/2.95.1/include/",
    "/usr/include",
    0};

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

static char *init_src0 = "\
/* #define __builtin_va_list int */\n\
/* #define __builtin_va_start(ap,arg) ap=(((int)(&arg))+sizeof(arg)) */\n\
/* #define __builtin_va_arg(ap,type)  (*((type *)ap)++) */\n\
#define __builtin_next_arg(arg)  (__gnuc_va_list)((char *)(&arg) + __va_rounded_size (typeof(arg)))\n\
#define __arm__ 1\n\
#define __STDC__ 1\n\
#define __SIZE_TYPE__ int\n\
#define __attribute__(a)\n\
#define __flexarr\n\
#define __ARM_ARCH_4__\n\
#define __WCHAR_TYPE__ int\n\
#define __alignof__(type) (sizeof(type)==1?1:sizeof(type)==2?2:sizeof(type)<=4?4:8)\n\
#define __PTRDIFF_TYPE__ int\n\
#define __GNUC__ 2\n\
#define __const const\n\
#define alloca __builtin_alloca\n\
#define __restrict\n\
#define __flexarr\n\
#define __const const\n\
#define __THORW\n\
// #define __attribute__(a)\n\
#define __inline__ inline\n\
#define size_t int\n\
#define wchar_t 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 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);
static void code_float_lib(char *opc,int oreg,int in_reg,int e1);
static void code_float_lib_c(char *lib,int from,int to,double value);
static void code_double_lib(char *lib,int to,int reg,int oreg);
static void code_double_lib_c(char *lib,int from,int to,double value);
static void dconst(int l,int h,double value);
static void code_assign_input_double_long(int e1,int e2) ;
static void code_assign_input_float_int(int e1,int e2) ;
#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;


static int freg,ireg,lreg;

int code_lassop_p = 1;

#define SIZE_OF_INT  4
#define SIZE_OF_SHORT  2
#define SIZE_OF_FLOAT  4
#define SIZE_OF_DOUBLE  8
#define SIZE_OF_LONGLONG  8
#define ENDIAN  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];  /* 実際のレジスタの領域 */

/* floating point registers */

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

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

#define REG_ip   13
#define REG_fp   14
#define REG_sp   15
#define REG_VAR_BASE 5
#define REG_VAR_MIN  5
#define REG_VAR_USER_MAX  9     /* at leat 6 tmp var */
#define REG_VAR_MAX 12
#define MIN_TMP_REG 1
#define MAX_TMP_REG 4

#define FREG_VAR_BASE 4
#define FREG_VAR_MIN  4
#define FREG_VAR_MAX  7
#define MIN_TMP_FREG 0
#define MAX_TMP_FREG 3

int MAX_REGISTER=17;             /* ARMのレジスタを10個まで使う*/
int MAX_FREGISTER=8;
#define  REAL_MAX_REGISTER 17    /* ARMのレジスタが32ということ*/
#define  REAL_MAX_FREGISTER 8    /* ARMのレジスタが32ということ*/
#define  REAL_MAX_LREGISTER 6

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

int MAX_INPUT_REGISTER_VAR = 4;
int MAX_INPUT_DREGISTER_VAR = 4;
int MAX_INPUT_FREGISTER_VAR = 4;
int MAX_CODE_INPUT_REGISTER_VAR = 4;
int MAX_CODE_INPUT_DREGISTER_VAR = 6;
int MAX_CODE_INPUT_FREGISTER_VAR = 6;

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

#define RET_REGISTER 1
#define REGISTER_OPERAND  1
#define REGISTER_OPERAND_1  2
#define RET_FREGISTER FREG_OFFSET
#define FREGISTER_OPERAND  (FREG_OFFSET)
#define FREGISTER_OPERAND_1  (FREG_OFFSET+1)

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

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

static int *regs  = arm_regs;

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


static int max_reg_var, max_freg_var;

static char *reg_name[] = { "xx",
    "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", 
    "r8", "r9", "sl", "lr", "ip", "fp", "sp", "pc",
    "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7"
    // "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
    // "s8", "s9", "s10", "s11", "s12", "s13", "s14", "s15",
    "s0", "s2", "s4", "s6", "s8", "s10","s12", "s14", 
    "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
}; 

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

char *rn(int i) { return register_name(i); }
char *fn(int i) { return fregister_name(i); }
char *dn(int i) { return dregister_name(i+8); }
char *lln(int i) { return lregister_name_low(i); }
char *lhn(int i) { return lregister_name_high(i); }
int ll(int i) { return regv_l(i); }
int lh(int i) { return regv_h(i); }

#define is_int_reg(i)  (0<=i&&i<REAL_MAX_REGISTER)
#define is_float_reg(i)  (arch_mode&UseFPP?(REAL_MAX_REGISTER<=i&&i<REAL_MAX_FREGISTER+REAL_MAX_REGISTER):is_int_reg(i))
#define is_longlong_reg(i)  (LREG_OFFSET<=i&&i<LREG_OFFSET+REAL_MAX_LREGISTER+LREG_V)
#define is_double_reg(i)  (arch_mode&UseFPP?is_float_reg(i):is_longlong_reg(i))

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

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

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

static void lmove(int to,int from);
#endif

#define USING_DREG 5
#define INPUT_DREG 6


#if FLOAT_CODE

#define dsuffix(d) (d?'d':'s')

#define  use_float(d,reg) if (reg==USE_CREG) { reg=(arch_mode&UseFPP)?use_float0():(d?use_double0():use_float0());}

static
int use_float0() { 
    int i = creg;
    if (!is_float_reg(i)) {
        if (lreg) { free_register(lreg); lreg = 0; }
        if (!freg) freg = get_dregister(0);
        // else if (freg!=i) free_register(i);
        i = freg;
    }
    if (!regs[i]) regs[i]=USING_REG;
    creg = i;
    return i;
}

static
int use_double0() { 
    int i;
    use_longlong0();
    i = lreg;
    if (!regs[i]) regs[i]=USING_DREG;
    if (!regs[regv_l(i)]) regs[regv_l(i)]=USING_REG;
    if (!regs[regv_h(i)]) regs[regv_h(i)]=USING_REG;
    creg = i;
    return i;
}

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

#endif



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

static void code_save_input_registers(int dots);
static void    set_ireg(int,int);
#if FLOAT_CODE
static void    set_dreg(int,int);
static void    set_freg(int,int);
#endif
#if LONGLONG_CODE
static void    set_lreg(int,int);
#endif
static void inc_inst(int count);

static int max_func_args;
static int my_func_args;





static void jcond(int l, int cond);

#define ARG_LVAR_OFFSET 0x10000000


/*          

     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 (4)
#define arg_offset1 (-16)
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 .L%d, %d\n",r1_offset_label,r1_offsetv);
    } else {
        lvar_offsetv = 
            -12 - max_reg_var*SIZE_OF_INT-max_freg_var*SIZE_OF_DOUBLE;
        printf("\t.set .L%d, %d\n",lvar_offset_label,lvar_offsetv);
    }
    if (max_func_arg_label) {
        printf("\t.set .L%d, %d\n",max_func_arg_label,max_func_args*SIZE_OF_INT);
        max_func_arg_label = 0;
    }


// printf("        @ args = %d, pretend = %d, frame = %d\n",
//      max_func_args,0,align(-disp,16));
// printf("        @ frame_needed = 1, current_function_anonymous_args = 0\n");


#if 0
printf("## vars= %d, regs= %d/%d, args= %d, extra= %d\n",
        align(-disp,16),
        max_reg_var+2,
        max_freg_var,
        align(max_func_args*SIZE_OF_INT,16),
        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_freg_var*SIZE_OF_FLOAT-2*SIZE_OF_INT+
        r1_offsetv);
   printf("## lvar_offset=\t\t%d %d\n",lvar_offsetv,lvar_offsetv%16);
   printf("## min local var=\t%d\n",FUNC_LVAR(0)+lvar_offsetv);
   printf("## max local var=\t%d\n",FUNC_LVAR(disp)+lvar_offsetv);
   printf("## min caller arg var=\t%d\n",
        CALLER_ARG(align(max_func_args*SIZE_OF_INT,16)));
   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("[%s, #0]%s\n",register_name(large_lvar),cext);
        free_register(large_lvar);
        return;
    }
    if (is_code(fnptr)) {
        if (l>=ARG_LVAR_OFFSET) {  /* caller's arguments */
            printf("[sp, #%d]%s\n",CODE_CALLER_ARG(l-ARG_LVAR_OFFSET),cext);
        } else
            printf("[fp, #%d]%s\n",CODE_LVAR(l),cext);
    } else if (l<0) {  /* local variable */
        printf("[fp, #%d+.L%d]%s\n",FUNC_LVAR(l),lvar_offset_label,cext);
    } else if (l>=ARG_LVAR_OFFSET) {  /* caller's arguments */
        printf("[sp, #%d]%s\n",CALLER_ARG(l-ARG_LVAR_OFFSET),cext);
    } else { /* callee's arguments */
        printf("[fp, #%d]%s\n",CALLEE_ARG(l),cext);
    }
}

static void
lvar_address(int l,int creg)
{
    int label,disp;
    int tmp = -1;
    char *trn;
    inc_inst(1);
    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 */
//        printf("\tsub\t%s, fp, #%d\n",register_name(creg), FUNC_LVAR(l));
        trn = register_name(tmp = get_register());
        disp = search_const(LVAR,glist2(lvar_offset_label,FUNC_LVAR(l)),&label);
        printf("\tldr\t%s, .L%d+%d\n",trn,label,disp);
        printf("\tadd\t%s, fp, %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)
{
    int reg;
    /* called only once */

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

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

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



void
gexpr_code_init(void){
}

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


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

    if (in) return;

    function_type(fnptr->ty,&dots);
    while (args) {
        /* process in reverse order */
        n = ncadddr(args);
        type = n->ty;
        if (scalar(type)) {
            if ((reg = get_input_register_var(reg_var,n,is_code0))) {
                n->sc = REGISTER;
                n->dsp = cadr(reg);
                regs[n->dsp]= INPUT_REG;
                reg_var++;
                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[regv_l(i)]= INPUT_REG;
                regs[regv_h(i)]= INPUT_REG;
                reg_var+=2;
                caddr(args)=size(type);
            }
        } else if (type==LONGLONG||type==ULONGLONG) {
            if ((reg = get_input_lregister_var(reg_var,n,is_code0))) {
                n->sc = LREGISTER;
                n->dsp = cadr(reg);
                regs[i=n->dsp]= INPUT_REG;
                regs[regv_l(i)]= INPUT_REG;
                regs[regv_h(i)]= INPUT_REG;
                reg_var+=2;
                caddr(args)=size(type);
            }
        }
        args = cadr(args);
    }
    if (is_function(fnptr))
        code_save_input_registers(dots);
}


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

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

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

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

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

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

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

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

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

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

void

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

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

int
get_input_dregister_var(int i,NMTBL *n,int is_code,int d)
{
    int j;
    if (is_code) {
        if (!(arch_mode&UseFPP)) {
            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);
        } else {
            return get_input_lregister_var(i,n,is_code);
        }
    }
    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)
{
    int ll;
    ll = get_lregister0();
    if (ll!=-1) {
        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++;
        }
#if ENDIAN==0
        regv_l(ll)=i;
        regv_h(ll)=i+1;
#else
        regv_h(ll)=i;
        regv_l(ll)=i+1;
#endif
    } else { error(-1); ll=LREG_OFFSET+2; }
    return list3n(LREGISTER,ll,n);
}

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+1;
    }
    return list3n(REGISTER,i,n);
}


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

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

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


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


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

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


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


void

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

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

#define reg_var_num(i) (REG_VAR_BASE+i)

int
get_register_var(NMTBL *n)
{
    int i,j;
    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),0);
}

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

int
get_dregister_var(NMTBL *n,int d)
{
    int i,j;
    if ((arch_mode&UseFPP)) {
        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_freg_var) max_freg_var=i+1;
                /* その場所を表す番号を返す */
                return list3n(FREGISTER,j,n); 
            }
        }
    }
    return list3n(LVAR,new_lvar(d?SIZE_OF_DOUBLE:SIZE_OF_FLOAT),0);
}

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

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

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

/*
     constant table
        glist3( tag, next, value )
                LVAR            glist2(label,offset)
                GVAR            nptr
                CONST           value
                LABEL           value
     nth element has offset n * SIZE_OF_INT
 */

#define CONST_TBL_COUNT 100

static int
search_const(int tag,int value,int *label)
{
    int p,i,j,list,prev;

    for(j=0;j<2;j++) {
        i = 0;
        if(j==1) {
            if (!const_list_label) const_list_label = fwdlabel();
            *label = const_list_label;
            if (const_list==0) {
                const_list = glist3(tag,0,value);
                return 0;
            } else {
                prev = list = const_list;
            }
        } else {
            prev = list = prev_const_list; *label = prev_const_list_label;
        }
        for(p = list; p ;prev=p,p=cadr(p),i+=SIZE_OF_INT) {
            if (car(p)!=tag) continue;
            switch(tag) {
            case GVAR: case CONST: case LABEL:
                if (caddr(p)!=value) continue;
                return i;
            case LVAR:
                if (car(caddr(p))!=car(value)) continue;
                if (cadr(caddr(p))!=cadr(value)) continue;
                return i;
            }
        }
    }
    cadr(prev) = glist3(tag,0,value);
    if (i>CONST_TBL_COUNT) {
        const_list_table();
    }
    return i;
}

#if FLOAT_CODE
static int
search_double_const(int tag,int value1,int value2,int *label)
{
    int p,i,j,list,prev;

    for(j=0;j<2;j++) {
        i = 0;
        if(j==1) {
            if (!const_list_label) const_list_label = fwdlabel();
            *label = const_list_label;
            if (const_list==0) {
                const_list = glist3(tag,glist3(tag,0,value2),value1);
                return 0;
            } else {
                prev = list = const_list;
            }
        } else {
            prev =list = prev_const_list; *label = prev_const_list_label;
        }
        for(p = list; p ;prev=p,p=cadr(p),i+=SIZE_OF_INT) {
            if (car(p)!=tag) continue;
            switch(tag) {
            case DCONST: case LCONST:
                if (caddr(p)!=value1) continue;
                p = cadr(p);
                if (!p||car(p)!=tag) error(-1);
                if (caddr(p)!=value2) continue;
                return i;
            }
            prev = p;
        }
    }
    cadr(prev) = glist3(tag,glist3(tag,0,value2),value1);
    if (i>CONST_TBL_COUNT) {
        const_list_table();
    }
    return i;
}
#endif

static int inst_count;

static void
const_list_table()
{
    int p,lb=0;
    inst_count = 0;

    if (const_list) {
        if (control) {
            lb = fwdlabel();
            gen_jmp(lb);
            // printf("\t.align\t2\n");
        }
        fwddef(const_list_label);
        control=0;
        for(p = const_list; p ; p = cadr(p)) {
            switch(car(p)) {
            case GVAR:  printf("\t.word\t%s\n",(ncaddr(p))->nm); break;
            case DCONST: case LCONST:
            case CONST: printf("\t.word\t%d\n",caddr(p)); break;
            case LABEL: printf("\t.word\t.L%d\n",caddr(p)); break;
            case LVAR:  printf("\t.word\t.L%d+%d\n",
                car(caddr(p)),cadr(caddr(p))); break;
            default: error(-1);
            }
        }
        if (lb) {
            fwddef(lb);  // control==1 now
        }
        prev_const_list_label=const_list_label;
        const_list_label = 0;
    }
    for(p = prev_const_list; p ; p = cadr(p)) {
        if (car(p)==LVAR) {
            free_glist2(caddr(p));
        }
    }
    free_glist3_a(prev_const_list);
    prev_const_list=const_list;
    const_list = 0;
}

static void
inc_inst(int count)
{
// printf("## inst %d\n",inst_count);
    if ((inst_count+=count)>CONST_TBL_COUNT) {
        inst_count = 0;
        const_list_table();
    }
}

extern void
code_ptr_cache_def(int r, NMTBL *nptr)
{
    int label,disp;
    char *rrn = register_name(r);
    disp = search_const(GVAR,(int)nptr,&label);
    inc_inst(1);
    printf("\tldr\t%s, .L%d+%d\n",rrn,label,disp);
}

#define mask8(d,bit)   (d & (255 << bit))

/*
     mode   CONST or other (CMP is Ok only in stage 1 result)
     *p1    stage 1 const
     *p2    stage 2 const
     *p3    stage 3 const
     return 0 ... require 4 stage
            1 ... positive result
           -1 ... negative result

 */
static int
make_const(int c,int *p1,int *p2,int *p3,int mode)
{
    int sign,im,jm,km;
    int min_stage = 4;
    int msign=0,mim=0,mjm,mkm;
    int id,jd,kd;
    int i,j,k;
    int d;
    if (c == (d = mask8(c,0))) {
        min_stage=1; min_stage=1; msign = 1;
            mim = d;mjm = 0;mkm = 0;
    } else if (mode!=CONST) {
        if (-c == (d = mask8(-c,0))) {
            min_stage=1; min_stage=1; msign = -1;
                mim = d;mjm = 0;mkm = 0;
        }
    } else {
        if (~c== (d = mask8(~c,0))) {
            min_stage=1; min_stage=1; msign = -1;
                mim = d;mjm = 0;mkm = 0;
        }
    }
    for(sign=1;sign>=-1;sign-=2) {
        if (min_stage==1) break;
        for(i=24;i>=0;i-=2) {
            jm = km = 0;
            if (sign>0) {
                if (c == (d = mask8(c,i))) {
                    min_stage=1; min_stage=1; msign = 1;
                        mim = d;mjm = 0;mkm = 0;
                    break;
                }
                id = c - d;
            } else if (mode!=CONST) {
                if (-c == (d = mask8(-c,i))) {
                    min_stage=1; min_stage=1; msign = -1;
                        mim = d;mjm = 0;mkm = 0;
                    break;
                }
                id = -c - d;
            } else {
                if (~c== (d = mask8(~c,i))) {
                    min_stage=1; min_stage=1; msign = -1;
                        mim = d;mjm = 0;mkm = 0;
                    break;
                }
                id = ~c - d;
            }
            if (d==0) continue;
            im = d;
            if (min_stage<=2) continue;
            for(j=i-8;j>=0;j-=2) {
                km = 0;
                if (!(jm=mask8(id,j))) continue;
                jd = id - jm;
                if (jd==0) {
                    min_stage=2; msign = sign;
                    mim = im;mjm = jm;mkm = km;
                    break;
                }
                if (min_stage<=3) continue;
                for(k=j-8;k>=0;k-=2) {
                    if (!(km=mask8(jd,k))) continue;
                    kd = jd - km;
                    if (kd==0) { 
                        min_stage=3; msign = sign;
                        mim = im;mjm = jm;mkm = km;
                        break;
                    }
                }
            }
        }
    }
    if (min_stage<=3) {
        *p1 = mim; *p2= mjm; *p3 = mkm; 
        return msign;
    } else return 0;
}

static int
is_stage1_const(int c,int mode)
{
    int sign,p1=0,p2=0,p3=0;
    sign = make_const(c,&p1,&p2,&p3,mode);
    return (c==0||(p1&&!p2&&!p3))?sign:0;
}


static void 
code_const0(int e2,int reg,char *opt)
{
    char *crn,*add,*mov;
    int s,p1,p2,p3;
    int label,disp;

    use_int(reg);
    crn = register_name(reg);
    inc_inst(3);
    if ((s=make_const(e2,&p1,&p2,&p3,CONST))) {
        add = s>0?"add":"sub";
        mov = s>0?"mov":"mvn";
        printf("\t%s\t%s, #%d\n",mov,crn,p1);
        if (p2) printf("\t%s\t%s, %s, #%d\n",add,crn,crn,p2);
        if (p3) printf("\t%s\t%s, %s, #%d\n",add,crn,crn,p3);
    } else {
        disp = search_const(CONST,e2,&label);
        printf("\tldr\t%s, .L%d+%d%s\n",crn,label,disp,opt);
    }
}

extern void 
code_const(int e2,int reg) {
    code_const0(e2,reg,"");
}

static void 
code_add(int reg,int offset,int r)
{
    char *crn = register_name(reg);
    char *rrn = register_name(r);
    char *drn;
    int dreg;
    int s,p1,p2,p3;
    char *add;
    int label,disp;
    inc_inst(3);
    if (offset==0) {
        if(r!=reg)
            printf("\tmov\t%s, %s\n",crn,rrn);
    } else if ((s=make_const(offset,&p1,&p2,&p3,0))) {
        add = s>0?"add":"sub";
        printf("\t%s\t%s, %s, #%d\n",add,crn,rrn,p1);
        if (p2) printf("\t%s\t%s, %s, #%d\n",add,crn,crn,p2);
        if (p3) printf("\t%s\t%s, %s, #%d\n",add,crn,crn,p3);
    } else {
        disp = search_const(CONST,offset,&label);
        drn = register_name(dreg = get_register());
        printf("\tldr\t%s, .L%d+%d\n",drn,label,disp);
        printf("\tadd\t%s, %s, %s\n",crn,drn,rrn);
        free_register(dreg);
    }
}


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

static void 
code_ldf(char *ld,char *crn,int offset,int r,char *cext)
{
    char *rrn = register_name(r);
    char *orn;
    int reg;
    inc_inst(1);
    if (-1024<offset&&offset<1024) {
        printf("\t%s\t%s, [%s, #%d]%s\n",ld,crn,rrn,offset,cext);
    } else {
        orn = register_name(reg=get_register());
        code_add(reg,offset,r);
        printf("\t%s\t%s, [%s, #0]%s\n",ld,crn,orn,cext);
        free_register(reg);
    }
}


#define cload(sz,sign) \
    (sz==1?(sign?"ldrsb":"ldrb"):sz==SIZE_OF_SHORT?(sign?"ldrsh":"ldrh"):"ldr")


#define cext(sign,sz,reg)

static char *
cext_at(int sz,int sign) 
{
    return ((sz==1&&!sign)?"  @ zero_extendqisi2":"");
}

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

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

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

void
code_crgvar(int e1,int reg,int sign,int sz){
    use_int(reg);
    code_ld(cload(sz,sign),reg,cadr(e1),get_ptr_cache(ncaddr(e1)),
        cext_at(sz,sign));
    cext(sign,sz,reg);
}


void
code_register(int e2,int reg) {
    use_int(reg);
    // reg = e2
    if (reg!=e2) {
        inc_inst(1);
        printf("\tmov\t%s, %s\n",register_name(reg),register_name(e2));
    }
}


void
code_rlvar(int e2,int reg) {
    use_int(reg);
    inc_inst(1);
    lvar_intro(e2);
    printf("\tldr\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);
    inc_inst(1);
    printf("and\t%s, %s, #255\n",register_name(reg),register_name(reg));
}

extern void
code_u2us(int reg)
{   
    use_int(reg);
    inc_inst(2);
    printf("bic\t%s, %s, #16711680\n",register_name(reg),register_name(reg));
    printf("bic\t%s, %s, #-16777216\n",register_name(reg),register_name(reg));
}

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

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

void
code_label_value(int label,int reg) {
    int lb,disp;
    use_int(reg);
    disp = search_const(LABEL,label,&lb);
    inc_inst(1);
    printf("\tldr\t%s, .L%d+%d\n",register_name(reg),lb,disp);
    return;
}

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


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


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

    inc_inst(3);
    printf("\tcmp\t%s, #0\n", register_name(creg));
    printf("\tmovne\t%s, #0\n", register_name(creg));
    printf("\tmoveq\t%s, #1\n", 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);
    }
//        ldrb    r2, [ip, #-1]!  @ zero_extendqisi2
//        これはoffsetだから、12bit!
    code_ld(cload(sz,sign),reg,0,xreg,cext_at(sz,sign));
    code_add(reg,dir,reg);
    code_ldf(cstore(sz),drn,0,xreg,sz==SIZE_OF_SHORT?"  @ movhi":"");
}


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);
    }
//_1:     ldrb    r4, [r0], #1            @ zero_extendqisi2 *buffer++
    code_ld(cload(sz,sign),reg,0,xreg,cext_at(sz,sign));
    code_add(nreg,dir,reg);
    code_ldf(cstore(sz),nrn,0,xreg,sz==SIZE_OF_SHORT?"  @ movhi":"");

    free_register(nreg);
}


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

    disp = search_const(LABEL,retcont,&label);
    inc_inst(1);
    printf("\tldr\t%s, .L%d+%d\n",crn,label,disp);
}

void
code_environment(int creg) {
    /* save frame pointer */
    if (is_code(fnptr)) {
        inc_inst(1);
        use_int(creg);
        printf("\tmov\t%s, fp\n",register_name(creg));
    } else {
        int disp,label;
        char *trn = register_name(REG_ip);
        inc_inst(2);
        use_int(creg);
        disp = search_const(LVAR,glist2(lvar_offset_label,FUNC_LVAR(0)),&label);
        printf("\tldr\t%s, .L%d+%d\n",trn,label,disp);
        printf("\tadd\t%s, fp, %s\n",register_name(creg),trn);
    }
}

static int rexpr_bool(int e1, int reg);
#if LONGLONG_CODE
static int lrexpr_bool(int e1, int reg);
#endif

void
code_bool(int e1,int reg) {
    int e2,e3;
    char *xrn;
    if (rexpr_bool(e1, reg)) return;
#if LONGLONG_CODE
    if (lrexpr_bool(e1, reg)) return;
#endif
    b_expr(e1,1,e2=fwdlabel(),1);  /* including > < ... */
    if (use) {
        inc_inst(2);
        use_int(reg);
        xrn = register_name(reg);
        printf("\tmov\t%s, #0\n",xrn);
        gen_jmp(e3=fwdlabel());
        fwddef(e2);
        printf("\tmov\t%s, #1\n",xrn);
        fwddef(e3);
    } else {
        fwddef(e2);
    }
}


void
code_cmp_crgvar(int e1,int reg,int sz,int label,int cond) {
    use_int(reg);
    code_ld(cload(sz,0),reg,cadr(e1),get_ptr_cache(ncaddr(e1)),
        cext_at(sz,0));
    cext(0,sz,r);
    inc_inst(1);
    printf("\tcmp\t%s, #0\n",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);
    inc_inst(1);
    printf("\t%s\t%s, ",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_ld("ldr",reg,cadr(e1),get_ptr_cache(ncaddr(e1)),"");
    code_cmp_register(reg,label,cond);
}


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


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


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

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

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

    disp = search_const(LABEL,lb,&label);
    printf("\tldr\t%s, .L%d+%d\n",crn,label,disp);
    inc_inst(1);
    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:
        inc_inst(2);
        printf("\tldrb\t%s, [%s,#%d]\n",drn,frn,offset);
        printf("\tstrb\t%s, [%s,#%d]\n",drn,trn,offset);
        break;
    case 2: case -2:
        inc_inst(2);
        printf("\tldrh\t%s, [%s,#%d]\n",drn,frn,offset);
        printf("\tstrh\t%s, [%s,#%d]\n",drn,trn,offset);
        break;
    case 4: case -4:
        inc_inst(2);
        printf("\tldr\t%s, [%s,#%d]\n",drn,frn,offset);
        printf("\tstr\t%s, [%s,#%d]\n",drn,trn,offset);
        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 */
        inc_inst(1);
        // offset have to be ignored */
        printf("\tbl\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; ireg=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 = 1-CALLER_ARG(arg_disp-ARG_LVAR_OFFSET)/SIZE_OF_INT;
    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) {
                inc_inst(2);
                printf("\tldr\t%s, [%s, #%d]\n",srn,crn,count+on_register*SIZE_OF_INT);
                printf("\tstr\t%s, [%s, #%d]\n",srn,drn,count);
            }
            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,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, [%s, #%d]\n", cload(0,0),
                register_name(arg_reg), crn,count);
        use_input_reg(arg_reg,1);
    }
    return length/SIZE_OF_INT;
}

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

static void
set_freg(int reg,int mode)
{
    if (!is_float_reg(reg)) error(-1);
    if (reg!=creg) {
        if (freg && reg!=freg) {
            free_register(freg);
            if (mode) {
                inc_inst(1);
                printf("\t%s\t%s, %s\n",
                    (arch_mode&UseFPP)?"mvfs":"mov",
                    fregister_name(reg),fregister_name(freg));
            }
        }
        // if (creg!=ireg) free_register(creg);
        regs[reg]=USING_REG;
    }
    creg = freg = reg;
}

#if LONGLONG_CODE||FLOAT_CODE
static void
set_lreg0(int reg,int mode)
{
    if (reg!=creg) {
        if (lreg && reg!=lreg) {
            if (mode) {
                inc_inst(2);
                lmove(reg,lreg);
            }
            free_register(lreg);
        }
        if (creg!=lreg) {
            free_register(creg);
            if (creg==ireg) ireg = 0;
        }
        regs[reg]=USING_REG;
        clear_ptr_cache_reg(regv_l(reg));
        regs[regv_l(reg)]=USING_REG;
        clear_ptr_cache_reg(regv_h(reg));
        regs[regv_h(reg)]=USING_REG;
    }
    creg = lreg = reg;
}
#endif

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

#if FLOAT_CODE
static void
set_dreg(int reg,int mode)
{
    if ((arch_mode&UseFPP)) {
        if (reg==RET_DREGISTER) {
            reg = RET_FREGISTER;
        }
        if (is_float_reg(reg)) {
            set_freg(reg,mode);
        }
        regs[reg]=USING_REG;
        return;
    }
    if (reg==RET_DREGISTER) {
        regv_l(reg) = RET_DREGISTER_L;
        regv_h(reg) = RET_DREGISTER_H;
    } else if (reg==DREGISTER_OPERAND) {
        regv_l(reg) = DREGISTER_OPERAND_L;
        regv_h(reg) = DREGISTER_OPERAND_H;
    } else if (reg==DREGISTER_OPERAND_1) {
        regv_l(reg) = DREGISTER_OPERAND_1_L;
        regv_h(reg) = DREGISTER_OPERAND_1_H;
    }
    set_lreg0(reg,mode);
    regs[regv_l(reg)]=USING_DREG;
    regs[regv_h(reg)]=USING_DREG;
}

static void
set_lreg_operand(int reg,int mode)
{
    // save_stack,clear_ptr_cache is assumed    
    if (!is_longlong_reg(reg)) { error(-1); return; }
    if (mode) {
        lmove(LREGISTER_OPERAND,reg);
    }
}

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


#endif

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

void
code_save_input_registers(int dots)
{
    int args;
    NMTBL *n;
    int reg;
    int tag;
    /* 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 = align(len,4);
            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 (is_int_reg(reg)) {
        if (ireg&&reg == ireg) {
            if (creg==ireg) creg = 0;
            ireg = 0;
        } 
        if (lreg) {
            if (regv_l(lreg)==reg) {
                regs[lreg]=0;
                if (regv_h(lreg)>reg&&
                    (regs[regv_h(lreg)]==USING_REG||
                     regs[regv_h(lreg)]==USING_DREG))
                  {
                    free_register(regv_h(lreg));
                }
                if (creg==lreg) creg = ireg;
                free_register(lreg);
                lreg = 0;
            } else if (regv_h(lreg)==reg) {
                regs[lreg]=0;
                if (regv_h(lreg)>reg && (
                    (regs[regv_l(lreg)]==USING_REG) ||
                    (regs[regv_l(lreg)]==USING_DREG) ))
                  {
                    free_register(regv_l(lreg));
                }
                if (creg==lreg) creg = ireg;
                free_register(lreg);
                lreg = 0;
            }
        }
    } else if (is_longlong_reg(reg)) {
        use_input_reg(regv_h(reg),0);
        use_input_reg(regv_l(reg),0);
    } else if (is_double_reg(reg)) {
        use_input_reg(regv_h(reg),0);
        use_input_reg(regv_l(reg),0);
    } else if (is_float_reg(reg)) {
        if (freg&&reg == freg) {
            if (creg==freg) creg = ireg;
            freg = 0;
        } 
    }
    if (mode) use_reg(reg);
}

static void
code_assign_input_float_int(int e1,int e2) {
#if FLOAT_CODE
    int r,tmp=-1;
    float f;
    char *frn;
    //  e1 = e2;
    if (car(e1)!=REGISTER) { error(-1); return; }
    frn = register_name(r=cadr(e1));
    switch(car(e2)) {
    case FCONST:
        f = dcadr(e2);
        code_const(*((int*)&f),r);
        break;
    case FRGVAR:
        code_rgvar(e2,r);
        break;
    case FRLVAR:
        code_rlvar(cadr(e2),r);
        break;
    default:
        g_expr(rvalue_t(e2,FLOAT));
    case FREGISTER:
        tmp = new_lvar(SIZE_OF_INT);
        code_dassign_lvar(tmp, (car(e2)==FREGISTER)?cadr(e2):freg,0);
        code_rlvar(tmp,r);
        if (tmp!=-1) free_lvar(tmp);
    }
#endif
}

static void
code_assign_input_double_long(int e1,int e2) {
#if FLOAT_CODE
    int r,tmp=-1,reg=0;
    double value;
    //  e1 = e2;
    if (car(e1)!=LREGISTER) { error(-1); return; }
    r=cadr(e1);
    switch(car(e2)) {
    case DCONST:
        value = dcadr(e2);
        dconst(regv_l(r),regv_h(r),value);
        break;
    case DRGVAR:
        code_lrgvar(e2,r);
        break;
    case DRLVAR:
        code_lrlvar(cadr(e2),r);
        break;
    default:
        g_expr(rvalue_t(e2,DOUBLE));
        reg = freg;
    case DREGISTER:
        if (car(e2)==DREGISTER) reg = cadr(e2);
        printf("\tstfd\t%s, [sp, #-8]!\n",fregister_name(reg));
        printf("\tldmfd\tsp!, {%s, %s}\n",lregister_name_low(r),lregister_name_high(r));
    }
    if (tmp!=-1) free_lvar(tmp);
#endif
}

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

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

#define AS_SAVE 1
#define AS_ARG  0

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

static void
code_call(int e2,NMTBL *fn,int jmp)
{
    if (car(e2) == FNAME) {
        inc_inst(1);
        printf("\tbl\t%s\n",fn->nm);
    } else {
        inc_inst(3);
        printf("\tmov\tlr, pc\n");
        printf("\tmov\tpc, %s\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 (car(e4)==INDIRECT) e4 = cadr(e4);
                if (!simple_arg(e4)) {
                    // Calculate complex struct address here.
                    // If simple, leave it.
                    arg = get_register_var(0);
                    g_expr_u(assign_expr0(arg,e4,INT,INT));
                    car(e3)=arg;
                    reg_arg_list = list2(arg,reg_arg_list);
                    if (car(arg)==REGISTER) use_input_reg(cadr(arg),1);
                    else car(e3) = rvalue_t(arg,INT);
                }
            }
            stargs = list4(e3,stargs,nargs,reg_arg);
        }
        increment_function_arg(e3,&nargs,&reg_arg,&freg_arg);
    }

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

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

    if (stargs) {
        if (complex_) {
            arg = get_input_arg(caddr(complex_),AS_SAVE,
                                    pnargs,preg_arg,pfreg_arg);
            reg_arg_list = compute_complex_arg(complex_,reg_arg_list,arg);
        }
        for(stargs=reverse0(stargs);stargs;stargs = cadr(stargs)) {
            e3 = car(stargs);
            e4 = car(e3);
            t  = caddr(e3);
            arg = get_input_arg(t,AS_ARG,caddr(stargs),cadddr(stargs),0);
            push_struct(e4,t,arg);
            car(e3)=0;  // done
            if (car(arg)==REGISTER)   // 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)==LREGISTER) {
                use_input_reg(cadr(arg),1);
                code_assign_input_double_long(arg, e4);
            } 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 */
                code_assign_input_float_int(arg, e4);
            } else {
                g_expr_u(assign_expr0(arg,e4,t,t)); /* XXX */
            }
        }
        // structs are finished
    }
    if (max_func_args<nargs) max_func_args=nargs;
    for(;arg_assign;arg_assign=cadr(arg_assign)) {
        g_expr_u(car(arg_assign));
    }
    clear_ptr_cache();
    code_call(e2,fn,jmp);
    free_register_var(reg_arg_list);
    for(i=1;i<MAX_INPUT_REGISTER_VAR;i++) {
        free_register(i);
    }
    if (ret_type==DOUBLE) {
#if FLOAT_CODE
        set_dreg(RET_DREGISTER,0);
        use_reg(RET_DREGISTER);
#endif
    } else if (ret_type==FLOAT) {
#if FLOAT_CODE
        set_freg(RET_FREGISTER,0);
#endif
    } else if (ret_type==ULONGLONG||ret_type==LONGLONG) {
#if LONGLONG_CODE
        set_lreg(RET_LREGISTER,0);
        use_reg(RET_LREGISTER);
#endif
    } else if (ret_type==VOID) {
    } else {
        set_ireg(RET_REGISTER,0);
    }
    return ret_type;
}

void
code_alloca(int e1,int reg)
{
    char *crn;
  
    g_expr(list3(BAND,list3(ADD,e1,list2(CONST,15)),list2(CONST,~15))); 
    use_int(reg);
    crn = register_name(reg);
    inc_inst(2);
    printf("\trsb\tsp, %s, sp\n",crn);
    if (!max_func_arg_label) max_func_arg_label = fwdlabel();
    code_label_value(max_func_arg_label ,REG_ip);
    printf("\tadd\t%s, sp, ip\n",crn);
}

void
code_frame_pointer(int e3) {
    use_int(e3);
    inc_inst(1);
    printf("\tmov\tfp, %s\n",register_name(e3));
}

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

void
code_fix_frame_pointer(int env) {
    char *trn;
    int disp,label;

    if (is_function(fnptr) && ! env) {
        trn = register_name(REG_ip);
        disp = search_const(LVAR,glist2(lvar_offset_label,FUNC_LVAR(0)),&label);
        printf("\tldr\t%s, .L%d+%d\n",trn,label,disp);
        printf("\tadd\tfp, fp, %s\n",trn);
    }
}

static void
code_unfix_frame_pointer()
{
    char *trn;
    int disp,label;

    trn = register_name(REG_ip);
    disp = search_const(LVAR,glist2(lvar_offset_label,FUNC_LVAR(0)),&label);
    printf("\tldr\t%s, .L%d+%d\n",trn,label,disp);
    printf("\tsub\tfp, fp, %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_freg_var = FREG_VAR_MAX-FREG_VAR_MIN;
    inc_inst(1);
    printf("\tb\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_freg_var = FREG_VAR_MAX-FREG_VAR_MIN;
    use_int(e2);
    inc_inst(1);
    printf("\tmov\tpc, %s @ indirect jump\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_at(sz,sign));
    cext(sign,sz,reg);
}

#if FLOAT_CODE
int
code_drindirect(int e1, int reg,int offset, int d)
{
    int xreg;
    if(!(arch_mode&UseFPP)) {
        if (d) {
            code_lrindirect(e1,reg,offset,1);
            return DOUBLE;
        } else {
            code_rindirect(e1,reg,offset,0,0);
            return FLOAT;
        }
    }
    g_expr(e1);
    if (!is_int_reg(creg)) error(-1);
    xreg = creg;
    use_float(d,reg);
    code_ldf(d?"ldfd":"ldfs",register_name(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);
    inc_inst(2);
#if ENDIAN_L==0
    if (creg!=regv_l(reg)) {
        printf("\tldr\t%s, [%s, #%d]\n",lregister_name_low(reg),crn,offset);
        printf("\tldr\t%s, [%s, #%d]\n",lregister_name_high(reg),crn,offset+SIZE_OF_INT);
    } else {
        printf("\tldr\t%s, [%s, #%d]\n",lregister_name_high(reg),crn,offset+SIZE_OF_INT);
        printf("\tldr\t%s, [%s, #%d]\n",lregister_name_low(reg),crn,offset);
    }
#else
    if (creg!=regv_l(reg)) {
        printf("\tldr\t%s, [%s, #%d]\n",lregister_name_low(reg),crn,offset+SIZE_OF_INT);
        printf("\tldr\t%s, [%s, #%d]\n",lregister_name_high(reg),crn,offset);
    } else {
        printf("\tldr\t%s, [%s, #%d]\n",lregister_name_high(reg),crn,offset);
        printf("\tldr\t%s, [%s, #%d]\n",lregister_name_low(reg),crn,offset+SIZE_OF_INT);
    }
#endif
}


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

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


static void
lstore(int e2,int creg)
{
    char *drn = register_name(e2);
    char *crn_h;
    char *crn_l;
    crn_h = lregister_name_high(creg);
    crn_l = lregister_name_low(creg);
    inc_inst(2);
#if ENDIAN_L==0
    printf("\tstr\t%s, [%s, #0]\n",crn_l,drn);
    printf("\tstr\t%s, [%s, #%d]\n",crn_h,drn,SIZE_OF_INT);
#else
    printf("\tstr\t%s, [%s, #0]\n",crn_h,drn);
    printf("\tstr\t%s, [%s, #%d]\n",crn_l,drn,SIZE_OF_INT);
#endif
}

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

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

static char *
cstore(int sz)
{
    switch(sz) {
    case 1: return "strb";
    case SIZE_OF_SHORT: return "strh";
    default: return "str";
    }
}

void
code_assign_gvar(int e2,int creg,int byte) {
    use_int(creg);
    code_ldf(cstore(byte),register_name(creg),cadr(e2),
        get_ptr_cache(ncaddr(e2)),byte==SIZE_OF_SHORT?" @ movhi":"");
}

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

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

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

    inc_inst(1);
    printf("\t%s\t%s, [%s, #0]\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, [%s, #0]\n",cstore(byte),crn,drn);
    free_register(edx);
    free_register(creg);
    emit_pop_free(xreg);
    inc_inst(1);
}

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:
        inc_inst(1);
        printf("\tadd\t%s, %s, %s\n",crn,crn,orn);
        break;
    case SUB:
        inc_inst(1);
        printf("\tsub\t%s, %s, %s\n",crn,crn,orn);
        break;
    case CMP:
        inc_inst(1);
        printf("\tcmp\t%s, %s\n",crn,orn);
        break;
    case BAND: 
        inc_inst(1);
        printf("\tand\t%s, %s, %s\n",crn,crn,orn);
        break;
    case EOR: 
        inc_inst(1);
        printf("\teor\t%s, %s, %s\n",crn,crn,orn);
        break;
    case BOR:
        inc_inst(1);
        printf("\torr\t%s, %s, %s\n",crn,crn,orn);
        break;
    case MUL:
    case UMUL:
        /* target!=source */
        inc_inst(1);
        printf("\tmul\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("\tmov\t%s, %s, asl #%d\n",crn,crn,v);
        break;
    case DIV:
        v=ilog(v);
    case RSHIFT:
        printf("\tmov\t%s, %s, asr #%d\n",crn,crn,v);
        break;
    case UDIV:
        v=ilog(v);
    case URSHIFT:
        printf("\tmov\t%s, %s, lsr #%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("\tcmp\t%s,%d\n",crn,v); break;
    case BOR: printf("\torr\t%s, %s, #%d\n",crn,crn,v); break;
    case EOR: printf("\teor\t%s, %s, #%d\n",crn,crn,v); break;
    case BAND: printf("\tand\t%s, %s, #%d\n",crn,crn,v); break;
    default:
        error(-1);
    }
    inc_inst(1);
}

void
shift(char *op, int creg, int reg)
{
    char *crn;
    char *rrn = register_name(reg);
    use_int(creg);
    crn = register_name(creg);
    inc_inst(1);
    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);

    inc_inst(2);
    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("\tcmp\t%s, %s\n",crn,rn);
    } else {
        if (sign>0) 
            printf("\tcmp\t%s, #%d\n",crn,e);
        else
            printf("\tcmn\t%s, #%d\n",crn,-e);
    }
    switch(cond) {
    case -1: break;
    case 1:
        printf("\tbne\t.L%d\n",label); break;
    case 0:
        printf("\tbeq\t.L%d\n",label); break;
    case LT:
        printf("\tblt\t.L%d\n",label); break;
    case GT:
        printf("\tbgt\t.L%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("\tcmp\t%s, #%d\n",rn1,r2);
        else
            printf("\tcmn\t%s, #%d\n",rn1,-r2);
    } else {
        rn1 =           register_name(r1);
        rn2 =           register_name(r2);
        printf("\tcmp\t%s, %s\n",rn1,rn2);
    }

    switch(op+(!cond)*BNOT) {
    case GT:  case LE+BNOT:     cc="gt"; ncc="le"; break;
    case LE:  case GT+BNOT:     cc="le"; ncc="gt"; break;
    case GE:  case LT+BNOT:     cc="ge"; ncc="lt"; break;
    case LT:  case GE+BNOT:     cc="lt"; ncc="ge"; break;
    case UGT: case ULE+BNOT:    cc="hi"; ncc="ls"; break;
    case ULE: case UGT+BNOT:    cc="ls"; ncc="hi"; break;
    case UGE: case ULT+BNOT:    cc="hs"; ncc="lo"; break;
    case ULT: case UGE+BNOT:    cc="lo"; ncc="hs"; break;
    case EQ:  case NEQ+BNOT:    cc="eq"; ncc="ne"; break;
    case NEQ: case EQ+BNOT:     cc="ne"; ncc="eq"; break;
    default:        error(-1);
    }

    if (mode==COND_BRANCH||mode==COND_BRANCH_CONST) {
        printf("\tb%s\t.L%d\n",cc,l1);
        inc_inst(2);
    } else if (mode==COND_VALUE||mode==COND_VALUE_CONST) {
        rn0 =           register_name(r0);
        printf("\tmov%s\t%s, #0\n",ncc,rn0);
        printf("\tmov%s\t%s, #1\n",cc,rn0);
        inc_inst(3);
    } 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 = ireg;
        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 = ireg;
        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)),ireg,0,cond,l1,COND_BRANCH_CONST);
    } else {
        g_expr(cadr(e1));
        emit_push();
        g_expr(caddr(e1));
        e2 = emit_pop(1);
        pcond(op, ireg,e2,0,cond,l1,COND_BRANCH);
        emit_pop_free(e2);
    }
    return l1;
}

#define CMP_C1T (-1)

static void
jcond(int l, int cond)
{       
    inc_inst(1);
    printf("\tb%s\t.L%d\n",cond?"ne":"eq",l);
}

void
jmp(int l)
{       
    printf("\tb\t.L%d\n",l);
    if (inst_count>CONST_TBL_COUNT/2) {
        const_list_table();
    } else
        inc_inst(1);
}

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

static int
code_register_save(int reg_save,int freg_save,int disp)
{
    int i;
    inc_inst(1);
    printf("\tstmfd\tsp!, {");
    for (i=reg_var_num(0);i<reg_var_num(reg_save);i++) {
            printf("%s, ",register_name(i));
    }
    printf("fp, ip, lr, pc}\n");
    if (freg_save>0) {
        inc_inst(1);
        printf("\tsfmfd\tf4, %d, [sp]!\n",freg_save);
    }
    return disp;
}

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("\tlfm\tf4, %d, [fp, #%d]\n",freg_save,-i);
        inc_inst(1);
    }
    inc_inst(1);
    printf("\tldmea\tfp, {");
    for (i=reg_var_num(0);i<reg_var_num(reg_save);i++) {
            printf("%s, ",register_name(i));
    }
    printf("fp, sp, pc}\n");
    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("\tadd\tsp, ip, fp\n");
    clear_ptr_cache();
}


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

void
code_leave(char *name)
{
    code_offset_set(fnptr);
    local_table();
    printf("\t.size\t%s,.L%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\tip, sp\n");
    gen_jmp(entry_label = fwdlabel());
    register_save_return_label = backdef();
    clear_ptr_cache();
}

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

void
code_label_call(int l)
{
        printf("\tbl\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 = freg = cadr(get_input_dregister_var(0,0,1,0));
        set_freg(RET_FREGISTER,1);
#endif
        printf("\tmov\t%s, %s\n",register_name(
                cadr(get_input_register_var(1,0,0))
                ),register_name(REG_fp));
    } else if (cadr(fnptr->ty)==DOUBLE) {
#if FLOAT_CODE
        creg = lreg = cadr(get_input_dregister_var(0,0,1,1));
        set_dreg(RET_DREGISTER,1);
        printf("\tmov\t%s, %s\n",register_name(
                cadr(get_input_register_var(2,0,0))
                ),register_name(REG_fp));
#endif
    } else if (cadr(fnptr->ty)>0&&(
        car(cadr(fnptr->ty))==STRUCT ||
        car(cadr(fnptr->ty))==UNION)) {
        int sz = size(cadr(fnptr->ty));
        inc_inst(3);
        code_const(sz,REGISTER_OPERAND);
        printf("\tsub\tr1, r2, fp\n");
        printf("\tldr\tr0, [fp, #%d]\n",(my_func_args-1)*SIZE_OF_INT);
        printf("\tmov\t%s, %s\n",register_name(
                cadr(get_input_register_var(1,0,0))
                ),register_name(REG_fp));
        // emit_copy(6,3,sz,0,1,1);
    } else if (cadr(fnptr->ty)!=VOID) {
        creg = ireg = cadr(get_input_register_var(0,0,1));
        if (creg!=RET_REGISTER)
            set_ireg(RET_REGISTER,1);
        printf("\tmov\t%s, %s\n",register_name(
                cadr(get_input_register_var(1,0,1))
                ),register_name(REG_fp));
    }
    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_freg_var,0);

//  leave part end

//  entry part  (save register)

    fwddef(entry_label);
    if (arg_on_register>0)
        printf("\tsub\tsp, sp, #%d\n",arg_on_register);
    code_register_save(max_reg_var,max_freg_var,0);

    printf("\tsub\tfp, ip, #%d\n",4+arg_on_register);
    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,.L%d-%s\n",name, backdef(),name);

    free_all_register();
}


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

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

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

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

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

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(".L%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 .L%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(".L%d:\n",lb);
        printf("\t.size\t%s,.L%d-%s\n",n->nm,lb,n->nm);
#endif
    }
}


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

void
global_table(void)
{
    NMTBL *n;
    int init;
    init=0;
    for(n=global_list;n;n=n->next) {
        if ((n->sc == GVAR) && n->dsp != -1) {
            /* n->dsp = -1 means initialized global */
            if (init==0) {
                data_mode(0);
                init=1;
            }
            comm(n);
        } else if ((n->sc==STATIC) && n->dsp != -1) {
            /* 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);
     inc_inst(1);
     printf("\tbl\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);
    inc_inst(1);
    printf("\tbl\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);

     inc_inst(2);
    if (!(arch_mode&UseFPP)) {
        code_save_stacks();
        clear_ptr_cache();
        if (d) {
            set_dreg(DREGISTER_OPERAND,0);
            dconst(DREGISTER_OPERAND_1_L,DREGISTER_OPERAND_1_H,0.0);
            extern_conv("__nedf2");
            printf("\tcmp\tr0, #0\n");
        } else {
            set_freg(FREGISTER_OPERAND,0);
            printf("\tmov\t%s, #0\n", register_name(REGISTER_OPERAND)); // fconst(REGISTER_OPERAND,0.0);
            extern_conv("__nesf2");
            printf("\tcmp\tr0, #0\n");
        }
    } else {
        frn = register_name(e2);
        printf("\tcmf\t%s, #0\n",frn);
    }
    jcond(label,cond);
    return;
}

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

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

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


void
code_dregister(int e2,int freg,int d)
{
    use_float(d,freg);
    if (!(arch_mode&UseFPP)) {
        if (d) {
            code_lregister(e2,freg); set_double(freg); return;
        } else {
            code_register(e2,freg); set_float(freg); return;
        }
    } else {
        if (freg!=e2) {
            if (is_int_reg(e2)) error(-1);
            inc_inst(1);
            printf("\t%s\t%s, %s\n",movef(d),
                fregister_name(freg),fregister_name(e2));
        }
    }
}

void
code_dassign_gvar(int e2,int freg,int d)
{ 
    if (!(arch_mode&UseFPP)) {
        if (d) {
             code_lassign_gvar(e2,freg);  set_double(freg); return;
        } else {
            code_assign_gvar(e2,freg,0); set_float(freg); return;
        }
    } else {
        use_float(d,freg);
        code_ldf(fstore(d),fregister_name(freg),cadr(e2),
            get_ptr_cache(ncaddr(e2))," @ float");
    }
}

void
code_dassign_lvar(int e2,int freg,int d)
{ 
    if (!(arch_mode&UseFPP)) {
        if (d) {
            code_lassign_lvar(e2,freg); set_double(freg); return;
        } else {
            code_assign_lvar(e2,freg,0); set_float(freg); return;
        }
    } else {
        use_float(d,freg);
        lvar_intro(e2);
        inc_inst(1);
        printf("\t%s\t%s, ",fstore(d),fregister_name(freg));
        lvar(e2,"@ float");
    }
}

void
code_dassign(int e2,int freg,int d)
{ 
    if (!(arch_mode&UseFPP)) {
        if (d) {
            code_lassign(e2,freg); set_double(freg); return;
        } else {
            code_assign(e2,freg,0); set_float(freg); return;
        }
    }
    use_float(d,freg);
    inc_inst(1);
    printf("\t%s\t%s, [%s, #0] @ float\n",fstore(d),
        fregister_name(freg),register_name(e2));
}

void
code_dassign_dregister(int e2,int d,int freg) {
    if (!(arch_mode&UseFPP)) {
        if (d) {
            code_lassign_lregister(e2,freg); set_double(freg); return;
        } else {
            code_assign_register(e2,0,freg); set_float(freg); return;
        }
    }
    use_float(d,freg);
    if (e2!=freg) {
        inc_inst(1);
        printf("\t%s\t%s, %s\n",movef(d),
                fregister_name(e2),fregister_name(freg));
    }
}

static double d0 = 1.0;

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

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

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

static void
dconst(int l,int h,double value)
{
#if ENDIAN_D==0
    code_const(code_d1(value),l);
    code_const(code_d2(value),h);
#else
    code_const(code_d1(value),h);
    code_const(code_d2(value),l);
#endif
}

static void
fconst(int reg,double value)
{
    float f = value;
    code_const(*((int*)&f),reg);
}

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

    use_float(d,freg);
    if (!(arch_mode&UseFPP)) {
        if (d) {
            dconst(regv_l(freg),regv_h(freg),value);
        } else {
            code_const0(*((int*)&f),freg,"\t@ float");
        }
    } else {
        frn = register_name(freg);
        inc_inst(1);
        if (value==0 || value==1 || value==10) {
            printf("\t%s\t%s, #%d\n",movef(d),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("\tldfd\t%s, .L%d+%d\n",frn,label,disp);
        } else {
            disp = search_const(CONST,*((int*)&f),&label);
            printf("\tldfs\t%s, .L%d+%d\n",frn,label,disp);
        }
    }
}


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);
    if (!(arch_mode&UseFPP)) {
        if (d) {
            code_save_stacks();
            clear_ptr_cache();
            set_dreg(DREGISTER_OPERAND_1,1);
            extern_conv("__negdf2");
            set_dreg(RET_DREGISTER,0);
            return;
        } else {
            code_save_stacks();
            clear_ptr_cache();
            set_dreg(FREGISTER_OPERAND,1);
            extern_conv("__negsf2");
            set_dreg(RET_FREGISTER,0);
            return;
        }
    } else {
        frn = fregister_name(freg);
        inc_inst(1);
        printf("\t%s\t%s, %s\n",d?"mnfd":"mnfs",frn,frn);
    }
}

void
code_d2i0(int reg,int d)
{ 
    int lreg;
    if (!(arch_mode&UseFPP)) {
        use_float(d,reg);
        code_save_stacks();
        clear_ptr_cache();
        set_dreg(d?DREGISTER_OPERAND:FREGISTER_OPERAND,1);
        extern_conv(d?"__fixdfsi":"__fixsfsi");
        set_ireg(RET_REGISTER,0);
    } else {
        use_float(d,reg);
        lreg = get_register();
        inc_inst(1);
        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;
    if (!(arch_mode&UseFPP)) {
        set_ireg(REGISTER_OPERAND,1);
        code_save_stacks();
        clear_ptr_cache();
        extern_conv(d?"__floatsidf":"__floatsisf");
        set_dreg(d?RET_DREGISTER:RET_FREGISTER,0);
        use_float(d,reg);
    } else {
        use_int(reg);
        lreg = get_dregister(1);
        inc_inst(1);
        printf("\tflt%c\t%s, %s\n",dsuffix(d),
                register_name(lreg),register_name(reg));
        set_dreg(lreg,0);
    }
    return;
}

void
code_d2u0(int reg,int d)
{ 
    int lreg,reg0;
    char *lrn,*frn,*crn;
    if (!(arch_mode&UseFPP)) {
        use_float(1,reg);
        code_save_stacks();
        clear_ptr_cache();
        set_dreg(d?DREGISTER_OPERAND:FREGISTER_OPERAND,1);
        extern_conv(d?"__fixunsdfsi":"__fixunssfsi");
        set_ireg(RET_REGISTER,0);
    } else {
        //   u = (d>2.1e9)?((int)(d-2.1e9)^2147483648):(int)d
        inc_inst(7);
        use_float(1,reg);
        frn = register_name(reg);
        if (!d) printf("\tmvfd\t%s, %s\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(freg);
        set_ireg(reg0=get_register(),0);
        crn = register_name(reg0);
        printf("\tcmfe\t%s, %s\n",lrn,frn);
        printf("\tbge\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 tmp,freg1;
    char *crn,*frn,*lrn;
    if (!(arch_mode&UseFPP)) {
        tmp=new_lvar(SIZE_OF_INT);
        set_ireg(REGISTER_OPERAND,1);
        code_assign_lvar(tmp,REGISTER_OPERAND,0);
        code_save_stacks();
        clear_ptr_cache();
        extern_conv(d?"__floatsidf":"__floatsisf");
        code_rlvar(tmp,REGISTER_OPERAND);
        inc_inst(2);
        printf("\tcmp\t%s, #0\n",register_name(REGISTER_OPERAND));
        printf("\tbge\t1f\n");
        if (d)
            code_double_lib_c("__adddf3",RET_DREGISTER,RET_DREGISTER,4.29496729600000000000e9);
        else 
            code_double_lib_c("__addsf3",RET_FREGISTER,RET_FREGISTER,4.29496729600000000000e9);
        printf("1:\n");
        set_dreg(d?RET_DREGISTER:RET_FREGISTER,0);
        if (reg!=USE_CREG) {
            use_float(d,reg);
            if (d) {
                if (reg!=RET_DREGISTER) {
                    lmove(reg,RET_DREGISTER);
                }
            } else {
                if (reg!=RET_FREGISTER) {
                    code_register(reg,RET_FREGISTER);
                }
            }
        }
        free_lvar(tmp);
    } else {
        use_int(reg);
        crn = register_name(reg);
        set_dreg(reg=get_dregister(1),0);
        frn = register_name(reg);
        inc_inst(5);
        printf("\tfltd\t%s, %s\n",frn,crn);
        printf("\tcmp\t%s, #0\n",crn);
        printf("\tbge\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;
    if (!(arch_mode&UseFPP)) {
        set_dreg(DREGISTER_OPERAND,1);
        code_save_stacks();
        clear_ptr_cache();
        extern_conv("__truncdfsf2");
        set_freg(RET_FREGISTER,0);
        use_float(0,reg);
    } else {
        frn = register_name(freg);
        inc_inst(1);
        printf("\tmvfs\t%s,%s\n",frn,frn);
    }
    return;
}

void
code_f2d(int reg) { 
    char *frn;
    if (!(arch_mode&UseFPP)) {
        set_freg(FREGISTER_OPERAND,1);
        code_save_stacks();
        clear_ptr_cache();
        extern_conv("__extendsfdf2");
        set_dreg(RET_DREGISTER,0);
        use_float(1,reg);
    } else {
        frn = register_name(freg);
        inc_inst(1);
        printf("\tmvfd\t%s, %s\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)
{ 
    if (!(arch_mode&UseFPP)) {
        if (d) {
             code_lrgvar(e2,freg);
             set_double(freg);
             return;
        } else {
             code_rgvar(e2,freg);
             set_float(freg);
             return;
        }
    } else {
        use_float(d,freg);
        code_ldf(fload(d),fregister_name(freg),cadr(e2),
            get_ptr_cache(ncaddr(e2)),"");
    }
}


void
code_drlvar(int e2,int d,int freg)
{ 
    if (!(arch_mode&UseFPP)) {
        if (d) {
             code_lrlvar(e2,freg);
             set_double(freg);
             return;
        } else {
             code_rlvar(e2,freg);
             set_float(freg);
             return;
        }
    } else {
        use_float(d,freg);
        lvar_intro(e2);
        inc_inst(1);
        printf("\t%s\t%s, ",fload(d),fregister_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);
}

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

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

static void
code_float_lib(char *lib,int to,int reg,int oreg)
{
    code_save_stacks();
    clear_ptr_cache();
    if (reg!=FREGISTER_OPERAND) code_dregister(FREGISTER_OPERAND,reg,0);
    if (to!=FREGISTER_OPERAND_1) code_dregister(FREGISTER_OPERAND_1,to,0);
    extern_conv(lib);
    set_freg(RET_FREGISTER,0);
    if (to!=RET_FREGISTER) {
        code_dregister(to,RET_FREGISTER,0);
    }
}

static void
code_float_lib_c(char *lib,int from,int to,double value)
{
    code_save_stacks();
    clear_ptr_cache();
    set_dreg_operand(from,1);
    fconst(FREGISTER_OPERAND_1,value);
    extern_conv(lib);
    set_freg(RET_FREGISTER,0);
    if (to!=RET_FREGISTER) {
        code_dregister(to,RET_FREGISTER,0);
    }
}

void
dtosop_d(int op,int reg,int e1)
{ 

    char *opc="";

    int d,reg0=reg;



    d=(op<FOP);
    use_float(d,reg);
    switch(op) {
    case DADD:  opc="__adddf3"; break;
    case DSUB:  opc="__subdf3"; break;
    case DDIV:  opc="__divdf3"; break;
    case DMUL:  opc="__muldf3"; break;
    case DCMPGE:         opc="__gedf2"; break;
    case DCMP:  opc="__gtdf2"; break;
    case FADD:  opc="__addsf3"; break;
    case FSUB:  opc="__subsf3"; break;
    case FDIV:  opc="__divsf3"; break;
    case FMUL:  opc="__mulsf3"; break;
    case FCMPGE:         opc="__gesf2"; break;
    case FCMP:  opc="__gtsf2"; break;
    default:
        error(-1); return;
    }
    if (d)
        code_double_lib(opc,reg0==USE_CREG?RET_DREGISTER:reg,reg,e1);
    else
        code_float_lib(opc,reg0==USE_CREG?RET_FREGISTER:reg,reg,e1);
}

void
dtosop_f(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 = fregister_name(e1);
    frn = fregister_name(reg);
    inc_inst(1);
    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
dtosop(int op,int reg,int e1)
{
    if (!(arch_mode&UseFPP))
        dtosop_d(op,reg,e1);
    else
        dtosop_f(op,reg,e1);
}

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


    int edx,edx0=-1;
    int reg0=reg;

    xreg = d?emit_lpop(0):emit_pop(0);       /* pop e3 value */
    if (!is_int_reg(creg)) error(-1);
    edx = ireg;
    emit_push();
    use_float(d,reg);
    if (d) {
        if (regv_l(lreg)==edx || regv_h(lreg)==edx) {
            edx0 = get_register(); if(!edx0) error(-1);
            inc_inst(1);
            printf("## dassop\n\tmov\t%s, %s\n",
                register_name(edx0),register_name(edx));
            edx = edx0;
        }
        lload(edx,reg,0);
        dtosop(op,USE_CREG,xreg);
        if (lreg!=RET_DREGISTER) error(-1);
        use_reg(lreg);
        edx = emit_pop(0);
        code_lassign(edx,RET_DREGISTER);
        if (edx0!=-1)
            free_register(edx0);
        emit_pop_free(edx);
        emit_lpop_free(xreg);
        if (reg0!=USE_CREG && reg!=RET_DREGISTER)
            lmove(reg,RET_DREGISTER);
        set_double(reg);
    } else {
        if (freg==edx) {
            edx0 = get_register(); if(!edx0) error(-1);
            inc_inst(1);
            printf("# dassop\n\tmov\t%s, %s\n",
                register_name(edx0),register_name(edx));
            edx = edx0;
        }
        code_rindirect(edx,reg,0,0,0);
        dtosop(op,USE_CREG,xreg);
        use_reg(freg);
        edx = emit_pop(0);
        code_assign(edx,0,RET_FREGISTER);
        if (edx0!=-1)
            free_register(edx0);
        emit_pop_free(edx);
        emit_pop_free(xreg);
        if (reg0!=USE_CREG && reg!=RET_DREGISTER)
            code_register(RET_DREGISTER,reg);
        set_float(reg);
    }
}

void
code_dassop_f(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(ireg);
    use_float(d,reg);
    frn  =fregister_name(reg);

    code_ldf(fload(d),fregister_name(freg),0,ireg,"");
    dtosop(op,reg,xreg);
    code_ldf(fstore(d),fregister_name(freg),0,ireg,"");
    emit_dpop_free(xreg,d);
}

void
code_dassop(int op,int reg,int d) {
    if (!(arch_mode&UseFPP))
        code_dassop_d(op,reg,d);
    else
        code_dassop_f(op,reg,d);
}

void
code_register_dassop(int reg,int op,int d) {
    // reg op= dpop()
    int  xreg;
    if (!(arch_mode&UseFPP)) {
        if (!d) {
            xreg=emit_pop(0);
            dtosop(op,reg,xreg);
            emit_pop_free(xreg);
        } else {
            xreg=emit_lpop();
            dtosop(op,reg,xreg);
            emit_lpop_free(xreg);
            set_double(lreg);
        }
    } else {
        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);
    if (!(arch_mode&UseFPP)) {
        error(-1);
    }
    inc_inst(1);
    printf("\tmvf%c\t%s,#1\n",dsuffix(d),fregister_name(g));
    return g;
}

void
code_dpreinc_d(int e1,int e2,int d,int reg) {


    int  xreg;

    int dir=caddr(e1);

    if (!d) {
        if (car(e2)==FREGISTER) {
            use_float(d,reg);
            code_save_stacks();
            code_float_lib_c("__addsf3",cadr(e2),cadr(e2),dir);
            if (reg!=cadr(e2)) code_register(cadr(e2),reg);
            return;
        } 
        g_expr(e2);
        if(!is_int_reg(creg)) error(-1);
        xreg = ireg;
        emit_push();
        code_save_stacks();
        code_rindirect(xreg,FREGISTER_OPERAND,0,0,0);
        code_double_lib_c("__addsf3",DREGISTER_OPERAND,RET_DREGISTER,dir);
        xreg = emit_pop(0);
        code_assign_register(xreg,0,RET_DREGISTER);
        if (use) {
            if (reg==USE_CREG)
                set_freg(RET_FREGISTER,0);
            else
                code_register(RET_DREGISTER,reg);
        }
        emit_pop_free(xreg);
    } else {
        if (car(e2)==DREGISTER) {
            use_float(d,reg);
            code_save_stacks();
            code_double_lib_c("__adddf3",cadr(e2),cadr(e2),dir);
            if (reg!=cadr(e2)) lmove(reg,cadr(e2));
            return;
        } 
        g_expr(e2);
        if(!is_int_reg(creg)) error(-1);
        xreg = ireg;
        emit_push();
        code_save_stacks();
        lload(xreg,DREGISTER_OPERAND,0);
        code_float_lib_c("__adddf3",DREGISTER_OPERAND,RET_DREGISTER,dir);
        xreg = emit_pop(0);
        lstore(xreg,RET_DREGISTER);
        if (use) {
            if (reg==USE_CREG)
                set_dreg(RET_DREGISTER,0);
            else
                lmove(reg,RET_DREGISTER);
        }
        emit_pop_free(xreg);
    }
}

void
code_dpostinc_d(int e1,int e2,int d,int reg) {


    int  g,xreg;

    int dir=caddr(e1);

    if (!d) {
        if (car(e2)==FREGISTER) {
            xreg = cadr(e2);
            code_float_lib_c("__addsf3",xreg,RET_DREGISTER,dir);
            //    xreg は increment する
            //    USE_CREG だと increment する前の値を creg にセット
            //    reg が指定されていれば、それに前の値をセット
            if (reg==USE_CREG) {
                use_float(d,reg);
                if (reg==RET_FREGISTER) {
                    reg = get_dregister(d);
                }
                set_freg(reg,0);
            }
            g = list3(reg,0,xreg);
            g = list3(xreg,g,RET_FREGISTER);
            parallel_rassign(g);
            return;
        } 
        g_expr(e2);
        if(!is_int_reg(creg)) error(-1);
        xreg = ireg;
        emit_push();
        code_save_stacks();
        use_float(0,reg);
        code_rindirect(xreg,FREGISTER_OPERAND,0,0,0);
        code_float_lib_c("__addsf3",DREGISTER_OPERAND,RET_DREGISTER,dir);
        xreg = emit_pop(0);
        emit_push();
        if (use) {
            use_longlong(reg);
            code_rindirect(xreg,reg,0,0,0);
        }
        reg = emit_lpop();
        code_assign_register(xreg,0,reg);
        emit_pop_free(reg);
        emit_pop_free(xreg);
    } else {
        if (car(e2)==DREGISTER) {
            xreg = cadr(e2);
            code_double_lib_c("__adddf3",xreg,RET_DREGISTER,dir);
            //    xreg は increment する
            //    USE_CREG だと increment する前の値を creg にセット
            //    reg が指定されていれば、それに前の値をセット
            if (reg==USE_CREG) {
                use_float(d,reg);
                if (reg==RET_DREGISTER) {
                    reg = get_dregister(d);
                }
                set_dreg(reg,0);
            }
            g = list3(regv_l(reg),0,regv_l(xreg));
            g = list3(regv_h(reg),g,regv_h(xreg));
            g = list3(regv_l(xreg),g,regv_l(RET_DREGISTER));
            g = list3(regv_h(xreg),g,regv_h(RET_DREGISTER));
            parallel_rassign(g);
            return;
        } 
        g_expr(e2);
        if(!is_int_reg(creg)) error(-1);
        xreg = ireg;
        emit_push();
        code_save_stacks();
        use_float(1,reg);
        lload(xreg,DREGISTER_OPERAND,0);
        code_double_lib_c("__adddf3",DREGISTER_OPERAND,RET_DREGISTER,dir);
        xreg = emit_pop(0);
        emit_lpush();
        if (use) {
            use_longlong(reg);
            lload(xreg,reg,0);
        }
        reg = emit_lpop();
        lstore(xreg,reg);
        emit_lpop_free(reg);
        emit_pop_free(xreg);
    }
}

void
code_dpreinc_f(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=fregister_name(g=code_dload_1(d));
        if (reg==USE_CREG) {
            reg=get_dregister(d); if (!reg) error(-1);
            if (d) set_dreg(reg,0); else set_freg(reg,0);
        }
        frn=fregister_name(reg);
        inc_inst(1);
        printf("\t%s%c\t%s, %s, %s\n",
            dir>0?"adf":"suf",dsuffix(d),
            crn,crn,grn);
        if (use && reg!=cadr(e2)) {
            inc_inst(1);
            printf("\tmvf%c\t%s, %s\n",dsuffix(d),frn,crn);
        }
    } else {
        g_expr(e2);
        if (!is_int_reg(creg)) error(-1);
        reg0 = ireg;
        if (reg==USE_CREG) {
            reg=get_dregister(d); if (!reg) error(-1);
            if (d) set_dreg(reg,0); else set_freg(reg,0);
        }
        grn = fregister_name(g = code_dload_1(d));
        frn=fregister_name(reg);
        code_ldf(fload(d),frn,0,reg0,"");
        inc_inst(1);
        printf("\t%s%c\t%s, %s, %s\n",
            dir>0?"adf":"suf",dsuffix(d),
            frn,frn,grn);
        code_ldf(fstore(d),frn,0,reg0,"");
    }
    free_register(g);
}

void
code_dpostinc_f(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=fregister_name(g=code_dload_1(d));
        if (reg==USE_CREG) {
            reg=get_dregister(d); if (!reg) error(-1);
            set_freg(reg,0);
        }
        frn=fregister_name(reg);
        if (use && reg!=cadr(e2)) {
            inc_inst(1);
            printf("\tmvf%c\t%s, %s\n",dsuffix(d),frn,crn);
        }
        inc_inst(1);
        printf("\t%s%c\t%s,%s,%s\n",dir>0?"adf":"suf",
            dsuffix(d),crn,crn,grn);
    } else {
        g_expr(e2);
        if (!is_int_reg(creg)) error(-1);
        crn=register_name(reg0=ireg);
        if (reg==USE_CREG) {
            reg=get_dregister(d); if (!reg) error(-1);
            set_freg(reg,0);
        }
        frn=fregister_name(reg);
        grn = fregister_name(g = code_dload_1(d));
        code_ldf(fload(d),frn,0,reg0,"");
        inc_inst(1);
        printf("\t%s%c\t%s, %s, %s\n",
            dir>0?"adf":"suf",dsuffix(d),
            grn,frn,grn);
        code_ldf(fstore(d),grn,0,reg0,"");
    }
    free_register(g);
}

void
code_dpreinc(int e1,int e2,int d,int reg) {
    if (!(arch_mode&UseFPP)) 
        code_dpreinc_d(e1,e2,d,reg);
    else
        code_dpreinc_f(e1,e2,d,reg);
}

void
code_dpostinc(int e1,int e2,int d,int reg) {
    if (!(arch_mode&UseFPP)) 
        code_dpostinc_d(e1,e2,d,reg);
    else
        code_dpostinc_f(e1,e2,d,reg);
}


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 = "bgt"; break;
    case FOP+GE:  op1=FOP+CMPGE; opn = "bge"; break;
    case FOP+EQ:  op1=FOP+CMP;   opn = "beq"; break;
    case FOP+NEQ: op1=FOP+CMP;   opn = "bne"; break;
    case DOP+GT:  op1=DOP+CMP;   opn = "bgt"; break;
    case DOP+GE:  op1=DOP+CMPGE; opn = "bge"; break;
    case DOP+EQ:  op1=DOP+CMP;   opn = "beq"; break;
    case DOP+NEQ: op1=DOP+CMP;   opn = "bne"; break;
    default: error(-1);
    }
    g_expr(list3(op1,e2,e1));
    inc_inst(1);
    printf("\t%s\t.L%d\n",opn,l1);
    return l1;
}

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


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

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

#endif

#if LONGLONG_CODE


/* 64bit int part */

int
lrexpr_bool(int e1, int reg)
{       
    return 0;
}

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();
    if (!is_longlong_reg(creg)) error(-1);
    reg = lreg;
    l2 = fwdlabel();
    regh = regv_h(reg); regl = regv_l(reg);
    e3h =  regv_h(e3);  e3l = regv_l(e3);
    switch(op) {
    case LOP+GT: case LOP+GE:
        pcond(GT,  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);
    inc_inst(1);
    printf("\torr\t%s, %s, %s\n",
                lregister_name_low(reg),
                lregister_name_low(reg),
                lregister_name_high(reg));
    code_cmp_register(regv_l(reg),label,cond);
}

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

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

#endif
#if LONGLONG_CODE||FLOAT_CODE

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

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


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

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

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

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

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

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

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

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

void
code_lrlvar(int e1,int creg)
{
    char *crn_h;
    char *crn_l;
    use_longlong(creg);
    crn_h = lregister_name_high(creg);
    crn_l = lregister_name_low(creg);
    inc_inst(2);
    lvar_intro(e1);
    printf("\tldr\t%s, ",crn_l); lvar(e1,"");
    printf("\tldr\t%s, ",crn_h); lvar(e1+SIZE_OF_INT,"");
}

#endif
#if LONGLONG_CODE

static long long ll0 = 1LL;

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

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

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

void
code_lneg(int creg)
{

    char *rh,*rl;
    use_longlong(creg);
    rl=lregister_name_low(creg);
    rh=lregister_name_high(creg);
    inc_inst(2);
    printf("\trsbs\t%s, %s, #0\n",rl,rl);
    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(regv_l(reg),regv_h(reg),regv_l(oreg),regv_h(oreg));
    extern_conv(lib);
    // set_lreg(RET_LREGISTER,0);
}

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

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

    // 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_h = lregister_name_high(oreg);
    orn_l = lregister_name_low(oreg);
    crn_h = lregister_name_high(reg);
    crn_l = lregister_name_low(reg);
    switch(op) {
    case LADD:
        inc_inst(2);
        printf("\tadds\t%s, %s, %s\n",crn_l,crn_l,orn_l);
        printf("\tadc\t%s, %s, %s\n",crn_h,crn_h,orn_h);
        break;
    case LSUB:
        inc_inst(2);
        printf("\tsubs\t%s, %s, %s\n",crn_l,crn_l,orn_l);
        printf("\tsbc\t%s, %s, %s\n",crn_h,crn_h,orn_h);
        break;
    case LCMP:
        error(-1);
        break;
    case LBAND: 
        inc_inst(2);
        printf("\tand\t%s, %s, %s\n",crn_l,crn_l,orn_l);
        printf("\tand\t%s, %s, %s\n",crn_h,crn_h,orn_h);
        break;
    case LEOR: 
        inc_inst(2);
        printf("\teor\t%s, %s, %s\n",crn_l,crn_l,orn_l);
        printf("\teor\t%s, %s, %s\n",crn_h,crn_h,orn_h);
        break;
    case LBOR:
        inc_inst(2);
        printf("\torr\t%s, %s, %s\n",crn_l,crn_l,orn_l);
        printf("\torr\t%s, %s, %s\n",crn_h,crn_h,orn_h);
        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_h;
    char *crn_l;
    char *grn;
    int v=0;
    int vh=0;
    int greg,dx=-1;

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

    if (car(e)==LCONST) { v = lcadr(e); 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(regv_l(creg),regv_h(creg));
            code_const(0,regv_l(creg));
            return;
        } else if (v>31) {
            printf("\tmov\t%s, %s, lsl #%d\n",crn_h,crn_l,v-32);
            code_const(0,regv_l(creg));
            return;
        }
        greg = get_register();
        grn = register_name(greg);
        inc_inst(4);
        printf("\tmov\t%s, %s, lsl #%d\n",crn_h,crn_h,v);
        printf("\tmov\t%s, %s, lsr #%d\n",grn,crn_l,32-v);
        printf("\torr\t%s, %s,%s\n",crn_h,crn_h,grn);
        printf("\tmov\t%s, %s, lsl #%d\n",crn_l,crn_l,v);
        free_register(greg);
        return;
    case LDIV:
        v=ilog(v);
    case LRSHIFT:
        if (v==0) return;
        if (v==32) {
            code_register(regv_h(creg),regv_l(creg));
            printf("\tmov\t%s, %s, asr #31\n",crn_h,crn_l);
            return;
        } else if (v>31) {
            printf("\tmov\t%s, %s, asr #%d\n",crn_l,crn_h,v-32);
            printf("\tmov\t%s, %s, asr #31\n",crn_h,crn_l);
            return;
        }
        greg = get_register();
        grn = register_name(greg);
        inc_inst(4);
        printf("\tmov\t%s, %s, lsr #%d\n",crn_l,crn_l,v);
        printf("\tmov\t%s, %s, lsl #%d\n",grn,crn_h,32-v);
        printf("\torr\t%s, %s,%s\n",crn_l,crn_l,grn);
        printf("\tmov\t%s, %s, asr #%d\n",crn_h,crn_h,v);
        free_register(greg);
        return;
    case LUDIV:
        v=ilog(v);
    case LURSHIFT:
        if (v==0) return;
        if (v==32) {
            code_register(regv_h(creg),regv_l(creg));
            code_const(0,regv_h(creg));
            return;
        } else if (v>31) {
            printf("\tmov\t%s, %s, lsr #%d\n",crn_l,crn_h,v-32);
            code_const(0,regv_h(creg));
            return;
        }
        greg = get_register();
        grn = register_name(greg);
        inc_inst(4);
        printf("\tmov\t%s, %s, lsl #%d\n",grn,crn_h,32-v);
        printf("\tmov\t%s, %s, lsr #%d\n",crn_l,crn_l,v);
        printf("\torr\t%s, %s,%s\n",crn_l,grn,crn_l);
        printf("\tmov\t%s, %s, lsr #%d\n",crn_h,crn_h,v);
        free_register(greg);
        return;
    case LADD:
        inc_inst(2);
        printf("\tadds\t%s, %s, #%d\n",crn_l,crn_l,v);
        printf("\tadc\t%s, %s, #%d\n",crn_h,crn_h,vh);
        break;
    case LSUB:
        inc_inst(2);
        printf("\tsubs\t%s, %s, #%d\n",crn_l,crn_l,v);
        printf("\tsbc\t%s, %s, #%d\n",crn_h,crn_h,vh);
        break;
    case LBAND: 
        inc_inst(2);
        printf("\tand\t%s, %s, #%d\n",crn_l,crn_l,v);
        printf("\tand\t%s, %s, #%d\n",crn_h,crn_h,vh);
        break;
    case LEOR: 
        inc_inst(2);
        printf("\teor\t%s, %s, #%d\n",crn_l,crn_l,v);
        printf("\teor\t%s, %s, #%d\n",crn_h,crn_h,vh);
        break;
    case LBOR:
        inc_inst(2);
        printf("\torr\t%s, %s, #%d\n",crn_l,crn_l,v);
        printf("\torr\t%s, %s, #%d\n",crn_h,crn_h,vh);
        break;
    default:
        error(-1);
    }
    if (dx!=-1) free_register(dx);
}


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

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

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

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

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

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

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

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

#if FLOAT_CODE

void
code_d2ll(int reg)
{
    // fixdfdi$stub
    code_assign_input_double_long(
        list2(LREGISTER,LREGISTER_OPERAND),list2(FREGISTER,freg)) ;
    extern_conv("__fixdfdi");
    set_lreg(RET_LREGISTER,0);
    if (reg!=USE_CREG&&reg!=RET_LREGISTER) {
        use_longlong(reg);
    }
}

void
code_d2ull(int reg)
{
    code_assign_input_double_long(
        list2(LREGISTER,LREGISTER_OPERAND),list2(FREGISTER,freg)) ;
    extern_conv("__fixunsdfdi");
    set_lreg(RET_LREGISTER,0);
    if (reg!=USE_CREG&&reg!=RET_LREGISTER) {
        use_longlong(reg);
    }
}

void
code_f2ll(int reg)
{
    code_assign_input_float_int(list2(REGISTER,REGISTER_OPERAND),list2(FREGISTER,freg));
    extern_conv("__fixsfdi");
    set_lreg(RET_LREGISTER,0);
    if (reg!=USE_CREG&&reg!=RET_LREGISTER) {
        use_longlong(reg);
    }
}

void
code_f2ull(int reg)
{
    code_assign_input_float_int(list2(REGISTER,REGISTER_OPERAND),list2(FREGISTER,freg));
    extern_conv("__fixunssfdi");
    set_lreg(RET_LREGISTER,0);
    if (reg!=USE_CREG&&reg!=RET_LREGISTER) {
        use_longlong(reg);
    }
}

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


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

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

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

#endif

static void
ladd(int dreg,int rreg,int v)   //   rreg = dreg + v
{
    // v should be 8bit const with 2's shift
    inc_inst(2);
    if (v>0) {
        printf("\tadds\t%s, %s, #%d\n", 
                lregister_name_low(rreg),lregister_name_low(dreg), v);
        printf("\tadc\t%s, %s, #0\n", 
                lregister_name_high(rreg),lregister_name_high(dreg));
    } else {
        printf("\tsubs\t%s, %s, #%d\n", 
                lregister_name_low(rreg),lregister_name_low(dreg), -v);
        printf("\tsbc\t%s, %s, #0\n", 
                lregister_name_high(rreg),lregister_name_high(dreg));
    }
}

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

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

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

    // (*creg) op = pop()
    xreg = emit_lpop(0);       /* pop e3 value */
    if (!is_int_reg(creg)) error(-1);
    edx = ireg;
    emit_push();
    use_longlong(reg);
    if (regv_l(lreg)==edx || regv_h(lreg)==edx) {
        edx0 = get_register(); if(!edx0) error(-1);
        inc_inst(1);
        printf("## lassop\n\tmov\t%s, %s\n",register_name(edx0),
               register_name(edx));
        edx = edx0;
    }
    lload(edx,reg,0);
    // free_register(edx); don't do this, it will free pushed register
    ltosop(op,reg,xreg);    // loprtc?
    emit_lpop_free(xreg);
    use_reg(reg);
    edx = emit_pop(0);
    code_lassign(edx,reg);
    emit_pop_free(edx);
    if (edx0!=-1)
        free_register(edx0);
    if (reg!=lreg)
        free_register(reg);
}

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


#endif

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

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

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

#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: 
        inc_inst(2+(max-min));
        code_cmpdimm(max-min,t,dlabel,-1);
        printf("\tldrls\tpc, [pc, %s, asl #2]\n",trn);
        break;
    case 2:
        inc_inst(4+(max-min)/2);
        printf("\ttst\t%s, #1\n",trn);
        printf("\tbne\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:
        inc_inst(5+(max-min)/4);
        printf("\ttst\t%s, #3\n",trn);
        printf("\tbne\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("\tb\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) { // STRINGS?
            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 {
            if (c=='\n') inc_inst(1);
            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),regv_h(reg),SIZE_OF_INT*2,adr,cext_at(0,0));
        /* load mmmmmm */
        code_ld(cload(0,0),regv_l(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);
        inc_inst(1);
        printf("\tadd\t%s,%s,%s\n",
                            register_name(regv_l(reg)),
                            register_name(regv_l(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);
        inc_inst(4);
        code_const(~mask,tmp);
        printf("\torr\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("\torr\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 = lregister_name_high(value);
        lrn = lregister_name_high(lvalue);
        /* shift left */
        if (bitpos) 
            loprtc(LLSHIFT,value,list2(CONST,bitpos));
        trn = register_name(tmp = get_register());
        if (bitpos+bitsize>=32) {
            /* make and-mask upper */
            mask = make_mask(64-bitpos-bitsize,bitpos>=32?63-bitpos:31);
            make_mask_and_or(mask,tmp,trn,crn,lrn);
        }
        crn = lregister_name_low(value);
        lrn = lregister_name_low(lvalue);
        if (bitpos<32) {
            /* make and-mask lower */
            mask = make_mask(bitpos+bitsize>=32?0:32-bitpos-bitsize,31-bitpos);
            make_mask_and_or(mask,tmp,trn,crn,lrn);
        }
        code_lassign(adr,value);
        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,regv_l(value),list2(CONST,i));
        if (i<0||32<=i) error(-1);
        crn = lregister_name_low(value);
        code_ld(cload(0,0),regv_h(value),0,adr,cext_at(0,0));
        lrn = lregister_name_high(value);
        mask = make_mask(0,31-bitpos);
        make_mask_and_or(mask,tmp,trn,crn,lrn);
        inc_inst(1);
        printf("\t%s\t%s, [%s, #0]\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);
        inc_inst(1);
        printf("\t%s\t%s, [%s, #4]\n",cstore(0),crn,register_name(adr));

        /* make and-mask lower */
        code_ld(cload(0,0),regv_l(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);
        inc_inst(1);
        printf("\t%s\t%s, [%s, #8]\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) {
        inc_inst(1);
        if (is_stage1_const(m,0)>0) {
            printf("\tand\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) {
        inc_inst(1);
        /* do disjunction  */
        if (is_stage1_const(c,0)>0) {
            printf("\torr\t%s, %s, #%d\n",crn,crn,c);
        } else {
            trn = register_name(tmp=get_register());
            code_const(c,tmp);
            printf("\torr\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 = lregister_name_high(lvalue);
        /* shift left */
        lc = lcadr(value);
        lc <<= bitpos;
        if (bitpos+bitsize>=32) {
            /* make and-mask upper */
            mask = make_mask(64-bitpos-bitsize,bitpos>=32?63-bitpos:31);
            make_mask_and_or_const(mask,crn,(int)(lc>>32));
        }
        crn = lregister_name_low(lvalue);
        if (bitpos<32) {
            /* make and-mask lower */
            mask = make_mask(bitpos+bitsize>=32?0:32-bitpos-bitsize,31-bitpos);
            make_mask_and_or_const(mask,crn,(int)lc);
        }
        code_lassign(adr,lvalue);
        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));
        inc_inst(1);
        printf("\t%s\t%s, [%s, #0]\n",cstore(0),trn,crn);
        /* store middle */
        code_const((int)(lc>>(32-bitpos)),tmp);
        inc_inst(1);
        printf("\t%s\t%s, [%s, #4]\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)));
        inc_inst(1);
        printf("\t%s\t%s, [%s, #8]\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)
{
    return code_arg_alignment0(args,n, type0,sz,is_code);
}


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


/* end */