PR tree-optimization/78257 - missing memcmp optimization with constant arrays

gcc/ChangeLog:

	PR middle-end/78257
	* builtins.c (expand_builtin_memory_copy_args): Rename called function.
	(expand_builtin_stpcpy_1): Remove argument from call.
	(expand_builtin_memcmp): Rename called function.
	(inline_expand_builtin_bytecmp): Same.
	* expr.c (convert_to_bytes): New function.
	(constant_byte_string): New function (formerly string_constant).
	(string_constant): Call constant_byte_string.
	(byte_representation): New function.
	* expr.h (byte_representation): Declare.
	* fold-const-call.c (fold_const_call): Rename called function.
	* fold-const.c (c_getstr): Remove an argument.
	(getbyterep): Define a new function.
	* fold-const.h (c_getstr): Remove an argument.
	(getbyterep): Declare a new function.
	* gimple-fold.c (gimple_fold_builtin_memory_op): Rename callee.
	(gimple_fold_builtin_string_compare): Same.
	(gimple_fold_builtin_memchr): Same.

gcc/testsuite/ChangeLog:

	PR middle-end/78257
	* gcc.dg/memchr.c: New test.
	* gcc.dg/memcmp-2.c: New test.
	* gcc.dg/memcmp-3.c: New test.
	* gcc.dg/memcmp-4.c: New test.
This commit is contained in:
Martin Sebor 2020-08-14 17:11:53 -06:00
parent 2867118ddd
commit 866626efd7
11 changed files with 915 additions and 44 deletions

View File

