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
					
				
					 4 changed files with 182 additions and 2 deletions
				
			
		
							
								
								
									
										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];
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										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…
	
	Add table
		
		Reference in a new issue