From 7609e7209a9078fec27fa04108cb5b8173911cbe Mon Sep 17 00:00:00 2001 From: Bernd Schmidt Date: Wed, 14 Oct 1998 01:14:42 +0000 Subject: [PATCH] reload.h (compute_use_by_pseudos): Declare. * reload.h (compute_use_by_pseudos): Declare. * reload1.c (spilled_pseudos, insns_need_reload): New variables. (something_needs_reloads): Delete variable. (finish_spills): New function. (compute_use_by_pseudos): New function. (delete_caller_save_insns): Lose argument FIRST. All callers changed. Use the reload_insn_chain instead of walking the rtl directly. (reload): Allocate and free spilled_pseudos. Ensure that all calls of spill_hard_reg are followed by a call to finish_spills. Use the insns_need_reload list instead of something_needs_reloads to find out if reload_as_needed must be called. Clear unused_insn_chains at the end. (calculate_needs_all_insns): Lose FIRST parameter. All callers changed. Delete code to keep track of current basic block. Walk reload_insn_chain instead of the rtl structure. Build the insns_need_reload chain. Remember which insns need reloading/elimination by setting the appropriate fields in struct insn_chain, not by putting modes on the insn. (calculate_needs): Lose THIS_BLOCK arg. Accept arg CHAIN instead of arg INSN. All callers changed. Delete declaration of struct needs. Don't set something_needs_reloads. Record insn needs in the CHAIN argument. (spill_hard_reg): Record the affected pseudos in spilled_pseudos. (reload_as_needed): Lose FIRST arg. All callers changed. Walk the reload_insn_chain instead of the rtx structure. Delete code to keep track of current basic block. Rename one of the NEXT variables to OLD_NEXT. (allocate_reload_reg): Accept arg CHAIN instead of arg INSN. All callers changed. (choose_reload_regs): Likewise. (emit_reload_insns): Replace INSN and BB args with arg CHAIN. All callers changed. * caller-save.c (MOVE_MAX_WORDS): New macro. Use it throughout instead of (MOVE_MAX / UNITS_PER_WORD) computation. (hard_regs_live, hard_regs_need_restore): Delete variables. (n_regs_saved): Now static. (referenced_regs, this_insn_sets): New variables. (setup_save_areas): Restructure the code a bit. (restore_referenced_regs): Delete function. (mark_referenced_regs): New function, similar to the old restore_referenced_regs, but mark registers in referenced_regs. (clear_reg_live): Delete function. (mark_set_regs): Renamed from set_reg_live. All callers changed. Only mark registers in this_insn_sets. (save_call_clobbered_regs): Rework this function to walk the reload_insn_chain instead of using the list of instructions directly. Delete code to keep track of register lives, compute live regs on the fly from information in the chain. Instead of calling restore_referenced_regs, use mark_referenced_regs, then walk the set it computes and call insert_restore as appropriate. (insert_restore): Lose INSN and BLOCK args. Add CHAIN arg. All callers changed. Restructure the code a bit. Test hard_regs_saved instead of hard_regs_need_restore. (insert_save): Lose INSN and BLOCK args. Add CHAIN and TO_SAVE args. All callers changed. Restructure the code a bit. Use TO_SAVE to determine which regs to save instead of more complicated test. (insert_one_arg): Lose INSN and BLOCK args. Add CHAIN arg. All callers changed. Create a new insn_chain structure for the new insn and place it into the chain. * rtl.texi: Update documentation to reflect that reload no longer puts modes on the insns. From-SVN: r23074 --- gcc/ChangeLog | 87 ++++++++ gcc/caller-save.c | 559 ++++++++++++++++++++++------------------------ gcc/reload.h | 1 + gcc/reload1.c | 301 +++++++++++++------------ gcc/rtl.texi | 4 +- 5 files changed, 514 insertions(+), 438 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index d62c789b0ade..2168f56bed96 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,90 @@ +Tue Oct 13 22:12:11 1998 Bernd Schmidt + + * reload.h (compute_use_by_pseudos): Declare. + + * reload1.c (spilled_pseudos, insns_need_reload): New variables. + (something_needs_reloads): Delete variable. + (finish_spills): New function. + (compute_use_by_pseudos): New function. + + (delete_caller_save_insns): Lose argument FIRST. All callers changed. + Use the reload_insn_chain instead of walking the rtl directly. + + (reload): Allocate and free spilled_pseudos. + Ensure that all calls of spill_hard_reg are followed by a call to + finish_spills. + Use the insns_need_reload list instead of something_needs_reloads + to find out if reload_as_needed must be called. + Clear unused_insn_chains at the end. + + (calculate_needs_all_insns): Lose FIRST parameter. All callers + changed. + Delete code to keep track of current basic block. + Walk reload_insn_chain instead of the rtl structure. Build the + insns_need_reload chain. + Remember which insns need reloading/elimination by setting the + appropriate fields in struct insn_chain, not by putting modes on the + insn. + + (calculate_needs): Lose THIS_BLOCK arg. Accept arg CHAIN instead of + arg INSN. All callers changed. + Delete declaration of struct needs. + Don't set something_needs_reloads. + Record insn needs in the CHAIN argument. + + (spill_hard_reg): Record the affected pseudos in spilled_pseudos. + + (reload_as_needed): Lose FIRST arg. All callers changed. + Walk the reload_insn_chain instead of the rtx structure. + Delete code to keep track of current basic block. + Rename one of the NEXT variables to OLD_NEXT. + + (allocate_reload_reg): Accept arg CHAIN instead of arg INSN. All + callers changed. + (choose_reload_regs): Likewise. + + (emit_reload_insns): Replace INSN and BB args with arg CHAIN. All + callers changed. + + * caller-save.c (MOVE_MAX_WORDS): New macro. Use it throughout + instead of (MOVE_MAX / UNITS_PER_WORD) computation. + (hard_regs_live, hard_regs_need_restore): Delete variables. + (n_regs_saved): Now static. + (referenced_regs, this_insn_sets): New variables. + + (setup_save_areas): Restructure the code a bit. + + (restore_referenced_regs): Delete function. + (mark_referenced_regs): New function, similar to the old + restore_referenced_regs, but mark registers in referenced_regs. + + (clear_reg_live): Delete function. + (mark_set_regs): Renamed from set_reg_live. All callers changed. + Only mark registers in this_insn_sets. + + (save_call_clobbered_regs): Rework this function to walk the + reload_insn_chain instead of using the list of instructions directly. + Delete code to keep track of register lives, compute live regs on the + fly from information in the chain. + Instead of calling restore_referenced_regs, use mark_referenced_regs, + then walk the set it computes and call insert_restore as appropriate. + + (insert_restore): Lose INSN and BLOCK args. Add CHAIN arg. All + callers changed. + Restructure the code a bit. Test hard_regs_saved instead of + hard_regs_need_restore. + (insert_save): Lose INSN and BLOCK args. Add CHAIN and TO_SAVE + args. All callers changed. + Restructure the code a bit. Use TO_SAVE to determine which regs to + save instead of more complicated test. + (insert_one_arg): Lose INSN and BLOCK args. Add CHAIN arg. All + callers changed. + Create a new insn_chain structure for the new insn and place it + into the chain. + + * rtl.texi: Update documentation to reflect that reload no longer + puts modes on the insns. + 1998-10-14 Andreas Schwab * function.c (purge_addressof_1): Force the first argument of a diff --git a/gcc/caller-save.c b/gcc/caller-save.c index 03fceaebe654..cbb19fba1a60 100644 --- a/gcc/caller-save.c +++ b/gcc/caller-save.c @@ -39,6 +39,8 @@ Boston, MA 02111-1307, USA. */ #define MIN_UNITS_PER_WORD UNITS_PER_WORD #endif +#define MOVE_MAX_WORDS (MOVE_MAX / UNITS_PER_WORD) + /* Modes for each hard register that we can save. The smallest mode is wide enough to save the entire contents of the register. When saving the register because it is live we first try to save in multi-register modes. @@ -64,29 +66,31 @@ static enum insn_code static enum insn_code reg_restore_code[FIRST_PSEUDO_REGISTER][MAX_MOVE_MAX / MIN_UNITS_PER_WORD + 1]; -/* Set of hard regs currently live (during scan of all insns). */ - -static HARD_REG_SET hard_regs_live; - /* Set of hard regs currently residing in save area (during insn scan). */ static HARD_REG_SET hard_regs_saved; -/* Set of hard regs which need to be restored before referenced. */ +/* Number of registers currently in hard_regs_saved. */ -static HARD_REG_SET hard_regs_need_restore; +static int n_regs_saved; -/* Number of registers currently in hard_regs_saved. */ +/* Computed by mark_referenced_regs, all regs referenced in a given + insn. */ +static HARD_REG_SET referenced_regs; -int n_regs_saved; +/* Computed in mark_set_regs, holds all registers set by the current + instruction. */ +static HARD_REG_SET this_insn_sets; -static void set_reg_live PROTO((rtx, rtx)); -static void clear_reg_live PROTO((rtx)); -static void restore_referenced_regs PROTO((rtx, rtx, int)); -static int insert_restore PROTO((rtx, int, int, int, int)); -static int insert_save PROTO((rtx, int, int, int)); -static void insert_one_insn PROTO((rtx, int, enum rtx_code, - rtx, int)); + +static void mark_set_regs PROTO((rtx, rtx)); +static void mark_referenced_regs PROTO((rtx)); +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)); /* Initialize for caller-save. @@ -115,7 +119,7 @@ init_caller_save () { if (call_used_regs[i] && ! call_fixed_regs[i]) { - for (j = 1; j <= MOVE_MAX / UNITS_PER_WORD; j++) + for (j = 1; j <= MOVE_MAX_WORDS; j++) { regno_save_mode[i][j] = HARD_REGNO_CALLER_SAVE_MODE (i, j); if (regno_save_mode[i][j] == VOIDmode && j == 1) @@ -172,7 +176,7 @@ init_caller_save () start_sequence (); for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - for (j = 1; j <= MOVE_MAX / UNITS_PER_WORD; j++) + for (j = 1; j <= MOVE_MAX_WORDS; j++) if (regno_save_mode[i][j] != VOIDmode) { rtx mem = gen_rtx_MEM (regno_save_mode[i][j], address); @@ -221,7 +225,7 @@ init_save_areas () int i, j; for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - for (j = 1; j <= MOVE_MAX / UNITS_PER_WORD; j++) + for (j = 1; j <= MOVE_MAX_WORDS; j++) regno_save_mem[i][j] = 0; } @@ -275,10 +279,9 @@ setup_save_areas () in a manner which allows multi-register saves/restores to be done. */ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - for (j = MOVE_MAX / UNITS_PER_WORD; j > 0; j--) + for (j = MOVE_MAX_WORDS; j > 0; j--) { - int ok = 1; - int do_save; + int do_save = 1; /* If no mode exists for this size, try another. Also break out if we have already saved this hard register. */ @@ -286,7 +289,6 @@ setup_save_areas () continue; /* See if any register in this group has been saved. */ - do_save = 1; for (k = 0; k < j; k++) if (regno_save_mem[i + k][1]) { @@ -297,189 +299,171 @@ setup_save_areas () continue; for (k = 0; k < j; k++) + if (! TEST_HARD_REG_BIT (hard_regs_used, i + k)) { - int regno = i + k; - ok &= (TEST_HARD_REG_BIT (hard_regs_used, regno) != 0); + do_save = 0; + break; } + if (! do_save) + continue; /* We have found an acceptable mode to store in. */ - if (ok) + regno_save_mem[i][j] + = assign_stack_local (regno_save_mode[i][j], + GET_MODE_SIZE (regno_save_mode[i][j]), 0); + + /* Setup single word save area just in case... */ + for (k = 0; k < j; k++) { + /* This should not depend on WORDS_BIG_ENDIAN. + The order of words in regs is the same as in memory. */ + rtx temp = gen_rtx_MEM (regno_save_mode[i+k][1], + XEXP (regno_save_mem[i][j], 0)); - regno_save_mem[i][j] - = assign_stack_local (regno_save_mode[i][j], - GET_MODE_SIZE (regno_save_mode[i][j]), 0); - - /* Setup single word save area just in case... */ - for (k = 0; k < j; k++) - { - /* This should not depend on WORDS_BIG_ENDIAN. - The order of words in regs is the same as in memory. */ - rtx temp = gen_rtx_MEM (regno_save_mode[i+k][1], - XEXP (regno_save_mem[i][j], 0)); - - regno_save_mem[i+k][1] - = adj_offsettable_operand (temp, k * UNITS_PER_WORD); - } + regno_save_mem[i+k][1] + = adj_offsettable_operand (temp, k * UNITS_PER_WORD); } } - - return; } /* Find the places where hard regs are live across calls and save them. */ - void save_call_clobbered_regs () { - rtx insn; - int b; + struct insn_chain *chain, *next; + + CLEAR_HARD_REG_SET (hard_regs_saved); + n_regs_saved = 0; - for (b = 0; b < n_basic_blocks; b++) + for (chain = reload_insn_chain; chain != 0; chain = next) { - regset regs_live = basic_block_live_at_start[b]; - int i, j; - int regno; - - /* Compute hard regs live at start of block -- this is the - real hard regs marked live, plus live pseudo regs that - have been renumbered to hard regs. No registers have yet been - saved because we restore all of them before the end of the basic - block. */ - - REG_SET_TO_HARD_REG_SET (hard_regs_live, regs_live); - CLEAR_HARD_REG_SET (hard_regs_saved); - CLEAR_HARD_REG_SET (hard_regs_need_restore); - n_regs_saved = 0; - - EXECUTE_IF_SET_IN_REG_SET (regs_live, 0, i, - { - if ((regno = reg_renumber[i]) >= 0) - for (j = regno; - j < regno + HARD_REGNO_NREGS (regno, - PSEUDO_REGNO_MODE (i)); - j++) - SET_HARD_REG_BIT (hard_regs_live, j); - }); - - /* Now scan the insns in the block, keeping track of what hard - regs are live as we go. When we see a call, save the live - call-clobbered hard regs. */ - - for (insn = basic_block_head[b]; ; insn = NEXT_INSN (insn)) + rtx insn = chain->insn; + enum rtx_code code = GET_CODE (insn); + + next = chain->next; + + if (chain->is_caller_save_insn) + abort (); + + if (GET_RTX_CLASS (code) == 'i') { - RTX_CODE code = GET_CODE (insn); + /* If some registers have been saved, see if INSN references + any of them. We must restore them before the insn if so. */ - if (GET_RTX_CLASS (code) == 'i') + if (n_regs_saved) { - rtx link; - - /* If some registers have been saved, see if INSN references - any of them. We must restore them before the insn if so. */ - - if (n_regs_saved) - restore_referenced_regs (PATTERN (insn), insn, b); - - /* NB: the normal procedure is to first enliven any - registers set by insn, then deaden any registers that - had their last use at insn. This is incorrect now, - since multiple pseudos may have been mapped to the - same hard reg, and the death notes are ambiguous. So - it must be done in the other, safe, order. */ - - for (link = REG_NOTES (insn); link; link = XEXP (link, 1)) - if (REG_NOTE_KIND (link) == REG_DEAD) - clear_reg_live (XEXP (link, 0)); - - /* When we reach a call, we need to save all registers that are - live, call-used, not fixed, and not already saved. We must - test at this point because registers that die in a CALL_INSN - are not live across the call and likewise for registers that - are born in the CALL_INSN. - - If registers are filled with parameters for this function, - and some of these are also being set by this function, then - they will not appear to die (no REG_DEAD note for them), - to check if in fact they do, collect the set registers in - hard_regs_live first. */ - - if (code == CALL_INSN) - { - HARD_REG_SET this_call_sets; - { - HARD_REG_SET old_hard_regs_live; - - /* Save the hard_regs_live information. */ - COPY_HARD_REG_SET (old_hard_regs_live, hard_regs_live); - - /* Now calculate hard_regs_live for this CALL_INSN - only. */ - CLEAR_HARD_REG_SET (hard_regs_live); - note_stores (PATTERN (insn), set_reg_live); - COPY_HARD_REG_SET (this_call_sets, hard_regs_live); - - /* Restore the hard_regs_live information. */ - COPY_HARD_REG_SET (hard_regs_live, old_hard_regs_live); - } - - for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) - if (call_used_regs[regno] && ! call_fixed_regs[regno] - && TEST_HARD_REG_BIT (hard_regs_live, regno) - /* 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 (insn, 1, regno, b); - - /* Put the information for this CALL_INSN on top of what - we already had. */ - IOR_HARD_REG_SET (hard_regs_live, this_call_sets); - COPY_HARD_REG_SET (hard_regs_need_restore, hard_regs_saved); - - /* Must recompute n_regs_saved. */ - n_regs_saved = 0; - for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) - if (TEST_HARD_REG_BIT (hard_regs_saved, regno)) - n_regs_saved++; - } + int regno; + + if (code == JUMP_INSN) + /* Restore all registers if this is a JUMP_INSN. */ + COPY_HARD_REG_SET (referenced_regs, hard_regs_saved); else { - note_stores (PATTERN (insn), set_reg_live); -#ifdef AUTO_INC_DEC - for (link = REG_NOTES (insn); link; link = XEXP (link, 1)) - if (REG_NOTE_KIND (link) == REG_INC) - set_reg_live (XEXP (link, 0), NULL_RTX); -#endif + CLEAR_HARD_REG_SET (referenced_regs); + mark_referenced_regs (PATTERN (insn)); + AND_HARD_REG_SET (referenced_regs, hard_regs_saved); } - for (link = REG_NOTES (insn); link; link = XEXP (link, 1)) - if (REG_NOTE_KIND (link) == REG_UNUSED) - clear_reg_live (XEXP (link, 0)); + for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) + if (TEST_HARD_REG_BIT (referenced_regs, regno)) + regno += insert_restore (chain, 1, regno, MOVE_MAX_WORDS); } - if (insn == basic_block_end[b]) - break; - } + if (code == CALL_INSN) + { + rtx x; + int regno, nregs; + 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); + + /* Record all registers set in this call insn. These don't need + to be saved. */ + CLEAR_HARD_REG_SET (this_insn_sets); + note_stores (PATTERN (insn), mark_set_regs); + + /* Compute which hard regs must be saved before this call. */ + AND_COMPL_HARD_REG_SET (hard_regs_to_save, call_fixed_reg_set); + AND_COMPL_HARD_REG_SET (hard_regs_to_save, this_insn_sets); + 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); + } - /* At the end of the basic block, we must restore any registers that - remain saved. If the last insn in the block is a JUMP_INSN, put - the restore before the insn, otherwise, put it after the insn. */ + /* 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); + + /* Must recompute n_regs_saved. */ + n_regs_saved = 0; + for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) + if (TEST_HARD_REG_BIT (hard_regs_saved, regno)) + n_regs_saved++; + } + } - if (n_regs_saved) - for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) - if (TEST_HARD_REG_BIT (hard_regs_need_restore, regno)) - regno += insert_restore (insn, GET_CODE (insn) == JUMP_INSN, - regno, - MOVE_MAX / UNITS_PER_WORD, b); - } + if (chain->next == 0 || chain->next->block > chain->block) + { + int regno; + /* At the end of the basic block, we must restore any registers that + remain saved. If the last insn in the block is a JUMP_INSN, put + the restore before the insn, otherwise, put it after the insn. */ + + if (n_regs_saved) + for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) + if (TEST_HARD_REG_BIT (hard_regs_saved, regno)) + regno += insert_restore (chain, GET_CODE (insn) == JUMP_INSN, + regno, MOVE_MAX_WORDS); + } + } } /* Here from note_stores when an insn stores a value in a register. - Set the proper bit or bits in hard_regs_live. All pseudos that have + Set the proper bit or bits in this_insn_sets. All pseudos that have been assigned hard regs have had their register number changed already, so we can ignore pseudos. */ - static void -set_reg_live (reg, setter) +mark_set_regs (reg, setter) rtx reg; rtx setter ATTRIBUTE_UNUSED; { @@ -500,104 +484,71 @@ set_reg_live (reg, setter) endregno = regno + HARD_REGNO_NREGS (regno, mode); for (i = regno; i < endregno; i++) - { - SET_HARD_REG_BIT (hard_regs_live, i); - CLEAR_HARD_REG_BIT (hard_regs_saved, i); - CLEAR_HARD_REG_BIT (hard_regs_need_restore, i); - } + SET_HARD_REG_BIT (this_insn_sets, i); } -/* Here when a REG_DEAD note records the last use of a reg. Clear - the appropriate bit or bits in hard_regs_live. Again we can ignore - pseudos. */ - -static void -clear_reg_live (reg) - rtx reg; -{ - register int regno, endregno, i; - - if (GET_CODE (reg) != REG || REGNO (reg) >= FIRST_PSEUDO_REGISTER) - return; - - regno = REGNO (reg); - endregno= regno + HARD_REGNO_NREGS (regno, GET_MODE (reg)); - - for (i = regno; i < endregno; i++) - { - CLEAR_HARD_REG_BIT (hard_regs_live, i); - CLEAR_HARD_REG_BIT (hard_regs_need_restore, i); - CLEAR_HARD_REG_BIT (hard_regs_saved, i); - } -} - -/* If any register currently residing in the save area is referenced in X, - which is part of INSN, emit code to restore the register in front of - INSN. */ - +/* Walk X and record all referenced registers in REFERENCED_REGS. */ static void -restore_referenced_regs (x, insn, block) +mark_referenced_regs (x) rtx x; - rtx insn; - int block; { enum rtx_code code = GET_CODE (x); char *fmt; int i, j; - if (code == CLOBBER) - return; + if (code == SET) + mark_referenced_regs (SET_SRC (x)); + if (code == SET || code == CLOBBER) + { + x = SET_DEST (x); + code = GET_CODE (x); + if (code == REG || code == PC || code == CC0 + || (code == SUBREG && GET_CODE (SUBREG_REG (x)) == REG)) + return; + } + if (code == MEM || code == SUBREG) + { + x = XEXP (x, 0); + code = GET_CODE (x); + } if (code == REG) { int regno = REGNO (x); + int hardregno = (regno < FIRST_PSEUDO_REGISTER ? regno + : reg_renumber[regno]); - /* If this is a pseudo, scan its memory location, since it might - involve the use of another register, which might be saved. */ - - if (regno >= FIRST_PSEUDO_REGISTER - && reg_equiv_mem[regno] != 0) - restore_referenced_regs (XEXP (reg_equiv_mem[regno], 0), - insn, block); - else if (regno >= FIRST_PSEUDO_REGISTER - && reg_equiv_address[regno] != 0) - restore_referenced_regs (reg_equiv_address[regno], - insn, block); - - /* Otherwise if this is a hard register, restore any piece of it that - is currently saved. */ - - else if (regno < FIRST_PSEUDO_REGISTER) + if (hardregno >= 0) { - 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_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_restore (insn, 1, i, saveregs, block); + int nregs = HARD_REGNO_NREGS (hardregno, GET_MODE (x)); + while (nregs-- > 0) + SET_HARD_REG_BIT (referenced_regs, hardregno + nregs); } - + /* If this is a pseudo that did not get a hard register, scan its + memory location, since it might involve the use of another + register, which might be saved. */ + else if (reg_equiv_mem[regno] != 0) + mark_referenced_regs (XEXP (reg_equiv_mem[regno], 0)); + else if (reg_equiv_address[regno] != 0) + mark_referenced_regs (reg_equiv_address[regno]); return; } - + fmt = GET_RTX_FORMAT (code); for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) { if (fmt[i] == 'e') - restore_referenced_regs (XEXP (x, i), insn, block); + mark_referenced_regs (XEXP (x, i)); else if (fmt[i] == 'E') for (j = XVECLEN (x, i) - 1; j >= 0; j--) - restore_referenced_regs (XVECEXP (x, i, j), insn, block); + mark_referenced_regs (XVECEXP (x, i, j)); } } -/* Insert a sequence of insns to restore REGNO. Place these insns in front - of or after INSN (determined by BEFORE_P). MAXRESTORE is the maximum - number of registers which should be restored during this call. It should - never be less than 1 since we only work with entire registers. +/* Insert a sequence of insns to restore. Place these insns in front of + CHAIN if BEFORE_P is nonzero, behind the insn otherwise. MAXRESTORE is + the maximum number of 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 @@ -608,18 +559,16 @@ restore_referenced_regs (x, insn, block) Return the extra number of registers saved. */ static int -insert_restore (insn, before_p, regno, maxrestore, block) - rtx insn; +insert_restore (chain, before_p, regno, maxrestore) + struct insn_chain *chain; int before_p; int regno; int maxrestore; - int block; { + int i; 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 @@ -631,37 +580,38 @@ insert_restore (insn, before_p, regno, maxrestore, block) if (regno_save_mem[regno][1] == 0) abort (); - /* Get the pattern to emit and update our status. */ + /* Get the pattern to emit and update our status. - /* See if we can restore `maxrestore' registers at once. Work + 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 + int j, k; + int ok = 1; + + if (regno_save_mem[regno][i] == 0) continue; + for (j = 0; j < i; j++) + if (! TEST_HARD_REG_BIT (hard_regs_saved, regno + j)) + { + ok = 0; + break; + } /* 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]; - /* Clear status for all registers we restored. */ for (k = 0; k < i; k++) { - CLEAR_HARD_REG_BIT (hard_regs_need_restore, regno + k); + CLEAR_HARD_REG_BIT (hard_regs_saved, regno + k); n_regs_saved--; } @@ -669,25 +619,24 @@ insert_restore (insn, before_p, regno, maxrestore, block) break; } - insert_one_insn (insn, before_p, code, pat, block); + insert_one_insn (chain, before_p, code, pat); /* Tell our callers how many extra registers we saved/restored */ return numregs - 1; } -/* Like insert_restore, but emit code to save REGNO. */ +/* Like insert_restore above, but save registers instead. */ static int -insert_save (insn, before_p, regno, block) - rtx insn; +insert_save (chain, before_p, regno, to_save) + struct insn_chain *chain; int before_p; int regno; - int block; + HARD_REG_SET *to_save; { + int i; 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 @@ -699,24 +648,23 @@ insert_save (insn, before_p, regno, block) if (regno_save_mem[regno][1] == 0) abort (); - /* Get the pattern to emit and update our status. */ + /* Get the pattern to emit and update our status. - /* See if we can save several registers with a single instruction. + 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--) + for (i = MOVE_MAX_WORDS; 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 + int j, k; + int ok = 1; + if (regno_save_mem[regno][i] == 0) continue; + for (j = 0; j < i; j++) + if (! TEST_HARD_REG_BIT (*to_save, regno + j)) + { + ok = 0; + break; + } /* Must do this one save at a time */ if (! ok) continue; @@ -730,7 +678,6 @@ insert_save (insn, before_p, regno, block) 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++; } @@ -738,23 +685,23 @@ insert_save (insn, before_p, regno, block) break; } - insert_one_insn (insn, before_p, code, pat, block); + insert_one_insn (chain, before_p, code, pat); /* Tell our callers how many extra registers we saved/restored */ return numregs - 1; } -/* Emit one insn, set the code, and update basic block boundaries. */ +/* Emit a new caller-save insn and set the code. */ static void -insert_one_insn (insn, before_p, code, pat, block) - rtx insn; +insert_one_insn (chain, before_p, code, pat) + struct insn_chain *chain; int before_p; - enum rtx_code code; + enum insn_code code; rtx pat; - int block; { - rtx insert_point = insn; - rtx new; + rtx insn = chain->insn; + struct insn_chain *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 @@ -765,21 +712,37 @@ insert_one_insn (insn, before_p, code, pat, block) 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); + chain = chain->prev, insn = chain->insn; #endif + new = new_insn_chain (); if (before_p) { - new = emit_insn_before (pat, insert_point); - if (insert_point == basic_block_head[block]) - basic_block_head[block] = new; + new->prev = chain->prev; + if (new->prev != 0) + new->prev->next = new; + else + reload_insn_chain = new; + + chain->prev = new; + new->next = chain; + new->insn = emit_insn_before (pat, insn); + if (chain->insn == basic_block_head[chain->block]) + basic_block_head[chain->block] = new->insn; } else { - new = emit_insn_after (pat, insert_point); - if (insert_point == basic_block_end[block]) - basic_block_end[block] = new; + new->next = chain->next; + if (new->next != 0) + new->next->prev = new; + chain->next = new; + new->prev = chain; + new->insn = emit_insn_after (pat, insn); + if (chain->insn == basic_block_end[chain->block]) + basic_block_end[chain->block] = new->insn; } + new->block = chain->block; + new->is_caller_save_insn = 1; - INSN_CODE (new) = code; + INSN_CODE (new->insn) = code; } diff --git a/gcc/reload.h b/gcc/reload.h index e86296cb87c4..41d66164db49 100644 --- a/gcc/reload.h +++ b/gcc/reload.h @@ -215,6 +215,7 @@ extern struct insn_chain *reload_insn_chain; /* Allocate a new insn_chain structure. */ extern struct insn_chain *new_insn_chain PROTO((void)); +extern void compute_use_by_pseudos PROTO((HARD_REG_SET *, regset)); #endif /* Functions from reload.c: */ diff --git a/gcc/reload1.c b/gcc/reload1.c index f996855e0d89..a9925cbaf9e1 100644 --- a/gcc/reload1.c +++ b/gcc/reload1.c @@ -177,12 +177,10 @@ static short spill_regs[FIRST_PSEUDO_REGISTER]; registers. This information is used in reorg.c, to help figure out what registers are live at any point. It is assumed that all spill_regs are dead at every CODE_LABEL. */ - HARD_REG_SET used_spill_regs; /* Index of last register assigned as a spill register. We allocate in a round-robin fashion. */ - static int last_spill_reg; /* Describes order of preference for putting regs into spill_regs. @@ -213,32 +211,29 @@ static HARD_REG_SET counted_for_nongroups; value indicates the level of indirect addressing supported, e.g., two means that (MEM (MEM (REG n))) is also valid if (REG n) does not get a hard register. */ - static char spill_indirect_levels; /* Nonzero if indirect addressing is supported when the innermost MEM is of the form (MEM (SYMBOL_REF sym)). It is assumed that the level to which these are valid is the same as spill_indirect_levels, above. */ - char indirect_symref_ok; /* Nonzero if an address (plus (reg frame_pointer) (reg ...)) is valid. */ - char double_reg_address_ok; /* Record the stack slot for each spilled hard register. */ - static rtx spill_stack_slot[FIRST_PSEUDO_REGISTER]; /* Width allocated so far for that stack slot. */ - static int spill_stack_slot_width[FIRST_PSEUDO_REGISTER]; +/* Record which pseudos needed to be spilled. */ +static regset spilled_pseudos; + /* Indexed by register class and basic block number, nonzero if there is any need for a spill register of that class in that basic block. The pointer is 0 if we did stupid allocation and don't know the structure of basic blocks. */ - char *basic_block_needs[N_REG_CLASSES]; /* First uid used by insns created by reload in this function. @@ -247,7 +242,6 @@ int reload_first_uid; /* Flag set by local-alloc or global-alloc if anything is live in a call-clobbered reg across calls. */ - int caller_save_needed; /* The register class to use for a base register when reloading an @@ -297,6 +291,9 @@ extern rtx forced_labels; /* List of insn_chain instructions, one for every insn that reload needs to examine. */ struct insn_chain *reload_insn_chain; + +/* List of insns needing reloads. */ +static struct insn_chain *insns_need_reload; /* This structure is used to record information about register eliminations. Each array entry describes one possible way of eliminating a register @@ -360,8 +357,8 @@ static int num_labels; struct hard_reg_n_uses { int regno; int uses; }; static void dump_needs PROTO((FILE *)); -static int calculate_needs_all_insns PROTO((rtx, int)); -static int calculate_needs PROTO((int, rtx, rtx, int)); +static int calculate_needs_all_insns PROTO((int)); +static int calculate_needs PROTO((struct insn_chain *, rtx, int)); static int find_reload_regs PROTO((int, FILE *)); static int find_tworeg_group PROTO((int, int, FILE *)); static int find_group PROTO((int, int, FILE *)); @@ -371,7 +368,7 @@ static void count_possible_groups PROTO((int *, enum machine_mode *, static int modes_equiv_for_class_p PROTO((enum machine_mode, enum machine_mode, enum reg_class)); -static void delete_caller_save_insns PROTO((rtx)); +static void delete_caller_save_insns PROTO((void)); static void spill_failure PROTO((rtx)); static int new_spill_reg PROTO((int, int, int *, int *, int, FILE *)); @@ -384,11 +381,12 @@ static void set_initial_elim_offsets PROTO((void)); static void init_elim_table PROTO((void)); static void update_eliminables PROTO((HARD_REG_SET *)); static int spill_hard_reg PROTO((int, int, FILE *, int)); +static void finish_spills PROTO((int, FILE *)); static void scan_paradoxical_subregs PROTO((rtx)); static int hard_reg_use_compare PROTO((const GENERIC_PTR, const GENERIC_PTR)); static void order_regs_for_reload PROTO((void)); static int compare_spill_regs PROTO((const GENERIC_PTR, const GENERIC_PTR)); -static void reload_as_needed PROTO((rtx, int)); +static void reload_as_needed PROTO((int)); static void forget_old_reloads_1 PROTO((rtx, rtx)); static int reload_reg_class_lower PROTO((const GENERIC_PTR, const GENERIC_PTR)); static void mark_reload_reg_in_use PROTO((int, int, enum reload_type, @@ -399,10 +397,10 @@ static int reload_reg_free_p PROTO((int, int, enum reload_type)); static int reload_reg_free_before_p PROTO((int, int, enum reload_type, int)); static int reload_reg_free_for_value_p PROTO((int, int, enum reload_type, rtx, rtx, int)); static int reload_reg_reaches_end_p PROTO((int, int, enum reload_type)); -static int allocate_reload_reg PROTO((int, rtx, int, int)); -static void choose_reload_regs PROTO((rtx, rtx)); +static int allocate_reload_reg PROTO((struct insn_chain *, int, int, int)); +static void choose_reload_regs PROTO((struct insn_chain *, rtx)); static void merge_assigned_reloads PROTO((rtx)); -static void emit_reload_insns PROTO((rtx, int)); +static void emit_reload_insns PROTO((struct insn_chain *)); static void delete_output_reload PROTO((rtx, int, rtx)); static void inc_for_reload PROTO((rtx, rtx, int)); static int constraint_accepts_reg_p PROTO((char *, rtx)); @@ -560,10 +558,29 @@ new_insn_chain () return c; } +/* Small utility function to set all regs in hard reg set TO which are + allocated to pseudos in regset FROM. */ +void +compute_use_by_pseudos (to, from) + HARD_REG_SET *to; + regset from; +{ + int regno; + EXECUTE_IF_SET_IN_REG_SET + (from, FIRST_PSEUDO_REGISTER, regno, + { + int r = reg_renumber[regno]; + int nregs; + if (r < 0) + abort (); + nregs = HARD_REGNO_NREGS (r, PSEUDO_REGNO_MODE (regno)); + while (nregs-- > 0) + SET_HARD_REG_BIT (*to, r + nregs); + }); +} + /* Global variables used by reload and its subroutines. */ -/* Set during calculate_needs if an insn needs reloading. */ -static int something_needs_reloads; /* Set during calculate_needs if an insn needs register elimination. */ static int something_needs_elimination; @@ -866,6 +883,8 @@ reload (first, global, dumpfile) if (! SMALL_REGISTER_CLASSES) COPY_HARD_REG_SET (forbidden_regs, bad_spill_regs); + spilled_pseudos = ALLOCA_REG_SET (); + /* Spill any hard regs that we know we can't eliminate. */ for (ep = reg_eliminate; ep < ®_eliminate[NUM_ELIMINABLE_REGS]; ep++) if (! ep->can_eliminate) @@ -876,6 +895,8 @@ reload (first, global, dumpfile) spill_hard_reg (HARD_FRAME_POINTER_REGNUM, global, dumpfile, 1); #endif + finish_spills (global, dumpfile); + if (global) for (i = 0; i < N_REG_CLASSES; i++) { @@ -894,8 +915,7 @@ reload (first, global, dumpfile) reg does not necessarily imply any pseudo reg was spilled; sometimes we find a reload reg that no pseudo reg was allocated in. */ something_changed = 1; - /* This flag is set if there are any insns that require reloading. */ - something_needs_reloads = 0; + /* This flag is set if there are any insns that require register eliminations. */ something_needs_elimination = 0; @@ -1002,7 +1022,7 @@ reload (first, global, dumpfile) reload_firstobj = (char *) obstack_alloc (&reload_obstack, 0); } - something_changed |= calculate_needs_all_insns (first, global); + something_changed |= calculate_needs_all_insns (global); /* If we allocated any new memory locations, make another pass since it might have changed elimination offsets. */ @@ -1024,6 +1044,8 @@ reload (first, global, dumpfile) } } + finish_spills (global, dumpfile); + /* If all needs are met, we win. */ for (i = 0; i < N_REG_CLASSES; i++) @@ -1075,8 +1097,10 @@ reload (first, global, dumpfile) if (failure) goto failed; + finish_spills (global, dumpfile); + if (something_changed) - delete_caller_save_insns (first); + delete_caller_save_insns (); } /* If global-alloc was run, notify it of any register eliminations we have @@ -1111,8 +1135,8 @@ reload (first, global, dumpfile) by generating move instructions to move the must-be-register values into or out of the reload registers. */ - if (something_needs_reloads || something_needs_elimination) - reload_as_needed (first, global); + if (insns_need_reload != 0 || something_needs_elimination) + reload_as_needed (global); /* If we were able to eliminate the frame pointer, show that it is no longer live at the start of any basic block. If it ls live by @@ -1225,8 +1249,6 @@ reload (first, global, dumpfile) warning ("frame size too large for reliable stack checking"); } - obstack_free (&reload_obstack, reload_startobj); - /* Indicate that we no longer have known memory locations or constants. */ if (reg_equiv_constant) free (reg_equiv_constant); @@ -1245,10 +1267,16 @@ reload (first, global, dumpfile) free (reg_equiv_address); free (reg_max_ref_width); + FREE_REG_SET (spilled_pseudos); + CLEAR_HARD_REG_SET (used_spill_regs); for (i = 0; i < n_spills; i++) SET_HARD_REG_BIT (used_spill_regs, spill_regs[i]); + /* Free all the insn_chain structures at once. */ + obstack_free (&reload_obstack, reload_startobj); + unused_insn_chains = 0; + return failure; } @@ -1256,24 +1284,20 @@ reload (first, global, dumpfile) information about the need to do register elimination and the need to perform reloads. */ static int -calculate_needs_all_insns (first, global) - rtx first; +calculate_needs_all_insns (global) int global; { - rtx insn; int something_changed = 0; rtx after_call = 0; - /* Keep track of which basic blocks are needing the reloads. */ - int this_block = 0; + struct insn_chain **pprev_reload = &insns_need_reload; + struct insn_chain *chain; /* Compute the most additional registers needed by any instruction. Collect information separately for each class of regs. */ - - for (insn = first; insn; insn = NEXT_INSN (insn)) + + for (chain = reload_insn_chain; chain; chain = chain->next) { - if (global && this_block + 1 < n_basic_blocks - && insn == basic_block_head[this_block+1]) - ++this_block; + rtx insn = chain->insn; /* If this is a label, a JUMP_INSN, or has REG_NOTES (which might include REG_LABEL), we need to see what effects this @@ -1326,23 +1350,9 @@ calculate_needs_all_insns (first, global) spill_reg_order); /* Remember for later shortcuts which insns had any reloads or - register eliminations. - - One might think that it would be worthwhile to mark insns - that need register replacements but not reloads, but this is - not safe because find_reloads may do some manipulation of - the insn (such as swapping commutative operands), which would - be lost when we restore the old pattern after register - replacement. So the actions of find_reloads must be redone in - subsequent passes or in reload_as_needed. - - However, it is safe to mark insns that need reloads - but not register replacement. */ - - PUT_MODE (insn, (did_elimination ? QImode - : n_reloads ? HImode - : GET_MODE (insn) == DImode ? DImode - : VOIDmode)); + register eliminations. */ + chain->need_elim = did_elimination; + chain->need_reload = n_reloads > 0; /* Discard any register replacements done. */ if (did_elimination) @@ -1355,12 +1365,15 @@ calculate_needs_all_insns (first, global) } if (n_reloads != 0) - something_changed |= calculate_needs (this_block, insn, - avoid_return_reg, global); + { + *pprev_reload = chain; + pprev_reload = &chain->next_need_reload; + something_changed |= calculate_needs (chain, avoid_return_reg, + global); + } } - - /* Note that there is a continue statement above. */ } + *pprev_reload = 0; return something_changed; } @@ -1383,21 +1396,15 @@ calculate_needs_all_insns (first, global) inputs and outputs. */ static int -calculate_needs (this_block, insn, avoid_return_reg, global) - int this_block; - rtx insn, avoid_return_reg; +calculate_needs (chain, avoid_return_reg, global) + struct insn_chain *chain; + rtx avoid_return_reg; int global; { + rtx insn = chain->insn; int something_changed = 0; int i; - struct needs - { - /* [0] is normal, [1] is nongroup. */ - int regs[2][N_REG_CLASSES]; - int groups[N_REG_CLASSES]; - }; - /* Each `struct needs' corresponds to one RELOAD_... type. */ struct { struct needs other; @@ -1413,7 +1420,6 @@ calculate_needs (this_block, insn, avoid_return_reg, global) struct needs out_addr_addr[MAX_RECOG_OPERANDS]; } insn_needs; - something_needs_reloads = 1; bzero ((char *) &insn_needs, sizeof insn_needs); /* Count each reload once in every class @@ -1441,9 +1447,9 @@ calculate_needs (this_block, insn, avoid_return_reg, global) in this basic block. We do not use insn_needs and insn_groups because they are overly conservative for this purpose. */ - if (global && ! basic_block_needs[(int) class][this_block]) + if (global && ! basic_block_needs[(int) class][chain->block]) { - basic_block_needs[(int) class][this_block] = 1; + basic_block_needs[(int) class][chain->block] = 1; something_changed = 1; } @@ -1703,6 +1709,10 @@ calculate_needs (this_block, insn, avoid_return_reg, global) max_nongroups_insn[i] = insn; } } + + /* Record the needs for later. */ + chain->need = insn_needs.other; + return something_changed; } @@ -2064,41 +2074,39 @@ dump_needs (dumpfile) /* Delete all insns that were inserted by emit_caller_save_insns during this iteration. */ static void -delete_caller_save_insns (first) - rtx first; +delete_caller_save_insns () { - rtx insn = first; - int b = -1; + struct insn_chain *c = reload_insn_chain; - while (insn != 0) + while (c != 0) { - if (b + 1 != n_basic_blocks - && basic_block_head[b + 1] == insn) - b++; - - while (insn != 0 && INSN_UID (insn) >= reload_first_uid) + while (c != 0 && c->is_caller_save_insn) { - rtx next = NEXT_INSN (insn); - rtx prev = PREV_INSN (insn); - - if (insn == basic_block_head[b]) - basic_block_head[b] = next; - if (insn == basic_block_end[b]) - basic_block_end[b] = prev; - - if (next != 0) - PREV_INSN (next) = prev; - if (prev != 0) - NEXT_INSN (prev) = next; - - insn = next; - - if (b + 1 != n_basic_blocks - && basic_block_head[b + 1] == insn) - b++; + struct insn_chain *next = c->next; + rtx insn = c->insn; + + if (insn == basic_block_head[c->block]) + basic_block_head[c->block] = NEXT_INSN (insn); + if (insn == basic_block_end[c->block]) + basic_block_end[c->block] = PREV_INSN (insn); + if (c == reload_insn_chain) + reload_insn_chain = next; + + if (NEXT_INSN (insn) != 0) + PREV_INSN (NEXT_INSN (insn)) = PREV_INSN (insn); + if (PREV_INSN (insn) != 0) + NEXT_INSN (PREV_INSN (insn)) = NEXT_INSN (insn); + + if (next) + next->prev = c->prev; + if (c->prev) + c->prev->next = next; + c->next = unused_insn_chains; + unused_insn_chains = c; + c = next; } - if (insn != 0) - insn = NEXT_INSN (insn); + if (c != 0) + c = c->next; } } @@ -3809,6 +3817,10 @@ spill_hard_reg (regno, global, dumpfile, cant_eliminate) retry_global_alloc (i, forbidden_regs); alter_reg (i, regno); + + if (reg_renumber[i] == -1) + SET_REGNO_REG_SET (spilled_pseudos, i); + if (dumpfile) { if (reg_renumber[i] == -1) @@ -3821,6 +3833,22 @@ spill_hard_reg (regno, global, dumpfile, cant_eliminate) return something_changed; } + +/* Clear the contents of spilled_pseudos from the life information in all + insn chains. */ +static void +finish_spills (global, dumpfile) + int global; + FILE *dumpfile; +{ + struct insn_chain *chain; + + for (chain = reload_insn_chain; chain; chain = chain->next) + { + AND_COMPL_REG_SET (chain->live_before, spilled_pseudos); + AND_COMPL_REG_SET (chain->live_after, spilled_pseudos); + } +} /* Find all paradoxical subregs within X and update reg_max_ref_width. Also mark any hard registers used to store user variables as @@ -4025,13 +4053,11 @@ compare_spill_regs (r1p, r2p) as the insns are scanned. */ static void -reload_as_needed (first, live_known) - rtx first; +reload_as_needed (live_known) int live_known; { - register rtx insn; + struct insn_chain *chain; register int i; - int this_block = 0; rtx x; rtx after_call = 0; @@ -4068,14 +4094,10 @@ reload_as_needed (first, live_known) spill_reg_order[spill_regs[i]] = i; } - for (insn = first; insn;) + for (chain = reload_insn_chain; chain; chain = chain->next) { - register rtx next = NEXT_INSN (insn); - - /* Notice when we move to a new basic block. */ - if (live_known && this_block + 1 < n_basic_blocks - && insn == basic_block_head[this_block+1]) - ++this_block; + rtx insn = chain->insn; + rtx old_next = NEXT_INSN (insn); /* If we pass a label, copy the offsets from the label information into the current offsets of each elimination. */ @@ -4133,17 +4155,21 @@ reload_as_needed (first, live_known) /* If we need to do register elimination processing, do so. This might delete the insn, in which case we are done. */ - if (num_eliminable && GET_MODE (insn) == QImode) + if (num_eliminable && chain->need_elim) { eliminate_regs_in_insn (insn, 1); if (GET_CODE (insn) == NOTE) - { - insn = next; - continue; - } + continue; } - if (GET_MODE (insn) == VOIDmode) + /* If need_elim is nonzero but need_reload is zero, one might think + that we could simply set n_reloads to 0. However, find_reloads + could have done some manipulation of the insn (such as swapping + commutative operands), and these manipulations are lost during + the first pass for every insn that needs register elimination. + So the actions of find_reloads must be redone here. */ + + if (! chain->need_elim && ! chain->need_reload) n_reloads = 0; /* First find the pseudo regs that must be reloaded for this insn. This info is returned in the tables reload_... (see reload.h). @@ -4170,7 +4196,7 @@ reload_as_needed (first, live_known) for (class = 0; class < N_REG_CLASSES; class++) if (basic_block_needs[class] != 0 - && basic_block_needs[class][this_block] == 0) + && basic_block_needs[class][chain->block] == 0) for (i = 0; i < n_reloads; i++) if (class == (int) reload_reg_class[i] && reload_reg_rtx[i] == 0 @@ -4183,7 +4209,7 @@ reload_as_needed (first, live_known) reusing reload regs from previous insns, or else output load insns to reload them. Maybe output store insns too. Record the choices of reload reg in reload_reg_rtx. */ - choose_reload_regs (insn, avoid_return_reg); + choose_reload_regs (chain, avoid_return_reg); /* Merge any reloads that we didn't combine for fear of increasing the number of spill registers needed but now @@ -4193,7 +4219,7 @@ reload_as_needed (first, live_known) /* Generate the insns to reload operands into or out of their reload regs. */ - emit_reload_insns (insn, this_block); + emit_reload_insns (chain); /* Substitute the chosen reload regs from reload_reg_rtx into the insn's body (or perhaps into the bodies of other @@ -4229,7 +4255,7 @@ reload_as_needed (first, live_known) /* There may have been CLOBBER insns placed after INSN. So scan between INSN and NEXT and use them to forget old reloads. */ - for (x = NEXT_INSN (insn); x != next; x = NEXT_INSN (x)) + for (x = NEXT_INSN (insn); x != old_next; x = NEXT_INSN (x)) if (GET_CODE (x) == INSN && GET_CODE (PATTERN (x)) == CLOBBER) note_stores (PATTERN (x), forget_old_reloads_1); @@ -4271,8 +4297,6 @@ reload_as_needed (first, live_known) CLEAR_HARD_REG_BIT (reg_reloaded_valid, i); #endif - insn = next; - #ifdef USE_C_ALLOCA alloca (0); #endif @@ -5216,12 +5240,13 @@ reload_reg_free_for_value_p (regno, opnum, type, value, out, reloadnum) or 0 if we couldn't find a spill reg and we didn't change anything. */ static int -allocate_reload_reg (r, insn, last_reload, noerror) +allocate_reload_reg (chain, r, last_reload, noerror) + struct insn_chain *chain; int r; - rtx insn; int last_reload; int noerror; { + rtx insn = chain->insn; int i; int pass; int count; @@ -5422,10 +5447,11 @@ allocate_reload_reg (r, insn, last_reload, noerror) finding a reload reg in the proper class. */ static void -choose_reload_regs (insn, avoid_return_reg) - rtx insn; +choose_reload_regs (chain, avoid_return_reg) + struct insn_chain *chain; rtx avoid_return_reg; { + rtx insn = chain->insn; register int i, j; int max_group_size = 1; enum reg_class group_class = NO_REGS; @@ -5668,7 +5694,7 @@ choose_reload_regs (insn, avoid_return_reg) || reload_secondary_p[reload_order[i]]) && ! reload_optional[reload_order[i]] && reload_reg_rtx[reload_order[i]] == 0) - allocate_reload_reg (reload_order[i], insn, 0, inheritance); + allocate_reload_reg (chain, reload_order[i], 0, inheritance); #endif /* First see if this pseudo is already available as reloaded @@ -5994,7 +6020,7 @@ choose_reload_regs (insn, avoid_return_reg) if (i == n_reloads) continue; - allocate_reload_reg (r, insn, j == n_reloads - 1, inheritance); + allocate_reload_reg (chain, r, j == n_reloads - 1, inheritance); #endif } @@ -6013,7 +6039,7 @@ choose_reload_regs (insn, avoid_return_reg) if (reload_reg_rtx[r] != 0 || reload_optional[r]) continue; - if (! allocate_reload_reg (r, insn, j == n_reloads - 1, inheritance)) + if (! allocate_reload_reg (chain, r, j == n_reloads - 1, inheritance)) break; } @@ -6312,10 +6338,11 @@ merge_assigned_reloads (insn) /* Output insns to reload values in and out of the chosen reload regs. */ static void -emit_reload_insns (insn, bb) - rtx insn; - int bb; +emit_reload_insns (chain) + struct insn_chain *chain; { + rtx insn = chain->insn; + register int j; rtx input_reload_insns[MAX_RECOG_OPERANDS]; rtx other_input_address_reload_insns = 0; @@ -7259,10 +7286,10 @@ emit_reload_insns (insn, bb) /* Keep basic block info up to date. */ if (n_basic_blocks) { - if (basic_block_head[bb] == insn) - basic_block_head[bb] = NEXT_INSN (before_insn); - if (basic_block_end[bb] == insn) - basic_block_end[bb] = PREV_INSN (following_insn); + if (basic_block_head[chain->block] == insn) + basic_block_head[chain->block] = NEXT_INSN (before_insn); + if (basic_block_end[chain->block] == insn) + basic_block_end[chain->block] = PREV_INSN (following_insn); } /* Move death notes from INSN diff --git a/gcc/rtl.texi b/gcc/rtl.texi index 9b69eaebabac..61aeb850e88d 100644 --- a/gcc/rtl.texi +++ b/gcc/rtl.texi @@ -2372,9 +2372,7 @@ These codes are printed symbolically when they appear in debugging dumps. @cindex @code{HImode}, in @code{insn} @cindex @code{QImode}, in @code{insn} The machine mode of an insn is normally @code{VOIDmode}, but some -phases use the mode for various purposes; for example, the reload pass -sets it to @code{HImode} if the insn needs reloading but not register -elimination and @code{QImode} if both are required. +phases use the mode for various purposes. The common subexpression elimination pass sets the mode of an insn to @code{QImode} when it is the first insn in a block that has already -- 2.43.5