> -----Original Message-----
> From: Tamar Christina <[email protected]>
> Sent: 30 March 2026 13:03
> To: Roger Sayle <[email protected]>; 'GCC Patches' <gcc-
> [email protected]>
> Cc: 'Richard Biener' <[email protected]>; 'Andrew Pinski'
> <[email protected]>; 'Hongtao Liu' <[email protected]>
> Subject: RE: [PATCH v2] PR target/123238: VCOND_MASK regression on
> aarch64 and x86_64.
> 
> > -----Original Message-----
> > From: Roger Sayle <[email protected]>
> > Sent: 30 March 2026 12:37
> > To: Tamar Christina <[email protected]>; 'GCC Patches' <gcc-
> > [email protected]>
> > Cc: 'Richard Biener' <[email protected]>; 'Andrew Pinski'
> > <[email protected]>; 'Hongtao Liu'
> <[email protected]>
> > Subject: RE: [PATCH v2] PR target/123238: VCOND_MASK regression on
> > aarch64 and x86_64.
> >
> >
> > Hi Tamar,
> > Forgive the terminology, I'm not fluent in aarch64, but cpu has a test
> > instruction, so that comparisons against zero are cheaper than
> > comparisons against other values/expressions.  If the backend's rtx_costs
> > returned false, the function would recurse and include the overhead
> > of setting a register to zero.
> > The goal is that "(eq x (const_int 0))" is cheaper than "(eq x (const_int
> > 1))".
> 
> Oh I see, yeah, but that's the case for all comparisons then, including scalar
> and unordered since scalar uses the zero register and for vector both SVE
> and Adv.SIMD support zero immediates, and so does scalar FP.
> 
> So then the correct costing would be to handle zero for all comparisons
> and only increase the cost of NE with non-zero for Adv. SIMD.
> 
> Let me run that change through regtest and benchmarking and will
> come back to you in a bit.

Hi Roger,

So how about this,

diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc
index 1902a4ee9fa..754a5b76d1d 100644
--- a/gcc/config/aarch64/aarch64.cc
+++ b/gcc/config/aarch64/aarch64.cc
@@ -16234,8 +16234,24 @@ cost_plus:
     case GEU:
     case LE:
     case LEU:
-
-      return false; /* All arguments must be in registers.  */
+      {
+       op0 = XEXP (x, 0);
+       op1 = XEXP (x, 1);
+       machine_mode inner_mode = GET_MODE (op0);
+       *cost += rtx_cost (op0, inner_mode, code, 0, speed);
+       if (op1 != CONST0_RTX (inner_mode))
+         {
+           unsigned int vec_flags = aarch64_classify_vector_mode (mode);
+           bool unsigned_p = code == LTU || code == LEU || code == GTU
+                             || code == GEU;
+           if ((vec_flags & VEC_SVE_DATA) == 0
+               || !aarch64_sve_cmp_immediate_p (op1, !unsigned_p))
+             *cost += rtx_cost (op1, inner_mode, code, 1, speed);
+           if (code == NE && (vec_flags & VEC_ADVSIMD))
+             *cost += COSTS_N_INSNS (1);
+         }
+       return true;
+      }

     case FMA:
       op0 = XEXP (x, 0);

Which should correctly cost all comparisons.   This also fixes the typo in your
original patch:

+         if (op1 != CONST0_RTX (inner_mode))
+           *cost += rtx_cost (op0, inner_mode, NE, 1, speed)
+                    + COSTS_N_INSNS (1);

where you meant to cost op1 here.

Thanks,
Tamar

