/* Code for RTL register eliminations.
- Copyright (C) 2010-2015 Free Software Foundation, Inc.
+ Copyright (C) 2010-2019 Free Software Foundation, Inc.
Contributed by Vladimir Makarov <vmakarov@redhat.com>.
This file is part of GCC.
#include "system.h"
#include "coretypes.h"
#include "backend.h"
-#include "tree.h"
+#include "target.h"
#include "rtl.h"
+#include "tree.h"
#include "df.h"
+#include "memmodel.h"
#include "tm_p.h"
+#include "optabs.h"
#include "regs.h"
-#include "insn-config.h"
-#include "insn-codes.h"
+#include "ira.h"
#include "recog.h"
#include "output.h"
-#include "addresses.h"
-#include "target.h"
-#include "flags.h"
-#include "alias.h"
-#include "expmed.h"
-#include "dojump.h"
-#include "explow.h"
-#include "calls.h"
-#include "emit-rtl.h"
-#include "varasm.h"
-#include "stmt.h"
-#include "expr.h"
-#include "except.h"
-#include "optabs.h"
-#include "ira.h"
#include "rtl-error.h"
-#include "lra.h"
-#include "insn-attr.h"
#include "lra-int.h"
/* This structure is used to record information about hard register
int to;
/* Difference between values of the two hard registers above on
previous iteration. */
- HOST_WIDE_INT previous_offset;
+ poly_int64 previous_offset;
/* Difference between the values on the current iteration. */
- HOST_WIDE_INT offset;
+ poly_int64 offset;
/* Nonzero if this elimination can be done. */
bool can_eliminate;
/* CAN_ELIMINATE since the last check. */
const int to;
} reg_eliminate_1[] =
-/* If a set of eliminable hard registers was specified, define the
- table from it. Otherwise, default to the normal case of the frame
- pointer being replaced by the stack pointer. */
-
-#ifdef ELIMINABLE_REGS
ELIMINABLE_REGS;
-#else
- {{ FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM}};
-#endif
#define NUM_ELIMINABLE_REGS ARRAY_SIZE (reg_eliminate_1)
struct lra_elim_table *ep;
for (ep = reg_eliminate; ep < ®_eliminate[NUM_ELIMINABLE_REGS]; ep++)
- fprintf (f, "%s eliminate %d to %d (offset=" HOST_WIDE_INT_PRINT_DEC
- ", prev_offset=" HOST_WIDE_INT_PRINT_DEC ")\n",
- ep->can_eliminate ? "Can" : "Can't",
- ep->from, ep->to, ep->offset, ep->previous_offset);
+ {
+ fprintf (f, "%s eliminate %d to %d (offset=",
+ ep->can_eliminate ? "Can" : "Can't", ep->from, ep->to);
+ print_dec (ep->offset, f);
+ fprintf (f, ", prev_offset=");
+ print_dec (ep->previous_offset, f);
+ fprintf (f, ")\n");
+ }
}
/* Print info about elimination table to stderr. */
/* Offsets should be used to restore original offsets for eliminable
hard register which just became not eliminable. Zero,
otherwise. */
-static HOST_WIDE_INT self_elim_offsets[FIRST_PSEUDO_REGISTER];
+static poly_int64_pod self_elim_offsets[FIRST_PSEUDO_REGISTER];
/* Map: hard regno -> RTL presentation. RTL presentations of all
potentially eliminable hard registers are stored in the map. */
static rtx
form_sum (rtx x, rtx y)
{
- rtx tem;
machine_mode mode = GET_MODE (x);
+ poly_int64 offset;
if (mode == VOIDmode)
mode = GET_MODE (y);
if (mode == VOIDmode)
mode = Pmode;
- if (CONST_INT_P (x))
- return plus_constant (mode, y, INTVAL (x));
- else if (CONST_INT_P (y))
- return plus_constant (mode, x, INTVAL (y));
+ if (poly_int_rtx_p (x, &offset))
+ return plus_constant (mode, y, offset);
+ else if (poly_int_rtx_p (y, &offset))
+ return plus_constant (mode, x, offset);
else if (CONSTANT_P (x))
- tem = x, x = y, y = tem;
+ std::swap (x, y);
if (GET_CODE (x) == PLUS && CONSTANT_P (XEXP (x, 1)))
return form_sum (XEXP (x, 0), form_sum (XEXP (x, 1), y));
{
int hard_regno;
struct lra_elim_table *ep;
- HOST_WIDE_INT offset;
lra_assert (REG_P (reg));
if ((hard_regno = REGNO (reg)) < 0 || hard_regno >= FIRST_PSEUDO_REGISTER)
return NULL;
if ((ep = elimination_map[hard_regno]) != NULL)
return ep->from_rtx != reg ? NULL : ep;
- if ((offset = self_elim_offsets[hard_regno]) == 0)
+ poly_int64 offset = self_elim_offsets[hard_regno];
+ if (known_eq (offset, 0))
return NULL;
/* This is an iteration to restore offsets just after HARD_REGNO
stopped to be eliminable. */
return &self_elim_table;
}
+/* Transform (subreg (plus reg const)) to (plus (subreg reg) const)
+ when it is possible. Return X or the transformation result if the
+ transformation is done. */
+static rtx
+move_plus_up (rtx x)
+{
+ rtx subreg_reg;
+ machine_mode x_mode, subreg_reg_mode;
+
+ if (GET_CODE (x) != SUBREG || !subreg_lowpart_p (x))
+ return x;
+ subreg_reg = SUBREG_REG (x);
+ x_mode = GET_MODE (x);
+ subreg_reg_mode = GET_MODE (subreg_reg);
+ if (!paradoxical_subreg_p (x)
+ && GET_CODE (subreg_reg) == PLUS
+ && CONSTANT_P (XEXP (subreg_reg, 1))
+ && GET_MODE_CLASS (x_mode) == MODE_INT
+ && GET_MODE_CLASS (subreg_reg_mode) == MODE_INT)
+ {
+ rtx cst = simplify_subreg (x_mode, XEXP (subreg_reg, 1), subreg_reg_mode,
+ subreg_lowpart_offset (x_mode,
+ subreg_reg_mode));
+ if (cst && CONSTANT_P (cst))
+ return gen_rtx_PLUS (x_mode, lowpart_subreg (x_mode,
+ XEXP (subreg_reg, 0),
+ subreg_reg_mode), cst);
+ }
+ return x;
+}
+
/* Scan X and replace any eliminable registers (such as fp) with a
replacement (such as sp) if SUBST_P, plus an offset. The offset is
a change in the offset between the eliminable register and its
rtx
lra_eliminate_regs_1 (rtx_insn *insn, rtx x, machine_mode mem_mode,
bool subst_p, bool update_p,
- HOST_WIDE_INT update_sp_offset, bool full_p)
+ poly_int64 update_sp_offset, bool full_p)
{
enum rtx_code code = GET_CODE (x);
struct lra_elim_table *ep;
int copied = 0;
lra_assert (!update_p || !full_p);
- lra_assert (update_sp_offset == 0 || (!subst_p && update_p && !full_p));
+ lra_assert (known_eq (update_sp_offset, 0)
+ || (!subst_p && update_p && !full_p));
if (! current_function_decl)
return x;
{
rtx to = subst_p ? ep->to_rtx : ep->from_rtx;
- if (update_sp_offset != 0)
+ if (maybe_ne (update_sp_offset, 0))
{
if (ep->to_rtx == stack_pointer_rtx)
return plus_constant (Pmode, to, update_sp_offset);
{
if ((ep = get_elimination (XEXP (x, 0))) != NULL)
{
- HOST_WIDE_INT offset;
+ poly_int64 offset, curr_offset;
rtx to = subst_p ? ep->to_rtx : ep->from_rtx;
if (! update_p && ! full_p)
return gen_rtx_PLUS (Pmode, to, XEXP (x, 1));
- if (update_sp_offset != 0)
+ if (maybe_ne (update_sp_offset, 0))
offset = ep->to_rtx == stack_pointer_rtx ? update_sp_offset : 0;
else
offset = (update_p
? ep->offset - ep->previous_offset : ep->offset);
if (full_p && insn != NULL_RTX && ep->to_rtx == stack_pointer_rtx)
offset -= lra_get_insn_recog_data (insn)->sp_offset;
- if (CONST_INT_P (XEXP (x, 1)) && INTVAL (XEXP (x, 1)) == -offset)
+ if (poly_int_rtx_p (XEXP (x, 1), &curr_offset)
+ && known_eq (curr_offset, -offset))
return to;
else
return gen_rtx_PLUS (Pmode, to,
subst_p, update_p,
update_sp_offset, full_p);
+ new0 = move_plus_up (new0);
+ new1 = move_plus_up (new1);
if (new0 != XEXP (x, 0) || new1 != XEXP (x, 1))
return form_sum (new0, new1);
}
{
rtx to = subst_p ? ep->to_rtx : ep->from_rtx;
- if (update_sp_offset != 0)
+ if (maybe_ne (update_sp_offset, 0))
{
if (ep->to_rtx == stack_pointer_rtx)
return plus_constant (Pmode,
* INTVAL (XEXP (x, 1)));
else if (full_p)
{
- HOST_WIDE_INT offset = ep->offset;
+ poly_int64 offset = ep->offset;
if (insn != NULL_RTX && ep->to_rtx == stack_pointer_rtx)
offset -= lra_get_insn_recog_data (insn)->sp_offset;
return gen_rtx_MULT (Pmode, to, XEXP (x, 1));
}
- /* ... fall through ... */
+ /* fall through */
case CALL:
case COMPARE:
}
}
- /* ... fall through ... */
+ /* fall through */
case INSN_LIST:
case INT_LIST:
if (new_rtx != SUBREG_REG (x))
{
- int x_size = GET_MODE_SIZE (GET_MODE (x));
- int new_size = GET_MODE_SIZE (GET_MODE (new_rtx));
-
- if (MEM_P (new_rtx) && x_size <= new_size)
+ if (MEM_P (new_rtx) && !paradoxical_subreg_p (x))
{
SUBREG_REG (x) = new_rtx;
alter_subreg (&x, false);
return x;
case CLOBBER:
+ case CLOBBER_HIGH:
case SET:
gcc_unreachable ();
/* Stack pointer offset before the current insn relative to one at the
func start. RTL insns can change SP explicitly. We keep the
changes from one insn to another through this variable. */
-static HOST_WIDE_INT curr_sp_change;
+static poly_int64 curr_sp_change;
/* Scan rtx X for references to elimination source or target registers
in contexts that would prevent the elimination from happening.
struct lra_elim_table *ep;
int i, j;
const char *fmt;
+ poly_int64 offset = 0;
switch (code)
{
&& ((code != PRE_MODIFY && code != POST_MODIFY)
|| (GET_CODE (XEXP (x, 1)) == PLUS
&& XEXP (x, 0) == XEXP (XEXP (x, 1), 0)
- && CONST_INT_P (XEXP (XEXP (x, 1), 1)))))
+ && poly_int_rtx_p (XEXP (XEXP (x, 1), 1), &offset))))
{
- int size = GET_MODE_SIZE (mem_mode);
+ poly_int64 size = GET_MODE_SIZE (mem_mode);
#ifdef PUSH_ROUNDING
/* If more bytes than MEM_MODE are pushed, account for
else if (code == PRE_INC || code == POST_INC)
curr_sp_change += size;
else if (code == PRE_MODIFY || code == POST_MODIFY)
- curr_sp_change += INTVAL (XEXP (XEXP (x, 1), 1));
+ curr_sp_change += offset;
}
else if (REG_P (XEXP (x, 0))
&& REGNO (XEXP (x, 0)) >= FIRST_PSEUDO_REGISTER)
setup_can_eliminate (ep, false);
return;
+ case CLOBBER_HIGH:
+ gcc_assert (REG_P (XEXP (x, 0)));
+ gcc_assert (REGNO (XEXP (x, 0)) < FIRST_PSEUDO_REGISTER);
+ for (ep = reg_eliminate;
+ ep < ®_eliminate[NUM_ELIMINABLE_REGS];
+ ep++)
+ if (reg_is_clobbered_by_clobber_high (ep->to_rtx, XEXP (x, 0)))
+ setup_can_eliminate (ep, false);
+ return;
+
case SET:
if (SET_DEST (x) == stack_pointer_rtx
&& GET_CODE (SET_SRC (x)) == PLUS
&& XEXP (SET_SRC (x), 0) == SET_DEST (x)
- && CONST_INT_P (XEXP (SET_SRC (x), 1)))
+ && poly_int_rtx_p (XEXP (SET_SRC (x), 1), &offset))
{
- curr_sp_change += INTVAL (XEXP (SET_SRC (x), 1));
+ curr_sp_change += offset;
return;
}
if (! REG_P (SET_DEST (x))
\f
-#ifdef HARD_FRAME_POINTER_REGNUM
-
-/* Find offset equivalence note for reg WHAT in INSN and return the
- found elmination offset. If the note is not found, return NULL.
- Remove the found note. */
-static rtx
-remove_reg_equal_offset_note (rtx_insn *insn, rtx what)
-{
- rtx link, *link_loc;
-
- for (link_loc = ®_NOTES (insn);
- (link = *link_loc) != NULL_RTX;
- link_loc = &XEXP (link, 1))
- if (REG_NOTE_KIND (link) == REG_EQUAL
- && GET_CODE (XEXP (link, 0)) == PLUS
- && XEXP (XEXP (link, 0), 0) == what
- && CONST_INT_P (XEXP (XEXP (link, 0), 1)))
- {
- *link_loc = XEXP (link, 1);
- return XEXP (XEXP (link, 0), 1);
- }
- return NULL_RTX;
-}
-
-#endif
-
/* Scan INSN and eliminate all eliminable hard registers in it.
If REPLACE_P is true, do the replacement destructively. Also
void
eliminate_regs_in_insn (rtx_insn *insn, bool replace_p, bool first_p,
- HOST_WIDE_INT update_sp_offset)
+ poly_int64 update_sp_offset)
{
int icode = recog_memoized (insn);
rtx old_set = single_set (insn);
return;
}
- /* Check for setting an eliminable register. */
- if (old_set != 0 && REG_P (SET_DEST (old_set))
- && (ep = get_elimination (SET_DEST (old_set))) != NULL)
- {
- for (ep = reg_eliminate; ep < ®_eliminate[NUM_ELIMINABLE_REGS]; ep++)
- if (ep->from_rtx == SET_DEST (old_set) && ep->can_eliminate)
- {
- bool delete_p = replace_p;
-
-#ifdef HARD_FRAME_POINTER_REGNUM
- if (ep->from == FRAME_POINTER_REGNUM
- && ep->to == HARD_FRAME_POINTER_REGNUM)
- /* If this is setting the frame pointer register to the
- hardware frame pointer register and this is an
- elimination that will be done (tested above), this
- insn is really adjusting the frame pointer downward
- to compensate for the adjustment done before a
- nonlocal goto. */
- {
- rtx src = SET_SRC (old_set);
- rtx off = remove_reg_equal_offset_note (insn, ep->to_rtx);
-
- /* We should never process such insn with non-zero
- UPDATE_SP_OFFSET. */
- lra_assert (update_sp_offset == 0);
-
- if (off != NULL_RTX
- || src == ep->to_rtx
- || (GET_CODE (src) == PLUS
- && XEXP (src, 0) == ep->to_rtx
- && CONST_INT_P (XEXP (src, 1))))
- {
- HOST_WIDE_INT offset;
-
- if (replace_p)
- {
- SET_DEST (old_set) = ep->to_rtx;
- lra_update_insn_recog_data (insn);
- return;
- }
- offset = (off != NULL_RTX ? INTVAL (off)
- : src == ep->to_rtx ? 0 : INTVAL (XEXP (src, 1)));
- offset -= (ep->offset - ep->previous_offset);
- src = plus_constant (Pmode, ep->to_rtx, offset);
-
- /* First see if this insn remains valid when we
- make the change. If not, keep the INSN_CODE
- the same and let the constraint pass fit it
- up. */
- validate_change (insn, &SET_SRC (old_set), src, 1);
- validate_change (insn, &SET_DEST (old_set),
- ep->from_rtx, 1);
- if (! apply_change_group ())
- {
- SET_SRC (old_set) = src;
- SET_DEST (old_set) = ep->from_rtx;
- }
- lra_update_insn_recog_data (insn);
- /* Add offset note for future updates. */
- add_reg_note (insn, REG_EQUAL, src);
- return;
- }
- }
-#endif
-
- /* This insn isn't serving a useful purpose. We delete it
- when REPLACE is set. */
- if (delete_p)
- lra_delete_dead_insn (insn);
- return;
- }
- }
-
/* We allow one special case which happens to work on all machines we
currently support: a single set with the source or a REG_EQUAL
note being a PLUS of an eliminable register and a constant. */
plus_src = plus_cst_src = 0;
+ poly_int64 offset = 0;
if (old_set && REG_P (SET_DEST (old_set)))
{
if (GET_CODE (SET_SRC (old_set)) == PLUS)
plus_src = SET_SRC (old_set);
/* First see if the source is of the form (plus (...) CST). */
- if (plus_src
- && CONST_INT_P (XEXP (plus_src, 1)))
+ if (plus_src && poly_int_rtx_p (XEXP (plus_src, 1), &offset))
plus_cst_src = plus_src;
/* Check that the first operand of the PLUS is a hard reg or
the lowpart subreg of one. */
if (plus_cst_src)
{
rtx reg = XEXP (plus_cst_src, 0);
- HOST_WIDE_INT offset = INTVAL (XEXP (plus_cst_src, 1));
if (GET_CODE (reg) == SUBREG)
reg = SUBREG_REG (reg);
if (! replace_p)
{
- if (update_sp_offset == 0)
+ if (known_eq (update_sp_offset, 0))
offset += (ep->offset - ep->previous_offset);
if (ep->to_rtx == stack_pointer_rtx)
{
the cost of the insn by replacing a simple REG with (plus
(reg sp) CST). So try only when we already had a PLUS
before. */
- if (offset == 0 || plus_src)
+ if (known_eq (offset, 0) || plus_src)
{
rtx new_src = plus_constant (GET_MODE (to_rtx), to_rtx, offset);
{
/* If we are assigning to a hard register that can be
eliminated, it must be as part of a PARALLEL, since
- the code above handles single SETs. This reg can not
+ the code above handles single SETs. This reg cannot
be longer eliminated -- it is forced by
mark_not_eliminable. */
for (ep = reg_eliminate;
if (bitmap_bit_p (&to_process, INSN_UID (insn)))
{
lra_push_insn (insn);
- lra_set_used_insn_alternative (insn, -1);
+ lra_set_used_insn_alternative (insn, LRA_UNKNOWN_ALT);
}
bitmap_clear (&to_process);
}
struct lra_elim_table *ep, *ep1;
HARD_REG_SET temp_hard_reg_set;
+ targetm.compute_frame_layout ();
+
/* Clear self elimination offsets. */
for (ep = reg_eliminate; ep < ®_eliminate[NUM_ELIMINABLE_REGS]; ep++)
self_elim_offsets[ep->from] = 0;
" Elimination %d to %d is not possible anymore\n",
ep->from, ep->to);
/* If after processing RTL we decides that SP can be used as
- a result of elimination, it can not be changed. */
+ a result of elimination, it cannot be changed. */
gcc_assert ((ep->to_rtx != stack_pointer_rtx)
|| (ep->from < FIRST_PSEUDO_REGISTER
&& fixed_regs [ep->from]));
if (lra_dump_file != NULL)
fprintf (lra_dump_file, " Using elimination %d to %d now\n",
ep1->from, ep1->to);
- lra_assert (ep1->previous_offset == 0);
+ lra_assert (known_eq (ep1->previous_offset, 0));
ep1->previous_offset = ep->offset;
}
else
fprintf (lra_dump_file, " %d is not eliminable at all\n",
ep->from);
self_elim_offsets[ep->from] = -ep->offset;
- if (ep->offset != 0)
+ if (maybe_ne (ep->offset, 0))
bitmap_ior_into (insns_with_changed_offsets,
&lra_reg_info[ep->from].insn_bitmap);
}
}
-#ifdef ELIMINABLE_REGS
INITIAL_ELIMINATION_OFFSET (ep->from, ep->to, ep->offset);
-#else
- INITIAL_FRAME_POINTER_OFFSET (ep->offset);
-#endif
}
setup_elimination_map ();
result = false;
CLEAR_HARD_REG_SET (temp_hard_reg_set);
for (ep = reg_eliminate; ep < ®_eliminate[NUM_ELIMINABLE_REGS]; ep++)
if (elimination_map[ep->from] == NULL)
- SET_HARD_REG_BIT (temp_hard_reg_set, ep->from);
+ add_to_hard_reg_set (&temp_hard_reg_set, Pmode, ep->from);
else if (elimination_map[ep->from] == ep)
{
/* Prevent the hard register into which we eliminate from
the usage for pseudos. */
if (ep->from != ep->to)
- SET_HARD_REG_BIT (temp_hard_reg_set, ep->to);
- if (ep->previous_offset != ep->offset)
+ add_to_hard_reg_set (&temp_hard_reg_set, Pmode, ep->to);
+ if (maybe_ne (ep->previous_offset, ep->offset))
{
bitmap_ior_into (insns_with_changed_offsets,
&lra_reg_info[ep->from].insn_bitmap);
init_elim_table (void)
{
struct lra_elim_table *ep;
-#ifdef ELIMINABLE_REGS
bool value_p;
const struct elim_table_1 *ep1;
-#endif
if (!reg_eliminate)
reg_eliminate = XCNEWVEC (struct lra_elim_table, NUM_ELIMINABLE_REGS);
/* Initiate member values which will be never changed. */
self_elim_table.can_eliminate = self_elim_table.prev_can_eliminate = true;
self_elim_table.previous_offset = 0;
-#ifdef ELIMINABLE_REGS
+
for (ep = reg_eliminate, ep1 = reg_eliminate_1;
ep < ®_eliminate[NUM_ELIMINABLE_REGS]; ep++, ep1++)
{
|| ! stack_realign_fp)));
setup_can_eliminate (ep, value_p);
}
-#else
- reg_eliminate[0].offset = reg_eliminate[0].previous_offset = 0;
- reg_eliminate[0].from = reg_eliminate_1[0].from;
- reg_eliminate[0].to = reg_eliminate_1[0].to;
- setup_can_eliminate (®_eliminate[0], ! frame_pointer_needed);
-#endif
/* Build the FROM and TO REG rtx's. Note that code in gen_rtx_REG
will cause, e.g., gen_rtx_REG (Pmode, STACK_POINTER_REGNUM) to
if (NONDEBUG_INSN_P (insn))
{
mark_not_eliminable (PATTERN (insn), VOIDmode);
- if (curr_sp_change != 0
+ if (maybe_ne (curr_sp_change, 0)
&& find_reg_note (insn, REG_LABEL_OPERAND, NULL_RTX))
stop_to_sp_elimination_p = true;
}
}
if (! frame_pointer_needed
- && (curr_sp_change != 0 || stop_to_sp_elimination_p)
+ && (maybe_ne (curr_sp_change, 0) || stop_to_sp_elimination_p)
&& bb->succs && bb->succs->length () != 0)
for (ep = reg_eliminate; ep < ®_eliminate[NUM_ELIMINABLE_REGS]; ep++)
if (ep->to == STACK_POINTER_REGNUM)
}
lra_update_insn_regno_info (insn);
lra_push_insn (insn);
- lra_set_used_insn_alternative (insn, -1);
+ lra_set_used_insn_alternative (insn, LRA_UNKNOWN_ALT);
}
}
bitmap_initialize (&insns_with_changed_offsets, ®_obstack);
if (final_p)
{
-#ifdef ENABLE_CHECKING
- update_reg_eliminate (&insns_with_changed_offsets);
- if (! bitmap_empty_p (&insns_with_changed_offsets))
- gcc_unreachable ();
-#endif
+ if (flag_checking)
+ {
+ update_reg_eliminate (&insns_with_changed_offsets);
+ gcc_assert (bitmap_empty_p (&insns_with_changed_offsets));
+ }
/* We change eliminable hard registers in insns so we should do
this for all insns containing any eliminable hard
register. */