diff mbox

RFC/A: A generic decompose_address routine

Message ID 87625ykxo0.fsf@talisman.home
State New
Headers show

Commit Message

Richard Sandiford Oct. 25, 2012, 10:13 p.m. UTC
"H.J. Lu" <hjl.tools@gmail.com> writes:
> On Thu, Oct 25, 2012 at 12:57 PM, Richard Sandiford
> <rdsandiford@googlemail.com> wrote:
>> This patch is an attempt at the routine sketched here:
>>
>>     http://gcc.gnu.org/ml/gcc-patches/2012-10/msg01016.html
>>
>> for decomposing addresses into constituent parts.  It applies
>> on top of the patches I sent out earlier today.  To summarise
>> that message, the main point is to have an address description
>> in which we know how the individual parts relate to one another.
>>
>> If the patch looks OK, I'd like to try doing the same for IRA.
>> Maybe other passes could use the routines too; I'm not sure.
>>
>> An alternative to having a generic routine would be to add a target hook.
>> However, the whole target address interface could do with some clean-up,
>> so I'd rather not add a new hook until either (a) it becomes absolutely
>> necessary, because of some target weirdness I don't know about or
>> (b) there's a coordinated plan to handle things like legitimacy,
>> base classes, and index classes.
>>
>> I'm hoping this will help with the x32 problems that HJ is seeing.
>> Like Vlad, I don't have a set-up to try for certain, but I tried
>
> Do you have a git branch I can try?  When I applied your patch on
> the current trunk, I got
>
> patching file Makefile.in
> patching file rtl.h
> patching file rtlanal.c
> patching file lra-constraints.c
> Hunk #3 FAILED at 464.
> Hunk #4 succeeded at 1319 (offset -22 lines).
> Hunk #5 succeeded at 1505 (offset -22 lines).
> Hunk #6 FAILED at 2380.
> Hunk #7 FAILED at 2499.
> Hunk #8 succeeded at 2468 (offset -88 lines).
> Hunk #9 FAILED at 2583.
> Hunk #10 FAILED at 2648.
> Hunk #11 succeeded at 2595 with fuzz 1 (offset -77 lines).
> Hunk #12 FAILED at 2690.
> Hunk #13 FAILED at 2716.
> 7 out of 13 hunks FAILED -- saving rejects to file lra-constraints.c.rej

Sorry, here's a combined patch with all 5 changes.

Richard

Comments

H.J. Lu Oct. 25, 2012, 10:34 p.m. UTC | #1
On Thu, Oct 25, 2012 at 3:13 PM, Richard Sandiford
<rdsandiford@googlemail.com> wrote:
> "H.J. Lu" <hjl.tools@gmail.com> writes:
>> On Thu, Oct 25, 2012 at 12:57 PM, Richard Sandiford
>> <rdsandiford@googlemail.com> wrote:
>>> This patch is an attempt at the routine sketched here:
>>>
>>>     http://gcc.gnu.org/ml/gcc-patches/2012-10/msg01016.html
>>>
>>> for decomposing addresses into constituent parts.  It applies
>>> on top of the patches I sent out earlier today.  To summarise
>>> that message, the main point is to have an address description
>>> in which we know how the individual parts relate to one another.
>>>
>>> If the patch looks OK, I'd like to try doing the same for IRA.
>>> Maybe other passes could use the routines too; I'm not sure.
>>>
>>> An alternative to having a generic routine would be to add a target hook.
>>> However, the whole target address interface could do with some clean-up,
>>> so I'd rather not add a new hook until either (a) it becomes absolutely
>>> necessary, because of some target weirdness I don't know about or
>>> (b) there's a coordinated plan to handle things like legitimacy,
>>> base classes, and index classes.
>>>
>>> I'm hoping this will help with the x32 problems that HJ is seeing.
>>> Like Vlad, I don't have a set-up to try for certain, but I tried
>>
>> Do you have a git branch I can try?  When I applied your patch on
>> the current trunk, I got
>>
>> patching file Makefile.in
>> patching file rtl.h
>> patching file rtlanal.c
>> patching file lra-constraints.c
>> Hunk #3 FAILED at 464.
>> Hunk #4 succeeded at 1319 (offset -22 lines).
>> Hunk #5 succeeded at 1505 (offset -22 lines).
>> Hunk #6 FAILED at 2380.
>> Hunk #7 FAILED at 2499.
>> Hunk #8 succeeded at 2468 (offset -88 lines).
>> Hunk #9 FAILED at 2583.
>> Hunk #10 FAILED at 2648.
>> Hunk #11 succeeded at 2595 with fuzz 1 (offset -77 lines).
>> Hunk #12 FAILED at 2690.
>> Hunk #13 FAILED at 2716.
>> 7 out of 13 hunks FAILED -- saving rejects to file lra-constraints.c.rej
>
> Sorry, here's a combined patch with all 5 changes.
>
> Richard
>

It fixes:

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=55049#c3

I can continue x32 build.

Thanks.
H.J. Lu Oct. 26, 2012, 3:53 a.m. UTC | #2
On Thu, Oct 25, 2012 at 3:34 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
> On Thu, Oct 25, 2012 at 3:13 PM, Richard Sandiford
> <rdsandiford@googlemail.com> wrote:
>> "H.J. Lu" <hjl.tools@gmail.com> writes:
>>> On Thu, Oct 25, 2012 at 12:57 PM, Richard Sandiford
>>> <rdsandiford@googlemail.com> wrote:
>>>> This patch is an attempt at the routine sketched here:
>>>>
>>>>     http://gcc.gnu.org/ml/gcc-patches/2012-10/msg01016.html
>>>>
>>>> for decomposing addresses into constituent parts.  It applies
>>>> on top of the patches I sent out earlier today.  To summarise
>>>> that message, the main point is to have an address description
>>>> in which we know how the individual parts relate to one another.
>>>>
>>>> If the patch looks OK, I'd like to try doing the same for IRA.
>>>> Maybe other passes could use the routines too; I'm not sure.
>>>>
>>>> An alternative to having a generic routine would be to add a target hook.
>>>> However, the whole target address interface could do with some clean-up,
>>>> so I'd rather not add a new hook until either (a) it becomes absolutely
>>>> necessary, because of some target weirdness I don't know about or
>>>> (b) there's a coordinated plan to handle things like legitimacy,
>>>> base classes, and index classes.
>>>>
>>>> I'm hoping this will help with the x32 problems that HJ is seeing.
>>>> Like Vlad, I don't have a set-up to try for certain, but I tried
>>>
>>> Do you have a git branch I can try?  When I applied your patch on
>>> the current trunk, I got
>>>
>>> patching file Makefile.in
>>> patching file rtl.h
>>> patching file rtlanal.c
>>> patching file lra-constraints.c
>>> Hunk #3 FAILED at 464.
>>> Hunk #4 succeeded at 1319 (offset -22 lines).
>>> Hunk #5 succeeded at 1505 (offset -22 lines).
>>> Hunk #6 FAILED at 2380.
>>> Hunk #7 FAILED at 2499.
>>> Hunk #8 succeeded at 2468 (offset -88 lines).
>>> Hunk #9 FAILED at 2583.
>>> Hunk #10 FAILED at 2648.
>>> Hunk #11 succeeded at 2595 with fuzz 1 (offset -77 lines).
>>> Hunk #12 FAILED at 2690.
>>> Hunk #13 FAILED at 2716.
>>> 7 out of 13 hunks FAILED -- saving rejects to file lra-constraints.c.rej
>>
>> Sorry, here's a combined patch with all 5 changes.
>>
>> Richard
>>
>
> It fixes:
>
> http://gcc.gnu.org/bugzilla/show_bug.cgi?id=55049#c3
>
> I can continue x32 build.

It fixes x32 bootstrap.

