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.

Reply via email to