aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Changelog1
-rw-r--r--TODO1
-rw-r--r--tcc-doc.texi16
-rw-r--r--tcc.h2
-rw-r--r--tccgen.c160
-rw-r--r--tests/tcctest.c22
6 files changed, 169 insertions, 33 deletions
diff --git a/Changelog b/Changelog
index fdccdbd..7a9f0c8 100644
--- a/Changelog
+++ b/Changelog
@@ -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:
diff --git a/TODO b/TODO
index 9d19153..74fe8a6 100644
--- a/TODO
+++ b/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)
diff --git a/tcc-doc.texi b/tcc-doc.texi
index fc65060..4ba4f46 100644
--- a/tcc-doc.texi
+++ b/tcc-doc.texi
@@ -929,7 +929,11 @@ 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 0x10000 /* 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 16 /* structure/enum name shift (16 bits left) */
@end example
@@ -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:
diff --git a/tcc.h b/tcc.h
index a3a9c78..22d79f0 100644
--- a/tcc.h
+++ b/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 {
@@ -616,6 +617,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 0x10000 /* 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 */
diff --git a/tccgen.c b/tccgen.c
index 7144ccd..7fb7cfe 100644
--- a/tccgen.c
+++ b/tccgen.c
@@ -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 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);
@@ -1455,6 +1456,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)
@@ -1543,11 +1550,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
@@ -1555,7 +1571,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 '-'. */
@@ -1567,16 +1586,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
@@ -1932,7 +1955,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;
@@ -1945,7 +1968,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;
@@ -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 */
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->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;
}
@@ -3490,11 +3558,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);
}
@@ -3653,7 +3728,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;
@@ -4782,7 +4860,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;
@@ -5013,6 +5102,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;
size = type_size(type, &align);
/* 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) {
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;
@@ -5087,7 +5181,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--;
@@ -5099,7 +5194,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);
@@ -5214,8 +5309,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);
@@ -5584,9 +5681,12 @@ ST_FUNC void decl(int l)
if (!(type.t & VT_ARRAY))
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_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 */
/* NOTE: as GCC, uninitialized global static
arrays of null size are considered as
diff --git a/tests/tcctest.c b/tests/tcctest.c
index 87e7ad1..499a071 100644
--- a/tests/tcctest.c
+++ b/tests/tcctest.c
@@ -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);
@@ -563,6 +564,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();
@@ -2018,6 +2020,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;