cleanup eh stmts and edges

Richard Henderson rth@redhat.com
Tue Jun 29 11:17:00 GMT 2004


Constant propagation can prove that statements formerly thought to trap
actually don't.  Similarly CSE can consolidate trapping operations,
such that we only have one potentially trapping stmt instead of many.
In both cases, we get to remove lots of EH edges, which is good.

I had added the code to verify_stmts to check a hunch and found all
sorts of places that needed fixing.

Tested i686-linux.


r~


        * tree-cfg.c (verify_stmt): Add last_in_block parameter.  Verify
        that eh stmts can throw.
        (verify_stmts): Update verify_stmt call.
        (tree_purge_dead_eh_edges, tree_purge_all_dead_eh_edges): New.
        * tree-eh.c (remove_stmt_from_eh_region): New.
        (lower_eh_constructs): Fix throw_stmt_table delete routine.
        (tree_could_trap_p): Match may_trap_p.
        (maybe_clean_eh_stmt): New.
        * tree-flow.h: Update decls.
        * tree-ssa-ccp.c (pass_ccp): Add TODO_verify_stmts.
        (substitute_and_fold): Clean eh edges.
        * tree-ssa-dce.c (mark_control_dependent_edges_necessary): Handle
        empty basic blocks.
        * tree-ssa-dom.c (need_eh_cleanup): New.
        (tree_ssa_dominator_optimize): Allocate it.  Cleanup eh edges.
        (optimize_stmt): Cleanup eh stmts; set need_eh_cleanup.

Index: tree-cfg.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/tree-cfg.c,v
retrieving revision 2.21
diff -c -p -d -r2.21 tree-cfg.c
*** tree-cfg.c	26 Jun 2004 05:03:52 -0000	2.21
--- tree-cfg.c	29 Jun 2004 06:52:22 -0000
*************** verify_expr (tree *tp, int *walk_subtree
*** 3316,3330 ****
     TODO: Implement type checking.  */
  
  static bool
! verify_stmt (tree stmt)
  {
    tree addr;
  
    if (!is_gimple_stmt (stmt))
      {
        error ("Is not a valid GIMPLE statement.");
!       debug_generic_stmt (stmt);
!       return true;
      }
  
    addr = walk_tree (&stmt, verify_expr, NULL, NULL);
--- 3316,3329 ----
     TODO: Implement type checking.  */
  
  static bool
! verify_stmt (tree stmt, bool last_in_block)
  {
    tree addr;
  
    if (!is_gimple_stmt (stmt))
      {
        error ("Is not a valid GIMPLE statement.");
!       goto fail;
      }
  
    addr = walk_tree (&stmt, verify_expr, NULL, NULL);
*************** verify_stmt (tree stmt)
*** 3334,3340 ****
--- 3333,3362 ----
        return true;
      }
  
+   /* If the statement is marked as part of an EH region, then it is
+      expected that the statement could throw.  Verify that when we
+      have optimizations that simplify statements such that we prove
+      that they cannot throw, that we update other data structures
+      to match.  */
+   if (lookup_stmt_eh_region (stmt) >= 0)
+     {
+       if (!tree_could_throw_p (stmt))
+ 	{
+ 	  error ("Statement marked for throw, but doesn't.");
+ 	  goto fail;
+ 	}
+       if (!last_in_block && tree_can_throw_internal (stmt))
+ 	{
+ 	  error ("Statement marked for throw in middle of block.");
+ 	  goto fail;
+ 	}
+     }
+ 
    return false;
+ 
+  fail:
+   debug_generic_stmt (stmt);
+   return true;
  }
  
  
*************** verify_stmts (void)
*** 3449,3458 ****
  	    }
  	}
  
!       for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi))
  	{
  	  tree stmt = bsi_stmt (bsi);
! 	  err |= verify_stmt (stmt);
  	  addr = walk_tree (&stmt, verify_node_sharing, htab, NULL);
  	  if (addr)
  	    {
--- 3471,3481 ----
  	    }
  	}
  
