This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: Rewrite of pure/const pass and nothrow stuff


Jan Hubicka wrote:
> Hi,
> this patch teach pure/const about nothrow as discussed earlier.  I eneded up
> rewriting the local analysis from scratch as after tuplification it ended up remarkably
> ugly.
>
> There are several new flasing features:
>   1) Local analysis should be more aggressive (I hope I did not miss anything important)
>   2) There is local variant of the pass.  It can be done during early optimization
>   and at the place original RTL pass executed.  Especially in early
>   optimization we are effective.  Main motivation for doing so early is to strip down EH
>   and dead calls to get early inliner more informed; main motivation for late pass is to avoid
>   dwarf2out from unnecesarily outputting unwind info and being stupid.
>   IPA pass is the only able to handle non-trivial recursion cycles.
>
>   On tramp3d 4828 functions are marked nothrow in early pass, 0 in IPA pass, 36 in late pass.
>   7386 functions are found const in early pass, 283 in IPA pass, 45 in late pass.
>   1462 functions are found pure in early pass, 516 in IPA pass, 22 in late pass.
>
>   Old implementation found 15 const and 12 pure functions.
>
>   3) There is readable debug output showing operation of local scanner
>   4) fixup_cfg is now pass that can be scheduled as needed that should help LTO too.
>
> Bootstrapping/regtesting x86_64-linux, will commit it once finished to pretty-ipa.
>
>   
Assuming that this all checks out with the regression tests, this is
also ok whenever stage 1 opens again.

kenny

