diff options
author | rth <rth@138bc75d-0d04-0410-961f-82ee72b054a4> | 1999-02-02 21:22:52 +0000 |
---|---|---|
committer | rth <rth@138bc75d-0d04-0410-961f-82ee72b054a4> | 1999-02-02 21:22:52 +0000 |
commit | 29bd1808340d56f994e393a956ebd4266565b242 (patch) | |
tree | bf741c1416516d6d4b65f9e54691a31b98962066 /gcc/reorg.c | |
parent | 31a0482ddd058487fa2d22824997c09c00742d98 (diff) | |
download | gcc-29bd1808340d56f994e393a956ebd4266565b242.tar.gz |
Bob Manson <manson@charmed.cygnus.com>
* resource.c, resource.h: New files.
* haifa-sched.c (regno_use_in): Moved to rtlanal.c.
(split_block_insns): Moved to recog.c.
(update_flow_info): Make public.
* reorg.c: Moved the functions dealing with computing resource
usage to resource.c.
* sched.c (regno_use_in): Moved to rtlanal.c.
(update_flow_info): Make public.
(schedule_insns): Use split_block_insns.
* recog.c (split_block_insns): New function.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@24982 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/reorg.c')
-rw-r--r-- | gcc/reorg.c | 1162 |
1 files changed, 16 insertions, 1146 deletions
diff --git a/gcc/reorg.c b/gcc/reorg.c index 2e41e71e492..a8f52f955c9 100644 --- a/gcc/reorg.c +++ b/gcc/reorg.c @@ -134,6 +134,7 @@ Boston, MA 02111-1307, USA. */ #include "output.h" #include "obstack.h" #include "insn-attr.h" +#include "resource.h" #ifdef DELAY_SLOTS @@ -164,57 +165,10 @@ static rtx *unfilled_firstobj; ((rtx *) obstack_next_free (&unfilled_slots_obstack)) /* This structure is used to indicate which hardware resources are set or - needed by insns so far. */ - -struct resources -{ - char memory; /* Insn sets or needs a memory location. */ - char unch_memory; /* Insn sets of needs a "unchanging" MEM. */ - char volatil; /* Insn sets or needs a volatile memory loc. */ - char cc; /* Insn sets or needs the condition codes. */ - HARD_REG_SET regs; /* Which registers are set or needed. */ -}; - -/* Macro to clear all resources. */ -#define CLEAR_RESOURCE(RES) \ - do { (RES)->memory = (RES)->unch_memory = (RES)->volatil = (RES)->cc = 0; \ - CLEAR_HARD_REG_SET ((RES)->regs); } while (0) - -/* Indicates what resources are required at the beginning of the epilogue. */ -static struct resources start_of_epilogue_needs; - -/* Indicates what resources are required at function end. */ -static struct resources end_of_function_needs; /* Points to the label before the end of the function. */ static rtx end_of_function_label; -/* This structure is used to record liveness information at the targets or - fallthrough insns of branches. We will most likely need the information - at targets again, so save them in a hash table rather than recomputing them - each time. */ - -struct target_info -{ - int uid; /* INSN_UID of target. */ - struct target_info *next; /* Next info for same hash bucket. */ - HARD_REG_SET live_regs; /* Registers live at target. */ - int block; /* Basic block number containing target. */ - int bb_tick; /* Generation count of basic block info. */ -}; - -#define TARGET_HASH_PRIME 257 - -/* Define the hash table itself. */ -static struct target_info **target_hash_table; - -/* For each basic block, we maintain a generation number of its basic - block info, which is updated each time we move an insn from the - target of a jump. This is the generation number indexed by block - number. */ - -static int *bb_ticks; - /* Mapping between INSN_UID's and position in the code since INSN_UID's do not always monotonically increase. */ static int *uid_to_ruid; @@ -222,9 +176,6 @@ static int *uid_to_ruid; /* Highest valid index in `uid_to_ruid'. */ static int max_uid; -static void mark_referenced_resources PROTO((rtx, struct resources *, int)); -static void mark_set_resources PROTO((rtx, struct resources *, - int, int)); static int stop_search_p PROTO((rtx, int)); static int resource_conflicts_p PROTO((struct resources *, struct resources *)); @@ -242,6 +193,9 @@ static int rare_destination PROTO((rtx)); static int mostly_true_jump PROTO((rtx, rtx)); static rtx get_branch_condition PROTO((rtx, rtx)); static int condition_dominates_p PROTO((rtx, rtx)); +static int redirect_with_delay_slots_safe_p PROTO ((rtx, rtx, rtx)); +static int redirect_with_delay_list_safe_p PROTO ((rtx, rtx, rtx)); +static int check_annul_list_true_false PROTO ((int, rtx)); static rtx steal_delay_list_from_target PROTO((rtx, rtx, rtx, rtx, struct resources *, struct resources *, @@ -252,443 +206,20 @@ static rtx steal_delay_list_from_fallthrough PROTO((rtx, rtx, rtx, rtx, struct resources *, struct resources *, int, int *, int *)); -static rtx find_dead_or_set_registers PROTO ((rtx, struct resources *, rtx *, - int, struct resources, - struct resources)); static void try_merge_delay_insns PROTO((rtx, rtx)); static rtx redundant_insn PROTO((rtx, rtx, rtx)); static int own_thread_p PROTO((rtx, rtx, int)); -static int find_basic_block PROTO((rtx)); static void update_block PROTO((rtx, rtx)); static int reorg_redirect_jump PROTO((rtx, rtx)); static void update_reg_dead_notes PROTO((rtx, rtx)); static void fix_reg_dead_note PROTO((rtx, rtx)); static void update_reg_unused_notes PROTO((rtx, rtx)); -static void update_live_status PROTO((rtx, rtx)); -static rtx next_insn_no_annul PROTO((rtx)); -static void mark_target_live_regs PROTO((rtx, struct resources *)); static void fill_simple_delay_slots PROTO((int)); static rtx fill_slots_from_thread PROTO((rtx, rtx, rtx, rtx, int, int, int, int, int *, rtx)); static void fill_eager_delay_slots PROTO((void)); static void relax_delay_slots PROTO((rtx)); static void make_return_insns PROTO((rtx)); -static int redirect_with_delay_slots_safe_p PROTO ((rtx, rtx, rtx)); -static int redirect_with_delay_list_safe_p PROTO ((rtx, rtx, rtx)); -static int check_annul_list_true_false PROTO ((int, rtx)); - -/* Given X, some rtl, and RES, a pointer to a `struct resource', mark - which resources are references by the insn. If INCLUDE_DELAYED_EFFECTS - is TRUE, resources used by the called routine will be included for - CALL_INSNs. */ - -static void -mark_referenced_resources (x, res, include_delayed_effects) - register rtx x; - register struct resources *res; - register int include_delayed_effects; -{ - register enum rtx_code code = GET_CODE (x); - register int i, j; - register char *format_ptr; - - /* Handle leaf items for which we set resource flags. Also, special-case - CALL, SET and CLOBBER operators. */ - switch (code) - { - case CONST: - case CONST_INT: - case CONST_DOUBLE: - case PC: - case SYMBOL_REF: - case LABEL_REF: - return; - - case SUBREG: - if (GET_CODE (SUBREG_REG (x)) != REG) - mark_referenced_resources (SUBREG_REG (x), res, 0); - else - { - int regno = REGNO (SUBREG_REG (x)) + SUBREG_WORD (x); - int last_regno = regno + HARD_REGNO_NREGS (regno, GET_MODE (x)); - for (i = regno; i < last_regno; i++) - SET_HARD_REG_BIT (res->regs, i); - } - return; - - case REG: - for (i = 0; i < HARD_REGNO_NREGS (REGNO (x), GET_MODE (x)); i++) - SET_HARD_REG_BIT (res->regs, REGNO (x) + i); - return; - - case MEM: - /* If this memory shouldn't change, it really isn't referencing - memory. */ - if (RTX_UNCHANGING_P (x)) - res->unch_memory = 1; - else - res->memory = 1; - res->volatil = MEM_VOLATILE_P (x); - - /* Mark registers used to access memory. */ - mark_referenced_resources (XEXP (x, 0), res, 0); - return; - - case CC0: - res->cc = 1; - return; - - case UNSPEC_VOLATILE: - case ASM_INPUT: - /* Traditional asm's are always volatile. */ - res->volatil = 1; - return; - - case TRAP_IF: - res->volatil = 1; - break; - - case ASM_OPERANDS: - res->volatil = MEM_VOLATILE_P (x); - - /* For all ASM_OPERANDS, we must traverse the vector of input operands. - We can not just fall through here since then we would be confused - by the ASM_INPUT rtx inside ASM_OPERANDS, which do not indicate - traditional asms unlike their normal usage. */ - - for (i = 0; i < ASM_OPERANDS_INPUT_LENGTH (x); i++) - mark_referenced_resources (ASM_OPERANDS_INPUT (x, i), res, 0); - return; - - case CALL: - /* The first operand will be a (MEM (xxx)) but doesn't really reference - memory. The second operand may be referenced, though. */ - mark_referenced_resources (XEXP (XEXP (x, 0), 0), res, 0); - mark_referenced_resources (XEXP (x, 1), res, 0); - return; - - case SET: - /* Usually, the first operand of SET is set, not referenced. But - registers used to access memory are referenced. SET_DEST is - also referenced if it is a ZERO_EXTRACT or SIGN_EXTRACT. */ - - mark_referenced_resources (SET_SRC (x), res, 0); - - x = SET_DEST (x); - if (GET_CODE (x) == SIGN_EXTRACT || GET_CODE (x) == ZERO_EXTRACT) - mark_referenced_resources (x, res, 0); - else if (GET_CODE (x) == SUBREG) - x = SUBREG_REG (x); - if (GET_CODE (x) == MEM) - mark_referenced_resources (XEXP (x, 0), res, 0); - return; - - case CLOBBER: - return; - - case CALL_INSN: - if (include_delayed_effects) - { - /* A CALL references memory, the frame pointer if it exists, the - stack pointer, any global registers and any registers given in - USE insns immediately in front of the CALL. - - However, we may have moved some of the parameter loading insns - into the delay slot of this CALL. If so, the USE's for them - don't count and should be skipped. */ - rtx insn = PREV_INSN (x); - rtx sequence = 0; - int seq_size = 0; - rtx next = NEXT_INSN (x); - int i; - - /* If we are part of a delay slot sequence, point at the SEQUENCE. */ - if (NEXT_INSN (insn) != x) - { - next = NEXT_INSN (NEXT_INSN (insn)); - sequence = PATTERN (NEXT_INSN (insn)); - seq_size = XVECLEN (sequence, 0); - if (GET_CODE (sequence) != SEQUENCE) - abort (); - } - - res->memory = 1; - SET_HARD_REG_BIT (res->regs, STACK_POINTER_REGNUM); - if (frame_pointer_needed) - { - SET_HARD_REG_BIT (res->regs, FRAME_POINTER_REGNUM); -#if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM - SET_HARD_REG_BIT (res->regs, HARD_FRAME_POINTER_REGNUM); -#endif - } - - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (global_regs[i]) - SET_HARD_REG_BIT (res->regs, i); - - /* Check for a NOTE_INSN_SETJMP. If it exists, then we must - assume that this call can need any register. - - This is done to be more conservative about how we handle setjmp. - We assume that they both use and set all registers. Using all - registers ensures that a register will not be considered dead - just because it crosses a setjmp call. A register should be - considered dead only if the setjmp call returns non-zero. */ - if (next && GET_CODE (next) == NOTE - && NOTE_LINE_NUMBER (next) == NOTE_INSN_SETJMP) - SET_HARD_REG_SET (res->regs); - - { - rtx link; - - for (link = CALL_INSN_FUNCTION_USAGE (x); - link; - link = XEXP (link, 1)) - if (GET_CODE (XEXP (link, 0)) == USE) - { - for (i = 1; i < seq_size; i++) - { - rtx slot_pat = PATTERN (XVECEXP (sequence, 0, i)); - if (GET_CODE (slot_pat) == SET - && rtx_equal_p (SET_DEST (slot_pat), - SET_DEST (XEXP (link, 0)))) - break; - } - if (i >= seq_size) - mark_referenced_resources (SET_DEST (XEXP (link, 0)), - res, 0); - } - } - } - - /* ... fall through to other INSN processing ... */ - - case INSN: - case JUMP_INSN: - -#ifdef INSN_REFERENCES_ARE_DELAYED - if (! include_delayed_effects - && INSN_REFERENCES_ARE_DELAYED (x)) - return; -#endif - - /* No special processing, just speed up. */ - mark_referenced_resources (PATTERN (x), res, include_delayed_effects); - return; - - default: - break; - } - - /* Process each sub-expression and flag what it needs. */ - format_ptr = GET_RTX_FORMAT (code); - for (i = 0; i < GET_RTX_LENGTH (code); i++) - switch (*format_ptr++) - { - case 'e': - mark_referenced_resources (XEXP (x, i), res, include_delayed_effects); - break; - - case 'E': - for (j = 0; j < XVECLEN (x, i); j++) - mark_referenced_resources (XVECEXP (x, i, j), res, - include_delayed_effects); - break; - } -} - -/* Given X, a part of an insn, and a pointer to a `struct resource', - RES, indicate which resources are modified by the insn. If - INCLUDE_DELAYED_EFFECTS is nonzero, also mark resources potentially - set by the called routine. - - If IN_DEST is nonzero, it means we are inside a SET. Otherwise, - objects are being referenced instead of set. - - We never mark the insn as modifying the condition code unless it explicitly - SETs CC0 even though this is not totally correct. The reason for this is - that we require a SET of CC0 to immediately precede the reference to CC0. - So if some other insn sets CC0 as a side-effect, we know it cannot affect - our computation and thus may be placed in a delay slot. */ - -static void -mark_set_resources (x, res, in_dest, include_delayed_effects) - register rtx x; - register struct resources *res; - int in_dest; - int include_delayed_effects; -{ - register enum rtx_code code; - register int i, j; - register char *format_ptr; - - restart: - - code = GET_CODE (x); - - switch (code) - { - case NOTE: - case BARRIER: - case CODE_LABEL: - case USE: - case CONST_INT: - case CONST_DOUBLE: - case LABEL_REF: - case SYMBOL_REF: - case CONST: - case PC: - /* These don't set any resources. */ - return; - - case CC0: - if (in_dest) - res->cc = 1; - return; - - case CALL_INSN: - /* Called routine modifies the condition code, memory, any registers - that aren't saved across calls, global registers and anything - explicitly CLOBBERed immediately after the CALL_INSN. */ - - if (include_delayed_effects) - { - rtx next = NEXT_INSN (x); - rtx prev = PREV_INSN (x); - rtx link; - - res->cc = res->memory = 1; - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (call_used_regs[i] || global_regs[i]) - SET_HARD_REG_BIT (res->regs, i); - - /* If X is part of a delay slot sequence, then NEXT should be - the first insn after the sequence. */ - if (NEXT_INSN (prev) != x) - next = NEXT_INSN (NEXT_INSN (prev)); - - for (link = CALL_INSN_FUNCTION_USAGE (x); - link; link = XEXP (link, 1)) - if (GET_CODE (XEXP (link, 0)) == CLOBBER) - mark_set_resources (SET_DEST (XEXP (link, 0)), res, 1, 0); - - /* Check for a NOTE_INSN_SETJMP. If it exists, then we must - assume that this call can clobber any register. */ - if (next && GET_CODE (next) == NOTE - && NOTE_LINE_NUMBER (next) == NOTE_INSN_SETJMP) - SET_HARD_REG_SET (res->regs); - } - - /* ... and also what its RTL says it modifies, if anything. */ - - case JUMP_INSN: - case INSN: - - /* An insn consisting of just a CLOBBER (or USE) is just for flow - and doesn't actually do anything, so we ignore it. */ - -#ifdef INSN_SETS_ARE_DELAYED - if (! include_delayed_effects - && INSN_SETS_ARE_DELAYED (x)) - return; -#endif - - x = PATTERN (x); - if (GET_CODE (x) != USE && GET_CODE (x) != CLOBBER) - goto restart; - return; - - case SET: - /* If the source of a SET is a CALL, this is actually done by - the called routine. So only include it if we are to include the - effects of the calling routine. */ - - mark_set_resources (SET_DEST (x), res, - (include_delayed_effects - || GET_CODE (SET_SRC (x)) != CALL), - 0); - - mark_set_resources (SET_SRC (x), res, 0, 0); - return; - - case CLOBBER: - mark_set_resources (XEXP (x, 0), res, 1, 0); - return; - - case SEQUENCE: - for (i = 0; i < XVECLEN (x, 0); i++) - if (! (INSN_ANNULLED_BRANCH_P (XVECEXP (x, 0, 0)) - && INSN_FROM_TARGET_P (XVECEXP (x, 0, i)))) - mark_set_resources (XVECEXP (x, 0, i), res, 0, - include_delayed_effects); - return; - - case POST_INC: - case PRE_INC: - case POST_DEC: - case PRE_DEC: - mark_set_resources (XEXP (x, 0), res, 1, 0); - return; - - case ZERO_EXTRACT: - mark_set_resources (XEXP (x, 0), res, in_dest, 0); - mark_set_resources (XEXP (x, 1), res, 0, 0); - mark_set_resources (XEXP (x, 2), res, 0, 0); - return; - - case MEM: - if (in_dest) - { - res->memory = 1; - res->unch_memory = RTX_UNCHANGING_P (x); - res->volatil = MEM_VOLATILE_P (x); - } - - mark_set_resources (XEXP (x, 0), res, 0, 0); - return; - - case SUBREG: - if (in_dest) - { - if (GET_CODE (SUBREG_REG (x)) != REG) - mark_set_resources (SUBREG_REG (x), res, - in_dest, include_delayed_effects); - else - { - int regno = REGNO (SUBREG_REG (x)) + SUBREG_WORD (x); - int last_regno = regno + HARD_REGNO_NREGS (regno, GET_MODE (x)); - for (i = regno; i < last_regno; i++) - SET_HARD_REG_BIT (res->regs, i); - } - } - return; - - case REG: - if (in_dest) - for (i = 0; i < HARD_REGNO_NREGS (REGNO (x), GET_MODE (x)); i++) - SET_HARD_REG_BIT (res->regs, REGNO (x) + i); - return; - - default: - break; - } - - /* Process each sub-expression and flag what it needs. */ - format_ptr = GET_RTX_FORMAT (code); - for (i = 0; i < GET_RTX_LENGTH (code); i++) - switch (*format_ptr++) - { - case 'e': - mark_set_resources (XEXP (x, i), res, in_dest, include_delayed_effects); - break; - - case 'E': - for (j = 0; j < XVECLEN (x, i); j++) - mark_set_resources (XVECEXP (x, i, j), res, in_dest, - include_delayed_effects); - break; - } -} /* Return TRUE if this insn should stop the search for insn to fill delay slots. LABELS_P indicates that labels should terminate the search. @@ -988,16 +519,7 @@ add_to_delay_list (insn, delay_list) if (delay_list == 0) { - struct target_info *tinfo; - - for (tinfo = target_hash_table[INSN_UID (insn) % TARGET_HASH_PRIME]; - tinfo; tinfo = tinfo->next) - if (tinfo->uid == INSN_UID (insn)) - break; - - if (tinfo) - tinfo->block = -1; - + clear_hashed_info_for_insn (insn); return gen_rtx_INSN_LIST (VOIDmode, insn, NULL_RTX); } @@ -2324,41 +1846,6 @@ own_thread_p (thread, label, allow_fallthrough) return 1; } -/* Find the number of the basic block that starts closest to INSN. Return -1 - if we couldn't find such a basic block. */ - -static int -find_basic_block (insn) - rtx insn; -{ - int i; - - /* Scan backwards to the previous BARRIER. Then see if we can find a - label that starts a basic block. Return the basic block number. */ - - for (insn = prev_nonnote_insn (insn); - insn && GET_CODE (insn) != BARRIER; - insn = prev_nonnote_insn (insn)) - ; - - /* The start of the function is basic block zero. */ - if (insn == 0) - return 0; - - /* See if any of the upcoming CODE_LABELs start a basic block. If we reach - anything other than a CODE_LABEL or note, we can't find this code. */ - for (insn = next_nonnote_insn (insn); - insn && GET_CODE (insn) == CODE_LABEL; - insn = next_nonnote_insn (insn)) - { - for (i = 0; i < n_basic_blocks; i++) - if (insn == BLOCK_HEAD (i)) - return i; - } - - return -1; -} - /* Called when INSN is being moved from a location near the target of a jump. We leave a marker of the form (use (INSN)) immediately in front of WHERE for mark_target_live_regs. These markers will be deleted when @@ -2373,8 +1860,6 @@ update_block (insn, where) rtx insn; rtx where; { - int b; - /* Ignore if this was in a delay slot and it came from the target of a branch. */ if (INSN_FROM_TARGET_P (insn)) @@ -2385,9 +1870,7 @@ update_block (insn, where) /* INSN might be making a value live in a block where it didn't use to be. So recompute liveness information for this block. */ - b = find_basic_block (insn); - if (b != -1) - bb_ticks[b]++; + incr_ticks_for_insn (insn); } /* Similar to REDIRECT_JUMP except that we update the BB_TICKS entry for @@ -2398,11 +1881,7 @@ reorg_redirect_jump (jump, nlabel) rtx jump; rtx nlabel; { - int b = find_basic_block (jump); - - if (b != -1) - bb_ticks[b]++; - + incr_ticks_for_insn (jump); return redirect_jump (jump, nlabel); } @@ -2502,549 +1981,6 @@ update_reg_unused_notes (insn, redundant_insn) } } -/* Marks registers possibly live at the current place being scanned by - mark_target_live_regs. Used only by next two function. */ - -static HARD_REG_SET current_live_regs; - -/* Marks registers for which we have seen a REG_DEAD note but no assignment. - Also only used by the next two functions. */ - -static HARD_REG_SET pending_dead_regs; - -/* Utility function called from mark_target_live_regs via note_stores. - It deadens any CLOBBERed registers and livens any SET registers. */ - -static void -update_live_status (dest, x) - rtx dest; - rtx x; -{ - int first_regno, last_regno; - int i; - - if (GET_CODE (dest) != REG - && (GET_CODE (dest) != SUBREG || GET_CODE (SUBREG_REG (dest)) != REG)) - return; - - if (GET_CODE (dest) == SUBREG) - first_regno = REGNO (SUBREG_REG (dest)) + SUBREG_WORD (dest); - else - first_regno = REGNO (dest); - - last_regno = first_regno + HARD_REGNO_NREGS (first_regno, GET_MODE (dest)); - - if (GET_CODE (x) == CLOBBER) - for (i = first_regno; i < last_regno; i++) - CLEAR_HARD_REG_BIT (current_live_regs, i); - else - for (i = first_regno; i < last_regno; i++) - { - SET_HARD_REG_BIT (current_live_regs, i); - CLEAR_HARD_REG_BIT (pending_dead_regs, i); - } -} - -/* Similar to next_insn, but ignores insns in the delay slots of - an annulled branch. */ - -static rtx -next_insn_no_annul (insn) - rtx insn; -{ - if (insn) - { - /* If INSN is an annulled branch, skip any insns from the target - of the branch. */ - if (INSN_ANNULLED_BRANCH_P (insn) - && NEXT_INSN (PREV_INSN (insn)) != insn) - while (INSN_FROM_TARGET_P (NEXT_INSN (insn))) - insn = NEXT_INSN (insn); - - insn = NEXT_INSN (insn); - if (insn && GET_CODE (insn) == INSN - && GET_CODE (PATTERN (insn)) == SEQUENCE) - insn = XVECEXP (PATTERN (insn), 0, 0); - } - - return insn; -} - -/* A subroutine of mark_target_live_regs. Search forward from TARGET - looking for registers that are set before they are used. These are dead. - Stop after passing a few conditional jumps, and/or a small - number of unconditional branches. */ - -static rtx -find_dead_or_set_registers (target, res, jump_target, jump_count, set, needed) - rtx target; - struct resources *res; - rtx *jump_target; - int jump_count; - struct resources set, needed; -{ - HARD_REG_SET scratch; - rtx insn, next; - rtx jump_insn = 0; - int i; - - for (insn = target; insn; insn = next) - { - rtx this_jump_insn = insn; - - next = NEXT_INSN (insn); - switch (GET_CODE (insn)) - { - case CODE_LABEL: - /* After a label, any pending dead registers that weren't yet - used can be made dead. */ - AND_COMPL_HARD_REG_SET (pending_dead_regs, needed.regs); - AND_COMPL_HARD_REG_SET (res->regs, pending_dead_regs); - CLEAR_HARD_REG_SET (pending_dead_regs); - - continue; - - case BARRIER: - case NOTE: - continue; - - case INSN: - if (GET_CODE (PATTERN (insn)) == USE) - { - /* If INSN is a USE made by update_block, we care about the - underlying insn. Any registers set by the underlying insn - are live since the insn is being done somewhere else. */ - if (GET_RTX_CLASS (GET_CODE (XEXP (PATTERN (insn), 0))) == 'i') - mark_set_resources (XEXP (PATTERN (insn), 0), res, 0, 1); - - /* All other USE insns are to be ignored. */ - continue; - } - else if (GET_CODE (PATTERN (insn)) == CLOBBER) - continue; - else if (GET_CODE (PATTERN (insn)) == SEQUENCE) - { - /* An unconditional jump can be used to fill the delay slot - of a call, so search for a JUMP_INSN in any position. */ - for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++) - { - this_jump_insn = XVECEXP (PATTERN (insn), 0, i); - if (GET_CODE (this_jump_insn) == JUMP_INSN) - break; - } - } - - default: - break; - } - - if (GET_CODE (this_jump_insn) == JUMP_INSN) - { - if (jump_count++ < 10) - { - if (simplejump_p (this_jump_insn) - || GET_CODE (PATTERN (this_jump_insn)) == RETURN) - { - next = JUMP_LABEL (this_jump_insn); - if (jump_insn == 0) - { - jump_insn = insn; - if (jump_target) - *jump_target = JUMP_LABEL (this_jump_insn); - } - } - else if (condjump_p (this_jump_insn) - || condjump_in_parallel_p (this_jump_insn)) - { - struct resources target_set, target_res; - struct resources fallthrough_res; - - /* We can handle conditional branches here by following - both paths, and then IOR the results of the two paths - together, which will give us registers that are dead - on both paths. Since this is expensive, we give it - a much higher cost than unconditional branches. The - cost was chosen so that we will follow at most 1 - conditional branch. */ - - jump_count += 4; - if (jump_count >= 10) - break; - - mark_referenced_resources (insn, &needed, 1); - - /* For an annulled branch, mark_set_resources ignores slots - filled by instructions from the target. This is correct - if the branch is not taken. Since we are following both - paths from the branch, we must also compute correct info - if the branch is taken. We do this by inverting all of - the INSN_FROM_TARGET_P bits, calling mark_set_resources, - and then inverting the INSN_FROM_TARGET_P bits again. */ - - if (GET_CODE (PATTERN (insn)) == SEQUENCE - && INSN_ANNULLED_BRANCH_P (this_jump_insn)) - { - for (i = 1; i < XVECLEN (PATTERN (insn), 0); i++) - INSN_FROM_TARGET_P (XVECEXP (PATTERN (insn), 0, i)) - = ! INSN_FROM_TARGET_P (XVECEXP (PATTERN (insn), 0, i)); - - target_set = set; - mark_set_resources (insn, &target_set, 0, 1); - - for (i = 1; i < XVECLEN (PATTERN (insn), 0); i++) - INSN_FROM_TARGET_P (XVECEXP (PATTERN (insn), 0, i)) - = ! INSN_FROM_TARGET_P (XVECEXP (PATTERN (insn), 0, i)); - - mark_set_resources (insn, &set, 0, 1); - } - else - { - mark_set_resources (insn, &set, 0, 1); - target_set = set; - } - - target_res = *res; - COPY_HARD_REG_SET (scratch, target_set.regs); - AND_COMPL_HARD_REG_SET (scratch, needed.regs); - AND_COMPL_HARD_REG_SET (target_res.regs, scratch); - - fallthrough_res = *res; - COPY_HARD_REG_SET (scratch, set.regs); - AND_COMPL_HARD_REG_SET (scratch, needed.regs); - AND_COMPL_HARD_REG_SET (fallthrough_res.regs, scratch); - - find_dead_or_set_registers (JUMP_LABEL (this_jump_insn), - &target_res, 0, jump_count, - target_set, needed); - find_dead_or_set_registers (next, - &fallthrough_res, 0, jump_count, - set, needed); - IOR_HARD_REG_SET (fallthrough_res.regs, target_res.regs); - AND_HARD_REG_SET (res->regs, fallthrough_res.regs); - break; - } - else - break; - } - else - { - /* Don't try this optimization if we expired our jump count - above, since that would mean there may be an infinite loop - in the function being compiled. */ - jump_insn = 0; - break; - } - } - - mark_referenced_resources (insn, &needed, 1); - mark_set_resources (insn, &set, 0, 1); - - COPY_HARD_REG_SET (scratch, set.regs); - AND_COMPL_HARD_REG_SET (scratch, needed.regs); - AND_COMPL_HARD_REG_SET (res->regs, scratch); - } - - return jump_insn; -} - -/* Set the resources that are live at TARGET. - - If TARGET is zero, we refer to the end of the current function and can - return our precomputed value. - - Otherwise, we try to find out what is live by consulting the basic block - information. This is tricky, because we must consider the actions of - reload and jump optimization, which occur after the basic block information - has been computed. - - Accordingly, we proceed as follows:: - - We find the previous BARRIER and look at all immediately following labels - (with no intervening active insns) to see if any of them start a basic - block. If we hit the start of the function first, we use block 0. - - Once we have found a basic block and a corresponding first insns, we can - accurately compute the live status from basic_block_live_regs and - reg_renumber. (By starting at a label following a BARRIER, we are immune - to actions taken by reload and jump.) Then we scan all insns between - that point and our target. For each CLOBBER (or for call-clobbered regs - when we pass a CALL_INSN), mark the appropriate registers are dead. For - a SET, mark them as live. - - We have to be careful when using REG_DEAD notes because they are not - updated by such things as find_equiv_reg. So keep track of registers - marked as dead that haven't been assigned to, and mark them dead at the - next CODE_LABEL since reload and jump won't propagate values across labels. - - If we cannot find the start of a basic block (should be a very rare - case, if it can happen at all), mark everything as potentially live. - - Next, scan forward from TARGET looking for things set or clobbered - before they are used. These are not live. - - Because we can be called many times on the same target, save our results - in a hash table indexed by INSN_UID. */ - -static void -mark_target_live_regs (target, res) - rtx target; - struct resources *res; -{ - int b = -1; - int i; - struct target_info *tinfo; - rtx insn; - rtx jump_insn = 0; - rtx jump_target; - HARD_REG_SET scratch; - struct resources set, needed; - - /* Handle end of function. */ - if (target == 0) - { - *res = end_of_function_needs; - return; - } - - /* We have to assume memory is needed, but the CC isn't. */ - res->memory = 1; - res->volatil = res->unch_memory = 0; - res->cc = 0; - - /* See if we have computed this value already. */ - for (tinfo = target_hash_table[INSN_UID (target) % TARGET_HASH_PRIME]; - tinfo; tinfo = tinfo->next) - if (tinfo->uid == INSN_UID (target)) - break; - - /* Start by getting the basic block number. If we have saved information, - we can get it from there unless the insn at the start of the basic block - has been deleted. */ - if (tinfo && tinfo->block != -1 - && ! INSN_DELETED_P (BLOCK_HEAD (tinfo->block))) - b = tinfo->block; - - if (b == -1) - b = find_basic_block (target); - - if (tinfo) - { - /* If the information is up-to-date, use it. Otherwise, we will - update it below. */ - if (b == tinfo->block && b != -1 && tinfo->bb_tick == bb_ticks[b]) - { - COPY_HARD_REG_SET (res->regs, tinfo->live_regs); - return; - } - } - else - { - /* Allocate a place to put our results and chain it into the - hash table. */ - tinfo = (struct target_info *) oballoc (sizeof (struct target_info)); - tinfo->uid = INSN_UID (target); - tinfo->block = b; - tinfo->next = target_hash_table[INSN_UID (target) % TARGET_HASH_PRIME]; - target_hash_table[INSN_UID (target) % TARGET_HASH_PRIME] = tinfo; - } - - CLEAR_HARD_REG_SET (pending_dead_regs); - - /* If we found a basic block, get the live registers from it and update - them with anything set or killed between its start and the insn before - TARGET. Otherwise, we must assume everything is live. */ - if (b != -1) - { - regset regs_live = basic_block_live_at_start[b]; - int j; - int regno; - rtx start_insn, stop_insn; - - /* 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. */ - - REG_SET_TO_HARD_REG_SET (current_live_regs, regs_live); - - EXECUTE_IF_SET_IN_REG_SET - (regs_live, FIRST_PSEUDO_REGISTER, 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 (current_live_regs, j); - }); - - /* Get starting and ending insn, handling the case where each might - be a SEQUENCE. */ - start_insn = (b == 0 ? get_insns () : BLOCK_HEAD (b)); - stop_insn = target; - - if (GET_CODE (start_insn) == INSN - && GET_CODE (PATTERN (start_insn)) == SEQUENCE) - start_insn = XVECEXP (PATTERN (start_insn), 0, 0); - - if (GET_CODE (stop_insn) == INSN - && GET_CODE (PATTERN (stop_insn)) == SEQUENCE) - stop_insn = next_insn (PREV_INSN (stop_insn)); - - for (insn = start_insn; insn != stop_insn; - insn = next_insn_no_annul (insn)) - { - rtx link; - rtx real_insn = insn; - - /* If this insn is from the target of a branch, it isn't going to - be used in the sequel. If it is used in both cases, this - test will not be true. */ - if (INSN_FROM_TARGET_P (insn)) - continue; - - /* If this insn is a USE made by update_block, we care about the - underlying insn. */ - if (GET_CODE (insn) == INSN && GET_CODE (PATTERN (insn)) == USE - && GET_RTX_CLASS (GET_CODE (XEXP (PATTERN (insn), 0))) == 'i') - real_insn = XEXP (PATTERN (insn), 0); - - if (GET_CODE (real_insn) == CALL_INSN) - { - /* CALL clobbers all call-used regs that aren't fixed except - sp, ap, and fp. Do this before setting the result of the - call live. */ - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (call_used_regs[i] - && i != STACK_POINTER_REGNUM && i != FRAME_POINTER_REGNUM - && i != ARG_POINTER_REGNUM -#if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM - && i != HARD_FRAME_POINTER_REGNUM -#endif -#if ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM - && ! (i == ARG_POINTER_REGNUM && fixed_regs[i]) -#endif -#ifdef PIC_OFFSET_TABLE_REGNUM - && ! (i == PIC_OFFSET_TABLE_REGNUM && flag_pic) -#endif - ) - CLEAR_HARD_REG_BIT (current_live_regs, i); - - /* A CALL_INSN sets any global register live, since it may - have been modified by the call. */ - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (global_regs[i]) - SET_HARD_REG_BIT (current_live_regs, i); - } - - /* Mark anything killed in an insn to be deadened at the next - label. Ignore USE insns; the only REG_DEAD notes will be for - parameters. But they might be early. A CALL_INSN will usually - clobber registers used for parameters. It isn't worth bothering - with the unlikely case when it won't. */ - if ((GET_CODE (real_insn) == INSN - && GET_CODE (PATTERN (real_insn)) != USE - && GET_CODE (PATTERN (real_insn)) != CLOBBER) - || GET_CODE (real_insn) == JUMP_INSN - || GET_CODE (real_insn) == CALL_INSN) - { - for (link = REG_NOTES (real_insn); link; link = XEXP (link, 1)) - if (REG_NOTE_KIND (link) == REG_DEAD - && GET_CODE (XEXP (link, 0)) == REG - && REGNO (XEXP (link, 0)) < FIRST_PSEUDO_REGISTER) - { - int first_regno = REGNO (XEXP (link, 0)); - int last_regno - = (first_regno - + HARD_REGNO_NREGS (first_regno, - GET_MODE (XEXP (link, 0)))); - - for (i = first_regno; i < last_regno; i++) - SET_HARD_REG_BIT (pending_dead_regs, i); - } - - note_stores (PATTERN (real_insn), update_live_status); - - /* If any registers were unused after this insn, kill them. - These notes will always be accurate. */ - for (link = REG_NOTES (real_insn); link; link = XEXP (link, 1)) - if (REG_NOTE_KIND (link) == REG_UNUSED - && GET_CODE (XEXP (link, 0)) == REG - && REGNO (XEXP (link, 0)) < FIRST_PSEUDO_REGISTER) - { - int first_regno = REGNO (XEXP (link, 0)); - int last_regno - = (first_regno - + HARD_REGNO_NREGS (first_regno, - GET_MODE (XEXP (link, 0)))); - - for (i = first_regno; i < last_regno; i++) - CLEAR_HARD_REG_BIT (current_live_regs, i); - } - } - - else if (GET_CODE (real_insn) == CODE_LABEL) - { - /* A label clobbers the pending dead registers since neither - reload nor jump will propagate a value across a label. */ - AND_COMPL_HARD_REG_SET (current_live_regs, pending_dead_regs); - CLEAR_HARD_REG_SET (pending_dead_regs); - } - - /* The beginning of the epilogue corresponds to the end of the - RTL chain when there are no epilogue insns. Certain resources - are implicitly required at that point. */ - else if (GET_CODE (real_insn) == NOTE - && NOTE_LINE_NUMBER (real_insn) == NOTE_INSN_EPILOGUE_BEG) - IOR_HARD_REG_SET (current_live_regs, start_of_epilogue_needs.regs); - } - - COPY_HARD_REG_SET (res->regs, current_live_regs); - tinfo->block = b; - tinfo->bb_tick = bb_ticks[b]; - } - else - /* We didn't find the start of a basic block. Assume everything - in use. This should happen only extremely rarely. */ - SET_HARD_REG_SET (res->regs); - - CLEAR_RESOURCE (&set); - CLEAR_RESOURCE (&needed); - - jump_insn = find_dead_or_set_registers (target, res, &jump_target, 0, - set, needed); - - /* If we hit an unconditional branch, we have another way of finding out - what is live: we can see what is live at the branch target and include - anything used but not set before the branch. The only things that are - live are those that are live using the above test and the test below. */ - - if (jump_insn) - { - struct resources new_resources; - rtx stop_insn = next_active_insn (jump_insn); - - mark_target_live_regs (next_active_insn (jump_target), &new_resources); - CLEAR_RESOURCE (&set); - CLEAR_RESOURCE (&needed); - - /* Include JUMP_INSN in the needed registers. */ - for (insn = target; insn != stop_insn; insn = next_active_insn (insn)) - { - mark_referenced_resources (insn, &needed, 1); - - COPY_HARD_REG_SET (scratch, needed.regs); - AND_COMPL_HARD_REG_SET (scratch, set.regs); - IOR_HARD_REG_SET (new_resources.regs, scratch); - - mark_set_resources (insn, &set, 0, 1); - } - - AND_HARD_REG_SET (res->regs, new_resources.regs); - } - - COPY_HARD_REG_SET (tinfo->live_regs, res->regs); -} - /* Scan a function looking for insns that need a delay slot and find insns to put into the delay slot. @@ -3317,9 +2253,11 @@ fill_simple_delay_slots (non_jumps_p) break; else if (JUMP_LABEL (trial_delay) != target) { - mark_target_live_regs - (next_active_insn (JUMP_LABEL (trial_delay)), - &needed_at_jump); + rtx ninsn = + next_active_insn (JUMP_LABEL (trial_delay)); + + mark_target_live_regs (get_insns (), ninsn, + &needed_at_jump); needed.memory |= needed_at_jump.memory; needed.unch_memory |= needed_at_jump.unch_memory; IOR_HARD_REG_SET (needed.regs, needed_at_jump.regs); @@ -3505,7 +2443,7 @@ fill_simple_delay_slots (non_jumps_p) current_function_epilogue_delay_list = gen_rtx_INSN_LIST (VOIDmode, trial, current_function_epilogue_delay_list); - mark_referenced_resources (trial, &end_of_function_needs, 1); + mark_end_of_function_resources (trial, 1); update_block (trial, trial); delete_insn (trial); @@ -3587,7 +2525,7 @@ fill_slots_from_thread (insn, condition, thread, opposite_thread, likely, if (condition == const_true_rtx) CLEAR_RESOURCE (&opposite_needed); else - mark_target_live_regs (opposite_thread, &opposite_needed); + mark_target_live_regs (get_insns (), opposite_thread, &opposite_needed); /* If the insn at THREAD can be split, do it here to avoid having to update THREAD and NEW_THREAD if it is done in the loop below. Also @@ -4623,80 +3561,11 @@ dbr_schedule (first, file) redirect_jump (insn, target); } - /* Indicate what resources are required to be valid at the end of the current - function. The condition code never is and memory always is. If the - frame pointer is needed, it is and so is the stack pointer unless - EXIT_IGNORE_STACK is non-zero. If the frame pointer is not needed, the - stack pointer is. Registers used to return the function value are - needed. Registers holding global variables are needed. */ - - end_of_function_needs.cc = 0; - end_of_function_needs.memory = 1; - end_of_function_needs.unch_memory = 0; - CLEAR_HARD_REG_SET (end_of_function_needs.regs); - - if (frame_pointer_needed) - { - SET_HARD_REG_BIT (end_of_function_needs.regs, FRAME_POINTER_REGNUM); -#if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM - SET_HARD_REG_BIT (end_of_function_needs.regs, HARD_FRAME_POINTER_REGNUM); -#endif -#ifdef EXIT_IGNORE_STACK - if (! EXIT_IGNORE_STACK - || current_function_sp_is_unchanging) -#endif - SET_HARD_REG_BIT (end_of_function_needs.regs, STACK_POINTER_REGNUM); - } - else - SET_HARD_REG_BIT (end_of_function_needs.regs, STACK_POINTER_REGNUM); - - if (current_function_return_rtx != 0) - mark_referenced_resources (current_function_return_rtx, - &end_of_function_needs, 1); - - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (global_regs[i] -#ifdef EPILOGUE_USES - || EPILOGUE_USES (i) -#endif - ) - SET_HARD_REG_BIT (end_of_function_needs.regs, i); - - /* The registers required to be live at the end of the function are - represented in the flow information as being dead just prior to - reaching the end of the function. For example, the return of a value - might be represented by a USE of the return register immediately - followed by an unconditional jump to the return label where the - return label is the end of the RTL chain. The end of the RTL chain - is then taken to mean that the return register is live. - - This sequence is no longer maintained when epilogue instructions are - added to the RTL chain. To reconstruct the original meaning, the - start of the epilogue (NOTE_INSN_EPILOGUE_BEG) is regarded as the - point where these registers become live (start_of_epilogue_needs). - If epilogue instructions are present, the registers set by those - instructions won't have been processed by flow. Thus, those - registers are additionally required at the end of the RTL chain - (end_of_function_needs). */ - - start_of_epilogue_needs = end_of_function_needs; - - while ((epilogue_insn = next_nonnote_insn (epilogue_insn))) - mark_set_resources (epilogue_insn, &end_of_function_needs, 0, 1); + init_resource_info (epilogue_insn); /* Show we haven't computed an end-of-function label yet. */ end_of_function_label = 0; - /* Allocate and initialize the tables used by mark_target_live_regs. */ - target_hash_table - = (struct target_info **) alloca ((TARGET_HASH_PRIME - * sizeof (struct target_info *))); - bzero ((char *) target_hash_table, - TARGET_HASH_PRIME * sizeof (struct target_info *)); - - bb_ticks = (int *) alloca (n_basic_blocks * sizeof (int)); - bzero ((char *) bb_ticks, n_basic_blocks * sizeof (int)); - /* Initialize the statistics for this function. */ bzero ((char *) num_insns_needing_delays, sizeof num_insns_needing_delays); bzero ((char *) num_filled_delays, sizeof num_filled_delays); @@ -4800,5 +3669,6 @@ dbr_schedule (first, file) GEN_INT (pred_flags), REG_NOTES (insn)); } + free_resource_info (); } #endif /* DELAY_SLOTS */ |