inline-3.c: New test.
authorJan Hubicka <jh@suse.cz>
Sat, 8 Mar 2003 13:26:37 +0000 (14:26 +0100)
committerJan Hubicka <hubicka@gcc.gnu.org>
Sat, 8 Mar 2003 13:26:37 +0000 (13:26 +0000)
* gcc.dg/inline-3.c: New test.

* c-decl.c: (finish_function): Update call of tree_inlinable_function_p.
* cgraph.h: (cgraph_local_info): Add can_inline_once
(cgraph_global_info): Add inline_once.
(cgraph_node): Add previous.
(cgraph_remove_node): New.
* cgraphunit.c (cgraph_mark_functions_to_inline_once): New static
function.
(cgraph_optimize): Call it.
(cgraph_finalize_function):  Set inlinable flags.
(cgraph_finalize_compilation_unit):  Actually remove the reclaimed nodes.
(cgraph_mark_functions_to_output):  Use new inlining heuristics flags.
(cgraph_expand_function): Likewise.
* cgraph.c
(cgraph_node): Put nodes into doubly linked chain.
(cgraph_remove_node): New function.
* flags.h (flag_inline_functions_called_once): Declare.
* tree-inline.c: Include cgraph.h
(inlinable_functions_p): Add extra argument to bypass limits.
(expand_call_inline):  Obey cgraph flag.
* tree-inline.h (tree_inlinable_function_p): Update prototype.

From-SVN: r63983

gcc/ChangeLog
gcc/c-decl.c
gcc/cgraph.c
gcc/cgraph.h
gcc/cgraphunit.c
gcc/config/i386/i386.md
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/inline-3.c [new file with mode: 0644]
gcc/tree-inline.c
gcc/tree-inline.h

index e3919a9ef8485ff8e77c1cc9a0895c2383fdd495..383f9e62bd4ba3625491c0520ca8c4244e361efc 100644 (file)
@@ -1,3 +1,26 @@
+Sat Mar  8 14:13:35 CET 2003  Jan Hubicka  <jh@suse.cz>
+
+       * c-decl.c: (finish_function): Update call of tree_inlinable_function_p.
+       * cgraph.h: (cgraph_local_info): Add can_inline_once
+       (cgraph_global_info): Add inline_once.
+       (cgraph_node): Add previous.
+       (cgraph_remove_node): New.
+       * cgraphunit.c (cgraph_mark_functions_to_inline_once): New static
+       function.
+       (cgraph_optimize): Call it.
+       (cgraph_finalize_function):  Set inlinable flags.
+       (cgraph_finalize_compilation_unit):  Actually remove the reclaimed nodes.
+       (cgraph_mark_functions_to_output):  Use new inlining heuristics flags.
+       (cgraph_expand_function): Likewise.
+       * cgraph.c 
+       (cgraph_node): Put nodes into doubly linked chain.
+       (cgraph_remove_node): New function.
+       * flags.h (flag_inline_functions_called_once): Declare.
+       * tree-inline.c: Include cgraph.h
+       (inlinable_functions_p): Add extra argument to bypass limits.
+       (expand_call_inline):  Obey cgraph flag.
+       * tree-inline.h (tree_inlinable_function_p): Update prototype.
+
 2003-03-08  Zdenek Dvorak  <rakdver@atrey.karlin.mff.cuni.cz>
 
        * gcse.c (bypass_block, bypass_conditional_jumps): Do not create
index 4779406277f13974abd9f5216b20ee05df00a958..f7996f1a8126bff746ba3c16d5a82b2041abbf81 100644 (file)
@@ -6444,7 +6444,7 @@ finish_function (nested, can_defer_p)
             predicates depend on cfun and current_function_decl to
             function completely.  */
          timevar_push (TV_INTEGRATION);