Thanks.
H.J. Lu Oct. 26, 2012, 6:24 a.m. UTC | #3
On Thu, Oct 25, 2012 at 8:53 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
> On Thu, Oct 25, 2012 at 3:34 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
>> On Thu, Oct 25, 2012 at 3:13 PM, Richard Sandiford
>> <rdsandiford@googlemail.com> wrote:
>>> "H.J. Lu" <hjl.tools@gmail.com> writes:
>>>> On Thu, Oct 25, 2012 at 12:57 PM, Richard Sandiford
>>>> <rdsandiford@googlemail.com> wrote:
>>>>> This patch is an attempt at the routine sketched here:
>>>>>
>>>>>     http://gcc.gnu.org/ml/gcc-patches/2012-10/msg01016.html
>>>>>
>>>>> for decomposing addresses into constituent parts.  It applies
>>>>> on top of the patches I sent out earlier today.  To summarise
>>>>> that message, the main point is to have an address description
>>>>> in which we know how the individual parts relate to one another.
>>>>>
>>>>> If the patch looks OK, I'd like to try doing the same for IRA.
>>>>> Maybe other passes could use the routines too; I'm not sure.
>>>>>
>>>>> An alternative to having a generic routine would be to add a target hook.
>>>>> However, the whole target address interface could do with some clean-up,
>>>>> so I'd rather not add a new hook until either (a) it becomes absolutely
>>>>> necessary, because of some target weirdness I don't know about or
>>>>> (b) there's a coordinated plan to handle things like legitimacy,
>>>>> base classes, and index classes.
>>>>>
>>>>> I'm hoping this will help with the x32 problems that HJ is seeing.
>>>>> Like Vlad, I don't have a set-up to try for certain, but I tried
>>>>
>>>> Do you have a git branch I can try?  When I applied your patch on
>>>> the current trunk, I got
>>>>
>>>> patching file Makefile.in
>>>> patching file rtl.h
>>>> patching file rtlanal.c
>>>> patching file lra-constraints.c
>>>> Hunk #3 FAILED at 464.
>>>> Hunk #4 succeeded at 1319 (offset -22 lines).
>>>> Hunk #5 succeeded at 1505 (offset -22 lines).
>>>> Hunk #6 FAILED at 2380.
>>>> Hunk #7 FAILED at 2499.
>>>> Hunk #8 succeeded at 2468 (offset -88 lines).
>>>> Hunk #9 FAILED at 2583.
>>>> Hunk #10 FAILED at 2648.
>>>> Hunk #11 succeeded at 2595 with fuzz 1 (offset -77 lines).
>>>> Hunk #12 FAILED at 2690.
>>>> Hunk #13 FAILED at 2716.
>>>> 7 out of 13 hunks FAILED -- saving rejects to file lra-constraints.c.rej
>>>
>>> Sorry, here's a combined patch with all 5 changes.
>>>
>>> Richard
>>>
>>
>> It fixes:
>>
>> http://gcc.gnu.org/bugzilla/show_bug.cgi?id=55049#c3
>>
>> I can continue x32 build.
>
> It fixes x32 bootstrap.
>
> Thanks.
>

Hi Richard,

Please try your patch on Linux/ia32 with go enabled.  There is
one go test which runs for a long time:

8149 hjl       20   0 49388  40m 9.8m R 99.3  0.3  15:18.35 go1

and it is still running.
Richard Sandiford Oct. 26, 2012, 8:35 a.m. UTC | #4
Sorry HJ, I got your message just after committing.

"H.J. Lu" <hjl.tools@gmail.com> writes:
> Please try your patch on Linux/ia32 with go enabled.  There is
> one go test which runs for a long time:
>
> 8149 hjl       20   0 49388  40m 9.8m R 99.3  0.3  15:18.35 go1
>
> and it is still running.

Are you sure this new?  I can see long tests in libgo too, but the ones
I looked at were because of long var-tracking times:

  http://gcc.gnu.org/bugzilla/show_bug.cgi?id=54507
  http://gcc.gnu.org/bugzilla/show_bug.cgi?id=54402

If it is new, which test has got worse?

Richard
Richard Sandiford Oct. 26, 2012, 10:55 a.m. UTC | #5
Richard Sandiford <rdsandiford@googlemail.com> writes:
> Sorry HJ, I got your message just after committing.
>
> "H.J. Lu" <hjl.tools@gmail.com> writes:
>> Please try your patch on Linux/ia32 with go enabled.  There is
>> one go test which runs for a long time:
>>
>> 8149 hjl       20   0 49388  40m 9.8m R 99.3  0.3  15:18.35 go1
>>
>> and it is still running.
>
> Are you sure this new?  I can see long tests in libgo too, but the ones
> I looked at were because of long var-tracking times:
>
>   http://gcc.gnu.org/bugzilla/show_bug.cgi?id=54507
>   http://gcc.gnu.org/bugzilla/show_bug.cgi?id=54402
>
> If it is new, which test has got worse?

FWIW, I tried reverting all my patches from yesterday and then
bootstrapped on ia32.  My results were similar to Uros's:
the libgo reflect test took 46m 40s to compile, but without
var tracking it took 20s.  I checked that the compile time
without var tracking was still 20s after reapplying my patches,
and that the patches didn't affect the asm output.

Richard
H.J. Lu Oct. 26, 2012, 11:33 a.m. UTC | #6
On Fri, Oct 26, 2012 at 3:55 AM, Richard Sandiford
<rdsandiford@googlemail.com> wrote:
> Richard Sandiford <rdsandiford@googlemail.com> writes:
>> Sorry HJ, I got your message just after committing.
>>
>> "H.J. Lu" <hjl.tools@gmail.com> writes:
>>> Please try your patch on Linux/ia32 with go enabled.  There is
>>> one go test which runs for a long time:
>>>
>>> 8149 hjl       20   0 49388  40m 9.8m R 99.3  0.3  15:18.35 go1
>>>
>>> and it is still running.
>>
>> Are you sure this new?  I can see long tests in libgo too, but the ones
>> I looked at were because of long var-tracking times:
>>
>>   http://gcc.gnu.org/bugzilla/show_bug.cgi?id=54507
>>   http://gcc.gnu.org/bugzilla/show_bug.cgi?id=54402
>>
>> If it is new, which test has got worse?
>
> FWIW, I tried reverting all my patches from yesterday and then
> bootstrapped on ia32.  My results were similar to Uros's:
> the libgo reflect test took 46m 40s to compile, but without
> var tracking it took 20s.  I checked that the compile time
> without var tracking was still 20s after reapplying my patches,
> and that the patches didn't affect the asm output.
>
> Richard
>

You are right.  reflect test took a long time to compile.
It has nothing to with LRA.
Jakub Jelinek Oct. 26, 2012, 11:51 a.m. UTC | #7
On Fri, Oct 26, 2012 at 04:33:07AM -0700, H.J. Lu wrote:
> You are right.  reflect test took a long time to compile.
> It has nothing to with LRA.

About the nothing to do with LRA I'm not sure, because on Tuesday my
make -j48 -k check (slowish 16way box, yes,rtl checking) took just 30
minutes and on Thursday already 1h20m.  It seems to be mostly
var-tracking clobber_overlapping_mems, but before LRA it clearly generated
different code that didn't have as many MEM accesses or MEM based tracked
values as it does now, otherwise it wouldn't take that long.

	Jakub
H.J. Lu Oct. 26, 2012, 9:55 p.m. UTC | #8
On Fri, Oct 26, 2012 at 4:51 AM, Jakub Jelinek <jakub@redhat.com> wrote:
> On Fri, Oct 26, 2012 at 04:33:07AM -0700, H.J. Lu wrote:
>> You are right.  reflect test took a long time to compile.
>> It has nothing to with LRA.
>
> About the nothing to do with LRA I'm not sure, because on Tuesday my
> make -j48 -k check (slowish 16way box, yes,rtl checking) took just 30
> minutes and on Thursday already 1h20m.  It seems to be mostly
> var-tracking clobber_overlapping_mems, but before LRA it clearly generated
> different code that didn't have as many MEM accesses or MEM based tracked
> values as it does now, otherwise it wouldn't take that long.
>
>         Jakub

I opened:

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=55092

It is a serious LRA regression.
H.J. Lu Oct. 27, 2012, 12:22 a.m. UTC | #9
On Thu, Oct 25, 2012 at 8:53 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
> On Thu, Oct 25, 2012 at 3:34 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
>> On Thu, Oct 25, 2012 at 3:13 PM, Richard Sandiford
>> <rdsandiford@googlemail.com> wrote:
>>> "H.J. Lu" <hjl.tools@gmail.com> writes:
>>>> On Thu, Oct 25, 2012 at 12:57 PM, Richard Sandiford
>>>> <rdsandiford@googlemail.com> wrote:
>>>>> This patch is an attempt at the routine sketched here:
>>>>>
>>>>>     http://gcc.gnu.org/ml/gcc-patches/2012-10/msg01016.html
>>>>>
>>>>> for decomposing addresses into constituent parts.  It applies
>>>>> on top of the patches I sent out earlier today.  To summarise
>>>>> that message, the main point is to have an address description
>>>>> in which we know how the individual parts relate to one another.
>>>>>
>>>>> If the patch looks OK, I'd like to try doing the same for IRA.
>>>>> Maybe other passes could use the routines too; I'm not sure.
>>>>>
>>>>> An alternative to having a generic routine would be to add a target hook.
>>>>> However, the whole target address interface could do with some clean-up,
>>>>> so I'd rather not add a new hook until either (a) it becomes absolutely
>>>>> necessary, because of some target weirdness I don't know about or
>>>>> (b) there's a coordinated plan to handle things like legitimacy,
>>>>> base classes, and index classes.
>>>>>
>>>>> I'm hoping this will help with the x32 problems that HJ is seeing.
>>>>> Like Vlad, I don't have a set-up to try for certain, but I tried
>>>>
>>>> Do you have a git branch I can try?  When I applied your patch on
>>>> the current trunk, I got
>>>>
>>> Sorry, here's a combined patch with all 5 changes.
>>>
>>> Richard
>>>
>>
>> It fixes:
>>
>> http://gcc.gnu.org/bugzilla/show_bug.cgi?id=55049#c3
>>
>> I can continue x32 build.
>
> It fixes x32 bootstrap.
>
> Thanks.
>

