Hello,
On Sat, Sep 20 2025, Josef Melcr wrote:
> Hi,
>
> thank you for the review :) Below are some of my thoughts, I will
> implement your suggestions as soon as I can.
>
> On 9/20/25 00:29, Martin Jambor wrote:
>> One more naming nit. In various comments and dumping strings, you use
>> the term "offloading" function. I guess the term is somewhat
>> understandable in the context of OpenMP/OpenACC (but there it usually
>> means something offloading computation to a GPU or other such special
>> device, not running GOMP_parallel). I'd suggest "callback-dispatching"
>> or something similar and more general (qsort is not offloading in any
>> way).
> I do use the term "offloading" very loosely and often times
> incorrectly. I think "callback-dispatching" is great, so I will use
> that instead. I apologize if it caused any confusion.
>>> gcc/testsuite/ChangeLog:
>>>
>>> * gcc.dg/ipa/ipcp-cb-spec1.c: New test.
>>> * gcc.dg/ipa/ipcp-cb-spec2.c: New test.
>>> * gcc.dg/ipa/ipcp-cb1.c: New test.
>> It seems you have lost the test in testsuite/gcc.dg/attr-callback.c ?
>> It looked useful.
>
> The test serves no purpose, as the attribute is not in the public API at
> the moment (suggested in
> https://gcc.gnu.org/pipermail/gcc-patches/2025-June/686859.html and
> implemented in v2). It will of course make a return once a consensus
> about the attribute is reached, but right now we would be adding a test
> that cannot be passed.
OH, right, I missed that. Make sense.
>
>>> + cgraph_edge *edge = (cgraph_edge *) *slot;
>>> + gcc_assert (edge->speculative || edge->has_callback);
>>> + if (edge->has_callback)
>>> + /* If the slot is already occupied, then the hashed edge is the
>>> + callback-carrying edge, which is desired behavior, so we can safely
>>> + return. */
>>> + return;
>>> if (e->callee && (!e->prev_callee
>>> || !e->prev_callee->speculative
>>> || e->prev_callee->call_stmt != e->call_stmt))
>> I still don't understand why the early exit is not enough.
>>
>> In one of your previous emails you wrote:
>>
>>> In some cases, the callback edge would be added before the
>>> callback-carrying edge, occupying its slot and leading to a crash on the
>>> assert when adding the callback-carrying edge. The early return
>>> prevents that. Sadly, I am afraid I no longer have the code snippet to
>>> reproduce the crash.
>> ...and I'd think that the early return would be enough and the second
>> condition is not necessary. But if it is not, I don't have an issue
>> with the code as it is.
> Right now I can't think of a case where the early return is not enough,
> I'll look into it. It's been a while since I've worked on it, so I am
> not entirely sure, but you are probably right.
Please do. If it turns out to be necessary, we can bring it back.
>>> + /* When updating a callback or a callback-carrying edge, update every
>>> edge
>>> + involved. */
>>> + if (update_derived_edges && (e->callback || e->has_callback))
>>> + {
>>> + cgraph_edge *current, *next, *carrying;
>>> + carrying = e->has_callback ? e : e->get_callback_carrying_edge ();
>>> +
>>> + current = e->first_callback_edge ();
>>> + if (current)
>>> + {
>>> + for (cgraph_edge *d = current; d; d = next)
>>> + {
>>> + next = d->next_callback_edge ();
>>> + cgraph_edge *d2 = set_call_stmt (d, new_stmt, false);
>>> + gcc_assert (d2 == d);
>>> + }
>>> + }
>>> + carrying = set_call_stmt (carrying, new_stmt, false);
>>> + return carrying;
>>> + }
>>> +
>> I'm not sure that always returning the carrying edge is the expected
>> thing to do. But it does not look like it ever matters that the
>> original callback edge is returned, so perhaps it is OK to just document
>> the behavior in the function comment - ah, it actually is done in the
>> header file, so please just copy it to the .c file description too.
>> (Though perhaps Honza will have a different opinion about this.)
> It doesn't really matter which edge is returned, as any of the related
> edges can be obtained with the provided methods. The carrying edge is
> usually more useful when calling set_call_stmt, it is the "owner" of the
> call statement (the call statement is calling the function the carrying
> edge is pointing to) and I also try to return the carrying edge whenever
> performing some action on all of the edges at once, for the sake of
> consistency. For these reasons, I chose to return the carrying edge,
> though I can see it not being entirely obvious behavior. I can change
> it to returning the original edge if you want me to, though I think the
> carrying edge is better suited.
I guess you are right. That is why I just wanted to document it
consistently.
>>> + /* Used to pair callback edges and the attributes that originated them
>>> + together. Needed in order to get ipa-icf to work with callbacks.
>> I think the sentence about ICF is outdated? In any case it seems quite
>> unnecessary.
>
> It's not outdated, as without the callback_id we wouldn't know how to
> redirect callback edges affected by icf, though mentioning it is
> probably unnecessary. I will remove it.
I see. Because the patch does not touch ICF I thought this was some
left-over. But then I guess we need the field for all redirections (and
more) so singling out ICF sounds redundant.
>
>>> +/* Initializes ipa_edge_args summary of CBE given its callback-carrying
>>> edge.
>>> + This primarily means allocating the correct amount of jump functions.
>>> */
>>> +
>>> +static inline void
>>> +init_callback_edge_summary (struct cgraph_edge *carrying,
>>> + struct cgraph_edge *cbe)
>>> +{
>>> + ipa_edge_args *carrying_args = ipa_edge_args_sum->get (carrying);
>>> + ipa_edge_args *cb_args = ipa_edge_args_sum->get_create (cbe);
>>> + vec_safe_grow_cleared (cb_args->jump_functions,
>>> + carrying_args->jump_functions->length (), true);
>> why is carrying_args->jump_functions->length () the correct number of
>> jump functions? You need the lenght of the vector
>> callback_get_arg_mapping would return, no? (I'm not suggesting that we
>> construct such a vector here but I think the length needs to be computed
>> from the attribute and not from the number of parameters the dispatching
>> function has.)
> You are right, that should be calculated from the attribute. It works
> because the dispatching function in OpenMP/OpenACC usually takes more
> arguments than the callback. The mistake looks to be isolated to this
> place, I am using the correct number of jump functions in the rest of
> the code. The doc comment above the function is pretty ironic though :)
It is a simple mistake to make. The comment made it easier to detect,
so it already was helpful.
>> Generally, this version is a clear improvement over the previous
>> versions and (with at least the last issue described above fixed), I'd
>> be happy to have the cgraph and ipa bits committed. Someone else needs
>> to approve the rest though - and Honza may want to chime in. I also
>> hope that soon (at Cauldron?) we'll be able to come to some kind of
>> conclusion what to do about the attribute name and behavior.
> That's what I am hoping for. I'll be giving a short talk about this
> patch and its future improvements at Cauldron on Sunday with the hopes
> of coming up with at least some general direction for the attribute.
I'm looking forward to it.
>> We may also consider enabling the new behavior with a compiler switch
>> (enabled by default at -O2 and higher). But that is also a detail.
> I don't think that's currently needed, as the new edges are bound to
> ipa-cp, which is enabled at -O2. Though that might change in the
> future, we might add some functionality not specific to any pass.
I don't have a strong opinion at the moment but it may turn out that it
we want it for debugging. I also suspect that at some point we will
want to have a debug counter check (see gcc/dbgcnt.h and description of
compiler option -fdbg-cnt in the user manual) somewhere too ;-)
Thanks a lot for working on this,
Martin