From 0691d1d4a31ff0a426d2ba07c64acbac5df6b8ae Mon Sep 17 00:00:00 2001
From: Richard Guenther <rguenther@suse.de>
Date: Fri, 15 Jul 2005 09:31:39 +0000
Subject: [PATCH] c-common.c (handle_flatten_attribute): New function.

2005-07-15  Richard Guenther  <rguenther@suse.de>

	* c-common.c (handle_flatten_attribute): New function.
	Add flatten function attribute.
	* doc/extend.texi: Document flatten function attribute.
	* Makefile.in (ipa-inline.o): Depend on hashtab.h.
	* ipa-inline.c (cgraph_find_cycles, cgraph_flatten_node):
	New functions.
	(cgraph_decide_inlining): Handle functions with flatten
	attribute.

	* gcc.dg/tree-ssa/flatten-1.c: New testcase.
	* gcc.dg/tree-ssa/flatten-2.c: Likewise.

From-SVN: r102051
---
 gcc/ChangeLog                             | 11 ++++
 gcc/Makefile.in                           |  2 +-
 gcc/c-common.c                            | 25 ++++++++
 gcc/doc/extend.texi                       | 10 ++-
 gcc/ipa-inline.c                          | 78 +++++++++++++++++++++++
 gcc/testsuite/ChangeLog                   |  5 ++
 gcc/testsuite/gcc.dg/tree-ssa/flatten-1.c | 57 +++++++++++++++++
 gcc/testsuite/gcc.dg/tree-ssa/flatten-2.c | 76 ++++++++++++++++++++++
 8 files changed, 262 insertions(+), 2 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/flatten-1.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/flatten-2.c

diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 68dec8a96c41..317e2d75f11a 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,14 @@
+2005-07-15  Richard Guenther  <rguenther@suse.de>
+
+	* c-common.c (handle_flatten_attribute): New function.
+	Add flatten function attribute.
+	* doc/extend.texi: Document flatten function attribute.
+	* Makefile.in (ipa-inline.o): Depend on hashtab.h.
+	* ipa-inline.c (cgraph_find_cycles, cgraph_flatten_node):
+	New functions.
+	(cgraph_decide_inlining): Handle functions with flatten
+	attribute.
+
 2005-07-14  David Edelsohn  <edelsohn@gnu.org>
 
 	* config/rs6000/rs6000.md (UNSPEC_SYNC, UNSPEC_LWSYNC,
diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 2bc109238992..3d8b8bc7c253 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -2141,7 +2141,7 @@ ipa.o : ipa.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(CGRAPH_H)
 ipa-inline.o : ipa-inline.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
    $(TREE_H) langhooks.h tree-inline.h $(FLAGS_H) $(CGRAPH_H) intl.h \
    $(DIAGNOSTIC_H) $(FIBHEAP_H) $(PARAMS_H) $(TIMEVAR_H) tree-pass.h \
-   $(COVERAGE_H)
+   $(COVERAGE_H) $(HASHTAB_H)
 coverage.o : coverage.c $(GCOV_IO_H) $(CONFIG_H) $(SYSTEM_H) coretypes.h \
    $(TM_H) $(RTL_H) $(TREE_H) $(FLAGS_H) output.h $(REGS_H) $(EXPR_H) \
    function.h toplev.h $(GGC_H) langhooks.h $(COVERAGE_H) gt-coverage.h \
diff --git a/gcc/c-common.c b/gcc/c-common.c
index f3d4a281bdad..f7463e1f5531 100644
--- a/gcc/c-common.c
+++ b/gcc/c-common.c
@@ -505,6 +505,7 @@ static tree handle_noreturn_attribute (tree *, tree, tree, int, bool *);
 static tree handle_noinline_attribute (tree *, tree, tree, int, bool *);
 static tree handle_always_inline_attribute (tree *, tree, tree, int,
 					    bool *);
+static tree handle_flatten_attribute (tree *, tree, tree, int, bool *);
 static tree handle_used_attribute (tree *, tree, tree, int, bool *);
 static tree handle_unused_attribute (tree *, tree, tree, int, bool *);
 static tree handle_externally_visible_attribute (tree *, tree, tree, int,
@@ -571,6 +572,8 @@ const struct attribute_spec c_common_attribute_table[] =
 			      handle_noinline_attribute },
   { "always_inline",          0, 0, true,  false, false,
 			      handle_always_inline_attribute },
+  { "flatten",                0, 0, true,  false, false,
+                              handle_flatten_attribute },
   { "used",                   0, 0, true,  false, false,
 			      handle_used_attribute },
   { "unused",                 0, 0, false, false, false,
@@ -4151,6 +4154,28 @@ handle_always_inline_attribute (tree *node, tree name,
   return NULL_TREE;
 }
 
+/* Handle a "flatten" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+handle_flatten_attribute (tree *node, tree name,
+                          tree args ATTRIBUTE_UNUSED,
+                          int flags ATTRIBUTE_UNUSED, bool *no_add_attrs)
+{
+  if (TREE_CODE (*node) == FUNCTION_DECL)
+    /* Do nothing else, just set the attribute.  We'll get at
+       it later with lookup_attribute.  */
+    ;
+  else
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored", name);
+      *no_add_attrs = true;
+    }
+
+  return NULL_TREE;
+}
+
+
 /* Handle a "used" attribute; arguments as in
    struct attribute_spec.handler.  */
 
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index cc6ea2de0535..f71c5200a0ae 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -1523,7 +1523,7 @@ attributes when making a declaration.  This keyword is followed by an
 attribute specification inside double parentheses.  The following
 attributes are currently defined for functions on all targets:
 @code{noreturn}, @code{returns_twice}, @code{noinline}, @code{always_inline},
