diff options
| author | Thomas Preud'homme <thomas.preudhomme@celest.fr> | 2011-01-15 17:50:55 +0100 |
|---|---|---|
| committer | Thomas Preud'homme <thomas.preudhomme@celest.fr> | 2011-02-04 02:22:25 +0100 |
| commit | a5a50eaafeea0d3ca47bc8fb6baf983743470c60 (patch) | |
| tree | 3053095ca76b5ce12761dc7d312307c5a16400e2 /tccgen.c | |
| parent | d35a3ac375473a584fa9dbb1320b5202a236a870 (diff) | |
| download | tinycc-a5a50eaafeea0d3ca47bc8fb6baf983743470c60.tar.gz tinycc-a5a50eaafeea0d3ca47bc8fb6baf983743470c60.tar.bz2 | |
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
Diffstat (limited to 'tccgen.c')
| -rw-r--r-- | tccgen.c | 160 |
1 files changed, 130 insertions, 30 deletions
@@ -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 |
