Add gcc attribute cleanup support

The major difficulty was to handle cleanup when a goto happen
to do so, I've had a "ScopeTracker" struct.
I can't use local_scope because that would not work with code like below
as local_scope would be at the same level:

{
    char * __attribute__ ((cleanup(clean_function))) str = "hej";
    goto next;
}
{
    next:
}
This commit is contained in:
matthias 2018-12-20 10:55:22 +01:00
parent f6be0d483b
commit 0d91ba749c
4 changed files with 278 additions and 30 deletions

31
tcc.h
View File

@ -456,15 +456,9 @@ struct FuncAttr {
func_args : 8; /* PE __stdcall args */
};
/* GNUC attribute definition */
typedef struct AttributeDef {
struct SymAttr a;
struct FuncAttr f;
struct Section *section;
int alias_target; /* token */
int asm_label; /* associated asm label */
char attr_mode; /* __attribute__((__mode__(...))) */
} AttributeDef;
typedef struct ScopeTacker {
struct ScopeTacker *prev;
} ScopeTacker;
/* symbol management */
typedef struct Sym {
@ -476,7 +470,10 @@ typedef struct Sym {
int c; /* associated number or Elf symbol index */
union {
int sym_scope; /* scope level for locals */
int jnext; /* next jump label */
struct {
ScopeTacker *scope;
int jnext; /* next jump label */
};
struct FuncAttr f; /* function attributes */
int auxtype; /* bitfield access type */
};
@ -590,6 +587,18 @@ typedef struct TokenString {
char alloc;
} TokenString;
/* GNUC attribute definition */
typedef struct AttributeDef {
struct SymAttr a;
struct FuncAttr f;
struct Section *section;
Sym *cleanup_func;
int should_remember;
int alias_target; /* token */
int asm_label; /* associated asm label */
char attr_mode; /* __attribute__((__mode__(...))) */
} AttributeDef;
/* inline functions */
typedef struct InlineFunc {
TokenString *func_str;
@ -1279,6 +1288,8 @@ ST_DATA Sym *sym_free_first;
ST_DATA void **sym_pools;
ST_DATA int nb_sym_pools;
ST_DATA ScopeTacker *scope_tracker;
ST_DATA Sym *global_stack;
ST_DATA Sym *local_stack;
ST_DATA Sym *local_label_stack;

274
tccgen.c
View File

@ -38,7 +38,13 @@ ST_DATA Sym *local_stack;
ST_DATA Sym *define_stack;
ST_DATA Sym *global_label_stack;
ST_DATA Sym *local_label_stack;
static int local_scope;
#define SCOPE_TCK_STORE_SIZE 1024
ST_DATA ScopeTacker *scope_tracker;
static ScopeTacker scope_tck_store[SCOPE_TCK_STORE_SIZE];
static int scope_tck_idx;
static int in_sizeof;
static int section_sym;
@ -77,7 +83,29 @@ ST_DATA struct temp_local_variable {
short size;
short align;
} arr_temp_local_vars[MAX_TEMP_LOCAL_VARIABLE_NUMBER];
short nb_temp_local_vars;
static struct cleanup {
ScopeTacker *scope;
struct {
Sym *var;
Sym *func;
} *syms;
int nb_cleanup;
} *cleanup_info;
static struct cleanup **cleanup_info_store;
static int cleanup_idx;
static struct CleanupGoto {
Sym *s;
ScopeTacker *scope;
int jnext;
int is_valide;
} **cleanup_goto_info;
static int last_cleanup_goto;
/* ------------------------------------------------------------------------- */
static void gen_cast(CType *type);
@ -108,6 +136,57 @@ static void gv_dup(void);
static int get_temp_local_var(int size,int align);
static void clear_temp_local_var_list();
static void incr_local_scope(void)
{
ScopeTacker *tmp = scope_tracker;
++local_scope;
/* if we have more scopes that SCOPE_TCK_STORE_SIZE */
/* cleanup will not work at all, but it should be a very rare case */
/* and I don't want to add too much complexiy for handeling case */
/* that should not happen */
if (scope_tck_idx < SCOPE_TCK_STORE_SIZE) {
scope_tracker = &scope_tck_store[scope_tck_idx];
scope_tracker->prev = tmp;
++scope_tck_idx;
}
}
static void decr_local_scope(void)
{
if (scope_tracker)
scope_tracker = scope_tracker->prev;
--local_scope;
}
static void reset_local_scope(void)
{
if (cleanup_info) {
for (int i = 0; i < cleanup_idx; ++i) {
cleanup_info = cleanup_info_store[i];
tcc_free(cleanup_info->syms);
tcc_free(cleanup_info);
}
cleanup_info = NULL;
cleanup_idx = 0;
tcc_free(cleanup_info_store);
cleanup_info_store = NULL;
dynarray_reset(&cleanup_goto_info, &last_cleanup_goto);
}
scope_tracker = NULL;
local_scope = 0;
scope_tck_idx = 0;
}
int is_scope_a_parent_of(ScopeTacker *parent, ScopeTacker *child)
{
for (ScopeTacker *cur = parent->prev; cur; cur = cur->prev) {
if (cur == child)
return 1;
}
return 0;
}
ST_INLN int is_float(int t)
{
int bt;
@ -3392,6 +3471,23 @@ redo:
t = tok;
next();
switch(t) {
case TOK_CLEANUP1:
case TOK_CLEANUP2:
{
Sym *s;
skip('(');
s = sym_find(tok);
if (!s) {
tcc_warning("implicit declaration of function '%s'",
get_tok_str(tok, &tokc));
s = external_global_sym(tok, &func_old_type, 0);
}
ad->cleanup_func = s;
next();
skip(')');
break;
}
case TOK_SECTION1:
case TOK_SECTION2:
skip('(');
@ -4541,6 +4637,8 @@ static CType *type_decl(CType *type, AttributeDef *ad, int *v, int td)
ret = pointed_type(type);
}
if (ad->cleanup_func)
ad->should_remember = 1;
if (tok == '(') {
/* This is possibly a parameter type list for abstract declarators
('int ()'), use post_type for testing this. */
@ -4692,6 +4790,59 @@ static void parse_builtin_params(int nc, const char *args)
nocode_wanted--;
}
static void try_call_scope_cleanup(ScopeTacker *scope)
{
if (!cleanup_info)
return;
if (cleanup_info->scope != scope) {
for (int i = 0; i < cleanup_idx; ++i) {
cleanup_info = cleanup_info_store[i];
if (cleanup_info->scope == scope)
goto found;
}
return;
}
found:
for (int i = cleanup_info->nb_cleanup - 1; i >= 0; --i) {
Sym *fs = cleanup_info->syms[i].func;
Sym *vs = cleanup_info->syms[i].var;
vpushsym(&fs->type, fs);
if (is_float(vs->type.t)) {
vs->type.t = VT_INT;
}
vset(&vs->type, vs->r, vs->c);
vtop->sym = vs;
gaddrof();
gfunc_param_typed(fs, vs);
gfunc_call(1);
}
}
static void try_call_cleanup_goto(ScopeTacker *dest_scope)
{
if (!cleanup_info)
return;
for (ScopeTacker *cur_scope_tracker = scope_tracker;
cur_scope_tracker && cur_scope_tracker != dest_scope;
cur_scope_tracker = cur_scope_tracker->prev) {
try_call_scope_cleanup(cur_scope_tracker);
}
}
static void try_call_all_cleanup(void)
{
if (!cleanup_info)
return;
for (ScopeTacker *cur_scope_tracker = scope_tracker;
cur_scope_tracker;
cur_scope_tracker = cur_scope_tracker->prev) {
try_call_scope_cleanup(cur_scope_tracker);
}
}
ST_FUNC void unary(void)
{
int n, t, align, size, r, sizeof_caller;
@ -6075,11 +6226,11 @@ static void block(int *bsym, int *csym, int is_expr)
skip(')');
a = gvtst(1, 0);
b = 0;
++local_scope;
incr_local_scope();
saved_nocode_wanted = nocode_wanted;
block(&a, &b, 0);
nocode_wanted = saved_nocode_wanted;
--local_scope;
decr_local_scope();
gjmp_addr(d);
gsym(a);
gsym_addr(b, d);
@ -6091,7 +6242,7 @@ static void block(int *bsym, int *csym, int is_expr)
/* record local declaration stack position */
s = local_stack;
llabel = local_label_stack;
++local_scope;
incr_local_scope();
/* handle local labels declarations */
while (tok == TOK_LABEL) {
@ -6120,10 +6271,36 @@ static void block(int *bsym, int *csym, int is_expr)
block(bsym, csym, is_expr);
}
}
if (cleanup_info) {
int jmp = 0;
jmp = gjmp(jmp);
for (int i = 0; i < last_cleanup_goto; ++i) {
struct CleanupGoto *cur = cleanup_goto_info[i];
if (!cur->is_valide)
continue;
if (scope_tracker == cur->scope ||
is_scope_a_parent_of(cur->scope, scope_tracker)) {
gsym(cur->jnext);
try_call_scope_cleanup(scope_tracker);
cur->jnext = 0;
cur->jnext = gjmp(cur->jnext);
}
}
gsym(jmp);
if (!nocode_wanted) {
try_call_scope_cleanup(scope_tracker);
}
}
/* pop locally defined labels */
label_pop(&local_label_stack, llabel, is_expr);
/* pop locally defined symbols */
--local_scope;
decr_local_scope();
/* In the is_expr case (a statement expression is finished here),
vtop might refer to symbols on the local_stack. Either via the
type or via vtop->sym. We can't pop those nor any that in turn
@ -6146,11 +6323,14 @@ static void block(int *bsym, int *csym, int is_expr)
if (tok != ';') {
gexpr();
gen_assign_cast(&func_vt);
try_call_all_cleanup();
if ((func_vt.t & VT_BTYPE) == VT_VOID)
vtop--;
else
gfunc_return(&func_vt);
}
} else {
try_call_all_cleanup();
}
skip(';');
/* jump unless last stmt in top-level block */
if (tok != '}' || local_scope != 1)
@ -6179,7 +6359,7 @@ static void block(int *bsym, int *csym, int is_expr)
next();
skip('(');
s = local_stack;
++local_scope;
incr_local_scope();
if (tok != ';') {
/* c99 for-loop init decl? */
if (!decl0(VT_LOCAL, 1, NULL)) {
@ -6215,7 +6395,7 @@ static void block(int *bsym, int *csym, int is_expr)
gjmp_addr(c);
gsym(a);
gsym_addr(b, c);
--local_scope;
decr_local_scope();
sym_pop(&local_stack, s, 0);
} else
@ -6319,21 +6499,42 @@ static void block(int *bsym, int *csym, int is_expr)
expect("pointer");
ggoto();
} else if (tok >= TOK_UIDENT) {
s = label_find(tok);
/* put forward definition if needed */
if (!s) {
s = label_push(&global_label_stack, tok, LABEL_FORWARD);
s = label_find(tok);
if (!s || s->jnext == -1) {
/* put forward definition if needed */
if (!s)
s = label_push(&global_label_stack, tok, LABEL_FORWARD);
if (cleanup_info) {
struct CleanupGoto *cur =
tcc_malloc(sizeof(struct CleanupGoto));
cur->s = s;
cur->is_valide = 1;
s->jnext = -1;
cur->jnext = 0;
cur->scope = scope_tracker;
cur->jnext = gjmp(cur->jnext);
dynarray_add(&cleanup_goto_info, &last_cleanup_goto, cur);
vla_sp_restore_root();
goto out_goto;
}
} else {
if (cleanup_info && is_scope_a_parent_of(scope_tracker,
s->scope))
try_call_cleanup_goto(s->scope);
if (s->r == LABEL_DECLARED)
s->r = LABEL_FORWARD;
}
vla_sp_restore_root();
vla_sp_restore_root();
if (s->r & LABEL_FORWARD)
s->jnext = gjmp(s->jnext);
else
gjmp_addr(s->jnext);
next();
} else {
s->jnext = gjmp(s->jnext);
else
gjmp_addr(s->jnext);
out_goto:
next();
} else {
expect("label identifier");
}
skip(';');
@ -6346,10 +6547,21 @@ static void block(int *bsym, int *csym, int is_expr)
next();
s = label_find(b);
if (s) {
int is_gen = 0;
if (s->r == LABEL_DEFINED)
tcc_error("duplicate label '%s'", get_tok_str(s->v, NULL));
gsym(s->jnext);
for (int i = 0; i < last_cleanup_goto; ++i) {
struct CleanupGoto *cur = cleanup_goto_info[i];
if (cur->s == s) {
cur->is_valide = 0;
gsym(cur->jnext);
is_gen = 1;
}
}
s->r = LABEL_DEFINED;
if (!is_gen)
gsym(s->jnext);
} else {
s = label_push(&global_label_stack, b, LABEL_DEFINED);
}
@ -7085,6 +7297,28 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r,
}
#endif
sym = sym_push(v, type, r, addr);
if (ad->cleanup_func) {
int nb_cleanup;
if (!cleanup_info || cleanup_info->scope != scope_tracker) {
cleanup_info = tcc_malloc(sizeof(struct cleanup));
dynarray_add(&cleanup_info_store, &cleanup_idx,
cleanup_info);
cleanup_info ->scope = scope_tracker;
cleanup_info->nb_cleanup = 0;
cleanup_info->syms = NULL;
}
nb_cleanup = cleanup_info->nb_cleanup + 1;
cleanup_info->syms = tcc_realloc(cleanup_info->syms,
nb_cleanup *
sizeof(*cleanup_info->syms));
cleanup_info->syms[nb_cleanup - 1].func = ad->cleanup_func;
cleanup_info->syms[nb_cleanup - 1].var = sym;
cleanup_info->nb_cleanup = nb_cleanup;
if (!ad->should_remember)
ad->cleanup_func = NULL;
}
sym->a = ad->a;
} else {
/* push local reference */
@ -7226,7 +7460,7 @@ static void gen_function(Sym *sym)
sym_push2(&local_stack, SYM_FIELD, 0, 0);
local_scope = 1; /* for function parameters */
gfunc_prolog(&sym->type);
local_scope = 0;
reset_local_scope();
rsym = 0;
clear_temp_local_var_list();
block(NULL, NULL, 0);
@ -7245,7 +7479,7 @@ static void gen_function(Sym *sym)
cur_text_section->data_offset = ind;
label_pop(&global_label_stack, NULL, 0);
/* reset local stack */
local_scope = 0;
reset_local_scope();
sym_pop(&local_stack, NULL, 0);
/* end of function */
/* patch symbol size */

View File

@ -1367,6 +1367,7 @@ ST_FUNC Sym *label_push(Sym **ptop, int v, int flags)
Sym *s, **ps;
s = sym_push2(ptop, v, 0, 0);
s->r = flags;
s->scope = scope_tracker;
ps = &table_ident[v - TOK_IDENT]->sym_label;
if (ptop == &global_label_stack) {
/* modify the top most local identifier, so that

View File

@ -123,6 +123,8 @@
DEF(TOK_FASTCALL3, "__fastcall__")
DEF(TOK_REGPARM1, "regparm")
DEF(TOK_REGPARM2, "__regparm__")
DEF(TOK_CLEANUP1, "cleanup")
DEF(TOK_CLEANUP2, "__cleanup__")
DEF(TOK_MODE, "__mode__")
DEF(TOK_MODE_QI, "__QI__")