On Fri, Oct 28, 2016 at 01:32:22AM -0600, Jeff Law wrote: > >I think so. I'll leave the rest to people more familiar with RTL > >expansion -- generally I thought the callers of expand() have to deal > >with expansions that return a different mode? > You generally have to deal with expansions that return the object in a new > pseudo instead of the one you asked for -- so the caller has to test for > that and emit a copy when it happens. > > I don't offhand recall cases where we have to deal with getting a result in > a different mode than was asked. But given the history of the expanders, I > wouldn't be surprised if there's oddball cases where that can happen.
I've already committed the original patch based on Eric's review, but managed to come up with another testcase that still ICEs (one with two different complex modes). Is the following ok for trunk if it passes bootstrap/regtest? 2016-10-28 Jakub Jelinek <ja...@redhat.com> PR rtl-optimization/77919 * expr.c (expand_expr_real_1) <normal_inner_ref>: Only avoid forcing into memory if both modes are complex and their inner modes have the same precision. If the two modes are different complex modes, convert each part separately and generate a new CONCAT. * g++.dg/torture/pr77919-2.C: New test. --- gcc/expr.c.jj 2016-10-28 10:35:14.753234774 +0200 +++ gcc/expr.c 2016-10-28 10:35:28.760057716 +0200 @@ -10422,10 +10422,35 @@ expand_expr_real_1 (tree exp, rtx target { if (bitpos == 0 && bitsize == GET_MODE_BITSIZE (GET_MODE (op0)) - && COMPLEX_MODE_P (mode1)) + && COMPLEX_MODE_P (mode1) + && COMPLEX_MODE_P (GET_MODE (op0)) + && (GET_MODE_PRECISION (GET_MODE_INNER (mode1)) + == GET_MODE_PRECISION (GET_MODE_INNER (GET_MODE (op0))))) { if (reversep) op0 = flip_storage_order (GET_MODE (op0), op0); + if (mode1 != GET_MODE (op0)) + { + rtx parts[2]; + for (int i = 0; i < 2; i++) + { + rtx op = read_complex_part (op0, i != 0); + if (GET_CODE (op) == SUBREG) + op = force_reg (GET_MODE (op), op); + rtx temp = gen_lowpart_common (GET_MODE_INNER (mode1), + op); + if (temp) + op = temp; + else + { + if (!REG_P (op) && !MEM_P (op)) + op = force_reg (GET_MODE (op), op); + op = gen_lowpart (GET_MODE_INNER (mode1), op); + } + parts[i] = op; + } + op0 = gen_rtx_CONCAT (mode1, parts[0], parts[1]); + } return op0; } if (bitpos == 0 --- gcc/testsuite/g++.dg/torture/pr77919-2.C.jj 2016-10-28 10:35:49.294798140 +0200 +++ gcc/testsuite/g++.dg/torture/pr77919-2.C 2016-10-28 10:29:38.000000000 +0200 @@ -0,0 +1,10 @@ +// PR rtl-optimization/77919 +// { dg-do compile } + +typedef _Complex long long B; +struct A { A (double) {} _Complex double i; }; +typedef struct { B b; } C; +struct D { D (const B &x) : b (x) {} B b; }; +static inline B foo (const double *x) { C *a; a = (C *) x; return a->b; } +static inline D baz (const A &x) { return foo ((double *) &x); } +D b = baz (0); Jakub