Extend ipa-pure-const pass to propagate malloc attribute.
2017-10-27 Prathamesh Kulkarni <prathamesh.kulkarni@linaro.org> * cgraph.h (set_malloc_flag): Declare. * cgraph.c (set_malloc_flag_1): New function. (set_malloc_flag): Likewise. * ipa-fnsummary.h (ipa_call_summary): Add new field is_return_callee. * ipa-fnsummary.c (ipa_call_summary::reset): Set is_return_callee to false. (read_ipa_call_summary): Add support for reading is_return_callee. (write_ipa_call_summary): Stream is_return_callee. * ipa-inline.c (ipa_inline): Remove call to ipa_free_fn_summary. * ipa-pure-const.c: Add headers ssa.h, alloc-pool.h, symbol-summary.h, ipa-prop.h, ipa-fnsummary.h. (pure_const_names): Change to static. (malloc_state_e): Define. (malloc_state_names): Define. (funct_state_d): Add field malloc_state. (varying_state): Set malloc_state to STATE_MALLOC_BOTTOM. (check_retval_uses): New function. (malloc_candidate_p): Likewise. (analyze_function): Add support for malloc attribute. (pure_const_write_summary): Stream malloc_state. (pure_const_read_summary): Add support for reading malloc_state. (dump_malloc_lattice): New function. (propagate_malloc): New function. (warn_function_malloc): New function. (ipa_pure_const::execute): Call propagate_malloc and ipa_free_fn_summary. (pass_local_pure_const::execute): Add support for malloc attribute. * ssa-iterators.h (RETURN_FROM_IMM_USE_STMT): New macro. * doc/invoke.texi: Document Wsuggest-attribute=malloc. testsuite/ * gcc.dg/ipa/propmalloc-1.c: New test-case. * gcc.dg/ipa/propmalloc-2.c: Likewise. * gcc.dg/ipa/propmalloc-3.c: Likewise. From-SVN: r254140
This commit is contained in:
parent
e89b556baf
commit
0fab169b28
@ -1,3 +1,35 @@
|
||||
2017-10-27 Prathamesh Kulkarni <prathamesh.kulkarni@linaro.org>
|
||||
|
||||
* cgraph.h (set_malloc_flag): Declare.
|
||||
* cgraph.c (set_malloc_flag_1): New function.
|
||||
(set_malloc_flag): Likewise.
|
||||
* ipa-fnsummary.h (ipa_call_summary): Add new field is_return_callee.
|
||||
* ipa-fnsummary.c (ipa_call_summary::reset): Set is_return_callee to
|
||||
false.
|
||||
(read_ipa_call_summary): Add support for reading is_return_callee.
|
||||
(write_ipa_call_summary): Stream is_return_callee.
|
||||
* ipa-inline.c (ipa_inline): Remove call to ipa_free_fn_summary.
|
||||
* ipa-pure-const.c: Add headers ssa.h, alloc-pool.h, symbol-summary.h,
|
||||
ipa-prop.h, ipa-fnsummary.h.
|
||||
(pure_const_names): Change to static.
|
||||
(malloc_state_e): Define.
|
||||
(malloc_state_names): Define.
|
||||
(funct_state_d): Add field malloc_state.
|
||||
(varying_state): Set malloc_state to STATE_MALLOC_BOTTOM.
|
||||
(check_retval_uses): New function.
|
||||
(malloc_candidate_p): Likewise.
|
||||
(analyze_function): Add support for malloc attribute.
|
||||
(pure_const_write_summary): Stream malloc_state.
|
||||
(pure_const_read_summary): Add support for reading malloc_state.
|
||||
(dump_malloc_lattice): New function.
|
||||
(propagate_malloc): New function.
|
||||
(warn_function_malloc): New function.
|
||||
(ipa_pure_const::execute): Call propagate_malloc and
|
||||
ipa_free_fn_summary.
|
||||
(pass_local_pure_const::execute): Add support for malloc attribute.
|
||||
* ssa-iterators.h (RETURN_FROM_IMM_USE_STMT): New macro.
|
||||
* doc/invoke.texi: Document Wsuggest-attribute=malloc.
|
||||
|
||||
2017-10-27 Martin Liska <mliska@suse.cz>
|
||||
|
||||
PR gcov-profile/82457
|
||||
|
47
gcc/cgraph.c
47
gcc/cgraph.c
@ -2530,6 +2530,53 @@ cgraph_node::set_nothrow_flag (bool nothrow)
|
||||
return changed;
|
||||
}
|
||||
|
||||
/* Worker to set malloc flag. */
|
||||
static void
|
||||
set_malloc_flag_1 (cgraph_node *node, bool malloc_p, bool *changed)
|
||||
{
|
||||
if (malloc_p && !DECL_IS_MALLOC (node->decl))
|
||||
{
|
||||
DECL_IS_MALLOC (node->decl) = true;
|
||||
*changed = true;
|
||||
}
|
||||
|
||||
ipa_ref *ref;
|
||||
FOR_EACH_ALIAS (node, ref)
|
||||
{
|
||||
cgraph_node *alias = dyn_cast<cgraph_node *> (ref->referring);
|
||||
if (!malloc_p || alias->get_availability () > AVAIL_INTERPOSABLE)
|
||||
set_malloc_flag_1 (alias, malloc_p, changed);
|
||||
}
|
||||
|
||||
for (cgraph_edge *e = node->callers; e; e = e->next_caller)
|
||||
if (e->caller->thunk.thunk_p
|
||||
&& (!malloc_p || e->caller->get_availability () > AVAIL_INTERPOSABLE))
|
||||
set_malloc_flag_1 (e->caller, malloc_p, changed);
|
||||
}
|
||||
|
||||
/* Set DECL_IS_MALLOC on NODE's decl and on NODE's aliases if any. */
|
||||
|
||||
bool
|
||||
cgraph_node::set_malloc_flag (bool malloc_p)
|
||||
{
|
||||
bool changed = false;
|
||||
|
||||
if (!malloc_p || get_availability () > AVAIL_INTERPOSABLE)
|
||||
set_malloc_flag_1 (this, malloc_p, &changed);
|
||||
else
|
||||
{
|
||||
ipa_ref *ref;
|
||||
|
||||
FOR_EACH_ALIAS (this, ref)
|
||||
{
|
||||
cgraph_node *alias = dyn_cast<cgraph_node *> (ref->referring);
|
||||
if (!malloc_p || alias->get_availability () > AVAIL_INTERPOSABLE)
|
||||
set_malloc_flag_1 (alias, malloc_p, &changed);
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
/* Worker to set_const_flag. */
|
||||
|
||||
static void
|
||||
|
@ -1151,6 +1151,10 @@ public:
|
||||
if any to NOTHROW. */
|
||||
bool set_nothrow_flag (bool nothrow);
|
||||
|
||||
/* SET DECL_IS_MALLOC on cgraph_node's decl and on aliases of the node
|
||||
if any. */
|
||||
bool set_malloc_flag (bool malloc_p);
|
||||
|
||||
/* If SET_CONST is true, mark function, aliases and thunks to be ECF_CONST.
|
||||
If SET_CONST if false, clear the flag.
|
||||
|
||||
|
@ -741,6 +741,10 @@ Wsuggest-attribute=noreturn
|
||||
Common Var(warn_suggest_attribute_noreturn) Warning
|
||||
Warn about functions which might be candidates for __attribute__((noreturn)).
|
||||
|
||||
Wsuggest-attribute=malloc
|
||||
Common Var(warn_suggest_attribute_malloc) Warning
|
||||
Warn about functions which might be candidates for __attribute__((malloc)).
|
||||
|
||||
Wsuggest-final-types
|
||||
Common Var(warn_suggest_final_types) Warning
|
||||
Warn about C++ polymorphic types where adding final keyword would improve code quality.
|
||||
|
@ -315,7 +315,7 @@ Objective-C and Objective-C++ Dialects}.
|
||||
-Wstack-protector -Wstack-usage=@var{len} -Wstrict-aliasing @gol
|
||||
-Wstrict-aliasing=n -Wstrict-overflow -Wstrict-overflow=@var{n} @gol
|
||||
-Wstringop-overflow=@var{n} @gol
|
||||
-Wsuggest-attribute=@r{[}pure@r{|}const@r{|}noreturn@r{|}format@r{]} @gol
|
||||
-Wsuggest-attribute=@r{[}pure@r{|}const@r{|}noreturn@r{|}format@r{|}malloc@r{]} @gol
|
||||
-Wsuggest-final-types @gol -Wsuggest-final-methods -Wsuggest-override @gol
|
||||
-Wmissing-format-attribute -Wsubobject-linkage @gol
|
||||
-Wswitch -Wswitch-bool -Wswitch-default -Wswitch-enum @gol
|
||||
@ -5204,7 +5204,7 @@ whether to issue a warning. Similarly to @option{-Wstringop-overflow=3} this
|
||||
setting of the option may result in warnings for benign code.
|
||||
@end table
|
||||
|
||||
@item -Wsuggest-attribute=@r{[}pure@r{|}const@r{|}noreturn@r{|}format@r{|}cold@r{]}
|
||||
@item -Wsuggest-attribute=@r{[}pure@r{|}const@r{|}noreturn@r{|}format@r{|}cold@r{|}malloc@r{]}
|
||||
@opindex Wsuggest-attribute=
|
||||
@opindex Wno-suggest-attribute=
|
||||
Warn for cases where adding an attribute may be beneficial. The
|
||||
@ -5214,21 +5214,25 @@ attributes currently supported are listed below.
|
||||
@item -Wsuggest-attribute=pure
|
||||
@itemx -Wsuggest-attribute=const
|
||||
@itemx -Wsuggest-attribute=noreturn
|
||||
@itemx -Wsuggest-attribute=malloc
|
||||
@opindex Wsuggest-attribute=pure
|
||||
@opindex Wno-suggest-attribute=pure
|
||||
@opindex Wsuggest-attribute=const
|
||||
@opindex Wno-suggest-attribute=const
|
||||
@opindex Wsuggest-attribute=noreturn
|
||||
@opindex Wno-suggest-attribute=noreturn
|
||||
@opindex Wsuggest-attribute=malloc
|
||||
@opindex Wno-suggest-attribute=malloc
|
||||
|
||||
Warn about functions that might be candidates for attributes
|
||||
@code{pure}, @code{const} or @code{noreturn}. The compiler only warns for
|
||||
functions visible in other compilation units or (in the case of @code{pure} and
|
||||
@code{const}) if it cannot prove that the function returns normally. A function
|
||||
returns normally if it doesn't contain an infinite loop or return abnormally
|
||||
by throwing, calling @code{abort} or trapping. This analysis requires option
|
||||
@option{-fipa-pure-const}, which is enabled by default at @option{-O} and
|
||||
higher. Higher optimization levels improve the accuracy of the analysis.
|
||||
@code{pure}, @code{const} or @code{noreturn} or @code{malloc}. The compiler
|
||||
only warns for functions visible in other compilation units or (in the case of
|
||||
@code{pure} and @code{const}) if it cannot prove that the function returns
|
||||
normally. A function returns normally if it doesn't contain an infinite loop or
|
||||
return abnormally by throwing, calling @code{abort} or trapping. This analysis
|
||||
requires option @option{-fipa-pure-const}, which is enabled by default at
|
||||
@option{-O} and higher. Higher optimization levels improve the accuracy
|
||||
of the analysis.
|
||||
|
||||
@item -Wsuggest-attribute=format
|
||||
@itemx -Wmissing-format-attribute
|
||||
|
@ -542,6 +542,7 @@ void
|
||||
ipa_call_summary::reset ()
|
||||
{
|
||||
call_stmt_size = call_stmt_time = 0;
|
||||
is_return_callee_uncaptured = false;
|
||||
if (predicate)
|
||||
edge_predicate_pool.remove (predicate);
|
||||
predicate = NULL;
|
||||
@ -3204,6 +3205,10 @@ read_ipa_call_summary (struct lto_input_block *ib, struct cgraph_edge *e)
|
||||
es->call_stmt_size = streamer_read_uhwi (ib);
|
||||
es->call_stmt_time = streamer_read_uhwi (ib);
|
||||
es->loop_depth = streamer_read_uhwi (ib);
|
||||
|
||||
bitpack_d bp = streamer_read_bitpack (ib);
|
||||
es->is_return_callee_uncaptured = bp_unpack_value (&bp, 1);
|
||||
|
||||
p.stream_in (ib);
|
||||
edge_set_predicate (e, &p);
|
||||
length = streamer_read_uhwi (ib);
|
||||
@ -3360,6 +3365,11 @@ write_ipa_call_summary (struct output_block *ob, struct cgraph_edge *e)
|
||||
streamer_write_uhwi (ob, es->call_stmt_size);
|
||||
streamer_write_uhwi (ob, es->call_stmt_time);
|
||||
streamer_write_uhwi (ob, es->loop_depth);
|
||||
|
||||
bitpack_d bp = bitpack_create (ob->main_stream);
|
||||
bp_pack_value (&bp, es->is_return_callee_uncaptured, 1);
|
||||
streamer_write_bitpack (&bp);
|
||||
|
||||
if (es->predicate)
|
||||
es->predicate->stream_out (ob);
|
||||
else
|
||||
|
@ -197,7 +197,9 @@ struct ipa_call_summary
|
||||
int call_stmt_time;
|
||||
/* Depth of loop nest, 0 means no nesting. */
|
||||
unsigned int loop_depth;
|
||||
|
||||
/* Indicates whether the caller returns the value of it's callee. */
|
||||
bool is_return_callee_uncaptured;
|
||||
|
||||
/* Keep all field empty so summary dumping works during its computation.
|
||||
This is useful for debugging. */
|
||||
ipa_call_summary ()
|
||||
|
@ -2526,9 +2526,6 @@ ipa_inline (void)
|
||||
|
||||
if (dump_file)
|
||||
ipa_dump_fn_summaries (dump_file);
|
||||
/* In WPA we use inline summaries for partitioning process. */
|
||||
if (!flag_wpa)
|
||||
ipa_free_fn_summary ();
|
||||
return remove_functions ? TODO_remove_functions : 0;
|
||||
}
|
||||
|
||||
|
@ -56,6 +56,11 @@ along with GCC; see the file COPYING3. If not see
|
||||
#include "tree-scalar-evolution.h"
|
||||
#include "intl.h"
|
||||
#include "opts.h"
|
||||
#include "ssa.h"
|
||||
#include "alloc-pool.h"
|
||||
#include "symbol-summary.h"
|
||||
#include "ipa-prop.h"
|
||||
#include "ipa-fnsummary.h"
|
||||
|
||||
/* Lattice values for const and pure functions. Everything starts out
|
||||
being const, then may drop to pure and then neither depending on
|
||||
@ -67,7 +72,16 @@ enum pure_const_state_e
|
||||
IPA_NEITHER
|
||||
};
|
||||
|
||||
const char *pure_const_names[3] = {"const", "pure", "neither"};
|
||||
static const char *pure_const_names[3] = {"const", "pure", "neither"};
|
||||
|
||||
enum malloc_state_e
|
||||
{
|
||||
STATE_MALLOC_TOP,
|
||||
STATE_MALLOC,
|
||||
STATE_MALLOC_BOTTOM
|
||||
};
|
||||
|
||||
static const char *malloc_state_names[] = {"malloc_top", "malloc", "malloc_bottom"};
|
||||
|
||||
/* Holder for the const_state. There is one of these per function
|
||||
decl. */
|
||||
@ -92,11 +106,13 @@ struct funct_state_d
|
||||
/* If function can call free, munmap or otherwise make previously
|
||||
non-trapping memory accesses trapping. */
|
||||
bool can_free;
|
||||
|
||||
enum malloc_state_e malloc_state;
|
||||
};
|
||||
|
||||
/* State used when we know nothing about function. */
|
||||
static struct funct_state_d varying_state
|
||||
= { IPA_NEITHER, IPA_NEITHER, true, true, true, true };
|
||||
= { IPA_NEITHER, IPA_NEITHER, true, true, true, true, STATE_MALLOC_BOTTOM };
|
||||
|
||||
|
||||
typedef struct funct_state_d * funct_state;
|
||||
@ -216,6 +232,19 @@ warn_function_const (tree decl, bool known_finite)
|
||||
known_finite, warned_about, "const");
|
||||
}
|
||||
|
||||
/* Emit suggestion about __attribute__((malloc)) for DECL. */
|
||||
|
||||
static void
|
||||
warn_function_malloc (tree decl)
|
||||
{
|
||||
static hash_set<tree> *warned_about;
|
||||
warned_about
|
||||
= suggest_attribute (OPT_Wsuggest_attribute_malloc, decl,
|
||||
false, warned_about, "malloc");
|
||||
}
|
||||
|
||||
/* Emit suggestion about __attribute__((noreturn)) for DECL. */
|
||||
|
||||
static void
|
||||
warn_function_noreturn (tree decl)
|
||||
{
|
||||
@ -827,6 +856,149 @@ check_stmt (gimple_stmt_iterator *gsip, funct_state local, bool ipa)
|
||||
}
|
||||
}
|
||||
|
||||
/* Check that RETVAL is used only in STMT and in comparisons against 0.
|
||||
RETVAL is return value of the function and STMT is return stmt. */
|
||||
|
||||
static bool
|
||||
check_retval_uses (tree retval, gimple *stmt)
|
||||
{
|
||||
imm_use_iterator use_iter;
|
||||
gimple *use_stmt;
|
||||
|
||||
FOR_EACH_IMM_USE_STMT (use_stmt, use_iter, retval)
|
||||
if (gcond *cond = dyn_cast<gcond *> (use_stmt))
|
||||
{
|
||||
tree op2 = gimple_cond_rhs (cond);
|
||||
if (!integer_zerop (op2))
|
||||
RETURN_FROM_IMM_USE_STMT (use_iter, false);
|
||||
}
|
||||
else if (gassign *ga = dyn_cast<gassign *> (use_stmt))
|
||||
{
|
||||
enum tree_code code = gimple_assign_rhs_code (ga);
|
||||
if (TREE_CODE_CLASS (code) != tcc_comparison)
|
||||
RETURN_FROM_IMM_USE_STMT (use_iter, false);
|
||||
if (!integer_zerop (gimple_assign_rhs2 (ga)))
|
||||
RETURN_FROM_IMM_USE_STMT (use_iter, false);
|
||||
}
|
||||
else if (is_gimple_debug (use_stmt))
|
||||
;
|
||||
else if (use_stmt != stmt)
|
||||
RETURN_FROM_IMM_USE_STMT (use_iter, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* malloc_candidate_p() checks if FUN can possibly be annotated with malloc
|
||||
attribute. Currently this function does a very conservative analysis.
|
||||
FUN is considered to be a candidate if
|
||||
1) It returns a value of pointer type.
|
||||
2) SSA_NAME_DEF_STMT (return_value) is either a function call or
|
||||
a phi, and element of phi is either NULL or
|
||||
SSA_NAME_DEF_STMT(element) is function call.
|
||||
3) The return-value has immediate uses only within comparisons (gcond or gassign)
|
||||
and return_stmt (and likewise a phi arg has immediate use only within comparison
|
||||
or the phi stmt). */
|
||||
|
||||
static bool
|
||||
malloc_candidate_p (function *fun, bool ipa)
|
||||
{
|
||||
basic_block exit_block = EXIT_BLOCK_PTR_FOR_FN (fun);
|
||||
edge e;
|
||||
edge_iterator ei;
|
||||
cgraph_node *node = cgraph_node::get_create (fun->decl);
|
||||
|
||||
#define DUMP_AND_RETURN(reason) \
|
||||
{ \
|
||||
if (dump_file && (dump_flags & TDF_DETAILS)) \
|
||||
fprintf (dump_file, "%s", (reason)); \
|
||||
return false; \
|
||||
}
|
||||
|
||||
if (EDGE_COUNT (exit_block->preds) == 0)
|
||||
return false;
|
||||
|
||||
FOR_EACH_EDGE (e, ei, exit_block->preds)
|
||||
{
|
||||
gimple_stmt_iterator gsi = gsi_last_bb (e->src);
|
||||
greturn *ret_stmt = dyn_cast<greturn *> (gsi_stmt (gsi));
|
||||
|
||||
if (!ret_stmt)
|
||||
return false;
|
||||
|
||||
tree retval = gimple_return_retval (ret_stmt);
|
||||
if (!retval)
|
||||
DUMP_AND_RETURN("No return value.")
|
||||
|
||||
if (TREE_CODE (retval) != SSA_NAME
|
||||
|| TREE_CODE (TREE_TYPE (retval)) != POINTER_TYPE)
|
||||
DUMP_AND_RETURN("Return value is not SSA_NAME or not a pointer type.")
|
||||
|
||||
if (!check_retval_uses (retval, ret_stmt))
|
||||
DUMP_AND_RETURN("Return value has uses outside return stmt"
|
||||
" and comparisons against 0.")
|
||||
|
||||
gimple *def = SSA_NAME_DEF_STMT (retval);
|
||||
if (gcall *call_stmt = dyn_cast<gcall *> (def))
|
||||
{
|
||||
tree callee_decl = gimple_call_fndecl (call_stmt);
|
||||
if (!callee_decl)
|
||||
return false;
|
||||
|
||||
if (!ipa && !DECL_IS_MALLOC (callee_decl))
|
||||
DUMP_AND_RETURN("callee_decl does not have malloc attribute for"
|
||||
" non-ipa mode.")
|
||||
|
||||
cgraph_edge *cs = node->get_edge (call_stmt);
|
||||
if (cs)
|
||||
{
|
||||
ipa_call_summary *es = ipa_call_summaries->get (cs);
|
||||
gcc_assert (es);
|
||||
es->is_return_callee_uncaptured = true;
|
||||
}
|
||||
}
|
||||
|
||||
else if (gphi *phi = dyn_cast<gphi *> (def))
|
||||
for (unsigned i = 0; i < gimple_phi_num_args (phi); ++i)
|
||||
{
|
||||
tree arg = gimple_phi_arg_def (phi, i);
|
||||
if (TREE_CODE (arg) != SSA_NAME)
|
||||
DUMP_AND_RETURN("phi arg is not SSA_NAME.")
|
||||
if (!(arg == null_pointer_node || check_retval_uses (arg, phi)))
|
||||
DUMP_AND_RETURN("phi arg has uses outside phi"
|
||||
" and comparisons against 0.")
|
||||
|
||||
gimple *arg_def = SSA_NAME_DEF_STMT (arg);
|
||||
gcall *call_stmt = dyn_cast<gcall *> (arg_def);
|
||||
if (!call_stmt)
|
||||
return false;
|
||||
tree callee_decl = gimple_call_fndecl (call_stmt);
|
||||
if (!callee_decl)
|
||||
return false;
|
||||
if (!ipa && !DECL_IS_MALLOC (callee_decl))
|
||||
DUMP_AND_RETURN("callee_decl does not have malloc attribute for"
|
||||
" non-ipa mode.")
|
||||
|
||||
cgraph_edge *cs = node->get_edge (call_stmt);
|
||||
if (cs)
|
||||
{
|
||||
ipa_call_summary *es = ipa_call_summaries->get (cs);
|
||||
gcc_assert (es);
|
||||
es->is_return_callee_uncaptured = true;
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
DUMP_AND_RETURN("def_stmt of return value is not a call or phi-stmt.")
|
||||
}
|
||||
|
||||
if (dump_file && (dump_flags & TDF_DETAILS))
|
||||
fprintf (dump_file, "\nFound %s to be candidate for malloc attribute\n",
|
||||
IDENTIFIER_POINTER (DECL_NAME (fun->decl)));
|
||||
return true;
|
||||
|
||||
#undef DUMP_AND_RETURN
|
||||
}
|
||||
|
||||
|
||||
/* This is the main routine for finding the reference patterns for
|
||||
global variables within a function FN. */
|
||||
@ -936,6 +1108,14 @@ end:
|
||||
if (TREE_NOTHROW (decl))
|
||||
l->can_throw = false;
|
||||
|
||||
l->malloc_state = STATE_MALLOC_BOTTOM;
|
||||
if (DECL_IS_MALLOC (decl))
|
||||
l->malloc_state = STATE_MALLOC;
|
||||
else if (ipa && malloc_candidate_p (DECL_STRUCT_FUNCTION (decl), true))
|
||||
l->malloc_state = STATE_MALLOC_TOP;
|
||||
else if (malloc_candidate_p (DECL_STRUCT_FUNCTION (decl), false))
|
||||
l->malloc_state = STATE_MALLOC;
|
||||
|
||||
pop_cfun ();
|
||||
if (dump_file)
|
||||
{
|
||||
@ -949,6 +1129,8 @@ end:
|
||||
fprintf (dump_file, "Function is locally pure.\n");
|
||||
if (l->can_free)
|
||||
fprintf (dump_file, "Function can locally free.\n");
|
||||
if (l->malloc_state == STATE_MALLOC)
|
||||
fprintf (dump_file, "Function is locally malloc.\n");
|
||||
}
|
||||
return l;
|
||||
}
|
||||
@ -1082,6 +1264,7 @@ pure_const_write_summary (void)
|
||||
bp_pack_value (&bp, fs->looping, 1);
|
||||
bp_pack_value (&bp, fs->can_throw, 1);
|
||||
bp_pack_value (&bp, fs->can_free, 1);
|
||||
bp_pack_value (&bp, fs->malloc_state, 2);
|
||||
streamer_write_bitpack (&bp);
|
||||
}
|
||||
}
|
||||
@ -1142,6 +1325,9 @@ pure_const_read_summary (void)
|
||||
fs->looping = bp_unpack_value (&bp, 1);
|
||||
fs->can_throw = bp_unpack_value (&bp, 1);
|
||||
fs->can_free = bp_unpack_value (&bp, 1);
|
||||
fs->malloc_state
|
||||
= (enum malloc_state_e) bp_unpack_value (&bp, 2);
|
||||
|
||||
if (dump_file)
|
||||
{
|
||||
int flags = flags_from_decl_or_type (node->decl);
|
||||
@ -1164,6 +1350,8 @@ pure_const_read_summary (void)
|
||||
fprintf (dump_file," function is locally throwing\n");
|
||||
if (fs->can_free)
|
||||
fprintf (dump_file," function can locally free\n");
|
||||
fprintf (dump_file, "\n malloc state: %s\n",
|
||||
malloc_state_names[fs->malloc_state]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1674,6 +1862,131 @@ propagate_nothrow (void)
|
||||
free (order);
|
||||
}
|
||||
|
||||
/* Debugging function to dump state of malloc lattice. */
|
||||
|
||||
DEBUG_FUNCTION
|
||||
static void
|
||||
dump_malloc_lattice (FILE *dump_file, const char *s)
|
||||
{
|
||||
if (!dump_file)
|
||||
return;
|
||||
|
||||
fprintf (dump_file, "\n\nMALLOC LATTICE %s:\n", s);
|
||||
cgraph_node *node;
|
||||
FOR_EACH_FUNCTION (node)
|
||||
{
|
||||
funct_state fs = get_function_state (node);
|
||||
malloc_state_e state = fs->malloc_state;
|
||||
fprintf (dump_file, "%s: %s\n", node->name (), malloc_state_names[state]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Propagate malloc attribute across the callgraph. */
|
||||
|
||||
static void
|
||||
propagate_malloc (void)
|
||||
{
|
||||
cgraph_node *node;
|
||||
FOR_EACH_FUNCTION (node)
|
||||
{
|
||||
if (DECL_IS_MALLOC (node->decl))
|
||||
if (!has_function_state (node))
|
||||
{
|
||||
funct_state l = XCNEW (struct funct_state_d);
|
||||
*l = varying_state;
|
||||
l->malloc_state = STATE_MALLOC;
|
||||
set_function_state (node, l);
|
||||
}
|
||||
}
|
||||
|
||||
dump_malloc_lattice (dump_file, "Initial");
|
||||
struct cgraph_node **order
|
||||
= XNEWVEC (struct cgraph_node *, symtab->cgraph_count);
|
||||
int order_pos = ipa_reverse_postorder (order);
|
||||
bool changed = true;
|
||||
|
||||
while (changed)
|
||||
{
|
||||
changed = false;
|
||||
/* Walk in postorder. */
|
||||
for (int i = order_pos - 1; i >= 0; --i)
|
||||
{
|
||||
cgraph_node *node = order[i];
|
||||
if (node->alias
|
||||
|| !node->definition
|
||||
|| !has_function_state (node))
|
||||
continue;
|
||||
|
||||
funct_state l = get_function_state (node);
|
||||
|
||||
/* FIXME: add support for indirect-calls. */
|
||||
if (node->indirect_calls)
|
||||
{
|
||||
l->malloc_state = STATE_MALLOC_BOTTOM;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (node->get_availability () <= AVAIL_INTERPOSABLE)
|
||||
{
|
||||
l->malloc_state = STATE_MALLOC_BOTTOM;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (l->malloc_state == STATE_MALLOC_BOTTOM)
|
||||
continue;
|
||||
|
||||
vec<cgraph_node *> callees = vNULL;
|
||||
for (cgraph_edge *cs = node->callees; cs; cs = cs->next_callee)
|
||||
{
|
||||
ipa_call_summary *es = ipa_call_summaries->get (cs);
|
||||
if (es && es->is_return_callee_uncaptured)
|
||||
callees.safe_push (cs->callee);
|
||||
}
|
||||
|
||||
malloc_state_e new_state = l->malloc_state;
|
||||
for (unsigned j = 0; j < callees.length (); j++)
|
||||
{
|
||||
cgraph_node *callee = callees[j];
|
||||
if (!has_function_state (callee))
|
||||
{
|
||||
new_state = STATE_MALLOC_BOTTOM;
|
||||
break;
|
||||
}
|
||||
malloc_state_e callee_state = get_function_state (callee)->malloc_state;
|
||||
if (new_state < callee_state)
|
||||
new_state = callee_state;
|
||||
}
|
||||
if (new_state != l->malloc_state)
|
||||
{
|
||||
changed = true;
|
||||
l->malloc_state = new_state;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FOR_EACH_DEFINED_FUNCTION (node)
|
||||
if (has_function_state (node))
|
||||
{
|
||||
funct_state l = get_function_state (node);
|
||||
if (!node->alias
|
||||
&& l->malloc_state == STATE_MALLOC
|
||||
&& !node->global.inlined_to)
|
||||
{
|
||||
if (dump_file && (dump_flags & TDF_DETAILS))
|
||||
fprintf (dump_file, "Function %s found to be malloc\n",
|
||||
node->name ());
|
||||
|
||||
bool malloc_decl_p = DECL_IS_MALLOC (node->decl);
|
||||
node->set_malloc_flag (true);
|
||||
if (!malloc_decl_p && warn_suggest_attribute_malloc)
|
||||
warn_function_malloc (node->decl);
|
||||
}
|
||||
}
|
||||
|
||||
dump_malloc_lattice (dump_file, "after propagation");
|
||||
ipa_free_postorder_info ();
|
||||
free (order);
|
||||
}
|
||||
|
||||
/* Produce the global information by preforming a transitive closure
|
||||
on the local information that was produced by generate_summary. */
|
||||
@ -1692,6 +2005,7 @@ execute (function *)
|
||||
/* Nothrow makes more function to not lead to return and improve
|
||||
later analysis. */
|
||||
propagate_nothrow ();
|
||||
propagate_malloc ();
|
||||
remove_p = propagate_pure_const ();
|
||||
|
||||
/* Cleanup. */
|
||||
@ -1699,6 +2013,10 @@ execute (function *)
|
||||
if (has_function_state (node))
|
||||
free (get_function_state (node));
|
||||
funct_state_vec.release ();
|
||||
|
||||
/* In WPA we use inline summaries for partitioning process. */
|
||||
if (!flag_wpa)
|
||||
ipa_free_fn_summary ();
|
||||
return remove_p ? TODO_remove_functions : 0;
|
||||
}
|
||||
|
||||
@ -1893,6 +2211,19 @@ pass_local_pure_const::execute (function *fun)
|
||||
fprintf (dump_file, "Function found to be nothrow: %s\n",
|
||||
current_function_name ());
|
||||
}
|
||||
|
||||
if (l->malloc_state == STATE_MALLOC
|
||||
&& !DECL_IS_MALLOC (current_function_decl))
|
||||
{
|
||||
node->set_malloc_flag (true);
|
||||
if (warn_suggest_attribute_malloc)
|
||||
warn_function_malloc (node->decl);
|
||||
changed = true;
|
||||
if (dump_file)
|
||||
fprintf (dump_file, "Function found to be malloc: %s\n",
|
||||
node->name ());
|
||||
}
|
||||
|
||||
free (l);
|
||||
if (changed)
|
||||
return execute_fixup_cfg ();
|
||||
|
@ -93,6 +93,12 @@ struct imm_use_iterator
|
||||
break; \
|
||||
}
|
||||
|
||||
/* Similarly for return. */
|
||||
#define RETURN_FROM_IMM_USE_STMT(ITER, VAL) \
|
||||
{ \
|
||||
end_imm_use_stmt_traverse (&(ITER)); \
|
||||
return (VAL); \
|
||||
}
|
||||
|
||||
/* Use this iterator in combination with FOR_EACH_IMM_USE_STMT to
|
||||
get access to each occurrence of ssavar on the stmt returned by
|
||||
|
@ -1,3 +1,9 @@
|
||||
2017-10-27 Prathamesh Kulkarni <prathamesh.kulkarni@linaro.org>
|
||||
|
||||
* gcc.dg/ipa/propmalloc-1.c: New test-case.
|
||||
* gcc.dg/ipa/propmalloc-2.c: Likewise.
|
||||
* gcc.dg/ipa/propmalloc-3.c: Likewise.
|
||||
|
||||
2017-10-27 Paolo Carlini <paolo.carlini@oracle.com>
|
||||
|
||||
PR c++/71385
|
||||
|
21
gcc/testsuite/gcc.dg/ipa/propmalloc-1.c
Normal file
21
gcc/testsuite/gcc.dg/ipa/propmalloc-1.c
Normal file
@ -0,0 +1,21 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-O2 -fdump-ipa-pure-const-details" } */
|
||||
|
||||
__attribute__((noinline, no_icf, used))
|
||||
static void *f(__SIZE_TYPE__ n)
|
||||
{
|
||||
void *p = __builtin_malloc (n);
|
||||
if (p == 0)
|
||||
__builtin_abort ();
|
||||
return p;
|
||||
}
|
||||
|
||||
__attribute__((noinline, no_icf, used))
|
||||
static void *bar(__SIZE_TYPE__ n)
|
||||
{
|
||||
void *p = f (n);
|
||||
return p;
|
||||
}
|
||||
|
||||
/* { dg-final { scan-ipa-dump "Function f found to be malloc" "pure-const" } } */
|
||||
/* { dg-final { scan-ipa-dump "Function bar found to be malloc" "pure-const" } } */
|
23
gcc/testsuite/gcc.dg/ipa/propmalloc-2.c
Normal file
23
gcc/testsuite/gcc.dg/ipa/propmalloc-2.c
Normal file
@ -0,0 +1,23 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-O2 -fdump-ipa-pure-const-details" } */
|
||||
|
||||
__attribute__((noinline, used, no_icf))
|
||||
static void *foo (__SIZE_TYPE__ n)
|
||||
{
|
||||
return __builtin_malloc (n * 10);
|
||||
}
|
||||
|
||||
__attribute__((noinline, used, no_icf))
|
||||
static void *bar(__SIZE_TYPE__ n, int cond)
|
||||
{
|
||||
void *p;
|
||||
if (cond)
|
||||
p = foo (n);
|
||||
else
|
||||
p = __builtin_malloc (n);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
/* { dg-final { scan-ipa-dump "Function foo found to be malloc" "pure-const" } } */
|
||||
/* { dg-final { scan-ipa-dump "Function bar found to be malloc" "pure-const" } } */
|
24
gcc/testsuite/gcc.dg/ipa/propmalloc-3.c
Normal file
24
gcc/testsuite/gcc.dg/ipa/propmalloc-3.c
Normal file
@ -0,0 +1,24 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-O2 -fdump-ipa-pure-const-details" } */
|
||||
|
||||
static void *foo(__SIZE_TYPE__, int) __attribute__((noinline, no_icf, used));
|
||||
|
||||
__attribute__((noinline, used, no_icf))
|
||||
static void *bar(__SIZE_TYPE__ n, int m)
|
||||
{
|
||||
return foo (n, m);
|
||||
}
|
||||
|
||||
static void *foo(__SIZE_TYPE__ n, int m)
|
||||
{
|
||||
void *p;
|
||||
if (m > 0)
|
||||
p = bar (n, --m);
|
||||
else
|
||||
p = __builtin_malloc (n);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
/* { dg-final { scan-ipa-dump "Function foo found to be malloc" "pure-const" } } */
|
||||
/* { dg-final { scan-ipa-dump "Function bar found to be malloc" "pure-const" } } */
|
Loading…
Reference in New Issue
Block a user