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
This commit is contained in:
committed by
grischka
parent
fb0ac27691
commit
0a9873aa22
138
tcc.c
138
tcc.c
@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user