aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan Hubicka <jh@suse.cz>2010-09-24 21:14:51 +0200
committerJan Hubicka <hubicka@gcc.gnu.org>2010-09-24 19:14:51 +0000
commit46a4da10f5b00da1091d9641836f0550ce9a5120 (patch)
tree3586ff30f00807c4e59b1bf02ac5e21a1cdad147
parente1b793e7c10393b112937ce3e7de4db903052618 (diff)
downloadgcc-46a4da10f5b00da1091d9641836f0550ce9a5120.zip
gcc-46a4da10f5b00da1091d9641836f0550ce9a5120.tar.gz
gcc-46a4da10f5b00da1091d9641836f0550ce9a5120.tar.bz2
extend.texi: (attribute leaf): Document.
* doc/extend.texi: (attribute leaf): Document. * tree.c (local_define_builtin): Handle ECF_LEAF. (build_common_builtin_nodes): Set ECF_LEAF where needed. * tree.h (ECF_LEAF): New. * ipa-reference.c (propagate_bits): For leaf calls propagate ever overwrittable and unavailable functions. (ipa_init): Put all_module_statics into optimization_summary_obstack. (copy_global_bitmap): Do not copy all_module_statics. (read_write_all_from_decl): Use cgraph_node argument; handle ECF_LEAF. (propagate): Handle overwritable and unavailable leaf functions; initialize global info for overwritable and unavailable leaf functions; do not free all module statics. (ipa_reference_get_not_read_global, ipa_reference_get_not_written_global): leaf calls don't clobber local statics. * calls.c (flags_from_decl_or_type): Handle leaf. * tree-cfg.c (stmt_can_make_abnormal_goto): Leaf functions can't do abnormal gotos. * c-common.c (handle_leaf_attribute): New function. (struct attribute_spec c_common_att): Add leaf. * gcc.dg/tree-ssa/leaf.c: New testcase. From-SVN: r164606
-rw-r--r--gcc/ChangeLog19
-rw-r--r--gcc/c-family/ChangeLog5
-rw-r--r--gcc/c-family/c-common.c25
-rw-r--r--gcc/calls.c2
-rw-r--r--gcc/doc/extend.texi25
-rw-r--r--gcc/ipa-reference.c52
-rw-r--r--gcc/testsuite/ChangeLog4
-rw-r--r--gcc/testsuite/gcc.dg/tree-ssa/leaf.c20
-rw-r--r--gcc/tree-cfg.c6
-rw-r--r--gcc/tree.c29
-rw-r--r--gcc/tree.h2
11 files changed, 168 insertions, 21 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 567e8f1..e4660af 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,22 @@
+2010-09-24 Jan Hubicka <jh@suse.cz>
+
+ * doc/extend.texi: (attribute leaf): Document.
+ * tree.c (local_define_builtin): Handle ECF_LEAF.
+ (build_common_builtin_nodes): Set ECF_LEAF where needed.
+ * tree.h (ECF_LEAF): New.
+ * ipa-reference.c (propagate_bits): For leaf calls propagate ever overwrittable
+ and unavailable functions.
+ (ipa_init): Put all_module_statics into optimization_summary_obstack.
+ (copy_global_bitmap): Do not copy all_module_statics.
+ (read_write_all_from_decl): Use cgraph_node argument; handle ECF_LEAF.
+ (propagate): Handle overwritable and unavailable leaf functions;
+ initialize global info for overwritable and unavailable leaf functions;
+ do not free all module statics.
+ (ipa_reference_get_not_read_global, ipa_reference_get_not_written_global):
+ leaf calls don't clobber local statics.
+ * calls.c (flags_from_decl_or_type): Handle leaf.
+ * tree-cfg.c (stmt_can_make_abnormal_goto): Leaf functions can't do
+ abnormal gotos.
2010-09-24 Basile Starynkevitch <basile@starynkevitch.net>
diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog
index d708edb..7d3e3dd 100644
--- a/gcc/c-family/ChangeLog
+++ b/gcc/c-family/ChangeLog
@@ -1,3 +1,8 @@
+2010-09-24 Jan Hubicka <jh@suse.cz>
+
+ * c-common.c (handle_leaf_attribute): New function.
+ (struct attribute_spec c_common_att): Add leaf.
+
2010-09-22 Joseph Myers <joseph@codesourcery.com>
* c.opt (-all-warnings, -ansi, -assert, -assert=, -comments,
diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index cf2fe17..6ef3bf9 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -308,6 +308,7 @@ static tree handle_hot_attribute (tree *, tree, tree, int, bool *);
static tree handle_cold_attribute (tree *, tree, tree, int, bool *);
static tree handle_noinline_attribute (tree *, tree, tree, int, bool *);
static tree handle_noclone_attribute (tree *, tree, tree, int, bool *);
+static tree handle_leaf_attribute (tree *, tree, tree, int, bool *);
static tree handle_always_inline_attribute (tree *, tree, tree, int,
bool *);
static tree handle_gnu_inline_attribute (tree *, tree, tree, int, bool *);
@@ -570,6 +571,8 @@ const struct attribute_spec c_common_attribute_table[] =
handle_noinline_attribute },
{ "noclone", 0, 0, true, false, false,
handle_noclone_attribute },
+ { "leaf", 0, 0, true, false, false,
+ handle_leaf_attribute },
{ "always_inline", 0, 0, true, false, false,
handle_always_inline_attribute },
{ "gnu_inline", 0, 0, true, false, false,
@@ -5873,6 +5876,28 @@ handle_gnu_inline_attribute (tree *node, tree name,
return NULL_TREE;
}
+/* Handle a "leaf" attribute; arguments as in
+ struct attribute_spec.handler. */
+
+static tree
+handle_leaf_attribute (tree *node, tree name,
+ tree ARG_UNUSED (args),
+ int ARG_UNUSED (flags), bool *no_add_attrs)
+{
+ if (TREE_CODE (*node) != FUNCTION_DECL)
+ {
+ warning (OPT_Wattributes, "%qE attribute ignored", name);
+ *no_add_attrs = true;
+ }
+ if (!TREE_PUBLIC (*node))
+ {
+ warning (OPT_Wattributes, "%qE attribute has no effect on unit local functions", name);
+ *no_add_attrs = true;
+ }
+
+ return NULL_TREE;
+}
+
/* Handle an "artificial" attribute; arguments as in
struct attribute_spec.handler. */
diff --git a/gcc/calls.c b/gcc/calls.c
index 3888831..9a4768a 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -610,6 +610,8 @@ flags_from_decl_or_type (const_tree exp)
if (DECL_IS_NOVOPS (exp))
flags |= ECF_NOVOPS;
+ if (lookup_attribute ("leaf", DECL_ATTRIBUTES (exp)))
+ flags |= ECF_LEAF;
if (TREE_NOTHROW (exp))
flags |= ECF_NOTHROW;
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index d737617a..7073c90 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -2671,6 +2671,31 @@ SRAM. The function will be put into a specific section named
@code{.l1.text}. With @option{-mfdpic}, callers of such functions will use
an inlined PLT.
+@item leaf
+@cindex @code{leaf} function attribute
+Calls to external functions with this attribute must return to the current
+compilation unit only by return or by exception handling. In particular, leaf
+functions are not allowed to call callback function passed to it from current
+compilation unit or directly call functions exported by the unit or longjmp
+into the unit. Still leaf function might call functions from other complation
+units and thus they are not neccesarily leaf in the sense that they contains no
+function calls at all.
+
+The attribute is intended for library functions to improve dataflow analysis.
+Compiler takes the hint that any data not escaping current compilation unit can
+not be used or modified by the leaf function. For example, function @code{sin}
+is leaf, function @code{qsort} is not.
+
+Note that the leaf functions might invoke signals and signal handlers might be
+defined in the current compilation unit and use static variables. Only
+compliant way to write such a signal handler is to declare such variables
+@code{volatile}.
+
+The attribute has no effect on functions defined within current compilation
+unit. This is to allow easy merging of multiple compilation units into one,
+for example, by using the link time optimization. For this reason the
+attribute is not allowed on types to annotate indirect calls.
+
@item long_call/short_call
@cindex indirect calls on ARM
This attribute specifies how a particular function is called on
diff --git a/gcc/ipa-reference.c b/gcc/ipa-reference.c
index 64ccb4e..96482f1 100644
--- a/gcc/ipa-reference.c
+++ b/gcc/ipa-reference.c
@@ -200,6 +200,8 @@ ipa_reference_get_not_read_global (struct cgraph_node *fn)
info = get_reference_optimization_summary (fn);
if (info)
return info->statics_not_read;
+ else if (flags_from_decl_or_type (fn->decl) & ECF_LEAF)
+ return all_module_statics;
else
return NULL;
}
@@ -217,6 +219,8 @@ ipa_reference_get_not_written_global (struct cgraph_node *fn)
info = get_reference_optimization_summary (fn);
if (info)
return info->statics_not_written;
+ else if (flags_from_decl_or_type (fn->decl) & ECF_LEAF)
+ return all_module_statics;
else
return NULL;
}
@@ -299,9 +303,13 @@ propagate_bits (ipa_reference_global_vars_info_t x_global, struct cgraph_node *x
for (e = x->callees; e; e = e->next_callee)
{
struct cgraph_node *y = e->callee;
+ enum availability avail;
+ avail = cgraph_function_body_availability (e->callee);
/* Only look into nodes we can propagate something. */
- if (cgraph_function_body_availability (e->callee) > AVAIL_OVERWRITABLE)
+ if (avail > AVAIL_OVERWRITABLE
+ || (avail == AVAIL_OVERWRITABLE
+ && (flags_from_decl_or_type (e->callee->decl) & ECF_LEAF)))
{
int flags = flags_from_decl_or_type (e->callee->decl);
if (get_reference_vars_info (y))
@@ -573,17 +581,28 @@ read_write_all_from_decl (struct cgraph_node *node, bool * read_all,
{
tree decl = node->decl;
int flags = flags_from_decl_or_type (decl);
- if (flags & ECF_CONST)
+ if ((flags & ECF_LEAF)
+ && cgraph_function_body_availability (node) <= AVAIL_OVERWRITABLE)
+ ;
+ else if (flags & ECF_CONST)
;
else if ((flags & ECF_PURE)
|| cgraph_node_cannot_return (node))
- *read_all = true;
+ {
+ *read_all = true;
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, " %s/%i -> read all\n",
+ cgraph_node_name (node), node->uid);
+ }
else
{
/* TODO: To be able to produce sane results, we should also handle
common builtins, in particular throw. */
*read_all = true;
*write_all = true;
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, " %s/%i -> read all, write all\n",
+ cgraph_node_name (node), node->uid);
}
}
@@ -629,6 +648,11 @@ propagate (void)
node_info = get_reference_vars_info (node);
gcc_assert (node_info);
+
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "Starting cycle with %s/%i\n",
+ cgraph_node_name (node), node->uid);
+
node_l = &node_info->local;
node_g = &node_info->global;
@@ -647,9 +671,15 @@ propagate (void)
if (!(ie->indirect_info->ecf_flags & ECF_CONST))
{
read_all = true;
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, " indirect call -> read all\n");
if (!cgraph_edge_cannot_lead_to_return (ie)
&& !(ie->indirect_info->ecf_flags & ECF_PURE))
- write_all = true;
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, " indirect call -> write all\n");
+ write_all = true;
+ }
}
@@ -659,6 +689,9 @@ propagate (void)
w = w_info->next_cycle;
while (w && (!read_all || !write_all))
{
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, " Visiting %s/%i\n",
+ cgraph_node_name (w), w->uid);
/* When function is overwrittable, we can not assume anything. */
if (cgraph_function_body_availability (w) <= AVAIL_OVERWRITABLE)
read_write_all_from_decl (w, &read_all, &write_all);
@@ -671,9 +704,15 @@ propagate (void)
if (!(ie->indirect_info->ecf_flags & ECF_CONST))
{
read_all = true;
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, " indirect call -> read all\n");
if (!cgraph_edge_cannot_lead_to_return (ie)
&& !(ie->indirect_info->ecf_flags & ECF_PURE))
- write_all = true;
+ {
+ write_all = true;
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, " indirect call -> write all\n");
+ }
}
w_info = (struct ipa_dfs_info *) w->aux;
@@ -841,7 +880,8 @@ propagate (void)
continue;
node_info = get_reference_vars_info (node);
- if (cgraph_function_body_availability (node) > AVAIL_OVERWRITABLE)
+ if (cgraph_function_body_availability (node) > AVAIL_OVERWRITABLE
+ || (flags_from_decl_or_type (node->decl) & ECF_LEAF))
{
node_g = &node_info->global;
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index e1cd3a1..30ef6fd 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,5 +1,9 @@
2010-09-24 Jan Hubicka <jh@suse.cz>
+ * gcc.dg/tree-ssa/leaf.c: New testcase.
+
+2010-09-24 Jan Hubicka <jh@suse.cz>
+
PR tree-optimization/45738
PR tree-optimization/45741
* gcc.c-torture/compile/pr45741.c: New.
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/leaf.c b/gcc/testsuite/gcc.dg/tree-ssa/leaf.c
new file mode 100644
index 0000000..936523a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/leaf.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+static int local_static;
+void __attribute__ ((leaf)) leaf_call (void);
+
+int
+clobber_it (void)
+{
+ return local_static++;
+}
+int
+test (void)
+{
+ local_static = 9;
+ leaf_call ();
+ return local_static;
+}
+/* { dg-final { scan-tree-dump-times "return 9" 1 "optimized"} } */
+
+/* { dg-final { cleanup-tree-dump "optimized" } } */
diff --git a/gcc/tree-cfg.c b/gcc/tree-cfg.c
index be8a84f..cf67fb8 100644
--- a/gcc/tree-cfg.c
+++ b/gcc/tree-cfg.c
@@ -2258,7 +2258,8 @@ is_ctrl_altering_stmt (gimple t)
/* A non-pure/const call alters flow control if the current
function has nonlocal labels. */
- if (!(flags & (ECF_CONST | ECF_PURE)) && cfun->has_nonlocal_label)
+ if (!(flags & (ECF_CONST | ECF_PURE | ECF_LEAF))
+ && cfun->has_nonlocal_label)
return true;
/* A call also alters control flow if it does not return. */
@@ -2314,7 +2315,8 @@ stmt_can_make_abnormal_goto (gimple t)
if (computed_goto_p (t))
return true;
if (is_gimple_call (t))
- return gimple_has_side_effects (t) && cfun->has_nonlocal_label;
+ return (gimple_has_side_effects (t) && cfun->has_nonlocal_label
+ && !(gimple_call_flags (t) & ECF_LEAF));
return false;
}
diff --git a/gcc/tree.c b/gcc/tree.c
index 8423416..738fa9c 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -9224,6 +9224,9 @@ local_define_builtin (const char *name, tree type, enum built_in_function code,
TREE_NOTHROW (decl) = 1;
if (ecf_flags & ECF_MALLOC)
DECL_IS_MALLOC (decl) = 1;
+ if (ecf_flags & ECF_LEAF)
+ DECL_ATTRIBUTES (decl) = tree_cons (get_identifier ("leaf"),
+ NULL, DECL_ATTRIBUTES (decl));
built_in_decls[code] = decl;
implicit_built_in_decls[code] = decl;
@@ -9247,10 +9250,10 @@ build_common_builtin_nodes (void)
if (built_in_decls[BUILT_IN_MEMCPY] == NULL)
local_define_builtin ("__builtin_memcpy", ftype, BUILT_IN_MEMCPY,
- "memcpy", ECF_NOTHROW);
+ "memcpy", ECF_NOTHROW | ECF_LEAF);
if (built_in_decls[BUILT_IN_MEMMOVE] == NULL)
local_define_builtin ("__builtin_memmove", ftype, BUILT_IN_MEMMOVE,
- "memmove", ECF_NOTHROW);
+ "memmove", ECF_NOTHROW | ECF_LEAF);
}
if (built_in_decls[BUILT_IN_MEMCMP] == NULL)
@@ -9259,7 +9262,7 @@ build_common_builtin_nodes (void)
const_ptr_type_node, size_type_node,
NULL_TREE);
local_define_builtin ("__builtin_memcmp", ftype, BUILT_IN_MEMCMP,
- "memcmp", ECF_PURE | ECF_NOTHROW);
+ "memcmp", ECF_PURE | ECF_NOTHROW | ECF_LEAF);
}
if (built_in_decls[BUILT_IN_MEMSET] == NULL)
@@ -9268,7 +9271,7 @@ build_common_builtin_nodes (void)
ptr_type_node, integer_type_node,
size_type_node, NULL_TREE);
local_define_builtin ("__builtin_memset", ftype, BUILT_IN_MEMSET,
- "memset", ECF_NOTHROW);
+ "memset", ECF_NOTHROW | ECF_LEAF);
}
if (built_in_decls[BUILT_IN_ALLOCA] == NULL)
@@ -9276,7 +9279,7 @@ build_common_builtin_nodes (void)
ftype = build_function_type_list (ptr_type_node,
size_type_node, NULL_TREE);
local_define_builtin ("__builtin_alloca", ftype, BUILT_IN_ALLOCA,
- "alloca", ECF_MALLOC | ECF_NOTHROW);
+ "alloca", ECF_MALLOC | ECF_NOTHROW | ECF_LEAF);
}
/* If we're checking the stack, `alloca' can throw. */
@@ -9288,7 +9291,7 @@ build_common_builtin_nodes (void)
ptr_type_node, NULL_TREE);
local_define_builtin ("__builtin_init_trampoline", ftype,
BUILT_IN_INIT_TRAMPOLINE,
- "__builtin_init_trampoline", ECF_NOTHROW);
+ "__builtin_init_trampoline", ECF_NOTHROW | ECF_LEAF);
ftype = build_function_type_list (ptr_type_node, ptr_type_node, NULL_TREE);
local_define_builtin ("__builtin_adjust_trampoline", ftype,
@@ -9322,12 +9325,12 @@ build_common_builtin_nodes (void)
ftype = build_function_type_list (ptr_type_node, NULL_TREE);
local_define_builtin ("__builtin_stack_save", ftype, BUILT_IN_STACK_SAVE,
- "__builtin_stack_save", ECF_NOTHROW);
+ "__builtin_stack_save", ECF_NOTHROW | ECF_LEAF);
ftype = build_function_type_list (void_type_node, ptr_type_node, NULL_TREE);
local_define_builtin ("__builtin_stack_restore", ftype,
BUILT_IN_STACK_RESTORE,
- "__builtin_stack_restore", ECF_NOTHROW);
+ "__builtin_stack_restore", ECF_NOTHROW | ECF_LEAF);
ftype = build_function_type_list (void_type_node, NULL_TREE);
local_define_builtin ("__builtin_profile_func_enter", ftype,
@@ -9342,7 +9345,7 @@ build_common_builtin_nodes (void)
ftype = build_function_type_list (void_type_node, NULL_TREE);
local_define_builtin ("__builtin_cxa_end_cleanup", ftype,
BUILT_IN_CXA_END_CLEANUP,
- "__cxa_end_cleanup", ECF_NORETURN);
+ "__cxa_end_cleanup", ECF_NORETURN | ECF_LEAF);
}
ftype = build_function_type_list (void_type_node, ptr_type_node, NULL_TREE);
@@ -9361,12 +9364,12 @@ build_common_builtin_nodes (void)
ftype = build_function_type_list (ptr_type_node,
integer_type_node, NULL_TREE);
local_define_builtin ("__builtin_eh_pointer", ftype, BUILT_IN_EH_POINTER,
- "__builtin_eh_pointer", ECF_PURE | ECF_NOTHROW);
+ "__builtin_eh_pointer", ECF_PURE | ECF_NOTHROW | ECF_LEAF);
tmp = lang_hooks.types.type_for_mode (targetm.eh_return_filter_mode (), 0);
ftype = build_function_type_list (tmp, integer_type_node, NULL_TREE);
local_define_builtin ("__builtin_eh_filter", ftype, BUILT_IN_EH_FILTER,
- "__builtin_eh_filter", ECF_PURE | ECF_NOTHROW);
+ "__builtin_eh_filter", ECF_PURE | ECF_NOTHROW | ECF_LEAF);
ftype = build_function_type_list (void_type_node,
integer_type_node, integer_type_node,
@@ -9408,11 +9411,11 @@ build_common_builtin_nodes (void)
built_in_names[mcode] = concat ("__mul", mode_name_buf, "3", NULL);
local_define_builtin (built_in_names[mcode], ftype, mcode,
- built_in_names[mcode], ECF_CONST | ECF_NOTHROW);
+ built_in_names[mcode], ECF_CONST | ECF_NOTHROW | ECF_LEAF);
built_in_names[dcode] = concat ("__div", mode_name_buf, "3", NULL);
local_define_builtin (built_in_names[dcode], ftype, dcode,
- built_in_names[dcode], ECF_CONST | ECF_NOTHROW);
+ built_in_names[dcode], ECF_CONST | ECF_NOTHROW | ECF_LEAF);
}
}
}
diff --git a/gcc/tree.h b/gcc/tree.h
index e7d0c53..1b843f8 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -5224,6 +5224,8 @@ extern tree build_duplicate_type (tree);
/* Function does not read or write memory (but may have side effects, so
it does not necessarily fit ECF_CONST). */
#define ECF_NOVOPS (1 << 9)
+/* The function does not lead to calls within current function unit. */
+#define ECF_LEAF (1 << 10)
extern int flags_from_decl_or_type (const_tree);
extern int call_expr_flags (const_tree);