gimple.h: Reorder prototypes to match .c declaration order...
[gcc.git] / gcc / tree-profile.c
index 69d77f07892ea58811557b89107da60ccd25d816..132ce0d4d13957c9bf0b02d053d81b44cd7bcffb 100644 (file)
@@ -1,7 +1,5 @@
 /* Calculate branch probabilities, and basic block execution counts.
-   Copyright (C) 1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998, 1999,
-   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2010
-   Free Software Foundation, Inc.
+   Copyright (C) 1990-2013 Free Software Foundation, Inc.
    Contributed by James E. Wilson, UC Berkeley/Cygnus Support;
    based on some ideas from Dain Samples of UC Berkeley.
    Further mangling by Bob Manson, Cygnus Support.
@@ -31,31 +29,36 @@ along with GCC; see the file COPYING3.  If not see
 #include "coretypes.h"
 #include "tm.h"
 #include "flags.h"
-#include "regs.h"
 #include "function.h"
 #include "basic-block.h"
 #include "diagnostic-core.h"
 #include "coverage.h"
 #include "tree.h"
-#include "tree-flow.h"
-#include "tree-dump.h"
+#include "gimple.h"
+#include "gimplify.h"
+#include "gimple-iterator.h"
+#include "gimplify-me.h"
+#include "gimple-ssa.h"
+#include "cgraph.h"
+#include "tree-cfg.h"
+#include "tree-ssanames.h"
+#include "tree-into-ssa.h"
 #include "tree-pass.h"
-#include "timevar.h"
 #include "value-prof.h"
-#include "cgraph.h"
 #include "profile.h"
 #include "target.h"
-#include "output.h"
+#include "tree-cfgcleanup.h"
+#include "tree-nested.h"
 
 static GTY(()) tree gcov_type_node;
-static GTY(()) tree gcov_type_tmp_var;
 static GTY(()) tree tree_interval_profiler_fn;
 static GTY(()) tree tree_pow2_profiler_fn;
 static GTY(()) tree tree_one_value_profiler_fn;
 static GTY(()) tree tree_indirect_call_profiler_fn;
+static GTY(()) tree tree_time_profiler_fn;
 static GTY(()) tree tree_average_profiler_fn;
 static GTY(()) tree tree_ior_profiler_fn;
-\f
+
 
 static GTY(()) tree ic_void_ptr_var;
 static GTY(()) tree ic_gcov_type_ptr_var;
@@ -64,8 +67,9 @@ static GTY(()) tree ptr_void;
 /* Do initialization work for the edge profiler.  */
 
 /* Add code:
-   static gcov*        __gcov_indirect_call_counters; // pointer to actual counter
-   static void*        __gcov_indirect_call_callee; // actual callee address
+   __thread gcov*      __gcov_indirect_call_counters; // pointer to actual counter
+   __thread void*      __gcov_indirect_call_callee; // actual callee address
+   __thread int __gcov_function_counter; // time profiler function counter
 */
 static void
 init_ic_make_global_vars (void)
@@ -74,12 +78,28 @@ init_ic_make_global_vars (void)
 
   ptr_void = build_pointer_type (void_type_node);
 
-  ic_void_ptr_var
-    = build_decl (UNKNOWN_LOCATION, VAR_DECL,
-                 get_identifier ("__gcov_indirect_call_callee"),
-                 ptr_void);
+  /* Workaround for binutils bug 14342.  Once it is fixed, remove lto path.  */
+  if (flag_lto)
+    {
+      ic_void_ptr_var
+       = build_decl (UNKNOWN_LOCATION, VAR_DECL,
+                     get_identifier ("__gcov_indirect_call_callee_ltopriv"),
+                     ptr_void);
+      TREE_PUBLIC (ic_void_ptr_var) = 1;
+      DECL_COMMON (ic_void_ptr_var) = 1;
+      DECL_VISIBILITY (ic_void_ptr_var) = VISIBILITY_HIDDEN;
+      DECL_VISIBILITY_SPECIFIED (ic_void_ptr_var) = true;
+    }
+  else
+    {
+      ic_void_ptr_var
+       = build_decl (UNKNOWN_LOCATION, VAR_DECL,
+                     get_identifier ("__gcov_indirect_call_callee"),
+                     ptr_void);
+      TREE_PUBLIC (ic_void_ptr_var) = 1;
+      DECL_EXTERNAL (ic_void_ptr_var) = 1;
+    }
   TREE_STATIC (ic_void_ptr_var) = 1;
