Question about function body and function specialization
Erick Ochoa
erick.ochoa@theobroma-systems.com
Wed Jul 15 15:30:09 GMT 2020
On 15.07.20 05:03, Martin Jambor wrote:
> Hi,
>
> On Tue, Jul 14 2020, Erick Ochoa wrote:
>> On 14/07/2020 12:37, Erick Ochoa wrote:
>>> Hello,
>>>
>>> I have a function foo defined on a source file. Sometimes, a function
>>> pointer pointing to foo is passed as a parameter to other functions
>>> where foo is called indirectly. This indirect call is specialized during
>>> link time. Still at link time, I analyze the function call the following
>
> Does "at link time" mean that all of the below happens as part of the
> execute method of an IPA pass? Statements and call graph do diverge for
> a while at that stage (perhaps watching
> https://youtu.be/LBn-0jYKLb8?t=2604 from time 26:04 until about 50:20
> might help a little).
Thanks, I'll give this a watch.
What I meant is that the indirect call is specialized during ipa-cp.
>
>>> way:
>>>
>>> // s is the gimple statement which corresponds to the indirect call
>>> tree fn = gimple_call_fndecl(s);
>>> // for this particular call the assertions are true
>>> gcc_assert(fn)
>
> That can't be. If s was an indirect call, gimple_call_fndecl(s) would
> return NULL (because gimple_call_fn(gs) would be an SSA_NAME). Perhaps
> early inlining already converted it to a direct one?
It is not early inlining which converted this indirect function call to
a direct call. It happens during ipa-cp.
In another pass (scheduled just after materialization) is when I inspect
gimple instructions of the caller to this indirect function which has
been specialized.
>
>>> cgraph_node *node = cgraph_node::get(fn)
>>> gcc_assert(node)
>>>
>>> I have analyzed the body of this function previously by using the
>>> FOR_EACH_FUNCTION_WITH_GIMPLE_BODY macro. However, I do not know if
>>> there's a way to link the cnode_graph (or function decl) analyzed in the
>>> macro with the one obtained at the call site. What would be the best way
>>> to say something like:
>>>
>>> tree fn = gimple_call_fndecl(s);
>>> // for this particular call the assertions are true
>>> gcc_assert(fn)
>>> cgraph_node *node = cgraph_node::get(fn)
>>> gcc_assert(node)
>>> bool i_want_this_to_be_true = saw_fn_in_loop_before(node, fn);
>
> At IPA time, the best way is always to look at the call graph edges,
> something like:
>
> cgraph_edge *cs = caller_cgraph_node->get_edge (s);
> examine (e->callee);
>
> Note that if the call is truly an indirect one cs->callee will be NULL
> (and cs->indirect_unknown_callee will be set). Things can also get
> quite a bit more complicated if cs->speculative is set, then there is
> both an indirect and guessed direct edge for a single call.
Thanks! I did read a bunch of source code and I was wondering why all
the constant propagation and specialization happens by navigating the
call graph edges and not the gimple code. I initially thought that
gimple_call_set_fndecl was not called because I couldn't find anything
except call graph edges. However, this was not the case.
Also, by the time I am inspecting the indirect edge, it has already been
specialized and it is not speculative. So, it has been changed to a
direct function.
>
>>>
>>> I don't think that using storing the for loop and checking in
>>> saw_fn_in_loop_before is the way to go because I believe cnode->decl
>>> pointers can be different. Is this correct? In other words
>>>
>>>
>>> FOR_EACH_FUNCTION_WITH_GIMPLE_BODY(cnode)
>>> {
>>> a_std_set.insert(cnode->decl)
>>> }
>>>
>>> // later
>>>
>>> bool
>>> saw_fn_in_loop_before(cnode_graph *cn, tree fn)
>>> {
>>> return a_std_set.find(fn) != a_std_set.end();
>>> }
>>>
>>> Should not work. Something I found interesting is that for the fndecl
>>> obtained through the callsite gimple_has_body_p returns false. Even
>>> though I saw a fndecl which corresponds to the same function in the
>>> FOR_EACH_FUNCTION_WITH_GIMPLE_BODY.
>
> I can only guess but it seems that something has created specialized
> clone for all contexts and you happen to be looking at the gimple bodies
> after clone materialization but before edge redirection... but I am only
> guessing.
I think this is close to the truth. So... the function I am interested
in finding has indeed been specialized for "all contexts", but I think
this "all contexts" did not take into account the function pointers.
During ipa-cp I looked at the code where the function call has been
changed from an indirect call to a direct call and I was able to find
that callee->has_gimple_body_p() returns true and that it was also found
in FOR_EACH_FUNCTION_WITH_GIMPLE_BODY loop.
I also did this during ipa-sra and the same was true.
But for the pass just after materialization it was false.
As mentioned in a sibling post, I found out that during the inlining
phase, the function is "reclaimed". I'm not 100% sure yet, but I think
the indirect functions are not inlined. I used -fdump-ipa-inline and in
my pass (after materialization) I used cnode->dump_name() to find its
dump name... I then looked for its dump_name and only found the following:
IPA function summary for $dump_name inlinable
$dump_name ($name) @0x40002a1936d8
Type: function
Body removed by symtab_remove_unreachable_nodes
Visibility: prevailing_def_ironly
Address is taken.
References:
Referring:
Read from file: $file
Availability: not_available
Profile id: 1677473182
Unit id: 2
Function flags: count:17343 (adjusted) first_run:3 hot
Called by:
Calls:
These are the only two mentions of the function by its dump_name
>
>>
>> Actually, another interesting hint is that the original foo function
>> takes two parameters. The function I am seeing inside the
>> FOR_EACH_FUNCTION_WITH_GIMPLE_BODY is a specialized function of foo with
>> only 1 parameter. However, the indirect function call is a version of
>> foo which has not been specialized (i.e. it takes the original two
>> parameters).
>>
>> I guess my questions would be:
>>
>> * Does FOR_EACH_FUNCTION_WITH_GIMPLE_BODY only iterates across functions
>> which are reachable for main across the call graph?
>
> No. But functions which are known not to be reachable are of course
> removed from the call graph and from the entire compilation too.
>
>> * Is the the underlying mechanism for FOR_EACH_FUNCTION_WITH_GIMPLE_BODY
>> not updated after ipa-prop discovers targets of indirect functions?
>
> I am not sure I understand the question but I guess that no. The
> mechanism is simple, just look it up.
>
>> * Or is it just that the new callsite does not have a gimple body for
>> its function? (This seems implausible since the new direct callsite
>> should refer to the original function implementation.) How can I access
>> this function's body?
>>
>
> In the IPA scheme of things, call-sites are updated last, even when the
> function where they are keeps its body from the start until the end. It
> seems to me that you really want to be looking at the call graph edges
> instead.
>
> Generally speaking, at IPA time you want to work only with the call
> graph and the data you collected in summary building stage and look at
> actual function bodies only as the last resort.
Thanks Martin, this is all very useful information. I'll see if I can
refactor my code to use the call graph and not the gimple code. However,
I still think that there might be an interesting behaviour on reclaiming
a cgraph_node that is reachable through function pointers...
>
> Martin
>
More information about the Gcc
mailing list