re PR target/60693 (ICE on funny memcpy)
[gcc.git] / gcc / ipa-pure-const.c
index 341501ea172e73e86e42c2b61d4eb2939757d44c..7d35880996503f1ba44590c6862876cf67428f72 100644 (file)
@@ -1,6 +1,5 @@
 /* Callgraph based analysis of static variables.
-   Copyright (C) 2004, 2005, 2007, 2008, 2009, 2010
-   Free Software Foundation, Inc.
+   Copyright (C) 2004-2014 Free Software Foundation, Inc.
    Contributed by Kenneth Zadeck <zadeck@naturalbridge.com>
 
 This file is part of GCC.
@@ -37,15 +36,23 @@ along with GCC; see the file COPYING3.  If not see
 #include "coretypes.h"
 #include "tm.h"
 #include "tree.h"
-#include "tree-flow.h"
+#include "print-tree.h"
+#include "calls.h"
+#include "basic-block.h"
+#include "tree-ssa-alias.h"
+#include "internal-fn.h"
+#include "tree-eh.h"
+#include "gimple-expr.h"
+#include "is-a.h"
+#include "gimple.h"
+#include "gimple-iterator.h"
+#include "gimple-walk.h"
+#include "tree-cfg.h"
+#include "tree-ssa-loop-niter.h"
 #include "tree-inline.h"
 #include "tree-pass.h"
 #include "langhooks.h"
-#include "pointer-set.h"
-#include "ggc.h"
 #include "ipa-utils.h"
-#include "gimple.h"
-#include "cgraph.h"
 #include "flags.h"
 #include "diagnostic.h"
 #include "gimple-pretty-print.h"
@@ -180,7 +187,7 @@ warn_function_const (tree decl, bool known_finite)
                         known_finite, warned_about, "const");
 }
 
-void
+static void
 warn_function_noreturn (tree decl)
 {
   static struct pointer_set_t *warned_about;
@@ -191,15 +198,6 @@ warn_function_noreturn (tree decl)
                           true, warned_about, "noreturn");
 }
 
-/* Init the function state.  */
-
-static void
-finish_state (void)
-{
-  funct_state_vec.release ();
-}
-
-
 /* Return true if we have a function state for NODE.  */
 
 static inline bool
@@ -407,6 +405,7 @@ better_state (enum pure_const_state_e *state, bool *looping,
        *looping = looping2;
       else
        *looping = MIN (*looping, looping2);
+      *state = state2;
     }
   else if (state2 != IPA_NEITHER)
     *looping = MIN (*looping, looping2);
@@ -542,7 +541,8 @@ check_call (funct_state local, gimple call, bool ipa)
     }
 
   /* When not in IPA mode, we can still handle self recursion.  */
