/* Save and restore call-clobbered registers which are live across a call.
- Copyright (C) 1989-2015 Free Software Foundation, Inc.
+ Copyright (C) 1989-2020 Free Software Foundation, Inc.
This file is part of GCC.
#include "config.h"
#include "system.h"
#include "coretypes.h"
-#include "tm.h"
+#include "backend.h"
#include "rtl.h"
-#include "regs.h"
-#include "insn-config.h"
-#include "flags.h"
-#include "hard-reg-set.h"
-#include "recog.h"
+#include "tree.h"
#include "predict.h"
-#include "function.h"
-#include "dominance.h"
-#include "cfg.h"
-#include "basic-block.h"
#include "df.h"
+#include "memmodel.h"
+#include "tm_p.h"
+#include "insn-config.h"
+#include "regs.h"
+#include "emit-rtl.h"
+#include "recog.h"
#include "reload.h"
-#include "symtab.h"
#include "alias.h"
-#include "tree.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 "diagnostic-core.h"
-#include "tm_p.h"
#include "addresses.h"
#include "dumpfile.h"
#include "rtl-iter.h"
+#include "target.h"
+#include "function-abi.h"
#define MOVE_MAX_WORDS (MOVE_MAX / UNITS_PER_WORD)
static void mark_referenced_regs (rtx *, refmarker_fn *mark, void *mark_arg);
static refmarker_fn mark_reg_as_referenced;
static refmarker_fn replace_reg_with_saved_mem;
-static int insert_save (struct insn_chain *, int, int, HARD_REG_SET *,
+static int insert_save (class insn_chain *, int, HARD_REG_SET *,
machine_mode *);
-static int insert_restore (struct insn_chain *, int, int, int,
+static int insert_restore (class insn_chain *, int, int, int,
machine_mode *);
-static struct insn_chain *insert_one_insn (struct insn_chain *, int, int,
+static class insn_chain *insert_one_insn (class insn_chain *, int, int,
rtx);
static void add_stored_regs (rtx, const_rtx, void *);
bool ok;
if (cached_reg_save_code[reg][mode])
return cached_reg_save_code[reg][mode];
- if (!HARD_REGNO_MODE_OK (reg, mode))
+ if (!targetm.hard_regno_mode_ok (reg, mode))
{
- /* Depending on how HARD_REGNO_MODE_OK is defined, range propagation
- might deduce here that reg >= FIRST_PSEUDO_REGISTER. So the assert
- below silences a warning. */
+ /* Depending on how targetm.hard_regno_mode_ok is defined, range
+ propagation might deduce here that reg >= FIRST_PSEUDO_REGISTER.
+ So the assert below silences a warning. */
gcc_assert (reg < FIRST_PSEUDO_REGISTER);
cached_reg_save_code[reg][mode] = -1;
cached_reg_restore_code[reg][mode] = -1;
caller_save_initialized_p = true;
- CLEAR_HARD_REG_SET (no_caller_save_reg_set);
/* First find all the registers that we need to deal with and all
the modes that they can have. If we can't find a mode to use,
we can't have the register live over calls. */
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
- {
- if (call_used_regs[i]
- && !TEST_HARD_REG_BIT (call_fixed_reg_set, i))
- {
- for (j = 1; j <= MOVE_MAX_WORDS; j++)
- {
- regno_save_mode[i][j] = HARD_REGNO_CALLER_SAVE_MODE (i, j,
- VOIDmode);
- if (regno_save_mode[i][j] == VOIDmode && j == 1)
- {
- SET_HARD_REG_BIT (call_fixed_reg_set, i);
- }
- }
- }
- else
- regno_save_mode[i][1] = VOIDmode;
- }
+ for (j = 1; j <= MOVE_MAX_WORDS; j++)
+ {
+ regno_save_mode[i][j] = HARD_REGNO_CALLER_SAVE_MODE (i, j, VOIDmode);
+ if (regno_save_mode[i][j] == VOIDmode && j == 1)
+ CLEAR_HARD_REG_BIT (savable_regs, i);
+ }
/* The following code tries to approximate the conditions under which
we can easily save and restore a register without scratch registers or
{
regno_save_mode[i][j] = VOIDmode;
if (j == 1)
- {
- SET_HARD_REG_BIT (call_fixed_reg_set, i);
- if (call_used_regs[i])
- SET_HARD_REG_BIT (no_caller_save_reg_set, i);
- }
+ CLEAR_HARD_REG_BIT (savable_regs, i);
}
}
HARD_REG_SET hard_regs_used;
struct saved_hard_reg *saved_reg;
rtx_insn *insn;
- struct insn_chain *chain, *next;
+ class insn_chain *chain, *next;
unsigned int regno;
HARD_REG_SET hard_regs_to_save, used_regs, this_insn_sets;
reg_set_iterator rsi;
freq = REG_FREQ_FROM_BB (BLOCK_FOR_INSN (insn));
REG_SET_TO_HARD_REG_SET (hard_regs_to_save,
&chain->live_throughout);
- get_call_reg_set_usage (insn, &used_regs, call_used_reg_set);
+ used_regs = insn_callee_abi (insn).full_reg_clobbers ();
/* Record all registers set in this call insn. These don't
need to be saved. N.B. the call insn might set a subreg
live during the call, but the subreg that is set
isn't. */
CLEAR_HARD_REG_SET (this_insn_sets);
- note_stores (PATTERN (insn), mark_set_regs, &this_insn_sets);
+ note_stores (insn, mark_set_regs, &this_insn_sets);
/* Sibcalls are considered to set the return value. */
if (SIBLING_CALL_P (insn) && crtl->return_rtx)
mark_set_regs (crtl->return_rtx, NULL_RTX, &this_insn_sets);
- AND_COMPL_HARD_REG_SET (used_regs, call_fixed_reg_set);
- AND_COMPL_HARD_REG_SET (used_regs, this_insn_sets);
- AND_HARD_REG_SET (hard_regs_to_save, used_regs);
+ used_regs &= ~(fixed_reg_set | this_insn_sets);
+ hard_regs_to_save &= used_regs & savable_regs;
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
if (TEST_HARD_REG_BIT (hard_regs_to_save, regno))
{
if (r < 0 || regno_reg_rtx[regno] == cheap)
continue;
- bound = r + hard_regno_nregs[r][PSEUDO_REGNO_MODE (regno)];
+ bound = r + hard_regno_nregs (r, PSEUDO_REGNO_MODE (regno));
for (; r < bound; r++)
if (TEST_HARD_REG_BIT (used_regs, r))
{
REG_SET_TO_HARD_REG_SET (hard_regs_to_save,
&chain->live_throughout);
- get_call_reg_set_usage (insn, &used_regs, call_used_reg_set);
+ used_regs = insn_callee_abi (insn).full_reg_clobbers ();
/* Record all registers set in this call insn. These don't
need to be saved. N.B. the call insn might set a subreg
live during the call, but the subreg that is set
isn't. */
CLEAR_HARD_REG_SET (this_insn_sets);
- note_stores (PATTERN (insn), mark_set_regs, &this_insn_sets);
+ note_stores (insn, mark_set_regs, &this_insn_sets);
/* Sibcalls are considered to set the return value,
compare df-scan.c:df_get_call_refs. */
if (SIBLING_CALL_P (insn) && crtl->return_rtx)
mark_set_regs (crtl->return_rtx, NULL_RTX, &this_insn_sets);
- AND_COMPL_HARD_REG_SET (used_regs, call_fixed_reg_set);
- AND_COMPL_HARD_REG_SET (used_regs, this_insn_sets);
- AND_HARD_REG_SET (hard_regs_to_save, used_regs);
+ used_regs &= ~(fixed_reg_set | this_insn_sets);
+ hard_regs_to_save &= used_regs & savable_regs;
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
if (TEST_HARD_REG_BIT (hard_regs_to_save, regno))
{
if (r < 0 || regno_reg_rtx[regno] == cheap)
continue;
- bound = r + hard_regno_nregs[r][PSEUDO_REGNO_MODE (regno)];
+ bound = r + hard_regno_nregs (r, PSEUDO_REGNO_MODE (regno));
for (; r < bound; r++)
if (TEST_HARD_REG_BIT (used_regs, r))
call_saved_regs[call_saved_regs_num++] = hard_reg_map[r];
break;
}
if (k < 0
- && (GET_MODE_SIZE (regno_save_mode[regno][1])
- <= GET_MODE_SIZE (regno_save_mode
- [saved_reg2->hard_regno][1])))
+ && known_le (GET_MODE_SIZE (regno_save_mode[regno][1]),
+ GET_MODE_SIZE (regno_save_mode
+ [saved_reg2->hard_regno][1])))
{
saved_reg->slot
= adjust_address_nv
slot = prev_save_slots[j];
if (slot == NULL_RTX)
continue;
- if (GET_MODE_SIZE (regno_save_mode[regno][1])
- <= GET_MODE_SIZE (GET_MODE (slot))
+ if (known_le (GET_MODE_SIZE (regno_save_mode[regno][1]),
+ GET_MODE_SIZE (GET_MODE (slot)))
&& best_slot_num < 0)
best_slot_num = j;
if (GET_MODE (slot) == regno_save_mode[regno][1])
void
save_call_clobbered_regs (void)
{
- struct insn_chain *chain, *next, *last = NULL;
+ class insn_chain *chain, *next, *last = NULL;
machine_mode save_mode [FIRST_PSEUDO_REGISTER];
/* Computed in mark_set_regs, holds all registers set by the current
if (code == JUMP_INSN)
/* Restore all registers if this is a JUMP_INSN. */
- COPY_HARD_REG_SET (referenced_regs, hard_regs_saved);
+ referenced_regs = hard_regs_saved;
else
{
CLEAR_HARD_REG_SET (referenced_regs);
mark_referenced_regs (&PATTERN (insn),
mark_reg_as_referenced, NULL);
- AND_HARD_REG_SET (referenced_regs, hard_regs_saved);
+ referenced_regs &= hard_regs_saved;
}
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
be live across the call, while the other is set
afterwards. */
CLEAR_HARD_REG_SET (this_insn_sets);
- note_stores (PATTERN (insn), mark_set_regs, &this_insn_sets);
- AND_COMPL_HARD_REG_SET (hard_regs_saved, this_insn_sets);
+ note_stores (insn, mark_set_regs, &this_insn_sets);
+ hard_regs_saved &= ~this_insn_sets;
}
if (code == CALL_INSN
{
unsigned regno;
HARD_REG_SET hard_regs_to_save;
- HARD_REG_SET call_def_reg_set;
reg_set_iterator rsi;
rtx cheap;
if (r < 0 || regno_reg_rtx[regno] == cheap)
continue;
- nregs = hard_regno_nregs[r][PSEUDO_REGNO_MODE (regno)];
+ nregs = hard_regno_nregs (r, PSEUDO_REGNO_MODE (regno));
mode = HARD_REGNO_CALLER_SAVE_MODE
(r, nregs, PSEUDO_REGNO_MODE (regno));
- if (GET_MODE_BITSIZE (mode)
- > GET_MODE_BITSIZE (save_mode[r]))
+ if (partial_subreg_p (save_mode[r], mode))
save_mode[r] = mode;
while (nregs-- > 0)
SET_HARD_REG_BIT (hard_regs_to_save, r + nregs);
multi-hard-reg pseudo; then the pseudo is considered live
during the call, but the subreg that is set isn't. */
CLEAR_HARD_REG_SET (this_insn_sets);
- note_stores (PATTERN (insn), mark_set_regs, &this_insn_sets);
+ note_stores (insn, mark_set_regs, &this_insn_sets);
/* Compute which hard regs must be saved before this call. */
- AND_COMPL_HARD_REG_SET (hard_regs_to_save, call_fixed_reg_set);
- AND_COMPL_HARD_REG_SET (hard_regs_to_save, this_insn_sets);
- AND_COMPL_HARD_REG_SET (hard_regs_to_save, hard_regs_saved);
- get_call_reg_set_usage (insn, &call_def_reg_set,
- call_used_reg_set);
- AND_HARD_REG_SET (hard_regs_to_save, call_def_reg_set);
+ function_abi callee_abi = insn_callee_abi (insn);
+ hard_regs_to_save &= ~(fixed_reg_set
+ | this_insn_sets
+ | hard_regs_saved);
+ hard_regs_to_save &= savable_regs;
+ hard_regs_to_save &= callee_abi.full_reg_clobbers ();
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
if (TEST_HARD_REG_BIT (hard_regs_to_save, regno))
- regno += insert_save (chain, 1, regno, &hard_regs_to_save, save_mode);
+ regno += insert_save (chain, regno,
+ &hard_regs_to_save, save_mode);
/* Must recompute n_regs_saved. */
n_regs_saved = 0;
if (cheap
&& HARD_REGISTER_P (cheap)
- && TEST_HARD_REG_BIT (call_used_reg_set, REGNO (cheap)))
+ && callee_abi.clobbers_reg_p (GET_MODE (cheap),
+ REGNO (cheap)))
{
rtx dest, newpat;
rtx pat = PATTERN (insn);
/* If we're setting only part of a multi-word register,
we shall mark it as referenced, because the words
that are not being set should be restored. */
- && ((GET_MODE_SIZE (GET_MODE (*loc))
- >= GET_MODE_SIZE (GET_MODE (SUBREG_REG (*loc))))
- || (GET_MODE_SIZE (GET_MODE (SUBREG_REG (*loc)))
- <= UNITS_PER_WORD))))
+ && !read_modify_subreg_p (*loc)))
return;
}
if (code == MEM || code == SUBREG)
int regno,
void *arg)
{
- unsigned int i, nregs = hard_regno_nregs [regno][mode];
+ unsigned int i, nregs = hard_regno_nregs (regno, mode);
rtx mem;
machine_mode *save_mode = (machine_mode *)arg;
{
mem = copy_rtx (regno_save_mem[regno][nregs]);
- if (nregs == (unsigned int) hard_regno_nregs[regno][save_mode[regno]])
+ if (nregs == hard_regno_nregs (regno, save_mode[regno]))
mem = adjust_address_nv (mem, save_mode[regno], 0);
if (GET_MODE (mem) != mode)
{
/* This is gen_lowpart_if_possible(), but without validating
the newly-formed address. */
- int offset = 0;
-
- if (WORDS_BIG_ENDIAN)
- offset = (MAX (GET_MODE_SIZE (GET_MODE (mem)), UNITS_PER_WORD)
- - MAX (GET_MODE_SIZE (mode), UNITS_PER_WORD));
- if (BYTES_BIG_ENDIAN)
- /* Adjust the address so that the address-after-the-data is
- unchanged. */
- offset -= (MIN (UNITS_PER_WORD, GET_MODE_SIZE (mode))
- - MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (mem))));
-
+ poly_int64 offset = byte_lowpart_offset (mode, GET_MODE (mem));
mem = adjust_address_nv (mem, mode, offset);
}
}
{
machine_mode smode = save_mode[regno];
gcc_assert (smode != VOIDmode);
- if (hard_regno_nregs [regno][smode] > 1)
- smode = mode_for_size (GET_MODE_SIZE (mode) / nregs,
- GET_MODE_CLASS (mode), 0);
+ if (hard_regno_nregs (regno, smode) > 1)
+ smode = mode_for_size (exact_div (GET_MODE_BITSIZE (mode),
+ nregs),
+ GET_MODE_CLASS (mode), 0).require ();
XVECEXP (mem, 0, i) = gen_rtx_REG (smode, regno + i);
}
}
Return the extra number of registers saved. */
static int
-insert_restore (struct insn_chain *chain, int before_p, int regno,
+insert_restore (class insn_chain *chain, int before_p, int regno,
int maxrestore, machine_mode *save_mode)
{
int i, k;
rtx pat = NULL_RTX;
int code;
unsigned int numregs = 0;
- struct insn_chain *new_chain;
+ class insn_chain *new_chain;
rtx mem;
/* A common failure mode if register status is not correct in the
mem = regno_save_mem [regno][numregs];
if (save_mode [regno] != VOIDmode
&& save_mode [regno] != GET_MODE (mem)
- && numregs == (unsigned int) hard_regno_nregs[regno][save_mode [regno]]
+ && numregs == hard_regno_nregs (regno, save_mode [regno])
/* Check that insn to restore REGNO in save_mode[regno] is
correct. */
&& reg_save_code (regno, save_mode[regno]) >= 0)
/* Like insert_restore above, but save registers instead. */
static int
-insert_save (struct insn_chain *chain, int before_p, int regno,
- HARD_REG_SET (*to_save), machine_mode *save_mode)
+insert_save (class insn_chain *chain, int regno,
+ HARD_REG_SET *to_save, machine_mode *save_mode)
{
int i;
unsigned int k;
rtx pat = NULL_RTX;
int code;
unsigned int numregs = 0;
- struct insn_chain *new_chain;
+ class insn_chain *new_chain;
rtx mem;
/* A common failure mode if register status is not correct in the
mem = regno_save_mem [regno][numregs];
if (save_mode [regno] != VOIDmode
&& save_mode [regno] != GET_MODE (mem)
- && numregs == (unsigned int) hard_regno_nregs[regno][save_mode [regno]]
+ && numregs == hard_regno_nregs (regno, save_mode [regno])
/* Check that insn to save REGNO in save_mode[regno] is
correct. */
&& reg_save_code (regno, save_mode[regno]) >= 0)
pat = gen_rtx_SET (mem, gen_rtx_REG (GET_MODE (mem), regno));
code = reg_save_code (regno, GET_MODE (mem));
- new_chain = insert_one_insn (chain, before_p, code, pat);
+ new_chain = insert_one_insn (chain, 1, code, pat);
/* Set hard_regs_saved and dead_or_set for all the registers we saved. */
for (k = 0; k < numregs; k++)
{
unsigned int regno = REGNO (x);
if (HARD_REGISTER_NUM_P (regno))
- bitmap_set_range ((regset) data, regno,
- hard_regno_nregs[regno][GET_MODE (x)]);
+ bitmap_set_range ((regset) data, regno, REG_NREGS (x));
else
gcc_checking_assert (reg_renumber[regno] < 0);
}
}
/* Emit a new caller-save insn and set the code. */
-static struct insn_chain *
-insert_one_insn (struct insn_chain *chain, int before_p, int code, rtx pat)
+static class insn_chain *
+insert_one_insn (class insn_chain *chain, int before_p, int code, rtx pat)
{
rtx_insn *insn = chain->insn;
- struct insn_chain *new_chain;
+ class insn_chain *new_chain;
/* If INSN references CC0, put our insns in front of the insn that sets
CC0. This is always safe, since the only way we could be passed an
/* Registers that are set in CHAIN->INSN live in the new insn.
(Unless there is a REG_UNUSED note for them, but we don't
look for them here.) */
- note_stores (PATTERN (chain->insn), add_stored_regs,
- &new_chain->live_throughout);
+ note_stores (chain->insn, add_stored_regs, &new_chain->live_throughout);
CLEAR_REG_SET (&new_chain->dead_or_set);
if (chain->insn == BB_END (BASIC_BLOCK_FOR_FN (cfun, chain->block)))
BB_END (BASIC_BLOCK_FOR_FN (cfun, chain->block)) = new_chain->insn;