!       for (bsi = bsi_start (bb); !bsi_end_p (bsi); )
  	{
  	  tree stmt = bsi_stmt (bsi);
! 	  bsi_next (&bsi);
! 	  err |= verify_stmt (stmt, bsi_end_p (bsi));
  	  addr = walk_tree (&stmt, verify_node_sharing, htab, NULL);
  	  if (addr)
  	    {
*************** tree_flow_call_edges_add (sbitmap blocks
*** 4637,4642 ****
--- 4660,4699 ----
    return blocks_split;
  }
  
+ bool
+ tree_purge_dead_eh_edges (basic_block bb)
+ {
+   bool changed = false;
+   edge e, next;
+   tree stmt = last_stmt (bb);
+ 
+   if (stmt && tree_can_throw_internal (stmt))
+     return false;
+ 
+   for (e = bb->succ; e ; e = next)
+     {
+       next = e->succ_next;
+       if (e->flags & EDGE_EH)
+ 	{
+ 	  ssa_remove_edge (e);
+ 	  changed = true;
+ 	}
+     }
+ 
+   return changed;
+ }
+ 
+ bool
+ tree_purge_all_dead_eh_edges (bitmap blocks)
+ {
+   bool changed = false;
+   size_t i;
+ 
+   EXECUTE_IF_SET_IN_BITMAP (blocks, 0, i,
+     { changed |= tree_purge_dead_eh_edges (BASIC_BLOCK (i)); });
+ 
+   return changed;
+ }
  
  struct cfg_hooks tree_cfg_hooks = {
    "tree",
Index: tree-eh.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/tree-eh.c,v
retrieving revision 2.7
diff -c -p -d -r2.7 tree-eh.c
*** tree-eh.c	26 Jun 2004 10:10:25 -0000	2.7
--- tree-eh.c	29 Jun 2004 06:52:22 -0000
*************** add_stmt_to_eh_region (tree t, int num)
*** 119,125 ****
      abort ();
    *slot = n;
  }
!   
  int
  lookup_stmt_eh_region (tree t)
  {
--- 119,145 ----
      abort ();
    *slot = n;
  }
! 
! bool
! remove_stmt_from_eh_region (tree t)
! {
!   struct throw_stmt_node dummy;
!   void **slot;
! 
!   if (!throw_stmt_table)
!     return false;
! 
!   dummy.stmt = t;
!   slot = htab_find_slot (throw_stmt_table, &dummy, NO_INSERT);
!   if (slot)
!     {
!       htab_clear_slot (throw_stmt_table, slot);
!       return true;
!     }
!   else
!     return false;
! }
! 
  int
  lookup_stmt_eh_region (tree t)
  {
*************** lower_eh_constructs (void)
*** 1600,1606 ****
    tree *tp = &DECL_SAVED_TREE (current_function_decl);
  
    finally_tree = htab_create (31, struct_ptr_hash, struct_ptr_eq, free);
!   throw_stmt_table = htab_create_ggc (31, struct_ptr_hash, struct_ptr_eq, free);
  
    collect_finally_tree (*tp, NULL);
  
--- 1620,1627 ----
    tree *tp = &DECL_SAVED_TREE (current_function_decl);
  
    finally_tree = htab_create (31, struct_ptr_hash, struct_ptr_eq, free);
!   throw_stmt_table = htab_create_ggc (31, struct_ptr_hash, struct_ptr_eq,
! 				      ggc_free);
  
    collect_finally_tree (*tp, NULL);
  
*************** make_eh_edges (tree stmt)
*** 1670,1684 ****
  
  
  
! /* Return true if the expr can trap, as in dereferencing an
!    invalid pointer location.  */
  
  bool
  tree_could_trap_p (tree expr)
  {
    enum tree_code code = TREE_CODE (expr);
    tree t;
  
    switch (code)
      {
      case ARRAY_REF:
--- 1691,1722 ----
  
  
  
! /* Return true if the expr can trap, as in dereferencing an invalid pointer
!    location or floating point arithmetic.  C.f. the rtl version, may_trap_p.
!    This routine expects only GIMPLE lhs or rhs input.  */
  
  bool
  tree_could_trap_p (tree expr)
  {
    enum tree_code code = TREE_CODE (expr);
+   bool honor_nans = false;
+   bool honor_snans = false;
+   bool fp_operation = false;
    tree t;
  
+   if (TREE_CODE_CLASS (code) == '<'
+       || TREE_CODE_CLASS (code) == '1'
+       || TREE_CODE_CLASS (code) == '2')
+     {
+       t = TREE_TYPE (expr);
+       fp_operation = FLOAT_TYPE_P (t);
+       if (fp_operation)
+ 	{
+ 	  honor_nans = flag_trapping_math && !flag_finite_math_only;
+ 	  honor_snans = flag_signaling_nans != 0;
+ 	}
+     }
+ 
    switch (code)
      {
      case ARRAY_REF:
*************** tree_could_trap_p (tree expr)
*** 1691,1697 ****
        return !t || tree_could_trap_p (t);
  
      case INDIRECT_REF:
!       return (TREE_THIS_NOTRAP (expr) == false);
  
      case TRUNC_DIV_EXPR:
      case CEIL_DIV_EXPR:
--- 1729,1738 ----
        return !t || tree_could_trap_p (t);
  
      case INDIRECT_REF:
!       return !TREE_THIS_NOTRAP (expr);
! 
!     case ASM_EXPR:
!       return TREE_THIS_VOLATILE (expr);
  
      case TRUNC_DIV_EXPR:
      case CEIL_DIV_EXPR:
*************** tree_could_trap_p (tree expr)
*** 1702,1717 ****
      case FLOOR_MOD_EXPR:
      case ROUND_MOD_EXPR:
      case TRUNC_MOD_EXPR:
!       return true;
  
      default:
!       break;
      }
- 
-   return false;
  }
  
- 
  bool
  tree_could_throw_p (tree t)
  {
--- 1743,1799 ----
      case FLOOR_MOD_EXPR:
      case ROUND_MOD_EXPR:
      case TRUNC_MOD_EXPR:
!     case RDIV_EXPR:
!       if (honor_snans)
! 	return true;
!       if (fp_operation && flag_trapping_math)
! 	return true;
!       t = TREE_OPERAND (expr, 1);
!       if (!TREE_CONSTANT (t) || integer_zerop (t))
!         return true;
!       return false;
! 
!     case LT_EXPR:
!     case LE_EXPR:
!     case GT_EXPR:
!     case GE_EXPR:
!     case LTGT_EXPR:
!       /* Some floating point comparisons may trap.  */
!       return honor_nans;
! 
!     case EQ_EXPR:
!     case NE_EXPR:
!     case UNORDERED_EXPR:
!     case ORDERED_EXPR:
!     case UNLT_EXPR:
!     case UNLE_EXPR:
!     case UNGT_EXPR:
!     case UNGE_EXPR:
!     case UNEQ_EXPR:
!       return honor_snans;
! 
!     case CONVERT_EXPR:
!     case FIX_TRUNC_EXPR:
!     case FIX_CEIL_EXPR:
!     case FIX_FLOOR_EXPR:
!     case FIX_ROUND_EXPR:
!       /* Conversion of floating point might trap.  */
!       return honor_nans;
! 
!     case NEGATE_EXPR:
!     case ABS_EXPR:
!     case CONJ_EXPR:
!       /* These operations don't trap even with floating point.  */
!       return false;
  
      default:
!       /* Any floating arithmetic may trap.  */
!       if (fp_operation && flag_trapping_math)
! 	return true;
!       return false;
      }
  }
  
  bool
  tree_could_throw_p (tree t)
  {
*************** tree_can_throw_external (tree stmt)
*** 1750,1753 ****
--- 1832,1844 ----
    return can_throw_external_1 (region_nr);
  }
  
+ bool
+ maybe_clean_eh_stmt (tree stmt)
+ {
+   if (!tree_could_throw_p (stmt))
+     if (remove_stmt_from_eh_region (stmt))
+       return true;
+   return false;
+ }
+ 
  #include "gt-tree-eh.h"
Index: tree-flow.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/tree-flow.h,v
retrieving revision 2.14
diff -c -p -d -r2.14 tree-flow.h
*** tree-flow.h	29 Jun 2004 01:53:03 -0000	2.14
--- tree-flow.h	29 Jun 2004 06:52:22 -0000
*************** extern void compute_dominance_frontiers 
*** 497,502 ****
--- 497,504 ----
  extern void verify_stmts (void);
  extern tree tree_block_label (basic_block bb);
  extern void extract_true_false_edges_from_block (basic_block, edge *, edge *);
+ extern bool tree_purge_dead_eh_edges (basic_block);
+ extern bool tree_purge_all_dead_eh_edges (bitmap);
  
  /* In tree-pretty-print.c.  */
  extern void dump_generic_bb (FILE *, basic_block, int, int);
*************** extern bool tree_could_trap_p (tree);
*** 600,606 ****
--- 602,611 ----
  extern bool tree_could_throw_p (tree);
  extern bool tree_can_throw_internal (tree);
  extern bool tree_can_throw_external (tree);
+ extern int lookup_stmt_eh_region (tree);
  extern void add_stmt_to_eh_region (tree, int);
+ extern bool remove_stmt_from_eh_region (tree);
+ extern bool maybe_clean_eh_stmt (tree);
  
  /* In tree-ssa-pre.c  */
  void add_to_value (tree, tree);
Index: tree-ssa-ccp.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/tree-ssa-ccp.c,v
retrieving revision 2.13
diff -c -p -d -r2.13 tree-ssa-ccp.c
*** tree-ssa-ccp.c	23 Jun 2004 00:25:55 -0000	2.13
--- tree-ssa-ccp.c	29 Jun 2004 06:52:22 -0000
*************** struct tree_opt_pass pass_ccp = 
*** 248,254 ****
    0,					/* properties_destroyed */
    0,					/* todo_flags_start */
    TODO_dump_func | TODO_rename_vars
!     | TODO_ggc_collect | TODO_verify_ssa /* todo_flags_finish */
  };
  
  
--- 248,255 ----
    0,					/* properties_destroyed */
    0,					/* todo_flags_start */
    TODO_dump_func | TODO_rename_vars
!     | TODO_ggc_collect | TODO_verify_ssa
!     | TODO_verify_stmts			/* todo_flags_finish */
  };
  
  
*************** substitute_and_fold (void)
*** 427,433 ****
  	      /* If we folded a builtin function, we'll likely
  		 need to rename VDEFs.  */
  	      if (replaced_address || changed)
! 		mark_new_vars_to_rename (stmt, vars_to_rename);
  	    }
  
  	  if (dump_file && (dump_flags & TDF_DETAILS))
--- 428,438 ----
  	      /* If we folded a builtin function, we'll likely
  		 need to rename VDEFs.  */
  	      if (replaced_address || changed)
! 		{
! 		  mark_new_vars_to_rename (stmt, vars_to_rename);
! 		  if (maybe_clean_eh_stmt (stmt))
! 		    tree_purge_dead_eh_edges (bb);
! 		}
  	    }
  
  	  if (dump_file && (dump_flags & TDF_DETAILS))