There is another address related regression:

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=55093
diff mbox

Patch

Index: gcc/rtlanal.c
===================================================================
--- gcc/rtlanal.c	(revision 192797)
+++ gcc/rtlanal.c	(working copy)
@@ -38,6 +38,7 @@ 
 #include "df.h"
 #include "tree.h"
 #include "emit-rtl.h"  /* FIXME: Can go away once crtl is moved to rtl.h.  */
+#include "addresses.h"
 
 /* Forward declarations */
 static void set_of_1 (rtx, const_rtx, void *);
@@ -5438,3 +5439,371 @@ 
     }
 }
 
+/* Strip outer address "mutations" from LOC and return a pointer to the
+   inner value.  If OUTER_CODE is nonnull, store the code of the innermost
+   stripped expression there.
+
+   "Mutations" either convert between modes or apply some kind of
+   alignment.  */
+
+rtx *
+strip_address_mutations (rtx *loc, enum rtx_code *outer_code)
+{
+  for (;;)
+    {
+      enum rtx_code code = GET_CODE (*loc);
+      if (GET_RTX_CLASS (code) == RTX_UNARY)
+	/* Things like SIGN_EXTEND, ZERO_EXTEND and TRUNCATE can be
+	   used to convert between pointer sizes.  */
+	loc = &XEXP (*loc, 0);
+      else if (code == AND && CONST_INT_P (XEXP (*loc, 1)))
+	/* (and ... (const_int -X)) is used to align to X bytes.  */
+	loc = &XEXP (*loc, 0);
+      else
+	return loc;
+      if (outer_code)
+	*outer_code = code;
+    }
+}
+
+/* Return true if X must be a base rather than an index.  */
+
+static bool
+must_be_base_p (rtx x)
+{
+  return GET_CODE (x) == LO_SUM;
+}
+
+/* Return true if X must be an index rather than a base.  */
+
+static bool
+must_be_index_p (rtx x)
+{
+  return GET_CODE (x) == MULT || GET_CODE (x) == ASHIFT;
+}
+
+/* Set the segment part of address INFO to LOC, given that INNER is the
+   unmutated value.  */
+
+static void
+set_address_segment (struct address_info *info, rtx *loc, rtx *inner)
+{
+  gcc_checking_assert (GET_CODE (*inner) == UNSPEC);
+
+  gcc_assert (!info->segment);
+  info->segment = loc;
+  info->segment_term = inner;
+}
+
+/* Set the base part of address INFO to LOC, given that INNER is the
+   unmutated value.  */
+
+static void
+set_address_base (struct address_info *info, rtx *loc, rtx *inner)
+{
+  if (GET_CODE (*inner) == LO_SUM)
+    inner = strip_address_mutations (&XEXP (*inner, 0));
+  gcc_checking_assert (REG_P (*inner)
+		       || MEM_P (*inner)
+		       || GET_CODE (*inner) == SUBREG);
+
+  gcc_assert (!info->base);
+  info->base = loc;
+  info->base_term = inner;
+}
+
+/* Set the index part of address INFO to LOC, given that INNER is the
+   unmutated value.  */
+
+static void
+set_address_index (struct address_info *info, rtx *loc, rtx *inner)
+{
+  if ((GET_CODE (*inner) == MULT || GET_CODE (*inner) == ASHIFT)
+      && CONSTANT_P (XEXP (*inner, 1)))
+    inner = strip_address_mutations (&XEXP (*inner, 0));
+  gcc_checking_assert (REG_P (*inner)
+		       || MEM_P (*inner)
+		       || GET_CODE (*inner) == SUBREG);
+
+  gcc_assert (!info->index);
+  info->index = loc;
+  info->index_term = inner;
+}
+
+/* Set the displacement part of address INFO to LOC, given that INNER
+   is the constant term.  */
+
+static void
+set_address_disp (struct address_info *info, rtx *loc, rtx *inner)
+{
+  gcc_checking_assert (CONSTANT_P (*inner));
+
+  gcc_assert (!info->disp);
+  info->disp = loc;
+  info->disp_term = inner;
+}
+
+/* INFO->INNER describes a {PRE,POST}_{INC,DEC} address.  Set up the
+   rest of INFO accordingly.  */
+
+static void
+decompose_incdec_address (struct address_info *info)
+{
+  info->autoinc_p = true;
+
+  rtx *base = &XEXP (*info->inner, 0);
+  set_address_base (info, base, base);
+  gcc_checking_assert (info->base == info->base_term);
+
+  /* These addresses are only valid when the size of the addressed
+     value is known.  */
+  gcc_checking_assert (info->mode != VOIDmode);
+}
+
+/* INFO->INNER describes a {PRE,POST}_MODIFY address.  Set up the rest
+   of INFO accordingly.  */
+
+static void
+decompose_automod_address (struct address_info *info)
+{
+  info->autoinc_p = true;
+
+  rtx *base = &XEXP (*info->inner, 0);
+  set_address_base (info, base, base);
+  gcc_checking_assert (info->base == info->base_term);
+
+  rtx plus = XEXP (*info->inner, 1);
+  gcc_assert (GET_CODE (plus) == PLUS);
+
+  info->base_term2 = &XEXP (plus, 0);
+  gcc_checking_assert (rtx_equal_p (*info->base_term, *info->base_term2));
+
+  rtx *step = &XEXP (plus, 1);
+  rtx *inner_step = strip_address_mutations (step);
+  if (CONSTANT_P (*inner_step))
+    set_address_disp (info, step, inner_step);
+  else
+    set_address_index (info, step, inner_step);
+}
+
+/* Treat *LOC as a tree of PLUS operands and store pointers to the summed
+   values in [PTR, END).  Return a pointer to the end of the used array.  */
+
+static rtx **
+extract_plus_operands (rtx *loc, rtx **ptr, rtx **end)
+{
+  rtx x = *loc;
+  if (GET_CODE (x) == PLUS)
+    {
+      ptr = extract_plus_operands (&XEXP (x, 0), ptr, end);
+      ptr = extract_plus_operands (&XEXP (x, 1), ptr, end);
+    }
+  else
+    {
+      gcc_assert (ptr != end);
+      *ptr++ = loc;
+    }
+  return ptr;
+}
+
+/* Evaluate the likelihood of X being a base or index value, returning
+   positive if it is likely to be a base, negative if it is likely to be
+   an index, and 0 if we can't tell.  Make the magnitude of the return
+   value reflect the amount of confidence we have in the answer.
+
+   MODE, AS, OUTER_CODE and INDEX_CODE are as for ok_for_base_p_1.  */
+
+static int
+baseness (rtx x, enum machine_mode mode, addr_space_t as,
+	  enum rtx_code outer_code, enum rtx_code index_code)
+{
+  /* See whether we can be certain.  */
+  if (must_be_base_p (x))
+    return 3;
+  if (must_be_index_p (x))
+    return -3;
+
+  /* Believe *_POINTER unless the address shape requires otherwise.  */
+  if (REG_P (x) && REG_POINTER (x))
+    return 2;
+  if (MEM_P (x) && MEM_POINTER (x))
+    return 2;
+
+  if (REG_P (x) && HARD_REGISTER_P (x))
+    {
+      /* X is a hard register.  If it only fits one of the base
+	 or index classes, choose that interpretation.  */
+      int regno = REGNO (x);
+      bool base_p = ok_for_base_p_1 (regno, mode, as, outer_code, index_code);
+      bool index_p = REGNO_OK_FOR_INDEX_P (regno);
+      if (base_p != index_p)
+	return base_p ? 1 : -1;
+    }
+  return 0;
+}
+
+/* INFO->INNER describes a normal, non-automodified address.
+   Fill in the rest of INFO accordingly.  */
+
+static void
+decompose_normal_address (struct address_info *info)
+{
+  /* Treat the address as the sum of up to four values.  */
+  rtx *ops[4];
+  size_t n_ops = extract_plus_operands (info->inner, ops,
+					ops + ARRAY_SIZE (ops)) - ops;
+
+  /* If there is more than one component, any base component is in a PLUS.  */
+  if (n_ops > 1)
+    info->base_outer_code = PLUS;
+
+  /* Separate the parts that contain a REG or MEM from those that don't.
+     Record the latter in INFO and leave the former in OPS.  */
+  rtx *inner_ops[4];
+  size_t out = 0;
+  for (size_t in = 0; in < n_ops; ++in)
+    {
+      rtx *loc = ops[in];
+      rtx *inner = strip_address_mutations (loc);
+      if (CONSTANT_P (*inner))
+	set_address_disp (info, loc, inner);
+      else if (GET_CODE (*inner) == UNSPEC)
+	set_address_segment (info, loc, inner);
+      else
+	{
+	  ops[out] = loc;
+	  inner_ops[out] = inner;
+	  ++out;
+	}
+    }
+
+  /* Classify the remaining OPS members as bases and indexes.  */
+  if (out == 1)
+    {
+      /* Assume that the remaining value is a base unless the shape
+	 requires otherwise.  */
+      if (!must_be_index_p (*inner_ops[0]))
+	set_address_base (info, ops[0], inner_ops[0]);
+      else
+	set_address_index (info, ops[0], inner_ops[0]);
+    }
+  else if (out == 2)
+    {
+      /* In the event of a tie, assume the base comes first.  */
+      if (baseness (*inner_ops[0], info->mode, info->as, PLUS,
+		    GET_CODE (*ops[1]))
+	  >= baseness (*inner_ops[1], info->mode, info->as, PLUS,
+		       GET_CODE (*ops[0])))
+	{
+	  set_address_base (info, ops[0], inner_ops[0]);
+	  set_address_index (info, ops[1], inner_ops[1]);
+	}
+      else
+	{
+	  set_address_base (info, ops[1], inner_ops[1]);
+	  set_address_index (info, ops[0], inner_ops[0]);
+	}
+    }
+  else
+    gcc_assert (out == 0);
+}
+
+/* Describe address *LOC in *INFO.  MODE is the mode of the addressed value,
+   or VOIDmode if not known.  AS is the address space associated with LOC.
+   OUTER_CODE is MEM if *LOC is a MEM address and ADDRESS otherwise.  */
+
+void
+decompose_address (struct address_info *info, rtx *loc, enum machine_mode mode,
+		   addr_space_t as, enum rtx_code outer_code)
+{
+  memset (info, 0, sizeof (*info));
+  info->mode = mode;
+  info->as = as;
+  info->addr_outer_code = outer_code;
+  info->outer = loc;
+  info->inner = strip_address_mutations (loc, &outer_code);
+  info->base_outer_code = outer_code;
+  switch (GET_CODE (*info->inner))
+    {
+    case PRE_DEC:
+    case PRE_INC:
+    case POST_DEC:
+    case POST_INC:
+      decompose_incdec_address (info);
+      break;
+
+    case PRE_MODIFY:
+    case POST_MODIFY:
+      decompose_automod_address (info);
+      break;
+
+    default:
+      decompose_normal_address (info);
+      break;
+    }
+}
+
+/* Describe address operand LOC in INFO.  */
+
+void
+decompose_lea_address (struct address_info *info, rtx *loc)
+{
+  decompose_address (info, loc, VOIDmode, ADDR_SPACE_GENERIC, ADDRESS);
+}
+
+/* Describe the address of MEM X in INFO.  */
+
+void
+decompose_mem_address (struct address_info *info, rtx x)
+{
+  gcc_assert (MEM_P (x));
+  decompose_address (info, &XEXP (x, 0), GET_MODE (x),
+		     MEM_ADDR_SPACE (x), MEM);
+}
+
+/* Update INFO after a change to the address it describes.  */
+
+void
+update_address (struct address_info *info)
+{
+  decompose_address (info, info->outer, info->mode, info->as,
+		     info->addr_outer_code);
+}
+
+/* Return the scale applied to *INFO->INDEX_TERM, or 0 if the index is
+   more complicated than that.  */
+
+HOST_WIDE_INT
+get_index_scale (const struct address_info *info)
+{
+  rtx index = *info->index;
+  if (GET_CODE (index) == MULT
+      && CONST_INT_P (XEXP (index, 1))
+      && info->index_term == &XEXP (index, 0))
+    return INTVAL (XEXP (index, 1));
+
+  if (GET_CODE (index) == ASHIFT
+      && CONST_INT_P (XEXP (index, 1))
+      && info->index_term == &XEXP (index, 0))
+    return (HOST_WIDE_INT) 1 << INTVAL (XEXP (index, 1));
+
+  if (info->index == info->index_term)
+    return 1;
+
+  return 0;
+}
+
+/* Return the "index code" of INFO, in the form required by
+   ok_for_base_p_1.  */
+
+enum rtx_code
+get_index_code (const struct address_info *info)
+{
+  if (info->index)
+    return GET_CODE (*info->index);
+
+  if (info->disp)
+    return GET_CODE (*info->disp);
+
+  return SCRATCH;
+}
Index: gcc/lra-constraints.c
===================================================================
--- gcc/lra-constraints.c	(revision 192797)
+++ gcc/lra-constraints.c	(working copy)
@@ -152,6 +152,13 @@ 
 static int new_regno_start;
 static int new_insn_uid_start;
 
