Implement C99 Variable Length Arrays

Implement C99 Variable Length Arrays in tinycc:
- Support VLA with multiple level (nested vla)
- Update documentation with regards to VT_VLA
- Add a testsuite in tcctest.c
master
Thomas Preud'homme 2011-01-15 17:50:55 +01:00
parent d35a3ac375
commit a5a50eaafe
6 changed files with 169 additions and 33 deletions

View File

@ -12,6 +12,7 @@ not released:
- fix 32 to 64 bit casts, alignment and struct issues (Shinichiro Hamaji) - fix 32 to 64 bit casts, alignment and struct issues (Shinichiro Hamaji)
- mimic all GNU -option forms supported by ld (Kirill Smelkov) - mimic all GNU -option forms supported by ld (Kirill Smelkov)
- support indirect functions as externals (Thomas Preud'homme) - support indirect functions as externals (Thomas Preud'homme)
- Add support for C99 variable length arrays (Thomas Preud'homme)
version 0.9.25: version 0.9.25:

1
TODO
View File

@ -48,7 +48,6 @@ Missing features:
- improve '-E' option. - improve '-E' option.
- atexit (Nigel Horne) - atexit (Nigel Horne)
- packed attribute - packed attribute
- C99: add variable size arrays (gcc 3.2 testsuite issue)
- C99: add complex types (gcc 3.2 testsuite issue) - C99: add complex types (gcc 3.2 testsuite issue)
- postfix compound literals (see 20010124-1.c) - postfix compound literals (see 20010124-1.c)

View File

@ -929,7 +929,11 @@ be the best solution.
#define VT_BTYPE 0x000f /* mask for basic type */ #define VT_BTYPE 0x000f /* mask for basic type */
#define VT_UNSIGNED 0x0010 /* unsigned type */ #define VT_UNSIGNED 0x0010 /* unsigned type */
#define VT_ARRAY 0x0020 /* array type (also has VT_PTR) */ #define VT_ARRAY 0x0020 /* array type (also has VT_PTR) */
#define VT_VLA 0x10000 /* VLA type (also has VT_PTR and VT_ARRAY) */
#define VT_BITFIELD 0x0040 /* bitfield modifier */ #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 16 /* structure/enum name shift (16 bits left) */ #define VT_STRUCT_SHIFT 16 /* structure/enum name shift (16 bits left) */
@end example @end example
@ -942,7 +946,8 @@ The @code{VT_UNSIGNED} flag can be set for chars, shorts, ints and long
longs. longs.
Arrays are considered as pointers @code{VT_PTR} with the flag 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 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 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_EXTERN 0x00000080 /* extern definition */
#define VT_STATIC 0x00000100 /* static variable */ #define VT_STATIC 0x00000100 /* static variable */
#define VT_TYPEDEF 0x00000200 /* typedef definition */ #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 @end example
@section Symbols @section Symbols
@ -969,7 +978,10 @@ contains @code{Sym} structures.
an idenfier is also a token, so a string is never necessary to store 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 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 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: Four main symbol stacks are defined:

2
tcc.h
View File

@ -224,6 +224,7 @@ typedef struct Sym {
union { union {
long c; /* associated number */ long c; /* associated number */
int *d; /* define token stream */ int *d; /* define token stream */
SValue *s; /* associated stack value */
}; };
CType type; /* associated type */ CType type; /* associated type */
union { union {
@ -616,6 +617,7 @@ struct TCCState {
#define VT_BTYPE 0x000f /* mask for basic type */ #define VT_BTYPE 0x000f /* mask for basic type */
#define VT_UNSIGNED 0x0010 /* unsigned type */ #define VT_UNSIGNED 0x0010 /* unsigned type */
#define VT_ARRAY 0x0020 /* array type (also has VT_PTR) */ #define VT_ARRAY 0x0020 /* array type (also has VT_PTR) */
#define VT_VLA 0x10000 /* VLA type (also has VT_PTR and VT_ARRAY) */
#define VT_BITFIELD 0x0040 /* bitfield modifier */ #define VT_BITFIELD 0x0040 /* bitfield modifier */
#define VT_CONSTANT 0x0800 /* const modifier */ #define VT_CONSTANT 0x0800 /* const modifier */
#define VT_VOLATILE 0x1000 /* volatile modifier */ #define VT_VOLATILE 0x1000 /* volatile modifier */

160
tccgen.c
View File

@ -78,6 +78,7 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym, int case_re
static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, int has_init, int v, int scope); static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, int has_init, int v, int scope);
static void expr_eq(void); static void expr_eq(void);
static void unary_type(CType *type); 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 int is_compatible_parameter_types(CType *type1, CType *type2);
static void expr_type(CType *type); static void expr_type(CType *type);
@ -1455,6 +1456,12 @@ static int pointed_size(CType *type)
return type_size(pointed_type(type), &align); 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) static inline int is_null_pointer(SValue *p)
{ {
if ((p->r & (VT_VALMASK | VT_LVAL | VT_SYM)) != VT_CONST) if ((p->r & (VT_VALMASK | VT_LVAL | VT_SYM)) != VT_CONST)
@ -1543,11 +1550,20 @@ ST_FUNC void gen_op(int op)
} }
/* if both pointers, then it must be the '-' op */ /* if both pointers, then it must be the '-' op */
if (bt1 == VT_PTR && bt2 == VT_PTR) { if (bt1 == VT_PTR && bt2 == VT_PTR) {
int ptr1_is_vla;
ptr1_is_vla = 0;
if (op != '-') if (op != '-')
error("cannot use pointers here"); error("cannot use pointers here");
check_comparison_pointer_types(vtop - 1, vtop, op); check_comparison_pointer_types(vtop - 1, vtop, op);
/* XXX: check that types are compatible */ /* 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); gen_opic(op);
/* set to integer type */ /* set to integer type */
#ifdef TCC_TARGET_X86_64 #ifdef TCC_TARGET_X86_64
@ -1555,7 +1571,10 @@ ST_FUNC void gen_op(int op)
#else #else
vtop->type.t = VT_INT; vtop->type.t = VT_INT;
#endif #endif
vpushi(u); if (ptr1_is_vla)
vswap();
else
vpushi(u);
gen_op(TOK_PDIV); gen_op(TOK_PDIV);
} else { } else {
/* exactly one pointer : must be '+' or '-'. */ /* exactly one pointer : must be '+' or '-'. */
@ -1567,16 +1586,20 @@ ST_FUNC void gen_op(int op)
swap(&t1, &t2); swap(&t1, &t2);
} }
type1 = vtop[-1].type; type1 = vtop[-1].type;
type1.t &= ~VT_ARRAY; type1.t &= ~(VT_ARRAY|VT_VLA);
u = pointed_size(&vtop[-1].type); if (vtop[-1].type.t & VT_VLA)
if (u < 0) vla_runtime_pointed_size(&vtop[-1].type);
error("unknown array element size"); else {
u = pointed_size(&vtop[-1].type);
if (u < 0)
error("unknown array element size");
#ifdef TCC_TARGET_X86_64 #ifdef TCC_TARGET_X86_64
vpushll(u); vpushll(u);
#else #else
/* XXX: cast to int ? (long long case) */ /* XXX: cast to int ? (long long case) */
vpushi(u); vpushi(u);
#endif #endif
}
gen_op('*'); gen_op('*');
#ifdef CONFIG_TCC_BCHECK #ifdef CONFIG_TCC_BCHECK
/* if evaluating constant expression, no code should be /* if evaluating constant expression, no code should be
@ -1932,7 +1955,7 @@ static void gen_cast(CType *type)
vtop->type = *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) ST_FUNC int type_size(CType *type, int *a)
{ {
Sym *s; Sym *s;
@ -1945,7 +1968,10 @@ ST_FUNC int type_size(CType *type, int *a)
*a = s->r; *a = s->r;
return s->c; return s->c;
} else if (bt == VT_PTR) { } 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; int ts;
s = type->ref; s = type->ref;
@ -1992,6 +2018,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 */ /* return the pointed type of t */
static inline CType *pointed_type(CType *type) static inline CType *pointed_type(CType *type)
{ {
@ -3103,25 +3153,43 @@ static void post_type(CType *type, AttributeDef *ad)
type->t = t1 | VT_FUNC; type->t = t1 | VT_FUNC;
type->ref = s; type->ref = s;
} else if (tok == '[') { } else if (tok == '[') {
SValue *last_vtop = NULL;
/* array definition */ /* array definition */
next(); next();
if (tok == TOK_RESTRICT1) if (tok == TOK_RESTRICT1)
next(); next();
n = -1; n = -1;
if (tok != ']') { if (tok != ']') {
n = expr_const(); gexpr();
if (n < 0) if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
error("invalid array size"); 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(']'); skip(']');
/* parse next post type */ /* parse next post type */
t1 = type->t & VT_STORAGE; t1 = type->t & (VT_STORAGE|VT_VLA);
type->t &= ~VT_STORAGE; type->t &= ~(VT_STORAGE|VT_VLA);
post_type(type, ad); 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 */ element type */
s = sym_push(SYM_FIELD, type, 0, n); 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->t = t1 | VT_ARRAY | VT_PTR;
type->ref = s; type->ref = s;
} }
@ -3490,11 +3558,18 @@ ST_FUNC void unary(void)
next(); next();
in_sizeof++; in_sizeof++;
unary_type(&type); // Perform a in_sizeof = 0; 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 (t == TOK_SIZEOF) {
if (size < 0) if (!(type.t & VT_VLA)) {
error("sizeof applied to an incomplete type"); if (size < 0)
vpushi(size); error("sizeof applied to an incomplete type");
vpushi(size);
}
} else { } else {
vpushi(align); vpushi(align);
} }
@ -3653,7 +3728,10 @@ ST_FUNC void unary(void)
} else { } else {
r = s->r; 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 forward reference, we must point to s */
if (vtop->r & VT_SYM) { if (vtop->r & VT_SYM) {
vtop->sym = s; vtop->sym = s;
@ -4782,7 +4860,18 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c,
Sym *s, *f; Sym *s, *f;
CType *t1; 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; s = type->ref;
n = s->c; n = s->c;
array_length = 0; array_length = 0;
@ -5013,6 +5102,7 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r,
ParseState saved_parse_state = {0}; ParseState saved_parse_state = {0};
TokenString init_str; TokenString init_str;
Section *sec; Section *sec;
Sym *vla = NULL;
size = type_size(type, &align); size = type_size(type, &align);
/* If unknown size, we must evaluate it before /* If unknown size, we must evaluate it before
@ -5078,8 +5168,12 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r,
if ((r & VT_VALMASK) == VT_LOCAL) { if ((r & VT_VALMASK) == VT_LOCAL) {
sec = NULL; sec = NULL;
#ifdef CONFIG_TCC_BCHECK #ifdef CONFIG_TCC_BCHECK
if (tcc_state->do_bounds_check && (type->t & VT_ARRAY)) if (tcc_state->do_bounds_check && (type->t & VT_ARRAY)) {
loc--; if (type->t & VT_VLA)
warning("Array bound check don't work for VLA");
else
loc--;
}
#endif #endif
loc = (loc - size) & -align; loc = (loc - size) & -align;
addr = loc; addr = loc;
@ -5087,7 +5181,8 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r,
/* handles bounds */ /* handles bounds */
/* XXX: currently, since we do only one pass, we cannot track /* XXX: currently, since we do only one pass, we cannot track
'&' operators, so we add only arrays */ '&' 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; unsigned long *bounds_ptr;
/* add padding between regions */ /* add padding between regions */
loc--; loc--;
@ -5099,7 +5194,7 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r,
#endif #endif
if (v) { if (v) {
/* local variable */ /* local variable */
sym_push(v, type, r, addr); vla = sym_push(v, type, r, addr);
} else { } else {
/* push local reference */ /* push local reference */
vset(type, r, addr); vset(type, r, addr);
@ -5214,8 +5309,10 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r,
} }
#endif #endif
} }
if (has_init) { if (has_init || (type->t & VT_VLA)) {
decl_initializer(type, sec, addr, 1, 0); decl_initializer(type, sec, addr, 1, 0);
if (type->t & VT_VLA)
vla->s = vtop;
/* restore parse state if needed */ /* restore parse state if needed */
if (init_str.str) { if (init_str.str) {
tok_str_free(init_str.str); tok_str_free(init_str.str);
@ -5584,9 +5681,12 @@ ST_FUNC void decl(int l)
if (!(type.t & VT_ARRAY)) if (!(type.t & VT_ARRAY))
r |= lvalue_type(type.t); r |= lvalue_type(type.t);
has_init = (tok == '='); has_init = (tok == '=');
if (has_init && (type.t & VT_VLA))
error("Variable length array cannot be initialized");
if ((btype.t & VT_EXTERN) || if ((btype.t & VT_EXTERN) ||
((type.t & VT_ARRAY) && (type.t & VT_STATIC) && ((type.t & VT_ARRAY) && (!(type.t & VT_VLA)) &&
!has_init && l == VT_CONST && type.ref->c < 0)) { (type.t & VT_STATIC) && !has_init && l == VT_CONST &&
type.ref->c < 0)) {
/* external variable */ /* external variable */
/* NOTE: as GCC, uninitialized global static /* NOTE: as GCC, uninitialized global static
arrays of null size are considered as arrays of null size are considered as

View File

@ -77,6 +77,7 @@ void whitespace_test(void);
void relocation_test(void); void relocation_test(void);
void old_style_function(void); void old_style_function(void);
void alloca_test(void); void alloca_test(void);
void c99_vla_test(int size1, int size2);
void sizeof_test(void); void sizeof_test(void);
void typeof_test(void); void typeof_test(void);
void local_label_test(void); void local_label_test(void);
@ -563,6 +564,7 @@ int main(int argc, char **argv)
relocation_test(); relocation_test();
old_style_function(); old_style_function();
alloca_test(); alloca_test();
c99_vla_test(5, 2);
sizeof_test(); sizeof_test();
typeof_test(); typeof_test();
statement_expr_test(); statement_expr_test();
@ -2018,6 +2020,26 @@ void alloca_test()
#endif #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) void sizeof_test(void)
{ {
int a; int a;