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.
Unified · Split
| @@ -45,7 +45,13 @@ endif · 44 unmodified lines | ||
| 45 | 45 | |
| 46 | 46 | # make libtcc as static or dynamic library? |
| 47 | 47 | ifdef DISABLE_STATIC |
| 48 | +ifndef CONFIG_WIN32 | |
| 48 | 49 | LIBTCC=libtcc.so.1.0 |
| 50 | +else | |
| 51 | +LIBTCC=libtcc.dll | |
| 52 | +LIBTCC_DLL=yes | |
| 53 | +LIBTCC_EXTRA=libtcc.def libtcc.a | |
| 54 | +endif | |
| 49 | 55 | LINK_LIBTCC=-Wl,-rpath,"$(libdir)" |
| 50 | 56 | ifdef DISABLE_RPATH |
| 51 | 57 | LINK_LIBTCC= |
| @@ -126,7 +132,7 @@ ifdef CONFIG_USE_LIBGCC · 74 unmodified lines | ||
| 126 | 132 | LIBTCC1= |
| 127 | 133 | endif |
| 128 | 134 | |
| 129 | −TCCLIBS = $(LIBTCC1) $(LIBTCC) | |
| 135 | +TCCLIBS = $(LIBTCC1) $(LIBTCC) $(LIBTCC_EXTRA) | |
| 130 | 136 | TCCDOCS = tcc.1 tcc-doc.html tcc-doc.info |
| 131 | 137 | |
| 132 | 138 | ifdef CONFIG_CROSS |
| @@ -185,14 +191,21 @@ endif · 52 unmodified lines | ||
| 185 | 191 | $(LIBTCC_OBJ) tcc.o : %.o : %.c $(LIBTCC_INC) |
| 186 | 192 | $(CC) -o $@ -c $< $(NATIVE_DEFINES) $(CPPFLAGS) $(CFLAGS) |
| 187 | 193 | |
| 194 | +ifndef LIBTCC_DLL | |
| 188 | 195 | libtcc.a: $(LIBTCC_OBJ) |
| 189 | 196 | $(AR) rcs $@ $^ |
| 197 | +endif | |
| 190 | 198 | |
| 191 | 199 | libtcc.so.1.0: $(LIBTCC_OBJ) |
| 192 | 200 | $(CC) -shared -Wl,-soname,$@ -o $@ $^ $(LDFLAGS) |
| 193 | 201 | |
| 194 | 202 | libtcc.so.1.0: CFLAGS+=-fPIC |
| 195 | 203 | |
| 204 | +ifdef LIBTCC_DLL | |
| 205 | +libtcc.dll libtcc.def libtcc.a: $(LIBTCC_OBJ) | |
| 206 | + $(CC) -shared $^ -o $@ $(LDFLAGS) -Wl,--output-def,libtcc.def,--out-implib,libtcc.a | |
| 207 | +endif | |
| 208 | + | |
| 196 | 209 | # windows utilities |
| 197 | 210 | tiny_impdef$(EXESUF): win32/tools/tiny_impdef.c |
| 198 | 211 | $(CC) -o $@ $< $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) |
| @@ -286,7 +299,8 @@ install: $(PROGS) $(TCCLIBS) $(TCCDOCS) · 87 unmodified lines | ||
| 286 | 299 | cp -r $(top_srcdir)/win32/examples/. "$(tccdir)/examples" |
| 287 | 300 | $(INSTALL) -m644 $(addprefix $(top_srcdir)/include/,$(TCC_INCLUDES)) "$(tccdir)/include" |
| 288 | 301 | $(INSTALL) -m644 tcc-doc.html $(top_srcdir)/win32/tcc-win32.txt "$(tccdir)/doc" |
| 289 | − $(INSTALL) -m644 $(LIBTCC) $(top_srcdir)/libtcc.h "$(tccdir)/libtcc" | |
| 302 | + $(INSTALL) -m644 $(top_srcdir)/libtcc.h $(LIBTCC_EXTRA) "$(tccdir)/libtcc" | |
| 303 | + $(INSTALL) -m644 $(LIBTCC) $(tccdir) | |
| 290 | 304 | ifdef CONFIG_CROSS |
| 291 | 305 | mkdir -p "$(tccdir)/lib/32" |
| 292 | 306 | mkdir -p "$(tccdir)/lib/64" |
| @@ -800,6 +800,19 @@ int assign_fpreg(struct avail_regs *avregs, int align, int size) · 799 unmodified lines | ||
| 800 | 800 | } |
| 801 | 801 | #endif |
| 802 | 802 | |
| 803 | +/* Return 1 if this function returns via an sret pointer, 0 otherwise */ | |
| 804 | +ST_FUNC int gfunc_sret(CType *vt, CType *ret, int *align) { | |
| 805 | + size = type_size(vt, &align); | |
| 806 | + if (size > 4) { | |
| 807 | + return 1; | |
| 808 | + } else { | |
| 809 | + *align = 4; | |
| 810 | + ret->ref = NULL; | |
| 811 | + ret->t = VT_INT; | |
| 812 | + } | |
| 813 | + return 0; | |
| 814 | +} | |
| 815 | + | |
| 803 | 816 | /* Generate function call. The function address is pushed first, then |
| 804 | 817 | all the parameters in call order. This functions pops all the |
| 805 | 818 | parameters and the function address. */ |
| @@ -364,6 +364,28 @@ static void gcall_or_jmp(int is_jmp) · 363 unmodified lines | ||
| 364 | 364 | static uint8_t fastcall_regs[3] = { TREG_EAX, TREG_EDX, TREG_ECX }; |
| 365 | 365 | static uint8_t fastcallw_regs[2] = { TREG_ECX, TREG_EDX }; |
| 366 | 366 | |
| 367 | +/* Return 1 if this function returns via an sret pointer, 0 otherwise */ | |
| 368 | +ST_FUNC int gfunc_sret(CType *vt, CType *ret, int *ret_align) { | |
| 369 | + *ret_align = 1; // Never have to re-align return values for x86 | |
| 370 | +#ifdef TCC_TARGET_PE | |
| 371 | + int size, align; | |
| 372 | + size = type_size(vt, &align); | |
| 373 | + if (size > 8) { | |
| 374 | + return 1; | |
| 375 | + } else if (size > 4) { | |
| 376 | + ret->ref = NULL; | |
| 377 | + ret->t = VT_LLONG; | |
| 378 | + return 0; | |
| 379 | + } else { | |
| 380 | + ret->ref = NULL; | |
| 381 | + ret->t = VT_INT; | |
| 382 | + return 0; | |
| 383 | + } | |
| 384 | +#else | |
| 385 | + return 1; | |
| 386 | +#endif | |
| 387 | +} | |
| 388 | + | |
| 367 | 389 | /* Generate function call. The function address is pushed first, then |
| 368 | 390 | all the parameters in call order. This functions pops all the |
| 369 | 391 | parameters and the function address. */ |
| @@ -444,10 +466,6 @@ ST_FUNC void gfunc_call(int nb_args) · 74 unmodified lines | ||
| 444 | 466 | } |
| 445 | 467 | gcall_or_jmp(0); |
| 446 | 468 | |
| 447 | −#ifdef TCC_TARGET_PE | |
| 448 | − if ((func_sym->type.t & VT_BTYPE) == VT_STRUCT) | |
| 449 | − args_size -= 4; | |
| 450 | −#endif | |
| 451 | 469 | if (args_size && func_call != FUNC_STDCALL) |
| 452 | 470 | gadd_sp(args_size); |
| 453 | 471 | vtop--; |
| @@ -491,7 +509,12 @@ ST_FUNC void gfunc_prolog(CType *func_type) · 37 unmodified lines | ||
| 491 | 509 | /* if the function returns a structure, then add an |
| 492 | 510 | implicit pointer parameter */ |
| 493 | 511 | func_vt = sym->type; |
| 512 | +#ifdef TCC_TARGET_PE | |
| 513 | + size = type_size(&func_vt,&align); | |
| 514 | + if (((func_vt.t & VT_BTYPE) == VT_STRUCT) && (size > 8)) { | |
| 515 | +#else | |
| 494 | 516 | if ((func_vt.t & VT_BTYPE) == VT_STRUCT) { |
| 517 | +#endif | |
| 495 | 518 | /* XXX: fastcall case ? */ |
| 496 | 519 | func_vc = addr; |
| 497 | 520 | addr += 4; |
| @@ -526,10 +549,6 @@ ST_FUNC void gfunc_prolog(CType *func_type) · 28 unmodified lines | ||
| 526 | 549 | /* pascal type call ? */ |
| 527 | 550 | if (func_call == FUNC_STDCALL) |
| 528 | 551 | func_ret_sub = addr - 8; |
| 529 | −#ifdef TCC_TARGET_PE | |
| 530 | − else if (func_vc) | |
| 531 | − func_ret_sub = 4; | |
| 532 | −#endif | |
| 533 | 552 | |
| 534 | 553 | #ifdef CONFIG_TCC_BCHECK |
| 535 | 554 | /* leave some room for bound checking code */ |
| @@ -1267,6 +1267,7 @@ ST_FUNC void gsym_addr(int t, int a); · 1266 unmodified lines | ||
| 1267 | 1267 | ST_FUNC void gsym(int t); |
| 1268 | 1268 | ST_FUNC void load(int r, SValue *sv); |
| 1269 | 1269 | ST_FUNC void store(int r, SValue *v); |
| 1270 | +ST_FUNC int gfunc_sret(CType *vt, CType *ret, int *align); | |
| 1270 | 1271 | ST_FUNC void gfunc_call(int nb_args); |
| 1271 | 1272 | ST_FUNC void gfunc_prolog(CType *func_type); |
| 1272 | 1273 | ST_FUNC void gfunc_epilog(void); |
| @@ -3850,7 +3850,7 @@ ST_FUNC void unary(void) · 3849 unmodified lines | ||
| 3850 | 3850 | } else if (tok == '(') { |
| 3851 | 3851 | SValue ret; |
| 3852 | 3852 | Sym *sa; |
| 3853 | − int nb_args; | |
| 3853 | + int nb_args, sret; | |
| 3854 | 3854 | |
| 3855 | 3855 | /* function call */ |
| 3856 | 3856 | if ((vtop->type.t & VT_BTYPE) != VT_FUNC) { |
| @@ -3874,18 +3874,26 @@ ST_FUNC void unary(void) · 17 unmodified lines | ||
| 3874 | 3874 | ret.r2 = VT_CONST; |
| 3875 | 3875 | /* compute first implicit argument if a structure is returned */ |
| 3876 | 3876 | if ((s->type.t & VT_BTYPE) == VT_STRUCT) { |
| 3877 | − /* get some space for the returned structure */ | |
| 3878 | − size = type_size(&s->type, &align); | |
| 3879 | − loc = (loc - size) & -align; | |
| 3880 | − ret.type = s->type; | |
| 3881 | − ret.r = VT_LOCAL | VT_LVAL; | |
| 3882 | − /* pass it as 'int' to avoid structure arg passing | |
| 3883 | − problems */ | |
| 3884 | − vseti(VT_LOCAL, loc); | |
| 3885 | − ret.c = vtop->c; | |
| 3886 | − nb_args++; | |
| 3877 | + int ret_align; | |
| 3878 | + sret = gfunc_sret(&s->type, &ret.type, &ret_align); | |
| 3879 | + if (sret) { | |
| 3880 | + /* get some space for the returned structure */ | |
| 3881 | + size = type_size(&s->type, &align); | |
| 3882 | + loc = (loc - size) & -align; | |
| 3883 | + ret.type = s->type; | |
| 3884 | + ret.r = VT_LOCAL | VT_LVAL; | |
| 3885 | + /* pass it as 'int' to avoid structure arg passing | |
| 3886 | + problems */ | |
| 3887 | + vseti(VT_LOCAL, loc); | |
| 3888 | + ret.c = vtop->c; | |
| 3889 | + nb_args++; | |
| 3890 | + } | |
| 3887 | 3891 | } else { |
| 3888 | − ret.type = s->type; | |
| 3892 | + sret = 0; | |
| 3893 | + ret.type = s->type; | |
| 3894 | + } | |
| 3895 | + | |
| 3896 | + if (!sret) { | |
| 3889 | 3897 | /* return in register */ |
| 3890 | 3898 | if (is_float(ret.type.t)) { |
| 3891 | 3899 | ret.r = reg_fret(ret.type.t); |
| @@ -3919,6 +3927,17 @@ ST_FUNC void unary(void) · 27 unmodified lines | ||
| 3919 | 3927 | /* return value */ |
| 3920 | 3928 | vsetc(&ret.type, ret.r, &ret.c); |
| 3921 | 3929 | vtop->r2 = ret.r2; |
| 3930 | + /* handle packed struct return */ | |
| 3931 | + if (((s->type.t & VT_BTYPE) == VT_STRUCT) && !sret) { | |
| 3932 | + size = type_size(&s->type, &align); | |
| 3933 | + loc = (loc - size) & -align; | |
| 3934 | + int addr = loc; | |
| 3935 | + vset(&ret.type, VT_LOCAL | VT_LVAL, addr); | |
| 3936 | + vswap(); | |
| 3937 | + vstore(); | |
| 3938 | + vtop--; | |
| 3939 | + vset(&s->type, VT_LOCAL | VT_LVAL, addr); | |
| 3940 | + } | |
| 3922 | 3941 | } else { |
| 3923 | 3942 | break; |
| 3924 | 3943 | } |
| @@ -4466,40 +4485,38 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym, · 541 unmodified lines | ||
| 4466 | 4485 | gexpr(); |
| 4467 | 4486 | gen_assign_cast(&func_vt); |
| 4468 | 4487 | if ((func_vt.t & VT_BTYPE) == VT_STRUCT) { |
| 4469 | − CType type; | |
| 4470 | − /* if returning structure, must copy it to implicit | |
| 4471 | − first pointer arg location */ | |
| 4472 | −#ifdef TCC_ARM_EABI | |
| 4473 | − int align, size; | |
| 4474 | − size = type_size(&func_vt,&align); | |
| 4475 | − if(size <= 4) | |
| 4476 | − { | |
| 4477 | − if((vtop->r != (VT_LOCAL | VT_LVAL) || (vtop->c.i & 3)) | |
| 4478 | − && (align & 3)) | |
| 4479 | − { | |
| 4480 | − int addr; | |
| 4481 | − loc = (loc - size) & -4; | |
| 4488 | + CType type, ret_type; | |
| 4489 | + int ret_align; | |
| 4490 | + if (gfunc_sret(&func_vt, &ret_type, &ret_align)) { | |
| 4491 | + /* if returning structure, must copy it to implicit | |
| 4492 | + first pointer arg location */ | |
| 4493 | + type = func_vt; | |
| 4494 | + mk_pointer(&type); | |
| 4495 | + vset(&type, VT_LOCAL | VT_LVAL, func_vc); | |
| 4496 | + indir(); | |
| 4497 | + vswap(); | |
| 4498 | + /* copy structure value to pointer */ | |
| 4499 | + vstore(); | |
| 4500 | + } else { | |
| 4501 | + /* returning structure packed into registers */ | |
| 4502 | + int size, addr, align; | |
| 4503 | + size = type_size(&func_vt,&align); | |
| 4504 | + if ((vtop->r != (VT_LOCAL | VT_LVAL) || (vtop->c.i & (ret_align-1))) | |
| 4505 | + && (align & (ret_align-1))) { | |
| 4506 | + loc = (loc - size) & -align; | |
| 4482 | 4507 | addr = loc; |
| 4483 | 4508 | type = func_vt; |
| 4484 | 4509 | vset(&type, VT_LOCAL | VT_LVAL, addr); |
| 4485 | 4510 | vswap(); |
| 4486 | 4511 | vstore(); |
| 4487 | − vset(&int_type, VT_LOCAL | VT_LVAL, addr); | |
| 4512 | + vset(&ret_type, VT_LOCAL | VT_LVAL, addr); | |
| 4488 | 4513 | } |
| 4489 | − vtop->type = int_type; | |
| 4490 | − gv(RC_IRET); | |
| 4491 | − } else { | |
| 4492 | −#endif | |
| 4493 | − type = func_vt; | |
| 4494 | − mk_pointer(&type); | |
| 4495 | − vset(&type, VT_LOCAL | VT_LVAL, func_vc); | |
| 4496 | − indir(); | |
| 4497 | − vswap(); | |
| 4498 | − /* copy structure value to pointer */ | |
| 4499 | − vstore(); | |
| 4500 | −#ifdef TCC_ARM_EABI | |
| 4514 | + vtop->type = ret_type; | |
| 4515 | + if (is_float(ret_type.t)) | |
| 4516 | + gv(rc_fret(ret_type.t)); | |
| 4517 | + else | |
| 4518 | + gv(RC_IRET); | |
| 4501 | 4519 | } |
| 4502 | −#endif | |
| 4503 | 4520 | } else if (is_float(func_vt.t)) { |
| 4504 | 4521 | gv(rc_fret(func_vt.t)); |
| 4505 | 4522 | } else { |
| @@ -13,7 +13,7 @@ TESTS = \ · 12 unmodified lines | ||
| 13 | 13 | hello-run \ |
| 14 | 14 | libtest \ |
| 15 | 15 | test3 \ |
| 16 | − abitest-exe \ | |
| 16 | + abitest \ | |
| 17 | 17 | moretests |
| 18 | 18 | |
| 19 | 19 | # test4 -- problem with -static |
| @@ -180,12 +180,16 @@ asmtest: asmtest.ref · 160 unmodified lines | ||
| 180 | 180 | |
| 181 | 181 | # Check that code generated by libtcc is binary compatible with |
| 182 | 182 | # that generated by CC |
| 183 | −abitest$(EXESUF): abitest.c $(top_builddir)/$(LIBTCC) | |
| 184 | − $(CC) -o $@ $^ $(CPPFLAGS) $(CFLAGS) $(NATIVE_DEFINES) $(LIBS) $(LINK_LIBTCC) $(LDFLAGS) -I$(top_srcdir) -g -O0 | |
| 183 | +abitest-cc$(EXESUF): abitest.c $(top_builddir)/$(LIBTCC) | |
| 184 | + $(CC) -o $@ $^ $(CPPFLAGS) $(CFLAGS) $(NATIVE_DEFINES) $(LIBS) $(LINK_LIBTCC) $(LDFLAGS) -I$(top_srcdir) | |
| 185 | + | |
| 186 | +abitest-tcc$(EXESUF): abitest.c $(top_builddir)/$(LIBTCC) | |
| 187 | + $(TCC) -o $@ $^ $(CPPFLAGS) $(CFLAGS) $(NATIVE_DEFINES) $(LIBS) $(LINK_LIBTCC) $(LDFLAGS) -I$(top_srcdir) | |
| 185 | 188 | |
| 186 | −abitest-exe: abitest$(EXESUF) | |
| 189 | +abitest: abitest-cc$(EXESUF) abitest-tcc$(EXESUF) | |
| 187 | 190 | @echo ------------ $@ ------------ |
| 188 | − abitest$(EXESUF) lib_path=.. | |
| 191 | + abitest-cc$(EXESUF) lib_path=.. | |
| 192 | + abitest-tcc$(EXESUF) lib_path=.. | |
| 189 | 193 | |
| 190 | 194 | # targets for development |
| 191 | 195 | %.bin: %.c tcc |
| @@ -37,24 +37,62 @@ static int run_callback(const char *src, callback_type callback) { · 36 unmodified lines | ||
| 37 | 37 | return result; |
| 38 | 38 | } |
| 39 | 39 | |
| 40 | −typedef struct test1_type_s {int x, y;} test1_type; | |
| 41 | −typedef test1_type (*test1_function_type) (); | |
| 40 | +/* | |
| 41 | + * reg_pack_test: return a small struct which should be packed into | |
| 42 | + * registers (Win32) during return. | |
| 43 | + */ | |
| 44 | +typedef struct reg_pack_test_type_s {int x, y;} reg_pack_test_type; | |
| 45 | +typedef reg_pack_test_type (*reg_pack_test_function_type) (reg_pack_test_type); | |
| 46 | + | |
| 47 | +static int reg_pack_test_callback(void *ptr) { | |
| 48 | + reg_pack_test_function_type f = (reg_pack_test_function_type)ptr; | |
| 49 | + reg_pack_test_type a = {10, 35}; | |
| 50 | + reg_pack_test_type r; | |
| 51 | + r = f(a); | |
| 52 | + return ((r.x == a.x*5) && (r.y == a.y*3)) ? 0 : -1; | |
| 53 | +} | |
| 54 | + | |
| 55 | +static int reg_pack_test(void) { | |
| 56 | + const char *src = | |
| 57 | + "typedef struct reg_pack_test_type_s {int x, y;} reg_pack_test_type;" | |
| 58 | + "reg_pack_test_type f(reg_pack_test_type a) {\n" | |
| 59 | + " reg_pack_test_type r = {a.x*5, a.y*3};\n" | |
| 60 | + " return r;\n" | |
| 61 | + "}\n"; | |
| 62 | + | |
| 63 | + return run_callback(src, reg_pack_test_callback); | |
| 64 | +} | |
| 65 | + | |
| 66 | +/* | |
| 67 | + * sret_test: Create a struct large enough to be returned via sret | |
| 68 | + * (hidden pointer as first function argument) | |
| 69 | + */ | |
| 70 | +typedef struct sret_test_type_s { | |
| 71 | + long long a; | |
| 72 | + long long b; | |
| 73 | +} sret_test_type; | |
| 74 | + | |
| 75 | +typedef sret_test_type (*sret_test_function_type) (sret_test_type); | |
| 42 | 76 | |
| 43 | −static int test1_callback(void *ptr) { | |
| 44 | − test1_type r; | |
| 45 | − r = ((test1_function_type)ptr)(); | |
| 46 | − return ((r.x == 10) && (r.y == 35)) ? 0 : -1; | |
| 77 | +static int sret_test_callback(void *ptr) { | |
| 78 | + sret_test_function_type f = (sret_test_function_type)(ptr); | |
| 79 | + sret_test_type x = {5436LL, 658277698LL}; | |
| 80 | + sret_test_type r = f(x); | |
| 81 | + return ((r.a==x.a*35)&&(r.b==x.b*19)) ? 0 : -1; | |
| 47 | 82 | } |
| 48 | 83 | |
| 49 | −static int test1() { | |
| 84 | +static int sret_test(void) { | |
| 50 | 85 | const char *src = |
| 51 | − "typedef struct test1_type_s {int x, y;} test1_type;" | |
| 52 | − "test1_type f() {\n" | |
| 53 | − " test1_type r = {10, 35};\n" | |
| 86 | + "typedef struct sret_test_type_s {\n" | |
| 87 | + " long long a;\n" | |
| 88 | + " long long b;\n" | |
| 89 | + "} sret_test_type;\n" | |
| 90 | + "sret_test_type f(sret_test_type x) {\n" | |
| 91 | + " sret_test_type r = {x.a*35, x.b*19};\n" | |
| 54 | 92 | " return r;\n" |
| 55 | 93 | "}\n"; |
| 56 | 94 | |
| 57 | − return run_callback(src, test1_callback); | |
| 95 | + return run_callback(src, sret_test_callback); | |
| 58 | 96 | } |
| 59 | 97 | |
| 60 | 98 | #define RUN_TEST(t) \ |
| @@ -75,6 +113,7 @@ int main(int argc, char **argv) { · 14 unmodified lines | ||
| 75 | 113 | if (argc == 2 && !memcmp(argv[1], "lib_path=",9)) |
| 76 | 114 | tccdir = argv[1] + 9; |
| 77 | 115 | |
| 78 | − RUN_TEST(test1); | |
| 116 | + RUN_TEST(reg_pack_test); | |
| 117 | + RUN_TEST(sret_test); | |
| 79 | 118 | return retval; |
| 80 | 119 | } |