Hi,

According to Richard's comment, here are changes in V2:

1. Remove the unused dominance info, DF chains and other dead code such
as merge_blocks, the BB_PARTITION checks and the explicit mode test.
2. Walk the function once and retry a block only after it was converted,
so the loop iterates at most 2 * n_basic_blocks times instead of rescanning
the whole function after every conversion.
3. Accept a ccmp at the test position for dfv input to accept longer chain,
with strict checks on the compare and jump that are converted.
4. Set the combined jcc probability to the product of both original
probabilities and keep its REG_BR_PROB note in sync, add a new test to check.

Bootstrapped && regtested on x86_64-pc-linux-gnu{-m32,}.

For a compound condition with an operand that is not a comparison, like

    struct S { int a; int b; };
    struct S gs;
    int sink;
    void ar (long x, long y) { if (gs.a != 0 && (x - y) > 0) sink++; }

(x - y) > 0 is not a tcc_comparison, so ifcombine keeps the short-circuit
&& as control flow, the ccmp expander never sees the condition, and the
final sequence is like

        movl    gs(%rip), %eax
        testl   %eax, %eax
        je      .L1
        cmpq    %rsi, %rdi ;(x-y) lowered to cmp by x86 specific pattern
        jle     .L1
        addl    $1, sink(%rip)
.L1:
        ret

while the two blocks can be merged into a ccmp chain

        movl    gs(%rip), %eax
        testl   %eax, %eax
        ccmpneq {dfv=sf, of, zf} %rsi, %rdi
        jle     .L1
        addl    $1, sink(%rip)
.L1:
        ret

The same applies to non_trapped memory and component references, which
ifcombine does not fuse either.

Current rtl-ifcvt has cond_exec semantics, but the ccmp cannot fit into
that as cond_exec convert requires no-op when condition is false, while
ccmp still writes FLAGS_REG with the encoded dfv value.

Add an apx_ccmp_convert pass after the nf_convert to convert the missed
scenario, which only happens when
1. APX_CCMP is enabled.
2. then_bb has a single predecessor, both blocks end with a conditional
   jump on a plain compare, and one of the two jumps targets join.
3. The compare that becomes the ccmp is QI/HI/SI/DImode and none of its
   operands may trap, as the ccmp evaluates them unconditionally.

The compare at the test position may itself be a ccmp, so a longer
cascade folds into a single chain. The probability of the combined jcc is
the product of the two original ones.

For micro benchmark the convert improves 50% on cycle if bad predicted,
while for well predicted case nearly no impact. It has negligible impact
on spec26/17.

gcc/ChangeLog:

        * config/i386/i386-features.cc (cmp_insn_p, ccmp_insn_p)
        (cmp_before_jcc, bb_could_join, update_jcc_reg_notes)
        (try_ccmp_convert, ix86_convert_ccmp_bb, ix86_apx_ccmp_convert):
        New functions.
        (class pass_apx_ccmp_convert, make_pass_apx_ccmp_convert): New RTL
        pass.
        (ix86_apx_nf_convert): Remove the timevar push and pop.
        * config/i386/i386-passes.def: Add pass_apx_ccmp_convert after
        pass_apx_nf_convert.
        * config/i386/i386-protos.h (make_pass_apx_ccmp_convert): Declare.
        (ix86_get_flags_cc): Add machine_mode argument.
        * config/i386/i386.cc (ix86_get_flags_cc): Add machine_mode argument
        and use it to select the SF-only encodings of GE and LT.

gcc/testsuite/ChangeLog:

        * gcc.target/i386/apx-ccmp-cvt-1.c: New test.
        * gcc.target/i386/apx-ccmp-cvt-2.c: Likewise.
        * gcc.target/i386/apx-ccmp-cvt-3.c: Likewise.
