forked from Mirrors/tinycc
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.master
parent
b303a00ce0
commit
31c7ea0165
15
tccgen.c
15
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)
|
static void block(int *bsym, int *csym, int is_expr)
|
||||||
{
|
{
|
||||||
int a, b, c, d;
|
int a, b, c, d, cond;
|
||||||
Sym *s;
|
Sym *s;
|
||||||
|
|
||||||
/* generate line number info */
|
/* generate line number info */
|
||||||
|
@ -5386,22 +5386,31 @@ static void block(int *bsym, int *csym, int is_expr)
|
||||||
|
|
||||||
if (tok == TOK_IF) {
|
if (tok == TOK_IF) {
|
||||||
/* if test */
|
/* if test */
|
||||||
|
int saved_nocode_wanted = nocode_wanted;
|
||||||
next();
|
next();
|
||||||
skip('(');
|
skip('(');
|
||||||
gexpr();
|
gexpr();
|
||||||
skip(')');
|
skip(')');
|
||||||
|
cond = condition_3way();
|
||||||
|
if (cond == 0)
|
||||||
|
nocode_wanted |= 2;
|
||||||
a = gvtst(1, 0);
|
a = gvtst(1, 0);
|
||||||
block(bsym, csym, 0);
|
block(bsym, csym, 0);
|
||||||
|
nocode_wanted = saved_nocode_wanted;
|
||||||
c = tok;
|
c = tok;
|
||||||
if (c == TOK_ELSE) {
|
if (c == TOK_ELSE) {
|
||||||
next();
|
next();
|
||||||
|
if (cond == 1)
|
||||||
|
nocode_wanted |= 2;
|
||||||
d = gjmp(0);
|
d = gjmp(0);
|
||||||
gsym(a);
|
gsym(a);
|
||||||
block(bsym, csym, 0);
|
block(bsym, csym, 0);
|
||||||
gsym(d); /* patch else jmp */
|
gsym(d); /* patch else jmp */
|
||||||
|
nocode_wanted = saved_nocode_wanted;
|
||||||
} else
|
} else
|
||||||
gsym(a);
|
gsym(a);
|
||||||
} else if (tok == TOK_WHILE) {
|
} else if (tok == TOK_WHILE) {
|
||||||
|
nocode_wanted &= ~2;
|
||||||
next();
|
next();
|
||||||
d = ind;
|
d = ind;
|
||||||
vla_sp_restore();
|
vla_sp_restore();
|
||||||
|
@ -5564,6 +5573,7 @@ static void block(int *bsym, int *csym, int is_expr)
|
||||||
skip(';');
|
skip(';');
|
||||||
} else if (tok == TOK_FOR) {
|
} else if (tok == TOK_FOR) {
|
||||||
int e;
|
int e;
|
||||||
|
nocode_wanted &= ~2;
|
||||||
next();
|
next();
|
||||||
skip('(');
|
skip('(');
|
||||||
s = local_stack;
|
s = local_stack;
|
||||||
|
@ -5607,6 +5617,7 @@ static void block(int *bsym, int *csym, int is_expr)
|
||||||
|
|
||||||
} else
|
} else
|
||||||
if (tok == TOK_DO) {
|
if (tok == TOK_DO) {
|
||||||
|
nocode_wanted &= ~2;
|
||||||
next();
|
next();
|
||||||
a = 0;
|
a = 0;
|
||||||
b = 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));
|
struct case_t *cr = tcc_malloc(sizeof(struct case_t));
|
||||||
if (!cur_switch)
|
if (!cur_switch)
|
||||||
expect("switch");
|
expect("switch");
|
||||||
|
nocode_wanted &= ~2;
|
||||||
next();
|
next();
|
||||||
cr->v1 = cr->v2 = expr_const();
|
cr->v1 = cr->v2 = expr_const();
|
||||||
if (gnu_ext && tok == TOK_DOTS) {
|
if (gnu_ext && tok == TOK_DOTS) {
|
||||||
|
@ -5735,6 +5747,7 @@ static void block(int *bsym, int *csym, int is_expr)
|
||||||
vla_sp_restore();
|
vla_sp_restore();
|
||||||
/* we accept this, but it is a mistake */
|
/* we accept this, but it is a mistake */
|
||||||
block_after_label:
|
block_after_label:
|
||||||
|
nocode_wanted &= ~2;
|
||||||
if (tok == '}') {
|
if (tok == '}') {
|
||||||
tcc_warning("deprecated use of label at end of compound statement");
|
tcc_warning("deprecated use of label at end of compound statement");
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -88,6 +88,7 @@ void struct_test();
|
||||||
void array_test();
|
void array_test();
|
||||||
void expr_ptr_test();
|
void expr_ptr_test();
|
||||||
void bool_test();
|
void bool_test();
|
||||||
|
void optimize_out();
|
||||||
void expr2_test();
|
void expr2_test();
|
||||||
void constant_expr_test();
|
void constant_expr_test();
|
||||||
void expr_cmp_test();
|
void expr_cmp_test();
|
||||||
|
@ -695,6 +696,7 @@ int main(int argc, char **argv)
|
||||||
array_test();
|
array_test();
|
||||||
expr_ptr_test();
|
expr_ptr_test();
|
||||||
bool_test();
|
bool_test();
|
||||||
|
optimize_out();
|
||||||
expr2_test();
|
expr2_test();
|
||||||
constant_expr_test();
|
constant_expr_test();
|
||||||
expr_cmp_test();
|
expr_cmp_test();
|
||||||
|
@ -1165,6 +1167,31 @@ void bool_test()
|
||||||
printf ("bits = 0x%x\n", calc_vm_flags (0x1));
|
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 */
|
/* GCC accepts that */
|
||||||
static int tab_reinit[];
|
static int tab_reinit[];
|
||||||
static int tab_reinit[10];
|
static int tab_reinit[10];
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
timeout=2
|
||||||
|
timeout=1
|
||||||
|
boo
|
||||||
|
yeah
|
||||||
|
twice
|
||||||
|
once
|
||||||
|
twice
|
||||||
|
SEtwice
|
||||||
|
SEonce
|
||||||
|
SEtwice
|
||||||
|
twice2
|
||||||
|
once2
|
||||||
|
twice2
|
||||||
|
twice3
|
||||||
|
once3
|
||||||
|
twice3
|
||||||
|
caseok
|
||||||
|
caseok2
|
Loading…
Reference in New Issue