/* Move registers around to reduce number of move instructions needed.
- Copyright (C) 1987, 88, 89, 92-98, 1999 Free Software Foundation, Inc.
+ Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
+ 1999, 2000 Free Software Foundation, Inc.
This file is part of GNU CC.
#include "config.h"
#include "system.h"
#include "rtl.h" /* stdio.h must precede rtl.h for FFS. */
+#include "tm_p.h"
#include "insn-config.h"
#include "recog.h"
#include "output.h"
-#include "reload.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "flags.h"
#include "basic-block.h"
#include "toplev.h"
-static int optimize_reg_copy_1 PROTO((rtx, rtx, rtx));
-static void optimize_reg_copy_2 PROTO((rtx, rtx, rtx));
-static void optimize_reg_copy_3 PROTO((rtx, rtx, rtx));
-static rtx gen_add3_insn PROTO((rtx, rtx, rtx));
-static void copy_src_to_dest PROTO((rtx, rtx, rtx, int, int));
+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;
struct match {
int early_clobber[MAX_RECOG_OPERANDS];
};
-static rtx discover_flags_reg PROTO((void));
-static void mark_flags_life_zones PROTO((rtx));
-static void flags_set_1 PROTO((rtx, rtx));
+static rtx discover_flags_reg PARAMS ((void));
+static void mark_flags_life_zones PARAMS ((rtx));
+static void flags_set_1 PARAMS ((rtx, rtx, void *));
-static int try_auto_increment PROTO((rtx, rtx, rtx, rtx, HOST_WIDE_INT, int));
-static int find_matches PROTO((rtx, struct match *));
-static int fixup_match_1 PROTO((rtx, rtx, rtx, rtx, rtx, int, int, int, FILE *))
+static int try_auto_increment PARAMS ((rtx, rtx, rtx, rtx, HOST_WIDE_INT, int));
+static int find_matches PARAMS ((rtx, struct match *));
+static int fixup_match_1 PARAMS ((rtx, rtx, rtx, rtx, rtx, int, int, int, FILE *))
;
-static int reg_is_remote_constant_p PROTO((rtx, rtx, rtx));
-static int stable_but_for_p PROTO((rtx, rtx, rtx));
-static int regclass_compatible_p PROTO((int, int));
-static int loop_depth;
+static int reg_is_remote_constant_p PARAMS ((rtx, rtx, rtx));
+static int stable_and_no_regs_but_for_p PARAMS ((rtx, rtx, rtx));
+static int regclass_compatible_p PARAMS ((int, int));
+static int replacement_quality PARAMS ((rtx));
+static int fixup_match_2 PARAMS ((rtx, rtx, rtx, rtx, FILE *));
/* Return non-zero if registers with CLASS1 and CLASS2 can be merged without
causing too much register allocation problems. */
int icode = (int) add_optab->handlers[(int) GET_MODE (r0)].insn_code;
if (icode == CODE_FOR_nothing
- || ! (*insn_operand_predicate[icode][0]) (r0, insn_operand_mode[icode][0])
- || ! (*insn_operand_predicate[icode][1]) (r1, insn_operand_mode[icode][1])
- || ! (*insn_operand_predicate[icode][2]) (c, insn_operand_mode[icode][2]))
+ || ! ((*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));
alive, death, birth. This lets more important info
overwrite the mode of lesser info. */
- if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
+ if (INSN_P (insn))
{
#ifdef HAVE_cc0
/* In the cc0 case, death is not marked in reg notes,
/* In either case, birth is denoted simply by it's presence
as the destination of a set. */
flags_set_1_set = 0;
- note_stores (PATTERN (insn), flags_set_1);
+ note_stores (PATTERN (insn), flags_set_1, NULL);
if (flags_set_1_set)
{
live = 1;
/* A subroutine of mark_flags_life_zones, called through note_stores. */
static void
-flags_set_1 (x, pat)
+flags_set_1 (x, pat, data)
rtx x, pat;
+ void *data ATTRIBUTE_UNUSED;
{
if (GET_CODE (pat) == SET
&& reg_overlap_mentioned_p (x, flags_set_1_rtx))
the choice. The main objective is to avoid using a register that is
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. */
-int
+static int
replacement_quality(reg)
rtx reg;
{
when the registers get tied. */
return 2;
}
+\f
+/* Return 1 if INSN might end a basic block. */
+
+static int perhaps_ends_bb_p (insn)
+ rtx insn;
+{
+ switch (GET_CODE (insn))
+ {
+ case CODE_LABEL:
+ case JUMP_INSN:
+ /* These always end a basic block. */
+ return 1;
+
+ case CALL_INSN:
+ /* 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;
+ default:
+ /* All others never end a basic block. */
+ return 0;
+ }
+}
+\f
/* INSN is a copy from SRC to DEST, both registers, and SRC does not die
in INSN.
for (p = NEXT_INSN (insn); p; p = NEXT_INSN (p))
{
- if (GET_CODE (p) == CODE_LABEL || GET_CODE (p) == JUMP_INSN
- || (GET_CODE (p) == NOTE
- && (NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_BEG
- || NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_END)))
- break;
-
/* ??? We can't scan past the end of a basic block without updating
- the register lifetime info (REG_DEAD/basic_block_live_at_start).
- A CALL_INSN might be the last insn of a basic block, if it is inside
- an EH region. There is no easy way to tell, so we just always break
- when we see a CALL_INSN if flag_exceptions is nonzero. */
- if (flag_exceptions && GET_CODE (p) == CALL_INSN)
+ the register lifetime info (REG_DEAD/basic_block_live_at_start). */
+ if (perhaps_ends_bb_p (p))
break;
-
- if (GET_RTX_CLASS (GET_CODE (p)) != 'i')
+ else if (! INSN_P (p))
continue;
if (reg_set_p (src, p) || reg_set_p (dest, p)
&& (sregno >= FIRST_PSEUDO_REGISTER
|| ! reg_overlap_mentioned_p (src,
PATTERN (q))))
- {
- /* We assume that a register is used exactly once per
- insn in the REG_N_REFS updates below. If this is not
- correct, no great harm is done.
-
- Since we do not know if we will change the lifetime of
- SREGNO or DREGNO, we must not update REG_LIVE_LENGTH
- or REG_N_CALLS_CROSSED at this time. */
- if (sregno >= FIRST_PSEUDO_REGISTER)
- REG_N_REFS (sregno) -= loop_depth;
-
- if (dregno >= FIRST_PSEUDO_REGISTER)
- REG_N_REFS (dregno) += loop_depth;
- }
+ ;
else
{
validate_replace_rtx (dest, src, q);
REG_NOTES (insn) = note;
}
+ /* DEST is also dead if INSN has a REG_UNUSED note for DEST. */
+ if (! dest_death
+ && (dest_death = find_regno_note (insn, REG_UNUSED, dregno)))
+ {
+ PUT_REG_NOTE_KIND (dest_death, REG_DEAD);
+ remove_note (insn, dest_death);
+ }
+
/* Put death note of DEST on P if we saw it die. */
if (dest_death)
{
for (p = NEXT_INSN (insn); p; p = NEXT_INSN (p))
{
- if (GET_CODE (p) == CODE_LABEL || GET_CODE (p) == JUMP_INSN
- || (GET_CODE (p) == NOTE
- && (NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_BEG
- || NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_END)))
- break;
-
/* ??? We can't scan past the end of a basic block without updating
- the register lifetime info (REG_DEAD/basic_block_live_at_start).
- A CALL_INSN might be the last insn of a basic block, if it is inside
- an EH region. There is no easy way to tell, so we just always break
- when we see a CALL_INSN if flag_exceptions is nonzero. */
- if (flag_exceptions && GET_CODE (p) == CALL_INSN)
+ the register lifetime info (REG_DEAD/basic_block_live_at_start). */
+ if (perhaps_ends_bb_p (p))
break;
-
- if (GET_RTX_CLASS (GET_CODE (p)) != 'i')
+ else if (! INSN_P (p))
continue;
set = single_set (p);
/* Set to stop at next insn. */
for (q = insn; q != NEXT_INSN (p); q = NEXT_INSN (q))
- if (GET_RTX_CLASS (GET_CODE (q)) == 'i')
+ if (INSN_P (q))
{
if (reg_mentioned_p (dest, PATTERN (q)))
- {
- PATTERN (q) = replace_rtx (PATTERN (q), dest, src);
-
- /* We assume that a register is used exactly once per
- insn in the updates below. If this is not correct,
- no great harm is done. */
- REG_N_REFS (dregno) -= loop_depth;
- REG_N_REFS (sregno) += loop_depth;
- }
+ PATTERN (q) = replace_rtx (PATTERN (q), dest, src);
if (GET_CODE (q) == CALL_INSN)
|| ! find_reg_note (insn, REG_DEAD, src_reg)
|| REG_N_SETS (src_no) != 1)
return;
- for (p = PREV_INSN (insn); ! reg_set_p (src_reg, p); p = PREV_INSN (p))
- {
- if (GET_CODE (p) == CODE_LABEL || GET_CODE (p) == JUMP_INSN
- || (GET_CODE (p) == NOTE
- && (NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_BEG
- || NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_END)))
- return;
+ for (p = PREV_INSN (insn); p && ! reg_set_p (src_reg, p); p = PREV_INSN (p))
+ /* ??? We can't scan past the end of a basic block without updating
+ the register lifetime info (REG_DEAD/basic_block_live_at_start). */
+ if (perhaps_ends_bb_p (p))
+ break;
+
+ if (! p)
+ return;
- /* ??? We can't scan past the end of a basic block without updating
- the register lifetime info (REG_DEAD/basic_block_live_at_start).
- A CALL_INSN might be the last insn of a basic block, if it is inside
- an EH region. There is no easy way to tell, so we just always break
- when we see a CALL_INSN if flag_exceptions is nonzero. */
- if (flag_exceptions && GET_CODE (p) == CALL_INSN)
- return;
-
- if (GET_RTX_CLASS (GET_CODE (p)) != 'i')
- continue;
- }
if (! (set = single_set (p))
|| GET_CODE (SET_SRC (set)) != MEM
|| SET_DEST (set) != src_reg)
subreg = gen_rtx_SUBREG (old_mode, src_reg, 0);
while (p = NEXT_INSN (p), p != insn)
{
- if (GET_RTX_CLASS (GET_CODE (p)) != 'i')
+ if (! INSN_P (p))
continue;
/* Make a tenative change. */
instead moving the value to dest directly before the operation. */
static void
-copy_src_to_dest (insn, src, dest, loop_depth, old_max_uid)
+copy_src_to_dest (insn, src, dest, old_max_uid)
rtx insn;
rtx src;
rtx dest;
- int loop_depth;
int old_max_uid;
{
rtx seq;
if (GET_CODE (src) == REG
&& REG_LIVE_LENGTH (REGNO (src)) > 0
&& GET_CODE (dest) == REG
+ && !RTX_UNCHANGING_P (dest)
&& REG_LIVE_LENGTH (REGNO (dest)) > 0
&& (set = single_set (insn)) != NULL_RTX
&& !reg_mentioned_p (dest, SET_SRC (set))
/* Update the various register tables. */
dest_regno = REGNO (dest);
- REG_N_SETS (dest_regno) += loop_depth;
- REG_N_REFS (dest_regno) += loop_depth;
+ REG_N_SETS (dest_regno) ++;
REG_LIVE_LENGTH (dest_regno)++;
if (REGNO_FIRST_UID (dest_regno) == insn_uid)
REGNO_FIRST_UID (dest_regno) = move_uid;
{
rtx s;
- if (GET_RTX_CLASS (GET_CODE (p)) != 'i')
+ if (! INSN_P (p))
continue;
s = single_set (p);
if (s != 0
/* cse disrupts preincrement / postdecrement squences when it finds a
hard register as ultimate source, like the frame pointer. */
-int
+static int
fixup_match_2 (insn, dst, src, offset, regmove_dump_file)
rtx insn, dst, src, offset;
FILE *regmove_dump_file;
{
rtx pset;
- if (GET_CODE (p) == CODE_LABEL
- || GET_CODE (p) == JUMP_INSN
- || (GET_CODE (p) == NOTE
- && (NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_BEG
- || NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_END)))
- break;
-
/* ??? We can't scan past the end of a basic block without updating
- the register lifetime info (REG_DEAD/basic_block_live_at_start).
- A CALL_INSN might be the last insn of a basic block, if it is inside
- an EH region. There is no easy way to tell, so we just always break
- when we see a CALL_INSN if flag_exceptions is nonzero. */
- if (flag_exceptions && GET_CODE (p) == CALL_INSN)
+ the register lifetime info (REG_DEAD/basic_block_live_at_start). */
+ if (perhaps_ends_bb_p (p))
break;
-
- if (GET_RTX_CLASS (GET_CODE (p)) != 'i')
+ else if (! INSN_P (p))
continue;
if (find_regno_note (p, REG_DEAD, REGNO (dst)))
REG_N_CALLS_CROSSED (REGNO (dst)) += num_calls;
}
- REG_N_REFS (REGNO (dst)) += loop_depth;
- REG_N_REFS (REGNO (src)) -= loop_depth;
-
if (regmove_dump_file)
fprintf (regmove_dump_file,
"Fixed operand of insn %d.\n",
for (p = PREV_INSN (insn); p; p = PREV_INSN (p))
{
if (GET_CODE (p) == CODE_LABEL
- || GET_CODE (p) == JUMP_INSN
- || (GET_CODE (p) == NOTE
- && (NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_BEG
- || NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_END)))
+ || GET_CODE (p) == JUMP_INSN)
break;
- if (GET_RTX_CLASS (GET_CODE (p)) != 'i')
+ if (! INSN_P (p))
continue;
if (reg_overlap_mentioned_p (dst, PATTERN (p)))
{
for (p = NEXT_INSN (insn); p; p = NEXT_INSN (p))
{
if (GET_CODE (p) == CODE_LABEL
- || GET_CODE (p) == JUMP_INSN
- || (GET_CODE (p) == NOTE
- && (NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_BEG
- || NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_END)))
+ || GET_CODE (p) == JUMP_INSN)
break;
- if (GET_RTX_CLASS (GET_CODE (p)) != 'i')
+ if (! INSN_P (p))
continue;
if (reg_overlap_mentioned_p (dst, PATTERN (p)))
{
can supress some optimizations in those zones. */
mark_flags_life_zones (discover_flags_reg ());
- regno_src_regno = (int *)alloca (sizeof *regno_src_regno * nregs);
+ regno_src_regno = (int *) xmalloc (sizeof *regno_src_regno * nregs);
for (i = nregs; --i >= 0; ) regno_src_regno[i] = -1;
- regmove_bb_head = (int *)alloca (sizeof (int) * (old_max_uid + 1));
+ regmove_bb_head = (int *) xmalloc (sizeof (int) * (old_max_uid + 1));
for (i = old_max_uid; i >= 0; i--) regmove_bb_head[i] = -1;
for (i = 0; i < n_basic_blocks; i++)
regmove_bb_head[INSN_UID (BLOCK_HEAD (i))] = i;
/* A forward/backward pass. Replace output operands with input operands. */
- loop_depth = 1;
-
for (pass = 0; pass <= 2; pass++)
{
if (! flag_regmove && pass >= flag_expensive_optimizations)
- return;
+ goto done;
if (regmove_dump_file)
fprintf (regmove_dump_file, "Starting %s pass...\n",
rtx set;
int op_no, match_no;
- if (GET_CODE (insn) == NOTE)
- {
- if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG)
- loop_depth++;
- else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END)
- loop_depth--;
- }
-
set = single_set (insn);
if (! set)
continue;
if (! flag_regmove)
continue;
-#ifdef REGISTER_CONSTRAINTS
if (! find_matches (insn, &match))
continue;
continue;
if (match.early_clobber[match_no]
- && count_occurrences (PATTERN (insn), src) > 1)
+ && count_occurrences (PATTERN (insn), src, 0) > 1)
continue;
/* Make sure match_operand is the destination. */
if (regmove_dump_file)
fprintf (regmove_dump_file, "Starting backward pass...\n");
- loop_depth = 1;
-
for (insn = get_last_insn (); insn; insn = PREV_INSN (insn))
{
- if (GET_CODE (insn) == NOTE)
- {
- if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END)
- loop_depth++;
- else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG)
- loop_depth--;
- }
- if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
+ if (INSN_P (insn))
{
int op_no, match_no;
int success = 0;
continue;
if (match.early_clobber[match_no]
- && count_occurrences (PATTERN (insn), src) > 1)
+ && count_occurrences (PATTERN (insn), src, 0) > 1)
continue;
/* Make sure match_no is the destination. */
{
rtx pset;
- if (GET_CODE (p) == CODE_LABEL
- || GET_CODE (p) == JUMP_INSN
- || (GET_CODE (p) == NOTE
- && (NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_BEG
- || NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_END)))
- break;
-
/* ??? We can't scan past the end of a basic block without
updating the register lifetime info
- (REG_DEAD/basic_block_live_at_start).
- A CALL_INSN might be the last insn of a basic block, if
- it is inside an EH region. There is no easy way to tell,
- so we just always break when we see a CALL_INSN if
- flag_exceptions is nonzero. */
- if (flag_exceptions && GET_CODE (p) == CALL_INSN)
+ (REG_DEAD/basic_block_live_at_start). */
+ if (perhaps_ends_bb_p (p))
break;
-
- if (GET_RTX_CLASS (GET_CODE (p)) != 'i')
+ else if (! INSN_P (p))
continue;
length++;
REG_LIVE_LENGTH (srcno) = 2;
}
- /* We assume that a register is used exactly once per
- insn in the updates above. If this is not correct,
- no great harm is done. */
-
- REG_N_REFS (dstno) += 2 * loop_depth;
- REG_N_REFS (srcno) -= 2 * loop_depth;
-
- /* If that was the only time src was set,
- and src was not live at the start of the
- function, we know that we have no more
- references to src; clear REG_N_REFS so it
- won't make reload do any work. */
- if (REG_N_SETS (REGNO (src)) == 0
- && ! regno_uninitialized (REGNO (src)))
- REG_N_REFS (REGNO (src)) = 0;
-
if (regmove_dump_file)
fprintf (regmove_dump_file,
"Fixed operand %d of insn %d matching operand %d.\n",
/* If we weren't able to replace any of the alternatives, try an
alternative appoach of copying the source to the destination. */
if (!success && copy_src != NULL_RTX)
- copy_src_to_dest (insn, copy_src, copy_dst, loop_depth,
- old_max_uid);
+ copy_src_to_dest (insn, copy_src, copy_dst, old_max_uid);
}
}
-#endif /* REGISTER_CONSTRAINTS */
/* In fixup_match_1, some insns may have been inserted after basic block
ends. Fix that here. */
new = next, next = NEXT_INSN (new);
BLOCK_END (i) = new;
}
+
+ done:
+ /* Clean up. */
+ free (regno_src_regno);
+ free (regmove_bb_head);
}
/* Returns nonzero if INSN's pattern has matching constraints for any operand.
DST is operand number MATCH_NUMBER in INSN.
If BACKWARD is nonzero, we have been called in a backward pass.
Return nonzero for success. */
+
static int
fixup_match_1 (insn, set, src, src_subreg, dst, backward, operand_number,
match_number, regmove_dump_file)
int success = 0;
int num_calls = 0, s_num_calls = 0;
enum rtx_code code = NOTE;
- HOST_WIDE_INT insn_const, newconst;
+ HOST_WIDE_INT insn_const = 0, newconst;
rtx overlap = 0; /* need to move insn ? */
- rtx src_note = find_reg_note (insn, REG_DEAD, src), dst_note;
- int length, s_length, true_loop_depth;
+ rtx src_note = find_reg_note (insn, REG_DEAD, src), dst_note = NULL_RTX;
+ int length, s_length;
+
+ /* If SRC is marked as unchanging, we may not change it.
+ ??? Maybe we could get better code by removing the unchanging bit
+ instead, and changing it back if we don't succeed? */
+ if (RTX_UNCHANGING_P (src))
+ return 0;
if (! src_note)
{
&& XEXP (SET_SRC (set), 0) == src
&& GET_CODE (XEXP (SET_SRC (set), 1)) == CONST_INT)
insn_const = INTVAL (XEXP (SET_SRC (set), 1));
- else if (! stable_but_for_p (SET_SRC (set), src, dst))
+ else if (! stable_and_no_regs_but_for_p (SET_SRC (set), src, dst))
return 0;
else
/* We might find a src_note while scanning. */
for (length = s_length = 0, p = NEXT_INSN (insn); p; p = NEXT_INSN (p))
{
- if (GET_CODE (p) == CODE_LABEL || GET_CODE (p) == JUMP_INSN
- || (GET_CODE (p) == NOTE
- && (NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_BEG
- || NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_END)))
- break;
-
/* ??? We can't scan past the end of a basic block without updating
- the register lifetime info (REG_DEAD/basic_block_live_at_start).
- A CALL_INSN might be the last insn of a basic block, if it is
- inside an EH region. There is no easy way to tell, so we just
- always break when we see a CALL_INSN if flag_exceptions is nonzero. */
- if (flag_exceptions && GET_CODE (p) == CALL_INSN)
+ the register lifetime info (REG_DEAD/basic_block_live_at_start). */
+ if (perhaps_ends_bb_p (p))
break;
-
- if (GET_RTX_CLASS (GET_CODE (p)) != 'i')
+ else if (! INSN_P (p))
continue;
length++;
if (! src_note)
{
rtx q;
- rtx set2;
+ rtx set2 = NULL_RTX;
/* If an optimization is done, the value of SRC while P
is executed will be changed. Check that this is OK. */
break;
for (q = p; q; q = NEXT_INSN (q))
{
- if (GET_CODE (q) == CODE_LABEL || GET_CODE (q) == JUMP_INSN
- || (GET_CODE (q) == NOTE
- && (NOTE_LINE_NUMBER (q) == NOTE_INSN_LOOP_BEG
- || NOTE_LINE_NUMBER (q) == NOTE_INSN_LOOP_END)))
- {
- q = 0;
- break;
- }
-
/* ??? We can't scan past the end of a basic block without
updating the register lifetime info
- (REG_DEAD/basic_block_live_at_start).
- A CALL_INSN might be the last insn of a basic block, if
- it is inside an EH region. There is no easy way to tell,
- so we just always break when we see a CALL_INSN if
- flag_exceptions is nonzero. */
- if (flag_exceptions && GET_CODE (q) == CALL_INSN)
+ (REG_DEAD/basic_block_live_at_start). */
+ if (perhaps_ends_bb_p (q))
{
q = 0;
break;
}
-
- if (GET_RTX_CLASS (GET_CODE (q)) != 'i')
+ else if (! INSN_P (q))
continue;
- if (reg_overlap_mentioned_p (src, PATTERN (q))
- || reg_set_p (src, q))
+ else if (reg_overlap_mentioned_p (src, PATTERN (q))
+ || reg_set_p (src, q))
break;
}
if (q)
/* Reject out of range shifts. */
if (code != PLUS
&& (newconst < 0
- || (newconst
- >= GET_MODE_BITSIZE (GET_MODE (SET_SRC (set2))))))
+ || ((unsigned HOST_WIDE_INT) newconst
+ >= (GET_MODE_BITSIZE (GET_MODE
+ (SET_SRC (set2)))))))
break;
if (code == PLUS)
{
if (! success)
return 0;
- true_loop_depth = backward ? 2 - loop_depth : loop_depth;
-
/* Remove the death note for DST from P. */
remove_note (p, dst_note);
if (code == MINUS)
post_inc = 0;
validate_change (insn, &XEXP (SET_SRC (set), 1), GEN_INT (insn_const), 0);
REG_N_SETS (REGNO (src))++;
- REG_N_REFS (REGNO (src)) += true_loop_depth;
REG_LIVE_LENGTH (REGNO (src))++;
}
if (overlap)
/* emit_insn_after_with_line_notes has no
return value, so search for the new insn. */
insn = p;
- while (GET_RTX_CLASS (GET_CODE (insn)) != 'i'
- || PATTERN (insn) != pat)
+ while (! INSN_P (insn) || PATTERN (insn) != pat)
insn = PREV_INSN (insn);
REG_NOTES (insn) = notes;
if (! overlap && (code == PLUS || code == MINUS))
{
rtx note = find_reg_note (insn, REG_EQUAL, NULL_RTX);
- rtx q, set2;
+ rtx q, set2 = NULL_RTX;
int num_calls2 = 0, s_length2 = 0;
if (note && CONSTANT_P (XEXP (note, 0)))
{
for (q = PREV_INSN (insn); q; q = PREV_INSN(q))
{
- if (GET_CODE (q) == CODE_LABEL || GET_CODE (q) == JUMP_INSN
- || (GET_CODE (q) == NOTE
- && (NOTE_LINE_NUMBER (q) == NOTE_INSN_LOOP_BEG
- || NOTE_LINE_NUMBER (q) == NOTE_INSN_LOOP_END)))
- {
- q = 0;
- break;
- }
-
/* ??? We can't scan past the end of a basic block without
updating the register lifetime info
- (REG_DEAD/basic_block_live_at_start).
- A CALL_INSN might be the last insn of a basic block, if
- it is inside an EH region. There is no easy way to tell,
- so we just always break when we see a CALL_INSN if
- flag_exceptions is nonzero. */
- if (flag_exceptions && GET_CODE (q) == CALL_INSN)
+ (REG_DEAD/basic_block_live_at_start). */
+ if (perhaps_ends_bb_p (q))
{
q = 0;
break;
}
-
- if (GET_RTX_CLASS (GET_CODE (q)) != 'i')
+ else if (! INSN_P (q))
continue;
+
s_length2++;
if (reg_set_p (src, q))
{
NOTE_SOURCE_FILE (q) = 0;
REG_N_SETS (REGNO (src))--;
REG_N_CALLS_CROSSED (REGNO (src)) -= num_calls2;
- REG_N_REFS (REGNO (src)) -= true_loop_depth;
REG_LIVE_LENGTH (REGNO (src)) -= s_length2;
insn_const = 0;
}
inc_dest = post_inc_set ? SET_DEST (post_inc_set) : src;
for (q = post_inc; (q = NEXT_INSN (q)); )
{
- if (GET_CODE (q) == CODE_LABEL || GET_CODE (q) == JUMP_INSN
- || (GET_CODE (q) == NOTE
- && (NOTE_LINE_NUMBER (q) == NOTE_INSN_LOOP_BEG
- || NOTE_LINE_NUMBER (q) == NOTE_INSN_LOOP_END)))
- break;
-
/* ??? We can't scan past the end of a basic block without updating
- the register lifetime info (REG_DEAD/basic_block_live_at_start).
- A CALL_INSN might be the last insn of a basic block, if it
- is inside an EH region. There is no easy way to tell so we
- just always break when we see a CALL_INSN if flag_exceptions
- is nonzero. */
- if (flag_exceptions && GET_CODE (q) == CALL_INSN)
+ the register lifetime info
+ (REG_DEAD/basic_block_live_at_start). */
+ if (perhaps_ends_bb_p (q))
break;
-
- if (GET_RTX_CLASS (GET_CODE (q)) != 'i')
+ else if (! INSN_P (q))
continue;
- if (src != inc_dest && (reg_overlap_mentioned_p (src, PATTERN (q))
- || reg_set_p (src, q)))
+ else if (src != inc_dest
+ && (reg_overlap_mentioned_p (src, PATTERN (q))
+ || reg_set_p (src, q)))
break;
- if (reg_set_p (inc_dest, q))
+ else if (reg_set_p (inc_dest, q))
break;
- if (reg_overlap_mentioned_p (inc_dest, PATTERN (q)))
+ else if (reg_overlap_mentioned_p (inc_dest, PATTERN (q)))
{
try_auto_increment (q, post_inc,
post_inc_set, inc_dest, newconst, 1);
}
}
}
+
/* Move the death note for DST to INSN if it is used
there. */
if (reg_overlap_mentioned_p (dst, PATTERN (insn)))
if (REG_LIVE_LENGTH (REGNO (dst)) < 2)
REG_LIVE_LENGTH (REGNO (dst)) = 2;
}
-
- /* We assume that a register is used exactly once per
- insn in the updates above. If this is not correct,
- no great harm is done. */
-
- REG_N_REFS (REGNO (src)) += 2 * true_loop_depth;
- REG_N_REFS (REGNO (dst)) -= 2 * true_loop_depth;
-
- /* If that was the only time dst was set,
- and dst was not live at the start of the
- function, we know that we have no more
- references to dst; clear REG_N_REFS so it
- won't make reload do any work. */
- if (REG_N_SETS (REGNO (dst)) == 0
- && ! regno_uninitialized (REGNO (dst)))
- REG_N_REFS (REGNO (dst)) = 0;
-
if (regmove_dump_file)
fprintf (regmove_dump_file,
"Fixed operand %d of insn %d matching operand %d.\n",
}
-/* return nonzero if X is stable but for mentioning SRC or mentioning /
- changing DST . If in doubt, presume it is unstable. */
+/* return nonzero if X is stable and mentions no regsiters but for
+ mentioning SRC or mentioning / changing DST . If in doubt, presume
+ it is unstable.
+ The rationale is that we want to check if we can move an insn easily
+ while just paying attention to SRC and DST. A register is considered
+ stable if it has the RTX_UNCHANGING_P bit set, but that would still
+ leave the burden to update REG_DEAD / REG_UNUSED notes, so we don't
+ want any registers but SRC and DST. */
static int
-stable_but_for_p (x, src, dst)
+stable_and_no_regs_but_for_p (x, src, dst)
rtx x, src, dst;
{
RTX_CODE code = GET_CODE (x);
int i;
const char *fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
- if (fmt[i] == 'e' && ! stable_but_for_p (XEXP (x, i), src, dst))
+ if (fmt[i] == 'e'
+ && ! stable_and_no_regs_but_for_p (XEXP (x, i), src, dst))
return 0;
return 1;
}
case 'o':
- if (x == src || x == dst)
- return 1;
+ if (code == REG)
+ return x == src || x == dst;
+ /* If this is a MEM, look inside - there might be a register hidden in
+ the address of an unchanging MEM. */
+ if (code == MEM
+ && ! stable_and_no_regs_but_for_p (XEXP (x, 0), src, dst))
+ return 0;
/* fall through */
default:
return ! rtx_unstable_p (x);
}
}
+\f
+/* Track stack adjustments and stack memory references. Attempt to
+ reduce the number of stack adjustments by back-propogating across
+ the memory references.
+
+ This is intended primarily for use with targets that do not define
+ ACCUMULATE_OUTGOING_ARGS. It is of significantly more value to
+ targets that define PREFERRED_STACK_BOUNDARY more aligned than
+ STACK_BOUNDARY (e.g. x86), or if not all registers can be pushed
+ (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
+ (a) no longer valid because we've exceeded their range, (b) a
+ non-trivial push instruction, or (c) a call instruction.
+
+ Restriction B is based on the assumption that push instructions
+ are smaller or faster. If a port really wants to remove all
+ pushes, it should have defined ACCUMULATE_OUTGOING_ARGS. The
+ one exception that is made is for an add immediately followed
+ by a push. */
+
+/* This structure records stack memory references between stack adjusting
+ instructions. */
+
+struct csa_memlist
+{
+ HOST_WIDE_INT sp_offset;
+ rtx insn, *mem;
+ struct csa_memlist *next;
+};
+
+static int stack_memref_p PARAMS ((rtx));
+static rtx single_set_for_csa PARAMS ((rtx));
+static void free_csa_memlist PARAMS ((struct csa_memlist *));
+static struct csa_memlist *record_one_stack_memref
+ PARAMS ((rtx, rtx *, struct csa_memlist *));
+static int try_apply_stack_adjustment
+ PARAMS ((rtx, struct csa_memlist *, HOST_WIDE_INT, HOST_WIDE_INT));
+static void combine_stack_adjustments_for_block PARAMS ((basic_block));
+static int record_stack_memrefs PARAMS ((rtx *, void *));
+
+
+/* Main entry point for stack adjustment combination. */
+
+void
+combine_stack_adjustments ()
+{
+ int i;
+
+ for (i = 0; i < n_basic_blocks; ++i)
+ combine_stack_adjustments_for_block (BASIC_BLOCK (i));
+}
+
+/* Recognize a MEM of the form (sp) or (plus sp const). */
+
+static int
+stack_memref_p (x)
+ rtx x;
+{
+ if (GET_CODE (x) != MEM)
+ return 0;
+ x = XEXP (x, 0);
+
+ if (x == stack_pointer_rtx)
+ return 1;
+ if (GET_CODE (x) == PLUS
+ && XEXP (x, 0) == stack_pointer_rtx
+ && GET_CODE (XEXP (x, 1)) == CONST_INT)
+ return 1;
+
+ return 0;
+}
+
+/* Recognize either normal single_set or the hack in i386.md for
+ tying fp and sp adjustments. */
+
+static rtx
+single_set_for_csa (insn)
+ rtx insn;
+{
+ int i;
+ rtx tmp = single_set (insn);
+ if (tmp)
+ return tmp;
+
+ if (GET_CODE (insn) != INSN
+ || GET_CODE (PATTERN (insn)) != PARALLEL)
+ return NULL_RTX;
+
+ tmp = PATTERN (insn);
+ if (GET_CODE (XVECEXP (tmp, 0, 0)) != SET)
+ return NULL_RTX;
+
+ for (i = 1; i < XVECLEN (tmp, 0); ++i)
+ {
+ rtx this = XVECEXP (tmp, 0, i);
+
+ /* The special case is allowing a no-op set. */
+ if (GET_CODE (this) == SET
+ && SET_SRC (this) == SET_DEST (this))
+ ;
+ else if (GET_CODE (this) != CLOBBER
+ && GET_CODE (this) != USE)
+ return NULL_RTX;
+ }
+
+ return XVECEXP (tmp, 0, 0);
+}
+
+/* Free the list of csa_memlist nodes. */
+
+static void
+free_csa_memlist (memlist)
+ struct csa_memlist *memlist;
+{
+ struct csa_memlist *next;
+ for (; memlist ; memlist = next)
+ {
+ next = memlist->next;
+ free (memlist);
+ }
+}
+
+/* Create a new csa_memlist node from the given memory reference.
+ It is already known that the memory is stack_memref_p. */
+
+static struct csa_memlist *
+record_one_stack_memref (insn, mem, next_memlist)
+ rtx insn, *mem;
+ struct csa_memlist *next_memlist;
+{
+ struct csa_memlist *ml;
+
+ ml = (struct csa_memlist *) xmalloc (sizeof (*ml));
+
+ if (XEXP (*mem, 0) == stack_pointer_rtx)
+ ml->sp_offset = 0;
+ else
+ ml->sp_offset = INTVAL (XEXP (XEXP (*mem, 0), 1));
+
+ ml->insn = insn;
+ ml->mem = mem;
+ ml->next = next_memlist;
+
+ return ml;
+}
+
+/* Attempt to apply ADJUST to the stack adjusting insn INSN, as well
+ as each of the memories in MEMLIST. Return true on success. */
+
+static int
+try_apply_stack_adjustment (insn, memlist, new_adjust, delta)
+ rtx insn;
+ struct csa_memlist *memlist;
+ HOST_WIDE_INT new_adjust, delta;
+{
+ 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);
+ }
+
+ if (apply_change_group ())
+ {
+ /* Succeeded. Update our knowledge of the memory references. */
+ for (ml = memlist; ml ; ml = ml->next)
+ ml->sp_offset -= delta;
+
+ return 1;
+ }
+ else
+ return 0;
+}
+
+/* Called via for_each_rtx and used to record all stack memory references in
+ the insn and discard all other stack pointer references. */
+struct record_stack_memrefs_data
+{
+ rtx insn;
+ struct csa_memlist *memlist;
+};
+
+static int
+record_stack_memrefs (xp, data)
+ rtx *xp;
+ void *data;
+{
+ rtx x = *xp;
+ struct record_stack_memrefs_data *d =
+ (struct record_stack_memrefs_data *) data;
+ if (!x)
+ return 0;
+ switch (GET_CODE (x))
+ {
+ case MEM:
+ 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. */
+ if (stack_memref_p (x))
+ {
+ d->memlist = record_one_stack_memref (d->insn, xp, d->memlist);
+ return -1;
+ }
+ 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)
+ return 1;
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+/* Subroutine of combine_stack_adjustments, called for each basic block. */
+
+static void
+combine_stack_adjustments_for_block (bb)
+ basic_block bb;
+{
+ HOST_WIDE_INT last_sp_adjust = 0;
+ rtx last_sp_set = NULL_RTX;
+ struct csa_memlist *memlist = NULL;
+ rtx pending_delete;
+ rtx insn, next;
+ struct record_stack_memrefs_data data;
+
+ for (insn = bb->head; ; insn = next)
+ {
+ rtx set;
+
+ pending_delete = NULL_RTX;
+ next = NEXT_INSN (insn);
+
+ if (! INSN_P (insn))
+ goto processed;
+
+ set = single_set_for_csa (insn);
+ if (set)
+ {
+ rtx dest = SET_DEST (set);
+ rtx src = SET_SRC (set);
+
+ /* Find constant additions to the stack pointer. */
+ if (dest == stack_pointer_rtx
+ && GET_CODE (src) == PLUS
+ && XEXP (src, 0) == stack_pointer_rtx
+ && GET_CODE (XEXP (src, 1)) == CONST_INT)
+ {
+ HOST_WIDE_INT this_adjust = INTVAL (XEXP (src, 1));
+
+ /* If we've not seen an adjustment previously, record
+ it now and continue. */
+ if (! last_sp_set)
+ {
+ last_sp_set = insn;
+ last_sp_adjust = this_adjust;
+ goto processed;
+ }
+
+ /* 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))
+ {
+ free_csa_memlist (memlist);
+ memlist = NULL;
+ last_sp_set = 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)
+ {
+ 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;
+ }
+
+ goto processed;
+ }
+
+ /* Find a predecrement of exactly the previous adjustment and
+ turn it into a direct store. Obviously we can't do this if
+ there were any intervening uses of the stack pointer. */
+ if (memlist == NULL
+ && GET_CODE (dest) == MEM
+ && ((GET_CODE (XEXP (dest, 0)) == PRE_DEC
+ && (last_sp_adjust
+ == (HOST_WIDE_INT) GET_MODE_SIZE (GET_MODE (dest))))
+ || (GET_CODE (XEXP (dest, 0)) == PRE_MODIFY
+ && GET_CODE (XEXP (XEXP (dest, 0), 1)) == PLUS
+ && XEXP (XEXP (XEXP (dest, 0), 1), 0) == stack_pointer_rtx
+ && (GET_CODE (XEXP (XEXP (XEXP (dest, 0), 1), 1))
+ == CONST_INT)
+ && (INTVAL (XEXP (XEXP (XEXP (dest, 0), 1), 1))
+ == -last_sp_adjust)))
+ && XEXP (XEXP (dest, 0), 0) == stack_pointer_rtx
+ && ! 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))
+ {
+ 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;
+ last_sp_adjust = 0;
+ goto processed;
+ }
+ }
+
+ data.insn = insn;
+ data.memlist = memlist;
+ if (GET_CODE (insn) != CALL_INSN && last_sp_set
+ && !for_each_rtx (&PATTERN (insn), record_stack_memrefs, &data))
+ {
+ memlist = data.memlist;
+ goto processed;
+ }
+ memlist = data.memlist;
+
+ /* Otherwise, we were not able to process the instruction.
+ Do not continue collecting data across such a one. */
+ if (last_sp_set
+ && (GET_CODE (insn) == CALL_INSN
+ || reg_mentioned_p (stack_pointer_rtx, PATTERN (insn))))
+ {
+ free_csa_memlist (memlist);
+ memlist = NULL;
+ last_sp_set = NULL_RTX;
+ last_sp_adjust = 0;
+ }
+
+ processed:
+ if (insn == bb->end)
+ break;
+
+ if (pending_delete)
+ flow_delete_insn (pending_delete);
+ }
+
+ if (pending_delete)
+ {
+ bb->end = PREV_INSN (pending_delete);
+ flow_delete_insn (pending_delete);
+ }
+}