@ -4441,7 +4441,7 @@ expand_builtin_memory_copy_args (tree dest, tree src, tree len,
/* Try to get the byte representation of the constant SRC points to,
with its byte size in NBYTES. */
unsigned HOST_WIDE_INT nbytes;
const char *rep = c_getstr (src, &nbytes);
const char *rep = getbyterep (src, &nbytes);
/* If the function's constant bound LEN_RTX is less than or equal
to the byte size of the representation of the constant argument,
@ -4449,7 +4449,7 @@ expand_builtin_memory_copy_args (tree dest, tree src, tree len,
the bytes from memory and only store the computed constant.
This works in the overlap (memmove) case as well because
store_by_pieces just generates a series of stores of constants
from the representation returned by c_getstr(). */
from the representation returned by getbyterep(). */
if (rep
&& CONST_INT_P (len_rtx)
&& (unsigned HOST_WIDE_INT) INTVAL (len_rtx) <= nbytes
@ -4698,7 +4698,7 @@ expand_builtin_stpcpy_1 (tree exp, rtx target, machine_mode mode)
because the latter will potentially produce pessimized code
when used to produce the return value. */
c_strlen_data lendata = { };
if (!c_getstr (src, NULL)
if (!c_getstr (src)
|| !(len = c_strlen (src, 0, &lendata, 1)))
return expand_movstr (dst, src, target,
/*retmode=*/ RETURN_END_MINUS_ONE);
@ -5351,11 +5351,11 @@ expand_builtin_memcmp (tree exp, rtx target, bool result_eq)
when the function's result is used for equality to zero, ARG1)
points to, with its byte size in NBYTES. */
unsigned HOST_WIDE_INT nbytes;
const char *rep = c_getstr (arg2, &nbytes);
const char *rep = getbyterep (arg2, &nbytes);
if (result_eq && rep == NULL)
{
/* For equality to zero the arguments are interchangeable. */
rep = c_getstr (arg1, &nbytes);
rep = getbyterep (arg1, &nbytes);
if (rep != NULL)
std::swap (arg1_rtx, arg2_rtx);
}
@ -7805,8 +7805,8 @@ inline_expand_builtin_bytecmp (tree exp, rtx target)
/* Get the object representation of the initializers of ARG1 and ARG2
as strings, provided they refer to constant objects, with their byte
sizes in LEN1 and LEN2, respectively. */
const char *bytes1 = c_getstr (arg1, &len1);
const char *bytes2 = c_getstr (arg2, &len2);
const char *bytes1 = getbyterep (arg1, &len1);
const char *bytes2 = getbyterep (arg2, &len2);
/* Fail if neither argument refers to an initialized constant. */
if (!bytes1 && !bytes2)

View File

@ -11600,15 +11600,112 @@ is_aligning_offset (const_tree offset, const_tree exp)
/* This must now be the address of EXP. */
return TREE_CODE (offset) == ADDR_EXPR && TREE_OPERAND (offset, 0) == exp;
}
/* Return the tree node if an ARG corresponds to a string constant or zero
if it doesn't. If we return nonzero, set *PTR_OFFSET to the (possibly
non-constant) offset in bytes within the string that ARG is accessing.
If MEM_SIZE is non-zero the storage size of the memory is returned.
If DECL is non-zero the constant declaration is returned if available. */
tree
string_constant (tree arg, tree *ptr_offset, tree *mem_size, tree *decl)
/* If EXPR is a constant initializer (either an expression or CONSTRUCTOR),
attempt to obtain its native representation as an array of nonzero BYTES.
Return true on success and false on failure (the latter without modifying
BYTES). */
static bool
convert_to_bytes (tree type, tree expr, vec<unsigned char> *bytes)
{
if (TREE_CODE (expr) == CONSTRUCTOR)
{
/* Set to the size of the CONSTRUCTOR elements. */
unsigned HOST_WIDE_INT ctor_size = bytes->length ();
if (TREE_CODE (type) == ARRAY_TYPE)
{
tree val, idx;
tree eltype = TREE_TYPE (type);
unsigned HOST_WIDE_INT elsize =
tree_to_uhwi (TYPE_SIZE_UNIT (eltype));
/* Jump through hoops to determine the lower bound for languages
like Ada that can set it to an (almost) arbitrary value. */
tree dom = TYPE_DOMAIN (type);
if (!dom)
return false;
tree min = TYPE_MIN_VALUE (dom);
if (!min || !tree_fits_uhwi_p (min))
return false;
unsigned HOST_WIDE_INT i, last_idx = tree_to_uhwi (min) - 1;
FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (expr), i, idx, val)
{
/* Append zeros for elements with no initializers. */
if (!tree_fits_uhwi_p (idx))
return false;
unsigned HOST_WIDE_INT cur_idx = tree_to_uhwi (idx);
if (unsigned HOST_WIDE_INT size = cur_idx - (last_idx + 1))
{
size = size * elsize + bytes->length ();
bytes->safe_grow_cleared (size);
}
if (!convert_to_bytes (eltype, val, bytes))
return false;
last_idx = cur_idx;
}
}
else if (TREE_CODE (type) == RECORD_TYPE)
{
tree val, fld;
unsigned HOST_WIDE_INT i;
FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (expr), i, fld, val)
{
/* Append zeros for members with no initializers and
any padding. */
unsigned HOST_WIDE_INT cur_off = int_byte_position (fld);
if (bytes->length () < cur_off)
bytes->safe_grow_cleared (cur_off);
if (!convert_to_bytes (TREE_TYPE (val), val, bytes))
return false;
}
}
else
return false;
/* Compute the size of the COSNTRUCTOR elements. */
ctor_size = bytes->length () - ctor_size;
/* Append zeros to the byte vector to the full size of the type.
The type size can be less than the size of the CONSTRUCTOR
if the latter contains initializers for a flexible array
member. */
tree size = TYPE_SIZE_UNIT (type);
unsigned HOST_WIDE_INT type_size = tree_to_uhwi (size);
if (ctor_size < type_size)
if (unsigned HOST_WIDE_INT size_grow = type_size - ctor_size)
bytes->safe_grow_cleared (bytes->length () + size_grow);
return true;
}
unsigned char charbuf[MAX_BITSIZE_MODE_ANY_MODE / BITS_PER_UNIT];
int len = native_encode_expr (expr, charbuf, sizeof charbuf, 0);
if (len <= 0)
return false;
unsigned n = bytes->length ();
bytes->safe_grow (n + len);
unsigned char *p = bytes->address ();
memcpy (p + n, charbuf, len);
return true;
}
/* Return a STRING_CST corresponding to ARG's constant initializer either
if it's a string constant, or, when VALREP is set, any other constant,
or null otherwise.
On success, set *PTR_OFFSET to the (possibly non-constant) byte offset
within the byte string that ARG is references. If nonnull set *MEM_SIZE
to the size of the byte string. If nonnull, set *DECL to the constant
declaration ARG refers to. */
static tree
constant_byte_string (tree arg, tree *ptr_offset, tree *mem_size, tree *decl,
bool valrep = false)
{
tree dummy = NULL_TREE;;
if (!mem_size)
@ -11755,18 +11852,43 @@ string_constant (tree arg, tree *ptr_offset, tree *mem_size, tree *decl)
return array;
}
if (!VAR_P (array) && TREE_CODE (array) != CONST_DECL)
return NULL_TREE;
tree init = ctor_for_folding (array);
/* Handle variables initialized with string literals. */
if (!init || init == error_mark_node)
return NULL_TREE;
if (valrep)
{
HOST_WIDE_INT cstoff;
if (!base_off.is_constant (&cstoff))
return NULL_TREE;
/* If value representation was requested convert the initializer
for the whole array or object into a string of bytes forming
its value representation and return it. */
auto_vec<unsigned char> bytes;
if (!convert_to_bytes (TREE_TYPE (init), init, &bytes))
return NULL_TREE;
unsigned n = bytes.length ();
const char *p = reinterpret_cast<const char *>(bytes.address ());
init = build_string_literal (n, p, char_type_node);
init = TREE_OPERAND (init, 0);
init = TREE_OPERAND (init, 0);
*mem_size = size_int (TREE_STRING_LENGTH (init));
*ptr_offset = wide_int_to_tree (ssizetype, base_off);
if (decl)
*decl = array;
return init;
}
if (TREE_CODE (init) == CONSTRUCTOR)
{
/* Convert the 64-bit constant offset to a wider type to avoid
overflow. */
overflow and use it to obtain the initializer for the subobject
it points into. */
offset_int wioff;
if (!base_off.is_constant (&wioff))
return NULL_TREE;
@ -11779,6 +11901,9 @@ string_constant (tree arg, tree *ptr_offset, tree *mem_size, tree *decl)
unsigned HOST_WIDE_INT fieldoff = 0;
init = fold_ctor_reference (TREE_TYPE (arg), init, base_off, 0, array,
&fieldoff);
if (!init || init == error_mark_node)
return NULL_TREE;
HOST_WIDE_INT cstoff;
if (!base_off.is_constant (&cstoff))
return NULL_TREE;
@ -11791,9 +11916,6 @@ string_constant (tree arg, tree *ptr_offset, tree *mem_size, tree *decl)
offset = off;
}
if (!init)
return NULL_TREE;
*ptr_offset = offset;
tree inittype = TREE_TYPE (init);
@ -11864,7 +11986,29 @@ string_constant (tree arg, tree *ptr_offset, tree *mem_size, tree *decl)
return init;
}
/* Return STRING_CST if an ARG corresponds to a string constant or zero
if it doesn't. If we return nonzero, set *PTR_OFFSET to the (possibly
non-constant) offset in bytes within the string that ARG is accessing.
If MEM_SIZE is non-zero the storage size of the memory is returned.
If DECL is non-zero the constant declaration is returned if available. */
tree
string_constant (tree arg, tree *ptr_offset, tree *mem_size, tree *decl)
{
return constant_byte_string (arg, ptr_offset, mem_size, decl, false);
}
/* Similar to string_constant, return a STRING_CST corresponding
to the value representation of the first argument if it's
a constant. */
tree
byte_representation (tree arg, tree *ptr_offset, tree *mem_size, tree *decl)
{
return constant_byte_string (arg, ptr_offset, mem_size, decl, true);
}
/* Optimize x % C1 == C2 for signed modulo if C1 is a power of two and C2
is non-zero and C3 ((1<<(prec-1)) | (C1 - 1)):
for C2 > 0 to x & C3 == C2

View File

@ -289,9 +289,13 @@ expand_normal (tree exp)
}
/* Return the tree node and offset if a given argument corresponds to
a string constant. */
/* Return STRING_CST and set offset, size and decl, if the first
argument corresponds to a string constant. */
extern tree string_constant (tree, tree *, tree *, tree *);
/* Similar to string_constant, return a STRING_CST corresponding
to the value representation of the first argument if it's
a constant. */
extern tree byte_representation (tree, tree *, tree *, tree *);
extern enum tree_code maybe_optimize_mod_cmp (enum tree_code, tree *, tree *);

