Ping on the series.

https://gcc.gnu.org/pipermail/gcc-patches/2026-September/730026.html

Thanks,
Kyrill


> On 4 Sep 2026, at 14:35, Kyrylo Tkachov <[email protected]> wrote:
> 
> From: Kyrylo Tkachov <[email protected]>
> 
> The conditional-compare expander accepts a plain boolean as a comparison
> against zero.  This is useful when a chain also contains an explicit
> comparison.  It is harmful when every leaf is a boolean.  For example:
> 
>  _Bool
>  f (_Bool x, _Bool y)
>  {
>    return x || y;
>  }
> 
> AArch64 emitted:
> 
> and w1, w1, 255
> tst w0, 255
> ccmp w1, 0, 0, eq
> cset w0, ne
> 
> Make candidate recognition report whether it found an explicit comparison.
> It already validates every statement before it follows the next definition.
> Reject a candidate without an explicit comparison.
> 
> TER fallback starts a new conditional-compare recognition call for each
> forwarded suffix.  Cache accepted all-boolean subchains in the existing
> per-function ssaexpand state so that these calls inspect a linear chain in
> linear time.  The state is shared by cfgexpand and expression expansion and
> is released when RTL expansion finishes.
> 
> The example then emits:
> 
> orr w0, w0, w1
> 
> Tests cover all-boolean and mixed chains, a non-binary boolean definition,
> and a long rejected chain.
> 
> Changes since v1:
> - Report explicit comparisons during candidate recognition.
> - Cache all-boolean subchains so TER fallback stays linear.
> - Add non-binary definition and long-chain tests.
> 
> gcc/
> 
> PR middle-end/109832
> * ccmp.cc (ccmp_tree_comparison_p): Report whether an accepted leaf
> is a comparison.
> (ccmp_candidate_p): Propagate the result and cache all-boolean
> candidates.
> (expand_ccmp_expr): Reject an all-boolean candidate.
> * tree-outof-ssa.h (ssaexpand::ccmp_no_comparison): New member.
> * tree-outof-ssa.cc (finish_out_of_ssa): Free it.
> 
> gcc/testsuite/
> 
> PR middle-end/109832
> * gcc.target/aarch64/ccmp_7.c: New test.
> * gcc.target/aarch64/ccmp_9.c: Likewise.
> * gcc.target/aarch64/ccmp_11.c: Likewise.
> * gcc.target/aarch64/ccmp_13.c: Likewise.
> 
> Signed-off-by: Kyrylo Tkachov <[email protected]>
> 
> ---
> diff --git a/gcc/ccmp.cc b/gcc/ccmp.cc
> index 0564ccf34bf..c30345f99d5 100644
> --- a/gcc/ccmp.cc
> +++ b/gcc/ccmp.cc
> @@ -38,14 +38,18 @@ along with GCC; see the file COPYING3.  If not see
> #include "ccmp.h"
> #include "predict.h"
> 
> -/* Check whether T is a simple boolean variable or a SSA name
> -   set by a comparison operator in the same basic block.  */
> +/* Check whether T is a simple boolean variable or an SSA name set by a
> +   comparison operator in BB.  Set *HAS_COMPARISON if T is the latter.  */
> static bool
> -ccmp_tree_comparison_p (tree t, basic_block bb)
> +ccmp_tree_comparison_p (tree t, basic_block bb,
> + bool *has_comparison = nullptr)
> {
>   gimple *g = get_gimple_for_ssa_name (t);
>   tree_code tcode;
> 
> +  if (has_comparison)
> +    *has_comparison = false;
> +
>   /* If we have a boolean variable allow it and generate a compare
>      to zero reg when expanding.  */
>   if (!g)
> @@ -58,7 +62,12 @@ ccmp_tree_comparison_p (tree t, basic_block bb)
>   if (bb != gimple_bb (g))
>     return false;
>   tcode = gimple_assign_rhs_code (g);
> -  return TREE_CODE_CLASS (tcode) == tcc_comparison;
> +  if (TREE_CODE_CLASS (tcode) != tcc_comparison)
> +    return false;
> +
> +  if (has_comparison)
> +    *has_comparison = true;
> +  return true;
> }
> 
> /* The following functions expand conditional compare (CCMP) instructions.
> @@ -90,16 +99,19 @@ ccmp_tree_comparison_p (tree t, basic_block bb)
>    If all checks OK in expand_ccmp_expr, it emits insns in prep_seq, then
>    insns in gen_seq.  */
> 
> -/* Check whether G is a potential conditional compare candidate; OUTER is 
> true if
> -   G is the outer most AND/IOR.  */
> +/* Return true if G is a potential conditional compare candidate.  Set
> +   *HAS_COMPARISON if it contains a comparison that will be folded into the
> +   flags.  OUTER is true if G is the outermost AND/IOR.  */
> static bool
> -ccmp_candidate_p (gimple *g, bool outer = false)
> +ccmp_candidate_p (gimple *g, bool *has_comparison, bool outer = false)
> {
>   tree lhs, op0, op1;
>   gimple *gs0, *gs1;
>   tree_code tcode;
>   basic_block bb;
> 
> +  *has_comparison = false;
> +
>   if (!g || !is_gimple_assign (g))
>     return false;
> 
> @@ -115,20 +127,46 @@ ccmp_candidate_p (gimple *g, bool outer = false)
>   if (!outer && !has_single_use (lhs))
>     return false;
> 
> +  if (SA.ccmp_no_comparison
> +      && bitmap_bit_p (SA.ccmp_no_comparison, SSA_NAME_VERSION (lhs)))
> +    {
> +      *has_comparison = false;
> +      return true;
> +    }
> +
>   bb = gimple_bb (g);
>   gs0 = get_gimple_for_ssa_name (op0); /* gs0 may be NULL */
>   gs1 = get_gimple_for_ssa_name (op1); /* gs1 may be NULL */
> 
> -  if (ccmp_tree_comparison_p (op0, bb) && ccmp_tree_comparison_p (op1, bb))
> -    return true;
> -  if (ccmp_tree_comparison_p (op0, bb) && ccmp_candidate_p (gs1))
> -    return true;
> -  if (ccmp_tree_comparison_p (op1, bb) && ccmp_candidate_p (gs0))
> -    return true;
> +  bool comparison0, comparison1;
> +  bool leaf0 = ccmp_tree_comparison_p (op0, bb, &comparison0);
> +  bool leaf1 = ccmp_tree_comparison_p (op1, bb, &comparison1);
> +
> +  if (leaf0 && leaf1)
> +    *has_comparison = comparison0 || comparison1;
> +  else if (leaf0 || leaf1)
> +    {
> +      bool inner_has_comparison;
> +      if (!ccmp_candidate_p (leaf0 ? gs1 : gs0, &inner_has_comparison))
> + return false;
> +      *has_comparison = inner_has_comparison
> + || (leaf0 ? comparison0 : comparison1);
> +    }
> +  else
> +    return false;
> +
>   /* We skip ccmp_candidate_p (gs1) && ccmp_candidate_p (gs0) since
>      there is no way to set and maintain the CC flag on both sides of
>      the logical operator at the same time.  */
> -  return false;
> +
> +  if (!*has_comparison)
> +    {
> +      if (!SA.ccmp_no_comparison)
> + SA.ccmp_no_comparison = BITMAP_ALLOC (NULL);
> +      bitmap_set_bit (SA.ccmp_no_comparison, SSA_NAME_VERSION (lhs));
> +    }
> +
> +  return true;
> }
> 
> /* Extract the comparison we want to do from the tree.  */
> @@ -291,7 +329,8 @@ expand_ccmp_expr (gimple *g, machine_mode mode)
>   rtx_insn *last;
>   rtx tmp;
> 
> -  if (!ccmp_candidate_p (g, true))
> +  bool has_comparison;
> +  if (!ccmp_candidate_p (g, &has_comparison, true) || !has_comparison)
>     return NULL_RTX;
> 
>   last = get_last_insn ();
> diff --git a/gcc/testsuite/gcc.target/aarch64/ccmp_11.c 
> b/gcc/testsuite/gcc.target/aarch64/ccmp_11.c
> new file mode 100644
> index 00000000000..8605e177989
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/aarch64/ccmp_11.c
> @@ -0,0 +1,20 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fno-tree-reassoc" } */
> +/* PR middle-end/109832 */
> +
> +/* Rejected all-boolean subchains are cached during expansion.  */
> +
> +extern _Bool t (void);
> +
> +#define B t ()
> +
> +_Bool
> +bool_call_chain (void)
> +{
> +  return B | B | B | B | B | B | B | B
> + | B | B | B | B | B | B | B | B
> + | B | B | B | B | B | B | B | B
> + | B | B | B | B | B | B | B | B;
> +}
> +
> +/* { dg-final { scan-assembler-not {\tccmp\t} } } */
> diff --git a/gcc/testsuite/gcc.target/aarch64/ccmp_13.c 
> b/gcc/testsuite/gcc.target/aarch64/ccmp_13.c
> new file mode 100644
> index 00000000000..81ef431e137
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/aarch64/ccmp_13.c
> @@ -0,0 +1,19 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fgimple" } */
> +/* PR middle-end/109832 */
> +
> +/* A non-binary boolean definition is not a conditional-compare chain.  */
> +
> +_Bool __GIMPLE (ssa, startwith ("expand"))
> +bool_not_ior (_Bool a, _Bool b)
> +{
> +  _Bool _1;
> +  _Bool _2;
> +
> +  __BB(2):
> +  _1 = ~a_3(D);
> +  _2 = _1 | b_4(D);
> +  return _2;
> +}
> +
> +/* { dg-final { scan-assembler-not {\tccmp\t} } } */
> diff --git a/gcc/testsuite/gcc.target/aarch64/ccmp_7.c 
> b/gcc/testsuite/gcc.target/aarch64/ccmp_7.c
> new file mode 100644
> index 00000000000..d9c75eb6b0b
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/aarch64/ccmp_7.c
> @@ -0,0 +1,38 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2" } */
> +/* PR middle-end/109832 */
> +
> +/* A conditional compare folds a comparison into the flags.  When the 
> operands
> +   are plain booleans there is no comparison to fold and the bitwise 
> operation
> +   is cheaper, so no conditional compare should be formed.  */
> +
> +_Bool
> +bool_ior (_Bool x, _Bool y)
> +{
> +  return x || y;
> +}
> +
> +_Bool
> +bool_and (_Bool x, _Bool y)
> +{
> +  return x && y;
> +}
> +
> +extern _Bool t (void);
> +
> +_Bool
> +bool_call_ior (void)
> +{
> +  _Bool x = t ();
> +  _Bool y = t ();
> +  return x | y;
> +}
> +
> +_Bool
> +bool_ior_tree (_Bool a, _Bool b, _Bool c, _Bool d)
> +{
> +  return (a | b) | (c | d);
> +}
> +
> +/* { dg-final { scan-assembler-not {\tccmp\t} } } */
> +/* { dg-final { scan-assembler-times {\torr\tw[0-9]+, w[0-9]+, w[0-9]+} 5 } 
> } */
> diff --git a/gcc/testsuite/gcc.target/aarch64/ccmp_9.c 
> b/gcc/testsuite/gcc.target/aarch64/ccmp_9.c
> new file mode 100644
> index 00000000000..029dffa1b93
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/aarch64/ccmp_9.c
> @@ -0,0 +1,20 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fno-tree-reassoc" } */
> +/* PR middle-end/109832 */
> +
> +/* One explicit comparison is enough for a chain to be worth a conditional
> +   compare, so the boolean operands become comparisons against zero.  */
> +
> +int
> +bool_and_cmp_last (_Bool a, _Bool b, _Bool c, int x, int y)
> +{
> +  return ((a & b) & c) & (x < y);
> +}
> +
> +int
> +bool_and_cmp_first (_Bool a, _Bool b, _Bool c, int x, int y)
> +{
> +  return (((x < y) & a) & b) & c;
> +}
> +
> +/* { dg-final { scan-assembler-times {\tccmp\t} 6 } } */
> diff --git a/gcc/tree-outof-ssa.cc b/gcc/tree-outof-ssa.cc
> index 409e951b212..ece86e2ab6f 100644
> --- a/gcc/tree-outof-ssa.cc
> +++ b/gcc/tree-outof-ssa.cc
> @@ -1489,6 +1489,8 @@ finish_out_of_ssa (struct ssaexpand *sa)
>   free (sa->partition_to_pseudo);
>   if (sa->values)
>     BITMAP_FREE (sa->values);
> +  if (sa->ccmp_no_comparison)
> +    BITMAP_FREE (sa->ccmp_no_comparison);
>   delete_var_map (sa->map);
>   BITMAP_FREE (sa->partitions_for_parm_default_defs);
>   BITMAP_FREE (sa->partitions_for_undefined_values);
> diff --git a/gcc/tree-outof-ssa.h b/gcc/tree-outof-ssa.h
> index bfe9a1290b8..3d05c623708 100644
> --- a/gcc/tree-outof-ssa.h
> +++ b/gcc/tree-outof-ssa.h
> @@ -34,6 +34,10 @@ struct ssaexpand
>      its definition should be forwarded.  */
>   bitmap values;
> 
> +  /* For an SSA name version V bit V is set iff conditional-compare
> +     recognition found no explicit comparison that can be folded.  */
> +  bitmap ccmp_no_comparison;
> +
>   /* For a partition number I partition_to_pseudo[I] contains the
>      RTL expression of the allocated space of it (either a MEM or
>      a pseudos REG).  */
> -- 
> 2.50.1 (Apple Git-155)

Reply via email to