+/* If LOC is nonnull, strip any outer subreg from it.  */
+static inline rtx *
+strip_subreg (rtx *loc)
+{
+  return loc && GET_CODE (*loc) == SUBREG ? &SUBREG_REG (*loc) : loc;
+}
+
 /* Return hard regno of REGNO or if it is was not assigned to a hard
    register, use a hard register from its allocno class.  */
 static int
@@ -435,28 +442,6 @@ 
 
 /* The page contains code to extract memory address parts.  */
 
-/* Info about base and index regs of an address.  In some rare cases,
-   base/index register can be actually memory.	In this case we will
-   reload it.  */
-struct address
-{
-  /* NULL if there is no a base register.  */
-  rtx *base_reg_loc;
-  /* Second location of {post/pre}_modify, NULL otherwise.  */
-  rtx *base_reg_loc2;
-  /* NULL if there is no an index register.  */
-  rtx *index_reg_loc;
-  /* Location of index reg * scale or index_reg_loc otherwise.  */
-  rtx *index_loc;
-  /* NULL if there is no a displacement.  */
-  rtx *disp_loc;
-  /* Defined if base_reg_loc is not NULL.  */
-  enum rtx_code base_outer_code, index_code;
-  /* True if the base register is modified in the address, for
-     example, in PRE_INC.  */
-  bool base_modify_p;
-};
-
 /* Wrapper around REGNO_OK_FOR_INDEX_P, to allow pseudos.  */
 static inline bool
 ok_for_index_p_nonstrict (rtx reg)
@@ -479,283 +464,6 @@ 
   return ok_for_base_p_1 (regno, mode, as, outer_code, index_code);
 }
 
