From: Richard Biener <rguenther@suse.de>
To: Prathamesh Kulkarni <prathamesh.kulkarni@linaro.org>
Cc: gcc Patches <gcc-patches@gcc.gnu.org>, Jan Hubicka <hubicka@ucw.cz>
Subject: Re: [RFC] propagate malloc attribute in ipa-pure-const pass
Date: Tue, 16 May 2017 11:48:00 -0000 [thread overview]
Message-ID: <alpine.LSU.2.20.1705161317450.20726@zhemvz.fhfr.qr> (raw)
In-Reply-To: <CAAgBjMkaTCAvwO6AVKu5C6_-TqpuQtTcs0EqYHqogDhru3juYg@mail.gmail.com>
On Mon, 15 May 2017, Prathamesh Kulkarni wrote:
> Hi,
> I have attached a bare-bones prototype patch that propagates malloc attribute in
> ipa-pure-const. As far as I understand, from the doc a function could
> be annotated
> with malloc attribute if it returns a pointer to a newly allocated
> memory block (uninitialized or zeroed) and the pointer does not alias
> any other valid pointer ?
>
> * Analysis
> The analysis used by the patch in malloc_candidate_p is too conservative,
> and I would be grateful for suggestions on improving it.
> Currently it only checks if:
> 1) The function returns a value of pointer type.
> 2) SSA_NAME_DEF_STMT (return_value) is either a function call or a phi, and
> SSA_NAME_DEF_STMT(each element of phi) 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).
>
> The intent is that malloc_candidate_p(node) returns true if node
> returns the return value of it's callee, and the return value is only
> used for comparisons within node.
> Then I assume it's safe (although conservative) to decide that node
> could possibly be a malloc function if callee is found to be malloc ?
>
> for eg:
> void *f(size_t n)
> {
> void *p = __builtin_malloc (n);
> if (p == 0)
> __builtin_abort ();
> return p;
> }
>
> malloc_candidate_p would determine f to be a candidate for malloc
> attribute since it returns the return-value of it's callee
> (__builtin_malloc) and the return value is used only within comparison
> and return_stmt.
>
> However due to the imprecise analysis it misses this case:
> char **g(size_t n)
> {
> char **p = malloc (sizeof(char *) * 10);
> for (int i = 0; i < 10; i++)
> p[i] = malloc (sizeof(char) * n);
> return p;
> }
> I suppose g() could be annotated with malloc here ?
It cannot because what p points to is initialized. If you do then
char **x = g(10);
**x = 1;
will be optimized away. Now what would be interesting is to do
"poor mans IPA PTA", namely identify functions that are a) small,
b) likely return a newly allocated pointer. At PTA time then
"inline" all such wrappers, but only for the PTA constraints.
That will give more precise points-to results (and can handle more
cases, esp. initialization) than just annotating functions with 'malloc'.
+ /* With non-call exceptions we can't say for sure if other function
+ body was not possibly optimized to still throw. */
+ if (!non_call || node->binds_to_current_def_p ())
+ {
+ DECL_IS_MALLOC (node->decl) = true;
+ *changed = true;
I don't see how malloc throwing would be an issue.
+ FOR_EACH_IMM_USE_STMT (use_stmt, use_iter, retval)
+ if (gcond *cond = dyn_cast<gcond *> (use_stmt))
+ {
+ enum tree_code code = gimple_cond_code (cond);
+ if (TREE_CODE_CLASS (code) != tcc_comparison)
always false
+ RETURN_FROM_IMM_USE_STMT (use_iter, false);
I think you want to disallow eq/ne_expr against sth not integer_zerop.
+ 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);
likewise.
+static bool
+malloc_candidate_p (function *fun, vec<cgraph_node *>& callees)
+{
+ basic_block bb;
+ FOR_EACH_BB_FN (bb, fun)
+ {
+ for (gimple_stmt_iterator gsi = gsi_start_bb (bb);
+ !gsi_end_p (gsi);
+ gsi_next (&gsi))
+ {
+ greturn *ret_stmt = dyn_cast<greturn *> (gsi_stmt (gsi));
+ if (ret_stmt)
I think you want do do this much cheaper by only walking the last
stmt of predecessor(s) of EXIT_BLOCK_FOR_FN. (s) because
we have multiple return stmts for
int foo (int i)
{
if (i)
return;
else
return i;
}
but all return stmts will be the last stmt in one of the exit block
predecessors.
+ if (!check_retval_uses (retval, ret_stmt))
+ return false;
+
+ gimple *def = SSA_NAME_DEF_STMT (retval);
+ if (gcall *call_stmt = dyn_cast<gcall *> (def))
+ {
+ tree callee_decl = gimple_call_fndecl (call_stmt);
+ /* FIXME: In case of indirect call, callee_decl might be
NULL
+ Not sure what to do in that case, punting for now.
*/
+ if (!callee_decl)
+ return false;
+ cgraph_node *callee = cgraph_node::get_create
(callee_decl);
+ callees.safe_push (callee);
+ }
+ 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)
+ return false;
+ if (!check_retval_uses (arg, phi))
+ return false;
+
I think you should refactor this to a separate function and handle
recursion. For recursion you miss simple SSA name assigns.
For PHI args you want to allow literal zero as well (for
-fdelete-null-pointer-checks).
> The patch uses return_calles_map which is a hash_map<node, callees> such
> that the return value of node is return value of one of these callees.
> For above eg it would be <f, [__builtin_malloc]>
> The analysis phase populates return_callees_map, and the propagation
> phase uses it to take the "meet" of callees.
I think you are supposed to treat functions optimistically as "malloc"
(use the DECL_IS_MALLOC flag) and during propagation revisit that
change by propagating across callgraph edges. callgraph edges that are
relevant (calls feeding a pointer return value) should then be
marked somehow (set a flag you stream).
This also means that indirect calls should be only handled during
propagation.
Not sure if we have an example of a "edge encoder". Honza?
Bonus points for auto-detecting alloc_size and alloc_align attributes
and warning with -Wsuggest-attribute=malloc{_size,_align,}.
> * LTO and memory management
> This is a general question about LTO and memory management.
> IIUC the following sequence takes place during normal LTO:
> LGEN: generate_summary, write_summary
> WPA: read_summary, execute ipa passes, write_opt_summary
>
> So I assumed it was OK in LGEN to allocate return_callees_map in
> generate_summary and free it in write_summary and during WPA, allocate
> return_callees_map in read_summary and free it after execute (since
> write_opt_summary does not require return_callees_map).
>
> However with fat LTO, it seems the sequence changes for LGEN with
> execute phase takes place after write_summary. However since
> return_callees_map is freed in pure_const_write_summary and
> propagate_malloc() accesses it in execute stage, it results in
> segmentation fault.
>
> To work around this, I am using the following hack in pure_const_write_summary:
> // FIXME: Do not free if -ffat-lto-objects is enabled.
> if (!global_options.x_flag_fat_lto_objects)
> free_return_callees_map ();
> Is there a better approach for handling this ?
>
> Also I am not sure if I have written cgraph_node::set_malloc_flag[_1] correctly.
> I tried to imitate cgraph_node::set_const_flag.
>
> The patch passes bootstrap+test on x86_64 and found a few functions in
> the source tree (attached func_names.txt) that could be annotated with
> malloc (I gave a brief look at some of the functions and didn't appear
> to be false positives but I will recheck thoroughly)
>
> Does the patch look in the right direction ?
> I would be grateful for suggestions on improving it.
>
> Thanks,
> Prathamesh
>
--
Richard Biener <rguenther@suse.de>
SUSE LINUX GmbH, GF: Felix Imendoerffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nuernberg)
next prev parent reply other threads:[~2017-05-16 11:41 UTC|newest]
Thread overview: 31+ messages / expand[flat|nested] mbox.gz Atom feed top
2017-05-15 10:56 Prathamesh Kulkarni
2017-05-16 1:14 ` Jeff Law
2017-05-16 11:48 ` Richard Biener [this message]
2017-05-17 21:22 ` Martin Sebor
2017-05-18 7:07 ` Richard Biener
2017-05-19 13:18 ` Jan Hubicka
2017-05-19 13:34 ` Jan Hubicka
2017-05-23 13:48 ` Prathamesh Kulkarni
2017-07-31 18:23 ` Prathamesh Kulkarni
2017-08-08 4:21 ` Prathamesh Kulkarni
2017-08-17 12:55 ` Prathamesh Kulkarni
2017-09-01 2:39 ` Prathamesh Kulkarni
2017-09-15 12:19 ` Prathamesh Kulkarni
2017-09-25 18:13 ` Prathamesh Kulkarni
2017-09-26 0:24 ` Jan Hubicka
2017-09-27 1:11 ` Prathamesh Kulkarni
2017-09-29 19:28 ` Jan Hubicka
2017-10-06 2:16 ` Prathamesh Kulkarni
2017-10-06 13:04 ` Jan Hubicka
2017-10-07 1:46 ` Prathamesh Kulkarni
2017-10-07 19:35 ` Jan Hubicka
2017-10-07 22:17 ` Prathamesh Kulkarni
2017-10-13 23:34 ` Prathamesh Kulkarni
2017-10-23 9:37 ` Prathamesh Kulkarni
2017-10-24 10:57 ` Jan Hubicka
2017-10-25 11:18 ` Prathamesh Kulkarni
2017-10-25 15:26 ` Jan Hubicka
2017-10-27 10:52 ` Prathamesh Kulkarni
2017-10-27 12:20 ` Jan Hubicka
2017-10-27 12:44 ` Jan Hubicka
2017-10-27 13:00 ` Richard Biener
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=alpine.LSU.2.20.1705161317450.20726@zhemvz.fhfr.qr \
--to=rguenther@suse.de \
--cc=gcc-patches@gcc.gnu.org \
--cc=hubicka@ucw.cz \
--cc=prathamesh.kulkarni@linaro.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).