From 167b550bfc882bd2fd23b33ca65ad2a6be197771 Mon Sep 17 00:00:00 2001
From: hubicka <hubicka@138bc75d-0d04-0410-961f-82ee72b054a4>
Date: Fri, 19 Jan 2007 18:34:02 +0000
Subject: [PATCH] Patch by Tomas Bily  <tbily@suse.cz>         * cgraphunit.c
 (cgraph_finalize_function): Updating of pid         * tree-profile.c:        
 (tree_init_ic_make_global_vars): New function        
 (tree_init_edge_profiler): call of tree_init_ic_make_global_vars        
 (tree_gen_ic_profiler): New function         (tree_gen_ic_func_profiler): New
 function         (tree_profiling): Added calling of tree_gen_ic_func_profiler
         (tree_profile_hooks): Added hook for indirec/virtual calls         *
 value-prof.c (tree_find_values_to_profile): New case for         indirect
 calls         (tree_values_to_profile): Call for determining indirect/virtual
         counters         (tree_indirect_call_to_profile): New function       
  (tree_ic_transform): New function         (tree_ic): New function        
 (find_func_by_pid): New function         (init_pid_map): New function        
 (tree_value_profile_transformations): Added check for        
 indirect/virtual call transformation         * value-prof.h (enum hist_type):
 New counter type for         indirect/virtual calls         (profile_hooks):
 Added new hook for profiling indirect/virtual         calls         *
 profile.c (instrument_values): New case for indirect/virtual         call
 added         * gcov-io.h (GCOV_LAST_VALUE_COUNTER): Changed to 6        
 (GCOV_COUNTER_V_INDIR): New counter type         (GCOV_COUNTER_NAMES): New
 name of counter "indirect" added         (GCOV_MERGE_FUNCTIONS): New merge
 function for indirect/virtual         call added         * cgraph.c:
 Definition of cgraph_max_pid         (cgraph_create_node): Default init of
 pid attribute         * cgraph.h: Declaration of cgraph_max_pid        
 (struct cgraph_node): Added pid attribute         * libgcov.c
 (__gcov_indirect_call_profiler): New function 
 (__gcov_one_value_profiler_body): New function 
 (__gcov_one_value_profiler): Body was moved to 
 __gcov_one_value_profiler_body and calls it

	gcc.dg/tree-prof/indir-call-prof.c: New.
	g++.dg/dg.exp: Add tree-prof subdirectory.
	g++.dg/tree-prof/indir-call-prof.C: New.
	g++.dg/tree-prof/tree-prof.exp: New.


git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@120975 138bc75d-0d04-0410-961f-82ee72b054a4
---
 gcc/ChangeLog                                 |  41 +++
 gcc/Makefile.in                               |   5 +-
 gcc/cgraph.c                                  |   6 +-
 gcc/cgraph.h                                  |   5 +
 gcc/cgraphunit.c                              |   1 +
 gcc/gcov-io.h                                 |  11 +-
 gcc/libgcov.c                                 |  23 +-
 gcc/predict.c                                 |   1 +
 gcc/profile.c                                 |   8 +
 gcc/testsuite/ChangeLog                       |   7 +
 gcc/testsuite/g++.dg/dg.exp                   |   1 +
 .../g++.dg/tree-prof/indir-call-prof.C        |  39 +++
 gcc/testsuite/g++.dg/tree-prof/tree-prof.exp  |  53 ++++
 .../gcc.dg/tree-prof/indir-call-prof.c        |  45 ++++
 gcc/tree-profile.c                            | 144 +++++++++-
 gcc/value-prof.c                              | 251 +++++++++++++++++-
 gcc/value-prof.h                              |   7 +-
 17 files changed, 635 insertions(+), 13 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/tree-prof/indir-call-prof.C
 create mode 100644 gcc/testsuite/g++.dg/tree-prof/tree-prof.exp
 create mode 100644 gcc/testsuite/gcc.dg/tree-prof/indir-call-prof.c

diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 7fff5e851767..7f8ac0e9622c 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,44 @@
+2007-01-19  Tomas Bily  <tbily@suse.cz>
+
+        * cgraphunit.c (cgraph_finalize_function): Updating of pid
+        * tree-profile.c:
+        (tree_init_ic_make_global_vars): New function
+        (tree_init_edge_profiler): call of tree_init_ic_make_global_vars
+        (tree_gen_ic_profiler): New function
+        (tree_gen_ic_func_profiler): New function
+        (tree_profiling): Added calling of tree_gen_ic_func_profiler
+        (tree_profile_hooks): Added hook for indirec/virtual calls
+        * value-prof.c (tree_find_values_to_profile): New case for
+        indirect calls
+        (tree_values_to_profile): Call for determining indirect/virtual
+        counters
+        (tree_indirect_call_to_profile): New function
+        (tree_ic_transform): New function
+        (tree_ic): New function
+        (find_func_by_pid): New function
+        (init_pid_map): New function
+        (tree_value_profile_transformations): Added check for
+        indirect/virtual call transformation
+        * value-prof.h (enum hist_type): New counter type for
+        indirect/virtual calls
+        (profile_hooks): Added new hook for profiling indirect/virtual
+        calls
+        * profile.c (instrument_values): New case for indirect/virtual
+        call added
+        * gcov-io.h (GCOV_LAST_VALUE_COUNTER): Changed to 6
+        (GCOV_COUNTER_V_INDIR): New counter type
+        (GCOV_COUNTER_NAMES): New name of counter "indirect" added
+        (GCOV_MERGE_FUNCTIONS): New merge function for indirect/virtual
+        call added
+        * cgraph.c: Definition of cgraph_max_pid
+        (cgraph_create_node): Default init of pid attribute
+        * cgraph.h: Declaration of cgraph_max_pid
+        (struct cgraph_node): Added pid attribute
+        * libgcov.c (__gcov_indirect_call_profiler): New function
+	(__gcov_one_value_profiler_body): New function
+	(__gcov_one_value_profiler): Body was moved to
+	__gcov_one_value_profiler_body and calls it
+
 2007-01-19  Basile Starynkevitch  <basile@starynkevitch.net>
 
 	* doc/gty.texi (Options): Document the mark_hook option to
diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index c63dec01989d..59ac3dd8b4f0 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1013,7 +1013,8 @@ LIB2FUNCS_ST = _eprintf __gcc_bcmp
 LIBGCOV = _gcov _gcov_merge_add _gcov_merge_single _gcov_merge_delta \
     _gcov_fork _gcov_execl _gcov_execlp _gcov_execle \
     _gcov_execv _gcov_execvp _gcov_execve \
-    _gcov_interval_profiler _gcov_pow2_profiler _gcov_one_value_profiler
+    _gcov_interval_profiler _gcov_pow2_profiler _gcov_one_value_profiler \
+    _gcov_indirect_call_profiler
 
 FPBIT_FUNCS = _pack_sf _unpack_sf _addsub_sf _mul_sf _div_sf \
     _fpcmp_parts_sf _compare_sf _eq_sf _ne_sf _gt_sf _ge_sf \
@@ -2355,7 +2356,7 @@ profile.o : profile.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \
 tree-profile.o : tree-profile.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \
    $(TM_H) $(RTL_H) $(TREE_H) $(FLAGS_H) output.h $(REGS_H) $(EXPR_H) \
    $(FUNCTION_H) toplev.h $(COVERAGE_H) $(TREE_H) value-prof.h $(TREE_DUMP_H) \
-   tree-pass.h $(TREE_FLOW_H) $(TIMEVAR_H) $(GGC_H) gt-tree-profile.h
+   tree-pass.h $(TREE_FLOW_H) $(TIMEVAR_H) $(GGC_H) gt-tree-profile.h $(CGRAPH_H)
 value-prof.o : value-prof.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
    $(BASIC_BLOCK_H) hard-reg-set.h value-prof.h $(EXPR_H) output.h $(FLAGS_H) \
    $(RECOG_H) insn-config.h $(OPTABS_H) $(REGS_H) $(GGC_H) $(DIAGNOSTIC_H) \
diff --git a/gcc/cgraph.c b/gcc/cgraph.c
index 1f7f0ad67426..13b7fcd2938c 100644
--- a/gcc/cgraph.c
+++ b/gcc/cgraph.c
@@ -109,6 +109,9 @@ int cgraph_n_nodes;
 /* Maximal uid used in cgraph nodes.  */
 int cgraph_max_uid;
 
+/* Maximal pid used for profiling */
+int cgraph_max_pid;
+
 /* Set when whole unit has been analyzed so we can access global info.  */
 bool cgraph_global_info_ready = false;
 
@@ -161,6 +164,7 @@ cgraph_create_node (void)
   node = GGC_CNEW (struct cgraph_node);
   node->next = cgraph_nodes;
   node->uid = cgraph_max_uid++;
+  node->pid = -1;
   node->order = cgraph_order++;
   if (cgraph_nodes)
     cgraph_nodes->previous = node;
