On 12/16/25 03:27, Richard Biener wrote:
On Mon, Dec 15, 2025 at 9:50 PM Andrew MacLeod <[email protected]> wrote:
I am experimenting with integrating VRP's Points-to analysis into
prange.  Its currently managed on the side via the class in
value_pointer_equiv.{cc,h} which maintains a global side table and a
push/pop for conditions during the dominator walk.

I have managed to get it mostly working, but Im running into an issue
with runtime and the Object Size Checking Built-in Functions such as
__builtin___memcpy_chk

I see we often generate code like

    _1 = __builtin_memcpy (&buf1, "ABCDEFGHI", 9);
    if (_1 != &buf1)
      goto abort ();

but the builtin function fnspec string is "1cO313" which says that _1
will always return the first argument,, which is &buf1.  It therefore
seems like we can fold away the condition in this case...   Which ranger
and the enhanced prange does.  This seems to cause no problem.

Where i do run into a problem is in
gcc.c-torture/execute/builtins/memcpy-chk.c ,   everything passes fine
up until this point in test2_sub () :


    /* buf3 points to an unknown object, so __memcpy_chk should not be
done.  */
    if (memcpy ((char *) buf3 + 4, buf5, n + 6) != (char *) buf1 + 4
        || memcmp (buf1, "aBcdRSTUVWklmnopq\0", 19))
      abort ();

With my changes, this sequence goes from

   <bb 31> :
    __builtin___memcpy_chk (buf3_60(D), "aBcdEFghijklmnopq\x00", 19, _5);
    _47 = n_73(D) + 6;
    _48 = (long unsigned int) _47;
    _50 = __builtin___memcpy_chk (_19, &buf5, _48, _20);
    if (_50 != &MEM <long int[64]> [(void *)&buf1 + 4B])
      goto <bb 33>; [INV]
    else
      goto <bb 32>; [INV]

    <bb 32> :
    _52 = memcmp (&buf1, "aBcdRSTUVWklmnopq\x00", 19);
    if (_52 != 0)
      goto <bb 33>; [INV]
    else
      goto <bb 34>; [INV]

    <bb 33> :
    abort ();

to

   <bb 23> :
    __builtin___memcpy_chk (&buf1, "aBcdEFghijklmnopq\x00", 19, _5);
    _47 = n_73(D) + 6;
    _48 = (long unsigned int) _47;
    _50 = __builtin___memcpy_chk (&MEM <long int[64]> [(void *)&buf1 +
4B], &buf5, _48, 508);
    _52 = memcmp (&buf1, "aBcdRSTUVWklmnopq\x00", 19);
    if (_52 != 0)
      goto <bb 24>; [INV]
    else
      goto <bb 25>; [INV]

    <bb 24> :
    abort ();


The new integrated PTA figures out a couple of things.. first, it knows
    *  from earlier that buf3 and buf1 points to the same thing,
    * and can also figure out that buf3 + 4 == buf1 + 4, so it can fold
away the condition afterwards since arg1 is the result
    * Since it knows buf3 points to buf1, it knows the object size if 512
so buf3+4 gives an object size of 508 which is propagates into the
memcpy_chk size field.

This is where I get confused.   The range if _48 is unknown, but known
to be   [irange] long unsigned int [0, 2147483647][18446744071562067974,
+INF]

I think this fails some of the logic in gimple_fold_builtin_memory_chk..
because the size is known to be 508 now (and before it was "all_ones")
gimple_fold_builtin_memory_chk refuses to replace it with memcpy ..  and
the testcase then goes and fails.    At the top of test2_sub() I see:

    /* All the memcpy/__builtin_memcpy/__builtin___memcpy_chk
       calls in this routine are either fixed length, or have
       side-effects in __builtin_object_size arguments, or
       dst doesn't point into a known object.  */
    chk_calls = 0;

And then at the end, we abort on
    if (chk_calls)
      abort ();

Presumably because we called memcpy_chk.

Is this just a "faulty" test situation now if we can identify that buf3
and buf1 points to the same thing?

Or am I missing something...?
IIRC the execute/builtins tests play tricks behind our backs, introducing
extra side-effects of the builtin calls.  That harness should be re-architected.

But your problem at hand seems to be that you are confusing
the objsz pass and the test expects to optimize _chk to no-_chk
variants?

Yes, as I keep digging and understand these builtins better, it appears all my execution errors are test case issues where the assumption is we didn't know something before but now do.    So far, in all cases, either the testcase expects there to be a _chk variant left, but we remove it (as in the str case), or the expectation is it will be removed but we do not because the size of the object is now known and was not before.

And yes, I see the testcase harness set local variables to count chk routines, disallow certain things,  and such. ick  :-)

Andrew

Reply via email to