Tests in abitest.c now work on Win32.

I expect that Linux-x86 is probably fine. All other architectures
except ARM are definitely broken since I haven't yet implemented
gfunc_sret for these, although replicating the current behaviour
should be straightforward.
master
James Lyon 2013-04-18 17:27:34 +01:00
parent ce5e12c2f9
commit 2bbfaf436f
7 changed files with 173 additions and 66 deletions

View File

@ -45,7 +45,13 @@ endif
# make libtcc as static or dynamic library? # make libtcc as static or dynamic library?
ifdef DISABLE_STATIC ifdef DISABLE_STATIC
ifndef CONFIG_WIN32
LIBTCC=libtcc.so.1.0 LIBTCC=libtcc.so.1.0
else
LIBTCC=libtcc.dll
LIBTCC_DLL=yes
LIBTCC_EXTRA=libtcc.def libtcc.a
endif
LINK_LIBTCC=-Wl,-rpath,"$(libdir)" LINK_LIBTCC=-Wl,-rpath,"$(libdir)"
ifdef DISABLE_RPATH ifdef DISABLE_RPATH
LINK_LIBTCC= LINK_LIBTCC=
@ -126,7 +132,7 @@ ifdef CONFIG_USE_LIBGCC
LIBTCC1= LIBTCC1=
endif endif
TCCLIBS = $(LIBTCC1) $(LIBTCC) TCCLIBS = $(LIBTCC1) $(LIBTCC) $(LIBTCC_EXTRA)
TCCDOCS = tcc.1 tcc-doc.html tcc-doc.info TCCDOCS = tcc.1 tcc-doc.html tcc-doc.info
ifdef CONFIG_CROSS ifdef CONFIG_CROSS
@ -185,14 +191,21 @@ endif
$(LIBTCC_OBJ) tcc.o : %.o : %.c $(LIBTCC_INC) $(LIBTCC_OBJ) tcc.o : %.o : %.c $(LIBTCC_INC)
$(CC) -o $@ -c $< $(NATIVE_DEFINES) $(CPPFLAGS) $(CFLAGS) $(CC) -o $@ -c $< $(NATIVE_DEFINES) $(CPPFLAGS) $(CFLAGS)
ifndef LIBTCC_DLL
libtcc.a: $(LIBTCC_OBJ) libtcc.a: $(LIBTCC_OBJ)
$(AR) rcs $@ $^ $(AR) rcs $@ $^
endif
libtcc.so.1.0: $(LIBTCC_OBJ) libtcc.so.1.0: $(LIBTCC_OBJ)
$(CC) -shared -Wl,-soname,$@ -o $@ $^ $(LDFLAGS) $(CC) -shared -Wl,-soname,$@ -o $@ $^ $(LDFLAGS)
libtcc.so.1.0: CFLAGS+=-fPIC libtcc.so.1.0: CFLAGS+=-fPIC
ifdef LIBTCC_DLL
libtcc.dll libtcc.def libtcc.a: $(LIBTCC_OBJ)
$(CC) -shared $^ -o $@ $(LDFLAGS) -Wl,--output-def,libtcc.def,--out-implib,libtcc.a
endif
# windows utilities # windows utilities
tiny_impdef$(EXESUF): win32/tools/tiny_impdef.c tiny_impdef$(EXESUF): win32/tools/tiny_impdef.c
$(CC) -o $@ $< $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) $(CC) -o $@ $< $(CPPFLAGS) $(CFLAGS) $(LDFLAGS)
@ -286,7 +299,8 @@ install: $(PROGS) $(TCCLIBS) $(TCCDOCS)
cp -r $(top_srcdir)/win32/examples/. "$(tccdir)/examples" cp -r $(top_srcdir)/win32/examples/. "$(tccdir)/examples"
$(INSTALL) -m644 $(addprefix $(top_srcdir)/include/,$(TCC_INCLUDES)) "$(tccdir)/include" $(INSTALL) -m644 $(addprefix $(top_srcdir)/include/,$(TCC_INCLUDES)) "$(tccdir)/include"
$(INSTALL) -m644 tcc-doc.html $(top_srcdir)/win32/tcc-win32.txt "$(tccdir)/doc" $(INSTALL) -m644 tcc-doc.html $(top_srcdir)/win32/tcc-win32.txt "$(tccdir)/doc"
$(INSTALL) -m644 $(LIBTCC) $(top_srcdir)/libtcc.h "$(tccdir)/libtcc" $(INSTALL) -m644 $(top_srcdir)/libtcc.h $(LIBTCC_EXTRA) "$(tccdir)/libtcc"
$(INSTALL) -m644 $(LIBTCC) $(tccdir)
ifdef CONFIG_CROSS ifdef CONFIG_CROSS
mkdir -p "$(tccdir)/lib/32" mkdir -p "$(tccdir)/lib/32"
mkdir -p "$(tccdir)/lib/64" mkdir -p "$(tccdir)/lib/64"