@@ -655,7 +659,7 @@ void
 dump_cgraph_node (FILE *f, struct cgraph_node *node)
 {
   struct cgraph_edge *edge;
-  fprintf (f, "%s/%i:", cgraph_node_name (node), node->uid);
+  fprintf (f, "%s/%i(%i):", cgraph_node_name (node), node->uid, node->pid);
   if (node->global.inlined_to)
     fprintf (f, " (inline copy in %s/%i)",
 	     cgraph_node_name (node->global.inlined_to),
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index 3a5805e9b813..179a5f1714e3 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -180,6 +180,10 @@ struct cgraph_node GTY((chain_next ("%h.next"), chain_prev ("%h.previous")))
      into clone before compiling so the function in original form can be
      inlined later.  This pointer points to the clone.  */
   tree inline_decl;
+
+  /* unique id for profiling. pid is not suitable because of different
+     number of cfg nodes with -fprofile-generate and -fprofile-use */
+  int pid;
 };
 
 struct cgraph_edge GTY((chain_next ("%h.next_caller"), chain_prev ("%h.prev_caller")))
@@ -253,6 +257,7 @@ struct cgraph_asm_node GTY(())
 extern GTY(()) struct cgraph_node *cgraph_nodes;
 extern GTY(()) int cgraph_n_nodes;
 extern GTY(()) int cgraph_max_uid;
+extern GTY(()) int cgraph_max_pid;
 extern bool cgraph_global_info_ready;
 enum cgraph_state
 {
diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c
index 5a0b9a99c499..055399e83303 100644
--- a/gcc/cgraphunit.c
+++ b/gcc/cgraphunit.c
@@ -444,6 +444,7 @@ cgraph_finalize_function (tree decl, bool nested)
   if (node->local.finalized)
     cgraph_reset_node (node);
 
+  node->pid = cgraph_max_pid ++;
   notice_global_symbol (decl);
   node->decl = decl;
   node->local.finalized = true;
diff --git a/gcc/gcov-io.h b/gcc/gcov-io.h
index f3607d978908..e819eb3fa2ac 100644
--- a/gcc/gcov-io.h
+++ b/gcc/gcov-io.h
@@ -327,23 +327,26 @@ typedef HOST_WIDEST_INT gcov_type;
 #define GCOV_COUNTER_V_SINGLE	3  /* The most common value of expression.  */
 #define GCOV_COUNTER_V_DELTA	4  /* The most common difference between
 				      consecutive values of expression.  */
-#define GCOV_LAST_VALUE_COUNTER 4  /* The last of counters used for value
+
+#define GCOV_COUNTER_V_INDIR	5  /* The most common indirect address */
+#define GCOV_LAST_VALUE_COUNTER 5  /* The last of counters used for value
 				      profiling.  */
-#define GCOV_COUNTERS		5
+#define GCOV_COUNTERS		6
 
 /* Number of counters used for value profiling.  */
 #define GCOV_N_VALUE_COUNTERS \
   (GCOV_LAST_VALUE_COUNTER - GCOV_FIRST_VALUE_COUNTER + 1)
   
   /* A list of human readable names of the counters */
-#define GCOV_COUNTER_NAMES	{"arcs", "interval", "pow2", "single", "delta"}
+#define GCOV_COUNTER_NAMES	{"arcs", "interval", "pow2", "single", "delta", "indirect_call"}
   
   /* Names of merge functions for counters.  */
 #define GCOV_MERGE_FUNCTIONS	{"__gcov_merge_add",	\
 				 "__gcov_merge_add",	\
 				 "__gcov_merge_add",	\
 				 "__gcov_merge_single",	\
-				 "__gcov_merge_delta"}
+				 "__gcov_merge_delta",  \
+				 "__gcov_merge_single" }
   
 /* Convert a counter index to a tag.  */
 #define GCOV_TAG_FOR_COUNTER(COUNT)				\
diff --git a/gcc/libgcov.c b/gcc/libgcov.c
index 494759e6bed1..880686eba04c 100644
--- a/gcc/libgcov.c
+++ b/gcc/libgcov.c
@@ -726,7 +726,6 @@ __gcov_pow2_profiler (gcov_type *counters, gcov_type value)
 }
 #endif
 
-#ifdef L_gcov_one_value_profiler
 /* Tries to determine the most common value among its inputs.  Checks if the
    value stored in COUNTERS[0] matches VALUE.  If this is the case, COUNTERS[1]
    is incremented.  If this is not the case and COUNTERS[1] is not zero,
@@ -737,8 +736,8 @@ __gcov_pow2_profiler (gcov_type *counters, gcov_type value)
 
    In any case, COUNTERS[2] is incremented.  */
 
-void
-__gcov_one_value_profiler (gcov_type *counters, gcov_type value)
+static inline void
+__gcov_one_value_profiler_body (gcov_type *counters, gcov_type value)
 {
   if (value == counters[0])
     counters[1]++;
@@ -751,6 +750,24 @@ __gcov_one_value_profiler (gcov_type *counters, gcov_type value)
     counters[1]--;
   counters[2]++;
 }
+
+#ifdef L_gcov_one_value_profiler
+void
+__gcov_one_value_profiler (gcov_type *counters, gcov_type value)
+{
+  __gcov_one_value_profiler_body (counters, value);
+}
+#endif
+
+#ifdef L_gcov_indirect_call_profiler
+/* Tries to determine the most common value among its inputs. */
+void
+__gcov_indirect_call_profiler (gcov_type* counter, gcov_type value, 
+			       void* cur_func, void* callee_func)
+{
+  if (cur_func == callee_func)
+    __gcov_one_value_profiler_body (counter, value);
+}
 #endif
 
 #ifdef L_gcov_fork
diff --git a/gcc/predict.c b/gcc/predict.c
index 61d8547def0c..b29d04a56cff 100644
--- a/gcc/predict.c
+++ b/gcc/predict.c
@@ -1638,6 +1638,7 @@ counts_to_freqs (void)
   count_max = MAX (true_count_max, 1);
   FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR, NULL, next_bb)
     bb->frequency = (bb->count * BB_FREQ_MAX + count_max / 2) / count_max;
+
   return true_count_max;
 }
 
diff --git a/gcc/profile.c b/gcc/profile.c
index 7c38b7fe149f..ef6b32661570 100644
--- a/gcc/profile.c
+++ b/gcc/profile.c
@@ -192,6 +192,10 @@ instrument_values (histogram_values values)
 	  t = GCOV_COUNTER_V_DELTA;
 	  break;
 
