Hi, Kyrylo Just curious why the arm_only values has to be rejected? Ideally the ge in reject_arm_only_values could also be a condition move like ge=(c>7)? e : q - 4. LLVM is able to convert it to csel. We just found this kind of diamond convert could help the PR126139 case, with allowing disjoint dest.
<[email protected]> 于2026年7月15日周三 17:09写道: > > From: Kyrylo Tkachov <[email protected]> > > This patch supersedes this one: > https://gcc.gnu.org/pipermail/gcc-patches/2026-July/722391.html > Consider that patch discarded. > > noce_convert_multiple_sets handles multi-set IF-THEN-JOIN regions but not > IF-THEN-ELSE-JOIN diamonds. After GIMPLE factors the selected load in > Snappy's tag decoder, such a diamond still defines the next tag and input > advance and leaves a data-dependent branch. > > Validate both arms using the existing multiple-set checks. Choose a > primary arm that defines every live-out pseudo defined by the secondary arm. > Require the primary arm to define at least two distinct live-out pseudos. > This leaves single-result diamonds to the existing specialized transforms. > Collect the live-out destinations while validating each arm and compare the > resulting bitmaps when choosing the primary arm. > > Evaluate secondary-arm SET_SRCs into fresh pseudos in dependency order > and preserve their source locations. For each primary destination, use the > final secondary value as the other conditional-move input. Use the incoming > value when the secondary arm does not define that destination. > > Either CFG arm may be primary. If the branch-target arm is primary, > swap the arms in a local noce_if_info and reverse the select orientation. > Reject speculative sequences that modify condition inputs. If they clobber > the condition code, stop sharing the comparison. Rematerialize it for each > conditional move instead. > > In outline, convert > > secondary: > (set advance secondary_advance) > primary: > (set next primary_next) > (set advance primary_advance) > > to > > (set secondary_tmp secondary_advance) > (set next > (if_then_else primary_p primary_next incoming_next)) > (set advance > (if_then_else primary_p primary_advance secondary_tmp)) > > where primary_p selects the primary arm. > > Weight arm costs by edge probability for speed and sum them for size. The > target noce_conversion_profitable_p hook retains the final profitability > decision. > > Add execution and code-generation tests for symmetric and asymmetric > diamonds, arm dependencies, repeated definitions, unmatched live-outs, > condition-code clobbers, and both arm orientations. > > This removes the badly-predicted conditional branch in the Snappy decode > loop and gives ~20% on the BM_UFlatMedley workload on my aarch64 > machine. > > On SPEC2026 it triggers a few thousand times with small improvements but > nothing I would call out above noise levels. > > Bootstrapped and tested on aarch64-linux-gnu and x86_64-pc-linux-gnu. > > Ideally this would land after the fix for the wrong-code PR126184 > (https://gcc.gnu.org/pipermail/gcc-patches/2026-July/723467.html) > > Ok for trunk? > Thanks, > Kyrill > > gcc/ChangeLog: > > PR tree-optimization/125557 > * ifcvt.cc: Include "explow.h". > (noce_convert_multiple_sets): Handle diamond CFG cleanup. > (noce_convert_multiple_sets_1): Evaluate secondary-arm values in fresh > pseudos, preserve their source locations, and use their final values > as conditional-move inputs. Use copy_to_mode_reg. Reject secondary > sequences that modify condition inputs and handle condition-code > clobbers. > (bb_ok_for_noce_convert_multiple_sets): Add REQUIRE_MULTIPLE and > LIVE_OUT_DESTS. Record distinct live-out destinations. > (noce_process_if_block): Recognize and cost multi-set diamonds. > Choose > the compatible primary arm from the validated live-out destinations. > > gcc/testsuite/ChangeLog: > > PR tree-optimization/125557 > * gcc.c-torture/execute/ifcvt-diamond-1.c: New test. > * gcc.target/aarch64/ifcvt_multiple_sets_diamond.c: New test. > * gcc.target/aarch64/ifcvt_multiple_sets_diamond_2.c: New test. > * gcc.target/aarch64/ifcvt_multiple_sets_diamond_3.c: New test. > * gcc.target/aarch64/ifcvt_multiple_sets_diamond_4.c: New test. > * gcc.target/aarch64/ifcvt_multiple_sets_diamond_5.c: New test. > * gcc.target/i386/ifcvt-multiple-sets-diamond-1.c: New test. > > Signed-off-by: Kyrylo Tkachov <[email protected]> > --- > gcc/ifcvt.cc | 216 +++++++++++++++--- > .../gcc.c-torture/execute/ifcvt-diamond-1.c | 143 ++++++++++++ > .../aarch64/ifcvt_multiple_sets_diamond.c | 66 ++++++ > .../aarch64/ifcvt_multiple_sets_diamond_2.c | 52 +++++ > .../aarch64/ifcvt_multiple_sets_diamond_3.c | 57 +++++ > .../aarch64/ifcvt_multiple_sets_diamond_4.c | 64 ++++++ > .../aarch64/ifcvt_multiple_sets_diamond_5.c | 31 +++ > .../i386/ifcvt-multiple-sets-diamond-1.c | 44 ++++ > 8 files changed, 644 insertions(+), 29 deletions(-) > create mode 100644 gcc/testsuite/gcc.c-torture/execute/ifcvt-diamond-1.c > create mode 100644 > gcc/testsuite/gcc.target/aarch64/ifcvt_multiple_sets_diamond.c > create mode 100644 > gcc/testsuite/gcc.target/aarch64/ifcvt_multiple_sets_diamond_2.c > create mode 100644 > gcc/testsuite/gcc.target/aarch64/ifcvt_multiple_sets_diamond_3.c > create mode 100644 > gcc/testsuite/gcc.target/aarch64/ifcvt_multiple_sets_diamond_4.c > create mode 100644 > gcc/testsuite/gcc.target/aarch64/ifcvt_multiple_sets_diamond_5.c > create mode 100644 > gcc/testsuite/gcc.target/i386/ifcvt-multiple-sets-diamond-1.c > > diff --git a/gcc/ifcvt.cc b/gcc/ifcvt.cc > index 03d9a4c6ab3..925e4e331b9 100644 > --- a/gcc/ifcvt.cc > +++ b/gcc/ifcvt.cc > @@ -37,6 +37,7 @@ > #include "cfgrtl.h" > #include "cfganal.h" > #include "cfgcleanup.h" > +#include "explow.h" > #include "expr.h" > #include "output.h" > #include "cfgloop.h" > @@ -3771,13 +3772,17 @@ 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. */ > + jump instructions. For an IF-THEN-ELSE-JOIN, first evaluate the secondary > + 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. */ > > static bool > noce_convert_multiple_sets (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_insn *jump = if_info->jump; > rtx_insn *cond_earliest; > @@ -3887,8 +3892,16 @@ noce_convert_multiple_sets (struct noce_if_info > *if_info) > emit_insn_before_setloc (seq, if_info->jump, > INSN_LOCATION (insn_info.last > ()->unmodified_insn)); > > - /* Clean up THEN_BB and the edges in and out of it. */ > - remove_edge (find_edge (test_bb, join_bb)); > + /* 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. > + Deleting ELSE_BB removes the test->else and else->join edges instead. > */ > + 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); > @@ -3906,8 +3919,15 @@ noce_convert_multiple_sets (struct noce_if_info > *if_info) > return true; > } > > -/* This goes through all relevant insns of IF_INFO->then_bb and tries to > create > - conditional moves. Information for the insns is kept in INSN_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. > + > + 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 > + on the second attempt. Set USE_COND_EARLIEST if the emitted sequence uses > + IF_INFO->cond_earliest. Return true if the complete sequence was > + emitted. */ > > static bool > noce_convert_multiple_sets_1 (struct noce_if_info *if_info, > @@ -3933,6 +3953,73 @@ noce_convert_multiple_sets_1 (struct noce_if_info > *if_info, > 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. */ > + 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 (); > + rtx_insn *else_insn; > + FOR_BB_INSNS (if_info->else_bb, else_insn) > + { > + if (!active_insn_p (else_insn)) > + continue; > + > + noce_multiple_sets_info *info = else_insn_info[else_count]; > + rtx set = single_set (else_insn); > + gcc_checking_assert (set && REG_P (SET_DEST (set)) > + && !HARD_REGISTER_P (SET_DEST (set))); > + > + rtx target = SET_DEST (set); > + rtx value = copy_rtx (SET_SRC (set)); > + int i, ii; > + FOR_EACH_VEC_ELT (info->rewired_src, i, ii) > + value = simplify_replace_rtx (value, > + else_insn_info[ii]->target, > + else_insn_info[ii]->temporary); > + > + 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++; > + } > + > + set_curr_insn_location (saved_location); > + > + gcc_checking_assert (else_count == (int) else_insn_info.length ()); > + > + /* These insns run ahead of the conditional moves. If they change a > + register the comparison reads we cannot reuse it, so bail. If they > + only clobber the condition code, drop the shared compare so that > every > + move re-materializes its own. */ > + > + rtx_insn *first_else > + = before_else ? NEXT_INSN (before_else) : get_insns (); > + for (rtx_insn *ei = first_else; ei; ei = NEXT_INSN (ei)) > + { > + if (modified_in_p (cond, ei)) > + { > + end_sequence (); > + return false; > + } > + if (cc_cmp > + && (modified_in_p (cc_cmp, ei) > + || (rev_cc_cmp && modified_in_p (rev_cc_cmp, ei)))) > + { > + cc_cmp = NULL_RTX; > + rev_cc_cmp = NULL_RTX; > + } > + } > + } > > FOR_BB_INSNS (then_bb, insn) > { > @@ -3957,6 +4044,17 @@ noce_convert_multiple_sets_1 (struct noce_if_info > *if_info, > > rtx 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; > + } > + > /* As we are transforming > if (x > y) > { > @@ -4244,19 +4342,29 @@ 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. Also check that we have more than one > - set (other routines can handle a single set better than we would), > - and fewer than PARAM_MAX_RTL_IF_CONVERSION_INSNS sets. While going > - through the insns store the sum of their potential costs in COST. */ > + 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 fewer 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. */ > > static bool > -bb_ok_for_noce_convert_multiple_sets (basic_block test_bb, unsigned *cost) > +bb_ok_for_noce_convert_multiple_sets (basic_block test_bb, unsigned *cost, > + bool require_multiple = true, > + bitmap live_out_dests = NULL) > { > rtx_insn *insn; > unsigned count = 0; > unsigned param = param_max_rtl_if_conversion_insns; > bool speed_p = optimize_bb_for_speed_p (test_bb); > unsigned potential_cost = 0; > + if (live_out_dests) > + bitmap_clear (live_out_dests); > + bitmap bb_live_out > + = live_out_dests ? df_get_live_out (test_bb) : NULL; > > FOR_BB_INSNS (test_bb, insn) > { > @@ -4289,6 +4397,9 @@ bb_ok_for_noce_convert_multiple_sets (basic_block > test_bb, unsigned *cost) > if (!can_conditionally_move_p (GET_MODE (dest))) > return false; > > + if (live_out_dests && bitmap_bit_p (bb_live_out, REGNO (dest))) > + bitmap_set_bit (live_out_dests, REGNO (dest)); > + > potential_cost += insn_cost (insn, speed_p); > > count++; > @@ -4297,11 +4408,13 @@ bb_ok_for_noce_convert_multiple_sets (basic_block > test_bb, unsigned *cost) > *cost += potential_cost; > > /* If we would only put out one conditional move, the other strategies > - this pass tries are better optimized and will be more appropriate. > + 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. > Some targets want to strictly limit the number of conditional moves > that are emitted, they set this through PARAM, we need to respect > that. */ > - return count > 1 && count <= param; > + return count >= (require_multiple ? 2u : 1u) && count <= param; > } > > /* Compute average of two given costs weighted by relative probabilities > @@ -4342,10 +4455,11 @@ noce_process_if_block (struct noce_if_info *if_info) > (2) x = b; if (...) x = a; > (3) if (...) x = a; // as if with an initial x = x. > (4) if (...) { x = a; y = b; z = c; } // Like 3, for multiple SETS. > + (5) A multi-set IF-THEN-ELSE-JOIN. > The later patterns require jumps to be more expensive. > - For the if (...) x = a; else x = b; case we allow multiple insns > - inside the then and else blocks as long as their only effect is > - to calculate a value for x. > + For the diamond case, 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. > ??? For future expansion, further expand the "multiple X" rules. */ > > /* First look for multiple SETS. > @@ -4354,25 +4468,69 @@ noce_process_if_block (struct noce_if_info *if_info) > If a target re-uses the existing CC comparison we keep track of that > and add the costs before default noce_conversion_profitable_p. */ > > - unsigned potential_cost = if_info->original_cost; > unsigned old_cost = if_info->original_cost; > - if (!else_bb > - && HAVE_conditional_move > - && bb_ok_for_noce_convert_multiple_sets (then_bb, &potential_cost)) > - { > - /* Temporarily set the original costs to what we estimated so > - we can determine if the transformation is worth it. */ > - if_info->original_cost = potential_cost; > - if (noce_convert_multiple_sets (if_info)) > + unsigned ms_then_cost = 0, ms_else_cost = 0; > + auto_bitmap ms_then_live_out_dests, ms_else_live_out_dests; > + noce_if_info ms_if_info = *if_info; > + bool multiple_sets_p = false; > + > + if (HAVE_conditional_move) > + { > + if (!else_bb) > + multiple_sets_p > + = bb_ok_for_noce_convert_multiple_sets (then_bb, &ms_then_cost); > + else if (!if_info->then_else_reversed > + && bb_ok_for_noce_convert_multiple_sets (then_bb, > + &ms_then_cost, false, > + ms_then_live_out_dests) > + && bb_ok_for_noce_convert_multiple_sets (else_bb, > + &ms_else_cost, false, > + > ms_else_live_out_dests)) > { > - if (dump_file && if_info->transform_name) > + 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); > + multiple_sets_p = true; > + } > + } > + } > + > + if (multiple_sets_p) > + { > + /* The original code runs the comparison and one arm. Estimate that > cost > + (for a diamond weight the two arms by their probabilities) and let > + noce_convert_multiple_sets convert only if the conditional moves come > + out cheaper. */ > + unsigned potential_cost = old_cost + ms_then_cost; > + if (ms_if_info.else_bb) > + { > + if (optimize_bb_for_speed_p (test_bb)) > + potential_cost > + = old_cost + average_cost (ms_then_cost, ms_else_cost, > + find_edge (test_bb, > + ms_if_info.then_bb)); > + else > + potential_cost = old_cost + ms_then_cost + ms_else_cost; > + } > + ms_if_info.original_cost = potential_cost; > + if (noce_convert_multiple_sets (&ms_if_info)) > + { > + if (dump_file && ms_if_info.transform_name) > fprintf (dump_file, "if-conversion succeeded through %s\n", > - if_info->transform_name); > + ms_if_info.transform_name); > return true; > } > - > - /* Restore the original costs. */ > - if_info->original_cost = old_cost; > } > > bool speed_p = optimize_bb_for_speed_p (test_bb); > diff --git a/gcc/testsuite/gcc.c-torture/execute/ifcvt-diamond-1.c > b/gcc/testsuite/gcc.c-torture/execute/ifcvt-diamond-1.c > new file mode 100644 > index 00000000000..b6a883e3de4 > --- /dev/null > +++ b/gcc/testsuite/gcc.c-torture/execute/ifcvt-diamond-1.c > @@ -0,0 +1,143 @@ > +/* Runtime correctness of if-converted IF-THEN-ELSE-JOIN diamonds with > + multiple output registers (noce_convert_multiple_sets). */ > + > +long g1, g2, g3; > + > +__attribute__ ((noipa)) void > +diamond2 (long c, long x, long y) > +{ > + long a, b; > + if (c & 3) > + { > + a = x + 1; > + b = y - 2; > + } > + else > + { > + a = x * 4; > + b = y + 9; > + } > + g1 = a; > + g2 = b; > +} > + > +/* Then arm reads an earlier then output (arm-internal dependency). */ > +__attribute__ ((noipa)) void > +diamond3 (long c, long p, long q) > +{ > + long a, b, d; > + if (c > 0) > + { > + a = p ^ q; > + b = a + 7; > + d = q * 2; > + } > + else > + { > + a = p & q; > + b = q | 1; > + d = p - 3; > + } > + g1 = a; > + g2 = b; > + g3 = d; > +} > + > +/* A single output in the else arm. The other register keeps its incoming > + value on the else path. */ > +__attribute__ ((noipa)) void > +diamond_then2_else1 (long c, long x, long y) > +{ > + long a = x, b = y; > + if (c < 0) > + { > + a = x + 100; > + b = y + 200; > + } > + else > + a = x - 50; > + g1 = a; > + g2 = b; > +} > + > +/* Keep the single-set arm as the likely fallthrough block. The multi-set > + arm must become the primary arm of the conversion. */ > +__attribute__ ((noipa)) void > +diamond_reversed_then2_else1 (long c, long x) > +{ > + long type = c & 3; > + long next = type; > + long advance; > + if (__builtin_expect (type != 0, 1)) > + advance = type + 1; > + else > + { > + next = x + 1; > + advance = x + 2; > + } > + g1 = next; > + g2 = advance; > +} > + > +/* Each arm produces a live-out value that the other arm does not. */ > +__attribute__ ((noipa)) void > +diamond_unmatched_liveouts (long c, long p, long q) > +{ > + long a = 100, t = 300, e = 200; > + if (c & 4) > + { > + a = p + q; > + t = p * 2; > + } > + else > + { > + a = p - q; > + e = q * 2; > + } > + g1 = a; > + g2 = e; > + g3 = t; > +} > + > +int > +main (void) > +{ > + for (long c = -4; c <= 12; c++) > + for (long p = -6; p <= 6; p++) > + for (long q = -6; q <= 6; q++) > + { > + diamond2 (c, p, q); > + if (g1 != ((c & 3) ? p + 1 : p * 4) > + || g2 != ((c & 3) ? q - 2 : q + 9)) > + __builtin_abort (); > + > + diamond3 (c, p, q); > + { > + long ea = (c > 0) ? (p ^ q) : (p & q); > + long eb = (c > 0) ? ea + 7 : (q | 1); > + long ed = (c > 0) ? (q * 2) : (p - 3); > + if (g1 != ea || g2 != eb || g3 != ed) > + __builtin_abort (); > + } > + > + diamond_then2_else1 (c, p, q); > + if (g1 != ((c < 0) ? p + 100 : p - 50) > + || g2 != ((c < 0) ? q + 200 : q)) > + __builtin_abort (); > + > + diamond_reversed_then2_else1 (c, p); > + { > + long type = c & 3; > + if (g1 != (type ? type : p + 1) > + || g2 != (type ? type + 1 : p + 2)) > + __builtin_abort (); > + } > + > + diamond_unmatched_liveouts (c, p, q); > + if (g1 != ((c & 4) ? p + q : p - q) > + || g2 != ((c & 4) ? 200 : q * 2) > + || g3 != ((c & 4) ? p * 2 : 300)) > + __builtin_abort (); > + } > + return 0; > +} > diff --git a/gcc/testsuite/gcc.target/aarch64/ifcvt_multiple_sets_diamond.c > b/gcc/testsuite/gcc.target/aarch64/ifcvt_multiple_sets_diamond.c > new file mode 100644 > index 00000000000..58105001fd2 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/aarch64/ifcvt_multiple_sets_diamond.c > @@ -0,0 +1,66 @@ > +/* Test if-conversion of IF-THEN-ELSE-JOIN diamonds with multiple output > + registers through noce_convert_multiple_sets. */ > +/* { dg-do compile } */ > +/* { dg-options "-O2 -fdump-rtl-ce1" } */ > + > +void sink2 (long, long); > + > +/* Two outputs, both arms write the same registers. */ > +void > +diamond_arith (long c, long x, long y) > +{ > + long a, b; > + if (c > 7) > + { > + a = x + 1; > + b = y - 2; > + } > + else > + { > + a = x * 4; > + b = y + 9; > + } > + sink2 (a, b); > +} > + > +/* Two outputs computed from constants on each arm. */ > +void > +diamond_const (long c, long x, long y) > +{ > + long a, b; > + if (c == 3) > + { > + a = 5; > + b = 7; > + } > + else > + { > + a = 9; > + b = 11; > + } > + sink2 (a, b); > +} > + > +/* Two outputs in the then arm, a single output in the else arm. The second > + register keeps its incoming value on the else path. */ > +void > +diamond_then2_else1 (long c, long x, long y) > +{ > + long a = x, b = y; > + if (c < 0) > + { > + a = x + 100; > + b = y + 200; > + } > + else > + a = x - 50; > + sink2 (a, b); > +} > + > +/* { dg-final { scan-rtl-dump-times "if-conversion succeeded through > noce_convert_multiple_sets" 3 "ce1" } } */ > + > +/* The converted diamonds are branchless: no conditional branch remains. */ > +/* { dg-final { scan-assembler-not > {\tb(eq|ne|cs|cc|mi|pl|vs|vc|hi|ls|ge|lt|gt|le)\t} } } */ > +/* { dg-final { scan-assembler-not "\tcbn?z\t" } } */ > +/* { dg-final { scan-assembler-not "\ttbn?z\t" } } */ > +/* { dg-final { scan-assembler "\tcsel\t" } } */ > diff --git a/gcc/testsuite/gcc.target/aarch64/ifcvt_multiple_sets_diamond_2.c > b/gcc/testsuite/gcc.target/aarch64/ifcvt_multiple_sets_diamond_2.c > new file mode 100644 > index 00000000000..f5891730cad > --- /dev/null > +++ b/gcc/testsuite/gcc.target/aarch64/ifcvt_multiple_sets_diamond_2.c > @@ -0,0 +1,52 @@ > +/* Each arm of the diamond loads from a selected address and advances a > + pointer by a selected amount. Once the two arm loads are commoned the > + diamond writes two registers on both arms (the load result and the > + advance), which noce_convert_multiple_sets turns into conditional moves. > */ > +/* { dg-do compile } */ > +/* { dg-options "-O2 -fdump-rtl-ce1" } */ > + > +#include <stddef.h> > +#include <stdint.h> > + > +extern const int16_t lentab[256]; > + > +static inline uint32_t > +extract (uint32_t val, size_t type) > +{ > + const uint64_t masks = 0x0000FFFF00FF0000ull; > + return val & (uint32_t) ((masks >> (type * 16)) & 0xFFFF); > +} > + > +ptrdiff_t > +f (const uint8_t *ip, size_t tag, const uint8_t *end, ptrdiff_t op) > +{ > + do > + { > + const uint8_t *old_ip = ip; > + ptrdiff_t lmo = lentab[tag]; > + size_t type = tag & 3; > + if (type == 0) > + { > + size_t n = (tag >> 2) + 1; > + tag = ip[n]; > + ip += n + 1; > + } > + else > + { > + tag = ip[type]; > + ip += type + 1; > + } > + uint32_t next = (uint32_t) old_ip[0] | ((uint32_t) old_ip[1] << 8); > + ptrdiff_t extracted = extract (next, type); > + op += lmo - extracted; > + } > + while (ip < end); > + return op; > +} > + > +/* { dg-final { scan-rtl-dump "if-conversion succeeded through > noce_convert_multiple_sets" "ce1" } } */ > +/* { 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} } } */ > +/* { dg-final { scan-assembler-not {\ttbn?z\t} } } */ > diff --git a/gcc/testsuite/gcc.target/aarch64/ifcvt_multiple_sets_diamond_3.c > b/gcc/testsuite/gcc.target/aarch64/ifcvt_multiple_sets_diamond_3.c > new file mode 100644 > index 00000000000..c459703e9bd > --- /dev/null > +++ b/gcc/testsuite/gcc.target/aarch64/ifcvt_multiple_sets_diamond_3.c > @@ -0,0 +1,57 @@ > +/* Test dependencies between sets in both arms of an IF-THEN-ELSE-JOIN > + diamond. */ > +/* { dg-do run } */ > +/* { dg-options "-O2 --param=max-rtl-if-conversion-unpredictable-cost=100 > -fdump-rtl-ce1" } */ > +/* Keep both assignments to x in the same RTL pseudo. */ > +/* { dg-additional-options "-fno-tree-ter -fno-tree-coalesce-vars" } */ > + > +volatile long gx, gy; > + > +__attribute__ ((noipa)) void > +diamond_dependencies (long c, long a, long b) > +{ > + long x, y; > + if (c & 1) > + { > + x = a + 1; > + y = x ^ b; > + x = y + 3; > + } > + else > + { > + x = b - 1; > + y = x ^ a; > + x = y - 3; > + } > + gx = x; > + gy = y; > +} > + > +__attribute__ ((optimize ("O0"))) int > +main (void) > +{ > + for (long c = -3; c <= 3; ++c) > + for (long a = -5; a <= 5; ++a) > + for (long b = -5; b <= 5; ++b) > + { > + long x, y; > + diamond_dependencies (c, a, b); > + if (c & 1) > + { > + long first_x = a + 1; > + y = first_x ^ b; > + x = y + 3; > + } > + else > + { > + long first_x = b - 1; > + y = first_x ^ a; > + x = y - 3; > + } > + if (gx != x || gy != y) > + __builtin_abort (); > + } > + return 0; > +} > + > +/* { dg-final { scan-rtl-dump-times "if-conversion succeeded through > noce_convert_multiple_sets" 1 "ce1" } } */ > 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 > new file mode 100644 > index 00000000000..22617c4d098 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/aarch64/ifcvt_multiple_sets_diamond_4.c > @@ -0,0 +1,64 @@ > +/* { dg-do run } */ > +/* { dg-options "-O2 --param=max-rtl-if-conversion-unpredictable-cost=100 > -fdump-rtl-ce1" } */ > + > +volatile long ga, gt, ge; > + > +__attribute__ ((noipa)) void > +convertible (long c, long p, long q) > +{ > + long a, t; > + if (c > 7) > + { > + a = p + 1; > + t = q + 2; > + } > + else > + { > + a = p - 3; > + t = q - 4; > + } > + ga = a; > + gt = t; > +} > + > +__attribute__ ((noipa)) void > +reject_arm_only_values (long c, long p, long q, long t, long e) > +{ > + long a; > + if (c > 7) > + { > + a = p + 1; > + t = q + 2; > + } > + else > + { > + a = p - 3; > + e = q - 4; > + } > + ga = a; > + gt = t; > + ge = e; > +} > + > +__attribute__ ((optimize ("O0"))) int > +main (void) > +{ > + convertible (8, 10, 20); > + if (ga != 11 || gt != 22) > + __builtin_abort (); > + convertible (7, 10, 20); > + if (ga != 7 || gt != 16) > + __builtin_abort (); > + > + reject_arm_only_values (8, 10, 20, 31, 47); > + if (ga != 11 || gt != 22 || ge != 47) > + __builtin_abort (); > + reject_arm_only_values (7, 10, 20, 31, 47); > + if (ga != 7 || gt != 31 || ge != 16) > + __builtin_abort (); > + return 0; > +} > + > +/* The first diamond converts. The second must be rejected because each arm > + has a live-out value not assigned by the other arm. */ > +/* { dg-final { scan-rtl-dump-times "if-conversion succeeded through > noce_convert_multiple_sets" 1 "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 > new file mode 100644 > index 00000000000..7490df7ddc0 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/aarch64/ifcvt_multiple_sets_diamond_5.c > @@ -0,0 +1,31 @@ > +/* Test a diamond whose likely fallthrough arm has one set and whose other > + arm has multiple sets, including a live-out not changed by the fallthrough > + arm. */ > +/* { dg-do compile } */ > +/* { dg-options "-O2 -fdump-rtl-ce1" } */ > +/* { dg-additional-options > "--param=max-rtl-if-conversion-predictable-cost=100" } */ > + > +unsigned long > +f (const unsigned char *p, unsigned long tag) > +{ > + unsigned long type = tag & 3; > + unsigned long next = type; > + unsigned long advance; > + if (__builtin_expect (type != 0, 1)) > + advance = type + 1; > + else > + { > + unsigned long base = tag >> 2; > + next = base + 1; > + advance = base + 2; > + } > + return p[next] + (advance << 8); > +} > + > +/* { 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 "\tldrb\t" 1 } } */ > +/* { dg-final { scan-assembler-not {\tb(eq|ne)\t} } } */ > +/* { dg-final { scan-assembler-not {\tcbn?z\t} } } */ > +/* { dg-final { scan-assembler-not {\ttbn?z\t} } } */ > 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 > new file mode 100644 > index 00000000000..fb4491074f3 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/i386/ifcvt-multiple-sets-diamond-1.c > @@ -0,0 +1,44 @@ > +/* { 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" } */ > + > +/* The single-set arm is the likely fallthrough block. Speculative > arithmetic > + clobbers FLAGS, so each conditional move must re-materialize the > + comparison. */ > + > +volatile long ga, gb; > + > +__attribute__ ((noipa)) void > +f (long c, long x, long y, long b) > +{ > + long a; > + if (__builtin_expect (c <= 7, 1)) > + a = y + 3; > + else > + { > + a = x + 1; > + b = y + 2; > + } > + ga = a; > + gb = b; > +} > + > +/* Keep the runtime driver out of noce so that the dump count is specific to > + F. */ > +__attribute__ ((optimize ("O0"))) int > +main (void) > +{ > + for (long c = 5; c != 11; ++c) > + 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)) > + __builtin_abort (); > + } > + return 0; > +} > + > +/* { dg-final { scan-rtl-dump-times "if-conversion succeeded through > noce_convert_multiple_sets" 1 "ce1" } } */ > -- > 2.50.1 (Apple Git-155) >
