aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/stdarg.h52
-rw-r--r--lib/libtcc1.c79
-rw-r--r--tccgen.c28
-rw-r--r--tcctok.h7
-rw-r--r--tests/tcctest.c23
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 <stdlib.h>
-/* 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 <stdio.h>
+#include <stdlib.h>
+
+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)