aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Matz <matz@suse.de>2016-09-19 18:38:12 +0200
committerMichael Matz <matz@suse.de>2016-12-15 17:47:12 +0100
commit31c7ea0165882eaf09040ea7edfc37cc38f0a032 (patch)
treed1dad07537700b756d612c68b414d243202a9529
parentb303a00ce01876dfa2ba3fe532db04d200168b9d (diff)
downloadtinycc-31c7ea0165882eaf09040ea7edfc37cc38f0a032.tar.gz
tinycc-31c7ea0165882eaf09040ea7edfc37cc38f0a032.tar.bz2
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.
-rw-r--r--tccgen.c17
-rw-r--r--tests/tcctest.c27
-rw-r--r--tests/tests2/87_dead_code.c122
-rw-r--r--tests/tests2/87_dead_code.expect18
4 files changed, 182 insertions, 2 deletions
diff --git a/tccgen.c b/tccgen.c
index a811d11..8b1bcb1 100644
--- a/tccgen.c
+++ b/tccgen.c
@@ -5367,7 +5367,7 @@ static int gcase(struct case_t **base, int len, int case_reg, int *bsym)
static void block(int *bsym, int *csym, int is_expr)
{
- int a, b, c, d;
+ int a, b, c, d, cond;
Sym *s;
/* generate line number info */
@@ -5386,22 +5386,31 @@ static void block(int *bsym, int *csym, int is_expr)
if (tok == TOK_IF) {
/* if test */
+ int saved_nocode_wanted = nocode_wanted;
next();
skip('(');
gexpr();
skip(')');
- a = gvtst(1, 0);
+ cond = condition_3way();
+ if (cond == 0)
+ nocode_wanted |= 2;
+ a = gvtst(1, 0);
block(bsym, csym, 0);
+ nocode_wanted = saved_nocode_wanted;
c = tok;
if (c == TOK_ELSE) {
next();
+ if (cond == 1)
+ nocode_wanted |= 2;
d = gjmp(0);
gsym(a);
block(bsym, csym, 0);
gsym(d); /* patch else jmp */
+ nocode_wanted = saved_nocode_wanted;
} else
gsym(a);
} else if (tok == TOK_WHILE) {
+ nocode_wanted &= ~2;
next();
d = ind;
vla_sp_restore();
@@ -5564,6 +5573,7 @@ static void block(int *bsym, int *csym, int is_expr)
skip(';');
} else if (tok == TOK_FOR) {
int e;
+ nocode_wanted &= ~2;
next();
skip('(');
s = local_stack;
@@ -5607,6 +5617,7 @@ static void block(int *bsym, int *csym, int is_expr)
} else
if (tok == TOK_DO) {
+ nocode_wanted &= ~2;
next();
a = 0;
b = 0;
@@ -5658,6 +5669,7 @@ static void block(int *bsym, int *csym, int is_expr)
struct case_t *cr = tcc_malloc(sizeof(struct case_t));
if (!cur_switch)
expect("switch");
+ nocode_wanted &= ~2;
next();
cr->v1 = cr->v2 = expr_const();
if (gnu_ext && tok == TOK_DOTS) {
@@ -5735,6 +5747,7 @@ static void block(int *bsym, int *csym, int is_expr)
vla_sp_restore();
/* we accept this, but it is a mistake */
block_after_label:
+ nocode_wanted &= ~2;
if (tok == '}') {
tcc_warning("deprecated use of label at end of compound statement");
} else {
diff --git a/tests/tcctest.c b/tests/tcctest.c
index d1f6c9b..8031af4 100644
--- a/tests/tcctest.c
+++ b/tests/tcctest.c
@@ -88,6 +88,7 @@ void struct_test();
void array_test();
void expr_ptr_test();
void bool_test();
+void optimize_out();
void expr2_test();
void constant_expr_test();
void expr_cmp_test();
@@ -695,6 +696,7 @@ int main(int argc, char **argv)
array_test();
expr_ptr_test();
bool_test();
+ optimize_out();
expr2_test();
constant_expr_test();
expr_cmp_test();
@@ -1165,6 +1167,31 @@ void bool_test()
printf ("bits = 0x%x\n", calc_vm_flags (0x1));
}
+extern int undefined_function(void);
+extern int defined_function(void);
+
+void optimize_out(void)
+{
+ int i = 0 ? undefined_function() : defined_function();
+ printf ("oo:%d\n", i);
+ int j = 1 ? defined_function() : undefined_function();
+ printf ("oo:%d\n", j);
+ if (0)
+ printf("oo:%d\n", undefined_function());
+ else
+ printf("oo:%d\n", defined_function());
+ if (1)
+ printf("oo:%d\n", defined_function());
+ else
+ printf("oo:%d\n", undefined_function());
+}
+
+int defined_function(void)
+{
+ static int i = 40;
+ return i++;
+}
+
/* GCC accepts that */
static int tab_reinit[];
static int tab_reinit[10];
diff --git a/tests/tests2/87_dead_code.c b/tests/tests2/87_dead_code.c
new file mode 100644
index 0000000..92983f5
--- /dev/null
+++ b/tests/tests2/87_dead_code.c
@@ -0,0 +1,122 @@
+/* This checks various ways of dead code inside if statements
+ where there are non-obvious ways of how the code is actually
+ not dead due to reachable by labels. */
+extern int printf (const char *, ...);
+static void kb_wait_1(void)
+{
+ unsigned long timeout = 2;
+ do {
+ /* Here the else arm is a statement expression that's supposed
+ to be suppressed. The label inside the while would unsuppress
+ code generation again if not handled correctly. And that
+ would wreak havok to the cond-expression because there's no
+ jump-around emitted, the whole statement expression really
+ needs to not generate code (perhaps except useless forward jumps). */
+ (1 ?
+ printf("timeout=%ld\n", timeout) :
+ ({
+ int i = 1;
+ while (1)
+ while (i--)
+ some_label:
+ printf("error\n");
+ goto some_label;
+ })
+ );
+ timeout--;
+ } while (timeout);
+}
+int main (void)
+{
+ int i = 1;
+ kb_wait_1();
+
+ /* Simple test of dead code at first sight which isn't actually dead. */
+ if (0) {
+yeah:
+ printf ("yeah\n");
+ } else {
+ printf ("boo\n");
+ }
+ if (i--)
+ goto yeah;
+
+ /* Some more non-obvious uses where the problems are loops, so that even
+ the first loop statements aren't actually dead. */
+ i = 1;
+ if (0) {
+ while (i--) {
+ printf ("once\n");
+enterloop:
+ printf ("twice\n");
+ }
+ }
+ if (i >= 0)
+ goto enterloop;
+
+ /* The same with statement expressions. One might be tempted to
+ handle them specially by counting if inside statement exprs and
+ not unsuppressing code at loops at all then.
+ See kb_wait_1 for the other side of the medal where that wouldn't work. */
+ i = ({
+ int j = 1;
+ if (0) {
+ while (j--) {
+ printf ("SEonce\n");
+ enterexprloop:
+ printf ("SEtwice\n");
+ }
+ }
+ if (j >= 0)
+ goto enterexprloop;
+ j; });
+
+ /* The other two loop forms: */
+ i = 1;
+ if (0) {
+ for (i = 1; i--;) {
+ printf ("once2\n");
+enterloop2:
+ printf ("twice2\n");
+ }
+ }
+ if (i > 0)
+ goto enterloop2;
+
+ i = 1;
+ if (0) {
+ do {
+ printf ("once3\n");
+enterloop3:
+ printf ("twice3\n");
+ } while (i--);
+ }
+ if (i > 0)
+ goto enterloop3;
+
+ /* And check that case and default labels have the same effect
+ of disabling code suppression. */
+ i = 41;
+ switch (i) {
+ if (0) {
+ printf ("error\n");
+ case 42:
+ printf ("error2\n");
+ case 41:
+ printf ("caseok\n");
+ }
+ }
+
+ i = 41;
+ switch (i) {
+ if (0) {
+ printf ("error3\n");
+ default:
+ printf ("caseok2\n");
+ break;
+ case 42:
+ printf ("error4\n");
+ }
+ }
+ return 0;
+}
diff --git a/tests/tests2/87_dead_code.expect b/tests/tests2/87_dead_code.expect
new file mode 100644
index 0000000..0b3ec1d
--- /dev/null
+++ b/tests/tests2/87_dead_code.expect
@@ -0,0 +1,18 @@
+timeout=2
+timeout=1
+boo
+yeah
+twice
+once
+twice
+SEtwice
+SEonce
+SEtwice
+twice2
+once2
+twice2
+twice3
+once3
+twice3
+caseok
+caseok2