-@code{pure}, @code{const}, @code{nothrow}, @code{sentinel},
+@code{flatten}, @code{pure}, @code{const}, @code{nothrow}, @code{sentinel},
 @code{format}, @code{format_arg}, @code{no_instrument_function},
 @code{section}, @code{constructor}, @code{destructor}, @code{used},
 @code{unused}, @code{deprecated}, @code{weak}, @code{malloc},
@@ -1566,6 +1566,14 @@ Generally, functions are not inlined unless optimization is specified.
 For functions declared inline, this attribute inlines the function even
 if no optimization level was specified.
 
+@cindex @code{flatten} function attribute
+@item flatten
+Generally, inlining into a function is limited.  For a function marked with
+this attribute, every call inside this function will be inlined, if possible.
+Whether the function itself is considered for inlining depends on its size and
+the current inlining parameters.  The @code{flatten} attribute only works
+reliably in unit-at-a-time mode.
+
 @item cdecl
 @cindex functions that do pop the argument stack on the 386
 @opindex mrtd
diff --git a/gcc/ipa-inline.c b/gcc/ipa-inline.c
index cb71047d0aa7..df57ccce0530 100644
--- a/gcc/ipa-inline.c
+++ b/gcc/ipa-inline.c
@@ -78,6 +78,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
 #include "fibheap.h"
 #include "intl.h"
 #include "tree-pass.h"
+#include "hashtab.h"
 #include "coverage.h"
 #include "ggc.h"
 
@@ -438,6 +439,65 @@ lookup_recursive_calls (struct cgraph_node *node, struct cgraph_node *where,
       lookup_recursive_calls (node, e->callee, heap);
 }
 
+/* Find callgraph nodes closing a circle in the graph.  The
+   resulting hashtab can be used to avoid walking the circles.
+   Uses the cgraph nodes ->aux field which needs to be zero
+   before and will be zero after operation.  */
+
+static void
+cgraph_find_cycles (struct cgraph_node *node, htab_t cycles)
+{
+  struct cgraph_edge *e;
+
+  if (node->aux)
+    {
+      void **slot;
+      slot = htab_find_slot (cycles, node, INSERT);
+      if (!*slot)
+	{
+	  if (dump_file)
+	    fprintf (dump_file, "Cycle contains %s\n", cgraph_node_name (node));
+	  *slot = node;
+	}
+      return;
+    }
+
+  node->aux = node;
+  for (e = node->callees; e; e = e->next_callee)
+    cgraph_find_cycles (e->callee, cycles); 
+  node->aux = 0;
+}
+
+/* Leafify the cgraph node.  We have to be careful in recursing
+   as to not run endlessly in circles of the callgraph.
+   We do so by using a hashtab of cycle entering nodes as generated
+   by cgraph_find_cycles.  */
+
+static void
+cgraph_flatten_node (struct cgraph_node *node, htab_t cycles)
+{
+  struct cgraph_edge *e;
+
+  for (e = node->callees; e; e = e->next_callee)
+    {
+      /* Inline call, if possible, and recurse.  Be sure we are not
+	 entering callgraph circles here.  */
+      if (e->inline_failed
+	  && e->callee->local.inlinable
+	  && !cgraph_recursive_inlining_p (node, e->callee,
+				  	   &e->inline_failed)
+	  && !htab_find (cycles, e->callee))
+	{
+	  if (dump_file)
+    	    fprintf (dump_file, " inlining %s", cgraph_node_name (e->callee));
+          cgraph_mark_inline_edge (e);
+	  cgraph_flatten_node (e->callee, cycles);
+	}
+      else if (dump_file)
+	fprintf (dump_file, " !inlining %s", cgraph_node_name (e->callee));
+    }
+}
+
 /* Decide on recursive inlining: in the case function has recursive calls,
    inline until body size reaches given argument.  */
 
@@ -769,6 +829,24 @@ cgraph_decide_inlining (void)
 
       node = order[i];
 
