[tree-ssa] Nonlocal gotos and computed gotos

law@redhat.com law@redhat.com
Wed Jan 22 22:26:00 GMT 2003



This patch adds initial support for nonlocal gotos and computed gotos
when building the CFG at the tree level.  It fixes a few testsuite
failures on the tree-ssa branch and generates a much more accurate
CFG in the presence of these constructs.

It doesn't yet handle forced labels which appear in static initializers
and it doesn't yet support exception handling, but that's the ultimate
goal of this work :-)

Bootstraps tree-ssa, no regressions, fixes some c-torture failures.

	* gimplify.c (simplify_expr, case GOTO_EXPR): Identify and mark
	labels which are targets of nonlocal gotos and mark functions which
	have labels which are targets of nonlocal gotos.
	(simplify_expr, case LABEL_DECL): New case.  Mark labels which
	have their address taken.
	* tree-cfg.c (is_nonlocal_label_block): Remove.  All callers
	updated.
	(make_exit_edges, case GOTO_EXPR): Handle computed gotos sanely.
	(make_exit_edges, case CALL_EXPR): Handle abnormal edges from
	nonlocal gotos at call sites.
	(make_exit_edges, case RETURN_EXPR): Likewise.
	(make_exit_edges, case MODIFY_EXPR): New case to handle abnormal
	edges from nonlocal gotos as call sites.
	(make_goto_expr_edges): Handle computed gotos and nonlocal gotos.
	(is_ctrl_altering_stmt): Handle abnormal edges in CALL_EXPRs
	functions which receive nonlocal gotos.  Similarly for CALL_EXPRs
	which occur on the RHS of a MODIFY_EXPR.
	* tree.h (FORCED_LABEL, NONLOCAL_LABEL): New defines.
	(FUNCTION_RECEIVES_NONLOCAL_GOTO): Likewise.
	
