blob: 4d06322d1e179456af413275fce6e14a1f3aca98 [file] [log] [blame]
/* Optimize by combining instructions for GNU compiler.
Copyright (C) 1987, 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
1999, 2000, 2001, 2002, 2003, 2004 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 2, or (at your option) any later
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING. If not, write to the Free
Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA. */
/* 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 triples of insns A, B and C when
C has a link back to B and B has a link back to A.
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 use_crosses_set_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 created by
flow.c aren't completely updated:
- reg_live_length is not updated
- 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
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 "tm.h"
#include "rtl.h"
#include "tree.h"
#include "tm_p.h"
#include "flags.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "basic-block.h"
#include "insn-config.h"
#include "function.h"
/* Include expr.h after insn-config.h so we get HAVE_conditional_move. */
#include "expr.h"
#include "insn-attr.h"
#include "recog.h"
#include "real.h"
#include "toplev.h"
#include "target.h"
/* It is not safe to use ordinary gen_lowpart in combine.
Use gen_lowpart_for_combine instead. See comments there. */
#define gen_lowpart dont_use_gen_lowpart_you_dummy
/* 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;
/* Vector mapping INSN_UIDs to cuids.
The cuids are like uids but increase monotonically always.
Combine always uses cuids so that it can compare them.
But actually renumbering the uids, which we used to do,
proves to be a bad idea because it makes it hard to compare
the dumps produced by earlier passes with those from later passes. */
static int *uid_cuid;
static int max_uid_cuid;
/* Get the cuid of an insn. */
#define INSN_CUID(INSN) \
(INSN_UID (INSN) > max_uid_cuid ? insn_cuid (INSN) : uid_cuid[INSN_UID (INSN)])
/* In case BITS_PER_WORD == HOST_BITS_PER_WIDE_INT, shifting by
BITS_PER_WORD would invoke undefined behavior. Work around it. */
(((unsigned HOST_WIDE_INT) (val) << (BITS_PER_WORD - 1)) << 1)
#define nonzero_bits(X, M) \
cached_nonzero_bits (X, M, NULL_RTX, VOIDmode, 0)
#define num_sign_bit_copies(X, M) \
cached_num_sign_bit_copies (X, M, NULL_RTX, VOIDmode, 0)
/* Maximum register number, which is the size of the tables below. */
static unsigned int combine_max_regno;
/* Record last point of death of (hard or pseudo) register n. */
static rtx *reg_last_death;
/* Record last point of modification of (hard or pseudo) register n. */
static rtx *reg_last_set;
/* Record the cuid of the last insn that invalidated memory
(anything that writes memory, and subroutine calls, but not pushes). */
static int mem_last_set;
/* Record the cuid of the last CALL_INSN
so we can tell whether a potential combination crosses any calls. */
static int last_call_cuid;
/* 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 subst_insn;
/* This is the lowest CUID that `subst' is currently dealing with.
get_last_value will not return a value if the register was set at or
after this CUID. 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_cuid;
/* 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 added_links_insn;
/* Basic block in which we are performing combines. */
static basic_block this_basic_block;
/* A bitmap indicating which blocks had registers go dead at entry.
After combine, we'll need to re-do global life analysis with
those blocks as starting points. */
static sbitmap refresh_blocks;
/* The next group of arrays 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 arrays:
reg_last_set_value the last value assigned
reg_last_set_label records the value of label_tick when the
register was assigned
reg_last_set_table_tick records the value of label_tick when a
value using the register is assigned
reg_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 reg_last_set_value being valid
and the register being validly contained in some other expression in the
Entry I in reg_last_set_value is valid if it is nonzero, and either
reg_n_sets[i] is 1 or reg_last_set_label[i] == 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_last_set_label[i] < reg_last_set_label[j] or
reg_last_set_invalid[j] is zero.
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)).
reg_last_set_invalid[i] is set nonzero when register I is being assigned
to and reg_last_set_table_tick[i] == label_tick. */
/* Record last value assigned to (hard or pseudo) register n. */
static rtx *reg_last_set_value;
/* Record the value of label_tick when the value for register n is placed in
reg_last_set_value[n]. */
static int *reg_last_set_label;
/* Record the value of label_tick when an expression involving register n
is placed in reg_last_set_value. */
static int *reg_last_set_table_tick;
/* Set nonzero if references to register n in expressions should not be
used. */
static char *reg_last_set_invalid;
/* Incremented for each label. */
static int label_tick;
/* 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 array 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. */
static unsigned HOST_WIDE_INT *reg_nonzero_bits;
/* Mode used to compute significance in reg_nonzero_bits. It is the largest
integer mode that can fit in HOST_BITS_PER_WIDE_INT. */
static enum machine_mode nonzero_bits_mode;
/* Nonzero if we know that a register has some leading bits that are always
equal to the sign bit. */
static unsigned char *reg_sign_bit_copies;
/* Nonzero when reg_nonzero_bits and reg_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;
/* These arrays are maintained in parallel with reg_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. */
static enum machine_mode *reg_last_set_mode;
static unsigned HOST_WIDE_INT *reg_last_set_nonzero_bits;
static char *reg_last_set_sign_bit_copies;
/* Record one modification to rtl structure
to be undone by storing old_contents into *where.
is_int is 1 if the contents are an int. */
struct undo
struct undo *next;
int is_int;
union {rtx r; int i;} old_contents;
union {rtx *r; int *i;} 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 other_insn;
static struct undobuf undobuf;
/* Number of times the pseudo being substituted for
was found and replaced. */
static int n_occurrences;
static void do_SUBST (rtx *, rtx);
static void do_SUBST_INT (int *, int);
static void init_reg_last_arrays (void);
static void setup_incoming_promotions (void);
static void set_nonzero_bits_and_sign_copies (rtx, rtx, void *);
static int cant_combine_insn_p (rtx);
static int can_combine_p (rtx, rtx, rtx, rtx, rtx *, rtx *);
static int combinable_i3pat (rtx, rtx *, rtx, rtx, int, rtx *);
static int contains_muldiv (rtx);
static rtx try_combine (rtx, rtx, rtx, int *);
static void undo_all (void);
static void undo_commit (void);
static rtx *find_split_point (rtx *, rtx);
static rtx subst (rtx, rtx, rtx, int, int);
static rtx combine_simplify_rtx (rtx, enum machine_mode, int, int);
static rtx simplify_if_then_else (rtx);
static rtx simplify_set (rtx);
static rtx simplify_logical (rtx, int);
static rtx expand_compound_operation (rtx);
static rtx expand_field_assignment (rtx);
static rtx make_extraction (enum machine_mode, rtx, HOST_WIDE_INT,
rtx, unsigned HOST_WIDE_INT, int, int, int);
static rtx extract_left_shift (rtx, int);
static rtx make_compound_operation (rtx, enum rtx_code);
static int get_pos_from_mask (unsigned HOST_WIDE_INT,
unsigned HOST_WIDE_INT *);
static rtx force_to_mode (rtx, enum machine_mode,
unsigned HOST_WIDE_INT, rtx, 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);
static rtx make_field_assignment (rtx);
static rtx apply_distributive_law (rtx);
static rtx simplify_and_const_int (rtx, enum machine_mode, rtx,
unsigned HOST_WIDE_INT);
static unsigned HOST_WIDE_INT cached_nonzero_bits (rtx, enum machine_mode,
rtx, enum machine_mode,
unsigned HOST_WIDE_INT);
static unsigned HOST_WIDE_INT nonzero_bits1 (rtx, enum machine_mode, rtx,
enum machine_mode,
unsigned HOST_WIDE_INT);
static unsigned int cached_num_sign_bit_copies (rtx, enum machine_mode, rtx,
enum machine_mode,
unsigned int);
static unsigned int num_sign_bit_copies1 (rtx, enum machine_mode, rtx,
enum machine_mode, unsigned int);
static int merge_outer_ops (enum rtx_code *, HOST_WIDE_INT *, enum rtx_code,
HOST_WIDE_INT, enum machine_mode, int *);
static rtx simplify_shift_const (rtx, enum rtx_code, enum machine_mode, rtx,
static int recog_for_combine (rtx *, rtx, rtx *);
static rtx gen_lowpart_for_combine (enum machine_mode, rtx);
static rtx gen_binary (enum rtx_code, enum 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, rtx);
static void check_promoted_subreg (rtx, rtx);
static void record_dead_and_set_regs_1 (rtx, rtx, void *);
static void record_dead_and_set_regs (rtx);
static int get_last_value_validate (rtx *, rtx, int, int);
static rtx get_last_value (rtx);
static int use_crosses_set_p (rtx, int);
static void reg_dead_at_p_1 (rtx, rtx, void *);
static int reg_dead_at_p (rtx, rtx);
static void move_deaths (rtx, rtx, int, rtx, rtx *);
static int reg_bitfield_target_p (rtx, rtx);
static void distribute_notes (rtx, rtx, rtx, rtx);
static void distribute_links (rtx);
static void mark_used_regs_combine (rtx);
static int insn_cuid (rtx);
static void record_promoted_value (rtx, rtx);
static rtx reversed_comparison (rtx, enum machine_mode, rtx, rtx);
static enum rtx_code combine_reversed_comparison_code (rtx);
/* 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)
/* 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. */
&& GET_CODE (newval) == CONST_INT)
/* Sanity check that we're replacing oldval with a CONST_INT
that is a valid sign-extension for the original mode. */
if (INTVAL (newval) != trunc_int_for_mode (INTVAL (newval),
GET_MODE (oldval)))
abort ();
/* 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. */
if ((GET_CODE (oldval) == SUBREG
|| (GET_CODE (oldval) == ZERO_EXTEND
&& GET_CODE (XEXP (oldval, 0)) == CONST_INT))
abort ();
if (undobuf.frees)
buf = undobuf.frees, undobuf.frees = buf->next;
buf = xmalloc (sizeof (struct undo));
buf->is_int = 0;
buf->where.r = into;
buf->old_contents.r = oldval;
*into = newval;
buf->next = undobuf.undos, undobuf.undos = buf;
/* 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)
if (undobuf.frees)
buf = undobuf.frees, undobuf.frees = buf->next;
buf = xmalloc (sizeof (struct undo));
buf->is_int = 1;
buf->where.i = into;
buf->old_contents.i = oldval;
*into = newval;
buf->next = undobuf.undos, undobuf.undos = buf;
/* 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 combiner has turned an indirect jump
instruction into a direct jump. */
combine_instructions (rtx f, unsigned int nregs)
rtx insn, next;
#ifdef HAVE_cc0
rtx prev;
int i;
rtx links, nextlinks;
int new_direct_jump_p = 0;
combine_attempts = 0;
combine_merges = 0;
combine_extras = 0;
combine_successes = 0;
combine_max_regno = nregs;
reg_nonzero_bits = xcalloc (nregs, sizeof (unsigned HOST_WIDE_INT));
reg_sign_bit_copies = xcalloc (nregs, sizeof (unsigned char));
reg_last_death = xmalloc (nregs * sizeof (rtx));
reg_last_set = xmalloc (nregs * sizeof (rtx));
reg_last_set_value = xmalloc (nregs * sizeof (rtx));
reg_last_set_table_tick = xmalloc (nregs * sizeof (int));
reg_last_set_label = xmalloc (nregs * sizeof (int));
reg_last_set_invalid = xmalloc (nregs * sizeof (char));
reg_last_set_mode = xmalloc (nregs * sizeof (enum machine_mode));
reg_last_set_nonzero_bits = xmalloc (nregs * sizeof (HOST_WIDE_INT));
reg_last_set_sign_bit_copies = xmalloc (nregs * sizeof (char));
init_reg_last_arrays ();
init_recog_no_volatile ();
/* Compute maximum uid value so uid_cuid can be allocated. */
for (insn = f, i = 0; insn; insn = NEXT_INSN (insn))
if (INSN_UID (insn) > i)
i = INSN_UID (insn);
uid_cuid = xmalloc ((i + 1) * sizeof (int));
max_uid_cuid = i;
nonzero_bits_mode = mode_for_size (HOST_BITS_PER_WIDE_INT, MODE_INT, 0);
/* Don't use reg_nonzero_bits when computing it. This can cause problems
when, for example, we have j <<= 1 in a loop. */
nonzero_sign_valid = 0;
/* Compute the mapping from uids to cuids.
Cuids are numbers assigned to insns, like uids,
except that cuids increase monotonically through the code.
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. */
label_tick = 1;
setup_incoming_promotions ();
refresh_blocks = sbitmap_alloc (last_basic_block);
sbitmap_zero (refresh_blocks);
for (insn = f, i = 0; insn; insn = NEXT_INSN (insn))
uid_cuid[INSN_UID (insn)] = ++i;
subst_low_cuid = i;
subst_insn = insn;
if (INSN_P (insn))
note_stores (PATTERN (insn), set_nonzero_bits_and_sign_copies,
record_dead_and_set_regs (insn);
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,
if (GET_CODE (insn) == CODE_LABEL)
nonzero_sign_valid = 1;
/* Now scan all the insns in forward order. */
label_tick = 1;
last_call_cuid = 0;
mem_last_set = 0;
init_reg_last_arrays ();
setup_incoming_promotions ();
FOR_EACH_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 (GET_CODE (insn) == CODE_LABEL)
else if (INSN_P (insn))
/* See if we know about function return values before this
insn based upon SUBREG flags. */
check_promoted_subreg (insn, PATTERN (insn));
/* Try this insn with each insn it links back to. */
for (links = LOG_LINKS (insn); links; links = XEXP (links, 1))
if ((next = try_combine (insn, XEXP (links, 0),
NULL_RTX, &new_direct_jump_p)) != 0)
goto retry;
/* Try each sequence of three linked insns ending with this one. */
for (links = LOG_LINKS (insn); links; links = XEXP (links, 1))
rtx link = XEXP (links, 0);
/* If the linked insn has been replaced by a note, then there
is no point in pursuing this chain any further. */
if (GET_CODE (link) == NOTE)
for (nextlinks = LOG_LINKS (link);
nextlinks = XEXP (nextlinks, 1))
if ((next = try_combine (insn, link,
XEXP (nextlinks, 0),
&new_direct_jump_p)) != 0)
goto retry;
#ifdef HAVE_cc0
/* 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 (GET_CODE (insn) == JUMP_INSN
&& (prev = prev_nonnote_insn (insn)) != 0
&& GET_CODE (prev) == INSN
&& sets_cc0_p (PATTERN (prev)))
if ((next = try_combine (insn, prev,
NULL_RTX, &new_direct_jump_p)) != 0)
goto retry;
for (nextlinks = LOG_LINKS (prev); nextlinks;
nextlinks = XEXP (nextlinks, 1))
if ((next = try_combine (insn, prev,
XEXP (nextlinks, 0),
&new_direct_jump_p)) != 0)
goto retry;
/* Do the same for an insn that explicitly references CC0. */
if (GET_CODE (insn) == INSN
&& (prev = prev_nonnote_insn (insn)) != 0
&& GET_CODE (prev) == INSN
&& 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_RTX, &new_direct_jump_p)) != 0)
goto retry;
for (nextlinks = LOG_LINKS (prev); nextlinks;
nextlinks = XEXP (nextlinks, 1))
if ((next = try_combine (insn, prev,
XEXP (nextlinks, 0),
&new_direct_jump_p)) != 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. */
for (links = LOG_LINKS (insn); links; links = XEXP (links, 1))
if (GET_CODE (XEXP (links, 0)) == INSN
&& GET_CODE (PATTERN (XEXP (links, 0))) == SET
&& reg_mentioned_p (cc0_rtx, SET_SRC (PATTERN (XEXP (links, 0))))
&& (prev = prev_nonnote_insn (XEXP (links, 0))) != 0
&& GET_CODE (prev) == INSN
&& sets_cc0_p (PATTERN (prev))
&& (next = try_combine (insn, XEXP (links, 0),
prev, &new_direct_jump_p)) != 0)
goto retry;
/* Try combining an insn with two different insns whose results it
uses. */
for (links = LOG_LINKS (insn); links; links = XEXP (links, 1))
for (nextlinks = XEXP (links, 1); nextlinks;
nextlinks = XEXP (nextlinks, 1))
if ((next = try_combine (insn, XEXP (links, 0),
XEXP (nextlinks, 0),
&new_direct_jump_p)) != 0)
goto retry;
if (GET_CODE (insn) != NOTE)
record_dead_and_set_regs (insn);
clear_bb_flags ();
EXECUTE_IF_SET_IN_SBITMAP (refresh_blocks, 0, i,
BASIC_BLOCK (i)->flags |= BB_DIRTY);
new_direct_jump_p |= purge_all_dead_edges (0);
delete_noop_moves (f);
update_life_info_in_dirty_blocks (UPDATE_LIFE_GLOBAL_RM_NOTES,
/* Clean up. */
sbitmap_free (refresh_blocks);
free (reg_nonzero_bits);
free (reg_sign_bit_copies);
free (reg_last_death);
free (reg_last_set);
free (reg_last_set_value);
free (reg_last_set_table_tick);
free (reg_last_set_label);
free (reg_last_set_invalid);
free (reg_last_set_mode);
free (reg_last_set_nonzero_bits);
free (reg_last_set_sign_bit_copies);
free (uid_cuid);
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;
/* Make recognizer allow volatile MEMs again. */
init_recog ();
return new_direct_jump_p;
/* Wipe the reg_last_xxx arrays in preparation for another pass. */
static void
init_reg_last_arrays (void)
unsigned int nregs = combine_max_regno;
memset (reg_last_death, 0, nregs * sizeof (rtx));
memset (reg_last_set, 0, nregs * sizeof (rtx));
memset (reg_last_set_value, 0, nregs * sizeof (rtx));
memset (reg_last_set_table_tick, 0, nregs * sizeof (int));
memset (reg_last_set_label, 0, nregs * sizeof (int));
memset (reg_last_set_invalid, 0, nregs * sizeof (char));
memset (reg_last_set_mode, 0, nregs * sizeof (enum machine_mode));
memset (reg_last_set_nonzero_bits, 0, nregs * sizeof (HOST_WIDE_INT));
memset (reg_last_set_sign_bit_copies, 0, nregs * sizeof (char));
/* Set up any promoted values for incoming argument registers. */
static void
setup_incoming_promotions (void)
unsigned int regno;
rtx reg;
enum machine_mode mode;
int unsignedp;
rtx first = get_insns ();
if (targetm.calls.promote_function_args (TREE_TYPE (cfun->decl)))
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
/* Check whether this register can hold an incoming pointer
argument. FUNCTION_ARG_REGNO_P tests outgoing register
numbers, so translate if necessary due to register windows. */
&& (reg = promoted_input_arg (regno, &mode, &unsignedp)) != 0)
(reg, first, gen_rtx_fmt_e ((unsignedp ? ZERO_EXTEND
GET_MODE (reg),
gen_rtx_CLOBBER (mode, const0_rtx)));
/* 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, rtx set,
unsigned int num;
if (GET_CODE (x) == REG
/* If this register is undefined at the start of the file, we can't
say what its contents were. */
&& ! REGNO_REG_SET_P (ENTRY_BLOCK_PTR->next_bb->global_live_at_start, REGNO (x))
if (set == 0 || GET_CODE (set) == CLOBBER)
reg_nonzero_bits[REGNO (x)] = GET_MODE_MASK (GET_MODE (x));
reg_sign_bit_copies[REGNO (x)] = 1;
/* 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
&& SUBREG_REG (SET_DEST (set)) == x))
rtx src = SET_SRC (set);
/* If X is narrower than a word and SRC is a non-negative
constant that would appear negative in the mode of X,
sign-extend it for use in reg_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. */
&& GET_CODE (src) == CONST_INT
&& INTVAL (src) > 0
&& 0 != (INTVAL (src)
<< (GET_MODE_BITSIZE (GET_MODE (x)) - 1))))
src = GEN_INT (INTVAL (src)
| ((HOST_WIDE_INT) (-1)
/* Don't call nonzero_bits if it cannot change anything. */
if (reg_nonzero_bits[REGNO (x)] != ~(unsigned HOST_WIDE_INT) 0)
reg_nonzero_bits[REGNO (x)]
|= nonzero_bits (src, nonzero_bits_mode);
num = num_sign_bit_copies (SET_SRC (set), GET_MODE (x));
if (reg_sign_bit_copies[REGNO (x)] == 0
|| reg_sign_bit_copies[REGNO (x)] > num)
reg_sign_bit_copies[REGNO (x)] = num;
reg_nonzero_bits[REGNO (x)] = GET_MODE_MASK (GET_MODE (x));
reg_sign_bit_copies[REGNO (x)] = 1;
/* See if INSN can be combined into I3. PRED and SUCC are optionally
insns that were previously combined into I3 or that will be combined
into the merger of INSN and 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, rtx i3, rtx pred ATTRIBUTE_UNUSED, rtx succ,
rtx *pdest, rtx *psrc)
int i;
rtx set = 0, src, dest;
rtx p;
rtx link;
int all_adjacent = (succ ? (next_active_insn (insn) == succ
&& next_active_insn (succ) == i3)
: next_active_insn (insn) == i3);
/* 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
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);
rtx note;
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 (GET_CODE (XEXP (elt, 0)) == REG
rtx i3pat = PATTERN (i3);
int i = XVECLEN (i3pat, 0) - 1;
unsigned int regno = REGNO (XEXP (elt, 0));
rtx i3elt = XVECEXP (i3pat, 0, i);
if (GET_CODE (i3elt) == USE
&& GET_CODE (XEXP (i3elt, 0)) == REG
&& (REGNO (XEXP (i3elt, 0)) == regno
? reg_set_between_p (XEXP (elt, 0),
PREV_INSN (insn), i3)
return 0;
while (--i >= 0);
/* We can ignore CLOBBERs. */
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))
&& (!(note = find_reg_note (insn, REG_EH_REGION, NULL_RTX))
|| INTVAL (XEXP (note, 0)) <= 0)
&& ! side_effects_p (elt))
/* 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;
/* 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. */
return 0;
return 0;
if (set == 0)
return 0;
set = expand_field_assignment (set);
src = SET_SRC (set), dest = SET_DEST (set);
/* 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 REG_NO_CONFLICT sequence. */
|| (rtx_equal_p (src, dest) && find_reg_note (insn, REG_EQUAL, NULL_RTX))
/* Can't merge an ASM_OPERANDS. */
/* Can't merge a function call. */
|| GET_CODE (src) == CALL
/* Don't eliminate a function call argument. */
|| (GET_CODE (i3) == CALL_INSN
&& (find_reg_fusage (i3, USE, dest)
|| (GET_CODE (dest) == REG
&& global_regs[REGNO (dest)])))
/* Don't substitute into an incremented register. */
|| FIND_REG_INC_NOTE (i3, dest)
|| (succ && FIND_REG_INC_NOTE (succ, dest))
#if 0
/* Don't combine the end of a libcall into anything. */
/* ??? This gives worse code, and appears to be unnecessary, since no
pass after flow uses REG_LIBCALL/REG_RETVAL notes. Local-alloc does
use REG_RETVAL notes for noconflict blocks, but other code here
makes sure that those insns don't disappear. */
|| find_reg_note (insn, REG_RETVAL, NULL_RTX)
/* Make sure that DEST is not used after SUCC but before I3. */
|| (succ && ! all_adjacent
&& reg_used_between_p (dest, succ, i3))
/* 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; reg_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
&& (((GET_CODE (src) != MEM
|| ! find_reg_note (insn, REG_EQUIV, src))
&& use_crosses_set_p (src, INSN_CUID (insn)))
/* If there is a REG_NO_CONFLICT note for DEST in I3 or SUCC, we get
better register allocation by not doing the combine. */
|| find_reg_note (i3, REG_NO_CONFLICT, dest)
|| (succ && find_reg_note (succ, REG_NO_CONFLICT, dest))
/* 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 special case, because it helps -fforce-addr a lot. */
|| (INSN_CUID (insn) < last_call_cuid && ! CONSTANT_P (src)))
return 0;
/* DEST must either be a REG or CC0. */
if (GET_CODE (dest) == REG)
/* 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 (GET_CODE (src) == REG
&& ! 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. */
&& ! HARD_REGNO_MODE_OK (REGNO (src), GET_MODE (src)))))
return 0;
else if (GET_CODE (dest) != CC0)
return 0;
/* Don't substitute for a register intended as a clobberable operand.
Similarly, don't substitute an expression containing a register that
will be clobbered in I3. */
for (i = XVECLEN (PATTERN (i3), 0) - 1; i >= 0; i--)
&& (reg_overlap_mentioned_p (XEXP (XVECEXP (PATTERN (i3), 0, i), 0),
|| rtx_equal_p (XEXP (XVECEXP (PATTERN (i3), 0, i), 0), dest)))
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 succ doesn't contain a volatile reference. */
if (succ != 0 && volatile_refs_p (PATTERN (succ)))
return 0;
for (p = NEXT_INSN (insn); p != i3; p = NEXT_INSN (p))
if (INSN_P (p) && p != succ && volatile_refs_p (PATTERN (p)))
return 0;
/* 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. */
return 0;
/* If there are any volatile insns between INSN and I3, reject, because
they might affect machine state. */
for (p = NEXT_INSN (insn); p != i3; p = NEXT_INSN (p))
if (INSN_P (p) && p != succ && volatile_insn_p (PATTERN (p)))
return 0;
/* If INSN or I2 contains an autoincrement or autodecrement,
make sure that register is not used between there and I3,
and not already used in I3 either.
Also insist that I3 not be a jump; if it were one
and the incremented register were spilled, we would lose. */
for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
if (REG_NOTE_KIND (link) == REG_INC
&& (GET_CODE (i3) == JUMP_INSN
|| reg_used_between_p (XEXP (link, 0), insn, i3)
|| reg_overlap_mentioned_p (XEXP (link, 0), PATTERN (i3))))
return 0;
#ifdef HAVE_cc0
/* 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. */
p = prev_nonnote_insn (insn);
if (p && p != pred && GET_CODE (p) == INSN && 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 or I1DEST as doing
so would produce an insn that is not equivalent to the original insns.
(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.
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 i3, rtx *loc, rtx i2dest, rtx i1dest,
int i1_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;
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 &&
(GET_CODE (inner_dest) != MEM
|| rtx_equal_p (i2dest, inner_dest)
|| (i1dest && rtx_equal_p (i1dest, inner_dest)))
&& (reg_overlap_mentioned_p (i2dest, inner_dest)
|| (i1dest && reg_overlap_mentioned_p (i1dest, 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
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. */
|| (GET_CODE (inner_dest) == REG
&& (! HARD_REGNO_MODE_OK (REGNO (inner_dest),
GET_MODE (inner_dest))))
|| (i1_not_in_src && reg_overlap_mentioned_p (i1dest, src)))
return 0;
/* If DEST is used in I3, it is being killed in this insn,
so record that for later.
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. */
if (pi3dest_killed && GET_CODE (dest) == REG
&& reg_referenced_p (dest, PATTERN (i3))
|| ! fixed_regs [REGNO (dest)])
if (*pi3dest_killed)
return 0;
*pi3dest_killed = dest;
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,
i1_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 ! (GET_CODE (XEXP (x, 1)) == CONST_INT
&& exact_log2 (INTVAL (XEXP (x, 1))) >= 0);
switch (GET_RTX_CLASS (GET_CODE (x)))
case 'c': case '<': case '2':
return contains_muldiv (XEXP (x, 0))
|| contains_muldiv (XEXP (x, 1));
case '1':
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)
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 (! 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, we may abort in reload.
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)
&& ! fixed_regs[REGNO (src)]
&& ! fixed_regs[REGNO (dest)]
return 1;
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)
rtx *loc;
/* For notes, be conservative and simply remove them. */
loc = &REG_NOTES (insn);
while (*loc)
enum reg_note kind = REG_NOTE_KIND (*loc);
if (kind == REG_EQUAL || kind == REG_EQUIV)
*loc = XEXP (*loc, 1);
loc = &XEXP (*loc, 1);
/* 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. */
distribute_links (gen_rtx_INSN_LIST (VOIDmode, insn, NULL_RTX));
/* Try to combine the insns I1 and I2 into I3.
Here I1 and I2 appear earlier than I3.
I1 can be zero; then we combine just I2 into I3.
If we are combining three insns and the resulting insn is not recognized,
try splitting it into two insns. If that happens, I2 and I3 are retained
and I1 is pseudo-deleted by turning it into a NOTE. Otherwise, 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. */
static rtx
try_combine (rtx i3, rtx i2, rtx i1, int *new_direct_jump_p)
/* New patterns for I3 and I2, respectively. */
rtx newpat, newi2pat = 0;
int substed_i2 = 0, substed_i1 = 0;
/* Indicates need to preserve SET in I1 or I2 in I3 if it is not dead. */
int added_sets_1, added_sets_2;
/* Total number of SETs to put into I3. */
int total_sets;
/* Nonzero is I2's body now appears in I3. */
int i2_is_used;
/* 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 and I1. */
rtx i2dest, i2src, i1dest = 0, i1src = 0;
/* PATTERN (I2), or a copy of it in certain cases. */
rtx i2pat;
/* 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 i1_feeds_i3 = 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 maxreg;
rtx temp;
rtx link;
int i;
/* Exit early if one of the insns involved can't be used for
combinations. */
if (cant_combine_insn_p (i3)
|| cant_combine_insn_p (i2)
|| (i1 && cant_combine_insn_p (i1))
/* We also can't do anything if I3 has a
REG_LIBCALL note since we don't want to disrupt the contiguity of a
libcall. */
#if 0
/* ??? This gives worse code, and appears to be unnecessary, since no
pass after flow uses REG_LIBCALL/REG_RETVAL notes. */
|| find_reg_note (i3, REG_LIBCALL, NULL_RTX)
return 0;
undobuf.other_insn = 0;
/* Reset the hard register usage information. */
CLEAR_HARD_REG_SET (newpat_used_regs);
/* If I1 and I2 both feed I3, they can be in any order. To simplify the
code below, set I1 to be the earlier of the two insns. */
if (i1 && INSN_CUID (i1) > INSN_CUID (i2))
temp = i1, i1 = i2, i2 = temp;
added_links_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 CLOBBER or PARALLELs.
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 && GET_CODE (i3) == INSN && GET_CODE (PATTERN (i3)) == SET
&& find_reg_note (i3, REG_DEAD, SET_SRC (PATTERN (i3)))
&& ! 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. */
&& ! reg_overlap_mentioned_p (SET_SRC (PATTERN (i3)),
&& next_real_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. */
for (i = 0; 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))))
if (i == XVECLEN (p2, 0))
for (i = 0; i < XVECLEN (p2, 0); i++)
if ((GET_CODE (XVECEXP (p2, 0, i)) == SET
|| GET_CODE (XVECEXP (p2, 0, i)) == CLOBBER)
&& SET_DEST (XVECEXP (p2, 0, i)) == SET_SRC (PATTERN (i3)))
subst_insn = i3;
subst_low_cuid = INSN_CUID (i2);
added_sets_2 = added_sets_1 = 0;
i2dest = SET_SRC (PATTERN (i3));
/* 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. */
newpat = p2;
i3_subst_into_i2 = 1;
goto validate_replacement;
/* If I2 is setting a double-word pseudo to a constant and I3 is setting
one of those words to another constant, merge them by making a new
constant. */
if (i1 == 0
&& (temp = single_set (i2)) != 0
&& (GET_CODE (SET_SRC (temp)) == CONST_INT
&& GET_CODE (SET_DEST (temp)) == REG
if (GET_CODE (SET_SRC (temp)) == CONST_INT)
lo = INTVAL (SET_SRC (temp)), hi = lo < 0 ? -1 : 0;
if (subreg_lowpart_p (SET_DEST (PATTERN (i3))))
/* We don't handle the case of the target word being wider
than a host wide int. */
abort ();
lo |= (INTVAL (SET_SRC (PATTERN (i3)))
int sign = -(int) ((unsigned HOST_WIDE_INT) lo
if (hi == sign)
hi = lo < 0 ? -1 : 0;
/* We don't handle the case of the higher word not fitting
entirely in either hi or lo. */
abort ();
subst_insn = i3;
subst_low_cuid = INSN_CUID (i2);
added_sets_2 = added_sets_1 = 0;
i2dest = SET_DEST (temp);
SUBST (SET_SRC (temp),
immed_double_const (lo, hi, GET_MODE (SET_DEST (temp))));
newpat = PATTERN (i2);
goto validate_replacement;
#ifndef HAVE_cc0
/* 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 (i1 == 0 && GET_CODE (PATTERN (i2)) == PARALLEL
&& XVECLEN (PATTERN (i2), 0) >= 2
&& GET_CODE (XVECEXP (PATTERN (i2), 0, 0)) == SET
&& XEXP (SET_SRC (XVECEXP (PATTERN (i2), 0, 0)), 1) == const0_rtx
&& GET_CODE (XVECEXP (PATTERN (i2), 0, 1)) == SET
&& rtx_equal_p (XEXP (SET_SRC (XVECEXP (PATTERN (i2), 0, 0)), 0),
SET_SRC (XVECEXP (PATTERN (i2), 0, 1))))
for (i = XVECLEN (PATTERN (i2), 0) - 1; i >= 2; i--)
if (i == 1)
/* We make I1 with the same INSN_UID as I2. This gives it
the same INSN_CUID 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, INSN_UID (i2), NULL_RTX, i2,
XVECEXP (PATTERN (i2), 0, 1), -1, NULL_RTX,
/* Verify that I2 and I1 are valid for combining. */
if (! can_combine_p (i2, i3, i1, NULL_RTX, &i2dest, &i2src)
|| (i1 && ! can_combine_p (i1, i3, NULL_RTX, i2, &i1dest, &i1src)))
undo_all ();
return 0;
/* 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);
/* See if I1 directly feeds into I3. It does if I1DEST is not used
in I2SRC. */
i1_feeds_i3 = i1 && ! reg_overlap_mentioned_p (i1dest, i2src);
/* Ensure that I3's pattern can be the destination of combines. */
if (! combinable_i3pat (i3, &PATTERN (i3), i2dest, i1dest,
i1 && i2dest_in_i1src && i1_feeds_i3,
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)
|| (GET_CODE (PATTERN (i3)) == SET
have_mult = 1;
/* If I3 has an inc, then give up if I1 or I2 uses the reg that is inc'd.
We used to do this EXCEPT in one case: I3 has a post-inc in an
output operand. However, that exception can give rise to insns like
mov r3,(r3)+
which is a famous insn on the PDP-11 where the value of r3 used as the
source was model-dependent. Avoid this sort of thing. */
#if 0
if (!(GET_CODE (PATTERN (i3)) == SET
/* It's not the exception. */
for (link = REG_NOTES (i3); link; link = XEXP (link, 1))
if (REG_NOTE_KIND (link) == REG_INC
&& (reg_overlap_mentioned_p (XEXP (link, 0), PATTERN (i2))
|| (i1 != 0
&& reg_overlap_mentioned_p (XEXP (link, 0), PATTERN (i1)))))
undo_all ();
return 0;
/* See if the SETs in I1 or I2 need to be kept around in the merged
instruction: whenever the value set there is still needed past I3.
For the SETs in I2, this is easy: we see if I2DEST dies or is set in I3.
For the SET in I1, we have two cases: If I1 and I2 independently
feed into I3, the set in I1 needs to be kept around if I1DEST dies
or is set in I3. Otherwise (if I1 feeds I2 which feeds I3), the set
in I1 needs to be kept around unless I1DEST dies or is set in either
I2 or I3. We can distinguish these cases by seeing if I2SRC mentions
I1DEST. If so, we know I1 feeds into I2. */
added_sets_2 = ! dead_or_set_p (i3, i2dest);
= i1 && ! (i1_feeds_i3 ? dead_or_set_p (i3, i1dest)
: (dead_or_set_p (i3, i1dest) || dead_or_set_p (i2, i1dest)));
/* If the set in I2 needs to be kept around, we must make a copy of
PATTERN (I2), so that when we substitute I1SRC for I1DEST in
PATTERN (I2), we are only substituting for the original I1DEST, not into
an already-substituted copy. This also prevents making self-referential
rtx. If I2 is a PARALLEL, we just need the piece that assigns I2SRC to
I2DEST. */
i2pat = (GET_CODE (PATTERN (i2)) == PARALLEL
? gen_rtx_SET (VOIDmode, i2dest, i2src)
: PATTERN (i2));
if (added_sets_2)
i2pat = copy_rtx (i2pat);
/* Substitute in the latest insn for the regs set by the earlier ones. */
maxreg = max_reg_num ();
subst_insn = i3;
/* It is possible that the source of I2 or I1 may be performing an
unneeded operation, such as a ZERO_EXTEND of something that is known
to have the high part zero. Handle that case by letting subst look at
the innermost one of them.
Another way to do this would be to have a function that tries to
simplify a single insn instead of merging two or more insns. We don't
do this because of the potential of infinite loops and because
of the potential extra memory required. However, doing it the way
we are is a bit of a kludge and doesn't catch all cases.
But only do this if -fexpensive-optimizations since it slows things down
and doesn't usually win. */
if (flag_expensive_optimizations)
/* Pass pc_rtx so no substitutions are done, just simplifications.
The cases that we are interested in here do not involve the few
cases were is_replaced is checked. */
if (i1)
subst_low_cuid = INSN_CUID (i1);
i1src = subst (i1src, pc_rtx, pc_rtx, 0, 0);
subst_low_cuid = INSN_CUID (i2);
i2src = subst (i2src, pc_rtx, pc_rtx, 0, 0);
#ifndef HAVE_cc0
/* Many machines that don't use CC0 have insns that can both perform an
arithmetic operation and set the condition code. These operations will
be represented as a PARALLEL with the first element of the vector
being a COMPARE of an arithmetic operation with the constant zero.
The second element of the vector will set some pseudo to the result
of the same arithmetic operation. If we simplify the COMPARE, we won't
match such a pattern and so will generate an extra insn. Here we test
for this case, where both the comparison and the operation result are
needed, and make the PARALLEL by just replacing I2DEST in I3SRC with
I2SRC. Later we will make the PARALLEL that contains I2. */
if (i1 == 0 && added_sets_2 && GET_CODE (PATTERN (i3)) == SET
&& XEXP (SET_SRC (PATTERN (i3)), 1) == const0_rtx
&& rtx_equal_p (XEXP (SET_SRC (PATTERN (i3)), 0), i2dest))
rtx *cc_use;
enum machine_mode compare_mode;
newpat = PATTERN (i3);
SUBST (XEXP (SET_SRC (newpat), 0), i2src);
i2_is_used = 1;
/* See if a COMPARE with the operand we substituted in should be done
with the mode that is currently being used. If not, do the same
processing we do in `subst' for a SET; namely, if the destination
is used only once, try to replace it with a register of the proper
mode and also replace the COMPARE. */
if (undobuf.other_insn == 0
&& (cc_use = find_single_use (SET_DEST (newpat), i3,
&& ((compare_mode = SELECT_CC_MODE (GET_CODE (*cc_use),
i2src, const0_rtx))
!= GET_MODE (SET_DEST (newpat))))
unsigned int regno = REGNO (SET_DEST (newpat));
rtx new_dest = gen_rtx_REG (compare_mode, regno);
|| (REG_N_SETS (regno) == 1 && ! added_sets_2
&& ! REG_USERVAR_P (SET_DEST (newpat))))
SUBST (regno_reg_rtx[regno], new_dest);
SUBST (SET_DEST (newpat), new_dest);
SUBST (XEXP (*cc_use, 0), new_dest);
SUBST (SET_SRC (newpat),
gen_rtx_COMPARE (compare_mode, i2src, const0_rtx));
undobuf.other_insn = 0;
n_occurrences = 0; /* `subst' counts here */
/* If I1 feeds into I2 (not into I3) and I1DEST is in I1SRC, we
need to make a unique copy of I2SRC each time we substitute it
to avoid self-referential rtl. */
subst_low_cuid = INSN_CUID (i2);
newpat = subst (PATTERN (i3), i2dest, i2src, 0,
! i1_feeds_i3 && i1dest_in_i1src);
substed_i2 = 1;
/* Record whether i2's body now appears within i3's body. */
i2_is_used = n_occurrences;
/* If we already got a failure, don't try to do more. Otherwise,
try to substitute in I1 if we have it. */
if (i1 && GET_CODE (newpat) != CLOBBER)
/* Before we can do this substitution, we must redo the test done
above (see detailed comments there) that ensures that I1DEST
isn't mentioned in any SETs in NEWPAT that are field assignments. */
if (! combinable_i3pat (NULL_RTX, &newpat, i1dest, NULL_RTX,
0, (rtx*) 0))
undo_all ();
return 0;
n_occurrences = 0;
subst_low_cuid = INSN_CUID (i1);
newpat = subst (newpat, i1dest, i1src, 0, 0);
substed_i1 = 1;
/* Fail if an autoincrement side-effect has been duplicated. Be careful
to count all the ways that I2SRC and I1SRC can be used. */
if ((FIND_REG_INC_NOTE (i2, NULL_RTX) != 0
&& i2_is_used + added_sets_2 > 1)
|| (i1 != 0 && FIND_REG_INC_NOTE (i1, NULL_RTX) != 0
&& (n_occurrences + added_sets_1 + (added_sets_2 && ! i1_feeds_i3)
> 1))
/* Fail if we tried to make a new register (we used to abort, but there's
really no reason to). */
|| max_reg_num () != maxreg
/* Fail if we couldn't do something and have a CLOBBER. */
|| GET_CODE (newpat) == CLOBBER
/* Fail if this new pattern is a MULT and we didn't have one before
at the outer level. */
|| (GET_CODE (newpat) == SET && GET_CODE (SET_SRC (newpat)) == MULT
&& ! have_mult))
undo_all ();
return 0;
/* If the actions of the earlier insns must be kept
in addition to substituting them into the latest one,
we must make a new PARALLEL for the latest insn
to hold additional the SETs. */
if (added_sets_1 || added_sets_2)
if (GET_CODE (newpat) == PARALLEL)
rtvec old = XVEC (newpat, 0);
total_sets = XVECLEN (newpat, 0) + added_sets_1 + added_sets_2;
newpat = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (total_sets));
memcpy (XVEC (newpat, 0)->elem, &old->elem[0],
sizeof (old->elem[0]) * old->num_elem);
rtx old = newpat;
total_sets = 1 + added_sets_1 + added_sets_2;
newpat = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (total_sets));
XVECEXP (newpat, 0, 0) = old;
if (added_sets_1)
XVECEXP (newpat, 0, --total_sets)
? gen_rtx_SET (VOIDmode, i1dest, i1src) : PATTERN (i1));
if (added_sets_2)
/* If there is no I1, use I2's body as is. We used to also not do
the subst call below if I2 was substituted into I3,
but that could lose a simplification. */
if (i1 == 0)
XVECEXP (newpat, 0, --total_sets) = i2pat;
/* See comment where i2pat is assigned. */
XVECEXP (newpat, 0, --total_sets)
= subst (i2pat, i1dest, i1src, 0, 0);
/* We come here when we are replacing a destination in I2 with the
destination of I3. */
/* Note which hard regs this insn has as inputs. */
mark_used_regs_combine (newpat);
/* Is the result of combination a valid instruction? */
insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes);
/* If the result isn't valid, see if it is a PARALLEL of two SETs where
the second SET's destination is a register that is unused and isn't
marked as an instruction that might trap in an EH region. In that case,
we just need the first SET. This can occur when simplifying a divmod
insn. We *must* test for this case here because the code below that
splits two independent SETs doesn't handle this case correctly when it
updates the register status. Also check the case where the first
SET's destination is unused. That would not cause incorrect code, but
does cause an unneeded insn to remain. */
if (insn_code_number < 0 && GET_CODE (newpat) == PARALLEL
&& XVECLEN (newpat, 0) == 2
&& GET_CODE (XVECEXP (newpat, 0, 0)) == SET
&& GET_CODE (XVECEXP (newpat, 0, 1)) == SET
&& asm_noperands (newpat) < 0)
rtx set0 = XVECEXP (newpat, 0, 0);
rtx set1 = XVECEXP (newpat, 0, 1);
rtx note;
if (((GET_CODE (SET_DEST (set1)) == REG
&& find_reg_note (i3, REG_UNUSED, SET_DEST (set1)))
|| (GET_CODE (SET_DEST (set1)) == SUBREG
&& find_reg_note (i3, REG_UNUSED, SUBREG_REG (SET_DEST (set1)))))
&& (!(note = find_reg_note (i3, REG_EH_REGION, NULL_RTX))
|| INTVAL (XEXP (note, 0)) <= 0)
&& ! side_effects_p (SET_SRC (set1)))
newpat = set0;
insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes);
else if (((GET_CODE (SET_DEST (set0)) == REG
&& find_reg_note (i3, REG_UNUSED, SET_DEST (set0)))
|| (GET_CODE (SET_DEST (set0)) == SUBREG
&& find_reg_note (i3, REG_UNUSED,
&& (!(note = find_reg_note (i3, REG_EH_REGION, NULL_RTX))
|| INTVAL (XEXP (note, 0)) <= 0)
&& ! side_effects_p (SET_SRC (set0)))
newpat = set1;
insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes);
if (insn_code_number >= 0)
/* If we will be able to accept this, we have made a
change to the destination of I3. This requires us to
do a few adjustments. */
PATTERN (i3) = newpat;
adjust_for_new_dest (i3);
/* If we were combining three insns and the result is a simple SET
with no ASM_OPERANDS that wasn't recognized, try to split it into two
insns. There are two ways to do this. It can be split using a
machine-specific method (like when you have an addition of a large
constant) or by combine in the function find_split_point. */
if (i1 && insn_code_number < 0 && GET_CODE (newpat) == SET
&& asm_noperands (newpat) < 0)
rtx m_split, *split;
rtx ni2dest = i2dest;
/* See if the MD file can split NEWPAT. If it can't, see if letting it
use I2DEST as a scratch register will help. In the latter case,
convert I2DEST to the mode of the source of NEWPAT if we can. */
m_split = split_insns (newpat, i3);
/* We can only use I2DEST as a scratch reg if it doesn't overlap any
inputs of NEWPAT. */
/* ??? If I2DEST is not safe, and I1DEST exists, then it would be
possible to try that as a scratch reg. This would require adding
more code to make it work though. */
if (m_split == 0 && ! reg_overlap_mentioned_p (ni2dest, newpat))
/* If I2DEST is a hard register or the only use of a pseudo,
we can change its mode. */
if (GET_MODE (SET_DEST (newpat)) != GET_MODE (i2dest)
&& GET_MODE (SET_DEST (newpat)) != VOIDmode
&& GET_CODE (i2dest) == REG
|| (REG_N_SETS (REGNO (i2dest)) == 1 && ! added_sets_2
&& ! REG_USERVAR_P (i2dest))))
ni2dest = gen_rtx_REG (GET_MODE (SET_DEST (newpat)),
REGNO (i2dest));
m_split = split_insns (gen_rtx_PARALLEL
gen_rtvec (2, newpat,
gen_rtx_CLOBBER (VOIDmode,
/* If the split with the mode-changed register didn't work, try
the original register. */
if (! m_split && ni2dest != i2dest)
ni2dest = i2dest;
m_split = split_insns (gen_rtx_PARALLEL
gen_rtvec (2, newpat,
gen_rtx_CLOBBER (VOIDmode,
if (m_split && NEXT_INSN (m_split) == NULL_RTX)
m_split = PATTERN (m_split);
insn_code_number = recog_for_combine (&m_split, i3, &new_i3_notes);
if (insn_code_number >= 0)
newpat = m_split;
else if (m_split && NEXT_INSN (NEXT_INSN (m_split)) == NULL_RTX
&& (next_real_insn (i2) == i3
|| ! use_crosses_set_p (PATTERN (m_split), INSN_CUID (i2))))
rtx i2set, i3set;
rtx newi3pat = PATTERN (NEXT_INSN (m_split));
newi2pat = PATTERN (m_split);
i3set = single_set (NEXT_INSN (m_split));
i2set = single_set (m_split);
/* In case we changed the mode of I2DEST, replace it in the
pseudo-register table here. We can't do it above in case this
code doesn't get executed and we do a split the other way. */
SUBST (regno_reg_rtx[REGNO (i2dest)], ni2dest);
i2_code_number = recog_for_combine (&newi2pat, i2, &new_i2_notes);
/* If I2 or I3 has multiple SETs, we won't know how to track
register status, so don't use these insns. If I2's destination
is used between I2 and I3, we also can't use these insns. */
if (i2_code_number >= 0 && i2set && i3set
&& (next_real_insn (i2) == i3
|| ! reg_used_between_p (SET_DEST (i2set), i2, i3)))
insn_code_number = recog_for_combine (&newi3pat, i3,
if (insn_code_number >= 0)
newpat = newi3pat;
/* It is possible that both insns now set the destination of I3.
If so, we must show an extra use of it. */
if (insn_code_number >= 0)
rtx new_i3_dest = SET_DEST (i3set);
rtx new_i2_dest = SET_DEST (i2set);
while (GET_CODE (new_i3_dest) == ZERO_EXTRACT
|| GET_CODE (new_i3_dest) == STRICT_LOW_PART
|| GET_CODE (new_i3_dest) == SUBREG)
new_i3_dest = XEXP (new_i3_dest, 0);
while (GET_CODE (new_i2_dest) == ZERO_EXTRACT
|| GET_CODE (new_i2_dest) == STRICT_LOW_PART
|| GET_CODE (new_i2_dest) == SUBREG)
new_i2_dest = XEXP (new_i2_dest, 0);
if (GET_CODE (new_i3_dest) == REG
&& GET_CODE (new_i2_dest) == REG
&& REGNO (new_i3_dest) == REGNO (new_i2_dest))
REG_N_SETS (REGNO (new_i2_dest))++;
/* If we can split it and use I2DEST, go ahead and see if that
helps things be recognized. Verify that none of the registers
are set between I2 and I3. */
if (insn_code_number < 0 && (split = find_split_point (&newpat, i3)) != 0
#ifdef HAVE_cc0
&& GET_CODE (i2dest) == REG
/* We need I2DEST in the proper mode. If it is a hard register
or the only use of a pseudo, we can change its mode. */
&& (GET_MODE (*split) == GET_MODE (i2dest)
|| GET_MODE (*split) == VOIDmode
|| (REG_N_SETS (REGNO (i2dest)) == 1 && ! added_sets_2
&& ! REG_USERVAR_P (i2dest)))
&& (next_real_insn (i2) == i3
|| ! use_crosses_set_p (*split, INSN_CUID (i2)))
/* We can't overwrite I2DEST if its value is still used by
&& ! reg_referenced_p (i2dest, newpat))
rtx newdest = i2dest;
enum rtx_code split_code = GET_CODE (*split);
enum machine_mode split_mode = GET_MODE (*split);
/* Get NEWDEST as a register in the proper mode. We have already
validated that we can do this. */
if (GET_MODE (i2dest) != split_mode && split_mode != VOIDmode)
newdest = gen_rtx_REG (split_mode, REGNO (i2dest));
SUBST (regno_reg_rtx[REGNO (i2dest)], newdest);
/* If *SPLIT is a (mult FOO (const_int pow2)), convert it to
an ASHIFT. This can occur if it was inside a PLUS and hence
appeared to be a memory address. This is a kludge. */
if (split_code == MULT
&& GET_CODE (XEXP (*split, 1)) == CONST_INT
&& INTVAL (XEXP (*split, 1)) > 0
&& (i = exact_log2 (INTVAL (XEXP (*split, 1)))) >= 0)
SUBST (*split, gen_rtx_ASHIFT (split_mode,
XEXP (*split, 0), GEN_INT (i)));
/* Update split_code because we may not have a multiply
anymore. */
split_code = GET_CODE (*split);
/* If *SPLIT is a paradoxical SUBREG, when we split it, it should
be written as a ZERO_EXTEND. */
if (split_code == SUBREG && GET_CODE (SUBREG_REG (*split)) == MEM)
/* Or as a SIGN_EXTEND if LOAD_EXTEND_OP says that that's
what it really is. */
SUBST (*split, gen_rtx_SIGN_EXTEND (split_mode,
SUBREG_REG (*split)));
SUBST (*split, gen_rtx_ZERO_EXTEND (split_mode,
SUBREG_REG (*split)));
newi2pat = gen_rtx_SET (VOIDmode, newdest, *split);
SUBST (*split, newdest);
i2_code_number = recog_for_combine (&newi2pat, i2, &new_i2_notes);
/* If the split point was a MULT and we didn't have one before,
don't use one now. */
if (i2_code_number >= 0 && ! (split_code == MULT && ! have_mult))
insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes);
/* Check for a case where we loaded from memory in a narrow mode and
then sign extended it, but we need both registers. In that case,
we have a PARALLEL with both loads from the same memory location.
We can split this into a load from memory followed by a register-register
copy. This saves at least one insn, more if register allocation can
eliminate the copy.
We cannot do this if the destination of the first assignment is a
condition code register or cc0. We eliminate this case by making sure
the SET_DEST and SET_SRC have the same mode.
We cannot do this if the destination of the second assignment is
a register that we have already assumed is zero-extended. Similarly
for a SUBREG of such a register. */
else if (i1 && insn_code_number < 0 && asm_noperands (newpat) < 0
&& GET_CODE (newpat) == PARALLEL
&& XVECLEN (newpat, 0) == 2
&& GET_CODE (XVECEXP (newpat, 0, 0)) == SET
&& GET_CODE (SET_SRC (XVECEXP (newpat, 0, 0))) == SIGN_EXTEND
&& (GET_MODE (SET_DEST (XVECEXP (newpat, 0, 0)))
== GET_MODE (SET_SRC (XVECEXP (newpat, 0, 0))))
&& GET_CODE (XVECEXP (newpat, 0, 1)) == SET
&& rtx_equal_p (SET_SRC (XVECEXP (newpat, 0, 1)),
XEXP (SET_SRC (XVECEXP (newpat, 0, 0)), 0))
&& ! use_crosses_set_p (SET_SRC (XVECEXP (newpat, 0, 1)),
&& ! (temp = SET_DEST (XVECEXP (newpat, 0, 1)),
(GET_CODE (temp) == REG
&& reg_nonzero_bits[REGNO (temp)] != 0
&& (reg_nonzero_bits[REGNO (temp)]
!= GET_MODE_MASK (word_mode))))
&& ! (GET_CODE (SET_DEST (XVECEXP (newpat, 0, 1))) == SUBREG
&& (temp = SUBREG_REG (SET_DEST (XVECEXP (newpat, 0, 1))),
(GET_CODE (temp) == REG
&& reg_nonzero_bits[REGNO (temp)] != 0
&& (reg_nonzero_bits[REGNO (temp)]
!= GET_MODE_MASK (word_mode)))))
&& ! reg_overlap_mentioned_p (SET_DEST (XVECEXP (newpat, 0, 1)),
SET_SRC (XVECEXP (newpat, 0, 1)))
&& ! find_reg_note (i3, REG_UNUSED,
SET_DEST (XVECEXP (newpat, 0, 0))))
rtx ni2dest;
newi2pat = XVECEXP (newpat, 0, 0);
ni2dest = SET_DEST (XVECEXP (newpat, 0, 0));
newpat = XVECEXP (newpat, 0, 1);
SUBST (SET_SRC (newpat),
gen_lowpart_for_combine (GET_MODE (SET_SRC (newpat)), ni2dest));
i2_code_number = recog_for_combine (&newi2pat, i2, &new_i2_notes);
if (i2_code_number >= 0)
insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes);
if (insn_code_number >= 0)
rtx insn;
rtx link;
/* If we will be able to accept this, we have made a change to the
destination of I3. This requires us to do a few adjustments. */
PATTERN (i3) = newpat;
adjust_for_new_dest (i3);
/* I3 now uses what used to be its destination and which is
now I2's destination. That means we need a LOG_LINK from
I3 to I2. But we used to have one, so we still will.
However, some later insn might be using I2's dest and have
a LOG_LINK pointing at I3. We must remove this link.
The simplest way to remove the link is to point it at I1,
which we know will be a NOTE. */
for (insn = NEXT_INSN (i3);
insn && (this_basic_block->next_bb == EXIT_BLOCK_PTR
|| insn != BB_HEAD (this_basic_block->next_bb));
insn = NEXT_INSN (insn))
if (INSN_P (insn) && reg_referenced_p (ni2dest, PATTERN (insn)))
for (link = LOG_LINKS (insn); link;
link = XEXP (link, 1))
if (XEXP (link, 0) == i3)
XEXP (link, 0) = i1;
/* Similarly, check for a case where we have a PARALLEL of two independent
SETs but we started with three insns. In this case, we can do the sets
as two separate insns. This case occurs when some SET allows two
other insns to combine, but the destination of that SET is still live. */
else if (i1 && insn_code_number < 0 && asm_noperands (newpat) < 0
&& GET_CODE (newpat) == PARALLEL
&& XVECLEN (newpat, 0) == 2
&& GET_CODE (XVECEXP (newpat, 0, 0)) == SET
&& GET_CODE (XVECEXP (newpat, 0, 1)) == SET
&& ! use_crosses_set_p (SET_SRC (XVECEXP (newpat, 0, 1)),
/* Don't pass sets with (USE (MEM ...)) dests to the following. */
&& GET_CODE (SET_DEST (XVECEXP (newpat, 0, 1))) != USE
&& GET_CODE (SET_DEST (XVECEXP (newpat, 0, 0))) != USE
&& ! reg_referenced_p (SET_DEST (XVECEXP (newpat, 0, 1)),
XVECEXP (newpat, 0, 0))
&& ! reg_referenced_p (SET_DEST (XVECEXP (newpat, 0, 0)),
XVECEXP (newpat, 0, 1))
&& ! (contains_muldiv (SET_SRC (XVECEXP (newpat, 0, 0)))
&& contains_muldiv (SET_SRC (XVECEXP (newpat, 0, 1)))))
/* Normally, it doesn't matter which of the two is done first,
but it does if one references cc0. In that case, it has to
be first. */
#ifdef HAVE_cc0
if (reg_referenced_p (cc0_rtx, XVECEXP (newpat, 0, 0)))
newi2pat = XVECEXP (newpat, 0, 0);
newpat = XVECEXP (newpat, 0, 1);
newi2pat = XVECEXP (newpat, 0, 1);
newpat = XVECEXP (newpat, 0, 0);
i2_code_number = recog_for_combine (&newi2pat, i2, &new_i2_notes);
if (i2_code_number >= 0)
insn_code_number = recog_for_combine (&newpat, i3, &new_i3_notes);
/* If it still isn't recognized, fail and change things back the way they
were. */
if ((insn_code_number < 0
/* Is the result a reasonable ASM_OPERANDS? */
&& (! check_asm_operands (newpat) || added_sets_1 || added_sets_2)))
undo_all ();
return 0;
/* If we had to change another insn, make sure it is valid also. */
if (undobuf.other_insn)
rtx other_pat = PATTERN (undobuf.other_insn);
rtx new_other_notes;
rtx note, next;
CLEAR_HARD_REG_SET (newpat_used_regs);
other_code_number = recog_for_combine (&other_pat, undobuf.other_insn,
if (other_code_number < 0 && ! check_asm_operands (other_pat))
undo_all ();
return 0;
PATTERN (undobuf.other_insn) = other_pat;
/* If any of the notes in OTHER_INSN were REG_UNUSED, ensure that they
are still valid. Then add any non-duplicate notes added by
recog_for_combine. */
for (note = REG_NOTES (undobuf.other_insn); note; note = next)
next = XEXP (note, 1);
&& ! reg_set_p (XEXP (note, 0), PATTERN (undobuf.other_insn)))
if (GET_CODE (XEXP (note, 0)) == REG)
REG_N_DEATHS (REGNO (XEXP (note, 0)))--;
remove_note (undobuf.other_insn, note);
for (note = new_other_notes; note; note = XEXP (note, 1))
if (GET_CODE (XEXP (note, 0)) == REG)
REG_N_DEATHS (REGNO (XEXP (note, 0)))++;
distribute_notes (new_other_notes, undobuf.other_insn,
undobuf.other_insn, NULL_RTX);
#ifdef HAVE_cc0
/* If I2 is the setter CC0 and I3 is the user CC0 then check whether
they are adjacent to each other or not. */
rtx p = prev_nonnote_insn (i3);
if (p && p != i2 && GET_CODE (p) == INSN && newi2pat
&& sets_cc0_p (newi2pat))
undo_all ();
return 0;
/* We now know that we can do this combination. Merge the insns and
update the status of registers and LOG_LINKS. */
rtx i3notes, i2notes, i1notes = 0;
rtx i3links, i2links, i1links = 0;
rtx midnotes = 0;
unsigned int regno;
/* Get the old REG_NOTES and LOG_LINKS from all our insns and
clear them. */
i3notes = REG_NOTES (i3), i3links = LOG_LINKS (i3);
i2notes = REG_NOTES (i2), i2links = LOG_LINKS (i2);
if (i1)
i1notes = REG_NOTES (i1), i1links = LOG_LINKS (i1);
/* Ensure that we do not have something that should not be shared but
occurs multiple times in the new insns. Check this by first
resetting all the `used' flags and then copying anything is shared. */
reset_used_flags (i3notes);
reset_used_flags (i2notes);
reset_used_flags (i1notes);
reset_used_flags (newpat);
reset_used_flags (newi2pat);
if (undobuf.other_insn)
reset_used_flags (PATTERN (undobuf.other_insn));
i3notes = copy_rtx_if_shared (i3notes);
i2notes = copy_rtx_if_shared (i2notes);
i1notes = copy_rtx_if_shared (i1notes);
newpat = copy_rtx_if_shared (newpat);
newi2pat = copy_rtx_if_shared (newi2pat);
if (undobuf.other_insn)
reset_used_flags (PATTERN (undobuf.other_insn));
INSN_CODE (i3) = insn_code_number;
PATTERN (i3) = newpat;
rtx call_usage = CALL_INSN_FUNCTION_USAGE (i3);
reset_used_flags (call_usage);
call_usage = copy_rtx (call_usage);
if (substed_i2)
replace_rtx (call_usage, i2dest, i2src);
if (substed_i1)
replace_rtx (call_usage, i1dest, i1src);
CALL_INSN_FUNCTION_USAGE (i3) = call_usage;
if (undobuf.other_insn)
INSN_CODE (undobuf.other_insn) = other_code_number;
/* We had one special case above where I2 had more than one set and
we replaced a destination of one of those sets with the destination
of I3. In that case, we have to update LOG_LINKS of insns later
in this basic block. Note that this (expensive) case is rare.
Also, in this case, we must pretend that all REG_NOTEs for I2
actually came from I3, so that REG_UNUSED notes from I2 will be
properly handled. */
if (i3_subst_into_i2)
for (i = 0; i < XVECLEN (PATTERN (i2), 0); i++)
if (GET_CODE (XVECEXP (PATTERN (i2), 0, i)) != USE
&& SET_DEST (XVECEXP (PATTERN (i2), 0, i)) != i2dest
&& ! find_reg_note (i2, REG_UNUSED,
for (temp = NEXT_INSN (i2);
temp && (this_basic_block->next_bb == EXIT_BLOCK_PTR
|| BB_HEAD (this_basic_block) != temp);
temp = NEXT_INSN (temp))
if (temp != i3 && INSN_P (temp))
for (link = LOG_LINKS (temp); link; link = XEXP (link, 1))
if (XEXP (link, 0) == i2)
XEXP (link, 0) = i3;
if (i3notes)
rtx link = i3notes;
while (XEXP (link, 1))
link = XEXP (link, 1);
XEXP (link, 1) = i2notes;
i3notes = i2notes;
i2notes = 0;
LOG_LINKS (i3) = 0;
REG_NOTES (i3) = 0;
LOG_LINKS (i2) = 0;
REG_NOTES (i2) = 0;
if (newi2pat)
INSN_CODE (i2) = i2_code_number;
PATTERN (i2) = newi2pat;
if (i1)
LOG_LINKS (i1) = 0;
REG_NOTES (i1) = 0;
/* Get death notes for everything that is now used in either I3 or
I2 and used to die in a previous insn. If we built two new
patterns, move from I1 to I2 then I2 to I3 so that we get the
proper movement on registers that I2 modifies. */
if (newi2pat)
move_deaths (newi2pat, NULL_RTX, INSN_CUID (i1), i2, &midnotes);
move_deaths (newpat, newi2pat, INSN_CUID (i1), i3, &midnotes);
move_deaths (newpat, NULL_RTX, i1 ? INSN_CUID (i1) : INSN_CUID (i2),
i3, &midnotes);
/* Distribute all the LOG_LINKS and REG_NOTES from I1, I2, and I3. */
if (i3notes)
distribute_notes (i3notes, i3, i3, newi2pat ? i2 : NULL_RTX);
if (i2notes)
distribute_notes (i2notes, i2, i3, newi2pat ? i2 : NULL_RTX);
if (i1notes)
distribute_notes (i1notes, i1, i3, newi2pat ? i2 : NULL_RTX);
if (midnotes)
distribute_notes (midnotes, NULL_RTX, i3, newi2pat ? i2 : NULL_RTX);
/* Distribute any notes added to I2 or I3 by recog_for_combine. We
know these are REG_UNUSED and want them to go to the desired insn,
so we always pass it as i3. We have not counted the notes in
reg_n_deaths yet, so we need to do so now. */
if (newi2pat && new_i2_notes)
for (temp = new_i2_notes; temp; temp = XEXP (temp, 1))
if (GET_CODE (XEXP (temp, 0)) == REG)
REG_N_DEATHS (REGNO (XEXP (temp, 0)))++;
distribute_notes (new_i2_notes, i2, i2, NULL_RTX);
if (new_i3_notes)
for (temp = new_i3_notes; temp; temp = XEXP (temp, 1))
if (GET_CODE (XEXP (temp, 0)) == REG)
REG_N_DEATHS (REGNO (XEXP (temp, 0)))++;
distribute_notes (new_i3_notes, i3, i3, NULL_RTX);
/* If I3DEST was used in I3SRC, it really died in I3. We may need to
put a REG_DEAD note for it somewhere. If NEWI2PAT exists and sets
I3DEST, the death must be somewhere before I2, not I3. If we passed I3
in that case, it might delete I2. Similarly for I2 and I1.
Show an additional death due to the REG_DEAD note we make here. If
we discard it in distribute_notes, we will decrement it again. */
if (i3dest_killed)
if (GET_CODE (i3dest_killed) == REG)
REG_N_DEATHS (REGNO (i3dest_killed))++;
if (newi2pat && reg_set_p (i3dest_killed, newi2pat))
distribute_notes (gen_rtx_EXPR_LIST (REG_DEAD, i3dest_killed,
distribute_notes (gen_rtx_EXPR_LIST (REG_DEAD, i3dest_killed,
NULL_RTX, i3, newi2pat ? i2 : NULL_RTX);
if (i2dest_in_i2src)
if (GET_CODE (i2dest) == REG)
REG_N_DEATHS (REGNO (i2dest))++;
if (newi2pat && reg_set_p (i2dest, newi2pat))
distribute_notes (gen_rtx_EXPR_LIST (REG_DEAD, i2dest, NULL_RTX),
distribute_notes (gen_rtx_EXPR_LIST (REG_DEAD, i2dest, NULL_RTX),
NULL_RTX, i3, newi2pat ? i2 : NULL_RTX);
if (i1dest_in_i1src)
if (GET_CODE (i1dest) == REG)
REG_N_DEATHS (REGNO (i1dest))++;
if (newi2pat && reg_set_p (i1dest, newi2pat))
distribute_notes (gen_rtx_EXPR_LIST (REG_DEAD, i1dest, NULL_RTX),
distribute_notes (gen_rtx_EXPR_LIST (REG_DEAD, i1dest, NULL_RTX),
NULL_RTX, i3, newi2pat ? i2 : NULL_RTX);
distribute_links (i3links);
distribute_links (i2links);
distribute_links (i1links);
if (GET_CODE (i2dest) == REG)
rtx link;
rtx i2_insn = 0, i2_val = 0, set;
/* The insn that used to set this register doesn't exist, and
this life of the register may not exist either. See if one of
I3's links points to an insn that sets I2DEST. If it does,
that is now the last known value for I2DEST. If we don't update
this and I2 set the register to a value that depended on its old
contents, we will get confused. If this insn is used, thing
will be set correctly in combine_instructions. */
for (link = LOG_LINKS (i3); link; link = XEXP (link, 1))
if ((set = single_set (XEXP (link, 0))) != 0
&& rtx_equal_p (i2dest, SET_DEST (set)))
i2_insn = XEXP (link, 0), i2_val = SET_SRC (set);
record_value_for_reg (i2dest, i2_insn, i2_val);
/* If the reg formerly set in I2 died only once and that was in I3,
zero its use count so it won't make `reload' do any work. */
if (! added_sets_2
&& (newi2pat == 0 || ! reg_mentioned_p (i2dest, newi2pat))
&& ! i2dest_in_i2src)
regno = REGNO (i2dest);
REG_N_SETS (regno)--;
if (i1 && GET_CODE (i1dest) == REG)
rtx link;
rtx i1_insn = 0, i1_val = 0, set;
for (link = LOG_LINKS (i3); link; link = XEXP (link, 1))
if ((set = single_set (XEXP (link, 0))) != 0
&& rtx_equal_p (i1dest, SET_DEST (set)))
i1_insn = XEXP (link, 0), i1_val = SET_SRC (set);
record_value_for_reg (i1dest, i1_insn, i1_val);
regno = REGNO (i1dest);
if (! added_sets_1 && ! i1dest_in_i1src)
REG_N_SETS (regno)--;
/* Update reg_nonzero_bits et al for any changes that may have been made
to this insn. The order of set_nonzero_bits_and_sign_copies() is
important. Because newi2pat can affect nonzero_bits of newpat */
if (newi2pat)
note_stores (newi2pat, set_nonzero_bits_and_sign_copies, NULL);
note_stores (newpat, set_nonzero_bits_and_sign_copies, NULL);
/* Set new_direct_jump_p if a new return or simple jump instruction
has been created.
If I3 is now an unconditional jump, ensure that it has a
BARRIER following it since it may have initially been a
conditional jump. It may also be the last nonnote insn. */
if (returnjump_p (i3) || any_uncondjump_p (i3))
*new_direct_jump_p = 1;
mark_jump_label (PATTERN (i3), i3, 0);
if ((temp = next_nonnote_insn (i3)) == NULL_RTX
|| GET_CODE (temp) != BARRIER)
emit_barrier_after (i3);
if (undobuf.other_insn != NULL_RTX
&& (returnjump_p (undobuf.other_insn)
|| any_uncondjump_p (undobuf.other_insn)))
*new_direct_jump_p = 1;
if ((temp = next_nonnote_insn (undobuf.other_insn)) == NULL_RTX
|| GET_CODE (temp) != BARRIER)
emit_barrier_after (undobuf.other_insn);
/* An NOOP jump does not need barrier, but it does need cleaning up
of CFG. */
if (GET_CODE (newpat) == SET
&& SET_SRC (newpat) == pc_rtx
&& SET_DEST (newpat) == pc_rtx)
*new_direct_jump_p = 1;
undo_commit ();
if (added_links_insn
&& (newi2pat == 0 || INSN_CUID (added_links_insn) < INSN_CUID (i2))
&& INSN_CUID (added_links_insn) < INSN_CUID (i3))
return added_links_insn;
return newi2pat ? i2 : i3;
/* Undo all the modifications recorded in undobuf. */
static void
undo_all (void)
struct undo *undo, *next;
for (undo = undobuf.undos; undo; undo = next)
next = undo->next;
if (undo->is_int)
*undo->where.i = undo->old_contents.i;
*undo->where.r = undo->old_contents.r;
undo->next = undobuf.frees;
undobuf.frees = undo;
undobuf.undos = 0;
/* We've committed to accepting the changes we made. Move all
of the undos to the free list. */
static void
undo_commit (void)
struct undo *undo, *next;
for (undo = undobuf.undos; undo; undo = next)
next = undo->next;
undo->next = undobuf.frees;
undobuf.frees = undo;
undobuf.undos = 0;
/* Find the innermost point within the rtx at LOC, possibly LOC itself,
where we have an arithmetic expression and return that point. LOC will
be inside INSN.
try_combine will call this function to see if an insn can be split into
two insns. */
static rtx *
find_split_point (rtx *loc, rtx insn)
rtx x = *loc;
enum rtx_code code = GET_CODE (x);
rtx *split;
unsigned HOST_WIDE_INT len = 0;
HOST_WIDE_INT pos = 0;
int unsignedp = 0;
rtx inner = NULL_RTX;
/* First special-case some codes. */
switch (code)
case SUBREG:
/* If we are making a paradoxical SUBREG invalid, it becomes a split
point. */
return loc;
return find_split_point (&SUBREG_REG (x), insn);
case MEM:
#ifdef HAVE_lo_sum
/* If we have (mem (const ..)) or (mem (symbol_ref ...)), split it
using LO_SUM and HIGH. */
if (GET_CODE (XEXP (x, 0)) == CONST
|| GET_CODE (XEXP (x, 0)) == SYMBOL_REF)
SUBST (XEXP (x, 0),
gen_rtx_LO_SUM (Pmode,
gen_rtx_HIGH (Pmode, XEXP (x, 0)),
XEXP (x, 0)));
return &XEXP (XEXP (x, 0), 0);
/* If we have a PLUS whose second operand is a constant and the
address is not valid, perhaps will can split it up using
the machine-specific way to split large constants. We use
the first pseudo-reg (one of the virtual regs) as a placeholder;
it will not remain in the result. */
if (GET_CODE (XEXP (x, 0)) == PLUS
&& GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
&& ! memory_address_p (GET_MODE (x), XEXP (x, 0)))
rtx reg = regno_reg_rtx[FIRST_PSEUDO_REGISTER];
rtx seq = split_insns (gen_rtx_SET (VOIDmode, reg, XEXP (x, 0)),
/* This should have produced two insns, each of which sets our
placeholder. If the source of the second is a valid address,
we can make put both sources together and make a split point
in the middle. */
if (seq
&& NEXT_INSN (seq) != NULL_RTX
&& GET_CODE (seq) == INSN
&& GET_CODE (PATTERN (seq)) == SET
&& SET_DEST (PATTERN (seq)) == reg
&& ! reg_mentioned_p (reg,
&& SET_DEST (PATTERN (NEXT_INSN (seq))) == reg
&& memory_address_p (GET_MODE (x),
rtx src1 = SET_SRC (PATTERN (seq));
rtx src2 = SET_SRC (PATTERN (NEXT_INSN (seq)));
/* Replace the placeholder in SRC2 with SRC1. If we can
find where in SRC2 it was placed, that can become our
split point and we can replace this address with SRC2.
Just try two obvious places. */
src2 = replace_rtx (src2, reg, src1);
split = 0;
if (XEXP (src2, 0) == src1)
split = &XEXP (src2, 0);
else if (GET_RTX_FORMAT (GET_CODE (XEXP (src2, 0)))[0] == 'e'
&& XEXP (XEXP (src2, 0), 0) == src1)
split = &XEXP (XEXP (src2, 0), 0);
if (split)
SUBST (XEXP (x, 0), src2);
return split;
/* If that didn't work, perhaps the first operand is complex and
needs to be computed separately, so make a split point there.
This will occur on machines that just support REG + CONST
and have a constant moved through some previous computation. */
else if (GET_RTX_CLASS (GET_CODE (XEXP (XEXP (x, 0), 0))) != 'o'
&& ! (GET_CODE (XEXP (XEXP (x, 0), 0)) == SUBREG
== 'o')))
return &XEXP (XEXP (x, 0), 0);
case SET:
#ifdef HAVE_cc0
/* If SET_DEST is CC0 and SET_SRC is not an operand, a COMPARE, or a
ZERO_EXTRACT, the most likely reason why this doesn't match is that
we need to put the operand into a register. So split at that
point. */
if (SET_DEST (x) == cc0_rtx
&& GET_RTX_CLASS (GET_CODE (SET_SRC (x))) != 'o'
&& ! (GET_CODE (SET_SRC (x)) == SUBREG
return &SET_SRC (x);
/* See if we can split SET_SRC as it stands. */
split = find_split_point (&SET_SRC (x), insn);
if (split && split != &SET_SRC (x))
return split;
/* See if we can split SET_DEST as it stands. */
split = find_split_point (&SET_DEST (x), insn);
if (split && split != &SET_DEST (x))
return split;
/* See if this is a bitfield assignment with everything constant. If
so, this is an IOR of an AND, so split it into that. */
&& ((INTVAL (XEXP (SET_DEST (x), 1))
+ INTVAL (XEXP (SET_DEST (x), 2)))