Patchwork RFC/A: A generic decompose_address routine

login
register
mail settings
Submitter Richard Sandiford
Date Oct. 25, 2012, 7:57 p.m.
Message ID <87ip9yl3zi.fsf@talisman.home>
Download mbox | patch
Permalink /patch/194301/
State New
Headers show

Comments

Richard Sandiford - Oct. 25, 2012, 7:57 p.m.
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
compiling a set of non-x32 gcc .ii files with -mx32 -maddress-mode=long
and it fixed all but one ICE.  It should also fix the problem David found
with SPARC's double-addend LO_SUMs (although it sounds like David has
a workaround for that anyway).

The shortcut in process_address now needs to check explicitly for
autoinc addresses -- like Vlad's original code did -- since the new
structure can have nonnull disps for PRE_MODIFY and POST_MODIFY.
I also tightened the modes used in a couple of places.

It would probably make sense for process_addr_reg to reload SUBREGs
rather than their SUBREG_REGs, since we're unlikely to have paradoxical
subregs (with their undefined bits) in an address, but that's a separate
change.

Tested on x86_64-linux-gnu, no regressions, and no asm changes for a set
of gcc .ii files.

Richard


gcc/
	* Makefile.in (rtlanal.o): Add dependency on addresses.h.
	* rtl.h (address_info): New structure.
	(strip_address_mutations, decompose_address, decompose_lea_address)
	(decompose_mem_address, update_address, get_index_scale)
	(get_index_code): Declare.
	* rtlanal.c: Include addresses.h.
	(strip_address_mutations, must_be_base_p, must_be_index_p)
	(set_address_segment, set_address_base, set_address_index)
	(set_address_disp, decompose_incdec_address, decompose_automod_address)
	(extract_plus_operands, baseness, decompose_normal_address)
	(decompose_address, decompose_lea_address, decompose_mem_address)
	(update_address, get_index_scale, get_index_code): New functions.
	* lra-constraints.c (strip_subreg): New function.
	(address, extract_loc_address_regs, extract_address_regs)
	(get_index_scale): Delete.
	(process_addr_reg): Apply strip_subreg to the location.
	(uses_hard_regs_p): Use decompose_mem_address.
	(valid_address_p, base_plus_disp_to_reg, can_add_disp_p)
	(equiv_address_substitution): Take an address_info rather
	than an address.  Remove other arguments.  Avoid using Pmode.
	(process_address): Use decompose_mem_address and decompose_lea_address.
	Update calls to above functions.
David Miller - Oct. 25, 2012, 8:15 p.m.
From: Richard Sandiford <rdsandiford@googlemail.com>
Date: Thu, 25 Oct 2012 20:57:05 +0100

> 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
> compiling a set of non-x32 gcc .ii files with -mx32 -maddress-mode=long
> and it fixed all but one ICE.  It should also fix the problem David found
> with SPARC's double-addend LO_SUMs (although it sounds like David has
> a workaround for that anyway).

This looks great, I'll play around with it on sparc later today!
H.J. Lu - Oct. 25, 2012, 10:07 p.m.
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

Here are instructions how to build x32 glibc:

http://sourceware.org/glibc/wiki/x32

You only need x32 glibc to enable GCC x32 multilib on
Linux/x86-64.

Thanks.
Vladimir Makarov - Oct. 26, 2012, 3:04 a.m.
On 12-10-25 3:57 PM, Richard Sandiford 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
> compiling a set of non-x32 gcc .ii files with -mx32 -maddress-mode=long
> and it fixed all but one ICE.  It should also fix the problem David found
> with SPARC's double-addend LO_SUMs (although it sounds like David has
> a workaround for that anyway).
>
> The shortcut in process_address now needs to check explicitly for
> autoinc addresses -- like Vlad's original code did -- since the new
> structure can have nonnull disps for PRE_MODIFY and POST_MODIFY.
> I also tightened the modes used in a couple of places.
>
> It would probably make sense for process_addr_reg to reload SUBREGs
> rather than their SUBREG_REGs, since we're unlikely to have paradoxical
> subregs (with their undefined bits) in an address, but that's a separate
> change.
>
> Tested on x86_64-linux-gnu, no regressions, and no asm changes for a set
> of gcc .ii files.
>
>
This is really good, Richard.  I should have done this from the start (I 
tried but failed) and communicated with target maintainers to figure out 
the addresses forms (when I failed).  I very like mutation notion and 
segment.  The code has a good educational value too.