Index: gimplify.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/Attic/gimplify.c,v
retrieving revision 1.1.2.19
diff -c -3 -p -r1.1.2.19 gimplify.c
*** gimplify.c	15 Jan 2003 19:41:44 -0000	1.1.2.19
--- gimplify.c	22 Jan 2003 22:05:40 -0000
*************** simplify_expr (expr_p, pre_p, post_p, si
*** 442,451 ****
  	  break;
  
  	case GOTO_EXPR:
! 	  if (TREE_CODE (GOTO_DESTINATION (*expr_p)) != LABEL_DECL)
! 	    simplify_expr (&GOTO_DESTINATION (*expr_p), pre_p, NULL,
! 			   is_simple_val, fb_rvalue);
! 	  break;
  
  	case LABEL_EXPR:
  	case CASE_LABEL_EXPR:
--- 442,470 ----
  	  break;
  
  	case GOTO_EXPR:
! 	  {
! 	    tree dest = GOTO_DESTINATION (*expr_p);
! 
! 	    /* If this label is in a different context (function), then
! 	       mark it as a nonlocal label and mark its context as
! 	       receiving nonlocal gotos.  */
! 	    if (TREE_CODE (dest) == LABEL_DECL
! 		&& current_function_decl != decl_function_context (dest))
! 	      {
! 		tree context = decl_function_context (dest);
! 
! 		NONLOCAL_LABEL (dest) = 1;
! 		FUNCTION_RECEIVES_NONLOCAL_GOTO (context) = 1;
! 	      }
! 	
! 	    /* If the target is not LABEL, then it is a computed jump
! 	       and the target needs to be simplified.  */
! 	    if (TREE_CODE (GOTO_DESTINATION (*expr_p)) != LABEL_DECL)
! 	      simplify_expr (&GOTO_DESTINATION (*expr_p), pre_p, NULL,
! 			     is_simple_val, fb_rvalue);
! 
! 	    break;
! 	  }
  
  	case LABEL_EXPR:
  	case CASE_LABEL_EXPR:
*************** simplify_expr (expr_p, pre_p, post_p, si
*** 523,531 ****
  	  simplify_minimax_expr (expr_p, pre_p);
  	  break; */
  
  	  /* If *EXPR_P does not need to be special-cased, handle it
  	     according to its class.  */
- 	default:
  	  {
  	    if (TREE_CODE_CLASS (TREE_CODE (*expr_p)) == '1')
  	      {
--- 542,557 ----
  	  simplify_minimax_expr (expr_p, pre_p);
  	  break; */
  
+ 	case LABEL_DECL:
+ 	  /* We get here when taking the address of a label.  We mark
+ 	     the label as "forced"; meaning it can never be removed and
+ 	     it is a potential target for any computed goto.  */
+ 	  FORCED_LABEL (*expr_p) = 1;
+ 	  break;
+ 
+ 	default:
  	  /* If *EXPR_P does not need to be special-cased, handle it
  	     according to its class.  */
  	  {
  	    if (TREE_CODE_CLASS (TREE_CODE (*expr_p)) == '1')
  	      {
Index: tree-cfg.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/Attic/tree-cfg.c,v
retrieving revision 1.1.4.44
diff -c -3 -p -r1.1.4.44 tree-cfg.c
*** tree-cfg.c	21 Jan 2003 17:16:30 -0000	1.1.4.44
--- tree-cfg.c	22 Jan 2003 22:05:49 -0000
*************** static bool blocks_unreachable_p	PARAMS 
*** 91,97 ****
  static void remove_blocks		PARAMS ((varray_type));
  static varray_type find_subblocks	PARAMS ((basic_block));
  static bool is_parent			PARAMS ((basic_block, basic_block));
- static bool is_nonlocal_label_block	PARAMS ((basic_block));
  static void cleanup_control_flow	PARAMS ((void));
  static void cleanup_cond_expr_graph	PARAMS ((basic_block));
  static void cleanup_switch_expr_graph	PARAMS ((basic_block));
--- 91,96 ----
*************** make_exit_edges (bb)
*** 553,565 ****
--- 552,606 ----
      {
      case GOTO_EXPR:
        make_goto_expr_edges (bb);
+ 
+       /* If this is potentially a nonlocal goto, then this should also
+ 	 create an edge to the exit block.   */
+       if ((GOTO_DESTINATION (last) == LABEL_DECL
+ 	   && (decl_function_context (GOTO_DESTINATION (last))
+ 	       != current_function_decl))
+ 	  || (GOTO_DESTINATION (last) != LABEL_DECL
+ 	      && DECL_CONTEXT (current_function_decl)))
+ 	make_edge (bb, EXIT_BLOCK_PTR, EDGE_ABNORMAL);
        break;
  
        /* A CALL_EXPR node here means that the last statement of the block
  	 is a call to a non-returning function.  */
      case CALL_EXPR:
+       /* Some calls are known not to return, so we just need to make
+ 	 an edge from them to the exit block.  Note that we do not
+ 	 need to worry about nonlocal gotos for such calls.  */
+       if (call_expr_flags (last) & (ECF_NORETURN | ECF_LONGJMP))
+ 	make_edge (bb, EXIT_BLOCK_PTR, 0);
+       else if (FUNCTION_RECEIVES_NONLOCAL_GOTO (current_function_decl))
+ 	{
+ 	  make_goto_expr_edges (bb);
+           make_edge (bb, successor_block (bb), EDGE_FALLTHRU);
+ 	}
+       break;
+ 
      case RETURN_EXPR:
        make_edge (bb, EXIT_BLOCK_PTR, 0);
+ 
+       /* A RETURN_EXPR may contain a CALL_EXPR and the CALL_EXPR may
+ 	 have an abnormal edge.  Search the operand of the RETURN_EXPR
+ 	 for this case and create any required edges.  */
+       if (TREE_OPERAND (last, 0)
+ 	  && TREE_CODE (TREE_OPERAND (last, 0)) == MODIFY_EXPR
+ 	  && TREE_CODE (TREE_OPERAND (TREE_OPERAND (last, 0), 1)) == CALL_EXPR
+ 	  && FUNCTION_RECEIVES_NONLOCAL_GOTO (current_function_decl))
+ 	make_goto_expr_edges (bb);
+       break;
+ 
+     case MODIFY_EXPR:
+       /* A MODIFY_EXPR may have a CALL_EXPR on its RHS and the CALL_EXPR
+ 	 may have an abnormal edge.  Search the RHS for this case and
+ 	 create any required edges.  */
+       if (TREE_CODE (TREE_OPERAND (last, 1)) == CALL_EXPR
+ 	  && FUNCTION_RECEIVES_NONLOCAL_GOTO (current_function_decl))
+ 	{
+ 	  make_goto_expr_edges (bb);
+           make_edge (bb, successor_block (bb), EDGE_FALLTHRU);
+ 	}
        break;
  
      default:
*************** make_cond_expr_edges (bb)
*** 658,664 ****
  }
  
  
! /** @brief Create edges for a goto statement.
      @param bb is the basic block to create edges for.  */
  
  static void
--- 699,706 ----
  }
  
  
! /** @brief Create edges for a goto statement or for a nonlocal goto due
!     to a CALL_EXPR.
      @param bb is the basic block to create edges for.  */
  
  static void
*************** make_goto_expr_edges (bb)
*** 667,681 ****
  {
    tree goto_t, dest;
    basic_block target_bb;
  
    goto_t = last_stmt (bb);
  
! #if defined ENABLE_CHECKING
!   if (goto_t == NULL_TREE || TREE_CODE (goto_t) != GOTO_EXPR)
!     abort ();
! #endif
! 
!   dest = GOTO_DESTINATION (goto_t);
  
    /* Look for the block starting with the destination label.  In the
       case of a computed goto, make an edge to any label block we find
--- 709,738 ----
  {
    tree goto_t, dest;
    basic_block target_bb;
+   int edge_flags;
+   int for_call;
  
    goto_t = last_stmt (bb);
  
!   /* If the last statement is not a GOTO (ie, it is a RETURN_EXPR, CALL_EXPR
!      or MODIFY_EXPR, then the edge is an abnormal edge resulting from
!      a nonlocal goto.  */
!   if (TREE_CODE (goto_t) != GOTO_EXPR)
!     {
!       dest = error_mark_node;
!       for_call = 1;
!       edge_flags = EDGE_ABNORMAL;
!     }
!   else
!     {
!       dest = GOTO_DESTINATION (goto_t);
!       for_call = 0;
!       /* The RTL CFG code got this wrong.  A computed goto creates
! 	 normal edges, except for one case.  Namely a computed goto
! 	 in a nested function has an abnormal edge to the exit block
! 	 (which is handled elsewhere).  */
!       edge_flags = 0;
!     }
  
    /* Look for the block starting with the destination label.  In the
       case of a computed goto, make an edge to any label block we find
*************** make_goto_expr_edges (bb)
*** 693,715 ****
  	  && TREE_CODE (target) == LABEL_EXPR
  	  && LABEL_EXPR_LABEL (target) == dest)
  	{
! 	  make_edge (bb, target_bb, 0);
! 
! 	  /* FIXME.  This is stupid and unnecessary.  We should find
! 	     out which labels can be the target of a nonlocal goto and make
! 	     the abnormal edge from the blocks that call nested functions
! 	     or setjmp.   */
! 	  if (is_nonlocal_label_block (target_bb))
! 	    make_edge (BASIC_BLOCK (0), target_bb, EDGE_ABNORMAL);
  	  break;
  	}
  
!       /* Computed GOTOs.  Make an edge to every label block.  FIXME  it
! 	 should be possible to trim down the number of labels we make the
! 	 edges to.  See cfgbuild.c.  */
        else if (TREE_CODE (dest) != LABEL_DECL
! 	       && TREE_CODE (target) == LABEL_EXPR)
! 	make_edge (bb, target_bb, EDGE_ABNORMAL);
      }
  }
  
--- 750,774 ----
  	  && TREE_CODE (target) == LABEL_EXPR
  	  && LABEL_EXPR_LABEL (target) == dest)
  	{
! 	  make_edge (bb, target_bb, edge_flags);
  	  break;
  	}
  
!       /* Computed GOTOs.  Make an edge to every label block that has
! 	 been marked as a potential target for a computed goto.  */
        else if (TREE_CODE (dest) != LABEL_DECL
! 	       && TREE_CODE (target) == LABEL_EXPR
! 	       && FORCED_LABEL (target)
! 	       && for_call == 0)
! 	make_edge (bb, target_bb, edge_flags);
! 
!       /* Nonlocal GOTO target.  Make an edge to every label block that has
! 	 been marked as a potential target for a nonlocal goto.  */
!       else if (TREE_CODE (dest) != LABEL_DECL
! 	       && TREE_CODE (target) == LABEL_EXPR
! 	       && NONLOCAL_LABEL (target)
! 	       && for_call == 1)
! 	make_edge (bb, target_bb, edge_flags);
      }
  }
  
