Hi Jeff,
Could you also take a look at this one? The SPEC results I posted yesterday
were measured with it applied.
The patch extends if-conversion to handle a mutually exclusive min/max update,
which is what lets us handle the 734.vpr_r case. Reduced testcase:
void
bbox_chain (const int *x, int n, int *pmin, int *pmax)
{
int xmin = x[0];
int xmax = x[0];
for (int i = 1; i < n; i++)
{
if (x[i] < xmin)
xmin = x[i];
else if (x[i] > xmax)
xmax = x[i];
}
*pmin = xmin;
*pmax = xmax;
}
The improvement on 734.vpr_r:
-O2 -Ofast
EMR +3.2% +3.9%
znver5 +2.1% +2.9%
PR126139: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=126139
Thanks,
Lili
> -----Original Message-----
> From: [email protected] <[email protected]>
> Sent: Tuesday, September 1, 2026 1:39 PM
> To: [email protected]
> Cc: [email protected]; Cui, Lili <[email protected]>; Kyrylo
> Tkachov <[email protected]>
> Subject: [PATCH 2/2] ifcvt: Handle arm-only diamond live-outs [PR125557]
>
> From: Kyrylo Tkachov <[email protected]>
>
> The diamond conversion currently requires one arm's live-out destinations to
> contain the other's. This rejects diamonds in which the arms update different
> values, although a destination can keep its incoming value on the path that
> does not assign it.
>
> For example, convert
>
> then: else:
> x = a; z = c;
> y = b; w = d;
>
> to
>
> z_tmp = c;
> w_tmp = d;
> x = cond ? a : x;
> y = cond ? b : y;
> z = cond ? z : z_tmp;
> w = cond ? w : w_tmp;
>
> Build the metadata for both arms once. Append an output entry for the last
> else definition of each live-out destination that the then arm does not
> define.
> The existing conditional-move loop then selects between the else temporary
> and the incoming value for these entries. It continues to select between the
> then and else values for shared entries.
>
> Form the union of the live-out destinations and permit arm-only destinations
> and one-set arms. Do not restrict partially overlapping sets based on the
> source of a shared destination. Keep the CFG arm order and evaluate the else
> arm into temporaries. Include the complete live-out union and the appended
> output entries in the conversion limit.
>
> Add execution and code-generation tests for disjoint and partially overlapping
> outputs, one-set arms, cross-arm incoming values, repeated definitions,
> condition inputs, unequal output counts, condition-code clobbers, and the
> final output limit.
>
> Bootstrapped and tested on aarch64-none-linux-gnu.
> Ok fro trunk?
> Thanks,
> Kyrill
>
> gcc/ChangeLog:
>
> PR tree-optimization/125557
> * ifcvt.cc (noce_convert_multiple_sets): Build metadata for both arms.
> Append entries for else-only live-out destinations, enforce the final
> output limit, and preserve the then-arm source location.
> (noce_convert_multiple_sets_1): Consume the unified output
> metadata.
> Select incoming values for destinations defined by only one arm.
> (init_noce_multiple_sets_info): Initialize stable metadata fields.
> (bb_ok_for_noce_convert_multiple_sets): Remove final-source
> classification.
> (noce_process_if_block): Form the union of the arm live-outs, accept
> arm-only destinations and one-set arms without a shared-source
> gate, and
> keep the CFG arm order.
>
> gcc/testsuite/ChangeLog:
>
> PR tree-optimization/125557
> * gcc.target/aarch64/ifcvt_multiple_sets_diamond_4.c: Test disjoint,
> partially overlapping, and one-set outputs.
> * gcc.target/aarch64/ifcvt_multiple_sets_diamond_5.c: Update
> expected
> code generation for the fixed arm order.
> * gcc.target/aarch64/ifcvt_multiple_sets_diamond_6.c: New test.
> * gcc.target/aarch64/ifcvt_multiple_sets_diamond_7.c: New test.
> * gcc.target/i386/ifcvt-multiple-sets-diamond-1.c: Test disjoint
> outputs
> with a condition-code clobber.
>
> Co-authored-by: Cui Lili <[email protected]>
> Signed-off-by: Kyrylo Tkachov <[email protected]>
> ---
> gcc/ifcvt.cc | 237 +++++++++---------
> .../aarch64/ifcvt_multiple_sets_diamond_4.c | 83 +++---
> .../aarch64/ifcvt_multiple_sets_diamond_5.c | 3 +-
> .../aarch64/ifcvt_multiple_sets_diamond_6.c | 100 ++++++++
> .../aarch64/ifcvt_multiple_sets_diamond_7.c | 26 ++
> .../i386/ifcvt-multiple-sets-diamond-1.c | 25 +-
> 6 files changed, 315 insertions(+), 159 deletions(-) create mode 100644
> gcc/testsuite/gcc.target/aarch64/ifcvt_multiple_sets_diamond_6.c
> create mode 100644
> gcc/testsuite/gcc.target/aarch64/ifcvt_multiple_sets_diamond_7.c
>
> diff --git a/gcc/ifcvt.cc b/gcc/ifcvt.cc index b976ac9acc3..049e2560298
> 100644
> --- a/gcc/ifcvt.cc
> +++ b/gcc/ifcvt.cc
> @@ -102,7 +102,9 @@ static rtx_insn *block_has_only_trap (basic_block);
> static void init_noce_multiple_sets_info (basic_block,
> auto_delete_vec<noce_multiple_sets_info> &); static bool
> noce_convert_multiple_sets_1 (struct noce_if_info *,
> - auto_delete_vec<noce_multiple_sets_info> &, int *, bool *);
> + auto_delete_vec<noce_multiple_sets_info> &,
> + auto_delete_vec<noce_multiple_sets_info> &, unsigned, const
> + vec<unsigned> &, int *, bool *);
>
>
>
> /* Count the number of non-jump active insns in BB. */
>
> @@ -3970,7 +3972,7 @@ try_emit_cmove_seq (struct noce_if_info *if_info,
> rtx temp,
> conditional set to use the temporary we introduced earlier.
>
> IF_INFO contains the useful information about the block structure and
> - jump instructions. For an IF-THEN-ELSE-JOIN, first evaluate the secondary
> + jump instructions. For an IF-THEN-ELSE-JOIN, first evaluate the
> + else
> arm's sets into temporaries and retain their final values for the
> conditional moves. Return true if the replacement is valid and profitable
> and its CFG changes have been committed, otherwise return false. */ @@ -
> 3995,13 +3997,59 @@ noce_convert_multiple_sets (struct noce_if_info
> *if_info)
>
> auto_delete_vec<noce_multiple_sets_info> insn_info;
> init_noce_multiple_sets_info (then_bb, insn_info);
> + gcc_assert (!insn_info.is_empty ());
> + unsigned then_count = insn_info.length (); location_t
> + sequence_location
> + = INSN_LOCATION (insn_info.last ()->unmodified_insn);
> +
> + auto_delete_vec<noce_multiple_sets_info> else_insn_info;
> + auto_vec<unsigned> else_only_indices; if (else_bb)
> + {
> + init_noce_multiple_sets_info (else_bb, else_insn_info);
> +
> + /* Add one output entry for each live-out destination that is set only
> + by the else arm. Record the last definition, which holds the
> + value that reaches the join. */
> + auto_bitmap then_dests, seen;
> + for (unsigned i = 0; i < then_count; ++i)
> + bitmap_set_bit (then_dests, REGNO (insn_info[i]->target));
> +
> + for (int i = else_insn_info.length () - 1; i >= 0; --i)
> + if (else_insn_info[i]->need_cmov
> + && !bitmap_bit_p (then_dests,
> + REGNO (else_insn_info[i]->target))
> + && bitmap_set_bit (seen, REGNO (else_insn_info[i]->target)))
> + else_only_indices.safe_push (i);
> + else_only_indices.reverse ();
> +
> + unsigned i, else_index;
> + FOR_EACH_VEC_ELT (else_only_indices, i, else_index)
> + {
> + noce_multiple_sets_info *info = new noce_multiple_sets_info;
> + noce_multiple_sets_info *else_info = else_insn_info[else_index];
> + info->target = else_info->target;
> + info->temporary = NULL_RTX;
> + info->unmodified_insn = else_info->unmodified_insn;
> + info->need_cmov = true;
> + insn_info.safe_push (info);
> + }
> + }
> +
> + /* Include else-only outputs when enforcing the conversion limit. */
> + if (insn_info.length () > (unsigned) param_max_rtl_if_conversion_insns)
> + {
> + end_sequence ();
> + return false;
> + }
>
> int last_needs_comparison = -1;
>
> bool use_cond_earliest = false;
>
> bool ok = noce_convert_multiple_sets_1
> - (if_info, insn_info, &last_needs_comparison, &use_cond_earliest);
> + (if_info, insn_info, else_insn_info, then_count, else_only_indices,
> + &last_needs_comparison, &use_cond_earliest);
> if (!ok)
> return false;
>
> @@ -4013,17 +4061,14 @@ noce_convert_multiple_sets (struct noce_if_info
> *if_info)
> end_sequence ();
> start_sequence ();
> ok = noce_convert_multiple_sets_1
> - (if_info, insn_info, &last_needs_comparison, &use_cond_earliest);
> + (if_info, insn_info, else_insn_info, then_count, else_only_indices,
> + &last_needs_comparison, &use_cond_earliest);
>
> /* Actually we should not fail anymore if we reached here,
> but better still check. */
> if (!ok)
> return false;
>
> - /* We must have seen some sort of insn to insert, otherwise we were
> - given an empty BB to convert, and we can't handle that. */
> - gcc_assert (!insn_info.is_empty ());
> -
> /* Now fixup the assignments.
> PR116405: Iterate in reverse order and keep track of the targets so that
> a move does not overwrite a subsequent value when multiple instructions
> @@ -4092,8 +4137,7 @@ noce_convert_multiple_sets (struct noce_if_info
> *if_info)
> if (noce_clobbers_live_cc_p (test_bb, seq))
> return false;
>
> - emit_insn_before_setloc (seq, if_info->jump,
> - INSN_LOCATION (insn_info.last ()-
> >unmodified_insn));
> + emit_insn_before_setloc (seq, if_info->jump, sequence_location);
>
> /* Clean up the THEN (and, for a diamond, ELSE) block and the edges into
> and
> out of the if-region. An IF-THEN-ELSE-JOIN has no test->join edge.
> @@ -4123,8 +4167,10 @@ noce_convert_multiple_sets (struct noce_if_info
> *if_info) }
>
> /* Try to emit the multiple-set conversion described by IF_INFO. INSN_INFO
> - holds the primary-arm metadata. For a diamond, evaluate the secondary
> arm
> - first and retain its final values for the conditional moves.
> + holds THEN_COUNT then-arm entries followed by entries for the else-only
> + definitions recorded in ELSE_ONLY_INDICES. For a diamond,
> + evaluate ELSE_INSN_INFO first and retain its final values for the
> + conditional moves.
>
> LAST_NEEDS_COMPARISON is -1 on the first attempt. Record in it the last
> set
> that needs a temporary to preserve the comparison, then use that boundary
> @@ -4135,10 +4181,13 @@ noce_convert_multiple_sets (struct noce_if_info
> *if_info) static bool
> noce_convert_multiple_sets_1 (struct noce_if_info *if_info,
> auto_delete_vec<noce_multiple_sets_info>
> &insn_info,
> + auto_delete_vec<noce_multiple_sets_info>
> + &else_insn_info,
> + unsigned then_count,
> + const vec<unsigned> &else_only_indices,
> int *last_needs_comparison,
> bool *use_cond_earliest)
> {
> - basic_block then_bb = if_info->then_bb;
> rtx_insn *jump = if_info->jump;
> rtx_insn *cond_earliest;
>
> @@ -4152,19 +4201,16 @@ noce_convert_multiple_sets_1 (struct
> noce_if_info *if_info,
> if (rev_cc_cmp)
> rev_cc_cmp = copy_rtx (rev_cc_cmp);
>
> - rtx_insn *insn;
> int count = 0;
> bool second_try = *last_needs_comparison != -1;
> *use_cond_earliest = false;
> - auto_delete_vec<noce_multiple_sets_info> else_insn_info;
>
> /* For an IF-THEN-ELSE-JOIN, emit the else block's computations first into
> fresh temporaries. This leaves the incoming register values available
> to
> - the then block. The conditional moves below select the then values or
> - these else values. */
> + the then arm. The conditional moves below select the then or else
> + values. */
> if (if_info->else_bb)
> {
> - init_noce_multiple_sets_info (if_info->else_bb, else_insn_info);
> int else_count = 0;
> rtx_insn *before_else = get_last_insn ();
> location_t saved_location = curr_insn_location (); @@ -4190,9 +4236,7
> @@ noce_convert_multiple_sets_1 (struct noce_if_info *if_info,
> set_curr_insn_location (INSN_LOCATION (else_insn));
> rtx temporary = copy_to_mode_reg (GET_MODE (target), value);
>
> - info->target = target;
> info->temporary = temporary;
> - info->unmodified_insn = else_insn;
> else_count++;
> }
>
> @@ -4224,39 +4268,50 @@ noce_convert_multiple_sets_1 (struct
> noce_if_info *if_info,
> }
> }
>
> - FOR_BB_INSNS (then_bb, insn)
> + for (count = 0; count < (int) insn_info.length (); ++count)
> {
> - /* Skip over non-insns. */
> - if (!active_insn_p (insn))
> - continue;
> -
> noce_multiple_sets_info *info = insn_info[count];
> -
> + rtx_insn *insn = info->unmodified_insn;
> rtx set = single_set (insn);
> gcc_checking_assert (set);
>
> - rtx target = SET_DEST (set);
> + rtx target = info->target;
> rtx temp;
> + rtx new_val;
> + rtx old_val;
>
> - rtx new_val = SET_SRC (set);
> + if ((unsigned) count < then_count)
> + {
> + new_val = SET_SRC (set);
>
> - int i, ii;
> - FOR_EACH_VEC_ELT (info->rewired_src, i, ii)
> - new_val = simplify_replace_rtx (new_val, insn_info[ii]->target,
> - insn_info[ii]->temporary);
> + int i, ii;
> + FOR_EACH_VEC_ELT (info->rewired_src, i, ii)
> + new_val = simplify_replace_rtx (new_val, insn_info[ii]->target,
> + insn_info[ii]->temporary);
>
> - rtx old_val = target;
> + old_val = target;
>
> - /* Use the final value assigned to TARGET on the else arm. Scanning in
> - reverse is important when the arm assigns the same register more
> than
> - once. */
> - if (if_info->else_bb)
> - for (int j = else_insn_info.length () - 1; j >= 0; --j)
> - if (rtx_equal_p (target, else_insn_info[j]->target))
> - {
> - old_val = else_insn_info[j]->temporary;
> - break;
> - }
> + /* Use the final value assigned to TARGET on the else arm.
> + Scanning in reverse is important when the arm assigns the same
> + register more than once. */
> + if (if_info->else_bb)
> + for (int j = else_insn_info.length () - 1; j >= 0; --j)
> + if (rtx_equal_p (target, else_insn_info[j]->target))
> + {
> + old_val = else_insn_info[j]->temporary;
> + break;
> + }
> + }
> + else
> + {
> + unsigned else_index
> + = else_only_indices[(unsigned) count - then_count];
> + noce_multiple_sets_info *else_info = else_insn_info[else_index];
> + gcc_checking_assert (rtx_equal_p (target, else_info->target));
> +
> + new_val = target;
> + old_val = else_info->temporary;
> + }
>
> /* As we are transforming
> if (x > y)
> @@ -4451,11 +4506,7 @@ noce_convert_multiple_sets_1 (struct
> noce_if_info *if_info,
> emit_insn (seq);
>
> /* Bookkeeping. */
> - count++;
> -
> - info->target = target;
> info->temporary = temp_dest;
> - info->unmodified_insn = insn;
> }
>
> /* Even if we did not actually need the comparison, we want to make sure
> @@ -4515,9 +4566,7 @@ init_noce_multiple_sets_info (basic_block bb,
> continue;
>
> noce_multiple_sets_info *info = new noce_multiple_sets_info;
> - info->target = NULL_RTX;
> info->temporary = NULL_RTX;
> - info->unmodified_insn = NULL;
> insn_info.safe_push (info);
>
> rtx set = single_set (insn);
> @@ -4527,6 +4576,8 @@ init_noce_multiple_sets_info (basic_block bb,
> rtx dest = SET_DEST (set);
>
> gcc_checking_assert (REG_P (dest) && !HARD_REGISTER_P (dest));
> + info->target = dest;
> + info->unmodified_insn = insn;
> info->need_cmov = bitmap_bit_p (bb_live_out, REGNO (dest));
>
> /* Check if the current SET's source mentions any previously seen @@ -
> 4547,24 +4598,20 @@ init_noce_multiple_sets_info (basic_block bb,
> /* Return true iff basic block TEST_BB is suitable for conversion to a
> series of conditional moves. Unless REQUIRE_MULTIPLE is false, also check
> that we have more than one set (other routines can handle a single set
> - better than we would). A diamond arm may have a single set when it is
> - selected as the secondary arm. Require no more than
> - PARAM_MAX_RTL_IF_CONVERSION_INSNS sets. While going through the
> insns store
> - the sum of their potential costs in COST. On success, if LIVE_OUT_DESTS
> is
> - nonnull, record the distinct pseudo destinations that are live out of
> + better than we would). A diamond arm may have a single set.
> + Require no more than PARAM_MAX_RTL_IF_CONVERSION_INSNS sets.
> While going
> + through the insns, store the sum of their potential costs in COST. On
> + success, if LIVE_OUT_DESTS is nonnull, record the distinct pseudo
> + destinations that are live out of
> TEST_BB. If HAS_NON_SIMPLE_SRC is nonnull, set it when an instruction
> source is neither a constant nor a register operand. On success, if
> - NONREG_LIVE_OUT_DESTS is nonnull, record live-out destinations whose
> last
> - RTL definition has a non-register source. Constants are non-register
> - sources. On success, if INSN_COUNT is nonnull, store the number of active
> - sets in it. */
> + INSN_COUNT is nonnull, store the number of active sets in it. */
>
> static bool
> bb_ok_for_noce_convert_multiple_sets (basic_block test_bb, unsigned *cost,
> bool require_multiple = true,
> bitmap live_out_dests = NULL,
> bool *has_non_simple_src = NULL,
> - bitmap nonreg_live_out_dests = NULL,
> unsigned *insn_count = NULL)
> {
> rtx_insn *insn;
> @@ -4576,10 +4623,8 @@ bb_ok_for_noce_convert_multiple_sets
> (basic_block test_bb, unsigned *cost,
> bitmap_clear (live_out_dests);
> if (has_non_simple_src)
> *has_non_simple_src = false;
> - if (nonreg_live_out_dests)
> - bitmap_clear (nonreg_live_out_dests);
> bitmap bb_live_out = NULL;
> - if (live_out_dests || nonreg_live_out_dests)
> + if (live_out_dests)
> bb_live_out = df_get_live_out (test_bb);
>
> FOR_BB_INSNS (test_bb, insn)
> @@ -4619,17 +4664,7 @@ bb_ok_for_noce_convert_multiple_sets
> (basic_block test_bb, unsigned *cost,
> return false;
>
> if (bb_live_out && bitmap_bit_p (bb_live_out, REGNO (dest)))
> - {
> - if (live_out_dests)
> - bitmap_set_bit (live_out_dests, REGNO (dest));
> - if (nonreg_live_out_dests)
> - {
> - if (register_operand (src, VOIDmode))
> - bitmap_clear_bit (nonreg_live_out_dests, REGNO (dest));
> - else
> - bitmap_set_bit (nonreg_live_out_dests, REGNO (dest));
> - }
> - }
> + bitmap_set_bit (live_out_dests, REGNO (dest));
>
> potential_cost += insn_cost (insn, speed_p);
>
> @@ -4641,7 +4676,7 @@ bb_ok_for_noce_convert_multiple_sets
> (basic_block test_bb, unsigned *cost,
> /* If we would only put out one conditional move, the other strategies
> this pass tries are better optimized and will be more appropriate, so
> require more than one set unless REQUIRE_MULTIPLE is false. A diamond
> - arm may have one set when it is selected as the secondary arm.
> + arm may have one set.
> Some targets want to strictly limit the number of conditional moves
> that are emitted, they set this through PARAM, we need to respect
> that. */
> @@ -4697,9 +4732,8 @@ noce_process_if_block (struct noce_if_info
> *if_info)
> The later patterns require jumps to be more expensive.
> For the diamond case, defer diamonds with only register and constant
> assignments in both arms to the existing simple conditional-move
> - handling. Use an arm that sets at least two distinct live-out pseudos
> as
> - the primary arm. Every live-out destination set by the secondary arm
> - must also be set by the primary arm.
> + handling. Require at least two distinct live-out pseudos across the
> + arms.
> ??? For future expansion, further expand the "multiple X" rules. */
>
> /* First look for multiple SETS.
> @@ -4713,8 +4747,6 @@ noce_process_if_block (struct noce_if_info
> *if_info)
> unsigned ms_then_insn_count = 0, ms_else_insn_count = 0;
> bool ms_then_has_non_simple_src, ms_else_has_non_simple_src;
> auto_bitmap ms_then_live_out_dests, ms_else_live_out_dests;
> - auto_bitmap ms_then_nonreg_live_out_dests;
> - auto_bitmap ms_else_nonreg_live_out_dests;
> noce_if_info ms_if_info = *if_info;
> bool multiple_sets_p = false;
>
> @@ -4727,48 +4759,25 @@ noce_process_if_block (struct noce_if_info
> *if_info)
> && bb_ok_for_noce_convert_multiple_sets
> (then_bb, &ms_then_cost, false,
> ms_then_live_out_dests, &ms_then_has_non_simple_src,
> - ms_then_nonreg_live_out_dests, &ms_then_insn_count)
> + &ms_then_insn_count)
> && bb_ok_for_noce_convert_multiple_sets
> (else_bb, &ms_else_cost, false,
> ms_else_live_out_dests, &ms_else_has_non_simple_src,
> - ms_else_nonreg_live_out_dests, &ms_else_insn_count))
> + &ms_else_insn_count))
> {
> - /* Keep this path for diamonds that share a live-out whose last RTL
> - definition has a non-register source in at least one arm. This
> - includes a value computed in one arm and forwarded by the
> - other. */
> - bool has_shared_nonreg_live_out
> - = (bitmap_intersect_p (ms_then_nonreg_live_out_dests,
> - ms_else_live_out_dests)
> - || bitmap_intersect_p (ms_else_nonreg_live_out_dests,
> - ms_then_live_out_dests));
> -
> - if ((ms_then_has_non_simple_src || ms_else_has_non_simple_src)
> - && has_shared_nonreg_live_out)
> - {
> - if (bitmap_count_bits (ms_then_live_out_dests) >= 2
> - && !bitmap_intersect_compl_p (ms_else_live_out_dests,
> - ms_then_live_out_dests))
> - multiple_sets_p = true;
> - else if (bitmap_count_bits (ms_else_live_out_dests) >= 2
> - && !bitmap_intersect_compl_p (ms_then_live_out_dests,
> -
> ms_else_live_out_dests))
> - {
> - /* The branch-target arm is the compatible multi-set
> - superset. Make it the primary arm and reverse the select
> - orientation. */
> - std::swap (ms_if_info.then_bb, ms_if_info.else_bb);
> - ms_if_info.then_else_reversed
> - = !ms_if_info.then_else_reversed;
> - std::swap (ms_then_cost, ms_else_cost);
> - std::swap (ms_then_insn_count, ms_else_insn_count);
> - multiple_sets_p = true;
> - }
> - }
> + auto_bitmap ms_live_out_dests;
> + bitmap_ior (ms_live_out_dests, ms_then_live_out_dests,
> + ms_else_live_out_dests);
> + unsigned output_count = bitmap_count_bits (ms_live_out_dests);
> +
> + if (output_count >= 2
> + && output_count <= (unsigned)
> param_max_rtl_if_conversion_insns
> + && (ms_then_has_non_simple_src ||
> ms_else_has_non_simple_src))
> + multiple_sets_p = true;
> }
> }
>
> - /* The diamond conversion evaluates the secondary arm unconditionally.
> + /* The diamond conversion evaluates the else arm unconditionally.
> Honor the target limit on the number of instructions made
> unconditional. */
> if (multiple_sets_p
> diff --git a/gcc/testsuite/gcc.target/aarch64/ifcvt_multiple_sets_diamond_4.c
> b/gcc/testsuite/gcc.target/aarch64/ifcvt_multiple_sets_diamond_4.c
> index 428d7f51cc5..a6c8e400aee 100644
> --- a/gcc/testsuite/gcc.target/aarch64/ifcvt_multiple_sets_diamond_4.c
> +++ b/gcc/testsuite/gcc.target/aarch64/ifcvt_multiple_sets_diamond_4.c
> @@ -1,41 +1,58 @@
> /* { dg-do run } */
> -/* { dg-options "-O2 --param=max-rtl-if-conversion-unpredictable-cost=100 -
> fdump-rtl-ce1" } */
> +/* { dg-options "-O2 -fno-ssa-phiopt -fno-tree-ter
> +-fno-tree-coalesce-vars -fdump-rtl-ce1" } */
> +/* { dg-additional-options
> +"--param=max-rtl-if-conversion-unpredictable-cost=100
> +--param=max-rtl-if-conversion-predictable-cost=100" } */
>
> -volatile long ga, gt, ge;
> +volatile long ga, gt, ge, gw;
>
> __attribute__ ((noipa)) void
> -convertible (long c, long p, long q)
> +disjoint_arm_values (long a, long b, long c, long d, long s,
> + long x, long y, long z, long w)
> {
> - long a, t;
> - if (c > 7)
> + if (s > 0)
> {
> - a = p + 1;
> - t = q + 2;
> + x = a;
> + y = b - 2;
> }
> else
> {
> - a = p - 3;
> - t = q - 4;
> + z = c;
> + w = d - 4;
> }
> - ga = a;
> - gt = t;
> + ga = x;
> + gt = y;
> + ge = z;
> + gw = w;
> }
>
> +/* Each arm has one set and defines a different live-out destination.
> +*/
> __attribute__ ((noipa)) void
> -reject_arm_only_values (long c, long p, long q, long t, long e)
> +single_set_arms (long c, long a, long b, long x, long y)
> {
> - long a;
> - if (c > 7)
> + if (c > 0)
> + x = a + 1;
> + else
> + y = b - 1;
> + ga = x;
> + gt = y;
> +}
> +
> +/* The arms share X through register copies and compute distinct live-out
> + destinations. */
> +__attribute__ ((noipa)) void
> +partially_overlapping_values (long c, long a, long b,
> + long x, long t, long e)
> +{
> + if (c > 0)
> {
> - a = p + 1;
> - t = q + 2;
> + x = a;
> + t = b + 1;
> }
> else
> {
> - a = p - 3;
> - e = q - 4;
> + x = b;
> + e = a - 1;
> }
> - ga = a;
> + ga = x;
> gt = t;
> ge = e;
> }
> @@ -43,23 +60,27 @@ reject_arm_only_values (long c, long p, long q, long t,
> long e) __attribute__ ((optimize ("O0"))) int main (void) {
> - convertible (8, 10, 20);
> - if (ga != 11 || gt != 22)
> + disjoint_arm_values (10, 20, 30, 40, 1, 50, 60, 70, 80); if (ga !=
> + 10 || gt != 18 || ge != 70 || gw != 80)
> + __builtin_abort ();
> + disjoint_arm_values (10, 20, 30, 40, 0, 50, 60, 70, 80); if (ga !=
> + 50 || gt != 60 || ge != 30 || gw != 36)
> + __builtin_abort ();
> +
> + single_set_arms (1, 10, 20, 30, 40);
> + if (ga != 11 || gt != 40)
> __builtin_abort ();
> - convertible (7, 10, 20);
> - if (ga != 7 || gt != 16)
> + single_set_arms (0, 10, 20, 30, 40);
> + if (ga != 30 || gt != 19)
> __builtin_abort ();
>
> - reject_arm_only_values (8, 10, 20, 31, 47);
> - if (ga != 11 || gt != 22 || ge != 47)
> + partially_overlapping_values (1, 10, 20, 30, 40, 50); if (ga != 10
> + || gt != 21 || ge != 50)
> __builtin_abort ();
> - reject_arm_only_values (7, 10, 20, 31, 47);
> - if (ga != 7 || gt != 31 || ge != 16)
> + partially_overlapping_values (0, 10, 20, 30, 40, 50); if (ga != 20
> + || gt != 40 || ge != 9)
> __builtin_abort ();
> return 0;
> }
>
> -/* The first diamond is handled by the existing conditional-move path. The
> - second must be rejected because each arm has a live-out value not assigned
> - by the other arm. */
> -/* { dg-final { scan-rtl-dump-not "if-conversion succeeded through
> noce_convert_multiple_sets" "ce1" } } */
> +/* { dg-final { scan-rtl-dump-times "if-conversion succeeded through
> +noce_convert_multiple_sets" 3 "ce1" } } */
> diff --git a/gcc/testsuite/gcc.target/aarch64/ifcvt_multiple_sets_diamond_5.c
> b/gcc/testsuite/gcc.target/aarch64/ifcvt_multiple_sets_diamond_5.c
> index 7490df7ddc0..ccf21441089 100644
> --- a/gcc/testsuite/gcc.target/aarch64/ifcvt_multiple_sets_diamond_5.c
> +++ b/gcc/testsuite/gcc.target/aarch64/ifcvt_multiple_sets_diamond_5.c
> @@ -23,8 +23,7 @@ f (const unsigned char *p, unsigned long tag) }
>
> /* { dg-final { scan-rtl-dump-times "if-conversion succeeded through
> noce_convert_multiple_sets" 1 "ce1" } } */
> -/* { dg-final { scan-assembler-times "\tcsinc\t" 1 } } */
> -/* { dg-final { scan-assembler-times "\tcsel\t" 1 } } */
> +/* { dg-final { scan-assembler-times "\tcsinc\t" 2 } } */
> /* { dg-final { scan-assembler-times "\tldrb\t" 1 } } */
> /* { dg-final { scan-assembler-not {\tb(eq|ne)\t} } } */
> /* { dg-final { scan-assembler-not {\tcbn?z\t} } } */ diff --git
> a/gcc/testsuite/gcc.target/aarch64/ifcvt_multiple_sets_diamond_6.c
> b/gcc/testsuite/gcc.target/aarch64/ifcvt_multiple_sets_diamond_6.c
> new file mode 100644
> index 00000000000..a6e7be9c22a
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/aarch64/ifcvt_multiple_sets_diamond_6.c
> @@ -0,0 +1,100 @@
> +/* Test dependencies in diamonds with arm-only live-out destinations.
> +*/
> +/* { dg-do run } */
> +/* { dg-options "-O2 -fno-tree-ter -fno-tree-coalesce-vars
> +-fdump-rtl-ce1" } */
> +/* { dg-additional-options
> +"--param=max-rtl-if-conversion-unpredictable-cost=100
> +--param=max-rtl-if-conversion-predictable-cost=100" } */
> +
> +volatile long ga, gt, ge, gu;
> +
> +/* Each arm reads the value that the other arm can replace. The else
> + arm also defines E twice with a dependent live-out value in between.
> +*/ __attribute__ ((noipa)) void cross_arm_repeated (long c, long p,
> +long q, long t, long e) {
> + long k;
> + if (c > 7)
> + {
> + t = e + p;
> + k = q + 1;
> + }
> + else
> + {
> + e = t + q;
> + k = e ^ p;
> + e = k + 3;
> + }
> + ga = k;
> + gt = t;
> + ge = e;
> +}
> +
> +/* C is both a condition input and an arm-only live-out destination.
> +*/ __attribute__ ((noipa)) void condition_input (long c, long p, long
> +q, long a) {
> + long t;
> + if (c > 7)
> + {
> + a = p + 1;
> + t = q + 2;
> + }
> + else
> + {
> + t = p - 3;
> + c = q - 4;
> + }
> + ga = a;
> + gt = t;
> + ge = c;
> +}
> +
> +/* One arm has more live-out destinations. The conversion keeps the CFG
> arm
> + order. */
> +__attribute__ ((noipa)) void
> +unequal_output_counts (long c, long p, long q, long t, long e, long u)
> +{
> + long a;
> + if (__builtin_expect (c > 7, 1))
> + {
> + a = p + 1;
> + t = q + 2;
> + }
> + else
> + {
> + a = p - 3;
> + e = q - 4;
> + u = p + q;
> + }
> + ga = a;
> + gt = t;
> + ge = e;
> + gu = u;
> +}
> +
> +__attribute__ ((optimize ("O0"))) int
> +main (void)
> +{
> + cross_arm_repeated (8, 10, 20, 31, 47);
> + if (ga != 21 || gt != 57 || ge != 47)
> + __builtin_abort ();
> + cross_arm_repeated (7, 10, 20, 31, 47);
> + if (ga != 57 || gt != 31 || ge != 60)
> + __builtin_abort ();
> +
> + condition_input (8, 10, 20, 47);
> + if (ga != 11 || gt != 22 || ge != 8)
> + __builtin_abort ();
> + condition_input (7, 10, 20, 47);
> + if (ga != 47 || gt != 7 || ge != 16)
> + __builtin_abort ();
> +
> + unequal_output_counts (8, 10, 20, 31, 47, 59); if (ga != 11 || gt !=
> + 22 || ge != 47 || gu != 59)
> + __builtin_abort ();
> + unequal_output_counts (7, 10, 20, 31, 47, 59); if (ga != 7 || gt !=
> + 31 || ge != 16 || gu != 30)
> + __builtin_abort ();
> +
> + return 0;
> +}
> +
> +/* { dg-final { scan-rtl-dump-times "if-conversion succeeded through
> +noce_convert_multiple_sets" 3 "ce1" } } */
> diff --git a/gcc/testsuite/gcc.target/aarch64/ifcvt_multiple_sets_diamond_7.c
> b/gcc/testsuite/gcc.target/aarch64/ifcvt_multiple_sets_diamond_7.c
> new file mode 100644
> index 00000000000..f72223f6808
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/aarch64/ifcvt_multiple_sets_diamond_7.c
> @@ -0,0 +1,26 @@
> +/* Include arm-only outputs in the multiple-set conversion limit. */
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fno-tree-ter -fno-tree-coalesce-vars
> +-fdump-rtl-ce1" } */
> +/* { dg-additional-options "--param=max-rtl-if-conversion-insns=3
> +--param=max-rtl-if-conversion-unpredictable-cost=100" } */
> +
> +void sink3 (long, long, long);
> +
> +void
> +f (long c, long p, long q, long t, long e) {
> + long a;
> + if (c > 7)
> + {
> + a = p + 1;
> + t = a + q;
> + a = t + 3;
> + }
> + else
> + {
> + a = p - 3;
> + e = q - 4;
> + }
> + sink3 (a, t, e);
> +}
> +
> +/* { dg-final { scan-rtl-dump-not "if-conversion succeeded through
> +noce_convert_multiple_sets" "ce1" } } */
> diff --git a/gcc/testsuite/gcc.target/i386/ifcvt-multiple-sets-diamond-1.c
> b/gcc/testsuite/gcc.target/i386/ifcvt-multiple-sets-diamond-1.c
> index fb4491074f3..c23f9a75967 100644
> --- a/gcc/testsuite/gcc.target/i386/ifcvt-multiple-sets-diamond-1.c
> +++ b/gcc/testsuite/gcc.target/i386/ifcvt-multiple-sets-diamond-1.c
> @@ -1,27 +1,27 @@
> /* { dg-do run } */
> /* { dg-require-effective-target lp64 } */
> /* { dg-options "-O2 -mtune=generic -fdump-rtl-ce1" } */
> -/* { dg-additional-options "--param=max-rtl-if-conversion-predictable-
> cost=100" } */
> +/* { dg-additional-options "--param=max-rtl-if-conversion-insns=3
> +--param=max-rtl-if-conversion-predictable-cost=100
> +--param=max-rtl-if-conversion-unpredictable-cost=100" } */
>
> -/* The single-set arm is the likely fallthrough block. Speculative
> arithmetic
> - clobbers FLAGS, so each conditional move must re-materialize the
> +/* The arms have disjoint live-out destinations. The speculative xor
> + clobbers FLAGS, so the conversion must not reuse the incoming
> comparison. */
>
> -volatile long ga, gb;
> +volatile long ga, gb, gd;
>
> __attribute__ ((noipa)) void
> -f (long c, long x, long y, long b)
> +f (long c, long x, long y, long a, long b, long d)
> {
> - long a;
> - if (__builtin_expect (c <= 7, 1))
> - a = y + 3;
> - else
> + if (__builtin_expect (c > 7, 0))
> {
> a = x + 1;
> b = y + 2;
> }
> + else
> + d = x ^ y;
> ga = a;
> gb = b;
> + gd = d;
> }
>
> /* Keep the runtime driver out of noce so that the dump count is specific to
> @@ -33,9 +33,10 @@ main (void)
> for (long x = -8; x != 9; ++x)
> for (long y = -8; y != 9; ++y)
> {
> - f (c, x, y, 4);
> - if (ga != (c > 7 ? x + 1 : y + 3)
> - || gb != (c > 7 ? y + 2 : 4))
> + f (c, x, y, 2, 4, 6);
> + if (ga != (c > 7 ? x + 1 : 2)
> + || gb != (c > 7 ? y + 2 : 4)
> + || gd != (c > 7 ? 6 : (x ^ y)))
> __builtin_abort ();
> }
> return 0;
> --
> 2.50.1 (Apple Git-155)