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
				
			
		
							
								
								
									
										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…
	
	Add table
		
		Reference in a new issue