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;
 | |
| }
 |