Index: tree-ssa-dce.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/tree-ssa-dce.c,v
retrieving revision 2.7
diff -c -p -d -r2.7 tree-ssa-dce.c
*** tree-ssa-dce.c	16 Jun 2004 23:03:32 -0000	2.7
--- tree-ssa-dce.c	29 Jun 2004 06:52:22 -0000
*************** mark_control_dependent_edges_necessary (
*** 500,506 ****
        SET_BIT (last_stmt_necessary, cd_bb->index);
  
        t = last_stmt (cd_bb);
!       if (is_ctrl_stmt (t))
  	mark_stmt_necessary (t, true);
      });
  }
--- 500,506 ----
        SET_BIT (last_stmt_necessary, cd_bb->index);
  
        t = last_stmt (cd_bb);
!       if (t && is_ctrl_stmt (t))
  	mark_stmt_necessary (t, true);
      });
  }
Index: tree-ssa-dom.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/tree-ssa-dom.c,v
retrieving revision 2.17
diff -c -p -d -r2.17 tree-ssa-dom.c
*** tree-ssa-dom.c	29 Jun 2004 01:53:03 -0000	2.17
--- tree-ssa-dom.c	29 Jun 2004 06:52:23 -0000
*************** static bitmap nonzero_vars;
*** 95,100 ****
--- 95,104 ----
  /* Track whether or not we have changed the control flow graph.  */
  static bool cfg_altered;
  
