mirror of
https://github.com/mirror/tinycc.git
synced 2024-12-26 03:50:07 +08:00
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:
parent
b303a00ce0
commit
31c7ea0165
17
tccgen.c
17
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 {
|
||||
|
@ -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
122
tests/tests2/87_dead_code.c
Normal 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;
|
||||
}
|
18
tests/tests2/87_dead_code.expect
Normal file
18
tests/tests2/87_dead_code.expect
Normal 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
|
Loading…
Reference in New Issue
Block a user