+ 	case HIST_TYPE_INDIR_CALL:
+ 	  t = GCOV_COUNTER_V_INDIR;
+ 	  break;
+
 	default:
 	  gcc_unreachable ();
 	}
@@ -216,6 +220,10 @@ instrument_values (histogram_values values)
 	  (profile_hooks->gen_const_delta_profiler) (hist, t, 0);
 	  break;
 
+ 	case HIST_TYPE_INDIR_CALL:
+ 	  (profile_hooks->gen_ic_profiler) (hist, t, 0);
+  	  break;
+
 	default:
 	  gcc_unreachable ();
 	}
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 34b02acefa82..e0178ad1a1d6 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,10 @@
+2007-01-19  Tomas Bily  <tbily@suse.cz>
+
+	gcc.dg/tree-prof/indir-call-prof.c: New.
+	g++.dg/dg.exp: Add tree-prof subdirectory.
+	g++.dg/tree-prof/indir-call-prof.C: New.
+	g++.dg/tree-prof/tree-prof.exp: New.
+
 2007-01-19  Manuel Lopez-Ibanez  <manu@gcc.gnu.org>
 
 	PR c++/17947
diff --git a/gcc/testsuite/g++.dg/dg.exp b/gcc/testsuite/g++.dg/dg.exp
index 5ecb161bee7d..2581139eac32 100644
--- a/gcc/testsuite/g++.dg/dg.exp
+++ b/gcc/testsuite/g++.dg/dg.exp
@@ -41,6 +41,7 @@ set tests [prune $tests $srcdir/$subdir/special/*]
 set tests [prune $tests $srcdir/$subdir/tls/*]
 set tests [prune $tests $srcdir/$subdir/vect/*]
 set tests [prune $tests $srcdir/$subdir/gomp/*]
+set tests [prune $tests $srcdir/$subdir/tree-prof/*]
 
 # Main loop.
 dg-runtest $tests "" $DEFAULT_CXXFLAGS
diff --git a/gcc/testsuite/g++.dg/tree-prof/indir-call-prof.C b/gcc/testsuite/g++.dg/tree-prof/indir-call-prof.C
new file mode 100644
index 000000000000..98ab302663fd
--- /dev/null
+++ b/gcc/testsuite/g++.dg/tree-prof/indir-call-prof.C
@@ -0,0 +1,39 @@
+/* { dg-options "-O2 -fdump-tree-optimized -fdump-tree-tree_profile" } */
+
+struct A {
+  A () {}
+
+  virtual int AA (void)
+  { return 0; }
+
+};
+
+struct B : public A {
+  B () {}
+
+  virtual int AA (void)
+  { return 1; }
+};
+
+int
+main (void)
+{
+  A a;
+  B b;
+  
+  A* p;
+
+  p = &a;
+  p->AA ();
+
+  p = &b;
+  p->AA ();
+  
+  return 0;
+}
+
+/* { dg-final-use { scan-tree-dump "Indirect call -> direct call.* AA transformation on insn" "tree_profile"} } */
+/* { dg-final-use { scan-tree-dump-not "Invalid sum" "optimized"} } */                                                                                
+/* { dg-final-use { cleanup-tree-dump "optimized" } } */                                                                                              
+/* { dg-final-use { cleanup-tree-dump "tree_profile" } } */                                                                                           
+
diff --git a/gcc/testsuite/g++.dg/tree-prof/tree-prof.exp b/gcc/testsuite/g++.dg/tree-prof/tree-prof.exp
new file mode 100644
index 000000000000..f9ebbd695957
--- /dev/null
+++ b/gcc/testsuite/g++.dg/tree-prof/tree-prof.exp
@@ -0,0 +1,53 @@
+#   Copyright (C) 2001, 2002, 2004, 2005 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  
+
+# Test the functionality of programs compiled with profile-directed block
+# ordering using -fprofile-generate followed by -fbranch-use.
+
+load_lib target-supports.exp
+
+# Some targets don't support tree profiling.
+if { ![check_profiling_available ""] } {
+    return
+}
+
+# The procedures in profopt.exp need these parameters.
+set tool g++
+set prof_ext "gcda gcno"
+
+# Override the list defined in profopt.exp.
+set PROFOPT_OPTIONS [list {}]
+
+if $tracelevel then {
+    strace $tracelevel
+}
+
+# Load support procs.
+load_lib profopt.exp
+
+# These are globals used by profopt-execute.  The first is options
+# needed to generate profile data, the second is options to use the
+# profile data.
+set profile_option "-fprofile-generate"
+set feedback_option "-fprofile-use"
+
+foreach src [lsort [glob -nocomplain $srcdir/$subdir/*.C]] {
+    # If we're only testing specific files and this isn't one of them, skip it.
+    if ![runtest_file_p $runtests $src] then {
+        continue
+    }
+    profopt-execute $src
+}
diff --git a/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof.c b/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof.c
new file mode 100644
index 000000000000..101b9725f863
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof.c
@@ -0,0 +1,45 @@
+/* { dg-options "-O2 -fdump-tree-optimized -fdump-tree-tree_profile" } */
+
+static int a1 (void)
+{
+    return 10;
+}
+
+static int a2 (void)
+{
+    return 0;
+}
+
+typedef int (*tp) (void);
+
+static tp aa [] = {a2, a1, a1, a1, a1};
+
+void setp (int (**pp) (void), int i)
+{
+  if (!i)
+    *pp = aa [i];
+  else
+    *pp = aa [(i & 2) + 1];
+}
+
+int
+main (void)
+{
+  int (*p) (void);
+  int  i;
+
+  for (i = 0; i < 10; i ++)
+    {
+	setp (&p, i);
+	p ();
+    }
+  
+  return 0;
+}
+
+/* { dg-final-use { scan-tree-dump "Indirect call -> direct call.* a1 transformation on insn" "tree_profile"} } */
+/* { dg-final-use { scan-tree-dump-not "Invalid sum" "optimized"} } */                                                                                
+/* { dg-final-use { cleanup-tree-dump "optimized" } } */                                                                                              
+/* { dg-final-use { cleanup-tree-dump "tree_profile" } } */
+                                                                                           
+
diff --git a/gcc/tree-profile.c b/gcc/tree-profile.c
index 3ff39f34aec3..2a4ec2ae9eb7 100644
--- a/gcc/tree-profile.c
+++ b/gcc/tree-profile.c
@@ -45,15 +45,54 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
 #include "timevar.h"
 #include "value-prof.h"
 #include "ggc.h"
+#include "cgraph.h"
 
 static GTY(()) tree gcov_type_node;
 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 ic_void_ptr_var;
+static GTY(()) tree ic_gcov_type_ptr_var;
+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 callie addres
+*/
+static void
+tree_init_ic_make_global_vars (void)
+{
+  tree  gcov_type_ptr;
+
+  ptr_void = build_pointer_type (void_type_node);
+  
+  ic_void_ptr_var 
+    = build_decl (VAR_DECL, 
+		  get_identifier ("__gcov_indirect_call_callee"), 
+		  ptr_void);
+  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;
+  assemble_variable (ic_void_ptr_var, 0, 0, 0);
+
+  gcov_type_ptr = build_pointer_type (get_gcov_type ());
+  ic_gcov_type_ptr_var 
+    = build_decl (VAR_DECL, 
+		  get_identifier ("__gcov_indirect_call_counters"), 
+		  gcov_type_ptr);
+  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;
+  assemble_variable (ic_gcov_type_ptr_var, 0, 0, 0);
+}
+
 static void
 tree_init_edge_profiler (void)
 {
@@ -61,6 +100,7 @@ tree_init_edge_profiler (void)
   tree pow2_profiler_fn_type;
   tree one_value_profiler_fn_type;
   tree gcov_type_ptr;
+  tree ic_profiler_fn_type;
 
   if (!gcov_type_node)
     {
@@ -93,6 +133,18 @@ tree_init_edge_profiler (void)
       tree_one_value_profiler_fn
 	      = build_fn_decl ("__gcov_one_value_profiler",
 				     one_value_profiler_fn_type);
+
+      tree_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);
     }
 }
 
@@ -201,6 +253,90 @@ tree_gen_one_value_profiler (histogram_value value, unsigned tag, unsigned base)
   bsi_insert_before (&bsi, call, BSI_SAME_STMT);
 }
 
+
+/* Output instructions as GIMPLE trees for code to find the most
+   common called function in indirect call.  
+   VALUE is the call expression whose indirect callie is profiled.
+   TAG is the tag of the section for counters, BASE is offset of the
+   counter position.  */
+
+static void
+tree_gen_ic_profiler (histogram_value value, unsigned tag, unsigned base)
+{
+  tree tmp1, stmt1, stmt2, stmt3;
+  tree stmt = value->hvalue.stmt;
+  block_stmt_iterator bsi = bsi_for_stmt (stmt);
+  tree ref = tree_coverage_counter_ref (tag, base), ref_ptr;
+
+  ref_ptr = force_gimple_operand_bsi (&bsi,
+				      build_addr (ref, current_function_decl),
+				      true, NULL_TREE);
+
+  /* Insert code:
+    
+    __gcov_indirect_call_counters = get_relevant_counter_ptr (); 
+    __gcov_indirect_call_callee = (void *) indirect call argument;
+   */
+
+  tmp1 = create_tmp_var (ptr_void, "PROF");
+  stmt1 = build2 (GIMPLE_MODIFY_STMT, 
+		  build_pointer_type (get_gcov_type ()), 
+		  ic_gcov_type_ptr_var, ref_ptr);
+  stmt2 = build2 (GIMPLE_MODIFY_STMT, ptr_void, tmp1, 
+		  unshare_expr (value->hvalue.value));
+  stmt3 = build2 (GIMPLE_MODIFY_STMT, ptr_void, 
+		  ic_void_ptr_var, tmp1);
+
+  bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT);
+  bsi_insert_before (&bsi, stmt2, BSI_SAME_STMT);
+  bsi_insert_before (&bsi, stmt3, BSI_SAME_STMT);
+}
+
+
+/* Output instructions as GIMPLE trees for code to find the most
+   common called function in indirect call. Insert instructions at the
+   begining of every possible called function.
+  */
+
+static void
+tree_gen_ic_func_profiler (void)
+{
+  struct cgraph_node * c_node = cgraph_node (current_function_decl);
+  block_stmt_iterator bsi;
+  edge e;
+  basic_block bb;
+  edge_iterator ei;
+  tree stmt1;
+  tree args, tree_uid, cur_func;
+
+  if (flag_unit_at_a_time)
+    {
+      if (!c_node->needed)
+	return;
+    }
+  
+  tree_init_edge_profiler ();
+  
+  FOR_EACH_EDGE (e, ei, ENTRY_BLOCK_PTR->succs)
+    {
+      bb = split_edge (e);
+      bsi = bsi_start (bb);
+      cur_func = force_gimple_operand_bsi (&bsi,
+					   build_addr (current_function_decl, 
+						       current_function_decl),
+					   true, NULL_TREE);
+      tree_uid = build_int_cst (gcov_type_node, c_node->pid);
+      args = tree_cons (NULL_TREE, ic_gcov_type_ptr_var,
+			tree_cons (NULL_TREE, tree_uid,
+				   tree_cons (NULL_TREE, cur_func,
+					      tree_cons (NULL_TREE, 
+							 ic_void_ptr_var,
+							 NULL_TREE))));
+      stmt1 = build_function_call_expr (tree_indirect_call_profiler_fn, args);
+      bsi_insert_after (&bsi, stmt1, BSI_SAME_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
@@ -242,6 +378,11 @@ tree_profiling (void)
   if (cgraph_state == CGRAPH_STATE_FINISHED)
     return 0;
   branch_prob ();
+
+  if (! flag_branch_probabilities 
+      && flag_profile_values)
+    tree_gen_ic_func_profiler ();
+
   if (flag_branch_probabilities
       && flag_profile_values
       && flag_value_profile_transformations)
@@ -278,7 +419,8 @@ struct profile_hooks tree_profile_hooks =
   tree_gen_interval_profiler,   /* gen_interval_profiler */
   tree_gen_pow2_profiler,       /* gen_pow2_profiler */
   tree_gen_one_value_profiler,  /* gen_one_value_profiler */
-  tree_gen_const_delta_profiler /* gen_const_delta_profiler */
+  tree_gen_const_delta_profiler,/* gen_const_delta_profiler */
+  tree_gen_ic_profiler,		/* gen_ic_profiler */
 };
 
 #include "gt-tree-profile.h"
diff --git a/gcc/value-prof.c b/gcc/value-prof.c
index 2b2bb1b6c1e3..f23fd68dd6fe 100644
--- a/gcc/value-prof.c
+++ b/gcc/value-prof.c
@@ -40,6 +40,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
 #include "coverage.h"
 #include "tree.h"
 #include "gcov-io.h"
+#include "cgraph.h"
 #include "timevar.h"
 #include "tree-pass.h"
 #include "toplev.h"
@@ -60,6 +61,11 @@ static struct value_prof_hooks *value_prof_hooks;
       FIXME: This transformation was removed together with RTL based value
       profiling.
 
+   3) Indirect/virtual call specialization. If we can determine most
+      common function callee in indirect/virtual call. We can use this
+      information to improve code effectivity (espetialy info for
+      inliner).
+
    Every such optimization should add its requirements for profiled values to
    insn_values_to_profile function.  This function is called from branch_prob
    in profile.c and the requested values are instrumented by it in the first
@@ -81,6 +87,7 @@ static bool tree_divmod_fixed_value_transform (tree);
 static bool tree_mod_pow2_value_transform (tree);
 static bool tree_mod_subtract_transform (tree);
 static bool tree_stringops_transform (block_stmt_iterator *);
+static bool tree_ic_transform (tree);
 
 /* Allocate histogram value.  */
 
@@ -254,6 +261,19 @@ dump_histogram_value (FILE *dump_file, histogram_value hist)
 	}
       fprintf (dump_file, ".\n");
       break;
