Both -Os and -Oz enable size optimization, causing
cgraph_edge::maybe_hot_p to reject calls in size-optimized functions.
Consequently, the inliner cannot use reliable profile feedback to accept
a profitable call with small positive growth under -Os.

Do not change the generic callgraph hotness logic, since it is also
used by IPA transformations unrelated to inlining. Add an
inliner-specific predicate for OPTIMIZE_SIZE_BALANCED (-Os) instead.

Under -Os, permit small positive growth only when the call has reliable profile
feedback, is considered hot, and big_speedup_p predicts a significant
benefit. Reject probably-never-executed calls and simple tail-position
calls, and bound growth using the existing automatic inlining limit.

Calls without profile feedback retain the existing -Os behavior. -Oz
and non-size optimization modes remain unchanged, and no new inliner
parameters are introduced.

gcc/ChangeLog:

        * ipa-inline.cc: Include gimple-iterator.h and predict.h.
        (big_speedup_p): Add forward declaration.
        (probably_never_executed_call_p): New function.
        (tail_position_call_p): Likewise.
        (profitable_profiled_call_p): Likewise.
        (inliner_maybe_hot_p): Likewise.
        (allow_size_growth_p): Likewise.
        (want_early_inline_function_p): Use allow_size_growth_p when
        deciding whether positive growth is permitted.
        (want_inline_small_function_p): Use the inliner-specific hotness
        and size-growth predicates.

gcc/testsuite/ChangeLog:

        * gcc.dg/ipa/inline-os-no-profile-1.c: New test.
        * gcc.dg/tree-prof/inline-os-pgo-1.c: New test.
        * gcc.dg/tree-prof/inline-os-pgo-cold-1.c: New test.
        * gcc.dg/tree-prof/inline-os-pgo-tail-1.c: New test.
        * gcc.dg/tree-prof/inline-oz-pgo-1.c: New test.

---
 gcc/ipa-inline.cc                             | 134 +++++++++++++++++-
 .../gcc.dg/ipa/inline-os-no-profile-1.c       |  24 ++++
 .../gcc.dg/tree-prof/inline-os-pgo-1.c        |  33 +++++
 .../gcc.dg/tree-prof/inline-os-pgo-cold-1.c   |  34 +++++
 .../gcc.dg/tree-prof/inline-os-pgo-tail-1.c   |  32 +++++
 .../gcc.dg/tree-prof/inline-oz-pgo-1.c        |  33 +++++
 6 files changed, 287 insertions(+), 3 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/ipa/inline-os-no-profile-1.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-prof/inline-os-pgo-1.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-prof/inline-os-pgo-cold-1.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-prof/inline-os-pgo-tail-1.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-prof/inline-oz-pgo-1.c

diff --git a/gcc/ipa-inline.cc b/gcc/ipa-inline.cc
index 5a7615666cc..5451831797e 100644
--- a/gcc/ipa-inline.cc
+++ b/gcc/ipa-inline.cc
@@ -97,6 +97,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "rtl.h"
 #include "tree.h"
 #include "gimple.h"
+#include "gimple-iterator.h"
 #include "alloc-pool.h"
 #include "tree-pass.h"
 #include "gimple-ssa.h"
@@ -106,6 +107,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "calls.h"
 #include "tree-inline.h"
 #include "profile.h"
+#include "predict.h"
 #include "symbol-summary.h"
 #include "tree-vrp.h"
 #include "sreal.h"
@@ -777,6 +779,132 @@ num_calls (struct cgraph_node *n)
   return num;
 }
 