I am thinking that pseudo assignment (reg_renumber) could be used to 
differ base and index registers too.  Although I am not sure it is worth 
to do as it creates strange dependencies and that it can help probably 
in very rare cases or only for some weird targets.  But it is not a 
final code probably we will need some changes for porting other targets 
to LRA and if we really need this we could return to this.

It is ok for me to commit the patch.  I have no power to approve changes 
for files outside LRA although I think the place is not important for 
approval as LRA now only use it and with the point of LRA the code is 
good.  But I guess you have the power for changes on RTL side of the 
compiler.

Thanks very much, Richard.  I thought it will take some time to 
implement this but you were very quick.
Vladimir Makarov - Oct. 26, 2012, 3:22 a.m.
On 12-10-25 11:04 PM, Vladimir Makarov wrote:
> On 12-10-25 3:57 PM, Richard Sandiford wrote:
>
> It is ok for me to commit the patch.  I have no power to approve 
> changes for files outside LRA although I think the place is not 
> important for approval as LRA now only use it and with the point of 
> LRA the code is good.  But I guess you have the power for changes on 
> RTL side of the compiler.
>
I forgot to say there is no need to submit your patches to the branch.  
When they are in the trunk, I'll do the merge of trunk into LRA.
Richard Sandiford - Oct. 26, 2012, 6:45 a.m.
Vladimir Makarov <vmakarov@redhat.com> writes:
> I am thinking that pseudo assignment (reg_renumber) could be used to 
> differ base and index registers too.  Although I am not sure it is worth 
> to do as it creates strange dependencies and that it can help probably 
> in very rare cases or only for some weird targets.

Sounds like a good idea.

> But it is not a final code probably we will need some changes for
> porting other targets to LRA and if we really need this we could
> return to this.

Definitely. :-)  I'd be very surprised if this initial version
is general enough.

> It is ok for me to commit the patch.  I have no power to approve changes 
> for files outside LRA although I think the place is not important for 
> approval as LRA now only use it and with the point of LRA the code is 
> good.  But I guess you have the power for changes on RTL side of the 
> compiler.

Thanks, applied on that basis.

Richard

Patch

Index: gcc/Makefile.in
===================================================================
--- gcc/Makefile.in	2012-10-23 19:37:49.000000000 +0100
+++ gcc/Makefile.in	2012-10-25 16:44:11.913358869 +0100
@@ -2709,7 +2709,7 @@  print-rtl.o : print-rtl.c $(CONFIG_H) $(
 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) \
Index: gcc/rtl.h
===================================================================
--- gcc/rtl.h	2012-10-25 13:47:00.397249906 +0100
+++ gcc/rtl.h	2012-10-25 13:47:29.482249833 +0100
@@ -1237,6 +1237,77 @@  costs_add_n_insns (struct full_rtx_costs
   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 constant_pool_constant_p (rt
 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/rtlanal.c
===================================================================
--- gcc/rtlanal.c	2012-10-25 13:47:00.396249906 +0100
+++ gcc/rtlanal.c	2012-10-25 14:27:20.161243984 +0100
@@ -38,6 +38,7 @@  Software Foundation; either version 3, o
 #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 @@  split_double (rtx value, rtx *first, rtx
     }
 }
 
