On 12/16/25 10:34, Andrew MacLeod wrote:
On 12/16/25 03:27, Richard Biener wrote:
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?
And next I get a problem in gcc.dg/builtin-object-size-4.c. This
time, its because we have:
struct A
{
char a[10];
int b;
char c[10];
} y, w[4];
<...>
_28 = &a.a[4] + 2;
I attempt to fold that into a new reference via
gimple_fold_stmt_to_constant_1, and instead of
&a.a[6]
I get
&MEM <char> [(void *)&a + 6B]
When we invoke __builtin_object_size() on the second expression, it
uses the entire object size of A and returns 20 instead of the 4 we
are looking for .
in gimple_fold_stmt_to_constant_1 it executes:
if (subcode == POINTER_PLUS_EXPR)
{
tree op0 = (*valueize) (gimple_assign_rhs1 (stmt));
tree op1 = (*valueize) (gimple_assign_rhs2 (stmt));
if (TREE_CODE (op0) == ADDR_EXPR
&& TREE_CODE (op1) == INTEGER_CST)
{
tree off = fold_convert (ptr_type_node, op1);
return build1_loc
(loc, ADDR_EXPR, TREE_TYPE (op0),
fold_build2 (MEM_REF,
TREE_TYPE (TREE_TYPE (op0)),
unshare_expr (op0), off));
}
}
Which appears to eventually invoke fold_binary_loc () and simply lump
it as a gep codeneric char * MEMref.
Is there a place we currently do this that would be more precise? Or
does this need enhancing? or what is the best way to proceed
Andrew
In fact, with gcc trunk, the way the test case is written :
r = &a.a[4];
r = memset (r, 'a', 2);
r = memset (r + 2, 'b', 2) + 2;
if (__builtin_object_size (r, 3) != sizeof (a.a) - 8)
FAIL ();
It passes fine. the objsz pass evaluates the pointer and sorts it
out... if however we change the test to
r = &a.a[4];
r = memset (r, 'a', 2);
r = &a.a[4] + 2;
r = memset (r, 'b', 2) + 2;
if (__builtin_object_size (r, 3) != sizeof (a.a) - 8)
FAIL ();
we propagate the &MEM <char> [(void *)&a + 6B] into the memset, and the
test case fails as it no longer does the right thing, instead picking up
size of the structureu instead of the array.
Ive broken it into a stand alone testcase:
typedef __SIZE_TYPE__ size_t;
extern void *memset (void *, int, size_t);
struct A
{
char a[10];
int b;
char c[10];
} a;
int
passes (void)
{
void *r;
r = &a.a[4];
r = memset (r, 'a', 2);
r = memset (r + 2, 'b', 2) + 2;
if (__builtin_object_size (r, 3) != sizeof (a.a) - 8)
__builtin_abort ();
}
int
fails (void)
{
void *r;
r = &a.a[4];
r = memset (r + 2, 'b', 2) + 2;
if (__builtin_object_size (r, 3) != sizeof (a.a) - 8)
__builtin_abort ();
This breaks with my system gcc13 as well. The intervening memset in
the passes() case "hides" the root of 'r' as &a.a[4], so objsz gets to
follow the use def chains and does the right thing.
In the fail case, the folded &MEM <char> [(void *)&a + 6B] is
propagated into the memset and it no longer does the right thing:
Computing minimum subobject size for r_4:
Visiting use-def links for r_4
Visiting use-def links for _1
wholesize_for_memref: 28, offset: 6
_1: minimum subobject size 22
r_4: minimum subobject size 20
Simplified
_6 = __builtin_object_size (r_4, 3);
to 20
gimple_simplified to if (1 != 0)
Removing dead stmt:_6 = __builtin_object_size (r_4, 3);
Removing dead stmt:r_4 = _1 + 2;
int fails ()
{
void * r;
<bb 2> [local count: 1073741824]:
memset (&MEM <char> [(void *)&a + 6B], 98, 2);
if (1 != 0)
goto <bb 3>; [0.00%]
else
goto <bb 4>; [100.00%]
<bb 3> [count: 0]:
__builtin_abort ();
<bb 4> [local count: 1073741824]:
return;
I presume I should open a PR for this?
Andrew