-  TREE_PUBLIC (ic_void_ptr_var) = 0;
   DECL_ARTIFICIAL (ic_void_ptr_var) = 1;
   DECL_INITIAL (ic_void_ptr_var) = NULL;
   if (targetm.have_tls)
@@ -87,15 +107,30 @@ init_ic_make_global_vars (void)
       decl_default_tls_model (ic_void_ptr_var);
 
   varpool_finalize_decl (ic_void_ptr_var);
-  varpool_mark_needed_node (varpool_node (ic_void_ptr_var));
 
   gcov_type_ptr = build_pointer_type (get_gcov_type ());
-  ic_gcov_type_ptr_var
-    = build_decl (UNKNOWN_LOCATION, VAR_DECL,
-                 get_identifier ("__gcov_indirect_call_counters"),
-                 gcov_type_ptr);
+  /* Workaround for binutils bug 14342.  Once it is fixed, remove lto path.  */
+  if (flag_lto)
+    {
+      ic_gcov_type_ptr_var
+       = build_decl (UNKNOWN_LOCATION, VAR_DECL,
+                     get_identifier ("__gcov_indirect_call_counters_ltopriv"),
+                     gcov_type_ptr);
+      TREE_PUBLIC (ic_gcov_type_ptr_var) = 1;
+      DECL_COMMON (ic_gcov_type_ptr_var) = 1;
+      DECL_VISIBILITY (ic_gcov_type_ptr_var) = VISIBILITY_HIDDEN;
+      DECL_VISIBILITY_SPECIFIED (ic_gcov_type_ptr_var) = true;
+    }
+  else
+    {
+      ic_gcov_type_ptr_var
+       = build_decl (UNKNOWN_LOCATION, VAR_DECL,
+                     get_identifier ("__gcov_indirect_call_counters"),
+                     gcov_type_ptr);
+      TREE_PUBLIC (ic_gcov_type_ptr_var) = 1;
+      DECL_EXTERNAL (ic_gcov_type_ptr_var) = 1;
+    }
   TREE_STATIC (ic_gcov_type_ptr_var) = 1;
-  TREE_PUBLIC (ic_gcov_type_ptr_var) = 0;
   DECL_ARTIFICIAL (ic_gcov_type_ptr_var) = 1;
   DECL_INITIAL (ic_gcov_type_ptr_var) = NULL;
   if (targetm.have_tls)
@@ -103,9 +138,10 @@ init_ic_make_global_vars (void)
       decl_default_tls_model (ic_gcov_type_ptr_var);
 
   varpool_finalize_decl (ic_gcov_type_ptr_var);
-  varpool_mark_needed_node (varpool_node (ic_gcov_type_ptr_var));
 }
 
