From: Kyrylo Tkachov <[email protected]>

noce_process_if_block chooses among the branchless noce_try_* matchers
with a hand-written ladder of "if (noce_try_foo (if_info)) goto success;"
statements, in which the target-capability gating (conditional execution,
conditional move) is interleaved with the control flow.  Adding or
reordering a transform means editing that control flow.

Describe the transforms in an ordered table instead.  Each entry records
the matcher and the target capabilities it needs (NOCE_TF_NO_COND_EXEC,
NOCE_TF_NEED_COND_MOVE).  A single loop walks the table, skips entries
whose requirements the target does not meet, and stops at the first match.
The order and gating are preserved exactly, so this is no functional
change.  A new branchless transform is now added by inserting a row.

gcc/ChangeLog:

        * ifcvt.cc (enum noce_transform_flags): New.
        (struct noce_transform): New.
        (noce_transforms): New static table.
        (noce_process_if_block): Replace the open-coded dispatch ladder with
        a loop over noce_transforms.

Signed-off-by: Kyrylo Tkachov <[email protected]>
---
 gcc/ifcvt.cc | 95 ++++++++++++++++++++++++++++++----------------------
 1 file changed, 55 insertions(+), 40 deletions(-)

diff --git a/gcc/ifcvt.cc b/gcc/ifcvt.cc
index b5a1ccab9a6..0e7a6ab4e82 100644
--- a/gcc/ifcvt.cc
+++ b/gcc/ifcvt.cc
@@ -4871,6 +4871,52 @@ average_cost (unsigned then_cost, unsigned else_cost, 
edge e)
     + e->probability.apply ((gcov_type) then_cost - else_cost);
 }
 
+/* Target-capability requirements for a noce transform.  The dispatcher in
+   noce_process_if_block skips a transform whose requirements are not met by
+   the current target, so the transforms themselves need not re-check.  */
+enum noce_transform_flags
+{
+  NOCE_TF_NONE = 0,
+  /* Only attempt the transform when the target does not offer conditional
+     execution.  On such targets the predicated cond_exec path handles these
+     cases instead.  */
+  NOCE_TF_NO_COND_EXEC = 1 << 0,
+  /* The transform emits a conditional move and so needs the movcc optab.  */
+  NOCE_TF_NEED_COND_MOVE = 1 << 1,
+  /* Both of the above, which is what the arithmetic transforms need.  */
+  NOCE_TF_CMOVE_NO_COND_EXEC = NOCE_TF_NO_COND_EXEC | NOCE_TF_NEED_COND_MOVE
+};
+
+/* One entry in the ordered noce transform dispatch table: the matcher to
+   attempt and the target capabilities it requires.  Attempted in order;
+   the first that returns true wins.  New branchless transforms are added
+   by inserting a row here rather than by editing control flow.  */
+struct noce_transform
+{
+  bool (*fn) (noce_if_info *);
+  unsigned flags;
+};
+
+static const noce_transform noce_transforms[] = {
+  { noce_try_move,                     NOCE_TF_NONE },
+  { noce_try_ifelse_collapse,          NOCE_TF_NONE },
+  { noce_try_store_flag,               NOCE_TF_NONE },
+  { noce_try_bitop,                    NOCE_TF_NONE },
+  { noce_try_minmax,                   NOCE_TF_NONE },
+  { noce_try_abs,                      NOCE_TF_NONE },
+  { noce_try_inverse_constants,                NOCE_TF_NONE },
+  { noce_try_store_flag_constants,     NOCE_TF_NO_COND_EXEC },
+  { noce_try_shifted_store_flag,       NOCE_TF_NO_COND_EXEC },
+  { noce_try_sign_bit_splat,           NOCE_TF_NONE },
+  { noce_try_store_flag_logical,       NOCE_TF_NO_COND_EXEC },
+  { noce_try_cmove,                    NOCE_TF_NEED_COND_MOVE },
+  { noce_try_addcc,                    NOCE_TF_NO_COND_EXEC },
+  { noce_try_store_flag_mask,          NOCE_TF_NO_COND_EXEC },
+  { noce_try_cond_arith,               NOCE_TF_CMOVE_NO_COND_EXEC },
+  { noce_try_cmove_arith,              NOCE_TF_CMOVE_NO_COND_EXEC },
+  { noce_try_sign_mask,                        NOCE_TF_NO_COND_EXEC }
+};
+
 /* Given a simple IF-THEN-JOIN or IF-THEN-ELSE-JOIN block, attempt to convert
    it without using conditional execution.  Return TRUE if we were successful
    at converting the block.  */
@@ -5145,47 +5191,16 @@ noce_process_if_block (struct noce_if_info *if_info)
        which do a good enough job these days.  */
     return false;
 
-  if (noce_try_move (if_info))
-    goto success;
-  if (noce_try_ifelse_collapse (if_info))
-    goto success;
-  if (noce_try_store_flag (if_info))
-    goto success;
-  if (noce_try_bitop (if_info))
-    goto success;
-  if (noce_try_minmax (if_info))
-    goto success;
-  if (noce_try_abs (if_info))
-    goto success;
-  if (noce_try_inverse_constants (if_info))
-    goto success;
-  if (!targetm.have_conditional_execution ()
-      && noce_try_store_flag_constants (if_info))
-    goto success;
-  if (!targetm.have_conditional_execution ()
-      && noce_try_shifted_store_flag (if_info))
-    goto success;
-  if (noce_try_sign_bit_splat (if_info))
-    goto success;
-  if (!targetm.have_conditional_execution ()
-      && noce_try_store_flag_logical (if_info))
-    goto success;
-  if (HAVE_conditional_move
-      && noce_try_cmove (if_info))
-    goto success;
-  if (! targetm.have_conditional_execution ())
+  /* Try the branchless transforms in table order and stop at the first one
+     that succeeds.  */
+  for (const noce_transform &t : noce_transforms)
     {
-      if (noce_try_addcc (if_info))
-       goto success;
-      if (noce_try_store_flag_mask (if_info))
-       goto success;
-      if (HAVE_conditional_move
-          && noce_try_cond_arith (if_info))
-       goto success;
-      if (HAVE_conditional_move
-         && noce_try_cmove_arith (if_info))
-       goto success;
-      if (noce_try_sign_mask (if_info))
+      if ((t.flags & NOCE_TF_NO_COND_EXEC)
+         && targetm.have_conditional_execution ())
+       continue;
+      if ((t.flags & NOCE_TF_NEED_COND_MOVE) && !HAVE_conditional_move)
+       continue;
+      if (t.fn (if_info))
        goto success;
     }
 
-- 
2.50.1 (Apple Git-155)

Reply via email to