+      /* Handle nodes to be flattened, but don't update overall unit size.  */
+      if (lookup_attribute ("flatten", DECL_ATTRIBUTES (node->decl)) != NULL)
+        {
+	  int old_overall_insns = overall_insns;
+	  htab_t cycles;
+  	  if (dump_file)
+    	    fprintf (dump_file,
+	     	     "Leafifying %s\n", cgraph_node_name (node));
+	  cycles = htab_create (7, htab_hash_pointer, htab_eq_pointer, NULL);
+	  cgraph_find_cycles (node, cycles);
+	  cgraph_flatten_node (node, cycles);
+	  htab_delete (cycles);
+	  overall_insns = old_overall_insns;
+	  /* We don't need to consider always_inline functions inside the flattened
+	     function anymore.  */
+	  continue;
+        }
+
       if (!node->local.disregard_inline_limits)
 	continue;
       if (dump_file)
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 2e5eee9ccf40..6bef685f53d5 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,8 @@
+2005-07-15  Richard Guenther  <rguenther@suse.de>
+
+	* gcc.dg/tree-ssa/flatten-1.c: New testcase.
+	* gcc.dg/tree-ssa/flatten-2.c: Likewise.
+
 2005-07-15  Steven Bosscher  <stevenb@suse.de>
 
 	PR tree-optimization/22230
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/flatten-1.c b/gcc/testsuite/gcc.dg/tree-ssa/flatten-1.c
new file mode 100644
index 000000000000..4561f757b8c6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/flatten-1.c
@@ -0,0 +1,57 @@
+/* { dg-do compile } */
+/* { dg-options -O2 } */
+
+/* Basic tests for flatten attribute, check we end up
+   with only the flattened function bodies.  */
+
+static int foobar(int i);
+static int bar(int i);
+
+int __attribute__((flatten)) leaf0a(int i)
+{
+  return bar(i);
+}
+int __attribute__((flatten)) leaf0b(int i)
+{
+  return foobar(i);
+}
+int __attribute__((flatten)) leaf1(int i)
+{
+  return bar(foobar(i));
+}
+int __attribute__((flatten)) leaf2(int i)
+{
+  int j;
+  j = foobar(i);
+  return bar(j);
+}
+
+static int foobar(int i)
+{
+  return i-1;
+}
+static int bar(int i)
+{
+  return i + foobar(i);
+}
+
+
+static int g(int i)
+{
+  return i*5+1;
+}
+static int f(int i)
+{
+  return g(i);
+}
+int __attribute__((flatten)) leaf3(int i)
+{
+  int j;
+  j = f(i);
+  j += f(i);
+  return j;
+}
+
+/* { dg-final { scan-assembler-not "g:" } } */
+/* { dg-final { scan-assembler-not "f:" } } */
+/* { dg-final { scan-assembler-not "bar:" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/flatten-2.c b/gcc/testsuite/gcc.dg/tree-ssa/flatten-2.c
new file mode 100644
index 000000000000..56e8083febd4
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/flatten-2.c
@@ -0,0 +1,76 @@
+/* { dg-do compile } */
+/* { dg-options -O2 } */
+
+/* Check that we finish compiling even if instructed to
+   flatten a cyclic callgraph.  Verify we correctly
+   flatten with another function marked flatten in the
+   callgraph.  */
+
+void __attribute__((flatten)) direct(void)
+{
+  direct();
+}
+
+
+void __attribute__((flatten)) indirect(void);
+static void indirect1(void)
+{
+  indirect();
+}
+void __attribute__((flatten)) indirect(void)
+{
+  indirect1();
+}
+
+
+void __attribute__((flatten)) doubleindirect(void);
+static void doubleindirect2(void)
+{
+  doubleindirect();
+}
+static void doubleindirect1(void)
+{
+  doubleindirect2();
+}
+void __attribute__((flatten)) doubleindirect(void)
+{
+  doubleindirect1();
+}
+
+
+static void subcycle1(void);
+static void subcycle2(void)
+{
+  subcycle1();
+}
+static void subcycle1(void)
+{
+  subcycle2();
+}
+void __attribute__((flatten)) subcycle(void)
+{
+  subcycle1();
+}
+
+
+static void doublesubcycle1(void);
+static void doublesubcycle2(void);
+static void doublesubcycle3(void)
+{
+  doublesubcycle1();
+}
+static void doublesubcycle2(void)
+{
+  doublesubcycle3();
+}
+static void doublesubcycle1(void)
+{
+  doublesubcycle2();
+}
+void __attribute__((flatten)) doublesubcycle(void)
+{
+  doublesubcycle1();
+}
+
+/* { dg-final { scan-assembler "cycle.:" } } */
+/* { dg-final { scan-assembler-not "indirect.:" } } */
-- 
GitLab