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