Patchwork [google] dump inline decisions to stderr under -fopt-info

login
register
mail settings
Submitter Dehao Chen
Date Dec. 15, 2011, 8:30 a.m.
Message ID <CAO2gOZURn5PUj5QaMhYwKOVEwF8PFAci2u3A12LyXerhBE=oXQ@mail.gmail.com>
Download mbox | patch
Permalink /patch/131533/
State New
Headers show

Comments

Dehao Chen - Dec. 15, 2011, 8:30 a.m.
I change to use the bfd_name, which is much shorter for C++ symbols.

Thanks,
Dehao

On Thu, Dec 15, 2011 at 2:07 AM, Xinliang David Li <davidxl@google.com> wrote:
> Another usability related issue for C++. The long demangled function
> names will make the info messages very hard to swallow. Since there
> will be source lines to show the source context, it might be better to
> just use the short decl names. The downside is it can be ambiguous for
> template functions.
>
> David
>
> On Tue, Dec 13, 2011 at 11:18 PM, Xinliang David Li <davidxl@google.com> wrote:
>> There are a couple of problems with the patch (the patch is only
>> compatible with 4_6 branch)
>>
>> 1) the dump_inline_decision should be called inside
>> cgraph_mark_inline_edge when the edge is finally picked (and when the
>> callee node is cloned)
>> 2) The source location is not printed which makes the dumping less useful
>> 3) the information line should clearly print out the final caller
>> where the inlining happens, and the intermediate inline instance
>> information should be clearly printed as additional information
>> showing the inline context. For instance,
>>
>>  foo1->foo2->foo3->foo4
>>
>> If all callsites are inlined in top down order, the messages I expect
>> to see are:
>>
>> a.c:5: note: 'foo2' is inlined into 'foo1' with call count xxxx
>> a.c:10: note: 'foo3' is inlined into 'foo1' with call count yyyy (via
>> inline instance 'foo2')
>> a.c:20: note: 'foo4' is inlined into foo1' with call count zzzz (via
>> inline instances 'foo3', 'foo2')
>>
>>
>> If the decision is bottom up, the messages should look like:
>>
>> a.c:20: note: 'foo4' is inlined into 'foo3' with call count zzzz
>> a.c:10: note: 'foo3' is inlined into 'foo2' with call count yyyy
>> a.c:5: note: 'foo2' is inlined into 'foo1' with call count xxxx
>>
>> Ideally, the caller and callee should also be marked with 'entry count
>> and max bb count' information -- but that probably should be
>> controlled by opt-info level.
>>
>> 4) Also notice that for inline node clones, the right way to walk up
>> the call context chain is via 'edge->caller->callers'.
>> 5) there should be a test case.
>>
>> Thanks,
>>
>> David
>>
>> On Tue, Dec 13, 2011 at 5:13 PM, Dehao Chen <dehao@google.com> wrote:
>>> I've updated the patch to fix a bug in dump_inline_decision.
>>>
>>> Thanks,
>>> Dehao
>>>
>>> On Thu, Dec 1, 2011 at 9:59 AM, Dehao Chen <dehao@google.com> wrote:
>>>>
>>>> This patch is for google-{main|gcc_4.6} only.
>>>>
>>>> Tested with bootstrap and regression tests.
>>>>
>>>> Dump inline decisions, also output the inline chain.
>>>>
>>>> Dehao
>>>>
>>>> 2011-12-01  Dehao Chen  <dehao@google.com>
>>>>
>>>>        * ipa-inline.c (dump_inline_decision): New function.
>>>>        (inline_small_functions): Use it to dump the inline decisions to stderr.
>>>>
>>>> Index: gcc/ipa-inline.c
>>>> ===================================================================
>>>> --- gcc/ipa-inline.c    (revision 181835)
>>>> +++ gcc/ipa-inline.c    (working copy)
>>>> @@ -1377,6 +1377,45 @@
>>>>  }
>>>>
>>>>
>>>> +/* Dump the inline decision of EDGE to stderr.  */
>>>> +
>>>> +static void
>>>> +dump_inline_decision (struct cgraph_edge *edge)
>>>> +{
>>>> +  location_t locus;
>>>> +  size_t buf_size = 4096;
>>>> +  size_t current_string_len = 0;
>>>> +  char *buf = (char *) xmalloc (buf_size);
>>>> +  struct cgraph_node *inlined_to;
>>>> +  gcov_type callee_count = edge->callee->count;
>>>> +  buf[0] = 0;
>>>> +  if (edge->inline_failed == CIF_OK && edge->callee->clone_of)
>>>> +    callee_count += edge->callee->clone_of->count;
>>>> +  for (inlined_to = edge->caller->global.inlined_to;
>>>> +       inlined_to; inlined_to = inlined_to->global.inlined_to)
>>>> +    {
>>>> +      const char *name = cgraph_node_name (inlined_to);
>>>> +      if (!name)
>>>> +       name = "unknown";
>>>> +      current_string_len += (strlen (name) + 4);
>>>> +      while (current_string_len >= buf_size)
>>>> +       {
>>>> +         buf_size *= 2;
>>>> +         buf = (char *) xrealloc (buf, buf_size);
>>>> +       }
>>>> +      strcat (buf, "-->");
>>>> +      strcat (buf, name);
>>>> +    }
>>>> +  locus = gimple_location (edge->call_stmt);
>>>> +  inform (locus, "%s ("HOST_WIDEST_INT_PRINT_DEC") --"
>>>> +         HOST_WIDEST_INT_PRINT_DEC"--> %s ("
>>>> +         HOST_WIDEST_INT_PRINT_DEC") %s : %s",
>>>> +         cgraph_node_name (edge->callee), callee_count, edge->count,
>>>> +         cgraph_node_name (edge->caller), edge->caller->count, buf,
>>>> +         edge->inline_failed == CIF_OK ? "INLINED": "IGNORED");
>>>> +}
>>>> +
>>>> +
>>>>  /* We use greedy algorithm for inlining of small functions:
>>>>    All inline candidates are put into prioritized heap ordered in
>>>>    increasing badness.
>>>> @@ -1428,6 +1467,7 @@
>>>>   overall_size = initial_size;
>>>>   max_size = compute_max_insns (overall_size);
>>>>   min_size = overall_size;
>>>> +  edge = NULL;
>>>>
>>>>   /* Populate the heeap with all edges we might inline.  */
>>>>
>>>> @@ -1462,6 +1502,9 @@
>>>>       int current_badness;
>>>>       int growth;
>>>>
>>>> +      if (edge && flag_opt_info >= OPT_INFO_MIN)
>>>> +       dump_inline_decision (edge);
>>>> +
>>>>       edge = (struct cgraph_edge *) fibheap_extract_min (heap);
>>>>       gcc_assert (edge->aux);
>>>>       edge->aux = NULL;
>>>> @@ -1482,6 +1525,7 @@
>>>>       if (current_badness != badness)
>>>>        {
>>>>          edge->aux = fibheap_insert (heap, current_badness, edge);
>>>> +         edge = NULL;
>>>>          continue;
>>>>        }
>>>>
>>>> @@ -1636,6 +1680,8 @@
>>>>            fprintf (dump_file, "New minimal size reached: %i\n", min_size);
>>>>        }
>>>>     }
>>>> +  if (edge && flag_opt_info >= OPT_INFO_MIN)
>>>> +    dump_inline_decision (edge);
>>>>
>>>>   free_growth_caches ();
>>>>   if (new_indirect_edges)
Xinliang David Li - Dec. 15, 2011, 6:03 p.m.
Looks good in general.

