| @@ -38,7 +38,13 @@ ST_DATA Sym *local_stack; · 37 unmodified lines |
| 38 | 38 | ST_DATA Sym *define_stack; |
| 39 | 39 | ST_DATA Sym *global_label_stack; |
| 40 | 40 | ST_DATA Sym *local_label_stack; |
| 41 | + |
| 41 | 42 | static int local_scope; |
| 43 | +#define SCOPE_TCK_STORE_SIZE 1024 |
| 44 | +ST_DATA ScopeTacker *scope_tracker; |
| 45 | +static ScopeTacker scope_tck_store[SCOPE_TCK_STORE_SIZE]; |
| 46 | +static int scope_tck_idx; |
| 47 | + |
| 42 | 48 | static int in_sizeof; |
| 43 | 49 | static int section_sym; |
| 44 | 50 | |
| @@ -77,7 +83,29 @@ ST_DATA struct temp_local_variable { · 32 unmodified lines |
| 77 | 83 | short size; |
| 78 | 84 | short align; |
| 79 | 85 | } arr_temp_local_vars[MAX_TEMP_LOCAL_VARIABLE_NUMBER]; |
| 86 | + |
| 80 | 87 | short nb_temp_local_vars; |
| 88 | +static struct cleanup { |
| 89 | + ScopeTacker *scope; |
| 90 | + struct { |
| 91 | + Sym *var; |
| 92 | + Sym *func; |
| 93 | + } *syms; |
| 94 | + int nb_cleanup; |
| 95 | +} *cleanup_info; |
| 96 | + |
| 97 | +static struct cleanup **cleanup_info_store; |
| 98 | +static int cleanup_idx; |
| 99 | + |
| 100 | +static struct CleanupGoto { |
| 101 | + Sym *s; |
| 102 | + ScopeTacker *scope; |
| 103 | + int jnext; |
| 104 | + int is_valide; |
| 105 | +} **cleanup_goto_info; |
| 106 | + |
| 107 | +static int last_cleanup_goto; |
| 108 | + |
| 81 | 109 | /* ------------------------------------------------------------------------- */ |
| 82 | 110 | |
| 83 | 111 | static void gen_cast(CType *type); |
| @@ -108,6 +136,57 @@ static void gv_dup(void); · 24 unmodified lines |
| 108 | 136 | static int get_temp_local_var(int size,int align); |
| 109 | 137 | static void clear_temp_local_var_list(); |
| 110 | 138 | |
| 139 | +static void incr_local_scope(void) |
| 140 | +{ |
| 141 | + ScopeTacker *tmp = scope_tracker; |
| 142 | + |
| 143 | + ++local_scope; |
| 144 | + /* if we have more scopes that SCOPE_TCK_STORE_SIZE */ |
| 145 | + /* cleanup will not work at all, but it should be a very rare case */ |
| 146 | + /* and I don't want to add too much complexiy for handeling case */ |
| 147 | + /* that should not happen */ |
| 148 | + if (scope_tck_idx < SCOPE_TCK_STORE_SIZE) { |
| 149 | + scope_tracker = &scope_tck_store[scope_tck_idx]; |
| 150 | + scope_tracker->prev = tmp; |
| 151 | + ++scope_tck_idx; |
| 152 | + } |
| 153 | +} |
| 154 | + |
| 155 | +static void decr_local_scope(void) |
| 156 | +{ |
| 157 | + if (scope_tracker) |
| 158 | + scope_tracker = scope_tracker->prev; |
| 159 | + --local_scope; |
| 160 | +} |
| 161 | + |
| 162 | +static void reset_local_scope(void) |
| 163 | +{ |
| 164 | + if (cleanup_info) { |
| 165 | + for (int i = 0; i < cleanup_idx; ++i) { |
| 166 | + cleanup_info = cleanup_info_store[i]; |
| 167 | + tcc_free(cleanup_info->syms); |
| 168 | + tcc_free(cleanup_info); |
| 169 | + } |
| 170 | + cleanup_info = NULL; |
| 171 | + cleanup_idx = 0; |
| 172 | + tcc_free(cleanup_info_store); |
| 173 | + cleanup_info_store = NULL; |
| 174 | + dynarray_reset(&cleanup_goto_info, &last_cleanup_goto); |
| 175 | + } |
| 176 | + scope_tracker = NULL; |
| 177 | + local_scope = 0; |
| 178 | + scope_tck_idx = 0; |
| 179 | +} |
| 180 | + |
| 181 | +int is_scope_a_parent_of(ScopeTacker *parent, ScopeTacker *child) |
| 182 | +{ |
| 183 | + for (ScopeTacker *cur = parent->prev; cur; cur = cur->prev) { |
| 184 | + if (cur == child) |
| 185 | + return 1; |
| 186 | + } |
| 187 | + return 0; |
| 188 | +} |
| 189 | + |
| 111 | 190 | ST_INLN int is_float(int t) |
| 112 | 191 | { |
| 113 | 192 | int bt; |
| @@ -3392,6 +3471,23 @@ redo: · 3278 unmodified lines |
| 3392 | 3471 | t = tok; |
| 3393 | 3472 | next(); |
| 3394 | 3473 | switch(t) { |
| 3474 | + case TOK_CLEANUP1: |
| 3475 | + case TOK_CLEANUP2: |
| 3476 | + { |
| 3477 | + Sym *s; |
| 3478 | + |
| 3479 | + skip('('); |
| 3480 | + s = sym_find(tok); |
| 3481 | + if (!s) { |
| 3482 | + tcc_warning("implicit declaration of function '%s'", |
| 3483 | + get_tok_str(tok, &tokc)); |
| 3484 | + s = external_global_sym(tok, &func_old_type, 0); |
| 3485 | + } |
| 3486 | + ad->cleanup_func = s; |
| 3487 | + next(); |
| 3488 | + skip(')'); |
| 3489 | + break; |
| 3490 | + } |
| 3395 | 3491 | case TOK_SECTION1: |
| 3396 | 3492 | case TOK_SECTION2: |
| 3397 | 3493 | skip('('); |
| @@ -4541,6 +4637,8 @@ static CType *type_decl(CType *type, AttributeDef *ad, int *v, int td) · 1143 unmodified lines |
| 4541 | 4637 | ret = pointed_type(type); |
| 4542 | 4638 | } |
| 4543 | 4639 | |
| 4640 | + if (ad->cleanup_func) |
| 4641 | + ad->should_remember = 1; |
| 4544 | 4642 | if (tok == '(') { |
| 4545 | 4643 | /* This is possibly a parameter type list for abstract declarators |
| 4546 | 4644 | ('int ()'), use post_type for testing this. */ |
| @@ -4692,6 +4790,59 @@ static void parse_builtin_params(int nc, const char *args) · 145 unmodified lines |
| 4692 | 4790 | nocode_wanted--; |
| 4693 | 4791 | } |
| 4694 | 4792 | |
| 4793 | +static void try_call_scope_cleanup(ScopeTacker *scope) |
| 4794 | +{ |
| 4795 | + if (!cleanup_info) |
| 4796 | + return; |
| 4797 | + |
| 4798 | + if (cleanup_info->scope != scope) { |
| 4799 | + for (int i = 0; i < cleanup_idx; ++i) { |
| 4800 | + cleanup_info = cleanup_info_store[i]; |
| 4801 | + if (cleanup_info->scope == scope) |
| 4802 | + goto found; |
| 4803 | + } |
| 4804 | + return; |
| 4805 | + } |
| 4806 | + found: |
| 4807 | + for (int i = cleanup_info->nb_cleanup - 1; i >= 0; --i) { |
| 4808 | + Sym *fs = cleanup_info->syms[i].func; |
| 4809 | + Sym *vs = cleanup_info->syms[i].var; |
| 4810 | + |
| 4811 | + vpushsym(&fs->type, fs); |
| 4812 | + if (is_float(vs->type.t)) { |
| 4813 | + vs->type.t = VT_INT; |
| 4814 | + } |
| 4815 | + vset(&vs->type, vs->r, vs->c); |
| 4816 | + vtop->sym = vs; |
| 4817 | + gaddrof(); |
| 4818 | + gfunc_param_typed(fs, vs); |
| 4819 | + gfunc_call(1); |
| 4820 | + } |
| 4821 | +} |
| 4822 | + |
| 4823 | +static void try_call_cleanup_goto(ScopeTacker *dest_scope) |
| 4824 | +{ |
| 4825 | + if (!cleanup_info) |
| 4826 | + return; |
| 4827 | + for (ScopeTacker *cur_scope_tracker = scope_tracker; |
| 4828 | + cur_scope_tracker && cur_scope_tracker != dest_scope; |
| 4829 | + cur_scope_tracker = cur_scope_tracker->prev) { |
| 4830 | + try_call_scope_cleanup(cur_scope_tracker); |
| 4831 | + } |
| 4832 | +} |
| 4833 | + |
| 4834 | +static void try_call_all_cleanup(void) |
| 4835 | +{ |
| 4836 | + if (!cleanup_info) |
| 4837 | + return; |
| 4838 | + for (ScopeTacker *cur_scope_tracker = scope_tracker; |
| 4839 | + cur_scope_tracker; |
| 4840 | + cur_scope_tracker = cur_scope_tracker->prev) { |
| 4841 | + try_call_scope_cleanup(cur_scope_tracker); |
| 4842 | + } |
| 4843 | +} |
| 4844 | + |
| 4845 | + |
| 4695 | 4846 | ST_FUNC void unary(void) |
| 4696 | 4847 | { |
| 4697 | 4848 | int n, t, align, size, r, sizeof_caller; |
| @@ -6075,11 +6226,11 @@ static void block(int *bsym, int *csym, int is_expr) · 1377 unmodified lines |
| 6075 | 6226 | skip(')'); |
| 6076 | 6227 | a = gvtst(1, 0); |
| 6077 | 6228 | b = 0; |
| 6078 | | − ++local_scope; |
| 6229 | + incr_local_scope(); |
| 6079 | 6230 | saved_nocode_wanted = nocode_wanted; |
| 6080 | 6231 | block(&a, &b, 0); |
| 6081 | 6232 | nocode_wanted = saved_nocode_wanted; |
| 6082 | | − --local_scope; |
| 6233 | + decr_local_scope(); |
| 6083 | 6234 | gjmp_addr(d); |
| 6084 | 6235 | gsym(a); |
| 6085 | 6236 | gsym_addr(b, d); |
| @@ -6091,7 +6242,7 @@ static void block(int *bsym, int *csym, int is_expr) · 5 unmodified lines |
| 6091 | 6242 | /* record local declaration stack position */ |
| 6092 | 6243 | s = local_stack; |
| 6093 | 6244 | llabel = local_label_stack; |
| 6094 | | − ++local_scope; |
| 6245 | + incr_local_scope(); |
| 6095 | 6246 | |
| 6096 | 6247 | /* handle local labels declarations */ |
| 6097 | 6248 | while (tok == TOK_LABEL) { |
| @@ -6120,10 +6271,36 @@ static void block(int *bsym, int *csym, int is_expr) · 22 unmodified lines |
| 6120 | 6271 | block(bsym, csym, is_expr); |
| 6121 | 6272 | } |
| 6122 | 6273 | } |
| 6274 | + |
| 6275 | + if (cleanup_info) { |
| 6276 | + int jmp = 0; |
| 6277 | + |
| 6278 | + jmp = gjmp(jmp); |
| 6279 | + |
| 6280 | + for (int i = 0; i < last_cleanup_goto; ++i) { |
| 6281 | + struct CleanupGoto *cur = cleanup_goto_info[i]; |
| 6282 | + |
| 6283 | + if (!cur->is_valide) |
| 6284 | + continue; |
| 6285 | + if (scope_tracker == cur->scope || |
| 6286 | + is_scope_a_parent_of(cur->scope, scope_tracker)) { |
| 6287 | + gsym(cur->jnext); |
| 6288 | + try_call_scope_cleanup(scope_tracker); |
| 6289 | + cur->jnext = 0; |
| 6290 | + cur->jnext = gjmp(cur->jnext); |
| 6291 | + } |
| 6292 | + } |
| 6293 | + |
| 6294 | + gsym(jmp); |
| 6295 | + if (!nocode_wanted) { |
| 6296 | + try_call_scope_cleanup(scope_tracker); |
| 6297 | + } |
| 6298 | + } |
| 6299 | + |
| 6123 | 6300 | /* pop locally defined labels */ |
| 6124 | 6301 | label_pop(&local_label_stack, llabel, is_expr); |
| 6125 | 6302 | /* pop locally defined symbols */ |
| 6126 | | − --local_scope; |
| 6303 | + decr_local_scope(); |
| 6127 | 6304 | /* In the is_expr case (a statement expression is finished here), |
| 6128 | 6305 | vtop might refer to symbols on the local_stack. Either via the |
| 6129 | 6306 | 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) · 16 unmodified lines |
| 6146 | 6323 | if (tok != ';') { |
| 6147 | 6324 | gexpr(); |
| 6148 | 6325 | gen_assign_cast(&func_vt); |
| 6326 | + try_call_all_cleanup(); |
| 6149 | 6327 | if ((func_vt.t & VT_BTYPE) == VT_VOID) |
| 6150 | 6328 | vtop--; |
| 6151 | 6329 | else |
| 6152 | 6330 | gfunc_return(&func_vt); |
| 6153 | | − } |
| 6331 | + } else { |
| 6332 | + try_call_all_cleanup(); |
| 6333 | + } |
| 6154 | 6334 | skip(';'); |
| 6155 | 6335 | /* jump unless last stmt in top-level block */ |
| 6156 | 6336 | if (tok != '}' || local_scope != 1) |
| @@ -6179,7 +6359,7 @@ static void block(int *bsym, int *csym, int is_expr) · 22 unmodified lines |
| 6179 | 6359 | next(); |
| 6180 | 6360 | skip('('); |
| 6181 | 6361 | s = local_stack; |
| 6182 | | − ++local_scope; |
| 6362 | + incr_local_scope(); |
| 6183 | 6363 | if (tok != ';') { |
| 6184 | 6364 | /* c99 for-loop init decl? */ |
| 6185 | 6365 | if (!decl0(VT_LOCAL, 1, NULL)) { |
| @@ -6215,7 +6395,7 @@ static void block(int *bsym, int *csym, int is_expr) · 29 unmodified lines |
| 6215 | 6395 | gjmp_addr(c); |
| 6216 | 6396 | gsym(a); |
| 6217 | 6397 | gsym_addr(b, c); |
| 6218 | | − --local_scope; |
| 6398 | + decr_local_scope(); |
| 6219 | 6399 | sym_pop(&local_stack, s, 0); |
| 6220 | 6400 | |
| 6221 | 6401 | } else |
| @@ -6319,21 +6499,42 @@ static void block(int *bsym, int *csym, int is_expr) · 97 unmodified lines |
| 6319 | 6499 | expect("pointer"); |
| 6320 | 6500 | ggoto(); |
| 6321 | 6501 | } else if (tok >= TOK_UIDENT) { |
| 6322 | | − s = label_find(tok); |
| 6323 | | − /* put forward definition if needed */ |
| 6324 | | − if (!s) { |
| 6325 | | − s = label_push(&global_label_stack, tok, LABEL_FORWARD); |
| 6502 | + s = label_find(tok); |
| 6503 | + if (!s || s->jnext == -1) { |
| 6504 | + /* put forward definition if needed */ |
| 6505 | + if (!s) |
| 6506 | + s = label_push(&global_label_stack, tok, LABEL_FORWARD); |
| 6507 | + if (cleanup_info) { |
| 6508 | + struct CleanupGoto *cur = |
| 6509 | + tcc_malloc(sizeof(struct CleanupGoto)); |
| 6510 | + |
| 6511 | + cur->s = s; |
| 6512 | + cur->is_valide = 1; |
| 6513 | + s->jnext = -1; |
| 6514 | + cur->jnext = 0; |
| 6515 | + cur->scope = scope_tracker; |
| 6516 | + cur->jnext = gjmp(cur->jnext); |
| 6517 | + dynarray_add(&cleanup_goto_info, &last_cleanup_goto, cur); |
| 6518 | + vla_sp_restore_root(); |
| 6519 | + goto out_goto; |
| 6520 | + } |
| 6326 | 6521 | } else { |
| 6522 | + if (cleanup_info && is_scope_a_parent_of(scope_tracker, |
| 6523 | + s->scope)) |
| 6524 | + try_call_cleanup_goto(s->scope); |
| 6525 | + |
| 6327 | 6526 | if (s->r == LABEL_DECLARED) |
| 6328 | 6527 | s->r = LABEL_FORWARD; |
| 6329 | 6528 | } |
| 6330 | | − vla_sp_restore_root(); |
| 6529 | + |
| 6530 | + vla_sp_restore_root(); |
| 6331 | 6531 | if (s->r & LABEL_FORWARD) |
| 6332 | | − s->jnext = gjmp(s->jnext); |
| 6333 | | − else |
| 6334 | | − gjmp_addr(s->jnext); |
| 6335 | | − next(); |
| 6336 | | − } else { |
| 6532 | + s->jnext = gjmp(s->jnext); |
| 6533 | + else |
| 6534 | + gjmp_addr(s->jnext); |
| 6535 | + out_goto: |
| 6536 | + next(); |
| 6537 | + } else { |
| 6337 | 6538 | expect("label identifier"); |
| 6338 | 6539 | } |
| 6339 | 6540 | skip(';'); |
| @@ -6346,10 +6547,21 @@ static void block(int *bsym, int *csym, int is_expr) · 6 unmodified lines |
| 6346 | 6547 | next(); |
| 6347 | 6548 | s = label_find(b); |
| 6348 | 6549 | if (s) { |
| 6550 | + int is_gen = 0; |
| 6551 | + |
| 6349 | 6552 | if (s->r == LABEL_DEFINED) |
| 6350 | 6553 | tcc_error("duplicate label '%s'", get_tok_str(s->v, NULL)); |
| 6351 | | − gsym(s->jnext); |
| 6554 | + for (int i = 0; i < last_cleanup_goto; ++i) { |
| 6555 | + struct CleanupGoto *cur = cleanup_goto_info[i]; |
| 6556 | + if (cur->s == s) { |
| 6557 | + cur->is_valide = 0; |
| 6558 | + gsym(cur->jnext); |
| 6559 | + is_gen = 1; |
| 6560 | + } |
| 6561 | + } |
| 6352 | 6562 | s->r = LABEL_DEFINED; |
| 6563 | + if (!is_gen) |
| 6564 | + gsym(s->jnext); |
| 6353 | 6565 | } else { |
| 6354 | 6566 | s = label_push(&global_label_stack, b, LABEL_DEFINED); |
| 6355 | 6567 | } |
| @@ -7085,6 +7297,28 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, · 729 unmodified lines |
| 7085 | 7297 | } |
| 7086 | 7298 | #endif |
| 7087 | 7299 | sym = sym_push(v, type, r, addr); |
| 7300 | + if (ad->cleanup_func) { |
| 7301 | + int nb_cleanup; |
| 7302 | + |
| 7303 | + if (!cleanup_info || cleanup_info->scope != scope_tracker) { |
| 7304 | + cleanup_info = tcc_malloc(sizeof(struct cleanup)); |
| 7305 | + dynarray_add(&cleanup_info_store, &cleanup_idx, |
| 7306 | + cleanup_info); |
| 7307 | + cleanup_info ->scope = scope_tracker; |
| 7308 | + cleanup_info->nb_cleanup = 0; |
| 7309 | + cleanup_info->syms = NULL; |
| 7310 | + } |
| 7311 | + nb_cleanup = cleanup_info->nb_cleanup + 1; |
| 7312 | + cleanup_info->syms = tcc_realloc(cleanup_info->syms, |
| 7313 | + nb_cleanup * |
| 7314 | + sizeof(*cleanup_info->syms)); |
| 7315 | + cleanup_info->syms[nb_cleanup - 1].func = ad->cleanup_func; |
| 7316 | + cleanup_info->syms[nb_cleanup - 1].var = sym; |
| 7317 | + cleanup_info->nb_cleanup = nb_cleanup; |
| 7318 | + if (!ad->should_remember) |
| 7319 | + ad->cleanup_func = NULL; |
| 7320 | + } |
| 7321 | + |
| 7088 | 7322 | sym->a = ad->a; |
| 7089 | 7323 | } else { |
| 7090 | 7324 | /* push local reference */ |
| @@ -7226,7 +7460,7 @@ static void gen_function(Sym *sym) · 135 unmodified lines |
| 7226 | 7460 | sym_push2(&local_stack, SYM_FIELD, 0, 0); |
| 7227 | 7461 | local_scope = 1; /* for function parameters */ |
| 7228 | 7462 | gfunc_prolog(&sym->type); |
| 7229 | | − local_scope = 0; |
| 7463 | + reset_local_scope(); |
| 7230 | 7464 | rsym = 0; |
| 7231 | 7465 | clear_temp_local_var_list(); |
| 7232 | 7466 | block(NULL, NULL, 0); |
| @@ -7245,7 +7479,7 @@ static void gen_function(Sym *sym) · 12 unmodified lines |
| 7245 | 7479 | cur_text_section->data_offset = ind; |
| 7246 | 7480 | label_pop(&global_label_stack, NULL, 0); |
| 7247 | 7481 | /* reset local stack */ |
| 7248 | | − local_scope = 0; |
| 7482 | + reset_local_scope(); |
| 7249 | 7483 | sym_pop(&local_stack, NULL, 0); |
| 7250 | 7484 | /* end of function */ |
| 7251 | 7485 | /* patch symbol size */ |