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


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

pretty-ipa merge 6: pure-const pass rewrite


Hi,
this patch reorganize pure_const pass.
arlier version was already approved by
Kenny for stage 1 last year:
http://gcc.gnu.org/ml/gcc-patches/2008-11/msg00597.html

There are few improvements on cgraph side I believe I can approve so I will go
ahead and commit the patch.

To summarize changes:

  1) I rewrote local analysis to take into account existence of Gimple tuples
     and SSA infrastructure :)  Pure-const was among first IPA analyzers and
     it cumulated quite few of bitrot.

     I originally attempted to solve the problems in stage3 while ago,
     there was discussion about individual changes
     http://gcc.gnu.org/ml/gcc-patches/2008-09/msg01345.html

     later I ended up rewriting the whole analysis here:
     http://archives.free.net.ph/message/20081113.221759.4dffb1ec.fi.html
  2) We now discover nothrow functions. 
     stmt_can_throw_external was introduced for this reason.

     At a moment we discover nothrow only at local pureconst pass.  At IPA
     pass there is FIXME not setting the nothrow flag.  Problem here is that
     passmanager insist on running verify_cfg on every function body after
     every IPA pass and before fixup_cfg is done we have redundant EH edges
     that cause us to ICE.
     We need to relax this sanity check, but I will do this as followup to
     avoid snowballing effect of all EH related patches I have.

  3) We now do pass 3 times, once as early local pass, once as IPA pass
     and once as late optimization pass.  The reason is that this pass is
     very effective in early passes (basically just missing recursive
     callgraph oppurtunities) and it helps a lot to reduce amount of EH
     control flow and pureness/const discovery helps inliner subtitution
     and hopefully now also aliasing info once alias-improvements merge.
     The effect is really quite important.

     On IPA stage we re-do the job because it is ony place we can handle
     cyclic flow and in late optimization we re-do because for first time
     we use alias analysis to kill memory writes.  We might try to re-visit
     this with alias-improvements, but given the cheapness of analysis and
     quite noticeable effect if we match, I think we should keep it anyway.
     

On tramp3d this patch does:

  on early stage:  1058 pure functions, 2390 const functions, 3996 nothrow
	2009-03-28  Jan Hubicka  <jh@suse.cz>
  on IPA stage: 0 pure 0 const 0 nothrow
  on late stage: 21 pure 19 const 0 nothrow

THis compare well to about 40 pure functions discovered by original pass.

Tramp3d callgraph is largely acyclic so IPA stage does nothing

For Gerald's testcase we have 635 pure, 349 const and 750 nothrow functions.

Bootstrapped/regtested x86_64-linux, since stmt_could_throw_p patch was 
approved in meantime and it corelate a lot with this patch, I am
re-doing full testing from scratch.  Will commit it afterwards if there
are no complains.

	Merge from pretty-ipa:
	* cgraph.c (dump_cgraph_node): Add replace output flag by process.
	* tree-pass.h (function_called_by_processed_nodes_p): Declare.
	* passes.c (function_called_by_processed_nodes_p): New.
	* ipa-pure-const.c (check_call): Fix handling of operands.
	(analyze_function): Dump debug output for skipped bodies.
	(local_pure_const): Use function_called_by_processed_nodes_p.
	* dwarf2out.c (reference_to_unused): Use output.
	* passes.c (do_per_function_toporder): Likewise.

	2008-11-12  Jan Hubicka  <jh@suse.cz>

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

	2008-11-12  Jan Hubicka  <jh@suse.cz>

	* gcc.dg/attr-noinline.c: Avoid pure-const optimization.
	* gcc.dg/pr33826.c: Update dump files.
	* gcc.dg/ipa/ipa-3.c: Avoid pure-const optimization.
	* gcc.dg/ipa/ipa-5.c: Avoid pure-const optimization.

Index: tree-pass.h
===================================================================
--- tree-pass.h	(revision 145195)
+++ tree-pass.h	(working copy)
@@ -389,6 +389,7 @@ extern struct gimple_opt_pass pass_reass
 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;
