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

Reply via email to