-  if (!ipa && callee_t == current_function_decl)
+  if (!ipa && callee_t
+      && recursive_call_p (current_function_decl, callee_t))
     {
       if (dump_file)
         fprintf (dump_file, "    Recursive call can loop.\n");
@@ -589,7 +589,7 @@ check_call (funct_state local, gimple call, bool ipa)
 /* Wrapper around check_decl for loads in local more.  */
 
 static bool
-check_load (gimple stmt ATTRIBUTE_UNUSED, tree op, void *data)
+check_load (gimple, tree op, tree, void *data)
 {
   if (DECL_P (op))
     check_decl ((funct_state)data, op, false, false);
@@ -601,7 +601,7 @@ check_load (gimple stmt ATTRIBUTE_UNUSED, tree op, void *data)
 /* Wrapper around check_decl for stores in local more.  */
 
 static bool
-check_store (gimple stmt ATTRIBUTE_UNUSED, tree op, void *data)
+check_store (gimple, tree op, tree, void *data)
 {
   if (DECL_P (op))
     check_decl ((funct_state)data, op, true, false);
@@ -613,7 +613,7 @@ check_store (gimple stmt ATTRIBUTE_UNUSED, tree op, void *data)
 /* Wrapper around check_decl for loads in ipa mode.  */
 
 static bool
-check_ipa_load (gimple stmt ATTRIBUTE_UNUSED, tree op, void *data)
+check_ipa_load (gimple, tree op, tree, void *data)
 {
   if (DECL_P (op))
     check_decl ((funct_state)data, op, false, true);
@@ -625,7 +625,7 @@ check_ipa_load (gimple stmt ATTRIBUTE_UNUSED, tree op, void *data)
 /* Wrapper around check_decl for stores in ipa mode.  */
 
 static bool
-check_ipa_store (gimple stmt ATTRIBUTE_UNUSED, tree op, void *data)
+check_ipa_store (gimple, tree op, tree, void *data)
 {
   if (DECL_P (op))
     check_decl ((funct_state)data, op, true, true);
@@ -725,7 +725,7 @@ check_stmt (gimple_stmt_iterator *gsip, funct_state local, bool ipa)
 static funct_state
 analyze_function (struct cgraph_node *fn, bool ipa)
 {
-  tree decl = fn->symbol.decl;
+  tree decl = fn->decl;
   funct_state l;
   basic_block this_block;
 
@@ -736,7 +736,7 @@ analyze_function (struct cgraph_node *fn, bool ipa)
   l->looping = false;
   l->can_throw = false;
   state_from_flags (&l->state_previously_known, &l->looping_previously_known,
-                   flags_from_decl_or_type (fn->symbol.decl),
+                   flags_from_decl_or_type (fn->decl),
                    cgraph_node_cannot_return (fn));
 
   if (fn->thunk.thunk_p || fn->alias)
@@ -749,17 +749,17 @@ analyze_function (struct cgraph_node *fn, bool ipa)
   if (dump_file)
     {
       fprintf (dump_file, "\n\n local analysis of %s\n ",
-              cgraph_node_name (fn));
+              fn->name ());
     }
 
   push_cfun (DECL_STRUCT_FUNCTION (decl));
 
-  FOR_EACH_BB (this_block)
+  FOR_EACH_BB_FN (this_block, cfun)
     {
       gimple_stmt_iterator gsi;
       struct walk_stmt_info wi;
 
-      memset (&wi, 0, sizeof(wi));
+      memset (&wi, 0, sizeof (wi));
       for (gsi = gsi_start_bb (this_block);
           !gsi_end_p (gsi);
           gsi_next (&gsi))
@@ -780,8 +780,10 @@ end:
         {
          /* Preheaders are needed for SCEV to work.
             Simple latches and recorded exits improve chances that loop will
-            proved to be finite in testcases such as in loop-15.c and loop-24.c  */
-         loop_optimizer_init (LOOPS_NORMAL
+            proved to be finite in testcases such as in loop-15.c
+            and loop-24.c  */
+         loop_optimizer_init (LOOPS_HAVE_PREHEADERS
+                              | LOOPS_HAVE_SIMPLE_LATCHES
                               | LOOPS_HAVE_RECORDED_EXITS);
          if (dump_file && (dump_flags & TDF_DETAILS))
            flow_loops_dump (dump_file, NULL, 0);
@@ -793,16 +795,16 @@ end:
            }
          else
            {
-             loop_iterator li;
              struct loop *loop;
              scev_initialize ();
-             FOR_EACH_LOOP (li, loop, 0)
+             FOR_EACH_LOOP (loop, 0)
                if (!finite_loop_p (loop))
                  {
                    if (dump_file)
-                     fprintf (dump_file, "    can not prove finiteness of loop %i\n", loop->num);
+                     fprintf (dump_file, "    can not prove finiteness of "
+                              "loop %i\n", loop->num);
                    l->looping =true;
-                   FOR_EACH_LOOP_BREAK (li);
+                   break;
                  }
              scev_finalize ();
            }
@@ -904,7 +906,7 @@ register_hooks (void)
    CONST.  */
 
 static void
-generate_summary (void)
+pure_const_generate_summary (void)
 {
   struct cgraph_node *node;
 
@@ -949,7 +951,7 @@ pure_const_write_summary (void)
        lsei_next_function_in_partition (&lsei))
     {
       node = lsei_cgraph_node (lsei);
-      if (node->analyzed && has_function_state (node))
+      if (node->definition && has_function_state (node))
        count++;
     }
 
@@ -960,7 +962,7 @@ pure_const_write_summary (void)
        lsei_next_function_in_partition (&lsei))
     {
       node = lsei_cgraph_node (lsei);
-      if (node->analyzed && has_function_state (node))
+      if (node->definition && has_function_state (node))
        {
          struct bitpack_d bp;
          funct_state fs;
@@ -970,7 +972,7 @@ pure_const_write_summary (void)
          fs = get_function_state (node);
 
          encoder = ob->decl_state->symtab_node_encoder;
-         node_ref = lto_symtab_encoder_encode (encoder, (symtab_node)node);
+         node_ref = lto_symtab_encoder_encode (encoder, node);
          streamer_write_uhwi_stream (ob->main_stream, node_ref);
 
          /* Note that flags will need to be read in the opposite
@@ -1039,10 +1041,10 @@ pure_const_read_summary (void)
              fs->can_throw = bp_unpack_value (&bp, 1);
              if (dump_file)
                {
-                 int flags = flags_from_decl_or_type (node->symbol.decl);
+                 int flags = flags_from_decl_or_type (node->decl);
                  fprintf (dump_file, "Read info for %s/%i ",
-                          cgraph_node_name (node),
-                          node->uid);
+                          node->name (),
+                          node->order);
                  if (flags & ECF_CONST)
                    fprintf (dump_file, " const");
                  if (flags & ECF_PURE)
@@ -1077,8 +1079,9 @@ ignore_edge (struct cgraph_edge *e)
 }
 
 /* Return true if NODE is self recursive function.
-   ??? self recursive and indirectly recursive funcions should
-   be the same, so this function seems unnecesary.  */
+   Indirectly recursive functions appears as non-trivial strongly
+   connected components, so we need to care about self recursion
+   only.  */
 
 static bool
 self_recursive_p (struct cgraph_node *node)
@@ -1108,7 +1111,7 @@ propagate_pure_const (void)
   if (dump_file)
     {
       dump_cgraph (dump_file);
-      ipa_print_order(dump_file, "reduced", order, order_pos);
+      ipa_print_order (dump_file, "reduced", order, order_pos);
     }
 
   /* Propagate the local information through the call graph to produce
@@ -1140,8 +1143,8 @@ propagate_pure_const (void)
          funct_state w_l = get_function_state (w);
          if (dump_file && (dump_flags & TDF_DETAILS))
            fprintf (dump_file, "  Visiting %s/%i state:%s looping %i\n",
-                    cgraph_node_name (w),
-                    w->uid,
+                    w->name (),
+                    w->order,
                     pure_const_names[w_l->pure_const_state],
                     w_l->looping);
 
@@ -1187,8 +1190,8 @@ propagate_pure_const (void)
                {
                  fprintf (dump_file,
                           "    Call to %s/%i",
-                          cgraph_node_name (e->callee),
-                          e->callee->uid);
+                          e->callee->name (),
+                          e->callee->order);
                }
              if (avail > AVAIL_OVERWRITABLE)
                {
@@ -1217,11 +1220,11 @@ propagate_pure_const (void)
                    }
                }
              else if (special_builtin_state (&edge_state, &edge_looping,
-                                              y->symbol.decl))
+                                              y->decl))
                ;
              else
                state_from_flags (&edge_state, &edge_looping,
-                                 flags_from_decl_or_type (y->symbol.decl),
+                                 flags_from_decl_or_type (y->decl),
                                  cgraph_edge_cannot_lead_to_return (e));
 
              /* Merge the results with what we already know.  */
@@ -1260,7 +1263,7 @@ propagate_pure_const (void)
            break;
 
          /* And finally all loads and stores.  */
-         for (i = 0; ipa_ref_list_reference_iterate (&w->symbol.ref_list, i, ref); i++)
+         for (i = 0; ipa_ref_list_reference_iterate (&w->ref_list, i, ref); i++)
            {
              enum pure_const_state_e ref_state = IPA_CONST;
              bool ref_looping = false;
@@ -1268,7 +1271,7 @@ propagate_pure_const (void)
                {
                case IPA_REF_LOAD:
                  /* readonly reads are safe.  */
-                 if (TREE_READONLY (ipa_ref_varpool_node (ref)->symbol.decl))
+                 if (TREE_READONLY (ipa_ref_varpool_node (ref)->decl))
                    break;
                  if (dump_file && (dump_flags & TDF_DETAILS))
                    fprintf (dump_file, "    nonreadonly global var read\n");
@@ -1292,7 +1295,7 @@ propagate_pure_const (void)
              if (pure_const_state == IPA_NEITHER)
                break;
            }
-         w_info = (struct ipa_dfs_info *) w->symbol.aux;
+         w_info = (struct ipa_dfs_info *) w->aux;
          w = w_info->next_cycle;
        }
       if (dump_file && (dump_flags & TDF_DETAILS))
@@ -1324,36 +1327,40 @@ propagate_pure_const (void)
          w_l->pure_const_state = this_state;
          w_l->looping = this_looping;
 
-         switch (this_state)
-           {
-           case IPA_CONST:
-             if (!TREE_READONLY (w->symbol.decl))
-               {
-                 warn_function_const (w->symbol.decl, !this_looping);
-                 if (dump_file)
-                   fprintf (dump_file, "Function found to be %sconst: %s\n",
-                            this_looping ? "looping " : "",
-                            cgraph_node_name (w));
-               }
-             cgraph_set_const_flag (w, true, this_looping);
-             break;
+         /* Inline clones share declaration with their offline copies;
+            do not modify their declarations since the offline copy may
+            be different.  */
+         if (!w->global.inlined_to)
+           switch (this_state)
+             {
+             case IPA_CONST:
+               if (!TREE_READONLY (w->decl))
+                 {
+                   warn_function_const (w->decl, !this_looping);
+                   if (dump_file)
+                     fprintf (dump_file, "Function found to be %sconst: %s\n",
+                              this_looping ? "looping " : "",
+                              w->name ());
+                 }
+               cgraph_set_const_flag (w, true, this_looping);
+               break;
 
-           case IPA_PURE:
-             if (!DECL_PURE_P (w->symbol.decl))
-               {
-                 warn_function_pure (w->symbol.decl, !this_looping);
-                 if (dump_file)
-                   fprintf (dump_file, "Function found to be %spure: %s\n",
-                            this_looping ? "looping " : "",
-                            cgraph_node_name (w));
-               }
-             cgraph_set_pure_flag (w, true, this_looping);
-             break;
+             case IPA_PURE:
+               if (!DECL_PURE_P (w->decl))
+                 {
+                   warn_function_pure (w->decl, !this_looping);
+                   if (dump_file)
+                     fprintf (dump_file, "Function found to be %spure: %s\n",
+                              this_looping ? "looping " : "",
+                              w->name ());
+                 }
+               cgraph_set_pure_flag (w, true, this_looping);
+               break;
 
-           default:
-             break;
-           }
-         w_info = (struct ipa_dfs_info *) w->symbol.aux;
+             default:
+               break;
+             }
+         w_info = (struct ipa_dfs_info *) w->aux;
          w = w_info->next_cycle;
        }
     }
@@ -1420,17 +1427,20 @@ propagate_nothrow (void)
 
                  if (can_throw)
                    break;
-                 if (y_l->can_throw && !TREE_NOTHROW (w->symbol.decl)
+                 if (y_l->can_throw && !TREE_NOTHROW (w->decl)
                      && e->can_throw_external)
                    can_throw = true;
                }
-             else if (e->can_throw_external && !TREE_NOTHROW (y->symbol.decl))
+             else if (e->can_throw_external && !TREE_NOTHROW (y->decl))
                can_throw = true;
            }
           for (ie = node->indirect_calls; ie; ie = ie->next_callee)
            if (ie->can_throw_external)
-             can_throw = true;
-         w_info = (struct ipa_dfs_info *) w->symbol.aux;
+             {
+               can_throw = true;
+               break;
+             }
+         w_info = (struct ipa_dfs_info *) w->aux;
          w = w_info->next_cycle;
        }
 
@@ -1440,16 +1450,22 @@ propagate_nothrow (void)
       while (w)
        {
          funct_state w_l = get_function_state (w);
-         if (!can_throw && !TREE_NOTHROW (w->symbol.decl))
+         if (!can_throw && !TREE_NOTHROW (w->decl))
            {
-             cgraph_set_nothrow_flag (w, true);
-             if (dump_file)
-               fprintf (dump_file, "Function found to be nothrow: %s\n",
-                        cgraph_node_name (w));
+             /* Inline clones share declaration with their offline copies;
+                do not modify their declarations since the offline copy may
+                be different.  */
+             if (!w->global.inlined_to)
+               {
+                 cgraph_set_nothrow_flag (w, true);
+                 if (dump_file)
+                   fprintf (dump_file, "Function found to be nothrow: %s\n",
+                            w->name ());
+               }
            }
-         else if (can_throw && !TREE_NOTHROW (w->symbol.decl))
+         else if (can_throw && !TREE_NOTHROW (w->decl))
            w_l->can_throw = true;
-         w_info = (struct ipa_dfs_info *) w->symbol.aux;
+         w_info = (struct ipa_dfs_info *) w->aux;
          w = w_info->next_cycle;
        }
     }
@@ -1477,11 +1493,10 @@ propagate (void)
   propagate_pure_const ();
 
   /* Cleanup. */
-  FOR_EACH_DEFINED_FUNCTION (node)
+  FOR_EACH_FUNCTION (node)
     if (has_function_state (node))
       free (get_function_state (node));
   funct_state_vec.release ();
-  finish_state ();
   return 0;
 }
 
@@ -1493,35 +1508,53 @@ gate_pure_const (void)
          && !seen_error ());
 }
 
