]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
PR tree-optimization/78257 - missing memcmp optimization with constant arrays
authorMartin Sebor <msebor@redhat.com>
Fri, 14 Aug 2020 23:11:53 +0000 (17:11 -0600)
committerMartin Sebor <msebor@redhat.com>
Fri, 14 Aug 2020 23:11:53 +0000 (17:11 -0600)
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.

gcc/builtins.c
gcc/expr.c
gcc/expr.h
gcc/fold-const-call.c
gcc/fold-const.c
gcc/fold-const.h
gcc/gimple-fold.c
gcc/testsuite/gcc.dg/memchr.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/memcmp-2.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/memcmp-3.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/memcmp-4.c [new file with mode: 0644]

index beb56e06d8a3d9240412762df621f05a96e255cb..8845816aebdd00670f520ddbaefb69019d848fd2 100644 (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)
index 2406f9039eabd7f0c5bd16124eadb486b460b5eb..dd2200ddea87324ffdd97053d4b6b1e6dcbd1986 100644 (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;
 }
-\f
-/* 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;
 }
-\f
+
+/* 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
index 725991ff217c4011f33982cd18c2008e67cb7fb9..88d55bac30efaa2345ab831287c46839b76e1113 100644 (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 *);
 
index c9e368db9d01b77a141c29056ed095a329cce7ac..11ed47db3d95b95da2cb4e42d95f1c7d41ed2544 100644 (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))
        {
index 5d27927f6bfd788011e3d0a872557e5be8f08521..9fc4c2a06fb14886394c226e0ac05aece6c96952 100644 (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 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
index 0f788a458f21e802431fdeaaa449c20e35c0b048..0c0f5fd46ccc7292bf94ed3c84c3f3ebad26d251 100644 (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
index 436881728887bc4dc30556cdd1ecae4d19f5ccaf..db56cb6aa479d8eaa64cf4317a49dfad32025d8f 100644 (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)
     {
diff --git a/gcc/testsuite/gcc.dg/memchr.c b/gcc/testsuite/gcc.dg/memchr.c
new file mode 100644 (file)
index 0000000..fb21d58
--- /dev/null
@@ -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" } } */
diff --git a/gcc/testsuite/gcc.dg/memcmp-2.c b/gcc/testsuite/gcc.dg/memcmp-2.c
new file mode 100644 (file)
index 0000000..ff99c12
--- /dev/null
@@ -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" } } */
diff --git a/gcc/testsuite/gcc.dg/memcmp-3.c b/gcc/testsuite/gcc.dg/memcmp-3.c
new file mode 100644 (file)
index 0000000..b5b8ac1
--- /dev/null
@@ -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" } } */
diff --git a/gcc/testsuite/gcc.dg/memcmp-4.c b/gcc/testsuite/gcc.dg/memcmp-4.c
new file mode 100644 (file)
index 0000000..bbac719
--- /dev/null
@@ -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" } } */