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...?
Thanks
Andrew
PS. Why do we never eliminate those checks after calls to memcpy and
friends? ie the pattern
_1 = __builtin_memcpy (&buf1, "ABCDEFGHI", 9);
if (_1 != &buf1)
goto <bb 4>; [INV]
else
goto <bb 3>; [INV]
Shouldn't that be trivially removable? the fnspec string:
case BUILT_IN_STRNCPY:
case BUILT_IN_MEMCPY:
case BUILT_IN_MEMMOVE:
case BUILT_IN_TM_MEMCPY:
case BUILT_IN_TM_MEMMOVE:
case BUILT_IN_STRNCPY_CHK:
case BUILT_IN_MEMCPY_CHK:
case BUILT_IN_MEMMOVE_CHK:
return "1cO313";
Shows that arg 1 is *always* the return range, and prange is now quite
happy to propagate that points to info and triviaslly removes them in EVRP
2 range_of_stmt (_1) at stmt _1 = __builtin_memcpy (&buf1,
"ABCDEFGHI", 9);
TRUE : (2) range_of_stmt (_1) [prange] void * [1, +INF] ->
&buf1 <<-- non-zero and points to &buf1
But up to now, those branches go right through past .optimized... and
since that seems like very low hanging fruit, I figure I have to be
missing something