From ace0f7f2598f5e9d9d2ad8999e3140b8535d9459 Mon Sep 17 00:00:00 2001 From: Joe Soroka Date: Wed, 6 Apr 2011 09:17:03 -0700 Subject: [PATCH] re-apply VLA by Thomas Preud'homme --- Changelog | 1 + TODO | 1 - tcc-doc.texi | 18 +++++- tcc.h | 4 +- tccgen.c | 160 +++++++++++++++++++++++++++++++++++++++--------- tcctok.h | 2 + tests/tcctest.c | 22 +++++++ 7 files changed, 173 insertions(+), 35 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 8c91d0f..2804d57 100644 --- a/tcc-doc.texi +++ b/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: diff --git a/tcc.h b/tcc.h index ae48047..be53b65 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 { @@ -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) diff --git a/tccgen.c b/tccgen.c index 97a13e4..c9629d4 100644 --- a/tccgen.c +++ b/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 diff --git a/tcctok.h b/tcctok.h index aabbd05..6e7ca1f 100644 --- a/tcctok.h +++ b/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 diff --git a/tests/tcctest.c b/tests/tcctest.c index df0a0c6..8a6dd16 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); @@ -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;