> 
> Cheers,
> Tamar
> 
> >
> > I hope this explains why the patch is structured the way it is.
> >
> > [As shown by PR 124605, the implementation of rtx_costs in the arm
> > backend is less than ideal].
> >
> > Cheers,
> > Roger
> > --
> >
> > > -----Original Message-----
> > > From: Tamar Christina <[email protected]>
> > > Sent: 30 March 2026 12:10
> > > To: Roger Sayle <[email protected]>; 'GCC Patches' <gcc-
> > > [email protected]>
> > > Cc: 'Richard Biener' <[email protected]>; 'Andrew Pinski'
> > > <[email protected]>; 'Hongtao Liu'
> > <[email protected]>
> > > Subject: RE: [PATCH v2] PR target/123238: VCOND_MASK regression on
> > aarch64
> > > and x86_64.
> > >
> > > > -----Original Message-----
> > > > From: Roger Sayle <[email protected]>
> > > > Sent: 01 March 2026 12:02
> > > > To: 'GCC Patches' <[email protected]>
> > > > Cc: 'Richard Biener' <[email protected]>; 'Andrew Pinski'
> > > > <[email protected]>; 'Hongtao Liu'
> > <[email protected]>
> > > > Subject: [PATCH v2] PR target/123238: VCOND_MASK regression on
> > aarch64
> > > > and x86_64.
> > > >
> > > >
> > > > This is my revised patch for PR target/123238, previously posted at
> > > > https://gcc.gnu.org/pipermail/gcc-patches/2026-
> February/708283.html
> > > > incorporating the feedback from both Andrew Pinksi and Richard Biener,
> > > > and additionally resolving this code quality regression on aarch64,
> > > > which exhibits many of the same issues as x86_64.  Alas, the only
> > > > native aarch64 that I have convenient access to is an Apple M1 laptop,
> > > > so the examples and patches below are against Iain Sandoe's tree for
> > > > aarch64-apple-darwin24.3.0 [as that target isn't yet supported by
> > > > mainline GCC 16].
> > > >
> > > > To explain and motivate things for the aarch64 maintainers:
> > > >
> > > > void foo(char c[])
> > > > {
> > > >     for (int i = 0; i < 8; i++)
> > > >         c[i] = c[i] != 'a' ? 'c' : 'e'; }
> > > >
> > > > currently generates with -O2 the following:
> > > >
> > > > foo:    movi    v30.8b, 0x61
> > > >         ldr     d0, [x0]
> > > >         movi    v29.8b, 0x63
> > > >         movi    v31.8b, 0x65
> > > >         cmeq    v30.8b, v0.8b, v30.8b
> > > >         not     v30.8b, v30.8b
> > > >         bit     v31.8b, v29.8b, v30.8b
> > > >         str     d31, [x0]
> > > >         ret
> > > >
> > > > where a cmeq followed by a not is used to implement NE_EXPR.
> > > > c.f. the comment "Handle NE as !EQ" in aarch64-simd.md's expander of
> > > > vec_cmp<mode><mode>.  With this patch, including the change to
> > > > aarch64_rtx_costs to indicate that NE is more expensive than EQ, the
> > > > middle-end swaps the VCOND_EXPR, reducing the number of
> instructions
> > > > in the example above [to what it was in GCC 14].
> > > >
> > > > Changes from the previous version of this patch include Richard's
> > > > recommendation to use get_gimple_for_ssa_name instead of
> > > > SSA_NAME_DEF_STMT, and Andrew's suggestion to log expand's
> decision
> > to
> > > > the dump file, which now contains lines such as:
> > > >
> > > > ;; swapping operands of .VCOND_MASK
> > > > ;; cost of original ne: 8
> > > > ;; cost of replacement eq: 4
> > > >
> > > > or (for failure)
> > > >
> > > > ;; not swapping operands of .VCOND_MASK ;; cost of original eq: 4 ;;
> > > > cost of replacement ne: 8
> > > >
> > > > I've kept the handling of an embedded tcc_comparison, this both helps
> > > > document what the code intends to do, and (defensively) supports
> > > > expand's ability to work with GENERIC, and not just GIMPLE trees (with
> > > > or without TER).  During RTL expansion, the middle-end may temporarily
> > > > construct trees (for intermediate
> > > > expressions) that aren't always valid in earlier passes.
> > > >
> > > > Finally, I've no objection to someone else attempting to perform this
> > > > type of (target dependent) optimization during the gimple-isel pass,
> > > > though I expect it will be difficult to maintain hardware specific
> > > > idioms, such as aarch64's representation of NE as !EQ (in some machine
> > > > modes) when match.pd and elsewhere in the middle-end attempts to
> keep
> > > > tree's canonical (and machine independent) by reducing operators,
> > > > commuting constants in binary expressions etc.
> > > >
> > > > I've previously explained how the ISEL pass adversely affects code
> > > > generation on x86_64 (making decisions about lowering that are better
> > > > left to the backends), but the same issue is also there for aarch64.
> > > >
> > > > Consider the (closely related) case:
> > > >
> > > > void bar(char c[])
> > > > {
> > > >     for (int i = 0; i < 8; i++)
> > > >         c[i] = c[i] != 'a';
> > > > }
> > > >
> > > > Here gimple-isel makes the decision to turn a 0/-1 vector to a
> > > > 0/1 vector using bitwise-AND with 1, so on aarch64 we generate:
> > > >
> > > > bar:    movi    v30.8b, 0x61
> > > >         ldr     d0, [x0]
> > > >         movi    v31.8b, 0x1
> > > >         cmeq    v30.8b, v0.8b, v30.8b
> > > >         bic     v31.8b, v31.8b, v30.8b
> > > >         str     d31, [x0]
> > > >         ret
> > > >
> > > > Ideally, aarch64 should be able to use "ushr v31.8b, v31.8b, 7"
> > > > but once the .VCOND_MASK is lowered to an AND, the semantics that
> the
> > > > original vector was a -1/0 mask has been lost, so later passes are
> > > > unable to improve things.  Alas because gimple-isel thinks it knows
> > > > best, GCC even de-optimizes code, introducing instructions into:
> > > >
> > > > typedef char v8qi __attribute__((vector_size(8))); void baz(v8qi *c) {
> > > >     v8qi t = *c;
> > > >     t = t == (v8qi){'a','a','a','a','a','a','a','a'};
> > > >     t >>= 7;
> > > >     *c = t;
> > > > }
> > > >
> > > > In theory, one might be able to work around the above glitch with
> > > > -fdisable-tree-isel but that currently ICEs, and would need my
> > > > (rejected) patch from May 2022:
> > > > https://gcc.gnu.org/pipermail/gcc-patches/2022-May/595382.html
> > > > But this is digressing far off topic.
> > > >
> > > > This patch has been tested on x86_64-pc-linux-gnu with make bootstrap
> > > > and make -k check, both with and without --target_board=unix{-m32}
> > > > with no new failures.  It has also been tested on iains'
> > > > aarch64-apple-darwin24.3.0 also with no new failures.
> > > > Ok for mainline?  (the middle-end, aarch64 and x86_64 pieces can all
> > > > be approved/applied independently).
> > > >
> > >
> > > Sorry for the very late reply. I was on holidays.
> > >
> > > > diff --git a/gcc/config/aarch64/aarch64.cc
> > > > b/gcc/config/aarch64/aarch64.cc index f2ecb0ee8cb..d92a5aa491c
> > 100644
> > > > --- a/gcc/config/aarch64/aarch64.cc
> > > > +++ b/gcc/config/aarch64/aarch64.cc
> > > > @@ -16224,8 +16224,34 @@ cost_plus:
> > > >        return aarch64_if_then_else_costs (XEXP (x, 0), XEXP (x, 1),
> > > >                                          XEXP (x, 2), cost, speed);
> > > >
> > > > -    case EQ:
> > > >      case NE:
> > > > +      if (GET_MODE_CLASS (mode) == MODE_VECTOR_INT)
> > >
> > > SVE does have CMPNE as an operation, so this would be incorrect for it.
> > > I think you want instead
> > >
> > > if (aarch64_classify_vector_mode (mode) & VEC_ADVSIMD)
> > >
> > > > +       {
> > > > +         op0 = XEXP (x, 0);
> > > > +         op1 = XEXP (x, 1);
> > > > +         machine_mode inner_mode = GET_MODE (op0);
> > > > +         *cost += rtx_cost (op0, inner_mode, NE, 0, speed);
> > > > +         /* NE costs one_cmpl<mode>2 more than EQ.  */
> > > > +         if (op1 != CONST0_RTX (inner_mode))
> > > > +           *cost += rtx_cost (op0, inner_mode, NE, 1, speed)
> > > > +                    + COSTS_N_INSNS (1);
> > > > +         return true;
> > > > +       }
> > >
> > > Isn't this the same as
> > >
> > > op0 = XEXP (x, 0);
> > > if (op0 != CONST0_RTX (GET_MODE (op0);))
> > >   *cost += COSTS_N_INSNS (1);
> > > return false;
> > >
> > > since the false will cost the operands appropriately.
> > >
> > > > +      return false;  /* All arguments need to be in registers.  */
> > > > +
> > > > +    case EQ:
> > > > +      if (GET_MODE_CLASS (mode) == MODE_VECTOR_INT)
> > > > +       {
> > > > +         op0 = XEXP (x, 0);
> > > > +         op1 = XEXP (x, 1);
> > > > +         machine_mode inner_mode = GET_MODE (op0);
> > > > +         *cost += rtx_cost (op0, inner_mode, EQ, 0, speed);
> > > > +         if (op1 != CONST0_RTX (inner_mode))
> > > > +           *cost += rtx_cost (op0, inner_mode, EQ, 1, speed);
> > > > +         return true;
> > > > +       }
> > >
> > > Do we need this hunk? Returning false should have it iterate over the
> > operands
> > > and cost them already.  So I think we can drop it?
> > >
> > > Thanks,
> > > Tamar
> > >
> > > > +      return false; /* All arguments must be in registers.  */
> > > > +
> > > >      case GT:
> > > >      case GTU:
> > > >      case LT:
> > >
> > > > Thanks in advance (and for the previous reviews).
> > > >
> > > >
> > > > 2026-03-01  Roger Sayle  <[email protected]>
> > > >
> > > > gcc/ChangeLog
> > > >         PR target/123238
> > > >         * expr.cc (convert_tree_comp_to_rtx): Make global.
> > > >         * expr.h (convert_tree_comp_to_rtx): Prototype here.
> > > >         * internal-fn.cc (expand_vec_cond_mask_optab_fn): Use rtx_costs
> > > >         to determine whether swapping operands would result in better
> > > >         code.
> > > >
> > > >         * config/aarch64/aarch64.cc (aarch64_rtx_costs) <case NE/EQ>:
> > > >         Provide costs for integer vector equality and inequality.
> > > >
> > > >         * config/i386/i386-expand.cc (ix86_expand_int_vec_cmp): On
> > > >         AVX512 targets use a ternlog instead of a comparison to negate
> > > >         the mask (requires one instruction instead of two).
> > > >         * config/i386/i386.cc (ix86_rtx_costs): Refactor code for
> > UNSPEC.
> > > >         Provide costs for UNSPEC_BLENDV and  UNSPEC_MOVMSK.  Provide
> > > >         costs for comparison operators of integer vector modes.
> > > >
> > > > gcc/testsuite/ChangeLog
> > > >         PR target/123238
> > > >         * gcc.target/aarch64/pr123238.c: New test case.
> > > >         * gcc.target/i386/pr123238.c: Likewise.
> > > >
> > > > Roger
> > > > --
> >

Reply via email to