-/* Process address part in space AS (or all address if TOP_P) with
-   location *LOC to extract address characteristics.
-
-   If CONTEXT_P is false, we are looking at the base part of an
-   address, otherwise we are looking at the index part.
-
-   MODE is the mode of the memory reference; OUTER_CODE and INDEX_CODE
-   give the context that the rtx appears in; MODIFY_P if *LOC is
-   modified.  */
-static void
-extract_loc_address_regs (bool top_p, enum machine_mode mode, addr_space_t as,
-			  rtx *loc, bool context_p, enum rtx_code outer_code,
-			  enum rtx_code index_code,
-			  bool modify_p, struct address *ad)
-{
-  rtx x = *loc;
-  enum rtx_code code = GET_CODE (x);
-  bool base_ok_p;
-
-  switch (code)
-    {
-    case CONST_INT:
-    case CONST:
-    case SYMBOL_REF:
-    case LABEL_REF:
-      if (! context_p)
-	{
-	  lra_assert (top_p);
-	  ad->disp_loc = loc;
-	}
-      return;
-
-    case CC0:
-    case PC:
-      return;
-
-    case ZERO_EXTEND:
-      /* Pass TOP_P for displacement.  */
-      extract_loc_address_regs (top_p, mode, as, &XEXP (*loc, 0), context_p,
-				code, index_code, modify_p, ad);
-      return;
-
-    case PLUS:
-    case LO_SUM:
-      /* When we have an address that is a sum, we must determine
-	 whether registers are "base" or "index" regs.	If there is a
-	 sum of two registers, we must choose one to be the
-	 "base".  */
-      {
-	rtx *arg0_loc = &XEXP (x, 0);
-	rtx *arg1_loc = &XEXP (x, 1);
-	rtx *tloc;
-	rtx arg0 = *arg0_loc;
-	rtx arg1 = *arg1_loc;
-	enum rtx_code code0 = GET_CODE (arg0);
-	enum rtx_code code1 = GET_CODE (arg1);
-
-	/* Look inside subregs.	 */
-	if (code0 == SUBREG)
-	  {
-	    arg0_loc = &SUBREG_REG (arg0);
-	    arg0 = *arg0_loc;
-	    code0 = GET_CODE (arg0);
-	  }
-	if (code1 == SUBREG)
-	  {
-	    arg1_loc = &SUBREG_REG (arg1);
-	    arg1 = *arg1_loc;
-	    code1 = GET_CODE (arg1);
-	  }
-
-	if (CONSTANT_P (arg0)
-	    || code1 == PLUS || code1 == MULT || code1 == ASHIFT)
-	  {
-	    tloc = arg1_loc;
-	    arg1_loc = arg0_loc;
-	    arg0_loc = tloc;
-	    arg0 = *arg0_loc;
-	    code0 = GET_CODE (arg0);
-	    arg1 = *arg1_loc;
-	    code1 = GET_CODE (arg1);
-	  }
-	/* If this machine only allows one register per address, it
-	   must be in the first operand.  */
-	if (MAX_REGS_PER_ADDRESS == 1 || code == LO_SUM)
-	  {
-	    lra_assert (ad->disp_loc == NULL);
-	    ad->disp_loc = arg1_loc;
-	    extract_loc_address_regs (false, mode, as, arg0_loc, false, code,
-				      code1, modify_p, ad);
-	  }
-	/* Base + disp addressing  */
-	else if (code0 != PLUS && code0 != MULT && code0 != ASHIFT
-		 && CONSTANT_P (arg1))
-	  {
-	    lra_assert (ad->disp_loc == NULL);
-	    ad->disp_loc = arg1_loc;
-	    extract_loc_address_regs (false, mode, as, arg0_loc, false, PLUS,
-				      code1, modify_p, ad);
-	  }
-	/* If index and base registers are the same on this machine,
-	   just record registers in any non-constant operands.	We
-	   assume here, as well as in the tests below, that all
-	   addresses are in canonical form.  */
-	else if (INDEX_REG_CLASS
-		 == base_reg_class (VOIDmode, as, PLUS, SCRATCH)
-		 && code0 != PLUS && code0 != MULT && code0 != ASHIFT)
-	  {
-	    extract_loc_address_regs (false, mode, as, arg0_loc, false, PLUS,
-				      code1, modify_p, ad);
-	    lra_assert (! CONSTANT_P (arg1));
-	    extract_loc_address_regs (false, mode, as, arg1_loc, true, PLUS,
-				      code0, modify_p, ad);
-	  }
-	/* It might be [base + ]index * scale + disp. */
-	else if (CONSTANT_P (arg1))
-	  {
-	    lra_assert (ad->disp_loc == NULL);
-	    ad->disp_loc = arg1_loc;
-	    extract_loc_address_regs (false, mode, as, arg0_loc, context_p,
-				      PLUS, code0, modify_p, ad);
-	  }
-	/* If both operands are registers but one is already a hard
-	   register of index or reg-base class, give the other the
-	   class that the hard register is not.	 */
-	else if (code0 == REG && code1 == REG
-		 && REGNO (arg0) < FIRST_PSEUDO_REGISTER
-		 && ((base_ok_p
-		      = ok_for_base_p_nonstrict (arg0, mode, as, PLUS, REG))
-		     || ok_for_index_p_nonstrict (arg0)))
-	  {
-	    extract_loc_address_regs (false, mode, as, arg0_loc, ! base_ok_p,
-				      PLUS, REG, modify_p, ad);
-	    extract_loc_address_regs (false, mode, as, arg1_loc, base_ok_p,
-				      PLUS, REG, modify_p, ad);
-	  }
-	else if (code0 == REG && code1 == REG
-		 && REGNO (arg1) < FIRST_PSEUDO_REGISTER
-		 && ((base_ok_p
-		      = ok_for_base_p_nonstrict (arg1, mode, as, PLUS, REG))
-		     || ok_for_index_p_nonstrict (arg1)))
-	  {
-	    extract_loc_address_regs (false, mode, as, arg0_loc, base_ok_p,
-				      PLUS, REG, modify_p, ad);
-	    extract_loc_address_regs (false, mode, as, arg1_loc, ! base_ok_p,
-				      PLUS, REG, modify_p, ad);
-	  }
-	/* Otherwise, count equal chances that each might be a base or
-	   index register.  This case should be rare.  */
-	else
-	  {
-	    extract_loc_address_regs (false, mode, as, arg0_loc, false, PLUS,
-				      code1, modify_p, ad);
-	    extract_loc_address_regs (false, mode, as, arg1_loc,
-				      ad->base_reg_loc != NULL, PLUS,
-				      code0, modify_p, ad);
-	  }
-      }
-      break;
-
-    case MULT:
-    case ASHIFT:
-      {
-	rtx *arg0_loc = &XEXP (x, 0);
-	enum rtx_code code0 = GET_CODE (*arg0_loc);
-	
-	if (code0 == CONST_INT)
-	  arg0_loc = &XEXP (x, 1);
-	extract_loc_address_regs (false, mode, as, arg0_loc, true,
-				  outer_code, code, modify_p, ad);
-	lra_assert (ad->index_loc == NULL);
-	ad->index_loc = loc;
-	break;
-      }
-
-    case POST_MODIFY:
-    case PRE_MODIFY:
-      extract_loc_address_regs (false, mode, as, &XEXP (x, 0), false,
-				code, GET_CODE (XEXP (XEXP (x, 1), 1)),
-				true, ad);
-      lra_assert (rtx_equal_p (XEXP (XEXP (x, 1), 0), XEXP (x, 0)));
-      ad->base_reg_loc2 = &XEXP (XEXP (x, 1), 0);
-      if (REG_P (XEXP (XEXP (x, 1), 1)))
-	extract_loc_address_regs (false, mode, as, &XEXP (XEXP (x, 1), 1),
-				  true, code, REG, modify_p, ad);
-      break;
-
-    case POST_INC:
-    case PRE_INC:
-    case POST_DEC:
-    case PRE_DEC:
-      extract_loc_address_regs (false, mode, as, &XEXP (x, 0), false, code,
-				SCRATCH, true, ad);
-      break;
-
-      /* We process memory as a register.  That means we flatten
-	 addresses.  In other words, the final code will never
-	 contains memory in an address even if the target supports
-	 such addresses (it is too rare these days).  Memory also can
-	 occur in address as a result some previous transformations
-	 like equivalence substitution.	 */
-    case MEM:
-    case REG:
-      if (context_p)
-	{
-	  lra_assert (ad->index_reg_loc == NULL);
-	  ad->index_reg_loc = loc;
-	}
-      else
-	{
-	  lra_assert (ad->base_reg_loc == NULL);
-	  ad->base_reg_loc = loc;
-	  ad->base_outer_code = outer_code;
-	  ad->index_code = index_code;
-	  ad->base_modify_p = modify_p;
-	}
-      break;
-    default:
-      {
-	const char *fmt = GET_RTX_FORMAT (code);
-	int i;
-
-	if (GET_RTX_LENGTH (code) != 1
-	    || fmt[0] != 'e' || GET_CODE (XEXP (x, 0)) != UNSPEC)
-	  {
-	    for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
-	      if (fmt[i] == 'e')
-		extract_loc_address_regs (false, mode, as, &XEXP (x, i),
-					  context_p, code, SCRATCH,
-					  modify_p, ad);
-	    break;
-	  }
-	/* fall through for case UNARY_OP (UNSPEC ...)	*/
-      }
-
-    case UNSPEC:
-      if (ad->disp_loc == NULL)
-	ad->disp_loc = loc;
-      else if (ad->base_reg_loc == NULL)
-	{
-	  ad->base_reg_loc = loc;
-	  ad->base_outer_code = outer_code;
-	  ad->index_code = index_code;
-	  ad->base_modify_p = modify_p;
-	}
-      else
-	{
-	  lra_assert (ad->index_reg_loc == NULL);
-	  ad->index_reg_loc = loc;
-	}
-      break;
-
-    }
-}
-
-
-/* Describe address *LOC in AD.  There are two cases:
-   - *LOC is the address in a (mem ...).  In this case OUTER_CODE is MEM
-     and AS is the mem's address space.
-   - *LOC is matched to an address constraint such as 'p'.  In this case
-     OUTER_CODE is ADDRESS and AS is ADDR_SPACE_GENERIC.  */
-static void
-extract_address_regs (enum machine_mode mem_mode, addr_space_t as,
-		      rtx *loc, enum rtx_code outer_code, struct address *ad)
-{
-  ad->base_reg_loc = ad->base_reg_loc2
-    = ad->index_reg_loc = ad->index_loc = ad->disp_loc = NULL;
-  ad->base_outer_code = SCRATCH;
-  ad->index_code = SCRATCH;
-  ad->base_modify_p = false;
-  extract_loc_address_regs (true, mem_mode, as, loc, false, outer_code,
-			    SCRATCH, false, ad);  
-  if (ad->index_loc == NULL)
-    /* SUBREG ??? */
-    ad->index_loc = ad->index_reg_loc;
-}
-
 
 
 /* The page contains major code to choose the current insn alternative
@@ -1334,11 +1042,13 @@ 
 {
   int regno;
   enum reg_class rclass, new_class;
-  rtx reg = *loc;
+  rtx reg;
   rtx new_reg;
   enum machine_mode mode;
   bool before_p = false;
 
+  loc = strip_subreg (loc);
+  reg = *loc;
   mode = GET_MODE (reg);
   if (! REG_P (reg))
     {
@@ -1518,21 +1228,13 @@ 
     }
   if (MEM_P (x))
     {
-      struct address ad;
-      enum machine_mode mode = GET_MODE (x);
-      rtx *addr_loc = &XEXP (x, 0);
+      struct address_info ad;
 
-      extract_address_regs (mode, MEM_ADDR_SPACE (x), addr_loc, MEM, &ad);
-      if (ad.base_reg_loc != NULL)
-	{
-	  if (uses_hard_regs_p (*ad.base_reg_loc, set))
-	    return true;
-	}
-      if (ad.index_reg_loc != NULL)
-	{
-	  if (uses_hard_regs_p (*ad.index_reg_loc, set))
-	    return true;
-	}
+      decompose_mem_address (&ad, x);
+      if (ad.base_term != NULL && uses_hard_regs_p (*ad.base_term, set))
+	return true;
+      if (ad.index_term != NULL && uses_hard_regs_p (*ad.index_term, set))
+	return true;
     }
   fmt = GET_RTX_FORMAT (code);
   for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
@@ -2379,46 +2081,92 @@ 
 #endif
 }
 
-/* Make reload base reg + disp from address AD in space AS of memory
-   with MODE into a new pseudo.	 Return the new pseudo.	 */
+/* Return whether address AD is valid.  */
+
+static bool
+valid_address_p (struct address_info *ad)
+{
+  /* Some ports do not check displacements for eliminable registers,
+     so we replace them temporarily with the elimination target.  */
+  rtx saved_base_reg = NULL_RTX;
+  rtx saved_index_reg = NULL_RTX;
+  rtx *base_term = strip_subreg (ad->base_term);
+  rtx *index_term = strip_subreg (ad->index_term);
+  if (base_term != NULL)
+    {
+      saved_base_reg = *base_term;
+      lra_eliminate_reg_if_possible (base_term);
+      if (ad->base_term2 != NULL)
+	*ad->base_term2 = *ad->base_term;
+    }
+  if (index_term != NULL)
+    {
+      saved_index_reg = *index_term;
+      lra_eliminate_reg_if_possible (index_term);
+    }
+  bool ok_p = valid_address_p (ad->mode, *ad->outer, ad->as);
+  if (saved_base_reg != NULL_RTX)
+    {
+      *base_term = saved_base_reg;
+      if (ad->base_term2 != NULL)
+	*ad->base_term2 = *ad->base_term;
+    }
+  if (saved_index_reg != NULL_RTX)
+    *index_term = saved_index_reg;
+  return ok_p;
+}
+
+/* Make reload base reg + disp from address AD.  Return the new pseudo.  */
 static rtx
