diff options
Diffstat (limited to 'gcc/flow.c')
-rw-r--r-- | gcc/flow.c | 372 |
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 |