From 9cb064c2db59fb92a3cee7c0e9fd447aec2083f3 Mon Sep 17 00:00:00 2001 From: Thomas Preud'homme Date: Tue, 12 Jun 2012 17:51:49 +0200 Subject: Imported Upstream version 0.9.26~git20120612.ad5f375 --- .gitignore | 11 +- Changelog | 1 + Makefile | 21 ++- arm-gen.c | 384 ++++++++++++++++++++++++++++++++++++++++++---------- configure | 4 +- examples/ex1.c | 2 +- examples/ex4.c | 2 +- i386-gen.c | 6 +- lib/Makefile | 18 ++- lib/bcheck.c | 8 ++ libtcc.c | 30 ++-- tcc-doc.texi | 2 + tcc.c | 21 ++- tcc.h | 127 ++++++++++------- tccasm.c | 2 + tccelf.c | 25 ++-- tccgen.c | 102 ++++++++++---- tccpe.c | 11 +- tccpp.c | 55 +++++--- tccrun.c | 21 ++- tests/Makefile | 8 +- tests/tcctest.c | 216 +++++++++++++++++++++++++++-- win32/build-tcc.bat | 5 +- win32/tcc-win32.txt | 2 +- x86_64-gen.c | 90 ++++++++---- 25 files changed, 907 insertions(+), 267 deletions(-) diff --git a/.gitignore b/.gitignore index a845ddc..bfcf93a 100644 --- a/.gitignore +++ b/.gitignore @@ -30,11 +30,20 @@ p.c p2.c tcctest[1234] test[1234].out +tests/tcclib.h +tests/tcctest.gcc .gdb_history tcc.1 tcc.pod config.h config.mak config.texi -tests tags +.DS_Store +*.swp +lib/x86_64 +lib/i386 +tcc-doc.info +conftest* +tiny_libmaker +*.dSYM diff --git a/Changelog b/Changelog index 14cc9a1..2a19902 100644 --- a/Changelog +++ b/Changelog @@ -14,6 +14,7 @@ not released: - Support indirect functions as externals (Thomas Preud'homme) - Add support for C99 variable length arrays (Thomas Preud'homme & Joe Soroka) - Improve support of ARM (Daniel Glöckner) +- Support ARM hardfloat calling convention (Thomas Preud'homme) version 0.9.25: diff --git a/Makefile b/Makefile index 43d7cfd..3b6acdd 100644 --- a/Makefile +++ b/Makefile @@ -15,6 +15,7 @@ CFLAGS+=-Wno-pointer-sign -Wno-sign-compare -D_FORTIFY_SOURCE=0 endif endif +ifneq ($(TARGETOS),Darwin) ifeq ($(ARCH),i386) CFLAGS+=-mpreferred-stack-boundary=2 ifeq ($(GCC_MAJOR),2) @@ -23,6 +24,7 @@ else CFLAGS+=-march=i386 -falign-functions=0 endif endif +endif ifdef CONFIG_WIN64 CONFIG_WIN32=yes @@ -36,11 +38,15 @@ endif endif ifeq ($(ARCH),i386) -NATIVE_DEFINES=-DTCC_TARGET_I386 +NATIVE_DEFINES=-DTCC_TARGET_I386 +NATIVE_DEFINES+=$(if $(wildcard /lib/i386-linux-gnu),-DCONFIG_MULTIARCHDIR=\"i386-linux-gnu\") +CFLAGS+=-m32 else ifeq ($(ARCH),x86-64) NATIVE_DEFINES=-DTCC_TARGET_X86_64 -NATIVE_DEFINES+=$(if $(wildcard /lib64/ld-linux-x86-64.so.2),-DTCC_TARGET_X86_64_CENTOS) +CFLAGS+=-m64 +NATIVE_DEFINES+=$(if $(wildcard /usr/lib64),-DCONFIG_LDDIR=\"lib64\") +NATIVE_DEFINES+=$(if $(wildcard /lib/x86_64-linux-gnu),-DCONFIG_MULTIARCHDIR=\"x86_64-linux-gnu\") endif endif @@ -48,7 +54,10 @@ ifeq ($(ARCH),arm) NATIVE_DEFINES=-DTCC_TARGET_ARM NATIVE_DEFINES+=-DWITHOUT_LIBTCC NATIVE_DEFINES+=$(if $(wildcard /lib/ld-linux.so.3),-DTCC_ARM_EABI) +NATIVE_DEFINES+=$(if $(wildcard /lib/arm-linux-gnueabi),-DCONFIG_MULTIARCHDIR=\"arm-linux-gnueabi\") NATIVE_DEFINES+=$(if $(shell grep -l "^Features.* \(vfp\|iwmmxt\) " /proc/cpuinfo),-DTCC_ARM_VFP) +# To use ARM hardfloat calling convension +#NATIVE_DEFINES+=-DTCC_ARM_HARDFLOAT endif ifdef CONFIG_WIN32 @@ -136,6 +145,10 @@ endif ifdef CONFIG_UCLIBC BCHECK_O= endif +ifeq ($(TARGETOS),Darwin) +BCHECK_O= +PROGS+=tiny_libmaker$(EXESUF) +endif ifdef CONFIG_USE_LIBGCC LIBTCC1= @@ -153,7 +166,7 @@ all: $(PROGS) $(TCCLIBS) $(TCCDOCS) # Host Tiny C Compiler tcc$(EXESUF): tcc.o $(LIBTCC) - $(CC) -o $@ $^ $(LIBS) $(LDFLAGS) $(LINK_LIBTCC) + $(CC) -o $@ $^ $(LIBS) $(CFLAGS) $(LDFLAGS) $(LINK_LIBTCC) # Cross Tiny C Compilers %-tcc$(EXESUF): @@ -190,7 +203,7 @@ LIBTCC_INC = $(filter %.h,$(CORE_FILES)) $(filter-out $(CORE_FILES),$(NATIVE_FIL else LIBTCC_OBJ = libtcc.o LIBTCC_INC = $(NATIVE_FILES) -$(LIBTCC_OBJ) tcc.o : NATIVE_DEFINES += -DONE_SOURCE +libtcc.o : NATIVE_DEFINES += -DONE_SOURCE endif $(LIBTCC_OBJ) tcc.o : %.o : %.c $(LIBTCC_INC) diff --git a/arm-gen.c b/arm-gen.c index b7e8665..d8e68ae 100644 --- a/arm-gen.c +++ b/arm-gen.c @@ -737,16 +737,85 @@ static void gcall_or_jmp(int is_jmp) } } +#ifdef TCC_ARM_HARDFLOAT +static int is_float_hgen_aggr(CType *type) +{ + if ((type->t & VT_BTYPE) == VT_STRUCT) { + struct Sym *ref; + int btype, nb_fields = 0; + + ref = type->ref; + btype = ref->type.t & VT_BTYPE; + if (btype == VT_FLOAT || btype == VT_DOUBLE) { + for(; ref && btype == (ref->type.t & VT_BTYPE); ref = ref->next, nb_fields++); + return !ref && nb_fields <= 4; + } + } + return 0; +} + +struct avail_regs { + /* worst case: f(float, double, 3 float struct, double, 3 float struct, double) */ + signed char avail[3]; + int first_hole; + int last_hole; + int first_free_reg; +}; + +#define AVAIL_REGS_INITIALIZER (struct avail_regs) { { 0, 0, 0}, 0, 0, 0 } + +/* Assign a register for a CPRC param with correct size and alignment + * size and align are in bytes, as returned by type_size */ +int assign_fpreg(struct avail_regs *avregs, int align, int size) +{ + int first_reg = 0; + + if (avregs->first_free_reg == -1) + return -1; + if (align >> 3) { // alignment needed (base type: double) + first_reg = avregs->first_free_reg; + if (first_reg & 1) + avregs->avail[avregs->last_hole++] = first_reg++; + } else { + if (size == 4 && avregs->first_hole != avregs->last_hole) + return avregs->avail[avregs->first_hole++]; + else + first_reg = avregs->first_free_reg; + } + if (first_reg + size / 4 <= 16) { + avregs->first_free_reg = first_reg + size / 4; + return first_reg; + } + avregs->first_free_reg = -1; + return -1; +} +#endif + /* Generate function call. The function address is pushed first, then all the parameters in call order. This functions pops all the parameters and the function address. */ void gfunc_call(int nb_args) { - int size, align, r, args_size, i; - Sym *func_sym; + int size, align, r, args_size, i, ncrn, ncprn, argno, vfp_argno; signed char plan[4][2]={{-1,-1},{-1,-1},{-1,-1},{-1,-1}}; - int todo=0xf, keep, plan2[4]={0,0,0,0}; + SValue *before_stack = NULL; /* SValue before first on stack argument */ + SValue *before_vfpreg_hfa = NULL; /* SValue before first in VFP reg hfa argument */ +#ifdef TCC_ARM_HARDFLOAT + struct avail_regs avregs = AVAIL_REGS_INITIALIZER; + signed char vfp_plan[16]; + int plan2[4+16]; + int variadic; +#else + int plan2[4]={0,0,0,0}; +#endif + int vfp_todo=0; + int todo=0, keep; +#ifdef TCC_ARM_HARDFLOAT + memset(vfp_plan, -1, sizeof(vfp_plan)); + memset(plan2, 0, sizeof(plan2)); + variadic = (vtop[-nb_args].type.ref->c == FUNC_ELLIPSIS); +#endif r = vtop->r & VT_VALMASK; if (r == VT_CMP || (r & ~1) == VT_JMP) gv(RC_INT); @@ -763,42 +832,131 @@ void gfunc_call(int nb_args) vpushi(0); vtop->type.t = VT_LLONG; args_size = 0; +#endif + ncrn = ncprn = argno = vfp_argno = 0; + /* Assign argument to registers and stack with alignment. + If, considering alignment constraints, enough registers of the correct type + (core or VFP) are free for the current argument, assign them to it, else + allocate on stack with correct alignment. Whenever a structure is allocated + in registers or on stack, it is always put on the stack at this stage. The + stack is divided in 3 zones. The zone are, from low addresses to high + addresses: structures to be loaded in core registers, structures to be + loaded in VFP registers, argument allocated to stack. SValue's representing + structures in the first zone are moved just after the SValue pointed by + before_vfpreg_hfa. SValue's representing structures in the second zone are + moved just after the SValue pointer by before_stack. */ for(i = nb_args + 1 ; i-- ;) { + int j, assigned_vfpreg = 0; size = type_size(&vtop[-i].type, &align); - if(args_size & (align-1)) { - vpushi(0); - vtop->type.t = VT_VOID; /* padding */ - vrott(i+2); - args_size += 4; - ++nb_args; - } - args_size += (size + 3) & -4; - } - vtop--; -#endif - args_size = 0; - for(i = nb_args ; i-- && args_size < 16 ;) { switch(vtop[-i].type.t & VT_BTYPE) { case VT_STRUCT: case VT_FLOAT: case VT_DOUBLE: case VT_LDOUBLE: - size = type_size(&vtop[-i].type, &align); - size = (size + 3) & -4; - args_size += size; +#ifdef TCC_ARM_HARDFLOAT + if (!variadic) { + int hfa = 0; /* Homogeneous float aggregate */ + + if (is_float(vtop[-i].type.t) + || (hfa = is_float_hgen_aggr(&vtop[-i].type))) { + int end_reg; + + assigned_vfpreg = assign_fpreg(&avregs, align, size); + end_reg = assigned_vfpreg + (size - 1) / 4; + if (assigned_vfpreg >= 0) { + vfp_plan[vfp_argno++]=TREG_F0 + assigned_vfpreg/2; + if (hfa) { + /* before_stack can only have been set because all core registers + are assigned, so no need to care about before_vfpreg_hfa if + before_stack is set */ + if (before_stack) { + vrote(&vtop[-i], &vtop[-i] - before_stack); + before_stack++; + } else if (!before_vfpreg_hfa) + before_vfpreg_hfa = &vtop[-i-1]; + for (j = assigned_vfpreg; j <= end_reg; j++) + vfp_todo|=(1< 4) { + args_size = (ncrn - 4) * 4; + if (!before_stack) + before_stack = &vtop[-i-1]; + } + } + else { + ncrn = 4; + /* No need to set before_vfpreg_hfa if not set since there will no + longer be any structure assigned to core registers */ + if (!before_stack) + before_stack = &vtop[-i-1]; break; + } + continue; default: - plan[nb_args-1-i][0]=args_size/4; - args_size += 4; - if ((vtop[-i].type.t & VT_BTYPE) == VT_LLONG && args_size < 16) { - plan[nb_args-1-i][1]=args_size/4; - args_size += 4; + if (!i) { + break; } + if (ncrn < 4) { + int is_long = (vtop[-i].type.t & VT_BTYPE) == VT_LLONG; + + if (is_long) { + ncrn = (ncrn + 1) & -2; + if (ncrn == 4) { + argno++; + break; + } + } + plan[argno++][0]=ncrn++; + if (is_long) { + plan[argno-1][1]=ncrn++; + } + continue; + } + argno++; } +#ifdef TCC_ARM_EABI + if(args_size & (align-1)) { + vpushi(0); + vtop->type.t = VT_VOID; /* padding */ + vrott(i+2); + args_size += 4; + nb_args++; + argno++; + } +#endif + args_size += (size + 3) & -4; } + vtop--; args_size = keep = 0; for(i = 0;i < nb_args; i++) { - vnrott(keep+1); + vrotb(keep+1); if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) { size = type_size(&vtop->type, &align); /* align to stack align size */ @@ -814,6 +972,12 @@ void gfunc_call(int nb_args) vtop--; args_size += size; } else if (is_float(vtop->type.t)) { +#ifdef TCC_ARM_HARDFLOAT + if (!variadic && --vfp_argno<16 && vfp_plan[vfp_argno]!=-1) { + plan2[keep++]=vfp_plan[vfp_argno]; + continue; + } +#endif #ifdef TCC_ARM_VFP r=vfpr(gv(RC_FLOAT))<<12; size=4; @@ -848,57 +1012,59 @@ void gfunc_call(int nb_args) size=4; if ((vtop->type.t & VT_BTYPE) == VT_LLONG) { lexpand_nr(); - s=RC_INT; - if(nb_args-i<5 && plan[nb_args-i-1][1]!=-1) { - s=regmask(plan[nb_args-i-1][1]); - todo&=~(1<type.t == VT_VOID) { - if(s == RC_INT) + if(s == -1) o(0xE24DD004); /* sub sp,sp,#4 */ vtop--; } else -#endif - if(s == RC_INT) { - r = gv(s); +#endif + if(s == -1) { + r = gv(RC_INT); o(0xE52D0004|(intr(r)<<12)); /* str r,[sp,#-4]! */ vtop--; } else { + size=0; plan2[keep]=s; keep++; } args_size += size; } } - for(i=keep;i--;) { - gv(plan2[i]); - vrott(keep); + for(i = 0; i < keep; i++) { + vrotb(keep); + gv(regmask(plan2[i])); + /* arg is in s(2d+1): plan2[i] alignment occured (ex f,d,f) */ + if (i < keep - 1 && is_float(vtop->type.t) && (plan2[i] <= plan2[i + 1])) { + o(0xEEF00A40|(vfpr(plan2[i])<<12)|vfpr(plan2[i])); + } } save_regs(keep); /* save used temporary registers */ keep++; - if(args_size) { - int n; - n=args_size/4; - if(n>4) - n=4; - todo&=((1<4) + ncrn=4; + todo&=((1<r=i; keep++; + nb_regs++; } } - args_size-=n*4; + args_size-=nb_regs*4; } - vnrott(keep); - func_sym = vtop->type.ref; + if(vfp_todo) { + int nb_fregs=0; + + for(i=0;i<16;i++) + if(vfp_todo&(1<>1)<<12|nb_fregs); + vpushi(0); + /* There might be 2 floats in a double VFP reg but that doesn't seem + to matter */ + if (!(i%2)) + vtop->r=TREG_F0+i/2; + keep++; + nb_fregs++; + } + if (nb_fregs) { + gadd_sp(nb_fregs*4); + args_size-=nb_fregs*4; + } + } + vrotb(keep); gcall_or_jmp(0); if (args_size) gadd_sp(args_size); @@ -924,7 +1109,11 @@ save_regs(keep); /* save used temporary registers */ ++keep; } #ifdef TCC_ARM_VFP +#ifdef TCC_ARM_HARDFLOAT + else if(variadic && is_float(vtop->type.ref->type.t)) { +#else else if(is_float(vtop->type.ref->type.t)) { +#endif if((vtop->type.ref->type.t & VT_BTYPE) == VT_FLOAT) { o(0xEE000A10); /* fmsr s0,r0 */ } else { @@ -942,26 +1131,38 @@ save_regs(keep); /* save used temporary registers */ void gfunc_prolog(CType *func_type) { Sym *sym,*sym2; - int n,addr,size,align; + int n,nf,size,align, variadic, struct_ret = 0; +#ifdef TCC_ARM_HARDFLOAT + struct avail_regs avregs = AVAIL_REGS_INITIALIZER; +#endif sym = func_type->ref; func_vt = sym->type; - - n = 0; - addr = 0; + + n = nf = 0; + variadic = (func_type->ref->c == FUNC_ELLIPSIS); if((func_vt.t & VT_BTYPE) == VT_STRUCT && type_size(&func_vt,&align) > 4) { - func_vc = addr; - addr += 4; n++; + struct_ret = 1; } - for(sym2=sym->next;sym2 && n<4;sym2=sym2->next) { + for(sym2=sym->next;sym2 && (n<4 || nf<16);sym2=sym2->next) { size = type_size(&sym2->type, &align); - n += (size + 3) / 4; +#ifdef TCC_ARM_HARDFLOAT + if (!variadic && (is_float(sym2->type.t) + || is_float_hgen_aggr(&sym2->type))) { + int tmpnf = assign_fpreg(&avregs, align, size) + 1; + nf = (tmpnf > nf) ? tmpnf : nf; + } else +#endif + if (n < 4) + n += (size + 3) / 4; } + if (struct_ret) + func_vc = nf * 4; o(0xE1A0C00D); /* mov ip,sp */ - if(func_type->ref->c == FUNC_ELLIPSIS) + if(variadic) n=4; if(n) { if(n>4) @@ -971,20 +1172,57 @@ void gfunc_prolog(CType *func_type) #endif o(0xE92D0000|((1<16) + nf=16; + nf=(nf+1)&-2; /* nf => HARDFLOAT => EABI */ + o(0xED2D0A00|nf); /* save s0-s15 on stack if needed */ + } o(0xE92D5800); /* save fp, ip, lr */ o(0xE28DB00C); /* add fp, sp, #12 */ func_sub_sp_offset = ind; - o(0xE1A00000); /* nop, leave space for stack adjustment */ - while ((sym = sym->next)) { - CType *type; - type = &sym->type; - size = type_size(type, &align); - size = (size + 3) & -4; + o(0xE1A00000); /* nop, leave space for stack adjustment in epilogue */ + { + int addr, pn = struct_ret, sn = 0; /* pn=core, sn=stack */ + +#ifdef TCC_ARM_HARDFLOAT + avregs = AVAIL_REGS_INITIALIZER; +#endif + while ((sym = sym->next)) { + CType *type; + type = &sym->type; + size = type_size(type, &align); + size = (size + 3) >> 2; +#ifdef TCC_ARM_HARDFLOAT + if (!variadic && (is_float(sym->type.t) + || is_float_hgen_aggr(&sym->type))) { + int fpn = assign_fpreg(&avregs, align, size << 2); + if (fpn >= 0) { + addr = fpn * 4; + } else + goto from_stack; + } else +#endif + if (pn < 4) { +#ifdef TCC_ARM_EABI + pn = (pn + (align-1)/4) & -(align/4); +#endif + addr = (nf + pn) * 4; + pn += size; + if (!sn && pn > 4) + sn = (pn - 4); + } else { +#ifdef TCC_ARM_HARDFLOAT +from_stack: +#endif #ifdef TCC_ARM_EABI - addr = (addr + align - 1) & -align; + sn = (sn + (align-1)/4) & -(align/4); #endif - sym_push(sym->v & ~SYM_FIELD, type, VT_LOCAL | lvalue_type(type->t), addr); - addr += size; + addr = (n + nf + sn) * 4; + sn += size; + } + sym_push(sym->v & ~SYM_FIELD, type, VT_LOCAL | lvalue_type(type->t), addr); + } } last_itod_magic=0; leaffunc = 1; @@ -997,6 +1235,8 @@ void gfunc_epilog(void) uint32_t x; int diff; #ifdef TCC_ARM_EABI + /* Useless but harmless copy of the float result into main register(s) in case + of variadic function in the hardfloat variant */ if(is_float(func_vt.t)) { if((func_vt.t & VT_BTYPE) == VT_FLOAT) o(0xEE100A10); /* fmrs r0, s0 */ diff --git a/configure b/configure index 3d68b1c..f152cf9 100755 --- a/configure +++ b/configure @@ -168,7 +168,7 @@ for opt do ;; --help|-h) show_help="yes" ;; - *) echo "configure: unrecognized option $opt"; exit 1 + *) echo "configure: WARNING: unrecognized option $opt" ;; esac done @@ -352,6 +352,7 @@ echo "Doc directory $docdir" echo "Target root prefix $sysroot" echo "Source path $source_path" echo "C compiler $cc" +echo "Target OS $targetos" echo "CPU $cpu" echo "Big Endian $bigendian" echo "gprof enabled $gprof" @@ -425,6 +426,7 @@ else echo "Unsupported CPU" exit 1 fi +echo "TARGETOS=$targetos" >> config.mak if test "$noldl" = "yes" ; then echo "CONFIG_NOLDL=yes" >> config.mak fi diff --git a/examples/ex1.c b/examples/ex1.c index 87d6019..3d2a3e1 100755 --- a/examples/ex1.c +++ b/examples/ex1.c @@ -1,4 +1,4 @@ -#!/usr/bin/env tcc -run +#!/usr/local/bin/tcc -run #include int main() diff --git a/examples/ex4.c b/examples/ex4.c index 7611910..f92c0da 100755 --- a/examples/ex4.c +++ b/examples/ex4.c @@ -1,4 +1,4 @@ -#!/usr/bin/env tcc -run -L/usr/X11R6/lib -lX11 +#!/usr/local/bin/tcc -run -L/usr/X11R6/lib -lX11 #include #include #include diff --git a/i386-gen.c b/i386-gen.c index 686419c..6635559 100644 --- a/i386-gen.c +++ b/i386-gen.c @@ -229,8 +229,10 @@ ST_FUNC void load(int r, SValue *sv) v1.type.t = VT_INT; v1.r = VT_LOCAL | VT_LVAL; v1.c.ul = fc; - load(r, &v1); fr = r; + if (!(reg_classes[fr] & RC_INT)) + fr = get_reg(RC_INT); + load(fr, &v1); } if ((ft & VT_BTYPE) == VT_FLOAT) { o(0xd9); /* flds */ @@ -1068,7 +1070,7 @@ ST_FUNC void gen_bounded_ptr_deref(void) case 12: func = TOK___bound_ptr_indir12; break; case 16: func = TOK___bound_ptr_indir16; break; default: - tcc_error("unhandled size when derefencing bounded pointer"); + tcc_error("unhandled size when dereferencing bounded pointer"); func = 0; break; } diff --git a/lib/Makefile b/lib/Makefile index 071c49f..d7d2c3f 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -14,11 +14,15 @@ ifndef TARGET else ifeq ($(ARCH),i386) TARGET = i386 - XCC = gcc -O2 + ifneq ($(TARGETOS),Darwin) + XCC = gcc -O2 -m32 + endif else ifeq ($(ARCH),x86-64) TARGET = x86_64 - XCC = gcc -O2 + ifneq ($(TARGETOS),Darwin) + XCC = gcc -O2 -m64 + endif endif endif endif @@ -55,12 +59,18 @@ else ifeq "$(TARGET)" "i386" OBJ = $(addprefix $(DIR)/,$(I386_O)) TGT = -DTCC_TARGET_I386 - XCC ?= $(TCC) -B$(TOP) + XCC ?= $(TCC) -B$(TOP) -m32 -D_ANSI_SOURCE + ifeq ($(TARGETOS),Darwin) + XAR = $(DIR)/tiny_libmaker$(EXESUF) + endif else ifeq "$(TARGET)" "x86_64" OBJ = $(addprefix $(DIR)/,$(X86_64_O)) TGT = -DTCC_TARGET_X86_64 - XCC ?= $(TCC) -B$(TOP) + XCC ?= $(TCC) -B$(TOP) -m64 -D_ANSI_SOURCE + ifeq ($(TARGETOS),Darwin) + XAR = $(DIR)/tiny_libmaker$(EXESUF) + endif else $(error libtcc1.a not supported on target '$(TARGET)') endif diff --git a/lib/bcheck.c b/lib/bcheck.c index 9996649..48d7606 100644 --- a/lib/bcheck.c +++ b/lib/bcheck.c @@ -216,6 +216,14 @@ BOUND_PTR_INDIR(16) __asm__ __volatile__ ("movl %%ebp,%0" :"=g" (fp1));\ fp = fp1[0];\ } +#elif defined(__x86_64__) +/* TCC always creates %rbp frames also on x86_64, so use them. */ +#define GET_CALLER_FP(fp)\ +{\ + unsigned long *fp1;\ + __asm__ __volatile__ ("movq %%rbp,%0" :"=g" (fp1));\ + fp = fp1[0];\ +} #else #error put code to extract the calling frame pointer #endif diff --git a/libtcc.c b/libtcc.c index 8f5cd0e..b0a9b1a 100644 --- a/libtcc.c +++ b/libtcc.c @@ -237,6 +237,8 @@ PUB_FUNC void *tcc_realloc(void *ptr, unsigned long size) mem_cur_size -= malloc_usable_size(ptr); #endif ptr1 = realloc(ptr, size); + if (!ptr1 && size) + tcc_error("memory full"); #ifdef MEM_DEBUG /* NOTE: count not correct if alloc error, but not critical */ mem_cur_size += malloc_usable_size(ptr1); @@ -282,8 +284,6 @@ PUB_FUNC void dynarray_add(void ***ptab, int *nb_ptr, void *data) else nb_alloc = nb * 2; pp = tcc_realloc(pp, nb_alloc * sizeof(void *)); - if (!pp) - tcc_error("memory full"); *ptab = pp; } pp[nb++] = data; @@ -377,8 +377,6 @@ ST_FUNC void section_realloc(Section *sec, unsigned long new_size) while (size < new_size) size = size * 2; data = tcc_realloc(sec->data, size); - if (!data) - tcc_error("memory full"); memset(data + sec->data_allocated, 0, size - sec->data_allocated); sec->data = data; sec->data_allocated = size; @@ -425,7 +423,7 @@ ST_FUNC Section *find_section(TCCState *s1, const char *name) /* update sym->c so that it points to an external symbol in section 'section' with value 'value' */ ST_FUNC void put_extern_sym2(Sym *sym, Section *section, - unsigned long value, unsigned long size, + uplong value, unsigned long size, int can_add_underscore) { int sym_type, sym_bind, sh_num, info, other; @@ -529,7 +527,7 @@ ST_FUNC void put_extern_sym2(Sym *sym, Section *section, } ST_FUNC void put_extern_sym(Sym *sym, Section *section, - unsigned long value, unsigned long size) + uplong value, unsigned long size) { put_extern_sym2(sym, section, value, size, 1); } @@ -747,6 +745,12 @@ static int tcc_compile(TCCState *s1) char_pointer_type.t = VT_BYTE; mk_pointer(&char_pointer_type); +#if PTR_SIZE == 4 + size_type.t = VT_INT; +#else + size_type.t = VT_LLONG; +#endif + func_old_type.t = VT_FUNC; func_old_type.ref = sym_push(SYM_FIELD, &int_type, FUNC_CDECL, FUNC_OLD); @@ -980,6 +984,7 @@ LIBTCCAPI TCCState *tcc_new(void) #endif /* glibc defines */ + tcc_define_symbol(s, "__REDIRECT(name, proto, alias)", "name proto __asm__ (#alias)"); tcc_define_symbol(s, "__REDIRECT_NTH(name, proto, alias)", "name proto __asm__ (#alias) __THROW"); #ifndef TCC_TARGET_PE @@ -1002,6 +1007,7 @@ LIBTCCAPI TCCState *tcc_new(void) ".strtab", ".hashtab", SHF_PRIVATE); strtab_section = symtab_section->link; + s->symtab = symtab_section; /* private symbol table for dynamic symbols */ s->dynsymtab_section = new_symtab(s, ".dynsymtab", SHT_SYMTAB, SHF_PRIVATE, @@ -1488,12 +1494,14 @@ PUB_FUNC const char * tcc_set_linker(TCCState *s, char *option, int multi) end = NULL; if (link_option(option, "Bsymbolic", &p)) { s->symbolic = TRUE; + } else if (link_option(option, "nostdlib", &p)) { + s->nostdlib = TRUE; } else if (link_option(option, "fini=", &p)) { s->fini_symbol = p; if (s->warn_unsupported) tcc_warning("ignoring -fini %s", p); } else if (link_option(option, "image-base=", &p)) { - s->text_addr = strtoul(p, &end, 16); + s->text_addr = strtoull(p, &end, 16); s->has_text_addr = 1; } else if (link_option(option, "init=", &p)) { s->init_symbol = p; @@ -1562,10 +1570,12 @@ PUB_FUNC const char * tcc_set_linker(TCCState *s, char *option, int multi) #endif } else if (link_option(option, "Ttext=", &p)) { - s->text_addr = strtoul(p, &end, 16); + s->text_addr = strtoull(p, &end, 16); s->has_text_addr = 1; - } else { + char *comma_ptr = strchr(option, ','); + if (comma_ptr) + *comma_ptr = '\0'; return option; } @@ -1657,7 +1667,7 @@ PUB_FUNC void tcc_gen_makedeps(TCCState *s, const char *target, const char *file fprintf(depout, "%s : \\\n", target); for (i=0; inb_target_deps; ++i) - fprintf(depout, "\t%s \\\n", s->target_deps[i]); + fprintf(depout, " %s \\\n", s->target_deps[i]); fprintf(depout, "\n"); fclose(depout); } diff --git a/tcc-doc.texi b/tcc-doc.texi index 2522e48..4d4a029 100644 --- a/tcc-doc.texi +++ b/tcc-doc.texi @@ -334,6 +334,8 @@ Generate additional support code to check memory allocations and array/pointer bounds. @option{-g} is implied. Note that the generated code is slower and bigger in this case. +Note: @option{-b} is only available on i386 for the moment. + @item -bt N Display N callers in stack traces. This is useful with @option{-g} or @option{-b}. diff --git a/tcc.c b/tcc.c index 0c51451..5dd5725 100644 --- a/tcc.c +++ b/tcc.c @@ -35,6 +35,7 @@ static int do_bench = 0; static int gen_deps; static const char *deps_outfile; static const char *m_option; +static CString linker_arg; #define TCC_OPTION_HAS_ARG 0x0001 #define TCC_OPTION_NOSEP 0x0002 /* cannot have space before option and arg */ @@ -282,8 +283,9 @@ static int parse_args(TCCState *s, int argc, char **argv) int was_pthread; was_pthread = 0; /* is set if commandline contains -pthread key */ + optind = 0; + cstr_new(&linker_arg); - optind = 1; while (optind < argc) { r = argv[optind++]; @@ -442,8 +444,10 @@ static int parse_args(TCCState *s, int argc, char **argv) s->rdynamic = 1; break; case TCC_OPTION_Wl: - if ((r = (char *) tcc_set_linker(s, (char *)optarg, TRUE))) - tcc_error("unsupported linker option '%s'", r); + if (linker_arg.size) + --linker_arg.size, cstr_ccat(&linker_arg, ','); + cstr_cat(&linker_arg, optarg); + cstr_ccat(&linker_arg, '\0'); break; case TCC_OPTION_E: output_type = TCC_OUTPUT_PREPROCESS; @@ -465,6 +469,8 @@ static int parse_args(TCCState *s, int argc, char **argv) } } } + if (NULL != (r1 = tcc_set_linker(s, (char *) linker_arg.data, TRUE))) + tcc_error("unsupported linker option '%s'", r1); /* fixme: these options could be different on your platform */ if (was_pthread && output_type != TCC_OUTPUT_OBJ) { dynarray_add((void ***)&files, &nb_files, "-lpthread"); @@ -494,7 +500,7 @@ int main(int argc, char **argv) m_option = NULL; ret = 0; - optind = parse_args(s, argc, argv); + optind = parse_args(s, argc - 1, argv + 1); #if defined TCC_TARGET_X86_64 || defined TCC_TARGET_I386 if (m_option) @@ -578,7 +584,11 @@ int main(int argc, char **argv) tcc_print_stats(s, getclock_us() - start_time); if (s->output_type == TCC_OUTPUT_MEMORY) { - ret = tcc_run(s, argc - optind, argv + optind); +#ifdef TCC_IS_NATIVE + ret = tcc_run(s, argc - 1 - optind, argv + 1 + optind); +#else + tcc_error_noabort("-run is not available in a cross compiler"); +#endif } else if (s->output_type == TCC_OUTPUT_PREPROCESS) { if (s->outfile) fclose(s->outfile); @@ -593,6 +603,7 @@ int main(int argc, char **argv) } tcc_delete(s); + cstr_free(&linker_arg); tcc_free(outfile); #ifdef MEM_DEBUG diff --git a/tcc.h b/tcc.h index d158829..8bca9ae 100644 --- a/tcc.h +++ b/tcc.h @@ -49,11 +49,9 @@ #include /* getcwd */ #define inline __inline #define inp next_inp -#ifdef _WIN64 -# define uplong unsigned long long -#endif #ifdef LIBTCC_AS_DLL # define LIBTCCAPI __declspec(dllexport) +# define PUB_FUNC LIBTCCAPI #endif #endif @@ -67,17 +65,10 @@ #endif /* !CONFIG_TCCBOOT */ -#ifndef uplong -#define uplong unsigned long -#endif - #ifndef PAGESIZE #define PAGESIZE 4096 #endif -#include "elf.h" -#include "stab.h" - #ifndef O_BINARY #define O_BINARY 0 #endif @@ -86,6 +77,8 @@ #define SA_SIGINFO 0x00000004u #endif +#include "elf.h" +#include "stab.h" #include "libtcc.h" /* parser debug */ @@ -131,8 +124,26 @@ #define TCC_TARGET_COFF #endif -#if !defined(CONFIG_TCCBOOT) -#define CONFIG_TCC_BACKTRACE +/* only native compiler supports -run */ +#if defined _WIN32 == defined TCC_TARGET_PE +# if (defined __i386__ || defined _X86_) && defined TCC_TARGET_I386 +# define TCC_IS_NATIVE +# elif (defined __x86_64__ || defined _AMD64_) && defined TCC_TARGET_X86_64 +# define TCC_IS_NATIVE +# elif defined __arm__ && defined TCC_TARGET_ARM +# define TCC_IS_NATIVE +# endif +#endif + +#if defined TCC_IS_NATIVE && !defined CONFIG_TCCBOOT +# define CONFIG_TCC_BACKTRACE +#endif + +/* target address type */ +#if defined TCC_TARGET_X86_64 && (!defined __x86_64__ || defined _WIN32) +# define uplong unsigned long long +#else +# define uplong unsigned long #endif /* ------------ path configuration ------------ */ @@ -141,17 +152,17 @@ # define CONFIG_SYSROOT "" #endif -#ifndef CONFIG_TCC_LDDIR -# if defined(TCC_TARGET_X86_64_CENTOS) -# define CONFIG_TCC_LDDIR "/lib64" -# else -# define CONFIG_TCC_LDDIR "/lib" -# endif +#ifdef CONFIG_MULTIARCHDIR +# define CONFIG_LDDIR "lib/" CONFIG_MULTIARCHDIR +#endif + +#ifndef CONFIG_LDDIR +# define CONFIG_LDDIR "lib" #endif /* path to find crt1.o, crti.o and crtn.o */ #ifndef CONFIG_TCC_CRTPREFIX -# define CONFIG_TCC_CRTPREFIX CONFIG_SYSROOT "/usr" CONFIG_TCC_LDDIR +# define CONFIG_TCC_CRTPREFIX CONFIG_SYSROOT "/usr/" CONFIG_LDDIR #endif /* Below: {B} is substituted by CONFIG_TCCDIR (rsp. -B option) */ @@ -160,6 +171,13 @@ #ifndef CONFIG_TCC_SYSINCLUDEPATHS # ifdef TCC_TARGET_PE # define CONFIG_TCC_SYSINCLUDEPATHS "{B}/include;{B}/include/winapi" +# elif defined CONFIG_MULTIARCHDIR +# define CONFIG_TCC_SYSINCLUDEPATHS \ + CONFIG_SYSROOT "/usr/local/include" \ + ":" CONFIG_SYSROOT "/usr/local/include/" CONFIG_MULTIARCHDIR \ + ":" CONFIG_SYSROOT "/usr/include" \ + ":" CONFIG_SYSROOT "/usr/include/" CONFIG_MULTIARCHDIR \ + ":" "{B}/include" # else # define CONFIG_TCC_SYSINCLUDEPATHS \ CONFIG_SYSROOT "/usr/local/include" \ @@ -174,9 +192,9 @@ # define CONFIG_TCC_LIBPATHS "{B}/lib" # else # define CONFIG_TCC_LIBPATHS \ - CONFIG_SYSROOT "/usr" CONFIG_TCC_LDDIR \ - ":" CONFIG_SYSROOT CONFIG_TCC_LDDIR \ - ":" CONFIG_SYSROOT "/usr/local" CONFIG_TCC_LDDIR + CONFIG_SYSROOT "/usr/" CONFIG_LDDIR \ + ":" CONFIG_SYSROOT "/" CONFIG_LDDIR \ + ":" CONFIG_SYSROOT "/usr/local/" CONFIG_LDDIR # endif #endif @@ -185,20 +203,22 @@ # if defined __FreeBSD__ # define CONFIG_TCC_ELFINTERP "/libexec/ld-elf.so.1" # elif defined __FreeBSD_kernel__ -# define CONFIG_TCC_ELFINTERP CONFIG_TCC_LDDIR"/ld.so.1" +# define CONFIG_TCC_ELFINTERP "/lib/ld.so.1" +# elif defined TCC_ARM_HARDFLOAT +# define CONFIG_TCC_ELFINTERP "/lib/ld-linux-armhf.so.3" # elif defined TCC_ARM_EABI -# define CONFIG_TCC_ELFINTERP CONFIG_TCC_LDDIR"/ld-linux.so.3" +# define CONFIG_TCC_ELFINTERP "/lib/ld-linux.so.3" # elif defined(TCC_TARGET_X86_64) -# define CONFIG_TCC_ELFINTERP CONFIG_TCC_LDDIR"/ld-linux-x86-64.so.2" +# define CONFIG_TCC_ELFINTERP "/lib64/ld-linux-x86-64.so.2" # elif defined(TCC_UCLIBC) -# define CONFIG_TCC_ELFINTERP CONFIG_TCC_LDDIR"/ld-uClibc.so.0" +# define CONFIG_TCC_ELFINTERP "/lib/ld-uClibc.so.0" # else -# define CONFIG_TCC_ELFINTERP CONFIG_TCC_LDDIR"/ld-linux.so.2" +# define CONFIG_TCC_ELFINTERP "/lib/ld-linux.so.2" # endif #endif /* library to use with CONFIG_USE_LIBGCC instead of libtcc1.a */ -#define TCC_LIBGCC CONFIG_SYSROOT CONFIG_TCC_LDDIR "/libgcc_s.so.1" +#define TCC_LIBGCC CONFIG_SYSROOT "/" CONFIG_LDDIR "/libgcc_s.so.1" /* -------------------------------------------- */ @@ -313,8 +333,8 @@ typedef struct Section { int sh_addralign; /* elf section alignment */ int sh_entsize; /* elf entry size */ unsigned long sh_size; /* section size (only used during output) */ - unsigned long sh_addr; /* address at which the section is relocated */ - unsigned long sh_offset; /* file offset */ + uplong sh_addr; /* address at which the section is relocated */ + unsigned long sh_offset; /* file offset */ int nb_hashed_syms; /* used to resize the hash table */ struct Section *link; /* link to another section */ struct Section *reloc; /* corresponding section for relocation, if any */ @@ -509,6 +529,9 @@ struct TCCState { /* exported dynamic symbol section */ Section *dynsym; + /* copy of the gobal symtab_section variable */ + Section *symtab; + int nostdinc; /* if true, no standard headers are added */ int nostdlib; /* if true, no standard libraries are added */ int nocommon; /* if true, do not use common symbols for .bss data */ @@ -531,7 +554,7 @@ struct TCCState { int alacarte_link; /* address of text section */ - unsigned long text_addr; + uplong text_addr; int has_text_addr; /* symbols to call at load-time / unload-time */ @@ -871,7 +894,8 @@ enum tcc_token { #ifndef __GNUC__ #define strtold (long double)strtod #define strtof (float)strtod - #define strtoll (long long)strtol + #define strtoll _strtoi64 + #define strtoull _strtoui64 #endif #else /* XXX: need to define this to use them in non ISOC99 context */ @@ -944,12 +968,6 @@ ST_DATA int tcc_ext; /* XXX: get rid of this ASAP */ ST_DATA struct TCCState *tcc_state; -#ifdef CONFIG_TCC_BACKTRACE -ST_DATA int rt_num_callers; -ST_DATA const char **rt_bound_error_msg; -ST_DATA void *rt_prog_main; -#endif - #define AFF_PRINT_ERROR 0x0001 /* print error if file not found */ #define AFF_REFERENCED_DLL 0x0002 /* load a referenced dll from another dll */ #define AFF_PREPROCESS 0x0004 /* preprocess file */ @@ -978,12 +996,11 @@ PUB_FUNC void tcc_error(const char *fmt, ...); PUB_FUNC void tcc_warning(const char *fmt, ...); /* other utilities */ -ST_INLN void cstr_ccat(CString *cstr, int ch); -ST_FUNC void cstr_cat(CString *cstr, const char *str); -ST_FUNC void cstr_wccat(CString *cstr, int ch); -ST_FUNC void cstr_new(CString *cstr); -ST_FUNC void cstr_free(CString *cstr); -ST_FUNC void add_char(CString *cstr, int c); +PUB_FUNC void cstr_ccat(CString *cstr, int ch); +PUB_FUNC void cstr_cat(CString *cstr, const char *str); +PUB_FUNC void cstr_wccat(CString *cstr, int ch); +PUB_FUNC void cstr_new(CString *cstr); +PUB_FUNC void cstr_free(CString *cstr); #define cstr_reset(cstr) cstr_free(cstr) ST_FUNC Section *new_section(TCCState *s1, const char *name, int sh_type, int sh_flags); @@ -992,8 +1009,8 @@ ST_FUNC void *section_ptr_add(Section *sec, unsigned long size); ST_FUNC void section_reserve(Section *sec, unsigned long size); ST_FUNC Section *find_section(TCCState *s1, const char *name); -ST_FUNC void put_extern_sym2(Sym *sym, Section *section, unsigned long value, unsigned long size, int can_add_underscore); -ST_FUNC void put_extern_sym(Sym *sym, Section *section, unsigned long value, unsigned long size); +ST_FUNC void put_extern_sym2(Sym *sym, Section *section, uplong value, unsigned long size, int can_add_underscore); +ST_FUNC void put_extern_sym(Sym *sym, Section *section, uplong value, unsigned long size); ST_FUNC void greloc(Section *s, Sym *sym, unsigned long offset, int type); ST_INLN void sym_free(Sym *sym); @@ -1019,9 +1036,6 @@ PUB_FUNC int tcc_set_flag(TCCState *s, const char *flag_name, int value); PUB_FUNC void tcc_print_stats(TCCState *s, int64_t total_time); PUB_FUNC char *tcc_default_target(TCCState *s, const char *default_file); PUB_FUNC void tcc_gen_makedeps(TCCState *s, const char *target, const char *filename); -#ifdef CONFIG_TCC_BACKTRACE -PUB_FUNC void tcc_set_num_callers(int n); -#endif /* ------------ tccpp.c ------------ */ @@ -1105,7 +1119,7 @@ ST_DATA Sym *local_stack; ST_DATA Sym *local_label_stack; ST_DATA Sym *global_label_stack; ST_DATA Sym *define_stack; -ST_DATA CType char_pointer_type, func_old_type, int_type; +ST_DATA CType char_pointer_type, func_old_type, int_type, size_type; ST_DATA SValue vstack[VSTACK_SIZE], *vtop; ST_DATA int rsym, anon_sym, ind, loc; @@ -1126,10 +1140,11 @@ ST_FUNC Sym *external_global_sym(int v, CType *type, int r); ST_FUNC void vset(CType *type, int r, int v); ST_FUNC void vswap(void); ST_FUNC void vpush_global_sym(CType *type, int v); +ST_FUNC void vrote(SValue *e, int n); ST_FUNC void vrott(int n); +ST_FUNC void vrotb(int n); #ifdef TCC_TARGET_ARM ST_FUNC int get_reg_ex(int rc, int rc2); -ST_FUNC void vnrott(int n); ST_FUNC void lexpand_nr(void); #endif ST_FUNC void vpushv(SValue *v); @@ -1328,6 +1343,14 @@ ST_FUNC void *resolve_sym(TCCState *s1, const char *symbol); #elif !defined TCC_TARGET_PE || !defined _WIN32 ST_FUNC void *resolve_sym(TCCState *s1, const char *symbol); #endif + +#ifdef CONFIG_TCC_BACKTRACE +ST_DATA int rt_num_callers; +ST_DATA const char **rt_bound_error_msg; +ST_DATA void *rt_prog_main; +PUB_FUNC void tcc_set_num_callers(int n); +#endif + /********************************************************/ /* include the target specific definitions */ @@ -1347,7 +1370,7 @@ ST_FUNC void *resolve_sym(TCCState *s1, const char *symbol); #endif #undef TARGET_DEFS_ONLY -ST_DATA const int reg_classes[NB_REGS]; +ST_DATA const int reg_classes[]; /********************************************************/ #undef ST_DATA diff --git a/tccasm.c b/tccasm.c index 527c006..d1d2af1 100644 --- a/tccasm.c +++ b/tccasm.c @@ -19,6 +19,7 @@ */ #include "tcc.h" +#ifdef CONFIG_TCC_ASM ST_FUNC int asm_get_local_label_name(TCCState *s1, unsigned int n) { @@ -1115,3 +1116,4 @@ ST_FUNC void asm_global_instr(void) cstr_free(&astr); } +#endif /* CONFIG_TCC_ASM */ diff --git a/tccelf.c b/tccelf.c index 40c88ab..655860b 100644 --- a/tccelf.c +++ b/tccelf.c @@ -162,8 +162,8 @@ static void *get_elf_sym_addr(TCCState *s, const char *name, int err) int sym_index; ElfW(Sym) *sym; - sym_index = find_elf_sym(symtab_section, name); - sym = &((ElfW(Sym) *)symtab_section->data)[sym_index]; + sym_index = find_elf_sym(s->symtab, name); + sym = &((ElfW(Sym) *)s->symtab->data)[sym_index]; if (!sym_index || sym->st_shndx == SHN_UNDEF) { if (err) tcc_error("%s not defined", name); @@ -634,6 +634,8 @@ ST_FUNC void relocate_section(TCCState *s1, Section *s) case R_ARM_ABS32: *(int *)ptr += val; break; + case R_ARM_REL32: + *(int *)ptr += val - addr; case R_ARM_BASE_PREL: *(int *)ptr += s1->got->sh_addr - addr; break; @@ -836,8 +838,6 @@ static void put_got_offset(TCCState *s1, int index, unsigned long val) while (index >= n) n *= 2; tab = tcc_realloc(s1->got_offsets, n * sizeof(unsigned long)); - if (!tab) - tcc_error("memory full"); s1->got_offsets = tab; memset(s1->got_offsets + s1->nb_got_offsets, 0, (n - s1->nb_got_offsets) * sizeof(unsigned long)); @@ -1153,7 +1153,7 @@ ST_FUNC Section *new_symtab(TCCState *s1, } /* put dynamic tag */ -static void put_dt(Section *dynamic, int dt, unsigned long val) +static void put_dt(Section *dynamic, int dt, uplong val) { ElfW(Dyn) *dyn; dyn = section_ptr_add(dynamic, sizeof(ElfW(Dyn))); @@ -1381,7 +1381,7 @@ ST_FUNC void fill_got_entry(TCCState *s1, ElfW_Rel *rel) section_reserve(s1->got, offset + PTR_SIZE); #ifdef TCC_TARGET_X86_64 /* only works for x86-64 */ - put32(s1->got->data + offset, sym->st_value >> 32); + put32(s1->got->data + offset + 4, sym->st_value >> 32); #endif put32(s1->got->data + offset, sym->st_value & 0xffffffff); } @@ -1421,8 +1421,9 @@ static int elf_output_file(TCCState *s1, const char *filename) FILE *f; int fd, mode, ret; int *section_order; - int shnum, i, phnum, file_offset, offset, size, j, tmp, sh_order_index, k; - unsigned long addr; + int shnum, i, phnum, file_offset, offset, size, j, sh_order_index, k; + long long tmp; + uplong addr; Section *strsec, *s; ElfW(Shdr) shdr, *sh; ElfW(Phdr) *phdr, *ph; @@ -1430,9 +1431,9 @@ static int elf_output_file(TCCState *s1, const char *filename) unsigned long saved_dynamic_data_offset; ElfW(Sym) *sym; int type, file_type; - unsigned long rel_addr, rel_size; + uplong rel_addr, rel_size; #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) - unsigned long bss_addr, bss_size; + uplong bss_addr, bss_size; #endif file_type = s1->output_type; @@ -1747,7 +1748,7 @@ static int elf_output_file(TCCState *s1, const char *filename) addr = s1->text_addr; /* we ensure that (addr % ELF_PAGE_SIZE) == file_offset % ELF_PAGE_SIZE */ - a_offset = addr & (s1->section_align - 1); + a_offset = (int) (addr & (s1->section_align - 1)); p_offset = file_offset & (s1->section_align - 1); if (a_offset < p_offset) a_offset += s1->section_align; @@ -1821,7 +1822,7 @@ static int elf_output_file(TCCState *s1, const char *filename) tmp = addr; addr = (addr + s->sh_addralign - 1) & ~(s->sh_addralign - 1); - file_offset += addr - tmp; + file_offset += (int) ( addr - tmp ); s->sh_offset = file_offset; s->sh_addr = addr; diff --git a/tccgen.c b/tccgen.c index dc67f02..7295267 100644 --- a/tccgen.c +++ b/tccgen.c @@ -64,7 +64,7 @@ ST_DATA int func_vc; ST_DATA int last_line_num, last_ind, func_ind; /* debug last line number and pc */ ST_DATA char *funcname; -ST_DATA CType char_pointer_type, func_old_type, int_type; +ST_DATA CType char_pointer_type, func_old_type, int_type, size_type; /* ------------------------------------------------------------------------- */ static void gen_cast(CType *type); @@ -325,6 +325,17 @@ ST_FUNC void vpushi(int v) vsetc(&int_type, VT_CONST, &cval); } +/* push a pointer sized constant */ +static void vpushs(long long v) +{ + CValue cval; + if (PTR_SIZE == 4) + cval.i = (int)v; + else + cval.ull = v; + vsetc(&size_type, VT_CONST, &cval); +} + /* push long long constant */ static void vpushll(long long v) { @@ -441,6 +452,14 @@ ST_FUNC void vswap(void) { SValue tmp; + /* cannot let cpu flags if other instruction are generated. Also + avoid leaving VT_JMP anywhere except on the top of the stack + because it would complicate the code generator. */ + if (vtop >= vstack) { + int v = vtop->r & VT_VALMASK; + if (v == VT_CMP || (v & ~1) == VT_JMP) + gv(RC_INT); + } tmp = vtop[0]; vtop[0] = vtop[-1]; vtop[-1] = tmp; @@ -942,7 +961,7 @@ static void lbuild(int t) /* rotate n first stack elements to the bottom I1 ... In -> I2 ... In I1 [top is right] */ -static void vrotb(int n) +ST_FUNC void vrotb(int n) { int i; SValue tmp; @@ -953,35 +972,27 @@ static void vrotb(int n) vtop[0] = tmp; } -/* rotate n first stack elements to the top - I1 ... In -> In I1 ... I(n-1) [top is right] +/* rotate the n elements before entry e towards the top + I1 ... In ... -> In I1 ... I(n-1) ... [top is right] */ -ST_FUNC void vrott(int n) +ST_FUNC void vrote(SValue *e, int n) { int i; SValue tmp; - tmp = vtop[0]; + tmp = *e; for(i = 0;i < n - 1; i++) - vtop[-i] = vtop[-i - 1]; - vtop[-n + 1] = tmp; + e[-i] = e[-i - 1]; + e[-n + 1] = tmp; } -#ifdef TCC_TARGET_ARM -/* like vrott but in other direction - In ... I1 -> I(n-1) ... I1 In [top is right] +/* rotate n first stack elements to the top + I1 ... In -> In I1 ... I(n-1) [top is right] */ -ST_FUNC void vnrott(int n) +ST_FUNC void vrott(int n) { - int i; - SValue tmp; - - tmp = vtop[-n + 1]; - for(i = n - 1; i > 0; i--) - vtop[-i] = vtop[-i + 1]; - vtop[0] = tmp; + vrote(vtop, n); } -#endif /* pop stack value */ ST_FUNC void vpop(void) @@ -1505,7 +1516,8 @@ static inline int is_null_pointer(SValue *p) if ((p->r & (VT_VALMASK | VT_LVAL | VT_SYM)) != VT_CONST) return 0; return ((p->type.t & VT_BTYPE) == VT_INT && p->c.i == 0) || - ((p->type.t & VT_BTYPE) == VT_LLONG && p->c.ll == 0); + ((p->type.t & VT_BTYPE) == VT_LLONG && p->c.ll == 0) || + ((p->type.t & VT_BTYPE) == VT_PTR && p->c.ptr == 0); } static inline int is_integer_btype(int bt) @@ -2258,6 +2270,8 @@ static void gen_assign_cast(CType *dt) st = &vtop->type; /* source type */ dbt = dt->t & VT_BTYPE; sbt = st->t & VT_BTYPE; + if (sbt == VT_VOID) + tcc_error("Cannot assign void value"); if (dt->t & VT_CONSTANT) tcc_warning("assignment of read-only location"); switch(dbt) { @@ -2333,8 +2347,9 @@ ST_FUNC void vstore(void) ft = vtop[-1].type.t; sbt = vtop->type.t & VT_BTYPE; dbt = ft & VT_BTYPE; - if (((sbt == VT_INT || sbt == VT_SHORT) && dbt == VT_BYTE) || - (sbt == VT_INT && dbt == VT_SHORT)) { + if ((((sbt == VT_INT || sbt == VT_SHORT) && dbt == VT_BYTE) || + (sbt == VT_INT && dbt == VT_SHORT)) + && !(vtop->type.t & VT_BITFIELD)) { /* optimize char/short casts */ delayed_cast = VT_MUSTCAST; vtop->type.t = ft & (VT_TYPE & ~(VT_BITFIELD | (-1 << VT_STRUCT_SHIFT))); @@ -3587,12 +3602,12 @@ ST_FUNC void unary(void) if (!(type.t & VT_VLA)) { if (size < 0) tcc_error("sizeof applied to an incomplete type"); - vpushi(size); + vpushs(size); } else { vla_runtime_type_size(&type, &align); } } else { - vpushi(align); + vpushs(align); } vtop->type.t |= VT_UNSIGNED; break; @@ -4130,14 +4145,22 @@ static void expr_cond(void) (t2 & (VT_BTYPE | VT_UNSIGNED)) == (VT_LLONG | VT_UNSIGNED)) type.t |= VT_UNSIGNED; } else if (bt1 == VT_PTR || bt2 == VT_PTR) { - /* XXX: test pointer compatibility */ - type = type1; + /* If one is a null ptr constant the result type + is the other. */ + if (is_null_pointer (vtop)) + type = type1; + else if (is_null_pointer (&sv)) + type = type2; + /* XXX: test pointer compatibility, C99 has more elaborate + rules here. */ + else + type = type1; } else if (bt1 == VT_FUNC || bt2 == VT_FUNC) { /* XXX: test function pointer compatibility */ - type = type1; + type = bt1 == VT_FUNC ? type1 : type2; } else if (bt1 == VT_STRUCT || bt2 == VT_STRUCT) { /* XXX: test structure compatibility */ - type = type1; + type = bt1 == VT_STRUCT ? type1 : type2; } else if (bt1 == VT_VOID || bt2 == VT_VOID) { /* NOTE: as an extension, we accept void on only one side */ type.t = VT_VOID; @@ -4290,6 +4313,25 @@ static int is_label(void) } } +static void label_or_decl(int l) +{ + int last_tok; + + /* fast test first */ + if (tok >= TOK_UIDENT) + { + /* no need to save tokc because tok is an identifier */ + last_tok = tok; + next(); + if (tok == ':') { + unget_tok(last_tok); + return; + } + unget_tok(last_tok); + } + decl(l); +} + static void block(int *bsym, int *csym, int *case_sym, int *def_sym, int case_reg, int is_expr) { @@ -4363,7 +4405,7 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym, } } while (tok != '}') { - decl(VT_LOCAL); + label_or_decl(VT_LOCAL); if (tok != '}') { if (is_expr) vpop(); diff --git a/tccpe.c b/tccpe.c index 51f4c53..4dec1d8 100644 --- a/tccpe.c +++ b/tccpe.c @@ -38,10 +38,6 @@ # define ADDR3264 DWORD #endif -#if defined _WIN32 && (defined _WIN64) == (defined TCC_TARGET_X86_64) -#define TCC_IS_NATIVE -#endif - #ifdef _WIN32 void dbg_printf (const char *fmt, ...) { @@ -339,7 +335,7 @@ struct pe_info { const char *filename; int type; DWORD sizeofheaders; - DWORD imagebase; + ADDR3264 imagebase; DWORD start_addr; DWORD imp_offs; DWORD imp_size; @@ -1870,11 +1866,10 @@ ST_FUNC int pe_output_file(TCCState * s1, const char *filename) ret = pe_write(&pe); tcc_free(pe.sec_info); } else { -#ifndef TCC_IS_NATIVE - tcc_error_noabort("-run supported only on native platform"); -#endif +#ifdef TCC_IS_NATIVE pe.thunk = data_section; pe_build_imports(&pe); +#endif } #ifdef PE_PRINT_SECTIONS diff --git a/tccpp.c b/tccpp.c index ff47838..aff5a53 100644 --- a/tccpp.c +++ b/tccpp.c @@ -112,15 +112,13 @@ static void cstr_realloc(CString *cstr, int new_size) while (size < new_size) size = size * 2; data = tcc_realloc(cstr->data_allocated, size); - if (!data) - tcc_error("memory full"); cstr->data_allocated = data; cstr->size_allocated = size; cstr->data = data; } /* add a byte */ -ST_INLN void cstr_ccat(CString *cstr, int ch) +PUB_FUNC void cstr_ccat(CString *cstr, int ch) { int size; size = cstr->size + 1; @@ -130,7 +128,7 @@ ST_INLN void cstr_ccat(CString *cstr, int ch) cstr->size = size; } -ST_FUNC void cstr_cat(CString *cstr, const char *str) +PUB_FUNC void cstr_cat(CString *cstr, const char *str) { int c; for(;;) { @@ -143,7 +141,7 @@ ST_FUNC void cstr_cat(CString *cstr, const char *str) } /* add a wide char */ -ST_FUNC void cstr_wccat(CString *cstr, int ch) +PUB_FUNC void cstr_wccat(CString *cstr, int ch) { int size; size = cstr->size + sizeof(nwchar_t); @@ -153,20 +151,20 @@ ST_FUNC void cstr_wccat(CString *cstr, int ch) cstr->size = size; } -ST_FUNC void cstr_new(CString *cstr) +PUB_FUNC void cstr_new(CString *cstr) { memset(cstr, 0, sizeof(CString)); } /* free string and reset it to NULL */ -ST_FUNC void cstr_free(CString *cstr) +PUB_FUNC void cstr_free(CString *cstr) { tcc_free(cstr->data_allocated); cstr_new(cstr); } /* XXX: unicode ? */ -ST_FUNC void add_char(CString *cstr, int c) +static void add_char(CString *cstr, int c) { if (c == '\'' || c == '\"' || c == '\\') { /* XXX: could be more precise if char or string */ @@ -200,8 +198,6 @@ static TokenSym *tok_alloc_new(TokenSym **pts, const char *str, int len) i = tok_ident - TOK_IDENT; if ((i % TOK_ALLOC_INCR) == 0) { ptable = tcc_realloc(table_ident, (i + TOK_ALLOC_INCR) * sizeof(TokenSym *)); - if (!ptable) - tcc_error("memory full"); table_ident = ptable; } @@ -844,8 +840,6 @@ static int *tok_str_realloc(TokenString *s) len = s->allocated_len * 2; } str = tcc_realloc(s->str, len * sizeof(int)); - if (!str) - tcc_error("memory full"); s->allocated_len = len; s->str = str; return str; @@ -2028,12 +2022,16 @@ static void parse_number(const char *p) if (lcount >= 2) tcc_error("three 'l's in integer constant"); lcount++; +#if !defined TCC_TARGET_X86_64 || defined TCC_TARGET_PE if (lcount == 2) { +#endif if (tok == TOK_CINT) tok = TOK_CLLONG; else if (tok == TOK_CUINT) tok = TOK_CULLONG; +#if !defined TCC_TARGET_X86_64 || defined TCC_TARGET_PE } +#endif ch = *p++; } else if (t == 'U') { if (ucount >= 1) @@ -2699,10 +2697,25 @@ static int macro_subst_tok(TokenString *tok_str, goto redo; } } else { - /* XXX: incorrect with comments */ ch = file->buf_ptr[0]; - while (is_space(ch) || ch == '\n') - cinp(); + while (is_space(ch) || ch == '\n' || ch == '/') + { + if (ch == '/') + { + int c; + uint8_t *p = file->buf_ptr; + PEEKC(c, p); + if (c == '*') { + p = parse_comment(p); + file->buf_ptr = p - 1; + } else if (c == '/') { + p = parse_line_comment(p); + file->buf_ptr = p - 1; + } else + break; + } + cinp(); + } t = ch; } if (t != '(') /* no macro subst */ @@ -2997,8 +3010,16 @@ ST_INLN void unget_tok(int last_tok) { int i, n; int *q; - unget_saved_macro_ptr = macro_ptr; - unget_buffer_enabled = 1; + if (unget_buffer_enabled) + { + /* assert(macro_ptr == unget_saved_buffer + 1); + assert(*macro_ptr == 0); */ + } + else + { + unget_saved_macro_ptr = macro_ptr; + unget_buffer_enabled = 1; + } q = unget_saved_buffer; macro_ptr = q; *q++ = tok; diff --git a/tccrun.c b/tccrun.c index 64a1dfd..41081cc 100644 --- a/tccrun.c +++ b/tccrun.c @@ -20,6 +20,9 @@ #include "tcc.h" +/* only native compiler supports -run */ +#ifdef TCC_IS_NATIVE + #ifdef CONFIG_TCC_BACKTRACE ST_DATA int rt_num_callers = 6; ST_DATA const char **rt_bound_error_msg; @@ -217,6 +220,8 @@ static void set_pages_executable(void *ptr, unsigned long length) } /* ------------------------------------------------------------- */ +#endif /* TCC_IS_NATIVE */ + #ifdef CONFIG_TCC_BACKTRACE PUB_FUNC void tcc_set_num_callers(int n) @@ -461,7 +466,9 @@ static int rt_get_caller_pc(unsigned long *paddr, ucontext_t *uc, int level) int i; if (level == 0) { -#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) +#if defined(__APPLE__) + *paddr = uc->uc_mcontext->__ss.__eip; +#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) *paddr = uc->uc_mcontext.mc_eip; #elif defined(__dietlibc__) *paddr = uc->uc_mcontext.eip; @@ -470,7 +477,9 @@ static int rt_get_caller_pc(unsigned long *paddr, ucontext_t *uc, int level) #endif return 0; } else { -#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) +#if defined(__APPLE__) + fp = uc->uc_mcontext->__ss.__ebp; +#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) fp = uc->uc_mcontext.mc_ebp; #elif defined(__dietlibc__) fp = uc->uc_mcontext.ebp; @@ -500,14 +509,18 @@ static int rt_get_caller_pc(unsigned long *paddr, if (level == 0) { /* XXX: only support linux */ -#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) +#if defined(__APPLE__) + *paddr = uc->uc_mcontext->__ss.__rip; +#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) *paddr = uc->uc_mcontext.mc_rip; #else *paddr = uc->uc_mcontext.gregs[REG_RIP]; #endif return 0; } else { -#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) +#if defined(__APPLE__) + fp = uc->uc_mcontext->__ss.__rbp; +#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) fp = uc->uc_mcontext.mc_rbp; #else fp = uc->uc_mcontext.gregs[REG_RBP]; diff --git a/tests/Makefile b/tests/Makefile index 8cd906d..e07cdc3 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -18,6 +18,12 @@ ifdef DISABLE_STATIC export LD_LIBRARY_PATH:=$(CURDIR)/.. endif +ifeq ($(TARGETOS),Darwin) +CFLAGS+=-Wl,-flat_namespace,-undefined,warning +export MACOSX_DEPLOYMENT_TARGET:=10.2 +NATIVE_DEFINES+=-D_ANSI_SOURCE +endif + # run local version of tcc with local libraries and includes TCC = ../tcc -B.. RUN_TCC = $(NATIVE_DEFINES) -run -DONE_SOURCE ../tcc.c -B.. @@ -45,7 +51,7 @@ libtcc_test$(EXESUF): libtcc_test.c ../$(LIBTCC) # test.ref - generate using gcc # copy only tcclib.h so GCC's stddef and stdarg will be used test.ref: tcctest.c - cp -u ../include/tcclib.h . + cp ../include/tcclib.h . $(CC) -o tcctest.gcc $< -I. -w $(CFLAGS) -std=gnu99 ./tcctest.gcc > $@ diff --git a/tests/tcctest.c b/tests/tcctest.c index f0d82cf..1b04c1f 100644 --- a/tests/tcctest.c +++ b/tests/tcctest.c @@ -85,6 +85,10 @@ void statement_expr_test(void); void asm_test(void); void builtin_test(void); void weak_test(void); +void global_data_test(void); +void cmp_comparison_test(void); +void math_cmp_test(void); +void callsave_test(void); int fib(int n); void num(int n); @@ -287,6 +291,10 @@ comment /* test function macro substitution when the function name is substituted */ TEST2(); + + /* And again when the name and parenthes are separated by a + comment. */ + TEST2 /* the comment */ (); } @@ -431,6 +439,7 @@ void loop_test() printf("\n"); } +typedef int typedef_and_label; void goto_test() { @@ -439,6 +448,8 @@ void goto_test() printf("goto:\n"); i = 0; + /* This needs to parse as label, not as start of decl. */ + typedef_and_label: s_loop: if (i >= 10) goto s_end; @@ -583,6 +594,10 @@ int main(int argc, char **argv) asm_test(); builtin_test(); weak_test(); + global_data_test(); + cmp_comparison_test(); + math_cmp_test(); + callsave_test(); return 0; } @@ -1122,25 +1137,30 @@ struct structa1 struct_assign_test2(struct structa1 s1, int t) void struct_assign_test(void) { - struct structa1 lsta1, lsta2; + struct S { + struct structa1 lsta1, lsta2; + int i; + } s, *ps; + ps = &s; + ps->i = 4; #if 0 printf("struct_assign_test:\n"); - lsta1.f1 = 1; - lsta1.f2 = 2; - printf("%d %d\n", lsta1.f1, lsta1.f2); - lsta2 = lsta1; - printf("%d %d\n", lsta2.f1, lsta2.f2); + s.lsta1.f1 = 1; + s.lsta1.f2 = 2; + printf("%d %d\n", s.lsta1.f1, s.lsta1.f2); + s.lsta2 = s.lsta1; + printf("%d %d\n", s.lsta2.f1, s.lsta2.f2); #else - lsta2.f1 = 1; - lsta2.f2 = 2; + s.lsta2.f1 = 1; + s.lsta2.f2 = 2; #endif - struct_assign_test1(lsta2, 3, 4.5); + struct_assign_test1(ps->lsta2, 3, 4.5); - printf("before call: %d %d\n", lsta2.f1, lsta2.f2); - lsta2 = struct_assign_test2(lsta2, 4); - printf("after call: %d %d\n", lsta2.f1, lsta2.f2); + printf("before call: %d %d\n", s.lsta2.f1, s.lsta2.f2); + ps->lsta2 = struct_assign_test2(ps->lsta2, ps->i); + printf("after call: %d %d\n", ps->lsta2.f1, ps->lsta2.f2); static struct { void (*elem)(); @@ -1461,6 +1481,8 @@ void c99_bool_test(void) void bitfield_test(void) { int a; + short sa; + unsigned char ca; struct sbf1 { int f1 : 3; int : 2; @@ -1482,6 +1504,9 @@ void bitfield_test(void) st1.f5++; printf("%d %d %d %d %d\n", st1.f1, st1.f2, st1.f3, st1.f4, st1.f5); + sa = st1.f5; + ca = st1.f5; + printf("%d %d\n", sa, ca); st1.f1 = 7; if (st1.f1 == -1) @@ -2136,6 +2161,8 @@ void c99_vla_test(int size1, int size2) #endif } +typedef __SIZE_TYPE__ uintptr_t; + void sizeof_test(void) { int a; @@ -2156,6 +2183,20 @@ void sizeof_test(void) ptr = NULL; printf("sizeof(**ptr) = %d\n", sizeof (**ptr)); + /* The type of sizeof should be as large as a pointer, actually + it should be size_t. */ + printf("sizeof(sizeof(int) = %d\n", sizeof(sizeof(int))); + uintptr_t t = 1; + uintptr_t t2; + /* Effectively <<32, but defined also on 32bit machines. */ + t <<= 16; + t <<= 16; + t++; + /* This checks that sizeof really can be used to manipulate + uintptr_t objects, without truncation. */ + t2 = t & -sizeof(uintptr_t); + printf ("%lu %lu\n", t, t2); + /* some alignof tests */ printf("__alignof__(int) = %d\n", __alignof__(int)); printf("__alignof__(unsigned int) = %d\n", __alignof__(unsigned int)); @@ -2486,3 +2527,154 @@ void const_warn_test(void) { const_func(1); } + +struct condstruct { + int i; +}; + +int getme (struct condstruct *s, int i) +{ + int i1 = (i == 0 ? 0 : s)->i; + int i2 = (i == 0 ? s : 0)->i; + int i3 = (i == 0 ? (void*)0 : s)->i; + int i4 = (i == 0 ? s : (void*)0)->i; + return i1 + i2 + i3 + i4; +} + +struct global_data +{ + int a[40]; + int *b[40]; +}; + +struct global_data global_data; + +int global_data_getstuff (int *, int); + +void global_data_callit (int i) +{ + *global_data.b[i] = global_data_getstuff (global_data.b[i], 1); +} + +int global_data_getstuff (int *p, int i) +{ + return *p + i; +} + +void global_data_test (void) +{ + global_data.a[0] = 42; + global_data.b[0] = &global_data.a[0]; + global_data_callit (0); + printf ("%d\n", global_data.a[0]); +} + +struct cmpcmpS +{ + unsigned char fill : 3; + unsigned char b1 : 1; + unsigned char b2 : 1; + unsigned char fill2 : 3; +}; + +int glob1, glob2, glob3; + +void compare_comparisons (struct cmpcmpS *s) +{ + if (s->b1 != (glob1 == glob2) + || (s->b2 != (glob1 == glob3))) + printf ("comparing comparisons broken\n"); +} + +void cmp_comparison_test(void) +{ + struct cmpcmpS s; + s.b1 = 1; + glob1 = 42; glob2 = 42; + s.b2 = 0; + glob3 = 43; + compare_comparisons (&s); + return 0; +} + +int fcompare (double a, double b, int code) +{ + switch (code) { + case 0: return a == b; + case 1: return a != b; + case 2: return a < b; + case 3: return a >= b; + case 4: return a > b; + case 5: return a <= b; + } +} + +void math_cmp_test(void) +{ + double nan = 0.0/0.0; + double one = 1.0; + double two = 2.0; + int comp = 0; +#define bug(a,b,op,iop,part) printf("Test broken: %s %s %s %s %d\n", #a, #b, #op, #iop, part) + + /* This asserts that "a op b" is _not_ true, but "a iop b" is true. + And it does this in various ways so that all code generation paths + are checked (generating inverted tests, or non-inverted tests, or + producing a 0/1 value without jumps (that's done in the fcompare + function). */ +#define FCMP(a,b,op,iop,code) \ + if (fcompare (a,b,code)) \ + bug (a,b,op,iop,1); \ + if (a op b) \ + bug (a,b,op,iop,2); \ + if (a iop b) \ + ; \ + else \ + bug (a,b,op,iop,3); \ + if ((a op b) || comp) \ + bug (a,b,op,iop,4); \ + if ((a iop b) || comp) \ + ; \ + else \ + bug (a,b,op,iop,5); + + /* Equality tests. */ + FCMP(nan, nan, ==, !=, 0); + FCMP(one, two, ==, !=, 0); + FCMP(one, one, !=, ==, 1); + /* Non-equality is a bit special. */ + if (!fcompare (nan, nan, 1)) + bug (nan, nan, !=, ==, 6); + + /* Relational tests on numbers. */ + FCMP(two, one, <, >=, 2); + FCMP(one, two, >=, <, 3); + FCMP(one, two, >, <=, 4); + FCMP(two, one, <=, >, 5); + + /* Relational tests on NaNs. Note that the inverse op here is + always !=, there's no operator in C that is equivalent to !(a < b), + when NaNs are involved, same for the other relational ops. */ + FCMP(nan, nan, <, !=, 2); + FCMP(nan, nan, >=, !=, 3); + FCMP(nan, nan, >, !=, 4); + FCMP(nan, nan, <=, !=, 5); +} + +double get100 () { return 100.0; } + +void callsave_test(void) +{ + int i, s; double *d; double t; + s = sizeof (double); + printf ("callsavetest: %d\n", s); + d = alloca (sizeof(double)); + d[0] = 10.0; + /* x86-64 had a bug were the next call to get100 would evict + the lvalue &d[0] as VT_LLOCAL, and the reload would be done + in int type, not pointer type. When alloca returns a pointer + with the high 32 bit set (which is likely on x86-64) the access + generates a segfault. */ + i = d[0] > get100 (); + printf ("%d\n", i); +} diff --git a/win32/build-tcc.bat b/win32/build-tcc.bat index 21bf3a8..9e7f8eb 100644 --- a/win32/build-tcc.bat +++ b/win32/build-tcc.bat @@ -27,10 +27,7 @@ echo>..\config.h #define TCC_VERSION "0.9.25" :libtcc if not exist libtcc\nul mkdir libtcc copy ..\libtcc.h libtcc\libtcc.h -%CC% %target% -DONE_SOURCE ../libtcc.c -c -o libtcc.o -%AR% rcs libtcc/libtcc.a libtcc.o -:libtcc.dll -%CC% %target% -shared -DLIBTCC_AS_DLL -DONE_SOURCE ../libtcc.c -o libtcc.dll +%CC% %target% -shared -DLIBTCC_AS_DLL -DONE_SOURCE ../libtcc.c -o libtcc.dll -Wl,-out-implib,libtcc/libtcc.a tiny_impdef libtcc.dll -o lib/libtcc.def :tcc diff --git a/win32/tcc-win32.txt b/win32/tcc-win32.txt index 563967f..64ff885 100644 --- a/win32/tcc-win32.txt +++ b/win32/tcc-win32.txt @@ -64,7 +64,7 @@ For the 'libtcc_test' example type - tcc examples/libtcc_test.c -I. libtcc.dll + tcc examples/libtcc_test.c -I libtcc -ltcc Import Definition Files: diff --git a/x86_64-gen.c b/x86_64-gen.c index 4d2521d..1fa8dd5 100644 --- a/x86_64-gen.c +++ b/x86_64-gen.c @@ -36,6 +36,8 @@ #define RC_RDX 0x0010 #define RC_R8 0x0100 #define RC_R9 0x0200 +#define RC_R10 0x0400 +#define RC_R11 0x0800 #define RC_XMM0 0x0020 #define RC_ST0 0x0040 /* only for long double */ #define RC_IRET RC_RAX /* function return: integer register */ @@ -104,19 +106,19 @@ ST_FUNC void gen_le64(int64_t c); #include "tcc.h" #include -ST_DATA const int reg_classes[NB_REGS] = { +ST_DATA const int reg_classes[] = { /* eax */ RC_INT | RC_RAX, /* ecx */ RC_INT | RC_RCX, /* edx */ RC_INT | RC_RDX, /* xmm0 */ RC_FLOAT | RC_XMM0, /* st0 */ RC_ST0, -#if NB_REGS == 10 0, 0, 0, RC_INT | RC_R8, RC_INT | RC_R9, -#endif + RC_INT | RC_R10, + RC_INT | RC_R11 }; static unsigned long func_sub_sp_offset; @@ -367,8 +369,10 @@ void load(int r, SValue *sv) v1.type.t = VT_PTR; v1.r = VT_LOCAL | VT_LVAL; v1.c.ul = fc; - load(r, &v1); fr = r; + if (!(reg_classes[fr] & RC_INT)) + fr = get_reg(RC_INT); + load(fr, &v1); } ll = 0; if ((ft & VT_BTYPE) == VT_FLOAT) { @@ -410,7 +414,7 @@ void load(int r, SValue *sv) } else { orex(1,0,r,0x8b); o(0x05 + REG_VALUE(r) * 8); /* mov xx(%rip), r */ - gen_gotpcrel(fr, sv->sym, fc); + gen_gotpcrel(r, sv->sym, fc); } #endif } else if (is64_type(ft)) { @@ -425,7 +429,18 @@ void load(int r, SValue *sv) gen_modrm(r, VT_LOCAL, sv->sym, fc); } else if (v == VT_CMP) { orex(0,r,0,0); - oad(0xb8 + REG_VALUE(r), 0); /* mov $0, r */ + if ((fc & ~0x100) != TOK_NE) + oad(0xb8 + REG_VALUE(r), 0); /* mov $0, r */ + else + oad(0xb8 + REG_VALUE(r), 1); /* mov $1, r */ + if (fc & 0x100) + { + /* This was a float compare. If the parity bit is + set the result was unordered, meaning false for everything + except TOK_NE, and true for TOK_NE. */ + fc &= ~0x100; + o(0x037a + (REX_BASE(r) << 8)); + } orex(0,r,0, 0x0f); /* setxx %br */ o(fc); o(0xc0 + REG_VALUE(r)); @@ -816,7 +831,6 @@ static const uint8_t arg_regs[REGN] = { void gfunc_call(int nb_args) { int size, align, r, args_size, i; - SValue *orig_vtop; int nb_reg_args = 0; int nb_sse_args = 0; int sse_reg, gen_reg; @@ -841,7 +855,6 @@ void gfunc_call(int nb_args) /* for struct arguments, we need to call memcpy and the function call breaks register passing arguments we are preparing. So, we process arguments which will be passed by stack first. */ - orig_vtop = vtop; gen_reg = nb_reg_args; sse_reg = nb_sse_args; @@ -857,6 +870,14 @@ void gfunc_call(int nb_args) } for(i = 0; i < nb_args; i++) { + /* Swap argument to top, it will possibly be changed here, + and might use more temps. All arguments must remain on the + stack, so that get_reg can correctly evict some of them onto + stack. We could use also use a vrott(nb_args) at the end + of this loop, but this seems faster. */ + SValue tmp = vtop[0]; + vtop[0] = vtop[-i]; + vtop[-i] = tmp; if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) { size = type_size(&vtop->type, &align); /* align to stack align size */ @@ -868,18 +889,9 @@ void gfunc_call(int nb_args) r = get_reg(RC_INT); orex(1, r, 0, 0x89); /* mov %rsp, r */ o(0xe0 + REG_VALUE(r)); - { - /* following code breaks vtop[1], vtop[2], and vtop[3] */ - SValue tmp1 = vtop[1]; - SValue tmp2 = vtop[2]; - SValue tmp3 = vtop[3]; - vset(&vtop->type, r | VT_LVAL, 0); - vswap(); - vstore(); - vtop[1] = tmp1; - vtop[2] = tmp2; - vtop[3] = tmp3; - } + vset(&vtop->type, r | VT_LVAL, 0); + vswap(); + vstore(); args_size += size; } else if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) { gv(RC_ST0); @@ -909,10 +921,14 @@ void gfunc_call(int nb_args) args_size += 8; } } - vtop--; + + /* And swap the argument back to it's original position. */ + tmp = vtop[0]; + vtop[0] = vtop[-i]; + vtop[-i] = tmp; } - vtop = orig_vtop; + /* XXX This should be superfluous. */ save_regs(0); /* save used temporary registers */ /* then, we prepare register passing arguments. @@ -949,6 +965,12 @@ void gfunc_call(int nb_args) vtop--; } + /* We shouldn't have many operands on the stack anymore, but the + call address itself is still there, and it might be in %eax + (or edx/ecx) currently, which the below writes would clobber. + So evict all remaining operands here. */ + save_regs(0); + /* Copy R10 and R11 into RDX and RCX, respectively */ if (nb_reg_args > 2) { o(0xd2894c); /* mov %r10, %rdx */ @@ -1150,6 +1172,24 @@ int gtst(int inv, int t) v = vtop->r & VT_VALMASK; if (v == VT_CMP) { /* fast case : can jump directly since flags are set */ + if (vtop->c.i & 0x100) + { + /* This was a float compare. If the parity flag is set + the result was unordered. For anything except != this + means false and we don't jump (anding both conditions). + For != this means true (oring both). + Take care about inverting the test. We need to jump + to our target if the result was unordered and test wasn't NE, + otherwise if unordered we don't want to jump. */ + vtop->c.i &= ~0x100; + if (!inv == (vtop->c.i != TOK_NE)) + o(0x067a); /* jp +6 */ + else + { + g(0x0f); + t = psym(0x8a, t); /* jp t */ + } + } g(0x0f); t = psym((vtop->c.i - 16) ^ inv, t); } else if (v == VT_JMP || v == VT_JMPI) { @@ -1420,7 +1460,7 @@ void gen_opf(int op) if ((r & VT_VALMASK) == VT_LLOCAL) { SValue v1; r = get_reg(RC_INT); - v1.type.t = VT_INT; + v1.type.t = VT_PTR; v1.r = VT_LOCAL | VT_LVAL; v1.c.ul = fc; load(r, &v1); @@ -1458,7 +1498,7 @@ void gen_opf(int op) vtop--; vtop->r = VT_CMP; - vtop->c.i = op; + vtop->c.i = op | 0x100; } else { /* no memory reference possible for long double operations */ if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) { @@ -1491,7 +1531,7 @@ void gen_opf(int op) if ((r & VT_VALMASK) == VT_LLOCAL) { SValue v1; r = get_reg(RC_INT); - v1.type.t = VT_INT; + v1.type.t = VT_PTR; v1.r = VT_LOCAL | VT_LVAL; v1.c.ul = fc; load(r, &v1); -- cgit v1.2.3