-         uninlinable = ! tree_inlinable_function_p (fndecl);
+         uninlinable = ! tree_inlinable_function_p (fndecl, 0);
          
          if (! uninlinable && can_defer_p
              /* Save function tree for inlining.  Should return 0 if the
index 1a078f6750e8f10e97bc184e0f116d2f8e43fffd..bb035b2ea74aeff1d10c29425d2e391625a3ac76 100644 (file)
@@ -48,7 +48,7 @@ bool cgraph_global_info_ready = false;
 
 static struct cgraph_edge *create_edge PARAMS ((struct cgraph_node *,
                                                struct cgraph_node *));
-static void remove_edge PARAMS ((struct cgraph_node *, struct cgraph_node *));
+static void cgraph_remove_edge PARAMS ((struct cgraph_node *, struct cgraph_node *));
 static hashval_t hash_node PARAMS ((const PTR));
 static int eq_node PARAMS ((const PTR, const PTR));
 
@@ -95,6 +95,9 @@ cgraph_node (decl)
   node = xcalloc (sizeof (*node), 1);
   node->decl = decl;
   node->next = cgraph_nodes;
+  if (cgraph_nodes)
+    cgraph_nodes->previous = node;
+  node->previous = NULL;
   cgraph_nodes = node;
   cgraph_n_nodes++;
   *slot = node;
@@ -127,7 +130,7 @@ create_edge (caller, callee)
 /* Remove the edge from CALLER to CALLEE in the cgraph.  */
 
 static void
-remove_edge (caller, callee)
+cgraph_remove_edge (caller, callee)
      struct cgraph_node *caller, *callee;
 {
   struct cgraph_edge **edge, **edge2;
@@ -146,6 +149,37 @@ remove_edge (caller, callee)
   *edge2 = (*edge2)->next_callee;
 }
 
+/* Remove the node from cgraph.  */
+
+void
+cgraph_remove_node (node)
+     struct cgraph_node *node;
+{
+  while (node->callers)
+    cgraph_remove_edge (node->callers->caller, node);
+  while (node->callees)
+    cgraph_remove_edge (node, node->callees->callee);
+  while (node->nested)
+    cgraph_remove_node (node->nested);
+  if (node->origin)
+    {
+      struct cgraph_node **node2 = &node->origin->nested;
+
+      while (*node2 != node)
+       node2 = &(*node2)->next_nested;
+      *node2 = node->next_nested;
+    }
+  if (node->previous)
+    node->previous->next = node->next;
+  else
+    cgraph_nodes = node;
+  if (node->next)
+    node->next->previous = node->previous;
+  DECL_SAVED_TREE (node->decl) = NULL;
+  /* Do not free the structure itself so the walk over chain can continue.  */
+}
+
+
 /* Record call from CALLER to CALLEE  */
 
 struct cgraph_edge *
@@ -159,7 +193,7 @@ void
 cgraph_remove_call (caller, callee)
      tree caller, callee;
 {
-  remove_edge (cgraph_node (caller), cgraph_node (callee));
+  cgraph_remove_edge (cgraph_node (caller), cgraph_node (callee));
 }
 
 /* Return true when CALLER_DECL calls CALLEE_DECL.  */
index 6c8c8afece01fb1327372d9f4cbf8cdeb264acf4..b785e172665dd2ffbfa44d5375298ddf2c9aebf4 100644 (file)
@@ -30,7 +30,11 @@ struct cgraph_local_info
   /* Set when function function is visiable in current compilation unit only
      and it's address is never taken.  */
   bool local;
+  /* Set when function is small enought to be inlinable many times.  */
   bool inline_many;
+  /* Set when function can be inlined once (false only for functions calling
+     alloca, using varargs and so on).  */
+  bool can_inline_once;
 };
 
 /* Information about the function that needs to be computed globally
@@ -38,8 +42,8 @@ struct cgraph_local_info
 
 struct cgraph_global_info
 {
-  /* Empty for the moment.  */
-  int dummy;
+  /* Set when the function will be inlined exactly once.  */
+  bool inline_once;
 };
 
 /* Information about the function that is propagated by the RTL backend.
@@ -60,7 +64,7 @@ struct cgraph_node
   tree decl;
   struct cgraph_edge *callees;
   struct cgraph_edge *callers;
-  struct cgraph_node *next;
+  struct cgraph_node *next, *previous;
   /* For nested functions points to function the node is nested in.  */
   struct cgraph_node *origin;
   /* Points to first nested function, if any.  */
@@ -100,6 +104,7 @@ extern bool cgraph_global_info_ready;
 /* In cgraph.c  */
 void dump_cgraph                       PARAMS ((FILE *));
 void cgraph_remove_call                        PARAMS ((tree, tree));
+void cgraph_remove_node                        PARAMS ((struct cgraph_node *));
 struct cgraph_edge *cgraph_record_call PARAMS ((tree, tree));
 struct cgraph_node *cgraph_node                PARAMS ((tree decl));
 bool cgraph_calls_p                    PARAMS ((tree, tree));
index 81a8b2e29f47d387c04517511182a48cb1047dbc..c01bba6b815e8d822bd46023f4387d8ba056ebb3 100644 (file)
@@ -40,6 +40,8 @@ static void cgraph_mark_functions_to_output PARAMS ((void));
 static void cgraph_expand_function PARAMS ((struct cgraph_node *));
 static tree record_call_1 PARAMS ((tree *, int *, void *));
 static void cgraph_mark_local_functions PARAMS ((void));