View File

@ -1800,8 +1800,8 @@ fold_const_call (combined_fn fn, tree type, tree arg0, tree arg1, tree arg2)
&& !TREE_SIDE_EFFECTS (arg0)
&& !TREE_SIDE_EFFECTS (arg1))
return build_int_cst (type, 0);
if ((p0 = c_getstr (arg0, &s0))
&& (p1 = c_getstr (arg1, &s1))
if ((p0 = getbyterep (arg0, &s0))
&& (p1 = getbyterep (arg1, &s1))
&& s2 <= s0
&& s2 <= s1)
return build_cmp_result (type, memcmp (p0, p1, s2));
@ -1814,7 +1814,7 @@ fold_const_call (combined_fn fn, tree type, tree arg0, tree arg1, tree arg2)
&& !TREE_SIDE_EFFECTS (arg0)
&& !TREE_SIDE_EFFECTS (arg1))
return build_int_cst (type, 0);
if ((p0 = c_getstr (arg0, &s0))
if ((p0 = getbyterep (arg0, &s0))
&& s2 <= s0
&& target_char_cst_p (arg1, &c))
{

View File

@ -15485,19 +15485,19 @@ fold_build_pointer_plus_hwi_loc (location_t loc, tree ptr, HOST_WIDE_INT off)
ptr, size_int (off));
}
/* Return a pointer P to a NUL-terminated string containing the sequence
/* Return a pointer to a NUL-terminated string containing the sequence
of bytes corresponding to the representation of the object referred to
by SRC (or a subsequence of such bytes within it if SRC is a reference
to an initialized constant array plus some constant offset).
If STRSIZE is non-null, store the number of bytes in the constant
sequence including the terminating NUL byte. *STRSIZE is equal to
sizeof(A) - OFFSET where A is the array that stores the constant
sequence that SRC points to and OFFSET is the byte offset of SRC from
the beginning of A. SRC need not point to a string or even an array
of characters but may point to an object of any type. */
Set *STRSIZE the number of bytes in the constant sequence including
the terminating NUL byte. *STRSIZE is equal to sizeof(A) - OFFSET
where A is the array that stores the constant sequence that SRC points
to and OFFSET is the byte offset of SRC from the beginning of A. SRC
need not point to a string or even an array of characters but may point
to an object of any type. */
const char *
c_getstr (tree src, unsigned HOST_WIDE_INT *strsize /* = NULL */)
getbyterep (tree src, unsigned HOST_WIDE_INT *strsize)
{
/* The offset into the array A storing the string, and A's byte size. */
tree offset_node;
@ -15506,7 +15506,10 @@ c_getstr (tree src, unsigned HOST_WIDE_INT *strsize /* = NULL */)
if (strsize)
*strsize = 0;
src = string_constant (src, &offset_node, &mem_size, NULL);
if (strsize)
src = byte_representation (src, &offset_node, &mem_size, NULL);
else
src = string_constant (src, &offset_node, &mem_size, NULL);
if (!src)
return NULL;
@ -15574,6 +15577,18 @@ c_getstr (tree src, unsigned HOST_WIDE_INT *strsize /* = NULL */)
return offset < init_bytes ? string + offset : "";
}
/* Return a pointer to a NUL-terminated string corresponding to
the expression STR referencing a constant string, possibly
involving a constant offset. Return null if STR either doesn't
reference a constant string or if it involves a nonconstant
offset. */
const char *
c_getstr (tree str)
{
return getbyterep (str, NULL);
}
/* Given a tree T, compute which bits in T may be nonzero. */
wide_int

View File

