public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* Rewrite of pure/const pass and nothrow stuff
@ 2008-11-13 20:08 Jan Hubicka
  2008-11-13 20:50 ` Kenneth Zadeck
                   ` (3 more replies)
  0 siblings, 4 replies; 7+ messages in thread
From: Jan Hubicka @ 2008-11-13 20:08 UTC (permalink / raw)
  To: gcc-patches, dnovillo, zadeck

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.

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

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Rewrite of pure/const pass and nothrow stuff
  2008-11-13 20:08 Rewrite of pure/const pass and nothrow stuff Jan Hubicka
@ 2008-11-13 20:50 ` Kenneth Zadeck
  2008-11-13 22:18 ` Diego Novillo
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 7+ messages in thread
From: Kenneth Zadeck @ 2008-11-13 20:50 UTC (permalink / raw)
  To: Jan Hubicka; +Cc: gcc-patches, dnovillo

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

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Rewrite of pure/const pass and nothrow stuff
  2008-11-13 20:08 Rewrite of pure/const pass and nothrow stuff Jan Hubicka
  2008-11-13 20:50 ` Kenneth Zadeck
@ 2008-11-13 22:18 ` Diego Novillo
  2008-11-13 22:53   ` Jan Hubicka
  2008-11-14 13:03 ` Rafael Espindola
       [not found] ` <4aca3dc20811131159q539ac15bg837267aef5c85ff8@mail.gmail.com>
  3 siblings, 1 reply; 7+ messages in thread
From: Diego Novillo @ 2008-11-13 22:18 UTC (permalink / raw)
  To: Jan Hubicka; +Cc: gcc-patches, zadeck

On Thu, Nov 13, 2008 at 14:08, Jan Hubicka <jh@suse.cz> wrote:

> 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.

Thanks!

Shouldn't you also remove pass_set_nothrow_function_flags?

The patch does not apply to LTO.  I suppose that it relies on the
previous patch you applied to the branch?  I will try to do a
merge after you commit to pretty-ipa.

A couple of nits.

>
> ! static void
> ! check_stmt (gimple_stmt_iterator *gsip, funct_state local, bool ipa)
>  {

Needs comment.

> +
> + static unsigned int
> + local_pure_const (void)
> + {

Needs comment.


Diego.

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Rewrite of pure/const pass and nothrow stuff
  2008-11-13 22:18 ` Diego Novillo
@ 2008-11-13 22:53   ` Jan Hubicka
  0 siblings, 0 replies; 7+ messages in thread
From: Jan Hubicka @ 2008-11-13 22:53 UTC (permalink / raw)
  To: Diego Novillo; +Cc: Jan Hubicka, gcc-patches, zadeck

> On Thu, Nov 13, 2008 at 14:08, Jan Hubicka <jh@suse.cz> wrote:
> 
> > 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.
> 
> Thanks!
> 
> Shouldn't you also remove pass_set_nothrow_function_flags?

It is still looking for the all_throwers_are_sibcalls, so we will need
to keep this analysis and I am just running a test that we don't find
more positives.
> 
> The patch does not apply to LTO.  I suppose that it relies on the
> previous patch you applied to the branch?  I will try to do a
> merge after you commit to pretty-ipa.

I will send combined patch after more testing.  Just found small problem
in nothrow discovery so we now get 500 more hits on tramp3d.
Unfortunately now we get some failures in testsuite because we now
optimize too much so I need to update testsuite now too.

Honza
> 
> A couple of nits.
> 
> >
> > ! static void
> > ! check_stmt (gimple_stmt_iterator *gsip, funct_state local, bool ipa)
> >  {
> 
> Needs comment.
> 
> > +
> > + static unsigned int
> > + local_pure_const (void)
> > + {
> 
> Needs comment.
> 
> 
> Diego.

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Rewrite of pure/const pass and nothrow stuff
  2008-11-13 20:08 Rewrite of pure/const pass and nothrow stuff Jan Hubicka
  2008-11-13 20:50 ` Kenneth Zadeck
  2008-11-13 22:18 ` Diego Novillo
@ 2008-11-14 13:03 ` Rafael Espindola
       [not found] ` <4aca3dc20811131159q539ac15bg837267aef5c85ff8@mail.gmail.com>
  3 siblings, 0 replies; 7+ messages in thread
From: Rafael Espindola @ 2008-11-14 13:03 UTC (permalink / raw)
  To: Jan Hubicka; +Cc: gcc-patches, dnovillo, zadeck

Sorry for taking so long to reply. Just a small comment.

>  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.

The current pass has a hidden ordering problem. If we have

void f(void) {g()};

the pass will mark g as nothrow and that will invalidate f's eh
regions since the call will be in a eh region but will not throw. An
IPA pass could update f to fix it. With your patch, don't we still
have the same problem in the early and RTL passes?

Thanks!
-- 
Rafael Avila de Espindola

Google | Gordon House | Barrow Street | Dublin 4 | Ireland
Registered in Dublin, Ireland | Registration Number: 368047

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Rewrite of pure/const pass and nothrow stuff
       [not found]   ` <20081113224119.GB11360@kam.mff.cuni.cz>
