If a condition is always zero/non-zero we can omit the then or else code. This is complicated a bit by having to deal with labels that might make such code reachable without us yet knowing during parsing.
Unified · Split
| @@ -5367,7 +5367,7 @@ static int gcase(struct case_t **base, int len, int case_reg, int *bsym) · 5366 unmodified lines | ||
| 5367 | 5367 | |
| 5368 | 5368 | static void block(int *bsym, int *csym, int is_expr) |
| 5369 | 5369 | { |
| 5370 | − int a, b, c, d; | |
| 5370 | + int a, b, c, d, cond; | |
| 5371 | 5371 | Sym *s; |
| 5372 | 5372 | |
| 5373 | 5373 | /* generate line number info */ |
| @@ -5386,22 +5386,31 @@ static void block(int *bsym, int *csym, int is_expr) · 12 unmodified lines | ||
| 5386 | 5386 | |
| 5387 | 5387 | if (tok == TOK_IF) { |
| 5388 | 5388 | /* if test */ |
| 5389 | + int saved_nocode_wanted = nocode_wanted; | |
| 5389 | 5390 | next(); |
| 5390 | 5391 | skip('('); |
| 5391 | 5392 | gexpr(); |
| 5392 | 5393 | skip(')'); |
| 5393 | − a = gvtst(1, 0); | |
| 5394 | + cond = condition_3way(); | |
| 5395 | + if (cond == 0) | |
| 5396 | + nocode_wanted |= 2; | |
| 5397 | + a = gvtst(1, 0); | |
| 5394 | 5398 | block(bsym, csym, 0); |
| 5399 | + nocode_wanted = saved_nocode_wanted; | |
| 5395 | 5400 | c = tok; |
| 5396 | 5401 | if (c == TOK_ELSE) { |
| 5397 | 5402 | next(); |
| 5403 | + if (cond == 1) | |
| 5404 | + nocode_wanted |= 2; | |
| 5398 | 5405 | d = gjmp(0); |
| 5399 | 5406 | gsym(a); |
| 5400 | 5407 | block(bsym, csym, 0); |
| 5401 | 5408 | gsym(d); /* patch else jmp */ |
| 5409 | + nocode_wanted = saved_nocode_wanted; | |
| 5402 | 5410 | } else |
| 5403 | 5411 | gsym(a); |
| 5404 | 5412 | } else if (tok == TOK_WHILE) { |
| 5413 | + nocode_wanted &= ~2; | |
| 5405 | 5414 | next(); |
| 5406 | 5415 | d = ind; |
| 5407 | 5416 | vla_sp_restore(); |
| @@ -5564,6 +5573,7 @@ static void block(int *bsym, int *csym, int is_expr) · 156 unmodified lines | ||
| 5564 | 5573 | skip(';'); |
| 5565 | 5574 | } else if (tok == TOK_FOR) { |
| 5566 | 5575 | int e; |
| 5576 | + nocode_wanted &= ~2; | |
| 5567 | 5577 | next(); |
| 5568 | 5578 | skip('('); |
| 5569 | 5579 | s = local_stack; |
| @@ -5607,6 +5617,7 @@ static void block(int *bsym, int *csym, int is_expr) · 37 unmodified lines | ||
| 5607 | 5617 | |
| 5608 | 5618 | } else |
| 5609 | 5619 | if (tok == TOK_DO) { |
| 5620 | + nocode_wanted &= ~2; | |
| 5610 | 5621 | next(); |
| 5611 | 5622 | a = 0; |
| 5612 | 5623 | b = 0; |
| @@ -5658,6 +5669,7 @@ static void block(int *bsym, int *csym, int is_expr) · 45 unmodified lines | ||
| 5658 | 5669 | struct case_t *cr = tcc_malloc(sizeof(struct case_t)); |
| 5659 | 5670 | if (!cur_switch) |
| 5660 | 5671 | expect("switch"); |
| 5672 | + nocode_wanted &= ~2; | |
| 5661 | 5673 | next(); |
| 5662 | 5674 | cr->v1 = cr->v2 = expr_const(); |
| 5663 | 5675 | if (gnu_ext && tok == TOK_DOTS) { |
| @@ -5735,6 +5747,7 @@ static void block(int *bsym, int *csym, int is_expr) · 71 unmodified lines | ||
| 5735 | 5747 | vla_sp_restore(); |
| 5736 | 5748 | /* we accept this, but it is a mistake */ |
| 5737 | 5749 | block_after_label: |
| 5750 | + nocode_wanted &= ~2; | |
| 5738 | 5751 | if (tok == '}') { |
| 5739 | 5752 | tcc_warning("deprecated use of label at end of compound statement"); |
| 5740 | 5753 | } else { |
| @@ -88,6 +88,7 @@ void struct_test(); · 87 unmodified lines | ||
| 88 | 88 | void array_test(); |
| 89 | 89 | void expr_ptr_test(); |
| 90 | 90 | void bool_test(); |
| 91 | +void optimize_out(); | |
| 91 | 92 | void expr2_test(); |
| 92 | 93 | void constant_expr_test(); |
| 93 | 94 | void expr_cmp_test(); |
| @@ -695,6 +696,7 @@ int main(int argc, char **argv) · 601 unmodified lines | ||
| 695 | 696 | array_test(); |
| 696 | 697 | expr_ptr_test(); |
| 697 | 698 | bool_test(); |
| 699 | + optimize_out(); | |
| 698 | 700 | expr2_test(); |
| 699 | 701 | constant_expr_test(); |
| 700 | 702 | expr_cmp_test(); |
| @@ -1165,6 +1167,31 @@ void bool_test() · 464 unmodified lines | ||
| 1165 | 1167 | printf ("bits = 0x%x\n", calc_vm_flags (0x1)); |
| 1166 | 1168 | } |
| 1167 | 1169 | |
| 1170 | +extern int undefined_function(void); | |
| 1171 | +extern int defined_function(void); | |
| 1172 | + | |
| 1173 | +void optimize_out(void) | |
| 1174 | +{ | |
| 1175 | + int i = 0 ? undefined_function() : defined_function(); | |
| 1176 | + printf ("oo:%d\n", i); | |
| 1177 | + int j = 1 ? defined_function() : undefined_function(); | |
| 1178 | + printf ("oo:%d\n", j); | |
| 1179 | + if (0) | |
| 1180 | + printf("oo:%d\n", undefined_function()); | |
| 1181 | + else | |
| 1182 | + printf("oo:%d\n", defined_function()); | |
| 1183 | + if (1) | |
| 1184 | + printf("oo:%d\n", defined_function()); | |
| 1185 | + else | |
| 1186 | + printf("oo:%d\n", undefined_function()); | |
| 1187 | +} | |
| 1188 | + | |
| 1189 | +int defined_function(void) | |
| 1190 | +{ | |
| 1191 | + static int i = 40; | |
| 1192 | + return i++; | |
| 1193 | +} | |
| 1194 | + | |
| 1168 | 1195 | /* GCC accepts that */ |
| 1169 | 1196 | static int tab_reinit[]; |
| 1170 | 1197 | static int tab_reinit[10]; |
| @@ -0,0 +1,122 @@ | ||
| 1 | +/* This checks various ways of dead code inside if statements | |
| 2 | + where there are non-obvious ways of how the code is actually | |
| 3 | + not dead due to reachable by labels. */ | |
| 4 | +extern int printf (const char *, ...); | |
| 5 | +static void kb_wait_1(void) | |
| 6 | +{ | |
| 7 | + unsigned long timeout = 2; | |
| 8 | + do { | |
| 9 | + /* Here the else arm is a statement expression that's supposed | |
| 10 | + to be suppressed. The label inside the while would unsuppress | |
| 11 | + code generation again if not handled correctly. And that | |
| 12 | + would wreak havok to the cond-expression because there's no | |
| 13 | + jump-around emitted, the whole statement expression really | |
| 14 | + needs to not generate code (perhaps except useless forward jumps). */ | |
| 15 | + (1 ? | |
| 16 | + printf("timeout=%ld\n", timeout) : | |
| 17 | + ({ | |
| 18 | + int i = 1; | |
| 19 | + while (1) | |
| 20 | + while (i--) | |
| 21 | + some_label: | |
| 22 | + printf("error\n"); | |
| 23 | + goto some_label; | |
| 24 | + }) | |
| 25 | + ); | |
| 26 | + timeout--; | |
| 27 | + } while (timeout); | |
| 28 | +} | |
| 29 | +int main (void) | |
| 30 | +{ | |
| 31 | + int i = 1; | |
| 32 | + kb_wait_1(); | |
| 33 | + | |
| 34 | + /* Simple test of dead code at first sight which isn't actually dead. */ | |
| 35 | + if (0) { | |
| 36 | +yeah: | |
| 37 | + printf ("yeah\n"); | |
| 38 | + } else { | |
| 39 | + printf ("boo\n"); | |
| 40 | + } | |
| 41 | + if (i--) | |
| 42 | + goto yeah; | |
| 43 | + | |
| 44 | + /* Some more non-obvious uses where the problems are loops, so that even | |
| 45 | + the first loop statements aren't actually dead. */ | |
| 46 | + i = 1; | |
| 47 | + if (0) { | |
| 48 | + while (i--) { | |
| 49 | + printf ("once\n"); | |
| 50 | +enterloop: | |
| 51 | + printf ("twice\n"); | |
| 52 | + } | |
| 53 | + } | |
| 54 | + if (i >= 0) | |
| 55 | + goto enterloop; | |
| 56 | + | |
| 57 | + /* The same with statement expressions. One might be tempted to | |
| 58 | + handle them specially by counting if inside statement exprs and | |
| 59 | + not unsuppressing code at loops at all then. | |
| 60 | + See kb_wait_1 for the other side of the medal where that wouldn't work. */ | |
| 61 | + i = ({ | |
| 62 | + int j = 1; | |
| 63 | + if (0) { | |
| 64 | + while (j--) { | |
| 65 | + printf ("SEonce\n"); | |
| 66 | + enterexprloop: | |
| 67 | + printf ("SEtwice\n"); | |
| 68 | + } | |
| 69 | + } | |
| 70 | + if (j >= 0) | |
| 71 | + goto enterexprloop; | |
| 72 | + j; }); | |
| 73 | + | |
| 74 | + /* The other two loop forms: */ | |
| 75 | + i = 1; | |
| 76 | + if (0) { | |
| 77 | + for (i = 1; i--;) { | |
| 78 | + printf ("once2\n"); | |
| 79 | +enterloop2: | |
| 80 | + printf ("twice2\n"); | |
| 81 | + } | |
| 82 | + } | |
| 83 | + if (i > 0) | |
| 84 | + goto enterloop2; | |
| 85 | + | |
| 86 | + i = 1; | |
| 87 | + if (0) { | |
| 88 | + do { | |
| 89 | + printf ("once3\n"); | |
| 90 | +enterloop3: | |
| 91 | + printf ("twice3\n"); | |
| 92 | + } while (i--); | |
| 93 | + } | |
| 94 | + if (i > 0) | |
| 95 | + goto enterloop3; | |
| 96 | + | |
| 97 | + /* And check that case and default labels have the same effect | |
| 98 | + of disabling code suppression. */ | |
| 99 | + i = 41; | |
| 100 | + switch (i) { | |
| 101 | + if (0) { | |
| 102 | + printf ("error\n"); | |
| 103 | + case 42: | |
| 104 | + printf ("error2\n"); | |
| 105 | + case 41: | |
| 106 | + printf ("caseok\n"); | |
| 107 | + } | |
| 108 | + } | |
| 109 | + | |
| 110 | + i = 41; | |
| 111 | + switch (i) { | |
| 112 | + if (0) { | |
| 113 | + printf ("error3\n"); | |
| 114 | + default: | |
| 115 | + printf ("caseok2\n"); | |
| 116 | + break; | |
| 117 | + case 42: | |
| 118 | + printf ("error4\n"); | |
| 119 | + } | |
| 120 | + } | |
| 121 | + return 0; | |
| 122 | +} | |
| @@ -0,0 +1,18 @@ | ||
| 1 | +timeout=2 | |
| 2 | +timeout=1 | |
| 3 | +boo | |
| 4 | +yeah | |
| 5 | +twice | |
| 6 | +once | |
| 7 | +twice | |
| 8 | +SEtwice | |
| 9 | +SEonce | |
| 10 | +SEtwice | |
| 11 | +twice2 | |
| 12 | +once2 | |
| 13 | +twice2 | |
| 14 | +twice3 | |
| 15 | +once3 | |
| 16 | +twice3 | |
| 17 | +caseok | |
| 18 | +caseok2 | |