History

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

Thomas Preud'homme committed 15 years ago

Commit a5a50ea1 parent d35a3ac6 files · +169 −33Browse files

Unified · Split

6 files
  1. Changelog +1 −0
  2. TODO +0 −1
  3. tcc-doc.texi +14 −2
  4. tcc.h +2 −0
  5. tccgen.c +130 −30
  6. tests/tcctest.c +22 −0
Changelog+1−0
@@ -12,6 +12,7 @@ not released: · 11 unmodified lines
1212 - fix 32 to 64 bit casts, alignment and struct issues (Shinichiro Hamaji)
1313 - mimic all GNU -option forms supported by ld (Kirill Smelkov)
1414 - support indirect functions as externals (Thomas Preud'homme)
15+- Add support for C99 variable length arrays (Thomas Preud'homme)
1516
1617 version 0.9.25:
1718
TODO+0−1
@@ -48,7 +48,6 @@ Missing features: · 47 unmodified lines
4848 - improve '-E' option.
4949 - atexit (Nigel Horne)
5050 - packed attribute
51−- C99: add variable size arrays (gcc 3.2 testsuite issue)
5251 - C99: add complex types (gcc 3.2 testsuite issue)
5352 - postfix compound literals (see 20010124-1.c)
5453
tcc-doc.texi+14−2
@@ -929,7 +929,11 @@ be the best solution. · 928 unmodified lines
929929 #define VT_BTYPE 0x000f /* mask for basic type */
930930 #define VT_UNSIGNED 0x0010 /* unsigned type */
931931 #define VT_ARRAY 0x0020 /* array type (also has VT_PTR) */
932+#define VT_VLA 0x10000 /* VLA type (also has VT_PTR and VT_ARRAY) */
932933 #define VT_BITFIELD 0x0040 /* bitfield modifier */
934+#define VT_CONSTANT 0x0800 /* const modifier */
935+#define VT_VOLATILE 0x1000 /* volatile modifier */
936+#define VT_SIGNED 0x2000 /* signed type */
933937
934938 #define VT_STRUCT_SHIFT 16 /* structure/enum name shift (16 bits left) */
935939 @end example
@@ -942,7 +946,8 @@ The @code{VT_UNSIGNED} flag can be set for chars, shorts, ints and long · 6 unmodified lines
942946 longs.
943947
944948 Arrays are considered as pointers @code{VT_PTR} with the flag
945−@code{VT_ARRAY} set.
949+@code{VT_ARRAY} set. Variable length arrays are considered as special
950+arrays and therefore also have flag @code{VT_VLA} set.
946951
947952 The @code{VT_BITFIELD} flag can be set for chars, shorts, ints and long
948953 longs. If it is set, then the bitfield position is stored from bits
@@ -958,6 +963,10 @@ integer: · 9 unmodified lines
958963 #define VT_EXTERN 0x00000080 /* extern definition */
959964 #define VT_STATIC 0x00000100 /* static variable */
960965 #define VT_TYPEDEF 0x00000200 /* typedef definition */
966+#define VT_INLINE 0x00000400 /* inline definition */
967+#define VT_IMPORT 0x00004000 /* win32: extern data imported from dll */
968+#define VT_EXPORT 0x00008000 /* win32: data exported from dll */
969+#define VT_WEAK 0x00010000 /* win32: data exported from dll */
961970 @end example
962971
963972 @section Symbols
@@ -969,7 +978,10 @@ contains @code{Sym} structures. · 5 unmodified lines
969978 an idenfier is also a token, so a string is never necessary to store
970979 it). @code{Sym.t} gives the type of the symbol. @code{Sym.r} is usually
971980 the register in which the corresponding variable is stored. @code{Sym.c} is
972−usually a constant associated to the symbol.
981+usually a constant associated to the symbol like its address for normal
982+symbols, and the number of entries for symbols representing arrays.
983+Variable length arrays use @code{Sym.r} instead, which is a pointer to
984+a @code{SValue} holding its runtime size.
973985
974986 Four main symbol stacks are defined:
975987
tcc.h+2−0
@@ -224,6 +224,7 @@ typedef struct Sym { · 223 unmodified lines
224224 union {
225225 long c; /* associated number */
226226 int *d; /* define token stream */
227+ SValue *s; /* associated stack value */
227228 };
228229 CType type; /* associated type */
229230 union {
@@ -616,6 +617,7 @@ struct TCCState { · 386 unmodified lines
616617 #define VT_BTYPE 0x000f /* mask for basic type */
617618 #define VT_UNSIGNED 0x0010 /* unsigned type */
618619 #define VT_ARRAY 0x0020 /* array type (also has VT_PTR) */
620+#define VT_VLA 0x10000 /* VLA type (also has VT_PTR and VT_ARRAY) */
619621 #define VT_BITFIELD 0x0040 /* bitfield modifier */
620622 #define VT_CONSTANT 0x0800 /* const modifier */
621623 #define VT_VOLATILE 0x1000 /* volatile modifier */
tccgen.c+130−30
@@ -78,6 +78,7 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym, int case_re · 77 unmodified lines
7878 static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, int has_init, int v, int scope);
7979 static void expr_eq(void);
8080 static void unary_type(CType *type);
81+static void vla_runtime_type_size(CType *type, int *a);
8182 static int is_compatible_parameter_types(CType *type1, CType *type2);
8283 static void expr_type(CType *type);
8384
@@ -1455,6 +1456,12 @@ static int pointed_size(CType *type) · 1371 unmodified lines
14551456 return type_size(pointed_type(type), &align);
14561457 }
14571458
1459+static void vla_runtime_pointed_size(CType *type)
1460+{
1461+ int align;
1462+ vla_runtime_type_size(pointed_type(type), &align);
1463+}
1464+
14581465 static inline int is_null_pointer(SValue *p)
14591466 {
14601467 if ((p->r & (VT_VALMASK | VT_LVAL | VT_SYM)) != VT_CONST)
@@ -1543,11 +1550,20 @@ ST_FUNC void gen_op(int op) · 82 unmodified lines
15431550 }
15441551 /* if both pointers, then it must be the '-' op */
15451552 if (bt1 == VT_PTR && bt2 == VT_PTR) {
1553+ int ptr1_is_vla;
1554+
1555+ ptr1_is_vla = 0;
15461556 if (op != '-')
15471557 error("cannot use pointers here");
15481558 check_comparison_pointer_types(vtop - 1, vtop, op);
15491559 /* XXX: check that types are compatible */
1550− u = pointed_size(&vtop[-1].type);
1560+ if (vtop[-1].type.t & VT_VLA) {
1561+ vla_runtime_pointed_size(&vtop[-1].type);
1562+ vrott(3);
1563+ ptr1_is_vla = 1;
1564+ } else {
1565+ u = pointed_size(&vtop[-1].type);
1566+ }
15511567 gen_opic(op);
15521568 /* set to integer type */
15531569 #ifdef TCC_TARGET_X86_64
@@ -1555,7 +1571,10 @@ ST_FUNC void gen_op(int op) · 1 unmodified line
15551571 #else
15561572 vtop->type.t = VT_INT;
15571573 #endif
1558− vpushi(u);
1574+ if (ptr1_is_vla)
1575+ vswap();
1576+ else
1577+ vpushi(u);
15591578 gen_op(TOK_PDIV);
15601579 } else {
15611580 /* exactly one pointer : must be '+' or '-'. */
@@ -1567,16 +1586,20 @@ ST_FUNC void gen_op(int op) · 5 unmodified lines
15671586 swap(&t1, &t2);
15681587 }
15691588 type1 = vtop[-1].type;
1570− type1.t &= ~VT_ARRAY;
1571− u = pointed_size(&vtop[-1].type);
1572− if (u < 0)
1573− error("unknown array element size");
1589+ type1.t &= ~(VT_ARRAY|VT_VLA);
1590+ if (vtop[-1].type.t & VT_VLA)
1591+ vla_runtime_pointed_size(&vtop[-1].type);
1592+ else {
1593+ u = pointed_size(&vtop[-1].type);
1594+ if (u < 0)
1595+ error("unknown array element size");
15741596 #ifdef TCC_TARGET_X86_64
1575− vpushll(u);
1597+ vpushll(u);
15761598 #else
1577− /* XXX: cast to int ? (long long case) */
1578− vpushi(u);
1599+ /* XXX: cast to int ? (long long case) */
1600+ vpushi(u);
15791601 #endif
1602+ }
15801603 gen_op('*');
15811604 #ifdef CONFIG_TCC_BCHECK
15821605 /* if evaluating constant expression, no code should be
@@ -1932,7 +1955,7 @@ static void gen_cast(CType *type) · 349 unmodified lines
19321955 vtop->type = *type;
19331956 }
19341957
1935−/* return type size. Put alignment at 'a' */
1958+/* return type size as known at compile time. Put alignment at 'a' */
19361959 ST_FUNC int type_size(CType *type, int *a)
19371960 {
19381961 Sym *s;
@@ -1945,7 +1968,10 @@ ST_FUNC int type_size(CType *type, int *a) · 6 unmodified lines
19451968 *a = s->r;
19461969 return s->c;
19471970 } else if (bt == VT_PTR) {
1948− if (type->t & VT_ARRAY) {
1971+ if (type->t & VT_VLA) {
1972+ *a = 1;
1973+ return 0;
1974+ } else if (type->t & VT_ARRAY) {
19491975 int ts;
19501976
19511977 s = type->ref;
@@ -1992,6 +2018,30 @@ ST_FUNC int type_size(CType *type, int *a) · 40 unmodified lines
19922018 }
19932019 }
19942020
2021+/* push type size as known at runtime time on top of value stack. Put
2022+ alignment at 'a' */
2023+ST_FUNC void vla_runtime_type_size(CType *type, int *a)
2024+{
2025+ if (type->t & VT_VLA) {
2026+ Sym *s;
2027+
2028+ s = type->ref;
2029+ vla_runtime_type_size(&s->type, a);
2030+ vpushv(s->s);
2031+ if ((vtop->r & (VT_SYM|VT_LVAL|VT_VALMASK)) != VT_CONST) {
2032+ gv_dup();
2033+ vswap();
2034+ vpop();
2035+ }
2036+ gen_op('*');
2037+ } else {
2038+ int size;
2039+
2040+ size = type_size(type, a);
2041+ vpushi(size);
2042+ }
2043+}
2044+
19952045 /* return the pointed type of t */
19962046 static inline CType *pointed_type(CType *type)
19972047 {
@@ -3103,25 +3153,43 @@ static void post_type(CType *type, AttributeDef *ad) · 1105 unmodified lines
31033153 type->t = t1 | VT_FUNC;
31043154 type->ref = s;
31053155 } else if (tok == '[') {
3156+ SValue *last_vtop = NULL;
3157+
31063158 /* array definition */
31073159 next();
31083160 if (tok == TOK_RESTRICT1)
31093161 next();
31103162 n = -1;
31113163 if (tok != ']') {
3112− n = expr_const();
3113− if (n < 0)
3114− error("invalid array size");
3164+ gexpr();
3165+ if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
3166+ n = vtop->c.i;
3167+ last_vtop = vtop;
3168+ if (n < 0)
3169+ error("invalid array size");
3170+ } else {
3171+ if (!is_integer_btype(vtop->type.t & VT_BTYPE))
3172+ error("size of variable length array should be an integer");
3173+ type->t |= VT_VLA;
3174+ last_vtop = vtop;
3175+ }
31153176 }
31163177 skip(']');
31173178 /* parse next post type */
3118− t1 = type->t & VT_STORAGE;
3119− type->t &= ~VT_STORAGE;
3179+ t1 = type->t & (VT_STORAGE|VT_VLA);
3180+ type->t &= ~(VT_STORAGE|VT_VLA);
31203181 post_type(type, ad);
3182+ t1 |= type->t & VT_VLA;
31213183
3122− /* we push a anonymous symbol which will contain the array
3184+ /* we push an anonymous symbol which will contain the array
31233185 element type */
31243186 s = sym_push(SYM_FIELD, type, 0, n);
3187+ if (t1 & VT_VLA) {
3188+ s->s = last_vtop; // That's ok, we don't need n with VLA
3189+ } else {
3190+ if (n >= 0)
3191+ vpop();
3192+ }
31253193 type->t = t1 | VT_ARRAY | VT_PTR;
31263194 type->ref = s;
31273195 }
@@ -3490,11 +3558,18 @@ ST_FUNC void unary(void) · 362 unmodified lines
34903558 next();
34913559 in_sizeof++;
34923560 unary_type(&type); // Perform a in_sizeof = 0;
3493− size = type_size(&type, &align);
3561+ if ((t == TOK_SIZEOF) && (type.t & VT_VLA)) {
3562+ vla_runtime_type_size(&type, &align);
3563+ size = 0;
3564+ } else {
3565+ size = type_size(&type, &align);
3566+ }
34943567 if (t == TOK_SIZEOF) {
3495− if (size < 0)
3496− error("sizeof applied to an incomplete type");
3497− vpushi(size);
3568+ if (!(type.t & VT_VLA)) {
3569+ if (size < 0)
3570+ error("sizeof applied to an incomplete type");
3571+ vpushi(size);
3572+ }
34983573 } else {
34993574 vpushi(align);
35003575 }
@@ -3653,7 +3728,10 @@ ST_FUNC void unary(void) · 152 unmodified lines
36533728 } else {
36543729 r = s->r;
36553730 }
3656− vset(&s->type, r, s->c);
3731+ if (s->type.t & VT_VLA)
3732+ vpushv(s->s);
3733+ else
3734+ vset(&s->type, r, s->c);
36573735 /* if forward reference, we must point to s */
36583736 if (vtop->r & VT_SYM) {
36593737 vtop->sym = s;
@@ -4782,7 +4860,18 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c, · 1122 unmodified lines
47824860 Sym *s, *f;
47834861 CType *t1;
47844862
4785− if (type->t & VT_ARRAY) {
4863+ if (type->t & VT_VLA) {
4864+ int a;
4865+ CValue retcval;
4866+
4867+ vpush_global_sym(&func_old_type, TOK_alloca);
4868+ vla_runtime_type_size(type, &a);
4869+ gfunc_call(1);
4870+
4871+ /* return value */
4872+ retcval.i = 0;
4873+ vsetc(type, REG_IRET, &retcval);
4874+ } else if (type->t & VT_ARRAY) {
47864875 s = type->ref;
47874876 n = s->c;
47884877 array_length = 0;
@@ -5013,6 +5102,7 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, · 224 unmodified lines
50135102 ParseState saved_parse_state = {0};
50145103 TokenString init_str;
50155104 Section *sec;
5105+ Sym *vla = NULL;
50165106
50175107 size = type_size(type, &align);
50185108 /* If unknown size, we must evaluate it before
@@ -5078,8 +5168,12 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, · 59 unmodified lines
50785168 if ((r & VT_VALMASK) == VT_LOCAL) {
50795169 sec = NULL;
50805170 #ifdef CONFIG_TCC_BCHECK
5081− if (tcc_state->do_bounds_check && (type->t & VT_ARRAY))
5082− loc--;
5171+ if (tcc_state->do_bounds_check && (type->t & VT_ARRAY)) {
5172+ if (type->t & VT_VLA)
5173+ warning("Array bound check don't work for VLA");
5174+ else
5175+ loc--;
5176+ }
50835177 #endif
50845178 loc = (loc - size) & -align;
50855179 addr = loc;
@@ -5087,7 +5181,8 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, · 1 unmodified line
50875181 /* handles bounds */
50885182 /* XXX: currently, since we do only one pass, we cannot track
50895183 '&' operators, so we add only arrays */
5090− if (tcc_state->do_bounds_check && (type->t & VT_ARRAY)) {
5184+ if (tcc_state->do_bounds_check && (type->t & VT_ARRAY) &&
5185+ !(type->t & VT_VLA)) {
50915186 unsigned long *bounds_ptr;
50925187 /* add padding between regions */
50935188 loc--;
@@ -5099,7 +5194,7 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, · 5 unmodified lines
50995194 #endif
51005195 if (v) {
51015196 /* local variable */
5102− sym_push(v, type, r, addr);
5197+ vla = sym_push(v, type, r, addr);
51035198 } else {
51045199 /* push local reference */
51055200 vset(type, r, addr);
@@ -5214,8 +5309,10 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, · 108 unmodified lines
52145309 }
52155310 #endif
52165311 }
5217− if (has_init) {
5312+ if (has_init || (type->t & VT_VLA)) {
52185313 decl_initializer(type, sec, addr, 1, 0);
5314+ if (type->t & VT_VLA)
5315+ vla->s = vtop;
52195316 /* restore parse state if needed */
52205317 if (init_str.str) {
52215318 tok_str_free(init_str.str);
@@ -5584,9 +5681,12 @@ ST_FUNC void decl(int l) · 362 unmodified lines
55845681 if (!(type.t & VT_ARRAY))
55855682 r |= lvalue_type(type.t);
55865683 has_init = (tok == '=');
5684+ if (has_init && (type.t & VT_VLA))
5685+ error("Variable length array cannot be initialized");
55875686 if ((btype.t & VT_EXTERN) ||
5588− ((type.t & VT_ARRAY) && (type.t & VT_STATIC) &&
5589− !has_init && l == VT_CONST && type.ref->c < 0)) {
5687+ ((type.t & VT_ARRAY) && (!(type.t & VT_VLA)) &&
5688+ (type.t & VT_STATIC) && !has_init && l == VT_CONST &&
5689+ type.ref->c < 0)) {
55905690 /* external variable */
55915691 /* NOTE: as GCC, uninitialized global static
55925692 arrays of null size are considered as
tests/tcctest.c+22−0
@@ -77,6 +77,7 @@ void whitespace_test(void); · 76 unmodified lines
7777 void relocation_test(void);
7878 void old_style_function(void);
7979 void alloca_test(void);
80+void c99_vla_test(int size1, int size2);
8081 void sizeof_test(void);
8182 void typeof_test(void);
8283 void local_label_test(void);
@@ -563,6 +564,7 @@ int main(int argc, char **argv) · 480 unmodified lines
563564 relocation_test();
564565 old_style_function();
565566 alloca_test();
567+ c99_vla_test(5, 2);
566568 sizeof_test();
567569 typeof_test();
568570 statement_expr_test();
@@ -2018,6 +2020,26 @@ void alloca_test() · 1449 unmodified lines
20182020 #endif
20192021 }
20202022
2023+void c99_vla_test(int size1, int size2)
2024+{
2025+#if defined __i386__ || defined __x86_64__
2026+ int tab1[size1 * size2][2], tab2[10][2];
2027+ void *tab1_ptr, *tab2_ptr;
2028+
2029+ printf("Test C99 VLA 1 (sizeof): ");
2030+ printf("%s\n", (sizeof tab1 == size1 * size2 * 2 * sizeof(int)) ? "PASSED" : "FAILED");
2031+ tab1_ptr = tab1;
2032+ tab2_ptr = tab2;
2033+ printf("Test C99 VLA 2 (ptrs substract): ");
2034+ printf("%s\n", (tab2 - tab1 == (tab2_ptr - tab1_ptr) / (sizeof(int) * 2)) ? "PASSED" : "FAILED");
2035+ printf("Test C99 VLA 3 (ptr add): ");
2036+ printf("%s\n", &tab1[5][1] == (tab1_ptr + (5 * 2 + 1) * sizeof(int)) ? "PASSED" : "FAILED");
2037+ printf("Test C99 VLA 4 (ptr access): ");
2038+ tab1[size1][1] = 42;
2039+ printf("%s\n", (*((int *) (tab1_ptr + (size1 * 2 + 1) * sizeof(int))) == 42) ? "PASSED" : "FAILED");
2040+#endif
2041+}
2042+
20212043 void sizeof_test(void)
20222044 {
20232045 int a;

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t