coverage.c (get_coverage_counts): Use current_function_decl.
authorJan Hubicka <hubicka@ucw.cz>
Tue, 1 Jan 2019 12:49:18 +0000 (13:49 +0100)
committerJan Hubicka <hubicka@gcc.gnu.org>
Tue, 1 Jan 2019 12:49:18 +0000 (12:49 +0000)
* coverage.c (get_coverage_counts): Use current_function_decl.
* profile.c (read_thunk_profile): New function.
(branch_prob): Add THUNK parameter.
* tree-profile.c (tree_profiling): Handle thunks.
* value-prof.c (init_node_map): Handle thunks.
* value-prof.h (branch_prob): Upate prototype.
(read_thunk_profile): Declare.

* g++.dg/tree-prof/devirt.C: Update testcase.

From-SVN: r267495

gcc/ChangeLog
gcc/coverage.c
gcc/profile.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/tree-prof/devirt.C
gcc/tree-profile.c
gcc/value-prof.c
gcc/value-prof.h

index 53e546f0707ea70e68162924e046d3cd8863cf1c..f7c7109a89fcc04b39c8aa055272ca1c5359922a 100644 (file)
@@ -1,3 +1,13 @@
+2019-01-01  Jan Hubicka  <hubicka@ucw.cz>
+
+       * coverage.c (get_coverage_counts): Use current_function_decl.
+       * profile.c (read_thunk_profile): New function.
+       (branch_prob): Add THUNK parameter.
+       * tree-profile.c (tree_profiling): Handle thunks.
+       * value-prof.c (init_node_map): Handle thunks.
+       * value-prof.h (branch_prob): Upate prototype.
+       (read_thunk_profile): Declare.
+
 2019-01-01  Jakub Jelinek  <jakub@redhat.com>
 
        Update copyright years.
index 0e1873b9589c0792eabc744803f125dc8cedbf23..a34c5da49bfccf5b1f3187a8e5fa6cf238c5e341 100644 (file)
@@ -329,7 +329,7 @@ get_coverage_counts (unsigned counter, unsigned cfg_checksum,
   else
     {
       gcc_assert (coverage_node_map_initialized_p ());
-      elt.ident = cgraph_node::get (cfun->decl)->profile_id;
+      elt.ident = cgraph_node::get (current_function_decl)->profile_id;
     }
   elt.ctr = counter;
   entry = counts_hash->find (&elt);
index 14b35ff465c0fb2cc8e164328ac95d2c18d15652..7374b0d6883cb349fad8584ffa5bafb3d68cb996 100644 (file)
@@ -963,6 +963,25 @@ compare_freqs (const void *p1, const void *p2)
   return e2->dest->index - e1->dest->index;
 }
 
+/* Only read execution count for thunks.  */
+
+void
+read_thunk_profile (struct cgraph_node *node)
+{
+  tree old = current_function_decl;
+  current_function_decl = node->decl;
+  gcov_type *counts = get_coverage_counts (GCOV_COUNTER_ARCS, 0, 0, 1);
+  if (counts)
+    {
+      node->callees->count = node->count
+        = profile_count::from_gcov_type (counts[0]);
+      free (counts);
+    }
+  current_function_decl = old;
+  return;
+}
+
+
 /* Instrument and/or analyze program behavior based on program the CFG.
 
    This function creates a representation of the control flow graph (of
@@ -983,7 +1002,7 @@ compare_freqs (const void *p1, const void *p2)
    Main entry point of this file.  */
 
 void
