diff options
Diffstat (limited to 'gcc/function.c')
-rw-r--r-- | gcc/function.c | 65 |
1 files changed, 54 insertions, 11 deletions
diff --git a/gcc/function.c b/gcc/function.c index ef1afaa..f191aec 100644 --- a/gcc/function.c +++ b/gcc/function.c @@ -7296,9 +7296,12 @@ struct epi_info rtx equiv_reg_src; /* If nonzero, the value that SP_EQUIV_REG should be set to once we no longer need its value. */ + rtx const_equiv[FIRST_PSEUDO_REGISTER]; /* Any known constant equivalences + for registers. */ }; static void handle_epilogue_set (rtx, struct epi_info *); +static void update_epilogue_consts PARAMS ((rtx, rtx, void *)); static void emit_equiv_load (struct epi_info *); /* Modify INSN, a list of one or more insns that is part of the epilogue, to @@ -7311,8 +7314,7 @@ keep_stack_depressed (rtx insns) struct epi_info info; rtx insn, next; - /* If the epilogue is just a single instruction, it ust be OK as is. */ - + /* If the epilogue is just a single instruction, it must be OK as is. */ if (NEXT_INSN (insns) == NULL_RTX) return insns; @@ -7324,6 +7326,9 @@ keep_stack_depressed (rtx insns) info.sp_offset = 0; info.equiv_reg_src = 0; + for (j = 0; j < FIRST_PSEUDO_REGISTER; j++) + info.const_equiv[j] = 0; + insn = insns; next = NULL_RTX; while (insn != NULL_RTX) @@ -7415,7 +7420,8 @@ keep_stack_depressed (rtx insns) && !refers_to_regno_p (regno, regno + HARD_REGNO_NREGS (regno, Pmode), - info.equiv_reg_src, NULL)) + info.equiv_reg_src, NULL) + && info.const_equiv[regno] == 0) break; if (regno == FIRST_PSEUDO_REGISTER) @@ -7471,6 +7477,8 @@ keep_stack_depressed (rtx insns) info.sp_equiv_reg = info.new_sp_equiv_reg; info.sp_offset = info.new_sp_offset; + /* Now update any constants this insn sets. */ + note_stores (PATTERN (insn), update_epilogue_consts, &info); insn = next; } @@ -7494,11 +7502,18 @@ handle_epilogue_set (rtx set, struct epi_info *p) if (SET_DEST (set) != stack_pointer_rtx) abort (); - if (GET_CODE (SET_SRC (set)) == PLUS - && GET_CODE (XEXP (SET_SRC (set), 1)) == CONST_INT) + if (GET_CODE (SET_SRC (set)) == PLUS) { p->new_sp_equiv_reg = XEXP (SET_SRC (set), 0); - p->new_sp_offset = INTVAL (XEXP (SET_SRC (set), 1)); + if (GET_CODE (XEXP (SET_SRC (set), 1)) == CONST_INT) + p->new_sp_offset = INTVAL (XEXP (SET_SRC (set), 1)); + else if (GET_CODE (XEXP (SET_SRC (set), 1)) == REG + && REGNO (XEXP (SET_SRC (set), 1)) < FIRST_PSEUDO_REGISTER + && p->const_equiv[REGNO (XEXP (SET_SRC (set), 1))] != 0) + p->new_sp_offset + = INTVAL (p->const_equiv[REGNO (XEXP (SET_SRC (set), 1))]); + else + abort (); } else p->new_sp_equiv_reg = SET_SRC (set), p->new_sp_offset = 0; @@ -7521,11 +7536,16 @@ handle_epilogue_set (rtx set, struct epi_info *p) there seems little point in handling that case. Note that we have to allow for the case where we are setting the register set in the previous part of a PARALLEL inside a single insn. But use the - old offset for any updates within this insn. */ + old offset for any updates within this insn. We must allow for the case + where the register is being set in a different (usually wider) mode than + Pmode). */ else if (p->new_sp_equiv_reg != 0 && reg_set_p (p->new_sp_equiv_reg, set)) { - if (!rtx_equal_p (p->new_sp_equiv_reg, SET_DEST (set)) - || p->equiv_reg_src != 0) + if (p->equiv_reg_src != 0 + || GET_CODE (p->new_sp_equiv_reg) != REG + || GET_CODE (SET_DEST (set)) != REG + || GET_MODE_BITSIZE (GET_MODE (SET_DEST (set))) > BITS_PER_WORD + || REGNO (p->new_sp_equiv_reg) != REGNO (SET_DEST (set))) abort (); else p->equiv_reg_src @@ -7548,15 +7568,38 @@ handle_epilogue_set (rtx set, struct epi_info *p) } } +/* Update the tracking information for registers set to constants. */ + +static void +update_epilogue_consts (rtx dest, rtx x, void *data) +{ + struct epi_info *p = (struct epi_info *) data; + + if (GET_CODE (dest) != REG || REGNO (dest) >= FIRST_PSEUDO_REGISTER) + return; + else if (GET_CODE (x) == CLOBBER || ! rtx_equal_p (dest, SET_DEST (x)) + || GET_CODE (SET_SRC (x)) != CONST_INT) + p->const_equiv[REGNO (dest)] = 0; + else + p->const_equiv[REGNO (dest)] = SET_SRC (x); +} + /* Emit an insn to do the load shown in p->equiv_reg_src, if needed. */ static void emit_equiv_load (struct epi_info *p) { if (p->equiv_reg_src != 0) - emit_move_insn (p->sp_equiv_reg, p->equiv_reg_src); + { + rtx dest = p->sp_equiv_reg; + + if (GET_MODE (p->equiv_reg_src) != GET_MODE (dest)) + dest = gen_rtx_REG (GET_MODE (p->equiv_reg_src), + REGNO (p->sp_equiv_reg)); - p->equiv_reg_src = 0; + emit_move_insn (dest, p->equiv_reg_src); + p->equiv_reg_src = 0; + } } #endif |