From 0ed7ba3f5e79f407a206b387079c9fd68327064b Mon Sep 17 00:00:00 2001 From: Shinichiro Hamaji Date: Tue, 28 Dec 2010 19:32:40 +0900 Subject: [PATCH] Support struct arguments with stdarg.h - add __builtin_va_arg_types to check how arguments were passed - move most code of stdarg into libtcc1.c - remove __builtin_malloc and __builtin_free - add a test case based on the bug report (http://www.mail-archive.com/tinycc-devel@nongnu.org/msg03036.html) --- include/stdarg.h | 52 ++++++------------------------- lib/libtcc1.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++++ tccgen.c | 28 +++++++++++++---- tcctok.h | 7 +---- tests/tcctest.c | 23 ++++++++++++++ 5 files changed, 135 insertions(+), 54 deletions(-) diff --git a/include/stdarg.h b/include/stdarg.h index b5844d0..6dd6e9e 100644 --- a/include/stdarg.h +++ b/include/stdarg.h @@ -3,51 +3,19 @@ #ifdef __x86_64__ #ifndef _WIN64 -#include -/* GCC compatible definition of va_list. */ -struct __va_list_struct { - unsigned int gp_offset; - unsigned int fp_offset; - union { - unsigned int overflow_offset; - char *overflow_arg_area; - }; - char *reg_save_area; -}; +typedef void *va_list; -typedef struct __va_list_struct *va_list; +va_list __va_start(void *fp); +void *__va_arg(va_list ap, int arg_type, int size); +va_list __va_copy(va_list src); +void __va_end(va_list ap); -/* we use __builtin_(malloc|free) to avoid #define malloc tcc_malloc */ -/* XXX: this lacks the support of aggregated types. */ -#define va_start(ap, last) \ - (ap = (va_list)__builtin_malloc(sizeof(struct __va_list_struct)), \ - *ap = *(struct __va_list_struct*)( \ - (char*)__builtin_frame_address(0) - 16), \ - ap->overflow_arg_area = ((char *)__builtin_frame_address(0) + \ - ap->overflow_offset), \ - ap->reg_save_area = (char *)__builtin_frame_address(0) - 176 - 16 \ - ) -#define va_arg(ap, type) \ - (*(type*)(__builtin_types_compatible_p(type, long double) \ - ? (ap->overflow_arg_area += 16, \ - ap->overflow_arg_area - 16) \ - : __builtin_types_compatible_p(type, double) \ - ? (ap->fp_offset < 128 + 48 \ - ? (ap->fp_offset += 16, \ - ap->reg_save_area + ap->fp_offset - 16) \ - : (ap->overflow_arg_area += 8, \ - ap->overflow_arg_area - 8)) \ - : (ap->gp_offset < 48 \ - ? (ap->gp_offset += 8, \ - ap->reg_save_area + ap->gp_offset - 8) \ - : (ap->overflow_arg_area += 8, \ - ap->overflow_arg_area - 8)) \ - )) -#define va_copy(dest, src) \ - ((dest) = (va_list)malloc(sizeof(struct __va_list_struct)), \ - *(dest) = *(src)) -#define va_end(ap) __builtin_free(ap) +#define va_start(ap, last) ((ap) = __va_start(__builtin_frame_address(0))) +#define va_arg(ap, type) \ + (*(type *)(__va_arg(ap, __builtin_va_arg_types(type), sizeof(type)))); +#define va_copy(dest, src) ((dest) = __va_copy(src)) +#define va_end(ap) __va_end(ap) #else /* _WIN64 */ typedef char *va_list; diff --git a/lib/libtcc1.c b/lib/libtcc1.c index b079477..946c6c8 100644 --- a/lib/libtcc1.c +++ b/lib/libtcc1.c @@ -605,3 +605,82 @@ unsigned long long __fixunsxfdi (long double a1) return 0; } +#if defined(__x86_64__) && !defined(_WIN64) + +/* helper functions for stdarg.h */ + +#include +#include + +enum __va_arg_type { + __va_gen_reg, __va_float_reg, __va_stack +}; + +/* GCC compatible definition of va_list. */ +struct __va_list_struct { + unsigned int gp_offset; + unsigned int fp_offset; + union { + unsigned int overflow_offset; + char *overflow_arg_area; + }; + char *reg_save_area; +}; + +void *__va_start(void *fp) +{ + struct __va_list_struct *ap = + (struct __va_list_struct *)malloc(sizeof(struct __va_list_struct)); + *ap = *(struct __va_list_struct *)((char *)fp - 16); + ap->overflow_arg_area = (char *)fp + ap->overflow_offset; + ap->reg_save_area = (char *)fp - 176 - 16; + return ap; +} + +void *__va_arg(struct __va_list_struct *ap, + enum __va_arg_type arg_type, + int size) +{ + size = (size + 7) & ~7; + switch (arg_type) { + case __va_gen_reg: + if (ap->gp_offset < 48) { + ap->gp_offset += 8; + return ap->reg_save_area + ap->gp_offset - 8; + } + size = 8; + goto use_overflow_area; + + case __va_float_reg: + if (ap->fp_offset < 128 + 48) { + ap->fp_offset += 16; + return ap->reg_save_area + ap->fp_offset - 16; + } + size = 8; + goto use_overflow_area; + + case __va_stack: + use_overflow_area: + ap->overflow_arg_area += size; + return ap->overflow_arg_area - size; + + default: + fprintf(stderr, "unknown ABI type for __va_arg\n"); + abort(); + } +} + +void *__va_copy(struct __va_list_struct *src) +{ + struct __va_list_struct *dest = + (struct __va_list_struct *)malloc(sizeof(struct __va_list_struct)); + *dest = *src; + return dest; +} + +void __va_end(struct __va_list_struct *ap) +{ + free(ap); +} + +#endif /* __x86_64__ */ diff --git a/tccgen.c b/tccgen.c index 82ec44f..3c6adbc 100644 --- a/tccgen.c +++ b/tccgen.c @@ -3542,12 +3542,28 @@ ST_FUNC void unary(void) } break; #ifdef TCC_TARGET_X86_64 - case TOK_builtin_malloc: - tok = TOK_malloc; - goto tok_identifier; - case TOK_builtin_free: - tok = TOK_free; - goto tok_identifier; + case TOK_builtin_va_arg_types: + { + /* This definition must be synced with stdarg.h */ + enum __va_arg_type { + __va_gen_reg, __va_float_reg, __va_stack + }; + CType type; + int bt; + next(); + skip('('); + parse_type(&type); + skip(')'); + bt = type.t & VT_BTYPE; + if (bt == VT_STRUCT || bt == VT_LDOUBLE) { + vpushi(__va_stack); + } else if (bt == VT_FLOAT || bt == VT_DOUBLE) { + vpushi(__va_float_reg); + } else { + vpushi(__va_gen_reg); + } + } + break; #endif case TOK_INC: case TOK_DEC: diff --git a/tcctok.h b/tcctok.h index 28c4236..ec14c1a 100644 --- a/tcctok.h +++ b/tcctok.h @@ -123,10 +123,7 @@ DEF(TOK_builtin_constant_p, "__builtin_constant_p") DEF(TOK_builtin_frame_address, "__builtin_frame_address") #ifdef TCC_TARGET_X86_64 - DEF(TOK_builtin_malloc, "__builtin_malloc") - DEF(TOK_builtin_free, "__builtin_free") - DEF(TOK_malloc, "malloc") - DEF(TOK_free, "free") + DEF(TOK_builtin_va_arg_types, "__builtin_va_arg_types") #endif DEF(TOK_REGPARM1, "regparm") DEF(TOK_REGPARM2, "__regparm__") @@ -231,10 +228,8 @@ DEF(TOK___bound_local_new, "__bound_local_new") DEF(TOK___bound_local_delete, "__bound_local_delete") #ifdef TCC_TARGET_PE -#ifndef TCC_TARGET_X86_64 DEF(TOK_malloc, "malloc") DEF(TOK_free, "free") -#endif DEF(TOK_realloc, "realloc") DEF(TOK_memalign, "memalign") DEF(TOK_calloc, "calloc") diff --git a/tests/tcctest.c b/tests/tcctest.c index 9ceca94..14ad1e6 100644 --- a/tests/tcctest.c +++ b/tests/tcctest.c @@ -1834,10 +1834,30 @@ void vprintf1(const char *fmt, ...) va_end(ap); } +struct myspace { + short int profile; +}; + +void stdarg_for_struct(struct myspace bob, ...) +{ + struct myspace george, bill; + va_list ap; + short int validate; + + va_start(ap, bob); + bill = va_arg(ap, struct myspace); + george = va_arg(ap, struct myspace); + validate = va_arg(ap, int); + printf("stdarg_for_struct: %d %d %d %d\n", + bob.profile, bill.profile, george.profile, validate); + va_end(ap); +} void stdarg_test(void) { long double ld = 1234567891234LL; + struct myspace bob; + vprintf1("%d %d %d\n", 1, 2, 3); vprintf1("%f %d %f\n", 1.0, 2, 3.0); vprintf1("%l %l %d %f\n", 1234567891234LL, 987654321986LL, 3, 1234.0); @@ -1879,6 +1899,9 @@ void stdarg_test(void) 0.1, 1.2, 2.3, 3.4, 4.5, 5.6, 6.7, 7.8, 8.9, 9.0, ld, 1234567891234LL, 987654321986LL, 42.0, 43.0, ld); + + bob.profile = 42; + stdarg_for_struct(bob, bob, bob, bob.profile); } void whitespace_test(void)