---
 gcc/config/i386/i386-features.cc              | 361 +++++++++++++++++-
 gcc/config/i386/i386-passes.def               |   1 +
 gcc/config/i386/i386-protos.h                 |   3 +-
 gcc/config/i386/i386.cc                       |  13 +-
 .../gcc.target/i386/apx-ccmp-cvt-1.c          |  47 +++
 .../gcc.target/i386/apx-ccmp-cvt-2.c          |  52 +++
 .../gcc.target/i386/apx-ccmp-cvt-3.c          |  18 +
 7 files changed, 488 insertions(+), 7 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/i386/apx-ccmp-cvt-1.c
 create mode 100644 gcc/testsuite/gcc.target/i386/apx-ccmp-cvt-2.c
 create mode 100644 gcc/testsuite/gcc.target/i386/apx-ccmp-cvt-3.c

diff --git a/gcc/config/i386/i386-features.cc b/gcc/config/i386/i386-features.cc
index f95bc41fb47..9d5c980b1d1 100644
--- a/gcc/config/i386/i386-features.cc
+++ b/gcc/config/i386/i386-features.cc
@@ -28,6 +28,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimple.h"
 #include "cfghooks.h"
 #include "cfgloop.h"
+#include "cfgcleanup.h"
+#include "cfganal.h"
 #include "df.h"
 #include "tm_p.h"
 #include "stringpool.h"
@@ -5365,8 +5367,6 @@ make_pass_x86_cse (gcc::context *ctxt)
 static unsigned int
 ix86_apx_nf_convert (void)
 {
-  timevar_push (TV_MACH_DEP);
-
   basic_block bb;
   rtx_insn *insn;
   hash_map <rtx_insn *, rtx> converting_map;
@@ -5471,7 +5471,6 @@ ix86_apx_nf_convert (void)
        }
     }
 
-  timevar_pop (TV_MACH_DEP);
   return 0;
 }
 
@@ -5520,6 +5519,362 @@ make_pass_apx_nf_convert (gcc::context *ctxt)
   return new pass_apx_nf_convert (ctxt);
 }
 
