The following fixes PR61762 and 61894 by making folding from const aggregates use native_encode/interpret when reaching a tcc_constant initializer. To make this work efficiently the patch introduces the ability to encode only a part of a tree expression, namely from [off, off + len] when off is not -1 (in which case old semantics apply).
Bootstrapped on x86_64-unknown-linux-gnu, testing in progress. Richard. 2014-07-24 Richard Biener <rguent...@suse.de> PR middle-end/61762 PR middle-end/61894 * fold-const.c (native_encode_int): Add and handle offset parameter to do partial encodings of expr. (native_encode_fixed): Likewise. (native_encode_real): Likewise. (native_encode_complex): Likewise. (native_encode_vector): Likewise. (native_encode_string): Likewise. (native_encode_expr): Likewise. * fold-const.c (native_encode_expr): Add offset parameter defaulting to -1. * gimple-fold.c (fold_string_cst_ctor_reference): Remove. (fold_ctor_reference): Handle all reads from tcc_constant ctors. * gcc.dg/pr61762.c: New testcase. * gcc.dg/fold-cstring.c: Likewise. * gcc.dg/fold-cvect.c: Likewise. Index: gcc/fold-const.c =================================================================== *** gcc/fold-const.c.orig 2014-07-16 09:48:06.089217352 +0200 --- gcc/fold-const.c 2014-07-24 12:54:11.483860342 +0200 *************** fold_plusminus_mult_expr (location_t loc *** 7240,7254 **** upon failure. */ static int ! native_encode_int (const_tree expr, unsigned char *ptr, int len) { tree type = TREE_TYPE (expr); int total_bytes = GET_MODE_SIZE (TYPE_MODE (type)); int byte, offset, word, words; unsigned char value; ! if (total_bytes > len) return 0; words = total_bytes / UNITS_PER_WORD; for (byte = 0; byte < total_bytes; byte++) --- 7240,7257 ---- upon failure. */ static int ! native_encode_int (const_tree expr, unsigned char *ptr, int len, int off) { tree type = TREE_TYPE (expr); int total_bytes = GET_MODE_SIZE (TYPE_MODE (type)); int byte, offset, word, words; unsigned char value; ! if ((off == -1 && total_bytes > len) ! || off >= total_bytes) return 0; + if (off == -1) + off = 0; words = total_bytes / UNITS_PER_WORD; for (byte = 0; byte < total_bytes; byte++) *************** native_encode_int (const_tree expr, unsi *** 7271,7279 **** } else offset = BYTES_BIG_ENDIAN ? (total_bytes - 1) - byte : byte; ! ptr[offset] = value; } ! return total_bytes; } --- 7274,7284 ---- } else offset = BYTES_BIG_ENDIAN ? (total_bytes - 1) - byte : byte; ! if (offset >= off ! && offset - off < len) ! ptr[offset - off] = value; } ! return MIN (len, total_bytes - off); } *************** native_encode_int (const_tree expr, unsi *** 7283,7289 **** upon failure. */ static int ! native_encode_fixed (const_tree expr, unsigned char *ptr, int len) { tree type = TREE_TYPE (expr); enum machine_mode mode = TYPE_MODE (type); --- 7288,7294 ---- upon failure. */ static int ! native_encode_fixed (const_tree expr, unsigned char *ptr, int len, int off) { tree type = TREE_TYPE (expr); enum machine_mode mode = TYPE_MODE (type); *************** native_encode_fixed (const_tree expr, un *** 7303,7309 **** value = TREE_FIXED_CST (expr); i_value = double_int_to_tree (i_type, value.data); ! return native_encode_int (i_value, ptr, len); } --- 7308,7314 ---- value = TREE_FIXED_CST (expr); i_value = double_int_to_tree (i_type, value.data); ! return native_encode_int (i_value, ptr, len, off); } *************** native_encode_fixed (const_tree expr, un *** 7313,7319 **** upon failure. */ static int ! native_encode_real (const_tree expr, unsigned char *ptr, int len) { tree type = TREE_TYPE (expr); int total_bytes = GET_MODE_SIZE (TYPE_MODE (type)); --- 7318,7324 ---- upon failure. */ static int ! native_encode_real (const_tree expr, unsigned char *ptr, int len, int off) { tree type = TREE_TYPE (expr); int total_bytes = GET_MODE_SIZE (TYPE_MODE (type)); *************** native_encode_real (const_tree expr, uns *** 7325,7332 **** up to 192 bits. */ long tmp[6]; ! if (total_bytes > len) return 0; words = (32 / BITS_PER_UNIT) / UNITS_PER_WORD; real_to_target (tmp, TREE_REAL_CST_PTR (expr), TYPE_MODE (type)); --- 7330,7340 ---- up to 192 bits. */ long tmp[6]; ! if ((off == -1 && total_bytes > len) ! || off >= total_bytes) return 0; + if (off == -1) + off = 0; words = (32 / BITS_PER_UNIT) / UNITS_PER_WORD; real_to_target (tmp, TREE_REAL_CST_PTR (expr), TYPE_MODE (type)); *************** native_encode_real (const_tree expr, uns *** 7350,7358 **** } else offset = BYTES_BIG_ENDIAN ? 3 - byte : byte; ! ptr[offset + ((bitpos / BITS_PER_UNIT) & ~3)] = value; } ! return total_bytes; } /* Subroutine of native_encode_expr. Encode the COMPLEX_CST --- 7358,7369 ---- } else offset = BYTES_BIG_ENDIAN ? 3 - byte : byte; ! offset = offset + ((bitpos / BITS_PER_UNIT) & ~3); ! if (offset >= off ! && offset - off < len) ! ptr[offset - off] = value; } ! return MIN (len, total_bytes - off); } /* Subroutine of native_encode_expr. Encode the COMPLEX_CST *************** native_encode_real (const_tree expr, uns *** 7361,7378 **** upon failure. */ static int ! native_encode_complex (const_tree expr, unsigned char *ptr, int len) { int rsize, isize; tree part; part = TREE_REALPART (expr); ! rsize = native_encode_expr (part, ptr, len); ! if (rsize == 0) return 0; part = TREE_IMAGPART (expr); ! isize = native_encode_expr (part, ptr+rsize, len-rsize); ! if (isize != rsize) return 0; return rsize + isize; } --- 7372,7393 ---- upon failure. */ static int ! native_encode_complex (const_tree expr, unsigned char *ptr, int len, int off) { int rsize, isize; tree part; part = TREE_REALPART (expr); ! rsize = native_encode_expr (part, ptr, len, off); ! if (off == -1 ! && rsize == 0) return 0; part = TREE_IMAGPART (expr); ! if (off != -1) ! off = MAX (0, off - GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (part)))); ! isize = native_encode_expr (part, ptr+rsize, len-rsize, off); ! if (off == -1 ! && isize != rsize) return 0; return rsize + isize; } *************** native_encode_complex (const_tree expr, *** 7384,7390 **** upon failure. */ static int ! native_encode_vector (const_tree expr, unsigned char *ptr, int len) { unsigned i, count; int size, offset; --- 7399,7405 ---- upon failure. */ static int ! native_encode_vector (const_tree expr, unsigned char *ptr, int len, int off) { unsigned i, count; int size, offset; *************** native_encode_vector (const_tree expr, u *** 7396,7405 **** size = GET_MODE_SIZE (TYPE_MODE (itype)); for (i = 0; i < count; i++) { elem = VECTOR_CST_ELT (expr, i); ! if (native_encode_expr (elem, ptr+offset, len-offset) != size) return 0; ! offset += size; } return offset; } --- 7411,7431 ---- size = GET_MODE_SIZE (TYPE_MODE (itype)); for (i = 0; i < count; i++) { + if (off >= size) + { + off -= size; + continue; + } elem = VECTOR_CST_ELT (expr, i); ! int res = native_encode_expr (elem, ptr+offset, len-offset, off); ! if ((off == -1 && res != size) ! || res == 0) return 0; ! offset += res; ! if (offset >= len) ! return offset; ! if (off != -1) ! off = 0; } return offset; } *************** native_encode_vector (const_tree expr, u *** 7411,7417 **** upon failure. */ static int ! native_encode_string (const_tree expr, unsigned char *ptr, int len) { tree type = TREE_TYPE (expr); HOST_WIDE_INT total_bytes; --- 7437,7443 ---- upon failure. */ static int ! native_encode_string (const_tree expr, unsigned char *ptr, int len, int off) { tree type = TREE_TYPE (expr); HOST_WIDE_INT total_bytes; *************** native_encode_string (const_tree expr, u *** 7422,7468 **** || !tree_fits_shwi_p (TYPE_SIZE_UNIT (type))) return 0; total_bytes = tree_to_shwi (TYPE_SIZE_UNIT (type)); ! if (total_bytes > len) return 0; ! if (TREE_STRING_LENGTH (expr) < total_bytes) { ! memcpy (ptr, TREE_STRING_POINTER (expr), TREE_STRING_LENGTH (expr)); ! memset (ptr + TREE_STRING_LENGTH (expr), 0, ! total_bytes - TREE_STRING_LENGTH (expr)); } else ! memcpy (ptr, TREE_STRING_POINTER (expr), total_bytes); ! return total_bytes; } /* Subroutine of fold_view_convert_expr. Encode the INTEGER_CST, REAL_CST, COMPLEX_CST or VECTOR_CST specified by EXPR into the ! buffer PTR of length LEN bytes. Return the number of bytes ! placed in the buffer, or zero upon failure. */ int ! native_encode_expr (const_tree expr, unsigned char *ptr, int len) { switch (TREE_CODE (expr)) { case INTEGER_CST: ! return native_encode_int (expr, ptr, len); case REAL_CST: ! return native_encode_real (expr, ptr, len); case FIXED_CST: ! return native_encode_fixed (expr, ptr, len); case COMPLEX_CST: ! return native_encode_complex (expr, ptr, len); case VECTOR_CST: ! return native_encode_vector (expr, ptr, len); case STRING_CST: ! return native_encode_string (expr, ptr, len); default: return 0; --- 7448,7503 ---- || !tree_fits_shwi_p (TYPE_SIZE_UNIT (type))) return 0; total_bytes = tree_to_shwi (TYPE_SIZE_UNIT (type)); ! if ((off == -1 && total_bytes > len) ! || off >= total_bytes) return 0; ! if (off == -1) ! off = 0; ! if (TREE_STRING_LENGTH (expr) - off < MIN (total_bytes, len)) { ! int written = 0; ! if (off < TREE_STRING_LENGTH (expr)) ! { ! written = MIN (len, TREE_STRING_LENGTH (expr) - off); ! memcpy (ptr, TREE_STRING_POINTER (expr) + off, written); ! } ! memset (ptr + written, 0, ! MIN (total_bytes - written, len - written)); } else ! memcpy (ptr, TREE_STRING_POINTER (expr) + off, MIN (total_bytes, len)); ! return MIN (total_bytes - off, len); } /* Subroutine of fold_view_convert_expr. Encode the INTEGER_CST, REAL_CST, COMPLEX_CST or VECTOR_CST specified by EXPR into the ! buffer PTR of length LEN bytes. If OFF is not -1 then start ! the encoding at byte offset OFF and encode at most LEN bytes. ! Return the number of bytes placed in the buffer, or zero upon failure. */ int ! native_encode_expr (const_tree expr, unsigned char *ptr, int len, int off) { switch (TREE_CODE (expr)) { case INTEGER_CST: ! return native_encode_int (expr, ptr, len, off); case REAL_CST: ! return native_encode_real (expr, ptr, len, off); case FIXED_CST: ! return native_encode_fixed (expr, ptr, len, off); case COMPLEX_CST: ! return native_encode_complex (expr, ptr, len, off); case VECTOR_CST: ! return native_encode_vector (expr, ptr, len, off); case STRING_CST: ! return native_encode_string (expr, ptr, len, off); default: return 0; Index: gcc/fold-const.h =================================================================== *** gcc/fold-const.h.orig 2014-07-11 12:34:03.435274479 +0200 --- gcc/fold-const.h 2014-07-24 12:00:29.662082160 +0200 *************** along with GCC; see the file COPYING3. *** 25,31 **** extern int folding_initializer; /* Convert between trees and native memory representation. */ ! extern int native_encode_expr (const_tree, unsigned char *, int); extern tree native_interpret_expr (tree, const unsigned char *, int); /* Fold constants as much as possible in an expression. --- 25,31 ---- extern int folding_initializer; /* Convert between trees and native memory representation. */ ! extern int native_encode_expr (const_tree, unsigned char *, int, int off = -1); extern tree native_interpret_expr (tree, const unsigned char *, int); /* Fold constants as much as possible in an expression. Index: gcc/testsuite/gcc.dg/pr61762.c =================================================================== *** /dev/null 1970-01-01 00:00:00.000000000 +0000 --- gcc/testsuite/gcc.dg/pr61762.c 2014-07-24 12:00:36.749081672 +0200 *************** *** 0 **** --- 1,19 ---- + /* { dg-do compile } */ + /* { dg-options "-O -fdump-tree-ssa" } */ + + unsigned int f() + { + static const char string[] = "Private"; + + unsigned int priv; + __builtin_memcpy(&priv, &string[0], sizeof(priv)); + return priv; + } + + /* We should have removed the static string and simplified the + memcpy to a store from an integer constant. Gimplification + already performs the simplification but only after into-SSA + the unused local static is removed. */ + + /* { dg-final { scan-tree-dump-not "Private" "ssa" } } */ + /* { dg-final { cleanup-tree-dump "ssa" } } */ Index: gcc/gimple-fold.c =================================================================== *** gcc/gimple-fold.c.orig 2014-07-11 12:07:36.508383737 +0200 --- gcc/gimple-fold.c 2014-07-24 12:39:08.639922501 +0200 *************** get_base_constructor (tree base, HOST_WI *** 2881,2921 **** } } - /* CTOR is STRING_CST. Fold reference of type TYPE and size SIZE - to the memory at bit OFFSET. - - We do only simple job of folding byte accesses. */ - - static tree - fold_string_cst_ctor_reference (tree type, tree ctor, - unsigned HOST_WIDE_INT offset, - unsigned HOST_WIDE_INT size) - { - if (INTEGRAL_TYPE_P (type) - && (TYPE_MODE (type) - == TYPE_MODE (TREE_TYPE (TREE_TYPE (ctor)))) - && (GET_MODE_CLASS (TYPE_MODE (TREE_TYPE (TREE_TYPE (ctor)))) - == MODE_INT) - && GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (TREE_TYPE (ctor)))) == 1 - && size == BITS_PER_UNIT - && !(offset % BITS_PER_UNIT)) - { - offset /= BITS_PER_UNIT; - if (offset < (unsigned HOST_WIDE_INT) TREE_STRING_LENGTH (ctor)) - return build_int_cst_type (type, (TREE_STRING_POINTER (ctor) - [offset])); - /* Folding - const char a[20]="hello"; - return a[10]; - - might lead to offset greater than string length. In this case we - know value is either initialized to 0 or out of bounds. Return 0 - in both cases. */ - return build_zero_cst (type); - } - return NULL_TREE; - } - /* CTOR is CONSTRUCTOR of an array type. Fold reference of type TYPE and size SIZE to the memory at bit OFFSET. */ --- 2881,2886 ---- *************** fold_ctor_reference (tree type, tree cto *** 3107,3114 **** STRIP_NOPS (ret); return ret; } ! if (TREE_CODE (ctor) == STRING_CST) ! return fold_string_cst_ctor_reference (type, ctor, offset, size); if (TREE_CODE (ctor) == CONSTRUCTOR) { --- 3072,3090 ---- STRIP_NOPS (ret); return ret; } ! /* For constants and byte-aligned/sized reads try to go through ! native_encode/interpret. */ ! if (CONSTANT_CLASS_P (ctor) ! && BITS_PER_UNIT == 8 ! && offset % BITS_PER_UNIT == 0 ! && size % BITS_PER_UNIT == 0 ! && size <= MAX_BITSIZE_MODE_ANY_MODE) ! { ! unsigned char buf[MAX_BITSIZE_MODE_ANY_MODE / BITS_PER_UNIT]; ! if (native_encode_expr (ctor, buf, size / BITS_PER_UNIT, ! offset / BITS_PER_UNIT) > 0) ! return native_interpret_expr (type, buf, size / BITS_PER_UNIT); ! } if (TREE_CODE (ctor) == CONSTRUCTOR) { Index: gcc/testsuite/gcc.dg/fold-cstring.c =================================================================== *** /dev/null 1970-01-01 00:00:00.000000000 +0000 --- gcc/testsuite/gcc.dg/fold-cstring.c 2014-07-24 12:24:40.375982280 +0200 *************** *** 0 **** --- 1,44 ---- + /* { dg-do run } */ + /* { dg-options "-O" } */ + + /* The following are testcases for native_interpret_int, + native_interpret_complex and native_interpret_vector decoding + pieces of a string constant encoded by native_encode_string. */ + + extern void abort (void); + + /* We should fold all reads from xconstant and eliminate it, removing + the reference to blah which cannot be resolved at link time. */ + extern int blah; + + static const struct { + int *y; + const char x[32] __attribute__((aligned(32))); + } xconstant = { &blah, "01234567899876543210123456789000" }; + + typedef int v4si __attribute__((vector_size(16))); + + int main() + { + if (sizeof (int) != 4) + return 0; + if (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) + { + if (*(int *)&xconstant.x[4] != 0x34353637) + abort (); + if ((*(v4si *)&xconstant.x[16])[1] != 0x31323334) + abort (); + if (__imag (*(_Complex int *)&xconstant.x[8]) != 0x37363534) + abort (); + } + else if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) + { + if (*(int *)&xconstant.x[4] != 0x37363534) + abort (); + if ((*(v4si *)&xconstant.x[16])[1] != 0x34333231) + abort (); + if (__imag (*(_Complex int *)&xconstant.x[8]) != 0x34353637) + abort (); + } + return 0; + } Index: gcc/testsuite/gcc.dg/fold-cvect.c =================================================================== *** /dev/null 1970-01-01 00:00:00.000000000 +0000 --- gcc/testsuite/gcc.dg/fold-cvect.c 2014-07-24 12:49:16.330880663 +0200 *************** *** 0 **** --- 1,38 ---- + /* { dg-do run } */ + /* { dg-options "-O" } */ + + extern void abort (void); + + /* We should fold all reads from xconstant and eliminate it, removing + the reference to blah which cannot be resolved at link time. */ + extern int blah; + + typedef int v4si __attribute__((vector_size(16))); + + static const struct { + int *y; + const v4si x[2] __attribute__((aligned(32))); + } xconstant = { &blah, { { 0, 1, 2, 3 }, { 2, 3, 4, 5 } } }; + + int main() + { + if (sizeof (int) != 4) + return 0; + if (*(int *)&xconstant.x[0][0] != 0) + abort (); + if (*(int *)&xconstant.x[0][1] != 1) + abort (); + if (*(int *)&xconstant.x[0][2] != 2) + abort (); + if (*(int *)&xconstant.x[0][3] != 3) + abort (); + if (*(int *)&xconstant.x[1][0] != 2) + abort (); + if (*(int *)&xconstant.x[1][1] != 3) + abort (); + if (*(int *)&xconstant.x[1][2] != 4) + abort (); + if (*(int *)&xconstant.x[1][3] != 5) + abort (); + return 0; + }