+    case HIST_TYPE_INDIR_CALL:
+      fprintf (dump_file, "Indirect call ");
+      if (hist->hvalue.counters)
+	{
+	   fprintf (dump_file, "value:"HOST_WIDEST_INT_PRINT_DEC
+		    " match:"HOST_WIDEST_INT_PRINT_DEC
+		    " all:"HOST_WIDEST_INT_PRINT_DEC,
+		    (HOST_WIDEST_INT) hist->hvalue.counters[0],
+		    (HOST_WIDEST_INT) hist->hvalue.counters[1],
+		    (HOST_WIDEST_INT) hist->hvalue.counters[2]);
+	}
+      fprintf (dump_file, ".\n");
+      break;
    }
 }
 
@@ -432,7 +452,8 @@ tree_value_profile_transformations (void)
 	      && (tree_mod_subtract_transform (stmt)
 		  || tree_divmod_fixed_value_transform (stmt)
 		  || tree_mod_pow2_value_transform (stmt)
-		  || tree_stringops_transform (&bsi)))
+		  || tree_stringops_transform (&bsi)
+		  || tree_ic_transform (stmt)))
 	    {
 	      stmt = bsi_stmt (bsi);
 	      changed = true;
@@ -967,6 +988,201 @@ tree_mod_subtract_transform (tree stmt)
   return true;
 }
 