View File

@ -800,6 +800,19 @@ int assign_fpreg(struct avail_regs *avregs, int align, int size)
} }
#endif #endif
/* Return 1 if this function returns via an sret pointer, 0 otherwise */
ST_FUNC int gfunc_sret(CType *vt, CType *ret, int *align) {
size = type_size(vt, &align);
if (size > 4) {
return 1;
} else {
*align = 4;
ret->ref = NULL;
ret->t = VT_INT;
}
return 0;
}
/* Generate function call. The function address is pushed first, then /* Generate function call. The function address is pushed first, then
all the parameters in call order. This functions pops all the all the parameters in call order. This functions pops all the
parameters and the function address. */ parameters and the function address. */

View File

@ -364,6 +364,28 @@ static void gcall_or_jmp(int is_jmp)
static uint8_t fastcall_regs[3] = { TREG_EAX, TREG_EDX, TREG_ECX }; static uint8_t fastcall_regs[3] = { TREG_EAX, TREG_EDX, TREG_ECX };
static uint8_t fastcallw_regs[2] = { TREG_ECX, TREG_EDX }; static uint8_t fastcallw_regs[2] = { TREG_ECX, TREG_EDX };
/* Return 1 if this function returns via an sret pointer, 0 otherwise */
ST_FUNC int gfunc_sret(CType *vt, CType *ret, int *ret_align) {
*ret_align = 1; // Never have to re-align return values for x86
#ifdef TCC_TARGET_PE
int size, align;
size = type_size(vt, &align);
if (size > 8) {
return 1;
} else if (size > 4) {
ret->ref = NULL;
ret->t = VT_LLONG;
return 0;
} else {
ret->ref = NULL;
ret->t = VT_INT;
return 0;
}
#else
return 1;
#endif
}
/* Generate function call. The function address is pushed first, then /* Generate function call. The function address is pushed first, then
all the parameters in call order. This functions pops all the all the parameters in call order. This functions pops all the
parameters and the function address. */ parameters and the function address. */
@ -444,10 +466,6 @@ ST_FUNC void gfunc_call(int nb_args)
} }
gcall_or_jmp(0); gcall_or_jmp(0);
#ifdef TCC_TARGET_PE
if ((func_sym->type.t & VT_BTYPE) == VT_STRUCT)
args_size -= 4;
#endif
if (args_size && func_call != FUNC_STDCALL) if (args_size && func_call != FUNC_STDCALL)
gadd_sp(args_size); gadd_sp(args_size);
vtop--; vtop--;
@ -491,7 +509,12 @@ ST_FUNC void gfunc_prolog(CType *func_type)
/* if the function returns a structure, then add an /* if the function returns a structure, then add an
implicit pointer parameter */ implicit pointer parameter */
func_vt = sym->type; func_vt = sym->type;
#ifdef TCC_TARGET_PE
size = type_size(&func_vt,&align);
if (((func_vt.t & VT_BTYPE) == VT_STRUCT) && (size > 8)) {
#else
if ((func_vt.t & VT_BTYPE) == VT_STRUCT) { if ((func_vt.t & VT_BTYPE) == VT_STRUCT) {
#endif
/* XXX: fastcall case ? */ /* XXX: fastcall case ? */
func_vc = addr; func_vc = addr;
addr += 4; addr += 4;
@ -526,10 +549,6 @@ ST_FUNC void gfunc_prolog(CType *func_type)
/* pascal type call ? */ /* pascal type call ? */
if (func_call == FUNC_STDCALL) if (func_call == FUNC_STDCALL)
func_ret_sub = addr - 8; func_ret_sub = addr - 8;
#ifdef TCC_TARGET_PE
else if (func_vc)
func_ret_sub = 4;
#endif
#ifdef CONFIG_TCC_BCHECK #ifdef CONFIG_TCC_BCHECK
/* leave some room for bound checking code */ /* leave some room for bound checking code */

1
tcc.h
View File

@ -1267,6 +1267,7 @@ ST_FUNC void gsym_addr(int t, int a);
ST_FUNC void gsym(int t); ST_FUNC void gsym(int t);
ST_FUNC void load(int r, SValue *sv); ST_FUNC void load(int r, SValue *sv);
ST_FUNC void store(int r, SValue *v); ST_FUNC void store(int r, SValue *v);
ST_FUNC int gfunc_sret(CType *vt, CType *ret, int *align);
ST_FUNC void gfunc_call(int nb_args); ST_FUNC void gfunc_call(int nb_args);
ST_FUNC void gfunc_prolog(CType *func_type); ST_FUNC void gfunc_prolog(CType *func_type);
ST_FUNC void gfunc_epilog(void); ST_FUNC void gfunc_epilog(void);

View File

@ -3850,7 +3850,7 @@ ST_FUNC void unary(void)
} else if (tok == '(') { } else if (tok == '(') {
SValue ret; SValue ret;
Sym *sa; Sym *sa;
int nb_args; int nb_args, sret;
/* function call */ /* function call */
if ((vtop->type.t & VT_BTYPE) != VT_FUNC) { if ((vtop->type.t & VT_BTYPE) != VT_FUNC) {
@ -3874,18 +3874,26 @@ ST_FUNC void unary(void)
ret.r2 = VT_CONST; ret.r2 = VT_CONST;
/* compute first implicit argument if a structure is returned */ /* compute first implicit argument if a structure is returned */
if ((s->type.t & VT_BTYPE) == VT_STRUCT) { if ((s->type.t & VT_BTYPE) == VT_STRUCT) {
/* get some space for the returned structure */ int ret_align;
size = type_size(&s->type, &align); sret = gfunc_sret(&s->type, &ret.type, &ret_align);
loc = (loc - size) & -align; if (sret) {
ret.type = s->type; /* get some space for the returned structure */
ret.r = VT_LOCAL | VT_LVAL; size = type_size(&s->type, &align);
/* pass it as 'int' to avoid structure arg passing loc = (loc - size) & -align;
problems */ ret.type = s->type;
vseti(VT_LOCAL, loc); ret.r = VT_LOCAL | VT_LVAL;
ret.c = vtop->c; /* pass it as 'int' to avoid structure arg passing
nb_args++; problems */
vseti(VT_LOCAL, loc);
ret.c = vtop->c;
nb_args++;
}
} else { } else {
ret.type = s->type; sret = 0;
ret.type = s->type;
}
if (!sret) {
/* return in register */ /* return in register */
if (is_float(ret.type.t)) { if (is_float(ret.type.t)) {
ret.r = reg_fret(ret.type.t); ret.r = reg_fret(ret.type.t);
@ -3919,6 +3927,17 @@ ST_FUNC void unary(void)
/* return value */ /* return value */
vsetc(&ret.type, ret.r, &ret.c); vsetc(&ret.type, ret.r, &ret.c);
vtop->r2 = ret.r2; vtop->r2 = ret.r2;
/* handle packed struct return */
if (((s->type.t & VT_BTYPE) == VT_STRUCT) && !sret) {
size = type_size(&s->type, &align);
loc = (loc - size) & -align;
int addr = loc;
vset(&ret.type, VT_LOCAL | VT_LVAL, addr);
vswap();
vstore();
vtop--;
vset(&s->type, VT_LOCAL | VT_LVAL, addr);
}
} else { } else {
break; break;
} }
@ -4466,40 +4485,38 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym,
gexpr(); gexpr();
gen_assign_cast(&func_vt); gen_assign_cast(&func_vt);
if ((func_vt.t & VT_BTYPE) == VT_STRUCT) { if ((func_vt.t & VT_BTYPE) == VT_STRUCT) {
CType type; CType type, ret_type;
/* if returning structure, must copy it to implicit int ret_align;
first pointer arg location */ if (gfunc_sret(&func_vt, &ret_type, &ret_align)) {
#ifdef TCC_ARM_EABI /* if returning structure, must copy it to implicit
int align, size; first pointer arg location */
size = type_size(&func_vt,&align); type = func_vt;
if(size <= 4) mk_pointer(&type);
{ vset(&type, VT_LOCAL | VT_LVAL, func_vc);
if((vtop->r != (VT_LOCAL | VT_LVAL) || (vtop->c.i & 3)) indir();
&& (align & 3)) vswap();
{ /* copy structure value to pointer */
int addr; vstore();
loc = (loc - size) & -4; } else {
/* returning structure packed into registers */
int size, addr, align;
size = type_size(&func_vt,&align);
if ((vtop->r != (VT_LOCAL | VT_LVAL) || (vtop->c.i & (ret_align-1)))
&& (align & (ret_align-1))) {
loc = (loc - size) & -align;
addr = loc; addr = loc;
type = func_vt; type = func_vt;
vset(&type, VT_LOCAL | VT_LVAL, addr); vset(&type, VT_LOCAL | VT_LVAL, addr);
vswap(); vswap();
vstore(); vstore();
vset(&int_type, VT_LOCAL | VT_LVAL, addr); vset(&ret_type, VT_LOCAL | VT_LVAL, addr);
} }
vtop->type = int_type; vtop->type = ret_type;
gv(RC_IRET); if (is_float(ret_type.t))
} else { gv(rc_fret(ret_type.t));
#endif else
type = func_vt; gv(RC_IRET);
mk_pointer(&type);
vset(&type, VT_LOCAL | VT_LVAL, func_vc);
indir();
vswap();
/* copy structure value to pointer */
vstore();
#ifdef TCC_ARM_EABI
} }
#endif
} else if (is_float(func_vt.t)) { } else if (is_float(func_vt.t)) {
gv(rc_fret(func_vt.t)); gv(rc_fret(func_vt.t));
} else { } else {

View File

@ -13,7 +13,7 @@ TESTS = \
hello-run \ hello-run \
libtest \ libtest \
test3 \ test3 \
abitest-exe \ abitest \
moretests moretests
# test4 -- problem with -static # test4 -- problem with -static
@ -180,12 +180,16 @@ asmtest: asmtest.ref
# Check that code generated by libtcc is binary compatible with # Check that code generated by libtcc is binary compatible with
# that generated by CC # that generated by CC
abitest$(EXESUF): abitest.c $(top_builddir)/$(LIBTCC) abitest-cc$(EXESUF): abitest.c $(top_builddir)/$(LIBTCC)
$(CC) -o $@ $^ $(CPPFLAGS) $(CFLAGS) $(NATIVE_DEFINES) $(LIBS) $(LINK_LIBTCC) $(LDFLAGS) -I$(top_srcdir) -g -O0 $(CC) -o $@ $^ $(CPPFLAGS) $(CFLAGS) $(NATIVE_DEFINES) $(LIBS) $(LINK_LIBTCC) $(LDFLAGS) -I$(top_srcdir)
abitest-exe: abitest$(EXESUF) abitest-tcc$(EXESUF): abitest.c $(top_builddir)/$(LIBTCC)
$(TCC) -o $@ $^ $(CPPFLAGS) $(CFLAGS) $(NATIVE_DEFINES) $(LIBS) $(LINK_LIBTCC) $(LDFLAGS) -I$(top_srcdir)
abitest: abitest-cc$(EXESUF) abitest-tcc$(EXESUF)
@echo ------------ $@ ------------ @echo ------------ $@ ------------
abitest$(EXESUF) lib_path=.. abitest-cc$(EXESUF) lib_path=..
abitest-tcc$(EXESUF) lib_path=..
# targets for development # targets for development
%.bin: %.c tcc %.bin: %.c tcc

View File

@ -37,24 +37,62 @@ static int run_callback(const char *src, callback_type callback) {
return result; return result;
} }
typedef struct test1_type_s {int x, y;} test1_type; /*
typedef test1_type (*test1_function_type) (); * reg_pack_test: return a small struct which should be packed into
* registers (Win32) during return.
*/
typedef struct reg_pack_test_type_s {int x, y;} reg_pack_test_type;
typedef reg_pack_test_type (*reg_pack_test_function_type) (reg_pack_test_type);
static int test1_callback(void *ptr) { static int reg_pack_test_callback(void *ptr) {
test1_type r; reg_pack_test_function_type f = (reg_pack_test_function_type)ptr;
r = ((test1_function_type)ptr)(); reg_pack_test_type a = {10, 35};
return ((r.x == 10) && (r.y == 35)) ? 0 : -1; reg_pack_test_type r;
r = f(a);
return ((r.x == a.x*5) && (r.y == a.y*3)) ? 0 : -1;
} }
static int test1() { static int reg_pack_test(void) {
const char *src = const char *src =
"typedef struct test1_type_s {int x, y;} test1_type;" "typedef struct reg_pack_test_type_s {int x, y;} reg_pack_test_type;"
"test1_type f() {\n" "reg_pack_test_type f(reg_pack_test_type a) {\n"
" test1_type r = {10, 35};\n" " reg_pack_test_type r = {a.x*5, a.y*3};\n"
" return r;\n" " return r;\n"
"}\n"; "}\n";
return run_callback(src, test1_callback); return run_callback(src, reg_pack_test_callback);
}
/*
* sret_test: Create a struct large enough to be returned via sret
* (hidden pointer as first function argument)
*/
typedef struct sret_test_type_s {
long long a;
long long b;
} sret_test_type;
typedef sret_test_type (*sret_test_function_type) (sret_test_type);
static int sret_test_callback(void *ptr) {
sret_test_function_type f = (sret_test_function_type)(ptr);
sret_test_type x = {5436LL, 658277698LL};
sret_test_type r = f(x);
return ((r.a==x.a*35)&&(r.b==x.b*19)) ? 0 : -1;
}
static int sret_test(void) {
const char *src =
"typedef struct sret_test_type_s {\n"
" long long a;\n"
" long long b;\n"
"} sret_test_type;\n"
"sret_test_type f(sret_test_type x) {\n"
" sret_test_type r = {x.a*35, x.b*19};\n"
" return r;\n"
"}\n";
return run_callback(src, sret_test_callback);
} }
#define RUN_TEST(t) \ #define RUN_TEST(t) \
@ -75,6 +113,7 @@ int main(int argc, char **argv) {
if (argc == 2 && !memcmp(argv[1], "lib_path=",9)) if (argc == 2 && !memcmp(argv[1], "lib_path=",9))
tccdir = argv[1] + 9; tccdir = argv[1] + 9;
RUN_TEST(test1); RUN_TEST(reg_pack_test);
RUN_TEST(sret_test);
return retval; return retval;
} }