diff options
| author | Shinichiro Hamaji <shinichiro.hamaji _at_ gmail.com> | 2008-12-02 03:19:25 +0900 |
|---|---|---|
| committer | grischka <grischka> | 2008-12-02 02:30:47 +0100 |
| commit | 0a9873aa22731077fad295a4aad2fc1f390c8ac7 (patch) | |
| tree | 95294fbf05ff654ad927276a72dd4f0779a9bba2 /tcc.c | |
| parent | fb0ac27691c1a027d3322c2e1d2d0876163314a1 (diff) | |
| download | tinycc-0a9873aa22731077fad295a4aad2fc1f390c8ac7.tar.gz tinycc-0a9873aa22731077fad295a4aad2fc1f390c8ac7.tar.bz2 | |
Add support of x86-64.
Most change was done in #ifdef TCC_TARGET_X86_64. So, nothing should be broken by this change.
Summary of current status of x86-64 support:
- produces x86-64 object files and executables.
- the x86-64 code generator is based on x86's.
-- for long long integers, we use 64bit registers instead of tcc's generic implementation.
-- for float or double, we use SSE. SSE registers are not utilized well (we only use xmm0 and xmm1).
-- for long double, we use x87 FPU.
- passes make test.
- passes ./libtcc_test.
- can compile tcc.c. The compiled tcc can compile tcc.c, too. (there should be some bugs since the binary size of tcc2 and tcc3 is differ where tcc tcc.c -o tcc2 and tcc2 tcc.c -o tcc3)
- can compile links browser. It seems working.
- not tested well. I tested this work only on my linux box with few programs.
- calling convention of long-double-integer or struct is not exactly the same as GCC's x86-64 ABI.
- implementation of tcc -run is naive (tcc -run tcctest.c works, but tcc -run tcc.c doesn't work). Relocating 64bit addresses seems to be not as simple as 32bit environments.
- shared object support isn't unimplemented
- no bounds checker support
- some builtin functions such as __divdi3 aren't supported
Diffstat (limited to 'tcc.c')
| -rw-r--r-- | tcc.c | 138 |
1 files changed, 128 insertions, 10 deletions
@@ -79,15 +79,16 @@ //#define TCC_TARGET_I386 /* i386 code generator */ //#define TCC_TARGET_ARM /* ARMv4 code generator */ //#define TCC_TARGET_C67 /* TMS320C67xx code generator */ +//#define TCC_TARGET_X86_64 /* x86-64 code generator */ /* default target is I386 */ #if !defined(TCC_TARGET_I386) && !defined(TCC_TARGET_ARM) && \ - !defined(TCC_TARGET_C67) + !defined(TCC_TARGET_C67) && !defined(TCC_TARGET_X86_64) #define TCC_TARGET_I386 #endif #if !defined(_WIN32) && !defined(TCC_UCLIBC) && !defined(TCC_TARGET_ARM) && \ - !defined(TCC_TARGET_C67) + !defined(TCC_TARGET_C67) && !defined(TCC_TARGET_X86_64) #define CONFIG_TCC_BCHECK /* enable bound checking code */ #endif @@ -96,7 +97,8 @@ #endif /* define it to include assembler support */ -#if !defined(TCC_TARGET_ARM) && !defined(TCC_TARGET_C67) +#if !defined(TCC_TARGET_ARM) && !defined(TCC_TARGET_C67) && \ + !defined(TCC_TARGET_X86_64) #define CONFIG_TCC_ASM #endif @@ -531,6 +533,12 @@ struct TCCState { /* output file for preprocessing */ FILE *outfile; + +#ifdef TCC_TARGET_X86_64 + /* buffer to store jump tables */ + char *jmp_table; + int jmp_table_num; +#endif }; /* The current value can be: */ @@ -938,6 +946,10 @@ static inline int is_float(int t) #include "c67-gen.c" #endif +#ifdef TCC_TARGET_X86_64 +#include "x86_64-gen.c" +#endif + #ifdef CONFIG_TCC_STATIC #define RTLD_LAZY 0x001 @@ -4769,26 +4781,33 @@ void save_reg(int r) r = p->r & VT_VALMASK; /* store register in the stack */ type = &p->type; +#ifndef TCC_TARGET_X86_64 if ((p->r & VT_LVAL) || (!is_float(type->t) && (type->t & VT_BTYPE) != VT_LLONG)) type = &int_type; +#else + if (p->r & VT_LVAL) + type = &char_pointer_type; +#endif size = type_size(type, &align); loc = (loc - size) & -align; sv.type.t = type->t; sv.r = VT_LOCAL | VT_LVAL; sv.c.ul = loc; store(r, &sv); -#ifdef TCC_TARGET_I386 +#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64) /* x86 specific: need to pop fp register ST0 if saved */ if (r == TREG_ST0) { o(0xd9dd); /* fstp %st(1) */ } #endif +#ifndef TCC_TARGET_X86_64 /* special long long case */ if ((type->t & VT_BTYPE) == VT_LLONG) { sv.c.ul += 4; store(p->r2, &sv); } +#endif l = loc; saved = 1; } @@ -4939,8 +4958,7 @@ void gbound(void) register value (such as structures). */ int gv(int rc) { - int r, r2, rc2, bit_pos, bit_size, size, align, i; - unsigned long long ll; + int r, rc2, bit_pos, bit_size, size, align, i; /* NOTE: get_reg can modify vstack[] */ if (vtop->type.t & VT_BITFIELD) { @@ -5019,7 +5037,10 @@ int gv(int rc) ((vtop->type.t & VT_BTYPE) == VT_LLONG && !(reg_classes[vtop->r2] & rc2))) { r = get_reg(rc); +#ifndef TCC_TARGET_X86_64 if ((vtop->type.t & VT_BTYPE) == VT_LLONG) { + int r2; + unsigned long long ll; /* two register type load : expand to two words temporarily */ if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) { @@ -5059,7 +5080,9 @@ int gv(int rc) vpop(); /* write second register */ vtop->r2 = r2; - } else if ((vtop->r & VT_LVAL) && !is_float(vtop->type.t)) { + } else +#endif + if ((vtop->r & VT_LVAL) && !is_float(vtop->type.t)) { int t1, t; /* lvalue of scalar type : need to use lvalue type because of possible cast */ @@ -5224,7 +5247,7 @@ void vpop(void) { int v; v = vtop->r & VT_VALMASK; -#ifdef TCC_TARGET_I386 +#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64) /* for x86, we need to pop the FP stack */ if (v == TREG_ST0 && !nocode_wanted) { o(0xd9dd); /* fstp %st(1) */ @@ -5265,6 +5288,11 @@ void gv_dup(void) sv.type.t = VT_INT; if (is_float(t)) { rc = RC_FLOAT; +#ifdef TCC_TARGET_X86_64 + if ((t & VT_BTYPE) == VT_LDOUBLE) { + rc = RC_ST0; + } +#endif sv.type.t = t; } r = gv(rc); @@ -5278,6 +5306,7 @@ void gv_dup(void) } } +#ifndef TCC_TARGET_X86_64 /* generate CPU independent (unsigned) long long operations */ void gen_opl(int op) { @@ -5512,6 +5541,7 @@ void gen_opl(int op) break; } } +#endif /* handle integer constant optimizations and various machine independent opt */ @@ -5790,7 +5820,11 @@ 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 + t = VT_LLONG | VT_UNSIGNED; +#else t = VT_INT | VT_UNSIGNED; +#endif goto std_op; } /* if both pointers, then it must be the '-' op */ @@ -5802,7 +5836,11 @@ void gen_op(int op) u = pointed_size(&vtop[-1].type); gen_opic(op); /* set to integer type */ +#ifdef TCC_TARGET_X86_64 + vtop->type.t = VT_LLONG; +#else vtop->type.t = VT_INT; +#endif vpushi(u); gen_op(TOK_PDIV); } else { @@ -5815,8 +5853,18 @@ void gen_op(int op) swap(&t1, &t2); } type1 = vtop[-1].type; +#ifdef TCC_TARGET_X86_64 + { + CValue cval; + CType ctype; + ctype.t = VT_LLONG; + cval.ull = pointed_size(&vtop[-1].type); + vsetc(&ctype, VT_CONST, &cval); + } +#else /* XXX: cast to int ? (long long case) */ vpushi(pointed_size(&vtop[-1].type)); +#endif gen_op('*'); #ifdef CONFIG_TCC_BCHECK /* if evaluating constant expression, no code should be @@ -6099,6 +6147,7 @@ static void gen_cast(CType *type) } else if ((dbt & VT_BTYPE) == VT_LLONG) { if ((sbt & VT_BTYPE) != VT_LLONG) { /* scalar to long long */ +#ifndef TCC_TARGET_X86_64 /* machine independent conversion */ gv(RC_INT); /* generate high word */ @@ -6113,6 +6162,14 @@ static void gen_cast(CType *type) /* patch second register */ vtop[-1].r2 = vtop->r; vpop(); +#else + int r = gv(RC_INT); + if (sbt != (VT_INT | VT_UNSIGNED)) { + /* x86_64 specific: movslq */ + o(0x6348); + o(0xc0 + (REG_VALUE(r) << 3) + REG_VALUE(r)); + } +#endif } } else if (dbt == VT_BOOL) { /* scalar to bool */ @@ -6571,20 +6628,31 @@ void vstore(void) #endif if (!nocode_wanted) { rc = RC_INT; - if (is_float(ft)) + if (is_float(ft)) { rc = RC_FLOAT; +#ifdef TCC_TARGET_X86_64 + if ((ft & VT_BTYPE) == VT_LDOUBLE) { + rc = RC_ST0; + } +#endif + } r = gv(rc); /* generate value */ /* if lvalue was saved on stack, must read it */ if ((vtop[-1].r & VT_VALMASK) == VT_LLOCAL) { SValue sv; t = get_reg(RC_INT); +#ifdef TCC_TARGET_X86_64 + sv.type.t = VT_PTR; +#else sv.type.t = VT_INT; +#endif sv.r = VT_LOCAL | VT_LVAL; sv.c.ul = vtop[-1].c.ul; load(t, &sv); vtop[-1].r = t | VT_LVAL; } store(r, vtop - 1); +#ifndef TCC_TARGET_X86_64 /* two word case handling : store second register at word + 4 */ if ((ft & VT_BTYPE) == VT_LLONG) { vswap(); @@ -6598,6 +6666,7 @@ void vstore(void) /* XXX: it works because r2 is spilled last ! */ store(vtop->r2, vtop - 1); } +#endif } vswap(); vtop--; /* NOT vpop() because on x86 it would flush the fp stack */ @@ -7107,7 +7176,11 @@ the_end: /* long is never used as type */ if ((t & VT_BTYPE) == VT_LONG) +#ifndef TCC_TARGET_X86_64 t = (t & ~VT_BTYPE) | VT_INT; +#else + t = (t & ~VT_BTYPE) | VT_LLONG; +#endif type->t = t; return type_found; } @@ -8044,8 +8117,14 @@ static void expr_eq(void) if (vtop != vstack) { /* needed to avoid having different registers saved in each branch */ - if (is_float(vtop->type.t)) + if (is_float(vtop->type.t)) { rc = RC_FLOAT; +#ifdef TCC_TARGET_X86_64 + if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) { + rc = RC_ST0; + } +#endif + } else rc = RC_INT; gv(rc); @@ -8115,6 +8194,11 @@ static void expr_eq(void) rc = RC_INT; if (is_float(type.t)) { rc = RC_FLOAT; +#ifdef TCC_TARGET_X86_64 + if ((type.t & VT_BTYPE) == VT_LDOUBLE) { + rc = RC_ST0; + } +#endif } else if ((type.t & VT_BTYPE) == VT_LLONG) { /* for long longs, we use fixed registers to avoid having to handle a complicated move */ @@ -9982,6 +10066,30 @@ static int rt_get_caller_pc(unsigned long *paddr, return 0; } } +#elif defined(__x86_64__) +/* return the PC at frame level 'level'. Return non zero if not found */ +static int rt_get_caller_pc(unsigned long *paddr, + ucontext_t *uc, int level) +{ + unsigned long fp; + int i; + + if (level == 0) { + /* XXX: only support linux */ + *paddr = uc->uc_mcontext.gregs[REG_RIP]; + return 0; + } else { + fp = uc->uc_mcontext.gregs[REG_RBP]; + for(i=1;i<level;i++) { + /* XXX: check address validity with program info */ + if (fp <= 0x1000 || fp >= 0xc0000000) + return -1; + fp = ((unsigned long *)fp)[0]; + } + *paddr = ((unsigned long *)fp)[1]; + return 0; + } +} #else #warning add arch specific rt_get_caller_pc() @@ -10235,6 +10343,9 @@ TCCState *tcc_new(void) #if defined(TCC_TARGET_I386) tcc_define_symbol(s, "__i386__", NULL); #endif +#if defined(TCC_TARGET_X86_64) + tcc_define_symbol(s, "__x86_64__", NULL); +#endif #if defined(TCC_TARGET_ARM) tcc_define_symbol(s, "__ARM_ARCH_4__", NULL); tcc_define_symbol(s, "__arm_elf__", NULL); @@ -10301,6 +10412,10 @@ TCCState *tcc_new(void) /* XXX: currently the PE linker is not ready to support that */ s->leading_underscore = 1; #endif + +#ifdef TCC_TARGET_X86_64 + s->jmp_table = NULL; +#endif return s; } @@ -10336,6 +10451,9 @@ void tcc_delete(TCCState *s1) dynarray_reset(&s1->include_paths, &s1->nb_include_paths); dynarray_reset(&s1->sysinclude_paths, &s1->nb_sysinclude_paths); +#ifdef TCC_TARGET_X86_64 + tcc_free(s1->jmp_table); +#endif tcc_free(s1); } |
