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.