@ 2008-11-14 23:31     ` Jan Hubicka
  2008-11-14 23:48       ` Jan Hubicka
  0 siblings, 1 reply; 7+ messages in thread
From: Jan Hubicka @ 2008-11-14 23:31 UTC (permalink / raw)
  To: Jan Hubicka; +Cc: gcc-patches, dnovillo

Hi,
this is variant of patch I comitted.  Several minnor issues:
  - some of testsuite needed updating
  - stmt_can_throw_external had bug returning true for non-trhowing
    statements (it was ignored in this case except for calls)
  - cgraph_function_body_availability had important bug returning true
    for weak functions.  This bug is on mainline too, will send separate
    patch to fix it.

Index: cgraph.c
===================================================================
*** cgraph.c	(revision 141798)
--- cgraph.c	(working copy)
*************** cgraph_function_body_availability (struc
*** 1423,1429 ****
      avail = AVAIL_NOT_AVAILABLE;
    else if (node->local.local)
      avail = AVAIL_LOCAL;
!   else if (node->local.externally_visible)
      avail = AVAIL_AVAILABLE;
  
    /* If the function can be overwritten, return OVERWRITABLE.  Take
--- 1423,1429 ----
      avail = AVAIL_NOT_AVAILABLE;
    else if (node->local.local)
      avail = AVAIL_LOCAL;
!   else if (!node->local.externally_visible)
      avail = AVAIL_AVAILABLE;
  
    /* If the function can be overwritten, return OVERWRITABLE.  Take
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: testsuite/gcc.dg/attr-noinline.c
===================================================================
*** testsuite/gcc.dg/attr-noinline.c	(revision 141798)
--- testsuite/gcc.dg/attr-noinline.c	(working copy)
***************
*** 1,39 ****
  /* { dg-do compile } */
  /* { dg-options "-O2 -finline-functions" } */
  
! static inline void __attribute__((__noinline__)) function_definition(void) {} /* { dg-warning "inline function \[^\n\]* given attribute noinline" "" } */
  
  static inline void __attribute__((__noinline__)) function_declaration_both_before(void); /* { dg-warning "inline function \[^\n\]* given attribute noinline" "" } */
  
! static void function_declaration_both_before(void) {}
  
  static void function_declaration_both_after(void);
  
  static inline void __attribute__((__noinline__)) function_declaration_both_after(void); /* { dg-warning "(inline function \[^\n\]* given attribute noinline|declared inline after its definition)" "" } */
  
! static void function_declaration_both_after(void) {}
  
  static void function_declaration_noinline_before(void) __attribute__((__noinline__)); /* { dg-message "note: previous declaration" "" } */
  
! static inline void function_declaration_noinline_before(void) {} /* { dg-warning "follows declaration with attribute noinline" "" } */
  
! static inline void function_declaration_noinline_after(void) {} /* { dg-message "note: previous definition" "" } */
  
  static void function_declaration_noinline_after(void) __attribute__((__noinline__)); /* { dg-warning "follows inline declaration" "" } */
  
  static inline void function_declaration_inline_before(void); /* { dg-message "note: previous declaration" "" } */
  
! static void __attribute__((__noinline__)) function_declaration_inline_before(void) {} /* { dg-warning "follows inline declaration" "" } */
  
  static inline void function_declaration_inline_noinline_before(void); /* { dg-message "note: previous declaration" "" } */
  
  static void function_declaration_inline_noinline_before(void) __attribute__((__noinline__)); /* { dg-warning "follows inline declaration" "" } */
  
! static void function_declaration_inline_noinline_before(void) {}
  
  static inline void function_declaration_inline_noinline_after(void);
  
! static void function_declaration_inline_noinline_after(void) {} /* { dg-message "note: previous definition" "" } */
  
  static void function_declaration_inline_noinline_after(void) __attribute__((__noinline__)); /* { dg-warning "follows inline declaration" "" } */
  
--- 1,41 ----
  /* { dg-do compile } */
  /* { dg-options "-O2 -finline-functions" } */
  
! extern int t();
! 
! static inline void __attribute__((__noinline__)) function_definition(void) {t();} /* { dg-warning "inline function \[^\n\]* given attribute noinline" "" } */
  
  static inline void __attribute__((__noinline__)) function_declaration_both_before(void); /* { dg-warning "inline function \[^\n\]* given attribute noinline" "" } */
  
! static void function_declaration_both_before(void) {t();}
  
  static void function_declaration_both_after(void);
  
  static inline void __attribute__((__noinline__)) function_declaration_both_after(void); /* { dg-warning "(inline function \[^\n\]* given attribute noinline|declared inline after its definition)" "" } */
  
! static void function_declaration_both_after(void) {t();}
  
  static void function_declaration_noinline_before(void) __attribute__((__noinline__)); /* { dg-message "note: previous declaration" "" } */
  
! static inline void function_declaration_noinline_before(void) {t();} /* { dg-warning "follows declaration with attribute noinline" "" } */
  
! static inline void function_declaration_noinline_after(void) {t();} /* { dg-message "note: previous definition" "" } */
  
  static void function_declaration_noinline_after(void) __attribute__((__noinline__)); /* { dg-warning "follows inline declaration" "" } */
  
  static inline void function_declaration_inline_before(void); /* { dg-message "note: previous declaration" "" } */
  
! static void __attribute__((__noinline__)) function_declaration_inline_before(void) {t();} /* { dg-warning "follows inline declaration" "" } */
  
  static inline void function_declaration_inline_noinline_before(void); /* { dg-message "note: previous declaration" "" } */
  
  static void function_declaration_inline_noinline_before(void) __attribute__((__noinline__)); /* { dg-warning "follows inline declaration" "" } */
  
! static void function_declaration_inline_noinline_before(void) {t();}
  
  static inline void function_declaration_inline_noinline_after(void);
  
! static void function_declaration_inline_noinline_after(void) {t();} /* { dg-message "note: previous definition" "" } */
  
  static void function_declaration_inline_noinline_after(void) __attribute__((__noinline__)); /* { dg-warning "follows inline declaration" "" } */
  
*************** static void function_declaration_noinlin
*** 41,47 ****
  
  static inline void function_declaration_noinline_inline_before(void); /* { dg-warning "follows declaration with attribute noinline" "" } */
  
! static void function_declaration_noinline_inline_before(void) {}
  
  void f () {
    function_definition ();
--- 43,49 ----
  
  static inline void function_declaration_noinline_inline_before(void); /* { dg-warning "follows declaration with attribute noinline" "" } */
  
! static void function_declaration_noinline_inline_before(void) {t();}
  
  void f () {
    function_definition ();
Index: testsuite/gcc.dg/pr33826.c
===================================================================
*** testsuite/gcc.dg/pr33826.c	(revision 141798)
--- testsuite/gcc.dg/pr33826.c	(working copy)
***************
*** 3,9 ****
  
  /* { dg-do compile } */
  /* { dg-require-effective-target nonpic } */
! /* { dg-options "-O1 -fdump-ipa-pure-const" } */
  
  int recurese1 (int i)
  {
--- 3,9 ----
  
  /* { dg-do compile } */
  /* { dg-require-effective-target nonpic } */
! /* { dg-options "-O1 -fdump-tree-local-pure-const1 -fdump-ipa-pure-const" } */
  
  int recurese1 (int i)
  {
*************** int norecurse1b (int i)
*** 30,37 ****
    return i+1;
  }
  
! /* { dg-final { scan-ipa-dump "found to be const: norecurse1a" "pure-const" } } */
! /* { dg-final { scan-ipa-dump "found to be const: norecurse1b" "pure-const" } } */
  /* { dg-final { scan-ipa-dump-not "found to be pure: recurse1" "pure-const" } } */
  /* { dg-final { scan-ipa-dump-not "found to be pure: recurse2a" "pure-const" } } */
  /* { dg-final { scan-ipa-dump-not "found to be pure: recurse2b" "pure-const" } } */
--- 30,43 ----
    return i+1;
  }
  
! /* { dg-final { scan-tree-dump "found to be const: norecurse1a" "local-pure-const1" } } */
! /* { dg-final { scan-tree-dump "found to be const: norecurse1b" "local-pure-const1" } } */
! /* { dg-final { scan-tree-dump-not "found to be pure: recurse1" "local-pure-const1" } } */
! /* { dg-final { scan-tree-dump-not "found to be pure: recurse2a" "local-pure-const1" } } */
! /* { dg-final { scan-tree-dump-not "found to be pure: recurse2b" "local-pure-const1" } } */
! /* { dg-final { scan-tree-dump-not "found to be const: recurse1" "local-pure-const1" } } */
! /* { dg-final { scan-tree-dump-not "found to be const: recurse2a" "local-pure-const1" } } */
! /* { dg-final { scan-tree-dump-not "found to be const: recurse2b" "local-pure-const1" } } */
  /* { dg-final { scan-ipa-dump-not "found to be pure: recurse1" "pure-const" } } */
  /* { dg-final { scan-ipa-dump-not "found to be pure: recurse2a" "pure-const" } } */
  /* { dg-final { scan-ipa-dump-not "found to be pure: recurse2b" "pure-const" } } */
*************** int norecurse1b (int i)
*** 39,41 ****
--- 45,48 ----
  /* { dg-final { scan-ipa-dump-not "found to be const: recurse2a" "pure-const" } } */
  /* { dg-final { scan-ipa-dump-not "found to be const: recurse2b" "pure-const" } } */
  /* { dg-final { cleanup-ipa-dump "pure-const" } } */
+ /* { dg-final { cleanup-tree-dump "local-pure-const1" } } */
Index: testsuite/gcc.dg/ipa/ipa-3.c
===================================================================
*** testsuite/gcc.dg/ipa/ipa-3.c	(revision 141798)
--- testsuite/gcc.dg/ipa/ipa-3.c	(working copy)
***************
*** 6,13 ****
--- 6,15 ----
  /* Double constants.  */
  
  #include <stdio.h>
+ void t(void);
  int g (double b, double c)
  {
+   t();
    return (int)(b+c);  
  }
  int f (double a)
Index: testsuite/gcc.dg/ipa/ipa-5.c
===================================================================
*** testsuite/gcc.dg/ipa/ipa-5.c	(revision 141798)
--- testsuite/gcc.dg/ipa/ipa-5.c	(working copy)
***************
*** 5,16 ****
--- 5,19 ----
  /* Float & short constants.  */
  
  #include <stdio.h>
+ void t(void);
  int g (float b, short c)
  {
+   t();
    return c + (int)b;
  }
  int f (float a)
  {
+   t();
    /* a is modified.  */
    if (a++ > 0)
      g (a, 3);
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 (INDIRECT_REF_P (t) || 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,287 ----
     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 = stmt_could_throw_p (call);
!   bool possibly_throws_externally = (possibly_throws
!   				     && stmt_can_throw_external (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)
--- 302,311 ----
  	 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.  */
--- 313,554 ----
  	  {
  	  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.  */
  }
  
! /* Look into pointer pointed to by GSIP and figure out what interesting side effects
!    it have.  */
! 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, TREE_VALUE (gimple_asm_output_op (stmt, i)), true);
!       for (i = 0; i < gimple_asm_ninputs (stmt); i++)
!          check_op (local, TREE_VALUE (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;
  }
--- 562,568 ----
       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;
--- 623,629 ----
    */
    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 ****
--- 663,669 ----
      {
        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;
--- 671,712 ----
        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;
--- 719,766 ----
        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 ****
--- 820,951 ----
   NULL,			                /* function_transform */
   NULL					/* variable_transform */
  };
+ 
+ /* Simple local pass for pure const discovery reusing the analysis from
+    ipa_pure_const.   This pass is effective when executed together with
+    other optimization passes in early optimization pass queue.  */
+ 
+ static unsigned int
+ local_pure_const (void)
+ {
+   bool changed = false;
+   funct_state l;
+   struct cgraph_edge *e;
+ 
+   /* Because we do not schedule pass_fixup_cfg over whole program after early optimizations
+      we must not promote functions that are called by already processed functions.  This is
+      hitting only the recursive cases that are not too interesting at first place.  
+      FIXME: there should be more robust way of testing this.  */
+ 
+   for (e = cgraph_node (current_function_decl)->callers; e; e = e->next_caller)
+     {
+       if (e->caller->decl == current_function_decl)
+         continue;
+       if (!e->caller->analyzed || !DECL_STRUCT_FUNCTION (e->caller->decl))
+         continue;
+       if (gimple_in_ssa_p (DECL_STRUCT_FUNCTION (e->caller->decl)))
+         break;
+     }
+   if (e)
+     {
+       if (dump_file)
+         fprintf (dump_file, "Function called in recursive cycle; ignoring\n");
+       return 0;
+     }
+ 
+   l = analyze_function (cgraph_node (current_function_decl), false);
+   if (!l)
+     {
+       if (dump_file)
+         fprintf (dump_file, "Function has wrong visibility; ignoring\n");
+       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,2414 ----
    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 (!stmt_could_throw_p (stmt))
+     return 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);

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Rewrite of pure/const pass and nothrow stuff
  2008-11-14 23:31     ` Jan Hubicka
@ 2008-11-14 23:48       ` Jan Hubicka
  0 siblings, 0 replies; 7+ messages in thread
From: Jan Hubicka @ 2008-11-14 23:48 UTC (permalink / raw)
  To: Jan Hubicka; +Cc: gcc-patches, dnovillo

Hi,
this is variant of patch I comitted.  Several minnor issues:
  - some of testsuite needed updating
  - stmt_can_throw_external had bug returning true for non-trhowing
    statements (it was ignored in this case except for calls)
  - cgraph_function_body_availability had important bug returning true
    for weak functions.  This bug is on mainline too, will send separate
    patch to fix it.

Index: cgraph.c
===================================================================
*** cgraph.c	(revision 141798)
--- cgraph.c	(working copy)
*************** cgraph_function_body_availability (struc
*** 1423,1429 ****
      avail = AVAIL_NOT_AVAILABLE;
    else if (node->local.local)
      avail = AVAIL_LOCAL;
!   else if (node->local.externally_visible)
      avail = AVAIL_AVAILABLE;
  
    /* If the function can be overwritten, return OVERWRITABLE.  Take
--- 1423,1429 ----
      avail = AVAIL_NOT_AVAILABLE;
    else if (node->local.local)
      avail = AVAIL_LOCAL;
!   else if (!node->local.externally_visible)
      avail = AVAIL_AVAILABLE;
  
    /* If the function can be overwritten, return OVERWRITABLE.  Take
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: testsuite/gcc.dg/attr-noinline.c
===================================================================
*** testsuite/gcc.dg/attr-noinline.c	(revision 141798)
--- testsuite/gcc.dg/attr-noinline.c	(working copy)
***************
*** 1,39 ****
  /* { dg-do compile } */
  /* { dg-options "-O2 -finline-functions" } */
  
! static inline void __attribute__((__noinline__)) function_definition(void) {} /* { dg-warning "inline function \[^\n\]* given attribute noinline" "" } */
  
  static inline void __attribute__((__noinline__)) function_declaration_both_before(void); /* { dg-warning "inline function \[^\n\]* given attribute noinline" "" } */
  
! static void function_declaration_both_before(void) {}
  
  static void function_declaration_both_after(void);
  
  static inline void __attribute__((__noinline__)) function_declaration_both_after(void); /* { dg-warning "(inline function \[^\n\]* given attribute noinline|declared inline after its definition)" "" } */
  
! static void function_declaration_both_after(void) {}
  
  static void function_declaration_noinline_before(void) __attribute__((__noinline__)); /* { dg-message "note: previous declaration" "" } */
  
! static inline void function_declaration_noinline_before(void) {} /* { dg-warning "follows declaration with attribute noinline" "" } */
  
! static inline void function_declaration_noinline_after(void) {} /* { dg-message "note: previous definition" "" } */
  
  static void function_declaration_noinline_after(void) __attribute__((__noinline__)); /* { dg-warning "follows inline declaration" "" } */
  
  static inline void function_declaration_inline_before(void); /* { dg-message "note: previous declaration" "" } */
  
! static void __attribute__((__noinline__)) function_declaration_inline_before(void) {} /* { dg-warning "follows inline declaration" "" } */
  
  static inline void function_declaration_inline_noinline_before(void); /* { dg-message "note: previous declaration" "" } */
  
  static void function_declaration_inline_noinline_before(void) __attribute__((__noinline__)); /* { dg-warning "follows inline declaration" "" } */
  
! static void function_declaration_inline_noinline_before(void) {}
  
  static inline void function_declaration_inline_noinline_after(void);
  
! static void function_declaration_inline_noinline_after(void) {} /* { dg-message "note: previous definition" "" } */
  
  static void function_declaration_inline_noinline_after(void) __attribute__((__noinline__)); /* { dg-warning "follows inline declaration" "" } */
  
--- 1,41 ----
  /* { dg-do compile } */
  /* { dg-options "-O2 -finline-functions" } */
  
! extern int t();
! 
! static inline void __attribute__((__noinline__)) function_definition(void) {t();} /* { dg-warning "inline function \[^\n\]* given attribute noinline" "" } */
  
  static inline void __attribute__((__noinline__)) function_declaration_both_before(void); /* { dg-warning "inline function \[^\n\]* given attribute noinline" "" } */
  
! static void function_declaration_both_before(void) {t();}
  
  static void function_declaration_both_after(void);
  
  static inline void __attribute__((__noinline__)) function_declaration_both_after(void); /* { dg-warning "(inline function \[^\n\]* given attribute noinline|declared inline after its definition)" "" } */
  
! static void function_declaration_both_after(void) {t();}
  
  static void function_declaration_noinline_before(void) __attribute__((__noinline__)); /* { dg-message "note: previous declaration" "" } */
  
! static inline void function_declaration_noinline_before(void) {t();} /* { dg-warning "follows declaration with attribute noinline" "" } */
  
! static inline void function_declaration_noinline_after(void) {t();} /* { dg-message "note: previous definition" "" } */
  
  static void function_declaration_noinline_after(void) __attribute__((__noinline__)); /* { dg-warning "follows inline declaration" "" } */
  
  static inline void function_declaration_inline_before(void); /* { dg-message "note: previous declaration" "" } */
  
! static void __attribute__((__noinline__)) function_declaration_inline_before(void) {t();} /* { dg-warning "follows inline declaration" "" } */
  
  static inline void function_declaration_inline_noinline_before(void); /* { dg-message "note: previous declaration" "" } */
  
  static void function_declaration_inline_noinline_before(void) __attribute__((__noinline__)); /* { dg-warning "follows inline declaration" "" } */
  
! static void function_declaration_inline_noinline_before(void) {t();}
  
  static inline void function_declaration_inline_noinline_after(void);
  
! static void function_declaration_inline_noinline_after(void) {t();} /* { dg-message "note: previous definition" "" } */
  
  static void function_declaration_inline_noinline_after(void) __attribute__((__noinline__)); /* { dg-warning "follows inline declaration" "" } */
  
*************** static void function_declaration_noinlin
*** 41,47 ****
  
  static inline void function_declaration_noinline_inline_before(void); /* { dg-warning "follows declaration with attribute noinline" "" } */
  
! static void function_declaration_noinline_inline_before(void) {}
  
  void f () {
    function_definition ();
--- 43,49 ----
  
  static inline void function_declaration_noinline_inline_before(void); /* { dg-warning "follows declaration with attribute noinline" "" } */
  
! static void function_declaration_noinline_inline_before(void) {t();}
  
  void f () {
    function_definition ();
Index: testsuite/gcc.dg/pr33826.c
===================================================================
*** testsuite/gcc.dg/pr33826.c	(revision 141798)
--- testsuite/gcc.dg/pr33826.c	(working copy)
***************
*** 3,9 ****
  
  /* { dg-do compile } */
  /* { dg-require-effective-target nonpic } */
! /* { dg-options "-O1 -fdump-ipa-pure-const" } */
  
  int recurese1 (int i)
  {
--- 3,9 ----
  
  /* { dg-do compile } */
  /* { dg-require-effective-target nonpic } */
! /* { dg-options "-O1 -fdump-tree-local-pure-const1 -fdump-ipa-pure-const" } */
  
  int recurese1 (int i)
  {
*************** int norecurse1b (int i)
*** 30,37 ****
    return i+1;
  }
  
! /* { dg-final { scan-ipa-dump "found to be const: norecurse1a" "pure-const" } } */
! /* { dg-final { scan-ipa-dump "found to be const: norecurse1b" "pure-const" } } */
  /* { dg-final { scan-ipa-dump-not "found to be pure: recurse1" "pure-const" } } */
  /* { dg-final { scan-ipa-dump-not "found to be pure: recurse2a" "pure-const" } } */
  /* { dg-final { scan-ipa-dump-not "found to be pure: recurse2b" "pure-const" } } */
--- 30,43 ----
    return i+1;
  }
  
! /* { dg-final { scan-tree-dump "found to be const: norecurse1a" "local-pure-const1" } } */
! /* { dg-final { scan-tree-dump "found to be const: norecurse1b" "local-pure-const1" } } */
! /* { dg-final { scan-tree-dump-not "found to be pure: recurse1" "local-pure-const1" } } */
! /* { dg-final { scan-tree-dump-not "found to be pure: recurse2a" "local-pure-const1" } } */
! /* { dg-final { scan-tree-dump-not "found to be pure: recurse2b" "local-pure-const1" } } */
! /* { dg-final { scan-tree-dump-not "found to be const: recurse1" "local-pure-const1" } } */
! /* { dg-final { scan-tree-dump-not "found to be const: recurse2a" "local-pure-const1" } } */
! /* { dg-final { scan-tree-dump-not "found to be const: recurse2b" "local-pure-const1" } } */
  /* { dg-final { scan-ipa-dump-not "found to be pure: recurse1" "pure-const" } } */
  /* { dg-final { scan-ipa-dump-not "found to be pure: recurse2a" "pure-const" } } */
  /* { dg-final { scan-ipa-dump-not "found to be pure: recurse2b" "pure-const" } } */
*************** int norecurse1b (int i)
*** 39,41 ****
--- 45,48 ----
  /* { dg-final { scan-ipa-dump-not "found to be const: recurse2a" "pure-const" } } */
  /* { dg-final { scan-ipa-dump-not "found to be const: recurse2b" "pure-const" } } */
  /* { dg-final { cleanup-ipa-dump "pure-const" } } */
+ /* { dg-final { cleanup-tree-dump "local-pure-const1" } } */
Index: testsuite/gcc.dg/ipa/ipa-3.c
===================================================================
*** testsuite/gcc.dg/ipa/ipa-3.c	(revision 141798)
--- testsuite/gcc.dg/ipa/ipa-3.c	(working copy)
***************
*** 6,13 ****
--- 6,15 ----
  /* Double constants.  */
  
  #include <stdio.h>
+ void t(void);
  int g (double b, double c)
  {
+   t();
    return (int)(b+c);  
  }
  int f (double a)
Index: testsuite/gcc.dg/ipa/ipa-5.c
===================================================================
*** testsuite/gcc.dg/ipa/ipa-5.c	(revision 141798)
--- testsuite/gcc.dg/ipa/ipa-5.c	(working copy)
***************
*** 5,16 ****
--- 5,19 ----
  /* Float & short constants.  */
  
  #include <stdio.h>
+ void t(void);
  int g (float b, short c)
  {
+   t();
    return c + (int)b;
  }
  int f (float a)
  {
+   t();
    /* a is modified.  */
    if (a++ > 0)
      g (a, 3);
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 (INDIRECT_REF_P (t) || 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,287 ----
     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 = stmt_could_throw_p (call);
!   bool possibly_throws_externally = (possibly_throws
!   				     && stmt_can_throw_external (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)
--- 302,311 ----
  	 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.  */
--- 313,554 ----
  	  {
  	  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.  */
  }
  
! /* Look into pointer pointed to by GSIP and figure out what interesting side effects
!    it have.  */
! 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, TREE_VALUE (gimple_asm_output_op (stmt, i)), true);
!       for (i = 0; i < gimple_asm_ninputs (stmt); i++)
!          check_op (local, TREE_VALUE (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;
  }
--- 562,568 ----
       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;
--- 623,629 ----
    */
    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 ****
--- 663,669 ----
      {
        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;
--- 671,712 ----
        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;
--- 719,766 ----
        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 ****
--- 820,951 ----
   NULL,			                /* function_transform */
   NULL					/* variable_transform */
  };
+ 
+ /* Simple local pass for pure const discovery reusing the analysis from
+    ipa_pure_const.   This pass is effective when executed together with
+    other optimization passes in early optimization pass queue.  */
+ 
+ static unsigned int
+ local_pure_const (void)
+ {
+   bool changed = false;
+   funct_state l;
+   struct cgraph_edge *e;
+ 
+   /* Because we do not schedule pass_fixup_cfg over whole program after early optimizations
+      we must not promote functions that are called by already processed functions.  This is
+      hitting only the recursive cases that are not too interesting at first place.  
+      FIXME: there should be more robust way of testing this.  */
+ 
+   for (e = cgraph_node (current_function_decl)->callers; e; e = e->next_caller)
+     {
+       if (e->caller->decl == current_function_decl)
+         continue;
+       if (!e->caller->analyzed || !DECL_STRUCT_FUNCTION (e->caller->decl))
+         continue;
+       if (gimple_in_ssa_p (DECL_STRUCT_FUNCTION (e->caller->decl)))
+         break;
+     }
+   if (e)
+     {
+       if (dump_file)
+         fprintf (dump_file, "Function called in recursive cycle; ignoring\n");
+       return 0;
+     }
+ 
+   l = analyze_function (cgraph_node (current_function_decl), false);
+   if (!l)
+     {
+       if (dump_file)
+         fprintf (dump_file, "Function has wrong visibility; ignoring\n");
+       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,2414 ----
    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 (!stmt_could_throw_p (stmt))
+     return 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);

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2008-11-14 23:31 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-11-13 20:08 Rewrite of pure/const pass and nothrow stuff Jan Hubicka
2008-11-13 20:50 ` Kenneth Zadeck
2008-11-13 22:18 ` Diego Novillo
2008-11-13 22:53   ` Jan Hubicka
2008-11-14 13:03 ` Rafael Espindola
     [not found] ` <4aca3dc20811131159q539ac15bg837267aef5c85ff8@mail.gmail.com>
     [not found]   ` <20081113224119.GB11360@kam.mff.cuni.cz>
2008-11-14 23:31     ` Jan Hubicka
2008-11-14 23:48       ` Jan Hubicka

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).