On Tue, 23 Jun 2026, Filip Kastl wrote:
> On Mon 2026-06-22 09:16:05, Richard Biener wrote:
> > On Sat, 20 Jun 2026, Filip Kastl wrote:
> >
> > > Hi richi,
> > >
> > > I found a case where the function.clobber varinfo gets included in a
> > > points-to
> > > set. That shouldn't happen right? It causes trouble for my PTA
> > > Steensgaard
> > > project.
> > >
> > > Here is a testcase:
> > >
> > > ---- pr35065-reduced.c ----
> > > enum vlc_module_properties { VLC_MODULE_CB_OPEN };
> > > char ParseNALBlock_p_frag_0;
> > > int vlc_entry__0_9_0f_p_submodule;
> > > void Open();
> > > void vlc_module_set(int *, enum vlc_module_properties, void *);
> > > void vlc_entry__0_9_0f() {
> > > vlc_module_set(&vlc_entry__0_9_0f_p_submodule, VLC_MODULE_CB_OPEN,
> > > Open);
> > > }
> > > void ParseNALBlock();
> > > void Open() { ParseNALBlock(); }
> > > void bs_read_ue();
> > > void ParseNALBlock() {
> > > if (ParseNALBlock_p_frag_0)
> > > bs_read_ue();
> > > }
> > > ---- ----
> > >
> > > I compile it with trunk GCC using these flags:
> > > gcc -c pr35065-reduced.c -fipa-pta -O2 -march=native -fno-inline
> > > -fdump-ipa-pta2-details-alias
> > >
> > > In the dump you can see this set:
> > > callarg(34) = { ESCAPED Open Open.clobber Open.use Open.result }
> > >
> > > I've stepped through PTA in GDB and found that initially,
> > > callarg(34) = { Open }
> > > When we process the constraints callarg(34) = callarg(34) + UNKNOWN,
> > > pta-andersen.cc:solution_set_expand() happilly expands the Open varinfo
> > > into
> > > all the subvariables Open.clobber Open.use Open.result.
> > >
> > > Is this intentional?
> > >
> > > Open.clobber, Open.use, Open.result all have is_full_var = 1, but Open
> > > does
> > > not. What's your gut feeling about setting is_full_var = 1 for fninfos?
> > > It
> > > does help with this testcase but I didn't yet look through the sources to
> > > see
> > > if it doesn't break something else.
> >
> > I think this will break indirect call handling. So what we have is
> >
> > vlc_entry__0_9_0f ()
> > {
> > vlc_module_set (&vlc_entry__0_9_0f_p_submodule, 0u, &Open);
> > }
> >
> > where 'Open' is internal but vlc_module_set is not. We generate
> >
> > callarg(34) = &Open
> > callarg(34) = callarg(34) + UNKNOWN
> > callarg(34) = *callarg(34) + UNKNOWN
> > CALLUSED(30) = callarg(34)
> > *callarg(34) = callescape(29)
> > CALLCLOBBERED(31) = callarg(34)
> > callescape(29) = callarg(34)
> > ESCAPED = &Open
> >
> > where this tries to set up things in a way to allow vlc_module_set
> > to call 'Open' indirectly, meaning the vlc_module_set call clobbers
> > include call clobbers of 'Open' and the 'Open' incoming arguments
> > now have to contain the vars escaped to vlc_module_set and of course
> > all other ESCAPED vars. If you consider 'Open' being program-local,
> > having actual arguments and having meaningful uses/clobbers, then
> > this should make sense.
>
> > If you consider vlc_module_set being
> > not external and an indirect call visble you can also see how the
> > indirect call processing then works during PTA solving.
>
> I'm not sure if I understand this part. Are variables like callarg(...)
> used even in IPA PTA and even when all involved function bodies are
> visible? Or are you refering to intra PTA?
callarg is a helper variable that gets "used" above in
> > CALLUSED(30) = callarg(34)
> > CALLCLOBBERED(31) = callarg(34)
> > callescape(29) = callarg(34)
with considering vlc_module_set being visible I wanted to explain
to what we have to consider happens and that overall effect has
to be correct for both cases, when a body is visible and we
have constraints for the use of the function pointer and for
the case where we have not (but the function could still use
that pointer, of course).
>
> > So I'm not sure that only solution_set_expand is an issue with
> > the way you do clobbers/uses, even if we'd do this in a more
> > explicit way you'd get clobbers and uses part of the solving
> > process.
>
> So you think that my approach of handling clobbers and uses separately (not in
> the solver) won't work? I understand that CALLCLOBBERED(31) must be a
> superset
> of Open.clobber. But I don't see why callarg(34) must be a superset of
> Open.clobbers. Couln't we modify GCC to not expand Open into all the
> subvariables and instead handle them explicitly like this?
>
> Instead of:
> callarg(34) = &Open
> callarg(34) = callarg(34) + UNKNOWN
> callarg(34) = *callarg(34) + UNKNOWN
> CALLUSED(30) = callarg(34)
> *callarg(34) = callescape(29)
> CALLCLOBBERED(31) = callarg(34)
> callescape(29) = callarg(34)
> ESCAPED = &Open
>
> Do:
> callarg(34) = &Open
> callarg(34) = callarg(34) + UNKNOWN
> (but this only expands real aggregates, not fninfo subvariables)
> callarg(34) = *callarg(34) + UNKNOWN
> CALLUSED(30) = &Open.use
> *callarg(34) = callescape(29)
> CALLCLOBBERED(31) = &Open.clobber
> callescape(29) = callarg(34)
> ESCAPED = &Open
>
> With constraints like this, it would no longer happen that clobbers are used
> to
> compute non-clobbers sets and I could return to the idea of handling clobbers
> separately (similarly for uses).
We could possibly "optimize" function pointer argument constraints.
The current setup was done for simplicity because it easily extended
to actual function pointers.
I'm not sure the postprocessing idea will work for indirect calls where
for
void foo (void (*p)())
{
p();
}
CALLUSE/CALLCLOBBER/CALLESCAPE of 'foo' has to include everything
from what 'p' points to.
> We would have to think through how to handle situation like this where Open
> isn't passed into the parameter directly but through a variable instead:
>
> void vlc_entry__0_9_0f() {
> void *fn;
> if (p)
> fn = Open1;
> else
> fn = Open2;
> vlc_module_set(&vlc_entry__0_9_0f_p_submodule, VLC_MODULE_CB_OPEN, fn);
> }
>
> We could do something like this (assume that the offsets for .use and .clobber
> are +1 and +2, I don't remember right now what they actually are):
>
> fn = &Open1;
> fn = &Open2;
> CALLUSED(30) = fn + 1;
> CALLCLOBBERED(31) = fn + 2;
>
> But then what if fn can be both a function pointer and a data pointer:
>
> void vlc_entry__0_9_0f() {
> void *fn;
> if (p)
> fn = Open1;
> else
> fn = malloc(4);
> vlc_module_set(&vlc_entry__0_9_0f_p_submodule, VLC_MODULE_CB_OPEN, fn);
> }
>
> Not sure what to do here to not mix up actuall aggregates and fninfo
> subvariables. But we currently mix them up anyway, right? So the constraints
> above would still be an improvement.
Yes, the fact that callused/callclobbered live at offsets from 'fn'
make this transparent and conservatively correct. Of course we
get bogus uses/clobbers if 'fn' is a data pointer and vice
versa for data accesses. And we can't really do better I think.
> > There should be some very incomplete coverage of indirect call
> > handing in the gcc.dg/ipa/ipa-pta-* tests.
> >
> > Now, that we get
> >
> > allarg(34) = { ESCAPED Open Open.clobber Open.use Open.result }
> >
> > shows that we fail to maintain the fact that we of course cannot
> > take the address of Open.clobber or Open.use. I think this
> > means we fail to set ->is_artificial_var on those (but not on
> > arguments or result)? So if that helps, I think that's the
> > correct thing to do here.
>
> I'll look into this (though maybe not right away).
Maybe I was misled here, but we should never construct "&Open.use",
aka take the address of this subvariable. I don't see how we
currently avoid this, setting ->is_artificial_var might have
other side-effects.
Richard.
>
> > Richard.
>
--
Richard Biener <[email protected]>
SUSE Software Solutions Germany GmbH,
Frankenstrasse 146, 90461 Nuernberg, Germany;
GF: Jochen Jaser, Andrew McDonald, Werner Knoblich; (HRB 36809, AG Nuernberg)