From patchwork Wed Nov 3 16:03:21 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: "H.J. Lu" X-Patchwork-Id: 70035 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) by ozlabs.org (Postfix) with SMTP id B08B0B6EF0 for ; Thu, 4 Nov 2010 03:03:40 +1100 (EST) Received: (qmail 14210 invoked by alias); 3 Nov 2010 16:03:38 -0000 Received: (qmail 14196 invoked by uid 22791); 3 Nov 2010 16:03:32 -0000 X-SWARE-Spam-Status: No, hits=-0.4 required=5.0 tests=AWL, BAYES_50, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, FREEMAIL_FROM, RCVD_IN_DNSWL_NONE, TW_CF, TW_RR, TW_XT X-Spam-Check-By: sourceware.org Received: from mail-pv0-f175.google.com (HELO mail-pv0-f175.google.com) (74.125.83.175) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Wed, 03 Nov 2010 16:03:23 +0000 Received: by pvc7 with SMTP id 7so309116pvc.20 for ; Wed, 03 Nov 2010 09:03:21 -0700 (PDT) MIME-Version: 1.0 Received: by 10.142.178.10 with SMTP id a10mr7298896wff.391.1288800201256; Wed, 03 Nov 2010 09:03:21 -0700 (PDT) Received: by 10.220.182.10 with HTTP; Wed, 3 Nov 2010 09:03:21 -0700 (PDT) Date: Wed, 3 Nov 2010 09:03:21 -0700 Message-ID: Subject: PATCH: Revert revision 164552 From: "H.J. Lu" To: Diego Novillo Cc: Rainer Orth , GCC Patches , Bernd Schmidt X-IsSubscribed: yes Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org On Wed, Nov 3, 2010 at 8:02 AM, Diego Novillo wrote: > On Wed, Nov 3, 2010 at 10:02, Rainer Orth wrote: >> H.J., >> >>> Revision 164552: >>> >>> http://gcc.gnu.org/ml/gcc-cvs/2010-09/msg00849.html >>> >>> which fixes: >>> >>> http://gcc.gnu.org/bugzilla/show_bug.cgi?id=44374 >>> >>> a mixed optimization bug, but caused many failures: >>> >>> http://gcc.gnu.org/bugzilla/show_bug.cgi?id=46257 >>> >>> including bootstrap failures on x86. It was reported more than a month ago >>> and nothing has changed.  Should it be reverted for now? >> >> I think it should: it probably also caused PR bootstrap/46018 and >> certainly PR rtl-optimization/46114, where Bernd indicated that he would >> be away for several weeks. > > I agree.  Let's revert the patch.  Bernd should be able to figure out > a fix after he gets back. > Here is the patch to revert revision 164552. OK for trunk? Thanks. diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 2e7b3d1..459dbee 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -3168,7 +3168,7 @@ df-core.o : df-core.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ df-problems.o : df-problems.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \ $(RTL_H) insn-config.h $(RECOG_H) $(FUNCTION_H) $(REGS_H) alloc-pool.h \ hard-reg-set.h $(BASIC_BLOCK_H) $(DF_H) $(BITMAP_H) sbitmap.h $(TIMEVAR_H) \ - $(TM_P_H) $(TARGET_H) $(FLAGS_H) output.h $(EXCEPT_H) dce.h vecprim.h + $(TM_P_H) $(FLAGS_H) output.h $(EXCEPT_H) dce.h vecprim.h df-scan.o : df-scan.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ insn-config.h $(RECOG_H) $(FUNCTION_H) $(REGS_H) alloc-pool.h \ hard-reg-set.h $(BASIC_BLOCK_H) $(DF_H) $(BITMAP_H) sbitmap.h $(TIMEVAR_H) \ diff --git a/gcc/basic-block.h b/gcc/basic-block.h index c90fc11..022dc9f 100644 --- a/gcc/basic-block.h +++ b/gcc/basic-block.h @@ -246,13 +246,7 @@ enum bb_flags /* Set on blocks that cannot be threaded through. Only used in cfgcleanup.c. */ - BB_NONTHREADABLE_BLOCK = 1 << 11, - - /* Set on blocks that were modified in some way. This bit is set in - df_set_bb_dirty, but not cleared by df_analyze, so it can be used - to test whether a block has been modified prior to a df_analyze - call. */ - BB_MODIFIED = 1 << 12 + BB_NONTHREADABLE_BLOCK = 1 << 11 }; /* Dummy flag for convenience in the hot/cold partitioning code. */ diff --git a/gcc/cfgcleanup.c b/gcc/cfgcleanup.c index 881a4f3..dc41f45 100644 --- a/gcc/cfgcleanup.c +++ b/gcc/cfgcleanup.c @@ -66,10 +66,6 @@ static bool first_pass; /* Set to true if crossjumps occured in the latest run of try_optimize_cfg. */ static bool crossjumps_occured; -/* Set to true if we couldn't run an optimization due to stale liveness - information; we should run df_analyze to enable more opportunities. */ -static bool block_was_dirty; - static bool try_crossjump_to_edge (int, edge, edge); static bool try_crossjump_bb (int, basic_block); static bool outgoing_edges_match (int, basic_block, basic_block); @@ -436,7 +432,7 @@ try_forward_edges (int mode, basic_block b) int counter, goto_locus; bool threaded = false; int nthreaded_edges = 0; - bool may_thread = first_pass || (b->flags & BB_MODIFIED) != 0; + bool may_thread = first_pass | df_get_bb_dirty (b); /* Skip complex edges because we don't know how to update them. @@ -471,7 +467,7 @@ try_forward_edges (int mode, basic_block b) { basic_block new_target = NULL; bool new_target_threaded = false; - may_thread |= (target->flags & BB_MODIFIED) != 0; + may_thread |= df_get_bb_dirty (target); if (FORWARDER_BLOCK_P (target) && !(single_succ_edge (target)->flags & EDGE_CROSSING) @@ -1855,8 +1851,8 @@ try_crossjump_bb (int mode, basic_block bb) /* If nothing changed since the last attempt, there is nothing we can do. */ if (!first_pass - && !((e->src->flags & BB_MODIFIED) - || (fallthru->src->flags & BB_MODIFIED))) + && (!(df_get_bb_dirty (e->src)) + && !(df_get_bb_dirty (fallthru->src)))) continue; if (try_crossjump_to_edge (mode, e, fallthru)) @@ -1905,8 +1901,8 @@ try_crossjump_bb (int mode, basic_block bb) /* If nothing changed since the last attempt, there is nothing we can do. */ if (!first_pass - && !((e->src->flags & BB_MODIFIED) - || (e2->src->flags & BB_MODIFIED))) + && (!(df_get_bb_dirty (e->src)) + && !(df_get_bb_dirty (e2->src)))) continue; if (try_crossjump_to_edge (mode, e, e2)) @@ -1925,289 +1921,6 @@ try_crossjump_bb (int mode, basic_block bb) return changed; } -/* Search the successors of BB for common insn sequences. When found, - share code between them by moving it across the basic block - boundary. Return true if any changes made. */ - -static bool -try_head_merge_bb (basic_block bb) -{ - basic_block final_dest_bb = NULL; - int max_match = INT_MAX; - edge e0; - rtx *headptr, *currptr, *nextptr; - bool changed, moveall; - unsigned ix; - rtx e0_last_head, cond, move_before; - unsigned nedges = EDGE_COUNT (bb->succs); - rtx jump = BB_END (bb); - regset live, live_union; - - /* Nothing to do if there is not at least two outgoing edges. */ - if (nedges < 2) - return false; - - /* Don't crossjump if this block ends in a computed jump, - unless we are optimizing for size. */ - if (optimize_bb_for_size_p (bb) - && bb != EXIT_BLOCK_PTR - && computed_jump_p (BB_END (bb))) - return false; - - cond = get_condition (jump, &move_before, true, false); - if (cond == NULL_RTX) - move_before = jump; - - for (ix = 0; ix < nedges; ix++) - if (EDGE_SUCC (bb, ix)->dest == EXIT_BLOCK_PTR) - return false; - - for (ix = 0; ix < nedges; ix++) - { - edge e = EDGE_SUCC (bb, ix); - basic_block other_bb = e->dest; - - if (df_get_bb_dirty (other_bb)) - { - block_was_dirty = true; - return false; - } - - if (e->flags & EDGE_ABNORMAL) - return false; - - /* Normally, all destination blocks must only be reachable from this - block, i.e. they must have one incoming edge. - - There is one special case we can handle, that of multiple consecutive - jumps where the first jumps to one of the targets of the second jump. - This happens frequently in switch statements for default labels. - The structure is as follows: - FINAL_DEST_BB - .... - if (cond) jump A; - fall through - BB - jump with targets A, B, C, D... - A - has two incoming edges, from FINAL_DEST_BB and BB - - In this case, we can try to move the insns through BB and into - FINAL_DEST_BB. */ - if (EDGE_COUNT (other_bb->preds) != 1) - { - edge incoming_edge, incoming_bb_other_edge; - edge_iterator ei; - - if (final_dest_bb != NULL - || EDGE_COUNT (other_bb->preds) != 2) - return false; - - /* We must be able to move the insns across the whole block. */ - move_before = BB_HEAD (bb); - while (!NONDEBUG_INSN_P (move_before)) - move_before = NEXT_INSN (move_before); - - if (EDGE_COUNT (bb->preds) != 1) - return false; - incoming_edge = EDGE_PRED (bb, 0); - final_dest_bb = incoming_edge->src; - if (EDGE_COUNT (final_dest_bb->succs) != 2) - return false; - FOR_EACH_EDGE (incoming_bb_other_edge, ei, final_dest_bb->succs) - if (incoming_bb_other_edge != incoming_edge) - break; - if (incoming_bb_other_edge->dest != other_bb) - return false; - } - } - - e0 = EDGE_SUCC (bb, 0); - e0_last_head = NULL_RTX; - changed = false; - - for (ix = 1; ix < nedges; ix++) - { - edge e = EDGE_SUCC (bb, ix); - rtx e0_last, e_last; - int nmatch; - - nmatch = flow_find_head_matching_sequence (e0->dest, e->dest, - &e0_last, &e_last, 0); - if (nmatch == 0) - return false; - - if (nmatch < max_match) - { - max_match = nmatch; - e0_last_head = e0_last; - } - } - - /* If we matched an entire block, we probably have to avoid moving the - last insn. */ - if (max_match > 0 - && e0_last_head == BB_END (e0->dest) - && (find_reg_note (e0_last_head, REG_EH_REGION, 0) - || control_flow_insn_p (e0_last_head))) - { - max_match--; - if (max_match == 0) - return false; - do - e0_last_head = prev_real_insn (e0_last_head); - while (DEBUG_INSN_P (e0_last_head)); - } - - if (max_match == 0) - return false; - - /* We must find a union of the live registers at each of the end points. */ - live = BITMAP_ALLOC (NULL); - live_union = BITMAP_ALLOC (NULL); - - currptr = XNEWVEC (rtx, nedges); - headptr = XNEWVEC (rtx, nedges); - nextptr = XNEWVEC (rtx, nedges); - - for (ix = 0; ix < nedges; ix++) - { - int j; - basic_block merge_bb = EDGE_SUCC (bb, ix)->dest; - rtx head = BB_HEAD (merge_bb); - - while (!NONDEBUG_INSN_P (head)) - head = NEXT_INSN (head); - headptr[ix] = head; - currptr[ix] = head; - - /* Compute the end point and live information */ - for (j = 1; j < max_match; j++) - do - head = NEXT_INSN (head); - while (!NONDEBUG_INSN_P (head)); - simulate_backwards_to_point (merge_bb, live, head); - IOR_REG_SET (live_union, live); - } - - /* If we're moving across two blocks, verify the validity of the - first move, then adjust the target and let the loop below deal - with the final move. */ - if (final_dest_bb != NULL) - { - rtx move_upto; - - moveall = can_move_insns_across (currptr[0], e0_last_head, move_before, - jump, e0->dest, live_union, - NULL, &move_upto); - if (!moveall) - e0_last_head = move_upto; - if (e0_last_head == NULL_RTX) - goto out; - - jump = BB_END (final_dest_bb); - cond = get_condition (jump, &move_before, true, false); - if (cond == NULL_RTX) - move_before = jump; - } - - do - { - rtx move_upto; - moveall = can_move_insns_across (currptr[0], e0_last_head, - move_before, jump, e0->dest, live_union, - NULL, &move_upto); - if (!moveall && move_upto == NULL_RTX) - { - if (jump == move_before) - break; - - /* Try again, using a different insertion point. */ - move_before = jump; - -#ifdef HAVE_cc0 - /* Don't try moving before a cc0 user, as that may invalidate - the cc0. */ - if (reg_mentioned_p (cc0_rtx, jump)) - break; -#endif - - continue; - } - - if (final_dest_bb && !moveall) - /* We haven't checked whether a partial move would be OK for the first - move, so we have to fail this case. */ - break; - - changed = true; - for (;;) - { - if (currptr[0] == move_upto) - break; - for (ix = 0; ix < nedges; ix++) - { - rtx curr = currptr[ix]; - do - curr = NEXT_INSN (curr); - while (!NONDEBUG_INSN_P (curr)); - currptr[ix] = curr; - } - } - - /* If we can't currently move all of the identical insns, remember - each insn after the range that we'll merge. */ - if (!moveall) - for (ix = 0; ix < nedges; ix++) - { - rtx curr = currptr[ix]; - do - curr = NEXT_INSN (curr); - while (!NONDEBUG_INSN_P (curr)); - nextptr[ix] = curr; - } - - reorder_insns (headptr[0], currptr[0], PREV_INSN (move_before)); - df_set_bb_dirty (EDGE_SUCC (bb, 0)->dest); - if (final_dest_bb != NULL) - df_set_bb_dirty (final_dest_bb); - df_set_bb_dirty (bb); - for (ix = 1; ix < nedges; ix++) - { - df_set_bb_dirty (EDGE_SUCC (bb, ix)->dest); - delete_insn_chain (headptr[ix], currptr[ix], false); - } - if (!moveall) - { - if (jump == move_before) - break; - - /* For the unmerged insns, try a different insertion point. */ - move_before = jump; - -#ifdef HAVE_cc0 - /* Don't try moving before a cc0 user, as that may invalidate - the cc0. */ - if (reg_mentioned_p (cc0_rtx, jump)) - break; -#endif - - for (ix = 0; ix < nedges; ix++) - currptr[ix] = headptr[ix] = nextptr[ix]; - } - } - while (!moveall); - - out: - free (currptr); - free (headptr); - free (nextptr); - - crossjumps_occured |= changed; - - return changed; -} - /* Return true if BB contains just bb note, or bb note followed by only DEBUG_INSNs. */ @@ -2253,7 +1966,6 @@ try_optimize_cfg (int mode) one predecessor, they may be combined. */ do { - block_was_dirty = false; changed = false; iterations++; @@ -2453,13 +2165,6 @@ try_optimize_cfg (int mode) && try_crossjump_bb (mode, b)) changed_here = true; - if ((mode & CLEANUP_CROSSJUMP) - /* This can lengthen register lifetimes. Do it only after - reload. */ - && reload_completed - && try_head_merge_bb (b)) - changed_here = true; - /* Don't get confused by the index shift caused by deleting blocks. */ if (!changed_here) @@ -2472,13 +2177,6 @@ try_optimize_cfg (int mode) && try_crossjump_bb (mode, EXIT_BLOCK_PTR)) changed = true; - if (block_was_dirty) - { - /* This should only be set by head-merging. */ - gcc_assert (mode & CLEANUP_CROSSJUMP); - df_analyze (); - } - #ifdef ENABLE_CHECKING if (changed) verify_flow_info (); @@ -2663,7 +2361,8 @@ cleanup_cfg (int mode) if ((mode & CLEANUP_EXPENSIVE) && !reload_completed && !delete_trivially_dead_insns (get_insns (), max_reg_num ())) break; - if ((mode & CLEANUP_CROSSJUMP) && crossjumps_occured) + else if ((mode & CLEANUP_CROSSJUMP) + && crossjumps_occured) run_fast_dce (); } else diff --git a/gcc/df-core.c b/gcc/df-core.c index 7c49ccd..181c1e7 100644 --- a/gcc/df-core.c +++ b/gcc/df-core.c @@ -1413,7 +1413,6 @@ df_get_bb_dirty (basic_block bb) void df_set_bb_dirty (basic_block bb) { - bb->flags |= BB_MODIFIED; if (df) { int p; diff --git a/gcc/df-problems.c b/gcc/df-problems.c index 82a0d0b..e18ffe3 100644 --- a/gcc/df-problems.c +++ b/gcc/df-problems.c @@ -39,7 +39,6 @@ along with GCC; see the file COPYING3. If not see #include "basic-block.h" #include "sbitmap.h" #include "bitmap.h" -#include "target.h" #include "timevar.h" #include "df.h" #include "except.h" @@ -3501,27 +3500,6 @@ df_simulate_find_defs (rtx insn, bitmap defs) } } -/* Find the set of uses for INSN. This includes partial defs. */ - -static void -df_simulate_find_uses (rtx insn, bitmap uses) -{ - df_ref *rec; - unsigned int uid = INSN_UID (insn); - - for (rec = DF_INSN_UID_DEFS (uid); *rec; rec++) - { - df_ref def = *rec; - if (DF_REF_FLAGS (def) & (DF_REF_PARTIAL | DF_REF_CONDITIONAL)) - bitmap_set_bit (uses, DF_REF_REGNO (def)); - } - for (rec = DF_INSN_UID_USES (uid); *rec; rec++) - { - df_ref use = *rec; - bitmap_set_bit (uses, DF_REF_REGNO (use)); - } -} - /* Find the set of real DEFs, which are not clobbers, for INSN. */ void @@ -3749,301 +3727,7 @@ df_simulate_one_insn_forwards (basic_block bb, rtx insn, bitmap live) } df_simulate_fixup_sets (bb, live); } - -/* Used by the next two functions to encode information about the - memory references we found. */ -#define MEMREF_NORMAL 1 -#define MEMREF_VOLATILE 2 - -/* A subroutine of can_move_insns_across_p called through for_each_rtx. - Return either MEMREF_NORMAL or MEMREF_VOLATILE if a memory is found. */ - -static int -find_memory (rtx *px, void *data ATTRIBUTE_UNUSED) -{ - rtx x = *px; - - if (GET_CODE (x) == ASM_OPERANDS && MEM_VOLATILE_P (x)) - return MEMREF_VOLATILE; - - if (!MEM_P (x)) - return 0; - if (MEM_VOLATILE_P (x)) - return MEMREF_VOLATILE; - if (MEM_READONLY_P (x)) - return 0; - - return MEMREF_NORMAL; -} - -/* A subroutine of can_move_insns_across_p called through note_stores. - DATA points to an integer in which we set either the bit for - MEMREF_NORMAL or the bit for MEMREF_VOLATILE if we find a MEM - of either kind. */ - -static void -find_memory_stores (rtx x, const_rtx pat ATTRIBUTE_UNUSED, - void *data ATTRIBUTE_UNUSED) -{ - int *pflags = (int *)data; - if (GET_CODE (x) == SUBREG) - x = XEXP (x, 0); - /* Treat stores to SP as stores to memory, this will prevent problems - when there are references to the stack frame. */ - if (x == stack_pointer_rtx) - *pflags |= MEMREF_VOLATILE; - if (!MEM_P (x)) - return; - *pflags |= MEM_VOLATILE_P (x) ? MEMREF_VOLATILE : MEMREF_NORMAL; -} - -/* Scan BB backwards, using df_simulate functions to keep track of - lifetimes, up to insn POINT. The result is stored in LIVE. */ - -void -simulate_backwards_to_point (basic_block bb, regset live, rtx point) -{ - rtx insn; - bitmap_copy (live, df_get_live_out (bb)); - df_simulate_initialize_backwards (bb, live); - - /* Scan and update life information until we reach the point we're - interested in. */ - for (insn = BB_END (bb); insn != point; insn = PREV_INSN (insn)) - df_simulate_one_insn_backwards (bb, insn, live); -} - -/* Return true if it is safe to move a group of insns, described by - the range FROM to TO, backwards across another group of insns, - described by ACROSS_FROM to ACROSS_TO. It is assumed that there - are no insns between ACROSS_TO and FROM, but they may be in - different basic blocks; MERGE_BB is the block from which the - insns will be moved. The caller must pass in a regset MERGE_LIVE - which specifies the registers live after TO. - - This function may be called in one of two cases: either we try to - move identical instructions from all successor blocks into their - predecessor, or we try to move from only one successor block. If - OTHER_BRANCH_LIVE is nonnull, it indicates that we're dealing with - the second case. It should contain a set of registers live at the - end of ACROSS_TO which must not be clobbered by moving the insns. - In that case, we're also more careful about moving memory references - and trapping insns. - - We return false if it is not safe to move the entire group, but it - may still be possible to move a subgroup. PMOVE_UPTO, if nonnull, - is set to point at the last moveable insn in such a case. */ -bool -can_move_insns_across (rtx from, rtx to, rtx across_from, rtx across_to, - basic_block merge_bb, regset merge_live, - regset other_branch_live, rtx *pmove_upto) -{ - rtx insn, next, max_to; - bitmap merge_set, merge_use, local_merge_live; - bitmap test_set, test_use; - unsigned i, fail = 0; - bitmap_iterator bi; - int memrefs_in_across = 0; - int mem_sets_in_across = 0; - bool trapping_insns_in_across = false; - - if (pmove_upto != NULL) - *pmove_upto = NULL_RTX; - - /* Find real bounds, ignoring debug insns. */ - while (!NONDEBUG_INSN_P (from) && from != to) - from = NEXT_INSN (from); - while (!NONDEBUG_INSN_P (to) && from != to) - to = PREV_INSN (to); - - for (insn = across_to; ; insn = next) - { - if (NONDEBUG_INSN_P (insn)) - { - memrefs_in_across |= for_each_rtx (&PATTERN (insn), find_memory, - NULL); - note_stores (PATTERN (insn), find_memory_stores, - &mem_sets_in_across); - /* This is used just to find sets of the stack pointer. */ - memrefs_in_across |= mem_sets_in_across; - trapping_insns_in_across |= may_trap_p (PATTERN (insn)); - } - next = PREV_INSN (insn); - if (insn == across_from) - break; - } - - /* Collect: - MERGE_SET = set of registers set in MERGE_BB - MERGE_USE = set of registers used in MERGE_BB and live at its top - MERGE_LIVE = set of registers live at the point inside the MERGE - range that we've reached during scanning - TEST_SET = set of registers set between ACROSS_FROM and ACROSS_END. - TEST_USE = set of registers used between ACROSS_FROM and ACROSS_END, - and live before ACROSS_FROM. */ - - merge_set = BITMAP_ALLOC (®_obstack); - merge_use = BITMAP_ALLOC (®_obstack); - local_merge_live = BITMAP_ALLOC (®_obstack); - test_set = BITMAP_ALLOC (®_obstack); - test_use = BITMAP_ALLOC (®_obstack); - - /* Compute the set of registers set and used in the ACROSS range. */ - if (other_branch_live != NULL) - bitmap_copy (test_use, other_branch_live); - df_simulate_initialize_backwards (merge_bb, test_use); - for (insn = across_to; ; insn = next) - { - if (NONDEBUG_INSN_P (insn)) - { - df_simulate_find_defs (insn, test_set); - df_simulate_defs (insn, test_use); - df_simulate_uses (insn, test_use); - } - next = PREV_INSN (insn); - if (insn == across_from) - break; - } - - /* Compute an upper bound for the amount of insns moved, by finding - the first insn in MERGE that sets a register in TEST_USE, or uses - a register in TEST_SET. We also check for calls, trapping operations, - and memory references. */ - max_to = NULL_RTX; - for (insn = from; ; insn = next) - { - if (CALL_P (insn)) - break; - if (NONDEBUG_INSN_P (insn)) - { - if (may_trap_p (PATTERN (insn)) - && (trapping_insns_in_across || other_branch_live != NULL)) - break; - - /* We cannot move memory stores past each other, or move memory - reads past stores, at least not without tracking them and - calling true_dependence on every pair. - - If there is no other branch and no memory references or - sets in the ACROSS range, we can move memory references - freely, even volatile ones. - - Otherwise, the rules are as follows: volatile memory - references and stores can't be moved at all, and any type - of memory reference can't be moved if there are volatile - accesses or stores in the ACROSS range. That leaves - normal reads, which can be moved, as the trapping case is - dealt with elsewhere. */ - if (other_branch_live != NULL || memrefs_in_across != 0) - { - int mem_ref_flags = 0; - int mem_set_flags = 0; - note_stores (PATTERN (insn), find_memory_stores, &mem_set_flags); - mem_ref_flags = for_each_rtx (&PATTERN (insn), find_memory, - NULL); - /* Catch sets of the stack pointer. */ - mem_ref_flags |= mem_set_flags; - - if ((mem_ref_flags | mem_set_flags) & MEMREF_VOLATILE) - break; - if ((memrefs_in_across & MEMREF_VOLATILE) && mem_ref_flags != 0) - break; - if (mem_set_flags != 0 - || (mem_sets_in_across != 0 && mem_ref_flags != 0)) - break; - } - df_simulate_find_uses (insn, merge_use); - /* We're only interested in uses which use a value live at - the top, not one previously set in this block. */ - bitmap_and_compl_into (merge_use, merge_set); - df_simulate_find_defs (insn, merge_set); - if (bitmap_intersect_p (merge_set, test_use) - || bitmap_intersect_p (merge_use, test_set)) - break; - max_to = insn; - } - next = NEXT_INSN (insn); - if (insn == to) - break; - } - if (max_to != to) - fail = 1; - - if (max_to == NULL_RTX || (fail && pmove_upto == NULL)) - goto out; - - /* Now, lower this upper bound by also taking into account that - a range of insns moved across ACROSS must not leave a register - live at the end that will be clobbered in ACROSS. We need to - find a point where TEST_SET & LIVE == 0. - - Insns in the MERGE range that set registers which are also set - in the ACROSS range may still be moved as long as we also move - later insns which use the results of the set, and make the - register dead again. This is verified by the condition stated - above. We only need to test it for registers that are set in - the moved region. - - MERGE_LIVE is provided by the caller and holds live registers after - TO. */ - bitmap_copy (local_merge_live, merge_live); - for (insn = to; insn != max_to; insn = PREV_INSN (insn)) - df_simulate_one_insn_backwards (merge_bb, insn, local_merge_live); - - /* We're not interested in registers that aren't set in the moved - region at all. */ - bitmap_and_into (local_merge_live, merge_set); - for (;;) - { - if (NONDEBUG_INSN_P (insn)) - { - if (!bitmap_intersect_p (test_set, local_merge_live)) - { - max_to = insn; - break; - } - - df_simulate_one_insn_backwards (merge_bb, insn, - local_merge_live); - } - if (insn == from) - { - fail = 1; - goto out; - } - insn = PREV_INSN (insn); - } - - if (max_to != to) - fail = 1; - - if (pmove_upto) - *pmove_upto = max_to; - - /* For small register class machines, don't lengthen lifetimes of - hard registers before reload. */ - if (! reload_completed - && targetm.small_register_classes_for_mode_p (VOIDmode)) - { - EXECUTE_IF_SET_IN_BITMAP (merge_set, 0, i, bi) - { - if (i < FIRST_PSEUDO_REGISTER - && ! fixed_regs[i] - && ! global_regs[i]) - fail = 1; - } - } - - out: - BITMAP_FREE (merge_set); - BITMAP_FREE (merge_use); - BITMAP_FREE (local_merge_live); - BITMAP_FREE (test_set); - BITMAP_FREE (test_use); - - return !fail; -} /*---------------------------------------------------------------------------- diff --git a/gcc/df.h b/gcc/df.h index a831c9a..e52cc0e 100644 --- a/gcc/df.h +++ b/gcc/df.h @@ -971,9 +971,7 @@ extern void df_simulate_one_insn_backwards (basic_block, rtx, bitmap); extern void df_simulate_finalize_backwards (basic_block, bitmap); extern void df_simulate_initialize_forwards (basic_block, bitmap); extern void df_simulate_one_insn_forwards (basic_block, rtx, bitmap); -extern void simulate_backwards_to_point (basic_block, regset, rtx); -extern bool can_move_insns_across (rtx, rtx, rtx, rtx, basic_block, regset, - regset, rtx *); + /* Functions defined in df-scan.c. */ extern void df_scan_alloc (bitmap); diff --git a/gcc/ifcvt.c b/gcc/ifcvt.c index 56d9d9e..ff75ed1 100644 --- a/gcc/ifcvt.c +++ b/gcc/ifcvt.c @@ -103,6 +103,7 @@ static int noce_find_if_block (basic_block, edge, edge, int); static int cond_exec_find_if_block (ce_if_block_t *); static int find_if_case_1 (basic_block, edge, edge); static int find_if_case_2 (basic_block, edge, edge); +static int find_memory (rtx *, void *); static int dead_or_predicable (basic_block, basic_block, basic_block, basic_block, int); static void noce_emit_move_insn (rtx, rtx); @@ -3975,6 +3976,15 @@ find_if_case_2 (basic_block test_bb, edge then_edge, edge else_edge) return TRUE; } +/* A subroutine of dead_or_predicable called through for_each_rtx. + Return 1 if a memory is found. */ + +static int +find_memory (rtx *px, void *data ATTRIBUTE_UNUSED) +{ + return MEM_P (*px); +} + /* Used by the code above to perform the actual rtl transformations. Return TRUE if successful. @@ -4076,38 +4086,131 @@ dead_or_predicable (basic_block test_bb, basic_block merge_bb, earliest = jump; } #endif - /* If we allocated new pseudos (e.g. in the conditional move - expander called from noce_emit_cmove), we must resize the - array first. */ - if (max_regno < max_reg_num ()) - max_regno = max_reg_num (); - /* Try the NCE path if the CE path did not result in any changes. */ if (n_validated_changes == 0) { - rtx cond; - regset live; - bool success; - /* In the non-conditional execution case, we have to verify that there are no trapping operations, no calls, no references to memory, and that any registers modified are dead at the branch site. */ - if (!any_condjump_p (jump)) + rtx insn, cond, prev; + bitmap merge_set, merge_set_noclobber, test_live, test_set; + unsigned i, fail = 0; + bitmap_iterator bi; + + /* Check for no calls or trapping operations. */ + for (insn = head; ; insn = NEXT_INSN (insn)) + { + if (CALL_P (insn)) + return FALSE; + if (NONDEBUG_INSN_P (insn)) + { + if (may_trap_p (PATTERN (insn))) + return FALSE; + + /* ??? Even non-trapping memories such as stack frame + references must be avoided. For stores, we collect + no lifetime info; for reads, we'd have to assert + true_dependence false against every store in the + TEST range. */ + if (for_each_rtx (&PATTERN (insn), find_memory, NULL)) + return FALSE; + } + if (insn == end) + break; + } + + if (! any_condjump_p (jump)) return FALSE; /* Find the extent of the conditional. */ cond = noce_get_condition (jump, &earliest, false); - if (!cond) + if (! cond) return FALSE; - live = BITMAP_ALLOC (®_obstack); - simulate_backwards_to_point (merge_bb, live, end); - success = can_move_insns_across (head, end, earliest, jump, - merge_bb, live, - df_get_live_in (other_bb), NULL); - BITMAP_FREE (live); - if (!success) + /* Collect: + MERGE_SET = set of registers set in MERGE_BB + MERGE_SET_NOCLOBBER = like MERGE_SET, but only includes registers + that are really set, not just clobbered. + TEST_LIVE = set of registers live at EARLIEST + TEST_SET = set of registers set between EARLIEST and the + end of the block. */ + + merge_set = BITMAP_ALLOC (®_obstack); + merge_set_noclobber = BITMAP_ALLOC (®_obstack); + test_live = BITMAP_ALLOC (®_obstack); + test_set = BITMAP_ALLOC (®_obstack); + + /* ??? bb->local_set is only valid during calculate_global_regs_live, + so we must recompute usage for MERGE_BB. Not so bad, I suppose, + since we've already asserted that MERGE_BB is small. */ + /* If we allocated new pseudos (e.g. in the conditional move + expander called from noce_emit_cmove), we must resize the + array first. */ + if (max_regno < max_reg_num ()) + max_regno = max_reg_num (); + + FOR_BB_INSNS (merge_bb, insn) + { + if (NONDEBUG_INSN_P (insn)) + { + df_simulate_find_defs (insn, merge_set); + df_simulate_find_noclobber_defs (insn, merge_set_noclobber); + } + } + + /* For small register class machines, don't lengthen lifetimes of + hard registers before reload. */ + if (! reload_completed + && targetm.small_register_classes_for_mode_p (VOIDmode)) + { + EXECUTE_IF_SET_IN_BITMAP (merge_set_noclobber, 0, i, bi) + { + if (i < FIRST_PSEUDO_REGISTER + && ! fixed_regs[i] + && ! global_regs[i]) + fail = 1; + } + } + + /* For TEST, we're interested in a range of insns, not a whole block. + Moreover, we're interested in the insns live from OTHER_BB. */ + + /* The loop below takes the set of live registers + after JUMP, and calculates the live set before EARLIEST. */ + bitmap_copy (test_live, df_get_live_in (other_bb)); + df_simulate_initialize_backwards (test_bb, test_live); + for (insn = jump; ; insn = prev) + { + if (INSN_P (insn)) + { + df_simulate_find_defs (insn, test_set); + df_simulate_one_insn_backwards (test_bb, insn, test_live); + } + prev = PREV_INSN (insn); + if (insn == earliest) + break; + } + + /* We can perform the transformation if + MERGE_SET_NOCLOBBER & TEST_SET + and + MERGE_SET & TEST_LIVE) + and + TEST_SET & DF_LIVE_IN (merge_bb) + are empty. */ + + if (bitmap_intersect_p (test_set, merge_set_noclobber) + || bitmap_intersect_p (test_live, merge_set) + || bitmap_intersect_p (test_set, df_get_live_in (merge_bb))) + fail = 1; + + BITMAP_FREE (merge_set_noclobber); + BITMAP_FREE (merge_set); + BITMAP_FREE (test_live); + BITMAP_FREE (test_set); + + if (fail) return FALSE; } diff --git a/gcc/testsuite/gcc.dg/pr45865.c b/gcc/testsuite/gcc.dg/pr45865.c new file mode 100644 index 0000000..6edea31 --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr45865.c @@ -0,0 +1,28 @@ +/* PR rtl-optimization/45865 */ +/* { dg-do compile } */ +/* { dg-options "-O2 -fcompare-debug" } */ + +typedef union tree_node *tree; +enum ix86_builtin_type { + IX86_BT_LAST_VECT, + IX86_BT_LAST_PTR +}; +extern const enum ix86_builtin_type ix86_builtin_type_ptr_base[]; +extern tree build_qualified_type (tree, int); +extern tree build_pointer_type (tree); +tree +ix86_get_builtin_type (enum ix86_builtin_type tcode, unsigned int index) +{ + tree type, itype; + int quals; + if (tcode <= IX86_BT_LAST_PTR) + quals = 0x0; + else + quals = 0x1; + itype = ix86_get_builtin_type (ix86_builtin_type_ptr_base[index], + index); + if (quals != 0x0) + itype = build_qualified_type (itype, quals); + type = build_pointer_type (itype); + return type; +} diff --git a/gcc/testsuite/gcc.dg/torture/pr45865.c b/gcc/testsuite/gcc.dg/torture/pr45865.c new file mode 100644 index 0000000..f2e31db --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/pr45865.c @@ -0,0 +1,54 @@ +/* { dg-do compile } */ + +typedef struct rtx_def *rtx; +enum machine_mode { + VOIDmode, + CCFPmode, + CCFPUmode, + MAX_MACHINE_MODE +}; +enum mode_class { + MODE_CC, + MODE_FLOAT, + MODE_COMPLEX_FLOAT, + MODE_VECTOR_FLOAT +}; +extern const enum mode_class mode_class[(int) MAX_MACHINE_MODE]; +enum rtx_code { + UNKNOWN, + GEU, + ORDERED, + CONST_INT +}; +struct rtx_def { + unsigned int code: 16; + unsigned int mode : 8; +}; +extern enum rtx_code reverse_condition (enum rtx_code); +enum rtx_code +reversed_comparison_code_parts (enum rtx_code code, rtx insn, rtx arg0, + rtx arg1) +{ + enum machine_mode mode; + mode = (enum machine_mode) (arg0)->mode; + if (mode == VOIDmode) + mode = (enum machine_mode) (arg1)->mode; + if ((mode_class[(int) (mode)]) == MODE_CC) + return (mode != CCFPmode && mode != CCFPUmode + ? reverse_condition (code) + : reverse_condition_maybe_unordered (code)); + switch (code) + { + case GEU: + return reverse_condition (code); + case ORDERED: + return UNKNOWN; + } + if (((enum rtx_code) (arg0)->code) == CONST_INT + || (((enum machine_mode) (arg0)->mode) != VOIDmode + && ! ((mode_class[(int) (mode)]) == MODE_FLOAT + || (mode_class[(int) (mode)]) == MODE_COMPLEX_FLOAT + || (mode_class[(int) (mode)]) == MODE_VECTOR_FLOAT))) + return reverse_condition (code); + return UNKNOWN; +} diff --git a/gcc/testsuite/gcc.target/arm/headmerge-1.c b/gcc/testsuite/gcc.target/arm/headmerge-1.c deleted file mode 100644 index 218c6a2..0000000 --- a/gcc/testsuite/gcc.target/arm/headmerge-1.c +++ /dev/null @@ -1,14 +0,0 @@ -/* { dg-do compile } */ -/* { dg-options "-O2" } */ -/* { dg-final { scan-assembler-times "#120" 1 } } */ - -extern void foo1 (int); -extern void foo2 (int); - -void t (int x, int y) -{ - if (y < 5) - foo1 (120); - else - foo2 (120); -} diff --git a/gcc/testsuite/gcc.target/arm/headmerge-2.c b/gcc/testsuite/gcc.target/arm/headmerge-2.c deleted file mode 100644 index 36637a6..0000000 --- a/gcc/testsuite/gcc.target/arm/headmerge-2.c +++ /dev/null @@ -1,35 +0,0 @@ -/* { dg-do compile } */ -/* { dg-options "-O2" } */ -/* { dg-final { scan-assembler-times "120" 1 } } */ - -extern void foo1 (int); -extern void foo2 (int); -extern void foo3 (int); -extern void foo4 (int); -extern void foo5 (int); -extern void foo6 (int); - -void t (int x, int y) -{ - switch (y) - { - case 1: - foo1 (120); - break; - case 5: - foo2 (120); - break; - case 7: - foo3 (120); - break; - case 10: - foo4 (120); - break; - case 13: - foo5 (120); - break; - default: - foo6 (120); - break; - } -} diff --git a/gcc/testsuite/gcc.target/i386/headmerge-1.c b/gcc/testsuite/gcc.target/i386/headmerge-1.c deleted file mode 100644 index 941028c..0000000 --- a/gcc/testsuite/gcc.target/i386/headmerge-1.c +++ /dev/null @@ -1,14 +0,0 @@ -/* { dg-do compile } */ -/* { dg-options "-O2" } */ -/* { dg-final { scan-assembler-times "120" 1 } } */ - -extern void foo1 (int); -extern void foo2 (int); - -void t (int x, int y) -{ - if (y < 5) - foo1 (120); - else - foo2 (120); -} diff --git a/gcc/testsuite/gcc.target/i386/headmerge-2.c b/gcc/testsuite/gcc.target/i386/headmerge-2.c deleted file mode 100644 index 36637a6..0000000 --- a/gcc/testsuite/gcc.target/i386/headmerge-2.c +++ /dev/null @@ -1,35 +0,0 @@ -/* { dg-do compile } */ -/* { dg-options "-O2" } */ -/* { dg-final { scan-assembler-times "120" 1 } } */ - -extern void foo1 (int); -extern void foo2 (int); -extern void foo3 (int); -extern void foo4 (int); -extern void foo5 (int); -extern void foo6 (int); - -void t (int x, int y) -{ - switch (y) - { - case 1: - foo1 (120); - break; - case 5: - foo2 (120); - break; - case 7: - foo3 (120); - break; - case 10: - foo4 (120); - break; - case 13: - foo5 (120); - break; - default: - foo6 (120); - break; - } -}