@ -199,7 +199,8 @@ extern bool expr_not_equal_to (tree t, const wide_int &);
extern tree const_unop (enum tree_code, tree, tree);
extern tree const_binop (enum tree_code, tree, tree, tree);
extern bool negate_mathfn_p (combined_fn);
extern const char *c_getstr (tree, unsigned HOST_WIDE_INT * = NULL);
extern const char *getbyterep (tree, unsigned HOST_WIDE_INT *);
extern const char *c_getstr (tree);
extern wide_int tree_nonzero_bits (const_tree);
/* Return OFF converted to a pointer offset type suitable as offset for

View File

@ -774,7 +774,7 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
strlenopt tests that rely on it for passing are adjusted, this
hack can be removed. */
&& !c_strlen (src, 1)
&& !((tmp_str = c_getstr (src, &tmp_len)) != NULL
&& !((tmp_str = getbyterep (src, &tmp_len)) != NULL
&& memchr (tmp_str, 0, tmp_len) == NULL)
&& !(srctype
&& AGGREGATE_TYPE_P (srctype)
@ -2464,8 +2464,8 @@ gimple_fold_builtin_string_compare (gimple_stmt_iterator *gsi)
For nul-terminated strings then adjusted to their length so that
LENx == NULPOSx holds. */
unsigned HOST_WIDE_INT len1 = HOST_WIDE_INT_MAX, len2 = len1;
const char *p1 = c_getstr (str1, &len1);
const char *p2 = c_getstr (str2, &len2);
const char *p1 = getbyterep (str1, &len1);
const char *p2 = getbyterep (str2, &len2);
/* The position of the terminating nul character if one exists, otherwise
a value greater than LENx. */
@ -2662,7 +2662,7 @@ gimple_fold_builtin_memchr (gimple_stmt_iterator *gsi)
unsigned HOST_WIDE_INT length = tree_to_uhwi (len);
unsigned HOST_WIDE_INT string_length;
const char *p1 = c_getstr (arg1, &string_length);
const char *p1 = getbyterep (arg1, &string_length);
if (p1)
{

View File

@ -0,0 +1,94 @@
/* PR middle-end/78257 - missing memcmp optimization with constant arrays
{ dg-do compile }
{ dg-options "-O -Wall -fdump-tree-optimized" } */
typedef __INT8_TYPE__ int8_t;
typedef __INT16_TYPE__ int16_t;
typedef __INT32_TYPE__ int32_t;
typedef __SIZE_TYPE__ size_t;
extern void* memchr (const void*, int, size_t);
/* Verify that initializers for flexible array members are handled
correctly. */
struct SX
{
/* offset */
/* 0 */ int32_t n;
/* 4 */ int8_t: 1;
/* 6 */ int16_t a[];
};
_Static_assert (__builtin_offsetof (struct SX, a) == 6);
const struct SX sx =
{
0x11121314, { 0x2122, 0x3132, 0x4142, 0x5152 }
};
const char sx_rep[] =
{
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
0x11, 0x12, 0x13, 0x14, 0, 0, 0x21, 0x22, 0x31, 0x32, 0x41, 0x42, 0x51, 0x52
#else
0x14, 0x13, 0x12, 0x11, 0, 0, 0x22, 0x21, 0x32, 0x31, 0x42, 0x41, 0x52, 0x51
#endif
};
void test_find (void)
{
int n = 0, nb = (const char*)&sx.a[4] - (const char*)&sx;
const char *p = (const char*)&sx, *q = sx_rep;
if (nb != sizeof sx_rep)
__builtin_abort ();
n += p == memchr (p, q[ 0], nb);
n += p + 1 == memchr (p, q[ 1], nb);
n += p + 2 == memchr (p, q[ 2], nb);
n += p + 3 == memchr (p, q[ 3], nb);
n += p + 4 == memchr (p, q[ 4], nb);
n += p + 4 == memchr (p, q[ 5], nb);
n += p + 6 == memchr (p, q[ 6], nb);
n += p + 7 == memchr (p, q[ 7], nb);
n += p + 8 == memchr (p, q[ 8], nb);
n += p + 9 == memchr (p, q[ 9], nb);
n += p + 10 == memchr (p, q[10], nb);
n += p + 11 == memchr (p, q[11], nb);
n += p + 12 == memchr (p, q[12], nb);
n += p + 13 == memchr (p, q[13], nb);
if (n != 14)
__builtin_abort ();
}
void test_not_find (void)
{
int n = 0, nb = (const char*)&sx.a[4] - (const char*)&sx;
const char *p = (const char*)&sx, *q = sx_rep;
if (nb != sizeof sx_rep)
__builtin_abort ();
n += 0 == memchr (p, 0xff, nb);
n += 0 == memchr (p + 1, q[ 0], nb - 1);
n += 0 == memchr (p + 2, q[ 1], nb - 2);
n += 0 == memchr (p + 3, q[ 2], nb - 3);
n += 0 == memchr (p + 4, q[ 3], nb - 4);
n += 0 == memchr (p + 6, q[ 4], nb - 6);
n += 0 == memchr (p + 7, q[ 6], nb - 7);
n += 0 == memchr (p + 8, q[ 7], nb - 8);
n += 0 == memchr (p + 9, q[ 8], nb - 9);
n += 0 == memchr (p + 10, q[ 9], nb - 10);
n += 0 == memchr (p + 11, q[10], nb - 11);
n += 0 == memchr (p + 12, q[11], nb - 12);
n += 0 == memchr (p + 13, q[12], nb - 13);
n += 0 == memchr (p + 14, q[13], nb - 14);
if (n != 14)
__builtin_abort ();
}
/* { dg-final { scan-tree-dump-not "abort" "optimized" } } */

View File

@ -0,0 +1,183 @@
/* PR middle-end/78257 - missing memcmp optimization with constant arrays
{ dg-do compile }
{ dg-options "-O -Wall -fdump-tree-optimized" } */
#define assert(e) ((e) ? (void)0 : __builtin_abort ())
typedef __INT32_TYPE__ int32_t;
extern int memcmp (const void*, const void*, __SIZE_TYPE__);
const int32_t i_0 = 0;
const int32_t j_0 = 0;
void eq_i0_j0 (void)
{
const char *pi = (char*)&i_0, *pj = (char*)&j_0;
int n = 0;
n += 0 == memcmp (pi, pj, sizeof (int32_t));
n += 0 == memcmp (pi + 1, pj + 1, sizeof (int32_t) - 1);
n += 0 == memcmp (pi + 2, pj + 2, sizeof (int32_t) - 2);
n += 0 == memcmp (pi + 3, pj + 3, sizeof (int32_t) - 3);
n += 0 == memcmp (pi + 4, pj + 4, sizeof (int32_t) - 4);
assert (n == 5);
}
const int32_t i1234 = 1234;
const int32_t j1234 = 1234;
void eq_i1234_j1245 (void)
{
const char *pi = (char*)&i1234, *pj = (char*)&j1234;
int n = 0;
n += 0 == memcmp (pi, pj, sizeof (int32_t));
n += 0 == memcmp (pi + 1, pj + 1, sizeof (int32_t) - 1);
n += 0 == memcmp (pi + 2, pj + 2, sizeof (int32_t) - 2);
n += 0 == memcmp (pi + 3, pj + 3, sizeof (int32_t) - 3);
n += 0 == memcmp (pi + 4, pj + 4, sizeof (int32_t) - 4);
assert (n == 5);
}
const int32_t a1[2] = { 1234 };
const int32_t b1[2] = { 1234 };
void eq_a1_b1 (void)
{
const char *pi = (char*)&a1, *pj = (char*)&b1;
int n = 0, nb = sizeof a1;
n += 0 == memcmp (pi, pj, nb);
n += 0 == memcmp (pi + 1, pj + 1, nb - 1);
n += 0 == memcmp (pi + 2, pj + 2, nb - 2);
n += 0 == memcmp (pi + 3, pj + 3, nb - 3);
n += 0 == memcmp (pi + 4, pj + 4, nb - 4);
n += 0 == memcmp (pi + 5, pj + 5, nb - 5);
n += 0 == memcmp (pi + 6, pj + 6, nb - 6);
n += 0 == memcmp (pi + 7, pj + 7, nb - 7);
n += 0 == memcmp (pi + 8, pj + 8, nb - 8);
assert (n == 9);
}
const int32_t a2[2] = { 1234 };
const int32_t b2[2] = { 1234, 0 };
void eq_a2_b2 (void)
{
const char *pi = (char*)&a2, *pj = (char*)&b2;
int n = 0, nb = sizeof a2;
n += 0 == memcmp (pi, pj, nb);
n += 0 == memcmp (pi + 1, pj + 1, nb - 1);
n += 0 == memcmp (pi + 2, pj + 2, nb - 2);
n += 0 == memcmp (pi + 3, pj + 3, nb - 3);
n += 0 == memcmp (pi + 4, pj + 4, nb - 4);
n += 0 == memcmp (pi + 5, pj + 5, nb - 5);
n += 0 == memcmp (pi + 6, pj + 6, nb - 6);
n += 0 == memcmp (pi + 7, pj + 7, nb - 7);
n += 0 == memcmp (pi + 8, pj + 8, nb - 8);
assert (n == 9);
}
const int32_t a5[5] = { [3] = 1234, [1] = 0 };
const int32_t b5[5] = { 0, 0, 0, 1234 };
void eq_a5_b5 (void)
{
int n = 0, b = sizeof a5;
const char *pi = (char*)a5, *pj = (char*)b5;
n += 0 == memcmp (pi, pj, b);
n += 0 == memcmp (pi + 1, pj + 1, b - 1);
n += 0 == memcmp (pi + 2, pj + 2, b - 2);
n += 0 == memcmp (pi + 3, pj + 3, b - 3);
n += 0 == memcmp (pi + 4, pj + 4, b - 4);
n += 0 == memcmp (pi + 5, pj + 5, b - 5);
n += 0 == memcmp (pi + 6, pj + 6, b - 6);
n += 0 == memcmp (pi + 7, pj + 7, b - 7);
n += 0 == memcmp (pi + 8, pj + 8, b - 8);
n += 0 == memcmp (pi + 9, pj + 9, b - 9);
n += 0 == memcmp (pi + 10, pj + 10, b - 10);
n += 0 == memcmp (pi + 11, pj + 11, b - 11);
n += 0 == memcmp (pi + 12, pj + 12, b - 12);
n += 0 == memcmp (pi + 13, pj + 13, b - 13);
n += 0 == memcmp (pi + 14, pj + 14, b - 14);
n += 0 == memcmp (pi + 15, pj + 15, b - 15);
n += 0 == memcmp (pi + 16, pj + 16, b - 16);
n += 0 == memcmp (pi + 17, pj + 17, b - 17);
n += 0 == memcmp (pi + 18, pj + 18, b - 18);
n += 0 == memcmp (pi + 19, pj + 19, b - 19);
assert (n == 20);
}
const int32_t a19[19] = { [13] = 13, [8] = 8, [4] = 4, [1] = 1 };
const int32_t b19[19] = { 0, 1, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 0, 13 };
void eq_a19_b19 (void)
{
int n = 0, b = sizeof a19;
const char *pi = (char*)a19, *pj = (char*)b19;
n += 0 == memcmp (pi, pj, b);
n += 0 == memcmp (pi + 1, pj + 1, b - 1);
n += 0 == memcmp (pi + 2, pj + 2, b - 2);
n += 0 == memcmp (pi + 3, pj + 3, b - 3);
n += 0 == memcmp (pi + 14, pj + 14, b - 14);
n += 0 == memcmp (pi + 15, pj + 15, b - 15);
n += 0 == memcmp (pi + 16, pj + 16, b - 16);
n += 0 == memcmp (pi + 17, pj + 17, b - 17);
n += 0 == memcmp (pi + 28, pj + 28, b - 28);
n += 0 == memcmp (pi + 29, pj + 29, b - 29);
n += 0 == memcmp (pi + 30, pj + 30, b - 30);
n += 0 == memcmp (pi + 31, pj + 31, b - 31);
n += 0 == memcmp (pi + 42, pj + 42, b - 42);
n += 0 == memcmp (pi + 43, pj + 43, b - 43);
n += 0 == memcmp (pi + 44, pj + 44, b - 44);
n += 0 == memcmp (pi + 45, pj + 45, b - 45);
n += 0 == memcmp (pi + 56, pj + 56, b - 56);
n += 0 == memcmp (pi + 57, pj + 57, b - 57);
n += 0 == memcmp (pi + 58, pj + 58, b - 58);
n += 0 == memcmp (pi + 59, pj + 59, b - 59);
assert (n == 20);
}
const int32_t A20[20] = { [13] = 14, [8] = 8, [4] = 4, [1] = 1 };
const int32_t b20[20] = { 0, 1, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 0, 13 };
void gt_A20_b20 (void)
{
int n = memcmp (A20, b20, sizeof A20) > 0;
assert (n == 1);
}
const int32_t a21[21] = { [13] = 12, [8] = 8, [4] = 4, [1] = 1 };
const int32_t B21[21] = { 0, 1, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 0, 13 };
void lt_a21_B21 (void)
{
int n = memcmp (a21, B21, sizeof a21) < 0;
assert (n == 1);
}
/* { dg-final { scan-tree-dump-not "abort" "optimized" } } */

View File

@ -0,0 +1,349 @@
/* PR middle-end/78257 - missing memcmp optimization with constant arrays
{ dg-do compile }
{ dg-options "-O -Wall -fdump-tree-optimized" }
{ dg-skip-if "missing data representation" { "pdp11-*-*" } } */
#define offsetof(T, m) __builtin_offsetof (T, m)
typedef __INT8_TYPE__ int8_t;
typedef __INT16_TYPE__ int16_t;
typedef __INT32_TYPE__ int32_t;
typedef __INT64_TYPE__ int64_t;
typedef __SIZE_TYPE__ size_t;
extern int memcmp (const void*, const void*, size_t);
const int32_t ia4[4] = { 0x11121314, 0x21222324, 0x31323334, 0x41424344 };
const int32_t ia4_des[4] =
{ [2] = 0x31323334, [0] = 0x11121314, 0x21222324, [3] = 0x41424344 };
const char ia4_rep[] =
{
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
"\x11\x12\x13\x14" "\x21\x22\x23\x24"
"\x31\x32\x33\x34" "\x41\x42\x43\x44"
#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
"\x14\x13\x12\x11" "\x24\x23\x22\x21"
"\x34\x33\x32\x31" "\x44\x43\x42\x41"
#endif
};
void eq_ia4 (void)
{
int n = 0, b = sizeof ia4;
const char *p = (const char*)ia4, *q = ia4_rep;
n += memcmp (p, q, b);
n += memcmp (p + 1, q + 1, b - 1);
n += memcmp (p + 2, q + 2, b - 2);
n += memcmp (p + 3, q + 3, b - 3);
n += memcmp (p + 4, q + 4, b - 4);
n += memcmp (p + 5, q + 5, b - 5);
n += memcmp (p + 6, q + 6, b - 6);
n += memcmp (p + 7, q + 7, b - 7);
n += memcmp (p + 8, q + 8, b - 8);
n += memcmp (p + 9, q + 9, b - 9);
n += memcmp (p + 10, q + 10, b - 10);
n += memcmp (p + 11, q + 11, b - 11);
n += memcmp (p + 12, q + 12, b - 12);
n += memcmp (p + 13, q + 13, b - 13);
n += memcmp (p + 14, q + 14, b - 14);
n += memcmp (p + 15, q + 15, b - 15);
n += memcmp (p + 16, q + 16, b - 16);
p = (const char*)ia4_des;
n += memcmp (p, q, b);
n += memcmp (p + 1, q + 1, b - 1);
n += memcmp (p + 2, q + 2, b - 2);
n += memcmp (p + 3, q + 3, b - 3);
n += memcmp (p + 4, q + 4, b - 4);
n += memcmp (p + 5, q + 5, b - 5);
n += memcmp (p + 6, q + 6, b - 6);
n += memcmp (p + 7, q + 7, b - 7);
n += memcmp (p + 8, q + 8, b - 8);
n += memcmp (p + 9, q + 9, b - 9);
n += memcmp (p + 10, q + 10, b - 10);
n += memcmp (p + 11, q + 11, b - 11);
n += memcmp (p + 12, q + 12, b - 12);
n += memcmp (p + 13, q + 13, b - 13);
n += memcmp (p + 14, q + 14, b - 14);
n += memcmp (p + 15, q + 15, b - 15);
n += memcmp (p + 16, q + 16, b - 16);
if (n != 0)
__builtin_abort ();
}
const float fa4[4] = { 1.0, 2.0, 3.0, 4.0 };
const float fa4_des[4] = { [0] = fa4[0], [1] = 2.0, [2] = fa4[2], [3] = 4.0 };
void eq_fa4 (void)
{
int n = 0, b = sizeof fa4;
const char *p = (const char*)fa4, *q = (const char*)fa4_des;
n += memcmp (p, q, b);
n += memcmp (p + 1, q + 1, b - 1);
n += memcmp (p + 2, q + 2, b - 2);
n += memcmp (p + 3, q + 3, b - 3);
n += memcmp (p + 4, q + 4, b - 4);
n += memcmp (p + 5, q + 5, b - 5);
n += memcmp (p + 6, q + 6, b - 6);
n += memcmp (p + 7, q + 7, b - 7);
n += memcmp (p + 8, q + 8, b - 8);
n += memcmp (p + 9, q + 9, b - 9);
n += memcmp (p + 10, q + 10, b - 10);
n += memcmp (p + 11, q + 11, b - 11);
n += memcmp (p + 12, q + 12, b - 12);
n += memcmp (p + 13, q + 13, b - 13);
n += memcmp (p + 14, q + 14, b - 14);
n += memcmp (p + 15, q + 15, b - 15);
n += memcmp (p + 16, q + 16, b - 16);
if (n != 0)
__builtin_abort ();
}
/* Verify "greater than" comparison with the difference in the last byte. */
const char ia4_xrep_16[sizeof ia4] =
{
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
0x11, 0x12, 0x13, 0x14, 0x21, 0x22, 0x23, 0x24,
0x31, 0x32, 0x33, 0x34, 0x41, 0x42, 0x43
#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
0x14, 0x13, 0x12, 0x11, 0x24, 0x23, 0x22, 0x21,
0x34, 0x33, 0x32, 0x31, 0x44, 0x43, 0x42
#endif
};
void gt_ia4 (void)
{
int n = 0, b = sizeof ia4;
const char *p = (const char*)ia4, *q = ia4_xrep_16;
n += 0 < memcmp (p, q, b);
n += 0 < memcmp (p + 1, q + 1, b - 1);
n += 0 < memcmp (p + 2, q + 2, b - 2);
n += 0 < memcmp (p + 3, q + 3, b - 3);
n += 0 < memcmp (p + 4, q + 4, b - 4);
n += 0 < memcmp (p + 5, q + 5, b - 5);
n += 0 < memcmp (p + 6, q + 6, b - 6);
n += 0 < memcmp (p + 7, q + 7, b - 7);
n += 0 < memcmp (p + 8, q + 8, b - 8);
n += 0 < memcmp (p + 9, q + 9, b - 9);
n += 0 < memcmp (p + 10, q + 10, b - 10);
n += 0 < memcmp (p + 11, q + 11, b - 11);
n += 0 < memcmp (p + 12, q + 12, b - 12);
n += 0 < memcmp (p + 13, q + 13, b - 13);
n += 0 < memcmp (p + 14, q + 14, b - 14);
n += 0 < memcmp (p + 15, q + 15, b - 15);
if (n != 16)
__builtin_abort ();
}
struct S8_16_32
{
int8_t i8;
int16_t i16;
int32_t i32;
};
_Static_assert (sizeof (struct S8_16_32) == 8);
const struct S8_16_32 s8_16_32 = { 1, 0x2122, 0x31323334 };
const struct S8_16_32 s8_16_32_des =
{ .i8 = 1, .i16 = 0x2122, .i32 = 0x31323334 };
const char s8_16_32_rep[] =
{
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
1, 0, 0x21, 0x22, 0x31, 0x32, 0x33, 0x34
#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
1, 0, 0x22, 0x21, 0x34, 0x33, 0x32, 0x31
#endif
};
void eq_s8_16_32 (void)
{
int n = 0, b = sizeof s8_16_32;
const char *p = (char*)&s8_16_32, *q = s8_16_32_rep;
n += memcmp (p, q, b);
n += memcmp (p + 1, q + 1, b - 1);
n += memcmp (p + 2, q + 2, b - 2);
n += memcmp (p + 3, q + 3, b - 3);
n += memcmp (p + 4, q + 4, b - 4);
n += memcmp (p + 5, q + 5, b - 5);
n += memcmp (p + 6, q + 6, b - 6);
n += memcmp (p + 7, q + 7, b - 7);
p = (char*)&s8_16_32_des;
n += memcmp (p, q, b);
n += memcmp (p + 1, q + 1, b - 1);
n += memcmp (p + 2, q + 2, b - 2);
n += memcmp (p + 3, q + 3, b - 3);
n += memcmp (p + 4, q + 4, b - 4);
n += memcmp (p + 5, q + 5, b - 5);
n += memcmp (p + 6, q + 6, b - 6);
n += memcmp (p + 7, q + 7, b - 7);
if (n != 0)
__builtin_abort ();
}
struct S8_16_32_64
{
/* 0 */ int8_t i8;
/* 1 */ int8_t: 1;
/* 2 */ int16_t i16;
/* 4 */ int32_t: 1;
/* 8 */ int32_t i32;
/* 12 */ int32_t: 1;
/* 16 */ int64_t i64;
/* 24 */ int8_t: 0;
};
_Static_assert (offsetof (struct S8_16_32_64, i16) == 2);
_Static_assert (offsetof (struct S8_16_32_64, i32) == 8);
_Static_assert (offsetof (struct S8_16_32_64, i64) == 16);
_Static_assert (sizeof (struct S8_16_32_64) == 24);
const struct S8_16_32_64 s8_16_32_64 =
{ 1, 0x2122, 0x31323334, 0x4142434445464748LLU };
const char s8_16_32_64_rep[sizeof s8_16_32_64] =
{
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
"\x01" "\x00" "\x21\x22" "\x00\x00\x00\x00" "\x31\x32\x33\x34"
"\x00\x00\x00\x00" "\x41\x42\x43\x44\x45\x46\x47\x48"
#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
"\x01" "\x00" "\x22\x21" "\x00\x00\x00\x00" "\x34\x33\x32\x31"
"\x00\x00\x00\x00" "\x48\x47\x46\x45\x44\x43\x42\x41"
#endif
};
const struct S8_16_32_64 s8_16_32_64_des =
{ .i64 = 0x4142434445464748LLU, .i16 = 0x2122, .i32 = 0x31323334, .i8 = 1 };
void eq_8_16_32_64 (void)
{
int n = 0, b = sizeof s8_16_32_64;
const char *p = (char*)&s8_16_32_64, *q = s8_16_32_64_rep;
n += memcmp (p, q, b);
n += memcmp (p + 1, q + 1, b - 1);
n += memcmp (p + 2, q + 2, b - 2);
n += memcmp (p + 3, q + 3, b - 3);
n += memcmp (p + 4, q + 4, b - 4);
n += memcmp (p + 5, q + 5, b - 5);
n += memcmp (p + 6, q + 6, b - 6);
n += memcmp (p + 7, q + 7, b - 7);
n += memcmp (p + 8, q + 8, b - 8);
n += memcmp (p + 9, q + 9, b - 9);
n += memcmp (p + 10, q + 10, b - 10);
n += memcmp (p + 11, q + 11, b - 11);
n += memcmp (p + 12, q + 12, b - 12);
n += memcmp (p + 13, q + 13, b - 13);
n += memcmp (p + 14, q + 14, b - 14);
n += memcmp (p + 15, q + 15, b - 15);
n += memcmp (p + 16, q + 16, b - 16);
n += memcmp (p + 17, q + 17, b - 17);
n += memcmp (p + 18, q + 18, b - 18);
n += memcmp (p + 19, q + 19, b - 19);
n += memcmp (p + 20, q + 20, b - 20);
n += memcmp (p + 21, q + 21, b - 21);
n += memcmp (p + 22, q + 22, b - 22);
n += memcmp (p + 23, q + 23, b - 23);
p = (char*)&s8_16_32_64_des;
n += memcmp (p, q, b);
n += memcmp (p + 1, q + 1, b - 1);
n += memcmp (p + 2, q + 2, b - 2);
n += memcmp (p + 3, q + 3, b - 3);
n += memcmp (p + 4, q + 4, b - 4);
n += memcmp (p + 5, q + 5, b - 5);
n += memcmp (p + 6, q + 6, b - 6);
n += memcmp (p + 7, q + 7, b - 7);
n += memcmp (p + 8, q + 8, b - 8);
n += memcmp (p + 9, q + 9, b - 9);
n += memcmp (p + 10, q + 10, b - 10);
n += memcmp (p + 11, q + 11, b - 11);
n += memcmp (p + 12, q + 12, b - 12);
n += memcmp (p + 13, q + 13, b - 13);
n += memcmp (p + 14, q + 14, b - 14);
n += memcmp (p + 15, q + 15, b - 15);
n += memcmp (p + 16, q + 16, b - 16);
n += memcmp (p + 17, q + 17, b - 17);
n += memcmp (p + 18, q + 18, b - 18);
n += memcmp (p + 19, q + 19, b - 19);
n += memcmp (p + 20, q + 20, b - 20);
n += memcmp (p + 21, q + 21, b - 21);
n += memcmp (p + 22, q + 22, b - 22);
n += memcmp (p + 23, q + 23, b - 23);
if (n != 0)
__builtin_abort ();
}
struct S64_x_3
{
int64_t i64a[3];
};
_Static_assert (sizeof (struct S64_x_3) == 24);
const struct S64_x_3 s64_x_3 =
{ { 0x0000000021220001LLU, 0x0000000031323334LLU, 0x4142434445464748LLU } };
const char s64_x_3_rep[sizeof s64_x_3] =
{
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
"\x00\x00\x00\x00\x21\x22\x00\x01"
"\x00\x00\x00\x00\x31\x32\x33\x34"
"\x41\x42\x43\x44\x45\x46\x47\x48"
#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
"\x01\x00\x22\x21\x00\x00\x00\x00"
"\x34\x33\x32\x31\x00\x00\x00\x00"
"\x48\x47\x46\x45\x44\x43\x42\x41"
#endif
};
void eq_64_x_3 (void)
{
int n = 0, b = sizeof s8_16_32_64;
const char *p = (char*)&s8_16_32_64, *q = s64_x_3_rep;
n += memcmp (p, q, b);
n += memcmp (p + 1, q + 1, b - 1);
n += memcmp (p + 2, q + 2, b - 2);
n += memcmp (p + 3, q + 3, b - 3);
n += memcmp (p + 4, q + 4, b - 4);
n += memcmp (p + 5, q + 5, b - 5);
n += memcmp (p + 6, q + 6, b - 6);
n += memcmp (p + 7, q + 7, b - 7);
n += memcmp (p + 8, q + 8, b - 8);
n += memcmp (p + 9, q + 9, b - 9);
n += memcmp (p + 10, q + 10, b - 10);
n += memcmp (p + 11, q + 11, b - 11);
n += memcmp (p + 12, q + 12, b - 12);
n += memcmp (p + 13, q + 13, b - 13);
n += memcmp (p + 14, q + 14, b - 14);
n += memcmp (p + 15, q + 15, b - 15);
n += memcmp (p + 16, q + 16, b - 16);
n += memcmp (p + 17, q + 17, b - 17);
n += memcmp (p + 18, q + 18, b - 18);
n += memcmp (p + 19, q + 19, b - 19);
n += memcmp (p + 20, q + 20, b - 20);
n += memcmp (p + 21, q + 21, b - 21);
n += memcmp (p + 22, q + 22, b - 22);
n += memcmp (p + 23, q + 23, b - 23);
if (n != 0)
__builtin_abort ();
}
/* { dg-final { scan-tree-dump-not "abort" "optimized" } } */

View File

@ -0,0 +1,81 @@
/* PR middle-end/78257 - missing memcmp optimization with constant arrays
{ dg-do compile }
{ dg-options "-O -Wall -fdump-tree-optimized" } */
typedef __INT8_TYPE__ int8_t;
typedef __INT16_TYPE__ int16_t;
typedef __INT32_TYPE__ int32_t;
typedef __SIZE_TYPE__ size_t;
extern int memcmp (const void*, const void*, size_t);
/* Verify that initializers for flexible array members are handled
correctly. */
struct Si16_x
{
int16_t n, a[];
};
const struct Si16_x si16_4 =
{
0x1112, { 0x2122, 0x3132, 0x4142 }
};
const char si16_4_rep[] =
{
0x12, 0x11, 0x22, 0x21, 0x32, 0x31, 0x42, 0x41
};
void eq_si16_x (void)
{
int n = 0, b = sizeof si16_4_rep;
const char *p = (const char*)&si16_4, *q = si16_4_rep;
n += memcmp (p, q, b);
n += memcmp (p + 1, q + 1, b - 1);
n += memcmp (p + 2, q + 2, b - 2);
n += memcmp (p + 3, q + 3, b - 3);
n += memcmp (p + 4, q + 4, b - 4);
n += memcmp (p + 5, q + 5, b - 5);
n += memcmp (p + 6, q + 6, b - 6);
n += memcmp (p + 7, q + 7, b - 7);
n += memcmp (p + 8, q + 8, b - 8);
p = (const char*)&si16_4.n;
n += memcmp (p, q, b);
n += memcmp (p + 1, q + 1, b - 1);
n += memcmp (p + 2, q + 2, b - 2);
n += memcmp (p + 3, q + 3, b - 3);
n += memcmp (p + 4, q + 4, b - 4);
n += memcmp (p + 5, q + 5, b - 5);
n += memcmp (p + 6, q + 6, b - 6);
n += memcmp (p + 7, q + 7, b - 7);
n += memcmp (p + 8, q + 8, b - 8);
p = (const char*)si16_4.a;
q = si16_4_rep + 2;
n += memcmp (p, q, b - 2);
n += memcmp (p + 1, q + 1, b - 3);
n += memcmp (p + 2, q + 2, b - 4);
n += memcmp (p + 3, q + 3, b - 5);
n += memcmp (p + 4, q + 4, b - 6);
n += memcmp (p + 5, q + 5, b - 7);
n += memcmp (p + 6, q + 6, b - 8);
p = (const char*)&si16_4.a[1];
q = si16_4_rep + 4;
n += memcmp (p, q, b - 4);
n += memcmp (p + 1, q + 1, b - 5);
n += memcmp (p + 2, q + 2, b - 6);
n += memcmp (p + 3, q + 3, b - 7);
n += memcmp (p + 4, q + 4, b - 8);
if (n != 0)
__builtin_abort ();
}
/* { dg-final { scan-tree-dump-not "abort" "optimized" } } */