aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorJan Hubicka <jh@suse.cz>2003-03-08 14:26:37 +0100
committerJan Hubicka <hubicka@gcc.gnu.org>2003-03-08 13:26:37 +0000
commit18d13f34175f157bc3152afdf48da798452afc1b (patch)
tree8a4f34953d22192851b98b6fb46d7a44532ecfc0 /gcc
parent4a07c08a47f2a4fbce5634b93834ed824ebafc79 (diff)
downloadgcc-18d13f34175f157bc3152afdf48da798452afc1b.zip
gcc-18d13f34175f157bc3152afdf48da798452afc1b.tar.gz
gcc-18d13f34175f157bc3152afdf48da798452afc1b.tar.bz2
inline-3.c: New test.
* 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
Diffstat (limited to 'gcc')
-rw-r--r--gcc/ChangeLog23
-rw-r--r--gcc/c-decl.c2
-rw-r--r--gcc/cgraph.c40
-rw-r--r--gcc/cgraph.h11
-rw-r--r--gcc/cgraphunit.c77
-rw-r--r--gcc/config/i386/i386.md40
-rw-r--r--gcc/testsuite/ChangeLog5
-rw-r--r--gcc/testsuite/gcc.dg/inline-3.c47
-rw-r--r--gcc/tree-inline.c21
-rw-r--r--gcc/tree-inline.h2
10 files changed, 246 insertions, 22 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index e3919a9..383f9e6 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -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
diff --git a/gcc/c-decl.c b/gcc/c-decl.c
index 4779406..f7996f1 100644
--- a/gcc/c-decl.c
+++ b/gcc/c-decl.c
@@ -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
diff --git a/gcc/cgraph.c b/gcc/cgraph.c
index 1a078f6..bb035b2 100644
--- a/gcc/cgraph.c
+++ b/gcc/cgraph.c
@@ -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. */
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index 6c8c8af..b785e17 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -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));
diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c
index 81a8b2e..c01bba6 100644
--- a/gcc/cgraphunit.c
+++ b/gcc/cgraphunit.c
@@ -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:");
diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
index d5bc360..3152299 100644
--- a/gcc/config/i386/i386.md
+++ b/gcc/config/i386/i386.md
@@ -4519,6 +4519,16 @@
[(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")))]
@@ -4527,6 +4537,16 @@
[(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"
@@ -4630,6 +4650,16 @@
[(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")))]
@@ -4638,6 +4668,16 @@
[(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" "")))
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index e85ff23..d44271e 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -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
index 0000000..a3de193
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/inline-3.c
@@ -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());
+}
diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c
index e1fab06..a49dca9 100644
--- a/gcc/tree-inline.c
+++ b/gcc/tree-inline.c
@@ -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))
diff --git a/gcc/tree-inline.h b/gcc/tree-inline.h
index 9c11436..0b9fc89 100644
--- a/gcc/tree-inline.h
+++ b/gcc/tree-inline.h
@@ -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*));