http://gcc.gnu.org/bugzilla/show_bug.cgi?id=47543
--- Comment #7 from Jeffrey A. Law <law at redhat dot com> 2011-01-31 17:58:37 UTC --- When I first started looking at this problem I was ready to point the finger at the ARM backend. A missing secondary reload or something along those lines.. However, after further investigation, this may be a latent reload bug. So basically what happens is we determine that a particular pseudo is better left in memory and the equivalent address is ip + 32. So we've got this memory address: (plus:SI (reg/v/f:SI 12 ip [orig:161 state ] [161]) (const_int 32 [0x20])) There's two problems. The first is (reg:SI 12) is not a valid base address for the thumb. Second (const_int 32) is an invalid offset for QImode on thumb. Reload reloads the base register generating: (plus:SI (reg:SI 5 r5) (const_int 32 [0x20])) That's an improvement (we've got a valid base register), but the memory address is still invalid for QImode due to the out-of-range offset. As I've sat here tracking find_reloads_address, I'm really starting to wonder if this really is a latent reload bug. The best chance I see to fix this problem is the reloading code guarded by this conditional (it will reload the displacement into a reg or reload the entire address into a reg). The code doesn't trigger because the base register is not valid. /* If we have address of a stack slot but it's not valid because the displacement is too large, compute the sum in a register. Handle all base registers here, not just fp/ap/sp, because on some targets (namely SH) we can also get too large displacements from big-endian corrections. */ else if (GET_CODE (ad) == PLUS && REG_P (XEXP (ad, 0)) && REGNO (XEXP (ad, 0)) < FIRST_PSEUDO_REGISTER && CONST_INT_P (XEXP (ad, 1)) && regno_ok_for_base_p (REGNO (XEXP (ad, 0)), mode, PLUS, CONST_INT)) There's no other clauses that apply so we eventually call: return find_reloads_address_1 (mode, ad, 0, MEM, SCRATCH, loc, opnum, type, ind_levels, insn); Which recurses via the code: else if (code1 == CONST_INT || code1 == CONST || code1 == SYMBOL_REF || code1 == LABEL_REF) find_reloads_address_1 (mode, orig_op0, 0, PLUS, code1, &XEXP (x, 0), opnum, type, ind_levels, insn); Which ultimately reloads the base register leaving the out-of-range constant. Presumably this hasn't caused us many problems in the past because we only rarely decide to leave pseudos in memory and even rarer do we address them via an invalid base register and an out-of-range offset. The fix is relatively simple, given an address of the form (plus (reg) (const_int)) If reloading the reg doesn't make a valid address, then the problem is the constant and we should reload the entire address. That's a 1-line change. Which looks something like that attached patch.