https://gcc.gnu.org/bugzilla/show_bug.cgi?id=127747

            Bug ID: 127747
           Summary: [17 Regression] min/max narrowing adds conversion even
                    when widened values are not otherwise dead
           Product: gcc
           Version: 17.0
            Status: UNCONFIRMED
          Keywords: missed-optimization
          Severity: normal
          Priority: P3
         Component: tree-optimization
          Assignee: unassigned at gcc dot gnu.org
          Reporter: gabravier at gmail dot com
                CC: hjl.tools at gmail dot com
  Target Milestone: ---

(note: needs to be compiled as C++, unless I missed a way to express to get the
C frontend to consume vec ternaries)

typedef short v8hi __attribute__((vector_size (16)));
typedef int v8si __attribute__((vector_size (32)));

v8si
f (v8hi a, v8hi b, v8si *p)
{
  v8si wa = __builtin_convertvector (a, v8si);
  v8si wb = __builtin_convertvector (b, v8si);
  *p = wa + wb;                 /* wa and wb are live */
  return wa < wb ? wa : wb;     /* vec_cond -> MIN_EXPR -> narrow */
}

On g++ 16.2.0, with -O2 -mavx2, this compiles to:

        vpmovsxwd       ymm1, xmm1
        vpmovsxwd       ymm0, xmm0
        vpaddd  ymm2, ymm0, ymm1
        vpminsd ymm0, ymm0, ymm1
        vmovdqa YMMWORD PTR [rdi], ymm2
        ret

On trunk (g++
(Compiler-Explorer-Build-gcc-748199b3435594e1521480258897ea0b39cde910-binutils-2.44)
17.0.0 20261006 (experimental)), with -O2 -mavx2, this compiles to:

        vpmovsxwd       ymm2, xmm0
        vpmovsxwd       ymm3, xmm1
        vpminsw xmm0, xmm0, xmm1
        vpaddd  ymm2, ymm2, ymm3
        vpmovsxwd       ymm0, xmm0
        vmovdqa YMMWORD PTR [rdi], ymm2
        ret

The first output seems better, since the second requires an extra vpmovsxwd
(e.g. on an intel core with a single shuffle port, this runs 3 uops on it
instead of 2, and llvm-mca explicitly tells me trunk's output is ~30% slower on
Zen 1).

This seems specifically to be caused by this pattern:

/* minmax ((wide_type) a, (wide_type) b) -> (wide_type) minmax (a, b)
   when type matches the type of a and b, and wide_type is a wider
   type with the same signedness as type.  Extension is monotone, so it
   commutes with the comparison, and the truncation is then exact.  The
   same holds lanewise for a vector, where the narrow operation has to be
   available so that lowering does not scalarise it.  The wide operation
   must become dead before a narrow vector operation is introduced.  */
(for minmax (min max)
     MINMAX (MIN_EXPR MAX_EXPR)
 (simplify
  (minmax (convert @0) (convert @1))
  (if (ANY_INTEGRAL_TYPE_P (type)
       && ANY_INTEGRAL_TYPE_P (TREE_TYPE (@0))
       && types_match (TREE_TYPE (@0), TREE_TYPE (@1))
       && element_precision (TREE_TYPE (@0)) < element_precision (type)
       && TYPE_UNSIGNED (TREE_TYPE (@0)) == TYPE_UNSIGNED (type)
       && (!VECTOR_TYPE_P (type)
           || target_supports_op_p (TREE_TYPE (@0), MINMAX, optab_vector)))
   (convert (minmax @0 @1)))))


...whose comment even seems to explicitly say that it should be using `:s` on
those first two converts (I have not personally tested that adding them fixes
this, but it seems likely to me that it will).

I am fairly certain this is introduced by r17-3355-ga6173e867de7f9, since it
removed its single use guard. Reintroducing it in the way I suggest should, as
far as I can tell, make no difference for the code r17-3355-ga6173e867de7f9 was
introduced to optimize.

Reply via email to