+/* Create the type and function decls for the interface with gcov.  */
+
 void
 gimple_init_edge_profiler (void)
 {
@@ -115,6 +151,7 @@ gimple_init_edge_profiler (void)
   tree gcov_type_ptr;
   tree ic_profiler_fn_type;
   tree average_profiler_fn_type;
+  tree time_profiler_fn_type;
 
   if (!gcov_type_node)
     {
@@ -162,20 +199,48 @@ gimple_init_edge_profiler (void)
 
       init_ic_make_global_vars ();
 
-      /* void (*) (gcov_type *, gcov_type, void *, void *)  */
-      ic_profiler_fn_type
-              = build_function_type_list (void_type_node,
-                                         gcov_type_ptr, gcov_type_node,
-                                         ptr_void,
-                                         ptr_void, NULL_TREE);
-      tree_indirect_call_profiler_fn
-             = build_fn_decl ("__gcov_indirect_call_profiler",
-                                    ic_profiler_fn_type);
+      /* Workaround for binutils bug 14342.  Once it is fixed, remove lto path.  */
+      if (flag_lto)
+        {
+         /* void (*) (gcov_type, void *)  */
+         ic_profiler_fn_type
+                  = build_function_type_list (void_type_node,
+                                             gcov_type_ptr, gcov_type_node,
+                                             ptr_void, ptr_void,
+                                             NULL_TREE);
+         tree_indirect_call_profiler_fn
+                 = build_fn_decl ("__gcov_indirect_call_profiler",
+                                        ic_profiler_fn_type);
+        }
+      else
+        {
+         /* void (*) (gcov_type, void *)  */
+         ic_profiler_fn_type
+                  = build_function_type_list (void_type_node,
+                                             gcov_type_node,
+                                             ptr_void,
+                                             NULL_TREE);
+         tree_indirect_call_profiler_fn
+                 = build_fn_decl ("__gcov_indirect_call_profiler_v2",
+                                        ic_profiler_fn_type);
+        }
       TREE_NOTHROW (tree_indirect_call_profiler_fn) = 1;
       DECL_ATTRIBUTES (tree_indirect_call_profiler_fn)
        = tree_cons (get_identifier ("leaf"), NULL,
                     DECL_ATTRIBUTES (tree_indirect_call_profiler_fn));
 
+      /* void (*) (gcov_type *, gcov_type, void *)  */
+      time_profiler_fn_type
+              = build_function_type_list (void_type_node,
+                                         gcov_type_ptr, NULL_TREE);
+      tree_time_profiler_fn
+             = build_fn_decl ("__gcov_time_profiler",
+                                    time_profiler_fn_type);
+      TREE_NOTHROW (tree_time_profiler_fn) = 1;
+      DECL_ATTRIBUTES (tree_time_profiler_fn)
+       = tree_cons (get_identifier ("leaf"), NULL,
+                    DECL_ATTRIBUTES (tree_time_profiler_fn));
+
       /* void (*) (gcov_type *, gcov_type)  */
       average_profiler_fn_type
              = build_function_type_list (void_type_node,
@@ -201,6 +266,7 @@ gimple_init_edge_profiler (void)
       DECL_ASSEMBLER_NAME (tree_pow2_profiler_fn);
       DECL_ASSEMBLER_NAME (tree_one_value_profiler_fn);
       DECL_ASSEMBLER_NAME (tree_indirect_call_profiler_fn);
+      DECL_ASSEMBLER_NAME (tree_time_profiler_fn);
       DECL_ASSEMBLER_NAME (tree_average_profiler_fn);
       DECL_ASSEMBLER_NAME (tree_ior_profiler_fn);
     }
@@ -213,20 +279,18 @@ gimple_init_edge_profiler (void)
 void
 gimple_gen_edge_profiler (int edgeno, edge e)
 {
-  tree ref, one;
+  tree ref, one, gcov_type_tmp_var;
   gimple stmt1, stmt2, stmt3;
 
-  /* We share one temporary variable declaration per function.  This
-     gets re-set in tree_profiling.  */
-  if (gcov_type_tmp_var == NULL_TREE)
-    gcov_type_tmp_var = create_tmp_reg (gcov_type_node, "PROF_edge_counter");
   ref = tree_coverage_counter_ref (GCOV_COUNTER_ARCS, edgeno);
   one = build_int_cst (gcov_type_node, 1);
+  gcov_type_tmp_var = make_temp_ssa_name (gcov_type_node,
+                                         NULL, "PROF_edge_counter");
   stmt1 = gimple_build_assign (gcov_type_tmp_var, ref);
-  gimple_assign_set_lhs (stmt1, make_ssa_name (gcov_type_tmp_var, stmt1));
+  gcov_type_tmp_var = make_temp_ssa_name (gcov_type_node,
+                                         NULL, "PROF_edge_counter");
   stmt2 = gimple_build_assign_with_ops (PLUS_EXPR, gcov_type_tmp_var,
                                        gimple_assign_lhs (stmt1), one);
-  gimple_assign_set_lhs (stmt2, make_ssa_name (gcov_type_tmp_var, stmt2));
   stmt3 = gimple_build_assign (unshare_expr (ref), gimple_assign_lhs (stmt2));
   gsi_insert_on_edge (e, stmt1);
   gsi_insert_on_edge (e, stmt2);
@@ -241,7 +305,8 @@ prepare_instrumented_value (gimple_stmt_iterator *gsi, histogram_value value)
 {
   tree val = value->hvalue.value;
   if (POINTER_TYPE_P (TREE_TYPE (val)))
-    val = fold_convert (sizetype, val);
+    val = fold_convert (build_nonstandard_integer_type
+                         (TYPE_PRECISION (TREE_TYPE (val)), 1), val);
   return force_gimple_operand_gsi (gsi, fold_convert (gcov_type_node, val),
                                   true, NULL_TREE, true, GSI_SAME_STMT);
 }
@@ -333,14 +398,14 @@ gimple_gen_ic_profiler (histogram_value value, unsigned tag, unsigned base)
 
   /* Insert code:
 
-    __gcov_indirect_call_counters = get_relevant_counter_ptr ();
-    __gcov_indirect_call_callee = (void *) indirect call argument;
+    stmt1: __gcov_indirect_call_counters = get_relevant_counter_ptr ();
+    stmt2: tmp1 = (void *) (indirect call argument value)
+    stmt3: __gcov_indirect_call_callee = tmp1;
    */
 
-  tmp1 = create_tmp_reg (ptr_void, "PROF");
   stmt1 = gimple_build_assign (ic_gcov_type_ptr_var, ref_ptr);
+  tmp1 = make_temp_ssa_name (ptr_void, NULL, "PROF");
   stmt2 = gimple_build_assign (tmp1, unshare_expr (value->hvalue.value));
-  gimple_assign_set_lhs (stmt2, make_ssa_name (tmp1, stmt2));
   stmt3 = gimple_build_assign (ic_void_ptr_var, gimple_assign_lhs (stmt2));
 
   gsi_insert_before (&gsi, stmt1, GSI_SAME_STMT);
@@ -360,29 +425,46 @@ gimple_gen_ic_func_profiler (void)
   struct cgraph_node * c_node = cgraph_get_node (current_function_decl);
   gimple_stmt_iterator gsi;
   gimple stmt1, stmt2;
-  tree tree_uid, cur_func, counter_ptr, ptr_var, void0;
+  tree tree_uid, cur_func, void0;
 
   if (cgraph_only_called_directly_p (c_node))
     return;
 
   gimple_init_edge_profiler ();
 
-  gsi = gsi_after_labels (single_succ (ENTRY_BLOCK_PTR));
+  /* Insert code:
+
+    stmt1: __gcov_indirect_call_profiler_v2 (profile_id,
+                                            &current_function_decl)
+   */
+  gsi = gsi_after_labels (split_edge (single_succ_edge (ENTRY_BLOCK_PTR)));
 
   cur_func = force_gimple_operand_gsi (&gsi,
                                       build_addr (current_function_decl,
                                                   current_function_decl),
                                       true, NULL_TREE,
                                       true, GSI_SAME_STMT);
-  counter_ptr = force_gimple_operand_gsi (&gsi, ic_gcov_type_ptr_var,
+  tree_uid = build_int_cst
+             (gcov_type_node, cgraph_get_node (current_function_decl)->profile_id);
+  /* Workaround for binutils bug 14342.  Once it is fixed, remove lto path.  */
+  if (flag_lto)
+    {
+      tree counter_ptr, ptr_var;
+      counter_ptr = force_gimple_operand_gsi (&gsi, ic_gcov_type_ptr_var,
+                                             true, NULL_TREE, true,
+                                             GSI_SAME_STMT);
+      ptr_var = force_gimple_operand_gsi (&gsi, ic_void_ptr_var,
                                          true, NULL_TREE, true,
                                          GSI_SAME_STMT);
-  ptr_var = force_gimple_operand_gsi (&gsi, ic_void_ptr_var,
-                                     true, NULL_TREE, true,
-                                     GSI_SAME_STMT);
-  tree_uid = build_int_cst (gcov_type_node, current_function_funcdef_no);
-  stmt1 = gimple_build_call (tree_indirect_call_profiler_fn, 4,
-                            counter_ptr, tree_uid, cur_func, ptr_var);
+
+      stmt1 = gimple_build_call (tree_indirect_call_profiler_fn, 4,
+                                counter_ptr, tree_uid, cur_func, ptr_var);
+    }
+  else
+    {
+      stmt1 = gimple_build_call (tree_indirect_call_profiler_fn, 2,
+                                tree_uid, cur_func);
+    }
   gsi_insert_before (&gsi, stmt1, GSI_SAME_STMT);
 
   /* Set __gcov_indirect_call_callee to 0,
@@ -393,6 +475,23 @@ gimple_gen_ic_func_profiler (void)
   gsi_insert_before (&gsi, stmt2, GSI_SAME_STMT);
 }
 
+/* Output instructions as GIMPLE tree at the beginning for each function.
+   TAG is the tag of the section for counters, BASE is offset of the
+   counter position and GSI is the iterator we place the counter.  */
+
+void
+gimple_gen_time_profiler (unsigned tag, unsigned base,
+                          gimple_stmt_iterator &gsi)
+{
+  tree ref_ptr = tree_coverage_counter_addr (tag, base);
+  gimple call;
+
+  ref_ptr = force_gimple_operand_gsi (&gsi, ref_ptr,
+                                     true, NULL_TREE, true, GSI_SAME_STMT);
+  call = gimple_build_call (tree_time_profiler_fn, 1, ref_ptr);
+  gsi_insert_before (&gsi, call, GSI_NEW_STMT);
+}
+
 /* Output instructions as GIMPLE trees for code to find the most common value
    of a difference between two evaluations of an expression.
    VALUE is the expression whose value is profiled.  TAG is the tag of the
@@ -458,32 +557,26 @@ tree_profiling (void)
 {
   struct cgraph_node *node;
 
-  /* Don't profile functions produced at destruction time, particularly
-     the gcov datastructure initializer.  Don't profile if it has been
-     already instrumented either (when OpenMP expansion creates
-     child function from already instrumented body).  */
-  if (cgraph_state == CGRAPH_STATE_FINISHED)
-    return 0;
+  /* This is a small-ipa pass that gets called only once, from
+     cgraphunit.c:ipa_passes().  */
+  gcc_assert (cgraph_state == CGRAPH_STATE_IPA_SSA);
 
-  init_node_map();
+  init_node_map (true);
 
-  for (node = cgraph_nodes; node; node = node->next)
+  FOR_EACH_DEFINED_FUNCTION (node)
     {
-      if (!node->analyzed
-         || !gimple_has_body_p (node->decl)
-         || !(!node->clone_of || node->decl != node->clone_of->decl))
+      if (!gimple_has_body_p (node->decl))
        continue;
 
       /* Don't profile functions produced for builtin stuff.  */
-      if (DECL_SOURCE_LOCATION (node->decl) == BUILTINS_LOCATION
-         || DECL_STRUCT_FUNCTION (node->decl)->after_tree_profile)
+      if (DECL_SOURCE_LOCATION (node->decl) == BUILTINS_LOCATION)
        continue;
 
       push_cfun (DECL_STRUCT_FUNCTION (node->decl));
-      current_function_decl = node->decl;
 
-      /* Re-set global shared temporary variable for edge-counters.  */
-      gcov_type_tmp_var = NULL_TREE;
+      /* Local pure-const may imply need to fixup the cfg.  */
+      if (execute_fixup_cfg () & TODO_cleanup_cfg)
+       cleanup_tree_cfg ();
 
       branch_prob ();
 
@@ -501,22 +594,19 @@ tree_profiling (void)
         easy to adjust it, if and when there is some.  */
       free_dominance_info (CDI_DOMINATORS);
       free_dominance_info (CDI_POST_DOMINATORS);
-
-      current_function_decl = NULL;
       pop_cfun ();
     }
 
   /* Drop pure/const flags from instrumented functions.  */
-  for (node = cgraph_nodes; node; node = node->next)
+  FOR_EACH_DEFINED_FUNCTION (node)
     {
-      if (!node->analyzed
-         || !gimple_has_body_p (node->decl)
-         || !(!node->clone_of || node->decl != node->clone_of->decl))
+      if (!gimple_has_body_p (node->decl)
+         || !(!node->clone_of
+         || node->decl != node->clone_of->decl))
        continue;
 
       /* Don't profile functions produced for builtin stuff.  */
-      if (DECL_SOURCE_LOCATION (node->decl) == BUILTINS_LOCATION
-         || DECL_STRUCT_FUNCTION (node->decl)->after_tree_profile)
+      if (DECL_SOURCE_LOCATION (node->decl) == BUILTINS_LOCATION)
        continue;
 
       cgraph_set_const_flag (node, false, false);
@@ -524,22 +614,20 @@ tree_profiling (void)
     }
 
   /* Update call statements and rebuild the cgraph.  */
-  for (node = cgraph_nodes; node; node = node->next)
+  FOR_EACH_DEFINED_FUNCTION (node)
     {
       basic_block bb;
 
-      if (!node->analyzed
-         || !gimple_has_body_p (node->decl)
-         || !(!node->clone_of || node->decl != node->clone_of->decl))
+      if (!gimple_has_body_p (node->decl)
+         || !(!node->clone_of
+         || node->decl != node->clone_of->decl))
        continue;
 
       /* Don't profile functions produced for builtin stuff.  */
-      if (DECL_SOURCE_LOCATION (node->decl) == BUILTINS_LOCATION
-         || DECL_STRUCT_FUNCTION (node->decl)->after_tree_profile)
+      if (DECL_SOURCE_LOCATION (node->decl) == BUILTINS_LOCATION)
        continue;
 
       push_cfun (DECL_STRUCT_FUNCTION (node->decl));
-      current_function_decl = node->decl;
 
       FOR_EACH_BB (bb)
        {
@@ -552,16 +640,18 @@ tree_profiling (void)
            }
        }
 
-      cfun->after_tree_profile = 1;
+      /* re-merge split blocks.  */
+      cleanup_tree_cfg ();
       update_ssa (TODO_update_ssa);
 
       rebuild_cgraph_edges ();
 
-      current_function_decl = NULL;
       pop_cfun ();
     }
 
-  del_node_map();
+  handle_missing_profiles ();
+
+  del_node_map ();
   return 0;
 }
 
@@ -575,23 +665,42 @@ gate_tree_profile_ipa (void)
              || profile_arc_flag));
 }
 