+/* Check whether INSN is a plain compare.  */
+static bool
+cmp_insn_p (rtx_insn *insn)
+{
+  if (!NONJUMP_INSN_P (insn) || GET_CODE (PATTERN (insn)) != SET)
+    return false;
+
+  rtx set = PATTERN (insn);
+  rtx dest = SET_DEST (set);
+  rtx src = SET_SRC (set);
+
+  return (REG_P (dest)
+         && REGNO (dest) == FLAGS_REG
+         && GET_CODE (src) == COMPARE
+         && general_operand (XEXP (src, 0), VOIDmode)
+         && general_operand (XEXP (src, 1), VOIDmode));
+}
+
+/* Check whether an insn is a ccmp.  */
+static bool
+ccmp_insn_p (rtx_insn *insn)
+{
+  rtx set = single_set (insn);
+  if (set)
+    {
+      rtx dest = SET_DEST (set);
+      rtx src = SET_SRC (set);
+      if (REG_P (dest)
+         && REGNO (dest) == FLAGS_REG
+         && GET_CODE (src) == IF_THEN_ELSE)
+       {
+         rtx dfv = XEXP (src, 2);
+         if (GET_CODE (dfv) == UNSPEC
+             && XINT (dfv, 1) == UNSPEC_APX_DFV)
+           return true;
+       }
+    }
+  return false;
+}
+
+/* If BB ends with a compare and a conditional jump to a label reading its
+   flags, return that compare, otherwise NULL.  With ALLOW_CCMP the compare
+   may also be a ccmp, so that a converted block can head a longer chain.  */
+static rtx_insn *
+cmp_before_jcc (basic_block bb, bool allow_ccmp = false)
+{
+  rtx_insn *jcc = BB_END (bb);
+
+  if (!JUMP_P (jcc)
+      || !any_condjump_p (jcc)
+      || !JUMP_LABEL (jcc)
+      || !LABEL_P (JUMP_LABEL (jcc)))
+    return NULL;
+
+  rtx_insn *cmp = prev_nonnote_nondebug_insn_bb (jcc);
+  if (cmp && (cmp_insn_p (cmp) || (allow_ccmp && ccmp_insn_p (cmp))))
+    return cmp;
+
+  return NULL;
+}
+
+/* If BB1 has two successors and one of them is BB2, return the other one,
+   otherwise NULL.  */
+static basic_block
+bb_could_join (basic_block bb1, basic_block bb2)
+{
+  if (EDGE_COUNT (bb1->succs) != 2)
+    return NULL;
+  if (EDGE_SUCC (bb1, 0)->dest == bb2)
+    return EDGE_SUCC (bb1, 1)->dest;
+  if (EDGE_SUCC (bb1, 1)->dest == bb2)
+    return EDGE_SUCC (bb1, 0)->dest;
+  return NULL;
+}
+
+/* Replace the flags notes on JCC, which no longer describe the compare it
+   reads, by a REG_DEAD note for FLAGS_REG in CC_MODE.  */
+static void
+update_jcc_reg_notes (rtx_insn *jcc, machine_mode cc_mode)
+{
+  rtx note, next;
+
+  for (note = REG_NOTES (jcc); note; note = next)
+    {
+      next = XEXP (note, 1);
+      if ((REG_NOTE_KIND (note) == REG_DEAD
+          || REG_NOTE_KIND (note) == REG_UNUSED)
+         && REG_P (XEXP (note, 0))
+         && REGNO (XEXP (note, 0)) == FLAGS_REG)
+       remove_note (jcc, note);
+    }
+
+  add_reg_note (jcc, REG_DEAD, gen_rtx_REG (cc_mode, FLAGS_REG));
+}
+
+/* Convert the found candidate for ccmp.  Return the ccmp sequence to be
+   emitted on success, otherwise NULL.  */
+static rtx_insn *
+try_ccmp_convert (rtx_insn *cand, rtx_insn *jcc_prev, rtx_insn *jcc_curr,
+                 basic_block join_bb)
+{
+  rtx_code prev_code, dfv_code2;
+  rtx target, prev_dfv, curr_cmp, new_dfv, pat, old_label, new_label;
+  machine_mode op_mode, dfv_mode, prev_mode;
+  int dfv;
+
+  old_label = JUMP_LABEL (jcc_prev);
+  new_label = JUMP_LABEL (jcc_curr);
+  prev_dfv = XEXP (SET_SRC (PATTERN (jcc_prev)), 0);
+  prev_code = GET_CODE (prev_dfv);
+  prev_mode = GET_MODE (XEXP (prev_dfv, 0));
+  curr_cmp = XEXP (SET_SRC (PATTERN (jcc_curr)), 0);
+  dfv_code2 = GET_CODE (curr_cmp);
+  dfv_mode = GET_MODE (XEXP (curr_cmp, 0));
+  target = gen_rtx_REG (CCmode, FLAGS_REG);
+
+  /* CAND was verified to be a plain compare by the caller.  */
+  rtx compare = SET_SRC (PATTERN (cand));
+  rtx op0 = XEXP (compare, 0);
+  rtx op1 = XEXP (compare, 1);
+
+  op_mode = GET_MODE (op0);
+
+  /* The ccmp evaluates its operands unconditionally, so it must not pick up
+     an operand that may trap.  */
+  if (may_trap_p (op0) || may_trap_p (op1))
+    return NULL;
+
+  /* An FP cmp1 needs its condition code rewritten into an integer flag
+     for the dfv value, which is only valid under fast-math.  */
+  if (prev_mode == CCFPmode)
+    {
+      if (flag_unsafe_math_optimizations)
+       prev_code = ix86_fp_compare_code_to_integer (prev_code);
+      else
+       return NULL;
+    }
+
+  /* The default flags value below is only correct if one of the jumps goes
+     to join_bb.  */
+  bool first_jump_to_join = BLOCK_FOR_INSN (old_label) == join_bb;
+  bool second_jump_to_join = BLOCK_FOR_INSN (new_label) == join_bb;
+  if (!first_jump_to_join && !second_jump_to_join)
+    return NULL;
+
+  if (first_jump_to_join)
+    prev_code = reverse_condition (prev_code);
+
+  dfv_code2 = second_jump_to_join ? dfv_code2 : reverse_condition (dfv_code2);
+  dfv = ix86_get_flags_cc (dfv_code2, dfv_mode);
+  if (dfv < 0)
+    return NULL;
+
+  /* Use the flags in the mode that cmp1 sets instead of direct CCmode.  */
+  prev_dfv = gen_rtx_fmt_ee (prev_code, VOIDmode,
+                            gen_rtx_REG (prev_mode, FLAGS_REG), const0_rtx);
+
+  start_sequence ();
+  new_dfv = GEN_INT (dfv);
+  /* maybe_gen_ccmp returns NULL for a mode without a ccmp pattern.  */
+  pat = maybe_gen_ccmp (op_mode, target, prev_dfv, op0, op1, new_dfv);
+  if (!pat)
+    {
+      end_sequence ();
+      return NULL;
+    }
+  rtx_insn *ccmp_new = emit_insn (pat);
+  if (recog_memoized (ccmp_new) < 0)
+    {
+      end_sequence ();
+      return NULL;
+    }
+
+  /* Retarget jcc_prev to jcc_curr's condition and label.  */
+  rtx new_jcc = copy_rtx (PATTERN (jcc_curr));
+  machine_mode cc_mode = GET_MODE (XEXP (XEXP (SET_SRC (new_jcc), 0), 0));
+  if (!validate_change (jcc_prev, &PATTERN (jcc_prev), new_jcc, 0))
+    {
+      end_sequence ();
+      return NULL;
+    }
+  update_jcc_reg_notes (jcc_prev, cc_mode);
+  if (old_label != new_label)
+    {
+      if (--LABEL_NUSES (old_label) <= 0)
+       delete_insn (as_a<rtx_insn *> (old_label));
+      ++LABEL_NUSES (new_label);
+    }
+  JUMP_LABEL (jcc_prev) = new_label;
+
+  rtx_insn *seq = get_insns ();
+  set_used_flags (pat);
+  unshare_all_rtl_in_chain (seq);
+  end_sequence ();
+
+  return seq;
+}
+
+/* Find the ccmp convertable diamond headed by TEST_BB, i.e. a test block and
+   its single-predecessor successor that both end with a compare and a
+   conditional jump, one of the jumps going to the block where they join
+
+     test_bb: cmp1; jcc1  ->  then_bb: cmp2; jcc2  ->  join_bb
+     test_bb: cmp1; jcc1  ->  join_bb
+
+   cmp2 is turned into a ccmp predicated on the flags of cmp1, and the
+   condition and the target of jcc2 are moved to jcc1, so that then_bb and its
+   branch go away
+
+     test_bb: cmp1; ccmp2; jcc
+
+   Return true if the conversion happened.  */
+static bool
+ix86_convert_ccmp_bb (basic_block test_bb)
+{
+  edge then_edge, else_edge;
+  basic_block then_bb, join_bb, new_then_bb;
+  bool then_else_reversed = false;
+
+  rtx_insn *test_cmp = cmp_before_jcc (test_bb, /*allow_ccmp=*/true);
+  if (!test_cmp || EDGE_COUNT (test_bb->succs) != 2)
+    return false;
+
+  then_edge = EDGE_SUCC (test_bb, 0);
+  else_edge = EDGE_SUCC (test_bb, 1);
+
+  /* Look for the diamond on either arm of test_bb.  */
+  if (single_pred_p (then_edge->dest)
+      && (new_then_bb = bb_could_join (then_edge->dest, else_edge->dest)))
+    {
+      then_bb = then_edge->dest;
+      join_bb = else_edge->dest;
+    }
+  else if (single_pred_p (else_edge->dest)
+          && (new_then_bb = bb_could_join (else_edge->dest, then_edge->dest)))
+    {
+      then_bb = else_edge->dest;
+      join_bb = then_edge->dest;
+      then_else_reversed = true;
+    }
+  else
+    return false;
+
+  edge then_to_new_then = find_edge (then_bb, new_then_bb);
+  if (then_to_new_then->flags & EDGE_COMPLEX)
+    return false;
+
+  /* then_bb has to consist of a plain cmp and its jcc only.  */
+  rtx_insn *cmp_cand = cmp_before_jcc (then_bb);
+  if (!cmp_cand || prev_nonnote_nondebug_insn_bb (cmp_cand))
+    return false;
+
+  rtx_insn *jcc_test = BB_END (test_bb);
+  rtx_insn *jcc_then = BB_END (then_bb);
+  rtx_insn *seq = try_ccmp_convert (cmp_cand, jcc_test, jcc_then, join_bb);
+  if (!seq)
+    return false;
+
+  emit_insn_before (seq, jcc_test);
+  df_set_bb_dirty (test_bb);
+
+  /* The edge that used to lead to then_bb now carries the conjunction of
+     both compares, so its probability is the product of the two.  */
+  edge conv_edge = then_else_reversed ? else_edge : then_edge;
+  edge join_edge = then_else_reversed ? then_edge : else_edge;
+  profile_probability prob
+    = conv_edge->probability * then_to_new_then->probability;
+
+  redirect_edge_succ (conv_edge, new_then_bb);
+  conv_edge->probability = prob;
+  join_edge->probability = prob.invert ();
+
+  /* Delete then_bb along with its remaining edges.  */
+  delete_basic_block (then_bb);
+
+  /* redirect_edge_succ leaves the edge flags alone, so recompute
+     EDGE_FALLTHRU from the new jump target.  */
+  basic_block jump_bb = BLOCK_FOR_INSN (JUMP_LABEL (jcc_test));
+  edge e;
+  edge_iterator ei;
+  FOR_EACH_EDGE (e, ei, test_bb->succs)
+    if (e->dest == jump_bb)
+      e->flags &= ~EDGE_FALLTHRU;
+    else
+      e->flags |= EDGE_FALLTHRU;
+
+  /* BRANCH_EDGE depends on the fallthru flags fixed above, so only sync the
+     note now.  */
+  update_br_prob_note (test_bb);
+
+  return true;
+}
+
+/* Driver to convert all ccmp candidates in the current function.  Return the
+   number of conversions done.  */
+static int
+ix86_apx_ccmp_convert (void)
+{
+  basic_block bb;
+  int num_block_updated = 0;
+
+  /* Retry each converted block: its new ccmp can head a longer chain.  Each
+     success deletes a block and each failure ends the retries, so there are
+     at most 2 * n_basic_blocks calls.  */
+  FOR_EACH_BB_FN (bb, cfun)
+    while (ix86_convert_ccmp_bb (bb))
+      num_block_updated++;
+
+  return num_block_updated;
+}
+
+namespace {
+
+const pass_data pass_data_apx_ccmp_convert =
+{
+  RTL_PASS, /* type */
+  "apx_ccmpcvt", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_MACH_DEP, /* tv_id */
+  0, /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  TODO_df_finish, /* todo_flags_finish */
+};
+
+class pass_apx_ccmp_convert : public rtl_opt_pass
+{
+public:
+  pass_apx_ccmp_convert (gcc::context *ctxt)
+    : rtl_opt_pass (pass_data_apx_ccmp_convert, ctxt)
+  {}
+
+  bool gate (function *) final override
+    {
+      return (TARGET_APX_CCMP
+             && optimize
+             && optimize_function_for_speed_p (cfun));
+    }
+
+  unsigned int execute (function *) final override
+    {
+      if (ix86_apx_ccmp_convert () > 0)
+       cleanup_cfg (0);
+      return 0;
+    }
+}; // class pass_apx_ccmp_convert
+
+} // anon namespace
+
+rtl_opt_pass *
+make_pass_apx_ccmp_convert (gcc::context *ctxt)
+{
+  return new pass_apx_ccmp_convert (ctxt);
+}
+
 /* When a hot loop can be fit into one cacheline,
    force align the loop without considering the max skip.  */
 static void