+static void cgraph_mark_functions_to_inline_once PARAMS ((void));
+static void cgraph_optimize_function PARAMS ((struct cgraph_node *));
 
 /* Analyze function once it is parsed.  Set up the local information
    available - create cgraph edges for function calles via BODY.  */
@@ -53,8 +55,9 @@ cgraph_finalize_function (decl, body)
 
   node->decl = decl;
 
+  node->local.can_inline_once = tree_inlinable_function_p (decl, 1);
   if (flag_inline_trees)
-    node->local.inline_many = tree_inlinable_function_p (decl);
+    node->local.inline_many = tree_inlinable_function_p (decl, 0);
   else
     node->local.inline_many = 0;
 
@@ -200,7 +203,7 @@ cgraph_finalize_compilation_unit ()
 
       if (!node->reachable && DECL_SAVED_TREE (decl))
        {
-         DECL_SAVED_TREE (decl) = NULL;
+         cgraph_remove_node (node);
          announce_function (decl);
        }
     }
@@ -221,7 +224,8 @@ cgraph_mark_functions_to_output ()
 
       if (DECL_SAVED_TREE (decl)
          && (node->needed
-             || (!node->local.inline_many && node->reachable)
+             || (!node->local.inline_many && !node->global.inline_once
+                 && node->reachable)
              || TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl)))
          && !TREE_ASM_WRITTEN (decl) && !node->origin
          && !DECL_EXTERNAL (decl))
@@ -229,6 +233,22 @@ cgraph_mark_functions_to_output ()
     }
 }
 
+/* Optimize the function before expansion.  */
+static void
+cgraph_optimize_function (node)
+     struct cgraph_node *node;
+{
+  tree decl = node->decl;
+
+  if (flag_inline_trees)
+    optimize_inline_calls (decl);
+  if (node->nested)
+    {
+      for (node = node->nested; node; node = node->next_nested)
+       cgraph_optimize_function (node);
+    }
+}
+
 /* Expand function specified by NODE.  */
 static void
 cgraph_expand_function (node)
@@ -237,12 +257,18 @@ cgraph_expand_function (node)
   tree decl = node->decl;
 
   announce_function (decl);
-  if (flag_inline_trees)
-    optimize_inline_calls (decl);
+
+  cgraph_optimize_function (node);
 
   /* Avoid RTL inlining from taking place.  */
   (*lang_hooks.callgraph.expand_function) (decl);
-  if (DECL_UNINLINABLE (decl))
+
+  /* When we decided to inline the function once, we never ever should need to
+     output it separately.  */
+  if (node->global.inline_once)
+    abort ();
+  if (!node->local.inline_many
+      || !node->callers)
     DECL_SAVED_TREE (decl) = NULL;
   current_function_decl = NULL;
 }
@@ -354,6 +380,43 @@ cgraph_mark_local_functions ()
     }
 }
 
+/*  Decide what function should be inlined because they are invoked once
+    (so inlining won't result in duplication of the code).  */
+
+static void
+cgraph_mark_functions_to_inline_once ()
+{
+  struct cgraph_node *node, *node1;
+
+  if (!quiet_flag)
+    fprintf (stderr, "\n\nMarking functions to inline once:");
+
+  /* Now look for function called only once and mark them to inline.  From this
+     point number of calls to given function won't grow.  */
+  for (node = cgraph_nodes; node; node = node->next)
+    {
+      if (node->callers && !node->callers->next_caller && !node->needed
+         && node->local.can_inline_once)
+       {
+         bool ok = true;
+
+         /* Verify that we won't duplicate the caller.  */
+         for (node1 = node->callers->caller;
+              node1->local.inline_many
+              && node1->callers
+              && ok;
+              node1 = node1->callers->caller)
+           if (node1->callers->next_caller || node1->needed)
+             ok = false;
+         if (ok)
+           {
+             node->global.inline_once = true;
+             announce_function (node->decl);
+           }
+       }
+    }
+}
+
 
 /* Perform simple optimizations based on callgraph.  */
 
@@ -365,6 +428,8 @@ cgraph_optimize ()
 
   cgraph_mark_local_functions ();
 
+  cgraph_mark_functions_to_inline_once ();
+
   cgraph_global_info_ready = true;
   if (!quiet_flag)
     fprintf (stderr, "\n\nAssembling functions:");
index d5bc3602bea1e20f3cefecff81ca86d57e44544a..3152299f7340dc2c5fc7fe575762c1feacd42568 100644 (file)
   [(set_attr "type" "sseicvt")
    (set_attr "athlon_decode" "double,vector")])
 
