/* Move registers around to reduce number of move instructions needed.
Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
- 1999, 2000, 2001 Free Software Foundation, Inc.
+ 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
-This file is part of GNU CC.
+This file is part of GCC.
-GNU CC 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 version.
+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
+version.
-GNU CC 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.
+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 GNU CC; see the file COPYING. If not, write to
-the Free Software Foundation, 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA. */
+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 looks for cases where matching constraints would force
#include "flags.h"
#include "function.h"
#include "expr.h"
-#include "insn-flags.h"
#include "basic-block.h"
+#include "except.h"
#include "toplev.h"
+#include "reload.h"
+
+
+/* Turn STACK_GROWS_DOWNWARD into a boolean. */
+#ifdef STACK_GROWS_DOWNWARD
+#undef STACK_GROWS_DOWNWARD
+#define STACK_GROWS_DOWNWARD 1
+#else
+#define STACK_GROWS_DOWNWARD 0
+#endif
static int perhaps_ends_bb_p PARAMS ((rtx));
static int optimize_reg_copy_1 PARAMS ((rtx, rtx, rtx));
static void optimize_reg_copy_2 PARAMS ((rtx, rtx, rtx));
static void optimize_reg_copy_3 PARAMS ((rtx, rtx, rtx));
-static rtx gen_add3_insn PARAMS ((rtx, rtx, rtx));
static void copy_src_to_dest PARAMS ((rtx, rtx, rtx, int));
static int *regmove_bb_head;
static int try_auto_increment PARAMS ((rtx, rtx, rtx, rtx, HOST_WIDE_INT, int));
static int find_matches PARAMS ((rtx, struct match *));
-static void replace_in_call_usage PARAMS ((rtx *, int, rtx, rtx));
+static void replace_in_call_usage PARAMS ((rtx *, unsigned int, rtx, rtx));
static int fixup_match_1 PARAMS ((rtx, rtx, rtx, rtx, rtx, int, int, int, FILE *))
;
static int reg_is_remote_constant_p PARAMS ((rtx, rtx, rtx));
&& ! CLASS_LIKELY_SPILLED_P (class1)));
}
-/* Generate and return an insn body to add r1 and c,
- storing the result in r0. */
-static rtx
-gen_add3_insn (r0, r1, c)
- rtx r0, r1, c;
-{
- int icode = (int) add_optab->handlers[(int) GET_MODE (r0)].insn_code;
-
- if (icode == CODE_FOR_nothing
- || ! ((*insn_data[icode].operand[0].predicate)
- (r0, insn_data[icode].operand[0].mode))
- || ! ((*insn_data[icode].operand[1].predicate)
- (r1, insn_data[icode].operand[1].mode))
- || ! ((*insn_data[icode].operand[2].predicate)
- (c, insn_data[icode].operand[2].mode)))
- return NULL_RTX;
-
- return (GEN_FCN (icode) (r0, r1, c));
-}
-
-
/* INC_INSN is an instruction that adds INCREMENT to REG.
Try to fold INC_INSN as a post/pre in/decrement into INSN.
Iff INC_INSN_SET is nonzero, inc_insn has a destination different from src.
/* Can't use the size of SET_SRC, we might have something like
(sign_extend:SI (mem:QI ... */
rtx use = find_use_as_address (pset, reg, 0);
- if (use != 0 && use != (rtx) 1)
+ if (use != 0 && use != (rtx) (size_t) 1)
{
int size = GET_MODE_SIZE (GET_MODE (use));
if (0
= gen_rtx_EXPR_LIST (REG_INC,
reg, REG_NOTES (insn));
if (! inc_insn_set)
- {
- PUT_CODE (inc_insn, NOTE);
- NOTE_LINE_NUMBER (inc_insn) = NOTE_INSN_DELETED;
- NOTE_SOURCE_FILE (inc_insn) = 0;
- }
+ delete_insn (inc_insn);
return 1;
}
}
{
enum machine_mode mode = (flags ? HImode : VOIDmode);
rtx insn;
- for (insn = get_insns(); insn; insn = NEXT_INSN (insn))
+ for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
PUT_MODE (insn, mode);
return;
}
a candidate for tying to a hard register, since the output might in
turn be a candidate to be tied to a different hard register. */
static int
-replacement_quality(reg)
+replacement_quality (reg)
rtx reg;
{
int src_regno;
/* A CALL_INSN might be the last insn of a basic block, if it is inside
an EH region or if there are nonlocal gotos. Note that this test is
very conservative. */
- return flag_exceptions || nonlocal_goto_handler_labels;
-
+ if (nonlocal_goto_handler_labels)
+ return 1;
+ /* FALLTHRU */
default:
- /* All others never end a basic block. */
- return 0;
+ return can_throw_internal (insn);
}
}
\f
int sregno = REGNO (src);
int dregno = REGNO (dest);
- /* We don't want to mess with hard regs if register classes are small. */
+ /* We don't want to mess with hard regs if register classes are small. */
if (sregno == dregno
|| (SMALL_REGISTER_CLASSES
&& (sregno < FIRST_PSEUDO_REGISTER
if (! (set = single_set (p))
|| GET_CODE (SET_SRC (set)) != MEM
+ /* If there's a REG_EQUIV note, this must be an insn that loads an
+ argument. Prefer keeping the note over doing this optimization. */
+ || find_reg_note (p, REG_EQUIV, NULL_RTX)
|| SET_DEST (set) != src_reg)
return;
/* Now walk forward making additional replacements. We want to be able
to undo all the changes if a later substitution fails. */
- subreg = gen_rtx_SUBREG (old_mode, src_reg, 0);
+ subreg = gen_lowpart_SUBREG (old_mode, src_reg);
while (p = NEXT_INSN (p), p != insn)
{
if (! INSN_P (p))
PUT_MODE (src_reg, old_mode);
XEXP (src, 0) = src_reg;
}
+ else
+ {
+ rtx note = find_reg_note (p, REG_EQUAL, NULL_RTX);
+ if (note)
+ remove_note (p, note);
+ }
}
\f
rtx insn;
rtx first;
{
- register rtx p;
+ rtx p;
if (REG_N_SETS (REGNO (reg)) != 1)
return 0;
return 0;
}
+/* Main entry for the register move optimization.
+ F is the first instruction.
+ NREGS is one plus the highest pseudo-reg number used in the instruction.
+ REGMOVE_DUMP_FILE is a stream for output of a trace of actions taken
+ (or 0 if none should be output). */
+
void
regmove_optimize (f, nregs, regmove_dump_file)
rtx f;
int i;
rtx copy_src, copy_dst;
+ /* ??? Hack. Regmove doesn't examine the CFG, and gets mightily
+ confused by non-call exceptions ending blocks. */
+ if (flag_non_call_exceptions)
+ return;
+
/* Find out where a potential flags register is live, and so that we
can supress some optimizations in those zones. */
mark_flags_life_zones (discover_flags_reg ());
&& (GET_CODE (SET_SRC (set)) == SIGN_EXTEND
|| GET_CODE (SET_SRC (set)) == ZERO_EXTEND)
&& GET_CODE (XEXP (SET_SRC (set), 0)) == REG
- && GET_CODE (SET_DEST(set)) == REG)
+ && GET_CODE (SET_DEST (set)) == REG)
optimize_reg_copy_3 (insn, SET_DEST (set), SET_SRC (set));
if (flag_expensive_optimizations && ! pass
&& GET_CODE (SET_SRC (set)) == REG
- && GET_CODE (SET_DEST(set)) == REG)
+ && GET_CODE (SET_DEST (set)) == REG)
{
/* If this is a register-register copy where SRC is not dead,
see if we can optimize it. If this optimization succeeds,
if (regno_src_regno[REGNO (SET_DEST (set))] < 0
&& SET_SRC (set) != SET_DEST (set))
{
- int srcregno = REGNO (SET_SRC(set));
+ int srcregno = REGNO (SET_SRC (set));
if (regno_src_regno[srcregno] >= 0)
srcregno = regno_src_regno[srcregno];
regno_src_regno[REGNO (SET_DEST (set))] = srcregno;
{
src_subreg
= gen_rtx_SUBREG (GET_MODE (SUBREG_REG (dst)),
- src, SUBREG_WORD (dst));
+ src, SUBREG_BYTE (dst));
dst = SUBREG_REG (dst);
}
if (GET_CODE (dst) != REG
if (recog_data.operand[match_no] != SET_DEST (set))
continue;
- /* If the operands already match, then there is nothing to do. */
+ /* If the operands already match, then there is nothing to do. */
if (operands_match_p (src, dst))
continue;
if (! regclass_compatible_p (src_class, dst_class))
continue;
+ if (GET_MODE (src) != GET_MODE (dst))
+ continue;
+
if (fixup_match_1 (insn, set, src, src_subreg, dst, pass,
op_no, match_no,
regmove_dump_file))
if (GET_CODE (dst) != REG
|| REGNO (dst) < FIRST_PSEUDO_REGISTER
- || REG_LIVE_LENGTH (REGNO (dst)) < 0)
+ || REG_LIVE_LENGTH (REGNO (dst)) < 0
+ || RTX_UNCHANGING_P (dst))
continue;
- /* If the operands already match, then there is nothing to do. */
+ /* If the operands already match, then there is nothing to do. */
if (operands_match_p (src, dst))
continue;
if (! set)
continue;
+ /* Note that single_set ignores parts of a parallel set for
+ which one of the destinations is REG_UNUSED. We can't
+ handle that here, since we can wind up rewriting things
+ such that a single register is set twice within a single
+ parallel. */
+ if (reg_set_p (src, insn))
+ continue;
+
/* match_no/dst must be a write-only operand, and
operand_operand/src must be a read-only operand. */
if (match.use[op_no] != READ
}
src_class = reg_preferred_class (REGNO (src));
dst_class = reg_preferred_class (REGNO (dst));
- if (! regclass_compatible_p (src_class, dst_class))
+
+ if (! (src_note = find_reg_note (insn, REG_DEAD, src)))
{
- if (!copy_src)
- {
- copy_src = src;
- copy_dst = dst;
- }
+ /* We used to force the copy here like in other cases, but
+ it produces worse code, as it eliminates no copy
+ instructions and the copy emitted will be produced by
+ reload anyway. On patterns with multiple alternatives,
+ there may be better sollution availble.
+
+ In particular this change produced slower code for numeric
+ i387 programs. */
+
continue;
}
- /* Can not modify an earlier insn to set dst if this insn
- uses an old value in the source. */
- if (reg_overlap_mentioned_p (dst, SET_SRC (set)))
+ if (! regclass_compatible_p (src_class, dst_class))
{
if (!copy_src)
{
continue;
}
- if (! (src_note = find_reg_note (insn, REG_DEAD, src)))
+ /* Can not modify an earlier insn to set dst if this insn
+ uses an old value in the source. */
+ if (reg_overlap_mentioned_p (dst, SET_SRC (set)))
{
if (!copy_src)
{
continue;
}
-
/* If src is set once in a different basic block,
and is set equal to a constant, then do not use
it for this optimization, as this would make it
matchp->commutative[op_no] = op_no + 1;
matchp->commutative[op_no + 1] = op_no;
break;
+
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
- c -= '0';
- if (c < op_no && likely_spilled[(unsigned char) c])
- break;
- matchp->with[op_no] = c;
- any_matches = 1;
- if (matchp->commutative[op_no] >= 0)
- matchp->with[matchp->commutative[op_no]] = c;
+ {
+ char *end;
+ unsigned long match_ul = strtoul (p - 1, &end, 10);
+ int match = match_ul;
+
+ p = end;
+
+ if (match < op_no && likely_spilled[match])
+ break;
+ matchp->with[op_no] = match;
+ any_matches = 1;
+ if (matchp->commutative[op_no] >= 0)
+ matchp->with[matchp->commutative[op_no]] = match;
+ }
break;
+
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'h':
case 'j': case 'k': case 'l': case 'p': case 'q': case 't': case 'u':
case 'v': case 'w': case 'x': case 'y': case 'z': case 'A': case 'B':
case 'C': case 'D': case 'W': case 'Y': case 'Z':
- if (CLASS_LIKELY_SPILLED_P (REG_CLASS_FROM_LETTER ((unsigned char)c)))
+ if (CLASS_LIKELY_SPILLED_P (REG_CLASS_FROM_LETTER ((unsigned char) c)))
likely_spilled[op_no] = 1;
break;
}
static void
replace_in_call_usage (loc, dst_reg, src, insn)
rtx *loc;
- int dst_reg;
+ unsigned int dst_reg;
rtx src;
rtx insn;
{
rtx notes = REG_NOTES (insn);
emit_insn_after_with_line_notes (pat, PREV_INSN (p), insn);
- PUT_CODE (insn, NOTE);
- NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
- NOTE_SOURCE_FILE (insn) = 0;
+ delete_insn (insn);
/* emit_insn_after_with_line_notes has no
return value, so search for the new insn. */
insn = p;
if (note && CONSTANT_P (XEXP (note, 0)))
{
- for (q = PREV_INSN (insn); q; q = PREV_INSN(q))
+ for (q = PREV_INSN (insn); q; q = PREV_INSN (q))
{
/* ??? We can't scan past the end of a basic block without
updating the register lifetime info
if (q && set2 && SET_DEST (set2) == src && CONSTANT_P (SET_SRC (set2))
&& validate_change (insn, &SET_SRC (set), XEXP (note, 0), 0))
{
- PUT_CODE (q, NOTE);
- NOTE_LINE_NUMBER (q) = NOTE_INSN_DELETED;
- NOTE_SOURCE_FILE (q) = 0;
+ delete_insn (q);
REG_N_SETS (REGNO (src))--;
REG_N_CALLS_CROSSED (REGNO (src)) -= num_calls2;
REG_LIVE_LENGTH (REGNO (src)) -= s_length2;
{
/* ??? We can't scan past the end of a basic block without updating
the register lifetime info
- (REG_DEAD/basic_block_live_at_start). */
+ (REG_DEAD/basic_block_live_at_start). */
if (perhaps_ends_bb_p (q))
break;
else if (! INSN_P (q))
}
\f
/* Track stack adjustments and stack memory references. Attempt to
- reduce the number of stack adjustments by back-propogating across
+ reduce the number of stack adjustments by back-propagating across
the memory references.
This is intended primarily for use with targets that do not define
(e.g. x86 fp regs) which would ordinarily have to be implemented
as a sub/mov pair due to restrictions in calls.c.
- Propogation stops when any of the insns that need adjusting are
+ Propagation stops when any of the insns that need adjusting are
(a) no longer valid because we've exceeded their range, (b) a
non-trivial push instruction, or (c) a call instruction.
struct csa_memlist *ml;
rtx set;
- /* We know INSN matches single_set_for_csa, because that's what we
- recognized earlier. However, if INSN is not single_set, it is
- doing double duty as a barrier for frame pointer memory accesses,
- which we are not recording. Therefore, an adjust insn that is not
- single_set may not have a positive delta applied. */
-
- if (delta > 0 && ! single_set (insn))
- return 0;
set = single_set_for_csa (insn);
validate_change (insn, &XEXP (SET_SRC (set), 1), GEN_INT (new_adjust), 1);
for (ml = memlist; ml ; ml = ml->next)
- {
- HOST_WIDE_INT c = ml->sp_offset - delta;
- rtx new = gen_rtx_MEM (GET_MODE (*ml->mem),
- plus_constant (stack_pointer_rtx, c));
-
- /* Don't reference memory below the stack pointer. */
- if (c < 0)
- {
- cancel_changes (0);
- return 0;
- }
-
- MEM_COPY_ATTRIBUTES (new, *ml->mem);
- validate_change (ml->insn, ml->mem, new, 1);
- }
+ validate_change
+ (ml->insn, ml->mem,
+ replace_equiv_address_nv (*ml->mem,
+ plus_constant (stack_pointer_rtx,
+ ml->sp_offset - delta)), 1);
if (apply_change_group ())
{
if (!reg_mentioned_p (stack_pointer_rtx, x))
return -1;
/* We are not able to handle correctly all possible memrefs containing
- stack pointer, so this check is neccesary. */
+ stack pointer, so this check is necessary. */
if (stack_memref_p (x))
{
d->memlist = record_one_stack_memref (d->insn, xp, d->memlist);
}
return 1;
case REG:
- /* ??? We want be able to handle non-memory stack pointer references
- later. For now just discard all insns refering to stack pointer
- outside mem expressions. We would probably want to teach
- validate_replace to simplify expressions first. */
- if (x == stack_pointer_rtx)
+ /* ??? We want be able to handle non-memory stack pointer
+ references later. For now just discard all insns refering to
+ stack pointer outside mem expressions. We would probably
+ want to teach validate_replace to simplify expressions first.
+
+ We can't just compare with STACK_POINTER_RTX because the
+ reference to the stack pointer might be in some other mode.
+ In particular, an explict clobber in an asm statement will
+ result in a QImode clober. */
+ if (REGNO (x) == STACK_POINTER_REGNUM)
return 1;
break;
default:
/* If not all recorded memrefs can be adjusted, or the
adjustment is now too large for a constant addition,
- we cannot merge the two stack adjustments. */
- if (! try_apply_stack_adjustment (last_sp_set, memlist,
- last_sp_adjust + this_adjust,
- this_adjust))
+ we cannot merge the two stack adjustments.
+
+ Also we need to be carefull to not move stack pointer
+ such that we create stack accesses outside the allocated
+ area. We can combine an allocation into the first insn,
+ or a deallocation into the second insn. We can not
+ combine an allocation followed by a deallocation.
+
+ The only somewhat frequent occurrence of the later is when
+ a function allocates a stack frame but does not use it.
+ For this case, we would need to analyze rtl stream to be
+ sure that allocated area is really unused. This means not
+ only checking the memory references, but also all registers
+ or global memory references possibly containing a stack
+ frame address.
+
+ Perhaps the best way to address this problem is to teach
+ gcc not to allocate stack for objects never used. */
+
+ /* Combine an allocation into the first instruction. */
+ if (STACK_GROWS_DOWNWARD ? this_adjust <= 0 : this_adjust >= 0)
{
- free_csa_memlist (memlist);
- memlist = NULL;
- last_sp_set = insn;
- last_sp_adjust = this_adjust;
- goto processed;
+ if (try_apply_stack_adjustment (last_sp_set, memlist,
+ last_sp_adjust + this_adjust,
+ this_adjust))
+ {
+ /* It worked! */
+ pending_delete = insn;
+ last_sp_adjust += this_adjust;
+ goto processed;
+ }
}
- /* It worked! */
- pending_delete = insn;
- last_sp_adjust += this_adjust;
-
- /* If, by some accident, the adjustments cancel out,
- delete both insns and start from scratch. */
- if (last_sp_adjust == 0)
+ /* Otherwise we have a deallocation. Do not combine with
+ a previous allocation. Combine into the second insn. */
+ else if (STACK_GROWS_DOWNWARD
+ ? last_sp_adjust >= 0 : last_sp_adjust <= 0)
{
- if (last_sp_set == bb->head)
- bb->head = NEXT_INSN (last_sp_set);
- flow_delete_insn (last_sp_set);
-
- free_csa_memlist (memlist);
- memlist = NULL;
- last_sp_set = NULL_RTX;
+ if (try_apply_stack_adjustment (insn, memlist,
+ last_sp_adjust + this_adjust,
+ -last_sp_adjust))
+ {
+ /* It worked! */
+ delete_insn (last_sp_set);
+ last_sp_set = insn;
+ last_sp_adjust += this_adjust;
+ free_csa_memlist (memlist);
+ memlist = NULL;
+ goto processed;
+ }
}
+ /* Combination failed. Restart processing from here. */
+ free_csa_memlist (memlist);
+ memlist = NULL;
+ last_sp_set = insn;
+ last_sp_adjust = this_adjust;
goto processed;
}
&& ! reg_mentioned_p (stack_pointer_rtx, src)
&& memory_address_p (GET_MODE (dest), stack_pointer_rtx)
&& validate_change (insn, &SET_DEST (set),
- change_address (dest, VOIDmode,
- stack_pointer_rtx), 0))
+ replace_equiv_address (dest,
+ stack_pointer_rtx),
+ 0))
{
if (last_sp_set == bb->head)
bb->head = NEXT_INSN (last_sp_set);
- flow_delete_insn (last_sp_set);
+ delete_insn (last_sp_set);
free_csa_memlist (memlist);
memlist = NULL;
break;
if (pending_delete)
- flow_delete_insn (pending_delete);
+ delete_insn (pending_delete);
}
if (pending_delete)
- {
- bb->end = PREV_INSN (pending_delete);
- flow_delete_insn (pending_delete);
- }
+ delete_insn (pending_delete);
}