mirror of
git://gcc.gnu.org/git/gcc.git
synced 2025-03-26 19:51:12 +08:00
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
This commit is contained in:
parent
f9d4ecd445
commit
277f65de19
@ -1,3 +1,29 @@
|
||||
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
|
||||
|
@ -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) \
|
||||
|
@ -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 ();
|
||||
|
79
gcc/rtl.h
79
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
|
||||
|
369
gcc/rtlanal.c
369
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;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user