+;; Avoid vector decoded form of the instruction.
+(define_peephole2
+  [(match_scratch:SF 2 "x")
+   (set (match_operand:DI 0 "register_operand" "")
+       (fix:DI (match_operand:SF 1 "nonimmediate_operand" "")))]
+  "TARGET_K8 && !optimize_size"
+  [(set (match_dup 2) (match_dup 1))
+   (set (match_dup 0) (fix:DI (match_dup 2)))]
+  "")
+
 (define_insn "fix_truncdfdi_sse"
   [(set (match_operand:DI 0 "register_operand" "=r,r")
        (fix:DI (match_operand:DF 1 "nonimmediate_operand" "Y,Ym")))]
   [(set_attr "type" "sseicvt,sseicvt")
    (set_attr "athlon_decode" "double,vector")])
 
+;; Avoid vector decoded form of the instruction.
+(define_peephole2
+  [(match_scratch:DF 2 "Y")
+   (set (match_operand:DI 0 "register_operand" "")
+       (fix:DI (match_operand:DF 1 "nonimmediate_operand" "")))]
+  "TARGET_K8 && !optimize_size"
+  [(set (match_dup 2) (match_dup 1))
+   (set (match_dup 0) (fix:DI (match_dup 2)))]
+  "")
+
 ;; Signed conversion to SImode.
 
 (define_expand "fix_truncxfsi2"
   [(set_attr "type" "sseicvt")
    (set_attr "athlon_decode" "double,vector")])
 
+;; Avoid vector decoded form of the instruction.
+(define_peephole2
+  [(match_scratch:SF 2 "x")
+   (set (match_operand:SI 0 "register_operand" "")
+       (fix:SI (match_operand:SF 1 "nonimmediate_operand" "")))]
+  "TARGET_K8 && !optimize_size"
+  [(set (match_dup 2) (match_dup 1))
+   (set (match_dup 0) (fix:SI (match_dup 2)))]
+  "")
+
 (define_insn "fix_truncdfsi_sse"
   [(set (match_operand:SI 0 "register_operand" "=r,r")
        (fix:SI (match_operand:DF 1 "nonimmediate_operand" "Y,Ym")))]
   [(set_attr "type" "sseicvt")
    (set_attr "athlon_decode" "double,vector")])
 