+ /* Bitmap of blocks that have had EH statements cleaned.  We should
+    remove thier dead edges eventually.  */
+ static bitmap need_eh_cleanup;
+ 
  /* Statistics for dominator optimizations.  */
  struct opt_stats_d
  {
*************** tree_ssa_dominator_optimize (void)
*** 554,559 ****
--- 558,564 ----
    nonzero_vars = BITMAP_XMALLOC ();
    VARRAY_EDGE_INIT (redirection_edges, 20, "redirection_edges");
    VARRAY_GENERIC_PTR_INIT (vrp_data, num_ssa_names, "vrp_data");
+   need_eh_cleanup = BITMAP_XMALLOC ();
  
    /* Setup callbacks for the generic dominator tree walker.  */
    walk_data.walk_stmts_backward = false;
*************** tree_ssa_dominator_optimize (void)
*** 599,613 ****
        if (VARRAY_ACTIVE_SIZE (redirection_edges) > 0)
  	redirect_edges_and_update_ssa_graph (redirection_edges);
  
        /* We may have made some basic blocks unreachable, remove them.  */
        cfg_altered |= delete_unreachable_blocks ();
  
        /* If the CFG was altered, then recompute the dominator tree.  This
  	 is not strictly needed if we only removed unreachable blocks, but
  	 may produce better results.  If we threaded jumps, then rebuilding
! 	 the dominator tree is strictly necessary.  */
        if (cfg_altered)
  	{
  	  cleanup_tree_cfg ();
  	  calculate_dominance_info (CDI_DOMINATORS);
  	}
--- 604,627 ----
        if (VARRAY_ACTIVE_SIZE (redirection_edges) > 0)
  	redirect_edges_and_update_ssa_graph (redirection_edges);
  
+       if (bitmap_first_set_bit (need_eh_cleanup) >= 0)
+ 	{
+ 	  cfg_altered = tree_purge_all_dead_eh_edges (need_eh_cleanup);
+ 	  bitmap_zero (need_eh_cleanup);
+ 	}
+ 
        /* We may have made some basic blocks unreachable, remove them.  */
        cfg_altered |= delete_unreachable_blocks ();
  
        /* If the CFG was altered, then recompute the dominator tree.  This
  	 is not strictly needed if we only removed unreachable blocks, but
  	 may produce better results.  If we threaded jumps, then rebuilding
! 	 the dominator tree is strictly necessary.  Likewise with EH cleanup.
! 	 Free the dominance info first so that cleanup_tree_cfg doesn't try
! 	 to verify it.  */
        if (cfg_altered)
  	{
+           free_dominance_info (CDI_DOMINATORS);
  	  cleanup_tree_cfg ();
  	  calculate_dominance_info (CDI_DOMINATORS);
  	}
*************** tree_ssa_dominator_optimize (void)
*** 656,661 ****
--- 670,676 ----
  
    /* Free nonzero_vars.   */
    BITMAP_XFREE (nonzero_vars);
+   BITMAP_XFREE (need_eh_cleanup);
  }
  
  static bool
