https://gcc.gnu.org/bugzilla/show_bug.cgi?id=126130
Richard Biener <rguenth at gcc dot gnu.org> changed:
What |Removed |Added
----------------------------------------------------------------------------
Ever confirmed|0 |1
Keywords| |alias, missed-optimization
Status|UNCONFIRMED |NEW
Last reconfirmed| |2026-07-06
CC| |rguenth at gcc dot gnu.org
--- Comment #3 from Richard Biener <rguenth at gcc dot gnu.org> ---
The interesting correctness case is indirectly called variadic functions and
setting/accessing the variadic arguments. For
#include <stdarg.h>
static int q (int n, ...)
{
va_list va;
va_start (va, n);
int *a = va_arg (va, int *);
int *b = va_arg (va, int *);
va_end (va);
return *a + *b;
}
int foo (int (*p)(int, ...))
{
int i = 1, j = 3;
return p(2, &i, &j);
}
int main()
{
if (foo (q) != 4)
__builtin_abort ();
}
we generate
Generating constraints for main/3 (main)
foo.arg0 = &q
_1 = foo.result
...
Generating constraints for foo/2 (foo)
ESCAPED = foo.result
foo.arg0 = &NONLOCAL
i = &NONLOCAL
j = &NONLOCAL
derefaddrtmp(31) = &NONLOCAL
*foo.arg0 + 5 = derefaddrtmp(31)
derefaddrtmp(32) = &i
*foo.arg0 + 6 = derefaddrtmp(32)
derefaddrtmp(33) = &j
*foo.arg0 + 7 = derefaddrtmp(33)
_6 = *foo.arg0 + 4
foo.use = &i
foo.use = &j
foo.clobber = *foo.arg0 + 1
foo.use = *foo.arg0 + 2
foo.result = _6
Generating constraints for q/1 (q)
va = &q.varargs
CALLCLOBBERED(37) = &va
ANYTHING = &va
ANYTHING = &NULL
ANYTHING = &NULL
a_6 = ANYTHING
q.use = &va
q.clobber = &ANYTHING
q.use = &ANYTHING
ANYTHING = &va
ANYTHING = &NULL
ANYTHING = &NULL
b_8 = ANYTHING
q.use = &va
q.clobber = &ANYTHING
q.use = &ANYTHING
_1 = *a_6
q.use = a_6
_2 = *b_8
q.use = b_8
_10 = _1
_10 = _2
q.result = _10
and we correctly get i and j included in q.varargs:
q.varargs = { ESCAPED NONLOCAL i j }
q.use ends up as { NULL ANYTHING ESCAPED NONLOCAL va }
Making foo static and forcing -fno-inline makes the result a little bit
more precise. I believe we miss handling of .VA_ARG from
int q (int n)
{
int * b;
int * a;
struct va[1];
int _1;
int _2;
int _10;
<bb 2> [local count: 1073741824]:
__builtin_va_start (&va, 0);
a_6 = .VA_ARG (&va, 0B, 0B);
b_8 = .VA_ARG (&va, 0B, 0B);
__builtin_va_end (&va);
_1 = *a_6;
_2 = *b_8;
_10 = _1 + _2;
va ={v} {CLOBBER(eos)};
return _10;
}
that should just return what &va points to.
I'm not sure we ever need to "expand" a fn_info, but as with Steensgard
Andersen can end up unifying nodes, so if one is a function pointer and
one a variable we can have a + UNKNOWN constraint for the latter. The
question is what we should semantically do here.
That said, it seems the .use/clobber, etc. variables are made pointed-to
by the solver in build_pred_graph:
else if (rhs.type == ADDRESSOF)
{
...
/* All related variables are no longer direct nodes. */
bitmap_clear_bit (graph->direct_nodes, rhsvar);
v = get_varinfo (rhsvar);
if (!v->is_full_var)
{
v = get_varinfo (v->head);
do
{
bitmap_clear_bit (graph->direct_nodes, v->id);
v = vi_next (v);
we do need to make the argument slots indirect nodes to make the
*foo.arg0 + 5 = derefaddrtmp(31)
constraints work though. But not the use/clobber part. But neither
blocking the use/clobber from the above nor making them artificial vars
helps your testcase.