From e83f48010bb27b7fd1db2ebdecc9e4611fe97793 Mon Sep 17 00:00:00 2001 From: Steven Bosscher Date: Fri, 14 May 2004 15:35:11 +0000 Subject: [PATCH] passes.c (rest_of_handle_null_pointer): Remove. * passes.c (rest_of_handle_null_pointer): Remove. (rest_of_handle_cse): Don't call rest_of_handle_null_pointer. (rest_of_compilation): Likewise. * rtl.h (delete_null_pointer_checks): Remove prototype. * gcse.c (rd_kill, rd_gen, reaching_defs, rd_out, ae_in, ae_out): Remove declarations. (get_bitmap_width, alloc_rd_mem, free_rd_mem, handle_rd_kill_set, compute_kill_rd, compute_rd, alloc_avail_expr_mem, free_avail_expr_mem, compute_ae_gen, expr_killed_p, compute_ae_kill, expr_reaches_here_p, computing_insn, def_reaches_here_p, can_disregard_other_sets, handle_avail_expr, classic_gcse, one_classic_gcse_pass, invalidate_nonnull_info, delete_null_pointer_checks_1, delete_null_pointer_checks, expr_reached_here_p_work): Remove. (gcse_main): Do not perform classic GCSE when optimizing for size. (alloc_pre_mem, free_pre_mem): Don't touch ae_in and ae_out, they are never used. From-SVN: r81849 --- gcc/ChangeLog | 20 + gcc/gcse.c | 1431 +++++-------------------------------------------- gcc/passes.c | 30 -- gcc/rtl.h | 2 - 4 files changed, 169 insertions(+), 1314 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 97cb0440a85..67de34125e3 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,23 @@ +2004-05-14 Steven Bosscher + + * passes.c (rest_of_handle_null_pointer): Remove. + (rest_of_handle_cse): Don't call rest_of_handle_null_pointer. + (rest_of_compilation): Likewise. + * rtl.h (delete_null_pointer_checks): Remove prototype. + * gcse.c (rd_kill, rd_gen, reaching_defs, rd_out, ae_in, ae_out): + Remove declarations. + (get_bitmap_width, alloc_rd_mem, free_rd_mem, handle_rd_kill_set, + compute_kill_rd, compute_rd, alloc_avail_expr_mem, + free_avail_expr_mem, compute_ae_gen, expr_killed_p, compute_ae_kill, + expr_reaches_here_p, computing_insn, def_reaches_here_p, + can_disregard_other_sets, handle_avail_expr, classic_gcse, + one_classic_gcse_pass, invalidate_nonnull_info, + delete_null_pointer_checks_1, delete_null_pointer_checks, + expr_reached_here_p_work): Remove. + (gcse_main): Do not perform classic GCSE when optimizing for size. + (alloc_pre_mem, free_pre_mem): Don't touch ae_in and ae_out, they + are never used. + 2004-05-14 Andrew Pinski PR optimization/14466 diff --git a/gcc/gcse.c b/gcc/gcse.c index 9e6ced54dd1..b6d0a6b4d4a 100644 --- a/gcc/gcse.c +++ b/gcc/gcse.c @@ -192,7 +192,8 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA 3) Perform copy/constant propagation. - 4) Perform global cse. + 4) Perform global cse using lazy code motion if not optimizing + for size, or code hoisting if we are. 5) Perform another pass of copy/constant propagation. @@ -485,7 +486,7 @@ static struct ls_expr * pre_ldst_mems = NULL; static regset reg_set_bitmap; /* For each block, a bitmap of registers set in the block. - This is used by expr_killed_p and compute_transp. + This is used by compute_transp. It is computed during hash table computation and not by compute_sets as it includes registers added since the last pass (or between cprop and gcse) and it's currently not easy to realloc sbitmap vectors. */ @@ -515,25 +516,8 @@ static int const_prop_count; /* Number of copys propagated. */ static int copy_prop_count; -/* These variables are used by classic GCSE. - Normally they'd be defined a bit later, but `rd_gen' needs to - be declared sooner. */ - -/* Each block has a bitmap of each type. - The length of each blocks bitmap is: - - max_cuid - for reaching definitions - n_exprs - for available expressions - - Thus we view the bitmaps as 2 dimensional arrays. i.e. - rd_kill[block_num][cuid_num] - ae_kill[block_num][expr_num] */ - -/* For reaching defs */ -static sbitmap *rd_kill, *rd_gen, *reaching_defs, *rd_out; - -/* for available exprs */ -static sbitmap *ae_kill, *ae_gen, *ae_in, *ae_out; +/* For available exprs */ +static sbitmap *ae_kill, *ae_gen; /* Objects of this type are passed around by the null-pointer check removal routines. */ @@ -558,7 +542,6 @@ static void alloc_gcse_mem (rtx); static void free_gcse_mem (void); static void alloc_reg_set_mem (int); static void free_reg_set_mem (void); -static int get_bitmap_width (int, int, int); static void record_one_set (int, rtx); static void replace_one_set (int, rtx, rtx); static void record_set_info (rtx, rtx, void *); @@ -640,31 +623,8 @@ static void compute_code_hoist_data (void); static int hoist_expr_reaches_here_p (basic_block, int, basic_block, char *); static void hoist_code (void); static int one_code_hoisting_pass (void); -static void alloc_rd_mem (int, int); -static void free_rd_mem (void); -static void handle_rd_kill_set (rtx, int, basic_block); -static void compute_kill_rd (void); -static void compute_rd (void); -static void alloc_avail_expr_mem (int, int); -static void free_avail_expr_mem (void); -static void compute_ae_gen (struct hash_table *); -static int expr_killed_p (rtx, basic_block); -static void compute_ae_kill (sbitmap *, sbitmap *, struct hash_table *); -static int expr_reaches_here_p (struct occr *, struct expr *, basic_block, - int); -static rtx computing_insn (struct expr *, rtx); -static int def_reaches_here_p (rtx, rtx); -static int can_disregard_other_sets (struct reg_set **, rtx, int); -static int handle_avail_expr (rtx, struct expr *); -static int classic_gcse (void); -static int one_classic_gcse_pass (int); -static void invalidate_nonnull_info (rtx, rtx, void *); -static int delete_null_pointer_checks_1 (unsigned int *, sbitmap *, sbitmap *, - struct null_pointer_info *); static rtx process_insert_insn (struct expr *); static int pre_edge_insert (struct edge_list *, struct expr **); -static int expr_reaches_here_p_work (struct occr *, struct expr *, - basic_block, int, char *); static int pre_expr_reaches_here_p_work (basic_block, struct expr *, basic_block, char *); static struct ls_expr * ldst_entry (rtx); @@ -790,7 +750,7 @@ gcse_main (rtx f, FILE *file) changed = one_cprop_pass (pass + 1, 0, 0); if (optimize_size) - changed |= one_classic_gcse_pass (pass + 1); + /* Do nothing. */ ; else { changed |= one_pre_gcse_pass (pass + 1); @@ -821,8 +781,7 @@ gcse_main (rtx f, FILE *file) /* It does not make sense to run code hoisting unless we are optimizing for code size -- it rarely makes programs faster, and can make them bigger if we did partial redundancy elimination (when optimizing - for space, we use a classic gcse algorithm instead of partial - redundancy algorithms). */ + for space, we don't run the partial redundancy algorithms). */ if (optimize_size) { max_gcse_regno = max_reg_num (); @@ -1025,46 +984,6 @@ free_gcse_mem (void) BITMAP_XFREE (modify_mem_list_set); BITMAP_XFREE (canon_modify_mem_list_set); } - -/* Many of the global optimization algorithms work by solving dataflow - equations for various expressions. Initially, some local value is - computed for each expression in each block. Then, the values across the - various blocks are combined (by following flow graph edges) to arrive at - global values. Conceptually, each set of equations is independent. We - may therefore solve all the equations in parallel, solve them one at a - time, or pick any intermediate approach. - - When you're going to need N two-dimensional bitmaps, each X (say, the - number of blocks) by Y (say, the number of expressions), call this - function. It's not important what X and Y represent; only that Y - correspond to the things that can be done in parallel. This function will - return an appropriate chunking factor C; you should solve C sets of - equations in parallel. By going through this function, we can easily - trade space against time; by solving fewer equations in parallel we use - less space. */ - -static int -get_bitmap_width (int n, int x, int y) -{ - /* It's not really worth figuring out *exactly* how much memory will - be used by a particular choice. The important thing is to get - something approximately right. */ - size_t max_bitmap_memory = 10 * 1024 * 1024; - - /* The number of bytes we'd use for a single column of minimum - width. */ - size_t column_size = n * x * sizeof (SBITMAP_ELT_TYPE); - - /* Often, it's reasonable just to solve all the equations in - parallel. */ - if (column_size * SBITMAP_SET_SIZE (y) <= max_bitmap_memory) - return y; - - /* Otherwise, pick the largest width we can, without going over the - limit. */ - return SBITMAP_ELT_BITS * ((max_bitmap_memory + column_size - 1) - / column_size); -} /* Compute the local properties of each recorded expression. @@ -2878,204 +2797,136 @@ mark_oprs_set (rtx insn) } -/* Classic GCSE reaching definition support. */ - -/* Allocate reaching def variables. */ - -static void -alloc_rd_mem (int n_blocks, int n_insns) -{ - rd_kill = sbitmap_vector_alloc (n_blocks, n_insns); - sbitmap_vector_zero (rd_kill, n_blocks); - - rd_gen = sbitmap_vector_alloc (n_blocks, n_insns); - sbitmap_vector_zero (rd_gen, n_blocks); +/* Compute copy/constant propagation working variables. */ - reaching_defs = sbitmap_vector_alloc (n_blocks, n_insns); - sbitmap_vector_zero (reaching_defs, n_blocks); +/* Local properties of assignments. */ +static sbitmap *cprop_pavloc; +static sbitmap *cprop_absaltered; - rd_out = sbitmap_vector_alloc (n_blocks, n_insns); - sbitmap_vector_zero (rd_out, n_blocks); -} +/* Global properties of assignments (computed from the local properties). */ +static sbitmap *cprop_avin; +static sbitmap *cprop_avout; -/* Free reaching def variables. */ +/* Allocate vars used for copy/const propagation. N_BLOCKS is the number of + basic blocks. N_SETS is the number of sets. */ static void -free_rd_mem (void) +alloc_cprop_mem (int n_blocks, int n_sets) { - sbitmap_vector_free (rd_kill); - sbitmap_vector_free (rd_gen); - sbitmap_vector_free (reaching_defs); - sbitmap_vector_free (rd_out); + cprop_pavloc = sbitmap_vector_alloc (n_blocks, n_sets); + cprop_absaltered = sbitmap_vector_alloc (n_blocks, n_sets); + + cprop_avin = sbitmap_vector_alloc (n_blocks, n_sets); + cprop_avout = sbitmap_vector_alloc (n_blocks, n_sets); } -/* Add INSN to the kills of BB. REGNO, set in BB, is killed by INSN. */ +/* Free vars used by copy/const propagation. */ static void -handle_rd_kill_set (rtx insn, int regno, basic_block bb) +free_cprop_mem (void) { - struct reg_set *this_reg; - - for (this_reg = reg_set_table[regno]; this_reg; this_reg = this_reg ->next) - if (BLOCK_NUM (this_reg->insn) != BLOCK_NUM (insn)) - SET_BIT (rd_kill[bb->index], INSN_CUID (this_reg->insn)); + sbitmap_vector_free (cprop_pavloc); + sbitmap_vector_free (cprop_absaltered); + sbitmap_vector_free (cprop_avin); + sbitmap_vector_free (cprop_avout); } -/* Compute the set of kill's for reaching definitions. */ +/* For each block, compute whether X is transparent. X is either an + expression or an assignment [though we don't care which, for this context + an assignment is treated as an expression]. For each block where an + element of X is modified, set (SET_P == 1) or reset (SET_P == 0) the INDX + bit in BMAP. */ static void -compute_kill_rd (void) +compute_transp (rtx x, int indx, sbitmap *bmap, int set_p) { - int cuid; - unsigned int regno; - int i; + int i, j; basic_block bb; + enum rtx_code code; + reg_set *r; + const char *fmt; - /* For each block - For each set bit in `gen' of the block (i.e each insn which - generates a definition in the block) - Call the reg set by the insn corresponding to that bit regx - Look at the linked list starting at reg_set_table[regx] - For each setting of regx in the linked list, which is not in - this block - Set the bit in `kill' corresponding to that insn. */ - FOR_EACH_BB (bb) - for (cuid = 0; cuid < max_cuid; cuid++) - if (TEST_BIT (rd_gen[bb->index], cuid)) - { - rtx insn = CUID_INSN (cuid); - rtx pat = PATTERN (insn); + /* repeat is used to turn tail-recursion into iteration since GCC + can't do it when there's no return value. */ + repeat: - if (GET_CODE (insn) == CALL_INSN) + if (x == 0) + return; + + code = GET_CODE (x); + switch (code) + { + case REG: + if (set_p) + { + if (REGNO (x) < FIRST_PSEUDO_REGISTER) { - for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) - if (TEST_HARD_REG_BIT (regs_invalidated_by_call, regno)) - handle_rd_kill_set (insn, regno, bb); + FOR_EACH_BB (bb) + if (TEST_BIT (reg_set_in_block[bb->index], REGNO (x))) + SET_BIT (bmap[bb->index], indx); } - - if (GET_CODE (pat) == PARALLEL) + else { - for (i = XVECLEN (pat, 0) - 1; i >= 0; i--) - { - enum rtx_code code = GET_CODE (XVECEXP (pat, 0, i)); - - if ((code == SET || code == CLOBBER) - && GET_CODE (XEXP (XVECEXP (pat, 0, i), 0)) == REG) - handle_rd_kill_set (insn, - REGNO (XEXP (XVECEXP (pat, 0, i), 0)), - bb); - } + for (r = reg_set_table[REGNO (x)]; r != NULL; r = r->next) + SET_BIT (bmap[BLOCK_NUM (r->insn)], indx); } - else if (GET_CODE (pat) == SET && GET_CODE (SET_DEST (pat)) == REG) - /* Each setting of this register outside of this block - must be marked in the set of kills in this block. */ - handle_rd_kill_set (insn, REGNO (SET_DEST (pat)), bb); } -} - -/* Compute the reaching definitions as in - Compilers Principles, Techniques, and Tools. Aho, Sethi, Ullman, - Chapter 10. It is the same algorithm as used for computing available - expressions but applied to the gens and kills of reaching definitions. */ - -static void -compute_rd (void) -{ - int changed, passes; - basic_block bb; - - FOR_EACH_BB (bb) - sbitmap_copy (rd_out[bb->index] /*dst*/, rd_gen[bb->index] /*src*/); - - passes = 0; - changed = 1; - while (changed) - { - changed = 0; - FOR_EACH_BB (bb) + else { - sbitmap_union_of_preds (reaching_defs[bb->index], rd_out, bb->index); - changed |= sbitmap_union_of_diff_cg (rd_out[bb->index], rd_gen[bb->index], - reaching_defs[bb->index], rd_kill[bb->index]); + if (REGNO (x) < FIRST_PSEUDO_REGISTER) + { + FOR_EACH_BB (bb) + if (TEST_BIT (reg_set_in_block[bb->index], REGNO (x))) + RESET_BIT (bmap[bb->index], indx); + } + else + { + for (r = reg_set_table[REGNO (x)]; r != NULL; r = r->next) + RESET_BIT (bmap[BLOCK_NUM (r->insn)], indx); + } } - passes++; - } - - if (gcse_file) - fprintf (gcse_file, "reaching def computation: %d passes\n", passes); -} - -/* Classic GCSE available expression support. */ - -/* Allocate memory for available expression computation. */ - -static void -alloc_avail_expr_mem (int n_blocks, int n_exprs) -{ - ae_kill = sbitmap_vector_alloc (n_blocks, n_exprs); - sbitmap_vector_zero (ae_kill, n_blocks); - - ae_gen = sbitmap_vector_alloc (n_blocks, n_exprs); - sbitmap_vector_zero (ae_gen, n_blocks); - - ae_in = sbitmap_vector_alloc (n_blocks, n_exprs); - sbitmap_vector_zero (ae_in, n_blocks); - - ae_out = sbitmap_vector_alloc (n_blocks, n_exprs); - sbitmap_vector_zero (ae_out, n_blocks); -} - -static void -free_avail_expr_mem (void) -{ - sbitmap_vector_free (ae_kill); - sbitmap_vector_free (ae_gen); - sbitmap_vector_free (ae_in); - sbitmap_vector_free (ae_out); -} - -/* Compute the set of available expressions generated in each basic block. */ -static void -compute_ae_gen (struct hash_table *expr_hash_table) -{ - unsigned int i; - struct expr *expr; - struct occr *occr; + return; - /* For each recorded occurrence of each expression, set ae_gen[bb][expr]. - This is all we have to do because an expression is not recorded if it - is not available, and the only expressions we want to work with are the - ones that are recorded. */ - for (i = 0; i < expr_hash_table->size; i++) - for (expr = expr_hash_table->table[i]; expr != 0; expr = expr->next_same_hash) - for (occr = expr->avail_occr; occr != 0; occr = occr->next) - SET_BIT (ae_gen[BLOCK_NUM (occr->insn)], expr->bitmap_index); -} + case MEM: + FOR_EACH_BB (bb) + { + rtx list_entry = canon_modify_mem_list[bb->index]; -/* Return nonzero if expression X is killed in BB. */ + while (list_entry) + { + rtx dest, dest_addr; -static int -expr_killed_p (rtx x, basic_block bb) -{ - int i, j; - enum rtx_code code; - const char *fmt; + if (GET_CODE (XEXP (list_entry, 0)) == CALL_INSN) + { + if (set_p) + SET_BIT (bmap[bb->index], indx); + else + RESET_BIT (bmap[bb->index], indx); + break; + } + /* LIST_ENTRY must be an INSN of some kind that sets memory. + Examine each hunk of memory that is modified. */ - if (x == 0) - return 1; + dest = XEXP (list_entry, 0); + list_entry = XEXP (list_entry, 1); + dest_addr = XEXP (list_entry, 0); - code = GET_CODE (x); - switch (code) - { - case REG: - return TEST_BIT (reg_set_in_block[bb->index], REGNO (x)); + if (canon_true_dependence (dest, GET_MODE (dest), dest_addr, + x, rtx_addr_varies_p)) + { + if (set_p) + SET_BIT (bmap[bb->index], indx); + else + RESET_BIT (bmap[bb->index], indx); + break; + } + list_entry = XEXP (list_entry, 1); + } + } - case MEM: - if (load_killed_in_block_p (bb, get_max_uid () + 1, x, 0)) - return 1; - else - return expr_killed_p (XEXP (x, 0), bb); + x = XEXP (x, 0); + goto repeat; case PC: case CC0: /*FIXME*/ @@ -3087,7 +2938,7 @@ expr_killed_p (rtx x, basic_block bb) case LABEL_REF: case ADDR_VEC: case ADDR_DIFF_VEC: - return 0; + return; default: break; @@ -3101,755 +2952,68 @@ expr_killed_p (rtx x, basic_block bb) needed at this level, change it into iteration. This function is called enough to be worth it. */ if (i == 0) - return expr_killed_p (XEXP (x, i), bb); - else if (expr_killed_p (XEXP (x, i), bb)) - return 1; + { + x = XEXP (x, i); + goto repeat; + } + + compute_transp (XEXP (x, i), indx, bmap, set_p); } else if (fmt[i] == 'E') for (j = 0; j < XVECLEN (x, i); j++) - if (expr_killed_p (XVECEXP (x, i, j), bb)) - return 1; + compute_transp (XVECEXP (x, i, j), indx, bmap, set_p); } - - return 0; } -/* Compute the set of available expressions killed in each basic block. */ +/* Top level routine to do the dataflow analysis needed by copy/const + propagation. */ static void -compute_ae_kill (sbitmap *ae_gen, sbitmap *ae_kill, - struct hash_table *expr_hash_table) +compute_cprop_data (void) { - basic_block bb; - unsigned int i; - struct expr *expr; - - FOR_EACH_BB (bb) - for (i = 0; i < expr_hash_table->size; i++) - for (expr = expr_hash_table->table[i]; expr; expr = expr->next_same_hash) - { - /* Skip EXPR if generated in this block. */ - if (TEST_BIT (ae_gen[bb->index], expr->bitmap_index)) - continue; - - if (expr_killed_p (expr->expr, bb)) - SET_BIT (ae_kill[bb->index], expr->bitmap_index); - } + compute_local_properties (cprop_absaltered, cprop_pavloc, NULL, &set_hash_table); + compute_available (cprop_pavloc, cprop_absaltered, + cprop_avout, cprop_avin); } -/* Actually perform the Classic GCSE optimizations. */ +/* Copy/constant propagation. */ -/* Return nonzero if occurrence OCCR of expression EXPR reaches block BB. +/* Maximum number of register uses in an insn that we handle. */ +#define MAX_USES 8 - CHECK_SELF_LOOP is nonzero if we should consider a block reaching itself - as a positive reach. We want to do this when there are two computations - of the expression in the block. +/* Table of uses found in an insn. + Allocated statically to avoid alloc/free complexity and overhead. */ +static struct reg_use reg_use_table[MAX_USES]; - VISITED is a pointer to a working buffer for tracking which BB's have - been visited. It is NULL for the top-level call. +/* Index into `reg_use_table' while building it. */ +static int reg_use_count; - We treat reaching expressions that go through blocks containing the same - reaching expression as "not reaching". E.g. if EXPR is generated in blocks - 2 and 3, INSN is in block 4, and 2->3->4, we treat the expression in block - 2 as not reaching. The intent is to improve the probability of finding - only one reaching expression and to reduce register lifetimes by picking - the closest such expression. */ +/* Set up a list of register numbers used in INSN. The found uses are stored + in `reg_use_table'. `reg_use_count' is initialized to zero before entry, + and contains the number of uses in the table upon exit. -static int -expr_reaches_here_p_work (struct occr *occr, struct expr *expr, - basic_block bb, int check_self_loop, char *visited) + ??? If a register appears multiple times we will record it multiple times. + This doesn't hurt anything but it will slow things down. */ + +static void +find_used_regs (rtx *xptr, void *data ATTRIBUTE_UNUSED) { - edge pred; + int i, j; + enum rtx_code code; + const char *fmt; + rtx x = *xptr; - for (pred = bb->pred; pred != NULL; pred = pred->pred_next) + /* repeat is used to turn tail-recursion into iteration since GCC + can't do it when there's no return value. */ + repeat: + if (x == 0) + return; + + code = GET_CODE (x); + if (REG_P (x)) { - basic_block pred_bb = pred->src; - - if (visited[pred_bb->index]) - /* This predecessor has already been visited. Nothing to do. */ - ; - else if (pred_bb == bb) - { - /* BB loops on itself. */ - if (check_self_loop - && TEST_BIT (ae_gen[pred_bb->index], expr->bitmap_index) - && BLOCK_NUM (occr->insn) == pred_bb->index) - return 1; - - visited[pred_bb->index] = 1; - } - - /* Ignore this predecessor if it kills the expression. */ - else if (TEST_BIT (ae_kill[pred_bb->index], expr->bitmap_index)) - visited[pred_bb->index] = 1; - - /* Does this predecessor generate this expression? */ - else if (TEST_BIT (ae_gen[pred_bb->index], expr->bitmap_index)) - { - /* Is this the occurrence we're looking for? - Note that there's only one generating occurrence per block - so we just need to check the block number. */ - if (BLOCK_NUM (occr->insn) == pred_bb->index) - return 1; - - visited[pred_bb->index] = 1; - } - - /* Neither gen nor kill. */ - else - { - visited[pred_bb->index] = 1; - if (expr_reaches_here_p_work (occr, expr, pred_bb, check_self_loop, - visited)) - - return 1; - } - } - - /* All paths have been checked. */ - return 0; -} - -/* This wrapper for expr_reaches_here_p_work() is to ensure that any - memory allocated for that function is returned. */ - -static int -expr_reaches_here_p (struct occr *occr, struct expr *expr, basic_block bb, - int check_self_loop) -{ - int rval; - char *visited = xcalloc (last_basic_block, 1); - - rval = expr_reaches_here_p_work (occr, expr, bb, check_self_loop, visited); - - free (visited); - return rval; -} - -/* Return the instruction that computes EXPR that reaches INSN's basic block. - If there is more than one such instruction, return NULL. - - Called only by handle_avail_expr. */ - -static rtx -computing_insn (struct expr *expr, rtx insn) -{ - basic_block bb = BLOCK_FOR_INSN (insn); - - if (expr->avail_occr->next == NULL) - { - if (BLOCK_FOR_INSN (expr->avail_occr->insn) == bb) - /* The available expression is actually itself - (i.e. a loop in the flow graph) so do nothing. */ - return NULL; - - /* (FIXME) Case that we found a pattern that was created by - a substitution that took place. */ - return expr->avail_occr->insn; - } - else - { - /* Pattern is computed more than once. - Search backwards from this insn to see how many of these - computations actually reach this insn. */ - struct occr *occr; - rtx insn_computes_expr = NULL; - int can_reach = 0; - - for (occr = expr->avail_occr; occr != NULL; occr = occr->next) - { - if (BLOCK_FOR_INSN (occr->insn) == bb) - { - /* The expression is generated in this block. - The only time we care about this is when the expression - is generated later in the block [and thus there's a loop]. - We let the normal cse pass handle the other cases. */ - if (INSN_CUID (insn) < INSN_CUID (occr->insn) - && expr_reaches_here_p (occr, expr, bb, 1)) - { - can_reach++; - if (can_reach > 1) - return NULL; - - insn_computes_expr = occr->insn; - } - } - else if (expr_reaches_here_p (occr, expr, bb, 0)) - { - can_reach++; - if (can_reach > 1) - return NULL; - - insn_computes_expr = occr->insn; - } - } - - if (insn_computes_expr == NULL) - abort (); - - return insn_computes_expr; - } -} - -/* Return nonzero if the definition in DEF_INSN can reach INSN. - Only called by can_disregard_other_sets. */ - -static int -def_reaches_here_p (rtx insn, rtx def_insn) -{ - rtx reg; - - if (TEST_BIT (reaching_defs[BLOCK_NUM (insn)], INSN_CUID (def_insn))) - return 1; - - if (BLOCK_NUM (insn) == BLOCK_NUM (def_insn)) - { - if (INSN_CUID (def_insn) < INSN_CUID (insn)) - { - if (GET_CODE (PATTERN (def_insn)) == PARALLEL) - return 1; - else if (GET_CODE (PATTERN (def_insn)) == CLOBBER) - reg = XEXP (PATTERN (def_insn), 0); - else if (GET_CODE (PATTERN (def_insn)) == SET) - reg = SET_DEST (PATTERN (def_insn)); - else - abort (); - - return ! reg_set_between_p (reg, NEXT_INSN (def_insn), insn); - } - else - return 0; - } - - return 0; -} - -/* Return nonzero if *ADDR_THIS_REG can only have one value at INSN. The - value returned is the number of definitions that reach INSN. Returning a - value of zero means that [maybe] more than one definition reaches INSN and - the caller can't perform whatever optimization it is trying. i.e. it is - always safe to return zero. */ - -static int -can_disregard_other_sets (struct reg_set **addr_this_reg, rtx insn, int for_combine) -{ - int number_of_reaching_defs = 0; - struct reg_set *this_reg; - - for (this_reg = *addr_this_reg; this_reg != 0; this_reg = this_reg->next) - if (def_reaches_here_p (insn, this_reg->insn)) - { - number_of_reaching_defs++; - /* Ignore parallels for now. */ - if (GET_CODE (PATTERN (this_reg->insn)) == PARALLEL) - return 0; - - if (!for_combine - && (GET_CODE (PATTERN (this_reg->insn)) == CLOBBER - || ! rtx_equal_p (SET_SRC (PATTERN (this_reg->insn)), - SET_SRC (PATTERN (insn))))) - /* A setting of the reg to a different value reaches INSN. */ - return 0; - - if (number_of_reaching_defs > 1) - { - /* If in this setting the value the register is being set to is - equal to the previous value the register was set to and this - setting reaches the insn we are trying to do the substitution - on then we are ok. */ - if (GET_CODE (PATTERN (this_reg->insn)) == CLOBBER) - return 0; - else if (! rtx_equal_p (SET_SRC (PATTERN (this_reg->insn)), - SET_SRC (PATTERN (insn)))) - return 0; - } - - *addr_this_reg = this_reg; - } - - return number_of_reaching_defs; -} - -/* Expression computed by insn is available and the substitution is legal, - so try to perform the substitution. - - The result is nonzero if any changes were made. */ - -static int -handle_avail_expr (rtx insn, struct expr *expr) -{ - rtx pat, insn_computes_expr, expr_set; - rtx to; - struct reg_set *this_reg; - int found_setting, use_src; - int changed = 0; - - /* We only handle the case where one computation of the expression - reaches this instruction. */ - insn_computes_expr = computing_insn (expr, insn); - if (insn_computes_expr == NULL) - return 0; - expr_set = single_set (insn_computes_expr); - /* The set might be in a parallel with multiple sets; we could - probably handle that, but there's currently no easy way to find - the relevant sub-expression. */ - if (!expr_set) - return 0; - - found_setting = 0; - use_src = 0; - - /* At this point we know only one computation of EXPR outside of this - block reaches this insn. Now try to find a register that the - expression is computed into. */ - if (GET_CODE (SET_SRC (expr_set)) == REG) - { - /* This is the case when the available expression that reaches - here has already been handled as an available expression. */ - unsigned int regnum_for_replacing - = REGNO (SET_SRC (expr_set)); - - /* If the register was created by GCSE we can't use `reg_set_table', - however we know it's set only once. */ - if (regnum_for_replacing >= max_gcse_regno - /* If the register the expression is computed into is set only once, - or only one set reaches this insn, we can use it. */ - || (((this_reg = reg_set_table[regnum_for_replacing]), - this_reg->next == NULL) - || can_disregard_other_sets (&this_reg, insn, 0))) - { - use_src = 1; - found_setting = 1; - } - } - - if (!found_setting) - { - unsigned int regnum_for_replacing - = REGNO (SET_DEST (expr_set)); - - /* This shouldn't happen. */ - if (regnum_for_replacing >= max_gcse_regno) - abort (); - - this_reg = reg_set_table[regnum_for_replacing]; - - /* If the register the expression is computed into is set only once, - or only one set reaches this insn, use it. */ - if (this_reg->next == NULL - || can_disregard_other_sets (&this_reg, insn, 0)) - found_setting = 1; - } - - if (found_setting) - { - pat = PATTERN (insn); - if (use_src) - to = SET_SRC (expr_set); - else - to = SET_DEST (expr_set); - changed = validate_change (insn, &SET_SRC (pat), to, 0); - - /* We should be able to ignore the return code from validate_change but - to play it safe we check. */ - if (changed) - { - gcse_subst_count++; - if (gcse_file != NULL) - { - fprintf (gcse_file, "GCSE: Replacing the source in insn %d with", - INSN_UID (insn)); - fprintf (gcse_file, " reg %d %s insn %d\n", - REGNO (to), use_src ? "from" : "set in", - INSN_UID (insn_computes_expr)); - } - } - } - - /* The register that the expr is computed into is set more than once. */ - else if (1 /*expensive_op(this_pattrn->op) && do_expensive_gcse)*/) - { - /* Insert an insn after insnx that copies the reg set in insnx - into a new pseudo register call this new register REGN. - From insnb until end of basic block or until REGB is set - replace all uses of REGB with REGN. */ - rtx new_insn; - - to = gen_reg_rtx (GET_MODE (SET_DEST (expr_set))); - - /* Generate the new insn. */ - /* ??? If the change fails, we return 0, even though we created - an insn. I think this is ok. */ - new_insn - = emit_insn_after (gen_rtx_SET (VOIDmode, to, - SET_DEST (expr_set)), - insn_computes_expr); - - /* Keep register set table up to date. */ - record_one_set (REGNO (to), new_insn); - - gcse_create_count++; - if (gcse_file != NULL) - { - fprintf (gcse_file, "GCSE: Creating insn %d to copy value of reg %d", - INSN_UID (NEXT_INSN (insn_computes_expr)), - REGNO (SET_SRC (PATTERN (NEXT_INSN (insn_computes_expr))))); - fprintf (gcse_file, ", computed in insn %d,\n", - INSN_UID (insn_computes_expr)); - fprintf (gcse_file, " into newly allocated reg %d\n", - REGNO (to)); - } - - pat = PATTERN (insn); - - /* Do register replacement for INSN. */ - changed = validate_change (insn, &SET_SRC (pat), - SET_DEST (PATTERN - (NEXT_INSN (insn_computes_expr))), - 0); - - /* We should be able to ignore the return code from validate_change but - to play it safe we check. */ - if (changed) - { - gcse_subst_count++; - if (gcse_file != NULL) - { - fprintf (gcse_file, - "GCSE: Replacing the source in insn %d with reg %d ", - INSN_UID (insn), - REGNO (SET_DEST (PATTERN (NEXT_INSN - (insn_computes_expr))))); - fprintf (gcse_file, "set in insn %d\n", - INSN_UID (insn_computes_expr)); - } - } - } - - return changed; -} - -/* Perform classic GCSE. This is called by one_classic_gcse_pass after all - the dataflow analysis has been done. - - The result is nonzero if a change was made. */ - -static int -classic_gcse (void) -{ - int changed; - rtx insn; - basic_block bb; - - /* Note we start at block 1. */ - - if (ENTRY_BLOCK_PTR->next_bb == EXIT_BLOCK_PTR) - return 0; - - changed = 0; - FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR->next_bb->next_bb, EXIT_BLOCK_PTR, next_bb) - { - /* Reset tables used to keep track of what's still valid [since the - start of the block]. */ - reset_opr_set_tables (); - - for (insn = BB_HEAD (bb); - insn != NULL && insn != NEXT_INSN (BB_END (bb)); - insn = NEXT_INSN (insn)) - { - /* Is insn of form (set (pseudo-reg) ...)? */ - if (GET_CODE (insn) == INSN - && GET_CODE (PATTERN (insn)) == SET - && GET_CODE (SET_DEST (PATTERN (insn))) == REG - && REGNO (SET_DEST (PATTERN (insn))) >= FIRST_PSEUDO_REGISTER) - { - rtx pat = PATTERN (insn); - rtx src = SET_SRC (pat); - struct expr *expr; - - if (want_to_gcse_p (src) - /* Is the expression recorded? */ - && ((expr = lookup_expr (src, &expr_hash_table)) != NULL) - /* Is the expression available [at the start of the - block]? */ - && TEST_BIT (ae_in[bb->index], expr->bitmap_index) - /* Are the operands unchanged since the start of the - block? */ - && oprs_not_set_p (src, insn)) - changed |= handle_avail_expr (insn, expr); - } - - /* Keep track of everything modified by this insn. */ - /* ??? Need to be careful w.r.t. mods done to INSN. */ - if (INSN_P (insn)) - mark_oprs_set (insn); - } - } - - return changed; -} - -/* Top level routine to perform one classic GCSE pass. - - Return nonzero if a change was made. */ - -static int -one_classic_gcse_pass (int pass) -{ - int changed = 0; - - gcse_subst_count = 0; - gcse_create_count = 0; - - alloc_hash_table (max_cuid, &expr_hash_table, 0); - alloc_rd_mem (last_basic_block, max_cuid); - compute_hash_table (&expr_hash_table); - if (gcse_file) - dump_hash_table (gcse_file, "Expression", &expr_hash_table); - - if (expr_hash_table.n_elems > 0) - { - compute_kill_rd (); - compute_rd (); - alloc_avail_expr_mem (last_basic_block, expr_hash_table.n_elems); - compute_ae_gen (&expr_hash_table); - compute_ae_kill (ae_gen, ae_kill, &expr_hash_table); - compute_available (ae_gen, ae_kill, ae_out, ae_in); - changed = classic_gcse (); - free_avail_expr_mem (); - } - - free_rd_mem (); - free_hash_table (&expr_hash_table); - - if (gcse_file) - { - fprintf (gcse_file, "\n"); - fprintf (gcse_file, "GCSE of %s, pass %d: %d bytes needed, %d substs,", - current_function_name (), pass, bytes_used, gcse_subst_count); - fprintf (gcse_file, "%d insns created\n", gcse_create_count); - } - - return changed; -} - -/* Compute copy/constant propagation working variables. */ - -/* Local properties of assignments. */ -static sbitmap *cprop_pavloc; -static sbitmap *cprop_absaltered; - -/* Global properties of assignments (computed from the local properties). */ -static sbitmap *cprop_avin; -static sbitmap *cprop_avout; - -/* Allocate vars used for copy/const propagation. N_BLOCKS is the number of - basic blocks. N_SETS is the number of sets. */ - -static void -alloc_cprop_mem (int n_blocks, int n_sets) -{ - cprop_pavloc = sbitmap_vector_alloc (n_blocks, n_sets); - cprop_absaltered = sbitmap_vector_alloc (n_blocks, n_sets); - - cprop_avin = sbitmap_vector_alloc (n_blocks, n_sets); - cprop_avout = sbitmap_vector_alloc (n_blocks, n_sets); -} - -/* Free vars used by copy/const propagation. */ - -static void -free_cprop_mem (void) -{ - sbitmap_vector_free (cprop_pavloc); - sbitmap_vector_free (cprop_absaltered); - sbitmap_vector_free (cprop_avin); - sbitmap_vector_free (cprop_avout); -} - -/* For each block, compute whether X is transparent. X is either an - expression or an assignment [though we don't care which, for this context - an assignment is treated as an expression]. For each block where an - element of X is modified, set (SET_P == 1) or reset (SET_P == 0) the INDX - bit in BMAP. */ - -static void -compute_transp (rtx x, int indx, sbitmap *bmap, int set_p) -{ - int i, j; - basic_block bb; - enum rtx_code code; - reg_set *r; - const char *fmt; - - /* repeat is used to turn tail-recursion into iteration since GCC - can't do it when there's no return value. */ - repeat: - - if (x == 0) - return; - - code = GET_CODE (x); - switch (code) - { - case REG: - if (set_p) - { - if (REGNO (x) < FIRST_PSEUDO_REGISTER) - { - FOR_EACH_BB (bb) - if (TEST_BIT (reg_set_in_block[bb->index], REGNO (x))) - SET_BIT (bmap[bb->index], indx); - } - else - { - for (r = reg_set_table[REGNO (x)]; r != NULL; r = r->next) - SET_BIT (bmap[BLOCK_NUM (r->insn)], indx); - } - } - else - { - if (REGNO (x) < FIRST_PSEUDO_REGISTER) - { - FOR_EACH_BB (bb) - if (TEST_BIT (reg_set_in_block[bb->index], REGNO (x))) - RESET_BIT (bmap[bb->index], indx); - } - else - { - for (r = reg_set_table[REGNO (x)]; r != NULL; r = r->next) - RESET_BIT (bmap[BLOCK_NUM (r->insn)], indx); - } - } - - return; - - case MEM: - FOR_EACH_BB (bb) - { - rtx list_entry = canon_modify_mem_list[bb->index]; - - while (list_entry) - { - rtx dest, dest_addr; - - if (GET_CODE (XEXP (list_entry, 0)) == CALL_INSN) - { - if (set_p) - SET_BIT (bmap[bb->index], indx); - else - RESET_BIT (bmap[bb->index], indx); - break; - } - /* LIST_ENTRY must be an INSN of some kind that sets memory. - Examine each hunk of memory that is modified. */ - - dest = XEXP (list_entry, 0); - list_entry = XEXP (list_entry, 1); - dest_addr = XEXP (list_entry, 0); - - if (canon_true_dependence (dest, GET_MODE (dest), dest_addr, - x, rtx_addr_varies_p)) - { - if (set_p) - SET_BIT (bmap[bb->index], indx); - else - RESET_BIT (bmap[bb->index], indx); - break; - } - list_entry = XEXP (list_entry, 1); - } - } - - x = XEXP (x, 0); - goto repeat; - - case PC: - case CC0: /*FIXME*/ - case CONST: - case CONST_INT: - case CONST_DOUBLE: - case CONST_VECTOR: - case SYMBOL_REF: - case LABEL_REF: - case ADDR_VEC: - case ADDR_DIFF_VEC: - return; - - default: - break; - } - - for (i = GET_RTX_LENGTH (code) - 1, fmt = GET_RTX_FORMAT (code); i >= 0; i--) - { - if (fmt[i] == 'e') - { - /* If we are about to do the last recursive call - needed at this level, change it into iteration. - This function is called enough to be worth it. */ - if (i == 0) - { - x = XEXP (x, i); - goto repeat; - } - - compute_transp (XEXP (x, i), indx, bmap, set_p); - } - else if (fmt[i] == 'E') - for (j = 0; j < XVECLEN (x, i); j++) - compute_transp (XVECEXP (x, i, j), indx, bmap, set_p); - } -} - -/* Top level routine to do the dataflow analysis needed by copy/const - propagation. */ - -static void -compute_cprop_data (void) -{ - compute_local_properties (cprop_absaltered, cprop_pavloc, NULL, &set_hash_table); - compute_available (cprop_pavloc, cprop_absaltered, - cprop_avout, cprop_avin); -} - -/* Copy/constant propagation. */ - -/* Maximum number of register uses in an insn that we handle. */ -#define MAX_USES 8 - -/* Table of uses found in an insn. - Allocated statically to avoid alloc/free complexity and overhead. */ -static struct reg_use reg_use_table[MAX_USES]; - -/* Index into `reg_use_table' while building it. */ -static int reg_use_count; - -/* Set up a list of register numbers used in INSN. The found uses are stored - in `reg_use_table'. `reg_use_count' is initialized to zero before entry, - and contains the number of uses in the table upon exit. - - ??? If a register appears multiple times we will record it multiple times. - This doesn't hurt anything but it will slow things down. */ - -static void -find_used_regs (rtx *xptr, void *data ATTRIBUTE_UNUSED) -{ - int i, j; - enum rtx_code code; - const char *fmt; - rtx x = *xptr; - - /* repeat is used to turn tail-recursion into iteration since GCC - can't do it when there's no return value. */ - repeat: - if (x == 0) - return; - - code = GET_CODE (x); - if (REG_P (x)) - { - if (reg_use_count == MAX_USES) - return; + if (reg_use_count == MAX_USES) + return; reg_use_table[reg_use_count].reg_rtx = x; reg_use_count++; @@ -5046,8 +4210,6 @@ alloc_pre_mem (int n_blocks, int n_exprs) pre_redundant = NULL; pre_insert_map = NULL; pre_delete_map = NULL; - ae_in = NULL; - ae_out = NULL; ae_kill = sbitmap_vector_alloc (n_blocks, n_exprs); /* pre_insert and pre_delete are allocated later. */ @@ -5071,14 +4233,9 @@ free_pre_mem (void) sbitmap_vector_free (pre_insert_map); if (pre_delete_map) sbitmap_vector_free (pre_delete_map); - if (ae_in) - sbitmap_vector_free (ae_in); - if (ae_out) - sbitmap_vector_free (ae_out); transp = comp = NULL; pre_optimal = pre_redundant = pre_insert_map = pre_delete_map = NULL; - ae_in = ae_out = NULL; } /* Top level routine to do the dataflow analysis needed by PRE. */ @@ -5107,8 +4264,7 @@ compute_pre_data (void) /* Compute ae_kill for each basic block using: ~(TRANSP | COMP) - - This is significantly faster than compute_ae_kill. */ + */ FOR_EACH_BB (bb) { @@ -5913,295 +5069,6 @@ compute_transpout (void) } } -/* Removal of useless null pointer checks */ - -/* Called via note_stores. X is set by SETTER. If X is a register we must - invalidate nonnull_local and set nonnull_killed. DATA is really a - `null_pointer_info *'. - - We ignore hard registers. */ - -static void -invalidate_nonnull_info (rtx x, rtx setter ATTRIBUTE_UNUSED, void *data) -{ - unsigned int regno; - struct null_pointer_info *npi = (struct null_pointer_info *) data; - - while (GET_CODE (x) == SUBREG) - x = SUBREG_REG (x); - - /* Ignore anything that is not a register or is a hard register. */ - if (GET_CODE (x) != REG - || REGNO (x) < npi->min_reg - || REGNO (x) >= npi->max_reg) - return; - - regno = REGNO (x) - npi->min_reg; - - RESET_BIT (npi->nonnull_local[npi->current_block->index], regno); - SET_BIT (npi->nonnull_killed[npi->current_block->index], regno); -} - -/* Do null-pointer check elimination for the registers indicated in - NPI. NONNULL_AVIN and NONNULL_AVOUT are pre-allocated sbitmaps; - they are not our responsibility to free. */ - -static int -delete_null_pointer_checks_1 (unsigned int *block_reg, sbitmap *nonnull_avin, - sbitmap *nonnull_avout, - struct null_pointer_info *npi) -{ - basic_block bb, current_block; - sbitmap *nonnull_local = npi->nonnull_local; - sbitmap *nonnull_killed = npi->nonnull_killed; - int something_changed = 0; - - /* Compute local properties, nonnull and killed. A register will have - the nonnull property if at the end of the current block its value is - known to be nonnull. The killed property indicates that somewhere in - the block any information we had about the register is killed. - - Note that a register can have both properties in a single block. That - indicates that it's killed, then later in the block a new value is - computed. */ - sbitmap_vector_zero (nonnull_local, last_basic_block); - sbitmap_vector_zero (nonnull_killed, last_basic_block); - - FOR_EACH_BB (current_block) - { - rtx insn, stop_insn; - - /* Set the current block for invalidate_nonnull_info. */ - npi->current_block = current_block; - - /* Scan each insn in the basic block looking for memory references and - register sets. */ - stop_insn = NEXT_INSN (BB_END (current_block)); - for (insn = BB_HEAD (current_block); - insn != stop_insn; - insn = NEXT_INSN (insn)) - { - rtx set; - rtx reg; - - /* Ignore anything that is not a normal insn. */ - if (! INSN_P (insn)) - continue; - - /* Basically ignore anything that is not a simple SET. We do have - to make sure to invalidate nonnull_local and set nonnull_killed - for such insns though. */ - set = single_set (insn); - if (!set) - { - note_stores (PATTERN (insn), invalidate_nonnull_info, npi); - continue; - } - - /* See if we've got a usable memory load. We handle it first - in case it uses its address register as a dest (which kills - the nonnull property). */ - if (GET_CODE (SET_SRC (set)) == MEM - && GET_CODE ((reg = XEXP (SET_SRC (set), 0))) == REG - && REGNO (reg) >= npi->min_reg - && REGNO (reg) < npi->max_reg) - SET_BIT (nonnull_local[current_block->index], - REGNO (reg) - npi->min_reg); - - /* Now invalidate stuff clobbered by this insn. */ - note_stores (PATTERN (insn), invalidate_nonnull_info, npi); - - /* And handle stores, we do these last since any sets in INSN can - not kill the nonnull property if it is derived from a MEM - appearing in a SET_DEST. */ - if (GET_CODE (SET_DEST (set)) == MEM - && GET_CODE ((reg = XEXP (SET_DEST (set), 0))) == REG - && REGNO (reg) >= npi->min_reg - && REGNO (reg) < npi->max_reg) - SET_BIT (nonnull_local[current_block->index], - REGNO (reg) - npi->min_reg); - } - } - - /* Now compute global properties based on the local properties. This - is a classic global availability algorithm. */ - compute_available (nonnull_local, nonnull_killed, - nonnull_avout, nonnull_avin); - - /* Now look at each bb and see if it ends with a compare of a value - against zero. */ - FOR_EACH_BB (bb) - { - rtx last_insn = BB_END (bb); - rtx condition, earliest; - int compare_and_branch; - - /* Since MIN_REG is always at least FIRST_PSEUDO_REGISTER, and - since BLOCK_REG[BB] is zero if this block did not end with a - comparison against zero, this condition works. */ - if (block_reg[bb->index] < npi->min_reg - || block_reg[bb->index] >= npi->max_reg) - continue; - - /* LAST_INSN is a conditional jump. Get its condition. */ - condition = get_condition (last_insn, &earliest, false); - - /* If we can't determine the condition then skip. */ - if (! condition) - continue; - - /* Is the register known to have a nonzero value? */ - if (!TEST_BIT (nonnull_avout[bb->index], block_reg[bb->index] - npi->min_reg)) - continue; - - /* Try to compute whether the compare/branch at the loop end is one or - two instructions. */ - if (earliest == last_insn) - compare_and_branch = 1; - else if (earliest == prev_nonnote_insn (last_insn)) - compare_and_branch = 2; - else - continue; - - /* We know the register in this comparison is nonnull at exit from - this block. We can optimize this comparison. */ - if (GET_CODE (condition) == NE) - { - rtx new_jump; - - new_jump = emit_jump_insn_after (gen_jump (JUMP_LABEL (last_insn)), - last_insn); - JUMP_LABEL (new_jump) = JUMP_LABEL (last_insn); - LABEL_NUSES (JUMP_LABEL (new_jump))++; - emit_barrier_after (new_jump); - } - - something_changed = 1; - delete_insn (last_insn); -#ifdef HAVE_cc0 - if (compare_and_branch == 2) - delete_insn (earliest); -#endif - purge_dead_edges (bb); - - /* Don't check this block again. (Note that BB_END is - invalid here; we deleted the last instruction in the - block.) */ - block_reg[bb->index] = 0; - } - - return something_changed; -} - -/* Find EQ/NE comparisons against zero which can be (indirectly) evaluated - at compile time. - - This is conceptually similar to global constant/copy propagation and - classic global CSE (it even uses the same dataflow equations as cprop). - - If a register is used as memory address with the form (mem (reg)), then we - know that REG can not be zero at that point in the program. Any instruction - which sets REG "kills" this property. - - So, if every path leading to a conditional branch has an available memory - reference of that form, then we know the register can not have the value - zero at the conditional branch. - - So we merely need to compute the local properties and propagate that data - around the cfg, then optimize where possible. - - We run this pass two times. Once before CSE, then again after CSE. This - has proven to be the most profitable approach. It is rare for new - optimization opportunities of this nature to appear after the first CSE - pass. - - This could probably be integrated with global cprop with a little work. */ - -int -delete_null_pointer_checks (rtx f ATTRIBUTE_UNUSED) -{ - sbitmap *nonnull_avin, *nonnull_avout; - unsigned int *block_reg; - basic_block bb; - int reg; - int regs_per_pass; - int max_reg = max_reg_num (); - struct null_pointer_info npi; - int something_changed = 0; - - /* If we have only a single block, or it is too expensive, give up. */ - if (n_basic_blocks <= 1 - || is_too_expensive (_ ("NULL pointer checks disabled"))) - return 0; - - /* We need four bitmaps, each with a bit for each register in each - basic block. */ - regs_per_pass = get_bitmap_width (4, last_basic_block, max_reg); - - /* Allocate bitmaps to hold local and global properties. */ - npi.nonnull_local = sbitmap_vector_alloc (last_basic_block, regs_per_pass); - npi.nonnull_killed = sbitmap_vector_alloc (last_basic_block, regs_per_pass); - nonnull_avin = sbitmap_vector_alloc (last_basic_block, regs_per_pass); - nonnull_avout = sbitmap_vector_alloc (last_basic_block, regs_per_pass); - - /* Go through the basic blocks, seeing whether or not each block - ends with a conditional branch whose condition is a comparison - against zero. Record the register compared in BLOCK_REG. */ - block_reg = xcalloc (last_basic_block, sizeof (int)); - FOR_EACH_BB (bb) - { - rtx last_insn = BB_END (bb); - rtx condition, earliest, reg; - - /* We only want conditional branches. */ - if (GET_CODE (last_insn) != JUMP_INSN - || !any_condjump_p (last_insn) - || !onlyjump_p (last_insn)) - continue; - - /* LAST_INSN is a conditional jump. Get its condition. */ - condition = get_condition (last_insn, &earliest, false); - - /* If we were unable to get the condition, or it is not an equality - comparison against zero then there's nothing we can do. */ - if (!condition - || (GET_CODE (condition) != NE && GET_CODE (condition) != EQ) - || GET_CODE (XEXP (condition, 1)) != CONST_INT - || (XEXP (condition, 1) - != CONST0_RTX (GET_MODE (XEXP (condition, 0))))) - continue; - - /* We must be checking a register against zero. */ - reg = XEXP (condition, 0); - if (GET_CODE (reg) != REG) - continue; - - block_reg[bb->index] = REGNO (reg); - } - - /* Go through the algorithm for each block of registers. */ - for (reg = FIRST_PSEUDO_REGISTER; reg < max_reg; reg += regs_per_pass) - { - npi.min_reg = reg; - npi.max_reg = MIN (reg + regs_per_pass, max_reg); - something_changed |= delete_null_pointer_checks_1 (block_reg, - nonnull_avin, - nonnull_avout, - &npi); - } - - /* Free the table of registers compared at the end of every block. */ - free (block_reg); - - /* Free bitmaps. */ - sbitmap_vector_free (npi.nonnull_local); - sbitmap_vector_free (npi.nonnull_killed); - sbitmap_vector_free (nonnull_avin); - sbitmap_vector_free (nonnull_avout); - - return something_changed; -} - /* Code Hoisting variables and subroutines. */ /* Very busy expressions. */ diff --git a/gcc/passes.c b/gcc/passes.c index 43b46e2cae7..5c8e5a6fd0f 100644 --- a/gcc/passes.c +++ b/gcc/passes.c @@ -1004,20 +1004,6 @@ rest_of_handle_jump_bypass (tree decl, rtx insns) #endif } -/* Try to identify useless null pointer tests and delete them. */ -static void -rest_of_handle_null_pointer (tree decl, rtx insns) -{ - open_dump_file (DFI_null, decl); - if (dump_file) - dump_flow_info (dump_file); - - if (delete_null_pointer_checks (insns)) - cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_PRE_LOOP); - - close_dump_file (DFI_null, print_rtl_with_bb, insns); -} - /* Try combining insns through substitution. */ static void rest_of_handle_combine (tree decl, rtx insns) @@ -1120,19 +1106,6 @@ rest_of_handle_cse (tree decl, rtx insns) if (tem || optimize > 1) cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_PRE_LOOP); - /* Try to identify useless null pointer tests and delete them. */ - if (flag_delete_null_pointer_checks) - { - timevar_push (TV_JUMP); - - if (delete_null_pointer_checks (insns)) - cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_PRE_LOOP); - timevar_pop (TV_JUMP); - } - - /* The second pass of jump optimization is likely to have - removed a bunch more instructions. */ - renumber_insns (dump_file); timevar_pop (TV_CSE); close_dump_file (DFI_cse, print_rtl_with_bb, insns); @@ -1547,9 +1520,6 @@ rest_of_compilation (tree decl) if (optimize) cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_PRE_LOOP); - if (flag_delete_null_pointer_checks) - rest_of_handle_null_pointer (decl, insns); - /* Jump optimization, and the removal of NULL pointer checks, may have reduced the number of instructions substantially. CSE, and future passes, allocate arrays whose dimensions involve the diff --git a/gcc/rtl.h b/gcc/rtl.h index d5f7a1ec644..1041d020e1a 100644 --- a/gcc/rtl.h +++ b/gcc/rtl.h @@ -2320,8 +2320,6 @@ extern void cannot_change_mode_set_regs (HARD_REG_SET *, extern bool invalid_mode_change_p (unsigned int, enum reg_class, enum machine_mode); -extern int delete_null_pointer_checks (rtx); - /* In regmove.c */ #ifdef BUFSIZ extern void regmove_optimize (rtx, int, FILE *); -- 2.30.2