/* Perform simple optimizations to clean up the result of reload.
- Copyright (C) 1987-2016 Free Software Foundation, Inc.
+ Copyright (C) 1987-2020 Free Software Foundation, Inc.
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 "cselib.h"
#include "tree-pass.h"
#include "dbgcnt.h"
-
-#ifndef LOAD_EXTEND_OP
-#define LOAD_EXTEND_OP(M) UNKNOWN
-#endif
+#include "function-abi.h"
+#include "rtl-iter.h"
static int reload_cse_noop_set_p (rtx);
static bool reload_cse_simplify (rtx_insn *, rtx);
basic_block insn_bb = BLOCK_FOR_INSN (insn);
unsigned insn_bb_succs = EDGE_COUNT (insn_bb->succs);
+ /* If NO_FUNCTION_CSE has been set by the target, then we should not try
+ to cse function calls. */
+ if (NO_FUNCTION_CSE && CALL_P (insn))
+ return false;
+
+ /* Remember if this insn has been sp += const_int. */
+ rtx sp_set = set_for_reg_notes (insn);
+ rtx sp_addend = NULL_RTX;
+ if (sp_set
+ && SET_DEST (sp_set) == stack_pointer_rtx
+ && GET_CODE (SET_SRC (sp_set)) == PLUS
+ && XEXP (SET_SRC (sp_set), 0) == stack_pointer_rtx
+ && CONST_INT_P (XEXP (SET_SRC (sp_set), 1)))
+ sp_addend = XEXP (SET_SRC (sp_set), 1);
+
if (GET_CODE (body) == SET)
{
int count = 0;
value = SET_DEST (part);
}
}
- else if (GET_CODE (part) != CLOBBER
- && GET_CODE (part) != USE)
+ else if (GET_CODE (part) != CLOBBER && GET_CODE (part) != USE)
break;
}
reload_cse_simplify_operands (insn, testreg);
}
+ /* If sp += const_int insn is changed into sp = reg;, add REG_EQUAL
+ note so that the stack_adjustments pass can undo it if beneficial. */
+ if (sp_addend
+ && SET_DEST (sp_set) == stack_pointer_rtx
+ && REG_P (SET_SRC (sp_set)))
+ set_dst_reg_note (insn, REG_EQUAL,
+ gen_rtx_PLUS (Pmode, stack_pointer_rtx,
+ sp_addend), stack_pointer_rtx);
+
done:
return (EDGE_COUNT (insn_bb->succs) != insn_bb_succs);
}
generating an extend instruction instead of a reg->reg copy. Thus
the destination must be a register that we can widen. */
if (MEM_P (src)
- && GET_MODE_BITSIZE (GET_MODE (src)) < BITS_PER_WORD
- && (extend_op = LOAD_EXTEND_OP (GET_MODE (src))) != UNKNOWN
+ && (extend_op = load_extend_op (GET_MODE (src))) != UNKNOWN
&& !REG_P (SET_DEST (set)))
return 0;
switch (extend_op)
{
case ZERO_EXTEND:
- result = wide_int::from (std::make_pair (this_rtx,
- GET_MODE (src)),
+ result = wide_int::from (rtx_mode_t (this_rtx,
+ GET_MODE (src)),
BITS_PER_WORD, UNSIGNED);
break;
case SIGN_EXTEND:
- result = wide_int::from (std::make_pair (this_rtx,
- GET_MODE (src)),
+ result = wide_int::from (rtx_mode_t (this_rtx,
+ GET_MODE (src)),
BITS_PER_WORD, SIGNED);
break;
default:
&& REG_P (this_rtx)
&& !REG_P (SET_SRC (set))))
{
- if (GET_MODE_BITSIZE (GET_MODE (SET_DEST (set))) < BITS_PER_WORD
- && extend_op != UNKNOWN
-#ifdef CANNOT_CHANGE_MODE_CLASS
- && !CANNOT_CHANGE_MODE_CLASS (GET_MODE (SET_DEST (set)),
- word_mode,
- REGNO_REG_CLASS (REGNO (SET_DEST (set))))
-#endif
- )
+ if (extend_op != UNKNOWN
+ && REG_CAN_CHANGE_MODE_P (REGNO (SET_DEST (set)),
+ GET_MODE (SET_DEST (set)), word_mode))
{
rtx wide_dest = gen_rtx_REG (word_mode, REGNO (SET_DEST (set)));
ORIGINAL_REGNO (wide_dest) = ORIGINAL_REGNO (SET_DEST (set));
CLEAR_HARD_REG_SET (equiv_regs[i]);
/* cselib blows up on CODE_LABELs. Trying to fix that doesn't seem
- right, so avoid the problem here. Likewise if we have a constant
- and the insn pattern doesn't tell us the mode we need. */
+ right, so avoid the problem here. Similarly NOTE_INSN_DELETED_LABEL.
+ Likewise if we have a constant and the insn pattern doesn't tell us
+ the mode we need. */
if (LABEL_P (recog_data.operand[i])
+ || (NOTE_P (recog_data.operand[i])
+ && NOTE_KIND (recog_data.operand[i]) == NOTE_INSN_DELETED_LABEL)
|| (CONSTANT_P (recog_data.operand[i])
&& recog_data.operand_mode[i] == VOIDmode))
continue;
op = recog_data.operand[i];
- if (MEM_P (op)
- && GET_MODE_BITSIZE (GET_MODE (op)) < BITS_PER_WORD
- && LOAD_EXTEND_OP (GET_MODE (op)) != UNKNOWN)
+ if (MEM_P (op) && load_extend_op (GET_MODE (op)) != UNKNOWN)
{
rtx set = single_set (insn);
|| GET_CODE (SET_SRC (set)) == ZERO_EXTEND
|| GET_CODE (SET_SRC (set)) == SIGN_EXTEND)
; /* Continue ordinary processing. */
-#ifdef CANNOT_CHANGE_MODE_CLASS
/* If the register cannot change mode to word_mode, it follows that
it cannot have been used in word_mode. */
else if (REG_P (SET_DEST (set))
- && CANNOT_CHANGE_MODE_CLASS (GET_MODE (SET_DEST (set)),
- word_mode,
- REGNO_REG_CLASS (REGNO (SET_DEST (set)))))
+ && !REG_CAN_CHANGE_MODE_P (REGNO (SET_DEST (set)),
+ GET_MODE (SET_DEST (set)),
+ word_mode))
; /* Continue ordinary processing. */
-#endif
/* If this is a straight load, make the extension explicit. */
else if (REG_P (SET_DEST (set))
&& recog_data.n_operands == 2
&& SET_DEST (set) == recog_data.operand[1-i])
{
validate_change (insn, recog_data.operand_loc[i],
- gen_rtx_fmt_e (LOAD_EXTEND_OP (GET_MODE (op)),
+ gen_rtx_fmt_e (load_extend_op (GET_MODE (op)),
word_mode, op),
1);
validate_change (insn, recog_data.operand_loc[1-i],
STORE_RUID is always meaningful if we only want to use a value in a
register in a different place: it denotes the next insn in the insn
stream (i.e. the last encountered) that sets or clobbers the register.
- REAL_STORE_RUID is similar, but clobbers are ignored when updating it. */
+ REAL_STORE_RUID is similar, but clobbers are ignored when updating it.
+ EXPR is the expression used when storing the register. */
static struct
{
struct reg_use reg_use[RELOAD_COMBINE_MAX_USES];
int real_store_ruid;
int use_ruid;
bool all_offsets_match;
+ rtx expr;
} reg_state[FIRST_PSEUDO_REGISTER];
/* Reverse linear uid. This is increased in reload_combine while scanning
{
rtx t;
- if (!DEBUG_INSN_P (insn))
+ if (!DEBUG_BIND_INSN_P (insn))
continue;
t = INSN_VAR_LOCATION_LOC (insn);
struct reg_use *use = reg_state[regno].reg_use + i;
if (GET_MODE (*use->usep) != mode)
return false;
+ /* Don't try to adjust (use (REGX)). */
+ if (GET_CODE (PATTERN (use->insn)) == USE
+ && &XEXP (PATTERN (use->insn), 0) == use->usep)
+ return false;
}
/* Look for (set (REGX) (CONST_INT))
if (TEST_HARD_REG_BIT (reg_class_contents[INDEX_REG_CLASS], i)
&& reg_state[i].use_index == RELOAD_COMBINE_MAX_USES
&& reg_state[i].store_ruid <= reg_state[regno].use_ruid
- && (call_used_regs[i] || df_regs_ever_live_p (i))
+ && (crtl->abi->clobbers_full_reg_p (i)
+ || df_regs_ever_live_p (i))
&& (!frame_pointer_needed || i != HARD_FRAME_POINTER_REGNUM)
&& !fixed_regs[i] && !global_regs[i]
- && hard_regno_nregs[i][GET_MODE (reg)] == 1
+ && hard_regno_nregs (i, GET_MODE (reg)) == 1
&& targetm.hard_regno_scratch_ok (i))
{
index_reg = gen_rtx_REG (GET_MODE (reg), i);
value in PREV, the constant loading instruction. */
validate_change (prev, &SET_DEST (prev_set), index_reg, 1);
if (reg_state[regno].offset != const0_rtx)
- validate_change (prev,
- &SET_SRC (prev_set),
- GEN_INT (INTVAL (SET_SRC (prev_set))
- + INTVAL (reg_state[regno].offset)),
- 1);
+ {
+ HOST_WIDE_INT c
+ = trunc_int_for_mode (UINTVAL (SET_SRC (prev_set))
+ + UINTVAL (reg_state[regno].offset),
+ GET_MODE (index_reg));
+ validate_change (prev, &SET_SRC (prev_set), GEN_INT (c), 1);
+ }
/* Now for every use of REG that we have recorded, replace REG
with REG_SUM. */
REG_SET_TO_HARD_REG_SET (live, live_in);
compute_use_by_pseudos (&live, live_in);
- COPY_HARD_REG_SET (LABEL_LIVE (insn), live);
- IOR_HARD_REG_SET (ever_live_at_start, live);
+ LABEL_LIVE (insn) = live;
+ ever_live_at_start |= live;
}
}
|| reload_combine_recognize_pattern (insn))
continue;
- note_stores (PATTERN (insn), reload_combine_note_store, NULL);
+ note_stores (insn, reload_combine_note_store, NULL);
if (CALL_P (insn))
{
rtx link;
- HARD_REG_SET used_regs;
-
- get_call_reg_set_usage (insn, &used_regs, call_used_reg_set);
+ HARD_REG_SET used_regs = insn_callee_abi (insn).full_reg_clobbers ();
for (r = 0; r < FIRST_PSEUDO_REGISTER; r++)
if (TEST_HARD_REG_BIT (used_regs, r))
{
rtx setuse = XEXP (link, 0);
rtx usage_rtx = XEXP (setuse, 0);
- if ((GET_CODE (setuse) == USE || GET_CODE (setuse) == CLOBBER)
- && REG_P (usage_rtx))
+
+ if (GET_CODE (setuse) == USE && REG_P (usage_rtx))
{
unsigned int end_regno = END_REGNO (usage_rtx);
for (unsigned int i = REGNO (usage_rtx); i < end_regno; ++i)
- if (GET_CODE (XEXP (link, 0)) == CLOBBER)
- {
- reg_state[i].use_index = RELOAD_COMBINE_MAX_USES;
- reg_state[i].store_ruid = reload_combine_ruid;
- }
- else
- reg_state[i].use_index = -1;
+ reg_state[i].use_index = -1;
}
}
}
if (GET_CODE (SET_DEST (set)) == ZERO_EXTRACT
|| GET_CODE (SET_DEST (set)) == STRICT_LOW_PART)
{
- for (i = hard_regno_nregs[regno][mode] - 1 + regno; i >= regno; i--)
+ for (i = end_hard_regno (mode, regno) - 1; i >= regno; i--)
{
reg_state[i].use_index = -1;
reg_state[i].store_ruid = reload_combine_ruid;
}
else
{
- for (i = hard_regno_nregs[regno][mode] - 1 + regno; i >= regno; i--)
+ for (i = end_hard_regno (mode, regno) - 1; i >= regno; i--)
{
reg_state[i].store_ruid = reload_combine_ruid;
if (GET_CODE (set) == SET)
/* Check if REGNO contains a valid value in MODE. */
static bool
-move2add_valid_value_p (int regno, machine_mode mode)
+move2add_valid_value_p (int regno, scalar_int_mode mode)
{
if (reg_set_luid[regno] <= move2add_last_label_luid)
return false;
if (mode != reg_mode[regno])
{
- if (!MODES_OK_FOR_MOVE2ADD (mode, reg_mode[regno]))
+ scalar_int_mode old_mode;
+ if (!is_a <scalar_int_mode> (reg_mode[regno], &old_mode)
+ || !MODES_OK_FOR_MOVE2ADD (mode, old_mode))
return false;
/* The value loaded into regno in reg_mode[regno] is also valid in
mode after truncation only if (REG:mode regno) is the lowpart of
(REG:reg_mode[regno] regno). Now, for big endian, the starting
regno of the lowpart might be different. */
- int s_off = subreg_lowpart_offset (mode, reg_mode[regno]);
- s_off = subreg_regno_offset (regno, reg_mode[regno], s_off, mode);
- if (s_off != 0)
+ poly_int64 s_off = subreg_lowpart_offset (mode, old_mode);
+ s_off = subreg_regno_offset (regno, old_mode, s_off, mode);
+ if (maybe_ne (s_off, 0))
/* We could in principle adjust regno, check reg_mode[regno] to be
BLKmode, and return s_off to the caller (vs. -1 for failure),
but we currently have no callers that could make use of this
return false;
}
- for (int i = hard_regno_nregs[regno][mode] - 1; i > 0; i--)
- if (reg_mode[regno + i] != BLKmode)
+ for (int i = end_hard_regno (mode, regno) - 1; i > regno; i--)
+ if (reg_mode[i] != BLKmode)
return false;
return true;
}
-/* This function is called with INSN that sets REG to (SYM + OFF),
- while REG is known to already have value (SYM + offset).
+/* This function is called with INSN that sets REG (of mode MODE)
+ to (SYM + OFF), while REG is known to already have value (SYM + offset).
This function tries to change INSN into an add instruction
(set (REG) (plus (REG) (OFF - offset))) using the known value.
It also updates the information about REG's known value.
Return true if we made a change. */
static bool
-move2add_use_add2_insn (rtx reg, rtx sym, rtx off, rtx_insn *insn)
+move2add_use_add2_insn (scalar_int_mode mode, rtx reg, rtx sym, rtx off,
+ rtx_insn *insn)
{
rtx pat = PATTERN (insn);
rtx src = SET_SRC (pat);
int regno = REGNO (reg);
- rtx new_src = gen_int_mode (UINTVAL (off) - reg_offset[regno],
- GET_MODE (reg));
+ rtx new_src = gen_int_mode (UINTVAL (off) - reg_offset[regno], mode);
bool speed = optimize_bb_for_speed_p (BLOCK_FOR_INSN (insn));
bool changed = false;
else
{
struct full_rtx_costs oldcst, newcst;
- rtx tem = gen_rtx_PLUS (GET_MODE (reg), reg, new_src);
+ rtx tem = gen_rtx_PLUS (mode, reg, new_src);
get_full_set_rtx_cost (pat, &oldcst);
SET_SRC (pat) = tem;
if (costs_lt_p (&newcst, &oldcst, speed)
&& have_add2_insn (reg, new_src))
changed = validate_change (insn, &SET_SRC (pat), tem, 0);
- else if (sym == NULL_RTX && GET_MODE (reg) != BImode)
+ else if (sym == NULL_RTX && mode != BImode)
{
- machine_mode narrow_mode;
- for (narrow_mode = GET_CLASS_NARROWEST_MODE (MODE_INT);
- narrow_mode != VOIDmode
- && narrow_mode != GET_MODE (reg);
- narrow_mode = GET_MODE_WIDER_MODE (narrow_mode))
+ scalar_int_mode narrow_mode;
+ FOR_EACH_MODE_UNTIL (narrow_mode, mode)
{
if (have_insn_for (STRICT_LOW_PART, narrow_mode)
&& ((reg_offset[regno] & ~GET_MODE_MASK (narrow_mode))
}
-/* This function is called with INSN that sets REG to (SYM + OFF),
- but REG doesn't have known value (SYM + offset). This function
- tries to find another register which is known to already have
+/* This function is called with INSN that sets REG (of mode MODE) to
+ (SYM + OFF), but REG doesn't have known value (SYM + offset). This
+ function tries to find another register which is known to already have
value (SYM + offset) and change INSN into an add instruction
(set (REG) (plus (the found register) (OFF - offset))) if such
a register is found. It also updates the information about
Return true iff we made a change. */
static bool
-move2add_use_add3_insn (rtx reg, rtx sym, rtx off, rtx_insn *insn)
+move2add_use_add3_insn (scalar_int_mode mode, rtx reg, rtx sym, rtx off,
+ rtx_insn *insn)
{
rtx pat = PATTERN (insn);
rtx src = SET_SRC (pat);
SET_SRC (pat) = plus_expr;
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
- if (move2add_valid_value_p (i, GET_MODE (reg))
+ if (move2add_valid_value_p (i, mode)
&& reg_base_reg[i] < 0
&& reg_symbol_ref[i] != NULL_RTX
&& rtx_equal_p (sym, reg_symbol_ref[i]))
pat = PATTERN (insn);
/* For simplicity, we only perform this optimization on
straightforward SETs. */
+ scalar_int_mode mode;
if (GET_CODE (pat) == SET
- && REG_P (SET_DEST (pat)))
+ && REG_P (SET_DEST (pat))
+ && is_a <scalar_int_mode> (GET_MODE (SET_DEST (pat)), &mode))
{
rtx reg = SET_DEST (pat);
int regno = REGNO (reg);
/* Check if we have valid information on the contents of this
register in the mode of REG. */
- if (move2add_valid_value_p (regno, GET_MODE (reg))
+ if (move2add_valid_value_p (regno, mode)
&& dbg_cnt (cse2_move2add))
{
/* Try to transform (set (REGX) (CONST_INT A))
&& reg_base_reg[regno] < 0
&& reg_symbol_ref[regno] == NULL_RTX)
{
- changed |= move2add_use_add2_insn (reg, NULL_RTX, src, insn);
+ changed |= move2add_use_add2_insn (mode, reg, NULL_RTX,
+ src, insn);
continue;
}
else if (REG_P (src)
&& reg_set_luid[regno] == reg_set_luid[REGNO (src)]
&& reg_base_reg[regno] == reg_base_reg[REGNO (src)]
- && move2add_valid_value_p (REGNO (src), GET_MODE (reg)))
+ && move2add_valid_value_p (REGNO (src), mode))
{
rtx_insn *next = next_nonnote_nondebug_insn (insn);
rtx set = NULL_RTX;
gen_int_mode (added_offset
+ base_offset
- regno_offset,
- GET_MODE (reg));
+ mode);
bool success = false;
bool speed = optimize_bb_for_speed_p (BLOCK_FOR_INSN (insn));
{
rtx old_src = SET_SRC (set);
struct full_rtx_costs oldcst, newcst;
- rtx tem = gen_rtx_PLUS (GET_MODE (reg), reg, new_src);
+ rtx tem = gen_rtx_PLUS (mode, reg, new_src);
get_full_set_rtx_cost (set, &oldcst);
SET_SRC (set) = tem;
- get_full_set_src_cost (tem, GET_MODE (reg), &newcst);
+ get_full_set_src_cost (tem, mode, &newcst);
SET_SRC (set) = old_src;
costs_add_n_insns (&oldcst, 1);
move2add_record_mode (reg);
reg_offset[regno]
= trunc_int_for_mode (added_offset + base_offset,
- GET_MODE (reg));
+ mode);
continue;
}
}
/* If the reg already contains the value which is sum of
sym and some constant value, we can use an add2 insn. */
- if (move2add_valid_value_p (regno, GET_MODE (reg))
+ if (move2add_valid_value_p (regno, mode)
&& reg_base_reg[regno] < 0
&& reg_symbol_ref[regno] != NULL_RTX
&& rtx_equal_p (sym, reg_symbol_ref[regno]))
- changed |= move2add_use_add2_insn (reg, sym, off, insn);
+ changed |= move2add_use_add2_insn (mode, reg, sym, off, insn);
/* Otherwise, we have to find a register whose value is sum
of sym and some constant value. */
else
- changed |= move2add_use_add3_insn (reg, sym, off, insn);
+ changed |= move2add_use_add3_insn (mode, reg, sym, off, insn);
continue;
}
}
}
}
- note_stores (PATTERN (insn), move2add_note_store, insn);
+
+ /* There are no REG_INC notes for SP autoinc. */
+ subrtx_var_iterator::array_type array;
+ FOR_EACH_SUBRTX_VAR (iter, array, PATTERN (insn), NONCONST)
+ {
+ rtx mem = *iter;
+ if (mem
+ && MEM_P (mem)
+ && GET_RTX_CLASS (GET_CODE (XEXP (mem, 0))) == RTX_AUTOINC)
+ {
+ if (XEXP (XEXP (mem, 0), 0) == stack_pointer_rtx)
+ reg_mode[STACK_POINTER_REGNUM] = VOIDmode;
+ }
+ }
+
+ note_stores (insn, move2add_note_store, insn);
/* If INSN is a conditional branch, we try to extract an
implicit set out of it. */
unknown values. */
if (CALL_P (insn))
{
- rtx link;
-
+ function_abi callee_abi = insn_callee_abi (insn);
for (i = FIRST_PSEUDO_REGISTER - 1; i >= 0; i--)
- {
- if (call_used_regs[i])
- /* Reset the information about this register. */
- reg_mode[i] = VOIDmode;
- }
-
- for (link = CALL_INSN_FUNCTION_USAGE (insn); link;
- link = XEXP (link, 1))
- {
- rtx setuse = XEXP (link, 0);
- rtx usage_rtx = XEXP (setuse, 0);
- if (GET_CODE (setuse) == CLOBBER
- && REG_P (usage_rtx))
- {
- unsigned int end_regno = END_REGNO (usage_rtx);
- for (unsigned int r = REGNO (usage_rtx); r < end_regno; ++r)
- /* Reset the information about this register. */
- reg_mode[r] = VOIDmode;
- }
- }
+ if (reg_mode[i] != VOIDmode
+ && reg_mode[i] != BLKmode
+ && callee_abi.clobbers_reg_p (reg_mode[i], i))
+ /* Reset the information about this register. */
+ reg_mode[i] = VOIDmode;
}
}
return changed;
{
rtx_insn *insn = (rtx_insn *) data;
unsigned int regno = 0;
- machine_mode mode = GET_MODE (dst);
-
- /* Some targets do argument pushes without adding REG_INC notes. */
-
- if (MEM_P (dst))
- {
- dst = XEXP (dst, 0);
- if (GET_CODE (dst) == PRE_INC || GET_CODE (dst) == POST_INC
- || GET_CODE (dst) == PRE_DEC || GET_CODE (dst) == POST_DEC)
- reg_mode[REGNO (XEXP (dst, 0))] = VOIDmode;
- return;
- }
+ scalar_int_mode mode;
if (GET_CODE (dst) == SUBREG)
regno = subreg_regno (dst);
else
return;
- if (SCALAR_INT_MODE_P (mode)
- && GET_CODE (set) == SET)
+ if (!is_a <scalar_int_mode> (GET_MODE (dst), &mode))
+ goto invalidate;
+
+ if (GET_CODE (set) == SET)
{
rtx note, sym = NULL_RTX;
rtx off;
}
}
- if (SCALAR_INT_MODE_P (mode)
- && GET_CODE (set) == SET
+ if (GET_CODE (set) == SET
&& GET_CODE (SET_DEST (set)) != ZERO_EXTRACT
&& GET_CODE (SET_DEST (set)) != STRICT_LOW_PART)
{