* gcc.dg/attr-noinline.c: Avoid pure-const optimization.
authorhubicka <hubicka@138bc75d-0d04-0410-961f-82ee72b054a4>
Sat, 28 Mar 2009 19:29:35 +0000 (19:29 +0000)
committerhubicka <hubicka@138bc75d-0d04-0410-961f-82ee72b054a4>
Sat, 28 Mar 2009 19:29:35 +0000 (19:29 +0000)
* 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.

Merge from pretty-ipa:

2009-03-27  Jan Hubicka  <jh@suse.cz>
* 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.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@145204 138bc75d-0d04-0410-961f-82ee72b054a4

12 files changed:
gcc/ChangeLog
gcc/ipa-pure-const.c
gcc/passes.c
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/attr-noinline.c
gcc/testsuite/gcc.dg/ipa/ipa-3.c
gcc/testsuite/gcc.dg/ipa/ipa-5.c
gcc/testsuite/gcc.dg/pr33826.c
gcc/tree-eh.c
gcc/tree-flow.h
gcc/tree-optimize.c
gcc/tree-pass.h

index a977546..ee45bf8 100644 (file)
@@ -1,3 +1,40 @@
+2009-03-28  Jan Hubicka  <jh@suse.cz>
+
+       Merge from pretty-ipa:
+
+       2009-03-27  Jan Hubicka  <jh@suse.cz>
+       * 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.
 2009-03-28  Martin Jambor  <mjambor@suse.cz>
 
        * fold-const.c (get_pointer_modulus_and_residue): New parameter
index f21638f..21ecfd5 100644 (file)
@@ -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))
-    {
-      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))
+  if (INDIRECT_REF_P (t) || TREE_CODE (t) == TARGET_MEM_REF)
     {
-      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, gimple stmt)
    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);
+  bool possibly_throws = stmt_could_throw_p (call);
+  bool possibly_throws_externally = (possibly_throws
+                                    && stmt_can_throw_external (call));
 
-  for (i = 0; i < gimple_call_num_args (call); i++)
-    check_rhs_var (local, gimple_call_arg (call, i));
+  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 call)
         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 call)
          {
          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)
+/* 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 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);
+  gimple stmt = gsi_stmt (*gsip);
+  unsigned int i = 0;
+  bitmap_iterator bi;
 
-  switch (TREE_CODE (t))  
+  if (dump_file)
     {
-    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;
+      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;
+       }
     }
-  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;
-      }
+      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;
-
-  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;
+  tree old_decl = current_function_decl;
+  funct_state l;
+  basic_block this_block;
 
-  /* 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 (cgraph_function_body_availability (fn) <= AVAIL_OVERWRITABLE)
     {
-      l->pure_const_state = IPA_NEITHER;
-      return;
+      if (dump_file)
+        fprintf (dump_file, "Function is not available or overwrittable; not analyzing.\n");
+      return NULL;
     }
 
-  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;
-    }
+  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 local analysis of %s with initial value = %d\n ", 
-              cgraph_node_name (fn),
-              l->pure_const_state);
+      fprintf (dump_file, "\n\n local analysis of %s\n ", 
+              cgraph_node_name (fn));
     }
   
-  if (!l->state_set_in_source)
+  push_cfun (DECL_STRUCT_FUNCTION (decl));
+  current_function_decl = decl;
+  
+  FOR_EACH_BB (this_block)
     {
-      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;
-           }
-       }
+      gimple_stmt_iterator gsi;
+      struct walk_stmt_info wi;
 
-      if (l->pure_const_state != IPA_NEITHER)
+      memset (&wi, 0, sizeof(wi));
+      for (gsi = gsi_start_bb (this_block);
+          !gsi_end_p (gsi);
+          gsi_next (&gsi))
        {
-         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 ();
+         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)
     {
-      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 *node, void *data ATTRIBUTE_UNUSED)
      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 09094ca..5913464 100644 (file)
@@ -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 *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 467691c..7a870c2 100644 (file)
@@ -1,3 +1,10 @@
+2009-03-28  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.
+
 2009-03-28  Martin Jambor  <mjambor@suse.cz>
 
        * g++.dg/tree-ssa/fwprop-align.C: New test.
index 9f7abcd..7d57c01 100644 (file)
@@ -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_noinline_inline_before(void) __attribute__((__n
 
 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 c476b92..6eb3da4 100644 (file)
@@ -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 9352e19..9e8006a 100644 (file)
@@ -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 3e08b14..c091bbc 100644 (file)
@@ -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 c992f6d..9febe5a 100644 (file)
@@ -2402,6 +2402,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 c5b9bd7..07fb9be 100644 (file)
@@ -1077,6 +1077,7 @@ extern bool operation_could_trap_p (enum tree_code, bool, bool, tree);
 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 13a50c0..d69fd29 100644 (file)
@@ -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 2f4fed8..e8f0626 100644 (file)
@@ -389,6 +389,7 @@ extern struct gimple_opt_pass pass_reassoc;
 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 opt_pass *);
 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