History

opt: Start optimizing dead code a bit

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.

Michael Matz committed 10 years ago

Commit 31c7ea01 parent b303a004 files · +182 −2Browse files

Unified · Split

4 files
  1. tccgen.c +15 −2
  2. tests/tcctest.c +27 −0
  3. tests/tests2/87_dead_code.c +122 −0
  4. tests/tests2/87_dead_code.expect +18 −0
tccgen.c+15−2
@@ -5367,7 +5367,7 @@ static int gcase(struct case_t **base, int len, int case_reg, int *bsym) · 5366 unmodified lines
53675367
53685368 static void block(int *bsym, int *csym, int is_expr)
53695369 {
5370− int a, b, c, d;
5370+ int a, b, c, d, cond;
53715371 Sym *s;
53725372
53735373 /* generate line number info */
@@ -5386,22 +5386,31 @@ static void block(int *bsym, int *csym, int is_expr) · 12 unmodified lines
53865386
53875387 if (tok == TOK_IF) {
53885388 /* if test */
5389+ int saved_nocode_wanted = nocode_wanted;
53895390 next();
53905391 skip('(');
53915392 gexpr();
53925393 skip(')');
5393− a = gvtst(1, 0);
5394+ cond = condition_3way();
5395+ if (cond == 0)
5396+ nocode_wanted |= 2;
5397+ a = gvtst(1, 0);
53945398 block(bsym, csym, 0);
5399+ nocode_wanted = saved_nocode_wanted;
53955400 c = tok;
53965401 if (c == TOK_ELSE) {
53975402 next();
5403+ if (cond == 1)
5404+ nocode_wanted |= 2;
53985405 d = gjmp(0);
53995406 gsym(a);
54005407 block(bsym, csym, 0);
54015408 gsym(d); /* patch else jmp */
5409+ nocode_wanted = saved_nocode_wanted;
54025410 } else
54035411 gsym(a);
54045412 } else if (tok == TOK_WHILE) {
5413+ nocode_wanted &= ~2;
54055414 next();
54065415 d = ind;
54075416 vla_sp_restore();
@@ -5564,6 +5573,7 @@ static void block(int *bsym, int *csym, int is_expr) · 156 unmodified lines
55645573 skip(';');
55655574 } else if (tok == TOK_FOR) {
55665575 int e;
5576+ nocode_wanted &= ~2;
55675577 next();
55685578 skip('(');
55695579 s = local_stack;
@@ -5607,6 +5617,7 @@ static void block(int *bsym, int *csym, int is_expr) · 37 unmodified lines
56075617
56085618 } else
56095619 if (tok == TOK_DO) {
5620+ nocode_wanted &= ~2;
56105621 next();
56115622 a = 0;
56125623 b = 0;
@@ -5658,6 +5669,7 @@ static void block(int *bsym, int *csym, int is_expr) · 45 unmodified lines
56585669 struct case_t *cr = tcc_malloc(sizeof(struct case_t));
56595670 if (!cur_switch)
56605671 expect("switch");
5672+ nocode_wanted &= ~2;
56615673 next();
56625674 cr->v1 = cr->v2 = expr_const();
56635675 if (gnu_ext && tok == TOK_DOTS) {
@@ -5735,6 +5747,7 @@ static void block(int *bsym, int *csym, int is_expr) · 71 unmodified lines
57355747 vla_sp_restore();
57365748 /* we accept this, but it is a mistake */
57375749 block_after_label:
5750+ nocode_wanted &= ~2;
57385751 if (tok == '}') {
57395752 tcc_warning("deprecated use of label at end of compound statement");
57405753 } else {
tests/tcctest.c+27−0
@@ -88,6 +88,7 @@ void struct_test(); · 87 unmodified lines
8888 void array_test();
8989 void expr_ptr_test();
9090 void bool_test();
91+void optimize_out();
9192 void expr2_test();
9293 void constant_expr_test();
9394 void expr_cmp_test();
@@ -695,6 +696,7 @@ int main(int argc, char **argv) · 601 unmodified lines
695696 array_test();
696697 expr_ptr_test();
697698 bool_test();
699+ optimize_out();
698700 expr2_test();
699701 constant_expr_test();
700702 expr_cmp_test();
@@ -1165,6 +1167,31 @@ void bool_test() · 464 unmodified lines
11651167 printf ("bits = 0x%x\n", calc_vm_flags (0x1));
11661168 }
11671169
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+
11681195 /* GCC accepts that */
11691196 static int tab_reinit[];
11701197 static int tab_reinit[10];
tests/tests2/87_dead_code.c+122−0
@@ -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+}
tests/tests2/87_dead_code.expect+18−0
@@ -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