Commit 46a4da10 by Jan Hubicka Committed by Jan Hubicka

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
parent e1b793e7
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>
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;
......
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.
......
/* { 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;
}
......
......@@ -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);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment