/* LRA (local register allocator) driver and LRA utilities.
- Copyright (C) 2010-2016 Free Software Foundation, Inc.
+ Copyright (C) 2010-2021 Free Software Foundation, Inc.
Contributed by Vladimir Makarov <vmakarov@redhat.com>.
This file is part of GCC.
#include "tree.h"
#include "predict.h"
#include "df.h"
+#include "memmodel.h"
#include "tm_p.h"
#include "optabs.h"
#include "regs.h"
#include "lra.h"
#include "lra-int.h"
#include "print-rtl.h"
+#include "function-abi.h"
/* Dump bitmap SET with TITLE and BB INDEX. */
void
static int get_insn_freq (rtx_insn *);
static void invalidate_insn_data_regno_info (lra_insn_recog_data_t,
rtx_insn *, int);
-
/* Expand all regno related info needed for LRA. */
static void
expand_reg_data (int old)
/* The number of emitted reload insns so far. */
int lra_curr_reload_num;
+static void remove_insn_scratches (rtx_insn *insn);
+
/* Emit x := y, processing special case when y = u + v or y = u + v *
scale + w through emit_add (Y can be an address which is base +
index reg * scale + displacement in general case). X may be used
lra_emit_move (rtx x, rtx y)
{
int old;
-
+ rtx_insn *insn;
+
if (GET_CODE (y) != PLUS)
{
if (rtx_equal_p (x, y))
return;
old = max_reg_num ();
- emit_move_insn (x, y);
+
+ insn = (GET_CODE (x) != STRICT_LOW_PART
+ ? emit_move_insn (x, y) : emit_insn (gen_rtx_SET (x, y)));
+ /* The move pattern may require scratch registers, so convert them
+ into real registers now. */
+ if (insn != NULL_RTX)
+ remove_insn_scratches (insn);
if (REG_P (x))
lra_reg_info[ORIGINAL_REGNO (x)].last_reload = ++lra_curr_reload_num;
/* Function emit_move can create pseudos -- so expand the pseudo
/* Pools for insn reg info. */
object_allocator<lra_insn_reg> lra_insn_reg_pool ("insn regs");
-/* Create LRA insn related info about a reference to REGNO in INSN with
- TYPE (in/out/inout), biggest reference mode MODE, flag that it is
- reference through subreg (SUBREG_P), flag that is early clobbered
- in the insn (EARLY_CLOBBER), and reference to the next insn reg
- info (NEXT). */
+/* Create LRA insn related info about a reference to REGNO in INSN
+ with TYPE (in/out/inout), biggest reference mode MODE, flag that it
+ is reference through subreg (SUBREG_P), and reference to the next
+ insn reg info (NEXT). If REGNO can be early clobbered,
+ alternatives in which it can be early clobbered are given by
+ EARLY_CLOBBER_ALTS. */
static struct lra_insn_reg *
new_insn_reg (rtx_insn *insn, int regno, enum op_type type,
- machine_mode mode,
- bool subreg_p, bool early_clobber, struct lra_insn_reg *next)
+ machine_mode mode, bool subreg_p,
+ alternative_mask early_clobber_alts,
+ struct lra_insn_reg *next)
{
lra_insn_reg *ir = lra_insn_reg_pool.allocate ();
ir->type = type;
ir->biggest_mode = mode;
- if (GET_MODE_SIZE (mode) > GET_MODE_SIZE (lra_reg_info[regno].biggest_mode)
- && NONDEBUG_INSN_P (insn))
+ if (NONDEBUG_INSN_P (insn)
+ && partial_subreg_p (lra_reg_info[regno].biggest_mode, mode))
lra_reg_info[regno].biggest_mode = mode;
ir->subreg_p = subreg_p;
- ir->early_clobber = early_clobber;
+ ir->early_clobber_alts = early_clobber_alts;
ir->regno = regno;
ir->next = next;
return ir;
static struct lra_operand_data debug_operand_data =
{
NULL, /* alternative */
- VOIDmode, /* We are not interesting in the operand mode. */
+ 0, /* early_clobber_alts */
+ E_VOIDmode, /* We are not interesting in the operand mode. */
OP_IN,
- 0, 0, 0, 0
+ 0, 0, 0
};
/* The following data are used as static insn data for all debug
- insns. If structure lra_static_insn_data is changed, the
+ bind insns. If structure lra_static_insn_data is changed, the
initializer should be changed too. */
-static struct lra_static_insn_data debug_insn_static_data =
+static struct lra_static_insn_data debug_bind_static_data =
{
&debug_operand_data,
0, /* Duplication operands #. */
NULL /* Descriptions of operands in alternatives. */
};
+/* The following data are used as static insn data for all debug
+ marker insns. If structure lra_static_insn_data is changed, the
+ initializer should be changed too. */
+static struct lra_static_insn_data debug_marker_static_data =
+ {
+ &debug_operand_data,
+ 0, /* Duplication operands #. */
+ -1, /* Commutative operand #. */
+ 0, /* Operands #. There isn't any operand. */
+ 0, /* Duplications #. */
+ 0, /* Alternatives #. We are not interesting in alternatives
+ because we does not proceed debug_insns for reloads. */
+ NULL, /* Hard registers referenced in machine description. */
+ NULL /* Descriptions of operands in alternatives. */
+ };
+
/* Called once per compiler work to initialize some LRA data related
to insns. */
static void
for (unsigned int i = 0; i < NUM_INSN_CODES; i++)
{
if (insn_code_data[i] != NULL)
- free (insn_code_data[i]);
+ {
+ free (insn_code_data[i]);
+ insn_code_data[i] = NULL;
+ }
}
}
/* Map INSN_UID -> the insn recog data (NULL if unknown). */
lra_insn_recog_data_t *lra_insn_recog_data;
+/* Alloc pool we allocate entries for lra_insn_recog_data from. */
+static object_allocator<class lra_insn_recog_data>
+ lra_insn_recog_data_pool ("insn recog data pool");
+
/* Initialize LRA data about insns. */
static void
init_insn_recog_data (void)
}
free_insn_regs (data->regs);
data->regs = NULL;
- free (data);
+ lra_insn_recog_data_pool.remove (data);
}
/* Pools for copies. */
finish_insn_regs ();
lra_copy_pool.release ();
lra_insn_reg_pool.release ();
+ lra_insn_recog_data_pool.release ();
free (lra_insn_recog_data);
}
static_data->operand_alternative = op_alt;
for (i = 0; i < nop; i++)
{
- static_data->operand[i].early_clobber = false;
+ static_data->operand[i].early_clobber_alts = 0;
static_data->operand[i].is_address = false;
if (static_data->operand[i].constraint[0] == '%')
{
for (j = 0; j < nalt; j++)
for (i = 0; i < nop; i++, op_alt++)
{
- static_data->operand[i].early_clobber |= op_alt->earlyclobber;
+ if (op_alt->earlyclobber)
+ static_data->operand[i].early_clobber_alts |= (alternative_mask) 1 << j;
static_data->operand[i].is_address |= op_alt->is_address;
}
}
to LIST. X is a part of insn given by DATA. Return the result
list. */
static struct lra_insn_reg *
-collect_non_operand_hard_regs (rtx *x, lra_insn_recog_data_t data,
+collect_non_operand_hard_regs (rtx_insn *insn, rtx *x,
+ lra_insn_recog_data_t data,
struct lra_insn_reg *list,
enum op_type type, bool early_clobber)
{
const char *fmt = GET_RTX_FORMAT (code);
for (i = 0; i < data->insn_static_data->n_operands; i++)
- if (x == data->operand_loc[i])
+ if (! data->insn_static_data->operand[i].is_operator
+ && x == data->operand_loc[i])
/* It is an operand loc. Stop here. */
return list;
for (i = 0; i < data->insn_static_data->n_dups; i++)
subreg_p = false;
if (code == SUBREG)
{
+ mode = wider_subreg_mode (op);
+ if (read_modify_subreg_p (op))
+ subreg_p = true;
op = SUBREG_REG (op);
code = GET_CODE (op);
- if (GET_MODE_SIZE (mode) < GET_MODE_SIZE (GET_MODE (op)))
- {
- mode = GET_MODE (op);
- if (GET_MODE_SIZE (mode) > REGMODE_NATURAL_SIZE (mode))
- subreg_p = true;
- }
}
if (REG_P (op))
{
return list;
/* Process all regs even unallocatable ones as we need info
about all regs for rematerialization pass. */
- for (last = regno + hard_regno_nregs[regno][mode];
- regno < last;
- regno++)
+ for (last = end_hard_regno (mode, regno); regno < last; regno++)
{
for (curr = list; curr != NULL; curr = curr->next)
if (curr->regno == regno && curr->subreg_p == subreg_p
{
if (curr->type != type)
curr->type = OP_INOUT;
- if (curr->early_clobber != early_clobber)
- curr->early_clobber = true;
+ if (early_clobber)
+ curr->early_clobber_alts = ALL_ALTERNATIVES;
break;
}
if (curr == NULL)
{
- /* This is a new hard regno or the info can not be
+ /* This is a new hard regno or the info cannot be
integrated into the found structure. */
#ifdef STACK_REGS
early_clobber
&& regno <= LAST_STACK_REG));
#endif
list = new_insn_reg (data->insn, regno, type, mode, subreg_p,
- early_clobber, list);
+ early_clobber ? ALL_ALTERNATIVES : 0, list);
}
}
return list;
switch (code)
{
case SET:
- list = collect_non_operand_hard_regs (&SET_DEST (op), data,
+ list = collect_non_operand_hard_regs (insn, &SET_DEST (op), data,
list, OP_OUT, false);
- list = collect_non_operand_hard_regs (&SET_SRC (op), data,
+ list = collect_non_operand_hard_regs (insn, &SET_SRC (op), data,
list, OP_IN, false);
break;
case CLOBBER:
- /* We treat clobber of non-operand hard registers as early
- clobber (the behavior is expected from asm). */
- list = collect_non_operand_hard_regs (&XEXP (op, 0), data,
+ /* We treat clobber of non-operand hard registers as early clobber. */
+ list = collect_non_operand_hard_regs (insn, &XEXP (op, 0), data,
list, OP_OUT, true);
break;
case PRE_INC: case PRE_DEC: case POST_INC: case POST_DEC:
- list = collect_non_operand_hard_regs (&XEXP (op, 0), data,
+ list = collect_non_operand_hard_regs (insn, &XEXP (op, 0), data,
list, OP_INOUT, false);
break;
case PRE_MODIFY: case POST_MODIFY:
- list = collect_non_operand_hard_regs (&XEXP (op, 0), data,
+ list = collect_non_operand_hard_regs (insn, &XEXP (op, 0), data,
list, OP_INOUT, false);
- list = collect_non_operand_hard_regs (&XEXP (op, 1), data,
+ list = collect_non_operand_hard_regs (insn, &XEXP (op, 1), data,
list, OP_IN, false);
break;
default:
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
- list = collect_non_operand_hard_regs (&XEXP (op, i), data,
+ list = collect_non_operand_hard_regs (insn, &XEXP (op, i), data,
list, OP_IN, false);
else if (fmt[i] == 'E')
for (j = XVECLEN (op, i) - 1; j >= 0; j--)
- list = collect_non_operand_hard_regs (&XVECEXP (op, i, j), data,
- list, OP_IN, false);
+ list = collect_non_operand_hard_regs (insn, &XVECEXP (op, i, j),
+ data, list, OP_IN, false);
}
}
return list;
/* It might be a new simple insn which is not recognized yet. */
INSN_CODE (insn) = icode = recog_memoized (insn);
}
- data = XNEW (struct lra_insn_recog_data);
+ data = lra_insn_recog_data_pool.allocate ();
lra_insn_recog_data[uid] = data;
data->insn = insn;
- data->used_insn_alternative = -1;
+ data->used_insn_alternative = LRA_UNKNOWN_ALT;
data->icode = icode;
data->regs = NULL;
if (DEBUG_INSN_P (insn))
{
- data->insn_static_data = &debug_insn_static_data;
data->dup_loc = NULL;
data->arg_hard_regs = NULL;
data->preferred_alternatives = ALL_ALTERNATIVES;
- data->operand_loc = XNEWVEC (rtx *, 1);
- data->operand_loc[0] = &INSN_VAR_LOCATION_LOC (insn);
+ if (DEBUG_BIND_INSN_P (insn))
+ {
+ data->insn_static_data = &debug_bind_static_data;
+ data->operand_loc = XNEWVEC (rtx *, 1);
+ data->operand_loc[0] = &INSN_VAR_LOCATION_LOC (insn);
+ }
+ else if (DEBUG_MARKER_INSN_P (insn))
+ {
+ data->insn_static_data = &debug_marker_static_data;
+ data->operand_loc = NULL;
+ }
return data;
}
if (icode < 0)
data->operand_loc,
constraints, operand_mode, NULL);
if (nop > 0)
- {
- const char *p = recog_data.constraints[0];
-
- for (p = constraints[0]; *p; p++)
- nalt += *p == ',';
- }
+ for (const char *p =constraints[0]; *p; p++)
+ nalt += *p == ',';
data->insn_static_data = insn_static_data
= get_static_insn_data (-1, nop, 0, nalt);
for (i = 0; i < nop; i++)
{
operand_alternative *op_alt = XCNEWVEC (operand_alternative,
nalt * nop);
- preprocess_constraints (nop, nalt, constraints, op_alt);
+ preprocess_constraints (nop, nalt, constraints, op_alt,
+ data->operand_loc);
setup_operand_alternative (data, op_alt);
}
}
insn_static_data->hard_regs = NULL;
else
insn_static_data->hard_regs
- = collect_non_operand_hard_regs (&PATTERN (insn), data,
+ = collect_non_operand_hard_regs (insn, &PATTERN (insn), data,
NULL, OP_IN, false);
data->arg_hard_regs = NULL;
if (CALL_P (insn))
arg_hard_regs[n_hard_regs++]
= regno + i + (use_p ? 0 : FIRST_PSEUDO_REGISTER);
}
+
if (n_hard_regs != 0)
{
arg_hard_regs[n_hard_regs++] = -1;
int n;
unsigned int uid = INSN_UID (insn);
struct lra_static_insn_data *insn_static_data;
- HOST_WIDE_INT sp_offset = 0;
+ poly_int64 sp_offset = 0;
check_and_expand_insn_recog_data (uid);
if ((data = lra_insn_recog_data[uid]) != NULL
return data;
}
insn_static_data = data->insn_static_data;
- data->used_insn_alternative = -1;
+ data->used_insn_alternative = LRA_UNKNOWN_ALT;
if (DEBUG_INSN_P (insn))
return data;
if (data->icode < 0)
/* The size of the following array. */
static int reg_info_size;
/* Common info about each register. */
-struct lra_reg *lra_reg_info;
+class lra_reg *lra_reg_info;
+
+HARD_REG_SET hard_regs_spilled_into;
/* Last register value. */
static int last_reg_value;
lra_reg_info[i].no_stack_p = false;
#endif
CLEAR_HARD_REG_SET (lra_reg_info[i].conflict_hard_regs);
- CLEAR_HARD_REG_SET (lra_reg_info[i].actual_call_used_reg_set);
lra_reg_info[i].preferred_hard_regno1 = -1;
lra_reg_info[i].preferred_hard_regno2 = -1;
lra_reg_info[i].preferred_hard_regno_profit1 = 0;
lra_reg_info[i].live_ranges = NULL;
lra_reg_info[i].nrefs = lra_reg_info[i].freq = 0;
lra_reg_info[i].last_reload = 0;
- lra_reg_info[i].restore_regno = -1;
+ lra_reg_info[i].restore_rtx = NULL_RTX;
lra_reg_info[i].val = get_new_reg_value ();
lra_reg_info[i].offset = 0;
lra_reg_info[i].copies = NULL;
last_reg_value = 0;
reg_info_size = max_reg_num () * 3 / 2 + 1;
- lra_reg_info = XNEWVEC (struct lra_reg, reg_info_size);
+ lra_reg_info = XNEWVEC (class lra_reg, reg_info_size);
for (i = 0; i < reg_info_size; i++)
initialize_lra_reg_info_element (i);
copy_vec.truncate (0);
+ CLEAR_HARD_REG_SET (hard_regs_spilled_into);
}
if (reg_info_size > max_reg_num ())
return;
reg_info_size = max_reg_num () * 3 / 2 + 1;
- lra_reg_info = XRESIZEVEC (struct lra_reg, lra_reg_info, reg_info_size);
+ lra_reg_info = XRESIZEVEC (class lra_reg, lra_reg_info, reg_info_size);
for (i = old; i < reg_info_size; i++)
initialize_lra_reg_info_element (i);
}
/* This page contains code dealing with info about registers in
insns. */
-/* Process X of insn UID recursively and add info (operand type is
- given by TYPE, flag of that it is early clobber is EARLY_CLOBBER)
- about registers in X to the insn DATA. */
+/* Process X of INSN recursively and add info (operand type is given
+ by TYPE) about registers in X to the insn DATA. If X can be early
+ clobbered, alternatives in which it can be early clobbered are given
+ by EARLY_CLOBBER_ALTS. */
static void
-add_regs_to_insn_regno_info (lra_insn_recog_data_t data, rtx x, int uid,
- enum op_type type, bool early_clobber)
+add_regs_to_insn_regno_info (lra_insn_recog_data_t data, rtx x,
+ rtx_insn *insn, enum op_type type,
+ alternative_mask early_clobber_alts)
{
int i, j, regno;
bool subreg_p;
subreg_p = false;
if (GET_CODE (x) == SUBREG)
{
+ mode = wider_subreg_mode (x);
+ if (read_modify_subreg_p (x))
+ subreg_p = true;
x = SUBREG_REG (x);
code = GET_CODE (x);
- if (GET_MODE_SIZE (mode) < GET_MODE_SIZE (GET_MODE (x)))
- {
- mode = GET_MODE (x);
- if (GET_MODE_SIZE (mode) > REGMODE_NATURAL_SIZE (mode))
- subreg_p = true;
- }
}
if (REG_P (x))
{
/* Process all regs even unallocatable ones as we need info about
all regs for rematerialization pass. */
expand_reg_info ();
- if (bitmap_set_bit (&lra_reg_info[regno].insn_bitmap, uid))
+ if (bitmap_set_bit (&lra_reg_info[regno].insn_bitmap, INSN_UID (insn)))
{
data->regs = new_insn_reg (data->insn, regno, type, mode, subreg_p,
- early_clobber, data->regs);
+ early_clobber_alts, data->regs);
return;
}
else
if (curr->regno == regno)
{
if (curr->subreg_p != subreg_p || curr->biggest_mode != mode)
- /* The info can not be integrated into the found
+ /* The info cannot be integrated into the found
structure. */
data->regs = new_insn_reg (data->insn, regno, type, mode,
- subreg_p, early_clobber,
+ subreg_p, early_clobber_alts,
data->regs);
else
{
if (curr->type != type)
curr->type = OP_INOUT;
- if (curr->early_clobber != early_clobber)
- curr->early_clobber = true;
+ curr->early_clobber_alts |= early_clobber_alts;
}
return;
}
switch (code)
{
case SET:
- add_regs_to_insn_regno_info (data, SET_DEST (x), uid, OP_OUT, false);
- add_regs_to_insn_regno_info (data, SET_SRC (x), uid, OP_IN, false);
+ add_regs_to_insn_regno_info (data, SET_DEST (x), insn, OP_OUT, 0);
+ add_regs_to_insn_regno_info (data, SET_SRC (x), insn, OP_IN, 0);
break;
case CLOBBER:
/* We treat clobber of non-operand hard registers as early
- clobber (the behavior is expected from asm). */
- add_regs_to_insn_regno_info (data, XEXP (x, 0), uid, OP_OUT, true);
+ clobber. */
+ add_regs_to_insn_regno_info (data, XEXP (x, 0), insn, OP_OUT,
+ ALL_ALTERNATIVES);
break;
case PRE_INC: case PRE_DEC: case POST_INC: case POST_DEC:
- add_regs_to_insn_regno_info (data, XEXP (x, 0), uid, OP_INOUT, false);
+ add_regs_to_insn_regno_info (data, XEXP (x, 0), insn, OP_INOUT, 0);
break;
case PRE_MODIFY: case POST_MODIFY:
- add_regs_to_insn_regno_info (data, XEXP (x, 0), uid, OP_INOUT, false);
- add_regs_to_insn_regno_info (data, XEXP (x, 1), uid, OP_IN, false);
+ add_regs_to_insn_regno_info (data, XEXP (x, 0), insn, OP_INOUT, 0);
+ add_regs_to_insn_regno_info (data, XEXP (x, 1), insn, OP_IN, 0);
break;
default:
if ((code != PARALLEL && code != EXPR_LIST) || type != OP_OUT)
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
- add_regs_to_insn_regno_info (data, XEXP (x, i), uid, type, false);
+ add_regs_to_insn_regno_info (data, XEXP (x, i), insn, type, 0);
else if (fmt[i] == 'E')
{
for (j = XVECLEN (x, i) - 1; j >= 0; j--)
- add_regs_to_insn_regno_info (data, XVECEXP (x, i, j), uid,
- type, false);
+ add_regs_to_insn_regno_info (data, XVECEXP (x, i, j), insn,
+ type, 0);
}
}
}
void
lra_update_insn_regno_info (rtx_insn *insn)
{
- int i, uid, freq;
+ int i, freq;
lra_insn_recog_data_t data;
struct lra_static_insn_data *static_data;
enum rtx_code code;
return;
data = lra_get_insn_recog_data (insn);
static_data = data->insn_static_data;
- freq = get_insn_freq (insn);
+ freq = NONDEBUG_INSN_P (insn) ? get_insn_freq (insn) : 0;
invalidate_insn_data_regno_info (data, insn, freq);
- uid = INSN_UID (insn);
for (i = static_data->n_operands - 1; i >= 0; i--)
- add_regs_to_insn_regno_info (data, *data->operand_loc[i], uid,
+ add_regs_to_insn_regno_info (data, *data->operand_loc[i], insn,
static_data->operand[i].type,
- static_data->operand[i].early_clobber);
+ static_data->operand[i].early_clobber_alts);
if ((code = GET_CODE (PATTERN (insn))) == CLOBBER || code == USE)
- add_regs_to_insn_regno_info (data, XEXP (PATTERN (insn), 0), uid,
- code == USE ? OP_IN : OP_OUT, false);
+ add_regs_to_insn_regno_info (data, XEXP (PATTERN (insn), 0), insn,
+ code == USE ? OP_IN : OP_OUT, 0);
if (CALL_P (insn))
/* On some targets call insns can refer to pseudos in memory in
CALL_INSN_FUNCTION_USAGE list. Process them in order to
for (link = CALL_INSN_FUNCTION_USAGE (insn);
link != NULL_RTX;
link = XEXP (link, 1))
- if (((code = GET_CODE (XEXP (link, 0))) == USE || code == CLOBBER)
- && MEM_P (XEXP (XEXP (link, 0), 0)))
- add_regs_to_insn_regno_info (data, XEXP (XEXP (link, 0), 0), uid,
- code == USE ? OP_IN : OP_OUT, false);
+ {
+ code = GET_CODE (XEXP (link, 0));
+ if ((code == USE || code == CLOBBER)
+ && MEM_P (XEXP (XEXP (link, 0), 0)))
+ add_regs_to_insn_regno_info (data, XEXP (XEXP (link, 0), 0), insn,
+ code == USE ? OP_IN : OP_OUT, 0);
+ }
if (NONDEBUG_INSN_P (insn))
setup_insn_reg_info (data, freq);
}
\f
+/* Recursive hash function for RTL X. */
+hashval_t
+lra_rtx_hash (rtx x)
+{
+ int i, j;
+ enum rtx_code code;
+ const char *fmt;
+ hashval_t val = 0;
+
+ if (x == 0)
+ return val;
+
+ code = GET_CODE (x);
+ val += (int) code + 4095;
+
+ /* Some RTL can be compared nonrecursively. */
+ switch (code)
+ {
+ case REG:
+ return val + REGNO (x);
+
+ case LABEL_REF:
+ return iterative_hash_object (XEXP (x, 0), val);
+
+ case SYMBOL_REF:
+ return iterative_hash_object (XSTR (x, 0), val);
+
+ case SCRATCH:
+ case CONST_DOUBLE:
+ case CONST_VECTOR:
+ return val;
+
+ case CONST_INT:
+ return val + UINTVAL (x);
+
+ default:
+ break;
+ }
+
+ /* Hash the elements. */
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ switch (fmt[i])
+ {
+ case 'w':
+ val += XWINT (x, i);
+ break;
+
+ case 'n':
+ case 'i':
+ val += XINT (x, i);
+ break;
+
+ case 'V':
+ case 'E':
+ val += XVECLEN (x, i);
+
+ for (j = 0; j < XVECLEN (x, i); j++)
+ val += lra_rtx_hash (XVECEXP (x, i, j));
+ break;
+
+ case 'e':
+ val += lra_rtx_hash (XEXP (x, i));
+ break;
+
+ case 'S':
+ case 's':
+ val += htab_hash_string (XSTR (x, i));
+ break;
+
+ case 'u':
+ case '0':
+ case 't':
+ break;
+
+ /* It is believed that rtx's at this level will never
+ contain anything but integers and other rtx's, except for
+ within LABEL_REFs and SYMBOL_REFs. */
+ default:
+ abort ();
+ }
+ }
+ return val;
+}
+
+\f
+
/* This page contains code dealing with stack of the insns which
should be processed by the next constraint pass. */
static void
setup_sp_offset (rtx_insn *from, rtx_insn *last)
{
- rtx_insn *before = next_nonnote_insn_bb (last);
- HOST_WIDE_INT offset = (before == NULL_RTX || ! INSN_P (before)
- ? 0 : lra_get_insn_recog_data (before)->sp_offset);
+ rtx_insn *before = next_nonnote_nondebug_insn_bb (last);
+ poly_int64 offset = (before == NULL_RTX || ! INSN_P (before)
+ ? 0 : lra_get_insn_recog_data (before)->sp_offset);
for (rtx_insn *insn = from; insn != NEXT_INSN (last); insn = NEXT_INSN (insn))
lra_get_insn_recog_data (insn)->sp_offset = offset;
lra_process_new_insns (rtx_insn *insn, rtx_insn *before, rtx_insn *after,
const char *title)
{
- rtx_insn *last;
-
if (before == NULL_RTX && after == NULL_RTX)
return;
if (lra_dump_file != NULL)
fprintf (lra_dump_file," %s before:\n", title);
dump_rtl_slim (lra_dump_file, before, NULL, -1, 0);
}
- if (after != NULL_RTX)
- {
- fprintf (lra_dump_file, " %s after:\n", title);
- dump_rtl_slim (lra_dump_file, after, NULL, -1, 0);
- }
- fprintf (lra_dump_file, "\n");
}
if (before != NULL_RTX)
{
{
if (cfun->can_throw_non_call_exceptions)
copy_reg_eh_region_note_forward (insn, after, NULL);
- for (last = after; NEXT_INSN (last) != NULL_RTX; last = NEXT_INSN (last))
- ;
- emit_insn_after (after, insn);
- push_insns (last, insn);
- setup_sp_offset (after, last);
+ if (! JUMP_P (insn))
+ {
+ rtx_insn *last;
+
+ if (lra_dump_file != NULL)
+ {
+ fprintf (lra_dump_file, " %s after:\n", title);
+ dump_rtl_slim (lra_dump_file, after, NULL, -1, 0);
+ }
+ for (last = after;
+ NEXT_INSN (last) != NULL_RTX;
+ last = NEXT_INSN (last))
+ ;
+ emit_insn_after (after, insn);
+ push_insns (last, insn);
+ setup_sp_offset (after, last);
+ }
+ else
+ {
+ /* Put output reload insns on successor BBs: */
+ edge_iterator ei;
+ edge e;
+
+ FOR_EACH_EDGE (e, ei, BLOCK_FOR_INSN (insn)->succs)
+ if (e->dest != EXIT_BLOCK_PTR_FOR_FN (cfun))
+ {
+ /* We already made the edge no-critical in ira.c::ira */
+ lra_assert (!EDGE_CRITICAL_P (e));
+ rtx_insn *curr, *tmp = BB_HEAD (e->dest);
+ if (LABEL_P (tmp))
+ tmp = NEXT_INSN (tmp);
+ if (NOTE_INSN_BASIC_BLOCK_P (tmp))
+ tmp = NEXT_INSN (tmp);
+ /* Do not put reload insns if it is the last BB
+ without actual insns. */
+ if (tmp == NULL)
+ continue;
+ start_sequence ();
+ for (curr = after; curr != NULL_RTX; curr = NEXT_INSN (curr))
+ emit_insn (copy_insn (PATTERN (curr)));
+ rtx_insn *copy = get_insns (), *last = get_last_insn ();
+ end_sequence ();
+ if (lra_dump_file != NULL)
+ {
+ fprintf (lra_dump_file, " %s after in bb%d:\n", title,
+ e->dest->index);
+ dump_rtl_slim (lra_dump_file, copy, NULL, -1, 0);
+ }
+ /* Use the right emit func for setting up BB_END/BB_HEAD: */
+ if (BB_END (e->dest) == PREV_INSN (tmp))
+ emit_insn_after_noloc (copy, PREV_INSN (tmp), e->dest);
+ else
+ emit_insn_before_noloc (copy, tmp, e->dest);
+ push_insns (last, PREV_INSN (copy));
+ setup_sp_offset (copy, last);
+ /* We can ignore BB live info here as it and reg notes
+ will be updated before the next assignment
+ sub-pass. */
+ }
+ }
}
+ if (lra_dump_file != NULL)
+ fprintf (lra_dump_file, "\n");
if (cfun->can_throw_non_call_exceptions)
{
rtx note = find_reg_note (insn, REG_EH_REGION, NULL_RTX);
/* Replace all references to register OLD_REGNO in *LOC with pseudo
register NEW_REG. Try to simplify subreg of constant if SUBREG_P.
- Return true if any change was made. */
+ DEBUG_P is if LOC is within a DEBUG_INSN. Return true if any
+ change was made. */
bool
-lra_substitute_pseudo (rtx *loc, int old_regno, rtx new_reg, bool subreg_p)
+lra_substitute_pseudo (rtx *loc, int old_regno, rtx new_reg, bool subreg_p,
+ bool debug_p)
{
rtx x = *loc;
bool result = false;
machine_mode inner_mode = GET_MODE (new_reg);
if (mode != inner_mode
- && ! (CONST_INT_P (new_reg) && SCALAR_INT_MODE_P (mode)))
+ && ! (CONST_SCALAR_INT_P (new_reg) && SCALAR_INT_MODE_P (mode)))
{
- if (GET_MODE_SIZE (mode) >= GET_MODE_SIZE (inner_mode)
- || ! SCALAR_INT_MODE_P (inner_mode))
- new_reg = gen_rtx_SUBREG (mode, new_reg, 0);
+ poly_uint64 offset = 0;
+ if (partial_subreg_p (mode, inner_mode)
+ && SCALAR_INT_MODE_P (inner_mode))
+ offset = subreg_lowpart_offset (mode, inner_mode);
+ if (debug_p)
+ new_reg = gen_rtx_raw_SUBREG (mode, new_reg, offset);
else
- new_reg = gen_lowpart_SUBREG (mode, new_reg);
+ new_reg = gen_rtx_SUBREG (mode, new_reg, offset);
}
*loc = new_reg;
return true;
if (fmt[i] == 'e')
{
if (lra_substitute_pseudo (&XEXP (x, i), old_regno,
- new_reg, subreg_p))
+ new_reg, subreg_p, debug_p))
result = true;
}
else if (fmt[i] == 'E')
{
for (j = XVECLEN (x, i) - 1; j >= 0; j--)
if (lra_substitute_pseudo (&XVECEXP (x, i, j), old_regno,
- new_reg, subreg_p))
+ new_reg, subreg_p, debug_p))
result = true;
}
}
rtx new_reg, bool subreg_p)
{
rtx loc = insn;
- return lra_substitute_pseudo (&loc, old_regno, new_reg, subreg_p);
+ return lra_substitute_pseudo (&loc, old_regno, new_reg, subreg_p,
+ DEBUG_INSN_P (insn));
}
\f
-/* This page contains code dealing with scratches (changing them onto
- pseudos and restoring them from the pseudos).
-
- We change scratches into pseudos at the beginning of LRA to
- simplify dealing with them (conflicts, hard register assignments).
-
- If the pseudo denoting scratch was spilled it means that we do need
- a hard register for it. Such pseudos are transformed back to
- scratches at the end of LRA. */
-
-/* Description of location of a former scratch operand. */
-struct sloc
+/* Return new register of the same mode as ORIGINAL of class ALL_REGS.
+ Used in ira_remove_scratches. */
+static rtx
+get_scratch_reg (rtx original)
{
- rtx_insn *insn; /* Insn where the scratch was. */
- int nop; /* Number of the operand which was a scratch. */
-};
-
-typedef struct sloc *sloc_t;
-
-/* Locations of the former scratches. */
-static vec<sloc_t> scratches;
-
-/* Bitmap of scratch regnos. */
-static bitmap_head scratch_bitmap;
-
-/* Bitmap of scratch operands. */
-static bitmap_head scratch_operand_bitmap;
-
-/* Return true if pseudo REGNO is made of SCRATCH. */
-bool
-lra_former_scratch_p (int regno)
-{
- return bitmap_bit_p (&scratch_bitmap, regno);
+ return lra_create_new_reg (GET_MODE (original), original, ALL_REGS, NULL);
}
-/* Return true if the operand NOP of INSN is a former scratch. */
-bool
-lra_former_scratch_operand_p (rtx_insn *insn, int nop)
+/* Remove all insn scratches in INSN. */
+static void
+remove_insn_scratches (rtx_insn *insn)
{
- return bitmap_bit_p (&scratch_operand_bitmap,
- INSN_UID (insn) * MAX_RECOG_OPERANDS + nop) != 0;
+ if (ira_remove_insn_scratches (insn, true, lra_dump_file, get_scratch_reg))
+ df_insn_rescan (insn);
}
-/* Register operand NOP in INSN as a former scratch. It will be
- changed to scratch back, if it is necessary, at the LRA end. */
-void
-lra_register_new_scratch_op (rtx_insn *insn, int nop)
-{
- lra_insn_recog_data_t id = lra_get_insn_recog_data (insn);
- rtx op = *id->operand_loc[nop];
- sloc_t loc = XNEW (struct sloc);
- lra_assert (REG_P (op));
- loc->insn = insn;
- loc->nop = nop;
- scratches.safe_push (loc);
- bitmap_set_bit (&scratch_bitmap, REGNO (op));
- bitmap_set_bit (&scratch_operand_bitmap,
- INSN_UID (insn) * MAX_RECOG_OPERANDS + nop);
- add_reg_note (insn, REG_UNUSED, op);
-}
-
-/* Change scratches onto pseudos and save their location. */
+/* Remove all insn scratches in the current function. */
static void
remove_scratches (void)
{
- int i;
- bool insn_changed_p;
basic_block bb;
rtx_insn *insn;
- rtx reg;
- lra_insn_recog_data_t id;
- struct lra_static_insn_data *static_id;
- scratches.create (get_max_uid ());
- bitmap_initialize (&scratch_bitmap, ®_obstack);
- bitmap_initialize (&scratch_operand_bitmap, ®_obstack);
FOR_EACH_BB_FN (bb, cfun)
FOR_BB_INSNS (bb, insn)
- if (INSN_P (insn))
- {
- id = lra_get_insn_recog_data (insn);
- static_id = id->insn_static_data;
- insn_changed_p = false;
- for (i = 0; i < static_id->n_operands; i++)
- if (GET_CODE (*id->operand_loc[i]) == SCRATCH
- && GET_MODE (*id->operand_loc[i]) != VOIDmode)
- {
- insn_changed_p = true;
- *id->operand_loc[i] = reg
- = lra_create_new_reg (static_id->operand[i].mode,
- *id->operand_loc[i], ALL_REGS, NULL);
- lra_register_new_scratch_op (insn, i);
- if (lra_dump_file != NULL)
- fprintf (lra_dump_file,
- "Removing SCRATCH in insn #%u (nop %d)\n",
- INSN_UID (insn), i);
- }
- if (insn_changed_p)
- /* Because we might use DF right after caller-saves sub-pass
- we need to keep DF info up to date. */
- df_insn_rescan (insn);
- }
-}
-
-/* Changes pseudos created by function remove_scratches onto scratches. */
-static void
-restore_scratches (void)
-{
- int regno;
- unsigned i;
- sloc_t loc;
- rtx_insn *last = NULL;
- lra_insn_recog_data_t id = NULL;
-
- for (i = 0; scratches.iterate (i, &loc); i++)
- {
- /* Ignore already deleted insns. */
- if (NOTE_P (loc->insn)
- && NOTE_KIND (loc->insn) == NOTE_INSN_DELETED)
- continue;
- if (last != loc->insn)
- {
- last = loc->insn;
- id = lra_get_insn_recog_data (last);
- }
- if (REG_P (*id->operand_loc[loc->nop])
- && ((regno = REGNO (*id->operand_loc[loc->nop]))
- >= FIRST_PSEUDO_REGISTER)
- && lra_get_regno_hard_regno (regno) < 0)
- {
- /* It should be only case when scratch register with chosen
- constraint 'X' did not get memory or hard register. */
- lra_assert (lra_former_scratch_p (regno));
- *id->operand_loc[loc->nop]
- = gen_rtx_SCRATCH (GET_MODE (*id->operand_loc[loc->nop]));
- lra_update_dup (id, loc->nop);
- if (lra_dump_file != NULL)
- fprintf (lra_dump_file, "Restoring SCRATCH in insn #%u(nop %d)\n",
- INSN_UID (loc->insn), loc->nop);
- }
- }
- for (i = 0; scratches.iterate (i, &loc); i++)
- free (loc);
- scratches.release ();
- bitmap_clear (&scratch_bitmap);
- bitmap_clear (&scratch_operand_bitmap);
+ if (INSN_P (insn))
+ remove_insn_scratches (insn);
}
-\f
-
/* Function checks RTL for correctness. If FINAL_P is true, it is
done at the end of LRA and the check is more rigorous. */
static void
return false;
}
-
-/* Process recursively X of INSN and add REG_INC notes if necessary. */
-static void
-add_auto_inc_notes (rtx_insn *insn, rtx x)
-{
- enum rtx_code code = GET_CODE (x);
- const char *fmt;
- int i, j;
-
- if (code == MEM && auto_inc_p (XEXP (x, 0)))
- {
- add_reg_note (insn, REG_INC, XEXP (XEXP (x, 0), 0));
- return;
- }
-
- /* Scan all X sub-expressions. */
- fmt = GET_RTX_FORMAT (code);
- for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
- {
- if (fmt[i] == 'e')
- add_auto_inc_notes (insn, XEXP (x, i));
- else if (fmt[i] == 'E')
- for (j = XVECLEN (x, i) - 1; j >= 0; j--)
- add_auto_inc_notes (insn, XVECEXP (x, i, j));
- }
-}
-
-
/* Remove all REG_DEAD and REG_UNUSED notes and regenerate REG_INC.
We change pseudos by hard registers without notification of DF and
that can make the notes obsolete. DF-infrastructure does not deal
it is possible. */
int lra_bad_spill_regno_start;
+/* A pseudo of Pmode. */
+rtx lra_pmode_pseudo;
+
/* Inheritance pseudo regnos before the new spill pass. */
bitmap_head lra_inheritance_pseudos;
/* Split regnos before the new spill pass. */
bitmap_head lra_split_regs;
-/* Reload pseudo regnos before the new assignmnet pass which still can
- be spilled after the assinment pass as memory is also accepted in
+/* Reload pseudo regnos before the new assignment pass which still can
+ be spilled after the assignment pass as memory is also accepted in
insns for the reload pseudos. */
bitmap_head lra_optional_reload_pseudos;
/* Pseudo regnos used for subreg reloads before the new assignment
- pass. Such pseudos still can be spilled after the assinment
+ pass. Such pseudos still can be spilled after the assignment
pass. */
bitmap_head lra_subreg_reload_pseudos;
/* File used for output of LRA debug information. */
FILE *lra_dump_file;
+/* True if we split hard reg after the last constraint sub-pass. */
+bool lra_hard_reg_split_p;
+
+/* True if we found an asm error. */
+bool lra_asm_error_p;
+
/* True if we should try spill into registers of different classes
instead of memory. */
bool lra_reg_spill_p;
lra (FILE *f)
{
int i;
- bool live_p, scratch_p, inserted_p;
+ bool live_p, inserted_p;
lra_dump_file = f;
-
+ lra_asm_error_p = false;
+ lra_pmode_pseudo = gen_reg_rtx (Pmode);
+
timevar_push (TV_LRA);
/* Make sure that the last insn is a note. Some subsequent passes
need it. */
emit_note (NOTE_INSN_DELETED);
- COPY_HARD_REG_SET (lra_no_alloc_regs, ira_no_alloc_regs);
+ lra_no_alloc_regs = ira_no_alloc_regs;
init_reg_info ();
expand_reg_info ();
lra_constraint_new_regno_start = lra_new_regno_start = max_reg_num ();
lra_bad_spill_regno_start = INT_MAX;
remove_scratches ();
- scratch_p = lra_constraint_new_regno_start != max_reg_num ();
/* A function that has a non-local label that can reach the exit
block via non-exceptional paths must save all call-saved
if (crtl->saves_all_registers)
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
- if (! call_used_regs[i] && ! fixed_regs[i] && ! LOCAL_REGNO (i))
+ if (!crtl->abi->clobbers_full_reg_p (i)
+ && !fixed_regs[i]
+ && !LOCAL_REGNO (i))
df_set_regs_ever_live (i, true);
/* We don't DF from now and avoid its using because it is to
bitmap_initialize (&lra_optional_reload_pseudos, ®_obstack);
bitmap_initialize (&lra_subreg_reload_pseudos, ®_obstack);
live_p = false;
- if (get_frame_size () != 0 && crtl->stack_alignment_needed)
+ if (maybe_ne (get_frame_size (), 0) && crtl->stack_alignment_needed)
/* If we have a stack frame, we must align it now. The stack size
may be a part of the offset computation for register
elimination. */
{
for (;;)
{
- /* We should try to assign hard registers to scratches even
- if there were no RTL transformations in
- lra_constraints. */
- if (! lra_constraints (lra_constraint_iter == 0)
- && (lra_constraint_iter > 1
- || (! scratch_p && ! caller_save_needed)))
- break;
+ bool reloads_p = lra_constraints (lra_constraint_iter == 0);
/* Constraint transformations may result in that eliminable
hard regs become uneliminable and pseudos which use them
should be spilled. It is better to do it before pseudo
RS6000_PIC_OFFSET_TABLE_REGNUM uneliminable if we started
to use a constant pool. */
lra_eliminate (false, false);
- /* Do inheritance only for regular algorithms. */
- if (! lra_simple_p)
+ /* We should try to assign hard registers to scratches even
+ if there were no RTL transformations in lra_constraints.
+ Also we should check IRA assignments on the first
+ iteration as they can be wrong because of early clobbers
+ operands which are ignored in IRA. */
+ if (! reloads_p && lra_constraint_iter > 1)
{
- if (flag_ipa_ra)
- {
- if (live_p)
- lra_clear_live_ranges ();
- /* As a side-effect of lra_create_live_ranges, we calculate
- actual_call_used_reg_set, which is needed during
- lra_inheritance. */
- lra_create_live_ranges (true, true);
- live_p = true;
- }
- lra_inheritance ();
+ /* Stack is not empty here only when there are changes
+ during the elimination sub-pass. */
+ if (bitmap_empty_p (lra_constraint_insn_stack_bitmap))
+ break;
+ else
+ /* If there are no reloads but changing due
+ elimination, restart the constraint sub-pass
+ first. */
+ continue;
}
+ /* Do inheritance only for regular algorithms. */
+ if (! lra_simple_p)
+ lra_inheritance ();
if (live_p)
lra_clear_live_ranges ();
- /* We need live ranges for lra_assign -- so build them. But
- don't remove dead insns or change global live info as we
- can undo inheritance transformations after inheritance
- pseudo assigning. */
- lra_create_live_ranges (true, false);
- live_p = true;
- /* If we don't spill non-reload and non-inheritance pseudos,
- there is no sense to run memory-memory move coalescing.
- If inheritance pseudos were spilled, the memory-memory
- moves involving them will be removed by pass undoing
- inheritance. */
- if (lra_simple_p)
- lra_assign ();
- else
+ bool fails_p;
+ lra_hard_reg_split_p = false;
+ do
{
- bool spill_p = !lra_assign ();
-
- if (lra_undo_inheritance ())
- live_p = false;
- if (spill_p)
+ /* We need live ranges for lra_assign -- so build them.
+ But don't remove dead insns or change global live
+ info as we can undo inheritance transformations after
+ inheritance pseudo assigning. */
+ lra_create_live_ranges (true, !lra_simple_p);
+ live_p = true;
+ /* If we don't spill non-reload and non-inheritance
+ pseudos, there is no sense to run memory-memory move
+ coalescing. If inheritance pseudos were spilled, the
+ memory-memory moves involving them will be removed by
+ pass undoing inheritance. */
+ if (lra_simple_p)
+ lra_assign (fails_p);
+ else
{
- if (! live_p)
+ bool spill_p = !lra_assign (fails_p);
+
+ if (lra_undo_inheritance ())
+ live_p = false;
+ if (spill_p && ! fails_p)
{
- lra_create_live_ranges (true, true);
- live_p = true;
+ if (! live_p)
+ {
+ lra_create_live_ranges (true, true);
+ live_p = true;
+ }
+ if (lra_coalesce ())
+ live_p = false;
}
- if (lra_coalesce ())
- live_p = false;
+ if (! live_p)
+ lra_clear_live_ranges ();
+ }
+ if (fails_p)
+ {
+ /* It is a very rare case. It is the last hope to
+ split a hard regno live range for a reload
+ pseudo. */
+ if (live_p)
+ lra_clear_live_ranges ();
+ live_p = false;
+ if (! lra_split_hard_reg_for ())
+ break;
+ lra_hard_reg_split_p = true;
}
- if (! live_p)
- lra_clear_live_ranges ();
}
+ while (fails_p);
+ if (! live_p) {
+ /* We need the correct reg notes for work of constraint sub-pass. */
+ lra_create_live_ranges (true, true);
+ live_p = true;
+ }
}
/* Don't clear optional reloads bitmap until all constraints are
satisfied as we need to differ them from regular reloads. */
lra_create_live_ranges (lra_reg_spill_p, true);
live_p = true;
if (! lra_need_for_spills_p ())
- break;
+ {
+ if (lra_need_for_scratch_reg_p ())
+ continue;
+ break;
+ }
}
lra_spill ();
/* Assignment of stack slots changes elimination offsets for
lra_bad_spill_regno_start = lra_constraint_new_regno_start;
lra_assignment_iter_after_spill = 0;
}
- restore_scratches ();
+ ira_restore_scratches (lra_dump_file);
lra_eliminate (true, false);
lra_final_code_change ();
lra_in_progress = 0;
/* We've possibly turned single trapping insn into multiple ones. */
if (cfun->can_throw_non_call_exceptions)
{
- sbitmap blocks;
- blocks = sbitmap_alloc (last_basic_block_for_fn (cfun));
+ auto_sbitmap blocks (last_basic_block_for_fn (cfun));
bitmap_ones (blocks);
find_many_sub_basic_blocks (blocks);
- sbitmap_free (blocks);
}
if (inserted_p)