+static bool big_speedup_p (struct cgraph_edge*);
+
+/* Return true if E has precise evidence that its call is effectively never
+   executed. Keep this check local to the inliner: changing the cgraph
+   hotness predicate would also affect unrelated IPA transformations.  */
+
+static bool
+probably_never_executed_call_p (struct cgraph_edge *e)
+{
+  if (e->count.ipa () == profile_count::zero ())
+    return true;
+
+  if (!e->call_stmt)
+    return false;
+
+  basic_block bb = gimple_bb (e->call_stmt);
+  if (!bb)
+    return false;
+
+  cgraph_node *where = e->caller->inlined_to ? e->caller->inlined_to : 
e->caller;
+  struct function *fun = DECL_STRUCT_FUNCTION (where->decl);
+  if (!fun || !fun->cfg)
+    return false;
+
+  return probably_never_executed_bb_p (fun, bb);
+}
+
+/* Return true if E's call is in a simple tail position. The tail-call pass may
+   not have marked the call yet when IPA inlining runs, so also recognize the
+   common GIMPLE shape where the call is immediately followed by a return of
+   the call result.  */
+
+static bool
+tail_position_call_p (struct cgraph_edge *e)
+{
+  gcall *call = e->call_stmt;
+
+  if (!call)
+    return false;
+
+  if (gimple_call_tail_p (call) || gimple_call_must_tail_p (call))
+    return true;
+
+  if (!gimple_bb (call))
+    return false;
+
+  gimple_stmt_iterator gsi = gsi_for_stmt (call);
+  gsi_next_nondebug (&gsi);
+
+  if (gsi_end_p (gsi))
+    return false;
+
+  greturn *ret = dyn_cast <greturn *> (gsi_stmt (gsi));
+  if (!ret)
+    return false;
+
+  tree lhs = gimple_call_lhs (call);
+  tree retval = gimple_return_retval (ret);
+
+  if (!retval)
+    return lhs == NULL_TREE;
+
+  return lhs && retval == lhs;
+}
+
+/*  Return true if profile feedback shows that inlining E is profitable under
+    -Os. Unlike -O2, do not fall back to static frequency estimates. Also
+    require a significant estimated speedup, so profile hotness alone does not
+    turn size optimization into normal -O2-style inlining.  */
+
+static bool
+profitable_profiled_call_p (struct cgraph_edge *e, sreal scale)
+{
+  bool hot = false;
+
+  if (probably_never_executed_call_p (e))
+    return false;
+
+  profile_count c = e->count.ipa ();
+  if (c.reliable_p ()
+      || (c.quality () == AFDO && c.nonzero_p ()))
+    hot = maybe_hot_count_p (NULL, c * scale);
+
+  else if ((c.quality () == AFDO
+           || e->count.quality () == GUESSED_GLOBAL0_ADJUSTED)
+          && e->callee && e->callee->count.quality () == AFDO)
+    hot = maybe_hot_count_p (NULL, c.force_nonzero () * scale);
+
+  return hot && big_speedup_p (e);
+}
+
+/*  Return true if the inliner should treat E as hot. Keep the global cgraph
+    hotness predicate conservative for -Os, but allow the inliner to use
+    profile information for balanced size optimization.  */
+
+static bool
+inliner_maybe_hot_p (struct cgraph_edge *e, sreal scale)
+{
+  if (opt_for_fn (e->caller->decl, optimize_size) == OPTIMIZE_SIZE_BALANCED)
+    return profitable_profiled_call_p (e, scale);
+
+  return e->maybe_hot_p (scale);
+}
+
+/*  Return true if positive growth is allowed for E. Under -Os, only permit a
+    small, profile-proven profitable expansion. Do not spend that allowance on
+    a tail call, since the generic IPA cost model cannot account for the cheap
+    target-specific sibling-call sequence that inlining may replace.  */
+
+static bool
+allow_size_growth_p (struct cgraph_edge *e, sreal scale, int growth)
+{
+  if (opt_for_fn (e->caller->decl, optimize_size) != OPTIMIZE_SIZE_BALANCED)
+    return e->maybe_hot_p (scale);
+
+  if (tail_position_call_p (e))
+    return false;
+
+  /*  Reuse the existing automatic inlining limit rather than adding another
+      size parameter.  */
+  if (growth > inline_insns_auto (e->caller, false, false))
+    return false;
+
+  return profitable_profiled_call_p (e, scale);
+}
+
 
 /* Return true if we are interested in inlining small function.  */
 
@@ -819,7 +947,7 @@ want_early_inline_function_p (struct cgraph_edge *e)
 
       if (!want_inline || growth <= param_max_inline_insns_size)
        ;
-      else if (!e->maybe_hot_p ())
+      else if (!allow_size_growth_p (e, 1, growth))
        {
          if (dump_enabled_p ())
            dump_printf_loc (MSG_MISSED_OPTIMIZATION, e->call_stmt,
@@ -1001,7 +1129,7 @@ want_inline_small_function_p (struct cgraph_edge *e, bool 
report)
      inline candidate.  */
   if ((!DECL_DECLARED_INLINE_P (callee->decl)
       && (!e->count.ipa ().initialized_p ()
-         || !e->maybe_hot_p (callee_info->time)))
+         || !inliner_maybe_hot_p (e, callee_info->time)))
       && callee_info->min_size - call_info->call_stmt_size
         > inline_insns_auto (e->caller, true, true))
     {
@@ -1081,7 +1209,7 @@ want_inline_small_function_p (struct cgraph_edge *e, bool 
report)
            }
        }
       /* If call is cold, do not inline when function body would grow. */