-struct ipa_opt_pass_d pass_ipa_pure_const =
+namespace {
+
+const pass_data pass_data_ipa_pure_const =
 {
- {
-  IPA_PASS,
-  "pure-const",                                /* name */
-  OPTGROUP_NONE,                        /* optinfo_flags */
-  gate_pure_const,                     /* gate */
-  propagate,                           /* 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 */
- },
- generate_summary,                     /* generate_summary */
- pure_const_write_summary,             /* write_summary */
- pure_const_read_summary,              /* read_summary */
- NULL,                                 /* write_optimization_summary */
- NULL,                                 /* read_optimization_summary */
- NULL,                                 /* stmt_fixup */
- 0,                                    /* TODOs */
- NULL,                                 /* function_transform */
- NULL                                  /* variable_transform */
+  IPA_PASS, /* type */
+  "pure-const", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  true, /* has_gate */
+  true, /* has_execute */
+  TV_IPA_PURE_CONST, /* tv_id */
+  0, /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  0, /* todo_flags_finish */
 };
 
+class pass_ipa_pure_const : public ipa_opt_pass_d
+{
+public:
+  pass_ipa_pure_const (gcc::context *ctxt)
+    : ipa_opt_pass_d (pass_data_ipa_pure_const, ctxt,
+                     pure_const_generate_summary, /* generate_summary */
+                     pure_const_write_summary, /* write_summary */
+                     pure_const_read_summary, /* read_summary */
+                     NULL, /* write_optimization_summary */
+                     NULL, /* read_optimization_summary */
+                     NULL, /* stmt_fixup */
+                     0, /* function_transform_todo_flags_start */
+                     NULL, /* function_transform */
+                     NULL) /* variable_transform */
+  {}
+
+  /* opt_pass methods: */
+  bool gate () { return gate_pure_const (); }
+  unsigned int execute () { return propagate (); }
+
+}; // class pass_ipa_pure_const
+
+} // anon namespace
+
+ipa_opt_pass_d *
+make_pass_ipa_pure_const (gcc::context *ctxt)
+{
+  return new pass_ipa_pure_const (ctxt);
+}
+
 /* Return true if function should be skipped for local pure const analysis.  */
 
 static bool