diff --git a/gcc/config/i386/i386-passes.def b/gcc/config/i386/i386-passes.def
index 4f461116cac..76d9b603117 100644
--- a/gcc/config/i386/i386-passes.def
+++ b/gcc/config/i386/i386-passes.def
@@ -37,4 +37,5 @@ along with GCC; see the file COPYING3.  If not see
 
   INSERT_PASS_AFTER (pass_late_combine, 1, pass_x86_cse);
   INSERT_PASS_AFTER (pass_late_combine, 1, pass_remove_partial_avx_dependency);
+  INSERT_PASS_AFTER (pass_rtl_ifcvt, 1, pass_apx_ccmp_convert);
   INSERT_PASS_AFTER (pass_rtl_ifcvt, 1, pass_apx_nf_convert);
diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h
index 4e56bf70c8d..6539563faf0 100644
--- a/gcc/config/i386/i386-protos.h
+++ b/gcc/config/i386/i386-protos.h
@@ -254,7 +254,7 @@ extern rtx ix86_gen_ccmp_first (rtx_insn **, rtx_insn **, 
enum rtx_code,
                                tree, tree);
 extern rtx ix86_gen_ccmp_next (rtx_insn **, rtx_insn **, rtx,
                               enum rtx_code, tree, tree, enum rtx_code);