-struct simple_ipa_opt_pass pass_ipa_tree_profile =
+namespace {
+
+const pass_data pass_data_ipa_tree_profile =
 {
- {
-  SIMPLE_IPA_PASS,
-  "tree_profile_ipa",                  /* name */
-  gate_tree_profile_ipa,               /* gate */
-  tree_profiling,                      /* execute */
-  NULL,                                /* sub */
-  NULL,                                /* next */
-  0,                                   /* static_pass_number */
-  TV_IPA_PROFILE,                      /* tv_id */
-  0,                                   /* properties_required */
-  0,                                   /* properties_provided */
-  0,                                   /* properties_destroyed */
-  0,                                   /* todo_flags_start */
-  TODO_dump_func                       /* todo_flags_finish */
- }
+  SIMPLE_IPA_PASS, /* type */
+  "profile", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  true, /* has_gate */
+  true, /* has_execute */
+  TV_IPA_PROFILE, /* tv_id */
+  0, /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  0, /* todo_flags_finish */
 };
 
+class pass_ipa_tree_profile : public simple_ipa_opt_pass
+{
+public:
+  pass_ipa_tree_profile (gcc::context *ctxt)
+    : simple_ipa_opt_pass (pass_data_ipa_tree_profile, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  bool gate () { return gate_tree_profile_ipa (); }
+  unsigned int execute () { return tree_profiling (); }
+
+}; // class pass_ipa_tree_profile
+
+} // anon namespace
+
+simple_ipa_opt_pass *
+make_pass_ipa_tree_profile (gcc::context *ctxt)
+{
+  return new pass_ipa_tree_profile (ctxt);
+}
+
 #include "gt-tree-profile.h"