-base_plus_disp_to_reg (enum machine_mode mode, addr_space_t as,
-		       struct address *ad)
+base_plus_disp_to_reg (struct address_info *ad)
 {
   enum reg_class cl;
   rtx new_reg;
 
-  lra_assert (ad->base_reg_loc != NULL && ad->disp_loc != NULL);
-  cl = base_reg_class (mode, as, ad->base_outer_code, ad->index_code);
-  new_reg = lra_create_new_reg (Pmode, NULL_RTX, cl, "base + disp");
-  lra_emit_add (new_reg, *ad->base_reg_loc, *ad->disp_loc);
+  lra_assert (ad->base == ad->base_term && ad->disp == ad->disp_term);
+  cl = base_reg_class (ad->mode, ad->as, ad->base_outer_code,
+		       get_index_code (ad));
+  new_reg = lra_create_new_reg (GET_MODE (*ad->base_term), NULL_RTX,
+				cl, "base + disp");
+  lra_emit_add (new_reg, *ad->base_term, *ad->disp_term);
   return new_reg;
 }
 
-/* Make substitution in address AD in space AS with location ADDR_LOC.
-   Update AD and ADDR_LOC if it is necessary.  Return true if a
-   substitution was made.  */
+/* Return true if we can add a displacement to address AD, even if that
+   makes the address invalid.  The fix-up code requires any new address
+   to be the sum of the BASE_TERM, INDEX and DISP_TERM fields.  */
 static bool
-equiv_address_substitution (struct address *ad, rtx *addr_loc,
-			    enum machine_mode mode, addr_space_t as,
-			    enum rtx_code code)
+can_add_disp_p (struct address_info *ad)
 {
-  rtx base_reg, new_base_reg, index_reg, new_index_reg;
+  return (!ad->autoinc_p
+	  && ad->segment == NULL
+	  && ad->base == ad->base_term
+	  && ad->disp == ad->disp_term);
+}
+
+/* Make equiv substitution in address AD.  Return true if a substitution
+   was made.  */
+static bool
+equiv_address_substitution (struct address_info *ad)
+{
+  rtx base_reg, new_base_reg, index_reg, new_index_reg, *base_term, *index_term;
   HOST_WIDE_INT disp, scale;
   bool change_p;
 
-  if (ad->base_reg_loc == NULL)
+  base_term = strip_subreg (ad->base_term);
+  if (base_term == NULL)
     base_reg = new_base_reg = NULL_RTX;
   else
     {
-      base_reg = *ad->base_reg_loc;
+      base_reg = *base_term;
       new_base_reg = get_equiv_substitution (base_reg);
     }
-  if (ad->index_reg_loc == NULL)
+  index_term = strip_subreg (ad->index_term);
+  if (index_term == NULL)
     index_reg = new_index_reg = NULL_RTX;
   else
     {
-      index_reg = *ad->index_reg_loc;
+      index_reg = *index_term;
       new_index_reg = get_equiv_substitution (index_reg);
     }
   if (base_reg == new_base_reg && index_reg == new_index_reg)
@@ -2429,56 +2177,53 @@ 
     {
       fprintf (lra_dump_file, "Changing address in insn %d ",
 	       INSN_UID (curr_insn));
-      print_value_slim (lra_dump_file, *addr_loc, 1);
+      print_value_slim (lra_dump_file, *ad->outer, 1);
     }
   if (base_reg != new_base_reg)
     {
       if (REG_P (new_base_reg))
 	{
-	  *ad->base_reg_loc = new_base_reg;
+	  *base_term = new_base_reg;
 	  change_p = true;
 	}
       else if (GET_CODE (new_base_reg) == PLUS
 	       && REG_P (XEXP (new_base_reg, 0))
-	       && CONST_INT_P (XEXP (new_base_reg, 1)))
+	       && CONST_INT_P (XEXP (new_base_reg, 1))
+	       && can_add_disp_p (ad))
 	{
 	  disp += INTVAL (XEXP (new_base_reg, 1));
-	  *ad->base_reg_loc = XEXP (new_base_reg, 0);
+	  *base_term = XEXP (new_base_reg, 0);
 	  change_p = true;
 	}
-      if (ad->base_reg_loc2 != NULL)
-	*ad->base_reg_loc2 = *ad->base_reg_loc;
+      if (ad->base_term2 != NULL)
+	*ad->base_term2 = *ad->base_term;
     }
