diff options
| author | Edmund Grimley Evans <Edmund.Grimley.Evans@gmail.com> | 2015-02-13 18:58:31 +0000 |
|---|---|---|
| committer | Edmund Grimley Evans <Edmund.Grimley.Evans@gmail.com> | 2015-02-23 22:51:03 +0000 |
| commit | b14ef0e24bcb70d70360690130b2bf2a0c8f8d33 (patch) | |
| tree | 11faa1933ceaaba6ffd6975d57eec9d58f4a7cb1 /tccgen.c | |
| parent | 738606dbd5608cb2ed6789beecea40712809c397 (diff) | |
| download | tinycc-b14ef0e24bcb70d70360690130b2bf2a0c8f8d33.tar.gz tinycc-b14ef0e24bcb70d70360690130b2bf2a0c8f8d33.tar.bz2 | |
Add arm64 (AArch64) as a target architecture.
Diffstat (limited to 'tccgen.c')
| -rw-r--r-- | tccgen.c | 127 |
1 files changed, 102 insertions, 25 deletions
@@ -545,7 +545,7 @@ ST_FUNC void save_reg(int r) type = &p->type; if ((p->r & VT_LVAL) || (!is_float(type->t) && (type->t & VT_BTYPE) != VT_LLONG)) -#ifdef TCC_TARGET_X86_64 +#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64) type = &char_pointer_type; #else type = &int_type; @@ -562,7 +562,7 @@ ST_FUNC void save_reg(int r) o(0xd8dd); /* fstp %st(0) */ } #endif -#ifndef TCC_TARGET_X86_64 +#if !defined(TCC_TARGET_ARM64) && !defined(TCC_TARGET_X86_64) /* special long long case */ if ((type->t & VT_BTYPE) == VT_LLONG) { sv.c.ul += 4; @@ -681,7 +681,7 @@ static void move_reg(int r, int s, int t) } /* get address of vtop (vtop MUST BE an lvalue) */ -static void gaddrof(void) +ST_FUNC void gaddrof(void) { if (vtop->r & VT_REF) gv(RC_INT); @@ -803,12 +803,14 @@ ST_FUNC int gv(int rc) r = vtop->r & VT_VALMASK; rc2 = (rc & RC_FLOAT) ? RC_FLOAT : RC_INT; +#ifndef TCC_TARGET_ARM64 if (rc == RC_IRET) rc2 = RC_LRET; #ifdef TCC_TARGET_X86_64 else if (rc == RC_FRET) rc2 = RC_QRET; #endif +#endif /* need to reload if: - constant @@ -817,7 +819,7 @@ ST_FUNC int gv(int rc) if (r >= VT_CONST || (vtop->r & VT_LVAL) || !(reg_classes[r] & rc) -#ifdef TCC_TARGET_X86_64 +#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64) || ((vtop->type.t & VT_BTYPE) == VT_QLONG && !(reg_classes[vtop->r2] & rc2)) || ((vtop->type.t & VT_BTYPE) == VT_QFLOAT && !(reg_classes[vtop->r2] & rc2)) #else @@ -826,7 +828,7 @@ ST_FUNC int gv(int rc) ) { r = get_reg(rc); -#ifdef TCC_TARGET_X86_64 +#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64) if (((vtop->type.t & VT_BTYPE) == VT_QLONG) || ((vtop->type.t & VT_BTYPE) == VT_QFLOAT)) { int addr_type = VT_LLONG, load_size = 8, load_type = ((vtop->type.t & VT_BTYPE) == VT_QLONG) ? VT_LLONG : VT_DOUBLE; #else @@ -838,7 +840,7 @@ ST_FUNC int gv(int rc) original_type = vtop->type.t; /* two register type load : expand to two words temporarily */ -#ifndef TCC_TARGET_X86_64 +#if !defined(TCC_TARGET_ARM64) && !defined(TCC_TARGET_X86_64) if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) { /* load constant */ ll = vtop->c.ull; @@ -890,7 +892,7 @@ ST_FUNC int gv(int rc) t1 = t; /* compute memory access type */ if (vtop->r & VT_REF) -#ifdef TCC_TARGET_X86_64 +#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64) t = VT_PTR; #else t = VT_INT; @@ -952,6 +954,7 @@ ST_FUNC void gv2(int rc1, int rc2) } } +#ifndef TCC_TARGET_ARM64 /* wrapper around RC_FRET to return a register by type */ static int rc_fret(int t) { @@ -962,6 +965,7 @@ static int rc_fret(int t) #endif return RC_FRET; } +#endif /* wrapper around REG_FRET to return a register by type */ static int reg_fret(int t) @@ -1147,7 +1151,7 @@ ST_FUNC int gvtst(int inv, int t) return gtst(inv, t); } -#ifndef TCC_TARGET_X86_64 +#if !defined(TCC_TARGET_ARM64) && !defined(TCC_TARGET_X86_64) /* generate CPU independent (unsigned) long long operations */ static void gen_opl(int op) { @@ -1358,7 +1362,7 @@ static void gen_opl(int op) #elif defined(TCC_TARGET_ARM) b = ind; o(0x1A000000 | encbranch(ind, 0, 1)); -#elif defined(TCC_TARGET_C67) +#elif defined(TCC_TARGET_C67) || defined(TCC_TARGET_ARM64) tcc_error("not implemented"); #else #error not supported @@ -1512,7 +1516,8 @@ static void gen_opic(int op) general_case: if (!nocode_wanted) { /* call low level op generator */ - if (t1 == VT_LLONG || t2 == VT_LLONG) + if (t1 == VT_LLONG || t2 == VT_LLONG || + (PTR_SIZE == 8 && (t1 == VT_PTR || t2 == VT_PTR))) gen_opl(op); else gen_opi(op); @@ -1679,7 +1684,7 @@ ST_FUNC void gen_op(int op) if (op >= TOK_ULT && op <= TOK_LOR) { check_comparison_pointer_types(vtop - 1, vtop, op); /* pointers are handled are unsigned */ -#ifdef TCC_TARGET_X86_64 +#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64) t = VT_LLONG | VT_UNSIGNED; #else t = VT_INT | VT_UNSIGNED; @@ -1700,7 +1705,7 @@ ST_FUNC void gen_op(int op) vrott(3); gen_opic(op); /* set to integer type */ -#ifdef TCC_TARGET_X86_64 +#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64) vtop->type.t = VT_LLONG; #else vtop->type.t = VT_INT; @@ -1724,7 +1729,7 @@ ST_FUNC void gen_op(int op) u = pointed_size(&vtop[-1].type); if (u < 0) tcc_error("unknown array element size"); -#ifdef TCC_TARGET_X86_64 +#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64) vpushll(u); #else /* XXX: cast to int ? (long long case) */ @@ -1833,6 +1838,9 @@ ST_FUNC void gen_op(int op) /* generic itof for unsigned long long case */ static void gen_cvt_itof1(int t) { +#ifdef TCC_TARGET_ARM64 + gen_cvt_itof(t); +#else if ((vtop->type.t & (VT_BTYPE | VT_UNSIGNED)) == (VT_LLONG | VT_UNSIGNED)) { @@ -1851,12 +1859,16 @@ static void gen_cvt_itof1(int t) } else { gen_cvt_itof(t); } +#endif } #endif /* generic ftoi for unsigned long long case */ static void gen_cvt_ftoi1(int t) { +#ifdef TCC_TARGET_ARM64 + gen_cvt_ftoi(t); +#else int st; if (t == (VT_LLONG | VT_UNSIGNED)) { @@ -1878,6 +1890,7 @@ static void gen_cvt_ftoi1(int t) } else { gen_cvt_ftoi(t); } +#endif } /* force char or short cast */ @@ -1968,7 +1981,7 @@ static void gen_cast(CType *type) vtop->c.ll = vtop->c.ull; else if (sbt & VT_UNSIGNED) vtop->c.ll = vtop->c.ui; -#ifdef TCC_TARGET_X86_64 +#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64) else if (sbt == VT_PTR) ; #endif @@ -1979,7 +1992,7 @@ static void gen_cast(CType *type) vtop->c.ull = vtop->c.ll; else if (dbt == VT_BOOL) vtop->c.i = (vtop->c.ll != 0); -#ifdef TCC_TARGET_X86_64 +#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64) else if (dbt == VT_PTR) ; #endif @@ -2024,7 +2037,7 @@ static void gen_cast(CType *type) gen_cast(type); } } -#ifndef TCC_TARGET_X86_64 +#if !defined(TCC_TARGET_ARM64) && !defined(TCC_TARGET_X86_64) } else if ((dbt & VT_BTYPE) == VT_LLONG) { if ((sbt & VT_BTYPE) != VT_LLONG) { /* scalar to long long */ @@ -2056,11 +2069,18 @@ static void gen_cast(CType *type) (sbt & VT_BTYPE) != VT_PTR && (sbt & VT_BTYPE) != VT_FUNC) { /* need to convert from 32bit to 64bit */ - int r = gv(RC_INT); + gv(RC_INT); if (sbt != (VT_INT | VT_UNSIGNED)) { +#if defined(TCC_TARGET_ARM64) + gen_cvt_sxtw(); +#elif defined(TCC_TARGET_X86_64) + int r = gv(RC_INT); /* x86_64 specific: movslq */ o(0x6348); o(0xc0 + (REG_VALUE(r) << 3) + REG_VALUE(r)); +#else +#error +#endif } } #endif @@ -2589,7 +2609,7 @@ ST_FUNC void vstore(void) if ((vtop[-1].r & VT_VALMASK) == VT_LLOCAL) { SValue sv; t = get_reg(RC_INT); -#ifdef TCC_TARGET_X86_64 +#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64) sv.type.t = VT_PTR; #else sv.type.t = VT_INT; @@ -2600,7 +2620,7 @@ ST_FUNC void vstore(void) vtop[-1].r = t | VT_LVAL; } /* two word case handling : store second register at word + 4 (or +8 for x86-64) */ -#ifdef TCC_TARGET_X86_64 +#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64) if (((ft & VT_BTYPE) == VT_QLONG) || ((ft & VT_BTYPE) == VT_QFLOAT)) { int addr_type = VT_LLONG, load_size = 8, load_type = ((vtop->type.t & VT_BTYPE) == VT_QLONG) ? VT_LLONG : VT_DOUBLE; #else @@ -3098,6 +3118,13 @@ static int parse_btype(CType *type, AttributeDef *ad) goto basic_type1; } break; +#ifdef TCC_TARGET_ARM64 + case TOK_UINT128: + /* GCC's __uint128_t appears in some Linux header files. Make it a + synonym for long double to get the size and alignment right. */ + u = VT_LDOUBLE; + goto basic_type; +#endif case TOK_BOOL: u = VT_BOOL; goto basic_type; @@ -3233,7 +3260,8 @@ the_end: /* long is never used as type */ if ((t & VT_BTYPE) == VT_LONG) -#if !defined TCC_TARGET_X86_64 || defined TCC_TARGET_PE +#if (!defined TCC_TARGET_X86_64 && !defined TCC_TARGET_ARM64) || \ + defined TCC_TARGET_PE t = (t & ~VT_BTYPE) | VT_INT; #else t = (t & ~VT_BTYPE) | VT_LLONG; @@ -3881,6 +3909,36 @@ ST_FUNC void unary(void) break; #endif #endif + +#ifdef TCC_TARGET_ARM64 + case TOK___va_start: { + next(); + skip('('); + expr_eq(); + skip(','); + expr_eq(); + skip(')'); + //xx check types + gen_va_start(); + vpushi(0); + vtop->type.t = VT_VOID; + break; + } + case TOK___va_arg: { + CType type; + next(); + skip('('); + expr_eq(); + skip(','); + parse_type(&type); + skip(')'); + //xx check types + gen_va_arg(&type); + vtop->type = type; + break; + } +#endif + case TOK_INC: case TOK_DEC: t = tok; @@ -4071,6 +4129,15 @@ ST_FUNC void unary(void) if (!ret_nregs) { /* get some space for the returned structure */ size = type_size(&s->type, &align); +#ifdef TCC_TARGET_ARM64 + /* On arm64, a small struct is return in registers. + It is much easier to write it to memory if we know + that we are allowed to write some extra bytes, so + round the allocated space up to a power of 2: */ + if (size < 16) + while (size & (size - 1)) + size = (size | (size - 1)) + 1; +#endif loc = (loc - size) & -align; ret.type = s->type; ret.r = VT_LOCAL | VT_LVAL; @@ -4094,12 +4161,14 @@ ST_FUNC void unary(void) ret.r2 = REG_QRET; #endif } else { +#ifndef TCC_TARGET_ARM64 #ifdef TCC_TARGET_X86_64 if ((ret.type.t & VT_BTYPE) == VT_QLONG) #else if ((ret.type.t & VT_BTYPE) == VT_LLONG) #endif ret.r2 = REG_LRET; +#endif ret.r = REG_IRET; } ret.c.i = 0; @@ -4717,6 +4786,10 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym, if (tok != ';') { gexpr(); gen_assign_cast(&func_vt); +#ifdef TCC_TARGET_ARM64 + // Perhaps it would be better to use this for all backends: + greturn(); +#else if ((func_vt.t & VT_BTYPE) == VT_STRUCT) { CType type, ret_type; int ret_align, ret_nregs; @@ -4770,6 +4843,7 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym, } else { gv(RC_IRET); } +#endif vtop--; /* NOT vpop() because on x86 it would flush the fp stack */ } skip(';'); @@ -5160,9 +5234,9 @@ static void init_putv(CType *type, Section *sec, unsigned long c, /* XXX: generate error if incorrect relocation */ gen_assign_cast(&dtype); bt = type->t & VT_BTYPE; - /* we'll write at most 12 bytes */ - if (c + 12 > sec->data_allocated) { - section_realloc(sec, c + 12); + /* we'll write at most 16 bytes */ + if (c + 16 > sec->data_allocated) { + section_realloc(sec, c + 16); } ptr = sec->data + c; /* XXX: make code faster ? */ @@ -5184,6 +5258,9 @@ static void init_putv(CType *type, Section *sec, unsigned long c, (bt == VT_INT && bit_size != 32))) tcc_error("initializer element is not computable at load time"); switch(bt) { + /* XXX: when cross-compiling we assume that each type has the + same representation on host and target, which is likely to + be wrong in the case of long double */ case VT_BOOL: vtop->c.i = (vtop->c.i != 0); case VT_BYTE: @@ -5203,7 +5280,7 @@ static void init_putv(CType *type, Section *sec, unsigned long c, break; case VT_PTR: { addr_t val = (vtop->c.ptr_offset & bit_mask) << bit_pos; -#ifdef TCC_TARGET_X86_64 +#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64) if (vtop->r & VT_SYM) greloca(sec, vtop->sym, c, R_DATA_PTR, val); else @@ -5217,7 +5294,7 @@ static void init_putv(CType *type, Section *sec, unsigned long c, } default: { int val = (vtop->c.i & bit_mask) << bit_pos; -#ifdef TCC_TARGET_X86_64 +#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64) if (vtop->r & VT_SYM) greloca(sec, vtop->sym, c, R_DATA_PTR, val); else |
