On Thu, Nov 13, 2014 at 9:39 AM, Jakub Jelinek <ja...@redhat.com> wrote: > Hi! > > On Thu, Nov 13, 2014 at 12:11:08AM +0100, Jan Hubicka wrote: > >> Actually I think you want to do this for can_throw, too. >> We probably do not have throwing internal calls, but it is better to be safe. > > I'll leave that change to you ;), as I said in my last mail, it isn't > immediately clear to me what exactly you want to do with internal calls > for can_throw (if anything) and for pure_const/looping. > E.g. for pure_const, internal calls can be memory accesses (e.g. > MASK_LOAD/MASK_STORE/LOAD_LANES/STORE_LANES, supposedly even ASAN_CHECK > can be considered a memory access). Though, the first 4 should ATM > appear only post-IPA, and ASAN_CHECK should just accompany some normal > memory access. > >> The propagation seems fine, but I wonder if we won't get better memory >> locality doing this >> during the propagation of pure/const? > > Done below, though I chose to use a separate w = node; ... w = > w_info->next_cycle; > loop to compute can_free, because trying to do it in the same loop as > pure_const which can stop early etc. would be too ugly. The update of > can_free and setting of nonfreeing_fn flag is done in the same loop as > storing of pure_const/looping results. > >> You want to walk aliases, >> >> cgraph_node::get (fndecl)->function_symbol (&availability) >> >> > + if (!n || n->get_availability () <= AVAIL_INTERPOSABLE) >> >> and use availability here. > > Done below. > > What about the: >> > I wonder if the nonfreeing_call_p function shouldn't be moved elsewhere >> > though (suggestion where), so that gimple.c doesn't need the cgraph >> > includes. > question though (maybe it is more on Richard)?
Well - you could place it in cgraph.[ch]. Richard. > 2014-11-13 Jakub Jelinek <ja...@redhat.com> > > * 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. > * 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. > > --- gcc/cgraph.c.jj 2014-11-13 00:07:34.271125586 +0100 > +++ gcc/cgraph.c 2014-11-13 09:14:41.217090244 +0100 > @@ -1986,6 +1986,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)) > --- gcc/ipa-pure-const.c.jj 2014-11-13 00:08:37.566011221 +0100 > +++ gcc/ipa-pure-const.c 2014-11-13 09:25:02.933637928 +0100 > @@ -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 ca > 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 ca > 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, > 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, > 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 > 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 > 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) > { > --- gcc/cgraph.h.jj 2014-11-13 00:07:34.278125463 +0100 > +++ gcc/cgraph.h 2014-11-13 09:14:41.216090285 +0100 > @@ -1260,6 +1260,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 > --- gcc/gimple.c.jj 2014-11-13 09:14:28.853595937 +0100 > +++ gcc/gimple.c 2014-11-13 09:16:08.111606042 +0100 > @@ -58,6 +58,9 @@ along with GCC; see the file COPYING3. > #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 > @@ -2542,7 +2545,17 @@ nonfreeing_call_p (gimple call) > && gimple_call_flags (call) & ECF_LEAF) > return true; > > - 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. > --- gcc/lto-cgraph.c.jj 2014-11-13 00:07:34.306124970 +0100 > +++ gcc/lto-cgraph.c 2014-11-13 09:14:41.227089835 +0100 > @@ -549,6 +549,7 @@ lto_output_node (struct lto_simple_outpu > 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); > @@ -1097,6 +1098,7 @@ input_overwrite_node (struct lto_file_de > 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); > > > Jakub