summaryrefslogtreecommitdiff
path: root/gcc/flow.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/flow.c')
-rw-r--r--gcc/flow.c372
1 files changed, 214 insertions, 158 deletions
diff --git a/gcc/flow.c b/gcc/flow.c
index a8f6061aedf..7749d89bc98 100644
--- a/gcc/flow.c
+++ b/gcc/flow.c
@@ -372,6 +372,8 @@ static rtx not_reg_cond PARAMS ((rtx));
static rtx nand_reg_cond PARAMS ((rtx, rtx));
#endif
#ifdef AUTO_INC_DEC
+static void attempt_auto_inc PARAMS ((struct propagate_block_info *,
+ rtx, rtx, rtx, rtx, rtx));
static void find_auto_inc PARAMS ((struct propagate_block_info *,
rtx, rtx));
static int try_pre_increment_1 PARAMS ((struct propagate_block_info *,
@@ -4800,6 +4802,156 @@ nand_reg_cond (old, x)
#ifdef AUTO_INC_DEC
+/* Try to substitute the auto-inc expression INC as the address inside
+ MEM which occurs in INSN. Currently, the address of MEM is an expression
+ involving INCR_REG, and INCR is the next use of INCR_REG; it is an insn
+ that has a single set whose source is a PLUS of INCR_REG and something
+ else. */
+
+static void
+attempt_auto_inc (pbi, inc, insn, mem, incr, incr_reg)
+ struct propagate_block_info *pbi;
+ rtx inc, insn, mem, incr, incr_reg;
+{
+ int regno = REGNO (incr_reg);
+ rtx set = single_set (incr);
+ rtx q = SET_DEST (set);
+ rtx y = SET_SRC (set);
+ int opnum = XEXP (y, 0) == incr_reg ? 0 : 1;
+
+ /* Make sure this reg appears only once in this insn. */
+ if (count_occurrences (PATTERN (insn), incr_reg, 1) != 1)
+ return;
+
+ if (dead_or_set_p (incr, incr_reg)
+ /* Mustn't autoinc an eliminable register. */
+ && (regno >= FIRST_PSEUDO_REGISTER
+ || ! TEST_HARD_REG_BIT (elim_reg_set, regno)))
+ {
+ /* This is the simple case. Try to make the auto-inc. If
+ we can't, we are done. Otherwise, we will do any
+ needed updates below. */
+ if (! validate_change (insn, &XEXP (mem, 0), inc, 0))
+ return;
+ }
+ else if (GET_CODE (q) == REG
+ /* PREV_INSN used here to check the semi-open interval
+ [insn,incr). */
+ && ! reg_used_between_p (q, PREV_INSN (insn), incr)
+ /* We must also check for sets of q as q may be
+ a call clobbered hard register and there may
+ be a call between PREV_INSN (insn) and incr. */
+ && ! reg_set_between_p (q, PREV_INSN (insn), incr))
+ {
+ /* We have *p followed sometime later by q = p+size.
+ Both p and q must be live afterward,
+ and q is not used between INSN and its assignment.
+ Change it to q = p, ...*q..., q = q+size.
+ Then fall into the usual case. */
+ rtx insns, temp;
+ basic_block bb;
+
+ start_sequence ();
+ emit_move_insn (q, incr_reg);
+ insns = get_insns ();
+ end_sequence ();
+
+ if (basic_block_for_insn)
+ for (temp = insns; temp; temp = NEXT_INSN (temp))
+ set_block_for_insn (temp, pbi->bb);
+
+ /* If we can't make the auto-inc, or can't make the
+ replacement into Y, exit. There's no point in making
+ the change below if we can't do the auto-inc and doing
+ so is not correct in the pre-inc case. */
+
+ XEXP (inc, 0) = q;
+ validate_change (insn, &XEXP (mem, 0), inc, 1);
+ validate_change (incr, &XEXP (y, opnum), q, 1);
+ if (! apply_change_group ())
+ return;
+
+ /* We now know we'll be doing this change, so emit the
+ new insn(s) and do the updates. */
+ emit_insns_before (insns, insn);
+
+ if (pbi->bb->head == insn)
+ pbi->bb->head = insns;
+
+ /* INCR will become a NOTE and INSN won't contain a
+ use of INCR_REG. If a use of INCR_REG was just placed in
+ the insn before INSN, make that the next use.
+ Otherwise, invalidate it. */
+ if (GET_CODE (PREV_INSN (insn)) == INSN
+ && GET_CODE (PATTERN (PREV_INSN (insn))) == SET
+ && SET_SRC (PATTERN (PREV_INSN (insn))) == incr_reg)
+ pbi->reg_next_use[regno] = PREV_INSN (insn);
+ else
+ pbi->reg_next_use[regno] = 0;
+
+ incr_reg = q;
+ regno = REGNO (q);
+
+ /* REGNO is now used in INCR which is below INSN, but
+ it previously wasn't live here. If we don't mark
+ it as live, we'll put a REG_DEAD note for it
+ on this insn, which is incorrect. */
+ SET_REGNO_REG_SET (pbi->reg_live, regno);
+
+ /* If there are any calls between INSN and INCR, show
+ that REGNO now crosses them. */
+ for (temp = insn; temp != incr; temp = NEXT_INSN (temp))
+ if (GET_CODE (temp) == CALL_INSN)
+ REG_N_CALLS_CROSSED (regno)++;
+ }
+ else
+ return;
+
+ /* If we haven't returned, it means we were able to make the
+ auto-inc, so update the status. First, record that this insn
+ has an implicit side effect. */
+
+ REG_NOTES (insn)
+ = alloc_EXPR_LIST (REG_INC, incr_reg, REG_NOTES (insn));
+
+ /* Modify the old increment-insn to simply copy
+ the already-incremented value of our register. */
+ if (! validate_change (incr, &SET_SRC (set), incr_reg, 0))
+ abort ();
+
+ /* If that makes it a no-op (copying the register into itself) delete
+ it so it won't appear to be a "use" and a "set" of this
+ register. */
+ if (REGNO (SET_DEST (set)) == REGNO (incr_reg))
+ {
+ /* If the original source was dead, it's dead now. */
+ rtx note;
+
+ while (note = find_reg_note (incr, REG_DEAD, NULL_RTX))
+ {
+ remove_note (incr, note);
+ if (XEXP (note, 0) != incr_reg)
+ CLEAR_REGNO_REG_SET (pbi->reg_live, REGNO (XEXP (note, 0)));
+ }
+
+ PUT_CODE (incr, NOTE);
+ NOTE_LINE_NUMBER (incr) = NOTE_INSN_DELETED;
+ NOTE_SOURCE_FILE (incr) = 0;
+ }
+
+ if (regno >= FIRST_PSEUDO_REGISTER)
+ {
+ /* Count an extra reference to the reg. When a reg is
+ incremented, spilling it is worse, so we want to make
+ that less likely. */
+ REG_N_REFS (regno) += (optimize_size ? 1 : pbi->bb->loop_depth + 1);
+
+ /* Count the increment as a setting of the register,
+ even though it isn't a SET in rtl. */
+ REG_N_SETS (regno)++;
+ }
+}
+
/* X is a MEM found in INSN. See if we can convert it into an auto-increment
reference. */
@@ -4811,7 +4963,12 @@ find_auto_inc (pbi, x, insn)
{
rtx addr = XEXP (x, 0);
HOST_WIDE_INT offset = 0;
- rtx set;
+ rtx set, y, incr, inc_val;
+ int regno;
+ int size = GET_MODE_SIZE (GET_MODE (x));
+
+ if (GET_CODE (insn) == JUMP_INSN)
+ return;
/* Here we detect use of an index register which might be good for
postincrement, postdecrement, preincrement, or predecrement. */
@@ -4819,170 +4976,69 @@ find_auto_inc (pbi, x, insn)
if (GET_CODE (addr) == PLUS && GET_CODE (XEXP (addr, 1)) == CONST_INT)
offset = INTVAL (XEXP (addr, 1)), addr = XEXP (addr, 0);
- if (GET_CODE (addr) == REG)
- {
- register rtx y;
- register int size = GET_MODE_SIZE (GET_MODE (x));
- rtx use;
- rtx incr;
- int regno = REGNO (addr);
-
- /* Is the next use an increment that might make auto-increment? */
- if ((incr = pbi->reg_next_use[regno]) != 0
- && (set = single_set (incr)) != 0
- && GET_CODE (set) == SET
- && BLOCK_NUM (incr) == BLOCK_NUM (insn)
- /* Can't add side effects to jumps; if reg is spilled and
- reloaded, there's no way to store back the altered value. */
- && GET_CODE (insn) != JUMP_INSN
- && (y = SET_SRC (set), GET_CODE (y) == PLUS)
- && XEXP (y, 0) == addr
- && GET_CODE (XEXP (y, 1)) == CONST_INT
- && ((HAVE_POST_INCREMENT
- && (INTVAL (XEXP (y, 1)) == size && offset == 0))
- || (HAVE_POST_DECREMENT
- && (INTVAL (XEXP (y, 1)) == - size && offset == 0))
- || (HAVE_PRE_INCREMENT
- && (INTVAL (XEXP (y, 1)) == size && offset == size))
- || (HAVE_PRE_DECREMENT
- && (INTVAL (XEXP (y, 1)) == - size && offset == - size)))
- /* Make sure this reg appears only once in this insn. */
- && (use = find_use_as_address (PATTERN (insn), addr, offset),
- use != 0 && use != (rtx) 1))
- {
- rtx q = SET_DEST (set);
- enum rtx_code inc_code = (INTVAL (XEXP (y, 1)) == size
- ? (offset ? PRE_INC : POST_INC)
- : (offset ? PRE_DEC : POST_DEC));
-
- if (dead_or_set_p (incr, addr)
- /* Mustn't autoinc an eliminable register. */
- && (regno >= FIRST_PSEUDO_REGISTER
- || ! TEST_HARD_REG_BIT (elim_reg_set, regno)))
- {
- /* This is the simple case. Try to make the auto-inc. If
- we can't, we are done. Otherwise, we will do any
- needed updates below. */
- if (! validate_change (insn, &XEXP (x, 0),
- gen_rtx_fmt_e (inc_code, Pmode, addr),
- 0))
- return;
- }
- else if (GET_CODE (q) == REG
- /* PREV_INSN used here to check the semi-open interval
- [insn,incr). */
- && ! reg_used_between_p (q, PREV_INSN (insn), incr)
- /* We must also check for sets of q as q may be
- a call clobbered hard register and there may
- be a call between PREV_INSN (insn) and incr. */
- && ! reg_set_between_p (q, PREV_INSN (insn), incr))
- {
- /* We have *p followed sometime later by q = p+size.
- Both p and q must be live afterward,
- and q is not used between INSN and its assignment.
- Change it to q = p, ...*q..., q = q+size.
- Then fall into the usual case. */
- rtx insns, temp;
-
- start_sequence ();
- emit_move_insn (q, addr);
- insns = get_insns ();
- end_sequence ();
-
- if (basic_block_for_insn)
- for (temp = insns; temp; temp = NEXT_INSN (temp))
- set_block_for_insn (temp, pbi->bb);
-
- /* If we can't make the auto-inc, or can't make the
- replacement into Y, exit. There's no point in making
- the change below if we can't do the auto-inc and doing
- so is not correct in the pre-inc case. */
-
- validate_change (insn, &XEXP (x, 0),
- gen_rtx_fmt_e (inc_code, Pmode, q),
- 1);
- validate_change (incr, &XEXP (y, 0), q, 1);
- if (! apply_change_group ())
- return;
-
- /* We now know we'll be doing this change, so emit the
- new insn(s) and do the updates. */
- emit_insns_before (insns, insn);
-
- if (pbi->bb->head == insn)
- pbi->bb->head = insns;
-
- /* INCR will become a NOTE and INSN won't contain a
- use of ADDR. If a use of ADDR was just placed in
- the insn before INSN, make that the next use.
- Otherwise, invalidate it. */
- if (GET_CODE (PREV_INSN (insn)) == INSN
- && GET_CODE (PATTERN (PREV_INSN (insn))) == SET
- && SET_SRC (PATTERN (PREV_INSN (insn))) == addr)
- pbi->reg_next_use[regno] = PREV_INSN (insn);
- else
- pbi->reg_next_use[regno] = 0;
-
- addr = q;
- regno = REGNO (q);
-
- /* REGNO is now used in INCR which is below INSN, but it
- previously wasn't live here. If we don't mark it as
- live, we'll put a REG_DEAD note for it on this insn,
- which is incorrect. */
- SET_REGNO_REG_SET (pbi->reg_live, regno);
-
- /* If there are any calls between INSN and INCR, show
- that REGNO now crosses them. */
- for (temp = insn; temp != incr; temp = NEXT_INSN (temp))
- if (GET_CODE (temp) == CALL_INSN)
- REG_N_CALLS_CROSSED (regno)++;
- }
- else
- return;
-
- /* If we haven't returned, it means we were able to make the
- auto-inc, so update the status. First, record that this insn
- has an implicit side effect. */
+ if (GET_CODE (addr) != REG)
+ return;
- REG_NOTES (insn)
- = alloc_EXPR_LIST (REG_INC, addr, REG_NOTES (insn));
+ regno = REGNO (addr);
- /* Modify the old increment-insn to simply copy
- the already-incremented value of our register. */
- if (! validate_change (incr, &SET_SRC (set), addr, 0))
- abort ();
+ /* Is the next use an increment that might make auto-increment? */
+ incr = pbi->reg_next_use[regno];
+ if (incr == 0 || BLOCK_NUM (incr) != BLOCK_NUM (insn))
+ return;
+ set = single_set (incr);
+ if (set == 0 || GET_CODE (set) != SET)
+ return;
+ y = SET_SRC (set);
- /* If that makes it a no-op (copying the register into itself) delete
- it so it won't appear to be a "use" and a "set" of this
- register. */
- if (SET_DEST (set) == addr)
- {
- /* If the original source was dead, it's dead now. */
- rtx note = find_reg_note (incr, REG_DEAD, NULL_RTX);
- if (note && XEXP (note, 0) != addr)
- CLEAR_REGNO_REG_SET (pbi->reg_live, REGNO (XEXP (note, 0)));
-
- PUT_CODE (incr, NOTE);
- NOTE_LINE_NUMBER (incr) = NOTE_INSN_DELETED;
- NOTE_SOURCE_FILE (incr) = 0;
- }
+ if (GET_CODE (y) != PLUS)
+ return;
- if (regno >= FIRST_PSEUDO_REGISTER)
- {
- /* Count an extra reference to the reg. When a reg is
- incremented, spilling it is worse, so we want to make
- that less likely. */
- REG_N_REFS (regno) += (optimize_size ? 1
- : pbi->bb->loop_depth + 1);
+ if (REGNO (XEXP (y, 0)) == REGNO (addr))
+ inc_val = XEXP (y, 1);
+ else if (REGNO (XEXP (y, 1)) == REGNO (addr))
+ inc_val = XEXP (y, 0);
+ else
+ abort ();
- /* Count the increment as a setting of the register,
- even though it isn't a SET in rtl. */
- REG_N_SETS (regno)++;
- }
- }
+ if (GET_CODE (inc_val) == CONST_INT)
+ {
+ if (HAVE_POST_INCREMENT
+ && (INTVAL (inc_val) == size && offset == 0))
+ attempt_auto_inc (pbi, gen_rtx_POST_INC (Pmode, addr), insn, x,
+ incr, addr);
+ else if (HAVE_POST_DECREMENT
+ && (INTVAL (inc_val) == - size && offset == 0))
+ attempt_auto_inc (pbi, gen_rtx_POST_DEC (Pmode, addr), insn, x,
+ incr, addr);
+ else if (HAVE_PRE_INCREMENT
+ && (INTVAL (inc_val) == size && offset == size))
+ attempt_auto_inc (pbi, gen_rtx_PRE_INC (Pmode, addr), insn, x,
+ incr, addr);
+ else if (HAVE_PRE_DECREMENT
+ && (INTVAL (inc_val) == - size && offset == - size))
+ attempt_auto_inc (pbi, gen_rtx_PRE_DEC (Pmode, addr), insn, x,
+ incr, addr);
+ else if (HAVE_POST_MODIFY_DISP && offset == 0)
+ attempt_auto_inc (pbi, gen_rtx_POST_MODIFY (Pmode, addr,
+ gen_rtx_PLUS (Pmode,
+ addr,
+ inc_val)),
+ insn, x, incr, addr);
+ }
+ else if (GET_CODE (inc_val) == REG
+ && ! reg_set_between_p (inc_val, PREV_INSN (insn),
+ NEXT_INSN (incr)))
+
+ {
+ if (HAVE_POST_MODIFY_REG && offset == 0)
+ attempt_auto_inc (pbi, gen_rtx_POST_MODIFY (Pmode, addr,
+ gen_rtx_PLUS (Pmode,
+ addr,
+ inc_val)),
+ insn, x, incr, addr);
}
}
+
#endif /* AUTO_INC_DEC */
static void