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)
This commit is contained in:
Shinichiro Hamaji 2010-12-28 19:32:40 +09:00
parent 07fd82b411
commit 0ed7ba3f5e
5 changed files with 135 additions and 54 deletions

View File

@ -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;

View File

@ -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__ */

View File

@ -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:

View File

@ -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")

View File

@ -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)