re PR tree-optimization/93210 (Sub-optimal code optimization on struct/combound const...
authorJakub Jelinek <jakub@redhat.com>
Fri, 10 Jan 2020 21:18:22 +0000 (22:18 +0100)
committerJakub Jelinek <jakub@gcc.gnu.org>
Fri, 10 Jan 2020 21:18:22 +0000 (22:18 +0100)
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
gcc/dwarf2out.c
gcc/fold-const.c
gcc/fold-const.h
gcc/gimple-fold.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/opt/pr93210.C [new file with mode: 0644]
gcc/testsuite/gcc.dg/pr93210.c [new file with mode: 0644]

index 5471c9f40415bbef15bee52b85ef4c269a5f3041..6b24415a4d632bdbefb95ffa926f2950d1b0c60c 100644 (file)
@@ -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
index d0dee485cdb54d67cc5588bc6a9e6910b4ca3d63..70b3fad13a2df3351140544bb6a8a159977b2006 100644 (file)
@@ -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;
index 37c3432d4ddf7b3b77dec8a98ca684c952f6d6c4..aefa91666e2e9d731fe4b60f9a2c87a9e09f92d8 100644 (file)
@@ -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))
index 5e7c868064039761d05c29aee53c22a1a7f529a5..7ac792f16a80903b4dd5d5d5dbe0602a8f58e5c5 100644 (file)
@@ -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.
index d7c5097db404cf68b51f8554e3b3992b6f1feeac..569f91e492e53764233eaabed6e5abd8c25d315e 100644 (file)
@@ -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;
       }
 
index 26c4f6c5e6595eb9ef2561dc2d37c46cb2aa701b..527d53bf0efea1ead117fc7731f8d558d4e83476 100644 (file)
@@ -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 (file)
index 0000000..11ade7b
--- /dev/null
@@ -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 (file)
index 0000000..ec4194b
--- /dev/null
@@ -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;
+}