Hi!

The following testcase ICEs on most targets.  The problem is that
initialize_argument_information properly considers the type of the first
field in this case for how the aggregate should be passed, but then
load_register_parameters uses the type of the aggregate unmodified in
the computation of e.g. how many registers to use.  So, if as in the
testcase the first field needs at most one word, but the whole union
needs say 16 words, we use 15 hard registers after the one chosen,
which can include say special flags register, frame, floating point regs,
whatever, including running into virtual registers or pseudo registers.

Fixed thusly, bootstrapped/regtested on x86_64-linux and i686-linux, ok for
trunk?

2019-09-05  Jakub Jelinek  <ja...@redhat.com>

        PR middle-end/91001
        PR middle-end/91105
        PR middle-end/91106
        * calls.c (load_register_parameters): For TYPE_TRANSPARENT_AGGR
        types, use type of their first field instead of type of
        args[i].tree_value.

        * gcc.c-torture/compile/pr91001.c: New test.

--- gcc/calls.c.jj      2019-08-27 12:27:06.000000000 +0200
+++ gcc/calls.c 2019-09-04 13:34:10.764020515 +0200
@@ -2771,6 +2771,11 @@ load_register_parameters (struct arg_dat
          poly_int64 size = 0;
          HOST_WIDE_INT const_size = 0;
          rtx_insn *before_arg = get_last_insn ();
+         tree type = TREE_TYPE (args[i].tree_value);
+         if ((TREE_CODE (type) == UNION_TYPE
+              || TREE_CODE (type) == RECORD_TYPE)
+             && TYPE_TRANSPARENT_AGGR (type))
+           type = TREE_TYPE (first_field (type));
          /* Set non-negative if we must move a word at a time, even if
             just one word (e.g, partial == 4 && mode == DFmode).  Set
             to -1 if we just use a normal move insn.  This value can be
@@ -2783,11 +2788,11 @@ load_register_parameters (struct arg_dat
              gcc_assert (partial % UNITS_PER_WORD == 0);
              nregs = partial / UNITS_PER_WORD;
            }
-         else if (TYPE_MODE (TREE_TYPE (args[i].tree_value)) == BLKmode)
+         else if (TYPE_MODE (type) == BLKmode)
            {
              /* Variable-sized parameters should be described by a
                 PARALLEL instead.  */
-             const_size = int_size_in_bytes (TREE_TYPE (args[i].tree_value));
+             const_size = int_size_in_bytes (type);
              gcc_assert (const_size >= 0);
              nregs = (const_size + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD;
              size = const_size;
@@ -2914,8 +2919,7 @@ load_register_parameters (struct arg_dat
          if (GET_CODE (reg) == PARALLEL)
            use_group_regs (call_fusage, reg);
          else if (nregs == -1)
-           use_reg_mode (call_fusage, reg,
-                         TYPE_MODE (TREE_TYPE (args[i].tree_value)));
+           use_reg_mode (call_fusage, reg, TYPE_MODE (type));
          else if (nregs > 0)
            use_regs (call_fusage, REGNO (reg), nregs);
        }
--- gcc/testsuite/gcc.c-torture/compile/pr91001.c.jj    2019-09-04 
13:48:25.428062724 +0200
+++ gcc/testsuite/gcc.c-torture/compile/pr91001.c       2019-09-04 
13:47:55.849511169 +0200
@@ -0,0 +1,31 @@
+/* PR middle-end/91001 */
+/* PR middle-end/91105 */
+/* PR middle-end/91106 */
+
+struct __attribute__((packed)) S { short b; char c; };
+struct T { short b, c, d; };
+struct __attribute__((packed)) R { int b; char c; };
+union __attribute__((aligned(128), transparent_union)) U { struct S c; } u;
+union __attribute__((aligned(32), transparent_union)) V { struct T c; } v;
+union __attribute__((aligned(32), transparent_union)) W { struct R c; } w;
+void foo (union U);
+void bar (union V);
+void baz (union W);
+
+void
+qux (void)
+{
+  foo (u);
+}
+
+void
+quux (void)
+{
+  bar (v);
+}
+
+void
+corge (void)
+{
+  baz (w);
+}

        Jakub

Reply via email to