-  scale = 1;
-  if (ad->index_loc != NULL && GET_CODE (*ad->index_loc) == MULT)
-    {
-      lra_assert (CONST_INT_P (XEXP (*ad->index_loc, 1)));
-      scale = INTVAL (XEXP (*ad->index_loc, 1));
-    }
   if (index_reg != new_index_reg)
     {
       if (REG_P (new_index_reg))
 	{
-	  *ad->index_reg_loc = new_index_reg;
+	  *index_term = new_index_reg;
 	  change_p = true;
 	}
       else if (GET_CODE (new_index_reg) == PLUS
 	       && REG_P (XEXP (new_index_reg, 0))
-	       && CONST_INT_P (XEXP (new_index_reg, 1)))
+	       && CONST_INT_P (XEXP (new_index_reg, 1))
+	       && can_add_disp_p (ad)
+	       && (scale = get_index_scale (ad)))
 	{
 	  disp += INTVAL (XEXP (new_index_reg, 1)) * scale;
-	  *ad->index_reg_loc = XEXP (new_index_reg, 0);
+	  *index_term = XEXP (new_index_reg, 0);
 	  change_p = true;
 	}
     }
   if (disp != 0)
     {
-      if (ad->disp_loc != NULL)
-	*ad->disp_loc = plus_constant (Pmode, *ad->disp_loc, disp);
+      if (ad->disp != NULL)
+	*ad->disp = plus_constant (GET_MODE (*ad->inner), *ad->disp, disp);
       else
 	{
-	  *addr_loc = gen_rtx_PLUS (Pmode, *addr_loc, GEN_INT (disp));
-	  extract_address_regs (mode, as, addr_loc, code, ad);
+	  *ad->inner = plus_constant (GET_MODE (*ad->inner), *ad->inner, disp);
+	  update_address (ad);
 	}
       change_p = true;
     }
@@ -2489,143 +2234,118 @@ 
       else
 	{
 	  fprintf (lra_dump_file, " on equiv ");
-	  print_value_slim (lra_dump_file, *addr_loc, 1);
+	  print_value_slim (lra_dump_file, *ad->outer, 1);
 	  fprintf (lra_dump_file, "\n");
 	}
     }
   return change_p;
 }
 
-/* Major function to make reloads for address in operand NOP.  Add to
-   reloads to the list *BEFORE and *AFTER.  We might need to add
+/* Major function to make reloads for an address in operand NOP.
+   The supported cases are:
+
+   1) an address that existed before LRA started, at which point it must
+      have been valid.  These addresses are subject to elimination and
+      may have become invalid due to the elimination offset being out
+      of range.
+
+   2) an address created by forcing a constant to memory (force_const_to_mem).
+      The initial form of these addresses might not be valid, and it is this
+      function's job to make them valid.
+
+   3) a frame address formed from a register and a (possibly zero)
+      constant offset.  As above, these addresses might not be valid
+      and this function must make them so.
+
+   Add reloads to the lists *BEFORE and *AFTER.  We might need to add
    reloads to *AFTER because of inc/dec, {pre, post} modify in the
    address.  Return true for any RTL change.  */
 static bool
 process_address (int nop, rtx *before, rtx *after)
 {
-  struct address ad;
-  enum machine_mode mode;
-  rtx new_reg, *addr_loc, saved_index_reg, saved_base_reg;
-  bool ok_p;
-  addr_space_t as;
+  struct address_info ad;
+  rtx new_reg;
   rtx op = *curr_id->operand_loc[nop];
   const char *constraint = curr_static_id->operand[nop].constraint;
   bool change_p;
-  enum rtx_code code;
 
   if (constraint[0] == 'p'
       || EXTRA_ADDRESS_CONSTRAINT (constraint[0], constraint))
-    {
-      mode = VOIDmode;
-      addr_loc = curr_id->operand_loc[nop];
-      as = ADDR_SPACE_GENERIC;
-      code = ADDRESS;
-    }
+    decompose_lea_address (&ad, curr_id->operand_loc[nop]);
   else if (MEM_P (op))
-    {
-      mode = GET_MODE (op);
-      addr_loc = &XEXP (op, 0);
-      as = MEM_ADDR_SPACE (op);
-      code = MEM;
-    }
+    decompose_mem_address (&ad, op);
   else if (GET_CODE (op) == SUBREG
 	   && MEM_P (SUBREG_REG (op)))
-    {
-      mode = GET_MODE (SUBREG_REG (op));
-      addr_loc = &XEXP (SUBREG_REG (op), 0);
-      as = MEM_ADDR_SPACE (SUBREG_REG (op));
-      code = MEM;
-    }
+    decompose_mem_address (&ad, SUBREG_REG (op));
   else
     return false;
-  if (GET_CODE (*addr_loc) == AND)
-    addr_loc = &XEXP (*addr_loc, 0);
-  extract_address_regs (mode, as, addr_loc, code, &ad);
-  change_p = equiv_address_substitution (&ad, addr_loc, mode, as, code);
-  if (ad.base_reg_loc != NULL
+  change_p = equiv_address_substitution (&ad);
+  if (ad.base_term != NULL
       && (process_addr_reg
-	  (ad.base_reg_loc, before,
-	   (ad.base_modify_p && REG_P (*ad.base_reg_loc)
-	    && find_regno_note (curr_insn, REG_DEAD,
-				REGNO (*ad.base_reg_loc)) == NULL_RTX
+	  (ad.base_term, before,
+	   (ad.autoinc_p
+	    && !(REG_P (*ad.base_term)
+		 && find_regno_note (curr_insn, REG_DEAD,
+				     REGNO (*ad.base_term)) != NULL_RTX)
 	    ? after : NULL),
-	   base_reg_class (mode, as, ad.base_outer_code, ad.index_code))))
+	   base_reg_class (ad.mode, ad.as, ad.base_outer_code,
+			   get_index_code (&ad)))))
     {
       change_p = true;
-      if (ad.base_reg_loc2 != NULL)
-	*ad.base_reg_loc2 = *ad.base_reg_loc;
+      if (ad.base_term2 != NULL)
+	*ad.base_term2 = *ad.base_term;
     }
-  if (ad.index_reg_loc != NULL
-      && process_addr_reg (ad.index_reg_loc, before, NULL, INDEX_REG_CLASS))
+  if (ad.index_term != NULL
+      && process_addr_reg (ad.index_term, before, NULL, INDEX_REG_CLASS))
     change_p = true;
 
-  /* The address was valid before LRA.  We only change its form if the
-     address has a displacement, so if it has no displacement it must
-     still be valid.  */
-  if (ad.disp_loc == NULL)
-    return change_p;
+  /* There are three cases where the shape of *AD.INNER may now be invalid:
 
-  /* See whether the address is still valid.  Some ports do not check
-     displacements for eliminable registers, so we replace them
-     temporarily with the elimination target.  */
-  saved_base_reg = saved_index_reg = NULL_RTX;
-  if (ad.base_reg_loc != NULL)
-    {
-      saved_base_reg = *ad.base_reg_loc;
-      lra_eliminate_reg_if_possible (ad.base_reg_loc);
-      if (ad.base_reg_loc2 != NULL)
-	*ad.base_reg_loc2 = *ad.base_reg_loc;
-    }
-  if (ad.index_reg_loc != NULL)
-    {
-      saved_index_reg = *ad.index_reg_loc;
-      lra_eliminate_reg_if_possible (ad.index_reg_loc);
-    }
-  /* Some ports do not check displacements for virtual registers -- so
-     we substitute them temporarily by real registers.	*/
-  ok_p = valid_address_p (mode, *addr_loc, as);
-  if (saved_base_reg != NULL_RTX)
-    {
-      *ad.base_reg_loc = saved_base_reg;
-      if (ad.base_reg_loc2 != NULL)
-	*ad.base_reg_loc2 = saved_base_reg;
-    }
-  if (saved_index_reg != NULL_RTX)
-    *ad.index_reg_loc = saved_index_reg;
+     1) the original address was valid, but either elimination or
+	equiv_address_substitution applied a displacement that made
+	it invalid.
 
-  if (ok_p)
+     2) the address is an invalid symbolic address created by
+	force_const_to_mem.
+
+     3) the address is a frame address with an invalid offset.
+
+     All these cases involve a displacement and a non-autoinc address,
+     so there is no point revalidating other types.  */
+  if (ad.disp == NULL || ad.autoinc_p || valid_address_p (&ad))
     return change_p;
 
-  /* Addresses were legitimate before LRA.  So if the address has
-     two registers than it can have two of them.  We should also
-     not worry about scale for the same reason.	 */
+  /* Any index existed before LRA started, so we can assume that the
+     presence and shape of the index is valid.  */
   push_to_sequence (*before);