1) The profile information should be omitted when profile information
is not available
2) have a test case showing the call chain.

Ok for google branches with the above changes.

thanks,

David

On Thu, Dec 15, 2011 at 12:30 AM, Dehao Chen <dehao@google.com> wrote:
> I change to use the bfd_name, which is much shorter for C++ symbols.
>
> Thanks,
> Dehao
>
> On Thu, Dec 15, 2011 at 2:07 AM, Xinliang David Li <davidxl@google.com> wrote:
>> Another usability related issue for C++. The long demangled function
>> names will make the info messages very hard to swallow. Since there
>> will be source lines to show the source context, it might be better to
>> just use the short decl names. The downside is it can be ambiguous for
>> template functions.
>>
>> David
>>
>> On Tue, Dec 13, 2011 at 11:18 PM, Xinliang David Li <davidxl@google.com> wrote:
>>> There are a couple of problems with the patch (the patch is only
>>> compatible with 4_6 branch)
>>>
>>> 1) the dump_inline_decision should be called inside
>>> cgraph_mark_inline_edge when the edge is finally picked (and when the
>>> callee node is cloned)
>>> 2) The source location is not printed which makes the dumping less useful
>>> 3) the information line should clearly print out the final caller
>>> where the inlining happens, and the intermediate inline instance
>>> information should be clearly printed as additional information
>>> showing the inline context. For instance,
>>>
>>>  foo1->foo2->foo3->foo4
>>>
>>> If all callsites are inlined in top down order, the messages I expect
>>> to see are:
>>>
>>> a.c:5: note: 'foo2' is inlined into 'foo1' with call count xxxx
>>> a.c:10: note: 'foo3' is inlined into 'foo1' with call count yyyy (via
>>> inline instance 'foo2')
>>> a.c:20: note: 'foo4' is inlined into foo1' with call count zzzz (via
>>> inline instances 'foo3', 'foo2')
>>>
>>>
>>> If the decision is bottom up, the messages should look like:
>>>
>>> a.c:20: note: 'foo4' is inlined into 'foo3' with call count zzzz
>>> a.c:10: note: 'foo3' is inlined into 'foo2' with call count yyyy
>>> a.c:5: note: 'foo2' is inlined into 'foo1' with call count xxxx
>>>
>>> Ideally, the caller and callee should also be marked with 'entry count
>>> and max bb count' information -- but that probably should be
>>> controlled by opt-info level.
>>>
>>> 4) Also notice that for inline node clones, the right way to walk up
>>> the call context chain is via 'edge->caller->callers'.
>>> 5) there should be a test case.
>>>
>>> Thanks,
>>>
>>> David
>>>
>>> On Tue, Dec 13, 2011 at 5:13 PM, Dehao Chen <dehao@google.com> wrote:
>>>> I've updated the patch to fix a bug in dump_inline_decision.
>>>>
>>>> Thanks,
>>>> Dehao
>>>>
>>>> On Thu, Dec 1, 2011 at 9:59 AM, Dehao Chen <dehao@google.com> wrote:
>>>>>
>>>>> This patch is for google-{main|gcc_4.6} only.
>>>>>
>>>>> Tested with bootstrap and regression tests.
>>>>>
>>>>> Dump inline decisions, also output the inline chain.
>>>>>
>>>>> Dehao
>>>>>
>>>>> 2011-12-01  Dehao Chen  <dehao@google.com>
>>>>>
>>>>>        * ipa-inline.c (dump_inline_decision): New function.
>>>>>        (inline_small_functions): Use it to dump the inline decisions to stderr.
>>>>>
>>>>> Index: gcc/ipa-inline.c
>>>>> ===================================================================
>>>>> --- gcc/ipa-inline.c    (revision 181835)
>>>>> +++ gcc/ipa-inline.c    (working copy)
>>>>> @@ -1377,6 +1377,45 @@
>>>>>  }
>>>>>
>>>>>
>>>>> +/* Dump the inline decision of EDGE to stderr.  */
>>>>> +
>>>>> +static void
>>>>> +dump_inline_decision (struct cgraph_edge *edge)
>>>>> +{
>>>>> +  location_t locus;
>>>>> +  size_t buf_size = 4096;
>>>>> +  size_t current_string_len = 0;
>>>>> +  char *buf = (char *) xmalloc (buf_size);
>>>>> +  struct cgraph_node *inlined_to;
>>>>> +  gcov_type callee_count = edge->callee->count;
>>>>> +  buf[0] = 0;
>>>>> +  if (edge->inline_failed == CIF_OK && edge->callee->clone_of)
>>>>> +    callee_count += edge->callee->clone_of->count;
>>>>> +  for (inlined_to = edge->caller->global.inlined_to;
>>>>> +       inlined_to; inlined_to = inlined_to->global.inlined_to)
>>>>> +    {
>>>>> +      const char *name = cgraph_node_name (inlined_to);
>>>>> +      if (!name)
>>>>> +       name = "unknown";
>>>>> +      current_string_len += (strlen (name) + 4);
>>>>> +      while (current_string_len >= buf_size)
>>>>> +       {
>>>>> +         buf_size *= 2;
>>>>> +         buf = (char *) xrealloc (buf, buf_size);
>>>>> +       }
>>>>> +      strcat (buf, "-->");
>>>>> +      strcat (buf, name);
>>>>> +    }
>>>>> +  locus = gimple_location (edge->call_stmt);
>>>>> +  inform (locus, "%s ("HOST_WIDEST_INT_PRINT_DEC") --"
>>>>> +         HOST_WIDEST_INT_PRINT_DEC"--> %s ("
>>>>> +         HOST_WIDEST_INT_PRINT_DEC") %s : %s",
>>>>> +         cgraph_node_name (edge->callee), callee_count, edge->count,
>>>>> +         cgraph_node_name (edge->caller), edge->caller->count, buf,
>>>>> +         edge->inline_failed == CIF_OK ? "INLINED": "IGNORED");
>>>>> +}
>>>>> +
>>>>> +
>>>>>  /* We use greedy algorithm for inlining of small functions:
>>>>>    All inline candidates are put into prioritized heap ordered in
>>>>>    increasing badness.
>>>>> @@ -1428,6 +1467,7 @@
>>>>>   overall_size = initial_size;
>>>>>   max_size = compute_max_insns (overall_size);
>>>>>   min_size = overall_size;
>>>>> +  edge = NULL;
>>>>>
>>>>>   /* Populate the heeap with all edges we might inline.  */
>>>>>
>>>>> @@ -1462,6 +1502,9 @@
>>>>>       int current_badness;
>>>>>       int growth;
>>>>>
>>>>> +      if (edge && flag_opt_info >= OPT_INFO_MIN)
>>>>> +       dump_inline_decision (edge);
>>>>> +
>>>>>       edge = (struct cgraph_edge *) fibheap_extract_min (heap);
>>>>>       gcc_assert (edge->aux);
>>>>>       edge->aux = NULL;
>>>>> @@ -1482,6 +1525,7 @@
>>>>>       if (current_badness != badness)
>>>>>        {
>>>>>          edge->aux = fibheap_insert (heap, current_badness, edge);
>>>>> +         edge = NULL;
>>>>>          continue;
>>>>>        }
>>>>>
>>>>> @@ -1636,6 +1680,8 @@
>>>>>            fprintf (dump_file, "New minimal size reached: %i\n", min_size);
>>>>>        }
>>>>>     }
>>>>> +  if (edge && flag_opt_info >= OPT_INFO_MIN)
>>>>> +    dump_inline_decision (edge);
>>>>>
>>>>>   free_growth_caches ();
>>>>>   if (new_indirect_edges)

