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.
This commit is contained in:
Michael Matz 2016-09-19 18:38:12 +02:00
parent b303a00ce0
commit 31c7ea0165
4 changed files with 182 additions and 2 deletions

View File

@ -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 {

View File

@ -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];

122
tests/tests2/87_dead_code.c Normal file
View File

@ -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;
}

View File

@ -0,0 +1,18 @@
timeout=2
timeout=1
boo
yeah
twice
once
twice
SEtwice
SEonce
SEtwice
twice2
once2
twice2
twice3
once3
twice3
caseok
caseok2