diff options
author | Jan Hubicka <jh@suse.cz> | 2010-09-24 21:14:51 +0200 |
---|---|---|
committer | Jan Hubicka <hubicka@gcc.gnu.org> | 2010-09-24 19:14:51 +0000 |
commit | 46a4da10f5b00da1091d9641836f0550ce9a5120 (patch) | |
tree | 3586ff30f00807c4e59b1bf02ac5e21a1cdad147 | |
parent | e1b793e7c10393b112937ce3e7de4db903052618 (diff) | |
download | gcc-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/ChangeLog | 19 | ||||
-rw-r--r-- | gcc/c-family/ChangeLog | 5 | ||||
-rw-r--r-- | gcc/c-family/c-common.c | 25 | ||||
-rw-r--r-- | gcc/calls.c | 2 | ||||
-rw-r--r-- | gcc/doc/extend.texi | 25 | ||||
-rw-r--r-- | gcc/ipa-reference.c | 52 | ||||
-rw-r--r-- | gcc/testsuite/ChangeLog | 4 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/tree-ssa/leaf.c | 20 | ||||
-rw-r--r-- | gcc/tree-cfg.c | 6 | ||||
-rw-r--r-- | gcc/tree.c | 29 | ||||
-rw-r--r-- | gcc/tree.h | 2 |
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; } @@ -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); |