-  if (ad.base_reg_loc == NULL)
+  gcc_assert (ad.segment == NULL);
+  gcc_assert (ad.disp == ad.disp_term);
+  if (ad.base == NULL)
     {
-      if (ad.index_reg_loc == NULL)
+      if (ad.index == NULL)
 	{
 	  int code = -1;
-	  enum reg_class cl = base_reg_class (mode, as, SCRATCH, SCRATCH);
-	  
+	  enum reg_class cl = base_reg_class (ad.mode, ad.as,
+					      SCRATCH, SCRATCH);
+	  rtx disp = *ad.disp;
+
 	  new_reg = lra_create_new_reg (Pmode, NULL_RTX, cl, "disp");
 #ifdef HAVE_lo_sum
 	  {
 	    rtx insn;
 	    rtx last = get_last_insn ();
 
-	    /* disp => lo_sum (new_base, disp)	*/
+	    /* disp => lo_sum (new_base, disp), case (2) above.  */
 	    insn = emit_insn (gen_rtx_SET
 			      (VOIDmode, new_reg,
-			       gen_rtx_HIGH (Pmode, copy_rtx (*ad.disp_loc))));
+			       gen_rtx_HIGH (Pmode, copy_rtx (disp))));
 	    code = recog_memoized (insn);
 	    if (code >= 0)
 	      {
-		rtx save = *ad.disp_loc;
-
-		*ad.disp_loc = gen_rtx_LO_SUM (Pmode, new_reg, *ad.disp_loc);
-		if (! valid_address_p (mode, *ad.disp_loc, as))
+		*ad.disp = gen_rtx_LO_SUM (Pmode, new_reg, disp);
+		if (! valid_address_p (ad.mode, *ad.outer, ad.as))
 		  {
-		    *ad.disp_loc = save;
+		    *ad.disp = disp;
 		    code = -1;
 		  }
 	      }
@@ -2635,41 +2355,43 @@ 
 #endif
 	  if (code < 0)
 	    {
-	      /* disp => new_base  */
-	      lra_emit_move (new_reg, *ad.disp_loc);
-	      *ad.disp_loc = new_reg;
+	      /* disp => new_base, case (2) above.  */
+	      lra_emit_move (new_reg, disp);
+	      *ad.disp = new_reg;
 	    }
 	}
       else
 	{
-	  /* index * scale + disp => new base + index * scale  */
-	  enum reg_class cl = base_reg_class (mode, as, SCRATCH, SCRATCH);
+	  /* index * scale + disp => new base + index * scale,
+	     case (1) above.  */
+	  enum reg_class cl = base_reg_class (ad.mode, ad.as, PLUS,
+					      GET_CODE (*ad.index));
 
 	  lra_assert (INDEX_REG_CLASS != NO_REGS);
 	  new_reg = lra_create_new_reg (Pmode, NULL_RTX, cl, "disp");
-	  lra_assert (GET_CODE (*addr_loc) == PLUS);
-	  lra_emit_move (new_reg, *ad.disp_loc);
-	  if (CONSTANT_P (XEXP (*addr_loc, 1)))
-	    XEXP (*addr_loc, 1) = XEXP (*addr_loc, 0);
-	  XEXP (*addr_loc, 0) = new_reg;
+	  lra_emit_move (new_reg, *ad.disp);
+	  *ad.inner = simplify_gen_binary (PLUS, GET_MODE (new_reg),
+					   new_reg, *ad.index);
 	}
     }
-  else if (ad.index_reg_loc == NULL)
+  else if (ad.index == NULL)
     {
-      /* base + disp => new base  */
+      /* base + disp => new base, cases (1) and (3) above.  */
       /* Another option would be to reload the displacement into an
 	 index register.  However, postreload has code to optimize
 	 address reloads that have the same base and different
 	 displacements, so reloading into an index register would
 	 not necessarily be a win.  */
-      new_reg = base_plus_disp_to_reg (mode, as, &ad);
-      *addr_loc = new_reg;
+      new_reg = base_plus_disp_to_reg (&ad);
+      *ad.inner = new_reg;
     }
   else
     {
-      /* base + scale * index + disp => new base + scale * index  */
-      new_reg = base_plus_disp_to_reg (mode, as, &ad);
-      *addr_loc = gen_rtx_PLUS (Pmode, new_reg, *ad.index_loc);
+      /* base + scale * index + disp => new base + scale * index,
+	 case (1) above.  */
+      new_reg = base_plus_disp_to_reg (&ad);
+      *ad.inner = simplify_gen_binary (PLUS, GET_MODE (new_reg),
+				       new_reg, *ad.index);
     }
   *before = get_insns ();
   end_sequence ();
Index: gcc/rtl.h
===================================================================
--- gcc/rtl.h	(revision 192797)
+++ gcc/rtl.h	(working copy)
@@ -1237,6 +1237,77 @@ 
   c->size += COSTS_N_INSNS (n);
 }
 
+/* Information about an address.  This structure is supposed to be able
+   to represent all supported target addresses.  Please extend it if it
+   is not yet general enough.  */
+struct address_info {
+  /* The mode of the value being addressed, or VOIDmode if this is
+     a load-address operation with no known address mode.  */
+  enum machine_mode mode;
+
+  /* The address space.  */
+  addr_space_t as;
+
+  /* A pointer to the top-level address.  */
+  rtx *outer;
+
+  /* A pointer to the inner address, after all address mutations
+     have been stripped from the top-level address.  It can be one
+     of the following:
+
+     - A {PRE,POST}_{INC,DEC} of *BASE.  SEGMENT, INDEX and DISP are null.
+
+     - A {PRE,POST}_MODIFY of *BASE.  In this case either INDEX or DISP
+       points to the step value, depending on whether the step is variable
+       or constant respectively.  SEGMENT is null.
+
+     - A plain sum of the form SEGMENT + BASE + INDEX + DISP,
+       with null fields evaluating to 0.  */
+  rtx *inner;
+
+  /* Components that make up *INNER.  Each one may be null or nonnull.
+     When nonnull, their meanings are as follows:
+
+     - *SEGMENT is the "segment" of memory to which the address refers.
+       This value is entirely target-specific and is only called a "segment"
+       because that's its most typical use.  It contains exactly one UNSPEC,
+       pointed to by SEGMENT_TERM.  The contents of *SEGMENT do not need
+       reloading.
+
+     - *BASE is a variable expression representing a base address.
+       It contains exactly one REG, SUBREG or MEM, pointed to by BASE_TERM.
+
+     - *INDEX is a variable expression representing an index value.
+       It may be a scaled expression, such as a MULT.  It has exactly
+       one REG, SUBREG or MEM, pointed to by INDEX_TERM.
+
+     - *DISP is a constant, possibly mutated.  DISP_TERM points to the
+       unmutated RTX_CONST_OBJ.  */
+  rtx *segment;
+  rtx *base;
+  rtx *index;
+  rtx *disp;
+
+  rtx *segment_term;
+  rtx *base_term;
+  rtx *index_term;
+  rtx *disp_term;
+
+  /* In a {PRE,POST}_MODIFY address, this points to a second copy
+     of BASE_TERM, otherwise it is null.  */
+  rtx *base_term2;
+
+  /* ADDRESS if this describes an address operand, MEM if it describes
+     a MEM address.  */
+  enum rtx_code addr_outer_code;
+
+  /* If BASE is nonnull, this is the code of the rtx that contains it.  */
+  enum rtx_code base_outer_code;
+
+  /* True if this is an RTX_AUTOINC address.  */
+  bool autoinc_p;
+};
+
 extern void init_rtlanal (void);
 extern int rtx_cost (rtx, enum rtx_code, int, bool);
 extern int address_cost (rtx, enum machine_mode, addr_space_t, bool);
@@ -1260,6 +1331,14 @@ 
 extern bool truncated_to_mode (enum machine_mode, const_rtx);
 extern int low_bitmask_len (enum machine_mode, unsigned HOST_WIDE_INT);
 extern void split_double (rtx, rtx *, rtx *);
+extern rtx *strip_address_mutations (rtx *, enum rtx_code * = 0);
+extern void decompose_address (struct address_info *, rtx *,
+			       enum machine_mode, addr_space_t, enum rtx_code);
+extern void decompose_lea_address (struct address_info *, rtx *);
+extern void decompose_mem_address (struct address_info *, rtx);
+extern void update_address (struct address_info *);
+extern HOST_WIDE_INT get_index_scale (const struct address_info *);
+extern enum rtx_code get_index_code (const struct address_info *);
 
 #ifndef GENERATOR_FILE
 /* Return the cost of SET X.  SPEED_P is true if optimizing for speed
Index: gcc/Makefile.in
===================================================================
--- gcc/Makefile.in	(revision 192797)
+++ gcc/Makefile.in	(working copy)
@@ -2709,7 +2709,7 @@ 
 rtlanal.o : rtlanal.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(DIAGNOSTIC_CORE_H) \
    $(RTL_H) hard-reg-set.h $(TM_P_H) insn-config.h $(RECOG_H) \
    $(FLAGS_H) $(REGS_H) output.h $(TARGET_H) $(FUNCTION_H) $(TREE_H) \
-   $(DF_H) $(EMIT_RTL_H)
+   $(DF_H) $(EMIT_RTL_H) addresses.h
 
 varasm.o : varasm.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) \
    $(RTL_H) $(FLAGS_H) $(FUNCTION_H) $(EXPR_H) hard-reg-set.h $(REGS_H) \