@@ -1568,7 +1601,7 @@ local_pure_const (void)
 
   /* Do NORETURN discovery.  */
   if (!skip && !TREE_THIS_VOLATILE (current_function_decl)
-      && EDGE_COUNT (EXIT_BLOCK_PTR->preds) == 0)
+      && EDGE_COUNT (EXIT_BLOCK_PTR_FOR_FN (cfun)->preds) == 0)
     {
       warn_function_noreturn (cfun->decl);
       if (dump_file)
@@ -1659,22 +1692,98 @@ local_pure_const (void)
     return 0;
 }
 
-struct gimple_opt_pass pass_local_pure_const =
+namespace {
+
+const pass_data pass_data_local_pure_const =
 {
- {
-  GIMPLE_PASS,
-  "local-pure-const",                  /* name */
-  OPTGROUP_NONE,                        /* optinfo_flags */
-  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 */
- }
+  GIMPLE_PASS, /* type */
+  "local-pure-const", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  true, /* has_gate */
+  true, /* has_execute */
+  TV_IPA_PURE_CONST, /* tv_id */
+  0, /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  0, /* todo_flags_finish */
 };
+
+class pass_local_pure_const : public gimple_opt_pass
+{
+public:
+  pass_local_pure_const (gcc::context *ctxt)
+    : gimple_opt_pass (pass_data_local_pure_const, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  opt_pass * clone () { return new pass_local_pure_const (m_ctxt); }
+  bool gate () { return gate_pure_const (); }
+  unsigned int execute () { return local_pure_const (); }
+
+}; // class pass_local_pure_const
+
+} // anon namespace
+
+gimple_opt_pass *
+make_pass_local_pure_const (gcc::context *ctxt)
+{
+  return new pass_local_pure_const (ctxt);
+}
+
+/* Emit noreturn warnings.  */
+
+static unsigned int
+execute_warn_function_noreturn (void)
+{
+  if (!TREE_THIS_VOLATILE (current_function_decl)
+      && EDGE_COUNT (EXIT_BLOCK_PTR_FOR_FN (cfun)->preds) == 0)
+    warn_function_noreturn (current_function_decl);
+  return 0;
+}
+
+static bool
+gate_warn_function_noreturn (void)
+{
+  return warn_suggest_attribute_noreturn;
+}
+
+namespace {
+
+const pass_data pass_data_warn_function_noreturn =
+{
+  GIMPLE_PASS, /* type */
+  "*warn_function_noreturn", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  true, /* has_gate */
+  true, /* has_execute */
+  TV_NONE, /* tv_id */
+  PROP_cfg, /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  0, /* todo_flags_finish */
+};
+
+class pass_warn_function_noreturn : public gimple_opt_pass
+{
+public:
+  pass_warn_function_noreturn (gcc::context *ctxt)
+    : gimple_opt_pass (pass_data_warn_function_noreturn, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  bool gate () { return gate_warn_function_noreturn (); }
+  unsigned int execute () { return execute_warn_function_noreturn (); }
+
+}; // class pass_warn_function_noreturn
+
+} // anon namespace
+
+gimple_opt_pass *
+make_pass_warn_function_noreturn (gcc::context *ctxt)
+{
+  return new pass_warn_function_noreturn (ctxt);
+}
+
+