+/* 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	2012-10-25 13:47:00.396249906 +0100
+++ gcc/lra-constraints.c	2012-10-25 19:35:29.520333722 +0100
@@ -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 @@  get_reload_reg (enum op_type type, enum
 
 /* 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,305 +464,6 @@  ok_for_base_p_nonstrict (rtx reg, enum m
   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;
-}
-
-/* Return the scale applied to *AD->INDEX_REG_LOC, or 0 if the index is
-   more complicated than that.  */
-static HOST_WIDE_INT
-get_index_scale (struct address *ad)
-{
-  rtx index = *ad->index_loc;
-  if (GET_CODE (index) == MULT
-      && CONST_INT_P (XEXP (index, 1))
-      && ad->index_reg_loc == &XEXP (index, 0))
-    return INTVAL (XEXP (index, 1));
-
-  if (GET_CODE (index) == ASHIFT
-      && CONST_INT_P (XEXP (index, 1))
-      && ad->index_reg_loc == &XEXP (index, 0))
-    return (HOST_WIDE_INT) 1 << INTVAL (XEXP (index, 1));
-
-  if (ad->index_reg_loc == ad->index_loc)
-    return 1;
-
-  return 0;
-}
-
 
 
 /* The page contains major code to choose the current insn alternative
@@ -1356,11 +1042,13 @@  process_addr_reg (rtx *loc, rtx *before,
 {
   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))
     {
@@ -1540,21 +1228,13 @@  uses_hard_regs_p (rtx x, HARD_REG_SET se
     }
   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--)
@@ -2401,115 +2081,92 @@  valid_address_p (enum machine_mode mode
 #endif
 }
 
-/* Return whether address X, described by AD, is valid for mode MODE
-   and address space AS.  */
+/* Return whether address AD is valid.  */
 
 static bool
-valid_address_p (struct address *ad, enum machine_mode mode, rtx x,
-		 addr_space_t as)
+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;
-  if (ad->base_reg_loc != NULL)
+  rtx *base_term = strip_subreg (ad->base_term);
+  rtx *index_term = strip_subreg (ad->index_term);
+  if (base_term != 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;
+      saved_base_reg = *base_term;
+      lra_eliminate_reg_if_possible (base_term);
+      if (ad->base_term2 != NULL)
+	*ad->base_term2 = *ad->base_term;
     }
-  if (ad->index_reg_loc != NULL)
+  if (index_term != NULL)
     {
-      saved_index_reg = *ad->index_reg_loc;
-      lra_eliminate_reg_if_possible (ad->index_reg_loc);
+      saved_index_reg = *index_term;
+      lra_eliminate_reg_if_possible (index_term);
     }
-  bool ok_p = valid_address_p (mode, x, as);
+  bool ok_p = valid_address_p (ad->mode, *ad->outer, ad->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;
+      *base_term = saved_base_reg;
+      if (ad->base_term2 != NULL)
+	*ad->base_term2 = *ad->base_term;
     }
   if (saved_index_reg != NULL_RTX)
-    *ad->index_reg_loc = saved_index_reg;
+    *index_term = saved_index_reg;
   return ok_p;
 }
 
-/* Make reload base reg + disp from address AD in space AS of memory
-   with MODE into a new pseudo.	 Return the new pseudo.	 */
+/* 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;
 }
 
-/* Return true if we can add a displacement to address ADDR_LOC,
-   which is described by AD, even if that makes the address invalid.
-   The fix-up code requires any new address to be the sum of the base,
-   index and displacement fields of an AD-like structure.  */
+/* 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
-can_add_disp_p (struct address *ad, rtx *addr_loc)
+can_add_disp_p (struct address_info *ad)
 {
-  /* Automodified addresses have a fixed form.  */
-  if (ad->base_modify_p)
-    return false;
-
-  /* If the address already has a displacement, we can simply add the
-     new displacement to it.  */
-  if (ad->disp_loc)
-    return true;
-
-  /* If the address is entirely a base or index, we can try adding
-     a constant to it.  */
-  if (addr_loc == ad->base_reg_loc || addr_loc == ad->index_loc)
-    return true;
-
-  /* Likewise if the address is entirely a sum of the base and index.  */
-  if (GET_CODE (*addr_loc) == PLUS)
-    {
-      rtx *op0 = &XEXP (*addr_loc, 0);
-      rtx *op1 = &XEXP (*addr_loc, 1);
-      if (op0 == ad->base_reg_loc && op1 == ad->index_loc)
-	return true;
-      if (op1 == ad->base_reg_loc && op0 == ad->index_loc)
-	return true;
-    }
-  return false;
+  return (!ad->autoinc_p
+	  && ad->segment == NULL
+	  && ad->base == ad->base_term
+	  && ad->disp == ad->disp_term);
 }
 
