On Mon, Sep 7, 2026 at 7:48 AM <[email protected]> wrote:
>
> 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.


I have not looked into the next patches in the series but I think we
should do this slightly different.

Instead of a function pointer.
These should be each a class and we check the class properties.
So like what is done for passes right now.
Plus that puts the NOCE_TF_NONE next to the "sub-pass" rather than all
the way up in a different section of the code.

So something like:
```
struct ifcvt_props
{
  bool need_cmov : 1;
  bool need_no_cond_exec : 1;
};

struct ifcvt_subpass
{
  virtual bool execute (int *) = 0;
  const ifcvt_props props;
  constexpr ifcvt_subpass(const ifcvt_props &prop) : props (prop) {}
};

struct ifcvt_props subpass1_data = {
    true, false
};

struct ifcvt_subpass1 final: ifcvt_subpass
{
  ifcvt_subpass1 () : ifcvt_subpass (subpass1_data) {}
  virtual bool execute (int *) {return false;};
};

ifcvt_subpass **passes;

void init_passes ()
{
  passes = new ifcvt_subpass *[4];
  passes[0] = new ifcvt_subpass1 ();
}
```

Yes this requires some more refactoring but this is more C++ification
than what you have proposed here.

Thanks,
Andrea



>
> 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