diff options
Diffstat (limited to 'gcc/caller-save.c')
-rw-r--r-- | gcc/caller-save.c | 196 |
1 files changed, 113 insertions, 83 deletions
diff --git a/gcc/caller-save.c b/gcc/caller-save.c index b58fe87..bbd7831 100644 --- a/gcc/caller-save.c +++ b/gcc/caller-save.c @@ -90,8 +90,8 @@ static int insert_save PROTO((struct insn_chain *, int, int, HARD_REG_SET *)); static int insert_restore PROTO((struct insn_chain *, int, int, int)); -static void insert_one_insn PROTO((struct insn_chain *, int, - enum insn_code, rtx)); +static struct insn_chain *insert_one_insn PROTO((struct insn_chain *, int, + enum insn_code, rtx)); /* Initialize for caller-save. @@ -373,17 +373,20 @@ save_call_clobbered_regs () if (code == CALL_INSN) { - rtx x; - int regno, nregs; + int regno; HARD_REG_SET hard_regs_to_save; /* Use the register life information in CHAIN to compute which - regs are live before the call. */ - REG_SET_TO_HARD_REG_SET (hard_regs_to_save, chain->live_before); - compute_use_by_pseudos (&hard_regs_to_save, chain->live_before); + regs are live during the call. */ + REG_SET_TO_HARD_REG_SET (hard_regs_to_save, + chain->live_throughout); + compute_use_by_pseudos (&hard_regs_to_save, + chain->live_throughout); /* Record all registers set in this call insn. These don't need - to be saved. */ + to be saved. N.B. the call insn might set a subreg of a + multi-hard-reg pseudo; then the pseudo is considered live + during the call, but the subreg that is set isn't. */ CLEAR_HARD_REG_SET (this_insn_sets); note_stores (PATTERN (insn), mark_set_regs, NULL); @@ -393,45 +396,6 @@ save_call_clobbered_regs () AND_COMPL_HARD_REG_SET (hard_regs_to_save, hard_regs_saved); AND_HARD_REG_SET (hard_regs_to_save, call_used_reg_set); - /* Registers used for function parameters need not be saved. */ - for (x = CALL_INSN_FUNCTION_USAGE (insn); x != 0; - x = XEXP (x, 1)) - { - rtx y; - - if (GET_CODE (XEXP (x, 0)) != USE) - continue; - y = XEXP (XEXP (x, 0), 0); - if (GET_CODE (y) != REG) - abort (); - regno = REGNO (y); - if (REGNO (y) >= FIRST_PSEUDO_REGISTER) - abort (); - nregs = HARD_REGNO_NREGS (regno, GET_MODE (y)); - while (nregs-- > 0) - CLEAR_HARD_REG_BIT (hard_regs_to_save, regno + nregs); - } - - /* Neither do registers for which we find a death note. */ - for (x = REG_NOTES (insn); x != 0; x = XEXP (x, 1)) - { - rtx y = XEXP (x, 0); - - if (REG_NOTE_KIND (x) != REG_DEAD) - continue; - if (GET_CODE (y) != REG) - abort (); - regno = REGNO (y); - - if (regno >= FIRST_PSEUDO_REGISTER) - regno = reg_renumber[regno]; - if (regno < 0) - continue; - nregs = HARD_REGNO_NREGS (regno, GET_MODE (y)); - while (nregs-- > 0) - CLEAR_HARD_REG_BIT (hard_regs_to_save, regno + nregs); - } - for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) if (TEST_HARD_REG_BIT (hard_regs_to_save, regno)) regno += insert_save (chain, 1, regno, &hard_regs_to_save); @@ -490,6 +454,39 @@ mark_set_regs (reg, setter, data) SET_HARD_REG_BIT (this_insn_sets, i); } +/* Here from note_stores when an insn stores a value in a register. + Set the proper bit or bits in the passed regset. All pseudos that have + been assigned hard regs have had their register number changed already, + so we can ignore pseudos. */ +static void +add_stored_regs (reg, setter, data) + rtx reg; + rtx setter; + void *data; +{ + register int regno, endregno, i; + enum machine_mode mode = GET_MODE (reg); + int word = 0; + + if (GET_CODE (setter) == CLOBBER) + return; + + while (GET_CODE (reg) == SUBREG) + { + word += SUBREG_WORD (reg); + reg = SUBREG_REG (reg); + } + + if (GET_CODE (reg) != REG || REGNO (reg) >= FIRST_PSEUDO_REGISTER) + return; + + regno = REGNO (reg) + word; + endregno = regno + HARD_REGNO_NREGS (regno, mode); + + for (i = regno; i < endregno; i++) + SET_REGNO_REG_SET ((regset) data, i); +} + /* Walk X and record all referenced registers in REFERENCED_REGS. */ static void mark_referenced_regs (x) @@ -568,10 +565,11 @@ insert_restore (chain, before_p, regno, maxrestore) int regno; int maxrestore; { - int i; + int i, k; rtx pat = NULL_RTX; enum insn_code code = CODE_FOR_nothing; int numregs = 0; + struct insn_chain *new; /* A common failure mode if register status is not correct in the RTL is for this routine to be called with a REGNO we didn't expect to @@ -589,7 +587,7 @@ insert_restore (chain, before_p, regno, maxrestore) backwards to the single register case. */ for (i = maxrestore; i > 0; i--) { - int j, k; + int j; int ok = 1; if (regno_save_mem[regno][i] == 0) @@ -605,24 +603,26 @@ insert_restore (chain, before_p, regno, maxrestore) if (! ok) continue; - pat = gen_rtx_SET (VOIDmode, - gen_rtx_REG (GET_MODE (regno_save_mem[regno][i]), - regno), - regno_save_mem[regno][i]); - code = reg_restore_code[regno][i]; - - /* Clear status for all registers we restored. */ - for (k = 0; k < i; k++) - { - CLEAR_HARD_REG_BIT (hard_regs_saved, regno + k); - n_regs_saved--; - } - numregs = i; break; } - insert_one_insn (chain, before_p, code, pat); + pat = gen_rtx_SET (VOIDmode, + gen_rtx_REG (GET_MODE (regno_save_mem[regno][numregs]), + regno), + regno_save_mem[regno][numregs]); + code = reg_restore_code[regno][numregs]; + new = insert_one_insn (chain, before_p, code, pat); + + /* Clear status for all registers we restored. */ + for (k = 0; k < i; k++) + { + CLEAR_HARD_REG_BIT (hard_regs_saved, regno + k); + SET_REGNO_REG_SET (new->dead_or_set, regno + k); + n_regs_saved--; + } + + /* Tell our callers how many extra registers we saved/restored */ return numregs - 1; @@ -636,10 +636,11 @@ insert_save (chain, before_p, regno, to_save) int regno; HARD_REG_SET *to_save; { - int i; + int i, k; rtx pat = NULL_RTX; enum insn_code code = CODE_FOR_nothing; int numregs = 0; + struct insn_chain *new; /* A common failure mode if register status is not correct in the RTL is for this routine to be called with a REGNO we didn't expect to @@ -657,7 +658,7 @@ insert_save (chain, before_p, regno, to_save) Work backwards to the single register case. */ for (i = MOVE_MAX_WORDS; i > 0; i--) { - int j, k; + int j; int ok = 1; if (regno_save_mem[regno][i] == 0) continue; @@ -672,30 +673,30 @@ insert_save (chain, before_p, regno, to_save) if (! ok) continue; - pat = gen_rtx_SET (VOIDmode, regno_save_mem[regno][i], - gen_rtx_REG (GET_MODE (regno_save_mem[regno][i]), - regno)); - code = reg_save_code[regno][i]; - - /* Set hard_regs_saved for all the registers we saved. */ - for (k = 0; k < i; k++) - { - SET_HARD_REG_BIT (hard_regs_saved, regno + k); - n_regs_saved++; - } - numregs = i; break; } - insert_one_insn (chain, before_p, code, pat); + pat = gen_rtx_SET (VOIDmode, regno_save_mem[regno][numregs], + gen_rtx_REG (GET_MODE (regno_save_mem[regno][numregs]), + regno)); + code = reg_save_code[regno][numregs]; + new = insert_one_insn (chain, before_p, code, pat); + + /* Set hard_regs_saved and dead_or_set for all the registers we saved. */ + for (k = 0; k < numregs; k++) + { + SET_HARD_REG_BIT (hard_regs_saved, regno + k); + SET_REGNO_REG_SET (new->dead_or_set, regno + k); + n_regs_saved++; + } /* Tell our callers how many extra registers we saved/restored */ return numregs - 1; } /* Emit a new caller-save insn and set the code. */ -static void +static struct insn_chain * insert_one_insn (chain, before_p, code, pat) struct insn_chain *chain; int before_p; @@ -721,6 +722,8 @@ insert_one_insn (chain, before_p, code, pat) new = new_insn_chain (); if (before_p) { + rtx link; + new->prev = chain->prev; if (new->prev != 0) new->prev->next = new; @@ -732,8 +735,29 @@ insert_one_insn (chain, before_p, code, pat) new->insn = emit_insn_before (pat, insn); /* ??? It would be nice if we could exclude the already / still saved registers from the live sets. */ - COPY_REG_SET (new->live_before, chain->live_before); - COPY_REG_SET (new->live_after, chain->live_before); + COPY_REG_SET (new->live_throughout, chain->live_throughout); + /* Registers that die in CHAIN->INSN still live in the new insn. */ + for (link = REG_NOTES (chain->insn); link; link = XEXP (link, 1)) + { + if (REG_NOTE_KIND (link) == REG_DEAD) + { + rtx reg = XEXP (link, 0); + int regno, i; + + if (GET_CODE (reg) != REG) + abort (); + + regno = REGNO (reg); + if (regno >= FIRST_PSEUDO_REGISTER) + regno = reg_renumber[regno]; + if (regno < 0) + continue; + for (i = HARD_REGNO_NREGS (regno, GET_MODE (reg)) - 1; + i >= 0; i--) + SET_REGNO_REG_SET (new->live_throughout, regno + i); + } + } + CLEAR_REG_SET (new->dead_or_set); if (chain->insn == BLOCK_HEAD (chain->block)) BLOCK_HEAD (chain->block) = new->insn; } @@ -747,8 +771,13 @@ insert_one_insn (chain, before_p, code, pat) new->insn = emit_insn_after (pat, insn); /* ??? It would be nice if we could exclude the already / still saved registers from the live sets, and observe REG_UNUSED notes. */ - COPY_REG_SET (new->live_before, chain->live_after); - COPY_REG_SET (new->live_after, chain->live_after); + COPY_REG_SET (new->live_throughout, chain->live_throughout); + /* Registers that are set in CHAIN->INSN live in the new insn. + (Unless there is a REG_UNUSED note for them, but we don't + look for them here.) */ + note_stores (PATTERN (chain->insn), add_stored_regs, + new->live_throughout); + CLEAR_REG_SET (new->dead_or_set); if (chain->insn == BLOCK_END (chain->block)) BLOCK_END (chain->block) = new->insn; } @@ -756,4 +785,5 @@ insert_one_insn (chain, before_p, code, pat) new->is_caller_save_insn = 1; INSN_CODE (new->insn) = code; + return new; } |