> Honza
>
> 	* tree-pass.h (pass_fixup_cfg, pass_local_pure_const): Declare.
> 	* ipa-pure-const.c (funct_state_d): Add can throw field; make
> 	state_set_in_source enum
> 	(check_decl): Ignore memory tags; do not set fake looping flags;
> 	dump diagnostics.
> 	(check_operand, check_tree, check_rhs_var, check_lhs_var,
> 	get_asm_expr_operands, scan_function_op, scan_function_stmt): Remove.
> 	(check_call, analyze_function): Rewrite.
> 	(check_stmt): New.
> 	(add_new_function): Update call of analyze_function.
> 	(generate_summary): Add call of analyze_function.
> 	(propagate): Propagate can_throw; handle state_set_in_source correctly.
> 	(local_pure_const): New function.
> 	(pass_local_pure_const): New pass.
> 	* ipa-inline.c (inline_transform): Set after_inlining.
> 	* tree-eh.c (stmt_can_throw_external): New.
> 	* tree-optimize.c (execute_fixup_cfg): Do not set after_inlining;
> 	work with aliasing built.
> 	* tree-flow.h (stmt_can_throw_external): New.
> 	* passes.c (init_optimization_passes): Schedule fixup_cfg pass early;
> 	and local pure/const pass in early and late optimization queue.
> Index: tree-pass.h
> ===================================================================
> *** tree-pass.h	(revision 141798)
> --- tree-pass.h	(working copy)
> *************** extern struct gimple_opt_pass pass_build
> *** 308,313 ****
> --- 308,314 ----
>   extern struct gimple_opt_pass pass_tree_profile;
>   extern struct gimple_opt_pass pass_early_tree_profile;
>   extern struct gimple_opt_pass pass_cleanup_cfg;
> + extern struct gimple_opt_pass pass_fixup_cfg;
>   extern struct gimple_opt_pass pass_referenced_vars;
>   extern struct gimple_opt_pass pass_sra;
>   extern struct gimple_opt_pass pass_sra_early;
> *************** extern struct gimple_opt_pass pass_reass
> *** 388,393 ****
> --- 389,395 ----
>   extern struct gimple_opt_pass pass_rebuild_cgraph_edges;
>   extern struct gimple_opt_pass pass_build_cgraph_edges;
>   extern struct gimple_opt_pass pass_reset_cc_flags;
> + extern struct gimple_opt_pass pass_local_pure_const;
>   
>   /* IPA Passes */
>   extern struct ipa_opt_pass pass_ipa_inline;
> Index: ipa-pure-const.c
> ===================================================================
> *** ipa-pure-const.c	(revision 141798)
> --- ipa-pure-const.c	(working copy)
> *************** struct funct_state_d 
> *** 71,76 ****
> --- 71,78 ----
>   {
>     /* See above.  */
>     enum pure_const_state_e pure_const_state;
> +   /* What user set here; we can be always sure about this.  */
> +   enum pure_const_state_e state_set_in_source; 
>   
>     /* True if the function could possibly infinite loop.  There are a
>        lot of ways that this could be determined.  We are pretty
> *************** struct funct_state_d 
> *** 80,89 ****
>        a behavioral change.  */
>     bool looping;
>   
> !   /* If the state of the function was set in the source, then assume
> !      that it was done properly even if the analysis we do would be
> !      more pessimestic.  */
> !   bool state_set_in_source; 
>   };
>   
>   typedef struct funct_state_d * funct_state;
> --- 82,88 ----
>        a behavioral change.  */
>     bool looping;
>   
> !   bool can_throw;
>   };
>   
>   typedef struct funct_state_d * funct_state;
> *************** static inline void 
> *** 141,152 ****
>   check_decl (funct_state local, 
>   	    tree t, bool checking_write)
>   {
>     /* Do not want to do anything with volatile except mark any
>        function that uses one to be not const or pure.  */
>     if (TREE_THIS_VOLATILE (t)) 
>       { 
>         local->pure_const_state = IPA_NEITHER;
> !       local->looping = false;
>         return;
>       }
>   
> --- 140,154 ----
>   check_decl (funct_state local, 
>   	    tree t, bool checking_write)
>   {
> +   if (MTAG_P (t))
> +     return;
>     /* Do not want to do anything with volatile except mark any
>        function that uses one to be not const or pure.  */
>     if (TREE_THIS_VOLATILE (t)) 
>       { 
>         local->pure_const_state = IPA_NEITHER;
> !       if (dump_file)
> !         fprintf (dump_file, "    Volatile operand is not const/pure");
>         return;
>       }
>   
> *************** check_decl (funct_state local, 
> *** 159,165 ****
>     if (lookup_attribute ("used", DECL_ATTRIBUTES (t)))
>       {
>         local->pure_const_state = IPA_NEITHER;
> !       local->looping = false;
>         return;
>       }
>   
> --- 161,168 ----
>     if (lookup_attribute ("used", DECL_ATTRIBUTES (t)))
>       {
>         local->pure_const_state = IPA_NEITHER;
> !       if (dump_file)
> !         fprintf (dump_file, "    Used static/global variable is not const/pure\n");
>         return;
>       }
>   
> *************** check_decl (funct_state local, 
> *** 169,175 ****
>     if (checking_write) 
>       {
>         local->pure_const_state = IPA_NEITHER;
> !       local->looping = false;
>         return;
>       }
>   
> --- 172,179 ----
>     if (checking_write) 
>       {
>         local->pure_const_state = IPA_NEITHER;
> !       if (dump_file)
> !         fprintf (dump_file, "    static/global memory write is not const/pure\n");
>         return;
>       }
>   
> *************** check_decl (funct_state local, 
> *** 177,333 ****
>       {
>         /* Readonly reads are safe.  */
>         if (TREE_READONLY (t) && !TYPE_NEEDS_CONSTRUCTING (TREE_TYPE (t)))
> ! 	; /* Read of a constant, do not change the function state.  */
>         else 
>   	{
>   	  /* Just a regular read.  */
>   	  if (local->pure_const_state == IPA_CONST)
>   	    local->pure_const_state = IPA_PURE;
>   	}
>       }
> !   
> !   /* Compilation level statics can be read if they are readonly
> !      variables.  */
> !   if (TREE_READONLY (t))
> !     return;
> ! 
> !   /* Just a regular read.  */
> !   if (local->pure_const_state == IPA_CONST)
> !     local->pure_const_state = IPA_PURE;
>   }
>   
> - /* If T is a VAR_DECL check to see if it is an allowed reference.  */
> - 
> - static void
> - check_operand (funct_state local, 
> - 	       tree t, bool checking_write)
> - {
> -   if (!t) return;
> - 
> -   if (TREE_CODE (t) == VAR_DECL)
> -     check_decl (local, t, checking_write); 
> - }
>   
> ! /* Examine tree T for references.  */
>   
> ! static void
> ! check_tree (funct_state local, tree t, bool checking_write)
>   {
> !   if ((TREE_CODE (t) == EXC_PTR_EXPR) || (TREE_CODE (t) == FILTER_EXPR)
> !       || TREE_CODE (t) == SSA_NAME)
>       return;
> ! 
> !   /* Any tree which is volatile disqualifies this function from being
> !      const or pure. */
> !   if (TREE_THIS_VOLATILE (t))
>       {
> !       local->pure_const_state = IPA_NEITHER;
> !       local->looping = false;
> !       return;
> !     }
> ! 
> !   while (TREE_CODE (t) == REALPART_EXPR 
> ! 	 || TREE_CODE (t) == IMAGPART_EXPR
> ! 	 || handled_component_p (t))
> !     {
> !       if (TREE_CODE (t) == ARRAY_REF)
> ! 	check_operand (local, TREE_OPERAND (t, 1), false);
> !       t = TREE_OPERAND (t, 0);
> !     }
> ! 
> !   /* The bottom of an indirect reference can only be read, not
> !      written.  */
> !   if (INDIRECT_REF_P (t))
> !     {
> !       check_tree (local, TREE_OPERAND (t, 0), false);
> !       
> !       /* Any indirect reference that occurs on the lhs
> ! 	 disqualifies the function from being pure or const. Any
> ! 	 indirect reference that occurs on the rhs disqualifies the
> ! 	 function from being const.  */
> !       if (checking_write) 
> ! 	{
>   	  local->pure_const_state = IPA_NEITHER;
> ! 	  local->looping = false;
>   	  return;
>   	}
> !       else if (local->pure_const_state == IPA_CONST)
> ! 	local->pure_const_state = IPA_PURE;
> !     }
> ! 
> !   if (SSA_VAR_P (t))
> !     check_operand (local, t, checking_write);
> ! }
> ! 
> ! /* Check to see if T is a read or address of operation on a var we are
> !    interested in analyzing.  LOCAL is passed in to get access to its
> !    bit vectors.  */
> ! 
> ! static void
> ! check_rhs_var (funct_state local, tree t)
> ! {
> !   check_tree(local, t, false);
> ! }
> ! 
> ! /* Check to see if T is an assignment to a var we are interested in
> !    analyzing.  LOCAL is passed in to get access to its bit vectors. */
> ! 
> ! static void
> ! check_lhs_var (funct_state local, tree t)
> ! {
> !   check_tree(local, t, true);
> ! }
> ! 
> ! /* This is a scaled down version of get_asm_expr_operands from
> !    tree_ssa_operands.c.  The version there runs much later and assumes
> !    that aliasing information is already available. Here we are just
> !    trying to find if the set of inputs and outputs contain references
> !    or address of operations to local static variables.  STMT is the
> !    actual asm statement.  */
> ! 
> ! static void
> ! get_asm_expr_operands (funct_state local, gimple stmt)
> ! {
> !   size_t noutputs = gimple_asm_noutputs (stmt);
> !   const char **oconstraints
> !     = (const char **) alloca ((noutputs) * sizeof (const char *));
> !   size_t i;
> !   tree op;
> !   const char *constraint;
> !   bool allows_mem, allows_reg, is_inout;
> !   
> !   for (i = 0; i < noutputs; i++)
> !     {
> !       op = gimple_asm_output_op (stmt, i);
> !       oconstraints[i] = constraint
> ! 	= TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (op)));
> !       parse_output_constraint (&constraint, i, 0, 0,
> ! 			       &allows_mem, &allows_reg, &is_inout);
> !       
> !       check_lhs_var (local, TREE_VALUE (op));
> !     }
> ! 
> !   for (i = 0; i < gimple_asm_ninputs (stmt); i++)
> !     {
> !       op = gimple_asm_input_op (stmt, i);
> !       constraint
> ! 	= TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (op)));
> !       parse_input_constraint (&constraint, 0, 0, noutputs, 0,
> ! 			      oconstraints, &allows_mem, &allows_reg);
> !       
> !       check_rhs_var (local, TREE_VALUE (op));
> !     }
> !   
> !   for (i = 0; i < gimple_asm_nclobbers (stmt); i++)
> !     {
> !       op = gimple_asm_clobber_op (stmt, i);
> !       if (simple_cst_equal(TREE_VALUE (op), memory_identifier_string) == 1) 
> ! 	/* Abandon all hope, ye who enter here. */
> ! 	local->pure_const_state = IPA_NEITHER;
>       }
> - 
> -   if (gimple_asm_volatile_p (stmt))
> -     local->pure_const_state = IPA_NEITHER;
>   }
>   
>   /* Check the parameters of a function call to CALL_EXPR to see if
> --- 181,247 ----
>       {
>         /* Readonly reads are safe.  */
>         if (TREE_READONLY (t) && !TYPE_NEEDS_CONSTRUCTING (TREE_TYPE (t)))
> ! 	return; /* Read of a constant, do not change the function state.  */
>         else 
>   	{
> +           if (dump_file)
> +             fprintf (dump_file, "    global memory read is not const\n");
>   	  /* Just a regular read.  */
>   	  if (local->pure_const_state == IPA_CONST)
>   	    local->pure_const_state = IPA_PURE;
>   	}
>       }
> !   else
> !     {
> !       /* Compilation level statics can be read if they are readonly
> ! 	 variables.  */
> !       if (TREE_READONLY (t))
> ! 	return;
> ! 
> !       if (dump_file)
> ! 	fprintf (dump_file, "    static memory read is not const\n");
> !       /* Just a regular read.  */
> !       if (local->pure_const_state == IPA_CONST)
> ! 	local->pure_const_state = IPA_PURE;
> !     }
>   }
>   
>   
> ! /* Check to see if the use (or definition when CHECKING_WRITE is true)
> !    variable T is legal in a function that is either pure or const.  */
>   
> ! static inline void 
> ! check_op (funct_state local, 
> ! 	    tree t, bool checking_write)
>   {
> !   while (t && handled_component_p (t))
> !     t = TREE_OPERAND (t, 0);
> !   if (!t)
>       return;
> !   if (TREE_CODE (t) == INDIRECT_REF || TREE_CODE (t) == TARGET_MEM_REF)
>       {
> !       if (TREE_THIS_VOLATILE (t)) 
> ! 	{ 
>   	  local->pure_const_state = IPA_NEITHER;
> ! 	  if (dump_file)
> ! 	    fprintf (dump_file, "    Volatile indirect ref is not const/pure\n");
>   	  return;
>   	}
> !       else if (checking_write)
> ! 	{ 
> ! 	  local->pure_const_state = IPA_NEITHER;
> ! 	  if (dump_file)
> ! 	    fprintf (dump_file, "    Indirect ref write is not const/pure\n");
> ! 	  return;
> ! 	}
> !        else
> !         {
> ! 	  if (dump_file)
> ! 	    fprintf (dump_file, "    Indirect ref read is not const\n");
> !           if (local->pure_const_state == IPA_CONST)
> ! 	    local->pure_const_state = IPA_PURE;
> ! 	}
>       }
>   }
>   
>   /* Check the parameters of a function call to CALL_EXPR to see if
> *************** get_asm_expr_operands (funct_state local
> *** 338,357 ****
>      the entire call expression.  */
>   
>   static void
> ! check_call (funct_state local, gimple call) 
>   {
>     int flags = gimple_call_flags (call);
> !   tree lhs, callee_t = gimple_call_fndecl (call);
>     struct cgraph_node* callee;
>     enum availability avail = AVAIL_NOT_AVAILABLE;
> !   size_t i;
> ! 
> !   lhs = gimple_call_lhs (call);
> !   if (lhs)
> !     check_lhs_var (local, lhs);
>   
> !   for (i = 0; i < gimple_call_num_args (call); i++)
> !     check_rhs_var (local, gimple_call_arg (call, i));
>     
>     /* The const and pure flags are set by a variety of places in the
>        compiler (including here).  If someone has already set the flags
> --- 252,286 ----
>      the entire call expression.  */
>   
>   static void
> ! check_call (funct_state local, gimple call, bool ipa)
>   {
>     int flags = gimple_call_flags (call);
> !   tree callee_t = gimple_call_fndecl (call);
>     struct cgraph_node* callee;
>     enum availability avail = AVAIL_NOT_AVAILABLE;
> !   bool possibly_throws_externally = stmt_can_throw_external (call);
> !   bool possibly_throws = stmt_could_throw_p (call);
>   
> !   if (possibly_throws)
> !     {
> !       unsigned int i;
> !       for (i = 0; i < gimple_num_ops (call); i++)
> !         if (stmt_could_throw_p (call))
> ! 	  {
> ! 	    if (possibly_throws && flag_non_call_exceptions)
> ! 	      {
> ! 		if (dump_file)
> ! 		  fprintf (dump_file, "    operand can throw; looping");
> ! 		local->looping = true;
> ! 	      }
> ! 	    if (possibly_throws_externally)
> ! 	      {
> ! 		if (dump_file)
> ! 		  fprintf (dump_file, "    operand can throw externally");
> ! 		local->can_throw = true;
> ! 	      }
> ! 	  }
> !     }
>     
>     /* The const and pure flags are set by a variety of places in the
>        compiler (including here).  If someone has already set the flags
> *************** check_call (funct_state local, gimple ca
> *** 372,379 ****
>   	 or pure.  */
>         if (setjmp_call_p (callee_t))
>   	{
>   	  local->pure_const_state = IPA_NEITHER;
> - 	  local->looping = false;
>   	}
>   
>         if (DECL_BUILT_IN_CLASS (callee_t) == BUILT_IN_NORMAL)
> --- 301,310 ----
>   	 or pure.  */
>         if (setjmp_call_p (callee_t))
>   	{
> + 	  if (dump_file)
> + 	    fprintf (dump_file, "    setjmp is not const/pure\n");
> +           local->looping = true;
>   	  local->pure_const_state = IPA_NEITHER;
>   	}
>   
>         if (DECL_BUILT_IN_CLASS (callee_t) == BUILT_IN_NORMAL)
> *************** check_call (funct_state local, gimple ca
> *** 381,647 ****
>   	  {
>   	  case BUILT_IN_LONGJMP:
>   	  case BUILT_IN_NONLOCAL_GOTO:
>   	    local->pure_const_state = IPA_NEITHER;
> ! 	    local->looping = false;
>   	    break;
>   	  default:
>   	    break;
>   	  }
>       }
>   
>     /* The callee is either unknown (indirect call) or there is just no
>        scannable code for it (external call) .  We look to see if there
>        are any bits available for the callee (such as by declaration or
>        because it is builtin) and process solely on the basis of those
>        bits. */
> !   if (avail == AVAIL_NOT_AVAILABLE || avail == AVAIL_OVERWRITABLE)
>       {
> !       if (flags & ECF_PURE) 
> ! 	{
>   	  if (local->pure_const_state == IPA_CONST)
>   	    local->pure_const_state = IPA_PURE;
>   	}
>         else 
> - 	local->pure_const_state = IPA_NEITHER;
> -     }
> -   else
> -     {
> -       /* We have the code and we will scan it for the effects. */
> -       if (flags & ECF_PURE) 
>   	{
> ! 	  if (local->pure_const_state == IPA_CONST)
> ! 	    local->pure_const_state = IPA_PURE;
>   	}
>       }
>   }
>   
> ! /* TP is the part of the tree currently under the microscope.
> !    WALK_SUBTREES is part of the walk_tree api but is unused here.
> !    DATA is cgraph_node of the function being walked.  */
> ! 
> ! /* FIXME: When this is converted to run over SSA form, this code
> !    should be converted to use the operand scanner.  */
> ! 
> ! static tree
> ! scan_function_op (tree *tp, int *walk_subtrees, void *data)
> ! {
> !   struct walk_stmt_info *wi = (struct walk_stmt_info *) data;
> !   struct cgraph_node *fn = (struct cgraph_node *) wi->info;
> !   tree t = *tp;
> !   funct_state local = get_function_state (fn);
> ! 
> !   switch (TREE_CODE (t))  
> !     {
> !     case VAR_DECL:
> !       if (DECL_INITIAL (t))
> ! 	walk_tree (&DECL_INITIAL (t), scan_function_op, data, visited_nodes);
> !       *walk_subtrees = 0;
> !       break;
> ! 
> !     case ADDR_EXPR:
> !       /* This case is here to find addresses on rhs of constructors in
> ! 	 decl_initial of static variables. */
> !       check_rhs_var (local, t);
> !       *walk_subtrees = 0;
> !       break;
> ! 
> !     default:
> !       break;
> !     }
> !   return NULL;
> ! }
> ! 
> ! static tree
> ! scan_function_stmt (gimple_stmt_iterator *gsi_p,
> ! 		    bool *handled_ops_p,
> ! 		    struct walk_stmt_info *wi)
>   {
> !   struct cgraph_node *fn = (struct cgraph_node *) wi->info;
> !   gimple stmt = gsi_stmt (*gsi_p);
> !   funct_state local = get_function_state (fn);
>   
>     switch (gimple_code (stmt))
>       {
>       case GIMPLE_ASSIGN:
> !       {
> ! 	/* First look on the lhs and see what variable is stored to */
> ! 	tree lhs = gimple_assign_lhs (stmt);
> ! 	tree rhs1 = gimple_assign_rhs1 (stmt);
> ! 	tree rhs2 = gimple_assign_rhs2 (stmt);
> ! 	enum tree_code code = gimple_assign_rhs_code (stmt);
> ! 
> ! 	check_lhs_var (local, lhs);
> ! 
> ! 	/* For the purposes of figuring out what the cast affects */
> ! 
> ! 	/* Next check the operands on the rhs to see if they are ok. */
> ! 	switch (TREE_CODE_CLASS (code))
> ! 	  {
> ! 	  case tcc_binary:	    
> !  	    {
> !  	      check_rhs_var (local, rhs1);
> !  	      check_rhs_var (local, rhs2);
> ! 	    }
> ! 	    break;
> ! 	  case tcc_unary:
> !  	    {
> !  	      check_rhs_var (local, rhs1);
> !  	    }
> ! 
> ! 	    break;
> ! 	  case tcc_reference:
> ! 	    check_rhs_var (local, rhs1);
> ! 	    break;
> ! 	  case tcc_declaration:
> ! 	    check_rhs_var (local, rhs1);
> ! 	    break;
> ! 	  case tcc_expression:
> ! 	    switch (code)
> ! 	      {
> ! 	      case ADDR_EXPR:
> ! 		check_rhs_var (local, rhs1);
> ! 		break;
> ! 	      default:
> ! 		break;
> ! 	      }
> ! 	    break;
> ! 	  default:
> ! 	    break;
> ! 	  }
> ! 	*handled_ops_p = true;
> !       }
>         break;
> - 
>       case GIMPLE_LABEL:
>         if (DECL_NONLOCAL (gimple_label_label (stmt)))
>   	/* Target of long jump. */
>   	{
>   	  local->pure_const_state = IPA_NEITHER;
> - 	  local->looping = false;
>   	}
>         break;
> - 
> -     case GIMPLE_CALL:
> -       check_call (local, stmt);
> -       *handled_ops_p = true;
> -       break;
> -       
>       case GIMPLE_ASM:
> !       get_asm_expr_operands (local, stmt);
> !       *handled_ops_p = true;
> !       break;
> !       
>       default:
>         break;
>       }
> !   return NULL;
>   }
>   
>   
>   /* This is the main routine for finding the reference patterns for
>      global variables within a function FN.  */
>   
> ! static void
> ! analyze_function (struct cgraph_node *fn)
>   {
>     tree decl = fn->decl;
> !   funct_state l = XCNEW (struct funct_state_d);
> ! 
> !  if (cgraph_function_body_availability (fn) <= AVAIL_OVERWRITABLE)
> !    return;
>   
> !   set_function_state (fn, l);
>   
>     l->pure_const_state = IPA_CONST;
> !   l->state_set_in_source = false;
> !   if (DECL_LOOPING_CONST_OR_PURE_P (decl))
> !     l->looping = true;
> !   else
> !     l->looping = false;
>   
> !   /* If this function does not return normally or does not bind local,
> !      do not touch this unless it has been marked as const or pure by the
> !      front end.  */
> !   if (TREE_THIS_VOLATILE (decl)
> !       || !targetm.binds_local_p (decl))
>       {
> !       l->pure_const_state = IPA_NEITHER;
> !       return;
>       }
> ! 
> !   if (TREE_READONLY (decl))
>       {
> !       l->pure_const_state = IPA_CONST;
> !       l->state_set_in_source = true;
>       }
> !   if (DECL_PURE_P (decl))
>       {
> !       l->pure_const_state = IPA_PURE;
> !       l->state_set_in_source = true;
>       }
>   
> !   if (dump_file)
>       {
> !       fprintf (dump_file, "\n local analysis of %s with initial value = %d\n ", 
> ! 	       cgraph_node_name (fn),
> ! 	       l->pure_const_state);
>       }
> !   
> !   if (!l->state_set_in_source)
>       {
> !       struct function *this_cfun = DECL_STRUCT_FUNCTION (decl);
> !       basic_block this_block;
> !       
> !       FOR_EACH_BB_FN (this_block, this_cfun)
> ! 	{
> ! 	  gimple_stmt_iterator gsi;
> ! 	  struct walk_stmt_info wi;
> ! 
> ! 	  memset (&wi, 0, sizeof(wi));
> ! 	  for (gsi = gsi_start_bb (this_block);
> ! 	       !gsi_end_p (gsi);
> ! 	       gsi_next (&gsi))
> ! 	    {
> ! 	      wi.info = fn;
> ! 	      wi.pset = visited_nodes;
> ! 	      walk_gimple_stmt (&gsi, scan_function_stmt, scan_function_op, 
> ! 				&wi);
> ! 	      if (l->pure_const_state == IPA_NEITHER) 
> ! 		goto end;
> ! 	    }
> ! 	}
> ! 
> !       if (l->pure_const_state != IPA_NEITHER)
> ! 	{
> ! 	  tree old_decl = current_function_decl;
> ! 	  /* Const functions cannot have back edges (an
> ! 	     indication of possible infinite loop side
> ! 	     effect.  */
> ! 	    
> ! 	  current_function_decl = fn->decl;
> ! 
> ! 	  /* The C++ front end, has a tendency to some times jerk away
> ! 	     a function after it has created it.  This should have
> ! 	     been fixed.  */
> ! 	  gcc_assert (DECL_STRUCT_FUNCTION (fn->decl));
> ! 	  
> ! 	  push_cfun (DECL_STRUCT_FUNCTION (fn->decl));
> ! 	  
> ! 	  if (mark_dfs_back_edges ())
> ! 	    l->pure_const_state = IPA_NEITHER;
> ! 	  
> ! 	  current_function_decl = old_decl;
> ! 	  pop_cfun ();
> ! 	}
>       }
>   
> ! end:
>     if (dump_file)
>       {
> !       fprintf (dump_file, "after local analysis of %s with initial value = %d\n ", 
> ! 	       cgraph_node_name (fn),
> ! 	       l->pure_const_state);
>       }
>   }
>   
>   /* Called when new function is inserted to callgraph late.  */
> --- 312,551 ----
>   	  {
>   	  case BUILT_IN_LONGJMP:
>   	  case BUILT_IN_NONLOCAL_GOTO:
> + 	    if (dump_file)
> + 	      fprintf (dump_file, "    longjmp and nonlocal goto is not const/pure\n");
>   	    local->pure_const_state = IPA_NEITHER;
> !             local->looping = true;
>   	    break;
>   	  default:
>   	    break;
>   	  }
>       }
>   
> +   /* When not in IPA mode, we can still handle self recursion.  */
> +   if (!ipa && callee_t == current_function_decl)
> +     local->looping = true;
>     /* The callee is either unknown (indirect call) or there is just no
>        scannable code for it (external call) .  We look to see if there
>        are any bits available for the callee (such as by declaration or
>        because it is builtin) and process solely on the basis of those
>        bits. */
> !   else if (avail <= AVAIL_OVERWRITABLE || !ipa)
>       {
> !       if (possibly_throws && flag_non_call_exceptions)
> !         {
> ! 	  if (dump_file)
> ! 	    fprintf (dump_file, "    can throw; looping");
> !           local->looping = true;
> ! 	}
> !       if (possibly_throws_externally)
> !         {
> ! 	  if (dump_file)
> ! 	    fprintf (dump_file, "    can throw externally");
> !           local->can_throw = true;
> ! 	}
> !       if (flags & ECF_CONST) 
> ! 	{
> !           if (callee_t && DECL_LOOPING_CONST_OR_PURE_P (callee_t))
> !             local->looping = true;
> ! 	 }
> !       else if (flags & ECF_PURE) 
> ! 	{
> !           if (callee_t && DECL_LOOPING_CONST_OR_PURE_P (callee_t))
> !             local->looping = true;
> ! 	  if (dump_file)
> ! 	    fprintf (dump_file, "    pure function call in not const\n");
>   	  if (local->pure_const_state == IPA_CONST)
>   	    local->pure_const_state = IPA_PURE;
>   	}
>         else 
>   	{
> ! 	  if (dump_file)
> ! 	    fprintf (dump_file, "    uknown function call is not const/pure\n");
> ! 	  local->pure_const_state = IPA_NEITHER;
> !           local->looping = true;
>   	}
>       }
> +   /* Direct functions calls are handled by IPA propagation.  */
>   }
>   
> ! static void
> ! check_stmt (gimple_stmt_iterator *gsip, funct_state local, bool ipa)
>   {
> !   gimple stmt = gsi_stmt (*gsip);
> !   unsigned int i = 0;
> !   bitmap_iterator bi;
>   
> +   if (dump_file)
> +     {
> +       fprintf (dump_file, "  scanning: ");
> +       print_gimple_stmt (dump_file, stmt, 0, 0);
> +     }
> +   if (gimple_loaded_syms (stmt))
> +     EXECUTE_IF_SET_IN_BITMAP (gimple_loaded_syms (stmt), 0, i, bi)
> +       check_decl (local, referenced_var_lookup (i), false);
> +   if (gimple_stored_syms (stmt))
> +     EXECUTE_IF_SET_IN_BITMAP (gimple_stored_syms (stmt), 0, i, bi)
> +       check_decl (local, referenced_var_lookup (i), true);
> + 
> +   if (gimple_code (stmt) != GIMPLE_CALL
> +       && stmt_could_throw_p (stmt))
> +     {
> +       if (flag_non_call_exceptions)
> + 	{
> + 	  if (dump_file)
> + 	    fprintf (dump_file, "    can throw; looping");
> + 	  local->looping = true;
> + 	}
> +       if (stmt_can_throw_external (stmt))
> + 	{
> + 	  if (dump_file)
> + 	    fprintf (dump_file, "    can throw externally");
> + 	  local->can_throw = true;
> + 	}
> +     }
>     switch (gimple_code (stmt))
>       {
>       case GIMPLE_ASSIGN:
> !       check_op (local, gimple_assign_lhs (stmt), true);
> !       i = 1;
> !       break;
> !     case GIMPLE_CALL:
> !       check_op (local, gimple_call_lhs (stmt), true);
> !       i = 1;
> !       check_call (local, stmt, ipa);
>         break;
>       case GIMPLE_LABEL:
>         if (DECL_NONLOCAL (gimple_label_label (stmt)))
>   	/* Target of long jump. */
>   	{
> +           if (dump_file)
> +             fprintf (dump_file, "    nonlocal label is not const/pure");
>   	  local->pure_const_state = IPA_NEITHER;
>   	}
>         break;
>       case GIMPLE_ASM:
> !       for (i = 0; i < gimple_asm_noutputs (stmt); i++)
> !          check_op (local, gimple_asm_output_op (stmt, i), true);
> !       for (i = 0; i < gimple_asm_ninputs (stmt); i++)
> !          check_op (local, gimple_asm_input_op (stmt, i), false);
> !       for (i = 0; i < gimple_asm_nclobbers (stmt); i++)
> ! 	{
> ! 	  tree op = gimple_asm_clobber_op (stmt, i);
> ! 	  if (simple_cst_equal(TREE_VALUE (op), memory_identifier_string) == 1) 
> ! 	    {
> !               if (dump_file)
> !                 fprintf (dump_file, "    memory asm clobber is not const/pure");
> ! 	      /* Abandon all hope, ye who enter here. */
> ! 	      local->pure_const_state = IPA_NEITHER;
> ! 	    }
> ! 	}
> !       if (gimple_asm_volatile_p (stmt))
> ! 	{
> ! 	  if (dump_file)
> ! 	    fprintf (dump_file, "    volatile is not const/pure");
> ! 	  /* Abandon all hope, ye who enter here. */
> ! 	  local->pure_const_state = IPA_NEITHER;
> !           local->looping = true;
> ! 	}
> !       return;
>       default:
>         break;
>       }
> ! 
> !   for (; i < gimple_num_ops (stmt); i++)
> !     check_op (local, gimple_op (stmt, i), false);
>   }
>   
>   
>   /* This is the main routine for finding the reference patterns for
>      global variables within a function FN.  */
>   
> ! static funct_state
> ! analyze_function (struct cgraph_node *fn, bool ipa)
>   {
>     tree decl = fn->decl;
> !   tree old_decl = current_function_decl;
> !   funct_state l;
> !   basic_block this_block;
>   
> !   if (cgraph_function_body_availability (fn) <= AVAIL_OVERWRITABLE)
> !     return NULL;
>   
> +   l = XCNEW (struct funct_state_d);
>     l->pure_const_state = IPA_CONST;
> !   l->state_set_in_source = IPA_NEITHER;
> !   l->looping = false;
> !   l->can_throw = false;
>   
> !   if (dump_file)
>       {
> !       fprintf (dump_file, "\n\n local analysis of %s\n ", 
> ! 	       cgraph_node_name (fn));
>       }
> !   
> !   push_cfun (DECL_STRUCT_FUNCTION (decl));
> !   current_function_decl = decl;
> !   
> !   FOR_EACH_BB (this_block)
>       {
> !       gimple_stmt_iterator gsi;
> !       struct walk_stmt_info wi;
> ! 
> !       memset (&wi, 0, sizeof(wi));
> !       for (gsi = gsi_start_bb (this_block);
> ! 	   !gsi_end_p (gsi);
> ! 	   gsi_next (&gsi))
> ! 	{
> ! 	  check_stmt (&gsi, l, ipa);
> ! 	  if (l->pure_const_state == IPA_NEITHER && l->looping && l->can_throw)
> ! 	    goto end;
> ! 	}
>       }
> ! 
> ! end:
> !   if (l->pure_const_state != IPA_NEITHER)
>       {
> !       /* Const functions cannot have back edges (an
> ! 	 indication of possible infinite loop side
> ! 	 effect.  */
> !       if (mark_dfs_back_edges ())
> ! 	l->looping = true;
> !       
>       }
>   
> !   if (TREE_READONLY (decl))
>       {
> !       l->pure_const_state = IPA_CONST;
> !       l->state_set_in_source = IPA_CONST;
> !       if (!DECL_LOOPING_CONST_OR_PURE_P (decl))
> !         l->looping = false;
>       }
> !   if (DECL_PURE_P (decl))
>       {
> !       if (l->pure_const_state != IPA_CONST)
> !         l->pure_const_state = IPA_PURE;
> !       l->state_set_in_source = IPA_PURE;
> !       if (!DECL_LOOPING_CONST_OR_PURE_P (decl))
> !         l->looping = false;
>       }
> +   if (TREE_NOTHROW (decl))
> +     l->can_throw = false;
>   
> !   pop_cfun ();
> !   current_function_decl = old_decl;
>     if (dump_file)
>       {
> !       if (l->looping)
> !         fprintf (dump_file, "Function is locally looping.\n");
> !       if (l->can_throw)
> !         fprintf (dump_file, "Function is locally throwing.\n");
> !       if (l->pure_const_state == IPA_CONST)
> !         fprintf (dump_file, "Function is locally const.\n");
> !       if (l->pure_const_state == IPA_PURE)
> !         fprintf (dump_file, "Function is locally pure.\n");
>       }
> +   return l;
>   }
>   
>   /* Called when new function is inserted to callgraph late.  */
> *************** add_new_function (struct cgraph_node *no
> *** 655,661 ****
>        since all we would be interested in are the addressof
>        operations.  */
>     visited_nodes = pointer_set_create ();
> !   analyze_function (node);
>     pointer_set_destroy (visited_nodes);
>     visited_nodes = NULL;
>   }
> --- 559,565 ----
>        since all we would be interested in are the addressof
>        operations.  */
>     visited_nodes = pointer_set_create ();
> !   set_function_state (node, analyze_function (node, true));
>     pointer_set_destroy (visited_nodes);
>     visited_nodes = NULL;
>   }
> *************** generate_summary (void)
> *** 716,722 ****
>     */
>     for (node = cgraph_nodes; node; node = node->next)
>       if (cgraph_function_body_availability (node) > AVAIL_OVERWRITABLE)
> !       analyze_function (node);
>   
>     pointer_set_destroy (visited_nodes);
>     visited_nodes = NULL;
> --- 620,626 ----
>     */
>     for (node = cgraph_nodes; node; node = node->next)
>       if (cgraph_function_body_availability (node) > AVAIL_OVERWRITABLE)
> !       set_function_state (node, analyze_function (node, true));
>   
>     pointer_set_destroy (visited_nodes);
>     visited_nodes = NULL;
> *************** propagate (void)
> *** 756,761 ****
> --- 660,666 ----
>       {
>         enum pure_const_state_e pure_const_state = IPA_CONST;
>         bool looping = false;
> +       bool can_throw = false;
>         int count = 0;
>         node = order[i];
>   
> *************** propagate (void)
> *** 763,800 ****
>         w = node;
>         while (w)
>   	{
>   	  funct_state w_l = get_function_state (w);
>   	  if (pure_const_state < w_l->pure_const_state)
>   	    pure_const_state = w_l->pure_const_state;
>   
>   	  if (w_l->looping)
>   	    looping = true;
>   
>   	  if (pure_const_state == IPA_NEITHER) 
>   	    break;
>   
> ! 	  if (!w_l->state_set_in_source)
>   	    {
> ! 	      struct cgraph_edge *e;
> ! 	      count++;
>   
> ! 	      if (count > 1)
> ! 		looping = true;
> ! 		    
> ! 	      for (e = w->callees; e; e = e->next_callee) 
>   		{
> ! 		  struct cgraph_node *y = e->callee;
> ! 
> ! 		  if (cgraph_function_body_availability (y) > AVAIL_OVERWRITABLE)
> ! 		    {
> ! 		      funct_state y_l = get_function_state (y);
> ! 		      if (pure_const_state < y_l->pure_const_state)
> ! 			pure_const_state = y_l->pure_const_state;
> ! 		      if (pure_const_state == IPA_NEITHER) 
> ! 			break;
> ! 		      if (y_l->looping)
> ! 			looping = true;
> ! 		    }
>   		}
>   	    }
>   	  w_info = (struct ipa_dfs_info *) w->aux;
> --- 668,709 ----
>         w = node;
>         while (w)
>   	{
> + 	  struct cgraph_edge *e;
>   	  funct_state w_l = get_function_state (w);
>   	  if (pure_const_state < w_l->pure_const_state)
>   	    pure_const_state = w_l->pure_const_state;
>   
> + 	  if (w_l->can_throw)
> + 	    can_throw = true;
>   	  if (w_l->looping)
>   	    looping = true;
>   
>   	  if (pure_const_state == IPA_NEITHER) 
>   	    break;
>   
> ! 	  count++;
> ! 
> ! 	  if (count > 1)
> ! 	    looping = true;
> ! 		
> ! 	  for (e = w->callees; e; e = e->next_callee) 
>   	    {
> ! 	      struct cgraph_node *y = e->callee;
>   
> ! 	      if (cgraph_function_body_availability (y) > AVAIL_OVERWRITABLE)
>   		{
> ! 		  funct_state y_l = get_function_state (y);
> ! 		  if (pure_const_state < y_l->pure_const_state)
> ! 		    pure_const_state = y_l->pure_const_state;
> ! 		  if (pure_const_state == IPA_NEITHER) 
> ! 		    break;
> ! 		  if (y_l->looping)
> ! 		    looping = true;
> ! 		  if (y_l->can_throw && !TREE_NOTHROW (w->decl)
> ! 		      /* FIXME: We should check that the throw can get external.
> ! 		         We also should handle only loops formed by can throw external
> ! 			 edges.  */)
> ! 		    can_throw = true;
>   		}
>   	    }
>   	  w_info = (struct ipa_dfs_info *) w->aux;
> *************** propagate (void)
> *** 807,842 ****
>         while (w)
>   	{
>   	  funct_state w_l = get_function_state (w);
>   
> ! 	  /* All nodes within a cycle share the same info.  */
> ! 	  if (!w_l->state_set_in_source)
>   	    {
> ! 	      w_l->pure_const_state = pure_const_state;
> ! 	      w_l->looping = looping;
>   
> ! 	      switch (pure_const_state)
> ! 		{
> ! 		case IPA_CONST:
> ! 		  TREE_READONLY (w->decl) = 1;
> ! 		  DECL_LOOPING_CONST_OR_PURE_P (w->decl) = looping;
> ! 		  if (dump_file)
> ! 		    fprintf (dump_file, "Function found to be %sconst: %s\n",  
> ! 			     looping ? "looping " : "",
> ! 			     lang_hooks.decl_printable_name(w->decl, 2)); 
> ! 		  break;
> ! 		  
> ! 		case IPA_PURE:
> ! 		  DECL_PURE_P (w->decl) = 1;
> ! 		  DECL_LOOPING_CONST_OR_PURE_P (w->decl) = looping;
> ! 		  if (dump_file)
> ! 		    fprintf (dump_file, "Function found to be %spure: %s\n",  
> ! 			     looping ? "looping " : "",
> ! 			     lang_hooks.decl_printable_name(w->decl, 2)); 
> ! 		  break;
> ! 		  
> ! 		default:
> ! 		  break;
> ! 		}
>   	    }
>   	  w_info = (struct ipa_dfs_info *) w->aux;
>   	  w = w_info->next_cycle;
> --- 716,763 ----
>         while (w)
>   	{
>   	  funct_state w_l = get_function_state (w);
> + 	  enum pure_const_state_e this_state = pure_const_state;
> + 	  bool this_looping = looping;
>   
> ! 	  if (w_l->state_set_in_source != IPA_NEITHER)
>   	    {
> ! 	      if (this_state > w_l->state_set_in_source)
> ! 	        this_state = w_l->state_set_in_source;
> ! 	      this_looping = false;
> ! 	    }
>   
> ! 	  /* All nodes within a cycle share the same info.  */
> ! 	  w_l->pure_const_state = this_state;
> ! 	  w_l->looping = this_looping;
> ! 
> ! 	  switch (this_state)
> ! 	    {
> ! 	    case IPA_CONST:
> ! 	      if (!TREE_READONLY (w->decl) && dump_file)
> ! 		fprintf (dump_file, "Function found to be %sconst: %s\n",  
> ! 			 this_looping ? "looping " : "",
> ! 			 cgraph_node_name (w)); 
> ! 	      TREE_READONLY (w->decl) = 1;
> ! 	      DECL_LOOPING_CONST_OR_PURE_P (w->decl) = this_looping;
> ! 	      break;
> ! 	      
> ! 	    case IPA_PURE:
> ! 	      if (!DECL_PURE_P (w->decl) && dump_file)
> ! 		fprintf (dump_file, "Function found to be %spure: %s\n",  
> ! 			 this_looping ? "looping " : "",
> ! 			 cgraph_node_name (w)); 
> ! 	      DECL_PURE_P (w->decl) = 1;
> ! 	      DECL_LOOPING_CONST_OR_PURE_P (w->decl) = this_looping;
> ! 	      break;
> ! 	      
> ! 	    default:
> ! 	      break;
> ! 	    }
> ! 	  if (!can_throw && !TREE_NOTHROW (w->decl))
> ! 	    {
> ! 	      if (dump_file)
> ! 		fprintf (dump_file, "Function found to be nothrow: %s\n",  
> ! 			 cgraph_node_name (w));
>   	    }
>   	  w_info = (struct ipa_dfs_info *) w->aux;
>   	  w = w_info->next_cycle;
> *************** struct ipa_opt_pass pass_ipa_pure_const 
> *** 896,898 ****
> --- 817,917 ----
>    NULL,			                /* function_transform */
>    NULL					/* variable_transform */
>   };
> + 
> + static unsigned int
> + local_pure_const (void)
> + {
> +   bool changed = false;
> +   funct_state l =
> +     analyze_function (cgraph_node (current_function_decl), false);
> +   if (!l)
> +     return 0;
> + 
> +   switch (l->pure_const_state)
> +     {
> +     case IPA_CONST:
> +       if (!TREE_READONLY (current_function_decl))
> + 	{
> + 	  TREE_READONLY (current_function_decl) = 1;
> + 	  DECL_LOOPING_CONST_OR_PURE_P (current_function_decl) = l->looping;
> + 	  changed = true;
> + 	  if (dump_file)
> + 	    fprintf (dump_file, "Function found to be %sconst: %s\n",
> + 		     l->looping ? "looping " : "",
> + 		     lang_hooks.decl_printable_name (current_function_decl,
> + 						     2));
> + 	}
> +       else if (DECL_LOOPING_CONST_OR_PURE_P (current_function_decl)
> + 	       && !l->looping)
> + 	{
> + 	  DECL_LOOPING_CONST_OR_PURE_P (current_function_decl) = false;
> + 	  changed = true;
> + 	  if (dump_file)
> + 	    fprintf (dump_file, "Function found to be non-looping: %s\n",
> + 		     lang_hooks.decl_printable_name (current_function_decl,
> + 						     2));
> + 	}
> +       break;
> + 
> +     case IPA_PURE:
> +       if (!TREE_READONLY (current_function_decl))
> + 	{
> + 	  DECL_PURE_P (current_function_decl) = 1;
> + 	  DECL_LOOPING_CONST_OR_PURE_P (current_function_decl) = l->looping;
> + 	  changed = true;
> + 	  if (dump_file)
> + 	    fprintf (dump_file, "Function found to be %spure: %s\n",
> + 		     l->looping ? "looping " : "",
> + 		     lang_hooks.decl_printable_name (current_function_decl,
> + 						     2));
> + 	}
> +       else if (DECL_LOOPING_CONST_OR_PURE_P (current_function_decl)
> + 	       && !l->looping)
> + 	{
> + 	  DECL_LOOPING_CONST_OR_PURE_P (current_function_decl) = false;
> + 	  changed = true;
> + 	  if (dump_file)
> + 	    fprintf (dump_file, "Function found to be non-looping: %s\n",
> + 		     lang_hooks.decl_printable_name (current_function_decl,
> + 						     2));
> + 	}
> +       break;
> + 
> +     default:
> +       break;
> +     }
> +   if (!l->can_throw && !TREE_NOTHROW (current_function_decl))
> +     {
> +       TREE_NOTHROW (current_function_decl) = 1;
> +       changed = true;
> +       if (dump_file)
> + 	fprintf (dump_file, "Function found to be nothrow: %s\n",
> + 		 lang_hooks.decl_printable_name (current_function_decl,
> + 						 2));
> +     }
> +   if (l)
> +     free (l);
> +   if (changed)
> +     return execute_fixup_cfg ();
> +   else
> +     return 0;
> + }
> + 
> + struct gimple_opt_pass pass_local_pure_const =
> + {
> +  {
> +   GIMPLE_PASS,
> +   "local-pure-const",	                /* name */
> +   gate_pure_const,			/* gate */
> +   local_pure_const,		        /* execute */
> +   NULL,					/* sub */
> +   NULL,					/* next */
> +   0,					/* static_pass_number */
> +   TV_IPA_PURE_CONST,		        /* tv_id */
> +   0,	                                /* properties_required */
> +   0,					/* properties_provided */
> +   0,					/* properties_destroyed */
> +   0,					/* todo_flags_start */
> +   0                                     /* todo_flags_finish */
> +  }
> + };
> Index: ipa-inline.c
> ===================================================================
> *** ipa-inline.c	(revision 141798)
> --- ipa-inline.c	(working copy)
> *************** inline_transform (struct cgraph_node *no
> *** 1732,1737 ****
> --- 1732,1738 ----
>         todo = optimize_inline_calls (current_function_decl);
>         timevar_pop (TV_INTEGRATION);
>       }
> +   cfun->after_inlining = true;
>     return todo | execute_fixup_cfg ();
>   }
>   
> Index: tree-eh.c
> ===================================================================
> *** tree-eh.c	(revision 141798)
> --- tree-eh.c	(working copy)
> *************** tree_could_throw_p (tree t)
> *** 2384,2389 ****
> --- 2384,2411 ----
>     return false;
>   }
>   
> + /* Return true if STMT can throw an exception that is not caught within
> +    the current function (CFUN).  */
> + 
> + bool
> + stmt_can_throw_external (gimple stmt)
> + {
> +   int region_nr;
> +   bool is_resx = false;
> + 
> +   if (gimple_code (stmt) == GIMPLE_RESX)
> +     {
> +       region_nr = gimple_resx_region (stmt);
> +       is_resx = true;
> +     }
> +   else
> +     region_nr = lookup_stmt_eh_region (stmt);
> + 
> +   if (region_nr < 0)
> +     return true;
> + 
> +   return can_throw_external_1 (region_nr, is_resx);
> + }
>   
>   /* Return true if STMT can throw an exception that is caught within
>      the current function (CFUN).  */
> Index: tree-optimize.c
> ===================================================================
> *** tree-optimize.c	(revision 141798)
> --- tree-optimize.c	(working copy)
> *************** execute_fixup_cfg (void)
> *** 292,299 ****
>     gimple_stmt_iterator gsi;
>     int todo = gimple_in_ssa_p (cfun) ? TODO_verify_ssa : 0;
>   
> -   cfun->after_inlining = true;
> - 
>     if (cfun->eh)
>       FOR_EACH_BB (bb)
>         {
> --- 292,297 ----
> *************** execute_fixup_cfg (void)
> *** 312,322 ****
>   		if (gimple_in_ssa_p (cfun))
>   		  {
>   		    todo |= TODO_update_ssa | TODO_cleanup_cfg;
>   	            update_stmt (stmt);
>   		  }
>   	      }
>   
> ! 	    if (!stmt_could_throw_p (stmt) && lookup_stmt_eh_region (stmt))
>   	      remove_stmt_from_eh_region (stmt);
>   	  }
>   
> --- 310,321 ----
>   		if (gimple_in_ssa_p (cfun))
>   		  {
>   		    todo |= TODO_update_ssa | TODO_cleanup_cfg;
> +                     mark_symbols_for_renaming (stmt);
>   	            update_stmt (stmt);
>   		  }
>   	      }
>   
> ! 	    if (!stmt_could_throw_p (stmt) && lookup_stmt_eh_region (stmt) != -2)
>   	      remove_stmt_from_eh_region (stmt);
>   	  }
>   
> *************** execute_fixup_cfg (void)
> *** 331,336 ****
> --- 330,355 ----
>     return todo;
>   }
>   
> + struct gimple_opt_pass pass_fixup_cfg =
> + {
> +  {
> +   GIMPLE_PASS,
> +   NULL,					/* name */
> +   NULL,					/* gate */
> +   execute_fixup_cfg,		/* execute */
> +   NULL,					/* sub */
> +   NULL,					/* next */
> +   0,					/* static_pass_number */
> +   0,					/* tv_id */
> +   PROP_cfg,				/* properties_required */
> +   0,					/* properties_provided */
> +   0,					/* properties_destroyed */
> +   0,					/* todo_flags_start */
> +   0					/* todo_flags_finish */
> +  }
> + };
> + 
> + 
>   /* Do the actions required to initialize internal data structures used
>      in tree-ssa optimization passes.  */
>   
> Index: tree-flow.h
> ===================================================================
> *** tree-flow.h	(revision 141798)
> --- tree-flow.h	(working copy)
> *************** extern bool operation_could_trap_p (enum
> *** 1071,1076 ****
> --- 1071,1077 ----
>   extern bool stmt_could_throw_p (gimple);
>   extern bool tree_could_throw_p (tree);
>   extern bool stmt_can_throw_internal (gimple);
> + extern bool stmt_can_throw_external (gimple);
>   extern void add_stmt_to_eh_region (gimple, int);
>   extern bool remove_stmt_from_eh_region (gimple);
>   extern bool maybe_clean_or_replace_eh_stmt (gimple, gimple);
> Index: passes.c
> ===================================================================
> *** passes.c	(revision 141798)
> --- passes.c	(working copy)
> *************** init_optimization_passes (void)
> *** 536,541 ****
> --- 536,542 ----
>     NEXT_PASS (pass_early_local_passes);
>       {
>         struct opt_pass **p = &pass_early_local_passes.pass.sub;
> +       NEXT_PASS (pass_fixup_cfg);
>         NEXT_PASS (pass_tree_profile);
>         NEXT_PASS (pass_cleanup_cfg);
>         NEXT_PASS (pass_init_datastructures);
> *************** init_optimization_passes (void)
> *** 562,567 ****
> --- 563,569 ----
>   	  NEXT_PASS (pass_tail_recursion);
>   	  NEXT_PASS (pass_convert_switch);
>             NEXT_PASS (pass_profile);
> +           NEXT_PASS (pass_local_pure_const);
>   	}
>         NEXT_PASS (pass_release_ssa_names);
>         NEXT_PASS (pass_rebuild_cgraph_edges);
> *************** init_optimization_passes (void)
> *** 701,706 ****
> --- 703,709 ----
>         NEXT_PASS (pass_tail_calls);
>         NEXT_PASS (pass_rename_ssa_copies);
>         NEXT_PASS (pass_uncprop);
> +       NEXT_PASS (pass_local_pure_const);
>       }
>     NEXT_PASS (pass_del_ssa);
>     NEXT_PASS (pass_nrv);
>   


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]