summaryrefslogtreecommitdiff
path: root/gcc/rtlanal.c
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 /gcc/rtlanal.c
parentf9d4ecd445d717e0309cec1882ab57f92c2dad6d (diff)
downloadgcc-277f65de19c4bb93ee56a97d4a79b68d3d53dac5.tar.gz
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
Diffstat (limited to 'gcc/rtlanal.c')
-rw-r--r--gcc/rtlanal.c369
1 files changed, 369 insertions, 0 deletions
diff --git a/gcc/rtlanal.c b/gcc/rtlanal.c
index a101a292039..399886c1a94 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;
+}