See adjusted testcase, a lone break; in a do while loop was incorrectly disabling the exit test.
		
			
				
	
	
		
			155 lines
		
	
	
	
		
			3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			155 lines
		
	
	
	
		
			3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* 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 havoc 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);
 | 
						|
}
 | 
						|
 | 
						|
static int global;
 | 
						|
 | 
						|
static void foo(int i)
 | 
						|
{
 | 
						|
  global+=i;
 | 
						|
  printf ("g=%d\n", global);
 | 
						|
}
 | 
						|
 | 
						|
static int check(void)
 | 
						|
{
 | 
						|
  printf ("check %d\n", global);
 | 
						|
  return 1;
 | 
						|
}
 | 
						|
 | 
						|
static void dowhile(void)
 | 
						|
{
 | 
						|
  do {
 | 
						|
      foo(1);
 | 
						|
      if (global == 1) {
 | 
						|
	  continue;
 | 
						|
      } else if (global == 2) {
 | 
						|
	  continue;
 | 
						|
      }
 | 
						|
      /* The following break shouldn't disable the check() call,
 | 
						|
         as it's reachable by the continues above.  */
 | 
						|
      break;
 | 
						|
  } while (check());
 | 
						|
}
 | 
						|
 | 
						|
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");
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  dowhile();
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 |