-/* 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.  */
+/* Make equiv substitution in address AD.  Return true if a substitution
+   was made.  */
 static bool
-equiv_address_substitution (struct address *ad, rtx *addr_loc,
-			    enum machine_mode mode, addr_space_t as,
-			    enum rtx_code code)
+equiv_address_substitution (struct address_info *ad)
 {
-  rtx base_reg, new_base_reg, index_reg, new_index_reg;
+  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)
@@ -2520,53 +2177,53 @@  equiv_address_substitution (struct addre
     {
       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))
-	       && can_add_disp_p (ad, addr_loc))
+	       && 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;
     }
   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))
-	       && can_add_disp_p (ad, addr_loc)
+	       && 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;
     }
@@ -2577,7 +2234,7 @@  equiv_address_substitution (struct addre
       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");
 	}
     }
@@ -2604,62 +2261,43 @@  equiv_address_substitution (struct addre
 static bool
 process_address (int nop, rtx *before, rtx *after)
 {
-  struct address ad;
-  enum machine_mode mode;
-  rtx new_reg, *addr_loc;
-  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;
 
-  /* There are three cases where the shape of *ADDR_LOC may now be invalid:
+  /* There are three cases where the shape of *AD.INNER may now be invalid:
 
      1) the original address was valid, but equiv_address_substitution
 	applied a displacement that made it invalid.
@@ -2669,21 +2307,25 @@  process_address (int nop, rtx *before, r
 
      3) the address is a frame address with an invalid offset.
 
-     All these cases involve a displacement, so there is no point
-     revalidating when there is no displacement.  */
-  if (ad.disp_loc == NULL || valid_address_p (&ad, mode, *addr_loc, as))
+     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;
 
   /* 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
 	  {
@@ -2693,16 +2335,14 @@  process_address (int nop, rtx *before, r
 	    /* 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;
 		  }
 	      }
@@ -2713,25 +2353,25 @@  process_address (int nop, rtx *before, r
 	  if (code < 0)
 	    {
 	      /* disp => new_base, case (2) above.  */
-	      lra_emit_move (new_reg, *ad.disp_loc);
-	      *ad.disp_loc = new_reg;
+	      lra_emit_move (new_reg, disp);
+	      *ad.disp = new_reg;
 	    }
 	}
       else
 	{
 	  /* index * scale + disp => new base + index * scale,
 	     case (1) above.  */
-	  enum reg_class cl = base_reg_class (mode, as, PLUS,
-					      GET_CODE (*ad.index_loc));
+	  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_emit_move (new_reg, *ad.disp_loc);
-	  *addr_loc = simplify_gen_binary (PLUS, GET_MODE (new_reg),
-					   new_reg, *ad.index_loc);
+	  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, cases (1) and (3) above.  */
       /* Another option would be to reload the displacement into an
@@ -2739,16 +2379,16 @@  process_address (int nop, rtx *before, r
 	 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,
 	 case (1) above.  */
-      new_reg = base_plus_disp_to_reg (mode, as, &ad);
-      *addr_loc = simplify_gen_binary (PLUS, GET_MODE (new_reg),
-				       new_reg, *ad.index_loc);
+      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 ();