+static struct cgraph_node** pid_map = NULL;
+
+/* Initialize map of pids (pid -> cgraph node) */
+
+static void 
+init_pid_map (void)
+{
+  struct cgraph_node *n;
+
+  if (pid_map != NULL)
+    return;
+
+  pid_map 
+    = (struct cgraph_node**) xmalloc (sizeof (struct cgraph_node*) * cgraph_max_pid);
+
+  for (n = cgraph_nodes; n; n = n->next)
+    {
+      if (n->pid != -1)
+	pid_map [n->pid] = n;
+    }
+}
+
+/* Return cgraph node for function with pid */
+
+static inline struct cgraph_node*
+find_func_by_pid (int	pid)
+{
+  init_pid_map ();
+
+  return pid_map [pid];
+}
+
+/* Do transformation
+
+  if (actual_callee_addres == addres_of_most_common_function/method)
+    do direct call
+  else
+    old call
+ */
+
+static tree
+tree_ic (tree stmt, tree call, struct cgraph_node* direct_call, 
+	 int prob, gcov_type count, gcov_type all)
+{
+  tree stmt1, stmt2, stmt3;
+  tree tmp1, tmpv;
+  tree label_decl1 = create_artificial_label ();
+  tree label_decl2 = create_artificial_label ();
+  tree label1, label2;
+  tree bb1end, bb2end, bb3end;
+  tree new_call;
+  basic_block bb, bb2, bb3, bb4;
+  tree optype = build_pointer_type (void_type_node);
+  edge e12, e13, e23, e24, e34;
+  block_stmt_iterator bsi;
+  int region;
+
+  bb = bb_for_stmt (stmt);
+  bsi = bsi_for_stmt (stmt);
+
+  tmpv = create_tmp_var (optype, "PROF");
+  tmp1 = create_tmp_var (optype, "PROF");
+  stmt1 = build2 (GIMPLE_MODIFY_STMT, optype, tmpv, 
+		  unshare_expr (TREE_OPERAND (call, 0)));
+  stmt2 = build2 (GIMPLE_MODIFY_STMT, optype, tmp1, 
+		  fold_convert (optype, build_addr (direct_call->decl, 
+						    current_function_decl)));
+  stmt3 = build3 (COND_EXPR, void_type_node,
+		  build2 (NE_EXPR, boolean_type_node, tmp1, tmpv),
+		  build1 (GOTO_EXPR, void_type_node, label_decl2),
+		  build1 (GOTO_EXPR, void_type_node, label_decl1));
+  bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT);
+  bsi_insert_before (&bsi, stmt2, BSI_SAME_STMT);
+  bsi_insert_before (&bsi, stmt3, BSI_SAME_STMT);
+  bb1end = stmt3;
+
+  label1 = build1 (LABEL_EXPR, void_type_node, label_decl1);
+  stmt1 = unshare_expr (stmt);
+  new_call = get_call_expr_in (stmt1);
+  TREE_OPERAND (new_call, 0) = build_addr (direct_call->decl, 
+					   current_function_decl);
+  bsi_insert_before (&bsi, label1, BSI_SAME_STMT);
+  bsi_insert_before (&bsi, stmt1, BSI_SAME_STMT);
+  bb2end = stmt1;
+
+  label2 = build1 (LABEL_EXPR, void_type_node, label_decl2);
+  bsi_insert_before (&bsi, label2, BSI_SAME_STMT);
+  bb3end = stmt;
+
+  /* Fix CFG. */
+  /* Edge e23 connects bb2 to bb3, etc. */
+  e12 = split_block (bb, bb1end);
+  bb2 = e12->dest;
+  bb2->count = count;
+  e23 = split_block (bb2, bb2end);
+  bb3 = e23->dest;
+  bb3->count = all - count;
+  e34 = split_block (bb3, bb3end);
+  bb4 = e34->dest;
+  bb4->count = all;
+
+  e12->flags &= ~EDGE_FALLTHRU;
+  e12->flags |= EDGE_FALSE_VALUE;
+  e12->probability = prob;
+  e12->count = count;
+
+  e13 = make_edge (bb, bb3, EDGE_TRUE_VALUE);
+  e13->probability = REG_BR_PROB_BASE - prob;
+  e13->count = all - count;
+
+  remove_edge (e23);
+  
+  e24 = make_edge (bb2, bb4, EDGE_FALLTHRU);
+  e24->probability = REG_BR_PROB_BASE;
+  e24->count = count;
+  e34->probability = REG_BR_PROB_BASE;
+  e34->count = all - count;
+
+  /* Fix eh edges */
+  region = lookup_stmt_eh_region (stmt);
+  if (region >=0 && tree_could_throw_p (stmt1))
+    {
+      add_stmt_to_eh_region (stmt1, region);
+      make_eh_edges (stmt1);
+    }
+
+  if (region >=0 && tree_could_throw_p (stmt))
+    {
+      tree_purge_dead_eh_edges (bb4);
+      make_eh_edges (stmt);
+    }
+
+  return stmt1;
+}
+
+/*
+  For every checked indirect/virtual call determine if most common pid of
+  function/class method has probability more than 50%. If yes modify code of
+  this call to:
+ */
+
+static bool
+tree_ic_transform (tree stmt)
+{
+  histogram_value histogram;
+  gcov_type val, count, all;
+  int prob;
+  tree call, callee, modify;
+  struct cgraph_node *direct_call;
+  
+  call = get_call_expr_in (stmt);
+
+  if (!call || TREE_CODE (call) != CALL_EXPR)
+    return false;
+
+  callee = TREE_OPERAND (call, 0);
+
+  if (TREE_CODE (callee) == ADDR_EXPR)
+    return false;
+
+  histogram = gimple_histogram_value_of_type (cfun, stmt, HIST_TYPE_INDIR_CALL);
+  if (!histogram)
+    return false;
+
+  val = histogram->hvalue.counters [0];
+  count = histogram->hvalue.counters [1];
+  all = histogram->hvalue.counters [2];
+  gimple_remove_histogram_value (cfun, stmt, histogram);
+
+  if (4 * count <= 3 * all)
+    return false;
+
+  prob = (count * REG_BR_PROB_BASE + all / 2) / all;
+  direct_call = find_func_by_pid ((int)val);
+
+  if (direct_call == NULL)
+    return false;
+
+  modify = tree_ic (stmt, call, direct_call, prob, count, all);
+
+  if (dump_file)
+    {
+      fprintf (dump_file, "Indirect call -> direct call ");
+      print_generic_expr (dump_file, call, TDF_SLIM);
+      fprintf (dump_file, "=> ");
+      print_generic_expr (dump_file, direct_call->decl, TDF_SLIM);
+      fprintf (dump_file, " transformation on insn ");
+      print_generic_stmt (dump_file, stmt, TDF_SLIM);
+      fprintf (dump_file, " to ");
+      print_generic_stmt (dump_file, modify, TDF_SLIM);
+    }
+
+  return true;
+}
+
 /* Return true if the stringop FNDECL with ARGLIST shall be profiled.  */
 static bool
 interesting_stringop_to_profile_p (tree fndecl, tree arglist)