-extern int ix86_get_flags_cc (enum rtx_code);
+extern int ix86_get_flags_cc (enum rtx_code, machine_mode = CCmode);
 extern void ix86_expand_trunc_with_avx2_noavx512f (rtx, rtx, machine_mode);
 extern rtx ix86_memtag_untagged_pointer (rtx, rtx);
 extern bool ix86_memtag_can_tag_addresses (void);
@@ -433,6 +433,7 @@ extern rtl_opt_pass *make_pass_remove_partial_avx_dependency
   (gcc::context *);
 extern rtl_opt_pass *make_pass_x86_cse (gcc::context *);
 extern rtl_opt_pass *make_pass_apx_nf_convert (gcc::context *);
+extern rtl_opt_pass *make_pass_apx_ccmp_convert (gcc::context *);
 extern rtl_opt_pass *make_pass_align_tight_loops (gcc::context *);
 
 extern bool ix86_has_no_direct_extern_access;
diff --git a/gcc/config/i386/i386.cc b/gcc/config/i386/i386.cc
index b09a5077820..efa08b1e0ee 100644
--- a/gcc/config/i386/i386.cc
+++ b/gcc/config/i386/i386.cc
@@ -17012,16 +17012,23 @@ ix86_convert_const_vector_to_integer (rtx op, 
machine_mode mode)
   return val.to_shwi ();
 }
 
