When Y = sign_extend (X) holds before a conditional assignment to X,
computing sign_extend of the selected X already produces the right Y on
both paths.  noce_convert_multiple_sets currently adds another selection
for Y, losing this relation and sometimes rejecting the whole conversion
because of its cost.

Reuse the existing rewired-source and live-out information to identify
selected inputs.  Look for an identical expression at the last definition
on the other path, rejecting intervening input changes and unselected
inputs modified in the else arm.  Leave the existing emitter, target
costs and profitability checks unchanged.  This also handles direct
integer expressions, repeated assignments and diamonds without adding
symbolic substitution or a separate value-tracking data structure.

The existing if-conversion cleanup removes dead incoming computations;
no additional instruction deletion is needed here.

Runtime RTL tests cover positive transformations and cases that must
retain selection, including a self-updating definition and an unselected
input changed by the else arm.  Match complete function bodies to check
the conditional moves that remain, and check that the sign-extension
cases leave one extension.  Include an expression combining a selected
input and an unchanged input.  Exclude Windows ABIs from tests whose
RTL uses System V argument registers.

Bootstrapped and regression-tested on x86_64-linux-gnu, with C, C++,
Fortran, Objective-C and LTO enabled and multilib disabled.  Compared
with the unpatched tree, the complete compiler and target-library
results differ only by the 48 PASS results from the eight new tests.
The 85 FAIL and 18 XPASS results are unchanged.

The fixincludes checks passed after providing AutoGen.  A baseline-only
libbacktrace link failure was resolved by rerunning its check with
the subdirectory's configured static runtime link flags; both builds
then passed all 44 tests.

Without the patch, ten assembly checks and five transformation checks
fail as expected in a separate run of the new tests; all runtime
checks pass.

gcc/ChangeLog:

        * ifcvt.cc (noce_redundant_cmov_p): New function.
        (noce_convert_multiple_sets): Identify derived values that need no
        conditional move before generating and costing the sequence.
        (noce_convert_multiple_sets_1): Update comment.
        * ifcvt.h (noce_multiple_sets_info::need_cmov): Update comment.

gcc/testsuite/ChangeLog:

        * gcc.dg/rtl/x86_64/ifcvt-derived-1.c: New test.
        * gcc.dg/rtl/x86_64/ifcvt-derived-2.c: New test.
        * gcc.dg/rtl/x86_64/ifcvt-derived-3.c: New test.
        * gcc.dg/rtl/x86_64/ifcvt-derived-4.c: New test.
        * gcc.dg/rtl/x86_64/ifcvt-derived-5.c: New test.
        * gcc.dg/rtl/x86_64/ifcvt-derived-6.c: New test.
        * gcc.dg/rtl/x86_64/ifcvt-derived-7.c: New test.
        * gcc.dg/rtl/x86_64/ifcvt-derived-8.c: New test.
---
 gcc/ifcvt.cc                                  |  89 ++++-
 gcc/ifcvt.h                                   |   5 +-
 .../gcc.dg/rtl/x86_64/ifcvt-derived-1.c       | 359 ++++++++++++++++++
 .../gcc.dg/rtl/x86_64/ifcvt-derived-2.c       | 349 +++++++++++++++++
 .../gcc.dg/rtl/x86_64/ifcvt-derived-3.c       |  96 +++++
 .../gcc.dg/rtl/x86_64/ifcvt-derived-4.c       |  97 +++++
 .../gcc.dg/rtl/x86_64/ifcvt-derived-5.c       | 102 +++++
 .../gcc.dg/rtl/x86_64/ifcvt-derived-6.c       |  90 +++++
 .../gcc.dg/rtl/x86_64/ifcvt-derived-7.c       |  94 +++++
 .../gcc.dg/rtl/x86_64/ifcvt-derived-8.c       | 100 +++++
 10 files changed, 1376 insertions(+), 5 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/rtl/x86_64/ifcvt-derived-1.c
 create mode 100644 gcc/testsuite/gcc.dg/rtl/x86_64/ifcvt-derived-2.c
 create mode 100644 gcc/testsuite/gcc.dg/rtl/x86_64/ifcvt-derived-3.c
 create mode 100644 gcc/testsuite/gcc.dg/rtl/x86_64/ifcvt-derived-4.c
 create mode 100644 gcc/testsuite/gcc.dg/rtl/x86_64/ifcvt-derived-5.c
 create mode 100644 gcc/testsuite/gcc.dg/rtl/x86_64/ifcvt-derived-6.c
 create mode 100644 gcc/testsuite/gcc.dg/rtl/x86_64/ifcvt-derived-7.c
 create mode 100644 gcc/testsuite/gcc.dg/rtl/x86_64/ifcvt-derived-8.c

diff --git a/gcc/ifcvt.cc b/gcc/ifcvt.cc
index eb3a958074d..ff0f29c497b 100644
--- a/gcc/ifcvt.cc
+++ b/gcc/ifcvt.cc
@@ -4037,6 +4037,80 @@ noce_finish_if_conversion (noce_if_info *if_info)
   num_updated_if_blocks++;
 }
 
+/* Return true if the computation for INFO already gives its other-path
+   value when earlier selected values are substituted for its inputs.  For
+   example, if Y = sign_extend (X) before the branch, then
+
+     if (C) { X = A; Y = sign_extend (X); }
+
+   needs only the conditional move for X.  Require an identical expression
+   at the reaching definition of Y, with no intervening input changes.  */
+
+static bool
+noce_redundant_cmov_p (noce_if_info *if_info,
+                      const vec<noce_multiple_sets_info *> &insn_info,
+                      noce_multiple_sets_info *info)
+{
+  rtx src = SET_SRC (single_set (info->unmodified_insn));
+  auto_bitmap selected_regs;
+  for (int i : info->rewired_src)
+    {
+      rtx dest = insn_info[i]->target;
+      /* A dead temporary is evaluated unconditionally and need not have
+        its original value on the other path.  Live outputs are selected,
+        or have already been proved to give that value without selection.  */
+      if (!bitmap_bit_p (df_get_live_out (if_info->then_bb), REGNO (dest)))
+       return false;
+      bitmap_set_bit (selected_regs, REGNO (dest));
+    }
+
+  /* The caller has excluded memory, side effects and trapping operations.
+     Restrict this proof to integer pseudos and defined subregister bits.  */
+  subrtx_iterator::array_type array;
+  FOR_EACH_SUBRTX (iter, array, src, ALL)
+    {
+      const_rtx op = *iter;
+      if ((GET_MODE (op) != VOIDmode && !SCALAR_INT_MODE_P (GET_MODE (op)))
+         || (SUBREG_P (op) && paradoxical_subreg_p (op))
+         || (REG_P (op) && HARD_REGISTER_P (op)))
+       return false;
+
+      /* An input not rewired to an earlier selection still uses its
+        incoming value, even when the else arm assigns to that register.  */
+      if (if_info->else_bb && REG_P (op)
+         && !bitmap_bit_p (selected_regs, REGNO (op)))
+       {
+         rtx_insn *insn;
+         FOR_BB_INSNS (if_info->else_bb, insn)
+           if (NONDEBUG_INSN_P (insn) && modified_in_p (op, insn))
+             return false;
+       }
+    }
+
+  /* Look for the last definition on the other path, first in the else
+     arm, if any, and then in the test block.  Stop at any input change,
+     including changes in the defining instruction itself (Y = Y + 1).  */
+  basic_block bb = if_info->else_bb ? if_info->else_bb : if_info->test_bb;
+  for (;; bb = if_info->test_bb)
+    {
+      rtx_insn *insn;
+      FOR_BB_INSNS_REVERSE (bb, insn)
+       if (NONDEBUG_INSN_P (insn))
+         {
+           if (modified_in_p (src, insn))
+             return false;
+           if (modified_in_p (info->target, insn))
+             {
+               rtx set = single_set (insn);
+               return (set && rtx_equal_p (SET_DEST (set), info->target)
+                       && rtx_equal_p (SET_SRC (set), src));
+             }
+         }
+      if (bb == if_info->test_bb)
+       return false;
+    }
+}
+
 /* We have something like:
 
      if (x > y)
@@ -4144,6 +4218,16 @@ noce_convert_multiple_sets (noce_if_info *if_info)
       return false;
     }
 
+  for (unsigned i = 0; i < then_count; ++i)
+    if (insn_info[i]->need_cmov
+       && noce_redundant_cmov_p (if_info, insn_info, insn_info[i]))
+      {
+       insn_info[i]->need_cmov = false;
+       if (dump_file)
+         fprintf (dump_file, "noce: derived value needs no conditional move "
+                  "at insn %d\n", INSN_UID (insn_info[i]->unmodified_insn));
+      }
+
   int last_needs_comparison = -1;
 
   bool use_cond_earliest = false;
@@ -4435,9 +4519,8 @@ noce_convert_multiple_sets_1 (noce_if_info *if_info, rtx 
cond,
       else
        temp = target;
 
-      /* We have identified swap-style idioms before.  A normal
-        set will need to be a cmov while the first instruction of a swap-style
-        idiom can be a regular move.  This helps with costing.  */
+      /* Dead temporaries and derived values that are already correct on
+        both paths do not need a conditional move.  */
       bool need_cmov = info->need_cmov;
 
       /* If we had a non-canonical conditional jump (i.e. one where
diff --git a/gcc/ifcvt.h b/gcc/ifcvt.h
index 806ad47158e..0256582e24e 100644
--- a/gcc/ifcvt.h
+++ b/gcc/ifcvt.h
@@ -52,8 +52,9 @@ struct noce_multiple_sets_info
   rtx temporary;
   /* The insn from the original block that this entry describes.  */
   rtx_insn *unmodified_insn;
