OSDN Git Service

Daily bump.
[pf3gnuchains/gcc-fork.git] / gcc / ifcvt.c
index 9aff583..49b489e 100644 (file)
@@ -1,5 +1,6 @@
 /* If-conversion support.
-   Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2010
+   Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2010,
+   2011
    Free Software Foundation, Inc.
 
    This file is part of GCC.
@@ -36,7 +37,6 @@
 #include "output.h"
 #include "optabs.h"
 #include "diagnostic-core.h"
-#include "toplev.h"
 #include "tm_p.h"
 #include "cfgloop.h"
 #include "target.h"
@@ -44,6 +44,7 @@
 #include "tree-pass.h"
 #include "df.h"
 #include "vec.h"
+#include "pointer-set.h"
 #include "vecprim.h"
 #include "dbgcnt.h"
 
@@ -85,9 +86,11 @@ static int cond_exec_changed_p;
 
 /* Forward references.  */
 static int count_bb_insns (const_basic_block);
-static bool cheap_bb_rtx_cost_p (const_basic_block, int);
+static bool cheap_bb_rtx_cost_p (const_basic_block, int, int);
 static rtx first_active_insn (basic_block);
 static rtx last_active_insn (basic_block, int);
+static rtx find_active_insn_before (basic_block, rtx);
+static rtx find_active_insn_after (basic_block, rtx);
 static basic_block block_fallthru (basic_block);
 static int cond_exec_process_insns (ce_if_block_t *, rtx, rtx, rtx, rtx, int);
 static rtx cond_exec_get_condition (rtx);
@@ -101,9 +104,8 @@ 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);
+                              edge, int);
 static void noce_emit_move_insn (rtx, rtx);
 static rtx block_has_only_trap (basic_block);
 \f
@@ -130,20 +132,31 @@ count_bb_insns (const_basic_block bb)
 
 /* Determine whether the total insn_rtx_cost on non-jump insns in
    basic block BB is less than MAX_COST.  This function returns
-   false if the cost of any instruction could not be estimated.  */
+   false if the cost of any instruction could not be estimated. 
+
+   The cost of the non-jump insns in BB is scaled by REG_BR_PROB_BASE
+   as those insns are being speculated.  MAX_COST is scaled with SCALE
+   plus a small fudge factor.  */
 
 static bool