+;; Avoid vector decoded form of the instruction.
+(define_peephole2
+  [(match_scratch:DF 2 "Y")
+   (set (match_operand:SI 0 "register_operand" "")
+       (fix:SI (match_operand:DF 1 "nonimmediate_operand" "")))]
+  "TARGET_K8 && !optimize_size"
+  [(set (match_dup 2) (match_dup 1))
+   (set (match_dup 0) (fix:SI (match_dup 2)))]
+  "")
+
 (define_split 
   [(set (match_operand:SI 0 "register_operand" "")
        (fix:SI (match_operand 1 "register_operand" "")))
index e85ff238bd1db5dedc1a352090f500bc363329f3..d44271e05024650a7f8bd7e6805090487e42a0ff 100644 (file)
@@ -1,3 +1,7 @@
+Sat Mar  8 14:18:15 CET 2003  Jan Hubicka  <jh@suse.cz>
+
+       * gcc.dg/inline-3.c: New test.
+
 2003-03-08  Mark Mitchell  <mark@codesourcery.com>
 
        PR c++/9809
@@ -22,6 +26,7 @@
 Fri Mar  7 17:41:07 CET 2003  Jan Hubicka  <jh@suse.cz>
 
        * gcc.dg/i386-local2.c: Fix problems with certain versions of dejagnu.
+       * gcc.dg/inline-3.c:  New test.
 
 2003-03-06  Mark Mitchell  <mark@codesourcery.com>
 
diff --git a/gcc/testsuite/gcc.dg/inline-3.c b/gcc/testsuite/gcc.dg/inline-3.c
new file mode 100644 (file)
index 0000000..a3de193
--- /dev/null
@@ -0,0 +1,47 @@
+/* { dg-options "-O2 -funit-at-a-time" } */
+/* { dg-final { scan-assembler-not "big_function_2" } } */
+static void
+big_function_2(void);
+void
+big_function_1()
+{
+       while (t());
+       while (t());
+       while (t());
+       while (t());
+       while (t());
+       while (t());
+       while (t());
+       while (t());
+       while (t());
+       while (t());
+       while (t());
+       while (t());
+       while (t());
+       while (t());
+       while (t());
+       while (t());
+       while (t());
+       big_function_2();
+}
+static void
+big_function_2()
+{
+       while (t());
+       while (t());
+       while (t());
+       while (t());
+       while (t());
+       while (t());
+       while (t());
+       while (t());
+       while (t());
+       while (t());
+       while (t());
+       while (t());
+       while (t());
+       while (t());
+       while (t());
+       while (t());
+       while (t());
+}
index e1fab06177b049da80338c22a7b1280797387d5e..a49dca92d9019b5611bdf487460ab40b50ec9853 100644 (file)
@@ -115,7 +115,7 @@ static tree copy_body_r PARAMS ((tree *, int *, void *));
 static tree copy_body PARAMS ((inline_data *));
 static tree expand_call_inline PARAMS ((tree *, int *, void *));
 static void expand_calls_inline PARAMS ((tree *, inline_data *));
-static int inlinable_function_p PARAMS ((tree, inline_data *));
+static int inlinable_function_p PARAMS ((tree, inline_data *, int));
 static tree remap_decl PARAMS ((tree, inline_data *));
 #ifndef INLINER_FOR_JAVA
 static tree initialize_inlined_parameters PARAMS ((inline_data *, tree, tree));
@@ -872,10 +872,11 @@ declare_return_variable (id, return_slot_addr, var)
 /* Returns nonzero if a function can be inlined as a tree.  */
 
 int
-tree_inlinable_function_p (fn)
+tree_inlinable_function_p (fn, nolimit)
      tree fn;
+     int nolimit;
 {
-  return inlinable_function_p (fn, NULL);
+  return inlinable_function_p (fn, NULL, nolimit);
 }
 
 /* If *TP is possibly call to alloca, return nonzero.  */
@@ -939,9 +940,10 @@ find_builtin_longjmp_call (exp)
    can be inlined at all.  */
 
 static int
-inlinable_function_p (fn, id)
+inlinable_function_p (fn, id, nolimit)
      tree fn;
      inline_data *id;
+     int nolimit;
 {
   int inlinable;
   int currfn_insns;
@@ -973,12 +975,13 @@ inlinable_function_p (fn, id)
      front-end that must set DECL_INLINE in this case, because
      dwarf2out loses if a function is inlined that doesn't have
      DECL_INLINE set.  */
-  else if (! DECL_INLINE (fn))
+  else if (! DECL_INLINE (fn) && !nolimit)
     ;
   /* We can't inline functions that are too big.  Only allow a single
      function to be of MAX_INLINE_INSNS_SINGLE size.  Make special
      allowance for extern inline functions, though.  */
-  else if (! (*lang_hooks.tree_inlining.disregard_inline_limits) (fn)
+  else if (!nolimit
+          && ! (*lang_hooks.tree_inlining.disregard_inline_limits) (fn)
           && currfn_insns > max_inline_insns_single)
     ;
   /* We can't inline functions that call __builtin_longjmp at all.
@@ -1009,7 +1012,7 @@ inlinable_function_p (fn, id)
   /* In case we don't disregard the inlining limits and we basically
      can inline this function, investigate further.  */
   if (! (*lang_hooks.tree_inlining.disregard_inline_limits) (fn)
-      && inlinable)
+      && inlinable && !nolimit)
     {
       int sum_insns = (id ? id->inlined_stmts : 0) * INSNS_PER_STMT
                     + currfn_insns;
@@ -1158,7 +1161,9 @@ expand_call_inline (tp, walk_subtrees, data)
 
   /* Don't try to inline functions that are not well-suited to
      inlining.  */
-  if (!inlinable_function_p (fn, id))
+  if ((!flag_unit_at_a_time || !DECL_SAVED_TREE (fn)
+       || !cgraph_global_info (fn)->inline_once)
+      && !inlinable_function_p (fn, id, 0))
     return NULL_TREE;
 
   if (! (*lang_hooks.tree_inlining.start_inlining) (fn))
index 9c11436f39995bd99728ae9546d6c6a1405a1be5..0b9fc891259d616640dea79d92bb7da8134a6ad3 100644 (file)
@@ -25,7 +25,7 @@ Boston, MA 02111-1307, USA.  */
 /* Function prototypes.  */
 
 void optimize_inline_calls PARAMS ((tree));
-int tree_inlinable_function_p PARAMS ((tree));
+int tree_inlinable_function_p PARAMS ((tree, int));
 tree walk_tree PARAMS ((tree*, walk_tree_fn, void*, void*));
 tree walk_tree_without_duplicates PARAMS ((tree*, walk_tree_fn, void*));
 tree copy_tree_r PARAMS ((tree*, int*, void*));