mirror of git://gcc.gnu.org/git/gcc.git
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
This commit is contained in:
parent
e1b793e7c1
commit
46a4da10f5
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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. */
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
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
|
||||
|
|
|
|||
|
|
@ -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" } } */
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
29
gcc/tree.c
29
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue