From 8413ca874e088161ca0fbc3bf3a98cffc45564f3 Mon Sep 17 00:00:00 2001 From: Jakub Jelinek Date: Fri, 14 Nov 2014 18:46:50 +0100 Subject: [PATCH] ipa-pure-const.c (struct funct_state_d): Add can_free field. * ipa-pure-const.c (struct funct_state_d): Add can_free field. (varying_state): Add true for can_free. (check_call): For builtin or internal !nonfreeing_call_p set local->can_free. (check_stmt): For asm volatile and asm with "memory" set local->can_free. (analyze_function): Clear local->can_free initially, continue calling check_stmt until all flags are computed, dump can_free flag. (pure_const_write_summary): Write can_free flag. (pure_const_read_summary): Read it back. (propagate_pure_const): Propagate also can_free flag, set w->nonfreeing_fn if it is false after propagation. * cgraph.h (cgraph_node): Add nonfreeing_fn member. * gimple.c: Include ipa-ref.h, lto-streamer.h and cgraph.h. (nonfreeing_call_p): Return cgraph nonfreeing_fn flag if set. Also return true for IFN_ABNORMAL_DISPATCHER. * cgraph.c (cgraph_node::dump): Dump nonfreeing_fn flag. * lto-cgraph.c (lto_output_node): Write nonfreeing_fn flag. (input_overwrite_node): Read it back. From-SVN: r217582 --- gcc/ChangeLog | 23 +++++++++++++++++ gcc/cgraph.c | 2 ++ gcc/cgraph.h | 4 +++ gcc/gimple.c | 28 +++++++++++++++++--- gcc/ipa-pure-const.c | 61 +++++++++++++++++++++++++++++++++++++++++--- gcc/lto-cgraph.c | 2 ++ 6 files changed, 113 insertions(+), 7 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index e5ff6e9a615..c5ea4811458 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,26 @@ +2014-11-14 Jakub Jelinek + + * ipa-pure-const.c (struct funct_state_d): Add can_free field. + (varying_state): Add true for can_free. + (check_call): For builtin or internal !nonfreeing_call_p set + local->can_free. + (check_stmt): For asm volatile and asm with "memory" set + local->can_free. + (analyze_function): Clear local->can_free initially, continue + calling check_stmt until all flags are computed, dump can_free + flag. + (pure_const_write_summary): Write can_free flag. + (pure_const_read_summary): Read it back. + (propagate_pure_const): Propagate also can_free flag, set + w->nonfreeing_fn if it is false after propagation. + * cgraph.h (cgraph_node): Add nonfreeing_fn member. + * gimple.c: Include ipa-ref.h, lto-streamer.h and cgraph.h. + (nonfreeing_call_p): Return cgraph nonfreeing_fn flag if set. + Also return true for IFN_ABNORMAL_DISPATCHER. + * cgraph.c (cgraph_node::dump): Dump nonfreeing_fn flag. + * lto-cgraph.c (lto_output_node): Write nonfreeing_fn flag. + (input_overwrite_node): Read it back. + 2014-11-14 Jakub Jelinek Marek Polacek diff --git a/gcc/cgraph.c b/gcc/cgraph.c index 8dcccbf19c6..a66c9c0ea75 100644 --- a/gcc/cgraph.c +++ b/gcc/cgraph.c @@ -1995,6 +1995,8 @@ cgraph_node::dump (FILE *f) fprintf (f, " tm_clone"); if (icf_merged) fprintf (f, " icf_merged"); + if (nonfreeing_fn) + fprintf (f, " nonfreeing_fn"); if (DECL_STATIC_CONSTRUCTOR (decl)) fprintf (f," static_constructor (priority:%i)", get_init_priority ()); if (DECL_STATIC_DESTRUCTOR (decl)) diff --git a/gcc/cgraph.h b/gcc/cgraph.h index 82222217898..e9a14c4d492 100644 --- a/gcc/cgraph.h +++ b/gcc/cgraph.h @@ -1267,6 +1267,10 @@ public: /* True when function is clone created for Pointer Bounds Checker instrumentation. */ unsigned instrumentation_clone : 1; + /* True if call to node can't result in a call to free, munmap or + other operation that could make previously non-trapping memory + accesses trapping. */ + unsigned nonfreeing_fn : 1; }; /* A cgraph node set is a collection of cgraph nodes. A cgraph node diff --git a/gcc/gimple.c b/gcc/gimple.c index ac753650e18..f8459a448cb 100644 --- a/gcc/gimple.c +++ b/gcc/gimple.c @@ -58,6 +58,9 @@ along with GCC; see the file COPYING3. If not see #include "bitmap.h" #include "stringpool.h" #include "tree-ssanames.h" +#include "ipa-ref.h" +#include "lto-streamer.h" +#include "cgraph.h" /* All the tuples have their operand vector (if present) at the very bottom @@ -2538,11 +2541,28 @@ nonfreeing_call_p (gimple call) default: return true; } - else if (gimple_call_internal_p (call) - && gimple_call_flags (call) & ECF_LEAF) - return true; + else if (gimple_call_internal_p (call)) + switch (gimple_call_internal_fn (call)) + { + case IFN_ABNORMAL_DISPATCHER: + return true; + default: + if (gimple_call_flags (call) & ECF_LEAF) + return true; + return false; + } - return false; + tree fndecl = gimple_call_fndecl (call); + if (!fndecl) + return false; + struct cgraph_node *n = cgraph_node::get (fndecl); + if (!n) + return false; + enum availability availability; + n = n->function_symbol (&availability); + if (!n || availability <= AVAIL_INTERPOSABLE) + return false; + return n->nonfreeing_fn; } /* Callback for walk_stmt_load_store_ops. diff --git a/gcc/ipa-pure-const.c b/gcc/ipa-pure-const.c index 6f7b32c12bc..a55288d7ba4 100644 --- a/gcc/ipa-pure-const.c +++ b/gcc/ipa-pure-const.c @@ -112,11 +112,15 @@ struct funct_state_d bool looping; bool can_throw; + + /* If function can call free, munmap or otherwise make previously + non-trapping memory accesses trapping. */ + bool can_free; }; /* State used when we know nothing about function. */ static struct funct_state_d varying_state - = { IPA_NEITHER, IPA_NEITHER, true, true, true }; + = { IPA_NEITHER, IPA_NEITHER, true, true, true, true }; typedef struct funct_state_d * funct_state; @@ -559,6 +563,10 @@ check_call (funct_state local, gimple call, bool ipa) enum pure_const_state_e call_state; bool call_looping; + if (gimple_call_builtin_p (call, BUILT_IN_NORMAL) + && !nonfreeing_call_p (call)) + local->can_free = true; + if (special_builtin_state (&call_state, &call_looping, callee_t)) { worse_state (&local->pure_const_state, &local->looping, @@ -589,6 +597,8 @@ check_call (funct_state local, gimple call, bool ipa) break; } } + else if (gimple_call_internal_p (call) && !nonfreeing_call_p (call)) + local->can_free = true; /* When not in IPA mode, we can still handle self recursion. */ if (!ipa && callee_t @@ -753,6 +763,7 @@ check_stmt (gimple_stmt_iterator *gsip, funct_state local, bool ipa) fprintf (dump_file, " memory asm clobber is not const/pure\n"); /* Abandon all hope, ye who enter here. */ local->pure_const_state = IPA_NEITHER; + local->can_free = true; } if (gimple_asm_volatile_p (stmt)) { @@ -760,7 +771,8 @@ check_stmt (gimple_stmt_iterator *gsip, funct_state local, bool ipa) fprintf (dump_file, " volatile is not const/pure\n"); /* Abandon all hope, ye who enter here. */ local->pure_const_state = IPA_NEITHER; - local->looping = true; + local->looping = true; + local->can_free = true; } return; default: @@ -785,6 +797,7 @@ analyze_function (struct cgraph_node *fn, bool ipa) l->looping_previously_known = true; l->looping = false; l->can_throw = false; + l->can_free = false; state_from_flags (&l->state_previously_known, &l->looping_previously_known, flags_from_decl_or_type (fn->decl), fn->cannot_return_p ()); @@ -815,7 +828,10 @@ analyze_function (struct cgraph_node *fn, bool ipa) gsi_next (&gsi)) { check_stmt (&gsi, l, ipa); - if (l->pure_const_state == IPA_NEITHER && l->looping && l->can_throw) + if (l->pure_const_state == IPA_NEITHER + && l->looping + && l->can_throw + && l->can_free) goto end; } } @@ -882,6 +898,8 @@ end: fprintf (dump_file, "Function is locally const.\n"); if (l->pure_const_state == IPA_PURE) fprintf (dump_file, "Function is locally pure.\n"); + if (l->can_free) + fprintf (dump_file, "Function can locally free.\n"); } return l; } @@ -1021,6 +1039,7 @@ pure_const_write_summary (void) bp_pack_value (&bp, fs->looping_previously_known, 1); bp_pack_value (&bp, fs->looping, 1); bp_pack_value (&bp, fs->can_throw, 1); + bp_pack_value (&bp, fs->can_free, 1); streamer_write_bitpack (&bp); } } @@ -1080,6 +1099,7 @@ pure_const_read_summary (void) fs->looping_previously_known = bp_unpack_value (&bp, 1); fs->looping = bp_unpack_value (&bp, 1); fs->can_throw = bp_unpack_value (&bp, 1); + fs->can_free = bp_unpack_value (&bp, 1); if (dump_file) { int flags = flags_from_decl_or_type (node->decl); @@ -1102,6 +1122,8 @@ pure_const_read_summary (void) fprintf (dump_file," function is previously known looping\n"); if (fs->can_throw) fprintf (dump_file," function is locally throwing\n"); + if (fs->can_free) + fprintf (dump_file," function can locally free\n"); } } @@ -1347,6 +1369,33 @@ propagate_pure_const (void) pure_const_names [pure_const_state], looping); + /* Find the worst state of can_free for any node in the cycle. */ + bool can_free = false; + w = node; + while (w && !can_free) + { + struct cgraph_edge *e; + funct_state w_l = get_function_state (w); + + if (w_l->can_free + || w->get_availability () == AVAIL_INTERPOSABLE + || w->indirect_calls) + can_free = true; + + for (e = w->callees; e && !can_free; e = e->next_callee) + { + enum availability avail; + struct cgraph_node *y = e->callee->function_symbol (&avail); + + if (avail > AVAIL_INTERPOSABLE) + can_free = get_function_state (y)->can_free; + else + can_free = true; + } + w_info = (struct ipa_dfs_info *) w->aux; + w = w_info->next_cycle; + } + /* Copy back the region's pure_const_state which is shared by all nodes in the region. */ w = node; @@ -1356,6 +1405,12 @@ propagate_pure_const (void) enum pure_const_state_e this_state = pure_const_state; bool this_looping = looping; + w_l->can_free = can_free; + w->nonfreeing_fn = !can_free; + if (!can_free && dump_file) + fprintf (dump_file, "Function found not to call free: %s\n", + w->name ()); + if (w_l->state_previously_known != IPA_NEITHER && this_state > w_l->state_previously_known) { diff --git a/gcc/lto-cgraph.c b/gcc/lto-cgraph.c index b29d1b51a38..3ce2367a3bf 100644 --- a/gcc/lto-cgraph.c +++ b/gcc/lto-cgraph.c @@ -562,6 +562,7 @@ lto_output_node (struct lto_simple_output_block *ob, struct cgraph_node *node, bp_pack_value (&bp, node->tm_clone, 1); bp_pack_value (&bp, node->calls_comdat_local, 1); bp_pack_value (&bp, node->icf_merged, 1); + bp_pack_value (&bp, node->nonfreeing_fn, 1); bp_pack_value (&bp, node->thunk.thunk_p && !boundary_p, 1); bp_pack_enum (&bp, ld_plugin_symbol_resolution, LDPR_NUM_KNOWN, node->resolution); @@ -1168,6 +1169,7 @@ input_overwrite_node (struct lto_file_decl_data *file_data, node->tm_clone = bp_unpack_value (bp, 1); node->calls_comdat_local = bp_unpack_value (bp, 1); node->icf_merged = bp_unpack_value (bp, 1); + node->nonfreeing_fn = bp_unpack_value (bp, 1); node->thunk.thunk_p = bp_unpack_value (bp, 1); node->resolution = bp_unpack_enum (bp, ld_plugin_symbol_resolution, LDPR_NUM_KNOWN); -- 2.30.2