*************** remove_unreachable_blocks ()
*** 782,794 ****
  	      if (blocks_unreachable_p (subblocks))
  		{
  		  remove_blocks (subblocks);
! 		  remove_tree_bb (bb, !is_nonlocal_label_block (bb));
  		}
  	      else
  		remove_tree_bb (bb, 0);
  	    }
  	  else
! 	    remove_tree_bb (bb, !is_nonlocal_label_block (bb));
  	}
      }
  }
--- 841,853 ----
  	      if (blocks_unreachable_p (subblocks))
  		{
  		  remove_blocks (subblocks);
! 		  remove_tree_bb (bb, 1);
  		}
  	      else
  		remove_tree_bb (bb, 0);
  	    }
  	  else
! 	    remove_tree_bb (bb, 1);
  	}
      }
  }
*************** remove_blocks (bb_array)
*** 858,864 ****
    for (i = 0; i < VARRAY_ACTIVE_SIZE (bb_array); i++)
      {
        basic_block bb = VARRAY_BB (bb_array, i);
!       remove_tree_bb (bb, !is_nonlocal_label_block (bb));
      }
  }
  
--- 917,923 ----
    for (i = 0; i < VARRAY_ACTIVE_SIZE (bb_array); i++)
      {
        basic_block bb = VARRAY_BB (bb_array, i);
!       remove_tree_bb (bb, 1);
      }
  }
  
*************** blocks_unreachable_p (bb_array)
*** 874,880 ****
    for (i = 0; i < VARRAY_ACTIVE_SIZE (bb_array); i++)
      {
        basic_block bb = VARRAY_BB (bb_array, i);
!       if (bb->flags & BB_REACHABLE || is_nonlocal_label_block (bb))
  	return false;
      }
  
--- 933,939 ----
    for (i = 0; i < VARRAY_ACTIVE_SIZE (bb_array); i++)
      {
        basic_block bb = VARRAY_BB (bb_array, i);
!       if (bb->flags & BB_REACHABLE)
  	return false;
      }
  
*************** blocks_unreachable_p (bb_array)
*** 882,905 ****
  }
  
  
- /** @brief Return true if block BB starts with a nonlocal label.  */
- 
- static bool
- is_nonlocal_label_block (bb)
-      basic_block bb;
- {
-   tree t = first_stmt (bb);
- 
-   if (t == NULL_TREE)
-     return false;
- 
-   /* FIXME  We don't compute nonlocal labels until RTL expansion.  This
-      will always return true.  This will likely break programs with nested
-      functions and nonlocal gotos.  */
-   return (TREE_CODE (t) == LABEL_EXPR && DECL_NONLOCAL (LABEL_EXPR_LABEL 
(t)));
- }
- 
- 
  /** @brief Find all the blocks in the graph that are included in
  	   the compound structure starting at block BB.  */
  
--- 941,946 ----
*************** is_ctrl_altering_stmt (t)
*** 1700,1712 ****
      abort ();
  #endif
  
    if (TREE_CODE (t) == GOTO_EXPR || TREE_CODE (t) == RETURN_EXPR)
      return true;
  
