From ea69031c5facc70e4a96df83cd58702900fd54b6 Mon Sep 17 00:00:00 2001 From: Jakub Jelinek Date: Fri, 10 Jan 2020 22:18:22 +0100 Subject: [PATCH] re PR tree-optimization/93210 (Sub-optimal code optimization on struct/combound constexpr (gcc vs. clang)) PR tree-optimization/93210 * fold-const.h (native_encode_initializer, can_native_interpret_type_p): Declare. * fold-const.c (native_encode_string): Fix up handling with off != -1, simplify. (native_encode_initializer): New function, moved from dwarf2out.c. Adjust to native_encode_expr compatible arguments, including dry-run and partial extraction modes. Don't handle STRING_CST. (can_native_interpret_type_p): No longer static. * gimple-fold.c (fold_ctor_reference): For native_encode_expr, verify offset / BITS_PER_UNIT fits into int and don't call it if can_native_interpret_type_p fails. If suboff is NULL and for CONSTRUCTOR fold_{,non}array_ctor_reference returns NULL, retry with native_encode_initializer. (fold_const_aggregate_ref_1): Formatting fix. * dwarf2out.c (native_encode_initializer): Moved to fold-const.c. (tree_add_const_value_attribute): Adjust caller. * gcc.dg/pr93210.c: New test. * g++.dg/opt/pr93210.C: New test. From-SVN: r280141 --- gcc/ChangeLog | 18 +++ gcc/dwarf2out.c | 146 +------------------ gcc/fold-const.c | 219 ++++++++++++++++++++++++++++- gcc/fold-const.h | 3 + gcc/gimple-fold.c | 36 ++++- gcc/testsuite/ChangeLog | 6 + gcc/testsuite/g++.dg/opt/pr93210.C | 37 +++++ gcc/testsuite/gcc.dg/pr93210.c | 66 +++++++++ 8 files changed, 373 insertions(+), 158 deletions(-) create mode 100644 gcc/testsuite/g++.dg/opt/pr93210.C create mode 100644 gcc/testsuite/gcc.dg/pr93210.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 5471c9f4041..6b24415a4d6 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,5 +1,23 @@ 2020-01-10 Jakub Jelinek + PR tree-optimization/93210 + * fold-const.h (native_encode_initializer, + can_native_interpret_type_p): Declare. + * fold-const.c (native_encode_string): Fix up handling with off != -1, + simplify. + (native_encode_initializer): New function, moved from dwarf2out.c. + Adjust to native_encode_expr compatible arguments, including dry-run + and partial extraction modes. Don't handle STRING_CST. + (can_native_interpret_type_p): No longer static. + * gimple-fold.c (fold_ctor_reference): For native_encode_expr, verify + offset / BITS_PER_UNIT fits into int and don't call it if + can_native_interpret_type_p fails. If suboff is NULL and for + CONSTRUCTOR fold_{,non}array_ctor_reference returns NULL, retry with + native_encode_initializer. + (fold_const_aggregate_ref_1): Formatting fix. + * dwarf2out.c (native_encode_initializer): Moved to fold-const.c. + (tree_add_const_value_attribute): Adjust caller. + PR tree-optimization/90838 * tree-ssa-forwprop.c (simplify_count_trailing_zeroes): Use SCALAR_INT_TYPE_MODE instead of TYPE_MODE as operand of diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c index d0dee485cdb..70b3fad13a2 100644 --- a/gcc/dwarf2out.c +++ b/gcc/dwarf2out.c @@ -20258,150 +20258,6 @@ add_location_or_const_value_attribute (dw_die_ref die, tree decl, bool cache_p) return tree_add_const_value_attribute_for_decl (die, decl); } -/* Helper function for tree_add_const_value_attribute. Natively encode - initializer INIT into an array. Return true if successful. */ - -static bool -native_encode_initializer (tree init, unsigned char *array, int size) -{ - tree type; - - if (init == NULL_TREE) - return false; - - STRIP_NOPS (init); - switch (TREE_CODE (init)) - { - case STRING_CST: - type = TREE_TYPE (init); - if (TREE_CODE (type) == ARRAY_TYPE) - { - tree enttype = TREE_TYPE (type); - scalar_int_mode mode; - - if (!is_int_mode (TYPE_MODE (enttype), &mode) - || GET_MODE_SIZE (mode) != 1) - return false; - if (int_size_in_bytes (type) != size) - return false; - if (size > TREE_STRING_LENGTH (init)) - { - memcpy (array, TREE_STRING_POINTER (init), - TREE_STRING_LENGTH (init)); - memset (array + TREE_STRING_LENGTH (init), - '\0', size - TREE_STRING_LENGTH (init)); - } - else - memcpy (array, TREE_STRING_POINTER (init), size); - return true; - } - return false; - case CONSTRUCTOR: - type = TREE_TYPE (init); - if (int_size_in_bytes (type) != size) - return false; - if (TREE_CODE (type) == ARRAY_TYPE) - { - HOST_WIDE_INT min_index; - unsigned HOST_WIDE_INT cnt; - int curpos = 0, fieldsize; - constructor_elt *ce; - - if (TYPE_DOMAIN (type) == NULL_TREE - || !tree_fits_shwi_p (TYPE_MIN_VALUE (TYPE_DOMAIN (type)))) - return false; - - fieldsize = int_size_in_bytes (TREE_TYPE (type)); - if (fieldsize <= 0) - return false; - - min_index = tree_to_shwi (TYPE_MIN_VALUE (TYPE_DOMAIN (type))); - memset (array, '\0', size); - FOR_EACH_VEC_SAFE_ELT (CONSTRUCTOR_ELTS (init), cnt, ce) - { - tree val = ce->value; - tree index = ce->index; - int pos = curpos; - if (index && TREE_CODE (index) == RANGE_EXPR) - pos = (tree_to_shwi (TREE_OPERAND (index, 0)) - min_index) - * fieldsize; - else if (index) - pos = (tree_to_shwi (index) - min_index) * fieldsize; - - if (val) - { - STRIP_NOPS (val); - if (!native_encode_initializer (val, array + pos, fieldsize)) - return false; - } - curpos = pos + fieldsize; - if (index && TREE_CODE (index) == RANGE_EXPR) - { - int count = tree_to_shwi (TREE_OPERAND (index, 1)) - - tree_to_shwi (TREE_OPERAND (index, 0)); - while (count-- > 0) - { - if (val) - memcpy (array + curpos, array + pos, fieldsize); - curpos += fieldsize; - } - } - gcc_assert (curpos <= size); - } - return true; - } - else if (TREE_CODE (type) == RECORD_TYPE - || TREE_CODE (type) == UNION_TYPE) - { - tree field = NULL_TREE; - unsigned HOST_WIDE_INT cnt; - constructor_elt *ce; - - if (int_size_in_bytes (type) != size) - return false; - - if (TREE_CODE (type) == RECORD_TYPE) - field = TYPE_FIELDS (type); - - FOR_EACH_VEC_SAFE_ELT (CONSTRUCTOR_ELTS (init), cnt, ce) - { - tree val = ce->value; - int pos, fieldsize; - - if (ce->index != 0) - field = ce->index; - - if (val) - STRIP_NOPS (val); - - if (field == NULL_TREE || DECL_BIT_FIELD (field)) - return false; - - if (TREE_CODE (TREE_TYPE (field)) == ARRAY_TYPE - && TYPE_DOMAIN (TREE_TYPE (field)) - && ! TYPE_MAX_VALUE (TYPE_DOMAIN (TREE_TYPE (field)))) - return false; - else if (DECL_SIZE_UNIT (field) == NULL_TREE - || !tree_fits_shwi_p (DECL_SIZE_UNIT (field))) - return false; - fieldsize = tree_to_shwi (DECL_SIZE_UNIT (field)); - pos = int_byte_position (field); - gcc_assert (pos + fieldsize <= size); - if (val && fieldsize != 0 - && !native_encode_initializer (val, array + pos, fieldsize)) - return false; - } - return true; - } - return false; - case VIEW_CONVERT_EXPR: - case NON_LVALUE_EXPR: - return native_encode_initializer (TREE_OPERAND (init, 0), array, size); - default: - return native_encode_expr (init, array, size) == size; - } -} - /* Attach a DW_AT_const_value attribute to DIE. The value of the attribute is the const value T. */ @@ -20446,7 +20302,7 @@ tree_add_const_value_attribute (dw_die_ref die, tree t) { unsigned char *array = ggc_cleared_vec_alloc (size); - if (native_encode_initializer (init, array, size)) + if (native_encode_initializer (init, array, size) == size) { add_AT_vec (die, DW_AT_const_value, size, 1, array); return true; diff --git a/gcc/fold-const.c b/gcc/fold-const.c index 37c3432d4dd..aefa91666e2 100644 --- a/gcc/fold-const.c +++ b/gcc/fold-const.c @@ -7837,9 +7837,10 @@ native_encode_string (const_tree expr, unsigned char *ptr, int len, int off) return 0; if (off == -1) off = 0; + len = MIN (total_bytes - off, len); if (ptr == NULL) /* Dry run. */; - else if (TREE_STRING_LENGTH (expr) - off < MIN (total_bytes, len)) + else { int written = 0; if (off < TREE_STRING_LENGTH (expr)) @@ -7847,12 +7848,9 @@ native_encode_string (const_tree expr, unsigned char *ptr, int len, int off) 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)); + memset (ptr + written, 0, len - written); } - else - memcpy (ptr, TREE_STRING_POINTER (expr) + off, MIN (total_bytes, len)); - return MIN (total_bytes - off, len); + return len; } @@ -7895,6 +7893,213 @@ native_encode_expr (const_tree expr, unsigned char *ptr, int len, int off) } } +/* Similar to native_encode_expr, but also handle CONSTRUCTORs, VCEs, + NON_LVALUE_EXPRs and nops. */ + +int +native_encode_initializer (tree init, unsigned char *ptr, int len, + int off) +{ + /* We don't support starting at negative offset and -1 is special. */ + if (off < -1 || init == NULL_TREE) + return 0; + + STRIP_NOPS (init); + switch (TREE_CODE (init)) + { + case VIEW_CONVERT_EXPR: + case NON_LVALUE_EXPR: + return native_encode_initializer (TREE_OPERAND (init, 0), ptr, len, off); + default: + return native_encode_expr (init, ptr, len, off); + case CONSTRUCTOR: + tree type = TREE_TYPE (init); + HOST_WIDE_INT total_bytes = int_size_in_bytes (type); + if (total_bytes < 0) + return 0; + if ((off == -1 && total_bytes > len) || off >= total_bytes) + return 0; + int o = off == -1 ? 0 : off; + if (TREE_CODE (type) == ARRAY_TYPE) + { + HOST_WIDE_INT min_index; + unsigned HOST_WIDE_INT cnt; + HOST_WIDE_INT curpos = 0, fieldsize; + constructor_elt *ce; + + if (TYPE_DOMAIN (type) == NULL_TREE + || !tree_fits_shwi_p (TYPE_MIN_VALUE (TYPE_DOMAIN (type)))) + return 0; + + fieldsize = int_size_in_bytes (TREE_TYPE (type)); + if (fieldsize <= 0) + return 0; + + min_index = tree_to_shwi (TYPE_MIN_VALUE (TYPE_DOMAIN (type))); + if (ptr != NULL) + memset (ptr, '\0', MIN (total_bytes - off, len)); + + FOR_EACH_VEC_SAFE_ELT (CONSTRUCTOR_ELTS (init), cnt, ce) + { + tree val = ce->value; + tree index = ce->index; + HOST_WIDE_INT pos = curpos, count = 0; + bool full = false; + if (index && TREE_CODE (index) == RANGE_EXPR) + { + if (!tree_fits_shwi_p (TREE_OPERAND (index, 0)) + || !tree_fits_shwi_p (TREE_OPERAND (index, 1))) + return 0; + pos = (tree_to_shwi (TREE_OPERAND (index, 0)) - min_index) + * fieldsize; + count = (tree_to_shwi (TREE_OPERAND (index, 1)) + - tree_to_shwi (TREE_OPERAND (index, 0))); + } + else if (index) + { + if (!tree_fits_shwi_p (index)) + return 0; + pos = (tree_to_shwi (index) - min_index) * fieldsize; + } + + curpos = pos; + if (val) + do + { + if (off == -1 + || (curpos >= off + && (curpos + fieldsize + <= (HOST_WIDE_INT) off + len))) + { + if (full) + { + if (ptr) + memcpy (ptr + (curpos - o), ptr + (pos - o), + fieldsize); + } + else if (!native_encode_initializer (val, + ptr + ? ptr + curpos - o + : NULL, + fieldsize, + off == -1 ? -1 + : 0)) + return 0; + else + { + full = true; + pos = curpos; + } + } + else if (curpos + fieldsize > off + && curpos < (HOST_WIDE_INT) off + len) + { + /* Partial overlap. */ + unsigned char *p = NULL; + int no = 0; + int l; + if (curpos >= off) + { + if (ptr) + p = ptr + curpos - off; + l = MIN ((HOST_WIDE_INT) off + len - curpos, + fieldsize); + } + else + { + p = ptr; + no = off - curpos; + l = len; + } + if (!native_encode_initializer (val, p, l, no)) + return 0; + } + curpos += fieldsize; + } + while (count-- != 0); + } + return MIN (total_bytes - off, len); + } + else if (TREE_CODE (type) == RECORD_TYPE + || TREE_CODE (type) == UNION_TYPE) + { + unsigned HOST_WIDE_INT cnt; + constructor_elt *ce; + + if (ptr != NULL) + memset (ptr, '\0', MIN (total_bytes - off, len)); + FOR_EACH_VEC_SAFE_ELT (CONSTRUCTOR_ELTS (init), cnt, ce) + { + tree field = ce->index; + tree val = ce->value; + HOST_WIDE_INT pos, fieldsize; + + if (field == NULL_TREE) + return 0; + + pos = int_byte_position (field); + if (off != -1 && (HOST_WIDE_INT) off + len <= pos) + continue; + + if (TREE_CODE (TREE_TYPE (field)) == ARRAY_TYPE + && TYPE_DOMAIN (TREE_TYPE (field)) + && ! TYPE_MAX_VALUE (TYPE_DOMAIN (TREE_TYPE (field)))) + return 0; + if (DECL_SIZE_UNIT (field) == NULL_TREE + || !tree_fits_shwi_p (DECL_SIZE_UNIT (field))) + return 0; + fieldsize = tree_to_shwi (DECL_SIZE_UNIT (field)); + if (fieldsize == 0) + continue; + + if (off != -1 && pos + fieldsize <= off) + continue; + + if (DECL_BIT_FIELD (field)) + return 0; + + if (val == NULL_TREE) + continue; + + if (off == -1 + || (pos >= off + && (pos + fieldsize <= (HOST_WIDE_INT) off + len))) + { + if (!native_encode_initializer (val, ptr ? ptr + pos - o + : NULL, + fieldsize, + off == -1 ? -1 : 0)) + return 0; + } + else + { + /* Partial overlap. */ + unsigned char *p = NULL; + int no = 0; + int l; + if (pos >= off) + { + if (ptr) + p = ptr + pos - off; + l = MIN ((HOST_WIDE_INT) off + len - pos, + fieldsize); + } + else + { + p = ptr; + no = off - pos; + l = len; + } + if (!native_encode_initializer (val, p, l, no)) + return 0; + } + } + return MIN (total_bytes - off, len); + } + return 0; + } +} + /* Subroutine of native_interpret_expr. Interpret the contents of the buffer PTR of length LEN as an INTEGER_CST of type TYPE. @@ -8129,7 +8334,7 @@ native_interpret_expr (tree type, const unsigned char *ptr, int len) /* Returns true if we can interpret the contents of a native encoding as TYPE. */ -static bool +bool can_native_interpret_type_p (tree type) { switch (TREE_CODE (type)) diff --git a/gcc/fold-const.h b/gcc/fold-const.h index 5e7c8680640..7ac792f16a8 100644 --- a/gcc/fold-const.h +++ b/gcc/fold-const.h @@ -26,7 +26,10 @@ 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 int native_encode_initializer (tree, unsigned char *, int, + int off = -1); extern tree native_interpret_expr (tree, const unsigned char *, int); +extern bool can_native_interpret_type_p (tree); /* Fold constants as much as possible in an expression. Returns the simplified expression. diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c index d7c5097db40..569f91e492e 100644 --- a/gcc/gimple-fold.c +++ b/gcc/gimple-fold.c @@ -6919,8 +6919,10 @@ fold_ctor_reference (tree type, tree ctor, const poly_uint64 &poly_offset, if (CONSTANT_CLASS_P (ctor) && BITS_PER_UNIT == 8 && offset % BITS_PER_UNIT == 0 + && offset / BITS_PER_UNIT <= INT_MAX && size % BITS_PER_UNIT == 0 - && size <= MAX_BITSIZE_MODE_ANY_MODE) + && size <= MAX_BITSIZE_MODE_ANY_MODE + && can_native_interpret_type_p (type)) { unsigned char buf[MAX_BITSIZE_MODE_ANY_MODE / BITS_PER_UNIT]; int len = native_encode_expr (ctor, buf, size / BITS_PER_UNIT, @@ -6934,13 +6936,35 @@ fold_ctor_reference (tree type, tree ctor, const poly_uint64 &poly_offset, if (!suboff) suboff = &dummy; + tree ret; if (TREE_CODE (TREE_TYPE (ctor)) == ARRAY_TYPE || TREE_CODE (TREE_TYPE (ctor)) == VECTOR_TYPE) - return fold_array_ctor_reference (type, ctor, offset, size, - from_decl, suboff); + ret = fold_array_ctor_reference (type, ctor, offset, size, + from_decl, suboff); + else + ret = fold_nonarray_ctor_reference (type, ctor, offset, size, + from_decl, suboff); + + /* Fall back to native_encode_initializer. Needs to be done + only in the outermost fold_ctor_reference call (because it itself + recurses into CONSTRUCTORs) and doesn't update suboff. */ + if (ret == NULL_TREE + && suboff == &dummy + && BITS_PER_UNIT == 8 + && offset % BITS_PER_UNIT == 0 + && offset / BITS_PER_UNIT <= INT_MAX + && size % BITS_PER_UNIT == 0 + && size <= MAX_BITSIZE_MODE_ANY_MODE + && can_native_interpret_type_p (type)) + { + unsigned char buf[MAX_BITSIZE_MODE_ANY_MODE / BITS_PER_UNIT]; + int len = native_encode_initializer (ctor, buf, size / BITS_PER_UNIT, + offset / BITS_PER_UNIT); + if (len > 0) + return native_interpret_expr (type, buf, len); + } - return fold_nonarray_ctor_reference (type, ctor, offset, size, - from_decl, suboff); + return ret; } return NULL_TREE; @@ -7049,7 +7073,7 @@ fold_const_aggregate_ref_1 (tree t, tree (*valueize) (tree)) tree c = fold_const_aggregate_ref_1 (TREE_OPERAND (t, 0), valueize); if (c && TREE_CODE (c) == COMPLEX_CST) return fold_build1_loc (EXPR_LOCATION (t), - TREE_CODE (t), TREE_TYPE (t), c); + TREE_CODE (t), TREE_TYPE (t), c); break; } diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 26c4f6c5e65..527d53bf0ef 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,9 @@ +2020-01-10 Jakub Jelinek + + PR tree-optimization/93210 + * gcc.dg/pr93210.c: New test. + * g++.dg/opt/pr93210.C: New test. + 2020-01-10 Vladimir Makarov PR inline-asm/93027 diff --git a/gcc/testsuite/g++.dg/opt/pr93210.C b/gcc/testsuite/g++.dg/opt/pr93210.C new file mode 100644 index 00000000000..11ade7be785 --- /dev/null +++ b/gcc/testsuite/g++.dg/opt/pr93210.C @@ -0,0 +1,37 @@ +// PR tree-optimization/93210 +// { dg-do compile { target c++11 } } +// { dg-options "-O2 -fdump-tree-optimized" } +// { dg-final { scan-tree-dump-not "static_member\.d" "optimized" } } + +union U { struct { unsigned int a, b; } c; unsigned long long d; }; + +inline +bool operator == (U const &x, U const &y) noexcept +{ + return x.d == y.d; +}; + +struct S +{ + static constexpr U static_member = { { 13, 42 } }; + bool foo (U const &y) const noexcept; + bool bar (U const &y) const noexcept; +}; + +#if __cpp_inline_variables < 201606L +constexpr U S::static_member; +#endif + +#if __SIZEOF_INT__ * 2 == __SIZEOF_LONG_LONG__ +bool +S::foo (U const &y) const noexcept +{ + return static_member == y; +} + +bool +S::bar (U const &y) const noexcept +{ + return U (static_member) == y; +} +#endif diff --git a/gcc/testsuite/gcc.dg/pr93210.c b/gcc/testsuite/gcc.dg/pr93210.c new file mode 100644 index 00000000000..ec4194b6b49 --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr93210.c @@ -0,0 +1,66 @@ +/* PR tree-optimization/93210 */ +/* { dg-do run } */ +/* { dg-options "-O2 -fdump-tree-optimized" } */ +/* { dg-final { scan-tree-dump-times "return \[0-9]\[0-9a-fA-FxX]*;" 31 "optimized" } } */ + +#ifdef __SIZEOF_INT128__ +typedef unsigned __int128 L; +#else +typedef unsigned long long L; +#endif +struct S { signed char a, b; unsigned char c; }; +struct T { signed char d; struct S e[25]; signed char f; }; +union U { struct T g; L h[10]; }; +const union U u = { { 1, { { 2, 3, 4 }, { 5, 6, 7 }, { 8, 9, 10 }, + { 12, 13, 14 }, { 15, 16, 17 }, { 18, 19, 20 }, + { 22, 23, 24 }, { 25, 26, 27 }, { 28, 29, 30 }, + { 32, 33, 34 }, { 35, 36, 37 }, { 38, 39, 40 }, + { 42, 43, 44 }, { 45, 46, 47 }, { 48, 49, 50 }, + { 52, 53, 54 }, { 55, 56, 57 }, { 58, 59, 60 }, + { 62, 63, 64 }, { 65, 66, 67 }, { 68, 69, 70 }, + { 72, 73, 74 }, { 75, 76, 77 }, { 78, 79, 80 }, + { 82, 83, 84 } }, 85 } }; +const union U v = { { 1, { { 2, 3, 4 }, [1 ... 23] = { 5, 6, 7 }, + { 8, 9, 10 } }, 86 } }; +struct A { char a[5]; char b[16]; char c[7]; }; +union V { struct A d; unsigned int e[10]; }; +const union V w = { { "abcde", "ijkl", "mnopqr" } }; +#define N(n) __attribute__((noipa)) L foo##n (void) { return u.h[n]; } +#define M N(0) N(1) N(2) N(3) N(4) N(5) N(6) N(7) N(8) N(9) +M +#undef N +#define N(n) __attribute__((noipa)) L bar##n (void) { return v.h[n]; } +M +#undef N +#define N(n) __attribute__((noipa)) L baz##n (void) { return w.e[n]; } +M + +typedef L (*F) (void); +F arr[30] = { +#undef N +#define N(n) foo##n, +M +#undef N +#define N(n) bar##n, +M +#undef N +#define N(n) baz##n, +M +}; + +int +main () +{ + const union U *p = &u; + const union U *q = &v; + const union V *r = &w; + __asm ("" : "+g" (p)); + __asm ("" : "+g" (q)); + __asm ("" : "+g" (r)); + for (int i = 0; i < 10; i++) + if (arr[i] () != p->h[i] + || arr[i + 10] () != q->h[i] + || arr[i + 20] () != r->e[i]) + __builtin_abort (); + return 0; +} -- 2.30.2