-  /* True if the destination is live out of the block, so the value has to be
-     selected with a conditional move rather than moved unconditionally.  */
+  /* True if the destination is live out of the block and its value still
+     needs a conditional move.  A derived value that is already correct on
+     both paths can be computed unconditionally.  */
   bool need_cmov;
 };
 
diff --git a/gcc/testsuite/gcc.dg/rtl/x86_64/ifcvt-derived-1.c 
b/gcc/testsuite/gcc.dg/rtl/x86_64/ifcvt-derived-1.c
new file mode 100644
index 00000000000..e1f66508e59
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/rtl/x86_64/ifcvt-derived-1.c
@@ -0,0 +1,359 @@
+/* { dg-do run { target { { i?86-*-* x86_64-*-* } && lp64 } } } */
+/* { dg-skip-if "RTL uses the System V ABI" { *-*-mingw* *-*-cygwin* } } */
+/* { dg-options "-O2 -mtune=generic 
--param=max-rtl-if-conversion-unpredictable-cost=100 
--param=max-rtl-if-conversion-insns=12 -fdump-rtl-ce1 -save-temps" } */
+
+/* Exercise ce1 directly; tree passes must not fold the input shape.  */
+long __RTL (startwith ("ce1"))
+extend_signed (int x, int a, int c, int *out)
+{
+(function "extend_signed"
+  (param "x" (DECL_RTL (reg:SI <0>))
+    (DECL_RTL_INCOMING (reg:SI di)))
+  (param "a" (DECL_RTL (reg:SI <1>))
+    (DECL_RTL_INCOMING (reg:SI si)))
+  (param "c" (DECL_RTL (reg:SI <2>))
+    (DECL_RTL_INCOMING (reg:SI dx)))
+  (param "out" (DECL_RTL (reg:DI <3>))
+    (DECL_RTL_INCOMING (reg:DI cx)))
+  (insn-chain
+    (block 2
+      (edge-from entry (flags "FALLTHRU"))
+      (cnote 1 [bb 2] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 2 (set (reg:SI <0>) (reg:SI di)))
+      (cinsn 3 (set (reg:SI <1>) (reg:SI si)))
+      (cinsn 4 (set (reg:SI <2>) (reg:SI dx)))
+      (cinsn 5 (set (reg:DI <3>) (reg:DI cx)))
+      (cinsn 6 (set (reg:DI <4>) (sign_extend:DI (reg:SI <0>))))
+
+      (cinsn 9 (set (reg:CCZ flags)
+                    (compare:CCZ (reg:SI <2>) (const_int 0))))
+      (cjump_insn 10 (set (pc)
+                         (if_then_else (eq (reg:CCZ flags) (const_int 0))
+                                       (label_ref 20) (pc))))
+      (edge-to 3 (flags "FALLTHRU"))
+      (edge-to 4)
+    )
+    (block 3
+      (edge-from 2 (flags "FALLTHRU"))
+      (cnote 11 [bb 3] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 12 (set (reg:SI <0>) (reg:SI <1>)))
+      (cinsn 14 (set (reg:DI <4>) (sign_extend:DI (reg:SI <0>))))
+      (edge-to 4 (flags "FALLTHRU"))
+    )
+    (block 4
+      (edge-from 2)
+      (edge-from 3 (flags "FALLTHRU"))
+      (clabel 20 100000)
+      (cnote 21 [bb 4] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 32 (set (mem:SI (reg:DI <3>) [0 S4 A32]) (reg:SI <0>)))
+      (cinsn 33 (set (mem:SI (plus:DI (reg:DI <3>)
+                                           (const_int 8)) [0 S4 A32])
+                    (reg:SI <1>)))
+
+      (cinsn 34 (set (reg:DI ax) (reg:DI <4>)))
+      (cinsn 35 (use (reg:DI ax)))
+      (edge-to exit (flags "FALLTHRU"))
+    )
+  )
+  (crtl (return_rtx (reg:DI ax)))
+)
+}
+long __RTL (startwith ("ce1"))
+extend_unsigned (int x, int a, int c, int *out)
+{
+(function "extend_unsigned"
+  (param "x" (DECL_RTL (reg:SI <0>))
+    (DECL_RTL_INCOMING (reg:SI di)))
+  (param "a" (DECL_RTL (reg:SI <1>))
+    (DECL_RTL_INCOMING (reg:SI si)))
+  (param "c" (DECL_RTL (reg:SI <2>))
+    (DECL_RTL_INCOMING (reg:SI dx)))
+  (param "out" (DECL_RTL (reg:DI <3>))
+    (DECL_RTL_INCOMING (reg:DI cx)))
+  (insn-chain
+    (block 2
+      (edge-from entry (flags "FALLTHRU"))
+      (cnote 101 [bb 2] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 102 (set (reg:SI <0>) (reg:SI di)))
+      (cinsn 103 (set (reg:SI <1>) (reg:SI si)))
+      (cinsn 104 (set (reg:SI <2>) (reg:SI dx)))
+      (cinsn 105 (set (reg:DI <3>) (reg:DI cx)))
+      (cinsn 106 (set (reg:DI <4>) (zero_extend:DI (reg:SI <0>))))
+
+      (cinsn 109 (set (reg:CCZ flags)
+                    (compare:CCZ (reg:SI <2>) (const_int 0))))
+      (cjump_insn 110 (set (pc)
+                         (if_then_else (eq (reg:CCZ flags) (const_int 0))
+                                       (label_ref 120) (pc))))
+      (edge-to 3 (flags "FALLTHRU"))
+      (edge-to 4)
+    )
+    (block 3
+      (edge-from 2 (flags "FALLTHRU"))
+      (cnote 111 [bb 3] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 112 (set (reg:SI <0>) (reg:SI <1>)))
+      (cinsn 114 (set (reg:DI <4>) (zero_extend:DI (reg:SI <0>))))
+      (edge-to 4 (flags "FALLTHRU"))
+    )
+    (block 4
+      (edge-from 2)
+      (edge-from 3 (flags "FALLTHRU"))
+      (clabel 120 100100)
+      (cnote 121 [bb 4] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 132 (set (mem:SI (reg:DI <3>) [0 S4 A32]) (reg:SI <0>)))
+      (cinsn 133 (set (mem:SI (plus:DI (reg:DI <3>)
+                                           (const_int 8)) [0 S4 A32])
+                    (reg:SI <1>)))
+
+      (cinsn 134 (set (reg:DI ax) (reg:DI <4>)))
+      (cinsn 135 (use (reg:DI ax)))
+      (edge-to exit (flags "FALLTHRU"))
+    )
+  )
+  (crtl (return_rtx (reg:DI ax)))
+)
+}
+long __RTL (startwith ("ce1"))
+xor_unselected_input (long x, long a, int c, long *out)
+{
+(function "xor_unselected_input"
+  (param "x" (DECL_RTL (reg:DI <0>))
+    (DECL_RTL_INCOMING (reg:DI di)))
+  (param "a" (DECL_RTL (reg:DI <1>))
+    (DECL_RTL_INCOMING (reg:DI si)))
+  (param "c" (DECL_RTL (reg:SI <2>))
+    (DECL_RTL_INCOMING (reg:SI dx)))
+  (param "out" (DECL_RTL (reg:DI <3>))
+    (DECL_RTL_INCOMING (reg:DI cx)))
+  (insn-chain
+    (block 2
+      (edge-from entry (flags "FALLTHRU"))
+      (cnote 201 [bb 2] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 202 (set (reg:DI <0>) (reg:DI di)))
+      (cinsn 203 (set (reg:DI <1>) (reg:DI si)))
+      (cinsn 204 (set (reg:SI <2>) (reg:SI dx)))
+      (cinsn 205 (set (reg:DI <3>) (reg:DI cx)))
+      (cinsn 206
+        (parallel [(set (reg:DI <4>)
+                        (xor:DI (reg:DI <0>) (reg:DI <1>)))
+                   (clobber (reg:CC flags))]))
+
+      (cinsn 209 (set (reg:CCZ flags)
+                    (compare:CCZ (reg:SI <2>) (const_int 0))))
+      (cjump_insn 210 (set (pc)
+                         (if_then_else (eq (reg:CCZ flags) (const_int 0))
+                                       (label_ref 220) (pc))))
+      (edge-to 3 (flags "FALLTHRU"))
+      (edge-to 4)
+    )
+    (block 3
+      (edge-from 2 (flags "FALLTHRU"))
+      (cnote 211 [bb 3] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 212 (set (reg:DI <0>) (const_int 19)))
+      (cinsn 214
+        (parallel [(set (reg:DI <4>)
+                        (xor:DI (reg:DI <0>) (reg:DI <1>)))
+                   (clobber (reg:CC flags))]))
+      (edge-to 4 (flags "FALLTHRU"))
+    )
+    (block 4
+      (edge-from 2)
+      (edge-from 3 (flags "FALLTHRU"))
+      (clabel 220 100200)
+      (cnote 221 [bb 4] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 232 (set (mem:DI (reg:DI <3>) [0 S8 A64]) (reg:DI <0>)))
+      (cinsn 233 (set (mem:DI (plus:DI (reg:DI <3>)
+                                           (const_int 8)) [0 S8 A64])
+                    (reg:DI <1>)))
+
+      (cinsn 234 (set (reg:DI ax) (reg:DI <4>)))
+      (cinsn 235 (use (reg:DI ax)))
+      (edge-to exit (flags "FALLTHRU"))
+    )
+  )
+  (crtl (return_rtx (reg:DI ax)))
+)
+}
+int __RTL (startwith ("ce1"))
+truncate_value (long x, long a, int c, long *out)
+{
+(function "truncate_value"
+  (param "x" (DECL_RTL (reg:DI <0>))
+    (DECL_RTL_INCOMING (reg:DI di)))
+  (param "a" (DECL_RTL (reg:DI <1>))
+    (DECL_RTL_INCOMING (reg:DI si)))
+  (param "c" (DECL_RTL (reg:SI <2>))
+    (DECL_RTL_INCOMING (reg:SI dx)))
+  (param "out" (DECL_RTL (reg:DI <3>))
+    (DECL_RTL_INCOMING (reg:DI cx)))
+  (insn-chain
+    (block 2
+      (edge-from entry (flags "FALLTHRU"))
+      (cnote 301 [bb 2] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 302 (set (reg:DI <0>) (reg:DI di)))
+      (cinsn 303 (set (reg:DI <1>) (reg:DI si)))
+      (cinsn 304 (set (reg:SI <2>) (reg:SI dx)))
+      (cinsn 305 (set (reg:DI <3>) (reg:DI cx)))
+      (cinsn 306 (set (reg:SI <4>) (subreg:SI (reg:DI <0>) 0)))
+
+      (cinsn 309 (set (reg:CCZ flags)
+                    (compare:CCZ (reg:SI <2>) (const_int 0))))
+      (cjump_insn 310 (set (pc)
+                         (if_then_else (eq (reg:CCZ flags) (const_int 0))
+                                       (label_ref 320) (pc))))
+      (edge-to 3 (flags "FALLTHRU"))
+      (edge-to 4)
+    )
+    (block 3
+      (edge-from 2 (flags "FALLTHRU"))
+      (cnote 311 [bb 3] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 312 (set (reg:DI <0>) (reg:DI <1>)))
+      (cinsn 314 (set (reg:SI <4>) (subreg:SI (reg:DI <0>) 0)))
+      (edge-to 4 (flags "FALLTHRU"))
+    )
+    (block 4
+      (edge-from 2)
+      (edge-from 3 (flags "FALLTHRU"))
+      (clabel 320 100300)
+      (cnote 321 [bb 4] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 332 (set (mem:DI (reg:DI <3>) [0 S8 A64]) (reg:DI <0>)))
+      (cinsn 333 (set (mem:DI (plus:DI (reg:DI <3>)
+                                           (const_int 8)) [0 S8 A64])
+                    (reg:DI <1>)))
+
+      (cinsn 334 (set (reg:SI ax) (reg:SI <4>)))
+      (cinsn 335 (use (reg:SI ax)))
+      (edge-to exit (flags "FALLTHRU"))
+    )
+  )
+  (crtl (return_rtx (reg:SI ax)))
+)
+}
+long __RTL (startwith ("ce1"))
+xor_values (long x, long a, int c, long *out)
+{
+(function "xor_values"
+  (param "x" (DECL_RTL (reg:DI <0>))
+    (DECL_RTL_INCOMING (reg:DI di)))
+  (param "a" (DECL_RTL (reg:DI <1>))
+    (DECL_RTL_INCOMING (reg:DI si)))
+  (param "c" (DECL_RTL (reg:SI <2>))
+    (DECL_RTL_INCOMING (reg:SI dx)))
+  (param "out" (DECL_RTL (reg:DI <3>))
+    (DECL_RTL_INCOMING (reg:DI cx)))
+  (insn-chain
+    (block 2
+      (edge-from entry (flags "FALLTHRU"))
+      (cnote 401 [bb 2] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 402 (set (reg:DI <0>) (reg:DI di)))
+      (cinsn 403 (set (reg:DI <1>) (reg:DI si)))
+      (cinsn 404 (set (reg:SI <2>) (reg:SI dx)))
+      (cinsn 405 (set (reg:DI <3>) (reg:DI cx)))
+      (cinsn 406
+        (parallel [(set (reg:DI <4>)
+                        (xor:DI (reg:DI <0>) (reg:DI <1>)))
+                   (clobber (reg:CC flags))]))
+
+      (cinsn 409 (set (reg:CCZ flags)
+                    (compare:CCZ (reg:SI <2>) (const_int 0))))
+      (cjump_insn 410 (set (pc)
+                         (if_then_else (eq (reg:CCZ flags) (const_int 0))
+                                       (label_ref 420) (pc))))
+      (edge-to 3 (flags "FALLTHRU"))
+      (edge-to 4)
+    )
+    (block 3
+      (edge-from 2 (flags "FALLTHRU"))
+      (cnote 411 [bb 3] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 412 (set (reg:DI <0>) (reg:DI <1>)))
+      (cinsn 413 (set (reg:DI <1>) (const_int 19)))
+      (cinsn 414
+        (parallel [(set (reg:DI <4>)
+                        (xor:DI (reg:DI <0>) (reg:DI <1>)))
+                   (clobber (reg:CC flags))]))
+      (edge-to 4 (flags "FALLTHRU"))
+    )
+    (block 4
+      (edge-from 2)
+      (edge-from 3 (flags "FALLTHRU"))
+      (clabel 420 100400)
+      (cnote 421 [bb 4] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 432 (set (mem:DI (reg:DI <3>) [0 S8 A64]) (reg:DI <0>)))
+      (cinsn 433 (set (mem:DI (plus:DI (reg:DI <3>)
+                                           (const_int 8)) [0 S8 A64])
+                    (reg:DI <1>)))
+
+      (cinsn 434 (set (reg:DI ax) (reg:DI <4>)))
+      (cinsn 435 (use (reg:DI ax)))
+      (edge-to exit (flags "FALLTHRU"))
+    )
+  )
+  (crtl (return_rtx (reg:DI ax)))
+)
+}
+int
+main (void)
+{
+  const long v[] = {0, 1, -1, 2147483647L, -2147483647L - 1,
+                   0x123456789abcdefL, -0x123456789abcdefL};
+  for (unsigned i = 0; i < sizeof v / sizeof *v; ++i)
+    for (unsigned j = 0; j < sizeof v / sizeof *v; ++j)
+      for (int c = -1; c <= 1; ++c)
+       {
+         long x = v[i], a = v[j], out[3];
+         int ix = (int) x, ia = (int) a, io[3];
+         int expected = c ? ia : ix;
+         if (extend_signed (ix, ia, c, io) != (long) expected
+             || io[0] != expected || io[2] != ia)
+           __builtin_abort ();
+         if (extend_unsigned (ix, ia, c, io) != (unsigned int) expected
+             || io[0] != expected || io[2] != ia)
+           __builtin_abort ();
+         if (xor_unselected_input (x, a, c, out)
+             != ((c ? 19 : x) ^ a)
+             || out[0] != (c ? 19 : x) || out[1] != a)
+           __builtin_abort ();
+         if (truncate_value (x, a, c, out) != (int) (c ? a : x)
+             || out[0] != (c ? a : x) || out[1] != a)
+           __builtin_abort ();
+         if (xor_values (x, a, c, out) != (c ? (a ^ 19) : (x ^ a))
+             || out[0] != (c ? a : x) || out[1] != (c ? 19 : a))
+           __builtin_abort ();
+       }
+  return 0;
+}
+
+/* { dg-final { scan-rtl-dump-times "noce: derived value needs no conditional 
move" 5 "ce1" } } */
+/* { dg-final { scan-rtl-dump-times "if-conversion succeeded through 
noce_convert_multiple_sets" 5 "ce1" } } */
+
+/* Match each complete function body, so an extra conditional move cannot
+   hide in a wildcard.  Register allocation and unrelated moves may vary.  */
+/*
+** extend_signed:
+** (?:(?!cmov|movslq|cltq)[^\n]*\n\t)*cmov[^\n]*
+** (?:(?!cmov|movslq|cltq)[^\n]*\n\t)*(?:movslq|cltq)[^\n]*
+** (?:(?!cmov|movslq|cltq)[^\n]*\n\t)*ret
+*/
+/*
+** extend_unsigned:
+** (?:(?!cmov)[^\n]*\n\t)*cmov[^\n]*
+** (?:(?!cmov)[^\n]*\n\t)*ret
+*/
+/*
+** xor_unselected_input:
+** (?:(?!cmov)[^\n]*\n\t)*cmov[^\n]*
+** (?:(?!cmov)[^\n]*\n\t)*ret
+*/
+/*
+** truncate_value:
+** (?:(?!cmov)[^\n]*\n\t)*cmov[^\n]*
+** (?:(?!cmov)[^\n]*\n\t)*ret
+*/
+/*
+** xor_values:
+** (?:(?!cmov)[^\n]*\n\t)*cmov[^\n]*
+** (?:(?!cmov)[^\n]*\n\t)*cmov[^\n]*
+** (?:(?!cmov)[^\n]*\n\t)*ret
+*/
+
+/* { dg-final { check-function-bodies "**" "" } } */
diff --git a/gcc/testsuite/gcc.dg/rtl/x86_64/ifcvt-derived-2.c 
b/gcc/testsuite/gcc.dg/rtl/x86_64/ifcvt-derived-2.c
new file mode 100644
index 00000000000..78cfb773e79
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/rtl/x86_64/ifcvt-derived-2.c
@@ -0,0 +1,349 @@
+/* { dg-do run { target { { i?86-*-* x86_64-*-* } && lp64 } } } */
+/* { dg-skip-if "RTL uses the System V ABI" { *-*-mingw* *-*-cygwin* } } */
+/* { dg-options "-O2 -mtune=generic 
--param=max-rtl-if-conversion-unpredictable-cost=100 
--param=max-rtl-if-conversion-insns=12 -fdump-rtl-ce1 -save-temps" } */
+
+/* Exercise ce1 directly; tree passes must not fold the input shape.  */
+long __RTL (startwith ("ce1"))
+different_extension (int x, int a, int c, int *out)
+{
+(function "different_extension"
+  (param "x" (DECL_RTL (reg:SI <0>))
+    (DECL_RTL_INCOMING (reg:SI di)))
+  (param "a" (DECL_RTL (reg:SI <1>))
+    (DECL_RTL_INCOMING (reg:SI si)))
+  (param "c" (DECL_RTL (reg:SI <2>))
+    (DECL_RTL_INCOMING (reg:SI dx)))
+  (param "out" (DECL_RTL (reg:DI <3>))
+    (DECL_RTL_INCOMING (reg:DI cx)))
+  (insn-chain
+    (block 2
+      (edge-from entry (flags "FALLTHRU"))
+      (cnote 1 [bb 2] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 2 (set (reg:SI <0>) (reg:SI di)))
+      (cinsn 3 (set (reg:SI <1>) (reg:SI si)))
+      (cinsn 4 (set (reg:SI <2>) (reg:SI dx)))
+      (cinsn 5 (set (reg:DI <3>) (reg:DI cx)))
+      (cinsn 6 (set (reg:DI <4>) (zero_extend:DI (reg:SI <0>))))
+
+      (cinsn 9 (set (reg:CCZ flags)
+                    (compare:CCZ (reg:SI <2>) (const_int 0))))
+      (cjump_insn 10 (set (pc)
+                         (if_then_else (eq (reg:CCZ flags) (const_int 0))
+                                       (label_ref 20) (pc))))
+      (edge-to 3 (flags "FALLTHRU"))
+      (edge-to 4)
+    )
+    (block 3
+      (edge-from 2 (flags "FALLTHRU"))
+      (cnote 11 [bb 3] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 12 (set (reg:SI <0>) (reg:SI <1>)))
+      (cinsn 15 (set (reg:DI <4>) (sign_extend:DI (reg:SI <0>))))
+      (edge-to 4 (flags "FALLTHRU"))
+    )
+    (block 4
+      (edge-from 2)
+      (edge-from 3 (flags "FALLTHRU"))
+      (clabel 20 100000)
+      (cnote 21 [bb 4] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 32 (set (mem:SI (reg:DI <3>) [0 S4 A32]) (reg:SI <0>)))
+      (cinsn 33 (set (mem:SI (plus:DI (reg:DI <3>)
+                                           (const_int 8)) [0 S4 A32])
+                    (reg:SI <1>)))
+
+      (cinsn 34 (set (reg:DI ax) (reg:DI <4>)))
+      (cinsn 35 (use (reg:DI ax)))
+      (edge-to exit (flags "FALLTHRU"))
+    )
+  )
+  (crtl (return_rtx (reg:DI ax)))
+)
+}
+long __RTL (startwith ("ce1"))
+changed_input (int x, int a, int c, int *out)
+{
+(function "changed_input"
+  (param "x" (DECL_RTL (reg:SI <0>))
+    (DECL_RTL_INCOMING (reg:SI di)))
+  (param "a" (DECL_RTL (reg:SI <1>))
+    (DECL_RTL_INCOMING (reg:SI si)))
+  (param "c" (DECL_RTL (reg:SI <2>))
+    (DECL_RTL_INCOMING (reg:SI dx)))
+  (param "out" (DECL_RTL (reg:DI <3>))
+    (DECL_RTL_INCOMING (reg:DI cx)))
+  (insn-chain
+    (block 2
+      (edge-from entry (flags "FALLTHRU"))
+      (cnote 101 [bb 2] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 102 (set (reg:SI <0>) (reg:SI di)))
+      (cinsn 103 (set (reg:SI <1>) (reg:SI si)))
+      (cinsn 104 (set (reg:SI <2>) (reg:SI dx)))
+      (cinsn 105 (set (reg:DI <3>) (reg:DI cx)))
+      (cinsn 106 (set (reg:DI <4>) (sign_extend:DI (reg:SI <0>))))
+      (cinsn 107 (set (reg:SI <0>) (reg:SI <1>)))
+      (cinsn 109 (set (reg:CCZ flags)
+                    (compare:CCZ (reg:SI <2>) (const_int 0))))
+      (cjump_insn 110 (set (pc)
+                         (if_then_else (eq (reg:CCZ flags) (const_int 0))
+                                       (label_ref 120) (pc))))
+      (edge-to 3 (flags "FALLTHRU"))
+      (edge-to 4)
+    )
+    (block 3
+      (edge-from 2 (flags "FALLTHRU"))
+      (cnote 111 [bb 3] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 112 (set (reg:SI <0>) (const_int 7)))
+      (cinsn 115 (set (reg:DI <4>) (sign_extend:DI (reg:SI <0>))))
+      (edge-to 4 (flags "FALLTHRU"))
+    )
+    (block 4
+      (edge-from 2)
+      (edge-from 3 (flags "FALLTHRU"))
+      (clabel 120 100100)
+      (cnote 121 [bb 4] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 132 (set (mem:SI (reg:DI <3>) [0 S4 A32]) (reg:SI <0>)))
+      (cinsn 133 (set (mem:SI (plus:DI (reg:DI <3>)
+                                           (const_int 8)) [0 S4 A32])
+                    (reg:SI <1>)))
+
+      (cinsn 134 (set (reg:DI ax) (reg:DI <4>)))
+      (cinsn 135 (use (reg:DI ax)))
+      (edge-to exit (flags "FALLTHRU"))
+    )
+  )
+  (crtl (return_rtx (reg:DI ax)))
+)
+}
+long __RTL (startwith ("ce1"))
+dead_temporary (int x, int a, int c, int *out)
+{
+(function "dead_temporary"
+  (param "x" (DECL_RTL (reg:SI <0>))
+    (DECL_RTL_INCOMING (reg:SI di)))
+  (param "a" (DECL_RTL (reg:SI <1>))
+    (DECL_RTL_INCOMING (reg:SI si)))
+  (param "c" (DECL_RTL (reg:SI <2>))
+    (DECL_RTL_INCOMING (reg:SI dx)))
+  (param "out" (DECL_RTL (reg:DI <3>))
+    (DECL_RTL_INCOMING (reg:DI cx)))
+  (insn-chain
+    (block 2
+      (edge-from entry (flags "FALLTHRU"))
+      (cnote 201 [bb 2] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 202 (set (reg:SI <0>) (reg:SI di)))
+      (cinsn 203 (set (reg:SI <1>) (reg:SI si)))
+      (cinsn 204 (set (reg:SI <2>) (reg:SI dx)))
+      (cinsn 205 (set (reg:DI <3>) (reg:DI cx)))
+      (cinsn 206 (set (reg:DI <4>) (sign_extend:DI (reg:SI <0>))))
+
+      (cinsn 209 (set (reg:CCZ flags)
+                    (compare:CCZ (reg:SI <2>) (const_int 0))))
+      (cjump_insn 210 (set (pc)
+                         (if_then_else (eq (reg:CCZ flags) (const_int 0))
+                                       (label_ref 220) (pc))))
+      (edge-to 3 (flags "FALLTHRU"))
+      (edge-to 4)
+    )
+    (block 3
+      (edge-from 2 (flags "FALLTHRU"))
+      (cnote 211 [bb 3] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 212 (set (reg:SI <5>) (reg:SI <1>)))
+      (cinsn 213 (set (reg:SI <0>) (reg:SI <5>)))
+      (cinsn 215 (set (reg:DI <4>) (sign_extend:DI (reg:SI <5>))))
+      (edge-to 4 (flags "FALLTHRU"))
+    )
+    (block 4
+      (edge-from 2)
+      (edge-from 3 (flags "FALLTHRU"))
+      (clabel 220 100200)
+      (cnote 221 [bb 4] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 232 (set (mem:SI (reg:DI <3>) [0 S4 A32]) (reg:SI <0>)))
+      (cinsn 233 (set (mem:SI (plus:DI (reg:DI <3>)
+                                           (const_int 8)) [0 S4 A32])
+                    (reg:SI <1>)))
+
+      (cinsn 234 (set (reg:DI ax) (reg:DI <4>)))
+      (cinsn 235 (use (reg:DI ax)))
+      (edge-to exit (flags "FALLTHRU"))
+    )
+  )
+  (crtl (return_rtx (reg:DI ax)))
+)
+}
+long __RTL (startwith ("ce1"))
+swap_inputs (int x, int a, int c, int *out)
+{
+(function "swap_inputs"
+  (param "x" (DECL_RTL (reg:SI <0>))
+    (DECL_RTL_INCOMING (reg:SI di)))
+  (param "a" (DECL_RTL (reg:SI <1>))
+    (DECL_RTL_INCOMING (reg:SI si)))
+  (param "c" (DECL_RTL (reg:SI <2>))
+    (DECL_RTL_INCOMING (reg:SI dx)))
+  (param "out" (DECL_RTL (reg:DI <3>))
+    (DECL_RTL_INCOMING (reg:DI cx)))
+  (insn-chain
+    (block 2
+      (edge-from entry (flags "FALLTHRU"))
+      (cnote 301 [bb 2] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 302 (set (reg:SI <0>) (reg:SI di)))
+      (cinsn 303 (set (reg:SI <1>) (reg:SI si)))
+      (cinsn 304 (set (reg:SI <2>) (reg:SI dx)))
+      (cinsn 305 (set (reg:DI <3>) (reg:DI cx)))
+      (cinsn 306 (set (reg:DI <4>) (sign_extend:DI (reg:SI <0>))))
+
+      (cinsn 309 (set (reg:CCZ flags)
+                    (compare:CCZ (reg:SI <2>) (const_int 0))))
+      (cjump_insn 310 (set (pc)
+                         (if_then_else (eq (reg:CCZ flags) (const_int 0))
+                                       (label_ref 320) (pc))))
+      (edge-to 3 (flags "FALLTHRU"))
+      (edge-to 4)
+    )
+    (block 3
+      (edge-from 2 (flags "FALLTHRU"))
+      (cnote 311 [bb 3] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 312 (set (reg:SI <5>) (reg:SI <0>)))
+      (cinsn 313 (set (reg:SI <0>) (reg:SI <1>)))
+      (cinsn 314 (set (reg:SI <1>) (reg:SI <5>)))
+      (cinsn 315 (set (reg:DI <4>) (sign_extend:DI (reg:SI <0>))))
+      (edge-to 4 (flags "FALLTHRU"))
+    )
+    (block 4
+      (edge-from 2)
+      (edge-from 3 (flags "FALLTHRU"))
+      (clabel 320 100300)
+      (cnote 321 [bb 4] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 332 (set (mem:SI (reg:DI <3>) [0 S4 A32]) (reg:SI <0>)))
+      (cinsn 333 (set (mem:SI (plus:DI (reg:DI <3>)
+                                           (const_int 8)) [0 S4 A32])
+                    (reg:SI <1>)))
+
+      (cinsn 334 (set (reg:DI ax) (reg:DI <4>)))
+      (cinsn 335 (use (reg:DI ax)))
+      (edge-to exit (flags "FALLTHRU"))
+    )
+  )
+  (crtl (return_rtx (reg:DI ax)))
+)
+}
+long __RTL (startwith ("ce1"))
+repeated_input (int x, int a, int c, int *out)
+{
+(function "repeated_input"
+  (param "x" (DECL_RTL (reg:SI <0>))
+    (DECL_RTL_INCOMING (reg:SI di)))
+  (param "a" (DECL_RTL (reg:SI <1>))
+    (DECL_RTL_INCOMING (reg:SI si)))
+  (param "c" (DECL_RTL (reg:SI <2>))
+    (DECL_RTL_INCOMING (reg:SI dx)))
+  (param "out" (DECL_RTL (reg:DI <3>))
+    (DECL_RTL_INCOMING (reg:DI cx)))
+  (insn-chain
+    (block 2
+      (edge-from entry (flags "FALLTHRU"))
+      (cnote 401 [bb 2] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 402 (set (reg:SI <0>) (reg:SI di)))
+      (cinsn 403 (set (reg:SI <1>) (reg:SI si)))
+      (cinsn 404 (set (reg:SI <2>) (reg:SI dx)))
+      (cinsn 405 (set (reg:DI <3>) (reg:DI cx)))
+      (cinsn 406 (set (reg:DI <4>) (sign_extend:DI (reg:SI <0>))))
+
+      (cinsn 409 (set (reg:CCZ flags)
+                    (compare:CCZ (reg:SI <2>) (const_int 0))))
+      (cjump_insn 410 (set (pc)
+                         (if_then_else (eq (reg:CCZ flags) (const_int 0))
+                                       (label_ref 420) (pc))))
+      (edge-to 3 (flags "FALLTHRU"))
+      (edge-to 4)
+    )
+    (block 3
+      (edge-from 2 (flags "FALLTHRU"))
+      (cnote 411 [bb 3] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 412 (set (reg:SI <0>) (reg:SI <1>)))
+      (cinsn 413 (set (reg:SI <0>) (const_int 7)))
+      (cinsn 415 (set (reg:DI <4>) (sign_extend:DI (reg:SI <0>))))
+      (edge-to 4 (flags "FALLTHRU"))
+    )
+    (block 4
+      (edge-from 2)
+      (edge-from 3 (flags "FALLTHRU"))
+      (clabel 420 100400)
+      (cnote 421 [bb 4] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 432 (set (mem:SI (reg:DI <3>) [0 S4 A32]) (reg:SI <0>)))
+      (cinsn 433 (set (mem:SI (plus:DI (reg:DI <3>)
+                                           (const_int 8)) [0 S4 A32])
+                    (reg:SI <1>)))
+
+      (cinsn 434 (set (reg:DI ax) (reg:DI <4>)))
+      (cinsn 435 (use (reg:DI ax)))
+      (edge-to exit (flags "FALLTHRU"))
+    )
+  )
+  (crtl (return_rtx (reg:DI ax)))
+)
+}
+int
+main (void)
+{
+  const int v[] = {0, 1, -1, 2147483647, -2147483647 - 1, 19, -19};
+  for (unsigned i = 0; i < sizeof v / sizeof *v; ++i)
+    for (unsigned j = 0; j < sizeof v / sizeof *v; ++j)
+      for (int c = -1; c <= 1; ++c)
+       {
+         int x = v[i], a = v[j], out[3];
+         if (different_extension (x, a, c, out)
+             != (c ? (long) a : (long) (unsigned int) x)
+             || out[0] != (c ? a : x) || out[2] != (a))
+           __builtin_abort ();
+         if (changed_input (x, a, c, out) != (c ? 7 : (long) x)
+             || out[0] != (c ? 7 : a) || out[2] != (a))
+           __builtin_abort ();
+         if (dead_temporary (x, a, c, out) != (c ? (long) a : (long) x)
+             || out[0] != (c ? a : x) || out[2] != (a))
+           __builtin_abort ();
+         if (swap_inputs (x, a, c, out) != (c ? (long) a : (long) x)
+             || out[0] != (c ? a : x) || out[2] != (c ? x : a))
+           __builtin_abort ();
+         if (repeated_input (x, a, c, out) != (c ? 7 : (long) x)
+             || out[0] != (c ? 7 : x) || out[2] != (a))
+           __builtin_abort ();
+       }
+  return 0;
+}
+
+/* { dg-final { scan-rtl-dump-times "noce: derived value needs no conditional 
move" 2 "ce1" } } */
+/* { dg-final { scan-rtl-dump-times "if-conversion succeeded through 
noce_convert_multiple_sets" 5 "ce1" } } */
+
+/* Match each complete function body, so an extra conditional move cannot
+   hide in a wildcard.  Register allocation and unrelated moves may vary.  */
+/*
+** different_extension:
+** (?:(?!cmov)[^\n]*\n\t)*cmov[^\n]*
+** (?:(?!cmov)[^\n]*\n\t)*cmov[^\n]*
+** (?:(?!cmov)[^\n]*\n\t)*ret
+*/
+/*
+** changed_input:
+** (?:(?!cmov)[^\n]*\n\t)*cmov[^\n]*
+** (?:(?!cmov)[^\n]*\n\t)*cmov[^\n]*
+** (?:(?!cmov)[^\n]*\n\t)*ret
+*/
+/*
+** dead_temporary:
+** (?:(?!cmov)[^\n]*\n\t)*cmov[^\n]*
+** (?:(?!cmov)[^\n]*\n\t)*cmov[^\n]*
+** (?:(?!cmov)[^\n]*\n\t)*ret
+*/
+/*
+** swap_inputs:
+** (?:(?!cmov)[^\n]*\n\t)*cmov[^\n]*
+** (?:(?!cmov)[^\n]*\n\t)*cmov[^\n]*
+** (?:(?!cmov)[^\n]*\n\t)*ret
+*/
+/*
+** repeated_input:
+** (?:(?!cmov)[^\n]*\n\t)*cmov[^\n]*
+** (?:(?!cmov)[^\n]*\n\t)*ret
+*/
+
+/* { dg-final { check-function-bodies "**" "" } } */
diff --git a/gcc/testsuite/gcc.dg/rtl/x86_64/ifcvt-derived-3.c 
b/gcc/testsuite/gcc.dg/rtl/x86_64/ifcvt-derived-3.c
new file mode 100644
index 00000000000..6ad904fa58d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/rtl/x86_64/ifcvt-derived-3.c
@@ -0,0 +1,96 @@
+/* { dg-do run { target { { i?86-*-* x86_64-*-* } && lp64 } } } */
+/* { dg-skip-if "RTL uses the System V ABI" { *-*-mingw* *-*-cygwin* } } */
+/* { dg-options "-O2 -mtune=generic 
--param=max-rtl-if-conversion-unpredictable-cost=100 
--param=max-rtl-if-conversion-insns=12 -fdump-rtl-ce1 -save-temps" } */
+long __RTL (startwith ("ce1"))
+diamond_equal (int x, int a, int c, int *out)
+{
+(function "diamond_equal"
+  (param "x" (DECL_RTL (reg:SI <0>))
+    (DECL_RTL_INCOMING (reg:SI di)))
+  (param "a" (DECL_RTL (reg:SI <1>))
+    (DECL_RTL_INCOMING (reg:SI si)))
+  (param "c" (DECL_RTL (reg:SI <2>))
+    (DECL_RTL_INCOMING (reg:SI dx)))
+  (param "out" (DECL_RTL (reg:DI <3>))
+    (DECL_RTL_INCOMING (reg:DI cx)))
+  (insn-chain
+    (block 2
+      (edge-from entry (flags "FALLTHRU"))
+      (cnote 501 [bb 2] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 502 (set (reg:SI <0>) (reg:SI di)))
+      (cinsn 503 (set (reg:SI <1>) (reg:SI si)))
+      (cinsn 504 (set (reg:SI <2>) (reg:SI dx)))
+      (cinsn 505 (set (reg:DI <3>) (reg:DI cx)))
+      (cinsn 506 (set (reg:DI <4>) (sign_extend:DI (reg:SI <0>))))
+
+      (cinsn 509 (set (reg:CCZ flags)
+                    (compare:CCZ (reg:SI <2>) (const_int 0))))
+      (cjump_insn 510 (set (pc)
+                         (if_then_else (eq (reg:CCZ flags) (const_int 0))
+                                       (label_ref 520) (pc))))
+      (edge-to 3 (flags "FALLTHRU"))
+      (edge-to 4)
+    )
+    (block 3
+      (edge-from 2 (flags "FALLTHRU"))
+      (cnote 511 [bb 3] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 512 (set (reg:SI <0>) (reg:SI <1>)))
+      (cinsn 515 (set (reg:DI <4>) (sign_extend:DI (reg:SI <0>))))
+      (cjump_insn 518 (set (pc) (label_ref 530)))
+      (edge-to 5)
+    )
+    (block 4
+      (edge-from 2)
+      (clabel 520 100500)
+      (cnote 521 [bb 4] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 522 (set (reg:SI <0>) (const_int 7)))
+      (cinsn 523 (set (reg:DI <4>) (sign_extend:DI (reg:SI <0>))))
+      (edge-to 5 (flags "FALLTHRU"))
+    )
+    (block 5
+      (edge-from 3)
+      (edge-from 4 (flags "FALLTHRU"))
+      (clabel 530 100600)
+      (cnote 531 [bb 5] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 532 (set (mem:SI (reg:DI <3>) [0 S4 A32]) (reg:SI <0>)))
+      (cinsn 533 (set (mem:SI (plus:DI (reg:DI <3>)
+                                           (const_int 8)) [0 S4 A32])
+                    (reg:SI <1>)))
+
+      (cinsn 534 (set (reg:DI ax) (reg:DI <4>)))
+      (cinsn 535 (use (reg:DI ax)))
+      (edge-to exit (flags "FALLTHRU"))
+    )
+  )
+  (crtl (return_rtx (reg:DI ax)))
+)
+}
+
+int
+main (void)
+{
+  const int v[] = {0, 1, -1, 2147483647, -2147483647 - 1};
+  for (unsigned i = 0; i < sizeof v / sizeof *v; ++i)
+    for (unsigned j = 0; j < sizeof v / sizeof *v; ++j)
+      for (int c = -1; c <= 1; ++c)
+       {
+         int x = v[i], a = v[j], out[3];
+         if (diamond_equal (x, a, c, out) != (c ? (long) a : 7)
+             || out[0] != (c ? a : 7) || out[2] != a)
+           __builtin_abort ();
+       }
+  return 0;
+}
+
+/* { dg-final { scan-rtl-dump-times "noce: derived value needs no conditional 
move" 1 "ce1" } } */
+/* { dg-final { scan-rtl-dump-times "if-conversion succeeded through 
noce_convert_multiple_sets" 1 "ce1" } } */
+
+/* Match each complete function body, so an extra conditional move cannot
+   hide in a wildcard.  Register allocation and unrelated moves may vary.  */
+/*
+** diamond_equal:
+** (?:(?!cmov)[^\n]*\n\t)*cmov[^\n]*
+** (?:(?!cmov)[^\n]*\n\t)*ret
+*/
+
+/* { dg-final { check-function-bodies "**" "" } } */
diff --git a/gcc/testsuite/gcc.dg/rtl/x86_64/ifcvt-derived-4.c 
b/gcc/testsuite/gcc.dg/rtl/x86_64/ifcvt-derived-4.c
new file mode 100644
index 00000000000..5e271fded69
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/rtl/x86_64/ifcvt-derived-4.c
@@ -0,0 +1,97 @@
+/* { dg-do run { target { { i?86-*-* x86_64-*-* } && lp64 } } } */
+/* { dg-skip-if "RTL uses the System V ABI" { *-*-mingw* *-*-cygwin* } } */
+/* { dg-options "-O2 -mtune=generic 
--param=max-rtl-if-conversion-unpredictable-cost=100 
--param=max-rtl-if-conversion-insns=12 -fdump-rtl-ce1 -save-temps" } */
+long __RTL (startwith ("ce1"))
+diamond_unequal (int x, int a, int c, int *out)
+{
+(function "diamond_unequal"
+  (param "x" (DECL_RTL (reg:SI <0>))
+    (DECL_RTL_INCOMING (reg:SI di)))
+  (param "a" (DECL_RTL (reg:SI <1>))
+    (DECL_RTL_INCOMING (reg:SI si)))
+  (param "c" (DECL_RTL (reg:SI <2>))
+    (DECL_RTL_INCOMING (reg:SI dx)))
+  (param "out" (DECL_RTL (reg:DI <3>))
+    (DECL_RTL_INCOMING (reg:DI cx)))
+  (insn-chain
+    (block 2
+      (edge-from entry (flags "FALLTHRU"))
+      (cnote 601 [bb 2] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 602 (set (reg:SI <0>) (reg:SI di)))
+      (cinsn 603 (set (reg:SI <1>) (reg:SI si)))
+      (cinsn 604 (set (reg:SI <2>) (reg:SI dx)))
+      (cinsn 605 (set (reg:DI <3>) (reg:DI cx)))
+      (cinsn 606 (set (reg:DI <4>) (sign_extend:DI (reg:SI <0>))))
+
+      (cinsn 609 (set (reg:CCZ flags)
+                    (compare:CCZ (reg:SI <2>) (const_int 0))))
+      (cjump_insn 610 (set (pc)
+                         (if_then_else (eq (reg:CCZ flags) (const_int 0))
+                                       (label_ref 620) (pc))))
+      (edge-to 3 (flags "FALLTHRU"))
+      (edge-to 4)
+    )
+    (block 3
+      (edge-from 2 (flags "FALLTHRU"))
+      (cnote 611 [bb 3] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 612 (set (reg:SI <0>) (reg:SI <1>)))
+      (cinsn 615 (set (reg:DI <4>) (sign_extend:DI (reg:SI <0>))))
+      (cjump_insn 618 (set (pc) (label_ref 630)))
+      (edge-to 5)
+    )
+    (block 4
+      (edge-from 2)
+      (clabel 620 100700)
+      (cnote 621 [bb 4] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 622 (set (reg:SI <0>) (const_int 7)))
+      (cinsn 623 (set (reg:DI <4>) (const_int 8)))
+      (edge-to 5 (flags "FALLTHRU"))
+    )
+    (block 5
+      (edge-from 3)
+      (edge-from 4 (flags "FALLTHRU"))
+      (clabel 630 100800)
+      (cnote 631 [bb 5] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 632 (set (mem:SI (reg:DI <3>) [0 S4 A32]) (reg:SI <0>)))
+      (cinsn 633 (set (mem:SI (plus:DI (reg:DI <3>)
+                                           (const_int 8)) [0 S4 A32])
+                    (reg:SI <1>)))
+
+      (cinsn 634 (set (reg:DI ax) (reg:DI <4>)))
+      (cinsn 635 (use (reg:DI ax)))
+      (edge-to exit (flags "FALLTHRU"))
+    )
+  )
+  (crtl (return_rtx (reg:DI ax)))
+)
+}
+
+int
+main (void)
+{
+  const int v[] = {0, 1, -1, 2147483647, -2147483647 - 1};
+  for (unsigned i = 0; i < sizeof v / sizeof *v; ++i)
+    for (unsigned j = 0; j < sizeof v / sizeof *v; ++j)
+      for (int c = -1; c <= 1; ++c)
+       {
+         int x = v[i], a = v[j], out[3];
+         if (diamond_unequal (x, a, c, out) != (c ? (long) a : 8)
+             || out[0] != (c ? a : 7) || out[2] != a)
+           __builtin_abort ();
+       }
+  return 0;
+}
+
+/* { dg-final { scan-rtl-dump-not "noce: derived value needs no conditional 
move" "ce1" } } */
+/* { dg-final { scan-rtl-dump-times "if-conversion succeeded through 
noce_convert_multiple_sets" 1 "ce1" } } */
+
+/* Match each complete function body, so an extra conditional move cannot
+   hide in a wildcard.  Register allocation and unrelated moves may vary.  */
+/*
+** diamond_unequal:
+** (?:(?!cmov)[^\n]*\n\t)*cmov[^\n]*
+** (?:(?!cmov)[^\n]*\n\t)*cmov[^\n]*
+** (?:(?!cmov)[^\n]*\n\t)*ret
+*/
+
+/* { dg-final { check-function-bodies "**" "" } } */
diff --git a/gcc/testsuite/gcc.dg/rtl/x86_64/ifcvt-derived-5.c 
b/gcc/testsuite/gcc.dg/rtl/x86_64/ifcvt-derived-5.c
new file mode 100644
index 00000000000..d63256608c6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/rtl/x86_64/ifcvt-derived-5.c
@@ -0,0 +1,102 @@
+/* { dg-do run { target { { i?86-*-* x86_64-*-* } && lp64 } } } */
+/* { dg-skip-if "RTL uses the System V ABI" { *-*-mingw* *-*-cygwin* } } */
+/* { dg-options "-O2 -mtune=generic 
--param=max-rtl-if-conversion-unpredictable-cost=100 
--param=max-rtl-if-conversion-insns=12 -fdump-rtl-ce1 -save-temps" } */
+long __RTL (startwith ("ce1"))
+derived_chain (long x, long a, int c, long *out)
+{
+(function "derived_chain"
+  (param "x" (DECL_RTL (reg:DI <0>))
+    (DECL_RTL_INCOMING (reg:DI di)))
+  (param "a" (DECL_RTL (reg:DI <1>))
+    (DECL_RTL_INCOMING (reg:DI si)))
+  (param "c" (DECL_RTL (reg:SI <2>))
+    (DECL_RTL_INCOMING (reg:SI dx)))
+  (param "out" (DECL_RTL (reg:DI <3>))
+    (DECL_RTL_INCOMING (reg:DI cx)))
+  (insn-chain
+    (block 2
+      (edge-from entry (flags "FALLTHRU"))
+      (cnote 201 [bb 2] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 202 (set (reg:DI <0>) (reg:DI di)))
+      (cinsn 203 (set (reg:DI <1>) (reg:DI si)))
+      (cinsn 204 (set (reg:SI <2>) (reg:SI dx)))
+      (cinsn 205 (set (reg:DI <3>) (reg:DI cx)))
+      (cinsn 206
+        (parallel [(set (reg:DI <4>) (xor:DI (reg:DI <0>) (const_int 37)))
+                   (clobber (reg:CC flags))]))
+      (cinsn 207
+        (parallel [(set (reg:DI <5>) (plus:DI (reg:DI <4>) (const_int 11)))
+                   (clobber (reg:CC flags))]))
+
+      (cinsn 209 (set (reg:CCZ flags)
+                    (compare:CCZ (reg:SI <2>) (const_int 0))))
+      (cjump_insn 210 (set (pc)
+                         (if_then_else (eq (reg:CCZ flags) (const_int 0))
+                                       (label_ref 220) (pc))))
+      (edge-to 3 (flags "FALLTHRU"))
+      (edge-to 4)
+    )
+    (block 3
+      (edge-from 2 (flags "FALLTHRU"))
+      (cnote 211 [bb 3] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 212 (set (reg:DI <0>) (reg:DI <1>)))
+      (cinsn 214
+        (parallel [(set (reg:DI <4>) (xor:DI (reg:DI <0>) (const_int 37)))
+                   (clobber (reg:CC flags))]))
+      (cinsn 215
+        (parallel [(set (reg:DI <5>) (plus:DI (reg:DI <4>) (const_int 11)))
+                   (clobber (reg:CC flags))]))
+      (edge-to 4 (flags "FALLTHRU"))
+    )
+    (block 4
+      (edge-from 2)
+      (edge-from 3 (flags "FALLTHRU"))
+      (clabel 220 100200)
+      (cnote 221 [bb 4] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 232 (set (mem:DI (reg:DI <3>) [0 S8 A64]) (reg:DI <0>)))
+      (cinsn 233 (set (mem:DI (plus:DI (reg:DI <3>)
+                                           (const_int 8)) [0 S8 A64])
+                    (reg:DI <1>)))
+
+      (cinsn 236 (set (mem:DI (plus:DI (reg:DI <3>)
+                                      (const_int 16)) [0 S8 A64])
+                      (reg:DI <5>)))
+      (cinsn 234 (set (reg:DI ax) (reg:DI <4>)))
+      (cinsn 235 (use (reg:DI ax)))
+      (edge-to exit (flags "FALLTHRU"))
+    )
+  )
+  (crtl (return_rtx (reg:DI ax)))
+)
+}
+
+int
+main (void)
+{
+  const long v[] = {0, 1, -1, __LONG_MAX__, -__LONG_MAX__ - 1};
+  for (unsigned i = 0; i < sizeof v / sizeof *v; ++i)
+    for (unsigned j = 0; j < sizeof v / sizeof *v; ++j)
+      for (int c = -1; c <= 1; ++c)
+       {
+         long out[3], x = v[i], a = v[j];
+         long selected = c ? a : x;
+         if (derived_chain (x, a, c, out) != (selected ^ 37)
+             || out[0] != selected || out[1] != a
+             || (unsigned long) out[2] != (unsigned long) (selected ^ 37) + 11)
+           __builtin_abort ();
+       }
+  return 0;
+}
+
+/* { dg-final { scan-rtl-dump-times "noce: derived value needs no conditional 
move" 2 "ce1" } } */
+/* { dg-final { scan-rtl-dump-times "if-conversion succeeded through 
noce_convert_multiple_sets" 1 "ce1" } } */
+
+/* Match each complete function body, so an extra conditional move cannot
+   hide in a wildcard.  Register allocation and unrelated moves may vary.  */
+/*
+** derived_chain:
+** (?:(?!cmov)[^\n]*\n\t)*cmov[^\n]*
+** (?:(?!cmov)[^\n]*\n\t)*ret
+*/
+
+/* { dg-final { check-function-bodies "**" "" } } */
diff --git a/gcc/testsuite/gcc.dg/rtl/x86_64/ifcvt-derived-6.c 
b/gcc/testsuite/gcc.dg/rtl/x86_64/ifcvt-derived-6.c
new file mode 100644
index 00000000000..dd3044c8571
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/rtl/x86_64/ifcvt-derived-6.c
@@ -0,0 +1,90 @@
+/* { dg-do run { target { { i?86-*-* x86_64-*-* } && lp64 } } } */
+/* { dg-skip-if "RTL uses the System V ABI" { *-*-mingw* *-*-cygwin* } } */
+/* { dg-options "-O2 -mtune=generic 
--param=max-rtl-if-conversion-unpredictable-cost=100 
--param=max-rtl-if-conversion-insns=12 -fdump-rtl-ce1 -save-temps" } */
+long __RTL (startwith ("ce1"))
+reversed_arm (int x, int a, int c, int *out)
+{
+(function "reversed_arm"
+  (param "x" (DECL_RTL (reg:SI <0>))
+    (DECL_RTL_INCOMING (reg:SI di)))
+  (param "a" (DECL_RTL (reg:SI <1>))
+    (DECL_RTL_INCOMING (reg:SI si)))
+  (param "c" (DECL_RTL (reg:SI <2>))
+    (DECL_RTL_INCOMING (reg:SI dx)))
+  (param "out" (DECL_RTL (reg:DI <3>))
+    (DECL_RTL_INCOMING (reg:DI cx)))
+  (insn-chain
+    (block 2
+      (edge-from entry (flags "FALLTHRU"))
+      (cnote 1 [bb 2] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 2 (set (reg:SI <0>) (reg:SI di)))
+      (cinsn 3 (set (reg:SI <1>) (reg:SI si)))
+      (cinsn 4 (set (reg:SI <2>) (reg:SI dx)))
+      (cinsn 5 (set (reg:DI <3>) (reg:DI cx)))
+      (cinsn 6 (set (reg:DI <4>) (sign_extend:DI (reg:SI <0>))))
+
+      (cinsn 9 (set (reg:CCZ flags)
+                    (compare:CCZ (reg:SI <2>) (const_int 0))))
+      (cjump_insn 10 (set (pc)
+                         (if_then_else (eq (reg:CCZ flags) (const_int 0))
+                                       (label_ref 40) (pc))))
+      (edge-to 3)
+      (edge-to 4 (flags "FALLTHRU"))
+    )
+    (block 3
+      (edge-from 2)
+      (clabel 40 101000)
+      (cnote 11 [bb 3] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 12 (set (reg:SI <0>) (reg:SI <1>)))
+      (cinsn 14 (set (reg:DI <4>) (sign_extend:DI (reg:SI <0>))))
+      (edge-to 4 (flags "FALLTHRU"))
+    )
+    (block 4
+      (edge-from 2 (flags "FALLTHRU"))
+      (edge-from 3 (flags "FALLTHRU"))
+      (clabel 20 100000)
+      (cnote 21 [bb 4] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 32 (set (mem:SI (reg:DI <3>) [0 S4 A32]) (reg:SI <0>)))
+      (cinsn 33 (set (mem:SI (plus:DI (reg:DI <3>)
+                                           (const_int 8)) [0 S4 A32])
+                    (reg:SI <1>)))
+
+      (cinsn 34 (set (reg:DI ax) (reg:DI <4>)))
+      (cinsn 35 (use (reg:DI ax)))
+      (edge-to exit (flags "FALLTHRU"))
+    )
+  )
+  (crtl (return_rtx (reg:DI ax)))
+)
+}
+
+int
+main (void)
+{
+  const int v[] = {0, 1, -1, __INT_MAX__, -__INT_MAX__ - 1};
+  for (unsigned i = 0; i < sizeof v / sizeof *v; ++i)
+    for (unsigned j = 0; j < sizeof v / sizeof *v; ++j)
+      for (int c = -1; c <= 1; ++c)
+       {
+         int out[3], x = v[i], a = v[j];
+         int selected = c ? x : a;
+         if (reversed_arm (x, a, c, out) != selected
+             || out[0] != selected || out[2] != a)
+           __builtin_abort ();
+       }
+  return 0;
+}
+
+/* { dg-final { scan-rtl-dump-times "noce: derived value needs no conditional 
move" 1 "ce1" } } */
+/* { dg-final { scan-rtl-dump-times "if-conversion succeeded through 
noce_convert_multiple_sets" 1 "ce1" } } */
+
+/* Match each complete function body, so an extra conditional move cannot
+   hide in a wildcard.  Register allocation and unrelated moves may vary.  */
+/*
+** reversed_arm:
+** (?:(?!cmov|movslq|cltq)[^\n]*\n\t)*cmov[^\n]*
+** (?:(?!cmov|movslq|cltq)[^\n]*\n\t)*(?:movslq|cltq)[^\n]*
+** (?:(?!cmov|movslq|cltq)[^\n]*\n\t)*ret
+*/
+
+/* { dg-final { check-function-bodies "**" "" } } */
diff --git a/gcc/testsuite/gcc.dg/rtl/x86_64/ifcvt-derived-7.c 
b/gcc/testsuite/gcc.dg/rtl/x86_64/ifcvt-derived-7.c
new file mode 100644
index 00000000000..fdeeae6161a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/rtl/x86_64/ifcvt-derived-7.c
@@ -0,0 +1,94 @@
+/* { dg-do run { target { { i?86-*-* x86_64-*-* } && lp64 } } } */
+/* { dg-skip-if "RTL uses the System V ABI" { *-*-mingw* *-*-cygwin* } } */
+/* { dg-options "-O2 -mtune=generic 
--param=max-rtl-if-conversion-unpredictable-cost=100 
--param=max-rtl-if-conversion-insns=12 -fdump-rtl-ce1 -save-temps" } */
+/* A defining instruction that changes its own input cannot prove the
+   value of a subsequent evaluation of the same expression.  */
+long __RTL (startwith ("ce1"))
+self_update (long x, long a, int c, long *out)
+{
+(function "self_update"
+  (param "x" (DECL_RTL (reg:DI <0>))
+    (DECL_RTL_INCOMING (reg:DI di)))
+  (param "a" (DECL_RTL (reg:DI <1>))
+    (DECL_RTL_INCOMING (reg:DI si)))
+  (param "c" (DECL_RTL (reg:SI <2>))
+    (DECL_RTL_INCOMING (reg:SI dx)))
+  (param "out" (DECL_RTL (reg:DI <3>))
+    (DECL_RTL_INCOMING (reg:DI cx)))
+  (insn-chain
+    (block 2
+      (edge-from entry (flags "FALLTHRU"))
+      (cnote 201 [bb 2] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 202 (set (reg:DI <0>) (reg:DI di)))
+      (cinsn 203 (set (reg:DI <1>) (reg:DI si)))
+      (cinsn 204 (set (reg:SI <2>) (reg:SI dx)))
+      (cinsn 205 (set (reg:DI <3>) (reg:DI cx)))
+      (cinsn 206
+        (parallel [(set (reg:DI <0>) (plus:DI (reg:DI <0>) (const_int 1)))
+                   (clobber (reg:CC flags))]))
+
+      (cinsn 209 (set (reg:CCZ flags)
+                    (compare:CCZ (reg:SI <2>) (const_int 0))))
+      (cjump_insn 210 (set (pc)
+                         (if_then_else (eq (reg:CCZ flags) (const_int 0))
+                                       (label_ref 220) (pc))))
+      (edge-to 3 (flags "FALLTHRU"))
+      (edge-to 4)
+    )
+    (block 3
+      (edge-from 2 (flags "FALLTHRU"))
+      (cnote 211 [bb 3] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 212 (set (reg:DI <0>) (reg:DI <1>)))
+      (cinsn 215
+        (parallel [(set (reg:DI <0>) (plus:DI (reg:DI <0>) (const_int 1)))
+                   (clobber (reg:CC flags))]))
+      (edge-to 4 (flags "FALLTHRU"))
+    )
+    (block 4
+      (edge-from 2)
+      (edge-from 3 (flags "FALLTHRU"))
+      (clabel 220 100200)
+      (cnote 221 [bb 4] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 232 (set (mem:DI (reg:DI <3>) [0 S8 A64]) (reg:DI <0>)))
+      (cinsn 233 (set (mem:DI (plus:DI (reg:DI <3>)
+                                           (const_int 8)) [0 S8 A64])
+                    (reg:DI <1>)))
+
+      (cinsn 234 (set (reg:DI ax) (reg:DI <0>)))
+      (cinsn 235 (use (reg:DI ax)))
+      (edge-to exit (flags "FALLTHRU"))
+    )
+  )
+  (crtl (return_rtx (reg:DI ax)))
+)
+}
+
+int
+main (void)
+{
+  const long v[] = {0, 1, -1, __LONG_MAX__, -__LONG_MAX__ - 1};
+  for (unsigned i = 0; i < sizeof v / sizeof *v; ++i)
+    for (unsigned j = 0; j < sizeof v / sizeof *v; ++j)
+      for (int c = -1; c <= 1; ++c)
+       {
+         long out[2], x = v[i], a = v[j];
+         unsigned long expected = (unsigned long) (c ? a : x) + 1;
+         if ((unsigned long) self_update (x, a, c, out) != expected
+             || (unsigned long) out[0] != expected || out[1] != a)
+           __builtin_abort ();
+       }
+  return 0;
+}
+
+/* { dg-final { scan-rtl-dump-not "noce: derived value needs no conditional 
move" "ce1" } } */
+/* { dg-final { scan-rtl-dump-times "if-conversion succeeded through 
noce_convert_multiple_sets" 1 "ce1" } } */
+
+/* Match each complete function body, so an extra conditional move cannot
+   hide in a wildcard.  Register allocation and unrelated moves may vary.  */
+/*
+** self_update:
+** (?:(?!cmov)[^\n]*\n\t)*cmov[^\n]*
+** (?:(?!cmov)[^\n]*\n\t)*ret
+*/
+
+/* { dg-final { check-function-bodies "**" "" } } */
diff --git a/gcc/testsuite/gcc.dg/rtl/x86_64/ifcvt-derived-8.c 
b/gcc/testsuite/gcc.dg/rtl/x86_64/ifcvt-derived-8.c
new file mode 100644
index 00000000000..8b14a1cb6e6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/rtl/x86_64/ifcvt-derived-8.c
@@ -0,0 +1,100 @@
+/* { dg-do run { target { { i?86-*-* x86_64-*-* } && lp64 } } } */
+/* { dg-skip-if "RTL uses the System V ABI" { *-*-mingw* *-*-cygwin* } } */
+/* { dg-options "-O2 -mtune=generic 
--param=max-rtl-if-conversion-unpredictable-cost=100 
--param=max-rtl-if-conversion-insns=12 -fdump-rtl-ce1 -save-temps" } */
+/* The then arm uses incoming X, while the else arm computes the same
+   expression using its new X.  The values need not be equal.  */
+long __RTL (startwith ("ce1"))
+diamond_unselected_input (int x, int a, int c, int *out)
+{
+(function "diamond_unselected_input"
+  (param "x" (DECL_RTL (reg:SI <0>))
+    (DECL_RTL_INCOMING (reg:SI di)))
+  (param "a" (DECL_RTL (reg:SI <1>))
+    (DECL_RTL_INCOMING (reg:SI si)))
+  (param "c" (DECL_RTL (reg:SI <2>))
+    (DECL_RTL_INCOMING (reg:SI dx)))
+  (param "out" (DECL_RTL (reg:DI <3>))
+    (DECL_RTL_INCOMING (reg:DI cx)))
+  (insn-chain
+    (block 2
+      (edge-from entry (flags "FALLTHRU"))
+      (cnote 501 [bb 2] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 502 (set (reg:SI <0>) (reg:SI di)))
+      (cinsn 503 (set (reg:SI <1>) (reg:SI si)))
+      (cinsn 504 (set (reg:SI <2>) (reg:SI dx)))
+      (cinsn 505 (set (reg:DI <3>) (reg:DI cx)))
+      (cinsn 506 (set (reg:DI <4>) (sign_extend:DI (reg:SI <0>))))
+
+      (cinsn 509 (set (reg:CCZ flags)
+                    (compare:CCZ (reg:SI <2>) (const_int 0))))
+      (cjump_insn 510 (set (pc)
+                         (if_then_else (eq (reg:CCZ flags) (const_int 0))
+                                       (label_ref 520) (pc))))
+      (edge-to 3 (flags "FALLTHRU"))
+      (edge-to 4)
+    )
+    (block 3
+      (edge-from 2 (flags "FALLTHRU"))
+      (cnote 511 [bb 3] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 512 (set (reg:SI <1>) (const_int 9)))
+      (cinsn 515 (set (reg:DI <4>) (sign_extend:DI (reg:SI <0>))))
+      (cjump_insn 518 (set (pc) (label_ref 530)))
+      (edge-to 5)
+    )
+    (block 4
+      (edge-from 2)
+      (clabel 520 100500)
+      (cnote 521 [bb 4] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 522 (set (reg:SI <0>) (const_int 7)))
+      (cinsn 523 (set (reg:DI <4>) (sign_extend:DI (reg:SI <0>))))
+      (edge-to 5 (flags "FALLTHRU"))
+    )
+    (block 5
+      (edge-from 3)
+      (edge-from 4 (flags "FALLTHRU"))
+      (clabel 530 100600)
+      (cnote 531 [bb 5] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 532 (set (mem:SI (reg:DI <3>) [0 S4 A32]) (reg:SI <0>)))
+      (cinsn 533 (set (mem:SI (plus:DI (reg:DI <3>)
+                                           (const_int 8)) [0 S4 A32])
+                    (reg:SI <1>)))
+
+      (cinsn 534 (set (reg:DI ax) (reg:DI <4>)))
+      (cinsn 535 (use (reg:DI ax)))
+      (edge-to exit (flags "FALLTHRU"))
+    )
+  )
+  (crtl (return_rtx (reg:DI ax)))
+)
+}
+
+int
+main (void)
+{
+  const int v[] = {0, 1, -1, 2147483647, -2147483647 - 1};
+  for (unsigned i = 0; i < sizeof v / sizeof *v; ++i)
+    for (unsigned j = 0; j < sizeof v / sizeof *v; ++j)
+      for (int c = -1; c <= 1; ++c)
+       {
+         int x = v[i], a = v[j], out[3];
+         if (diamond_unselected_input (x, a, c, out) != (c ? (long) x : 7)
+             || out[0] != (c ? x : 7) || out[2] != (c ? 9 : a))
+           __builtin_abort ();
+       }
+  return 0;
+}
+
+/* { dg-final { scan-rtl-dump-not "noce: derived value needs no conditional 
move" "ce1" } } */
+/* { dg-final { scan-rtl-dump-times "if-conversion succeeded through 
noce_convert_multiple_sets" 1 "ce1" } } */
+
+/* Match each complete function body, so an extra conditional move cannot
+   hide in a wildcard.  Register allocation and unrelated moves may vary.  */
+/*
+** diamond_unselected_input:
+** (?:(?!cmov)[^\n]*\n\t)*cmov[^\n]*
+** (?:(?!cmov)[^\n]*\n\t)*cmov[^\n]*
+** (?:(?!cmov)[^\n]*\n\t)*cmov[^\n]*
+** (?:(?!cmov)[^\n]*\n\t)*ret
+*/
+
+/* { dg-final { check-function-bodies "**" "" } } */

base-commit: 3af8760505bedeca17283ca9f7daca8e53b50eb9

Reply via email to