@@ -1260,6 +1476,34 @@ tree_divmod_values_to_profile (tree stmt, histogram_values *values)
     }
 }
 
+/* Find calls inside STMT for that we want to measure histograms for 
+   indirect/virtual call optimization. */ 
+
+static void
+tree_indirect_call_to_profile (tree stmt, histogram_values *values)
+{
+  tree			call;
+  tree			callee;
+
+  call = get_call_expr_in (stmt);
+
+  if (!call || TREE_CODE (call) != CALL_EXPR)
+    return;
+
+  callee = TREE_OPERAND (call, 0);
+  
+  if (TREE_CODE (callee) == ADDR_EXPR)
+    return;
+
+  VEC_reserve (histogram_value, heap, *values, 3);
+
+  VEC_quick_push (histogram_value, *values, 
+		  gimple_alloc_histogram_value (cfun, HIST_TYPE_INDIR_CALL,
+						stmt, callee));
+
+  return;
+}
+
 /* Find values inside STMT for that we want to measure histograms for
    string operations.  */
 static void
@@ -1303,6 +1547,7 @@ tree_values_to_profile (tree stmt, histogram_values *values)
     {
       tree_divmod_values_to_profile (stmt, values);
       tree_stringops_values_to_profile (stmt, values);
+      tree_indirect_call_to_profile (stmt, values);
     }
 }
 