Patch

Index: testsuite/gcc.dg/inline-dump.c
===================================================================
--- testsuite/gcc.dg/inline-dump.c	(revision 0)
+++ testsuite/gcc.dg/inline-dump.c	(revision 0)
@@ -0,0 +1,6 @@ 
+/* Verify that -fopt-info can output correct inline info.  */
+/* { dg-do compile } */
+/* { dg-options "-Wall -fopt-info -O2 -fno-early-inlining" } */
+static inline int foo(void) { return 1; }
+int bar1(void) { return foo(); } /* { dg-message "note: foo inlined into bar1 with call count 0" } */
+int bar2(void) { return foo(); } /* { dg-message "note: foo inlined into bar2 with call count 0" } */
Index: ipa-inline.c
===================================================================
--- ipa-inline.c	(revision 181835)
+++ ipa-inline.c	(working copy)
@@ -292,6 +292,96 @@ 
       cgraph_clone_inlined_nodes (e, duplicate, update_original);
 }
 
+#define MAX_INT_LENGTH 16
+
+/* Return NODE's name and aux info. The output is controled by OPT_INFO
+   level.  */
+
+static const char *
+cgraph_node_opt_info (struct cgraph_node *node)
+{
+  char *buf;
+  size_t buf_size;
+  const char *bfd_name = lang_hooks.dwarf_name (node->decl, 0);
+  const char *count_text = "count=";
+  const char *max_count_text = "max_count=";
+
+  if (!bfd_name)
+    bfd_name = "unknown";
+
+  buf_size = strlen (bfd_name) + 1;
+  if (flag_opt_info >= OPT_INFO_MED)
+    buf_size += (strlen (count_text) + MAX_INT_LENGTH + 1);
+  if (flag_opt_info >= OPT_INFO_MAX)
+    buf_size += (strlen (max_count_text) + MAX_INT_LENGTH + 1);
+  buf = (char *) xmalloc (buf_size);
+
+  strcpy (buf, bfd_name);
+  if (flag_opt_info >= OPT_INFO_MED)
+    sprintf (buf, "%s,%s"HOST_WIDEST_INT_PRINT_DEC,
+	     buf, count_text, node->count);
+  if (flag_opt_info >= OPT_INFO_MAX)
+    sprintf (buf, "%s,%s"HOST_WIDEST_INT_PRINT_DEC, 
+	     buf, max_count_text, node->max_bb_count);
+  return buf;
+}
+
+/* Return CALLER's inlined call chain. Save the cgraph_node of the ultimate
+   function that the caller is inlined to in FINAL_CALLER.  */
+
+static const char *
+cgraph_node_call_chain (struct cgraph_node *caller,
+		        struct cgraph_node **final_caller)
+{
+  struct cgraph_node *node;
+  const char *via_str = " (via inline instance";
+  size_t current_string_len = strlen (via_str) + 1;
+  size_t buf_size = current_string_len;
+  char *buf = (char *) xmalloc (buf_size);
+
+  buf[0] = 0;
+  gcc_assert (caller->clone_of != NULL);
+  strcat (buf, via_str);
+  for (node = caller; node->clone_of != NULL; node = node->callers->caller)
+    {
+      const char *name = cgraph_node_opt_info (node);
+      current_string_len += (strlen (name) + 1);
+      if (current_string_len >= buf_size)
+	{
+	  buf_size = current_string_len * 2;
+	  buf = (char *) xrealloc (buf, buf_size);
+	}
+      strcat (buf, " ");
+      strcat (buf, name);
+    }
+  strcat (buf, ")");
+  *final_caller = node;
+  return buf;
+}
+
+/* Dump the inline decision of EDGE to stderr.  */
+
+static void
+dump_inline_decision (struct cgraph_edge *edge)
+{
+  location_t locus;
+  const char *inline_chain_text;
+  struct cgraph_node *final_caller = edge->caller;
+
+  if (final_caller->clone_of != NULL)
+    inline_chain_text = cgraph_node_call_chain (final_caller, &final_caller);
+  else
+    inline_chain_text = "";
+      
+  locus = gimple_location (edge->call_stmt);
+  inform (locus,
+	  "%s inlined into %s with call count "HOST_WIDEST_INT_PRINT_DEC"%s",
+	  cgraph_node_opt_info (edge->callee),
+	  cgraph_node_opt_info (final_caller),
+	  edge->count,
+	  inline_chain_text);
+}
+
 /* Mark edge E as inlined and update callgraph accordingly.  UPDATE_ORIGINAL
    specify whether profile of original function should be updated.  If any new
    indirect edges are discovered in the process, add them to NEW_EDGES, unless
@@ -311,6 +401,9 @@ 
   if (L_IPO_COMP_MODE && !e->call_stmt)
     return false;
 
+  if (flag_opt_info >= OPT_INFO_MIN)
+    dump_inline_decision (e);
+
   /* Don't inline inlined edges.  */
   gcc_assert (e->inline_failed);
   /* Don't even think of inlining inline clone.  */