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