+ /* Don't try to handle this if the condition uses the
+ destination register. */
+ if (reg_overlap_mentioned_p (dest, cond))
+ return FALSE;
+
+ /* Don't try to handle this if the source register is modified
+ later in the block. */
+ if (!CONSTANT_P (src)
+ && modified_between_p (src, insn, NEXT_INSN (BB_END (bb))))
+ return FALSE;
+
+ vals[REGNO (dest)] = src;
+
+ VEC_safe_push (int, heap, *regs, REGNO (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
+ 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. */
+
+static bool
+cond_move_convert_if_block (struct noce_if_info *if_infop,
+ basic_block bb, rtx cond,
+ rtx *then_vals, rtx *else_vals,
+ bool else_block_p)
+{
+ enum rtx_code code;
+ rtx insn, cond_arg0, cond_arg1;
+
+ code = GET_CODE (cond);
+ cond_arg0 = XEXP (cond, 0);
+ cond_arg1 = XEXP (cond, 1);
+
+ FOR_BB_INSNS (bb, insn)
+ {
+ rtx set, target, dest, t, e;
+ unsigned int regno;
+
+ if (!INSN_P (insn) || JUMP_P (insn))
+ continue;
+ set = single_set (insn);
+ gcc_assert (set && REG_P (SET_DEST (set)));
+
+ dest = SET_DEST (set);
+ regno = REGNO (dest);
+
+ t = then_vals[regno];
+ e = else_vals[regno];
+
+ if (else_block_p)
+ {
+ /* If this register was set in the then block, we already
+ handled this case there. */
+ if (t)
+ continue;
+ t = dest;
+ gcc_assert (e);
+ }
+ else
+ {
+ gcc_assert (t);
+ if (!e)
+ e = dest;
+ }
+
+ target = noce_emit_cmove (if_infop, dest, code, cond_arg0, cond_arg1,
+ t, e);
+ if (!target)
+ return false;
+
+ if (target != dest)
+ noce_emit_move_insn (dest, target);
+ }
+
+ return true;
+}
+
+/* Given a simple IF-THEN-JOIN or IF-THEN-ELSE-JOIN block, attempt to convert
+ it using only conditional moves. Return TRUE if we were successful at
+ converting the block. */
+
+static int
+cond_move_process_if_block (struct noce_if_info *if_info)
+{
+ basic_block test_bb = if_info->test_bb;
+ basic_block then_bb = if_info->then_bb;
+ basic_block else_bb = if_info->else_bb;
+ basic_block join_bb = if_info->join_bb;
+ 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;
+ unsigned int i;
+
+ /* 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);
+
+ /* 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;
+ }
+
+ /* 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
+ cases, then both blocks must set it to the same register. We
+ have already verified that if it is set to a register, that the
+ source register does not change after the assignment. Also count
+ the number of registers set in only one of the blocks. */
+ c = 0;
+ for (i = 0; VEC_iterate (int, then_regs, i, reg); i++)
+ {
+ if (!then_vals[reg] && !else_vals[reg])
+ continue;
+
+ if (!else_vals[reg])
+ ++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;
+ }
+ }
+ }
+
+ /* Finish off c for MAX_CONDITIONAL_EXECUTE. */
+ for (i = 0; VEC_iterate (int, else_regs, i, reg); ++i)
+ if (!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;
+ }
+
+ /* Try to emit the conditional moves. First do the then block,
+ then do anything left in the else blocks. */
+ start_sequence ();
+ if (!cond_move_convert_if_block (if_info, then_bb, cond,
+ then_vals, else_vals, false)
+ || (else_bb
+ && !cond_move_convert_if_block (if_info, else_bb, cond,
+ then_vals, else_vals, true)))
+ {
+ end_sequence ();
+ VEC_free (int, heap, then_regs);
+ VEC_free (int, heap, else_regs);
+ return FALSE;
+ }
+ seq = end_ifcvt_sequence (if_info);
+ if (!seq)
+ {
+ VEC_free (int, heap, then_regs);
+ VEC_free (int, heap, else_regs);
+ return FALSE;
+ }
+
+ loc_insn = first_active_insn (then_bb);
+ if (!loc_insn)
+ {
+ loc_insn = first_active_insn (else_bb);
+ gcc_assert (loc_insn);
+ }
+ emit_insn_before_setloc (seq, jump, INSN_LOCATOR (loc_insn));
+
+ if (else_bb)
+ {
+ delete_basic_block (else_bb);
+ num_true_changes++;
+ }
+ else
+ remove_edge (find_edge (test_bb, join_bb));
+
+ remove_edge (find_edge (then_bb, join_bb));
+ redirect_edge_and_branch_force (single_succ_edge (test_bb), join_bb);
+ delete_basic_block (then_bb);
+ num_true_changes++;
+
+ if (can_merge_blocks_p (test_bb, join_bb))
+ {
+ merge_blocks (test_bb, join_bb);
+ num_true_changes++;
+ }
+
+ num_updated_if_blocks++;
+
+ VEC_free (int, heap, then_regs);
+ VEC_free (int, heap, else_regs);
+ return TRUE;
+}
+
+\f
+/* Determine if a given basic block heads a simple IF-THEN-JOIN or an
+ IF-THEN-ELSE-JOIN block.
+
+ If so, we'll try to convert the insns to not require the branch,
+ using only transformations that do not require conditional execution.
+
+ Return TRUE if we were successful at converting the block. */
+
+static int
+noce_find_if_block (basic_block test_bb,
+ edge then_edge, edge else_edge,
+ int pass)
+{
+ basic_block then_bb, else_bb, join_bb;
+ bool then_else_reversed = false;
+ rtx jump, cond;
+ rtx cond_earliest;
+ struct noce_if_info if_info;
+
+ /* We only ever should get here before reload. */
+ gcc_assert (!reload_completed);
+
+ /* Recognize an IF-THEN-ELSE-JOIN block. */
+ if (single_pred_p (then_edge->dest)
+ && single_succ_p (then_edge->dest)
+ && single_pred_p (else_edge->dest)
+ && single_succ_p (else_edge->dest)
+ && single_succ (then_edge->dest) == single_succ (else_edge->dest))
+ {
+ then_bb = then_edge->dest;
+ else_bb = else_edge->dest;
+ join_bb = single_succ (then_bb);
+ }
+ /* Recognize an IF-THEN-JOIN block. */
+ else if (single_pred_p (then_edge->dest)
+ && single_succ_p (then_edge->dest)
+ && single_succ (then_edge->dest) == else_edge->dest)
+ {
+ then_bb = then_edge->dest;
+ else_bb = NULL_BLOCK;
+ join_bb = else_edge->dest;
+ }
+ /* Recognize an IF-ELSE-JOIN block. We can have those because the order
+ of basic blocks in cfglayout mode does not matter, so the fallthrough
+ edge can go to any basic block (and not just to bb->next_bb, like in
+ cfgrtl mode). */
+ else if (single_pred_p (else_edge->dest)
+ && single_succ_p (else_edge->dest)
+ && single_succ (else_edge->dest) == then_edge->dest)
+ {
+ /* The noce transformations do not apply to IF-ELSE-JOIN blocks.
+ To make this work, we have to invert the THEN and ELSE blocks
+ and reverse the jump condition. */
+ then_bb = else_edge->dest;
+ else_bb = NULL_BLOCK;
+ join_bb = single_succ (then_bb);
+ then_else_reversed = true;
+ }
+ else
+ /* Not a form we can handle. */
+ return FALSE;
+
+ /* The edges of the THEN and ELSE blocks cannot have complex edges. */
+ if (single_succ_edge (then_bb)->flags & EDGE_COMPLEX)
+ return FALSE;
+ if (else_bb
+ && single_succ_edge (else_bb)->flags & EDGE_COMPLEX)
+ return FALSE;
+
+ num_possible_if_blocks++;
+
+ if (dump_file)
+ {
+ fprintf (dump_file,
+ "\nIF-THEN%s-JOIN block found, pass %d, test %d, then %d",
+ (else_bb) ? "-ELSE" : "",
+ pass, test_bb->index, then_bb->index);
+
+ if (else_bb)
+ fprintf (dump_file, ", else %d", else_bb->index);
+
+ fprintf (dump_file, ", join %d\n", join_bb->index);
+ }
+
+ /* If the conditional jump is more than just a conditional
+ jump, then we can not do if-conversion on this block. */
+ jump = BB_END (test_bb);
+ if (! onlyjump_p (jump))
+ return FALSE;
+
+ /* If this is not a standard conditional jump, we can't parse it. */
+ cond = noce_get_condition (jump,
+ &cond_earliest,
+ then_else_reversed);
+ if (!cond)
+ return FALSE;
+
+ /* We must be comparing objects whose modes imply the size. */
+ if (GET_MODE (XEXP (cond, 0)) == BLKmode)
+ return FALSE;
+
+ /* Initialize an IF_INFO struct to pass around. */
+ memset (&if_info, 0, sizeof if_info);
+ if_info.test_bb = test_bb;
+ if_info.then_bb = then_bb;
+ if_info.else_bb = else_bb;
+ if_info.join_bb = join_bb;
+ if_info.cond = cond;
+ if_info.cond_earliest = cond_earliest;
+ if_info.jump = jump;
+ if_info.then_else_reversed = then_else_reversed;
+ if_info.branch_cost = BRANCH_COST (optimize_bb_for_speed_p (test_bb),
+ predictable_edge_p (then_edge));
+
+ /* Do the real work. */
+
+ if (noce_process_if_block (&if_info))
+ return TRUE;
+
+ if (HAVE_conditional_move
+ && cond_move_process_if_block (&if_info))
+ return TRUE;
+
+ return FALSE;
+}
+\f
+
+/* Merge the blocks and mark for local life update. */
+
+static void
+merge_if_block (struct ce_if_block * ce_info)
+{
+ basic_block test_bb = ce_info->test_bb; /* last test block */
+ basic_block then_bb = ce_info->then_bb; /* THEN */
+ basic_block else_bb = ce_info->else_bb; /* ELSE or NULL */
+ basic_block join_bb = ce_info->join_bb; /* join block */
+ basic_block combo_bb;
+
+ /* All block merging is done into the lower block numbers. */
+
+ combo_bb = test_bb;
+ df_set_bb_dirty (test_bb);
+
+ /* Merge any basic blocks to handle && and || subtests. Each of