tree-optimization/117355: object size for PHI nodes with negative offsets

When the object size estimate is returned for a PHI node, it is the
maximum possible value, which is fine in isolation.  When combined with
negative offsets however, it may sometimes end up in zero size because
the resultant size was larger than the wholesize, leading
size_for_offset to conclude that there's a potential underflow.  Fix
this by allowing a non-strict mode to size_for_offset, which
conservatively returns the size (or wholesize) in case of a negative
offset.

gcc/ChangeLog:

	PR tree-optimization/117355
	* tree-object-size.cc (size_for_offset): New argument STRICT,
	return SZ if it is set to false.
	(plus_stmt_object_size): Adjust call to SIZE_FOR_OFFSET.

gcc/testsuite/ChangeLog:

	PR tree-optimization/117355
	* g++.dg/ext/builtin-object-size2.C (test9): New test.
	(main): Call it.
	* gcc.dg/builtin-object-size-3.c (test10): Adjust expected size.

Signed-off-by: Siddhesh Poyarekar <siddhesh@gotplt.org>
This commit is contained in:
Siddhesh Poyarekar 2024-11-19 22:51:31 -05:00
parent 5f95136e5e
commit 684595188d
3 changed files with 50 additions and 7 deletions

View File

@ -406,6 +406,32 @@ test8 (union F *f)
FAIL ();
}
// PR117355
#define STR "bbbbbbbbbbbbbbbbbbbbbbbbbbb"
void
__attribute__ ((noinline))
test9 (void)
{
char line[256];
const char *p = STR;
const char *q = p + sizeof (STR) - 1;
char *q1 = line;
for (const char *p1 = p; p1 < q;)
{
*q1++ = *p1++;
if (p1 < q && (*q1++ = *p1++) != '\0')
{
if (__builtin_object_size (q1 - 2, 0) == 0)
__builtin_abort ();
if (__builtin_object_size (q1 - 2, 1) == 0)
__builtin_abort ();
}
}
}
int
main (void)
{
@ -430,5 +456,6 @@ main (void)
union F f, *fp = &f;
__asm ("" : "+r" (fp));
test8 (fp);
test9 ();
DONE ();
}

View File

@ -619,7 +619,7 @@ test10 (void)
if (__builtin_object_size (p - 3, 2) != sizeof (buf) - i + 3)
FAIL ();
#else
if (__builtin_object_size (p - 3, 2) != 0)
if (__builtin_object_size (p - 3, 2) != 3)
FAIL ();
#endif
break;

View File

@ -344,7 +344,8 @@ init_offset_limit (void)
be positive and hence, be within OFFSET_LIMIT for valid offsets. */
static tree
size_for_offset (tree sz, tree offset, tree wholesize = NULL_TREE)
size_for_offset (tree sz, tree offset, tree wholesize = NULL_TREE,
bool strict = true)
{
gcc_checking_assert (types_compatible_p (TREE_TYPE (sz), sizetype));
@ -377,9 +378,17 @@ size_for_offset (tree sz, tree offset, tree wholesize = NULL_TREE)
return sz;
/* Negative or too large offset even after adjustment, cannot be within
bounds of an object. */
bounds of an object. The exception here is when the base object size
has been overestimated (e.g. through PHI nodes or a COND_EXPR) and the
adjusted offset remains negative. If the caller wants to be
permissive, return the base size. */
if (compare_tree_int (offset, offset_limit) > 0)
return size_zero_node;
{
if (strict)
return size_zero_node;
else
return sz;
}
}
return size_binop (MINUS_EXPR, size_binop (MAX_EXPR, sz, offset), offset);
@ -1521,16 +1530,23 @@ plus_stmt_object_size (struct object_size_info *osi, tree var, gimple *stmt)
addr_object_size (osi, op0, object_size_type, &bytes, &wholesize);
}
bool pos_offset = (size_valid_p (op1, 0)
&& compare_tree_int (op1, offset_limit) <= 0);
/* size_for_offset doesn't make sense for -1 size, but it does for size 0
since the wholesize could be non-zero and a negative offset could give
a non-zero size. */
if (size_unknown_p (bytes, 0))
;
/* In the static case, We want SIZE_FOR_OFFSET to go a bit easy on us if
it sees a negative offset since BYTES could have been
overestimated. */
else if ((object_size_type & OST_DYNAMIC)
|| bytes != wholesize
|| (size_valid_p (op1, object_size_type)
&& compare_tree_int (op1, offset_limit) <= 0))
bytes = size_for_offset (bytes, op1, wholesize);
|| pos_offset)
bytes = size_for_offset (bytes, op1, wholesize,
((object_size_type & OST_DYNAMIC)
|| pos_offset));
/* In the static case, with a negative offset, the best estimate for
minimum size is size_unknown but for maximum size, the wholesize is a
better estimate than size_unknown. */