blob: 62bf4aeaabae58b0a548f7cf295896d3cd9484df [file] [log] [blame]
/* Optimize by combining instructions for GNU compiler.
Copyright (C) 1987-2021 Free Software Foundation, Inc.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 3, or (at your option) any later
version.
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
/* This module is essentially the "combiner" phase of the U. of Arizona
Portable Optimizer, but redone to work on our list-structured
representation for RTL instead of their string representation.
The LOG_LINKS of each insn identify the most recent assignment
to each REG used in the insn. It is a list of previous insns,
each of which contains a SET for a REG that is used in this insn
and not used or set in between. LOG_LINKs never cross basic blocks.
They were set up by the preceding pass (lifetime analysis).
We try to combine each pair of insns joined by a logical link.
We also try to combine triplets of insns A, B and C when C has
a link back to B and B has a link back to A. Likewise for a
small number of quadruplets of insns A, B, C and D for which
there's high likelihood of success.
LOG_LINKS does not have links for use of the CC0. They don't
need to, because the insn that sets the CC0 is always immediately
before the insn that tests it. So we always regard a branch
insn as having a logical link to the preceding insn. The same is true
for an insn explicitly using CC0.
We check (with modified_between_p) to avoid combining in such a way
as to move a computation to a place where its value would be different.
Combination is done by mathematically substituting the previous
insn(s) values for the regs they set into the expressions in
the later insns that refer to these regs. If the result is a valid insn
for our target machine, according to the machine description,
we install it, delete the earlier insns, and update the data flow
information (LOG_LINKS and REG_NOTES) for what we did.
There are a few exceptions where the dataflow information isn't
completely updated (however this is only a local issue since it is
regenerated before the next pass that uses it):
- reg_live_length is not updated
- reg_n_refs is not adjusted in the rare case when a register is
no longer required in a computation
- there are extremely rare cases (see distribute_notes) when a
REG_DEAD note is lost
- a LOG_LINKS entry that refers to an insn with multiple SETs may be
removed because there is no way to know which register it was
linking
To simplify substitution, we combine only when the earlier insn(s)
consist of only a single assignment. To simplify updating afterward,
we never combine when a subroutine call appears in the middle.
Since we do not represent assignments to CC0 explicitly except when that
is all an insn does, there is no LOG_LINKS entry in an insn that uses
the condition code for the insn that set the condition code.
Fortunately, these two insns must be consecutive.
Therefore, every JUMP_INSN is taken to have an implicit logical link
to the preceding insn. This is not quite right, since non-jumps can
also use the condition code; but in practice such insns would not
combine anyway. */
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "backend.h"
#include "target.h"
#include "rtl.h"
#include "tree.h"
#include "cfghooks.h"
#include "predict.h"
#include "df.h"
#include "memmodel.h"
#include "tm_p.h"
#include "optabs.h"
#include "regs.h"
#include "emit-rtl.h"
#include "recog.h"
#include "cgraph.h"
#include "stor-layout.h"
#include "cfgrtl.h"
#include "cfgcleanup.h"
/* Include expr.h after insn-config.h so we get HAVE_conditional_move. */
#include "explow.h"
#include "insn-attr.h"
#include "rtlhooks-def.h"
#include "expr.h"
#include "tree-pass.h"
#include "valtrack.h"
#include "rtl-iter.h"
#include "print-rtl.h"
#include "function-abi.h"
/* Number of attempts to combine instructions in this function. */
static int combine_attempts;
/* Number of attempts that got as far as substitution in this function. */
static int combine_merges;
/* Number of instructions combined with added SETs in this function. */
static int combine_extras;
/* Number of instructions combined in this function. */
static int combine_successes;
/* Totals over entire compilation. */
static int total_attempts, total_merges, total_extras, total_successes;
/* combine_instructions may try to replace the right hand side of the
second instruction with the value of an associated REG_EQUAL note
before throwing it at try_combine. That is problematic when there
is a REG_DEAD note for a register used in the old right hand side
and can cause distribute_notes to do wrong things. This is the
second instruction if it has been so modified, null otherwise. */
static rtx_insn *i2mod;
/* When I2MOD is nonnull, this is a copy of the old right hand side. */
static rtx i2mod_old_rhs;
/* When I2MOD is nonnull, this is a copy of the new right hand side. */
static rtx i2mod_new_rhs;
struct reg_stat_type {
/* Record last point of death of (hard or pseudo) register n. */
rtx_insn *last_death;
/* Record last point of modification of (hard or pseudo) register n. */
rtx_insn *last_set;
/* The next group of fields allows the recording of the last value assigned
to (hard or pseudo) register n. We use this information to see if an
operation being processed is redundant given a prior operation performed
on the register. For example, an `and' with a constant is redundant if
all the zero bits are already known to be turned off.
We use an approach similar to that used by cse, but change it in the
following ways:
(1) We do not want to reinitialize at each label.
(2) It is useful, but not critical, to know the actual value assigned
to a register. Often just its form is helpful.
Therefore, we maintain the following fields:
last_set_value the last value assigned
last_set_label records the value of label_tick when the
register was assigned
last_set_table_tick records the value of label_tick when a
value using the register is assigned
last_set_invalid set to nonzero when it is not valid
to use the value of this register in some
register's value
To understand the usage of these tables, it is important to understand
the distinction between the value in last_set_value being valid and
the register being validly contained in some other expression in the
table.
(The next two parameters are out of date).
reg_stat[i].last_set_value is valid if it is nonzero, and either
reg_n_sets[i] is 1 or reg_stat[i].last_set_label == label_tick.
Register I may validly appear in any expression returned for the value
of another register if reg_n_sets[i] is 1. It may also appear in the
value for register J if reg_stat[j].last_set_invalid is zero, or
reg_stat[i].last_set_label < reg_stat[j].last_set_label.
If an expression is found in the table containing a register which may
not validly appear in an expression, the register is replaced by
something that won't match, (clobber (const_int 0)). */
/* Record last value assigned to (hard or pseudo) register n. */
rtx last_set_value;
/* Record the value of label_tick when an expression involving register n
is placed in last_set_value. */
int last_set_table_tick;
/* Record the value of label_tick when the value for register n is placed in
last_set_value. */
int last_set_label;
/* These fields are maintained in parallel with last_set_value and are
used to store the mode in which the register was last set, the bits
that were known to be zero when it was last set, and the number of
sign bits copies it was known to have when it was last set. */
unsigned HOST_WIDE_INT last_set_nonzero_bits;
char last_set_sign_bit_copies;
ENUM_BITFIELD(machine_mode) last_set_mode : 8;
/* Set nonzero if references to register n in expressions should not be
used. last_set_invalid is set nonzero when this register is being
assigned to and last_set_table_tick == label_tick. */
char last_set_invalid;
/* Some registers that are set more than once and used in more than one
basic block are nevertheless always set in similar ways. For example,
a QImode register may be loaded from memory in two places on a machine
where byte loads zero extend.
We record in the following fields if a register has some leading bits
that are always equal to the sign bit, and what we know about the
nonzero bits of a register, specifically which bits are known to be
zero.
If an entry is zero, it means that we don't know anything special. */
unsigned char sign_bit_copies;
unsigned HOST_WIDE_INT nonzero_bits;
/* Record the value of the label_tick when the last truncation
happened. The field truncated_to_mode is only valid if
truncation_label == label_tick. */
int truncation_label;
/* Record the last truncation seen for this register. If truncation
is not a nop to this mode we might be able to save an explicit
truncation if we know that value already contains a truncated
value. */
ENUM_BITFIELD(machine_mode) truncated_to_mode : 8;
};
static vec<reg_stat_type> reg_stat;
/* One plus the highest pseudo for which we track REG_N_SETS.
regstat_init_n_sets_and_refs allocates the array for REG_N_SETS just once,
but during combine_split_insns new pseudos can be created. As we don't have
updated DF information in that case, it is hard to initialize the array
after growing. The combiner only cares about REG_N_SETS (regno) == 1,
so instead of growing the arrays, just assume all newly created pseudos
during combine might be set multiple times. */
static unsigned int reg_n_sets_max;
/* Record the luid of the last insn that invalidated memory
(anything that writes memory, and subroutine calls, but not pushes). */
static int mem_last_set;
/* Record the luid of the last CALL_INSN
so we can tell whether a potential combination crosses any calls. */
static int last_call_luid;
/* When `subst' is called, this is the insn that is being modified
(by combining in a previous insn). The PATTERN of this insn
is still the old pattern partially modified and it should not be
looked at, but this may be used to examine the successors of the insn
to judge whether a simplification is valid. */
static rtx_insn *subst_insn;
/* This is the lowest LUID that `subst' is currently dealing with.
get_last_value will not return a value if the register was set at or
after this LUID. If not for this mechanism, we could get confused if
I2 or I1 in try_combine were an insn that used the old value of a register
to obtain a new value. In that case, we might erroneously get the
new value of the register when we wanted the old one. */
static int subst_low_luid;
/* This contains any hard registers that are used in newpat; reg_dead_at_p
must consider all these registers to be always live. */
static HARD_REG_SET newpat_used_regs;
/* This is an insn to which a LOG_LINKS entry has been added. If this
insn is the earlier than I2 or I3, combine should rescan starting at
that location. */
static rtx_insn *added_links_insn;
/* And similarly, for notes. */
static rtx_insn *added_notes_insn;
/* Basic block in which we are performing combines. */
static basic_block this_basic_block;
static bool optimize_this_for_speed_p;
/* Length of the currently allocated uid_insn_cost array. */
static int max_uid_known;
/* The following array records the insn_cost for every insn
in the instruction stream. */
static int *uid_insn_cost;
/* The following array records the LOG_LINKS for every insn in the
instruction stream as struct insn_link pointers. */
struct insn_link {
rtx_insn *insn;
unsigned int regno;
struct insn_link *next;
};
static struct insn_link **uid_log_links;
static inline int
insn_uid_check (const_rtx insn)
{
int uid = INSN_UID (insn);
gcc_checking_assert (uid <= max_uid_known);
return uid;
}
#define INSN_COST(INSN) (uid_insn_cost[insn_uid_check (INSN)])
#define LOG_LINKS(INSN) (uid_log_links[insn_uid_check (INSN)])
#define FOR_EACH_LOG_LINK(L, INSN) \
for ((L) = LOG_LINKS (INSN); (L); (L) = (L)->next)
/* Links for LOG_LINKS are allocated from this obstack. */
static struct obstack insn_link_obstack;
/* Allocate a link. */
static inline struct insn_link *
alloc_insn_link (rtx_insn *insn, unsigned int regno, struct insn_link *next)
{
struct insn_link *l
= (struct insn_link *) obstack_alloc (&insn_link_obstack,
sizeof (struct insn_link));
l->insn = insn;
l->regno = regno;
l->next = next;
return l;
}
/* Incremented for each basic block. */
static int label_tick;
/* Reset to label_tick for each extended basic block in scanning order. */
static int label_tick_ebb_start;
/* Mode used to compute significance in reg_stat[].nonzero_bits. It is the
largest integer mode that can fit in HOST_BITS_PER_WIDE_INT. */
static scalar_int_mode nonzero_bits_mode;
/* Nonzero when reg_stat[].nonzero_bits and reg_stat[].sign_bit_copies can
be safely used. It is zero while computing them and after combine has
completed. This former test prevents propagating values based on
previously set values, which can be incorrect if a variable is modified
in a loop. */
static int nonzero_sign_valid;
/* Record one modification to rtl structure
to be undone by storing old_contents into *where. */
enum undo_kind { UNDO_RTX, UNDO_INT, UNDO_MODE, UNDO_LINKS };
struct undo
{
struct undo *next;
enum undo_kind kind;
union { rtx r; int i; machine_mode m; struct insn_link *l; } old_contents;
union { rtx *r; int *i; struct insn_link **l; } where;
};
/* Record a bunch of changes to be undone, up to MAX_UNDO of them.
num_undo says how many are currently recorded.
other_insn is nonzero if we have modified some other insn in the process
of working on subst_insn. It must be verified too. */
struct undobuf
{
struct undo *undos;
struct undo *frees;
rtx_insn *other_insn;
};
static struct undobuf undobuf;
/* Number of times the pseudo being substituted for
was found and replaced. */
static int n_occurrences;
static rtx reg_nonzero_bits_for_combine (const_rtx, scalar_int_mode,
scalar_int_mode,
unsigned HOST_WIDE_INT *);
static rtx reg_num_sign_bit_copies_for_combine (const_rtx, scalar_int_mode,
scalar_int_mode,
unsigned int *);
static void do_SUBST (rtx *, rtx);
static void do_SUBST_INT (int *, int);
static void init_reg_last (void);
static void setup_incoming_promotions (rtx_insn *);
static void set_nonzero_bits_and_sign_copies (rtx, const_rtx, void *);
static int cant_combine_insn_p (rtx_insn *);
static int can_combine_p (rtx_insn *, rtx_insn *, rtx_insn *, rtx_insn *,
rtx_insn *, rtx_insn *, rtx *, rtx *);
static int combinable_i3pat (rtx_insn *, rtx *, rtx, rtx, rtx, int, int, rtx *);
static int contains_muldiv (rtx);
static rtx_insn *try_combine (rtx_insn *, rtx_insn *, rtx_insn *, rtx_insn *,
int *, rtx_insn *);
static void undo_all (void);
static void undo_commit (void);
static rtx *find_split_point (rtx *, rtx_insn *, bool);
static rtx subst (rtx, rtx, rtx, int, int, int);
static rtx combine_simplify_rtx (rtx, machine_mode, int, int);
static rtx simplify_if_then_else (rtx);
static rtx simplify_set (rtx);
static rtx simplify_logical (rtx);
static rtx expand_compound_operation (rtx);
static const_rtx expand_field_assignment (const_rtx);
static rtx make_extraction (machine_mode, rtx, HOST_WIDE_INT,
rtx, unsigned HOST_WIDE_INT, int, int, int);
static int get_pos_from_mask (unsigned HOST_WIDE_INT,
unsigned HOST_WIDE_INT *);
static rtx canon_reg_for_combine (rtx, rtx);
static rtx force_int_to_mode (rtx, scalar_int_mode, scalar_int_mode,
scalar_int_mode, unsigned HOST_WIDE_INT, int);
static rtx force_to_mode (rtx, machine_mode,
unsigned HOST_WIDE_INT, int);
static rtx if_then_else_cond (rtx, rtx *, rtx *);
static rtx known_cond (rtx, enum rtx_code, rtx, rtx);
static int rtx_equal_for_field_assignment_p (rtx, rtx, bool = false);
static rtx make_field_assignment (rtx);
static rtx apply_distributive_law (rtx);
static rtx distribute_and_simplify_rtx (rtx, int);
static rtx simplify_and_const_int_1 (scalar_int_mode, rtx,
unsigned HOST_WIDE_INT);
static rtx simplify_and_const_int (rtx, scalar_int_mode, rtx,
unsigned HOST_WIDE_INT);
static int merge_outer_ops (enum rtx_code *, HOST_WIDE_INT *, enum rtx_code,
HOST_WIDE_INT, machine_mode, int *);
static rtx simplify_shift_const_1 (enum rtx_code, machine_mode, rtx, int);
static rtx simplify_shift_const (rtx, enum rtx_code, machine_mode, rtx,
int);
static int recog_for_combine (rtx *, rtx_insn *, rtx *);
static rtx gen_lowpart_for_combine (machine_mode, rtx);
static enum rtx_code simplify_compare_const (enum rtx_code, machine_mode,
rtx, rtx *);
static enum rtx_code simplify_comparison (enum rtx_code, rtx *, rtx *);
static void update_table_tick (rtx);
static void record_value_for_reg (rtx, rtx_insn *, rtx);
static void check_promoted_subreg (rtx_insn *, rtx);
static void record_dead_and_set_regs_1 (rtx, const_rtx, void *);
static void record_dead_and_set_regs (rtx_insn *);
static int get_last_value_validate (rtx *, rtx_insn *, int, int);
static rtx get_last_value (const_rtx);
static void reg_dead_at_p_1 (rtx, const_rtx, void *);
static int reg_dead_at_p (rtx, rtx_insn *);
static void move_deaths (rtx, rtx, int, rtx_insn *, rtx *);
static int reg_bitfield_target_p (rtx, rtx);
static void distribute_notes (rtx, rtx_insn *, rtx_insn *, rtx_insn *, rtx, rtx, rtx);
static void distribute_links (struct insn_link *);
static void mark_used_regs_combine (rtx);
static void record_promoted_value (rtx_insn *, rtx);
static bool unmentioned_reg_p (rtx, rtx);
static void record_truncated_values (rtx *, void *);
static bool reg_truncated_to_mode (machine_mode, const_rtx);
static rtx gen_lowpart_or_truncate (machine_mode, rtx);
/* It is not safe to use ordinary gen_lowpart in combine.
See comments in gen_lowpart_for_combine. */
#undef RTL_HOOKS_GEN_LOWPART
#define RTL_HOOKS_GEN_LOWPART gen_lowpart_for_combine
/* Our implementation of gen_lowpart never emits a new pseudo. */
#undef RTL_HOOKS_GEN_LOWPART_NO_EMIT
#define RTL_HOOKS_GEN_LOWPART_NO_EMIT gen_lowpart_for_combine
#undef RTL_HOOKS_REG_NONZERO_REG_BITS
#define RTL_HOOKS_REG_NONZERO_REG_BITS reg_nonzero_bits_for_combine
#undef RTL_HOOKS_REG_NUM_SIGN_BIT_COPIES
#define RTL_HOOKS_REG_NUM_SIGN_BIT_COPIES reg_num_sign_bit_copies_for_combine
#undef RTL_HOOKS_REG_TRUNCATED_TO_MODE
#define RTL_HOOKS_REG_TRUNCATED_TO_MODE reg_truncated_to_mode
static const struct rtl_hooks combine_rtl_hooks = RTL_HOOKS_INITIALIZER;
/* Convenience wrapper for the canonicalize_comparison target hook.
Target hooks cannot use enum rtx_code. */
static inline void
target_canonicalize_comparison (enum rtx_code *code, rtx *op0, rtx *op1,
bool op0_preserve_value)
{
int code_int = (int)*code;
targetm.canonicalize_comparison (&code_int, op0, op1, op0_preserve_value);
*code = (enum rtx_code)code_int;
}
/* Try to split PATTERN found in INSN. This returns NULL_RTX if
PATTERN cannot be split. Otherwise, it returns an insn sequence.
This is a wrapper around split_insns which ensures that the
reg_stat vector is made larger if the splitter creates a new
register. */
static rtx_insn *
combine_split_insns (rtx pattern, rtx_insn *insn)
{
rtx_insn *ret;
unsigned int nregs;
ret = split_insns (pattern, insn);
nregs = max_reg_num ();
if (nregs > reg_stat.length ())
reg_stat.safe_grow_cleared (nregs, true);
return ret;
}
/* This is used by find_single_use to locate an rtx in LOC that
contains exactly one use of DEST, which is typically either a REG
or CC0. It returns a pointer to the innermost rtx expression
containing DEST. Appearances of DEST that are being used to
totally replace it are not counted. */
static rtx *
find_single_use_1 (rtx dest, rtx *loc)
{
rtx x = *loc;
enum rtx_code code = GET_CODE (x);
rtx *result = NULL;
rtx *this_result;
int i;
const char *fmt;
switch (code)
{
case CONST:
case LABEL_REF:
case SYMBOL_REF:
CASE_CONST_ANY:
case CLOBBER:
return 0;
case SET:
/* If the destination is anything other than CC0, PC, a REG or a SUBREG
of a REG that occupies all of the REG, the insn uses DEST if
it is mentioned in the destination or the source. Otherwise, we
need just check the source. */
if (GET_CODE (SET_DEST (x)) != CC0
&& GET_CODE (SET_DEST (x)) != PC
&& !REG_P (SET_DEST (x))
&& ! (GET_CODE (SET_DEST (x)) == SUBREG
&& REG_P (SUBREG_REG (SET_DEST (x)))
&& !read_modify_subreg_p (SET_DEST (x))))
break;
return find_single_use_1 (dest, &SET_SRC (x));
case MEM:
case SUBREG:
return find_single_use_1 (dest, &XEXP (x, 0));
default:
break;
}
/* If it wasn't one of the common cases above, check each expression and
vector of this code. Look for a unique usage of DEST. */
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
{
if (dest == XEXP (x, i)
|| (REG_P (dest) && REG_P (XEXP (x, i))
&& REGNO (dest) == REGNO (XEXP (x, i))))
this_result = loc;
else
this_result = find_single_use_1 (dest, &XEXP (x, i));
if (result == NULL)
result = this_result;
else if (this_result)
/* Duplicate usage. */
return NULL;
}
else if (fmt[i] == 'E')
{
int j;
for (j = XVECLEN (x, i) - 1; j >= 0; j--)
{
if (XVECEXP (x, i, j) == dest
|| (REG_P (dest)
&& REG_P (XVECEXP (x, i, j))
&& REGNO (XVECEXP (x, i, j)) == REGNO (dest)))
this_result = loc;
else
this_result = find_single_use_1 (dest, &XVECEXP (x, i, j));
if (result == NULL)
result = this_result;
else if (this_result)
return NULL;
}
}
}
return result;
}
/* See if DEST, produced in INSN, is used only a single time in the
sequel. If so, return a pointer to the innermost rtx expression in which
it is used.
If PLOC is nonzero, *PLOC is set to the insn containing the single use.
If DEST is cc0_rtx, we look only at the next insn. In that case, we don't
care about REG_DEAD notes or LOG_LINKS.
Otherwise, we find the single use by finding an insn that has a
LOG_LINKS pointing at INSN and has a REG_DEAD note for DEST. If DEST is
only referenced once in that insn, we know that it must be the first
and last insn referencing DEST. */
static rtx *
find_single_use (rtx dest, rtx_insn *insn, rtx_insn **ploc)
{
basic_block bb;
rtx_insn *next;
rtx *result;
struct insn_link *link;
if (dest == cc0_rtx)
{
next = NEXT_INSN (insn);
if (next == 0
|| (!NONJUMP_INSN_P (next) && !JUMP_P (next)))
return 0;
result = find_single_use_1 (dest, &PATTERN (next));
if (result && ploc)
*ploc = next;
return result;
}
if (!REG_P (dest))
return 0;
bb = BLOCK_FOR_INSN (insn);
for (next = NEXT_INSN (insn);
next && BLOCK_FOR_INSN (next) == bb;
next = NEXT_INSN (next))
if (NONDEBUG_INSN_P (next) && dead_or_set_p (next, dest))
{
FOR_EACH_LOG_LINK (link, next)
if (link->insn == insn && link->regno == REGNO (dest))
break;
if (link)
{
result = find_single_use_1 (dest, &PATTERN (next));
if (ploc)
*ploc = next;
return result;
}
}
return 0;
}
/* Substitute NEWVAL, an rtx expression, into INTO, a place in some
insn. The substitution can be undone by undo_all. If INTO is already
set to NEWVAL, do not record this change. Because computing NEWVAL might
also call SUBST, we have to compute it before we put anything into
the undo table. */
static void
do_SUBST (rtx *into, rtx newval)
{
struct undo *buf;
rtx oldval = *into;
if (oldval == newval)
return;
/* We'd like to catch as many invalid transformations here as
possible. Unfortunately, there are way too many mode changes
that are perfectly valid, so we'd waste too much effort for
little gain doing the checks here. Focus on catching invalid
transformations involving integer constants. */
if (GET_MODE_CLASS (GET_MODE (oldval)) == MODE_INT
&& CONST_INT_P (newval))
{
/* Sanity check that we're replacing oldval with a CONST_INT
that is a valid sign-extension for the original mode. */
gcc_assert (INTVAL (newval)
== trunc_int_for_mode (INTVAL (newval), GET_MODE (oldval)));
/* Replacing the operand of a SUBREG or a ZERO_EXTEND with a
CONST_INT is not valid, because after the replacement, the
original mode would be gone. Unfortunately, we can't tell
when do_SUBST is called to replace the operand thereof, so we
perform this test on oldval instead, checking whether an
invalid replacement took place before we got here. */
gcc_assert (!(GET_CODE (oldval) == SUBREG
&& CONST_INT_P (SUBREG_REG (oldval))));
gcc_assert (!(GET_CODE (oldval) == ZERO_EXTEND
&& CONST_INT_P (XEXP (oldval, 0))));
}
if (undobuf.frees)
buf = undobuf.frees, undobuf.frees = buf->next;
else
buf = XNEW (struct undo);
buf->kind = UNDO_RTX;
buf->where.r = into;
buf->old_contents.r = oldval;
*into = newval;
buf->next = undobuf.undos, undobuf.undos = buf;
}
#define SUBST(INTO, NEWVAL) do_SUBST (&(INTO), (NEWVAL))
/* Similar to SUBST, but NEWVAL is an int expression. Note that substitution
for the value of a HOST_WIDE_INT value (including CONST_INT) is
not safe. */
static void
do_SUBST_INT (int *into, int newval)
{
struct undo *buf;
int oldval = *into;
if (oldval == newval)
return;
if (undobuf.frees)
buf = undobuf.frees, undobuf.frees = buf->next;
else
buf = XNEW (struct undo);
buf->kind = UNDO_INT;
buf->where.i = into;
buf->old_contents.i = oldval;
*into = newval;
buf->next = undobuf.undos, undobuf.undos = buf;
}
#define SUBST_INT(INTO, NEWVAL) do_SUBST_INT (&(INTO), (NEWVAL))
/* Similar to SUBST, but just substitute the mode. This is used when
changing the mode of a pseudo-register, so that any other
references to the entry in the regno_reg_rtx array will change as
well. */
static void
do_SUBST_MODE (rtx *into, machine_mode newval)
{
struct undo *buf;
machine_mode oldval = GET_MODE (*into);
if (oldval == newval)
return;
if (undobuf.frees)
buf = undobuf.frees, undobuf.frees = buf->next;
else
buf = XNEW (struct undo);
buf->kind = UNDO_MODE;
buf->where.r = into;
buf->old_contents.m = oldval;
adjust_reg_mode (*into, newval);
buf->next = undobuf.undos, undobuf.undos = buf;
}
#define SUBST_MODE(INTO, NEWVAL) do_SUBST_MODE (&(INTO), (NEWVAL))
/* Similar to SUBST, but NEWVAL is a LOG_LINKS expression. */
static void
do_SUBST_LINK (struct insn_link **into, struct insn_link *newval)
{
struct undo *buf;
struct insn_link * oldval = *into;
if (oldval == newval)
return;
if (undobuf.frees)
buf = undobuf.frees, undobuf.frees = buf->next;
else
buf = XNEW (struct undo);
buf->kind = UNDO_LINKS;
buf->where.l = into;
buf->old_contents.l = oldval;
*into = newval;
buf->next = undobuf.undos, undobuf.undos = buf;
}
#define SUBST_LINK(oldval, newval) do_SUBST_LINK (&oldval, newval)
/* Subroutine of try_combine. Determine whether the replacement patterns
NEWPAT, NEWI2PAT and NEWOTHERPAT are cheaper according to insn_cost
than the original sequence I0, I1, I2, I3 and undobuf.other_insn. Note
that I0, I1 and/or NEWI2PAT may be NULL_RTX. Similarly, NEWOTHERPAT and
undobuf.other_insn may also both be NULL_RTX. Return false if the cost
of all the instructions can be estimated and the replacements are more
expensive than the original sequence. */
static bool
combine_validate_cost (rtx_insn *i0, rtx_insn *i1, rtx_insn *i2, rtx_insn *i3,
rtx newpat, rtx newi2pat, rtx newotherpat)
{
int i0_cost, i1_cost, i2_cost, i3_cost;
int new_i2_cost, new_i3_cost;
int old_cost, new_cost;
/* Lookup the original insn_costs. */
i2_cost = INSN_COST (i2);
i3_cost = INSN_COST (i3);
if (i1)
{
i1_cost = INSN_COST (i1);
if (i0)
{
i0_cost = INSN_COST (i0);
old_cost = (i0_cost > 0 && i1_cost > 0 && i2_cost > 0 && i3_cost > 0
? i0_cost + i1_cost + i2_cost + i3_cost : 0);
}
else
{
old_cost = (i1_cost > 0 && i2_cost > 0 && i3_cost > 0
? i1_cost + i2_cost + i3_cost : 0);
i0_cost = 0;
}
}
else
{
old_cost = (i2_cost > 0 && i3_cost > 0) ? i2_cost + i3_cost : 0;
i1_cost = i0_cost = 0;
}
/* If we have split a PARALLEL I2 to I1,I2, we have counted its cost twice;
correct that. */
if (old_cost && i1 && INSN_UID (i1) == INSN_UID (i2))
old_cost -= i1_cost;
/* Calculate the replacement insn_costs. */
rtx tmp = PATTERN (i3);
PATTERN (i3) = newpat;
int tmpi = INSN_CODE (i3);
INSN_CODE (i3) = -1;
new_i3_cost = insn_cost (i3, optimize_this_for_speed_p);
PATTERN (i3) = tmp;
INSN_CODE (i3) = tmpi;
if (newi2pat)
{
tmp = PATTERN (i2);
PATTERN (i2) = newi2pat;
tmpi = INSN_CODE (i2);
INSN_CODE (i2) = -1;
new_i2_cost = insn_cost (i2, optimize_this_for_speed_p);
PATTERN (i2) = tmp;
INSN_CODE (i2) = tmpi;
new_cost = (new_i2_cost > 0 && new_i3_cost > 0)
? new_i2_cost + new_i3_cost : 0;
}
else
{
new_cost = new_i3_cost;
new_i2_cost = 0;
}
if (undobuf.other_insn)
{
int old_other_cost, new_other_cost;
old_other_cost = INSN_COST (undobuf.other_insn);
tmp = PATTERN (undobuf.other_insn);
PATTERN (undobuf.other_insn) = newotherpat;
tmpi = INSN_CODE (undobuf.other_insn);
INSN_CODE (undobuf.other_insn) = -1;
new_other_cost = insn_cost (undobuf.other_insn,
optimize_this_for_speed_p);
PATTERN (undobuf.other_insn) = tmp;
INSN_CODE (undobuf.other_insn) = tmpi;
if (old_other_cost > 0 && new_other_cost > 0)
{
old_cost += old_other_cost;
new_cost += new_other_cost;
}
else
old_cost = 0;
}
/* Disallow this combination if both new_cost and old_cost are greater than
zero, and new_cost is greater than old cost. */
int reject = old_cost > 0 && new_cost > old_cost;
if (dump_file)
{
fprintf (dump_file, "%s combination of insns ",
reject ? "rejecting" : "allowing");
if (i0)
fprintf (dump_file, "%d, ", INSN_UID (i0));
if (i1 && INSN_UID (i1) != INSN_UID (i2))
fprintf (dump_file, "%d, ", INSN_UID (i1));
fprintf (dump_file, "%d and %d\n", INSN_UID (i2), INSN_UID (i3));
fprintf (dump_file, "original costs ");
if (i0)
fprintf (dump_file, "%d + ", i0_cost);
if (i1 && INSN_UID (i1) != INSN_UID (i2))
fprintf (dump_file, "%d + ", i1_cost);
fprintf (dump_file, "%d + %d = %d\n", i2_cost, i3_cost, old_cost);
if (newi2pat)
fprintf (dump_file, "replacement costs %d + %d = %d\n",
new_i2_cost, new_i3_cost, new_cost);
else
fprintf (dump_file, "replacement cost %d\n", new_cost);
}
if (reject)
return false;
/* Update the uid_insn_cost array with the replacement costs. */
INSN_COST (i2) = new_i2_cost;
INSN_COST (i3) = new_i3_cost;
if (i1)
{
INSN_COST (i1) = 0;
if (i0)
INSN_COST (i0) = 0;
}
return true;
}
/* Delete any insns that copy a register to itself.
Return true if the CFG was changed. */
static bool
delete_noop_moves (void)
{
rtx_insn *insn, *next;
basic_block bb;
bool edges_deleted = false;
FOR_EACH_BB_FN (bb, cfun)
{
for (insn = BB_HEAD (bb); insn != NEXT_INSN (BB_END (bb)); insn = next)
{
next = NEXT_INSN (insn);
if (INSN_P (insn) && noop_move_p (insn))
{
if (dump_file)
fprintf (dump_file, "deleting noop move %d\n", INSN_UID (insn));
edges_deleted |= delete_insn_and_edges (insn);
}
}
}
return edges_deleted;
}
/* Return false if we do not want to (or cannot) combine DEF. */
static bool
can_combine_def_p (df_ref def)
{
/* Do not consider if it is pre/post modification in MEM. */
if (DF_REF_FLAGS (def) & DF_REF_PRE_POST_MODIFY)
return false;
unsigned int regno = DF_REF_REGNO (def);
/* Do not combine frame pointer adjustments. */
if ((regno == FRAME_POINTER_REGNUM
&& (!reload_completed || frame_pointer_needed))
|| (!HARD_FRAME_POINTER_IS_FRAME_POINTER
&& regno == HARD_FRAME_POINTER_REGNUM
&& (!reload_completed || frame_pointer_needed))
|| (FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
&& regno == ARG_POINTER_REGNUM && fixed_regs[regno]))
return false;
return true;
}
/* Return false if we do not want to (or cannot) combine USE. */
static bool
can_combine_use_p (df_ref use)
{
/* Do not consider the usage of the stack pointer by function call. */
if (DF_REF_FLAGS (use) & DF_REF_CALL_STACK_USAGE)
return false;
return true;
}
/* Fill in log links field for all insns. */
static void
create_log_links (void)
{
basic_block bb;
rtx_insn **next_use;
rtx_insn *insn;
df_ref def, use;
next_use = XCNEWVEC (rtx_insn *, max_reg_num ());
/* Pass through each block from the end, recording the uses of each
register and establishing log links when def is encountered.
Note that we do not clear next_use array in order to save time,
so we have to test whether the use is in the same basic block as def.
There are a few cases below when we do not consider the definition or
usage -- these are taken from original flow.c did. Don't ask me why it is
done this way; I don't know and if it works, I don't want to know. */
FOR_EACH_BB_FN (bb, cfun)
{
FOR_BB_INSNS_REVERSE (bb, insn)
{
if (!NONDEBUG_INSN_P (insn))
continue;
/* Log links are created only once. */
gcc_assert (!LOG_LINKS (insn));
FOR_EACH_INSN_DEF (def, insn)
{
unsigned int regno = DF_REF_REGNO (def);
rtx_insn *use_insn;
if (!next_use[regno])
continue;
if (!can_combine_def_p (def))
continue;
use_insn = next_use[regno];
next_use[regno] = NULL;
if (BLOCK_FOR_INSN (use_insn) != bb)
continue;
/* flow.c claimed:
We don't build a LOG_LINK for hard registers contained
in ASM_OPERANDs. If these registers get replaced,
we might wind up changing the semantics of the insn,
even if reload can make what appear to be valid
assignments later. */
if (regno < FIRST_PSEUDO_REGISTER
&& asm_noperands (PATTERN (use_insn)) >= 0)
continue;
/* Don't add duplicate links between instructions. */
struct insn_link *links;
FOR_EACH_LOG_LINK (links, use_insn)
if (insn == links->insn && regno == links->regno)
break;
if (!links)
LOG_LINKS (use_insn)
= alloc_insn_link (insn, regno, LOG_LINKS (use_insn));
}
FOR_EACH_INSN_USE (use, insn)
if (can_combine_use_p (use))
next_use[DF_REF_REGNO (use)] = insn;
}
}
free (next_use);
}
/* Walk the LOG_LINKS of insn B to see if we find a reference to A. Return
true if we found a LOG_LINK that proves that A feeds B. This only works
if there are no instructions between A and B which could have a link
depending on A, since in that case we would not record a link for B.
We also check the implicit dependency created by a cc0 setter/user
pair. */
static bool
insn_a_feeds_b (rtx_insn *a, rtx_insn *b)
{
struct insn_link *links;
FOR_EACH_LOG_LINK (links, b)
if (links->insn == a)
return true;
if (HAVE_cc0 && sets_cc0_p (a))
return true;
return false;
}
/* Main entry point for combiner. F is the first insn of the function.
NREGS is the first unused pseudo-reg number.
Return nonzero if the CFG was changed (e.g. if the combiner has
turned an indirect jump instruction into a direct jump). */
static int
combine_instructions (rtx_insn *f, unsigned int nregs)
{
rtx_insn *insn, *next;
rtx_insn *prev;
struct insn_link *links, *nextlinks;
rtx_insn *first;
basic_block last_bb;
int new_direct_jump_p = 0;
for (first = f; first && !NONDEBUG_INSN_P (first); )
first = NEXT_INSN (first);
if (!first)
return 0;
combine_attempts = 0;
combine_merges = 0;
combine_extras = 0;
combine_successes = 0;
rtl_hooks = combine_rtl_hooks;
reg_stat.safe_grow_cleared (nregs, true);
init_recog_no_volatile ();
/* Allocate array for insn info. */
max_uid_known = get_max_uid ();
uid_log_links = XCNEWVEC (struct insn_link *, max_uid_known + 1);
uid_insn_cost = XCNEWVEC (int, max_uid_known + 1);
gcc_obstack_init (&insn_link_obstack);
nonzero_bits_mode = int_mode_for_size (HOST_BITS_PER_WIDE_INT, 0).require ();
/* Don't use reg_stat[].nonzero_bits when computing it. This can cause
problems when, for example, we have j <<= 1 in a loop. */
nonzero_sign_valid = 0;
label_tick = label_tick_ebb_start = 1;
/* Scan all SETs and see if we can deduce anything about what
bits are known to be zero for some registers and how many copies
of the sign bit are known to exist for those registers.
Also set any known values so that we can use it while searching
for what bits are known to be set. */
setup_incoming_promotions (first);
/* Allow the entry block and the first block to fall into the same EBB.
Conceptually the incoming promotions are assigned to the entry block. */
last_bb = ENTRY_BLOCK_PTR_FOR_FN (cfun);
create_log_links ();
FOR_EACH_BB_FN (this_basic_block, cfun)
{
optimize_this_for_speed_p = optimize_bb_for_speed_p (this_basic_block);
last_call_luid = 0;
mem_last_set = -1;
label_tick++;
if (!single_pred_p (this_basic_block)
|| single_pred (this_basic_block) != last_bb)
label_tick_ebb_start = label_tick;
last_bb = this_basic_block;
FOR_BB_INSNS (this_basic_block, insn)
if (INSN_P (insn) && BLOCK_FOR_INSN (insn))
{
rtx links;
subst_low_luid = DF_INSN_LUID (insn);
subst_insn = insn;
note_stores (insn, set_nonzero_bits_and_sign_copies, insn);
record_dead_and_set_regs (insn);
if (AUTO_INC_DEC)
for (links = REG_NOTES (insn); links; links = XEXP (links, 1))
if (REG_NOTE_KIND (links) == REG_INC)
set_nonzero_bits_and_sign_copies (XEXP (links, 0), NULL_RTX,
insn);
/* Record the current insn_cost of this instruction. */
INSN_COST (insn) = insn_cost (insn, optimize_this_for_speed_p);
if (dump_file)
{
fprintf (dump_file, "insn_cost %d for ", INSN_COST (insn));
dump_insn_slim (dump_file, insn);
}
}
}
nonzero_sign_valid = 1;
/* Now scan all the insns in forward order. */
label_tick = label_tick_ebb_start = 1;
init_reg_last ();
setup_incoming_promotions (first);
last_bb = ENTRY_BLOCK_PTR_FOR_FN (cfun);
int max_combine = param_max_combine_insns;
FOR_EACH_BB_FN (this_basic_block, cfun)
{
rtx_insn *last_combined_insn = NULL;
/* Ignore instruction combination in basic blocks that are going to
be removed as unreachable anyway. See PR82386. */
if (EDGE_COUNT (this_basic_block->preds) == 0)
continue;
optimize_this_for_speed_p = optimize_bb_for_speed_p (this_basic_block);
last_call_luid = 0;
mem_last_set = -1;
label_tick++;
if (!single_pred_p (this_basic_block)
|| single_pred (this_basic_block) != last_bb)
label_tick_ebb_start = label_tick;
last_bb = this_basic_block;
rtl_profile_for_bb (this_basic_block);
for (insn = BB_HEAD (this_basic_block);
insn != NEXT_INSN (BB_END (this_basic_block));
insn = next ? next : NEXT_INSN (insn))
{
next = 0;
if (!NONDEBUG_INSN_P (insn))
continue;
while (last_combined_insn
&& (!NONDEBUG_INSN_P (last_combined_insn)
|| last_combined_insn->deleted ()))
last_combined_insn = PREV_INSN (last_combined_insn);
if (last_combined_insn == NULL_RTX
|| BLOCK_FOR_INSN (last_combined_insn) != this_basic_block
|| DF_INSN_LUID (last_combined_insn) <= DF_INSN_LUID (insn))
last_combined_insn = insn;
/* See if we know about function return values before this
insn based upon SUBREG flags. */
check_promoted_subreg (insn, PATTERN (insn));
/* See if we can find hardregs and subreg of pseudos in
narrower modes. This could help turning TRUNCATEs
into SUBREGs. */
note_uses (&PATTERN (insn), record_truncated_values, NULL);
/* Try this insn with each insn it links back to. */
FOR_EACH_LOG_LINK (links, insn)
if ((next = try_combine (insn, links->insn, NULL,
NULL, &new_direct_jump_p,
last_combined_insn)) != 0)
{
statistics_counter_event (cfun, "two-insn combine", 1);
goto retry;
}
/* Try each sequence of three linked insns ending with this one. */
if (max_combine >= 3)
FOR_EACH_LOG_LINK (links, insn)
{
rtx_insn *link = links->insn;
/* If the linked insn has been replaced by a note, then there
is no point in pursuing this chain any further. */
if (NOTE_P (link))
continue;
FOR_EACH_LOG_LINK (nextlinks, link)
if ((next = try_combine (insn, link, nextlinks->insn,
NULL, &new_direct_jump_p,
last_combined_insn)) != 0)
{
statistics_counter_event (cfun, "three-insn combine", 1);
goto retry;
}
}
/* Try to combine a jump insn that uses CC0
with a preceding insn that sets CC0, and maybe with its
logical predecessor as well.
This is how we make decrement-and-branch insns.
We need this special code because data flow connections
via CC0 do not get entered in LOG_LINKS. */
if (HAVE_cc0
&& JUMP_P (insn)
&& (prev = prev_nonnote_insn (insn)) != 0
&& NONJUMP_INSN_P (prev)
&& sets_cc0_p (PATTERN (prev)))
{
if ((next = try_combine (insn, prev, NULL, NULL,
&new_direct_jump_p,
last_combined_insn)) != 0)
goto retry;
FOR_EACH_LOG_LINK (nextlinks, prev)
if ((next = try_combine (insn, prev, nextlinks->insn,
NULL, &new_direct_jump_p,
last_combined_insn)) != 0)
goto retry;
}
/* Do the same for an insn that explicitly references CC0. */
if (HAVE_cc0 && NONJUMP_INSN_P (insn)
&& (prev = prev_nonnote_insn (insn)) != 0
&& NONJUMP_INSN_P (prev)
&& sets_cc0_p (PATTERN (prev))
&& GET_CODE (PATTERN (insn)) == SET
&& reg_mentioned_p (cc0_rtx, SET_SRC (PATTERN (insn))))
{
if ((next = try_combine (insn, prev, NULL, NULL,
&new_direct_jump_p,
last_combined_insn)) != 0)
goto retry;
FOR_EACH_LOG_LINK (nextlinks, prev)
if ((next = try_combine (insn, prev, nextlinks->insn,
NULL, &new_direct_jump_p,
last_combined_insn)) != 0)
goto retry;
}
/* Finally, see if any of the insns that this insn links to
explicitly references CC0. If so, try this insn, that insn,
and its predecessor if it sets CC0. */
if (HAVE_cc0)
{
FOR_EACH_LOG_LINK (links, insn)
if (NONJUMP_INSN_P (links->insn)
&& GET_CODE (PATTERN (links->insn)) == SET
&& reg_mentioned_p (cc0_rtx, SET_SRC (PATTERN (links->insn)))
&& (prev = prev_nonnote_insn (links->insn)) != 0
&& NONJUMP_INSN_P (prev)
&& sets_cc0_p (PATTERN (prev))
&& (next = try_combine (insn, links->insn,
prev, NULL, &new_direct_jump_p,
last_combined_insn)) != 0)
goto retry;
}
/* Try combining an insn with two different insns whose results it
uses. */
if (max_combine >= 3)
FOR_EACH_LOG_LINK (links, insn)
for (nextlinks = links->next; nextlinks;
nextlinks = nextlinks->next)
if ((next = try_combine (insn, links->insn,
nextlinks->insn, NULL,
&new_direct_jump_p,
last_combined_insn)) != 0)
{
statistics_counter_event (cfun, "three-insn combine", 1);
goto retry;
}
/* Try four-instruction combinations. */
if (max_combine >= 4)
FOR_EACH_LOG_LINK (links, insn)
{
struct insn_link *next1;
rtx_insn *link = links->insn;
/* If the linked insn has been replaced by a note, then there
is no point in pursuing this chain any further. */
if (NOTE_P (link))
continue;
FOR_EACH_LOG_LINK (next1, link)
{
rtx_insn *link1 = next1->insn;
if (NOTE_P (link1))
continue;
/* I0 -> I1 -> I2 -> I3. */
FOR_EACH_LOG_LINK (nextlinks, link1)
if ((next = try_combine (insn, link, link1,
nextlinks->insn,
&new_direct_jump_p,
last_combined_insn)) != 0)
{
statistics_counter_event (cfun, "four-insn combine", 1);
goto retry;
}
/* I0, I1 -> I2, I2 -> I3. */
for (nextlinks = next1->next; nextlinks;
nextlinks = nextlinks->next)
if ((next = try_combine (insn, link, link1,
nextlinks->insn,
&new_direct_jump_p,
last_combined_insn)) != 0)
{
statistics_counter_event (cfun, "four-insn combine", 1);
goto retry;
}
}
for (next1 = links->next; next1; next1 = next1->next)
{
rtx_insn *link1 = next1->insn;
if (NOTE_P (link1))
continue;
/* I0 -> I2; I1, I2 -> I3. */
FOR_EACH_LOG_LINK (nextlinks, link)
if ((next = try_combine (insn, link, link1,
nextlinks->insn,
&new_direct_jump_p,
last_combined_insn)) != 0)
{
statistics_counter_event (cfun, "four-insn combine", 1);
goto retry;
}
/* I0 -> I1; I1, I2 -> I3. */
FOR_EACH_LOG_LINK (nextlinks, link1)
if ((next = try_combine (insn, link, link1,
nextlinks->insn,
&new_direct_jump_p,
last_combined_insn)) != 0)
{
statistics_counter_event (cfun, "four-insn combine", 1);
goto retry;
}
}
}
/* Try this insn with each REG_EQUAL note it links back to. */
FOR_EACH_LOG_LINK (links, insn)
{
rtx set, note;
rtx_insn *temp = links->insn;
if ((set = single_set (temp)) != 0
&& (note = find_reg_equal_equiv_note (temp)) != 0
&& (note = XEXP (note, 0), GET_CODE (note)) != EXPR_LIST
&& ! side_effects_p (SET_SRC (set))
/* Avoid using a register that may already been marked
dead by an earlier instruction. */
&& ! unmentioned_reg_p (note, SET_SRC (set))
&& (GET_MODE (note) == VOIDmode
? SCALAR_INT_MODE_P (GET_MODE (SET_DEST (set)))
: (GET_MODE (SET_DEST (set)) == GET_MODE (note)
&& (GET_CODE (SET_DEST (set)) != ZERO_EXTRACT
|| (GET_MODE (XEXP (SET_DEST (set), 0))
== GET_MODE (note))))))
{
/* Temporarily replace the set's source with the
contents of the REG_EQUAL note. The insn will
be deleted or recognized by try_combine. */
rtx orig_src = SET_SRC (set);
rtx orig_dest = SET_DEST (set);
if (GET_CODE (SET_DEST (set)) == ZERO_EXTRACT)
SET_DEST (set) = XEXP (SET_DEST (set), 0);
SET_SRC (set) = note;
i2mod = temp;
i2mod_old_rhs = copy_rtx (orig_src);
i2mod_new_rhs = copy_rtx (note);
next = try_combine (insn, i2mod, NULL, NULL,
&new_direct_jump_p,
last_combined_insn);
i2mod = NULL;
if (next)
{
statistics_counter_event (cfun, "insn-with-note combine", 1);
goto retry;
}
SET_SRC (set) = orig_src;
SET_DEST (set) = orig_dest;
}
}
if (!NOTE_P (insn))
record_dead_and_set_regs (insn);
retry:
;
}
}
default_rtl_profile ();
clear_bb_flags ();
new_direct_jump_p |= purge_all_dead_edges ();
new_direct_jump_p |= delete_noop_moves ();
/* Clean up. */
obstack_free (&insn_link_obstack, NULL);
free (uid_log_links);
free (uid_insn_cost);
reg_stat.release ();
{
struct undo *undo, *next;
for (undo = undobuf.frees; undo; undo = next)
{
next = undo->next;
free (undo);
}
undobuf.frees = 0;
}
total_attempts += combine_attempts;
total_merges += combine_merges;
total_extras += combine_extras;
total_successes += combine_successes;
nonzero_sign_valid = 0;
rtl_hooks = general_rtl_hooks;
/* Make recognizer allow volatile MEMs again. */
init_recog ();
return new_direct_jump_p;
}
/* Wipe the last_xxx fields of reg_stat in preparation for another pass. */
static void
init_reg_last (void)
{
unsigned int i;
reg_stat_type *p;
FOR_EACH_VEC_ELT (reg_stat, i, p)
memset (p, 0, offsetof (reg_stat_type, sign_bit_copies));
}
/* Set up any promoted values for incoming argument registers. */
static void
setup_incoming_promotions (rtx_insn *first)
{
tree arg;
bool strictly_local = false;
for (arg = DECL_ARGUMENTS (current_function_decl); arg;
arg = DECL_CHAIN (arg))
{
rtx x, reg = DECL_INCOMING_RTL (arg);
int uns1, uns3;
machine_mode mode1, mode2, mode3, mode4;
/* Only continue if the incoming argument is in a register. */
if (!REG_P (reg))
continue;
/* Determine, if possible, whether all call sites of the current
function lie within the current compilation unit. (This does
take into account the exporting of a function via taking its
address, and so forth.) */
strictly_local
= cgraph_node::local_info_node (current_function_decl)->local;
/* The mode and signedness of the argument before any promotions happen
(equal to the mode of the pseudo holding it at that stage). */
mode1 = TYPE_MODE (TREE_TYPE (arg));
uns1 = TYPE_UNSIGNED (TREE_TYPE (arg));
/* The mode and signedness of the argument after any source language and
TARGET_PROMOTE_PROTOTYPES-driven promotions. */
mode2 = TYPE_MODE (DECL_ARG_TYPE (arg));
uns3 = TYPE_UNSIGNED (DECL_ARG_TYPE (arg));
/* The mode and signedness of the argument as it is actually passed,
see assign_parm_setup_reg in function.c. */
mode3 = promote_function_mode (TREE_TYPE (arg), mode1, &uns3,
TREE_TYPE (cfun->decl), 0);
/* The mode of the register in which the argument is being passed. */
mode4 = GET_MODE (reg);
/* Eliminate sign extensions in the callee when:
(a) A mode promotion has occurred; */
if (mode1 == mode3)
continue;
/* (b) The mode of the register is the same as the mode of
the argument as it is passed; */
if (mode3 != mode4)
continue;
/* (c) There's no language level extension; */
if (mode1 == mode2)
;
/* (c.1) All callers are from the current compilation unit. If that's
the case we don't have to rely on an ABI, we only have to know
what we're generating right now, and we know that we will do the
mode1 to mode2 promotion with the given sign. */
else if (!strictly_local)
continue;
/* (c.2) The combination of the two promotions is useful. This is
true when the signs match, or if the first promotion is unsigned.
In the later case, (sign_extend (zero_extend x)) is the same as
(zero_extend (zero_extend x)), so make sure to force UNS3 true. */
else if (uns1)
uns3 = true;
else if (uns3)
continue;
/* Record that the value was promoted from mode1 to mode3,
so that any sign extension at the head of the current
function may be eliminated. */
x = gen_rtx_CLOBBER (mode1, const0_rtx);
x = gen_rtx_fmt_e ((uns3 ? ZERO_EXTEND : SIGN_EXTEND), mode3, x);
record_value_for_reg (reg, first, x);
}
}
/* If MODE has a precision lower than PREC and SRC is a non-negative constant
that would appear negative in MODE, sign-extend SRC for use in nonzero_bits
because some machines (maybe most) will actually do the sign-extension and
this is the conservative approach.
??? For 2.5, try to tighten up the MD files in this regard instead of this
kludge. */
static rtx
sign_extend_short_imm (rtx src, machine_mode mode, unsigned int prec)
{
scalar_int_mode int_mode;
if (CONST_INT_P (src)
&& is_a <scalar_int_mode> (mode, &int_mode)
&& GET_MODE_PRECISION (int_mode) < prec
&& INTVAL (src) > 0
&& val_signbit_known_set_p (int_mode, INTVAL (src)))
src = GEN_INT (INTVAL (src) | ~GET_MODE_MASK (int_mode));
return src;
}
/* Update RSP for pseudo-register X from INSN's REG_EQUAL note (if one exists)
and SET. */
static void
update_rsp_from_reg_equal (reg_stat_type *rsp, rtx_insn *insn, const_rtx set,
rtx x)
{
rtx reg_equal_note = insn ? find_reg_equal_equiv_note (insn) : NULL_RTX;
unsigned HOST_WIDE_INT bits = 0;
rtx reg_equal = NULL, src = SET_SRC (set);
unsigned int num = 0;
if (reg_equal_note)
reg_equal = XEXP (reg_equal_note, 0);
if (SHORT_IMMEDIATES_SIGN_EXTEND)
{
src = sign_extend_short_imm (src, GET_MODE (x), BITS_PER_WORD);
if (reg_equal)
reg_equal = sign_extend_short_imm (reg_equal, GET_MODE (x), BITS_PER_WORD);
}
/* Don't call nonzero_bits if it cannot change anything. */
if (rsp->nonzero_bits != HOST_WIDE_INT_M1U)
{
machine_mode mode = GET_MODE (x);
if (GET_MODE_CLASS (mode) == MODE_INT
&& HWI_COMPUTABLE_MODE_P (mode))
mode = nonzero_bits_mode;
bits = nonzero_bits (src, mode);
if (reg_equal && bits)
bits &= nonzero_bits (reg_equal, mode);
rsp->nonzero_bits |= bits;
}
/* Don't call num_sign_bit_copies if it cannot change anything. */
if (rsp->sign_bit_copies != 1)
{
num = num_sign_bit_copies (SET_SRC (set), GET_MODE (x));
if (reg_equal && maybe_ne (num, GET_MODE_PRECISION (GET_MODE (x))))
{
unsigned int numeq = num_sign_bit_copies (reg_equal, GET_MODE (x));
if (num == 0 || numeq > num)
num = numeq;
}
if (rsp->sign_bit_copies == 0 || num < rsp->sign_bit_copies)
rsp->sign_bit_copies = num;
}
}
/* Called via note_stores. If X is a pseudo that is narrower than
HOST_BITS_PER_WIDE_INT and is being set, record what bits are known zero.
If we are setting only a portion of X and we can't figure out what
portion, assume all bits will be used since we don't know what will
be happening.
Similarly, set how many bits of X are known to be copies of the sign bit
at all locations in the function. This is the smallest number implied
by any set of X. */
static void
set_nonzero_bits_and_sign_copies (rtx x, const_rtx set, void *data)
{
rtx_insn *insn = (rtx_insn *) data;
scalar_int_mode mode;
if (REG_P (x)
&& REGNO (x) >= FIRST_PSEUDO_REGISTER
/* If this register is undefined at the start of the file, we can't
say what its contents were. */
&& ! REGNO_REG_SET_P
(DF_LR_IN (ENTRY_BLOCK_PTR_FOR_FN (cfun)->next_bb), REGNO (x))
&& is_a <scalar_int_mode> (GET_MODE (x), &mode)
&& HWI_COMPUTABLE_MODE_P (mode))
{
reg_stat_type *rsp = &reg_stat[REGNO (x)];
if (set == 0 || GET_CODE (set) == CLOBBER)
{
rsp->nonzero_bits = GET_MODE_MASK (mode);
rsp->sign_bit_copies = 1;
return;
}
/* If this register is being initialized using itself, and the
register is uninitialized in this basic block, and there are
no LOG_LINKS which set the register, then part of the
register is uninitialized. In that case we can't assume
anything about the number of nonzero bits.
??? We could do better if we checked this in
reg_{nonzero_bits,num_sign_bit_copies}_for_combine. Then we
could avoid making assumptions about the insn which initially
sets the register, while still using the information in other
insns. We would have to be careful to check every insn
involved in the combination. */
if (insn
&& reg_referenced_p (x, PATTERN (insn))
&& !REGNO_REG_SET_P (DF_LR_IN (BLOCK_FOR_INSN (insn)),
REGNO (x)))
{
struct insn_link *link;
FOR_EACH_LOG_LINK (link, insn)
if (dead_or_set_p (link->insn, x))
break;
if (!link)
{
rsp->nonzero_bits = GET_MODE_MASK (mode);
rsp->sign_bit_copies = 1;
return;
}
}
/* If this is a complex assignment, see if we can convert it into a
simple assignment. */
set = expand_field_assignment (set);
/* If this is a simple assignment, or we have a paradoxical SUBREG,
set what we know about X. */
if (SET_DEST (set) == x
|| (paradoxical_subreg_p (SET_DEST (set))
&& SUBREG_REG (SET_DEST (set)) == x))
update_rsp_from_reg_equal (rsp, insn, set, x);
else
{
rsp->nonzero_bits = GET_MODE_MASK (mode);
rsp->sign_bit_copies = 1;
}
}
}
/* See if INSN can be combined into I3. PRED, PRED2, SUCC and SUCC2 are
optionally insns that were previously combined into I3 or that will be
combined into the merger of INSN and I3. The order is PRED, PRED2,
INSN, SUCC, SUCC2, I3.
Return 0 if the combination is not allowed for any reason.
If the combination is allowed, *PDEST will be set to the single
destination of INSN and *PSRC to the single source, and this function
will return 1. */
static int
can_combine_p (rtx_insn *insn, rtx_insn *i3, rtx_insn *pred ATTRIBUTE_UNUSED,
rtx_insn *pred2 ATTRIBUTE_UNUSED, rtx_insn *succ, rtx_insn *succ2,
rtx *pdest, rtx *psrc)
{
int i;
const_rtx set = 0;
rtx src, dest;
rtx_insn *p;
rtx link;
bool all_adjacent = true;
int (*is_volatile_p) (const_rtx);
if (succ)
{
if (succ2)
{
if (next_active_insn (succ2) != i3)
all_adjacent = false;
if (next_active_insn (succ) != succ2)
all_adjacent = false;
}
else if (next_active_insn (succ) != i3)
all_adjacent = false;
if (next_active_insn (insn) != succ)
all_adjacent = false;
}
else if (next_active_insn (insn) != i3)
all_adjacent = false;
/* Can combine only if previous insn is a SET of a REG, a SUBREG or CC0.
or a PARALLEL consisting of such a SET and CLOBBERs.
If INSN has CLOBBER parallel parts, ignore them for our processing.
By definition, these happen during the execution of the insn. When it
is merged with another insn, all bets are off. If they are, in fact,
needed and aren't also supplied in I3, they may be added by
recog_for_combine. Otherwise, it won't match.
We can also ignore a SET whose SET_DEST is mentioned in a REG_UNUSED
note.
Get the source and destination of INSN. If more than one, can't
combine. */
if (GET_CODE (PATTERN (insn)) == SET)
set = PATTERN (insn);
else if (GET_CODE (PATTERN (insn)) == PARALLEL
&& GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == SET)
{
for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++)
{
rtx elt = XVECEXP (PATTERN (insn), 0, i);
switch (GET_CODE (elt))
{
/* This is important to combine floating point insns
for the SH4 port. */
case USE:
/* Combining an isolated USE doesn't make sense.
We depend here on combinable_i3pat to reject them. */
/* The code below this loop only verifies that the inputs of
the SET in INSN do not change. We call reg_set_between_p
to verify that the REG in the USE does not change between
I3 and INSN.
If the USE in INSN was for a pseudo register, the matching
insn pattern will likely match any register; combining this
with any other USE would only be safe if we knew that the
used registers have identical values, or if there was
something to tell them apart, e.g. different modes. For
now, we forgo such complicated tests and simply disallow
combining of USES of pseudo registers with any other USE. */
if (REG_P (XEXP (elt, 0))
&& GET_CODE (PATTERN (i3)) == PARALLEL)
{
rtx i3pat = PATTERN (i3);
int i = XVECLEN (i3pat, 0) - 1;
unsigned int regno = REGNO (XEXP (elt, 0));
do
{
rtx i3elt = XVECEXP (i3pat, 0, i);
if (GET_CODE (i3elt) == USE
&& REG_P (XEXP (i3elt, 0))
&& (REGNO (XEXP (i3elt, 0)) == regno
? reg_set_between_p (XEXP (elt, 0),
PREV_INSN (insn), i3)
: regno >= FIRST_PSEUDO_REGISTER))
return 0;
}
while (--i >= 0);
}
break;
/* We can ignore CLOBBERs. */
case CLOBBER:
break;
case SET:
/* Ignore SETs whose result isn't used but not those that
have side-effects. */
if (find_reg_note (insn, REG_UNUSED, SET_DEST (elt))
&& insn_nothrow_p (insn)
&& !side_effects_p (elt))
break;
/* If we have already found a SET, this is a second one and
so we cannot combine with this insn. */
if (set)
return 0;
set = elt;
break;
default:
/* Anything else means we can't combine. */
return 0;
}
}
if (set == 0
/* If SET_SRC is an ASM_OPERANDS we can't throw away these CLOBBERs,
so don't do anything with it. */
|| GET_CODE (SET_SRC (set)) == ASM_OPERANDS)
return 0;
}
else
return 0;
if (set == 0)
return 0;
/* The simplification in expand_field_assignment may call back to
get_last_value, so set safe guard here. */
subst_low_luid = DF_INSN_LUID (insn);
set = expand_field_assignment (set);
src = SET_SRC (set), dest = SET_DEST (set);
/* Do not eliminate user-specified register if it is in an
asm input because we may break the register asm usage defined
in GCC manual if allow to do so.
Be aware that this may cover more cases than we expect but this
should be harmless. */
if (REG_P (dest) && REG_USERVAR_P (dest) && HARD_REGISTER_P (dest)
&& extract_asm_operands (PATTERN (i3)))
return 0;
/* Don't eliminate a store in the stack pointer. */
if (dest == stack_pointer_rtx
/* Don't combine with an insn that sets a register to itself if it has
a REG_EQUAL note. This may be part of a LIBCALL sequence. */
|| (rtx_equal_p (src, dest) && find_reg_note (insn, REG_EQUAL, NULL_RTX))
/* Can't merge an ASM_OPERANDS. */
|| GET_CODE (src) == ASM_OPERANDS
/* Can't merge a function call. */
|| GET_CODE (src) == CALL
/* Don't eliminate a function call argument. */
|| (CALL_P (i3)
&& (find_reg_fusage (i3, USE, dest)
|| (REG_P (dest)
&& REGNO (dest) < FIRST_PSEUDO_REGISTER
&& global_regs[REGNO (dest)])))
/* Don't substitute into an incremented register. */
|| FIND_REG_INC_NOTE (i3, dest)
|| (succ && FIND_REG_INC_NOTE (succ, dest))
|| (succ2 && FIND_REG_INC_NOTE (succ2, dest))
/* Don't substitute into a non-local goto, this confuses CFG. */
|| (JUMP_P (i3) && find_reg_note (i3, REG_NON_LOCAL_GOTO, NULL_RTX))
/* Make sure that DEST is not used after INSN but before SUCC, or
after SUCC and before SUCC2, or after SUCC2 but before I3. */
|| (!all_adjacent
&& ((succ2
&& (reg_used_between_p (dest, succ2, i3)
|| reg_used_between_p (dest, succ, succ2)))
|| (!succ2 && succ && reg_used_between_p (dest, succ, i3))
|| (!succ2 && !succ && reg_used_between_p (dest, insn, i3))
|| (succ
/* SUCC and SUCC2 can be split halves from a PARALLEL; in
that case SUCC is not in the insn stream, so use SUCC2
instead for this test. */
&& reg_used_between_p (dest, insn,
succ2
&& INSN_UID (succ) == INSN_UID (succ2)
? succ2 : succ))))
/* Make sure that the value that is to be substituted for the register
does not use any registers whose values alter in between. However,
If the insns are adjacent, a use can't cross a set even though we
think it might (this can happen for a sequence of insns each setting
the same destination; last_set of that register might point to
a NOTE). If INSN has a REG_EQUIV note, the register is always
equivalent to the memory so the substitution is valid even if there
are intervening stores. Also, don't move a volatile asm or
UNSPEC_VOLATILE across any other insns. */
|| (! all_adjacent
&& (((!MEM_P (src)
|| ! find_reg_note (insn, REG_EQUIV, src))
&& modified_between_p (src, insn, i3))
|| (GET_CODE (src) == ASM_OPERANDS && MEM_VOLATILE_P (src))
|| GET_CODE (src) == UNSPEC_VOLATILE))
/* Don't combine across a CALL_INSN, because that would possibly
change whether the life span of some REGs crosses calls or not,
and it is a pain to update that information.
Exception: if source is a constant, moving it later can't hurt.
Accept that as a special case. */
|| (DF_INSN_LUID (insn) < last_call_luid && ! CONSTANT_P (src)))
return 0;
/* DEST must either be a REG or CC0. */
if (REG_P (dest))
{
/* If register alignment is being enforced for multi-word items in all
cases except for parameters, it is possible to have a register copy
insn referencing a hard register that is not allowed to contain the
mode being copied and which would not be valid as an operand of most
insns. Eliminate this problem by not combining with such an insn.
Also, on some machines we don't want to extend the life of a hard
register. */
if (REG_P (src)
&& ((REGNO (dest) < FIRST_PSEUDO_REGISTER
&& !targetm.hard_regno_mode_ok (REGNO (dest), GET_MODE (dest)))
/* Don't extend the life of a hard register unless it is
user variable (if we have few registers) or it can't
fit into the desired register (meaning something special
is going on).
Also avoid substituting a return register into I3, because
reload can't handle a conflict with constraints of other
inputs. */
|| (REGNO (src) < FIRST_PSEUDO_REGISTER
&& !targetm.hard_regno_mode_ok (REGNO (src),
GET_MODE (src)))))
return 0;
}
else if (GET_CODE (dest) != CC0)
return 0;
if (GET_CODE (PATTERN (i3)) == PARALLEL)
for (i = XVECLEN (PATTERN (i3), 0) - 1; i >= 0; i--)
if (GET_CODE (XVECEXP (PATTERN (i3), 0, i)) == CLOBBER)
{
rtx reg = XEXP (XVECEXP (PATTERN (i3), 0, i), 0);
/* If the clobber represents an earlyclobber operand, we must not
substitute an expression containing the clobbered register.
As we do not analyze the constraint strings here, we have to
make the conservative assumption. However, if the register is
a fixed hard reg, the clobber cannot represent any operand;
we leave it up to the machine description to either accept or
reject use-and-clobber patterns. */
if (!REG_P (reg)
|| REGNO (reg) >= FIRST_PSEUDO_REGISTER
|| !fixed_regs[REGNO (reg)])
if (reg_overlap_mentioned_p (reg, src))
return 0;
}
/* If INSN contains anything volatile, or is an `asm' (whether volatile
or not), reject, unless nothing volatile comes between it and I3 */
if (GET_CODE (src) == ASM_OPERANDS || volatile_refs_p (src))
{
/* Make sure neither succ nor succ2 contains a volatile reference. */
if (succ2 != 0 && volatile_refs_p (PATTERN (succ2)))
return 0;
if (succ != 0 && volatile_refs_p (PATTERN (succ)))
return 0;
/* We'll check insns between INSN and I3 below. */
}
/* If INSN is an asm, and DEST is a hard register, reject, since it has
to be an explicit register variable, and was chosen for a reason. */
if (GET_CODE (src) == ASM_OPERANDS
&& REG_P (dest) && REGNO (dest) < FIRST_PSEUDO_REGISTER)
return 0;
/* If INSN contains volatile references (specifically volatile MEMs),
we cannot combine across any other volatile references.
Even if INSN doesn't contain volatile references, any intervening
volatile insn might affect machine state. */
is_volatile_p = volatile_refs_p (PATTERN (insn))
? volatile_refs_p
: volatile_insn_p;
for (p = NEXT_INSN (insn); p != i3; p = NEXT_INSN (p))
if (INSN_P (p) && p != succ && p != succ2 && is_volatile_p (PATTERN (p)))
return 0;
/* If INSN contains an autoincrement or autodecrement, make sure that
register is not used between there and I3, and not already used in
I3 either. Neither must it be used in PRED or SUCC, if they exist.
Also insist that I3 not be a jump if using LRA; if it were one
and the incremented register were spilled, we would lose.
Reload handles this correctly. */
if (AUTO_INC_DEC)
for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
if (REG_NOTE_KIND (link) == REG_INC
&& ((JUMP_P (i3) && targetm.lra_p ())
|| reg_used_between_p (XEXP (link, 0), insn, i3)
|| (pred != NULL_RTX
&& reg_overlap_mentioned_p (XEXP (link, 0), PATTERN (pred)))
|| (pred2 != NULL_RTX
&& reg_overlap_mentioned_p (XEXP (link, 0), PATTERN (pred2)))
|| (succ != NULL_RTX
&& reg_overlap_mentioned_p (XEXP (link, 0), PATTERN (succ)))
|| (succ2 != NULL_RTX
&& reg_overlap_mentioned_p (XEXP (link, 0), PATTERN (succ2)))
|| reg_overlap_mentioned_p (XEXP (link, 0), PATTERN (i3))))
return 0;
/* Don't combine an insn that follows a CC0-setting insn.
An insn that uses CC0 must not be separated from the one that sets it.
We do, however, allow I2 to follow a CC0-setting insn if that insn
is passed as I1; in that case it will be deleted also.
We also allow combining in this case if all the insns are adjacent
because that would leave the two CC0 insns adjacent as well.
It would be more logical to test whether CC0 occurs inside I1 or I2,
but that would be much slower, and this ought to be equivalent. */
if (HAVE_cc0)
{
p = prev_nonnote_insn (insn);
if (p && p != pred && NONJUMP_INSN_P (p) && sets_cc0_p (PATTERN (p))
&& ! all_adjacent)
return 0;
}
/* If we get here, we have passed all the tests and the combination is
to be allowed. */
*pdest = dest;
*psrc = src;
return 1;
}
/* LOC is the location within I3 that contains its pattern or the component
of a PARALLEL of the pattern. We validate that it is valid for combining.
One problem is if I3 modifies its output, as opposed to replacing it
entirely, we can't allow the output to contain I2DEST, I1DEST or I0DEST as
doing so would produce an insn that is not equivalent to the original insns.
Consider:
(set (reg:DI 101) (reg:DI 100))
(set (subreg:SI (reg:DI 101) 0) <foo>)
This is NOT equivalent to:
(parallel [(set (subreg:SI (reg:DI 100) 0) <foo>)
(set (reg:DI 101) (reg:DI 100))])
Not only does this modify 100 (in which case it might still be valid
if 100 were dead in I2), it sets 101 to the ORIGINAL value of 100.
We can also run into a problem if I2 sets a register that I1
uses and I1 gets directly substituted into I3 (not via I2). In that
case, we would be getting the wrong value of I2DEST into I3, so we
must reject the combination. This case occurs when I2 and I1 both
feed into I3, rather than when I1 feeds into I2, which feeds into I3.
If I1_NOT_IN_SRC is nonzero, it means that finding I1 in the source
of a SET must prevent combination from occurring. The same situation
can occur for I0, in which case I0_NOT_IN_SRC is set.
Before doing the above check, we first try to expand a field assignment
into a set of logical operations.
If PI3_DEST_KILLED is nonzero, it is a pointer to a location in which
we place a register that is both set and used within I3. If more than one
such register is detected, we fail.
Return 1 if the combination is valid, zero otherwise. */
static int
combinable_i3pat (rtx_insn *i3, rtx *loc, rtx i2dest, rtx i1dest, rtx i0dest,
int i1_not_in_src, int i0_not_in_src, rtx *pi3dest_killed)
{
rtx x = *loc;
if (GET_CODE (x) == SET)
{
rtx set = x ;
rtx dest = SET_DEST (set);
rtx src = SET_SRC (set);
rtx inner_dest = dest;
rtx subdest;
while (GET_CODE (inner_dest) == STRICT_LOW_PART
|| GET_CODE (inner_dest) == SUBREG
|| GET_CODE (inner_dest) == ZERO_EXTRACT)
inner_dest = XEXP (inner_dest, 0);
/* Check for the case where I3 modifies its output, as discussed
above. We don't want to prevent pseudos from being combined
into the address of a MEM, so only prevent the combination if
i1 or i2 set the same MEM. */
if ((inner_dest != dest &&
(!MEM_P (inner_dest)
|| rtx_equal_p (i2dest, inner_dest)
|| (i1dest && rtx_equal_p (i1dest, inner_dest))
|| (i0dest && rtx_equal_p (i0dest, inner_dest)))
&& (reg_overlap_mentioned_p (i2dest, inner_dest)
|| (i1dest && reg_overlap_mentioned_p (i1dest, inner_dest))
|| (i0dest && reg_overlap_mentioned_p (i0dest, inner_dest))))
/* This is the same test done in can_combine_p except we can't test
all_adjacent; we don't have to, since this instruction will stay
in place, thus we are not considering increasing the lifetime of
INNER_DEST.
Also, if this insn sets a function argument, combining it with
something that might need a spill could clobber a previous
function argument; the all_adjacent test in can_combine_p also
checks this; here, we do a more specific test for this case. */
|| (REG_P (inner_dest)
&& REGNO (inner_dest) < FIRST_PSEUDO_REGISTER
&& !targetm.hard_regno_mode_ok (REGNO (inner_dest),
GET_MODE (inner_dest)))
|| (i1_not_in_src && reg_overlap_mentioned_p (i1dest, src))
|| (i0_not_in_src && reg_overlap_mentioned_p (i0dest, src)))
return 0;
/* If DEST is used in I3, it is being killed in this insn, so
record that for later. We have to consider paradoxical
subregs here, since they kill the whole register, but we
ignore partial subregs, STRICT_LOW_PART, etc.
Never add REG_DEAD notes for the FRAME_POINTER_REGNUM or the
STACK_POINTER_REGNUM, since these are always considered to be
live. Similarly for ARG_POINTER_REGNUM if it is fixed. */
subdest = dest;
if (GET_CODE (subdest) == SUBREG && !partial_subreg_p (subdest))
subdest = SUBREG_REG (subdest);
if (pi3dest_killed
&& REG_P (subdest)
&& reg_referenced_p (subdest, PATTERN (i3))
&& REGNO (subdest) != FRAME_POINTER_REGNUM
&& (HARD_FRAME_POINTER_IS_FRAME_POINTER
|| REGNO (subdest) != HARD_FRAME_POINTER_REGNUM)
&& (FRAME_POINTER_REGNUM == ARG_POINTER_REGNUM
|| (REGNO (subdest) != ARG_POINTER_REGNUM
|| ! fixed_regs [REGNO (subdest)]))
&& REGNO (subdest) != STACK_POINTER_REGNUM)
{
if (*pi3dest_killed)
return 0;
*pi3dest_killed = subdest;
}
}
else if (GET_CODE (x) == PARALLEL)
{
int i;
for (i = 0; i < XVECLEN (x, 0); i++)
if (! combinable_i3pat (i3, &XVECEXP (x, 0, i), i2dest, i1dest, i0dest,
i1_not_in_src, i0_not_in_src, pi3dest_killed))
return 0;
}
return 1;
}
/* Return 1 if X is an arithmetic expression that contains a multiplication
and division. We don't count multiplications by powers of two here. */
static int
contains_muldiv (rtx x)
{
switch (GET_CODE (x))
{
case MOD: case DIV: case UMOD: case UDIV:
return 1;
case MULT:
return ! (CONST_INT_P (XEXP (x, 1))
&& pow2p_hwi (UINTVAL (XEXP (x, 1))));
default:
if (BINARY_P (x))
return contains_muldiv (XEXP (x, 0))
|| contains_muldiv (XEXP (x, 1));
if (UNARY_P (x))
return contains_muldiv (XEXP (x, 0));
return 0;
}
}
/* Determine whether INSN can be used in a combination. Return nonzero if
not. This is used in try_combine to detect early some cases where we
can't perform combinations. */
static int
cant_combine_insn_p (rtx_insn *insn)
{
rtx set;
rtx src, dest;
/* If this isn't really an insn, we can't do anything.
This can occur when flow deletes an insn that it has merged into an
auto-increment address. */
if (!NONDEBUG_INSN_P (insn))
return 1;
/* Never combine loads and stores involving hard regs that are likely
to be spilled. The register allocator can usually handle such
reg-reg moves by tying. If we allow the combiner to make
substitutions of likely-spilled regs, reload might die.
As an exception, we allow combinations involving fixed regs; these are
not available to the register allocator so there's no risk involved. */
set = single_set (insn);
if (! set)
return 0;
src = SET_SRC (set);
dest = SET_DEST (set);
if (GET_CODE (src) == SUBREG)
src = SUBREG_REG (src);
if (GET_CODE (dest) == SUBREG)
dest = SUBREG_REG (dest);
if (REG_P (src) && REG_P (dest)
&& ((HARD_REGISTER_P (src)
&& ! TEST_HARD_REG_BIT (fixed_reg_set, REGNO (src))
#ifdef LEAF_REGISTERS
&& ! LEAF_REGISTERS [REGNO (src)])
#else
)
#endif
|| (HARD_REGISTER_P (dest)
&& ! TEST_HARD_REG_BIT (fixed_reg_set, REGNO (dest))
&& targetm.class_likely_spilled_p (REGNO_REG_CLASS (REGNO (dest))))))
return 1;
return 0;
}
struct likely_spilled_retval_info
{
unsigned regno, nregs;
unsigned mask;
};
/* Called via note_stores by likely_spilled_retval_p. Remove from info->mask
hard registers that are known to be written to / clobbered in full. */
static void
likely_spilled_retval_1 (rtx x, const_rtx set, void *data)
{
struct likely_spilled_retval_info *const info =
(struct likely_spilled_retval_info *) data;
unsigned regno, nregs;
unsigned new_mask;
if (!REG_P (XEXP (set, 0)))
return;
regno = REGNO (x);
if (regno >= info->regno + info->nregs)
return;
nregs = REG_NREGS (x);
if (regno + nregs <= info->regno)
return;
new_mask = (2U << (nregs - 1)) - 1;
if (regno < info->regno)
new_mask >>= info->regno - regno;
else
new_mask <<= regno - info->regno;
info->mask &= ~new_mask;
}
/* Return nonzero iff part of the return value is live during INSN, and
it is likely spilled. This can happen when more than one insn is needed
to copy the return value, e.g. when we consider to combine into the
second copy insn for a complex value. */
static int
likely_spilled_retval_p (rtx_insn *insn)
{
rtx_insn *use = BB_END (this_basic_block);
rtx reg;
rtx_insn *p;
unsigned regno, nregs;
/* We assume here that no machine mode needs more than
32 hard registers when the value overlaps with a register
for which TARGET_FUNCTION_VALUE_REGNO_P is true. */
unsigned mask;
struct likely_spilled_retval_info info;
if (!NONJUMP_INSN_P (use) || GET_CODE (PATTERN (use)) != USE || insn == use)
return 0;
reg = XEXP (PATTERN (use), 0);
if (!REG_P (reg) || !targetm.calls.function_value_regno_p (REGNO (reg)))
return 0;
regno = REGNO (reg);
nregs = REG_NREGS (reg);
if (nregs == 1)
return 0;
mask = (2U << (nregs - 1)) - 1;
/* Disregard parts of the return value that are set later. */
info.regno = regno;
info.nregs = nregs;
info.mask = mask;
for (p = PREV_INSN (use); info.mask && p != insn; p = PREV_INSN (p))
if (INSN_P (p))
note_stores (p, likely_spilled_retval_1, &info);
mask = info.mask;
/* Check if any of the (probably) live return value registers is
likely spilled. */
nregs --;
do
{
if ((mask & 1 << nregs)
&& targetm.class_likely_spilled_p (REGNO_REG_CLASS (regno + nregs)))
return 1;
} while (nregs--);
return 0;
}
/* Adjust INSN after we made a change to its destination.
Changing the destination can invalidate notes that say something about
the results of the insn and a LOG_LINK pointing to the insn. */
static void
adjust_for_new_dest (rtx_insn *insn)
{
/* For notes, be conservative and simply remove them. */
remove_reg_equal_equiv_notes (insn, true);
/* The new insn will have a destination that was previously the destination
of an insn just above it. Call distribute_links to make a LOG_LINK from
the next use of that destination. */
rtx set = single_set (insn);
gcc_assert (set);
rtx reg = SET_DEST (set);
while (GET_CODE (reg) == ZERO_EXTRACT
|| GET_CODE (reg) == STRICT_LOW_PART
|| GET_CODE (reg) == SUBREG)
reg = XEXP (reg, 0);
gcc_assert (REG_P (reg));
distribute_links (alloc_insn_link (insn, REGNO (reg), NULL));
df_insn_rescan (insn);
}
/* Return TRUE if combine can reuse reg X in mode MODE.
ADDED_SETS is nonzero if the original set is still required. */
static bool
can_change_dest_mode (rtx x, int added_sets, machine_mode mode)
{
unsigned int regno;
if (!REG_P (x))
return false;
/* Don't change between modes with different underlying register sizes,
since this could lead to invalid subregs. */
if (maybe_ne (REGMODE_NATURAL_SIZE (mode),
REGMODE_NATURAL_SIZE (GET_MODE (x))))
return false;
regno = REGNO (x);
/* Allow hard registers if the new mode is legal, and occupies no more
registers than the old mode. */
if (regno < FIRST_PSEUDO_REGISTER)
return (targetm.hard_regno_mode_ok (regno, mode)
&& REG_NREGS (x) >= hard_regno_nregs (regno, mode));
/* Or a pseudo that is only used once. */
return (regno < reg_n_sets_max
&& REG_N_SETS (regno) == 1
&& !added_sets
&& !REG_USERVAR_P (x));
}
/* Check whether X, the destination of a set, refers to part of
the register specified by REG. */
static bool
reg_subword_p (rtx x, rtx reg)
{
/* Check that reg is an integer mode register. */
if (!REG_P (reg) || GET_MODE_CLASS (GET_MODE (reg)) != MODE_INT)
return false;
if (GET_CODE (x) == STRICT_LOW_PART
|| GET_CODE (x) == ZERO_EXTRACT)
x = XEXP (x, 0);
return GET_CODE (x) == SUBREG
&& SUBREG_REG (x) == reg
&& GET_MODE_CLASS (GET_MODE (x)) == MODE_INT;
}
/* Return whether PAT is a PARALLEL of exactly N register SETs followed
by an arbitrary number of CLOBBERs. */
static bool
is_parallel_of_n_reg_sets (rtx pat, int n)
{
if (GET_CODE (pat) != PARALLEL)
return false;
int len = XVECLEN (pat, 0);
if (len < n)
return false;
int i;
for (i = 0; i < n; i++)
if (GET_CODE (XVECEXP (pat, 0, i)) != SET
|| !REG_P (SET_DEST (XVECEXP (pat, 0, i))))
return false;
for ( ; i < len; i++)
switch (GET_CODE (XVECEXP (pat, 0, i)))
{
case CLOBBER:
if (XEXP (XVECEXP (pat, 0, i), 0) == const0_rtx)
return false;
break;
default:
return false;
}
return true;
}
/* Return whether INSN, a PARALLEL of N register SETs (and maybe some
CLOBBERs), can be split into individual SETs in that order, without
changing semantics. */
static bool
can_split_parallel_of_n_reg_sets (rtx_insn *insn, int n)
{
if (!insn_nothrow_p (insn))
return false;
rtx pat = PATTERN (insn);
int i, j;
for (i = 0; i < n; i++)
{
if (side_effects_p (SET_SRC (XVECEXP (pat, 0, i))))
return false;
rtx reg = SET_DEST (XVECEXP (pat, 0, i));
for (j = i + 1; j < n; j++)
if (reg_referenced_p (reg, XVECEXP (pat, 0, j)))
return false;
}
return true;
}
/* Return whether X is just a single_set, with the source
a general_operand. */
static bool
is_just_move (rtx_insn *x)
{
rtx set = single_set (x);
if (!set)
return false;
return general_operand (SET_SRC (set), VOIDmode);
}
/* Callback function to count autoincs. */
static int
count_auto_inc (rtx, rtx, rtx, rtx, rtx, void *arg)
{
(*((int *) arg))++;
return 0;
}
/* Try to combine the insns I0, I1 and I2 into I3.
Here I0, I1 and I2 appear earlier than I3.
I0 and I1 can be zero; then we combine just I2 into I3, or I1 and I2 into
I3.
If we are combining more than two insns and the resulting insn is not
recognized, try splitting it into two insns. If that happens, I2 and I3
are retained and I1/I0 are pseudo-deleted by turning them into a NOTE.
Otherwise, I0, I1 and I2 are pseudo-deleted.
Return 0 if the combination does not work. Then nothing is changed.
If we did the combination, return the insn at which combine should
resume scanning.
Set NEW_DIRECT_JUMP_P to a nonzero value if try_combine creates a
new direct jump instruction.
LAST_COMBINED_INSN is either I3, or some insn after I3 that has
been I3 passed to an earlier try_combine within the same basic
block. */
static rtx_insn *
try_combine (rtx_insn *i3, rtx_insn *i2, rtx_insn *i1, rtx_insn *i0,
int *new_direct_jump_p, rtx_insn *last_combined_insn)
{
/* New patterns for I3 and I2, respectively. */
rtx newpat, newi2pat = 0;
rtvec newpat_vec_with_clobbers = 0;
int substed_i2 = 0, substed_i1 = 0, substed_i0 = 0;
/* Indicates need to preserve SET in I0, I1 or I2 in I3 if it is not
dead. */
int added_sets_0, added_sets_1, added_sets_2;
/* Total number of SETs to put into I3. */
int total_sets;
/* Nonzero if I2's or I1's body now appears in I3. */
int i2_is_used = 0, i1_is_used = 0;
/* INSN_CODEs for new I3, new I2, and user of condition code. */
int insn_code_number, i2_code_number = 0, other_code_number = 0;
/* Contains I3 if the destination of I3 is used in its source, which means
that the old life of I3 is being killed. If that usage is placed into
I2 and not in I3, a REG_DEAD note must be made. */
rtx i3dest_killed = 0;
/* SET_DEST and SET_SRC of I2, I1 and I0. */
rtx i2dest = 0, i2src = 0, i1dest = 0, i1src = 0, i0dest = 0, i0src = 0;
/* Copy of SET_SRC of I1 and I0, if needed. */
rtx i1src_copy = 0, i0src_copy = 0, i0src_copy2 = 0;
/* Set if I2DEST was reused as a scratch register. */
bool i2scratch = false;
/* The PATTERNs of I0, I1, and I2, or a copy of them in certain cases. */
rtx i0pat = 0, i1pat = 0, i2pat = 0;
/* Indicates if I2DEST or I1DEST is in I2SRC or I1_SRC. */
int i2dest_in_i2src = 0, i1dest_in_i1src = 0, i2dest_in_i1src = 0;
int i0dest_in_i0src = 0, i1dest_in_i0src = 0, i2dest_in_i0src = 0;
int i2dest_killed = 0, i1dest_killed = 0, i0dest_killed = 0;
int i1_feeds_i2_n = 0, i0_feeds_i2_n = 0, i0_feeds_i1_n = 0;
/* Notes that must be added to REG_NOTES in I3 and I2. */
rtx new_i3_notes, new_i2_notes;
/* Notes that we substituted I3 into I2 instead of the normal case. */
int i3_subst_into_i2 = 0;
/* Notes that I1, I2 or I3 is a MULT operation. */
int have_mult = 0;
int swap_i2i3 = 0;
int split_i2i3 = 0;
int changed_i3_dest = 0;
bool i2_was_move = false, i3_was_move = false;
int n_auto_inc = 0;
int maxreg;
rtx_insn *temp_insn;
rtx temp_expr;
struct insn_link *link;
rtx other_pat = 0;
rtx new_other_notes;
int i;
scalar_int_mode dest_mode, temp_mode;
/* Immediately return if any of I0,I1,I2 are the same insn (I3 can
never be). */
if (i1 == i2 || i0 == i2 || (i0 && i0 == i1))
return 0;
/* Only try four-insn combinations when there's high likelihood of
success. Look for simple insns, such as loads of constants or
binary operations involving a constant. */
if (i0)
{
int i;
int ngood = 0;
int nshift = 0;
rtx set0, set3;
if (!flag_expensive_optimizations)
return 0;
for (i = 0; i < 4; i++)
{
rtx_insn *insn = i == 0 ? i0 : i == 1 ? i1 : i == 2 ? i2 : i3;
rtx set = single_set (insn);
rtx src;
if (!set)
continue;
src = SET_SRC (set);
if (CONSTANT_P (src))
{
ngood += 2;
break;
}
else if (BINARY_P (src) && CONSTANT_P (XEXP (src, 1)))
ngood++;
else if (GET_CODE (src) == ASHIFT || GET_CODE (src) == ASHIFTRT
|| GET_CODE (src) == LSHIFTRT)
nshift++;
}
/* If I0 loads a memory and I3 sets the same memory, then I1 and I2
are likely manipulating its value. Ideally we'll be able to combine
all four insns into a bitfield insertion of some kind.
Note the source in I0 might be inside a sign/zero extension and the
memory modes in I0 and I3 might be different. So extract the address
from the destination of I3 and search for it in the source of I0.
In the event that there's a match but the source/dest do not actually
refer to the same memory, the worst that happens is we try some
combinations that we wouldn't have otherwise. */
if ((set0 = single_set (i0))
/* Ensure the source of SET0 is a MEM, possibly buried inside
an extension. */
&& (GET_CODE (SET_SRC (set0)) == MEM
|| ((GET_CODE (SET_SRC (set0)) == ZERO_EXTEND
|| GET_CODE (SET_SRC (set0)) == SIGN_EXTEND)
&& GET_CODE (XEXP (SET_SRC (set0), 0)) == MEM))
&& (set3 = single_set (i3))
/* Ensure the destination of SET3 is a MEM. */
&& GET_CODE (SET_DEST (set3)) == MEM
/* Would it be better to extract the base address for the MEM
in SET3 and look for that? I don't have cases where it matters
but I could envision such cases. */
&& rtx_referenced_p (XEXP (SET_DEST (set3), 0), SET_SRC (set0)))
ngood += 2;
if (ngood < 2 && nshift < 2)
return 0;
}
/* Exit early if one of the insns involved can't be used for
combinations. */
if (CALL_P (i2)
|| (i1 && CALL_P (i1))
|| (i0 && CALL_P (i0))
|| cant_combine_insn_p (i3)
|| cant_combine_insn_p (i2)
|| (i1 && cant_combine_insn_p (i1))
|| (i0 && cant_combine_insn_p (i0))
|| likely_spilled_retval_p (i3))
return 0;
combine_attempts++;
undobuf.other_insn = 0;
/* Reset the hard register usage information. */
CLEAR_HARD_REG_SET (newpat_used_regs);
if (dump_file && (dump_flags & TDF_DETAILS))
{
if (i0)
fprintf (dump_file, "\nTrying %d, %d, %d -> %d:\n",
INSN_UID (i0), INSN_UID (i1), INSN_UID (i2), INSN_UID (i3));
else if (i1)
fprintf (dump_file, "\nTrying %d, %d -> %d:\n",
INSN_UID (i1), INSN_UID (i2), INSN_UID (i3));
else
fprintf (dump_file, "\nTrying %d -> %d:\n",
INSN_UID (i2), INSN_UID (i3));
if (i0)
dump_insn_slim (dump_file, i0);
if (i1)
dump_insn_slim (dump_file, i1);
dump_insn_slim (dump_file, i2);
dump_insn_slim (dump_file, i3);
}
/* If multiple insns feed into one of I2 or I3, they can be in any
order. To simplify the code below, reorder them in sequence. */
if (i0 && DF_INSN_LUID (i0) > DF_INSN_LUID (i2))
std::swap (i0, i2);
if (i0 && DF_INSN_LUID (i0) > DF_INSN_LUID (i1))
std::swap (i0, i1);
if (i1 && DF_INSN_LUID (i1) > DF_INSN_LUID (i2))
std::swap (i1, i2);
added_links_insn = 0;
added_notes_insn = 0;
/* First check for one important special case that the code below will
not handle. Namely, the case where I1 is zero, I2 is a PARALLEL
and I3 is a SET whose SET_SRC is a SET_DEST in I2. In that case,
we may be able to replace that destination with the destination of I3.
This occurs in the common code where we compute both a quotient and
remainder into a structure, in which case we want to do the computation
directly into the structure to avoid register-register copies.
Note that this case handles both multiple sets in I2 and also cases
where I2 has a number of CLOBBERs inside the PARALLEL.
We make very conservative checks below and only try to handle the
most common cases of this. For example, we only handle the case
where I2 and I3 are adjacent to avoid making difficult register
usage tests. */
if (i1 == 0 && NONJUMP_INSN_P (i3) && GET_CODE (PATTERN (i3)) == SET
&& REG_P (SET_SRC (PATTERN (i3)))
&& REGNO (SET_SRC (PATTERN (i3))) >= FIRST_PSEUDO_REGISTER
&& find_reg_note (i3, REG_DEAD, SET_SRC (PATTERN (i3)))
&& GET_CODE (PATTERN (i2)) == PARALLEL
&& ! side_effects_p (SET_DEST (PATTERN (i3)))
/* If the dest of I3 is a ZERO_EXTRACT or STRICT_LOW_PART, the code
below would need to check what is inside (and reg_overlap_mentioned_p
doesn't support those codes anyway). Don't allow those destinations;
the resulting insn isn't likely to be recognized anyway. */
&& GET_CODE (SET_DEST (PATTERN (i3))) != ZERO_EXTRACT
&& GET_CODE (SET_DEST (PATTERN (i3))) != STRICT_LOW_PART
&& ! reg_overlap_mentioned_p (SET_SRC (PATTERN (i3)),
SET_DEST (PATTERN (i3)))
&& next_active_insn (i2) == i3)
{
rtx p2 = PATTERN (i2);
/* Make sure that the destination of I3,
which we are going to substitute into one output of I2,
is not used within another output of I2. We must avoid making this:
(parallel [(set (mem (reg 69)) ...)
(set (reg 69) ...)])
which is not well-defined as to order of actions.
(Besides, reload can't handle output reloads for this.)
The problem can also happen if the dest of I3 is a memory ref,
if another dest in I2 is an indirect memory ref.
Neither can this PARALLEL be an asm. We do not allow combining
that usually (see can_combine_p), so do not here either. */
bool ok = true;
for (i = 0; ok && i < XVECLEN (p2, 0); i++)
{
if ((GET_CODE (XVECEXP (p2, 0, i)) == SET
|| GET_CODE (XVECEXP (p2, 0, i)) == CLOBBER)
&& reg_overlap_mentioned_p (SET_DEST (PATTERN (i3)),
SET_DEST (XVECEXP (p2, 0, i))))
ok = false;
else if (GET_CODE (XVECEXP (p2, 0, i)) == SET
&& GET_CODE (SET_SRC (XVECEXP (p2, 0, i))) == ASM_OPERANDS)
ok = false;
}
if (ok)
for (i = 0; i < XVECLEN (p2, 0); i++)
if (GET_CODE (XVECEXP (p2, 0, i)) == SET
&& SET_DEST (XVECEXP (p2, 0, i)) == SET_SRC (PATTERN (i3)))
{
combine_merges++;
subst_insn = i3;
subst_low_luid = DF_INSN_LUID (i2);
added_sets_2 = added_sets_1 = added_sets_0 = 0;
i2src = SET_SRC (XVECEXP (p2, 0, i));
i2dest = SET_DEST (XVECEXP (p2, 0, i));
i2dest_killed = dead_or_set_p (i2, i2dest);
/* Replace the dest in I2 with our dest and make the resulting
insn the new pattern for I3. Then skip to where we validate
the pattern. Everything was set up above. */
SUBST (SET_DEST (XVECEXP (p2, 0, i)), SET_DEST (PATTERN (i3)));
newpat = p2;
i3_subst_into_i2 = 1;
goto validate_replacement;
}
}
/* If I2 is setting a pseudo to a constant and I3 is setting some
sub-part of it to another constant, merge them by making a new
constant. */
if (i1 == 0
&& (temp_expr = single_set (i2)) != 0
&& is_a <scalar_int_mode> (GET_MODE (SET_DEST (temp_expr)), &temp_mode)
&& CONST_SCALAR_INT_P (SET_SRC (temp_expr))
&& GET_CODE (PATTERN (i3)) == SET
&& CONST_SCALAR_INT_P (SET_SRC (PATTERN (i3)))
&& reg_subword_p (SET_DEST (PATTERN (i3)), SET_DEST (temp_expr)))
{
rtx dest = SET_DEST (PATTERN (i3));
rtx temp_dest = SET_DEST (temp_expr);
int offset = -1;
int width = 0;
if (GET_CODE (dest) == ZERO_EXTRACT)
{
if (CONST_INT_P (XEXP (dest, 1))
&& CONST_INT_P (XEXP (dest, 2))
&& is_a <scalar_int_mode> (GET_MODE (XEXP (dest, 0)),
&dest_mode))
{
width = INTVAL (XEXP (dest, 1));
offset = INTVAL (XEXP (dest, 2));
dest = XEXP (dest, 0);
if (BITS_BIG_ENDIAN)
offset = GET_MODE_PRECISION (dest_mode) - width - offset;
}
}
else
{
if (GET_CODE (dest) == STRICT_LOW_PART)
dest = XEXP (dest, 0);
if (is_a <scalar_int_mode> (GET_MODE (dest), &dest_mode))
{
width = GET_MODE_PRECISION (dest_mode);
offset = 0;
}
}
if (offset >= 0)
{
/* If this is the low part, we're done. */
if (subreg_lowpart_p (dest))
;
/* Handle the case where inner is twice the size of outer. */
else if (GET_MODE_PRECISION (temp_mode)
== 2 * GET_MODE_PRECISION (dest_mode))
offset += GET_MODE_PRECISION (dest_mode);
/* Otherwise give up for now. */
else
offset = -1;
}
if (offset >= 0)
{
rtx inner = SET_SRC (PATTERN (i3));
rtx outer = SET_SRC (temp_expr);
wide_int o = wi::insert (rtx_mode_t (outer, temp_mode),
rtx_mode_t (inner, dest_mode),
offset, width);
combine_merges++;
subst_insn = i3;
subst_low_luid = DF_INSN_LUID (i2);
added_sets_2 = added_sets_1 = added_sets_0 = 0;
i2dest = temp_dest;
i2dest_killed = dead_or_set_p (i2, i2dest);
/* Replace the source in I2 with the new constant and make the
resulting insn the new pattern for I3. Then skip to where we
validate the pattern. Everything was set up above. */
SUBST (SET_SRC (temp_expr),
immed_wide_int_const (o, temp_mode));
newpat = PATTERN (i2);
/* The dest of I3 has been replaced with the dest of I2. */
changed_i3_dest = 1;
goto validate_replacement;
}
}
/* If we have no I1 and I2 looks like:
(parallel [(set (reg:CC X) (compare:CC OP (const_int 0)))
(set Y OP)])
make up a dummy I1 that is
(set Y OP)
and change I2 to be
(set (reg:CC X) (compare:CC Y (const_int 0)))
(We can ignore any trailing CLOBBERs.)
This undoes a previous combination and allows us to match a branch-and-
decrement insn. */
if (!HAVE_cc0 && i1 == 0
&& is_parallel_of_n_reg_sets (PATTERN (i2), 2)
&& (GET_MODE_CLASS (GET_MODE (SET_DEST (XVECEXP (PATTERN (i2), 0, 0))))
== MODE_CC)
&& GET_CODE (SET_SRC (XVECEXP (PATTERN (i2), 0, 0))) == COMPARE
&& XEXP (SET_SRC (XVECEXP (PATTERN (i2), 0, 0)), 1) == const0_rtx
&& rtx_equal_p (XEXP (SET_SRC (XVECEXP (PATTERN (i2), 0, 0)), 0),
SET_SRC (XVECEXP (PATTERN (i2), 0, 1)))
&& !reg_used_between_p (SET_DEST (XVECEXP (PATTERN (i2), 0, 0)), i2, i3)
&& !reg_used_between_p (SET_DEST (XVECEXP (PATTERN (i2), 0, 1)), i2, i3))
{
/* We make I1 with the same INSN_UID as I2. This gives it
the same DF_INSN_LUID for value tracking. Our fake I1 will
never appear in the insn stream so giving it the same INSN_UID
as I2 will not cause a problem. */
i1 = gen_rtx_INSN (VOIDmode, NULL, i2, BLOCK_FOR_INSN (i2),
XVECEXP (PATTERN (i2), 0, 1), INSN_LOCATION (i2),
-1, NULL_RTX);
INSN_UID (i1) = INSN_UID (i2);
SUBST (PATTERN (i2), XVECEXP (PATTERN (i2), 0, 0));
SUBST (XEXP (SET_SRC (PATTERN (i2)), 0),
SET_DEST (PATTERN (i1)));
unsigned int regno = REGNO (SET_DEST (PATTERN (i1)));
SUBST_LINK (LOG_LINKS (i2),
alloc_insn_link (i1, regno, LOG_LINKS (i2)));
}
/* If I2 is a PARALLEL of two SETs of REGs (and perhaps some CLOBBERs),
make those two SETs separate I1 and I2 insns, and make an I0 that is
the original I1. */
if (!HAVE_cc0 && i0 == 0
&& is_parallel_of_n_reg_sets (PATTERN (i2), 2)
&& can_split_parallel_of_n_reg_sets (i2, 2)
&& !reg_used_between_p (SET_DEST (XVECEXP (PATTERN (i2), 0, 0)), i2, i3)
&& !reg_used_between_p (SET_DEST (XVECEXP (PATTERN (i2), 0, 1)), i2, i3)
&& !reg_set_between_p (SET_DEST (XVECEXP (PATTERN (i2), 0, 0)), i2, i3)
&& !reg_set_between_p (SET_DEST (XVECEXP (PATTERN (i2), 0, 1)), i2, i3))
{
/* If there is no I1, there is no I0 either. */
i0 = i1;
/* We make I1 with the same INSN_UID as I2. This gives it
the same DF_INSN_LUID for value tracking. Our fake I1 will
never appear in the insn stream so giving it the same INSN_UID
as I2 will not cause a problem. */
i1 = gen_rtx_INSN (VOIDmode, NULL, i2, BLOCK_FOR_INSN (i2),
XVECEXP (PATTERN (i2), 0, 0), INSN_LOCATION (i2),
-1, NULL_RTX);
INSN_UID (i1) = INSN_UID (i2);
SUBST (PATTERN (i2), XVECEXP (PATTERN (i2), 0, 1));
}
/* Verify that I2 and maybe I1 and I0 can be combined into I3. */
if (!can_combine_p (i2, i3, i0, i1, NULL, NULL, &i2dest, &i2src))
{
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file, "Can't combine i2 into i3\n");
undo_all ();
return 0;
}
if (i1 && !can_combine_p (i1, i3, i0, NULL, i2, NULL, &i1dest, &i1src))
{
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file, "Can't combine i1 into i3\n");
undo_all ();
return 0;
}
if (i0 && !can_combine_p (i0, i3, NULL, NULL, i1, i2, &i0dest, &i0src))
{
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file, "Can't combine i0 into i3\n");
undo_all ();
return 0;
}
/* Record whether i2 and i3 are trivial moves. */
i2_was_move = is_just_move (i2);
i3_was_move = is_just_move (i3);
/* Record whether I2DEST is used in I2SRC and similarly for the other
cases. Knowing this will help in register status updating below. */
i2dest_in_i2src = reg_overlap_mentioned_p (i2dest, i2src);
i1dest_in_i1src = i1 && reg_overlap_mentioned_p (i1dest, i1src);
i2dest_in_i1src = i1 && reg_overlap_mentioned_p (i2dest, i1src);
i0dest_in_i0src = i0 && reg_overlap_mentioned_p (i0dest, i0src);
i1dest_in_i0src = i0 && reg_overlap_mentioned_p (i1dest, i0src);
i2dest_in_i0src = i0 && reg_overlap_mentioned_p (i2dest, i0src);
i2dest_killed = dead_or_set_p (i2, i2dest);
i1dest_killed = i1 && dead_or_set_p (i1, i1dest);
i0dest_killed = i0 && dead_or_set_p (i0, i0dest);
/* For the earlier insns, determine which of the subsequent ones they
feed. */
i1_feeds_i2_n = i1 && insn_a_feeds_b (i1, i2);
i0_feeds_i1_n = i0 && insn_a_feeds_b (i0, i1);
i0_feeds_i2_n = (i0 && (!i0_feeds_i1_n ? insn_a_feeds_b (i0, i2)
: (!reg_overlap_mentioned_p (i1dest, i0dest)
&& reg_overlap_mentioned_p (i0dest, i2src))));
/* Ensure that I3's pattern can be the destination of combines. */
if (! combinable_i3pat (i3, &PATTERN (i3), i2dest, i1dest, i0dest,
i1 && i2dest_in_i1src && !i1_feeds_i2_n,
i0 && ((i2dest_in_i0src && !i0_feeds_i2_n)
|| (i1dest_in_i0src && !i0_feeds_i1_n)),
&i3dest_killed))
{
undo_all ();
return 0;
}
/* See if any of the insns is a MULT operation. Unless one is, we will
reject a combination that is, since it must be slower. Be conservative
here. */
if (GET_CODE (i2src) == MULT
|| (i1 != 0 && GET_CODE (i1src) == MULT)
|| (i0 != 0 && GET_CODE (i0src) == MULT)
|| (GET_CODE (PATTERN (i3)) == SET
&& GET_CODE (SET_SRC (PATTERN (i3))) == MULT))