-branch_prob (void)
+branch_prob (bool thunk)
 {
   basic_block bb;
   unsigned i;
@@ -1000,118 +1019,121 @@ branch_prob (void)
 
   hash_set <location_triplet_hash> streamed_locations;
 
-  /* We can't handle cyclic regions constructed using abnormal edges.
-     To avoid these we replace every source of abnormal edge by a fake
-     edge from entry node and every destination by fake edge to exit.
-     This keeps graph acyclic and our calculation exact for all normal
-     edges except for exit and entrance ones.
-
-     We also add fake exit edges for each call and asm statement in the
-     basic, since it may not return.  */
-
-  FOR_EACH_BB_FN (bb, cfun)
+  if (!thunk)
     {
-      int need_exit_edge = 0, need_entry_edge = 0;
-      int have_exit_edge = 0, have_entry_edge = 0;
-      edge e;
-      edge_iterator ei;
+      /* We can't handle cyclic regions constructed using abnormal edges.
+        To avoid these we replace every source of abnormal edge by a fake
+        edge from entry node and every destination by fake edge to exit.
+        This keeps graph acyclic and our calculation exact for all normal
+        edges except for exit and entrance ones.
 
-      /* Functions returning multiple times are not handled by extra edges.
-         Instead we simply allow negative counts on edges from exit to the
-         block past call and corresponding probabilities.  We can't go
-         with the extra edges because that would result in flowgraph that
-        needs to have fake edges outside the spanning tree.  */
+        We also add fake exit edges for each call and asm statement in the
+        basic, since it may not return.  */
 
-      FOR_EACH_EDGE (e, ei, bb->succs)
+      FOR_EACH_BB_FN (bb, cfun)
        {
-         gimple_stmt_iterator gsi;
-         gimple *last = NULL;
-
-         /* It may happen that there are compiler generated statements
-            without a locus at all.  Go through the basic block from the
-            last to the first statement looking for a locus.  */
-         for (gsi = gsi_last_nondebug_bb (bb);
-              !gsi_end_p (gsi);
-              gsi_prev_nondebug (&gsi))
+         int need_exit_edge = 0, need_entry_edge = 0;
+         int have_exit_edge = 0, have_entry_edge = 0;
+         edge e;
+         edge_iterator ei;
+
+         /* Functions returning multiple times are not handled by extra edges.
+            Instead we simply allow negative counts on edges from exit to the
+            block past call and corresponding probabilities.  We can't go
+            with the extra edges because that would result in flowgraph that
+            needs to have fake edges outside the spanning tree.  */
+
+         FOR_EACH_EDGE (e, ei, bb->succs)
            {
-             last = gsi_stmt (gsi);
-             if (!RESERVED_LOCATION_P (gimple_location (last)))
-               break;
-           }
+             gimple_stmt_iterator gsi;
+             gimple *last = NULL;
+
+             /* It may happen that there are compiler generated statements
+                without a locus at all.  Go through the basic block from the
+                last to the first statement looking for a locus.  */
+             for (gsi = gsi_last_nondebug_bb (bb);
+                  !gsi_end_p (gsi);
+                  gsi_prev_nondebug (&gsi))
+               {
+                 last = gsi_stmt (gsi);
+                 if (!RESERVED_LOCATION_P (gimple_location (last)))
+                   break;
+               }
 
-         /* Edge with goto locus might get wrong coverage info unless
-            it is the only edge out of BB.
-            Don't do that when the locuses match, so
-            if (blah) goto something;
-            is not computed twice.  */
-         if (last
-             && gimple_has_location (last)
-             && !RESERVED_LOCATION_P (e->goto_locus)
-             && !single_succ_p (bb)
-             && (LOCATION_FILE (e->goto_locus)
-                 != LOCATION_FILE (gimple_location (last))
-                 || (LOCATION_LINE (e->goto_locus)
-                     != LOCATION_LINE (gimple_location (last)))))
+             /* Edge with goto locus might get wrong coverage info unless
+                it is the only edge out of BB.
+                Don't do that when the locuses match, so
+                if (blah) goto something;
+                is not computed twice.  */
+             if (last
+                 && gimple_has_location (last)
+                 && !RESERVED_LOCATION_P (e->goto_locus)
+                 && !single_succ_p (bb)
+                 && (LOCATION_FILE (e->goto_locus)
+                     != LOCATION_FILE (gimple_location (last))
+                     || (LOCATION_LINE (e->goto_locus)
+                         != LOCATION_LINE (gimple_location (last)))))
+               {
+                 basic_block new_bb = split_edge (e);
+                 edge ne = single_succ_edge (new_bb);
+                 ne->goto_locus = e->goto_locus;
+               }
+             if ((e->flags & (EDGE_ABNORMAL | EDGE_ABNORMAL_CALL))
+                  && e->dest != EXIT_BLOCK_PTR_FOR_FN (cfun))
+               need_exit_edge = 1;
+             if (e->dest == EXIT_BLOCK_PTR_FOR_FN (cfun))
+               have_exit_edge = 1;
+           }
+         FOR_EACH_EDGE (e, ei, bb->preds)
            {
-             basic_block new_bb = split_edge (e);
-             edge ne = single_succ_edge (new_bb);
-             ne->goto_locus = e->goto_locus;
+             if ((e->flags & (EDGE_ABNORMAL | EDGE_ABNORMAL_CALL))
+                  && e->src != ENTRY_BLOCK_PTR_FOR_FN (cfun))
+               need_entry_edge = 1;
+             if (e->src == ENTRY_BLOCK_PTR_FOR_FN (cfun))
+               have_entry_edge = 1;
            }
-         if ((e->flags & (EDGE_ABNORMAL | EDGE_ABNORMAL_CALL))
-              && e->dest != EXIT_BLOCK_PTR_FOR_FN (cfun))
-           need_exit_edge = 1;
-         if (e->dest == EXIT_BLOCK_PTR_FOR_FN (cfun))
-           have_exit_edge = 1;
-       }
-      FOR_EACH_EDGE (e, ei, bb->preds)
-       {
-         if ((e->flags & (EDGE_ABNORMAL | EDGE_ABNORMAL_CALL))
-              && e->src != ENTRY_BLOCK_PTR_FOR_FN (cfun))
-           need_entry_edge = 1;
-         if (e->src == ENTRY_BLOCK_PTR_FOR_FN (cfun))
-           have_entry_edge = 1;
-       }
 
-      if (need_exit_edge && !have_exit_edge)
-       {
-         if (dump_file)
-           fprintf (dump_file, "Adding fake exit edge to bb %i\n",
-                    bb->index);
-         make_edge (bb, EXIT_BLOCK_PTR_FOR_FN (cfun), EDGE_FAKE);
-       }
-      if (need_entry_edge && !have_entry_edge)
-       {
-         if (dump_file)
-           fprintf (dump_file, "Adding fake entry edge to bb %i\n",
-                    bb->index);
-         make_edge (ENTRY_BLOCK_PTR_FOR_FN (cfun), bb, EDGE_FAKE);
-         /* Avoid bbs that have both fake entry edge and also some
-            exit edge.  One of those edges wouldn't be added to the
-            spanning tree, but we can't instrument any of them.  */
-         if (have_exit_edge || need_exit_edge)
+         if (need_exit_edge && !have_exit_edge)
            {
-             gimple_stmt_iterator gsi;
-             gimple *first;
-
-             gsi = gsi_start_nondebug_after_labels_bb (bb);
-             gcc_checking_assert (!gsi_end_p (gsi));
-             first = gsi_stmt (gsi);
-             /* Don't split the bbs containing __builtin_setjmp_receiver
-                or ABNORMAL_DISPATCHER calls.  These are very
-                special and don't expect anything to be inserted before
-                them.  */
-             if (is_gimple_call (first)
-                 && (gimple_call_builtin_p (first, BUILT_IN_SETJMP_RECEIVER)
-                     || (gimple_call_flags (first) & ECF_RETURNS_TWICE)
-                     || (gimple_call_internal_p (first)
-                         && (gimple_call_internal_fn (first)
-                             == IFN_ABNORMAL_DISPATCHER))))
-               continue;
-
              if (dump_file)
-               fprintf (dump_file, "Splitting bb %i after labels\n",
+               fprintf (dump_file, "Adding fake exit edge to bb %i\n",
                         bb->index);
-             split_block_after_labels (bb);
+             make_edge (bb, EXIT_BLOCK_PTR_FOR_FN (cfun), EDGE_FAKE);
+           }
+         if (need_entry_edge && !have_entry_edge)
+           {
+             if (dump_file)
+               fprintf (dump_file, "Adding fake entry edge to bb %i\n",
+                        bb->index);
+             make_edge (ENTRY_BLOCK_PTR_FOR_FN (cfun), bb, EDGE_FAKE);
+             /* Avoid bbs that have both fake entry edge and also some
+                exit edge.  One of those edges wouldn't be added to the
+                spanning tree, but we can't instrument any of them.  */
+             if (have_exit_edge || need_exit_edge)
+               {
+                 gimple_stmt_iterator gsi;
+                 gimple *first;
+
+                 gsi = gsi_start_nondebug_after_labels_bb (bb);
+                 gcc_checking_assert (!gsi_end_p (gsi));
+                 first = gsi_stmt (gsi);
+                 /* Don't split the bbs containing __builtin_setjmp_receiver
+                    or ABNORMAL_DISPATCHER calls.  These are very
+                    special and don't expect anything to be inserted before
+                    them.  */
+                 if (is_gimple_call (first)
+                     && (gimple_call_builtin_p (first, BUILT_IN_SETJMP_RECEIVER)
+                         || (gimple_call_flags (first) & ECF_RETURNS_TWICE)
+                         || (gimple_call_internal_p (first)
+                             && (gimple_call_internal_fn (first)
+                                 == IFN_ABNORMAL_DISPATCHER))))
+                   continue;
+
+                 if (dump_file)
+                   fprintf (dump_file, "Splitting bb %i after labels\n",
+                            bb->index);
+                 split_block_after_labels (bb);
+               }
            }
        }
     }
@@ -1143,7 +1165,18 @@ branch_prob (void)
      on the spanning tree.  We insert as many abnormal and critical edges
      as possible to minimize number of edge splits necessary.  */
 
-  find_spanning_tree (el);
+  if (!thunk)
+    find_spanning_tree (el);
+  else
+    {
+      edge e;
+      edge_iterator ei;
+      /* Keep only edge from entry block to be instrumented.  */
+      FOR_EACH_BB_FN (bb, cfun)
+       FOR_EACH_EDGE (e, ei, bb->succs)
+         EDGE_INFO (e)->ignore = true;
+    }
+
 
   /* Fake edges that are not on the tree will not be instrumented, so
      mark them ignored.  */
@@ -1183,8 +1216,17 @@ branch_prob (void)
      the checksum in only once place, since it depends on the shape
      of the control flow which can change during 
      various transformations.  */
-  cfg_checksum = coverage_compute_cfg_checksum (cfun);
-  lineno_checksum = coverage_compute_lineno_checksum ();
+  if (thunk)
+    {
+      /* At stream in time we do not have CFG, so we can not do checksums.  */
+      cfg_checksum = 0;
+      lineno_checksum = 0;
+    }
+  else
+    {
+      cfg_checksum = coverage_compute_cfg_checksum (cfun);
+      lineno_checksum = coverage_compute_lineno_checksum ();
+    }
 
   /* Write the data from which gcov can reconstruct the basic block
      graph and function line numbers (the gcno file).  */
index 4c9083a857bebc905d6202427123b0c4b17f168b..0f75fffb2f86f0e99725f0745f99be5d9973b640 100644 (file)
@@ -1,3 +1,7 @@
+2019-01-01  Jan Hubicka  <hubicka@ucw.cz>
+
+       * g++.dg/tree-prof/devirt.C: Update testcase.
+
 2019-01-01  Jakub Jelinek  <jakub@redhat.com>
 
        Update copyright years.
index 86cba41452e5d2f1dfd606209230ea1a4b9371a1..7d6797dd22694cd5ca92f4e6f389f509a83d6e37 100644 (file)
@@ -119,5 +119,5 @@ main ()
     __builtin_abort ();
 }
 