@@ -524,6 +525,7 @@ extern void execute_pass_list (struct op
 extern void execute_ipa_pass_list (struct opt_pass *);
 extern void print_current_pass (FILE *);
 extern void debug_pass (void);
+extern bool function_called_by_processed_nodes_p (void);
 
 /* Set to true if the pass is called the first time during compilation of the
    current function.  Note that using this information in the optimization
Index: testsuite/gcc.dg/attr-noinline.c
===================================================================
--- testsuite/gcc.dg/attr-noinline.c	(revision 145195)
+++ testsuite/gcc.dg/attr-noinline.c	(working copy)
@@ -1,39 +1,41 @@
 /* { dg-do compile } */
 /* { dg-options "-O2 -finline-functions" } */
 
-static inline void __attribute__((__noinline__)) function_definition(void) {} /* { dg-warning "inline function \[^\n\]* given attribute noinline" "" } */
+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) {}
+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) {}
+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) {} /* { dg-warning "follows declaration with attribute noinline" "" } */
+static inline void function_declaration_noinline_before(void) {t();} /* { dg-warning "follows declaration with attribute noinline" "" } */
 
-static inline void function_declaration_noinline_after(void) {} /* { dg-message "note: previous definition" "" } */
+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) {} /* { dg-warning "follows inline 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) {}
+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) {} /* { dg-message "note: previous definition" "" } */
+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" "" } */
 
@@ -41,7 +43,7 @@ static void function_declaration_noinlin
 
 static inline void function_declaration_noinline_inline_before(void); /* { dg-warning "follows declaration with attribute noinline" "" } */
 
-static void function_declaration_noinline_inline_before(void) {}
+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 145195)
+++ testsuite/gcc.dg/pr33826.c	(working copy)
@@ -3,7 +3,7 @@
 
 /* { dg-do compile } */
 /* { dg-require-effective-target nonpic } */
-/* { dg-options "-O1 -fdump-ipa-pure-const" } */
+/* { dg-options "-O1 -fdump-tree-local-pure-const1 -fdump-ipa-pure-const" } */
 
 int recurese1 (int i)
 {
@@ -30,8 +30,14 @@ int norecurse1b (int i)
   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-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" } } */
@@ -39,3 +45,4 @@ int norecurse1b (int i)
 /* { 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 145195)
+++ testsuite/gcc.dg/ipa/ipa-3.c	(working copy)
@@ -6,8 +6,10 @@
 /* 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 145195)
+++ testsuite/gcc.dg/ipa/ipa-5.c	(working copy)
@@ -5,12 +5,15 @@
 /* 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 145195)
+++ ipa-pure-const.c	(working copy)
@@ -71,6 +71,8 @@ struct funct_state_d 
 {
   /* 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
@@ -80,10 +82,7 @@ struct funct_state_d 
      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; 
+  bool can_throw;
 };
 
 typedef struct funct_state_d * funct_state;
@@ -141,21 +140,15 @@ static inline void 
 check_decl (funct_state local, 
 	    tree t, bool checking_write)
 {
-  /* If the variable has the "used" attribute, treat it as if it had a
-     been touched by the devil.  */
-  if (lookup_attribute ("used", DECL_ATTRIBUTES (t)))
-    {
-      local->pure_const_state = IPA_NEITHER;
-      local->looping = false;
-      return;
-    }
-
+  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;
-      local->looping = false;
+      if (dump_file)
+        fprintf (dump_file, "    Volatile operand is not const/pure");
       return;
     }
 
