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