From: Jeff Law Date: Thu, 16 Jun 2011 21:52:00 +0000 (-0600) Subject: tree-ssa-threadupdate.c (struct redirection_data): New field intermediate_edge. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=361b51c08030935d78a64228551afaa65106b773;p=gcc.git tree-ssa-threadupdate.c (struct redirection_data): New field intermediate_edge. * tree-ssa-threadupdate.c (struct redirection_data): New field intermediate_edge. (THREAD_TARGET2): Define. (redirection_data_eq): Also check that the intermediate edge is equal. (lookup_redirection_data): Drop useless argument. Extract the outgoing_edge and intermediate edge from E. Callers updated. (copy_phi_args, update_destination_phis): New functions. (fix_duplicate_block_edges): Likewise. (create_edge_and_update_destination_phis): Duplicate all the edges hung off e->aux. Use copy_phi_args. (create_duplicates): Use fix_duplicate_block_edges. (fixup_template_block): Likewise. (redirect_edges): If necessary, redirect the joiner block's incoming edge to the duplicate of the joiner block. (thread_block): Don't muck up loops when threading through a joiner block. (thread_through_loop_header): Handle threading through a joiner block. (mark_threaded_blocks, register_jump_thread): Likewise. * tree-flow.h (register_jump_thread): Add new argument. Callers updated. * tree-ssa-threadedge.c (phi_args_equal_on_edges): New function. (thread_across_edge): Handle threading through a joiner block. * gcc.dg/builtin-object-size-1.c: Update to handle changes from improved jump threading. * gcc.dg/builtin-object-size-2.c: Likewise. * gcc.dg/tree-ssa/20030728-1.c: Likewise. From-SVN: r175114 --- diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 0d04bc82e09..c1620d2eff4 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,30 @@ +2011-06-16 Jeff Law + + * tree-ssa-threadupdate.c (struct redirection_data): New field + intermediate_edge. + (THREAD_TARGET2): Define. + (redirection_data_eq): Also check that the intermediate edge is + equal. + (lookup_redirection_data): Drop useless argument. Extract the + outgoing_edge and intermediate edge from E. Callers updated. + (copy_phi_args, update_destination_phis): New functions. + (fix_duplicate_block_edges): Likewise. + (create_edge_and_update_destination_phis): Duplicate all the edges + hung off e->aux. Use copy_phi_args. + (create_duplicates): Use fix_duplicate_block_edges. + (fixup_template_block): Likewise. + (redirect_edges): If necessary, redirect the joiner block's incoming + edge to the duplicate of the joiner block. + (thread_block): Don't muck up loops when threading through a joiner + block. + (thread_through_loop_header): Handle threading through a joiner + block. + (mark_threaded_blocks, register_jump_thread): Likewise. + * tree-flow.h (register_jump_thread): Add new argument. Callers + updated. + * tree-ssa-threadedge.c (phi_args_equal_on_edges): New function. + (thread_across_edge): Handle threading through a joiner block. + 2011-06-16 Martin Jambor PR tree-optimization/49343 @@ -320,7 +347,7 @@ * ddg.c (add_intra_loop_mem_dep): New function. (build_intra_loop_deps): Call it. -2011-05-06 Jeff Law +2011-06-13 Jeff Law * df-problems.c (df_lr_local_compute): Manually CSE PIC_OFFSET_TABLE_REGNUM. diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 68da027be3d..bea872172d5 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,10 @@ +2011-06-16 Jeff Law + + * gcc.dg/builtin-object-size-1.c: Update to handle chances from + improved jump threading. + * gcc.dg/builtin-object-size-2.c: Likewise. + * gcc.dg/tree-ssa/20030728-1.c: Likewise. + 2011-06-16 Janus Weil PR fortran/49074 diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-1.c b/gcc/testsuite/gcc.dg/builtin-object-size-1.c index 404b7117f88..13ebeb15b25 100644 --- a/gcc/testsuite/gcc.dg/builtin-object-size-1.c +++ b/gcc/testsuite/gcc.dg/builtin-object-size-1.c @@ -64,7 +64,11 @@ test1 (void *q, int x) r = malloc (30); else r = calloc (2, 16); - if (__builtin_object_size (r, 0) != 2 * 16) + /* We may duplicate this test onto the two exit paths. On one path + the size will be 32, the other it will be 30. If we don't duplicate + this test, then the size will be 32. */ + if (__builtin_object_size (r, 0) != 2 * 16 + && __builtin_object_size (r, 0) != 30) abort (); if (x < 20) r = malloc (30); diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-2.c b/gcc/testsuite/gcc.dg/builtin-object-size-2.c index 34f16759d68..21aff5a958d 100644 --- a/gcc/testsuite/gcc.dg/builtin-object-size-2.c +++ b/gcc/testsuite/gcc.dg/builtin-object-size-2.c @@ -60,7 +60,11 @@ test1 (void *q, int x) r = malloc (30); else r = calloc (2, 16); - if (__builtin_object_size (r, 1) != 2 * 16) + /* We may duplicate this test onto the two exit paths. On one path + the size will be 32, the other it will be 30. If we don't duplicate + this test, then the size will be 32. */ + if (__builtin_object_size (r, 1) != 2 * 16 + && __builtin_object_size (r, 1) != 30) abort (); if (x < 20) r = malloc (30); diff --git a/gcc/testsuite/gcc.dg/tree-ssa/20030728-1.c b/gcc/testsuite/gcc.dg/tree-ssa/20030728-1.c index f884736a1c9..93a7979c56f 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/20030728-1.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/20030728-1.c @@ -41,7 +41,9 @@ objects_must_conflict_p (t1, t2) return foo (t2 ? get_alias_set (t2) : 0); } -/* There should be two assignments of variables to the value zero. */ -/* { dg-final { scan-rtl-dump-times "PART.. = 0" 2 "expand"} } */ +/* There should be one assignment of variables to the value zero. There + used to be two assignments, but improvements in threading allowed the + second to be propagated into all its uses and eliminated. */ +/* { dg-final { scan-rtl-dump-times "PART.. = 0" 1 "expand"} } */ /* { dg-final { cleanup-rtl-dump "expand" } } */ diff --git a/gcc/tree-flow.h b/gcc/tree-flow.h index ab5c80cdde0..a864e169eb7 100644 --- a/gcc/tree-flow.h +++ b/gcc/tree-flow.h @@ -808,7 +808,7 @@ bool may_be_nonaddressable_p (tree expr); /* In tree-ssa-threadupdate.c. */ extern bool thread_through_all_blocks (bool); -extern void register_jump_thread (edge, edge); +extern void register_jump_thread (edge, edge, edge); /* In gimplify.c */ tree force_gimple_operand_1 (tree, gimple_seq *, gimple_predicate, tree); diff --git a/gcc/tree-ssa-threadedge.c b/gcc/tree-ssa-threadedge.c index 24e63977c29..a485b211e59 100644 --- a/gcc/tree-ssa-threadedge.c +++ b/gcc/tree-ssa-threadedge.c @@ -1,5 +1,5 @@ /* SSA Jump Threading - Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 + Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc. Contributed by Jeff Law @@ -652,6 +652,27 @@ thread_around_empty_block (edge taken_edge, return NULL; } +/* E1 and E2 are edges into the same basic block. Return TRUE if the + PHI arguments associated with those edges are equal or there are no + PHI arguments, otherwise return FALSE. */ + +static bool +phi_args_equal_on_edges (edge e1, edge e2) +{ + gimple_stmt_iterator gsi; + int indx1 = e1->dest_idx; + int indx2 = e2->dest_idx; + + for (gsi = gsi_start_phis (e1->dest); !gsi_end_p (gsi); gsi_next (&gsi)) + { + gimple phi = gsi_stmt (gsi); + + if (!operand_equal_p (gimple_phi_arg_def (phi, indx1), + gimple_phi_arg_def (phi, indx2), 0)) + return false; + } + return true; +} /* We are exiting E->src, see if E->dest ends with a conditional jump which has a known value when reached via E. @@ -770,11 +791,73 @@ thread_across_edge (gimple dummy_cond, } remove_temporary_equivalences (stack); - register_jump_thread (e, taken_edge); + register_jump_thread (e, taken_edge, NULL); return; } } + /* We were unable to determine what out edge from E->dest is taken. However, + we might still be able to thread through successors of E->dest. This + often occurs when E->dest is a joiner block which then fans back out + based on redundant tests. + + If so, we'll copy E->dest and redirect the appropriate predecessor to + the copy. Within the copy of E->dest, we'll thread one or more edges + to points deeper in the CFG. + + This is a stopgap until we have a more structured approach to path + isolation. */ + { + edge e2, e3, taken_edge; + edge_iterator ei; + bool found = false; + bitmap visited = BITMAP_ALLOC (NULL); + + /* Look at each successor of E->dest to see if we can thread through it. */ + FOR_EACH_EDGE (taken_edge, ei, e->dest->succs) + { + /* Avoid threading to any block we have already visited. */ + bitmap_clear (visited); + bitmap_set_bit (visited, taken_edge->dest->index); + bitmap_set_bit (visited, e->dest->index); + + /* Record whether or not we were able to thread through a successor + of E->dest. */ + found = false; + e3 = taken_edge; + do + { + e2 = thread_around_empty_block (e3, + dummy_cond, + handle_dominating_asserts, + simplify, + visited); + if (e2) + { + e3 = e2; + found = true; + } + } + while (e2); + + /* If we were able to thread through a successor of E->dest, then + record the jump threading opportunity. */ + if (found) + { + edge tmp; + /* If there is already an edge from the block to be duplicated + (E2->src) to the final target (E3->dest), then make sure that + the PHI args associated with the edges E2 and E3 are the + same. */ + tmp = find_edge (taken_edge->src, e3->dest); + if (!tmp || phi_args_equal_on_edges (tmp, e3)) + register_jump_thread (e, taken_edge, e3); + } + + } + BITMAP_FREE (visited); + } + fail: remove_temporary_equivalences (stack); } diff --git a/gcc/tree-ssa-threadupdate.c b/gcc/tree-ssa-threadupdate.c index c6e34051c9c..e0335dc8c10 100644 --- a/gcc/tree-ssa-threadupdate.c +++ b/gcc/tree-ssa-threadupdate.c @@ -1,6 +1,6 @@ /* Thread edges through blocks and update the control flow and SSA graphs. - Copyright (C) 2004, 2005, 2006, 2007, 2008, 2010 Free Software Foundation, - Inc. + Copyright (C) 2004, 2005, 2006, 2007, 2008, 2010, 201 + Free Software Foundation, Inc. This file is part of GCC. @@ -121,6 +121,8 @@ struct redirection_data its single successor. */ edge outgoing_edge; + edge intermediate_edge; + /* A list of incoming edges which we want to thread to OUTGOING_EDGE->dest. */ struct el *incoming_edges; @@ -153,6 +155,7 @@ static VEC(edge,heap) *threaded_edges; threading is attached to the AUX field for the incoming edge. Use these macros to access the underlying structure attached to the AUX field. */ #define THREAD_TARGET(E) ((edge *)(E)->aux)[0] +#define THREAD_TARGET2(E) ((edge *)(E)->aux)[1] /* Jump threading statistics. */ @@ -231,8 +234,10 @@ redirection_data_eq (const void *p1, const void *p2) { edge e1 = ((const struct redirection_data *)p1)->outgoing_edge; edge e2 = ((const struct redirection_data *)p2)->outgoing_edge; + edge e3 = ((const struct redirection_data *)p1)->intermediate_edge; + edge e4 = ((const struct redirection_data *)p2)->intermediate_edge; - return e1 == e2; + return e1 == e2 && e3 == e4; } /* Given an outgoing edge E lookup and return its entry in our hash table. @@ -242,7 +247,7 @@ redirection_data_eq (const void *p1, const void *p2) edges associated with E in the hash table. */ static struct redirection_data * -lookup_redirection_data (edge e, edge incoming_edge, enum insert_option insert) +lookup_redirection_data (edge e, enum insert_option insert) { void **slot; struct redirection_data *elt; @@ -250,7 +255,9 @@ lookup_redirection_data (edge e, edge incoming_edge, enum insert_option insert) /* Build a hash table element so we can see if E is already in the table. */ elt = XNEW (struct redirection_data); - elt->outgoing_edge = e; + elt->intermediate_edge = THREAD_TARGET2 (e) ? THREAD_TARGET (e) : NULL; + elt->outgoing_edge = THREAD_TARGET2 (e) ? THREAD_TARGET2 (e) + : THREAD_TARGET (e); elt->dup_block = NULL; elt->incoming_edges = NULL; @@ -270,7 +277,7 @@ lookup_redirection_data (edge e, edge incoming_edge, enum insert_option insert) { *slot = (void *)elt; elt->incoming_edges = XNEW (struct el); - elt->incoming_edges->e = incoming_edge; + elt->incoming_edges->e = e; elt->incoming_edges->next = NULL; return elt; } @@ -290,7 +297,7 @@ lookup_redirection_data (edge e, edge incoming_edge, enum insert_option insert) { struct el *el = XNEW (struct el); el->next = elt->incoming_edges; - el->e = incoming_edge; + el->e = e; elt->incoming_edges = el; } @@ -298,6 +305,41 @@ lookup_redirection_data (edge e, edge incoming_edge, enum insert_option insert) } } +/* For each PHI in BB, copy the argument associated with SRC_E to TGT_E. */ + +static void +copy_phi_args (basic_block bb, edge src_e, edge tgt_e) +{ + gimple_stmt_iterator gsi; + int src_indx = src_e->dest_idx; + + for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi); gsi_next (&gsi)) + { + gimple phi = gsi_stmt (gsi); + source_location locus = gimple_phi_arg_location (phi, src_indx); + add_phi_arg (phi, gimple_phi_arg_def (phi, src_indx), tgt_e, locus); + } +} + +/* We have recently made a copy of ORIG_BB, including its outgoing + edges. The copy is NEW_BB. Every PHI node in every direct successor of + ORIG_BB has a new argument associated with edge from NEW_BB to the + successor. Initialize the PHI argument so that it is equal to the PHI + argument associated with the edge from ORIG_BB to the successor. */ + +static void +update_destination_phis (basic_block orig_bb, basic_block new_bb) +{ + edge_iterator ei; + edge e; + + FOR_EACH_EDGE (e, ei, orig_bb->succs) + { + edge e2 = find_edge (new_bb, e->dest); + copy_phi_args (e->dest, e, e2); + } +} + /* Given a duplicate block and its single destination (both stored in RD). Create an edge between the duplicate and its single destination. @@ -310,7 +352,6 @@ create_edge_and_update_destination_phis (struct redirection_data *rd, basic_block bb) { edge e = make_edge (bb, rd->outgoing_edge->dest, EDGE_FALLTHRU); - gimple_stmt_iterator gsi; rescan_loop_exit (e, true, false); e->probability = REG_BR_PROB_BASE; @@ -318,8 +359,9 @@ create_edge_and_update_destination_phis (struct redirection_data *rd, if (rd->outgoing_edge->aux) { - e->aux = (edge *) XNEWVEC (edge, 1); + e->aux = (edge *) XNEWVEC (edge, 2); THREAD_TARGET(e) = THREAD_TARGET (rd->outgoing_edge); + THREAD_TARGET2(e) = THREAD_TARGET2 (rd->outgoing_edge); } else { @@ -330,17 +372,43 @@ create_edge_and_update_destination_phis (struct redirection_data *rd, from the duplicate block, then we will need to add a new argument to them. The argument should have the same value as the argument associated with the outgoing edge stored in RD. */ - for (gsi = gsi_start_phis (e->dest); !gsi_end_p (gsi); gsi_next (&gsi)) + copy_phi_args (e->dest, rd->outgoing_edge, e); +} + +/* Wire up the outgoing edges from the duplicate block and + update any PHIs as needed. */ +static void +fix_duplicate_block_edges (struct redirection_data *rd, + struct local_info *local_info) +{ + /* If we were threading through an joiner block, then we want + to keep its control statement and redirect an outgoing edge. + Else we want to remove the control statement & edges, then create + a new outgoing edge. In both cases we may need to update PHIs. */ + if (THREAD_TARGET2 (rd->incoming_edges->e) == rd->outgoing_edge) { - gimple phi = gsi_stmt (gsi); - source_location locus; - int indx = rd->outgoing_edge->dest_idx; + edge victim; + edge e2; + edge e = rd->incoming_edges->e; + + /* This updates the PHIs at the destination of the duplicate + block. */ + update_destination_phis (local_info->bb, rd->dup_block); - locus = gimple_phi_arg_location (phi, indx); - add_phi_arg (phi, gimple_phi_arg_def (phi, indx), e, locus); + /* Find the edge from the duplicate block to the block we're + threading through. That's the edge we want to redirect. */ + victim = find_edge (rd->dup_block, THREAD_TARGET (e)->dest); + e2 = redirect_edge_and_branch (victim, THREAD_TARGET2 (e)->dest); + + /* This updates the PHI at the target of the threaded edge. */ + copy_phi_args (e2->dest, THREAD_TARGET2 (e), e2); + } + else + { + remove_ctrl_stmt_and_useless_edges (rd->dup_block, NULL); + create_edge_and_update_destination_phis (rd, rd->dup_block); } } - /* Hash table traversal callback routine to create duplicate blocks. */ static int @@ -365,9 +433,8 @@ create_duplicates (void **slot, void *data) create_block_for_threading (local_info->template_block, rd); /* Go ahead and wire up outgoing edges and update PHIs for the duplicate - block. */ - remove_ctrl_stmt_and_useless_edges (rd->dup_block, NULL); - create_edge_and_update_destination_phis (rd, rd->dup_block); + block. */ + fix_duplicate_block_edges (rd, local_info); } /* Keep walking the hash table. */ @@ -384,12 +451,16 @@ fixup_template_block (void **slot, void *data) struct redirection_data *rd = (struct redirection_data *) *slot; struct local_info *local_info = (struct local_info *)data; - /* If this is the template block, then create its outgoing edges - and halt the hash table traversal. */ + /* If this is the template block halt the traversal after updating + it appropriately. + + If we were threading through an joiner block, then we want + to keep its control statement and redirect an outgoing edge. + Else we want to remove the control statement & edges, then create + a new outgoing edge. In both cases we may need to update PHIs. */ if (rd->dup_block && rd->dup_block == local_info->template_block) { - remove_ctrl_stmt_and_useless_edges (rd->dup_block, NULL); - create_edge_and_update_destination_phis (rd, rd->dup_block); + fix_duplicate_block_edges (rd, local_info); return 0; } @@ -419,8 +490,18 @@ redirect_edges (void **slot, void *data) free (el); thread_stats.num_threaded_edges++; + /* If we are threading through a joiner block, then we have to + find the edge we want to redirect and update some PHI nodes. */ + if (THREAD_TARGET2 (e)) + { + edge e2; - if (rd->dup_block) + /* We want to redirect the incoming edge to the joiner block (E) + to instead reach the duplicate of the joiner block. */ + e2 = redirect_edge_and_branch (e, rd->dup_block); + flush_pending_stmts (e2); + } + else if (rd->dup_block) { edge e2; @@ -546,14 +627,18 @@ thread_block (basic_block bb, bool noloop_only) if (e->aux == NULL) continue; - e2 = THREAD_TARGET (e); + if (THREAD_TARGET2 (e)) + e2 = THREAD_TARGET2 (e); + else + e2 = THREAD_TARGET (e); if (!e2 /* If NOLOOP_ONLY is true, we only allow threading through the header of a loop to exit edges. */ || (noloop_only && bb == bb->loop_father->header - && (!loop_exit_edge_p (bb->loop_father, e2)))) + && (!loop_exit_edge_p (bb->loop_father, e2) + || THREAD_TARGET2 (e)))) continue; if (e->dest == e2->src) @@ -562,7 +647,7 @@ thread_block (basic_block bb, bool noloop_only) /* Insert the outgoing edge into the hash table if it is not already in the hash table. */ - lookup_redirection_data (e2, e, INSERT); + lookup_redirection_data (e, INSERT); } /* We do not update dominance info. */ @@ -817,6 +902,8 @@ thread_through_loop_header (struct loop *loop, bool may_peel_loop_headers) if (latch->aux) { + if (THREAD_TARGET2 (latch)) + goto fail; tgt_edge = THREAD_TARGET (latch); tgt_bb = tgt_edge->dest; } @@ -840,6 +927,8 @@ thread_through_loop_header (struct loop *loop, bool may_peel_loop_headers) goto fail; } + if (THREAD_TARGET2 (e)) + goto fail; tgt_edge = THREAD_TARGET (e); atgt_bb = tgt_edge->dest; if (!tgt_bb) @@ -967,13 +1056,14 @@ mark_threaded_blocks (bitmap threaded_blocks) edge e; edge_iterator ei; - for (i = 0; i < VEC_length (edge, threaded_edges); i += 2) + for (i = 0; i < VEC_length (edge, threaded_edges); i += 3) { edge e = VEC_index (edge, threaded_edges, i); - edge *x = (edge *) XNEWVEC (edge, 1); + edge *x = (edge *) XNEWVEC (edge, 2); e->aux = x; THREAD_TARGET (e) = VEC_index (edge, threaded_edges, i + 1); + THREAD_TARGET2 (e) = VEC_index (edge, threaded_edges, i + 2); bitmap_set_bit (tmp, e->dest->index); } @@ -1085,7 +1175,7 @@ thread_through_all_blocks (bool may_peel_loop_headers) after fixing the SSA graph. */ void -register_jump_thread (edge e, edge e2) +register_jump_thread (edge e, edge e2, edge e3) { /* This can occur if we're jumping to a constant address or or something similar. Just get out now. */ @@ -1102,4 +1192,5 @@ register_jump_thread (edge e, edge e2) VEC_safe_push (edge, heap, threaded_edges, e); VEC_safe_push (edge, heap, threaded_edges, e2); + VEC_safe_push (edge, heap, threaded_edges, e3); }