@@ -163,212 +156,94 @@ check_decl (funct_state local, 
   if (!TREE_STATIC (t) && !DECL_EXTERNAL (t))
     return;
 
+  /* If the variable has the "used" attribute, treat it as if it had a
+     been touched by the devil.  */
+  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;
+    }
+
   /* Since we have dealt with the locals and params cases above, if we
      are CHECKING_WRITE, this cannot be a pure or constant
      function.  */
   if (checking_write) 
     {
       local->pure_const_state = IPA_NEITHER;
-      local->looping = false;
+      if (dump_file)
+        fprintf (dump_file, "    static/global memory write is not const/pure\n");
       return;
     }
 
   if (DECL_EXTERNAL (t) || TREE_PUBLIC (t))
     {
-      /* If the front end set the variable to be READONLY and
-	 constant, we can allow this variable in pure or const
-	 functions but the scope is too large for our analysis to set
-	 these bits ourselves.  */
-      
-      if (TREE_READONLY (t)
-	  && DECL_INITIAL (t)
-	  && is_gimple_min_invariant (DECL_INITIAL (t)))
-	; /* Read of a constant, do not change the function state.  */
+      /* 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;
 	}
     }
-  
-  /* 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;
+  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;
+    }
 }
 
-/* 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.  */
+/* 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 void
-check_tree (funct_state local, tree t, bool checking_write)
+static inline void 
+check_op (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)
+  while (t && handled_component_p (t))
+    t = TREE_OPERAND (t, 0);
+  if (!t)
     return;
-
-  /* Any tree which is volatile disqualifies this function from being
-     const or pure. */
-  if (TREE_THIS_VOLATILE (t))
+  if (INDIRECT_REF_P (t) || TREE_CODE (t) == TARGET_MEM_REF)
     {
-      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) 
-	{
+      if (TREE_THIS_VOLATILE (t)) 
+	{ 
 	  local->pure_const_state = IPA_NEITHER;
-	  local->looping = false;
+	  if (dump_file)
+	    fprintf (dump_file, "    Volatile indirect ref is not const/pure\n");
 	  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);
-}
-
-/* Scan tree T to see if there are any addresses taken in within T.  */
-
-static void 
-look_for_address_of (funct_state local, tree t)
-{
-  if (TREE_CODE (t) == ADDR_EXPR)
-    {
-      tree x = get_base_var (t);
-      if (TREE_CODE (x) == VAR_DECL) 
-	{
-	  check_decl (local, x, false);
-	  
-	  /* Taking the address of something appears to be reasonable
-	     in PURE code.  Not allowed in const.  */
-	  if (local->pure_const_state == IPA_CONST)
+      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 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)
-{
-  look_for_address_of (local, t);
-
-  /* Memcmp and strlen can both trap and they are declared pure.  */
-  if (tree_could_trap_p (t)
-      && local->pure_const_state == IPA_CONST)
-    local->pure_const_state = IPA_PURE;
-
-  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)
-{
-  /* Memcmp and strlen can both trap and they are declared pure.
-     Which seems to imply that we can apply the same rule here.  */
-  if (tree_could_trap_p (t)
-      && local->pure_const_state == IPA_CONST)
-    local->pure_const_state = IPA_PURE;
-    
-  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
    there are any references in the parameters that are not allowed for
    pure or const functions.  Also check to see if this is either an
@@ -377,20 +252,37 @@ get_asm_expr_operands (funct_state local
    the entire call expression.  */
 
 static void
-check_call (funct_state local, gimple call) 
+check_call (funct_state local, gimple call, bool ipa)
 {
   int flags = gimple_call_flags (call);
-  tree lhs, callee_t = gimple_call_fndecl (call);
+  tree 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));
+  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 (gimple_op (call, i)
+	    && tree_could_throw_p (gimple_op (call, i)))
+	  {
+	    if (possibly_throws && flag_non_call_exceptions)
+	      {
+		if (dump_file)
+		  fprintf (dump_file, "    operand can throw; looping\n");
+		local->looping = true;
+	      }
+	    if (possibly_throws_externally)
+	      {
+		if (dump_file)
+		  fprintf (dump_file, "    operand can throw externally\n");
+		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
@@ -411,8 +303,10 @@ check_call (funct_state local, gimple ca
 	 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;
-	  local->looping = false;
 	}
 
       if (DECL_BUILT_IN_CLASS (callee_t) == BUILT_IN_NORMAL)
@@ -420,267 +314,252 @@ check_call (funct_state local, gimple ca
 	  {
 	  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 = false;
+            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. */
-  if (avail == AVAIL_NOT_AVAILABLE || avail == AVAIL_OVERWRITABLE)
+  else if (avail <= AVAIL_OVERWRITABLE || !ipa)
     {
-      if (flags & ECF_PURE) 
+      if (possibly_throws && flag_non_call_exceptions)
+        {
+	  if (dump_file)
+	    fprintf (dump_file, "    can throw; looping\n");
+          local->looping = true;
+	}
+      if (possibly_throws_externally)
+        {
+	  if (dump_file)
+	    {
+	      fprintf (dump_file, "    can throw externally in region %i\n",
+	      	       lookup_stmt_eh_region (call));
+	      if (callee_t)
+		fprintf (dump_file, "     callee:%s\n",
+			 IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (callee_t)));
+	    }
+          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 
-	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;
+	  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.  */
 }
 
-/* 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)
+/* 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)
 {
-  struct cgraph_node *fn = (struct cgraph_node *) wi->info;
-  gimple stmt = gsi_stmt (*gsi_p);
-  funct_state local = get_function_state (fn);
+  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:
-      {
-	/* 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;
-      }
+      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;
-	  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;
-      
+      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;
     }
-  return NULL;
+
+  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 void
-analyze_function (struct cgraph_node *fn)
+static funct_state
+analyze_function (struct cgraph_node *fn, bool ipa)
 {
   tree decl = fn->decl;
-  funct_state l = XCNEW (struct funct_state_d);
-
- if (cgraph_function_body_availability (fn) <= AVAIL_OVERWRITABLE)
-   return;
+  tree old_decl = current_function_decl;
+  funct_state l;
+  basic_block this_block;
 
-  set_function_state (fn, l);
+  if (cgraph_function_body_availability (fn) <= AVAIL_OVERWRITABLE)
+    {
+      if (dump_file)
+        fprintf (dump_file, "Function is not available or overwrittable; not analyzing.\n");
+      return NULL;
+    }
 
+  l = XCNEW (struct funct_state_d);
   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;
+  l->state_set_in_source = IPA_NEITHER;
+  l->looping = false;
+  l->can_throw = 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))
+  if (dump_file)
     {
-      l->pure_const_state = IPA_NEITHER;
-      return;
+      fprintf (dump_file, "\n\n local analysis of %s\n ", 
+	       cgraph_node_name (fn));
     }
-
-  if (TREE_READONLY (decl))
+  
+  push_cfun (DECL_STRUCT_FUNCTION (decl));
+  current_function_decl = decl;
+  
+  FOR_EACH_BB (this_block)
     {
-      l->pure_const_state = IPA_CONST;
-      l->state_set_in_source = true;
+      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;
+	}
     }
-  if (DECL_PURE_P (decl))
+
+end:
+  if (l->pure_const_state != IPA_NEITHER)
     {
-      l->pure_const_state = IPA_PURE;
-      l->state_set_in_source = true;
+      /* Const functions cannot have back edges (an
+	 indication of possible infinite loop side
+	 effect.  */
+      if (mark_dfs_back_edges ())
+	l->looping = true;
+      
     }
 
-  if (dump_file)
+  if (TREE_READONLY (decl))
     {
-      fprintf (dump_file, "\n local analysis of %s with initial value = %d\n ", 
-	       cgraph_node_name (fn),
-	       l->pure_const_state);
+      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 (!l->state_set_in_source)
+  if (DECL_PURE_P (decl))
     {
-      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 ();
-	}
+      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;
 
-end:
+  pop_cfun ();
+  current_function_decl = old_decl;
   if (dump_file)
     {
-      fprintf (dump_file, "after local analysis of %s with initial value = %d\n ", 
-	       cgraph_node_name (fn),
-	       l->pure_const_state);
+      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.  */
@@ -694,7 +573,7 @@ add_new_function (struct cgraph_node *no
      since all we would be interested in are the addressof
      operations.  */
   visited_nodes = pointer_set_create ();
-  analyze_function (node);
+  set_function_state (node, analyze_function (node, true));
   pointer_set_destroy (visited_nodes);
   visited_nodes = NULL;
 }
@@ -755,7 +634,7 @@ generate_summary (void)
   */
   for (node = cgraph_nodes; node; node = node->next)
     if (cgraph_function_body_availability (node) > AVAIL_OVERWRITABLE)
-      analyze_function (node);
+      set_function_state (node, analyze_function (node, true));
 
   pointer_set_destroy (visited_nodes);
   visited_nodes = NULL;
@@ -795,6 +674,7 @@ propagate (void)
     {
       enum pure_const_state_e pure_const_state = IPA_CONST;
       bool looping = false;
+      bool can_throw = false;
       int count = 0;
       node = order[i];
 
@@ -802,38 +682,44 @@ propagate (void)
       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) 
+	  if (pure_const_state == IPA_NEITHER
+	      && can_throw)
 	    break;
 
-	  if (!w_l->state_set_in_source)
+	  count++;
+
+	  if (count > 1)
+	    looping = true;
+		
+	  for (e = w->callees; e; e = e->next_callee) 
 	    {
-	      struct cgraph_edge *e;
-	      count++;
+	      struct cgraph_node *y = e->callee;
 
-	      if (count > 1)
-		looping = true;
-		    
-	      for (e = w->callees; e; e = e->next_callee) 
+	      if (cgraph_function_body_availability (y) > AVAIL_OVERWRITABLE)
 		{
-		  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;
-		    }
+		  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
+		      && can_throw) 
+		    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;
@@ -846,36 +732,52 @@ propagate (void)
       while (w)
 	{
 	  funct_state w_l = get_function_state (w);
+	  enum pure_const_state_e this_state = pure_const_state;
+	  bool this_looping = looping;
 
-	  /* All nodes within a cycle share the same info.  */
-	  if (!w_l->state_set_in_source)
+	  if (w_l->state_set_in_source != IPA_NEITHER)
 	    {
-	      w_l->pure_const_state = pure_const_state;
-	      w_l->looping = looping;
+	      if (this_state > w_l->state_set_in_source)
+	        this_state = w_l->state_set_in_source;
+	      this_looping = false;
+	    }
 
-	      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;
-		}
+	  /* 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))
+	    {
+	      /* FIXME: TREE_NOTHROW is not set because passmanager will execute
+	         verify_ssa and verify_cfg on every function.  Before fixup_cfg is done,
+	         those functions are going to have NOTHROW calls in EH regions reulting
+	         in ICE.  */
+	      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;
@@ -935,3 +837,120 @@ struct ipa_opt_pass pass_ipa_pure_const 
  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;
+
+  /* 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.  */
+
+  if (function_called_by_processed_nodes_p ())
+    {
+      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: tree-eh.c
===================================================================
--- tree-eh.c	(revision 145195)
+++ tree-eh.c	(working copy)
@@ -2410,6 +2410,32 @@ tree_could_throw_p (tree t)
   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;
+  bool inlinable_call = 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, inlinable_call);
+}
 
 /* Return true if STMT can throw an exception that is caught within
    the current function (CFUN).  */
Index: tree-optimize.c
===================================================================
--- tree-optimize.c	(revision 145195)
+++ tree-optimize.c	(working copy)
@@ -311,6 +311,7 @@ execute_fixup_cfg (void)
 		if (gimple_in_ssa_p (cfun))
 		  {
 		    todo |= TODO_update_ssa | TODO_cleanup_cfg;
+		    mark_symbols_for_renaming (stmt);
 	            update_stmt (stmt);
 		  }
 	      }
Index: tree-flow.h
===================================================================
--- tree-flow.h	(revision 145195)
+++ tree-flow.h	(working copy)
@@ -1077,6 +1077,7 @@ extern bool operation_could_trap_p (enum
 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 145195)
+++ passes.c	(working copy)
@@ -563,6 +563,7 @@ init_optimization_passes (void)
 	  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);
@@ -702,6 +703,7 @@ init_optimization_passes (void)
       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);
@@ -1373,4 +1375,30 @@ execute_ipa_pass_list (struct opt_pass *
   while (pass);
 }
 
+/* Called by local passes to see if function is called by already processed nodes.
+   Because we process nodes in topological order, this means that function is
+   in recursive cycle or we introduced new direct calls.  */
+bool
+function_called_by_processed_nodes_p (void)
+{
+  struct cgraph_edge *e;
+  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 || (!e->caller->needed && !e->caller->reachable))
+        continue;
+      if (TREE_ASM_WRITTEN (e->caller->decl))
+        continue;
+      if (!e->caller->process && !e->caller->global.inlined_to)
+      	break;
+    }
+  if (dump_file && e)
+    {
+      fprintf (dump_file, "Already processed call to:\n");
+      dump_cgraph_node (dump_file, e->caller);
+    }
+  return e != NULL;
+}
+
 #include "gt-passes.h"


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