aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorJakub Jelinek <jakub@redhat.com>2020-01-10 22:18:22 +0100
committerJakub Jelinek <jakub@gcc.gnu.org>2020-01-10 22:18:22 +0100
commitea69031c5facc70e4a96df83cd58702900fd54b6 (patch)
tree879c6e151d192e9ac99c83999572027edc8d0517 /gcc
parent974bb8a4dcbf51a153ab72da91a7256a296e7fa1 (diff)
downloadgcc-ea69031c5facc70e4a96df83cd58702900fd54b6.zip
gcc-ea69031c5facc70e4a96df83cd58702900fd54b6.tar.gz
gcc-ea69031c5facc70e4a96df83cd58702900fd54b6.tar.bz2
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
Diffstat (limited to 'gcc')
-rw-r--r--gcc/ChangeLog18
-rw-r--r--gcc/dwarf2out.c146
-rw-r--r--gcc/fold-const.c219
-rw-r--r--gcc/fold-const.h3
-rw-r--r--gcc/gimple-fold.c36
-rw-r--r--gcc/testsuite/ChangeLog6
-rw-r--r--gcc/testsuite/g++.dg/opt/pr93210.C37
-rw-r--r--gcc/testsuite/gcc.dg/pr93210.c66
8 files changed, 373 insertions, 158 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 5471c9f..6b24415 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,5 +1,23 @@
2020-01-10 Jakub Jelinek <jakub@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.
+
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 d0dee48..70b3fad 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<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;
diff --git a/gcc/fold-const.c b/gcc/fold-const.c
index 37c3432..aefa916 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 5e7c868..7ac792f 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 d7c5097..569f91e 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 26c4f6c..527d53b 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,9 @@
+2020-01-10 Jakub Jelinek <jakub@redhat.com>
+
+ PR tree-optimization/93210
+ * gcc.dg/pr93210.c: New test.
+ * g++.dg/opt/pr93210.C: New test.
+
2020-01-10 Vladimir Makarov <vmakarov@redhat.com>
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 0000000..11ade7b
--- /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 0000000..ec4194b
--- /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;
+}