*************** cprop_into_stmt (tree stmt, varray_type 
*** 2985,2992 ****
        the variable in the LHS in the CONST_AND_COPIES table.  */
  
  static void
! optimize_stmt (struct dom_walk_data *walk_data,
! 	       basic_block bb ATTRIBUTE_UNUSED,
  	       block_stmt_iterator si)
  {
    stmt_ann_t ann;
--- 3000,3006 ----
        the variable in the LHS in the CONST_AND_COPIES table.  */
  
  static void
! optimize_stmt (struct dom_walk_data *walk_data, basic_block bb,
  	       block_stmt_iterator si)
  {
    stmt_ann_t ann;
*************** optimize_stmt (struct dom_walk_data *wal
*** 3100,3110 ****
        else if (TREE_CODE (stmt) == SWITCH_EXPR)
  	val = SWITCH_COND (stmt);
  
!       if (val && TREE_CODE (val) == INTEGER_CST
! 	  && find_taken_edge (bb_for_stmt (stmt), val))
  	cfg_altered = true;
      }
!                                                                                 
    if (may_have_exposed_new_symbols)
      {
        if (! bd->stmts_to_rescan)
--- 3114,3132 ----
        else if (TREE_CODE (stmt) == SWITCH_EXPR)
  	val = SWITCH_COND (stmt);
  
!       if (val && TREE_CODE (val) == INTEGER_CST && find_taken_edge (bb, val))
  	cfg_altered = true;
+ 
+       /* If we simplified a statement in such a way as to be shown that it
+ 	 cannot trap, update the eh information and the cfg to match.  */
+       if (maybe_clean_eh_stmt (stmt))
+ 	{
+ 	  bitmap_set_bit (need_eh_cleanup, bb->index);
+ 	  if (dump_file && (dump_flags & TDF_DETAILS))
+ 	    fprintf (dump_file, "  Flagged to clear EH edges.\n");
+ 	}
      }
! 
    if (may_have_exposed_new_symbols)
      {
        if (! bd->stmts_to_rescan)



More information about the Gcc-patches mailing list