-int ix86_get_flags_cc (rtx_code code)
+/* Return the APX DFV encoding of the condition CODE tested on flags in MODE,
+   or -1 if there is no encoding for CODE.  GE and LT follow
+   put_condition_code, where they become sign flag tests in CCNOmode and
+   CCGOCmode.  */
+
+int ix86_get_flags_cc (rtx_code code, machine_mode mode)
 {
   switch (code)
     {
       case NE: return X86_CCNE;
       case EQ: return X86_CCE;
-      case GE: return X86_CCNL;
+      case GE: return (mode == CCGOCmode || mode == CCNOmode
+                      ? X86_CCNS : X86_CCNL);
+      case LT: return (mode == CCGOCmode || mode == CCNOmode
+                      ? X86_CCS : X86_CCL);
       case GT: return X86_CCNLE;
       case LE: return X86_CCLE;
-      case LT: return X86_CCL;
       case GEU: return X86_CCNB;
       case GTU: return X86_CCNBE;
       case LEU: return X86_CCBE;
diff --git a/gcc/testsuite/gcc.target/i386/apx-ccmp-cvt-1.c 
b/gcc/testsuite/gcc.target/i386/apx-ccmp-cvt-1.c
new file mode 100644
index 00000000000..c6837ecd23a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/apx-ccmp-cvt-1.c
@@ -0,0 +1,47 @@
+/* { dg-do compile { target { ! ia32 } } } */
+/* { dg-options "-O2 -mapx-features=ccmp" } */
+
+struct S { int a; int b; long long c; short s; char cb; unsigned u; };
+struct S gs;
+struct T { int a, b, d, e; };
+struct T gt;
+float gfa;
+int sink;
+
+/* AND, with the first condition selecting the DFV predicate.  */
+void a_ne  (void) { if (gs.a != 0 && gs.b != 0) sink++; }   /* ccmpne */
+void a_lt  (void) { if (gs.a <  5 && gs.b != 0) sink++; }   /* ccmple */
+void a_gt  (void) { if (gs.a >  5 && gs.b != 0) sink++; }   /* ccmpg  */
+void a_ltu (void) { if (gs.u < 5u && gs.b != 0) sink++; }   /* ccmpbe */
+void a_gtu (void) { if (gs.u > 5u && gs.b != 0) sink++; }   /* ccmpa  */
+
+/* Second compare in DImode, HImode and QImode.  */
+void m_di (void) { if (gs.a != 0 && gs.c != 0) sink++; }    /* ccmpne...q */
+void m_hi (void) { if (gs.a != 0 && gs.s != 0) sink++; }    /* ccmpne...w */
+void m_qi (void) { if (gs.a != 0 && gs.cb != 0) sink++; }   /* ccmpne...b */
+
+/* OR, i.e. then and else swapped.  The operands differ in field and mode so
+   that the two compares are not merged elsewhere.  */
+void o_1 (void) { if (gs.a < 5 || gs.c > 9) sink++; }       /* ccmpg...q */
+void o_2 (void) { if (gs.a > 5 || gs.s < 0) sink++; }       /* ccmple...w */
+
+/* The second compare tests an arithmetic result.  */
+void ar (long x, long y) { if (gs.a != 0 && (x - y) > 0) sink++; }
+
+/* Longer cascades fold into a single chain, with arithmetic operands that
+   ifcombine leaves as control flow as well as with plain compares.  */
+void ar3 (long x, long y, long z, long w)
+{ if (gs.a != 0 && (x - y) > 0 && (z - w) < 0) sink++; }
+void casc (void) { if (gt.a && gt.b && gt.d && gt.e) sink++; }
+
+/* An FP first compare only works with -funsafe-math-optimizations, where its
+   condition code can be turned into an integer DFV predicate.  */
+__attribute__((optimize("fast-math")))
+void fp_first (void) { if (gfa > 1.0f && gs.b != 0) sink++; }
+
+/* { dg-final { scan-assembler-times "\tccmp" 17 } } */
+/* { dg-final { scan-assembler-times "ccmpne" 9 } } */
+/* { dg-final { scan-assembler-times "ccmpg" 3 } } */
+/* { dg-final { scan-assembler-times "ccmple" 2 } } */
+/* { dg-final { scan-assembler-times "ccmpbe" 1 } } */
+/* { dg-final { scan-assembler-times "ccmpa" 2 } } */
diff --git a/gcc/testsuite/gcc.target/i386/apx-ccmp-cvt-2.c 
b/gcc/testsuite/gcc.target/i386/apx-ccmp-cvt-2.c
new file mode 100644
index 00000000000..4f81a67a5b7
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/apx-ccmp-cvt-2.c
@@ -0,0 +1,52 @@
+/* { dg-do run { target { ! ia32 } } } */
+/* { dg-require-effective-target apxf } */
+/* { dg-options "-O2 -mapxf" } */
+
+/* Check that the conversion preserves semantics for the shapes it handles.  */
+
+struct S { int a; int b; long long c; short s; unsigned char cb; unsigned u; };
+struct S gs;
+struct T { int a, b, d, e; };
+struct T gt;
+int sink;
+
+__attribute__((noinline)) void a_ne  (void) { if (gs.a != 0 && gs.b != 0) 
sink++; }
+__attribute__((noinline)) void a_lt  (void) { if (gs.a <  5 && gs.b != 0) 
sink++; }
+__attribute__((noinline)) void a_gtu (void) { if (gs.u > 5u && gs.b != 0) 
sink++; }
+__attribute__((noinline)) void m_di  (void) { if (gs.a != 0 && gs.c != 0) 
sink++; }
+__attribute__((noinline)) void m_hi  (void) { if (gs.a != 0 && gs.s != 0) 
sink++; }
+__attribute__((noinline)) void m_qi  (void) { if (gs.a != 0 && gs.cb != 0) 
sink++; }
+__attribute__((noinline)) void o_1   (void) { if (gs.a <  5 || gs.c > 9) 
sink++; }
+__attribute__((noinline)) void ar (long x, long y) { if (gs.a != 0 && (x - y) 
> 0) sink++; }
+__attribute__((noinline)) void ar3 (long x, long y, long z, long w)
+{ if (gs.a != 0 && (x - y) > 0 && (z - w) < 0) sink++; }
+__attribute__((noinline)) void casc  (void) { if (gt.a && gt.b && gt.d && 
gt.e) sink++; }
+
+int main (void)
+{
+  if (!__builtin_cpu_supports ("apxf"))
+    return 0;
+
+  for (int a = -1; a <= 6; a++)
+    for (int b = -1; b <= 1; b++)
+      {
+       gs.a = a; gs.b = b; gs.c = b; gs.s = b; gs.cb = (unsigned char) b;
+       gs.u = (unsigned) a;
+       gt.a = a; gt.b = b; gt.d = a; gt.e = b;
+
+       sink = 0; a_ne ();  if (sink != (a != 0 && b != 0)) __builtin_abort ();
+       sink = 0; a_lt ();  if (sink != (a <  5 && b != 0)) __builtin_abort ();
+       sink = 0; a_gtu (); if (sink != ((unsigned) a > 5u && b != 0)) 
__builtin_abort ();
+       sink = 0; m_di ();  if (sink != (a != 0 && (long long) b != 0)) 
__builtin_abort ();
+       sink = 0; m_hi ();  if (sink != (a != 0 && (short) b != 0)) 
__builtin_abort ();
+       sink = 0; m_qi ();  if (sink != (a != 0 && (unsigned char) b != 0)) 
__builtin_abort ();
+       sink = 0; o_1 ();   if (sink != (a < 5 || (long long) b > 9)) 
__builtin_abort ();
+       sink = 0; ar (a, b);if (sink != (a != 0 && (a - b) > 0)) 
__builtin_abort ();
+       sink = 0; ar3 (a, b, b, a);
+                           if (sink != (a != 0 && (a - b) > 0 && (b - a) < 0))
+                             __builtin_abort ();
+       sink = 0; casc ();  if (sink != (a != 0 && b != 0)) __builtin_abort ();
+      }
+
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/i386/apx-ccmp-cvt-3.c 
b/gcc/testsuite/gcc.target/i386/apx-ccmp-cvt-3.c
new file mode 100644
index 00000000000..1c38119dd36
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/apx-ccmp-cvt-3.c
@@ -0,0 +1,18 @@
+/* { dg-do compile { target { ! ia32 } } } */
+/* { dg-options "-O2 -mapx-features=ccmp -fdump-rtl-reginfo-blocks-details" } 
*/
+
+/* The combined jcc takes the product of both probabilities, 0.9 * 0.9.  */
+
+int s;
+
+void
+f (int a, int b, int c, int d)
+{
+  if (__builtin_expect ((a - b) > 0, 1) && __builtin_expect ((c - d) > 0, 1))
+    s++;
+}
+
+/* { dg-final { scan-assembler-times "\tccmp" 1 } } */
+/* { dg-final { scan-rtl-dump-times "81.0%" 2 "reginfo" } } */
+/* { dg-final { scan-rtl-dump-times "19.0%" 2 "reginfo" } } */
+/* { dg-final { scan-rtl-dump-times "REG_BR_PROB" 1 "reginfo" } } */
-- 
2.31.1

Reply via email to