@@ -1339,6 +1584,10 @@ tree_find_values_to_profile (histogram_values *values)
 	  hist->n_counters = 4;
 	  break;
 
+ 	case HIST_TYPE_INDIR_CALL:
+ 	  hist->n_counters = 3;
+	  break;
+
 	default:
 	  gcc_unreachable ();
 	}
diff --git a/gcc/value-prof.h b/gcc/value-prof.h
index 33670da4856c..78c9e887a27c 100644
--- a/gcc/value-prof.h
+++ b/gcc/value-prof.h
@@ -29,8 +29,10 @@ enum hist_type
   HIST_TYPE_POW2,	/* Histogram of power of 2 values.  */
   HIST_TYPE_SINGLE_VALUE, /* Tries to identify the value that is (almost)
 			   always constant.  */
-  HIST_TYPE_CONST_DELTA	/* Tries to identify the (almost) always constant
+  HIST_TYPE_CONST_DELTA, /* Tries to identify the (almost) always constant
 			   difference between two evaluations of a value.  */
+  HIST_TYPE_INDIR_CALL   /* Tries to identify the function that is (almost) 
+			    called in indirect call */
 };
 
 #define COUNTER_FOR_HIST_TYPE(TYPE) ((int) (TYPE) + GCOV_FIRST_VALUE_COUNTER)
@@ -94,6 +96,9 @@ struct profile_hooks {
   /* Insert code to find the most common value of a difference between two
      evaluations of an expression.  */
   void (*gen_const_delta_profiler) (histogram_value, unsigned, unsigned);
+
+  /* Insert code to find the most common indirect call */
+  void (*gen_ic_profiler) (histogram_value, unsigned, unsigned);
 };
 
 histogram_value gimple_histogram_value (struct function *, tree);
-- 
GitLab