http://gcc.gnu.org/bugzilla/show_bug.cgi?id=58369
--- Comment #4 from Mikael Pettersson <mikpe at it dot uu.se> --- (All source references here are for vanilla gcc-4.8.1.) The problem appears to start in choose_reload_regs, in the "if (inheritance)" block at lines 6497 to 6679. It finds (reg:DF 0 %d0 [orig:109 D.2384 ] [109]) in rld[r].in. The "if (regno >= 0 ...)" test at line 6543 passes so that block is entered. last_reg is (reg:XF 17 %fp1) and byte is still 0, so it calls subreg_regno_offset (17, XFmode, 0, DFmode) at line 6560. subreg_regno_offset in turn just calls subreg_get_info. In subreg_get_info xmode is XFmode with size 12 and precision 80, while ymode is DFmode with size 8 and precision 64. The first "if (HARD_REGNO_NREGS_HAS_PADDING ...)" is false so that block is skipped. nregs_xmode and nregs_ymode are computed, both are 1. The second if for paradoxical subregs is false, so that block is skipped. The third if "If registers store different numbers of bits in the different modes, we cannot generally form this subreg" at line 3345 is true, so that block is entered. regsize_xmode and regsize_ymode are computed, they are 12 and 8 respectively. None of the inner if:s there are true, because nregs_xmode and nregs_ymode are both 1, which isn't > 1, so we don't return. The fourth if for lowpart subregs at line 3371 is false, because the input offset is 0 ("byte" from choose_reload_regs), but subreg_lowpart_offset (DFmode, XFmode) returns 4, and 0 != 4. So that block is skipped. Next we reach the "gcc_assert ((GET_MODE_SIZE (xmode) % GET_MODE_SIZE (ymode)) == 0);" at line 3387. However, this evaluates as (12 % 8) == 0, which is false, so the assertion fails and we ICE. So the problem as I understand it is that choose_reload_regs forms a virtual subreg with differently-sized modes and offset 0, while on big-endian machines the offset must be >0 (see subreg_lowpart_offset in emit-rtl.c), so the virtual subreg is not recognized as a normal lowpart subreg. I *think* other machines don't see this problem because: a) offset 0 is correct for little-endian like x86 and most ARMs, b) many big-endian machines define CANNOT_CHANGE_MODE_CLASS to reject differently-sized modes in floating-point registers, which can prevent the bogus subreg_regno_offset call, c) their larger modes are whole multiples of their smaller modes, so the assertion doesn't fail. However, m68k: d) is big-endian, e) doesn't define CANNOT_CHANGE_MODE_CLASS, f) has an XFmode which is 1.5 times as large as its DFmode. If I add a suitable definition of CANNOT_CHANGE_MODE_CLASS, e.g. --- gcc-4.8.1/gcc/config/m68k/m68k.h.~1~ 2013-01-10 21:38:27.000000000 +0100 +++ gcc-4.8.1/gcc/config/m68k/m68k.h 2013-09-11 18:28:58.160242077 +0200 @@ -409,6 +409,11 @@ along with GCC; see the file COPYING3. #define HARD_REGNO_MODE_OK(REGNO, MODE) \ m68k_regno_mode_ok ((REGNO), (MODE)) +#define CANNOT_CHANGE_MODE_CLASS(FROM, TO, CLASS) \ + (GET_MODE_SIZE (FROM) != GET_MODE_SIZE (TO) \ + ? reg_classes_intersect_p (FP_REGS, CLASS) \ + : 0) + #define SECONDARY_RELOAD_CLASS(CLASS, MODE, X) \ m68k_secondary_reload_class (CLASS, MODE, X) then the ICE disappears. However, reading the m68k backend I think this is against its intentions, and I suspect it is stricter than necessary. Other calls to subreg_regno_offset either take the parameters from an existing real subreg, or compute a correct lowpart offset (regcprop.c:maybe_mode_change, var-tracking.c:var_lowpart). So I'm starting to think that the bug is in choose_reload_regs: it should pass a proper lowpart offset in byte, not the constant 0 (byte is only set for input subregs that are pseudos). The following patch: --- gcc-4.8.1/gcc/reload1.c.~1~ 2013-01-21 15:55:05.000000000 +0100 +++ gcc-4.8.1/gcc/reload1.c 2013-09-11 19:58:37.979482251 +0200 @@ -6497,6 +6497,7 @@ choose_reload_regs (struct insn_chain *c if (inheritance) { int byte = 0; + bool byte_is_fixed = false; int regno = -1; enum machine_mode mode = VOIDmode; @@ -6519,7 +6520,10 @@ choose_reload_regs (struct insn_chain *c if (regno < FIRST_PSEUDO_REGISTER) regno = subreg_regno (rld[r].in_reg); else - byte = SUBREG_BYTE (rld[r].in_reg); + { + byte = SUBREG_BYTE (rld[r].in_reg); + byte_is_fixed = true; + } mode = GET_MODE (rld[r].in_reg); } #ifdef AUTO_INC_DEC @@ -6557,6 +6561,8 @@ choose_reload_regs (struct insn_chain *c rtx last_reg = reg_last_reload_reg[regno]; i = REGNO (last_reg); + if (! byte_is_fixed) + byte = subreg_lowpart_offset (mode, GET_MODE (last_reg)); i += subreg_regno_offset (i, GET_MODE (last_reg), byte, mode); last_class = REGNO_REG_CLASS (i); also fixes the ICE, and seems more correct to me. I'll do full bootstraps and regression test runs with it on my usual targets (x86_64, arm, m68k, sparc64, powerpc64) shortly.