Expose existing vector conversion instructions through lrint optabs so
the vectorizer can use vcvtps2qq and vcvtpd2dq instead of scalar
conversion sequences.
Bootstrapped and regtested on x86_64-pc-linux-gnu{-m32,}.
Ready push to trunk.
gcc/ChangeLog:
PR target/126160
* config/i386/sse.md (lrint<ssePSmode2lower><mode>2): New expander.
(lrintv2sfv2di2): Likewise.
(ssedfsimode): New mode attribute.
(ssedfsimodelower): Likewise.
(lrint<mode><ssedfsimodelower>2): New expander.
gcc/testsuite/ChangeLog:
PR target/126160
* gcc.target/i386/pr126160.c: New test.
* gcc.target/i386/pr126160-2.c: New test.
---
gcc/config/i386/sse.md | 39 ++++++++++++++++++++++
gcc/testsuite/gcc.target/i386/pr126160-2.c | 37 ++++++++++++++++++++
gcc/testsuite/gcc.target/i386/pr126160.c | 24 +++++++++++++
3 files changed, 100 insertions(+)
create mode 100644 gcc/testsuite/gcc.target/i386/pr126160-2.c
create mode 100644 gcc/testsuite/gcc.target/i386/pr126160.c
diff --git a/gcc/config/i386/sse.md b/gcc/config/i386/sse.md
index 122dfb36764..7521034e128 100644
--- a/gcc/config/i386/sse.md
+++ b/gcc/config/i386/sse.md
@@ -9205,6 +9205,13 @@ (define_insn
"<mask_codefor><avx512>_fixuns_notrunc<sf2simodelower><mode><mask_n
(set_attr "prefix" "evex")
(set_attr "mode" "<sseinsnmode>")])
+;; vcvtps2qq widens V4SF/V8SF directly to V4DI/V8DI (float -> long).
+(define_expand "lrint<ssePSmode2lower><mode>2"
+ [(set (match_operand:VI8_256_512 0 "register_operand")
+ (unspec:VI8_256_512 [(match_operand:<ssePSmode2> 1
"nonimmediate_operand")]
+ UNSPEC_FIX_NOTRUNC))]
+ "TARGET_AVX512DQ")
+
(define_insn "<mask_codefor>avx512dq_cvtps2qq<mode><mask_name><round_name>"
[(set (match_operand:VI8_256_512 0 "register_operand" "=v")
(unspec:VI8_256_512 [(match_operand:<ssePSmode2> 1
"nonimmediate_operand" "<round_constraint>")]
@@ -9215,6 +9222,24 @@ (define_insn
"<mask_codefor>avx512dq_cvtps2qq<mode><mask_name><round_name>"
(set_attr "prefix" "evex")
(set_attr "mode" "<sseinsnmode>")])
+;; vcvtps2qq xmm converts the low 2 elements of V4SF to V2DI (float -> long).
+(define_expand "lrintv2sfv2di2"
+ [(match_operand:V2DI 0 "register_operand")
+ (match_operand:V2SF 1 "nonimmediate_operand")]
+ "TARGET_AVX512DQ && TARGET_AVX512VL"
+{
+ rtx op1 = gen_reg_rtx (V4SFmode);
+ emit_insn (gen_movq_v2sf_to_sse (op1, operands[1]));
+
+ rtx sel = gen_rtx_PARALLEL (VOIDmode,
+ gen_rtvec (2, const0_rtx, const1_rtx));
+ rtx src = gen_rtx_VEC_SELECT (V2SFmode, op1, sel);
+ emit_insn (gen_rtx_SET (operands[0],
+ gen_rtx_UNSPEC (V2DImode, gen_rtvec (1, src),
+ UNSPEC_FIX_NOTRUNC)));
+ DONE;
+})
+
(define_insn "<mask_codefor>avx512dq_cvtps2qqv2di<mask_name>"
[(set (match_operand:V2DI 0 "register_operand" "=v")
(unspec:V2DI
@@ -10032,6 +10057,20 @@ (define_insn "sse2_cvtdq2pd<mask_name>"
(set_attr "prefix" "maybe_vex")
(set_attr "mode" "V2DF")])
+;; Mapping of a DFmode vector to the SImode vector of the same length.
+(define_mode_attr ssedfsimode
+ [(V8DF "V8SI") (V4DF "V4SI") (V2DF "V2SI")])
+(define_mode_attr ssedfsimodelower
+ [(V8DF "v8si") (V4DF "v4si") (V2DF "v2si")])
+
+;; vcvtpd2dq narrows V2DF/V4DF/V8DF to V2SI/V4SI/V8SI (double -> int).
+(define_expand "lrint<mode><ssedfsimodelower>2"
+ [(set (match_operand:<ssedfsimode> 0 "register_operand")
+ (unspec:<ssedfsimode>
+ [(match_operand:VF2 1 "register_operand")]
+ UNSPEC_FIX_NOTRUNC))]
+ "TARGET_SSE2")
+
(define_insn "avx512f_cvtpd2dq512<mask_name><round_name>"
[(set (match_operand:V8SI 0 "register_operand" "=v")
(unspec:V8SI
diff --git a/gcc/testsuite/gcc.target/i386/pr126160-2.c
b/gcc/testsuite/gcc.target/i386/pr126160-2.c
new file mode 100644
index 00000000000..0fd5506ced2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr126160-2.c
@@ -0,0 +1,37 @@
+/* PR target/126160 */
+/* { dg-do compile } */
+/* { dg-options "-Ofast -mavx512f -mavx512vl -mavx512dq
-mprefer-vector-width=512" } */
+/* { dg-final { scan-assembler-times "vcvtpd2dq" 3 } } */
+/* { dg-final { scan-assembler-not "vcvtsd2si" } } */
+
+/* __builtin_irint (double -> int) should vectorize into vcvtpd2dq rather
+ than scalar vcvtsd2sil: V2DF -> V2SI, V4DF -> V4SI and V8DF -> V8SI. */
+
+void
+f2 (int *a, double *b)
+{
+ a[0] = __builtin_irint (b[0]);
+ a[1] = __builtin_irint (b[1]);
+}
+
+void
+f4 (int *a, double *b)
+{
+ a[0] = __builtin_irint (b[0]);
+ a[1] = __builtin_irint (b[1]);
+ a[2] = __builtin_irint (b[2]);
+ a[3] = __builtin_irint (b[3]);
+}
+
+void
+f8 (int *a, double *b)
+{
+ a[0] = __builtin_irint (b[0]);
+ a[1] = __builtin_irint (b[1]);
+ a[2] = __builtin_irint (b[2]);
+ a[3] = __builtin_irint (b[3]);
+ a[4] = __builtin_irint (b[4]);
+ a[5] = __builtin_irint (b[5]);
+ a[6] = __builtin_irint (b[6]);
+ a[7] = __builtin_irint (b[7]);
+}
diff --git a/gcc/testsuite/gcc.target/i386/pr126160.c
b/gcc/testsuite/gcc.target/i386/pr126160.c
new file mode 100644
index 00000000000..8c44727c1fc
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr126160.c
@@ -0,0 +1,24 @@
+/* PR target/126160 */
+/* { dg-do compile { target { ! ia32 } } } */
+/* { dg-options "-Ofast -mavx512dq -mavx512vl" } */
+/* { dg-final { scan-assembler-times "vcvtps2qq" 2 } } */
+/* { dg-final { scan-assembler-not "vcvtss2si" } } */
+
+/* The float -> long conversions should vectorize into vcvtps2qq rather
+ than scalar vcvtss2siq: V4SF -> V4DI for foo, V2SF -> V2DI for bar. */
+
+void
+foo (long *a, float *b)
+{
+ a[0] = __builtin_lrint (b[0]);
+ a[1] = __builtin_lrint (b[1]);
+ a[2] = __builtin_lrint (b[2]);
+ a[3] = __builtin_lrint (b[3]);
+}
+
+void
+bar (long *a, float *b)
+{
+ a[0] = __builtin_lrint (b[0]);
+ a[1] = __builtin_lrint (b[1]);
+}
--
2.34.1