On January 10, 2020 3:04:07 PM GMT+01:00, Jakub Jelinek <ja...@redhat.com> wrote: >Hi! > >The C++ testcase shows we aren't able to determine constant when >loading >from >const union U { struct V { int a, b; } c; long long d; } u = { { 1, 2 } >}; >u.d, but since your patch in the summer can handle >const union V { int a[2]; long long d; } v = { { 1, 2 } }; >v.d. > >I have noticed dwarf2out.c already has native_encode_initializer that >can >deal with extracting stuff from CONSTRUCTORs (recursively), the >following >patch just moves it over next to native_encode_expr and adjusts it to >match >the native_encode_expr argument standards, i.e. ptr == NULL for >dry-run, >off -1 when the whole object needs to be extracted and fitted into ptr >through ptr + len - 1, and then other off values to extract base + off >up to base + off + len - 1 bytes from the ctor into ptr up to ptr + len >- 1 >and added to the toplevel fold_ctor_reference this >native_encode_initializer >as fallback if other ways failed. E.g. we can't ATM extract >RECORD_TYPEs >etc. through native_interpret_expr, while >fold_{,non}array_ctor_reference >can extract them, but only if it doesn't overlap multiple initializers. >I;ve done it in the toplevel fold_ctor_reference, not recursive calls, >as I think that would imply exponential compile time behavior. > >Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
Ok. Richard. >2020-01-10 Jakub Jelinek <ja...@redhat.com> > > 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. > >--- gcc/fold-const.h.jj 2020-01-01 12:15:53.773522669 +0100 >+++ gcc/fold-const.h 2020-01-10 11:33:37.516681975 +0100 >@@ -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. >--- gcc/fold-const.c.jj 2020-01-01 12:15:52.894535959 +0100 >+++ gcc/fold-const.c 2020-01-10 12:47:13.637310535 +0100 >@@ -7837,9 +7837,10 @@ native_encode_string (const_tree expr, u > 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, u > 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, uns > } > } > >+/* 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 > /* 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)) >--- gcc/gimple-fold.c.jj 2020-01-08 16:54:04.781286448 +0100 >+++ gcc/gimple-fold.c 2020-01-10 11:23:54.071459431 +0100 >@@ -6919,8 +6919,10 @@ fold_ctor_reference (tree type, tree cto > 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 cto > 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 > 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; > } > >--- gcc/dwarf2out.c.jj 2020-01-01 12:15:43.000000000 +0100 >+++ gcc/dwarf2out.c 2020-01-10 11:06:03.370522471 +0100 >@@ -20258,150 +20258,6 @@ add_location_or_const_value_attribute (d > 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_r > { > unsigned char *array = ggc_cleared_vec_alloc<unsigned char> (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; >--- gcc/testsuite/gcc.dg/pr93210.c.jj 2020-01-10 13:03:25.901743750 >+0100 >+++ gcc/testsuite/gcc.dg/pr93210.c 2020-01-10 13:01:21.567607632 +0100 >@@ -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; >+} >--- gcc/testsuite/g++.dg/opt/pr93210.C.jj 2020-01-10 13:11:57.065082378 >+0100 >+++ gcc/testsuite/g++.dg/opt/pr93210.C 2020-01-10 13:13:53.138342895 >+0100 >@@ -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 > > Jakub