diff options
Diffstat (limited to 'gcc/caller-save.c')
-rw-r--r-- | gcc/caller-save.c | 278 |
1 files changed, 166 insertions, 112 deletions
diff --git a/gcc/caller-save.c b/gcc/caller-save.c index 60011f71c82..acb9ba43241 100644 --- a/gcc/caller-save.c +++ b/gcc/caller-save.c @@ -82,9 +82,14 @@ int n_regs_saved; static void set_reg_live PROTO((rtx, rtx)); static void clear_reg_live PROTO((rtx)); -static void restore_referenced_regs PROTO((rtx, rtx, enum machine_mode)); -static int insert_save_restore PROTO((rtx, int, int, +static void restore_referenced_regs PROTO((rtx, rtx, enum machine_mode, + int)); +static int insert_restore PROTO((rtx, int, int, + enum machine_mode, int, int)); +static int insert_save PROTO((rtx, int, int, enum machine_mode, int)); +static void insert_one_insn PROTO((rtx, int, enum rtx_code, + enum machine_mode, rtx, int)); /* Initialize for caller-save. @@ -358,7 +363,6 @@ save_call_clobbered_regs (insn_mode) for (b = 0; b < n_basic_blocks; b++) { regset regs_live = basic_block_live_at_start[b]; - rtx prev_block_last = PREV_INSN (basic_block_head[b]); int i, j; int regno; @@ -399,7 +403,7 @@ save_call_clobbered_regs (insn_mode) any of them. We must restore them before the insn if so. */ if (n_regs_saved) - restore_referenced_regs (PATTERN (insn), insn, insn_mode); + restore_referenced_regs (PATTERN (insn), insn, insn_mode, b); /* NB: the normal procedure is to first enliven any registers set by insn, then deaden any registers that @@ -449,8 +453,7 @@ save_call_clobbered_regs (insn_mode) /* It must not be set by this instruction. */ && ! TEST_HARD_REG_BIT (this_call_sets, regno) && ! TEST_HARD_REG_BIT (hard_regs_saved, regno)) - regno += insert_save_restore (insn, 1, regno, - insn_mode, 0); + regno += insert_save (insn, 1, regno, insn_mode, b); /* Put the information for this CALL_INSN on top of what we already had. */ @@ -489,13 +492,9 @@ save_call_clobbered_regs (insn_mode) if (n_regs_saved) for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) if (TEST_HARD_REG_BIT (hard_regs_need_restore, regno)) - regno += insert_save_restore ((GET_CODE (insn) == JUMP_INSN - ? insn : NEXT_INSN (insn)), 0, - regno, insn_mode, MOVE_MAX / UNITS_PER_WORD); - - /* If we added any insns at the start of the block, update the start - of the block to point at those insns. */ - basic_block_head[b] = NEXT_INSN (prev_block_last); + regno += insert_restore (insn, GET_CODE (insn) == JUMP_INSN, + regno, insn_mode, + MOVE_MAX / UNITS_PER_WORD, b); } } @@ -562,10 +561,11 @@ clear_reg_live (reg) INSN_MODE is the mode to assign to any insns that we add. */ static void -restore_referenced_regs (x, insn, insn_mode) +restore_referenced_regs (x, insn, insn_mode, block) rtx x; rtx insn; enum machine_mode insn_mode; + int block; { enum rtx_code code = GET_CODE (x); char *fmt; @@ -584,11 +584,11 @@ restore_referenced_regs (x, insn, insn_mode) if (regno >= FIRST_PSEUDO_REGISTER && reg_equiv_mem[regno] != 0) restore_referenced_regs (XEXP (reg_equiv_mem[regno], 0), - insn, insn_mode); + insn, insn_mode, block); else if (regno >= FIRST_PSEUDO_REGISTER && reg_equiv_address[regno] != 0) restore_referenced_regs (reg_equiv_address[regno], - insn, insn_mode); + insn, insn_mode, block); /* Otherwise if this is a hard register, restore any piece of it that is currently saved. */ @@ -597,13 +597,13 @@ restore_referenced_regs (x, insn, insn_mode) { int numregs = HARD_REGNO_NREGS (regno, GET_MODE (x)); /* Save at most SAVEREGS at a time. This can not be larger than - MOVE_MAX, because that causes insert_save_restore to fail. */ + MOVE_MAX, because that causes insert_restore to fail. */ int saveregs = MIN (numregs, MOVE_MAX / UNITS_PER_WORD); int endregno = regno + numregs; for (i = regno; i < endregno; i++) if (TEST_HARD_REG_BIT (hard_regs_need_restore, i)) - i += insert_save_restore (insn, 0, i, insn_mode, saveregs); + i += insert_restore (insn, 1, i, insn_mode, saveregs, block); } return; @@ -613,18 +613,18 @@ restore_referenced_regs (x, insn, insn_mode) for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) { if (fmt[i] == 'e') - restore_referenced_regs (XEXP (x, i), insn, insn_mode); + restore_referenced_regs (XEXP (x, i), insn, insn_mode, block); else if (fmt[i] == 'E') for (j = XVECLEN (x, i) - 1; j >= 0; j--) - restore_referenced_regs (XVECEXP (x, i, j), insn, insn_mode); + restore_referenced_regs (XVECEXP (x, i, j), insn, insn_mode, block); } } -/* Insert a sequence of insns to save or restore, SAVE_P says which, - REGNO. Place these insns in front of INSN. INSN_MODE is the mode +/* Insert a sequence of insns to restore REGNO. Place these insns in front + of or after INSN (determined by BEFORE_P). INSN_MODE is the mode to assign to these insns. MAXRESTORE is the maximum number of registers - which should be restored during this call (when SAVE_P == 0). It should - never be less than 1 since we only work with entire registers. + which should be restored during this call. It should never be less than + 1 since we only work with entire registers. Note that we have verified in init_caller_save that we can do this with a simple SET, so use it. Set INSN_CODE to what we save there @@ -635,16 +635,19 @@ restore_referenced_regs (x, insn, insn_mode) Return the extra number of registers saved. */ static int -insert_save_restore (insn, save_p, regno, insn_mode, maxrestore) +insert_restore (insn, before_p, regno, insn_mode, maxrestore, block) rtx insn; - int save_p; + int before_p; int regno; enum machine_mode insn_mode; int maxrestore; + int block; { rtx pat = NULL_RTX; enum insn_code code = CODE_FOR_nothing; int numregs = 0; + int i, j, k; + int ok; /* 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 @@ -656,108 +659,159 @@ insert_save_restore (insn, save_p, regno, insn_mode, maxrestore) if (regno_save_mem[regno][1] == 0) abort (); -#ifdef HAVE_cc0 - /* If INSN references CC0, put our insns in front of the insn that sets - CC0. This is always safe, since the only way we could be passed an - insn that references CC0 is for a restore, and doing a restore earlier - isn't a problem. We do, however, assume here that CALL_INSNs don't - reference CC0. Guard against non-INSN's like CODE_LABEL. */ - - if ((GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN) - && reg_referenced_p (cc0_rtx, PATTERN (insn))) - insn = prev_nonnote_insn (insn); -#endif - /* Get the pattern to emit and update our status. */ - if (save_p) + + /* See if we can restore `maxrestore' registers at once. Work + backwards to the single register case. */ + for (i = maxrestore; i > 0; i--) { - int i, j, k; - int ok; + ok = 1; + if (regno_save_mem[regno][i]) + for (j = 0; j < i; j++) + { + if (! TEST_HARD_REG_BIT (hard_regs_need_restore, regno + j)) + ok = 0; + } + else + continue; + + /* Must do this one restore at a time */ + 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]; + - /* See if we can save several registers with a single instruction. - Work backwards to the single register case. */ - for (i = MOVE_MAX / UNITS_PER_WORD; i > 0; i--) + /* Clear status for all registers we restored. */ + for (k = 0; k < i; k++) { - ok = 1; - if (regno_save_mem[regno][i] != 0) - for (j = 0; j < i; j++) - { - if (! call_used_regs[regno + j] || call_fixed_regs[regno + j] - || ! TEST_HARD_REG_BIT (hard_regs_live, regno + j) - || TEST_HARD_REG_BIT (hard_regs_saved, regno + j)) - ok = 0; - } - else - continue; + CLEAR_HARD_REG_BIT (hard_regs_need_restore, regno + k); + n_regs_saved--; + } - /* Must do this one save at a time */ - if (! ok) - continue; + numregs = i; + break; + } - 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]; + insert_one_insn (insn, before_p, code, insn_mode, pat, block); - /* 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); - SET_HARD_REG_BIT (hard_regs_need_restore, regno + k); - n_regs_saved++; - } + /* Tell our callers how many extra registers we saved/restored */ + return numregs - 1; +} - numregs = i; - break; - } - } - else - { - int i, j, k; - int ok; +/* Like insert_restore, but emit code to save REGNO. */ +static int +insert_save (insn, before_p, regno, insn_mode, block) + rtx insn; + int before_p; + int regno; + enum machine_mode insn_mode; + int block; +{ + rtx pat = NULL_RTX; + enum insn_code code = CODE_FOR_nothing; + int numregs = 0; + int i, j, k; + int ok; - /* See if we can restore `maxrestore' registers at once. Work - backwards to the single register case. */ - for (i = maxrestore; i > 0; i--) - { - ok = 1; - if (regno_save_mem[regno][i]) - for (j = 0; j < i; j++) - { - if (! TEST_HARD_REG_BIT (hard_regs_need_restore, regno + j)) - ok = 0; - } - else - continue; + /* 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 + save. That will cause us to write an insn with a (nil) SET_DEST + or SET_SRC. Instead of doing so and causing a crash later, check + for this common case and abort here instead. This will remove one + step in debugging such problems. */ - /* Must do this one restore at a time */ - 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]; + if (regno_save_mem[regno][1] == 0) + abort (); + /* Get the pattern to emit and update our status. */ - /* Clear status for all registers we restored. */ - for (k = 0; k < i; k++) - { - CLEAR_HARD_REG_BIT (hard_regs_need_restore, regno + k); - n_regs_saved--; - } + /* See if we can save several registers with a single instruction. + Work backwards to the single register case. */ + for (i = MOVE_MAX / UNITS_PER_WORD; i > 0; i--) + { + ok = 1; + if (regno_save_mem[regno][i] != 0) + for (j = 0; j < i; j++) + { + if (! call_used_regs[regno + j] || call_fixed_regs[regno + j] + || ! TEST_HARD_REG_BIT (hard_regs_live, regno + j) + || TEST_HARD_REG_BIT (hard_regs_saved, regno + j)) + ok = 0; + } + else + continue; - numregs = i; - break; - } + /* Must do this one save at a time */ + 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); + SET_HARD_REG_BIT (hard_regs_need_restore, regno + k); + n_regs_saved++; + } + + numregs = i; + break; } - /* Emit the insn and set the code and mode. */ - insn = emit_insn_before (pat, insn); - PUT_MODE (insn, insn_mode); - INSN_CODE (insn) = code; + insert_one_insn (insn, before_p, code, insn_mode, pat, block); /* Tell our callers how many extra registers we saved/restored */ return numregs - 1; } + +/* Emit one insn, set the code and mode, and update basic block + boundaries. */ +static void +insert_one_insn (insn, before_p, code, mode, pat, block) + rtx insn; + int before_p; + enum rtx_code code; + enum machine_mode mode; + rtx pat; + int block; +{ + rtx insert_point = insn; + rtx new; +#ifdef HAVE_cc0 + /* If INSN references CC0, put our insns in front of the insn that sets + CC0. This is always safe, since the only way we could be passed an + insn that references CC0 is for a restore, and doing a restore earlier + isn't a problem. We do, however, assume here that CALL_INSNs don't + reference CC0. Guard against non-INSN's like CODE_LABEL. */ + + if ((GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN) + && before_p + && reg_referenced_p (cc0_rtx, PATTERN (insn))) + insert_point = prev_nonnote_insn (insn); +#endif + + if (before_p) + { + new = emit_insn_before (pat, insert_point); + if (insert_point == basic_block_head[block]) + basic_block_head[block] = new; + } + else + { + new = emit_insn_after (pat, insert_point); + if (insert_point == basic_block_end[block]) + basic_block_end[block] = new; + } + + PUT_MODE (new, mode); + INSN_CODE (new) = code; +} |