diff options
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/builtins.c | 14 | ||||
-rw-r--r-- | gcc/expr.c | 180 | ||||
-rw-r--r-- | gcc/expr.h | 8 | ||||
-rw-r--r-- | gcc/fold-const-call.c | 6 | ||||
-rw-r--r-- | gcc/fold-const.c | 33 | ||||
-rw-r--r-- | gcc/fold-const.h | 3 | ||||
-rw-r--r-- | gcc/gimple-fold.c | 8 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/memchr.c | 94 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/memcmp-2.c | 183 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/memcmp-3.c | 349 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/memcmp-4.c | 81 |
11 files changed, 915 insertions, 44 deletions
diff --git a/gcc/builtins.c b/gcc/builtins.c index beb56e0..8845816 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -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) @@ -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 @@ -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 *); diff --git a/gcc/fold-const-call.c b/gcc/fold-const-call.c index c9e368d..11ed47d 100644 --- a/gcc/fold-const-call.c +++ b/gcc/fold-const-call.c @@ -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)) { diff --git a/gcc/fold-const.c b/gcc/fold-const.c index 5d27927..9fc4c2a 100644 --- a/gcc/fold-const.c +++ b/gcc/fold-const.c @@ -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 diff --git a/gcc/fold-const.h b/gcc/fold-const.h index 0f788a4..0c0f5fd 100644 --- a/gcc/fold-const.h +++ b/gcc/fold-const.h @@ -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 diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c index 4368817..db56cb6 100644 --- a/gcc/gimple-fold.c +++ b/gcc/gimple-fold.c @@ -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 index 0000000..fb21d58 --- /dev/null +++ b/gcc/testsuite/gcc.dg/memchr.c @@ -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 index 0000000..ff99c12 --- /dev/null +++ b/gcc/testsuite/gcc.dg/memcmp-2.c @@ -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 index 0000000..b5b8ac1 --- /dev/null +++ b/gcc/testsuite/gcc.dg/memcmp-3.c @@ -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 index 0000000..bbac719 --- /dev/null +++ b/gcc/testsuite/gcc.dg/memcmp-4.c @@ -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" } } */ |