-      else if (!e->maybe_hot_p (callee_speedup (e))
+      else if (!allow_size_growth_p (e, callee_speedup (e), growth)
               && (growth >= inline_insns_single (e->caller, false, false)
                   || growth_positive_p (callee, e, growth)))
        {
diff --git a/gcc/testsuite/gcc.dg/ipa/inline-os-no-profile-1.c 
b/gcc/testsuite/gcc.dg/ipa/inline-os-no-profile-1.c
new file mode 100644
index 00000000000..56a2b43adf2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/inline-os-no-profile-1.c
@@ -0,0 +1,24 @@
+/* { dg-do compile } */
+/* { dg-options "-Os -fno-early-inlining -fno-partial-inlining -fno-ipa-cp 
-fdump-ipa-inline-details" } */
+
+volatile unsigned int value;
+
+unsigned int
+callee (unsigned int x)
+{
+       x ^= x << 3;
+       x += x >> 2;
+       value += x;
+       value ^= x + 1;
+       return value;
+}
+
+__attribute__ ((noinline, noclone))
+unsigned int
+caller (unsigned int x)
+{
+       unsigned int y = callee (x);
+       return y + x;
+}
+
+/* { dg-final { scan-ipa-dump-not "Inlined callee/.* into caller/" "inline" } 
} */
diff --git a/gcc/testsuite/gcc.dg/tree-prof/inline-os-pgo-1.c 
b/gcc/testsuite/gcc.dg/tree-prof/inline-os-pgo-1.c
new file mode 100644
index 00000000000..dd4985d4b25
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-prof/inline-os-pgo-1.c
@@ -0,0 +1,33 @@
+/* { dg-options "-Os -fno-early-inlining -fno-partial-inlining -fno-ipa-cp 
-fdump-ipa-inline-details" } */
+
+volatile unsigned int value;
+
+unsigned int
+callee (unsigned int x)
+{
+       x ^= x << 3;
+       x += x >> 2;
+       value += x;
+       value ^= x + 1;
+       return value;
+}
+
+__attribute__ ((noinline, noclone))
+unsigned int
+caller (unsigned int x)
+{
+       unsigned int y = callee (x);
+       return y + x;
+}
+
+int
+main (void)
+{
+        unsigned int i;
+
+        for (i = 0; i < 100000; ++i)
+                value = caller (i & 7);
+        return 0;
+}
+
+/* { dg-final-use-not-autofdo { scan-ipa-dump "Inlined callee/.* into caller/" 
"inline" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-prof/inline-os-pgo-cold-1.c 
b/gcc/testsuite/gcc.dg/tree-prof/inline-os-pgo-cold-1.c
new file mode 100644
index 00000000000..ec6a3481c53
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-prof/inline-os-pgo-cold-1.c
@@ -0,0 +1,34 @@
+/* { dg-options "-Os -fno-early-inlining -fno-partial-inlining -fno-ipa-cp 
-fdump-ipa-inline-details" } */
+
+volatile unsigned int value;
+
+unsigned int
+callee (unsigned int x)
+{
+       x ^= x << 3;
+       x += x >> 2;
+       value += x;
+       value ^= x + 1;
+       return value;
+}
+
+__attribute__ ((noinline, noclone))
+unsigned int
+caller (unsigned int x)
+{
+        if (x != 0)
+          return callee (x) + x;
+        return 0;
+}
+
+int
+main (void)
+{
+        unsigned int i;
+
+        for (i = 0; i < 100000; ++i)
+                value = caller (0);
+        return 0;
+}
+
+/* { dg-final-use-not-autofdo { scan-ipa-dump-not "Inlined callee/.* into 
caller/" "inline" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-prof/inline-os-pgo-tail-1.c 
b/gcc/testsuite/gcc.dg/tree-prof/inline-os-pgo-tail-1.c
new file mode 100644
index 00000000000..79887a5104c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-prof/inline-os-pgo-tail-1.c
@@ -0,0 +1,32 @@
+/* { dg-options "-Os -fno-early-inlining -fno-partial-inlining -fno-ipa-cp 
-fdump-ipa-inline-details" } */
+
+volatile unsigned int value;
+
+unsigned int
+callee (unsigned int x)
+{
+       x ^= x << 3;
+       x += x >> 2;
+       value += x;
+       value ^= x + 1;
+       return value;
+}
+
+__attribute__ ((noinline, noclone))
+unsigned int
+caller (unsigned int x)
+{
+       return callee (x);
+}
+
+int
+main (void)
+{
+        unsigned int i;
+
+        for (i = 0; i < 100000; ++i)
+                value = caller (i & 7);
+        return 0;
+}
+
+/* { dg-final-use-not-autofdo { scan-ipa-dump-not "Inlined callee/.* into 
caller/" "inline" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-prof/inline-oz-pgo-1.c 
b/gcc/testsuite/gcc.dg/tree-prof/inline-oz-pgo-1.c
new file mode 100644
index 00000000000..cd8de7e6f92
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-prof/inline-oz-pgo-1.c
@@ -0,0 +1,33 @@
+/* { dg-options "-Oz -fno-early-inlining -fno-partial-inlining -fno-ipa-cp 
-fdump-ipa-inline-details" } */
+
+volatile unsigned int value;
+
+unsigned int
+callee (unsigned int x)
+{
+       x ^= x << 3;
+       x += x >> 2;
+       value += x;
+       value ^= x + 1;
+       return value;
+}
+
+__attribute__ ((noinline, noclone))
+unsigned int
+caller (unsigned int x)
+{
+       unsigned int y = callee (x);
+       return y + x;
+}
+
+int
+main (void)
+{
+        unsigned int i;
+
+        for (i = 0; i < 100000; ++i)
+                value = caller (i & 7);
+        return 0;
+}
+
+/* { dg-final-use-not-autofdo { scan-ipa-dump-not "Inlined callee/.* into 
caller/" "inline" } } */
-- 
2.43.0

Reply via email to