aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Sandiford <rdsandiford@googlemail.com>2012-10-26 06:41:53 +0000
committerRichard Sandiford <rsandifo@gcc.gnu.org>2012-10-26 06:41:53 +0000
commit277f65de19c4bb93ee56a97d4a79b68d3d53dac5 (patch)
tree14915af2d40b8cb97045fa32be1ed0f4df090f74
parentf9d4ecd445d717e0309cec1882ab57f92c2dad6d (diff)
downloadgcc-277f65de19c4bb93ee56a97d4a79b68d3d53dac5.zip
gcc-277f65de19c4bb93ee56a97d4a79b68d3d53dac5.tar.gz
gcc-277f65de19c4bb93ee56a97d4a79b68d3d53dac5.tar.bz2
re PR bootstrap/55049 (bootstrap failed with --with-multilib-list=m32,m64,mx32)
gcc/ PR bootstrap/55049 * 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. From-SVN: r192837
-rw-r--r--gcc/ChangeLog26
-rw-r--r--gcc/Makefile.in2
-rw-r--r--gcc/lra-constraints.c600
-rw-r--r--gcc/rtl.h79
-rw-r--r--gcc/rtlanal.c369
5 files changed, 595 insertions, 481 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index be75b81..9d1dd50 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,5 +1,31 @@
2012-10-26 Richard Sandiford <rdsandiford@googlemail.com>
+ PR bootstrap/55049
+ * 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.
+
+2012-10-26 Richard Sandiford <rdsandiford@googlemail.com>
+
* lra-constraints.c (process_address): Tighten arguments to
base_reg_class. Use simplify_gen_binary to generate PLUS rtxes.
diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index c729ee6..96765fe 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -2709,7 +2709,7 @@ print-rtl.o : print-rtl.c $(CONFIG_H) $(SYSTEM_H) coretypes.h dumpfile.h $(TM_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) \
diff --git a/gcc/lra-constraints.c b/gcc/lra-constraints.c
index ffc067b..9e4d3b1 100644
--- a/gcc/lra-constraints.c
+++ b/gcc/lra-constraints.c
@@ -152,6 +152,13 @@ static enum machine_mode curr_operand_mode[MAX_RECOG_OPERANDS];
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 machine_mode mode, rtx original,
/* 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 machine_mode mode, addr_space_t as,
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
@@ -1354,11 +1040,13 @@ process_addr_reg (rtx *loc, rtx *before, rtx *after, enum reg_class cl)
{
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))
{
@@ -1538,21 +1226,13 @@ uses_hard_regs_p (rtx x, HARD_REG_SET set)
}
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--)
@@ -2399,115 +2079,92 @@ valid_address_p (enum machine_mode mode ATTRIBUTE_UNUSED,
#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, and is not an UNSPEC,
- we can simply add the new displacement to it. */
- if (ad->disp_loc && GET_CODE (*ad->disp_loc) == UNSPEC)
- 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)
@@ -2518,53 +2175,53 @@ equiv_address_substitution (struct address *ad, rtx *addr_loc,
{
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;
}
@@ -2575,7 +2232,7 @@ equiv_address_substitution (struct address *ad, rtx *addr_loc,
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 address *ad, rtx *addr_loc,
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 either elimination or
equiv_address_substitution applied a displacement that made
@@ -2670,21 +2308,25 @@ process_address (int nop, rtx *before, rtx *after)
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
{
@@ -2694,16 +2336,14 @@ process_address (int nop, rtx *before, rtx *after)
/* 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;
}
}
@@ -2714,25 +2354,25 @@ process_address (int nop, rtx *before, rtx *after)
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
@@ -2740,16 +2380,16 @@ process_address (int nop, rtx *before, rtx *after)
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 ();
diff --git a/gcc/rtl.h b/gcc/rtl.h
index 361669a..43a49c4 100644
--- a/gcc/rtl.h
+++ b/gcc/rtl.h
@@ -1237,6 +1237,77 @@ costs_add_n_insns (struct full_rtx_costs *c, int n)
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 structure 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 (rtx);
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
diff --git a/gcc/rtlanal.c b/gcc/rtlanal.c
index a101a29..399886c 100644
--- a/gcc/rtlanal.c
+++ b/gcc/rtlanal.c
@@ -38,6 +38,7 @@ along with GCC; see the file COPYING3. If not see
#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 *second)
}
}
+/* 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;
+}