!   /* Special calls that may not return.  */
    if (TREE_CODE (t) == CALL_EXPR)
      return call_expr_flags (t) & (ECF_NORETURN | ECF_LONGJMP);
  
    return false;
  }
  
--- 1741,1767 ----
      abort ();
  #endif
  
+   /* GOTO_EXPRs and RETURN_EXPRs always alter flow control.  */
    if (TREE_CODE (t) == GOTO_EXPR || TREE_CODE (t) == RETURN_EXPR)
      return true;
  
!   /* A CALL_EXPR alters flow control if the current function has
!      nonlocal labels.  */
!   if (TREE_CODE (t) == CALL_EXPR
!       && FUNCTION_RECEIVES_NONLOCAL_GOTO (current_function_decl))
!     return true;
! 
!   /* A CALL_EXPR also alters flow control if it does not return.  */
    if (TREE_CODE (t) == CALL_EXPR)
      return call_expr_flags (t) & (ECF_NORETURN | ECF_LONGJMP);
  
+   /* A MODIFY_EXPR may contain a CALL_EXPR, which in turn may have
+      an abnormal edge if the current function has nonlocal labels.  */
+   if (TREE_CODE (t) == MODIFY_EXPR
+       && TREE_CODE (TREE_OPERAND (t, 1)) == CALL_EXPR
+       && FUNCTION_RECEIVES_NONLOCAL_GOTO (current_function_decl))
+     return true;
+   
    return false;
  }
  
Index: tree.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/tree.h,v
retrieving revision 1.342.2.39
diff -c -3 -p -r1.342.2.39 tree.h
*** tree.h	17 Jan 2003 19:52:53 -0000	1.342.2.39
--- tree.h	22 Jan 2003 22:06:03 -0000
*************** struct tree_common GTY(())
*** 224,229 ****
--- 224,231 ----
  
         TREE_SIDE_EFFECTS in
             all expressions
+        FORCED_LABEL in
+ 	   LABEL_DECL
  
     volatile_flag:
  
*************** struct tree_common GTY(())
*** 238,243 ****
--- 240,249 ----
             all expressions
         TYPE_READONLY in
             ..._TYPE
+        FUNCTION_RECEIVES_NONLOCAL_GOTO
+ 	   FUNCTION_DECL
+        NONLOCAL_LABEL in
+ 	   LABEL_DECL
  
     constant_flag:
  
*************** extern void tree_vec_elt_check_failed PA
*** 595,600 ****
--- 601,611 ----
     In a ..._DECL, this is set only if the declaration said `volatile'.  */
  #define TREE_SIDE_EFFECTS(NODE) ((NODE)->common.side_effects_flag)
  
+ /* In a LABEL_DECL, nonzero means this label had its address taken
+    and therefore can never be deleted and is a jump target for
+    computed gotos.  */
+ #define FORCED_LABEL(NODE) ((NODE)->common.side_effects_flag)
+ 
  /* Nonzero means this expression is volatile in the C sense:
     its address should be of type `volatile WHATEVER *'.
     In other words, the declared item is volatile qualified.
*************** extern void tree_vec_elt_check_failed PA
*** 613,618 ****
--- 624,635 ----
     (but the macro TYPE_READONLY should be used instead of this macro
     when the node is a type).  */
  #define TREE_READONLY(NODE) ((NODE)->common.readonly_flag)
+ 
+ #define FUNCTION_RECEIVES_NONLOCAL_GOTO(NODE) ((NODE)->common.readonly_flag)
+ 
+ /* In a LABEL_DECL, nonzero means this label is a jump target for
+    a nonlocal goto.  */
+ #define NONLOCAL_LABEL(NODE) ((NODE)->common.readonly_flag)
  
  /* Nonzero if NODE is a _DECL with TREE_READONLY set.  */
  #define TREE_READONLY_DECL_P(NODE) (TREE_READONLY (NODE) && DECL_P (NODE))




More information about the Gcc-patches mailing list