forked from Mirrors/tinycc
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)master
parent
07fd82b411
commit
0ed7ba3f5e
|
@ -3,51 +3,19 @@
|
||||||
|
|
||||||
#ifdef __x86_64__
|
#ifdef __x86_64__
|
||||||
#ifndef _WIN64
|
#ifndef _WIN64
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
/* GCC compatible definition of va_list. */
|
typedef void *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 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 */
|
#define va_start(ap, last) ((ap) = __va_start(__builtin_frame_address(0)))
|
||||||
/* XXX: this lacks the support of aggregated types. */
|
#define va_arg(ap, type) \
|
||||||
#define va_start(ap, last) \
|
(*(type *)(__va_arg(ap, __builtin_va_arg_types(type), sizeof(type))));
|
||||||
(ap = (va_list)__builtin_malloc(sizeof(struct __va_list_struct)), \
|
#define va_copy(dest, src) ((dest) = __va_copy(src))
|
||||||
*ap = *(struct __va_list_struct*)( \
|
#define va_end(ap) __va_end(ap)
|
||||||
(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)
|
|
||||||
|
|
||||||
#else /* _WIN64 */
|
#else /* _WIN64 */
|
||||||
typedef char *va_list;
|
typedef char *va_list;
|
||||||
|
|
|
@ -605,3 +605,82 @@ unsigned long long __fixunsxfdi (long double a1)
|
||||||
return 0;
|
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__ */
|
||||||
|
|
28
tccgen.c
28
tccgen.c
|
@ -3542,12 +3542,28 @@ ST_FUNC void unary(void)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
#ifdef TCC_TARGET_X86_64
|
#ifdef TCC_TARGET_X86_64
|
||||||
case TOK_builtin_malloc:
|
case TOK_builtin_va_arg_types:
|
||||||
tok = TOK_malloc;
|
{
|
||||||
goto tok_identifier;
|
/* This definition must be synced with stdarg.h */
|
||||||
case TOK_builtin_free:
|
enum __va_arg_type {
|
||||||
tok = TOK_free;
|
__va_gen_reg, __va_float_reg, __va_stack
|
||||||
goto tok_identifier;
|
};
|
||||||
|
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
|
#endif
|
||||||
case TOK_INC:
|
case TOK_INC:
|
||||||
case TOK_DEC:
|
case TOK_DEC:
|
||||||
|
|
7
tcctok.h
7
tcctok.h
|
@ -123,10 +123,7 @@
|
||||||
DEF(TOK_builtin_constant_p, "__builtin_constant_p")
|
DEF(TOK_builtin_constant_p, "__builtin_constant_p")
|
||||||
DEF(TOK_builtin_frame_address, "__builtin_frame_address")
|
DEF(TOK_builtin_frame_address, "__builtin_frame_address")
|
||||||
#ifdef TCC_TARGET_X86_64
|
#ifdef TCC_TARGET_X86_64
|
||||||
DEF(TOK_builtin_malloc, "__builtin_malloc")
|
DEF(TOK_builtin_va_arg_types, "__builtin_va_arg_types")
|
||||||
DEF(TOK_builtin_free, "__builtin_free")
|
|
||||||
DEF(TOK_malloc, "malloc")
|
|
||||||
DEF(TOK_free, "free")
|
|
||||||
#endif
|
#endif
|
||||||
DEF(TOK_REGPARM1, "regparm")
|
DEF(TOK_REGPARM1, "regparm")
|
||||||
DEF(TOK_REGPARM2, "__regparm__")
|
DEF(TOK_REGPARM2, "__regparm__")
|
||||||
|
@ -231,10 +228,8 @@
|
||||||
DEF(TOK___bound_local_new, "__bound_local_new")
|
DEF(TOK___bound_local_new, "__bound_local_new")
|
||||||
DEF(TOK___bound_local_delete, "__bound_local_delete")
|
DEF(TOK___bound_local_delete, "__bound_local_delete")
|
||||||
#ifdef TCC_TARGET_PE
|
#ifdef TCC_TARGET_PE
|
||||||
#ifndef TCC_TARGET_X86_64
|
|
||||||
DEF(TOK_malloc, "malloc")
|
DEF(TOK_malloc, "malloc")
|
||||||
DEF(TOK_free, "free")
|
DEF(TOK_free, "free")
|
||||||
#endif
|
|
||||||
DEF(TOK_realloc, "realloc")
|
DEF(TOK_realloc, "realloc")
|
||||||
DEF(TOK_memalign, "memalign")
|
DEF(TOK_memalign, "memalign")
|
||||||
DEF(TOK_calloc, "calloc")
|
DEF(TOK_calloc, "calloc")
|
||||||
|
|
|
@ -1834,10 +1834,30 @@ void vprintf1(const char *fmt, ...)
|
||||||
va_end(ap);
|
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)
|
void stdarg_test(void)
|
||||||
{
|
{
|
||||||
long double ld = 1234567891234LL;
|
long double ld = 1234567891234LL;
|
||||||
|
struct myspace bob;
|
||||||
|
|
||||||
vprintf1("%d %d %d\n", 1, 2, 3);
|
vprintf1("%d %d %d\n", 1, 2, 3);
|
||||||
vprintf1("%f %d %f\n", 1.0, 2, 3.0);
|
vprintf1("%f %d %f\n", 1.0, 2, 3.0);
|
||||||
vprintf1("%l %l %d %f\n", 1234567891234LL, 987654321986LL, 3, 1234.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,
|
0.1, 1.2, 2.3, 3.4, 4.5, 5.6, 6.7, 7.8, 8.9, 9.0,
|
||||||
ld, 1234567891234LL, 987654321986LL,
|
ld, 1234567891234LL, 987654321986LL,
|
||||||
42.0, 43.0, ld);
|
42.0, 43.0, ld);
|
||||||
|
|
||||||
|
bob.profile = 42;
|
||||||
|
stdarg_for_struct(bob, bob, bob, bob.profile);
|
||||||
}
|
}
|
||||||
|
|
||||||
void whitespace_test(void)
|
void whitespace_test(void)
|
||||||
|
|
Loading…
Reference in New Issue