-/* { dg-final-use-not-autofdo { scan-tree-dump-times "folding virtual function call to virtual unsigned int mozPersonalDictionary::_ZThn16" 3 "dom3" } } */
-/* { dg-final-use-not-autofdo { scan-tree-dump-times "folding virtual function call to virtual unsigned int mozPersonalDictionary::AddRef" 3 "dom3" } } */
+/* { dg-final-use-not-autofdo { scan-tree-dump-times "folding virtual function call to virtual unsigned int mozPersonalDictionary::_ZThn16" 1 "dom3" } } */
+/* { dg-final-use-not-autofdo { scan-tree-dump-times "folding virtual function call to virtual unsigned int mozPersonalDictionary::AddRef" 1 "dom3" } } */
index ab4a439a5e304e0ac0f825d82efb52f83bb2b2a7..30fadde86cf00ab4414234ae88a71bd735e7f5ea 100644 (file)
@@ -739,7 +739,8 @@ tree_profiling (void)
 
   FOR_EACH_DEFINED_FUNCTION (node)
     {
-      if (!gimple_has_body_p (node->decl))
+      bool thunk = false;
+      if (!gimple_has_body_p (node->decl) && !node->thunk.thunk_p)
        continue;
 
       /* Don't profile functions produced for builtin stuff.  */
@@ -760,22 +761,43 @@ tree_profiling (void)
       if (!include_source_file_for_profile (file))
        continue;
 
+      if (node->thunk.thunk_p)
+       {
+         /* We can not expand variadic thunks to Gimple.  */
+         if (stdarg_p (TREE_TYPE (node->decl)))
+           continue;
+         thunk = true;
+         /* When generate profile, expand thunk to gimple so it can be
+            instrumented same way as other functions.  */
+         if (profile_arc_flag)
+           node->expand_thunk (false, true);
+         /* Read cgraph profile but keep function as thunk at profile-use
+            time.  */
+         else
+           {
+             read_thunk_profile (node);
+             continue;
+           }
+       }
+
       push_cfun (DECL_STRUCT_FUNCTION (node->decl));
 
       if (dump_file)
        dump_function_header (dump_file, cfun->decl, dump_flags);
 
       /* Local pure-const may imply need to fixup the cfg.  */
-      if (execute_fixup_cfg () & TODO_cleanup_cfg)
+      if (gimple_has_body_p (node->decl)
+         && (execute_fixup_cfg () & TODO_cleanup_cfg))
        cleanup_tree_cfg ();
 
-      branch_prob ();
+      branch_prob (thunk);
 
       if (! flag_branch_probabilities
          && flag_profile_values)
        gimple_gen_ic_func_profiler ();
 
       if (flag_branch_probabilities
+         && !thunk
          && flag_profile_values
          && flag_value_profile_transformations)
        gimple_value_profile_transformations ();
index fbca6524a3653ca01d8ca84a5562a57440f7f942..5013956cf86f8ce5920f2646baf5883d12762584 100644 (file)
@@ -1188,7 +1188,7 @@ init_node_map (bool local)
   cgraph_node_map = new hash_map<profile_id_hash, cgraph_node *>;
 
   FOR_EACH_DEFINED_FUNCTION (n)
-    if (n->has_gimple_body_p ())
+    if (n->has_gimple_body_p () || n->thunk.thunk_p)
       {
        cgraph_node **val;
        if (local)
index 3e0e0580f93f2436c3470b95aba5d8923128b6e1..1251fa95e317c3df5689fc43706d06f2c34070d1 100644 (file)
@@ -112,7 +112,8 @@ extern struct cgraph_node* find_func_by_profile_id (int func_id);
 
 /* In profile.c.  */
 extern void init_branch_prob (void);
-extern void branch_prob (void);
+extern void branch_prob (bool);
+extern void read_thunk_profile (struct cgraph_node *);
 extern void end_branch_prob (void);
 
 #endif /* GCC_VALUE_PROF_H */