Improved variable length array support.

VLA storage is now freed when it goes out of scope. This makes it
possible to use a VLA inside a loop without consuming an unlimited
amount of memory.

Combining VLAs with alloca() should work as in GCC - when a VLA is
freed, memory allocated by alloca() after the VLA was created is also
freed. There are some exceptions to this rule when using goto: if a VLA
is in scope at the goto, jumping to a label will reset the stack pointer
to where it was immediately after the last VLA was created prior to the
label, or to what it was before the first VLA was created if the label
is outside the scope of any VLA. This means that in some cases combining
alloca() and VLAs will free alloca() memory where GCC would not.
This commit is contained in:
James Lyon
2013-04-27 20:39:34 +01:00
parent 6ee366e765
commit 41b3c7a507
12 changed files with 311 additions and 23 deletions

View File

@ -55,6 +55,11 @@ ST_DATA Sym *define_stack;
ST_DATA Sym *global_label_stack;
ST_DATA Sym *local_label_stack;
ST_DATA int vla_sp_loc_tmp; /* vla_sp_loc is set to this when the value won't be needed later */
ST_DATA int vla_sp_root_loc; /* vla_sp_loc for SP before any VLAs were pushed */
ST_DATA int *vla_sp_loc; /* Pointer to variable holding location to store stack pointer on the stack when modifying stack pointer */
ST_DATA int vla_flags; /* VLA_* flags */
ST_DATA SValue __vstack[1+VSTACK_SIZE], *vtop;
ST_DATA int const_wanted; /* true if constant wanted */
@ -81,6 +86,7 @@ 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 void vla_sp_save(void);
static int is_compatible_parameter_types(CType *type1, CType *type2);
static void expr_type(CType *type);
@ -2109,6 +2115,17 @@ ST_FUNC void vla_runtime_type_size(CType *type, int *a)
}
}
static void vla_sp_save(void) {
if (!(vla_flags & VLA_SP_LOC_SET)) {
*vla_sp_loc = (loc -= PTR_SIZE);
vla_flags |= VLA_SP_LOC_SET;
}
if (!(vla_flags & VLA_SP_SAVED)) {
gen_vla_sp_save(*vla_sp_loc);
vla_flags |= VLA_SP_SAVED;
}
}
/* return the pointed type of t */
static inline CType *pointed_type(CType *type)
{
@ -4484,6 +4501,16 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym,
frame_bottom->next = scope_stack_bottom;
scope_stack_bottom = frame_bottom;
llabel = local_label_stack;
/* save VLA state */
int block_vla_sp_loc = *vla_sp_loc;
int *saved_vla_sp_loc = vla_sp_loc;
if (saved_vla_sp_loc != &vla_sp_root_loc)
vla_sp_loc = &block_vla_sp_loc;
int saved_vla_flags = vla_flags;
vla_flags |= VLA_NEED_NEW_FRAME;
/* handle local labels declarations */
if (tok == TOK_LABEL) {
next();
@ -4527,6 +4554,16 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym,
/* pop locally defined symbols */
scope_stack_bottom = scope_stack_bottom->next;
sym_pop(&local_stack, s);
/* Pop VLA frames and restore stack pointer if required */
if (saved_vla_sp_loc != &vla_sp_root_loc)
*saved_vla_sp_loc = block_vla_sp_loc;
if (vla_sp_loc != (saved_vla_sp_loc == &vla_sp_root_loc ? &vla_sp_root_loc : &block_vla_sp_loc)) {
vla_sp_loc = saved_vla_sp_loc;
gen_vla_sp_restore(*vla_sp_loc);
}
vla_flags = (vla_flags & ~VLA_SCOPE_FLAGS) | (saved_vla_flags & VLA_SCOPE_FLAGS);
next();
} else if (tok == TOK_RETURN) {
next();
@ -4731,6 +4768,13 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym,
s->r = LABEL_FORWARD;
}
/* label already defined */
if (vla_flags & VLA_IN_SCOPE) {
/* If VLAs are in use, save the current stack pointer and
reset the stack pointer to what it was at function entry
(label will restore stack pointer in inner scopes) */
vla_sp_save();
gen_vla_sp_restore(vla_sp_root_loc);
}
if (s->r & LABEL_FORWARD)
s->jnext = gjmp(s->jnext);
else
@ -4746,6 +4790,12 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym,
b = is_label();
if (b) {
/* label case */
if (vla_flags & VLA_IN_SCOPE) {
/* save/restore stack pointer across label
this is a no-op when combined with the load immediately
after the label unless we arrive via goto */
vla_sp_save();
}
s = label_find(b);
if (s) {
if (s->r == LABEL_DEFINED)
@ -4756,6 +4806,10 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym,
s = label_push(&global_label_stack, b, LABEL_DEFINED);
}
s->jnext = ind;
if (vla_flags & VLA_IN_SCOPE) {
gen_vla_sp_restore(*vla_sp_loc);
vla_flags |= VLA_NEED_NEW_FRAME;
}
/* we accept this, but it is a mistake */
block_after_label:
if (tok == '}') {
@ -5026,24 +5080,21 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c,
CType *t1;
if (type->t & VT_VLA) {
#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
int a;
CValue retcval;
vpush_global_sym(&func_old_type, TOK_alloca);
/* save current stack pointer */
if (vla_flags & VLA_NEED_NEW_FRAME) {
vla_sp_save();
vla_flags = VLA_IN_SCOPE;
vla_sp_loc = &vla_sp_loc_tmp;
}
vla_runtime_type_size(type, &a);
gfunc_call(1);
/* return value */
retcval.i = 0;
vsetc(type, REG_IRET, &retcval);
gen_vla_alloc(type, a);
vset(type, VT_LOCAL|VT_LVAL, c);
vswap();
vstore();
vpop();
#else
tcc_error("variable length arrays unsupported for this target");
#endif
} else if (type->t & VT_ARRAY) {
s = type->ref;
n = s->c;
@ -5585,6 +5636,9 @@ static void gen_function(Sym *sym)
put_extern_sym(sym, cur_text_section, ind, 0);
funcname = get_tok_str(sym->v, NULL);
func_ind = ind;
/* Initialize VLA state */
vla_sp_loc = &vla_sp_root_loc;
vla_flags = VLA_NEED_NEW_FRAME;
/* put debug symbol */
if (tcc_state->do_debug)
put_func_debug(sym);