diff options
Diffstat (limited to 'contrib/gcc/flow.c')
-rw-r--r-- | contrib/gcc/flow.c | 2517 |
1 files changed, 1922 insertions, 595 deletions
diff --git a/contrib/gcc/flow.c b/contrib/gcc/flow.c index 1e4ff88..43ea11d 100644 --- a/contrib/gcc/flow.c +++ b/contrib/gcc/flow.c @@ -1,5 +1,5 @@ /* Data flow analysis for GNU compiler. - Copyright (C) 1987, 88, 92, 93, 94, 1995 Free Software Foundation, Inc. + Copyright (C) 1987, 88, 92-97, 1998 Free Software Foundation, Inc. This file is part of GNU CC. @@ -108,8 +108,8 @@ Boston, MA 02111-1307, USA. */ register usage: reg_n_refs, reg_n_deaths, reg_n_sets, reg_live_length, reg_n_calls_crosses and reg_basic_block. */ -#include <stdio.h> #include "config.h" +#include "system.h" #include "rtl.h" #include "basic-block.h" #include "insn-config.h" @@ -117,11 +117,20 @@ Boston, MA 02111-1307, USA. */ #include "hard-reg-set.h" #include "flags.h" #include "output.h" +#include "except.h" +#include "toplev.h" #include "obstack.h" #define obstack_chunk_alloc xmalloc #define obstack_chunk_free free +/* The contents of the current function definition are allocated + in this obstack, and all are freed at the end of the function. + For top-level functions, this is temporary_obstack. + Separate obstacks are made for nested functions. */ + +extern struct obstack *function_obstack; + /* List of labels that must never be deleted. */ extern rtx forced_labels; @@ -139,7 +148,7 @@ static int max_uid_for_flow; This is set up by find_basic_blocks and used there and in life_analysis, and then freed. */ -static int *uid_block_number; +int *uid_block_number; /* INSN_VOLATILE (insn) is 1 if the insn refers to anything volatile. */ @@ -154,7 +163,8 @@ int n_basic_blocks; int max_regno; -/* Maximum number of SCRATCH rtx's used in any basic block of this function. */ +/* Maximum number of SCRATCH rtx's used in any basic block of this + function. */ int max_scratch; @@ -162,56 +172,13 @@ int max_scratch; static int num_scratch; -/* Indexed by n, gives number of basic block that (REG n) is used in. - If the value is REG_BLOCK_GLOBAL (-2), - it means (REG n) is used in more than one basic block. - REG_BLOCK_UNKNOWN (-1) means it hasn't been seen yet so we don't know. - This information remains valid for the rest of the compilation - of the current function; it is used to control register allocation. */ - -int *reg_basic_block; - -/* Indexed by n, gives number of times (REG n) is used or set, each - weighted by its loop-depth. - This information remains valid for the rest of the compilation - of the current function; it is used to control register allocation. */ - -int *reg_n_refs; - -/* Indexed by N; says whether a pseudo register N was ever used - within a SUBREG that changes the size of the reg. Some machines prohibit - such objects to be in certain (usually floating-point) registers. */ - -char *reg_changes_size; - -/* Indexed by N, gives number of places register N dies. - This information remains valid for the rest of the compilation - of the current function; it is used to control register allocation. */ - -short *reg_n_deaths; - -/* Indexed by N, gives 1 if that reg is live across any CALL_INSNs. - This information remains valid for the rest of the compilation - of the current function; it is used to control register allocation. */ - -int *reg_n_calls_crossed; - -/* Total number of instructions at which (REG n) is live. - The larger this is, the less priority (REG n) gets for - allocation in a real register. - This information remains valid for the rest of the compilation - of the current function; it is used to control register allocation. +/* Indexed by n, giving various register information */ - local-alloc.c may alter this number to change the priority. +varray_type reg_n_info; - Negative values are special. - -1 is used to mark a pseudo reg which has a constant or memory equivalent - and is used infrequently enough that it should not get a hard register. - -2 is used to mark a pseudo reg for a parameter, when a frame pointer - is not required. global.c makes an allocno for this but does - not try to assign a hard register to it. */ +/* Size of the reg_n_info table. */ -int *reg_live_length; +unsigned int reg_n_max; /* Element N is the next insn that uses (hard or pseudo) register number N within the current basic block; or zero, if there is no such insn. @@ -235,6 +202,11 @@ rtx *basic_block_head; rtx *basic_block_end; +/* Element N indicates whether basic block N can be reached through a + computed jump. */ + +char *basic_block_computed_jump_target; + /* Element N is a regset describing the registers live at the start of basic block N. This info lasts until we finish compiling the function. */ @@ -286,12 +258,9 @@ static rtx last_mem_set; static HARD_REG_SET elim_reg_set; /* Forward declarations */ -static void find_basic_blocks PROTO((rtx, rtx)); -static int uses_reg_or_mem PROTO((rtx)); +static void find_basic_blocks_1 PROTO((rtx, rtx, int)); static void mark_label_ref PROTO((rtx, rtx, int)); -static void life_analysis PROTO((rtx, int)); -void allocate_for_life_analysis PROTO((void)); -static void init_regset_vector PROTO((regset *, regset, int, int)); +static void life_analysis_1 PROTO((rtx, int)); static void propagate_block PROTO((regset, rtx, rtx, int, regset, int)); static rtx flow_delete_insn PROTO((rtx)); @@ -301,53 +270,60 @@ static void mark_set_regs PROTO((regset, regset, rtx, rtx, regset)); static void mark_set_1 PROTO((regset, regset, rtx, rtx, regset)); +#ifdef AUTO_INC_DEC static void find_auto_inc PROTO((regset, rtx, rtx)); -static void mark_used_regs PROTO((regset, regset, rtx, int, rtx)); static int try_pre_increment_1 PROTO((rtx)); static int try_pre_increment PROTO((rtx, rtx, HOST_WIDE_INT)); -static rtx find_use_as_address PROTO((rtx, rtx, HOST_WIDE_INT)); +#endif +static void mark_used_regs PROTO((regset, regset, rtx, int, rtx)); void dump_flow_info PROTO((FILE *)); +static void add_pred_succ PROTO ((int, int, int_list_ptr *, + int_list_ptr *, int *, int *)); +static int_list_ptr alloc_int_list_node PROTO ((int_list_block **)); +static int_list_ptr add_int_list_node PROTO ((int_list_block **, + int_list **, int)); +static void init_regset_vector PROTO ((regset *, int, + struct obstack *)); +static void count_reg_sets_1 PROTO ((rtx)); +static void count_reg_sets PROTO ((rtx)); +static void count_reg_references PROTO ((rtx)); -/* Find basic blocks of the current function and perform data flow analysis. +/* Find basic blocks of the current function. F is the first insn of the function and NREGS the number of register numbers - in use. */ + in use. + LIVE_REACHABLE_P is non-zero if the caller needs all live blocks to + be reachable. This turns on a kludge that causes the control flow + information to be inaccurate and not suitable for passes like GCSE. */ void -flow_analysis (f, nregs, file) +find_basic_blocks (f, nregs, file, live_reachable_p) rtx f; int nregs; FILE *file; + int live_reachable_p; { register rtx insn; register int i; rtx nonlocal_label_list = nonlocal_label_rtx_list (); - -#ifdef ELIMINABLE_REGS - static struct {int from, to; } eliminables[] = ELIMINABLE_REGS; -#endif - - /* Record which registers will be eliminated. We use this in - mark_used_regs. */ - - CLEAR_HARD_REG_SET (elim_reg_set); - -#ifdef ELIMINABLE_REGS - for (i = 0; i < sizeof eliminables / sizeof eliminables[0]; i++) - SET_HARD_REG_BIT (elim_reg_set, eliminables[i].from); -#else - SET_HARD_REG_BIT (elim_reg_set, FRAME_POINTER_REGNUM); -#endif + int in_libcall_block = 0; /* Count the basic blocks. Also find maximum insn uid value used. */ { register RTX_CODE prev_code = JUMP_INSN; register RTX_CODE code; + int eh_region = 0; max_uid_for_flow = 0; for (insn = f, i = 0; insn; insn = NEXT_INSN (insn)) { + + /* Track when we are inside in LIBCALL block. */ + if (GET_RTX_CLASS (GET_CODE (insn)) == 'i' + && find_reg_note (insn, REG_LIBCALL, NULL_RTX)) + in_libcall_block = 1; + code = GET_CODE (insn); if (INSN_UID (insn) > max_uid_for_flow) max_uid_for_flow = INSN_UID (insn); @@ -355,72 +331,98 @@ flow_analysis (f, nregs, file) || (GET_RTX_CLASS (code) == 'i' && (prev_code == JUMP_INSN || (prev_code == CALL_INSN - && nonlocal_label_list != 0) + && (nonlocal_label_list != 0 || eh_region) + && ! in_libcall_block) || prev_code == BARRIER))) i++; + + if (code == CALL_INSN && find_reg_note (insn, REG_RETVAL, NULL_RTX)) + code = INSN; + if (code != NOTE) prev_code = code; + else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG) + ++eh_region; + else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_END) + --eh_region; + + if (GET_RTX_CLASS (GET_CODE (insn)) == 'i' + && find_reg_note (insn, REG_RETVAL, NULL_RTX)) + in_libcall_block = 0; } } + n_basic_blocks = i; + #ifdef AUTO_INC_DEC - /* Leave space for insns we make in some cases for auto-inc. These cases - are rare, so we don't need too much space. */ + /* Leave space for insns life_analysis makes in some cases for auto-inc. + These cases are rare, so we don't need too much space. */ max_uid_for_flow += max_uid_for_flow / 10; #endif /* Allocate some tables that last till end of compiling this function and some needed only in find_basic_blocks and life_analysis. */ - n_basic_blocks = i; - basic_block_head = (rtx *) oballoc (n_basic_blocks * sizeof (rtx)); - basic_block_end = (rtx *) oballoc (n_basic_blocks * sizeof (rtx)); - basic_block_drops_in = (char *) alloca (n_basic_blocks); - basic_block_loop_depth = (short *) alloca (n_basic_blocks * sizeof (short)); + basic_block_head = (rtx *) xmalloc (n_basic_blocks * sizeof (rtx)); + basic_block_end = (rtx *) xmalloc (n_basic_blocks * sizeof (rtx)); + basic_block_drops_in = (char *) xmalloc (n_basic_blocks); + basic_block_computed_jump_target = (char *) oballoc (n_basic_blocks); + basic_block_loop_depth = (short *) xmalloc (n_basic_blocks * sizeof (short)); uid_block_number - = (int *) alloca ((max_uid_for_flow + 1) * sizeof (int)); - uid_volatile = (char *) alloca (max_uid_for_flow + 1); + = (int *) xmalloc ((max_uid_for_flow + 1) * sizeof (int)); + uid_volatile = (char *) xmalloc (max_uid_for_flow + 1); bzero (uid_volatile, max_uid_for_flow + 1); - find_basic_blocks (f, nonlocal_label_list); - life_analysis (f, nregs); - if (file) - dump_flow_info (file); - - basic_block_drops_in = 0; - uid_block_number = 0; - basic_block_loop_depth = 0; + find_basic_blocks_1 (f, nonlocal_label_list, live_reachable_p); } - + /* Find all basic blocks of the function whose first insn is F. Store the correct data in the tables that describe the basic blocks, set up the chains of references for each CODE_LABEL, and delete any entire basic blocks that cannot be reached. - NONLOCAL_LABEL_LIST is the same local variable from flow_analysis. */ + NONLOCAL_LABEL_LIST is a list of non-local labels in the function. + Blocks that are otherwise unreachable may be reachable with a non-local + goto. + LIVE_REACHABLE_P is non-zero if the caller needs all live blocks to + be reachable. This turns on a kludge that causes the control flow + information to be inaccurate and not suitable for passes like GCSE. */ static void -find_basic_blocks (f, nonlocal_label_list) +find_basic_blocks_1 (f, nonlocal_label_list, live_reachable_p) rtx f, nonlocal_label_list; + int live_reachable_p; { register rtx insn; register int i; register char *block_live = (char *) alloca (n_basic_blocks); register char *block_marked = (char *) alloca (n_basic_blocks); + /* An array of CODE_LABELs, indexed by UID for the start of the active + EH handler for each insn in F. */ + int *active_eh_region; + int *nested_eh_region; /* List of label_refs to all labels whose addresses are taken and used as data. */ rtx label_value_list; - rtx x, note; + rtx x, note, eh_note; enum rtx_code prev_code, code; int depth, pass; + int in_libcall_block = 0; + int deleted_handler = 0; pass = 1; + active_eh_region = (int *) alloca ((max_uid_for_flow + 1) * sizeof (int)); + nested_eh_region = (int *) alloca ((max_label_num () + 1) * sizeof (int)); restart: label_value_list = 0; block_live_static = block_live; bzero (block_live, n_basic_blocks); bzero (block_marked, n_basic_blocks); + bzero (basic_block_computed_jump_target, n_basic_blocks); + bzero ((char *) active_eh_region, (max_uid_for_flow + 1) * sizeof (int)); + bzero ((char *) nested_eh_region, (max_label_num () + 1) * sizeof (int)); + current_function_has_computed_jump = 0; /* Initialize with just block 0 reachable and no blocks marked. */ if (n_basic_blocks > 0) @@ -431,9 +433,15 @@ find_basic_blocks (f, nonlocal_label_list) the block it is in. Also mark as reachable any blocks headed by labels that must not be deleted. */ - for (insn = f, i = -1, prev_code = JUMP_INSN, depth = 1; + for (eh_note = NULL_RTX, insn = f, i = -1, prev_code = JUMP_INSN, depth = 1; insn; insn = NEXT_INSN (insn)) { + + /* Track when we are inside in LIBCALL block. */ + if (GET_RTX_CLASS (GET_CODE (insn)) == 'i' + && find_reg_note (insn, REG_LIBCALL, NULL_RTX)) + in_libcall_block = 1; + code = GET_CODE (insn); if (code == NOTE) { @@ -448,7 +456,8 @@ find_basic_blocks (f, nonlocal_label_list) || (GET_RTX_CLASS (code) == 'i' && (prev_code == JUMP_INSN || (prev_code == CALL_INSN - && nonlocal_label_list != 0) + && (nonlocal_label_list != 0 || eh_note) + && ! in_libcall_block) || prev_code == BARRIER))) { basic_block_head[++i] = insn; @@ -476,14 +485,45 @@ find_basic_blocks (f, nonlocal_label_list) /* Make a list of all labels referred to other than by jumps. */ for (note = REG_NOTES (insn); note; note = XEXP (note, 1)) if (REG_NOTE_KIND (note) == REG_LABEL) - label_value_list = gen_rtx (EXPR_LIST, VOIDmode, XEXP (note, 0), - label_value_list); + label_value_list = gen_rtx_EXPR_LIST (VOIDmode, XEXP (note, 0), + label_value_list); } + /* Keep a lifo list of the currently active exception notes. */ + if (GET_CODE (insn) == NOTE) + { + if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG) + { + if (eh_note) + nested_eh_region [NOTE_BLOCK_NUMBER (insn)] = + NOTE_BLOCK_NUMBER (XEXP (eh_note, 0)); + else + nested_eh_region [NOTE_BLOCK_NUMBER (insn)] = 0; + eh_note = gen_rtx_EXPR_LIST (VOIDmode, + insn, eh_note); + } + else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_END) + eh_note = XEXP (eh_note, 1); + } + /* If we encounter a CALL_INSN, note which exception handler it + might pass control to. + + If doing asynchronous exceptions, record the active EH handler + for every insn, since most insns can throw. */ + else if (eh_note + && (asynchronous_exceptions + || (GET_CODE (insn) == CALL_INSN + && ! in_libcall_block))) + active_eh_region[INSN_UID (insn)] = + NOTE_BLOCK_NUMBER (XEXP (eh_note, 0)); BLOCK_NUM (insn) = i; if (code != NOTE) prev_code = code; + + if (GET_RTX_CLASS (GET_CODE (insn)) == 'i' + && find_reg_note (insn, REG_RETVAL, NULL_RTX)) + in_libcall_block = 0; } /* During the second pass, `n_basic_blocks' is only an upper bound. @@ -493,17 +533,6 @@ find_basic_blocks (f, nonlocal_label_list) abort (); n_basic_blocks = i + 1; - /* Don't delete the labels (in this function) - that are referenced by non-jump instructions. */ - - for (x = label_value_list; x; x = XEXP (x, 1)) - if (! LABEL_REF_NONLOCAL_P (x)) - block_live[BLOCK_NUM (XEXP (x, 0))] = 1; - - for (x = forced_labels; x; x = XEXP (x, 1)) - if (! LABEL_REF_NONLOCAL_P (x)) - block_live[BLOCK_NUM (XEXP (x, 0))] = 1; - /* Record which basic blocks control can drop in to. */ for (i = 0; i < n_basic_blocks; i++) @@ -524,76 +553,6 @@ find_basic_blocks (f, nonlocal_label_list) int something_marked = 1; int deleted; - /* Find all indirect jump insns and mark them as possibly jumping to all - the labels whose addresses are explicitly used. This is because, - when there are computed gotos, we can't tell which labels they jump - to, of all the possibilities. - - Tablejumps and casesi insns are OK and we can recognize them by - a (use (label_ref)). */ - - for (insn = f; insn; insn = NEXT_INSN (insn)) - if (GET_CODE (insn) == JUMP_INSN) - { - rtx pat = PATTERN (insn); - int computed_jump = 0; - - if (GET_CODE (pat) == PARALLEL) - { - int len = XVECLEN (pat, 0); - int has_use_labelref = 0; - - for (i = len - 1; i >= 0; i--) - if (GET_CODE (XVECEXP (pat, 0, i)) == USE - && (GET_CODE (XEXP (XVECEXP (pat, 0, i), 0)) - == LABEL_REF)) - has_use_labelref = 1; - - if (! has_use_labelref) - for (i = len - 1; i >= 0; i--) - if (GET_CODE (XVECEXP (pat, 0, i)) == SET - && SET_DEST (XVECEXP (pat, 0, i)) == pc_rtx - && uses_reg_or_mem (SET_SRC (XVECEXP (pat, 0, i)))) - computed_jump = 1; - } - else if (GET_CODE (pat) == SET - && SET_DEST (pat) == pc_rtx - && uses_reg_or_mem (SET_SRC (pat))) - computed_jump = 1; - - if (computed_jump) - { - for (x = label_value_list; x; x = XEXP (x, 1)) - mark_label_ref (gen_rtx (LABEL_REF, VOIDmode, XEXP (x, 0)), - insn, 0); - - for (x = forced_labels; x; x = XEXP (x, 1)) - mark_label_ref (gen_rtx (LABEL_REF, VOIDmode, XEXP (x, 0)), - insn, 0); - } - } - - /* Find all call insns and mark them as possibly jumping - to all the nonlocal goto handler labels. */ - - for (insn = f; insn; insn = NEXT_INSN (insn)) - if (GET_CODE (insn) == CALL_INSN) - { - for (x = nonlocal_label_list; x; x = XEXP (x, 1)) - mark_label_ref (gen_rtx (LABEL_REF, VOIDmode, XEXP (x, 0)), - insn, 0); - - /* ??? This could be made smarter: - in some cases it's possible to tell that certain - calls will not do a nonlocal goto. - - For example, if the nested functions that do the - nonlocal gotos do not have their addresses taken, then - only calls to those functions or to other nested - functions that use them could possibly do nonlocal - gotos. */ - } - /* Pass over all blocks, marking each block that is reachable and has not yet been marked. Keep doing this until, in one pass, no blocks have been marked. @@ -613,22 +572,144 @@ find_basic_blocks (f, nonlocal_label_list) insn = basic_block_end[i]; if (GET_CODE (insn) == JUMP_INSN) mark_label_ref (PATTERN (insn), insn, 0); + + /* If we have any forced labels, mark them as potentially + reachable from this block. */ + for (x = forced_labels; x; x = XEXP (x, 1)) + if (! LABEL_REF_NONLOCAL_P (x)) + mark_label_ref (gen_rtx_LABEL_REF (VOIDmode, XEXP (x, 0)), + insn, 0); + + /* Now scan the insns for this block, we may need to make + edges for some of them to various non-obvious locations + (exception handlers, nonlocal labels, etc). */ + for (insn = basic_block_head[i]; + insn != NEXT_INSN (basic_block_end[i]); + insn = NEXT_INSN (insn)) + { + if (GET_RTX_CLASS (GET_CODE (insn)) == 'i') + { + + /* References to labels in non-jumping insns have + REG_LABEL notes attached to them. + + This can happen for computed gotos; we don't care + about them here since the values are also on the + label_value_list and will be marked live if we find + a live computed goto. + + This can also happen when we take the address of + a label to pass as an argument to __throw. Note + throw only uses the value to determine what handler + should be called -- ie the label is not used as + a jump target, it just marks regions in the code. + + In theory we should be able to ignore the REG_LABEL + notes, but we have to make sure that the label and + associated insns aren't marked dead, so we make + the block in question live and create an edge from + this insn to the label. This is not strictly + correct, but it is close enough for now. */ + for (note = REG_NOTES (insn); + note; + note = XEXP (note, 1)) + { + if (REG_NOTE_KIND (note) == REG_LABEL) + { + x = XEXP (note, 0); + block_live[BLOCK_NUM (x)] = 1; + mark_label_ref (gen_rtx_LABEL_REF (VOIDmode, x), + insn, 0); + } + } + + /* If this is a computed jump, then mark it as + reaching everything on the label_value_list + and forced_labels list. */ + if (computed_jump_p (insn)) + { + current_function_has_computed_jump = 1; + for (x = label_value_list; x; x = XEXP (x, 1)) + { + int b = BLOCK_NUM (XEXP (x, 0)); + basic_block_computed_jump_target[b] = 1; + mark_label_ref (gen_rtx_LABEL_REF (VOIDmode, + XEXP (x, 0)), + insn, 0); + } + + for (x = forced_labels; x; x = XEXP (x, 1)) + { + int b = BLOCK_NUM (XEXP (x, 0)); + basic_block_computed_jump_target[b] = 1; + mark_label_ref (gen_rtx_LABEL_REF (VOIDmode, + XEXP (x, 0)), + insn, 0); + } + } + + /* If this is a CALL_INSN, then mark it as reaching + the active EH handler for this CALL_INSN. If + we're handling asynchronous exceptions mark every + insn as reaching the active EH handler. + + Also mark the CALL_INSN as reaching any nonlocal + goto sites. */ + else if (asynchronous_exceptions + || (GET_CODE (insn) == CALL_INSN + && ! find_reg_note (insn, REG_RETVAL, + NULL_RTX))) + { + if (active_eh_region[INSN_UID (insn)]) + { + int region; + handler_info *ptr; + region = active_eh_region[INSN_UID (insn)]; + for ( ; region; + region = nested_eh_region[region]) + { + ptr = get_first_handler (region); + for ( ; ptr ; ptr = ptr->next) + mark_label_ref (gen_rtx_LABEL_REF + (VOIDmode, ptr->handler_label), insn, 0); + } + } + if (!asynchronous_exceptions) + { + for (x = nonlocal_label_list; + x; + x = XEXP (x, 1)) + mark_label_ref (gen_rtx_LABEL_REF (VOIDmode, + XEXP (x, 0)), + insn, 0); + } + /* ??? This could be made smarter: + in some cases it's possible to tell that + certain calls will not do a nonlocal goto. + + For example, if the nested functions that + do the nonlocal gotos do not have their + addresses taken, then only calls to those + functions or to other nested functions that + use them could possibly do nonlocal gotos. */ + } + } + } } } - /* ??? See if we have a "live" basic block that is not reachable. - This can happen if it is headed by a label that is preserved or - in one of the label lists, but no call or computed jump is in - the loop. It's not clear if we can delete the block or not, - but don't for now. However, we will mess up register status if - it remains unreachable, so add a fake reachability from the - previous block. */ + /* This should never happen. If it does that means we've computed an + incorrect flow graph, which can lead to aborts/crashes later in the + compiler or incorrect code generation. + We used to try and continue here, but that's just asking for trouble + later during the compile or at runtime. It's easier to debug the + problem here than later! */ for (i = 1; i < n_basic_blocks; i++) if (block_live[i] && ! basic_block_drops_in[i] && GET_CODE (basic_block_head[i]) == CODE_LABEL && LABEL_REFS (basic_block_head[i]) == basic_block_head[i]) - basic_block_drops_in[i] = 1; + abort (); /* Now delete the code for any basic blocks that can't be reached. They can occur because jump_optimize does not recognize @@ -672,6 +753,37 @@ find_basic_blocks (f, nonlocal_label_list) /* Turn the head into a deleted insn note. */ if (GET_CODE (insn) == BARRIER) abort (); + + /* If the head of this block is a CODE_LABEL, then it might + be the label for an exception handler which can't be + reached. + + We need to remove the label from the exception_handler_label + list and remove the associated NOTE_EH_REGION_BEG and + NOTE_EH_REGION_END notes. */ + if (GET_CODE (insn) == CODE_LABEL) + { + rtx x, *prev = &exception_handler_labels; + + for (x = exception_handler_labels; x; x = XEXP (x, 1)) + { + if (XEXP (x, 0) == insn) + { + /* Found a match, splice this label out of the + EH label list. */ + *prev = XEXP (x, 1); + XEXP (x, 1) = NULL_RTX; + XEXP (x, 0) = NULL_RTX; + + /* Remove the handler from all regions */ + remove_handler (insn); + deleted_handler = 1; + break; + } + prev = &XEXP (x, 1); + } + } + PUT_CODE (insn, NOTE); NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; NOTE_SOURCE_FILE (insn) = 0; @@ -716,6 +828,14 @@ find_basic_blocks (f, nonlocal_label_list) && INSN_UID (label) != 0 && BLOCK_NUM (label) == j) { + int k; + + /* The deleted blocks still show up in the cfg, + so we must set basic_block_drops_in for blocks + I to J inclusive to keep the cfg accurate. */ + for (k = i; k <= j; k++) + basic_block_drops_in[k] = 1; + PUT_CODE (insn, NOTE); NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; NOTE_SOURCE_FILE (insn) = 0; @@ -727,6 +847,24 @@ find_basic_blocks (f, nonlocal_label_list) } } } + /* If we deleted an exception handler, we may have EH region + begin/end blocks to remove as well. */ + if (deleted_handler) + for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) + if (GET_CODE (insn) == NOTE) + { + if ((NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG) || + (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_END)) + { + int num = CODE_LABEL_NUMBER (insn); + /* A NULL handler indicates a region is no longer needed */ + if (get_first_handler (num) == NULL) + { + NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; + NOTE_SOURCE_FILE (insn) = 0; + } + } + } /* There are pathological cases where one function calling hundreds of nested inline functions can generate lots and lots of unreachable @@ -756,41 +894,27 @@ find_basic_blocks (f, nonlocal_label_list) } } } - -/* Subroutines of find_basic_blocks. */ -/* Return 1 if X contain a REG or MEM that is not in the constant pool. */ +/* Record INSN's block number as BB. */ -static int -uses_reg_or_mem (x) - rtx x; +void +set_block_num (insn, bb) + rtx insn; + int bb; { - enum rtx_code code = GET_CODE (x); - int i, j; - char *fmt; - - if (code == REG - || (code == MEM - && ! (GET_CODE (XEXP (x, 0)) == SYMBOL_REF - && CONSTANT_POOL_ADDRESS_P (XEXP (x, 0))))) - return 1; - - fmt = GET_RTX_FORMAT (code); - for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + if (INSN_UID (insn) >= max_uid_for_flow) { - if (fmt[i] == 'e' - && uses_reg_or_mem (XEXP (x, i))) - return 1; - - if (fmt[i] == 'E') - for (j = 0; j < XVECLEN (x, i); j++) - if (uses_reg_or_mem (XVECEXP (x, i, j))) - return 1; + /* Add one-eighth the size so we don't keep calling xrealloc. */ + max_uid_for_flow = INSN_UID (insn) + (INSN_UID (insn) + 7) / 8; + uid_block_number = (int *) + xrealloc (uid_block_number, (max_uid_for_flow + 1) * sizeof (int)); } - - return 0; + BLOCK_NUM (insn) = bb; } + +/* Subroutines of find_basic_blocks. */ + /* Check expression X for label references; if one is found, add INSN to the label's chain of references. @@ -866,6 +990,80 @@ flow_delete_insn (insn) return NEXT_INSN (insn); } +/* Perform data flow analysis. + F is the first insn of the function and NREGS the number of register numbers + in use. */ + +void +life_analysis (f, nregs, file) + rtx f; + int nregs; + FILE *file; +{ +#ifdef ELIMINABLE_REGS + register size_t i; + static struct {int from, to; } eliminables[] = ELIMINABLE_REGS; +#endif + + /* Record which registers will be eliminated. We use this in + mark_used_regs. */ + + CLEAR_HARD_REG_SET (elim_reg_set); + +#ifdef ELIMINABLE_REGS + for (i = 0; i < sizeof eliminables / sizeof eliminables[0]; i++) + SET_HARD_REG_BIT (elim_reg_set, eliminables[i].from); +#else + SET_HARD_REG_BIT (elim_reg_set, FRAME_POINTER_REGNUM); +#endif + + life_analysis_1 (f, nregs); + if (file) + dump_flow_info (file); + + free_basic_block_vars (1); +} + +/* Free the variables allocated by find_basic_blocks. + + KEEP_HEAD_END_P is non-zero if basic_block_head and basic_block_end + are not to be freed. */ + +void +free_basic_block_vars (keep_head_end_p) + int keep_head_end_p; +{ + if (basic_block_drops_in) + { + free (basic_block_drops_in); + /* Tell dump_flow_info this isn't available anymore. */ + basic_block_drops_in = 0; + } + if (basic_block_loop_depth) + { + free (basic_block_loop_depth); + basic_block_loop_depth = 0; + } + if (uid_block_number) + { + free (uid_block_number); + uid_block_number = 0; + } + if (uid_volatile) + { + free (uid_volatile); + uid_volatile = 0; + } + + if (! keep_head_end_p && basic_block_head) + { + free (basic_block_head); + basic_block_head = 0; + free (basic_block_end); + basic_block_end = 0; + } +} + /* Determine which registers are live at the start of each basic block of the function whose first insn is F. NREGS is the number of registers used in F. @@ -874,11 +1072,10 @@ flow_delete_insn (insn) regset_size and regset_bytes are also set here. */ static void -life_analysis (f, nregs) +life_analysis_1 (f, nregs) rtx f; int nregs; { - register regset tem; int first_pass; int changed; /* For each basic block, a bitmask of regs @@ -924,28 +1121,20 @@ life_analysis (f, nregs) if there isn't enough space. Don't use oballoc since we may need to allocate other things during this function on the temporary obstack. */ - tem = (regset) obstack_alloc (&flow_obstack, n_basic_blocks * regset_bytes); - bzero ((char *) tem, n_basic_blocks * regset_bytes); - init_regset_vector (basic_block_live_at_end, tem, - n_basic_blocks, regset_bytes); + init_regset_vector (basic_block_live_at_end, n_basic_blocks, &flow_obstack); basic_block_new_live_at_end = (regset *) alloca (n_basic_blocks * sizeof (regset)); - tem = (regset) obstack_alloc (&flow_obstack, n_basic_blocks * regset_bytes); - bzero ((char *) tem, n_basic_blocks * regset_bytes); - init_regset_vector (basic_block_new_live_at_end, tem, - n_basic_blocks, regset_bytes); + init_regset_vector (basic_block_new_live_at_end, n_basic_blocks, + &flow_obstack); basic_block_significant = (regset *) alloca (n_basic_blocks * sizeof (regset)); - tem = (regset) obstack_alloc (&flow_obstack, n_basic_blocks * regset_bytes); - bzero ((char *) tem, n_basic_blocks * regset_bytes); - init_regset_vector (basic_block_significant, tem, - n_basic_blocks, regset_bytes); + init_regset_vector (basic_block_significant, n_basic_blocks, &flow_obstack); /* Record which insns refer to any volatile memory or for any reason can't be deleted just because they are dead stores. - Also, delete any insns that copy a register to itself. */ + Also, delete any insns that copy a register to itself. */ for (insn = f; insn; insn = NEXT_INSN (insn)) { @@ -958,8 +1147,25 @@ life_analysis (f, nregs) if (GET_CODE (PATTERN (insn)) == SET && GET_CODE (SET_DEST (PATTERN (insn))) == REG && GET_CODE (SET_SRC (PATTERN (insn))) == REG - && REGNO (SET_DEST (PATTERN (insn))) == - REGNO (SET_SRC (PATTERN (insn))) + && (REGNO (SET_DEST (PATTERN (insn))) + == REGNO (SET_SRC (PATTERN (insn)))) + /* Insns carrying these notes are useful later on. */ + && ! find_reg_note (insn, REG_EQUAL, NULL_RTX)) + { + PUT_CODE (insn, NOTE); + NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; + NOTE_SOURCE_FILE (insn) = 0; + } + /* Delete (in effect) any obvious no-op moves. */ + else if (GET_CODE (PATTERN (insn)) == SET + && GET_CODE (SET_DEST (PATTERN (insn))) == SUBREG + && GET_CODE (SUBREG_REG (SET_DEST (PATTERN (insn)))) == REG + && GET_CODE (SET_SRC (PATTERN (insn))) == SUBREG + && GET_CODE (SUBREG_REG (SET_SRC (PATTERN (insn)))) == REG + && (REGNO (SUBREG_REG (SET_DEST (PATTERN (insn)))) + == REGNO (SUBREG_REG (SET_SRC (PATTERN (insn))))) + && SUBREG_WORD (SET_DEST (PATTERN (insn))) == + SUBREG_WORD (SET_SRC (PATTERN (insn))) /* Insns carrying these notes are useful later on. */ && ! find_reg_note (insn, REG_EQUAL, NULL_RTX)) { @@ -1020,17 +1226,17 @@ life_analysis (f, nregs) if (n_basic_blocks > 0) #ifdef EXIT_IGNORE_STACK if (! EXIT_IGNORE_STACK - || (! FRAME_POINTER_REQUIRED && flag_omit_frame_pointer)) + || (! FRAME_POINTER_REQUIRED + && ! current_function_calls_alloca + && flag_omit_frame_pointer)) #endif { /* If exiting needs the right stack value, consider the stack pointer live at the end of the function. */ - basic_block_live_at_end[n_basic_blocks - 1] - [STACK_POINTER_REGNUM / REGSET_ELT_BITS] - |= (REGSET_ELT_TYPE) 1 << (STACK_POINTER_REGNUM % REGSET_ELT_BITS); - basic_block_new_live_at_end[n_basic_blocks - 1] - [STACK_POINTER_REGNUM / REGSET_ELT_BITS] - |= (REGSET_ELT_TYPE) 1 << (STACK_POINTER_REGNUM % REGSET_ELT_BITS); + SET_REGNO_REG_SET (basic_block_live_at_end[n_basic_blocks - 1], + STACK_POINTER_REGNUM); + SET_REGNO_REG_SET (basic_block_new_live_at_end[n_basic_blocks - 1], + STACK_POINTER_REGNUM); } /* Mark the frame pointer is needed at the end of the function. If @@ -1039,38 +1245,33 @@ life_analysis (f, nregs) if (n_basic_blocks > 0) { - basic_block_live_at_end[n_basic_blocks - 1] - [FRAME_POINTER_REGNUM / REGSET_ELT_BITS] - |= (REGSET_ELT_TYPE) 1 << (FRAME_POINTER_REGNUM % REGSET_ELT_BITS); - basic_block_new_live_at_end[n_basic_blocks - 1] - [FRAME_POINTER_REGNUM / REGSET_ELT_BITS] - |= (REGSET_ELT_TYPE) 1 << (FRAME_POINTER_REGNUM % REGSET_ELT_BITS); + SET_REGNO_REG_SET (basic_block_live_at_end[n_basic_blocks - 1], + FRAME_POINTER_REGNUM); + SET_REGNO_REG_SET (basic_block_new_live_at_end[n_basic_blocks - 1], + FRAME_POINTER_REGNUM); #if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM /* If they are different, also mark the hard frame pointer as live */ - basic_block_live_at_end[n_basic_blocks - 1] - [HARD_FRAME_POINTER_REGNUM / REGSET_ELT_BITS] - |= (REGSET_ELT_TYPE) 1 << (HARD_FRAME_POINTER_REGNUM - % REGSET_ELT_BITS); - basic_block_new_live_at_end[n_basic_blocks - 1] - [HARD_FRAME_POINTER_REGNUM / REGSET_ELT_BITS] - |= (REGSET_ELT_TYPE) 1 << (HARD_FRAME_POINTER_REGNUM - % REGSET_ELT_BITS); + SET_REGNO_REG_SET (basic_block_live_at_end[n_basic_blocks - 1], + HARD_FRAME_POINTER_REGNUM); + SET_REGNO_REG_SET (basic_block_new_live_at_end[n_basic_blocks - 1], + HARD_FRAME_POINTER_REGNUM); #endif } - /* Mark all global registers as being live at the end of the function - since they may be referenced by our caller. */ + /* Mark all global registers and all registers used by the epilogue + as being live at the end of the function since they may be + referenced by our caller. */ if (n_basic_blocks > 0) for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (global_regs[i]) + if (global_regs[i] +#ifdef EPILOGUE_USES + || EPILOGUE_USES (i) +#endif + ) { - basic_block_live_at_end[n_basic_blocks - 1] - [i / REGSET_ELT_BITS] - |= (REGSET_ELT_TYPE) 1 << (i % REGSET_ELT_BITS); - basic_block_new_live_at_end[n_basic_blocks - 1] - [i / REGSET_ELT_BITS] - |= (REGSET_ELT_TYPE) 1 << (i % REGSET_ELT_BITS); + SET_REGNO_REG_SET (basic_block_live_at_end[n_basic_blocks - 1], i); + SET_REGNO_REG_SET (basic_block_new_live_at_end[n_basic_blocks - 1], i); } /* Propagate life info through the basic blocks @@ -1105,21 +1306,18 @@ life_analysis (f, nregs) reg that is live at the end now but was not live there before is one of the significant regs of this basic block). */ - for (j = 0; j < regset_size; j++) - { - register REGSET_ELT_TYPE x - = (basic_block_new_live_at_end[i][j] - & ~basic_block_live_at_end[i][j]); - if (x) - consider = 1; - if (x & basic_block_significant[i][j]) - { - must_rescan = 1; - consider = 1; - break; - } - } - + EXECUTE_IF_AND_COMPL_IN_REG_SET + (basic_block_new_live_at_end[i], + basic_block_live_at_end[i], 0, j, + { + consider = 1; + if (REGNO_REG_SET_P (basic_block_significant[i], j)) + { + must_rescan = 1; + goto done; + } + }); + done: if (! consider) continue; } @@ -1133,23 +1331,22 @@ life_analysis (f, nregs) /* No complete rescan needed; just record those variables newly known live at end as live at start as well. */ - for (j = 0; j < regset_size; j++) - { - register REGSET_ELT_TYPE x - = (basic_block_new_live_at_end[i][j] - & ~basic_block_live_at_end[i][j]); - basic_block_live_at_start[i][j] |= x; - basic_block_live_at_end[i][j] |= x; - } + IOR_AND_COMPL_REG_SET (basic_block_live_at_start[i], + basic_block_new_live_at_end[i], + basic_block_live_at_end[i]); + + IOR_AND_COMPL_REG_SET (basic_block_live_at_end[i], + basic_block_new_live_at_end[i], + basic_block_live_at_end[i]); } else { /* Update the basic_block_live_at_start by propagation backwards through the block. */ - bcopy ((char *) basic_block_new_live_at_end[i], - (char *) basic_block_live_at_end[i], regset_bytes); - bcopy ((char *) basic_block_live_at_end[i], - (char *) basic_block_live_at_start[i], regset_bytes); + COPY_REG_SET (basic_block_live_at_end[i], + basic_block_new_live_at_end[i]); + COPY_REG_SET (basic_block_live_at_start[i], + basic_block_live_at_end[i]); propagate_block (basic_block_live_at_start[i], basic_block_head[i], basic_block_end[i], 0, first_pass ? basic_block_significant[i] @@ -1164,12 +1361,8 @@ life_analysis (f, nregs) that falls through into this one (if any). */ head = basic_block_head[i]; if (basic_block_drops_in[i]) - { - register int j; - for (j = 0; j < regset_size; j++) - basic_block_new_live_at_end[i-1][j] - |= basic_block_live_at_start[i][j]; - } + IOR_REG_SET (basic_block_new_live_at_end[i-1], + basic_block_live_at_start[i]); /* Update the basic_block_new_live_at_end's of all the blocks that jump to this one. */ @@ -1179,10 +1372,8 @@ life_analysis (f, nregs) jump = LABEL_NEXTREF (jump)) { register int from_block = BLOCK_NUM (CONTAINING_INSN (jump)); - register int j; - for (j = 0; j < regset_size; j++) - basic_block_new_live_at_end[from_block][j] - |= basic_block_live_at_start[i][j]; + IOR_REG_SET (basic_block_new_live_at_end[from_block], + basic_block_live_at_start[i]); } } #ifdef USE_C_ALLOCA @@ -1198,10 +1389,11 @@ life_analysis (f, nregs) one basic block. */ if (n_basic_blocks > 0) - for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++) - if (basic_block_live_at_start[0][i / REGSET_ELT_BITS] - & ((REGSET_ELT_TYPE) 1 << (i % REGSET_ELT_BITS))) - reg_basic_block[i] = REG_BLOCK_GLOBAL; + EXECUTE_IF_SET_IN_REG_SET (basic_block_live_at_start[0], + FIRST_PSEUDO_REGISTER, i, + { + REG_BASIC_BLOCK (i) = REG_BLOCK_GLOBAL; + }); /* Now the life information is accurate. Make one more pass over each basic block @@ -1232,14 +1424,16 @@ life_analysis (f, nregs) But we don't need to do this for the user's variables, since ANSI says only volatile variables need this. */ #ifdef LONGJMP_RESTORE_FROM_STACK - for (i = FIRST_PSEUDO_REGISTER; i < nregs; i++) - if (regs_live_at_setjmp[i / REGSET_ELT_BITS] - & ((REGSET_ELT_TYPE) 1 << (i % REGSET_ELT_BITS)) - && regno_reg_rtx[i] != 0 && ! REG_USERVAR_P (regno_reg_rtx[i])) - { - reg_live_length[i] = -1; - reg_basic_block[i] = -1; - } + EXECUTE_IF_SET_IN_REG_SET (regs_live_at_setjmp, + FIRST_PSEUDO_REGISTER, i, + { + if (regno_reg_rtx[i] != 0 + && ! REG_USERVAR_P (regno_reg_rtx[i])) + { + REG_LIVE_LENGTH (i) = -1; + REG_BASIC_BLOCK (i) = -1; + } + }); #endif #endif @@ -1252,14 +1446,23 @@ life_analysis (f, nregs) If the pseudo goes in a hard reg, some other value may occupy that hard reg where this pseudo is dead, thus clobbering the pseudo. Conclusion: such a pseudo must not go in a hard reg. */ - for (i = FIRST_PSEUDO_REGISTER; i < nregs; i++) - if ((regs_live_at_setjmp[i / REGSET_ELT_BITS] - & ((REGSET_ELT_TYPE) 1 << (i % REGSET_ELT_BITS))) - && regno_reg_rtx[i] != 0) - { - reg_live_length[i] = -1; - reg_basic_block[i] = -1; - } + EXECUTE_IF_SET_IN_REG_SET (regs_live_at_setjmp, + FIRST_PSEUDO_REGISTER, i, + { + if (regno_reg_rtx[i] != 0) + { + REG_LIVE_LENGTH (i) = -1; + REG_BASIC_BLOCK (i) = -1; + } + }); + + + free_regset_vector (basic_block_live_at_end, n_basic_blocks); + free_regset_vector (basic_block_new_live_at_end, n_basic_blocks); + free_regset_vector (basic_block_significant, n_basic_blocks); + basic_block_live_at_end = (regset *)0; + basic_block_new_live_at_end = (regset *)0; + basic_block_significant = (regset *)0; obstack_free (&flow_obstack, NULL_PTR); } @@ -1273,66 +1476,60 @@ void allocate_for_life_analysis () { register int i; - register regset tem; - - regset_size = ((max_regno + REGSET_ELT_BITS - 1) / REGSET_ELT_BITS); - regset_bytes = regset_size * sizeof (*(regset)0); - - reg_n_refs = (int *) oballoc (max_regno * sizeof (int)); - bzero ((char *) reg_n_refs, max_regno * sizeof (int)); - - reg_n_sets = (short *) oballoc (max_regno * sizeof (short)); - bzero ((char *) reg_n_sets, max_regno * sizeof (short)); - - reg_n_deaths = (short *) oballoc (max_regno * sizeof (short)); - bzero ((char *) reg_n_deaths, max_regno * sizeof (short)); - - reg_changes_size = (char *) oballoc (max_regno * sizeof (char)); - bzero (reg_changes_size, max_regno * sizeof (char));; - - reg_live_length = (int *) oballoc (max_regno * sizeof (int)); - bzero ((char *) reg_live_length, max_regno * sizeof (int)); - reg_n_calls_crossed = (int *) oballoc (max_regno * sizeof (int)); - bzero ((char *) reg_n_calls_crossed, max_regno * sizeof (int)); + /* Recalculate the register space, in case it has grown. Old style + vector oriented regsets would set regset_{size,bytes} here also. */ + allocate_reg_info (max_regno, FALSE, FALSE); - reg_basic_block = (int *) oballoc (max_regno * sizeof (int)); + /* Because both reg_scan and flow_analysis want to set up the REG_N_SETS + information, explicitly reset it here. The allocation should have + already happened on the previous reg_scan pass. Make sure in case + some more registers were allocated. */ for (i = 0; i < max_regno; i++) - reg_basic_block[i] = REG_BLOCK_UNKNOWN; + REG_N_SETS (i) = 0; basic_block_live_at_start = (regset *) oballoc (n_basic_blocks * sizeof (regset)); - tem = (regset) oballoc (n_basic_blocks * regset_bytes); - bzero ((char *) tem, n_basic_blocks * regset_bytes); - init_regset_vector (basic_block_live_at_start, tem, - n_basic_blocks, regset_bytes); + init_regset_vector (basic_block_live_at_start, n_basic_blocks, + function_obstack); - regs_live_at_setjmp = (regset) oballoc (regset_bytes); - bzero ((char *) regs_live_at_setjmp, regset_bytes); + regs_live_at_setjmp = OBSTACK_ALLOC_REG_SET (function_obstack); + CLEAR_REG_SET (regs_live_at_setjmp); } -/* Make each element of VECTOR point at a regset, - taking the space for all those regsets from SPACE. - SPACE is of type regset, but it is really as long as NELTS regsets. - BYTES_PER_ELT is the number of bytes in one regset. */ +/* Make each element of VECTOR point at a regset. The vector has + NELTS elements, and space is allocated from the ALLOC_OBSTACK + obstack. */ static void -init_regset_vector (vector, space, nelts, bytes_per_elt) +init_regset_vector (vector, nelts, alloc_obstack) regset *vector; - regset space; int nelts; - int bytes_per_elt; + struct obstack *alloc_obstack; { register int i; - register regset p = space; for (i = 0; i < nelts; i++) { - vector[i] = p; - p += bytes_per_elt / sizeof (*p); + vector[i] = OBSTACK_ALLOC_REG_SET (alloc_obstack); + CLEAR_REG_SET (vector[i]); } } +/* Release any additional space allocated for each element of VECTOR point + other than the regset header itself. The vector has NELTS elements. */ + +void +free_regset_vector (vector, nelts) + regset *vector; + int nelts; +{ + register int i; + + for (i = 0; i < nelts; i++) + FREE_REG_SET (vector[i]); +} + /* Compute the registers live at the beginning of a basic block from those live at the end. @@ -1369,11 +1566,8 @@ propagate_block (old, first, last, final, significant, bnum) /* The following variables are used only if FINAL is nonzero. */ /* This vector gets one element for each reg that has been live at any point in the basic block that has been scanned so far. - SOMETIMES_MAX says how many elements are in use so far. - In each element, OFFSET is the byte-number within a regset - for the register described by the element, and BIT is a mask - for that register's bit within the byte. */ - register struct sometimes { short offset; short bit; } *regs_sometimes_live; + SOMETIMES_MAX says how many elements are in use so far. */ + register int *regs_sometimes_live; int sometimes_max = 0; /* This regset has 1 for each reg that we have seen live so far. It and REGS_SOMETIMES_LIVE are updated together. */ @@ -1384,8 +1578,8 @@ propagate_block (old, first, last, final, significant, bnum) current basic block, and adjust as we pass ends and starts of loops. */ loop_depth = basic_block_loop_depth[bnum]; - dead = (regset) alloca (regset_bytes); - live = (regset) alloca (regset_bytes); + dead = ALLOCA_REG_SET (); + live = ALLOCA_REG_SET (); cc0_live = 0; last_mem_set = 0; @@ -1405,32 +1599,22 @@ propagate_block (old, first, last, final, significant, bnum) if (final) { - register int i, offset; - REGSET_ELT_TYPE bit; + register int i; num_scratch = 0; - maxlive = (regset) alloca (regset_bytes); - bcopy ((char *) old, (char *) maxlive, regset_bytes); - regs_sometimes_live - = (struct sometimes *) alloca (max_regno * sizeof (struct sometimes)); + maxlive = ALLOCA_REG_SET (); + COPY_REG_SET (maxlive, old); + regs_sometimes_live = (int *) alloca (max_regno * sizeof (int)); /* Process the regs live at the end of the block. Enter them in MAXLIVE and REGS_SOMETIMES_LIVE. - Also mark them as not local to any one basic block. */ - - for (offset = 0, i = 0; offset < regset_size; offset++) - for (bit = 1; bit; bit <<= 1, i++) - { - if (i == max_regno) - break; - if (old[offset] & bit) - { - reg_basic_block[i] = REG_BLOCK_GLOBAL; - regs_sometimes_live[sometimes_max].offset = offset; - regs_sometimes_live[sometimes_max].bit = i % REGSET_ELT_BITS; - sometimes_max++; - } - } + Also mark them as not local to any one basic block. */ + EXECUTE_IF_SET_IN_REG_SET (old, 0, i, + { + REG_BASIC_BLOCK (i) = REG_BLOCK_GLOBAL; + regs_sometimes_live[sometimes_max] = i; + sometimes_max++; + }); } /* Scan the block an insn at a time from end to beginning. */ @@ -1457,11 +1641,7 @@ propagate_block (old, first, last, final, significant, bnum) warn if any non-volatile datum is live. */ if (final && NOTE_LINE_NUMBER (insn) == NOTE_INSN_SETJMP) - { - int i; - for (i = 0; i < regset_size; i++) - regs_live_at_setjmp[i] |= old[i]; - } + IOR_REG_SET (regs_live_at_setjmp, old); } /* Update the life-status of regs for this insn. @@ -1517,19 +1697,17 @@ propagate_block (old, first, last, final, significant, bnum) goto flushed; } - for (i = 0; i < regset_size; i++) - { - dead[i] = 0; /* Faster than bzero here */ - live[i] = 0; /* since regset_size is usually small */ - } + CLEAR_REG_SET (dead); + CLEAR_REG_SET (live); /* See if this is an increment or decrement that can be merged into a following memory address. */ #ifdef AUTO_INC_DEC { - register rtx x = PATTERN (insn); + register rtx x = single_set (insn); + /* Does this instruction increment or decrement a register? */ - if (final && GET_CODE (x) == SET + if (final && x != 0 && GET_CODE (SET_DEST (x)) == REG && (GET_CODE (SET_SRC (x)) == PLUS || GET_CODE (SET_SRC (x)) == MINUS) @@ -1604,26 +1782,24 @@ propagate_block (old, first, last, final, significant, bnum) final, insn); /* Each call clobbers all call-clobbered regs that are not - global. Note that the function-value reg is a + global or fixed. Note that the function-value reg is a call-clobbered reg, and mark_set_regs has already had a chance to handle it. */ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (call_used_regs[i] && ! global_regs[i]) - dead[i / REGSET_ELT_BITS] - |= ((REGSET_ELT_TYPE) 1 << (i % REGSET_ELT_BITS)); + if (call_used_regs[i] && ! global_regs[i] + && ! fixed_regs[i]) + SET_REGNO_REG_SET (dead, i); /* The stack ptr is used (honorarily) by a CALL insn. */ - live[STACK_POINTER_REGNUM / REGSET_ELT_BITS] - |= ((REGSET_ELT_TYPE) 1 - << (STACK_POINTER_REGNUM % REGSET_ELT_BITS)); + SET_REGNO_REG_SET (live, STACK_POINTER_REGNUM); /* Calls may also reference any of the global registers, so they are made live. */ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) if (global_regs[i]) mark_used_regs (old, live, - gen_rtx (REG, reg_raw_mode[i], i), + gen_rtx_REG (reg_raw_mode[i], i), final, insn); /* Calls also clobber memory. */ @@ -1631,11 +1807,8 @@ propagate_block (old, first, last, final, significant, bnum) } /* Update OLD for the registers used or set. */ - for (i = 0; i < regset_size; i++) - { - old[i] &= ~dead[i]; - old[i] |= live[i]; - } + AND_COMPL_REG_SET (old, dead); + IOR_REG_SET (old, live); if (GET_CODE (insn) == CALL_INSN && final) { @@ -1643,11 +1816,11 @@ propagate_block (old, first, last, final, significant, bnum) must not go in a register clobbered by calls. Find all regs now live and record this for them. */ - register struct sometimes *p = regs_sometimes_live; + register int *p = regs_sometimes_live; for (i = 0; i < sometimes_max; i++, p++) - if (old[p->offset] & ((REGSET_ELT_TYPE) 1 << p->bit)) - reg_n_calls_crossed[p->offset * REGSET_ELT_BITS + p->bit]+= 1; + if (REGNO_REG_SET_P (old, *p)) + REG_N_CALLS_CROSSED (*p)++; } } @@ -1657,33 +1830,23 @@ propagate_block (old, first, last, final, significant, bnum) if (final) { - for (i = 0; i < regset_size; i++) + register int regno; + register int *p; + + EXECUTE_IF_AND_COMPL_IN_REG_SET + (live, maxlive, 0, regno, + { + regs_sometimes_live[sometimes_max++] = regno; + SET_REGNO_REG_SET (maxlive, regno); + }); + + p = regs_sometimes_live; + for (i = 0; i < sometimes_max; i++) { - register REGSET_ELT_TYPE diff = live[i] & ~maxlive[i]; - - if (diff) - { - register int regno; - maxlive[i] |= diff; - for (regno = 0; diff && regno < REGSET_ELT_BITS; regno++) - if (diff & ((REGSET_ELT_TYPE) 1 << regno)) - { - regs_sometimes_live[sometimes_max].offset = i; - regs_sometimes_live[sometimes_max].bit = regno; - diff &= ~ ((REGSET_ELT_TYPE) 1 << regno); - sometimes_max++; - } - } + regno = *p++; + if (REGNO_REG_SET_P (old, regno)) + REG_LIVE_LENGTH (regno)++; } - - { - register struct sometimes *p = regs_sometimes_live; - for (i = 0; i < sometimes_max; i++, p++) - { - if (old[p->offset] & ((REGSET_ELT_TYPE) 1 << p->bit)) - reg_live_length[p->offset * REGSET_ELT_BITS + p->bit]++; - } - } } } flushed: ; @@ -1691,6 +1854,11 @@ propagate_block (old, first, last, final, significant, bnum) break; } + FREE_REG_SET (dead); + FREE_REG_SET (live); + if (final) + FREE_REG_SET (maxlive); + if (num_scratch > max_scratch) max_scratch = num_scratch; } @@ -1707,13 +1875,15 @@ insn_dead_p (x, needed, call_ok) regset needed; int call_ok; { - register RTX_CODE code = GET_CODE (x); + enum rtx_code code = GET_CODE (x); + /* If setting something that's a reg or part of one, see if that register's altered value will be live. */ if (code == SET) { - register rtx r = SET_DEST (x); + rtx r = SET_DEST (x); + /* A SET that is a subroutine call cannot be dead. */ if (! call_ok && GET_CODE (SET_SRC (x)) == CALL) return 0; @@ -1727,18 +1897,13 @@ insn_dead_p (x, needed, call_ok) && rtx_equal_p (r, last_mem_set)) return 1; - while (GET_CODE (r) == SUBREG - || GET_CODE (r) == STRICT_LOW_PART - || GET_CODE (r) == ZERO_EXTRACT - || GET_CODE (r) == SIGN_EXTRACT) + while (GET_CODE (r) == SUBREG || GET_CODE (r) == STRICT_LOW_PART + || GET_CODE (r) == ZERO_EXTRACT) r = SUBREG_REG (r); if (GET_CODE (r) == REG) { - register int regno = REGNO (r); - register int offset = regno / REGSET_ELT_BITS; - register REGSET_ELT_TYPE bit - = (REGSET_ELT_TYPE) 1 << (regno % REGSET_ELT_BITS); + int regno = REGNO (r); /* Don't delete insns to set global regs. */ if ((regno < FIRST_PSEUDO_REGISTER && global_regs[regno]) @@ -1750,10 +1915,10 @@ insn_dead_p (x, needed, call_ok) #if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM /* Make sure insns to set arg pointer are never deleted (if the arg pointer isn't fixed, there will be a USE for - it, so we can treat it normally). */ + it, so we can treat it normally). */ || (regno == ARG_POINTER_REGNUM && fixed_regs[regno]) #endif - || (needed[offset] & bit) != 0) + || REGNO_REG_SET_P (needed, regno)) return 0; /* If this is a hard register, verify that subsequent words are @@ -1763,35 +1928,40 @@ insn_dead_p (x, needed, call_ok) int n = HARD_REGNO_NREGS (regno, GET_MODE (r)); while (--n > 0) - if ((needed[(regno + n) / REGSET_ELT_BITS] - & ((REGSET_ELT_TYPE) 1 - << ((regno + n) % REGSET_ELT_BITS))) != 0) + if (REGNO_REG_SET_P (needed, regno+n)) return 0; } return 1; } } + /* If performing several activities, insn is dead if each activity is individually dead. Also, CLOBBERs and USEs can be ignored; a CLOBBER or USE that's inside a PARALLEL doesn't make the insn worth keeping. */ else if (code == PARALLEL) { - register int i = XVECLEN (x, 0); + int i = XVECLEN (x, 0); + for (i--; i >= 0; i--) - { - rtx elt = XVECEXP (x, 0, i); - if (!insn_dead_p (elt, needed, call_ok) - && GET_CODE (elt) != CLOBBER - && GET_CODE (elt) != USE) - return 0; - } + if (GET_CODE (XVECEXP (x, 0, i)) != CLOBBER + && GET_CODE (XVECEXP (x, 0, i)) != USE + && ! insn_dead_p (XVECEXP (x, 0, i), needed, call_ok)) + return 0; + return 1; } - /* We do not check CLOBBER or USE here. - An insn consisting of just a CLOBBER or just a USE - should not be deleted. */ + + /* A CLOBBER of a pseudo-register that is dead serves no purpose. That + is not necessarily true for hard registers. */ + else if (code == CLOBBER && GET_CODE (XEXP (x, 0)) == REG + && REGNO (XEXP (x, 0)) >= FIRST_PSEUDO_REGISTER + && ! REGNO_REG_SET_P (needed, REGNO (XEXP (x, 0)))) + return 1; + + /* We do not check other CLOBBER or USE here. An insn consisting of just + a CLOBBER or just a USE should not be deleted. */ return 0; } @@ -1862,18 +2032,19 @@ libcall_dead_p (x, needed, note, insn) /* Return 1 if register REGNO was used before it was set. In other words, if it is live at function entry. - Don't count global register variables, though. */ + Don't count global register variables or variables in registers + that can be used for function arg passing, though. */ int regno_uninitialized (regno) int regno; { if (n_basic_blocks == 0 - || (regno < FIRST_PSEUDO_REGISTER && global_regs[regno])) + || (regno < FIRST_PSEUDO_REGISTER + && (global_regs[regno] || FUNCTION_ARG_REGNO_P (regno)))) return 0; - return (basic_block_live_at_start[0][regno / REGSET_ELT_BITS] - & ((REGSET_ELT_TYPE) 1 << (regno % REGSET_ELT_BITS))); + return REGNO_REG_SET_P (basic_block_live_at_start[0], regno); } /* 1 if register REGNO was alive at a place where `setjmp' was called @@ -1887,11 +2058,9 @@ regno_clobbered_at_setjmp (regno) if (n_basic_blocks == 0) return 0; - return ((reg_n_sets[regno] > 1 - || (basic_block_live_at_start[0][regno / REGSET_ELT_BITS] - & ((REGSET_ELT_TYPE) 1 << (regno % REGSET_ELT_BITS)))) - && (regs_live_at_setjmp[regno / REGSET_ELT_BITS] - & ((REGSET_ELT_TYPE) 1 << (regno % REGSET_ELT_BITS)))); + return ((REG_N_SETS (regno) > 1 + || REGNO_REG_SET_P (basic_block_live_at_start[0], regno)) + && REGNO_REG_SET_P (regs_live_at_setjmp, regno)); } /* Process the registers that are set within X. @@ -1984,18 +2153,15 @@ mark_set_1 (needed, dead, x, insn, significant) && ! (regno < FIRST_PSEUDO_REGISTER && global_regs[regno])) /* && regno != STACK_POINTER_REGNUM) -- let's try without this. */ { - register int offset = regno / REGSET_ELT_BITS; - register REGSET_ELT_TYPE bit - = (REGSET_ELT_TYPE) 1 << (regno % REGSET_ELT_BITS); - REGSET_ELT_TYPE all_needed = (needed[offset] & bit); - REGSET_ELT_TYPE some_needed = (needed[offset] & bit); + int some_needed = REGNO_REG_SET_P (needed, regno); + int some_not_needed = ! some_needed; /* Mark it as a significant register for this basic block. */ if (significant) - significant[offset] |= bit; + SET_REGNO_REG_SET (significant, regno); - /* Mark it as as dead before this insn. */ - dead[offset] |= bit; + /* Mark it as dead before this insn. */ + SET_REGNO_REG_SET (dead, regno); /* A hard reg in a wide mode may really be multiple registers. If so, mark all of them just like the first. */ @@ -2011,17 +2177,14 @@ mark_set_1 (needed, dead, x, insn, significant) n = HARD_REGNO_NREGS (regno, GET_MODE (reg)); while (--n > 0) { + int regno_n = regno + n; + int needed_regno = REGNO_REG_SET_P (needed, regno_n); if (significant) - significant[(regno + n) / REGSET_ELT_BITS] - |= (REGSET_ELT_TYPE) 1 << ((regno + n) % REGSET_ELT_BITS); - dead[(regno + n) / REGSET_ELT_BITS] - |= (REGSET_ELT_TYPE) 1 << ((regno + n) % REGSET_ELT_BITS); - some_needed - |= (needed[(regno + n) / REGSET_ELT_BITS] - & (REGSET_ELT_TYPE) 1 << ((regno + n) % REGSET_ELT_BITS)); - all_needed - &= (needed[(regno + n) / REGSET_ELT_BITS] - & (REGSET_ELT_TYPE) 1 << ((regno + n) % REGSET_ELT_BITS)); + SET_REGNO_REG_SET (significant, regno_n); + + SET_REGNO_REG_SET (dead, regno_n); + some_needed |= needed_regno; + some_not_needed |= ! needed_regno; } } /* Additional data to record if this is the final pass. */ @@ -2044,7 +2207,7 @@ mark_set_1 (needed, dead, x, insn, significant) reg_next_use[i] = 0; regs_ever_live[i] = 1; - reg_n_sets[i]++; + REG_N_SETS (i)++; } } else @@ -2055,25 +2218,25 @@ mark_set_1 (needed, dead, x, insn, significant) /* Keep track of which basic blocks each reg appears in. */ - if (reg_basic_block[regno] == REG_BLOCK_UNKNOWN) - reg_basic_block[regno] = blocknum; - else if (reg_basic_block[regno] != blocknum) - reg_basic_block[regno] = REG_BLOCK_GLOBAL; + if (REG_BASIC_BLOCK (regno) == REG_BLOCK_UNKNOWN) + REG_BASIC_BLOCK (regno) = blocknum; + else if (REG_BASIC_BLOCK (regno) != blocknum) + REG_BASIC_BLOCK (regno) = REG_BLOCK_GLOBAL; /* Count (weighted) references, stores, etc. This counts a register twice if it is modified, but that is correct. */ - reg_n_sets[regno]++; + REG_N_SETS (regno)++; - reg_n_refs[regno] += loop_depth; + REG_N_REFS (regno) += loop_depth; /* The insns where a reg is live are normally counted elsewhere, but we want the count to include the insn where the reg is set, and the normal counting mechanism would not count it. */ - reg_live_length[regno]++; + REG_LIVE_LENGTH (regno)++; } - if (all_needed) + if (! some_not_needed) { /* Make a logical link from the next following insn that uses this register, back to this insn. @@ -2088,7 +2251,7 @@ mark_set_1 (needed, dead, x, insn, significant) && (regno >= FIRST_PSEUDO_REGISTER || asm_noperands (PATTERN (y)) < 0)) LOG_LINKS (y) - = gen_rtx (INSN_LIST, VOIDmode, insn, LOG_LINKS (y)); + = gen_rtx_INSN_LIST (VOIDmode, insn, LOG_LINKS (y)); } else if (! some_needed) { @@ -2097,8 +2260,8 @@ mark_set_1 (needed, dead, x, insn, significant) be eliminated (because the same insn does something useful). Indicate this by marking the reg being set as dying here. */ REG_NOTES (insn) - = gen_rtx (EXPR_LIST, REG_UNUSED, reg, REG_NOTES (insn)); - reg_n_deaths[REGNO (reg)]++; + = gen_rtx_EXPR_LIST (REG_UNUSED, reg, REG_NOTES (insn)); + REG_N_DEATHS (REGNO (reg))++; } else { @@ -2112,14 +2275,12 @@ mark_set_1 (needed, dead, x, insn, significant) for (i = HARD_REGNO_NREGS (regno, GET_MODE (reg)) - 1; i >= 0; i--) - if ((needed[(regno + i) / REGSET_ELT_BITS] - & ((REGSET_ELT_TYPE) 1 - << ((regno + i) % REGSET_ELT_BITS))) == 0) + if (!REGNO_REG_SET_P (needed, regno + i)) REG_NOTES (insn) - = gen_rtx (EXPR_LIST, REG_UNUSED, - gen_rtx (REG, reg_raw_mode[regno + i], - regno + i), - REG_NOTES (insn)); + = gen_rtx_EXPR_LIST (REG_UNUSED, + gen_rtx_REG (reg_raw_mode[regno + i], + regno + i), + REG_NOTES (insn)); } } } @@ -2131,7 +2292,7 @@ mark_set_1 (needed, dead, x, insn, significant) else if (GET_CODE (reg) == SCRATCH && insn != 0) { REG_NOTES (insn) - = gen_rtx (EXPR_LIST, REG_UNUSED, reg, REG_NOTES (insn)); + = gen_rtx_EXPR_LIST (REG_UNUSED, reg, REG_NOTES (insn)); num_scratch++; } } @@ -2205,18 +2366,22 @@ find_auto_inc (needed, x, insn) we can't, we are done. Otherwise, we will do any needed updates below. */ if (! validate_change (insn, &XEXP (x, 0), - gen_rtx (inc_code, Pmode, addr), + gen_rtx_fmt_e (inc_code, Pmode, addr), 0)) return; } else if (GET_CODE (q) == REG /* PREV_INSN used here to check the semi-open interval [insn,incr). */ - && ! reg_used_between_p (q, PREV_INSN (insn), incr)) + && ! reg_used_between_p (q, PREV_INSN (insn), incr) + /* We must also check for sets of q as q may be + a call clobbered hard register and there may + be a call between PREV_INSN (insn) and incr. */ + && ! reg_set_between_p (q, PREV_INSN (insn), incr)) { /* We have *p followed sometime later by q = p+size. Both p and q must be live afterward, - and q is not used between INSN and it's assignment. + and q is not used between INSN and its assignment. Change it to q = p, ...*q..., q = q+size. Then fall into the usual case. */ rtx insns, temp; @@ -2242,7 +2407,7 @@ find_auto_inc (needed, x, insn) so is not correct in the pre-inc case. */ validate_change (insn, &XEXP (x, 0), - gen_rtx (inc_code, Pmode, q), + gen_rtx_fmt_e (inc_code, Pmode, q), 1); validate_change (incr, &XEXP (y, 0), q, 1); if (! apply_change_group ()) @@ -2273,14 +2438,13 @@ find_auto_inc (needed, x, insn) it previously wasn't live here. If we don't mark it as needed, we'll put a REG_DEAD note for it on this insn, which is incorrect. */ - needed[regno / REGSET_ELT_BITS] - |= (REGSET_ELT_TYPE) 1 << (regno % REGSET_ELT_BITS); + SET_REGNO_REG_SET (needed, regno); /* If there are any calls between INSN and INCR, show that REGNO now crosses them. */ for (temp = insn; temp != incr; temp = NEXT_INSN (temp)) if (GET_CODE (temp) == CALL_INSN) - reg_n_calls_crossed[regno]++; + REG_N_CALLS_CROSSED (regno)++; } else return; @@ -2290,7 +2454,7 @@ find_auto_inc (needed, x, insn) has an implicit side effect. */ REG_NOTES (insn) - = gen_rtx (EXPR_LIST, REG_INC, addr, REG_NOTES (insn)); + = gen_rtx_EXPR_LIST (REG_INC, addr, REG_NOTES (insn)); /* Modify the old increment-insn to simply copy the already-incremented value of our register. */ @@ -2312,11 +2476,11 @@ find_auto_inc (needed, x, insn) /* Count an extra reference to the reg. When a reg is incremented, spilling it is worse, so we want to make that less likely. */ - reg_n_refs[regno] += loop_depth; + REG_N_REFS (regno) += loop_depth; /* Count the increment as a setting of the register, even though it isn't a SET in rtl. */ - reg_n_sets[regno]++; + REG_N_SETS (regno)++; } } } @@ -2374,9 +2538,13 @@ mark_used_regs (needed, live, x, final, insn) return; case MEM: - /* Invalidate the data for the last MEM stored. We could do this only - if the addresses conflict, but this doesn't seem worthwhile. */ - last_mem_set = 0; + /* Invalidate the data for the last MEM stored, but only if MEM is + something that can be stored into. */ + if (GET_CODE (XEXP (x, 0)) == SYMBOL_REF + && CONSTANT_POOL_ADDRESS_P (XEXP (x, 0))) + ; /* needn't clear last_mem_set */ + else + last_mem_set = 0; #ifdef AUTO_INC_DEC if (final) @@ -2389,7 +2557,7 @@ mark_used_regs (needed, live, x, final, insn) && REGNO (SUBREG_REG (x)) >= FIRST_PSEUDO_REGISTER && (GET_MODE_SIZE (GET_MODE (x)) != GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))) - reg_changes_size[REGNO (SUBREG_REG (x))] = 1; + REG_CHANGES_SIZE (REGNO (SUBREG_REG (x))) = 1; /* While we're here, optimize this case. */ x = SUBREG_REG (x); @@ -2401,7 +2569,7 @@ mark_used_regs (needed, live, x, final, insn) return; } - /* ... fall through ... */ + /* ... fall through ... */ case REG: /* See a register other than being set @@ -2409,13 +2577,11 @@ mark_used_regs (needed, live, x, final, insn) regno = REGNO (x); { - register int offset = regno / REGSET_ELT_BITS; - register REGSET_ELT_TYPE bit - = (REGSET_ELT_TYPE) 1 << (regno % REGSET_ELT_BITS); - REGSET_ELT_TYPE all_needed = needed[offset] & bit; - REGSET_ELT_TYPE some_needed = needed[offset] & bit; + int some_needed = REGNO_REG_SET_P (needed, regno); + int some_not_needed = ! some_needed; + + SET_REGNO_REG_SET (live, regno); - live[offset] |= bit; /* A hard reg in a wide mode may really be multiple registers. If so, mark all of them just like the first. */ if (regno < FIRST_PSEUDO_REGISTER) @@ -2456,14 +2622,12 @@ mark_used_regs (needed, live, x, final, insn) n = HARD_REGNO_NREGS (regno, GET_MODE (x)); while (--n > 0) { - live[(regno + n) / REGSET_ELT_BITS] - |= (REGSET_ELT_TYPE) 1 << ((regno + n) % REGSET_ELT_BITS); - some_needed - |= (needed[(regno + n) / REGSET_ELT_BITS] - & (REGSET_ELT_TYPE) 1 << ((regno + n) % REGSET_ELT_BITS)); - all_needed - &= (needed[(regno + n) / REGSET_ELT_BITS] - & (REGSET_ELT_TYPE) 1 << ((regno + n) % REGSET_ELT_BITS)); + int regno_n = regno + n; + int needed_regno = REGNO_REG_SET_P (needed, regno_n); + + SET_REGNO_REG_SET (live, regno_n); + some_needed |= needed_regno; + some_not_needed |= ! needed_regno; } } if (final) @@ -2491,14 +2655,14 @@ mark_used_regs (needed, live, x, final, insn) register int blocknum = BLOCK_NUM (insn); - if (reg_basic_block[regno] == REG_BLOCK_UNKNOWN) - reg_basic_block[regno] = blocknum; - else if (reg_basic_block[regno] != blocknum) - reg_basic_block[regno] = REG_BLOCK_GLOBAL; + if (REG_BASIC_BLOCK (regno) == REG_BLOCK_UNKNOWN) + REG_BASIC_BLOCK (regno) = blocknum; + else if (REG_BASIC_BLOCK (regno) != blocknum) + REG_BASIC_BLOCK (regno) = REG_BLOCK_GLOBAL; /* Count (weighted) number of uses of each reg. */ - reg_n_refs[regno] += loop_depth; + REG_N_REFS (regno) += loop_depth; } /* Record and count the insns in which a reg dies. @@ -2507,7 +2671,7 @@ mark_used_regs (needed, live, x, final, insn) we do not make a REG_DEAD note; likewise if we already made such a note. */ - if (! all_needed + if (some_not_needed && ! dead_or_set_p (insn, x) #if 0 && (regno >= FIRST_PSEUDO_REGISTER || ! fixed_regs[regno]) @@ -2529,8 +2693,8 @@ mark_used_regs (needed, live, x, final, insn) if (! some_needed) { REG_NOTES (insn) - = gen_rtx (EXPR_LIST, REG_DEAD, x, REG_NOTES (insn)); - reg_n_deaths[regno]++; + = gen_rtx_EXPR_LIST (REG_DEAD, x, REG_NOTES (insn)); + REG_N_DEATHS (regno)++; } else { @@ -2541,15 +2705,13 @@ mark_used_regs (needed, live, x, final, insn) for (i = HARD_REGNO_NREGS (regno, GET_MODE (x)) - 1; i >= 0; i--) - if ((needed[(regno + i) / REGSET_ELT_BITS] - & ((REGSET_ELT_TYPE) 1 - << ((regno + i) % REGSET_ELT_BITS))) == 0 + if (!REGNO_REG_SET_P (needed, regno + i) && ! dead_or_set_regno_p (insn, regno + i)) REG_NOTES (insn) - = gen_rtx (EXPR_LIST, REG_DEAD, - gen_rtx (REG, reg_raw_mode[regno + i], - regno + i), - REG_NOTES (insn)); + = gen_rtx_EXPR_LIST (REG_DEAD, + gen_rtx_REG (reg_raw_mode[regno + i], + regno + i), + REG_NOTES (insn)); } } } @@ -2591,7 +2753,7 @@ mark_used_regs (needed, live, x, final, insn) && REGNO (SUBREG_REG (testreg)) >= FIRST_PSEUDO_REGISTER && (GET_MODE_SIZE (GET_MODE (testreg)) != GET_MODE_SIZE (GET_MODE (SUBREG_REG (testreg))))) - reg_changes_size[REGNO (SUBREG_REG (testreg))] = 1; + REG_CHANGES_SIZE (REGNO (SUBREG_REG (testreg))) = 1; /* Modifying a single register in an alternate mode does not use any of the old value. But these other @@ -2631,19 +2793,26 @@ mark_used_regs (needed, live, x, final, insn) case RETURN: /* If exiting needs the right stack value, consider this insn as using the stack pointer. In any event, consider it as using - all global registers. */ + all global registers and all registers used by return. */ #ifdef EXIT_IGNORE_STACK if (! EXIT_IGNORE_STACK - || (! FRAME_POINTER_REQUIRED && flag_omit_frame_pointer)) + || (! FRAME_POINTER_REQUIRED + && ! current_function_calls_alloca + && flag_omit_frame_pointer)) #endif - live[STACK_POINTER_REGNUM / REGSET_ELT_BITS] - |= (REGSET_ELT_TYPE) 1 << (STACK_POINTER_REGNUM % REGSET_ELT_BITS); + SET_REGNO_REG_SET (live, STACK_POINTER_REGNUM); for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - if (global_regs[i]) - live[i / REGSET_ELT_BITS] - |= (REGSET_ELT_TYPE) 1 << (i % REGSET_ELT_BITS); + if (global_regs[i] +#ifdef EPILOGUE_USES + || EPILOGUE_USES (i) +#endif + ) + SET_REGNO_REG_SET (live, i); + break; + + default: break; } @@ -2683,7 +2852,7 @@ try_pre_increment_1 (insn) { /* Find the next use of this reg. If in same basic block, make it do pre-increment or pre-decrement if appropriate. */ - rtx x = PATTERN (insn); + rtx x = single_set (insn); HOST_WIDE_INT amount = ((GET_CODE (SET_SRC (x)) == PLUS ? 1 : -1) * INTVAL (XEXP (SET_SRC (x), 1))); int regno = REGNO (SET_DEST (x)); @@ -2691,10 +2860,9 @@ try_pre_increment_1 (insn) if (y != 0 && BLOCK_NUM (y) == BLOCK_NUM (insn) /* Don't do this if the reg dies, or gets set in y; a standard addressing - mode would be better. */ + mode would be better. */ && ! dead_or_set_p (y, SET_DEST (x)) - && try_pre_increment (y, SET_DEST (PATTERN (insn)), - amount)) + && try_pre_increment (y, SET_DEST (x), amount)) { /* We have found a suitable auto-increment and already changed insn Y to do it. @@ -2708,8 +2876,8 @@ try_pre_increment_1 (insn) less likely. */ if (regno >= FIRST_PSEUDO_REGISTER) { - reg_n_refs[regno] += loop_depth; - reg_n_sets[regno]++; + REG_N_REFS (regno) += loop_depth; + REG_N_SETS (regno)++; } return 1; } @@ -2788,14 +2956,14 @@ try_pre_increment (insn, reg, amount) /* See if this combination of instruction and addressing mode exists. */ if (! validate_change (insn, &XEXP (use, 0), - gen_rtx (amount > 0 - ? (do_post ? POST_INC : PRE_INC) - : (do_post ? POST_DEC : PRE_DEC), - Pmode, reg), 0)) + gen_rtx_fmt_e (amount > 0 + ? (do_post ? POST_INC : PRE_INC) + : (do_post ? POST_DEC : PRE_DEC), + Pmode, reg), 0)) return 0; /* Record that this insn now has an implicit side effect on X. */ - REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_INC, reg, REG_NOTES (insn)); + REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_INC, reg, REG_NOTES (insn)); return 1; } @@ -2810,7 +2978,7 @@ try_pre_increment (insn, reg, amount) If REG appears more than once, or is used other than in such an address, return (rtx)1. */ -static rtx +rtx find_use_as_address (x, reg, plusconst) register rtx x; rtx reg; @@ -2882,19 +3050,24 @@ dump_flow_info (file) fprintf (file, "%d registers.\n", max_regno); for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++) - if (reg_n_refs[i]) + if (REG_N_REFS (i)) { enum reg_class class, altclass; fprintf (file, "\nRegister %d used %d times across %d insns", - i, reg_n_refs[i], reg_live_length[i]); - if (reg_basic_block[i] >= 0) - fprintf (file, " in block %d", reg_basic_block[i]); - if (reg_n_deaths[i] != 1) - fprintf (file, "; dies in %d places", reg_n_deaths[i]); - if (reg_n_calls_crossed[i] == 1) + i, REG_N_REFS (i), REG_LIVE_LENGTH (i)); + if (REG_BASIC_BLOCK (i) >= 0) + fprintf (file, " in block %d", REG_BASIC_BLOCK (i)); + if (REG_N_SETS (i)) + fprintf (file, "; set %d time%s", REG_N_SETS (i), + (REG_N_SETS (i) == 1) ? "" : "s"); + if (REG_USERVAR_P (regno_reg_rtx[i])) + fprintf (file, "; user var"); + if (REG_N_DEATHS (i) != 1) + fprintf (file, "; dies in %d places", REG_N_DEATHS (i)); + if (REG_N_CALLS_CROSSED (i) == 1) fprintf (file, "; crosses 1 call"); - else if (reg_n_calls_crossed[i]) - fprintf (file, "; crosses %d calls", reg_n_calls_crossed[i]); + else if (REG_N_CALLS_CROSSED (i)) + fprintf (file, "; crosses %d calls", REG_N_CALLS_CROSSED (i)); if (PSEUDO_REGNO_BYTES (i) != UNITS_PER_WORD) fprintf (file, "; %d bytes", PSEUDO_REGNO_BYTES (i)); class = reg_preferred_class (i); @@ -2943,14 +3116,1168 @@ dump_flow_info (file) } fprintf (file, "\nRegisters live at start:"); for (regno = 0; regno < max_regno; regno++) + if (REGNO_REG_SET_P (basic_block_live_at_start[i], regno)) + fprintf (file, " %d", regno); + fprintf (file, "\n"); + } + fprintf (file, "\n"); +} + + +/* Like print_rtl, but also print out live information for the start of each + basic block. */ + +void +print_rtl_with_bb (outf, rtx_first) + FILE *outf; + rtx rtx_first; +{ + extern int flag_dump_unnumbered; + register rtx tmp_rtx; + + if (rtx_first == 0) + fprintf (outf, "(nil)\n"); + + else + { + int i, bb; + enum bb_state { NOT_IN_BB, IN_ONE_BB, IN_MULTIPLE_BB }; + int max_uid = get_max_uid (); + int *start = (int *) alloca (max_uid * sizeof (int)); + int *end = (int *) alloca (max_uid * sizeof (int)); + char *in_bb_p = (char *) alloca (max_uid * sizeof (enum bb_state)); + + for (i = 0; i < max_uid; i++) + { + start[i] = end[i] = -1; + in_bb_p[i] = NOT_IN_BB; + } + + for (i = n_basic_blocks-1; i >= 0; i--) + { + rtx x; + start[INSN_UID (basic_block_head[i])] = i; + end[INSN_UID (basic_block_end[i])] = i; + for (x = basic_block_head[i]; x != NULL_RTX; x = NEXT_INSN (x)) + { + in_bb_p[ INSN_UID(x)] + = (in_bb_p[ INSN_UID(x)] == NOT_IN_BB) + ? IN_ONE_BB : IN_MULTIPLE_BB; + if (x == basic_block_end[i]) + break; + } + } + + for (tmp_rtx = rtx_first; NULL != tmp_rtx; tmp_rtx = NEXT_INSN (tmp_rtx)) + { + if ((bb = start[INSN_UID (tmp_rtx)]) >= 0) + { + fprintf (outf, ";; Start of basic block %d, registers live:", + bb); + + EXECUTE_IF_SET_IN_REG_SET (basic_block_live_at_start[bb], 0, i, + { + fprintf (outf, " %d", i); + if (i < FIRST_PSEUDO_REGISTER) + fprintf (outf, " [%s]", + reg_names[i]); + }); + putc ('\n', outf); + } + + if (in_bb_p[ INSN_UID(tmp_rtx)] == NOT_IN_BB + && GET_CODE (tmp_rtx) != NOTE + && GET_CODE (tmp_rtx) != BARRIER) + fprintf (outf, ";; Insn is not within a basic block\n"); + else if (in_bb_p[ INSN_UID(tmp_rtx)] == IN_MULTIPLE_BB) + fprintf (outf, ";; Insn is in multiple basic blocks\n"); + + print_rtl_single (outf, tmp_rtx); + + if ((bb = end[INSN_UID (tmp_rtx)]) >= 0) + fprintf (outf, ";; End of basic block %d\n", bb); + + if (! flag_dump_unnumbered + || GET_CODE (tmp_rtx) != NOTE || NOTE_LINE_NUMBER (tmp_rtx) < 0) + putc ('\n', outf); + } + } +} + + +/* Integer list support. */ + +/* Allocate a node from list *HEAD_PTR. */ + +static int_list_ptr +alloc_int_list_node (head_ptr) + int_list_block **head_ptr; +{ + struct int_list_block *first_blk = *head_ptr; + + if (first_blk == NULL || first_blk->nodes_left <= 0) + { + first_blk = (struct int_list_block *) xmalloc (sizeof (struct int_list_block)); + first_blk->nodes_left = INT_LIST_NODES_IN_BLK; + first_blk->next = *head_ptr; + *head_ptr = first_blk; + } + + first_blk->nodes_left--; + return &first_blk->nodes[first_blk->nodes_left]; +} + +/* Pointer to head of predecessor/successor block list. */ +static int_list_block *pred_int_list_blocks; + +/* Add a new node to integer list LIST with value VAL. + LIST is a pointer to a list object to allow for different implementations. + If *LIST is initially NULL, the list is empty. + The caller must not care whether the element is added to the front or + to the end of the list (to allow for different implementations). */ + +static int_list_ptr +add_int_list_node (blk_list, list, val) + int_list_block **blk_list; + int_list **list; + int val; +{ + int_list_ptr p = alloc_int_list_node (blk_list); + + p->val = val; + p->next = *list; + *list = p; + return p; +} + +/* Free the blocks of lists at BLK_LIST. */ + +void +free_int_list (blk_list) + int_list_block **blk_list; +{ + int_list_block *p, *next; + + for (p = *blk_list; p != NULL; p = next) + { + next = p->next; + free (p); + } + + /* Mark list as empty for the next function we compile. */ + *blk_list = NULL; +} + +/* Predecessor/successor computation. */ + +/* Mark PRED_BB a precessor of SUCC_BB, + and conversely SUCC_BB a successor of PRED_BB. */ + +static void +add_pred_succ (pred_bb, succ_bb, s_preds, s_succs, num_preds, num_succs) + int pred_bb; + int succ_bb; + int_list_ptr *s_preds; + int_list_ptr *s_succs; + int *num_preds; + int *num_succs; +{ + if (succ_bb != EXIT_BLOCK) + { + add_int_list_node (&pred_int_list_blocks, &s_preds[succ_bb], pred_bb); + num_preds[succ_bb]++; + } + if (pred_bb != ENTRY_BLOCK) + { + add_int_list_node (&pred_int_list_blocks, &s_succs[pred_bb], succ_bb); + num_succs[pred_bb]++; + } +} + +/* Compute the predecessors and successors for each block. */ +void +compute_preds_succs (s_preds, s_succs, num_preds, num_succs) + int_list_ptr *s_preds; + int_list_ptr *s_succs; + int *num_preds; + int *num_succs; +{ + int bb, clear_local_bb_vars = 0; + + bzero ((char *) s_preds, n_basic_blocks * sizeof (int_list_ptr)); + bzero ((char *) s_succs, n_basic_blocks * sizeof (int_list_ptr)); + bzero ((char *) num_preds, n_basic_blocks * sizeof (int)); + bzero ((char *) num_succs, n_basic_blocks * sizeof (int)); + + /* This routine can be called after life analysis; in that case + basic_block_drops_in and uid_block_number will not be available + and we must recompute their values. */ + if (basic_block_drops_in == NULL || uid_block_number == NULL) + { + clear_local_bb_vars = 1; + basic_block_drops_in = (char *) alloca (n_basic_blocks); + uid_block_number = (int *) alloca ((get_max_uid () + 1) * sizeof (int)); + + bzero ((char *) basic_block_drops_in, n_basic_blocks * sizeof (char)); + bzero ((char *) uid_block_number, n_basic_blocks * sizeof (int)); + + /* Scan each basic block setting basic_block_drops_in and + uid_block_number as needed. */ + for (bb = 0; bb < n_basic_blocks; bb++) + { + rtx insn, stop_insn; + + if (bb == 0) + stop_insn = NULL_RTX; + else + stop_insn = basic_block_end[bb-1]; + + /* Look backwards from the start of this block. Stop if we + hit the start of the function or the end of a previous + block. Don't walk backwards through blocks that are just + deleted insns! */ + for (insn = PREV_INSN (basic_block_head[bb]); + insn && insn != stop_insn && GET_CODE (insn) == NOTE; + insn = PREV_INSN (insn)) + ; + + /* Never set basic_block_drops_in for the first block. It is + implicit. + + If we stopped on anything other than a BARRIER, then this + block drops in. */ + if (bb != 0) + basic_block_drops_in[bb] = (insn ? GET_CODE (insn) != BARRIER : 1); + + insn = basic_block_head[bb]; + while (insn) + { + BLOCK_NUM (insn) = bb; + if (insn == basic_block_end[bb]) + break; + insn = NEXT_INSN (insn); + } + } + } + + for (bb = 0; bb < n_basic_blocks; bb++) + { + rtx head; + rtx jump; + + head = BLOCK_HEAD (bb); + + if (GET_CODE (head) == CODE_LABEL) + for (jump = LABEL_REFS (head); + jump != head; + jump = LABEL_NEXTREF (jump)) + { + if (! INSN_DELETED_P (CONTAINING_INSN (jump)) + && (GET_CODE (CONTAINING_INSN (jump)) != NOTE + || (NOTE_LINE_NUMBER (CONTAINING_INSN (jump)) + != NOTE_INSN_DELETED))) + add_pred_succ (BLOCK_NUM (CONTAINING_INSN (jump)), bb, + s_preds, s_succs, num_preds, num_succs); + } + + jump = BLOCK_END (bb); + /* If this is a RETURN insn or a conditional jump in the last + basic block, or a non-jump insn in the last basic block, then + this block reaches the exit block. */ + if ((GET_CODE (jump) == JUMP_INSN && GET_CODE (PATTERN (jump)) == RETURN) + || (((GET_CODE (jump) == JUMP_INSN + && condjump_p (jump) && !simplejump_p (jump)) + || GET_CODE (jump) != JUMP_INSN) + && (bb == n_basic_blocks - 1))) + add_pred_succ (bb, EXIT_BLOCK, s_preds, s_succs, num_preds, num_succs); + + if (basic_block_drops_in[bb]) + add_pred_succ (bb - 1, bb, s_preds, s_succs, num_preds, num_succs); + } + + add_pred_succ (ENTRY_BLOCK, 0, s_preds, s_succs, num_preds, num_succs); + + + /* If we allocated any variables in temporary storage, clear out the + pointer to the local storage to avoid dangling pointers. */ + if (clear_local_bb_vars) + { + basic_block_drops_in = NULL; + uid_block_number = NULL; + + } +} + +void +dump_bb_data (file, preds, succs) + FILE *file; + int_list_ptr *preds; + int_list_ptr *succs; +{ + int bb; + int_list_ptr p; + + fprintf (file, "BB data\n\n"); + for (bb = 0; bb < n_basic_blocks; bb++) + { + fprintf (file, "BB %d, start %d, end %d\n", bb, + INSN_UID (BLOCK_HEAD (bb)), INSN_UID (BLOCK_END (bb))); + fprintf (file, " preds:"); + for (p = preds[bb]; p != NULL; p = p->next) { - register int offset = regno / REGSET_ELT_BITS; - register REGSET_ELT_TYPE bit - = (REGSET_ELT_TYPE) 1 << (regno % REGSET_ELT_BITS); - if (basic_block_live_at_start[i][offset] & bit) - fprintf (file, " %d", regno); + int pred_bb = INT_LIST_VAL (p); + if (pred_bb == ENTRY_BLOCK) + fprintf (file, " entry"); + else + fprintf (file, " %d", pred_bb); + } + fprintf (file, "\n"); + fprintf (file, " succs:"); + for (p = succs[bb]; p != NULL; p = p->next) + { + int succ_bb = INT_LIST_VAL (p); + if (succ_bb == EXIT_BLOCK) + fprintf (file, " exit"); + else + fprintf (file, " %d", succ_bb); } fprintf (file, "\n"); } fprintf (file, "\n"); } + +void +dump_sbitmap (file, bmap) + FILE *file; + sbitmap bmap; +{ + int i,j,n; + int set_size = bmap->size; + int total_bits = bmap->n_bits; + + fprintf (file, " "); + for (i = n = 0; i < set_size && n < total_bits; i++) + { + for (j = 0; j < SBITMAP_ELT_BITS && n < total_bits; j++, n++) + { + if (n != 0 && n % 10 == 0) + fprintf (file, " "); + fprintf (file, "%d", (bmap->elms[i] & (1L << j)) != 0); + } + } + fprintf (file, "\n"); +} + +void +dump_sbitmap_vector (file, title, subtitle, bmaps, n_maps) + FILE *file; + char *title, *subtitle; + sbitmap *bmaps; + int n_maps; +{ + int bb; + + fprintf (file, "%s\n", title); + for (bb = 0; bb < n_maps; bb++) + { + fprintf (file, "%s %d\n", subtitle, bb); + dump_sbitmap (file, bmaps[bb]); + } + fprintf (file, "\n"); +} + +/* Free basic block data storage. */ + +void +free_bb_mem () +{ + free_int_list (&pred_int_list_blocks); +} + +/* Bitmap manipulation routines. */ + +/* Allocate a simple bitmap of N_ELMS bits. */ + +sbitmap +sbitmap_alloc (n_elms) + int n_elms; +{ + int bytes, size, amt; + sbitmap bmap; + + size = SBITMAP_SET_SIZE (n_elms); + bytes = size * sizeof (SBITMAP_ELT_TYPE); + amt = (sizeof (struct simple_bitmap_def) + + bytes - sizeof (SBITMAP_ELT_TYPE)); + bmap = (sbitmap) xmalloc (amt); + bmap->n_bits = n_elms; + bmap->size = size; + bmap->bytes = bytes; + return bmap; +} + +/* Allocate a vector of N_VECS bitmaps of N_ELMS bits. */ + +sbitmap * +sbitmap_vector_alloc (n_vecs, n_elms) + int n_vecs, n_elms; +{ + int i, bytes, offset, elm_bytes, size, amt, vector_bytes; + sbitmap *bitmap_vector; + + size = SBITMAP_SET_SIZE (n_elms); + bytes = size * sizeof (SBITMAP_ELT_TYPE); + elm_bytes = (sizeof (struct simple_bitmap_def) + + bytes - sizeof (SBITMAP_ELT_TYPE)); + vector_bytes = n_vecs * sizeof (sbitmap *); + + /* Round up `vector_bytes' to account for the alignment requirements + of an sbitmap. One could allocate the vector-table and set of sbitmaps + separately, but that requires maintaining two pointers or creating + a cover struct to hold both pointers (so our result is still just + one pointer). Neither is a bad idea, but this is simpler for now. */ + { + /* Based on DEFAULT_ALIGNMENT computation in obstack.c. */ + struct { char x; SBITMAP_ELT_TYPE y; } align; + int alignment = (char *) & align.y - & align.x; + vector_bytes = (vector_bytes + alignment - 1) & ~ (alignment - 1); + } + + amt = vector_bytes + (n_vecs * elm_bytes); + bitmap_vector = (sbitmap *) xmalloc (amt); + + for (i = 0, offset = vector_bytes; + i < n_vecs; + i++, offset += elm_bytes) + { + sbitmap b = (sbitmap) ((char *) bitmap_vector + offset); + bitmap_vector[i] = b; + b->n_bits = n_elms; + b->size = size; + b->bytes = bytes; + } + + return bitmap_vector; +} + +/* Copy sbitmap SRC to DST. */ + +void +sbitmap_copy (dst, src) + sbitmap dst, src; +{ + int i; + sbitmap_ptr d,s; + + s = src->elms; + d = dst->elms; + for (i = 0; i < dst->size; i++) + *d++ = *s++; +} + +/* Zero all elements in a bitmap. */ + +void +sbitmap_zero (bmap) + sbitmap bmap; +{ + bzero ((char *) bmap->elms, bmap->bytes); +} + +/* Set to ones all elements in a bitmap. */ + +void +sbitmap_ones (bmap) + sbitmap bmap; +{ + memset (bmap->elms, -1, bmap->bytes); +} + +/* Zero a vector of N_VECS bitmaps. */ + +void +sbitmap_vector_zero (bmap, n_vecs) + sbitmap *bmap; + int n_vecs; +{ + int i; + + for (i = 0; i < n_vecs; i++) + sbitmap_zero (bmap[i]); +} + +/* Set to ones a vector of N_VECS bitmaps. */ + +void +sbitmap_vector_ones (bmap, n_vecs) + sbitmap *bmap; + int n_vecs; +{ + int i; + + for (i = 0; i < n_vecs; i++) + sbitmap_ones (bmap[i]); +} + +/* Set DST to be A union (B - C). + DST = A | (B & ~C). + Return non-zero if any change is made. */ + +int +sbitmap_union_of_diff (dst, a, b, c) + sbitmap dst, a, b, c; +{ + int i,changed; + sbitmap_ptr dstp, ap, bp, cp; + + changed = 0; + dstp = dst->elms; + ap = a->elms; + bp = b->elms; + cp = c->elms; + for (i = 0; i < dst->size; i++) + { + SBITMAP_ELT_TYPE tmp = *ap | (*bp & ~*cp); + if (*dstp != tmp) + changed = 1; + *dstp = tmp; + dstp++; ap++; bp++; cp++; + } + return changed; +} + +/* Set bitmap DST to the bitwise negation of the bitmap SRC. */ + +void +sbitmap_not (dst, src) + sbitmap dst, src; +{ + int i; + sbitmap_ptr dstp, ap; + + dstp = dst->elms; + ap = src->elms; + for (i = 0; i < dst->size; i++) + { + SBITMAP_ELT_TYPE tmp = ~(*ap); + *dstp = tmp; + dstp++; ap++; + } +} + +/* Set the bits in DST to be the difference between the bits + in A and the bits in B. i.e. dst = a - b. + The - operator is implemented as a & (~b). */ + +void +sbitmap_difference (dst, a, b) + sbitmap dst, a, b; +{ + int i; + sbitmap_ptr dstp, ap, bp; + + dstp = dst->elms; + ap = a->elms; + bp = b->elms; + for (i = 0; i < dst->size; i++) + *dstp++ = *ap++ & (~*bp++); +} + +/* Set DST to be (A and B)). + Return non-zero if any change is made. */ + +int +sbitmap_a_and_b (dst, a, b) + sbitmap dst, a, b; +{ + int i,changed; + sbitmap_ptr dstp, ap, bp; + + changed = 0; + dstp = dst->elms; + ap = a->elms; + bp = b->elms; + for (i = 0; i < dst->size; i++) + { + SBITMAP_ELT_TYPE tmp = *ap & *bp; + if (*dstp != tmp) + changed = 1; + *dstp = tmp; + dstp++; ap++; bp++; + } + return changed; +} +/* Set DST to be (A or B)). + Return non-zero if any change is made. */ + +int +sbitmap_a_or_b (dst, a, b) + sbitmap dst, a, b; +{ + int i,changed; + sbitmap_ptr dstp, ap, bp; + + changed = 0; + dstp = dst->elms; + ap = a->elms; + bp = b->elms; + for (i = 0; i < dst->size; i++) + { + SBITMAP_ELT_TYPE tmp = *ap | *bp; + if (*dstp != tmp) + changed = 1; + *dstp = tmp; + dstp++; ap++; bp++; + } + return changed; +} + +/* Set DST to be (A or (B and C)). + Return non-zero if any change is made. */ + +int +sbitmap_a_or_b_and_c (dst, a, b, c) + sbitmap dst, a, b, c; +{ + int i,changed; + sbitmap_ptr dstp, ap, bp, cp; + + changed = 0; + dstp = dst->elms; + ap = a->elms; + bp = b->elms; + cp = c->elms; + for (i = 0; i < dst->size; i++) + { + SBITMAP_ELT_TYPE tmp = *ap | (*bp & *cp); + if (*dstp != tmp) + changed = 1; + *dstp = tmp; + dstp++; ap++; bp++; cp++; + } + return changed; +} + +/* Set DST to be (A ann (B or C)). + Return non-zero if any change is made. */ + +int +sbitmap_a_and_b_or_c (dst, a, b, c) + sbitmap dst, a, b, c; +{ + int i,changed; + sbitmap_ptr dstp, ap, bp, cp; + + changed = 0; + dstp = dst->elms; + ap = a->elms; + bp = b->elms; + cp = c->elms; + for (i = 0; i < dst->size; i++) + { + SBITMAP_ELT_TYPE tmp = *ap & (*bp | *cp); + if (*dstp != tmp) + changed = 1; + *dstp = tmp; + dstp++; ap++; bp++; cp++; + } + return changed; +} + +/* Set the bitmap DST to the intersection of SRC of all predecessors or + successors of block number BB (PRED_SUCC says which). */ + +void +sbitmap_intersect_of_predsucc (dst, src, bb, pred_succ) + sbitmap dst; + sbitmap *src; + int bb; + int_list_ptr *pred_succ; +{ + int_list_ptr ps; + int ps_bb; + int set_size = dst->size; + + ps = pred_succ[bb]; + + /* It is possible that there are no predecessors(/successors). + This can happen for example in unreachable code. */ + + if (ps == NULL) + { + /* In APL-speak this is the `and' reduction of the empty set and thus + the result is the identity for `and'. */ + sbitmap_ones (dst); + return; + } + + /* Set result to first predecessor/successor. */ + + for ( ; ps != NULL; ps = ps->next) + { + ps_bb = INT_LIST_VAL (ps); + if (ps_bb == ENTRY_BLOCK || ps_bb == EXIT_BLOCK) + continue; + sbitmap_copy (dst, src[ps_bb]); + /* Break out since we're only doing first predecessor. */ + break; + } + if (ps == NULL) + return; + + /* Now do the remaining predecessors/successors. */ + + for (ps = ps->next; ps != NULL; ps = ps->next) + { + int i; + sbitmap_ptr p,r; + + ps_bb = INT_LIST_VAL (ps); + if (ps_bb == ENTRY_BLOCK || ps_bb == EXIT_BLOCK) + continue; + + p = src[ps_bb]->elms; + r = dst->elms; + + for (i = 0; i < set_size; i++) + *r++ &= *p++; + } +} + +/* Set the bitmap DST to the intersection of SRC of all predecessors + of block number BB. */ + +void +sbitmap_intersect_of_predecessors (dst, src, bb, s_preds) + sbitmap dst; + sbitmap *src; + int bb; + int_list_ptr *s_preds; +{ + sbitmap_intersect_of_predsucc (dst, src, bb, s_preds); +} + +/* Set the bitmap DST to the intersection of SRC of all successors + of block number BB. */ + +void +sbitmap_intersect_of_successors (dst, src, bb, s_succs) + sbitmap dst; + sbitmap *src; + int bb; + int_list_ptr *s_succs; +{ + sbitmap_intersect_of_predsucc (dst, src, bb, s_succs); +} + +/* Set the bitmap DST to the union of SRC of all predecessors/successors of + block number BB. */ + +void +sbitmap_union_of_predsucc (dst, src, bb, pred_succ) + sbitmap dst; + sbitmap *src; + int bb; + int_list_ptr *pred_succ; +{ + int_list_ptr ps; + int ps_bb; + int set_size = dst->size; + + ps = pred_succ[bb]; + + /* It is possible that there are no predecessors(/successors). + This can happen for example in unreachable code. */ + + if (ps == NULL) + { + /* In APL-speak this is the `or' reduction of the empty set and thus + the result is the identity for `or'. */ + sbitmap_zero (dst); + return; + } + + /* Set result to first predecessor/successor. */ + + for ( ; ps != NULL; ps = ps->next) + { + ps_bb = INT_LIST_VAL (ps); + if (ps_bb == ENTRY_BLOCK || ps_bb == EXIT_BLOCK) + continue; + sbitmap_copy (dst, src[ps_bb]); + /* Break out since we're only doing first predecessor. */ + break; + } + if (ps == NULL) + return; + + /* Now do the remaining predecessors/successors. */ + + for (ps = ps->next; ps != NULL; ps = ps->next) + { + int i; + sbitmap_ptr p,r; + + ps_bb = INT_LIST_VAL (ps); + if (ps_bb == ENTRY_BLOCK || ps_bb == EXIT_BLOCK) + continue; + + p = src[ps_bb]->elms; + r = dst->elms; + + for (i = 0; i < set_size; i++) + *r++ |= *p++; + } +} + +/* Set the bitmap DST to the union of SRC of all predecessors of + block number BB. */ + +void +sbitmap_union_of_predecessors (dst, src, bb, s_preds) + sbitmap dst; + sbitmap *src; + int bb; + int_list_ptr *s_preds; +{ + sbitmap_union_of_predsucc (dst, src, bb, s_preds); +} + +/* Set the bitmap DST to the union of SRC of all predecessors of + block number BB. */ + +void +sbitmap_union_of_successors (dst, src, bb, s_succ) + sbitmap dst; + sbitmap *src; + int bb; + int_list_ptr *s_succ; +{ + sbitmap_union_of_predsucc (dst, src, bb, s_succ); +} + +/* Compute dominator relationships. */ +void +compute_dominators (dominators, post_dominators, s_preds, s_succs) + sbitmap *dominators; + sbitmap *post_dominators; + int_list_ptr *s_preds; + int_list_ptr *s_succs; +{ + int bb, changed, passes; + sbitmap *temp_bitmap; + + temp_bitmap = sbitmap_vector_alloc (n_basic_blocks, n_basic_blocks); + sbitmap_vector_ones (dominators, n_basic_blocks); + sbitmap_vector_ones (post_dominators, n_basic_blocks); + sbitmap_vector_zero (temp_bitmap, n_basic_blocks); + + sbitmap_zero (dominators[0]); + SET_BIT (dominators[0], 0); + + sbitmap_zero (post_dominators[n_basic_blocks-1]); + SET_BIT (post_dominators[n_basic_blocks-1], 0); + + passes = 0; + changed = 1; + while (changed) + { + changed = 0; + for (bb = 1; bb < n_basic_blocks; bb++) + { + sbitmap_intersect_of_predecessors (temp_bitmap[bb], dominators, + bb, s_preds); + SET_BIT (temp_bitmap[bb], bb); + changed |= sbitmap_a_and_b (dominators[bb], + dominators[bb], + temp_bitmap[bb]); + sbitmap_intersect_of_successors (temp_bitmap[bb], post_dominators, + bb, s_succs); + SET_BIT (temp_bitmap[bb], bb); + changed |= sbitmap_a_and_b (post_dominators[bb], + post_dominators[bb], + temp_bitmap[bb]); + } + passes++; + } + + free (temp_bitmap); +} + +/* Count for a single SET rtx, X. */ + +static void +count_reg_sets_1 (x) + rtx x; +{ + register int regno; + register rtx reg = SET_DEST (x); + + /* Find the register that's set/clobbered. */ + while (GET_CODE (reg) == SUBREG || GET_CODE (reg) == ZERO_EXTRACT + || GET_CODE (reg) == SIGN_EXTRACT + || GET_CODE (reg) == STRICT_LOW_PART) + reg = XEXP (reg, 0); + + if (GET_CODE (reg) == REG) + { + regno = REGNO (reg); + if (regno >= FIRST_PSEUDO_REGISTER) + { + /* Count (weighted) references, stores, etc. This counts a + register twice if it is modified, but that is correct. */ + REG_N_SETS (regno)++; + + REG_N_REFS (regno) += loop_depth; + } + } +} + +/* Increment REG_N_SETS for each SET or CLOBBER found in X; also increment + REG_N_REFS by the current loop depth for each SET or CLOBBER found. */ + +static void +count_reg_sets (x) + rtx x; +{ + register RTX_CODE code = GET_CODE (x); + + if (code == SET || code == CLOBBER) + count_reg_sets_1 (x); + else if (code == PARALLEL) + { + register int i; + for (i = XVECLEN (x, 0) - 1; i >= 0; i--) + { + code = GET_CODE (XVECEXP (x, 0, i)); + if (code == SET || code == CLOBBER) + count_reg_sets_1 (XVECEXP (x, 0, i)); + } + } +} + +/* Increment REG_N_REFS by the current loop depth each register reference + found in X. */ + +static void +count_reg_references (x) + rtx x; +{ + register RTX_CODE code; + register int regno; + int i; + + retry: + code = GET_CODE (x); + switch (code) + { + case LABEL_REF: + case SYMBOL_REF: + case CONST_INT: + case CONST: + case CONST_DOUBLE: + case PC: + case ADDR_VEC: + case ADDR_DIFF_VEC: + case ASM_INPUT: + return; + +#ifdef HAVE_cc0 + case CC0: + return; +#endif + + case CLOBBER: + /* If we are clobbering a MEM, mark any registers inside the address + as being used. */ + if (GET_CODE (XEXP (x, 0)) == MEM) + count_reg_references (XEXP (XEXP (x, 0), 0)); + return; + + case SUBREG: + /* While we're here, optimize this case. */ + x = SUBREG_REG (x); + + /* In case the SUBREG is not of a register, don't optimize */ + if (GET_CODE (x) != REG) + { + count_reg_references (x); + return; + } + + /* ... fall through ... */ + + case REG: + if (REGNO (x) >= FIRST_PSEUDO_REGISTER) + REG_N_REFS (REGNO (x)) += loop_depth; + return; + + case SET: + { + register rtx testreg = SET_DEST (x); + int mark_dest = 0; + + /* If storing into MEM, don't show it as being used. But do + show the address as being used. */ + if (GET_CODE (testreg) == MEM) + { + count_reg_references (XEXP (testreg, 0)); + count_reg_references (SET_SRC (x)); + return; + } + + /* Storing in STRICT_LOW_PART is like storing in a reg + in that this SET might be dead, so ignore it in TESTREG. + but in some other ways it is like using the reg. + + Storing in a SUBREG or a bit field is like storing the entire + register in that if the register's value is not used + then this SET is not needed. */ + while (GET_CODE (testreg) == STRICT_LOW_PART + || GET_CODE (testreg) == ZERO_EXTRACT + || GET_CODE (testreg) == SIGN_EXTRACT + || GET_CODE (testreg) == SUBREG) + { + /* Modifying a single register in an alternate mode + does not use any of the old value. But these other + ways of storing in a register do use the old value. */ + if (GET_CODE (testreg) == SUBREG + && !(REG_SIZE (SUBREG_REG (testreg)) > REG_SIZE (testreg))) + ; + else + mark_dest = 1; + + testreg = XEXP (testreg, 0); + } + + /* If this is a store into a register, + recursively scan the value being stored. */ + + if (GET_CODE (testreg) == REG) + { + count_reg_references (SET_SRC (x)); + if (mark_dest) + count_reg_references (SET_DEST (x)); + return; + } + } + break; + + default: + break; + } + + /* Recursively scan the operands of this expression. */ + + { + register char *fmt = GET_RTX_FORMAT (code); + register int i; + + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + { + /* Tail recursive case: save a function call level. */ + if (i == 0) + { + x = XEXP (x, 0); + goto retry; + } + count_reg_references (XEXP (x, i)); + } + else if (fmt[i] == 'E') + { + register int j; + for (j = 0; j < XVECLEN (x, i); j++) + count_reg_references (XVECEXP (x, i, j)); + } + } + } +} + +/* Recompute register set/reference counts immediately prior to register + allocation. + + This avoids problems with set/reference counts changing to/from values + which have special meanings to the register allocators. + + Additionally, the reference counts are the primary component used by the + register allocators to prioritize pseudos for allocation to hard regs. + More accurate reference counts generally lead to better register allocation. + + It might be worthwhile to update REG_LIVE_LENGTH, REG_BASIC_BLOCK and + possibly other information which is used by the register allocators. */ + +void +recompute_reg_usage (f) + rtx f; +{ + rtx insn; + int i, max_reg; + + /* Clear out the old data. */ + max_reg = max_reg_num (); + for (i = FIRST_PSEUDO_REGISTER; i < max_reg; i++) + { + REG_N_SETS (i) = 0; + REG_N_REFS (i) = 0; + } + + /* Scan each insn in the chain and count how many times each register is + set/used. */ + loop_depth = 1; + for (insn = f; insn; insn = NEXT_INSN (insn)) + { + /* Keep track of loop depth. */ + if (GET_CODE (insn) == NOTE) + { + /* Look for loop boundaries. */ + if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END) + loop_depth--; + else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG) + loop_depth++; + + /* If we have LOOP_DEPTH == 0, there has been a bookkeeping error. + Abort now rather than setting register status incorrectly. */ + if (loop_depth == 0) + abort (); + } + else if (GET_RTX_CLASS (GET_CODE (insn)) == 'i') + { + rtx links; + + /* This call will increment REG_N_SETS for each SET or CLOBBER + of a register in INSN. It will also increment REG_N_REFS + by the loop depth for each set of a register in INSN. */ + count_reg_sets (PATTERN (insn)); + + /* count_reg_sets does not detect autoincrement address modes, so + detect them here by looking at the notes attached to INSN. */ + for (links = REG_NOTES (insn); links; links = XEXP (links, 1)) + { + if (REG_NOTE_KIND (links) == REG_INC) + /* Count (weighted) references, stores, etc. This counts a + register twice if it is modified, but that is correct. */ + REG_N_SETS (REGNO (XEXP (links, 0)))++; + } + + /* This call will increment REG_N_REFS by the current loop depth for + each reference to a register in INSN. */ + count_reg_references (PATTERN (insn)); + + /* count_reg_references will not include counts for arguments to + function calls, so detect them here by examining the + CALL_INSN_FUNCTION_USAGE data. */ + if (GET_CODE (insn) == CALL_INSN) + { + rtx note; + + for (note = CALL_INSN_FUNCTION_USAGE (insn); + note; + note = XEXP (note, 1)) + if (GET_CODE (XEXP (note, 0)) == USE) + count_reg_references (SET_DEST (XEXP (note, 0))); + } + } + } +} |