diff options
author | Dan Albert <danalbert@google.com> | 2015-06-17 11:09:54 -0700 |
---|---|---|
committer | Dan Albert <danalbert@google.com> | 2015-06-17 14:15:22 -0700 |
commit | f378ebf14df0952eae870c9865bab8326aa8f137 (patch) | |
tree | 31794503eb2a8c64ea5f313b93100f1163afcffb /gcc-4.4.0/gcc/resource.c | |
parent | 2c58169824949d3a597d9fa81931e001ef9b1bd0 (diff) | |
download | toolchain_gcc-f378ebf14df0952eae870c9865bab8326aa8f137.tar.gz toolchain_gcc-f378ebf14df0952eae870c9865bab8326aa8f137.tar.bz2 toolchain_gcc-f378ebf14df0952eae870c9865bab8326aa8f137.zip |
Delete old versions of GCC.
Change-Id: I710f125d905290e1024cbd67f48299861790c66c
Diffstat (limited to 'gcc-4.4.0/gcc/resource.c')
-rw-r--r-- | gcc-4.4.0/gcc/resource.c | 1264 |
1 files changed, 0 insertions, 1264 deletions
diff --git a/gcc-4.4.0/gcc/resource.c b/gcc-4.4.0/gcc/resource.c deleted file mode 100644 index 2f9a71bc6..000000000 --- a/gcc-4.4.0/gcc/resource.c +++ /dev/null @@ -1,1264 +0,0 @@ -/* Definitions for computing resource usage of specific insns. - Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 - Free Software Foundation, Inc. - -This file is part of GCC. - -GCC is free software; you can redistribute it and/or modify it under -the terms of the GNU General Public License as published by the Free -Software Foundation; either version 3, or (at your option) any later -version. - -GCC is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or -FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -for more details. - -You should have received a copy of the GNU General Public License -along with GCC; see the file COPYING3. If not see -<http://www.gnu.org/licenses/>. */ - -#include "config.h" -#include "system.h" -#include "coretypes.h" -#include "tm.h" -#include "toplev.h" -#include "rtl.h" -#include "tm_p.h" -#include "hard-reg-set.h" -#include "function.h" -#include "regs.h" -#include "flags.h" -#include "output.h" -#include "resource.h" -#include "except.h" -#include "insn-attr.h" -#include "params.h" -#include "df.h" - -/* 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 - -/* 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; - -/* Define the hash table itself. */ -static struct target_info **target_hash_table = NULL; - -/* 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; - -/* Marks registers possibly live at the current place being scanned by - mark_target_live_regs. Also used by update_live_status. */ - -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; - -static void update_live_status (rtx, const_rtx, void *); -static int find_basic_block (rtx, int); -static rtx next_insn_no_annul (rtx); -static rtx find_dead_or_set_registers (rtx, struct resources*, - rtx*, int, struct resources, - struct resources); - -/* 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 (rtx dest, const_rtx x, void *data ATTRIBUTE_UNUSED) -{ - int first_regno, last_regno; - int i; - - if (!REG_P (dest) - && (GET_CODE (dest) != SUBREG || !REG_P (SUBREG_REG (dest)))) - return; - - if (GET_CODE (dest) == SUBREG) - { - first_regno = subreg_regno (dest); - last_regno = first_regno + subreg_nregs (dest); - - } - else - { - first_regno = REGNO (dest); - last_regno = END_HARD_REGNO (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); - } -} - -/* Find the number of the basic block with correct live register - information that starts closest to INSN. Return -1 if we couldn't - find such a basic block or the beginning is more than - SEARCH_LIMIT instructions before INSN. Use SEARCH_LIMIT = -1 for - an unlimited search. - - The delay slot filling code destroys the control-flow graph so, - instead of finding the basic block containing INSN, we search - backwards toward a BARRIER where the live register information is - correct. */ - -static int -find_basic_block (rtx insn, int search_limit) -{ - basic_block bb; - - /* 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 && !BARRIER_P (insn) && search_limit != 0; - insn = prev_nonnote_insn (insn), --search_limit) - ; - - /* The closest BARRIER is too far away. */ - if (search_limit == 0) - return -1; - - /* The start of the function. */ - else if (insn == 0) - return ENTRY_BLOCK_PTR->next_bb->index; - - /* 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 && LABEL_P (insn); - insn = next_nonnote_insn (insn)) - { - FOR_EACH_BB (bb) - if (insn == BB_HEAD (bb)) - return bb->index; - } - - return -1; -} - -/* Similar to next_insn, but ignores insns in the delay slots of - an annulled branch. */ - -static rtx -next_insn_no_annul (rtx insn) -{ - if (insn) - { - /* If INSN is an annulled branch, skip any insns from the target - of the branch. */ - if (INSN_P (insn) - && INSN_ANNULLED_BRANCH_P (insn) - && NEXT_INSN (PREV_INSN (insn)) != insn) - { - rtx next = NEXT_INSN (insn); - enum rtx_code code = GET_CODE (next); - - while ((code == INSN || code == JUMP_INSN || code == CALL_INSN) - && INSN_FROM_TARGET_P (next)) - { - insn = next; - next = NEXT_INSN (insn); - code = GET_CODE (next); - } - } - - insn = NEXT_INSN (insn); - if (insn && NONJUMP_INSN_P (insn) - && GET_CODE (PATTERN (insn)) == SEQUENCE) - insn = XVECEXP (PATTERN (insn), 0, 0); - } - - return insn; -} - -/* Given X, some rtl, and RES, a pointer to a `struct resource', mark - which resources are referenced by the insn. If INCLUDE_DELAYED_EFFECTS - is TRUE, resources used by the called routine will be included for - CALL_INSNs. */ - -void -mark_referenced_resources (rtx x, struct resources *res, - int include_delayed_effects) -{ - enum rtx_code code = GET_CODE (x); - int i, j; - unsigned int r; - const 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 CONST_FIXED: - case CONST_VECTOR: - case PC: - case SYMBOL_REF: - case LABEL_REF: - return; - - case SUBREG: - if (!REG_P (SUBREG_REG (x))) - mark_referenced_resources (SUBREG_REG (x), res, 0); - else - { - unsigned int regno = subreg_regno (x); - unsigned int last_regno = regno + subreg_nregs (x); - - gcc_assert (last_regno <= FIRST_PSEUDO_REGISTER); - for (r = regno; r < last_regno; r++) - SET_HARD_REG_BIT (res->regs, r); - } - return; - - case REG: - gcc_assert (HARD_REGISTER_P (x)); - add_to_hard_reg_set (&res->regs, GET_MODE (x), REGNO (x)); - return; - - case MEM: - /* If this memory shouldn't change, it really isn't referencing - memory. */ - if (MEM_READONLY_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 TRAP_IF: - case ASM_INPUT: - /* Traditional asm's are always volatile. */ - 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. */ - - mark_referenced_resources (SET_SRC (x), res, 0); - - x = SET_DEST (x); - if (GET_CODE (x) == ZERO_EXTRACT - || GET_CODE (x) == STRICT_LOW_PART) - mark_referenced_resources (x, res, 0); - else if (GET_CODE (x) == SUBREG) - x = SUBREG_REG (x); - if (MEM_P (x)) - 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; - int i; - - /* If we are part of a delay slot sequence, point at the SEQUENCE. */ - if (NEXT_INSN (insn) != x) - { - sequence = PATTERN (NEXT_INSN (insn)); - seq_size = XVECLEN (sequence, 0); - gcc_assert (GET_CODE (sequence) == SEQUENCE); - } - - 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 REG_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 nonzero. */ - if (find_reg_note (x, REG_SETJMP, NULL)) - 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), - XEXP (XEXP (link, 0), 0))) - break; - } - if (i >= seq_size) - mark_referenced_resources (XEXP (XEXP (link, 0), 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; - } -} - -/* 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 (rtx target, struct resources *res, - rtx *jump_target, int jump_count, - struct resources set, struct resources 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); - - /* If this instruction can throw an exception, then we don't - know where we might end up next. That means that we have to - assume that whatever we have already marked as live really is - live. */ - if (can_throw_internal (insn)) - break; - - 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 (INSN_P (XEXP (PATTERN (insn), 0))) - mark_set_resources (XEXP (PATTERN (insn), 0), res, 0, - MARK_SRC_DEST_CALL); - - /* 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 (JUMP_P (this_jump_insn)) - break; - } - } - - default: - break; - } - - if (JUMP_P (this_jump_insn)) - { - if (jump_count++ < 10) - { - if (any_uncondjump_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 (any_condjump_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, - MARK_SRC_DEST_CALL); - - 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, MARK_SRC_DEST_CALL); - } - else - { - mark_set_resources (insn, &set, 0, MARK_SRC_DEST_CALL); - 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, MARK_SRC_DEST_CALL); - - 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; -} - -/* Given X, a part of an insn, and a pointer to a `struct resource', - RES, indicate which resources are modified by the insn. If - MARK_TYPE is MARK_SRC_DEST_CALL, 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. */ - -void -mark_set_resources (rtx x, struct resources *res, int in_dest, - enum mark_resource_type mark_type) -{ - enum rtx_code code; - int i, j; - unsigned int r; - const 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 CONST_FIXED: - case CONST_VECTOR: - 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 (mark_type == MARK_SRC_DEST_CALL) - { - rtx link; - - res->cc = res->memory = 1; - - IOR_HARD_REG_SET (res->regs, regs_invalidated_by_call); - - 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, - MARK_SRC_DEST); - - /* Check for a REG_SETJMP. If it exists, then we must - assume that this call can clobber any register. */ - if (find_reg_note (x, REG_SETJMP, NULL)) - 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 (mark_type != MARK_SRC_DEST_CALL - && 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, - (mark_type == MARK_SRC_DEST_CALL - || GET_CODE (SET_SRC (x)) != CALL), - mark_type); - - mark_set_resources (SET_SRC (x), res, 0, MARK_SRC_DEST); - return; - - case CLOBBER: - mark_set_resources (XEXP (x, 0), res, 1, MARK_SRC_DEST); - 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, mark_type); - return; - - case POST_INC: - case PRE_INC: - case POST_DEC: - case PRE_DEC: - mark_set_resources (XEXP (x, 0), res, 1, MARK_SRC_DEST); - return; - - case PRE_MODIFY: - case POST_MODIFY: - mark_set_resources (XEXP (x, 0), res, 1, MARK_SRC_DEST); - mark_set_resources (XEXP (XEXP (x, 1), 0), res, 0, MARK_SRC_DEST); - mark_set_resources (XEXP (XEXP (x, 1), 1), res, 0, MARK_SRC_DEST); - return; - - case SIGN_EXTRACT: - case ZERO_EXTRACT: - mark_set_resources (XEXP (x, 0), res, in_dest, MARK_SRC_DEST); - mark_set_resources (XEXP (x, 1), res, 0, MARK_SRC_DEST); - mark_set_resources (XEXP (x, 2), res, 0, MARK_SRC_DEST); - return; - - case MEM: - if (in_dest) - { - res->memory = 1; - res->unch_memory |= MEM_READONLY_P (x); - res->volatil |= MEM_VOLATILE_P (x); - } - - mark_set_resources (XEXP (x, 0), res, 0, MARK_SRC_DEST); - return; - - case SUBREG: - if (in_dest) - { - if (!REG_P (SUBREG_REG (x))) - mark_set_resources (SUBREG_REG (x), res, in_dest, mark_type); - else - { - unsigned int regno = subreg_regno (x); - unsigned int last_regno = regno + subreg_nregs (x); - - gcc_assert (last_regno <= FIRST_PSEUDO_REGISTER); - for (r = regno; r < last_regno; r++) - SET_HARD_REG_BIT (res->regs, r); - } - } - return; - - case REG: - if (in_dest) - { - gcc_assert (HARD_REGISTER_P (x)); - add_to_hard_reg_set (&res->regs, GET_MODE (x), REGNO (x)); - } - 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_set_resources (ASM_OPERANDS_INPUT (x, i), res, in_dest, - MARK_SRC_DEST); - 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, mark_type); - break; - - case 'E': - for (j = 0; j < XVECLEN (x, i); j++) - mark_set_resources (XVECEXP (x, i, j), res, in_dest, mark_type); - break; - } -} - -/* Return TRUE if INSN is a return, possibly with a filled delay slot. */ - -static bool -return_insn_p (const_rtx insn) -{ - if (JUMP_P (insn) && GET_CODE (PATTERN (insn)) == RETURN) - return true; - - if (NONJUMP_INSN_P (insn) && GET_CODE (PATTERN (insn)) == SEQUENCE) - return return_insn_p (XVECEXP (PATTERN (insn), 0, 0)); - - return false; -} - -/* 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. This is only done if the function - init_resource_info () was invoked before we are called. */ - -void -mark_target_live_regs (rtx insns, rtx target, struct resources *res) -{ - int b = -1; - unsigned int i; - struct target_info *tinfo = NULL; - 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; - } - - /* Handle return insn. */ - else if (return_insn_p (target)) - { - *res = end_of_function_needs; - mark_referenced_resources (target, res, 0); - 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. */ - if (target_hash_table != NULL) - { - 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 (BB_HEAD (BASIC_BLOCK (tinfo->block)))) - b = tinfo->block; - } - - if (b == -1) - b = find_basic_block (target, MAX_DELAY_SLOT_LIVE_SEARCH); - - if (target_hash_table != NULL) - { - 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 = XNEW (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 = DF_LR_IN (BASIC_BLOCK (b)); - 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); - - /* Get starting and ending insn, handling the case where each might - be a SEQUENCE. */ - start_insn = (b == ENTRY_BLOCK_PTR->next_bb->index ? - insns : BB_HEAD (BASIC_BLOCK (b))); - stop_insn = target; - - if (NONJUMP_INSN_P (start_insn) - && GET_CODE (PATTERN (start_insn)) == SEQUENCE) - start_insn = XVECEXP (PATTERN (start_insn), 0, 0); - - if (NONJUMP_INSN_P (stop_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; - enum rtx_code code = GET_CODE (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 ((code == INSN || code == JUMP_INSN || code == CALL_INSN) - && INSN_FROM_TARGET_P (insn)) - continue; - - /* If this insn is a USE made by update_block, we care about the - underlying insn. */ - if (code == INSN && GET_CODE (PATTERN (insn)) == USE - && INSN_P (XEXP (PATTERN (insn), 0))) - real_insn = XEXP (PATTERN (insn), 0); - - if (CALL_P (real_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. */ - AND_COMPL_HARD_REG_SET (current_live_regs, - regs_invalidated_by_call); - - /* 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 ((NONJUMP_INSN_P (real_insn) - && GET_CODE (PATTERN (real_insn)) != USE - && GET_CODE (PATTERN (real_insn)) != CLOBBER) - || JUMP_P (real_insn) - || CALL_P (real_insn)) - { - for (link = REG_NOTES (real_insn); link; link = XEXP (link, 1)) - if (REG_NOTE_KIND (link) == REG_DEAD - && REG_P (XEXP (link, 0)) - && REGNO (XEXP (link, 0)) < FIRST_PSEUDO_REGISTER) - add_to_hard_reg_set (&pending_dead_regs, - GET_MODE (XEXP (link, 0)), - REGNO (XEXP (link, 0))); - - note_stores (PATTERN (real_insn), update_live_status, NULL); - - /* 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 - && REG_P (XEXP (link, 0)) - && REGNO (XEXP (link, 0)) < FIRST_PSEUDO_REGISTER) - remove_from_hard_reg_set (¤t_live_regs, - GET_MODE (XEXP (link, 0)), - REGNO (XEXP (link, 0))); - } - - else if (LABEL_P (real_insn)) - { - /* 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 (NOTE_P (real_insn) - && NOTE_KIND (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); - if (tinfo != NULL) - { - 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. We add the live - resources found using the test below to those found until now. */ - - if (jump_insn) - { - struct resources new_resources; - rtx stop_insn = next_active_insn (jump_insn); - - mark_target_live_regs (insns, 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, MARK_SRC_DEST_CALL); - } - - IOR_HARD_REG_SET (res->regs, new_resources.regs); - } - - if (tinfo != NULL) - { - COPY_HARD_REG_SET (tinfo->live_regs, res->regs); - } -} - -/* Initialize the resources required by mark_target_live_regs (). - This should be invoked before the first call to mark_target_live_regs. */ - -void -init_resource_info (rtx epilogue_insn) -{ - int i; - - /* 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 nonzero. 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 - if (! EXIT_IGNORE_STACK - || current_function_sp_is_unchanging) - 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 (crtl->return_rtx != 0) - mark_referenced_resources (crtl->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, - MARK_SRC_DEST_CALL); - if (return_insn_p (epilogue_insn)) - break; - } - - /* Allocate and initialize the tables used by mark_target_live_regs. */ - target_hash_table = XCNEWVEC (struct target_info *, TARGET_HASH_PRIME); - bb_ticks = XCNEWVEC (int, last_basic_block); -} - -/* Free up the resources allocated to mark_target_live_regs (). This - should be invoked after the last call to mark_target_live_regs (). */ - -void -free_resource_info (void) -{ - if (target_hash_table != NULL) - { - int i; - - for (i = 0; i < TARGET_HASH_PRIME; ++i) - { - struct target_info *ti = target_hash_table[i]; - - while (ti) - { - struct target_info *next = ti->next; - free (ti); - ti = next; - } - } - - free (target_hash_table); - target_hash_table = NULL; - } - - if (bb_ticks != NULL) - { - free (bb_ticks); - bb_ticks = NULL; - } -} - -/* Clear any hashed information that we have stored for INSN. */ - -void -clear_hashed_info_for_insn (rtx insn) -{ - struct target_info *tinfo; - - if (target_hash_table != NULL) - { - 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; - } -} - -/* Increment the tick count for the basic block that contains INSN. */ - -void -incr_ticks_for_insn (rtx insn) -{ - int b = find_basic_block (insn, MAX_DELAY_SLOT_LIVE_SEARCH); - - if (b != -1) - bb_ticks[b]++; -} - -/* Add TRIAL to the set of resources used at the end of the current - function. */ -void -mark_end_of_function_resources (rtx trial, int include_delayed_effects) -{ - mark_referenced_resources (trial, &end_of_function_needs, - include_delayed_effects); -} |