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? Richard. > 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 >
