Hi,

This patch appears to introduce a regression for Cortex-M3/4/33. Other Cortex-M 
targets that I test are either fine or unsupported.

Testing arm/short-it-ifcvt-2.c
doing compile
Executing on host: /build/r17-3970-g301afaab5b8eaf/bin/arm-none-eabi-gcc  
/build/gcc_src/gcc/testsuite/gcc.target/arm/short-it-ifcvt-2.c  -mthumb 
-march=armv7-m -mcpu=cortex-m3 -mfloat-abi=soft -mfpu=auto   
-fdiagnostics-plain-output  -O2 -mthumb -mrestrict-it -ffat-lto-objects 
-fno-ident -S     -o short-it-ifcvt-2.s    (timeout = 800)
spawn -ignore SIGHUP /build/r17-3970-g301afaab5b8eaf/bin/arm-none-eabi-gcc 
/build/gcc_src/gcc/testsuite/gcc.target/arm/short-it-ifcvt-2.c -mthumb 
-march=armv7-m -mcpu=cortex-m3 -mfloat-abi=soft -mfpu=auto 
-fdiagnostics-plain-output -O2 -mthumb -mrestrict-it -ffat-lto-objects 
-fno-ident -S -o short-it-ifcvt-2.s
pid is 3087 -3087
pid is -1
output is  status 0
PASS: gcc.target/arm/short-it-ifcvt-2.c (test for excess errors)
FAIL: gcc.target/arm/short-it-ifcvt-2.c scan-assembler b(gt|le)

Produced assembler with r17-3970-g301afaab5b8eaf:
        .cpu cortex-m3
        .arch armv7-m
        .fpu softvfp
        .eabi_attribute 20, 1
        .eabi_attribute 21, 1
        .eabi_attribute 23, 3
        .eabi_attribute 24, 1
        .eabi_attribute 25, 1
        .eabi_attribute 26, 1
        .eabi_attribute 30, 2
        .eabi_attribute 34, 1
        .eabi_attribute 18, 4
        .file   "short-it-ifcvt-2.c"
        .text
        .align  1
        .p2align 2,,3
        .global f1
        .syntax unified
        .thumb
        .thumb_func
        .type   f1, %function
f1:
        @ args = 0, pretend = 0, frame = 0
        @ frame_needed = 0, uses_anonymous_args = 0
        @ link register save eliminated.
        cmp     r0, #101
        add     r3, r0, #1
        itte    lt
        rsblt   r1, r1, #0
        rsblt   r3, r0, #0
        rsbge   r2, r2, #0
        add     r3, r3, r1
        adds    r0, r3, r2
        bx      lr
        .size   f1, .-f1

Produced assembler with r17-3969-g17b02d8f2b385c:
        .cpu cortex-m3
        .arch armv7-m
        .fpu softvfp
        .eabi_attribute 20, 1
        .eabi_attribute 21, 1
        .eabi_attribute 23, 3
        .eabi_attribute 24, 1
        .eabi_attribute 25, 1
        .eabi_attribute 26, 1
        .eabi_attribute 30, 2
        .eabi_attribute 34, 1
        .eabi_attribute 18, 4
        .file   "short-it-ifcvt-2.c"
        .text
        .align  1
        .p2align 2,,3
        .global f1
        .syntax unified
        .thumb
        .thumb_func
        .type   f1, %function
f1:
        @ args = 0, pretend = 0, frame = 0
        @ frame_needed = 0, uses_anonymous_args = 0
        @ link register save eliminated.
        cmp     r0, #100
        iteet   gt
        addgt   r0, r0, #1
        rsble   r0, r0, #0
        rsble   r1, r1, #0
        rsbgt   r2, r2, #0
        add     r0, r0, r1
        add     r0, r0, r2
        bx      lr
        .size   f1, .-f1

I do not know what the correct action here is since the expression,

/* { dg-final { scan-assembler "b(gt|le)" } } */

passes for some targets, like Cortex-M85, but fails for others.

Let me know if you want me to test something.

Kind regards,
Torbjörn

On 2026-09-01 07:38, [email protected] wrote:
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;

Reply via email to