-cheap_bb_rtx_cost_p (const_basic_block bb, int max_cost)
+cheap_bb_rtx_cost_p (const_basic_block bb, int scale, int max_cost)
 {
   int count = 0;
   rtx insn = BB_HEAD (bb);
   bool speed = optimize_bb_for_speed_p (bb);
 
+  /* Our branch probability/scaling factors are just estimates and don't
+     account for cases where we can get speculation for free and other
+     secondary benefits.  So we fudge the scale factor to make speculating
+     appear a little more profitable.  */
+  scale += REG_BR_PROB_BASE / 8;
+  max_cost *= scale;
+
   while (1)
     {
       if (NONJUMP_INSN_P (insn))
        {
-         int cost = insn_rtx_cost (PATTERN (insn), speed);
+         int cost = insn_rtx_cost (PATTERN (insn), speed) * REG_BR_PROB_BASE;
          if (cost == 0)
            return false;
 
@@ -230,17 +243,54 @@ last_active_insn (basic_block bb, int skip_use_p)
   return insn;
 }
 
+/* Return the active insn before INSN inside basic block CURR_BB. */
+
+static rtx
+find_active_insn_before (basic_block curr_bb, rtx insn)
+{
+  if (!insn || insn == BB_HEAD (curr_bb))
+    return NULL_RTX;
+
+  while ((insn = PREV_INSN (insn)) != NULL_RTX)
+    {
+      if (NONJUMP_INSN_P (insn) || JUMP_P (insn) || CALL_P (insn))
+        break;
+
+      /* No other active insn all the way to the start of the basic block. */
+      if (insn == BB_HEAD (curr_bb))
+        return NULL_RTX;
+    }
+
+  return insn;
+}
+
+/* Return the active insn after INSN inside basic block CURR_BB. */
+
+static rtx
+find_active_insn_after (basic_block curr_bb, rtx insn)
+{
+  if (!insn || insn == BB_END (curr_bb))
+    return NULL_RTX;
+
+  while ((insn = NEXT_INSN (insn)) != NULL_RTX)
+    {
+      if (NONJUMP_INSN_P (insn) || JUMP_P (insn) || CALL_P (insn))
+        break;
+
+      /* No other active insn all the way to the end of the basic block. */
+      if (insn == BB_END (curr_bb))
+        return NULL_RTX;
+    }
+
+  return insn;
+}
+
 /* Return the basic block reached by falling though the basic block BB.  */
 
 static basic_block
 block_fallthru (basic_block bb)
 {
-  edge e;
-  edge_iterator ei;
-
-  FOR_EACH_EDGE (e, ei, bb->succs)
-    if (e->flags & EDGE_FALLTHRU)
-      break;
+  edge e = find_fallthru_edge (bb->succs);
 
   return (e) ? e->dest : NULL_BLOCK;
 }
@@ -267,6 +317,10 @@ cond_exec_process_insns (ce_if_block_t *ce_info ATTRIBUTE_UNUSED,
 
   for (insn = start; ; insn = NEXT_INSN (insn))
     {
+      /* dwarf2out can't cope with conditional prologues.  */
+      if (NOTE_P (insn) && NOTE_KIND (insn) == NOTE_INSN_PROLOGUE_END)
+       return FALSE;
+
       if (NOTE_P (insn) || DEBUG_INSN_P (insn))
        goto insn_done;
 
@@ -439,7 +493,8 @@ cond_exec_process_if_block (ce_if_block_t * ce_info,
       /* Look for matching sequences at the head and tail of the two blocks,
         and limit the range of insns to be converted if possible.  */
       n_matching = flow_find_cross_jump (then_bb, else_bb,
-                                        &then_first_tail, &else_first_tail);
+                                        &then_first_tail, &else_first_tail,
+                                        NULL);
       if (then_first_tail == BB_HEAD (then_bb))
        then_start = then_end = NULL_RTX;
       if (else_first_tail == BB_HEAD (else_bb))
@@ -448,9 +503,9 @@ cond_exec_process_if_block (ce_if_block_t * ce_info,
       if (n_matching > 0)
        {
          if (then_end)
-           then_end = prev_active_insn (then_first_tail);
+           then_end = find_active_insn_before (then_bb, then_first_tail);
          if (else_end)
-           else_end = prev_active_insn (else_first_tail);
+           else_end = find_active_insn_before (else_bb, else_first_tail);
          n_insns -= 2 * n_matching;
        }
 
@@ -488,9 +543,9 @@ cond_exec_process_if_block (ce_if_block_t * ce_info,
          if (n_matching > 0)
            {
              if (then_start)
-               then_start = next_active_insn (then_last_head);
+               then_start = find_active_insn_after (then_bb, then_last_head);
              if (else_start)
-               else_start = next_active_insn (else_last_head);
+               else_start = find_active_insn_after (else_bb, else_last_head);
              n_insns -= 2 * n_matching;
            }
        }
@@ -646,7 +701,7 @@ cond_exec_process_if_block (ce_if_block_t * ce_info,
     {
       rtx from = then_first_tail;
       if (!INSN_P (from))
-       from = next_active_insn (from);
+       from = find_active_insn_after (then_bb, from);
       delete_insn_chain (from, BB_END (then_bb), false);
     }
   if (else_last_head)
@@ -842,7 +897,7 @@ noce_emit_move_insn (rtx x, rtx y)
                }
 
              gcc_assert (start < (MEM_P (op) ? BITS_PER_UNIT : BITS_PER_WORD));
-             store_bit_field (op, size, start, GET_MODE (x), y);
+             store_bit_field (op, size, start, 0, 0, GET_MODE (x), y);
              return;
            }
 
@@ -896,7 +951,8 @@ noce_emit_move_insn (rtx x, rtx y)
   inner = XEXP (outer, 0);
   outmode = GET_MODE (outer);
   bitpos = SUBREG_BYTE (outer) * BITS_PER_UNIT;
-  store_bit_field (inner, GET_MODE_BITSIZE (outmode), bitpos, outmode, y);
+  store_bit_field (inner, GET_MODE_BITSIZE (outmode), bitpos,
+                  0, 0, outmode, y);
 }
 
 /* Return sequence of instructions generated by if conversion.  This
@@ -1294,6 +1350,9 @@ static rtx
 noce_emit_cmove (struct noce_if_info *if_info, rtx x, enum rtx_code code,
                 rtx cmp_a, rtx cmp_b, rtx vfalse, rtx vtrue)
 {
+  rtx target ATTRIBUTE_UNUSED;
+  int unsignedp ATTRIBUTE_UNUSED;
+
   /* If earliest == jump, try to build the cmove insn directly.
      This is helpful when combine has created some complex condition
      (like for alpha's cmovlbs) that we can't hope to regenerate
@@ -1328,10 +1387,62 @@ noce_emit_cmove (struct noce_if_info *if_info, rtx x, enum rtx_code code,
     return NULL_RTX;
 
 #if HAVE_conditional_move
-  return emit_conditional_move (x, code, cmp_a, cmp_b, VOIDmode,
-                               vtrue, vfalse, GET_MODE (x),
-                               (code == LTU || code == GEU
-                                || code == LEU || code == GTU));
+  unsignedp = (code == LTU || code == GEU
+              || code == LEU || code == GTU);
+
+  target = emit_conditional_move (x, code, cmp_a, cmp_b, VOIDmode,
+                                 vtrue, vfalse, GET_MODE (x),
+                                 unsignedp);
+  if (target)
+    return target;
+
+  /* We might be faced with a situation like:
+
+     x = (reg:M TARGET)
+     vtrue = (subreg:M (reg:N VTRUE) BYTE)
+     vfalse = (subreg:M (reg:N VFALSE) BYTE)
+
+     We can't do a conditional move in mode M, but it's possible that we
+     could do a conditional move in mode N instead and take a subreg of
+     the result.
+
+     If we can't create new pseudos, though, don't bother.  */
+  if (reload_completed)
+    return NULL_RTX;
+
+  if (GET_CODE (vtrue) == SUBREG && GET_CODE (vfalse) == SUBREG)
+    {
+      rtx reg_vtrue = SUBREG_REG (vtrue);
+      rtx reg_vfalse = SUBREG_REG (vfalse);
+      unsigned int byte_vtrue = SUBREG_BYTE (vtrue);
+      unsigned int byte_vfalse = SUBREG_BYTE (vfalse);
+      rtx promoted_target;
+
+      if (GET_MODE (reg_vtrue) != GET_MODE (reg_vfalse)
+         || byte_vtrue != byte_vfalse
+         || (SUBREG_PROMOTED_VAR_P (vtrue)
+             != SUBREG_PROMOTED_VAR_P (vfalse))
+         || (SUBREG_PROMOTED_UNSIGNED_P (vtrue)
+             != SUBREG_PROMOTED_UNSIGNED_P (vfalse)))
+       return NULL_RTX;
+
+      promoted_target = gen_reg_rtx (GET_MODE (reg_vtrue));
+
+      target = emit_conditional_move (promoted_target, code, cmp_a, cmp_b,
+                                     VOIDmode, reg_vtrue, reg_vfalse,
+                                     GET_MODE (reg_vtrue), unsignedp);
+      /* Nope, couldn't do it in that mode either.  */
+      if (!target)
+       return NULL_RTX;
+
+      target = gen_rtx_SUBREG (GET_MODE (vtrue), promoted_target, byte_vtrue);
+      SUBREG_PROMOTED_VAR_P (target) = SUBREG_PROMOTED_VAR_P (vtrue);
+      SUBREG_PROMOTED_UNSIGNED_SET (target, SUBREG_PROMOTED_UNSIGNED_P (vtrue));
+      emit_move_insn (x, target);
+      return x;
+    }
+  else
+    return NULL_RTX;
 #else
   /* We'll never get here, as noce_process_if_block doesn't call the
      functions involved.  Ifdef code, however, should be discouraged
@@ -1420,9 +1531,9 @@ noce_try_cmove_arith (struct noce_if_info *if_info)
     }
 
   /* ??? We could handle this if we knew that a load from A or B could
-     not fault.  This is also true if we've already loaded
+     not trap or fault.  This is also true if we've already loaded
      from the address along the path from ENTRY.  */
-  else if (may_trap_p (a) || may_trap_p (b))
+  else if (may_trap_or_fault_p (a) || may_trap_or_fault_p (b))
     return FALSE;
 
   /* if (test) x = a + b; else x = c - d;
@@ -1557,10 +1668,6 @@ noce_try_cmove_arith (struct noce_if_info *if_info)
       /* Copy over flags as appropriate.  */
       if (MEM_VOLATILE_P (if_info->a) || MEM_VOLATILE_P (if_info->b))
        MEM_VOLATILE_P (tmp) = 1;
-      if (MEM_IN_STRUCT_P (if_info->a) && MEM_IN_STRUCT_P (if_info->b))
-       MEM_IN_STRUCT_P (tmp) = 1;
-      if (MEM_SCALAR_P (if_info->a) && MEM_SCALAR_P (if_info->b))
-       MEM_SCALAR_P (tmp) = 1;
       if (MEM_ALIAS_SET (if_info->a) == MEM_ALIAS_SET (if_info->b))
        set_mem_alias_set (tmp, MEM_ALIAS_SET (if_info->a));
       set_mem_align (tmp,
@@ -2027,7 +2134,7 @@ noce_try_sign_mask (struct noce_if_info *if_info)
      && (if_info->insn_b == NULL_RTX
         || BLOCK_FOR_INSN (if_info->insn_b) == if_info->test_bb));
   if (!(t_unconditional
-       || (rtx_cost (t, SET, optimize_bb_for_speed_p (if_info->test_bb))
+       || (set_src_cost (t, optimize_bb_for_speed_p (if_info->test_bb))
            < COSTS_N_INSNS (2))))
     return FALSE;
 
@@ -2189,7 +2296,9 @@ noce_get_condition (rtx jump, rtx *earliest, bool then_else_reversed)
 
   cond = XEXP (SET_SRC (set), 0);
   tmp = XEXP (cond, 0);
-  if (REG_P (tmp) && GET_MODE_CLASS (GET_MODE (tmp)) == MODE_INT)
+  if (REG_P (tmp) && GET_MODE_CLASS (GET_MODE (tmp)) == MODE_INT
+      && (GET_MODE (tmp) != BImode
+          || !targetm.small_register_classes_for_mode_p (BImode)))
     {
       *earliest = jump;
 
@@ -2217,14 +2326,14 @@ noce_get_condition (rtx jump, rtx *earliest, bool then_else_reversed)
 static int
 noce_operand_ok (const_rtx op)
 {
+  if (side_effects_p (op))
+    return FALSE;
+
   /* We special-case memories, so handle any of them with
      no address side effects.  */
   if (MEM_P (op))
     return ! side_effects_p (XEXP (op, 0));
 
-  if (side_effects_p (op))
-    return FALSE;
-
   return ! may_trap_p (op);
 }
 
@@ -2581,12 +2690,14 @@ noce_process_if_block (struct noce_if_info *if_info)
 
 /* Check whether a block is suitable for conditional move conversion.
    Every insn must be a simple set of a register to a constant or a
-   register.  For each assignment, store the value in the array VALS,
-   indexed by register number, then store the register number in
-   REGS.  COND is the condition we will test.  */
+   register.  For each assignment, store the value in the pointer map
+   VALS, keyed indexed by register pointer, then store the register
+   pointer in REGS.  COND is the condition we will test.  */
 
 static int
-check_cond_move_block (basic_block bb, rtx *vals, VEC (int, heap) **regs,
+check_cond_move_block (basic_block bb,
+                      struct pointer_map_t *vals,
+                      VEC (rtx, heap) **regs,
                       rtx cond)
 {
   rtx insn;
@@ -2600,6 +2711,7 @@ check_cond_move_block (basic_block bb, rtx *vals, VEC (int, heap) **regs,
   FOR_BB_INSNS (bb, insn)
     {
       rtx set, dest, src;
+      void **slot;
 
       if (!NONDEBUG_INSN_P (insn) || JUMP_P (insn))
        continue;
@@ -2626,14 +2738,14 @@ check_cond_move_block (basic_block bb, rtx *vals, VEC (int, heap) **regs,
       /* Don't try to handle this if the source register was
         modified earlier in the block.  */
       if ((REG_P (src)
-          && vals[REGNO (src)] != NULL)
+          && pointer_map_contains (vals, src))
          || (GET_CODE (src) == SUBREG && REG_P (SUBREG_REG (src))
-             && vals[REGNO (SUBREG_REG (src))] != NULL))
+             && pointer_map_contains (vals, SUBREG_REG (src))))
        return FALSE;
 
       /* Don't try to handle this if the destination register was
         modified earlier in the block.  */
-      if (vals[REGNO (dest)] != NULL)
+      if (pointer_map_contains (vals, dest))
        return FALSE;
 
       /* Don't try to handle this if the condition uses the
@@ -2647,17 +2759,18 @@ check_cond_move_block (basic_block bb, rtx *vals, VEC (int, heap) **regs,
          && modified_between_p (src, insn, NEXT_INSN (BB_END (bb))))
        return FALSE;
 
-      vals[REGNO (dest)] = src;
+      slot = pointer_map_insert (vals, (void *) dest);
+      *slot = (void *) src;
 
-      VEC_safe_push (int, heap, *regs, REGNO (dest));
+      VEC_safe_push (rtx, heap, *regs, dest);
     }
 
   return TRUE;
 }
 
 /* Given a basic block BB suitable for conditional move conversion,
-   a condition COND, and arrays THEN_VALS and ELSE_VALS containing the
-   register values depending on COND, emit the insns in the block as
+   a condition COND, and pointer maps THEN_VALS and ELSE_VALS containing
+   the register values depending on COND, emit the insns in the block as
    conditional moves.  If ELSE_BLOCK is true, THEN_BB was already
    processed.  The caller has started a sequence for the conversion.
    Return true if successful, false if something goes wrong.  */
@@ -2665,7 +2778,8 @@ check_cond_move_block (basic_block bb, rtx *vals, VEC (int, heap) **regs,
 static bool
 cond_move_convert_if_block (struct noce_if_info *if_infop,
                            basic_block bb, rtx cond,
-                           rtx *then_vals, rtx *else_vals,
+                           struct pointer_map_t *then_vals,
+                           struct pointer_map_t *else_vals,
                            bool else_block_p)
 {
   enum rtx_code code;
@@ -2678,7 +2792,7 @@ cond_move_convert_if_block (struct noce_if_info *if_infop,
   FOR_BB_INSNS (bb, insn)
     {
       rtx set, target, dest, t, e;
-      unsigned int regno;
+      void **then_slot, **else_slot;
 
       /* ??? Maybe emit conditional debug insn?  */
       if (!NONDEBUG_INSN_P (insn) || JUMP_P (insn))
@@ -2687,10 +2801,11 @@ cond_move_convert_if_block (struct noce_if_info *if_infop,
       gcc_assert (set && REG_P (SET_DEST (set)));
 
       dest = SET_DEST (set);
-      regno = REGNO (dest);
 
-      t = then_vals[regno];
-      e = else_vals[regno];
+      then_slot = pointer_map_contains (then_vals, dest);
+      else_slot = pointer_map_contains (else_vals, dest);
+      t = then_slot ? (rtx) *then_slot : NULL_RTX;
+      e = else_slot ? (rtx) *else_slot : NULL_RTX;
 
       if (else_block_p)
        {
@@ -2734,31 +2849,25 @@ cond_move_process_if_block (struct noce_if_info *if_info)
   rtx jump = if_info->jump;
   rtx cond = if_info->cond;
   rtx seq, loc_insn;
-  int max_reg, size, c, reg;
-  rtx *then_vals;
-  rtx *else_vals;
-  VEC (int, heap) *then_regs = NULL;
-  VEC (int, heap) *else_regs = NULL;
+  rtx reg;
+  int c;
+  struct pointer_map_t *then_vals;
+  struct pointer_map_t *else_vals;
+  VEC (rtx, heap) *then_regs = NULL;
+  VEC (rtx, heap) *else_regs = NULL;
   unsigned int i;
+  int success_p = FALSE;
 
   /* Build a mapping for each block to the value used for each
      register.  */
-  max_reg = max_reg_num ();
-  size = (max_reg + 1) * sizeof (rtx);
-  then_vals = (rtx *) alloca (size);
-  else_vals = (rtx *) alloca (size);
-  memset (then_vals, 0, size);
-  memset (else_vals, 0, size);
+  then_vals = pointer_map_create ();
+  else_vals = pointer_map_create ();
 
   /* Make sure the blocks are suitable.  */
   if (!check_cond_move_block (then_bb, then_vals, &then_regs, cond)
       || (else_bb
          && !check_cond_move_block (else_bb, else_vals, &else_regs, cond)))
-    {
-      VEC_free (int, heap, then_regs);
-      VEC_free (int, heap, else_regs);
-      return FALSE;
-    }
+    goto done;
 
   /* Make sure the blocks can be used together.  If the same register
      is set in both blocks, and is not set to a constant in both
@@ -2767,41 +2876,38 @@ cond_move_process_if_block (struct noce_if_info *if_info)
      source register does not change after the assignment.  Also count
      the number of registers set in only one of the blocks.  */
   c = 0;
-  FOR_EACH_VEC_ELT (int, then_regs, i, reg)
+  FOR_EACH_VEC_ELT (rtx, then_regs, i, reg)
     {
-      if (!then_vals[reg] && !else_vals[reg])
-       continue;
+      void **then_slot = pointer_map_contains (then_vals, reg);
+      void **else_slot = pointer_map_contains (else_vals, reg);
 
-      if (!else_vals[reg])
+      gcc_checking_assert (then_slot);
+      if (!else_slot)
        ++c;
       else
        {
-         if (!CONSTANT_P (then_vals[reg])
-             && !CONSTANT_P (else_vals[reg])
-             && !rtx_equal_p (then_vals[reg], else_vals[reg]))
-           {
-             VEC_free (int, heap, then_regs);
-             VEC_free (int, heap, else_regs);
-             return FALSE;
-           }
+         rtx then_val = (rtx) *then_slot;
+         rtx else_val = (rtx) *else_slot;
+         if (!CONSTANT_P (then_val) && !CONSTANT_P (else_val)
+             && !rtx_equal_p (then_val, else_val))
+           goto done;
        }
     }
 
   /* Finish off c for MAX_CONDITIONAL_EXECUTE.  */
-  FOR_EACH_VEC_ELT (int, else_regs, i, reg)
-    if (!then_vals[reg])
-      ++c;
+  FOR_EACH_VEC_ELT (rtx, else_regs, i, reg)
+    {
+      gcc_checking_assert (pointer_map_contains (else_vals, reg));
+      if (!pointer_map_contains (then_vals, reg))
+       ++c;
+    }
 
   /* Make sure it is reasonable to convert this block.  What matters
      is the number of assignments currently made in only one of the
      branches, since if we convert we are going to always execute
      them.  */
   if (c > MAX_CONDITIONAL_EXECUTE)
-    {
-      VEC_free (int, heap, then_regs);
-      VEC_free (int, heap, else_regs);
-      return FALSE;
-    }
+    goto done;
 
   /* Try to emit the conditional moves.  First do the then block,
      then do anything left in the else blocks.  */
@@ -2813,17 +2919,11 @@ cond_move_process_if_block (struct noce_if_info *if_info)
                                          then_vals, else_vals, true)))
     {
       end_sequence ();
-      VEC_free (int, heap, then_regs);
-      VEC_free (int, heap, else_regs);
-      return FALSE;
+      goto done;
     }
   seq = end_ifcvt_sequence (if_info);
   if (!seq)
-    {
-      VEC_free (int, heap, then_regs);
-      VEC_free (int, heap, else_regs);
-      return FALSE;
-    }
+    goto done;
 
   loc_insn = first_active_insn (then_bb);
   if (!loc_insn)
@@ -2854,9 +2954,14 @@ cond_move_process_if_block (struct noce_if_info *if_info)
 
   num_updated_if_blocks++;
 
-  VEC_free (int, heap, then_regs);
-  VEC_free (int, heap, else_regs);
-  return TRUE;
+  success_p = TRUE;
+
+done:
+  pointer_map_destroy (then_vals);
+  pointer_map_destroy (else_vals);
+  VEC_free (rtx, heap, then_regs);
+  VEC_free (rtx, heap, else_regs);
+  return success_p;
 }
 
 \f
@@ -3697,7 +3802,8 @@ find_if_case_1 (basic_block test_bb, edge then_edge, edge else_edge)
   basic_block then_bb = then_edge->dest;
   basic_block else_bb = else_edge->dest;
   basic_block new_bb;
-  int then_bb_index;
+  int then_bb_index, then_prob;
+  rtx else_target = NULL_RTX;
 
   /* If we are partitioning hot/cold basic blocks, we don't want to
      mess up unconditional or indirect jumps that cross between hot
@@ -3740,15 +3846,28 @@ find_if_case_1 (basic_block test_bb, edge then_edge, edge else_edge)
             "\nIF-CASE-1 found, start %d, then %d\n",
             test_bb->index, then_bb->index);
 
-  /* THEN is small.  */
-  if (! cheap_bb_rtx_cost_p (then_bb,
+  if (then_edge->probability)
+    then_prob = REG_BR_PROB_BASE - then_edge->probability;
+  else
+    then_prob = REG_BR_PROB_BASE / 2;
+
+  /* We're speculating from the THEN path, we want to make sure the cost
+     of speculation is within reason.  */
+  if (! cheap_bb_rtx_cost_p (then_bb, then_prob,
        COSTS_N_INSNS (BRANCH_COST (optimize_bb_for_speed_p (then_edge->src),
                                    predictable_edge_p (then_edge)))))
     return FALSE;
 
+  if (else_bb == EXIT_BLOCK_PTR)
+    {
+      rtx jump = BB_END (else_edge->src);
+      gcc_assert (JUMP_P (jump));
+      else_target = JUMP_LABEL (jump);
+    }
+
   /* Registers set are dead, or are predicable.  */
   if (! dead_or_predicable (test_bb, then_bb, else_bb,
-                           single_succ (then_bb), 1))
+                           single_succ_edge (then_bb), 1))
     return FALSE;
 
   /* Conversion went ok, including moving the insns and fixing up the
@@ -3765,6 +3884,9 @@ find_if_case_1 (basic_block test_bb, edge then_edge, edge else_edge)
       redirect_edge_succ (FALLTHRU_EDGE (test_bb), else_bb);
       new_bb = 0;
     }
+  else if (else_bb == EXIT_BLOCK_PTR)
+    new_bb = force_nonfallthru_and_redirect (FALLTHRU_EDGE (test_bb),
+                                            else_bb, else_target);
   else
     new_bb = redirect_edge_and_branch_force (FALLTHRU_EDGE (test_bb),
                                             else_bb);
@@ -3800,7 +3922,7 @@ find_if_case_2 (basic_block test_bb, edge then_edge, edge else_edge)
   basic_block then_bb = then_edge->dest;
   basic_block else_bb = else_edge->dest;
   edge else_succ;
-  rtx note;
+  int then_prob, else_prob;
 
   /* If we are partitioning hot/cold basic blocks, we don't want to
      mess up unconditional or indirect jumps that cross between hot
@@ -3839,9 +3961,19 @@ find_if_case_2 (basic_block test_bb, edge then_edge, edge else_edge)
   if (then_bb->index < NUM_FIXED_BLOCKS)
     return FALSE;
 
+  if (else_edge->probability)
+    {
+      else_prob = else_edge->probability;
+      then_prob = REG_BR_PROB_BASE - else_prob;
+    }
+  else
+    {
+      else_prob = REG_BR_PROB_BASE / 2;
+      then_prob = REG_BR_PROB_BASE / 2;
+    }
+
   /* ELSE is predicted or SUCC(ELSE) postdominates THEN.  */
-  note = find_reg_note (BB_END (test_bb), REG_BR_PROB, NULL_RTX);
-  if (note && INTVAL (XEXP (note, 0)) >= REG_BR_PROB_BASE / 2)
+  if (else_prob > then_prob)
     ;
   else if (else_succ->dest->index < NUM_FIXED_BLOCKS
           || dominated_by_p (CDI_POST_DOMINATORS, then_bb,
@@ -3856,14 +3988,15 @@ find_if_case_2 (basic_block test_bb, edge then_edge, edge else_edge)
             "\nIF-CASE-2 found, start %d, else %d\n",
             test_bb->index, else_bb->index);
 
-  /* ELSE is small.  */
-  if (! cheap_bb_rtx_cost_p (else_bb,
+  /* We're speculating from the ELSE path, we want to make sure the cost
+     of speculation is within reason.  */
+  if (! cheap_bb_rtx_cost_p (else_bb, else_prob,
        COSTS_N_INSNS (BRANCH_COST (optimize_bb_for_speed_p (else_edge->src),
                                    predictable_edge_p (else_edge)))))
     return FALSE;
 
   /* Registers set are dead, or are predicable.  */
-  if (! dead_or_predicable (test_bb, else_bb, then_bb, else_succ->dest, 0))
+  if (! dead_or_predicable (test_bb, else_bb, then_bb, else_succ, 0))
     return FALSE;
 
   /* Conversion went ok, including moving the insns and fixing up the
@@ -3882,30 +4015,25 @@ 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.
 
    TEST_BB is the block containing the conditional branch.  MERGE_BB
-   is the block containing the code to manipulate.  NEW_DEST is the
-   label TEST_BB should be branching to after the conversion.
+   is the block containing the code to manipulate.  DEST_EDGE is an
+   edge representing a jump to the join block; after the conversion,
+   TEST_BB should be branching to its destination.
    REVERSEP is true if the sense of the branch should be reversed.  */
 
 static int
 dead_or_predicable (basic_block test_bb, basic_block merge_bb,
-                   basic_block other_bb, basic_block new_dest, int reversep)
+                   basic_block other_bb, edge dest_edge, int reversep)
 {
-  rtx head, end, jump, earliest = NULL_RTX, old_dest, new_label = NULL_RTX;
+  basic_block new_dest = dest_edge->dest;
+  rtx head, end, jump, earliest = NULL_RTX, old_dest;
+  bitmap merge_set = NULL;
   /* Number of pending changes.  */
   int n_validated_changes = 0;
+  rtx new_dest_label = NULL_RTX;
 
   jump = BB_END (test_bb);
 
@@ -3992,132 +4120,107 @@ 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, insn;
+      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.  */
 
-      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))
+      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;
 
-      /* 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.  */
+      live = BITMAP_ALLOC (&reg_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)
+       return FALSE;
 
+      /* Collect the set of registers set in MERGE_BB.  */
       merge_set = BITMAP_ALLOC (&reg_obstack);
-      merge_set_noclobber = BITMAP_ALLOC (&reg_obstack);
-      test_live = BITMAP_ALLOC (&reg_obstack);
-      test_set = BITMAP_ALLOC (&reg_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);
+
+#ifdef HAVE_simple_return
+      /* If shrink-wrapping, disable this optimization when test_bb is
+        the first basic block and merge_bb exits.  The idea is to not
+        move code setting up a return register as that may clobber a
+        register used to pass function parameters, which then must be
+        saved in caller-saved regs.  A caller-saved reg requires the
+        prologue, killing a shrink-wrap opportunity.  */
+      if ((flag_shrink_wrap && HAVE_simple_return && !epilogue_completed)
+         && ENTRY_BLOCK_PTR->next_bb == test_bb
+         && single_succ_p (new_dest)
+         && single_succ (new_dest) == EXIT_BLOCK_PTR
+         && bitmap_intersect_p (df_get_live_in (new_dest), merge_set))
        {
-         if (NONDEBUG_INSN_P (insn))
-           {
-             df_simulate_find_defs (insn, merge_set);
-             df_simulate_find_noclobber_defs (insn, merge_set_noclobber);
-           }
-       }
+         regset return_regs;
+         unsigned int i;
 
-      /* 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;
-           }
-       }
+         return_regs = BITMAP_ALLOC (&reg_obstack);
 
-      /* 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.  */
+         /* Start off with the intersection of regs used to pass
+            params and regs used to return values.  */
+         for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+           if (FUNCTION_ARG_REGNO_P (i)
+               && targetm.calls.function_value_regno_p (i))
+             bitmap_set_bit (return_regs, INCOMING_REGNO (i));
 
-      /* 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))
+         bitmap_and_into (return_regs, df_get_live_out (ENTRY_BLOCK_PTR));
+         bitmap_and_into (return_regs, df_get_live_in (EXIT_BLOCK_PTR));
+         if (!bitmap_empty_p (return_regs))
            {
-             df_simulate_find_defs (insn, test_set);
-             df_simulate_one_insn_backwards (test_bb, insn, test_live);
+             FOR_BB_INSNS_REVERSE (new_dest, insn)
+               if (NONDEBUG_INSN_P (insn))
+                 {
+                   df_ref *def_rec;
+                   unsigned int uid = INSN_UID (insn);
+
+                   /* If this insn sets any reg in return_regs..  */
+                   for (def_rec = DF_INSN_UID_DEFS (uid); *def_rec; def_rec++)
+                     {
+                       df_ref def = *def_rec;
+                       unsigned r = DF_REF_REGNO (def);
+
+                       if (bitmap_bit_p (return_regs, r))
+                         break;
+                     }
+                   /* ..then add all reg uses to the set of regs
+                      we're interested in.  */
+                   if (*def_rec)
+                     df_simulate_uses (insn, return_regs);
+                 }
+             if (bitmap_intersect_p (merge_set, return_regs))
+               {
+                 BITMAP_FREE (return_regs);
+                 BITMAP_FREE (merge_set);
+                 return FALSE;
+               }
            }
-         prev = PREV_INSN (insn);
-         if (insn == earliest)
-           break;
+         BITMAP_FREE (return_regs);
        }
-
-      /* 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;
+#endif
     }
 
  no_body:
@@ -4128,10 +4231,16 @@ dead_or_predicable (basic_block test_bb, basic_block merge_bb,
   old_dest = JUMP_LABEL (jump);
   if (other_bb != new_dest)
     {
-      new_label = block_label (new_dest);
+      if (JUMP_P (BB_END (dest_edge->src)))
+       new_dest_label = JUMP_LABEL (BB_END (dest_edge->src));
+      else if (new_dest == EXIT_BLOCK_PTR)
+       new_dest_label = ret_rtx;
+      else
+       new_dest_label = block_label (new_dest);
+
       if (reversep
-         ? ! invert_jump_1 (jump, new_label)
-         : ! redirect_jump_1 (jump, new_label))
+         ? ! invert_jump_1 (jump, new_dest_label)
+         : ! redirect_jump_1 (jump, new_dest_label))
        goto cancel;
     }
 
@@ -4142,7 +4251,7 @@ dead_or_predicable (basic_block test_bb, basic_block merge_bb,
 
   if (other_bb != new_dest)
     {
-      redirect_jump_2 (jump, old_dest, new_label, 0, reversep);
+      redirect_jump_2 (jump, old_dest, new_dest_label, 0, reversep);
 
       redirect_edge_succ (BRANCH_EDGE (test_bb), new_dest);
       if (reversep)
@@ -4167,8 +4276,8 @@ dead_or_predicable (basic_block test_bb, basic_block merge_bb,
       if (end == BB_END (merge_bb))
        BB_END (merge_bb) = PREV_INSN (head);
 
-      /* PR 21767: When moving insns above a conditional branch, REG_EQUAL
-        notes might become invalid.  */
+      /* PR 21767: when moving insns above a conditional branch, the REG_EQUAL
+        notes being moved might become invalid.  */
       insn = head;
       do
        {
@@ -4185,6 +4294,19 @@ dead_or_predicable (basic_block test_bb, basic_block merge_bb,
            remove_note (insn, note);
        } while (insn != end && (insn = NEXT_INSN (insn)));
 
+      /* PR46315: when moving insns above a conditional branch, the REG_EQUAL
+        notes referring to the registers being set might become invalid.  */
+      if (merge_set)
+       {
+         unsigned i;
+         bitmap_iterator bi;
+
+         EXECUTE_IF_SET_IN_BITMAP (merge_set, 0, i, bi)
+           remove_reg_equal_equiv_notes_for_regno (i);
+
+         BITMAP_FREE (merge_set);
+       }
+
       reorder_insns (head, end, PREV_INSN (earliest));
     }
 
@@ -4201,6 +4323,10 @@ dead_or_predicable (basic_block test_bb, basic_block merge_bb,
 
  cancel:
   cancel_changes (0);
+
+  if (merge_set)
+    BITMAP_FREE (merge_set);
+
   return FALSE;
 }
 \f
@@ -4346,7 +4472,7 @@ struct rtl_opt_pass pass_rtl_ifcvt =
   0,                                    /* properties_destroyed */
   0,                                    /* todo_flags_start */
   TODO_df_finish | TODO_verify_rtl_sharing |
-  TODO_dump_func                        /* todo_flags_finish */
+  0                                     /* todo_flags_finish */
  }
 };
 
@@ -4383,7 +4509,6 @@ struct rtl_opt_pass pass_if_after_combine =
   0,                                    /* properties_destroyed */
   0,                                    /* todo_flags_start */
   TODO_df_finish | TODO_verify_rtl_sharing |
-  TODO_dump_func |
   TODO_ggc_collect                      /* todo_flags_finish */
  }
 };
@@ -4420,7 +4545,6 @@ struct rtl_opt_pass pass_if_after_reload =
   0,                                    /* properties_destroyed */
   0,                                    /* todo_flags_start */
   TODO_df_finish | TODO_verify_rtl_sharing |
-  TODO_dump_func |
   TODO_ggc_collect                      /* todo_flags_finish */
  }
 };