mirror of
https://github.com/frida/tinycc
synced 2025-01-11 14:19:19 +03:00
re-apply VLA by Thomas Preud'homme
This commit is contained in:
parent
17571298f3
commit
ace0f7f259
@ -12,6 +12,7 @@ not released:
|
||||
- fix 32 to 64 bit casts, alignment and struct issues (Shinichiro Hamaji)
|
||||
- mimic all GNU -option forms supported by ld (Kirill Smelkov)
|
||||
- support indirect functions as externals (Thomas Preud'homme)
|
||||
- Add support for C99 variable length arrays (Thomas Preud'homme)
|
||||
|
||||
version 0.9.25:
|
||||
|
||||
|
1
TODO
1
TODO
@ -48,7 +48,6 @@ Missing features:
|
||||
- improve '-E' option.
|
||||
- atexit (Nigel Horne)
|
||||
- packed attribute
|
||||
- C99: add variable size arrays (gcc 3.2 testsuite issue)
|
||||
- C99: add complex types (gcc 3.2 testsuite issue)
|
||||
- postfix compound literals (see 20010124-1.c)
|
||||
|
||||
|
18
tcc-doc.texi
18
tcc-doc.texi
@ -929,9 +929,13 @@ be the best solution.
|
||||
#define VT_BTYPE 0x000f /* mask for basic type */
|
||||
#define VT_UNSIGNED 0x0010 /* unsigned type */
|
||||
#define VT_ARRAY 0x0020 /* array type (also has VT_PTR) */
|
||||
#define VT_VLA 0x20000 /* VLA type (also has VT_PTR and VT_ARRAY) */
|
||||
#define VT_BITFIELD 0x0040 /* bitfield modifier */
|
||||
#define VT_CONSTANT 0x0800 /* const modifier */
|
||||
#define VT_VOLATILE 0x1000 /* volatile modifier */
|
||||
#define VT_SIGNED 0x2000 /* signed type */
|
||||
|
||||
#define VT_STRUCT_SHIFT 17 /* structure/enum name shift (14 bits left) */
|
||||
#define VT_STRUCT_SHIFT 18 /* structure/enum name shift (14 bits left) */
|
||||
@end example
|
||||
|
||||
When a reference to another type is needed (for pointers, functions and
|
||||
@ -942,7 +946,8 @@ The @code{VT_UNSIGNED} flag can be set for chars, shorts, ints and long
|
||||
longs.
|
||||
|
||||
Arrays are considered as pointers @code{VT_PTR} with the flag
|
||||
@code{VT_ARRAY} set.
|
||||
@code{VT_ARRAY} set. Variable length arrays are considered as special
|
||||
arrays and therefore also have flag @code{VT_VLA} set.
|
||||
|
||||
The @code{VT_BITFIELD} flag can be set for chars, shorts, ints and long
|
||||
longs. If it is set, then the bitfield position is stored from bits
|
||||
@ -958,6 +963,10 @@ integer:
|
||||
#define VT_EXTERN 0x00000080 /* extern definition */
|
||||
#define VT_STATIC 0x00000100 /* static variable */
|
||||
#define VT_TYPEDEF 0x00000200 /* typedef definition */
|
||||
#define VT_INLINE 0x00000400 /* inline definition */
|
||||
#define VT_IMPORT 0x00004000 /* win32: extern data imported from dll */
|
||||
#define VT_EXPORT 0x00008000 /* win32: data exported from dll */
|
||||
#define VT_WEAK 0x00010000 /* win32: data exported from dll */
|
||||
@end example
|
||||
|
||||
@section Symbols
|
||||
@ -969,7 +978,10 @@ contains @code{Sym} structures.
|
||||
an idenfier is also a token, so a string is never necessary to store
|
||||
it). @code{Sym.t} gives the type of the symbol. @code{Sym.r} is usually
|
||||
the register in which the corresponding variable is stored. @code{Sym.c} is
|
||||
usually a constant associated to the symbol.
|
||||
usually a constant associated to the symbol like its address for normal
|
||||
symbols, and the number of entries for symbols representing arrays.
|
||||
Variable length arrays use @code{Sym.r} instead, which is a pointer to
|
||||
a @code{SValue} holding its runtime size.
|
||||
|
||||
Four main symbol stacks are defined:
|
||||
|
||||
|
4
tcc.h
4
tcc.h
@ -224,6 +224,7 @@ typedef struct Sym {
|
||||
union {
|
||||
long c; /* associated number */
|
||||
int *d; /* define token stream */
|
||||
SValue *s; /* associated stack value */
|
||||
};
|
||||
CType type; /* associated type */
|
||||
union {
|
||||
@ -617,6 +618,7 @@ struct TCCState {
|
||||
#define VT_BTYPE 0x000f /* mask for basic type */
|
||||
#define VT_UNSIGNED 0x0010 /* unsigned type */
|
||||
#define VT_ARRAY 0x0020 /* array type (also has VT_PTR) */
|
||||
#define VT_VLA 0x20000 /* VLA type (also has VT_PTR and VT_ARRAY) */
|
||||
#define VT_BITFIELD 0x0040 /* bitfield modifier */
|
||||
#define VT_CONSTANT 0x0800 /* const modifier */
|
||||
#define VT_VOLATILE 0x1000 /* volatile modifier */
|
||||
@ -631,7 +633,7 @@ struct TCCState {
|
||||
#define VT_EXPORT 0x00008000 /* win32: data exported from dll */
|
||||
#define VT_WEAK 0x00010000 /* win32: data exported from dll */
|
||||
|
||||
#define VT_STRUCT_SHIFT 17 /* shift for bitfield shift values */
|
||||
#define VT_STRUCT_SHIFT 18 /* shift for bitfield shift values */
|
||||
|
||||
/* type mask (except storage) */
|
||||
#define VT_STORAGE (VT_EXTERN | VT_STATIC | VT_TYPEDEF | VT_INLINE | VT_IMPORT | VT_EXPORT | VT_WEAK)
|
||||
|
160
tccgen.c
160
tccgen.c
@ -79,6 +79,7 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, int has
|
||||
static int decl0(int l, int is_for_loop_init);
|
||||
static void expr_eq(void);
|
||||
static void unary_type(CType *type);
|
||||
static void vla_runtime_type_size(CType *type, int *a);
|
||||
static int is_compatible_parameter_types(CType *type1, CType *type2);
|
||||
static void expr_type(CType *type);
|
||||
|
||||
@ -1471,6 +1472,12 @@ static int pointed_size(CType *type)
|
||||
return type_size(pointed_type(type), &align);
|
||||
}
|
||||
|
||||
static void vla_runtime_pointed_size(CType *type)
|
||||
{
|
||||
int align;
|
||||
vla_runtime_type_size(pointed_type(type), &align);
|
||||
}
|
||||
|
||||
static inline int is_null_pointer(SValue *p)
|
||||
{
|
||||
if ((p->r & (VT_VALMASK | VT_LVAL | VT_SYM)) != VT_CONST)
|
||||
@ -1559,11 +1566,20 @@ ST_FUNC void gen_op(int op)
|
||||
}
|
||||
/* if both pointers, then it must be the '-' op */
|
||||
if (bt1 == VT_PTR && bt2 == VT_PTR) {
|
||||
int ptr1_is_vla;
|
||||
|
||||
ptr1_is_vla = 0;
|
||||
if (op != '-')
|
||||
error("cannot use pointers here");
|
||||
check_comparison_pointer_types(vtop - 1, vtop, op);
|
||||
/* XXX: check that types are compatible */
|
||||
u = pointed_size(&vtop[-1].type);
|
||||
if (vtop[-1].type.t & VT_VLA) {
|
||||
vla_runtime_pointed_size(&vtop[-1].type);
|
||||
vrott(3);
|
||||
ptr1_is_vla = 1;
|
||||
} else {
|
||||
u = pointed_size(&vtop[-1].type);
|
||||
}
|
||||
gen_opic(op);
|
||||
/* set to integer type */
|
||||
#ifdef TCC_TARGET_X86_64
|
||||
@ -1571,7 +1587,10 @@ ST_FUNC void gen_op(int op)
|
||||
#else
|
||||
vtop->type.t = VT_INT;
|
||||
#endif
|
||||
vpushi(u);
|
||||
if (ptr1_is_vla)
|
||||
vswap();
|
||||
else
|
||||
vpushi(u);
|
||||
gen_op(TOK_PDIV);
|
||||
} else {
|
||||
/* exactly one pointer : must be '+' or '-'. */
|
||||
@ -1583,16 +1602,20 @@ ST_FUNC void gen_op(int op)
|
||||
swap(&t1, &t2);
|
||||
}
|
||||
type1 = vtop[-1].type;
|
||||
type1.t &= ~VT_ARRAY;
|
||||
u = pointed_size(&vtop[-1].type);
|
||||
if (u < 0)
|
||||
error("unknown array element size");
|
||||
type1.t &= ~(VT_ARRAY|VT_VLA);
|
||||
if (vtop[-1].type.t & VT_VLA)
|
||||
vla_runtime_pointed_size(&vtop[-1].type);
|
||||
else {
|
||||
u = pointed_size(&vtop[-1].type);
|
||||
if (u < 0)
|
||||
error("unknown array element size");
|
||||
#ifdef TCC_TARGET_X86_64
|
||||
vpushll(u);
|
||||
vpushll(u);
|
||||
#else
|
||||
/* XXX: cast to int ? (long long case) */
|
||||
vpushi(u);
|
||||
/* XXX: cast to int ? (long long case) */
|
||||
vpushi(u);
|
||||
#endif
|
||||
}
|
||||
gen_op('*');
|
||||
#ifdef CONFIG_TCC_BCHECK
|
||||
/* if evaluating constant expression, no code should be
|
||||
@ -1948,7 +1971,7 @@ static void gen_cast(CType *type)
|
||||
vtop->type = *type;
|
||||
}
|
||||
|
||||
/* return type size. Put alignment at 'a' */
|
||||
/* return type size as known at compile time. Put alignment at 'a' */
|
||||
ST_FUNC int type_size(CType *type, int *a)
|
||||
{
|
||||
Sym *s;
|
||||
@ -1961,7 +1984,10 @@ ST_FUNC int type_size(CType *type, int *a)
|
||||
*a = s->r;
|
||||
return s->c;
|
||||
} else if (bt == VT_PTR) {
|
||||
if (type->t & VT_ARRAY) {
|
||||
if (type->t & VT_VLA) {
|
||||
*a = 1;
|
||||
return 0;
|
||||
} else if (type->t & VT_ARRAY) {
|
||||
int ts;
|
||||
|
||||
s = type->ref;
|
||||
@ -2008,6 +2034,30 @@ ST_FUNC int type_size(CType *type, int *a)
|
||||
}
|
||||
}
|
||||
|
||||
/* push type size as known at runtime time on top of value stack. Put
|
||||
alignment at 'a' */
|
||||
ST_FUNC void vla_runtime_type_size(CType *type, int *a)
|
||||
{
|
||||
if (type->t & VT_VLA) {
|
||||
Sym *s;
|
||||
|
||||
s = type->ref;
|
||||
vla_runtime_type_size(&s->type, a);
|
||||
vpushv(s->s);
|
||||
if ((vtop->r & (VT_SYM|VT_LVAL|VT_VALMASK)) != VT_CONST) {
|
||||
gv_dup();
|
||||
vswap();
|
||||
vpop();
|
||||
}
|
||||
gen_op('*');
|
||||
} else {
|
||||
int size;
|
||||
|
||||
size = type_size(type, a);
|
||||
vpushi(size);
|
||||
}
|
||||
}
|
||||
|
||||
/* return the pointed type of t */
|
||||
static inline CType *pointed_type(CType *type)
|
||||
{
|
||||
@ -3125,25 +3175,43 @@ static void post_type(CType *type, AttributeDef *ad)
|
||||
type->t = t1 | VT_FUNC;
|
||||
type->ref = s;
|
||||
} else if (tok == '[') {
|
||||
SValue *last_vtop = NULL;
|
||||
|
||||
/* array definition */
|
||||
next();
|
||||
if (tok == TOK_RESTRICT1)
|
||||
next();
|
||||
n = -1;
|
||||
if (tok != ']') {
|
||||
n = expr_const();
|
||||
if (n < 0)
|
||||
error("invalid array size");
|
||||
gexpr();
|
||||
if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
|
||||
n = vtop->c.i;
|
||||
last_vtop = vtop;
|
||||
if (n < 0)
|
||||
error("invalid array size");
|
||||
} else {
|
||||
if (!is_integer_btype(vtop->type.t & VT_BTYPE))
|
||||
error("size of variable length array should be an integer");
|
||||
type->t |= VT_VLA;
|
||||
last_vtop = vtop;
|
||||
}
|
||||
}
|
||||
skip(']');
|
||||
/* parse next post type */
|
||||
t1 = type->t & VT_STORAGE;
|
||||
type->t &= ~VT_STORAGE;
|
||||
t1 = type->t & (VT_STORAGE|VT_VLA);
|
||||
type->t &= ~(VT_STORAGE|VT_VLA);
|
||||
post_type(type, ad);
|
||||
t1 |= type->t & VT_VLA;
|
||||
|
||||
/* we push a anonymous symbol which will contain the array
|
||||
/* we push an anonymous symbol which will contain the array
|
||||
element type */
|
||||
s = sym_push(SYM_FIELD, type, 0, n);
|
||||
if (t1 & VT_VLA) {
|
||||
s->s = last_vtop; // That's ok, we don't need n with VLA
|
||||
} else {
|
||||
if (n >= 0)
|
||||
vpop();
|
||||
}
|
||||
type->t = t1 | VT_ARRAY | VT_PTR;
|
||||
type->ref = s;
|
||||
}
|
||||
@ -3509,11 +3577,18 @@ ST_FUNC void unary(void)
|
||||
next();
|
||||
in_sizeof++;
|
||||
unary_type(&type); // Perform a in_sizeof = 0;
|
||||
size = type_size(&type, &align);
|
||||
if ((t == TOK_SIZEOF) && (type.t & VT_VLA)) {
|
||||
vla_runtime_type_size(&type, &align);
|
||||
size = 0;
|
||||
} else {
|
||||
size = type_size(&type, &align);
|
||||
}
|
||||
if (t == TOK_SIZEOF) {
|
||||
if (size < 0)
|
||||
error("sizeof applied to an incomplete type");
|
||||
vpushi(size);
|
||||
if (!(type.t & VT_VLA)) {
|
||||
if (size < 0)
|
||||
error("sizeof applied to an incomplete type");
|
||||
vpushi(size);
|
||||
}
|
||||
} else {
|
||||
vpushi(align);
|
||||
}
|
||||
@ -3672,7 +3747,10 @@ ST_FUNC void unary(void)
|
||||
} else {
|
||||
r = s->r;
|
||||
}
|
||||
vset(&s->type, r, s->c);
|
||||
if (s->type.t & VT_VLA)
|
||||
vpushv(s->s);
|
||||
else
|
||||
vset(&s->type, r, s->c);
|
||||
/* if forward reference, we must point to s */
|
||||
if (vtop->r & VT_SYM) {
|
||||
vtop->sym = s;
|
||||
@ -4807,7 +4885,18 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c,
|
||||
Sym *s, *f;
|
||||
CType *t1;
|
||||
|
||||
if (type->t & VT_ARRAY) {
|
||||
if (type->t & VT_VLA) {
|
||||
int a;
|
||||
CValue retcval;
|
||||
|
||||
vpush_global_sym(&func_old_type, TOK_alloca);
|
||||
vla_runtime_type_size(type, &a);
|
||||
gfunc_call(1);
|
||||
|
||||
/* return value */
|
||||
retcval.i = 0;
|
||||
vsetc(type, REG_IRET, &retcval);
|
||||
} else if (type->t & VT_ARRAY) {
|
||||
s = type->ref;
|
||||
n = s->c;
|
||||
array_length = 0;
|
||||
@ -5044,6 +5133,7 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r,
|
||||
ParseState saved_parse_state = {0};
|
||||
TokenString init_str;
|
||||
Section *sec;
|
||||
Sym *vla = NULL;
|
||||
Sym *flexible_array;
|
||||
|
||||
flexible_array = NULL;
|
||||
@ -5122,8 +5212,12 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r,
|
||||
if ((r & VT_VALMASK) == VT_LOCAL) {
|
||||
sec = NULL;
|
||||
#ifdef CONFIG_TCC_BCHECK
|
||||
if (tcc_state->do_bounds_check && (type->t & VT_ARRAY))
|
||||
loc--;
|
||||
if (tcc_state->do_bounds_check && (type->t & VT_ARRAY)) {
|
||||
if (type->t & VT_VLA)
|
||||
warning("Array bound check don't work for VLA");
|
||||
else
|
||||
loc--;
|
||||
}
|
||||
#endif
|
||||
loc = (loc - size) & -align;
|
||||
addr = loc;
|
||||
@ -5131,7 +5225,8 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r,
|
||||
/* handles bounds */
|
||||
/* XXX: currently, since we do only one pass, we cannot track
|
||||
'&' operators, so we add only arrays */
|
||||
if (tcc_state->do_bounds_check && (type->t & VT_ARRAY)) {
|
||||
if (tcc_state->do_bounds_check && (type->t & VT_ARRAY) &&
|
||||
!(type->t & VT_VLA)) {
|
||||
unsigned long *bounds_ptr;
|
||||
/* add padding between regions */
|
||||
loc--;
|
||||
@ -5143,7 +5238,7 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r,
|
||||
#endif
|
||||
if (v) {
|
||||
/* local variable */
|
||||
sym_push(v, type, r, addr);
|
||||
vla = sym_push(v, type, r, addr);
|
||||
} else {
|
||||
/* push local reference */
|
||||
vset(type, r, addr);
|
||||
@ -5256,8 +5351,10 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r,
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if (has_init) {
|
||||
if (has_init || (type->t & VT_VLA)) {
|
||||
decl_initializer(type, sec, addr, 1, 0);
|
||||
if (type->t & VT_VLA)
|
||||
vla->s = vtop;
|
||||
/* restore parse state if needed */
|
||||
if (init_str.str) {
|
||||
tok_str_free(init_str.str);
|
||||
@ -5610,9 +5707,12 @@ static int decl0(int l, int is_for_loop_init)
|
||||
r |= lvalue_type(type.t);
|
||||
}
|
||||
has_init = (tok == '=');
|
||||
if (has_init && (type.t & VT_VLA))
|
||||
error("Variable length array cannot be initialized");
|
||||
if ((btype.t & VT_EXTERN) || ((type.t & VT_BTYPE) == VT_FUNC) ||
|
||||
((type.t & VT_ARRAY) && (type.t & VT_STATIC) &&
|
||||
!has_init && l == VT_CONST && type.ref->c < 0)) {
|
||||
((type.t & VT_ARRAY) && (!(type.t & VT_VLA)) &&
|
||||
(type.t & VT_STATIC) && !has_init && l == VT_CONST &&
|
||||
type.ref->c < 0)) {
|
||||
/* external variable or function */
|
||||
/* NOTE: as GCC, uninitialized global static
|
||||
arrays of null size are considered as
|
||||
|
2
tcctok.h
2
tcctok.h
@ -239,6 +239,8 @@
|
||||
DEF(TOK_memmove, "memmove")
|
||||
DEF(TOK_strlen, "strlen")
|
||||
DEF(TOK_strcpy, "strcpy")
|
||||
#endif
|
||||
#if defined __i386__ || defined __x86_64__
|
||||
DEF(TOK_alloca, "alloca")
|
||||
#endif
|
||||
|
||||
|
@ -77,6 +77,7 @@ void whitespace_test(void);
|
||||
void relocation_test(void);
|
||||
void old_style_function(void);
|
||||
void alloca_test(void);
|
||||
void c99_vla_test(int size1, int size2);
|
||||
void sizeof_test(void);
|
||||
void typeof_test(void);
|
||||
void local_label_test(void);
|
||||
@ -569,6 +570,7 @@ int main(int argc, char **argv)
|
||||
relocation_test();
|
||||
old_style_function();
|
||||
alloca_test();
|
||||
c99_vla_test(5, 2);
|
||||
sizeof_test();
|
||||
typeof_test();
|
||||
statement_expr_test();
|
||||
@ -2068,6 +2070,26 @@ void alloca_test()
|
||||
#endif
|
||||
}
|
||||
|
||||
void c99_vla_test(int size1, int size2)
|
||||
{
|
||||
#if defined __i386__ || defined __x86_64__
|
||||
int tab1[size1 * size2][2], tab2[10][2];
|
||||
void *tab1_ptr, *tab2_ptr;
|
||||
|
||||
printf("Test C99 VLA 1 (sizeof): ");
|
||||
printf("%s\n", (sizeof tab1 == size1 * size2 * 2 * sizeof(int)) ? "PASSED" : "FAILED");
|
||||
tab1_ptr = tab1;
|
||||
tab2_ptr = tab2;
|
||||
printf("Test C99 VLA 2 (ptrs substract): ");
|
||||
printf("%s\n", (tab2 - tab1 == (tab2_ptr - tab1_ptr) / (sizeof(int) * 2)) ? "PASSED" : "FAILED");
|
||||
printf("Test C99 VLA 3 (ptr add): ");
|
||||
printf("%s\n", &tab1[5][1] == (tab1_ptr + (5 * 2 + 1) * sizeof(int)) ? "PASSED" : "FAILED");
|
||||
printf("Test C99 VLA 4 (ptr access): ");
|
||||
tab1[size1][1] = 42;
|
||||
printf("%s\n", (*((int *) (tab1_ptr + (size1 * 2 + 1) * sizeof(int))) == 42) ? "PASSED" : "FAILED");
|
||||
#endif
|
||||
}
|
||||
|
||||
void sizeof_test(void)
|
||||
{
|
||||
int a;
|
||||
|
Loading…
Reference in New Issue
Block a user