Late RTL pass that inserts reg-free zero idioms (vpxor) in large loops
to break false dependencies on physical SSE registers.  It walks
innermost loops in BFS order; when a register dying in one insn is
redefined far enough later, it emits a zeroing idiom at the kill point
as a hint to the hardware register renamer.  The pass is gated on
TARGET_AVX && X86_TUNE_INSERT_REG_FREE_ZERO_IDIOM (m_ZNVER, znver1-6)
and is tuned by the reg-free-zero-loop-threshold and
reg-free-zero-gap-threshold params.

gcc/ChangeLog:

        * config/i386/i386-features.cc (class pass_insert_reg_free_zero_idiom):
        New RTL pass.
        (pass_insert_reg_free_zero_idiom::insert_zero_idiom_after): New method.
        (pass_insert_reg_free_zero_idiom::execute): New method.
        (make_pass_insert_reg_free_zero_idiom): New function.
        * config/i386/i386-passes.def (INSERT_PASS_BEFORE): Register
        pass_insert_reg_free_zero_idiom before pass_shorten_branches.
        * config/i386/i386-protos.h (make_pass_insert_reg_free_zero_idiom):
        Declare.
        * config/i386/x86-tune.def (X86_TUNE_INSERT_REG_FREE_ZERO_IDIOM):
        New tune flag enabled for m_ZNVER.
        * params.opt (-param=reg-free-zero-loop-threshold=): New param.
        (-param=reg-free-zero-gap-threshold=): New param.

gcc/testsuite/ChangeLog:

        * gcc.target/i386/reg-free-zero-1.c: New test.
        * gcc.target/i386/reg-free-zero-2.c: New test.
        * gcc.target/i386/reg-free-zero-3.c: New test.
        * gcc.target/i386/reg-free-zero-4.c: New test.
        * gcc.target/i386/reg-free-zero-5.c: New test.
        * gcc.target/i386/reg-free-zero-6.c: New test.
        * gcc.target/i386/reg-free-zero-7.c: New test.
        * gcc.target/i386/reg-free-zero-8.c: New test.

Co-authored-by: Venkataramanan Kumar <[email protected]>
Signed-off-by: Sarvesh Chandra <[email protected]>
---
Bootstrapped and regression-tested on x86_64-pc-linux-gnu.

 gcc/config/i386/i386-features.cc              | 215 ++++++++++++++++++
 gcc/config/i386/i386-passes.def               |   1 +
 gcc/config/i386/i386-protos.h                 |   1 +
 gcc/config/i386/x86-tune.def                  |   6 +
 gcc/params.opt                                |   8 +
 .../gcc.target/i386/reg-free-zero-1.c         |  31 +++
 .../gcc.target/i386/reg-free-zero-2.c         |  31 +++
 .../gcc.target/i386/reg-free-zero-3.c         |  36 +++
 .../gcc.target/i386/reg-free-zero-4.c         |  30 +++
 .../gcc.target/i386/reg-free-zero-5.c         |  12 +
 .../gcc.target/i386/reg-free-zero-6.c         |  71 ++++++
 .../gcc.target/i386/reg-free-zero-7.c         |  29 +++
 .../gcc.target/i386/reg-free-zero-8.c         |  29 +++
 13 files changed, 500 insertions(+)
 create mode 100644 gcc/testsuite/gcc.target/i386/reg-free-zero-1.c
 create mode 100644 gcc/testsuite/gcc.target/i386/reg-free-zero-2.c
 create mode 100644 gcc/testsuite/gcc.target/i386/reg-free-zero-3.c
 create mode 100644 gcc/testsuite/gcc.target/i386/reg-free-zero-4.c
 create mode 100644 gcc/testsuite/gcc.target/i386/reg-free-zero-5.c
 create mode 100644 gcc/testsuite/gcc.target/i386/reg-free-zero-6.c
 create mode 100644 gcc/testsuite/gcc.target/i386/reg-free-zero-7.c
 create mode 100644 gcc/testsuite/gcc.target/i386/reg-free-zero-8.c

diff --git a/gcc/config/i386/i386-features.cc b/gcc/config/i386/i386-features.cc
index e0fc13efe86..0a3f6857e63 100644
--- a/gcc/config/i386/i386-features.cc
+++ b/gcc/config/i386/i386-features.cc
@@ -3117,6 +3117,221 @@ make_pass_stv (gcc::context *ctxt)
   return new pass_stv (ctxt);
 }
 
+
+/* Pass to insert reg-free zero idioms (vpxor) in large loops to break
+   false dependencies on physical SSE/AVX registers.  Walks innermost
+   loops in BFS order; when a register dying in one insn is redefined
+   far enough later, emits a zeroing idiom at the kill point as a hint
+   to the hardware renamer.  Gated on TARGET_AVX and
+   X86_TUNE_INSERT_REG_FREE_ZERO_IDIOM (m_ZNVER).  */
+
+namespace {
+
+#define LOOP_INSN_THRESHOLD ((int) param_reg_free_zero_loop_threshold)
+#define DEAD_GAP_THRESHOLD  ((int) param_reg_free_zero_gap_threshold)
+
+struct deferred_insertion
+{
+  rtx_insn *after;
+  unsigned int regno;
+};
+
+const pass_data pass_data_insert_reg_free_zero_idiom =
+{
+  RTL_PASS, /* type */
+  "insert_reg_free_zero_idiom", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_MACH_DEP, /* tv_id */
+  0, /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  TODO_df_finish, /* todo_flags_finish */
+};
+
+class pass_insert_reg_free_zero_idiom : public rtl_opt_pass
+{
+public:
+  pass_insert_reg_free_zero_idiom (gcc::context *ctxt)
+    : rtl_opt_pass (pass_data_insert_reg_free_zero_idiom, ctxt)
+  {}
+
+  bool gate (function *) final override
+  {
+    return TARGET_AVX
+      && ix86_tune_features[X86_TUNE_INSERT_REG_FREE_ZERO_IDIOM];
+  }
+
+  unsigned int execute (function *) final override;
+
+private:
+  void insert_zero_idiom_after (rtx_insn *after_insn, unsigned int regno);
+};
+
+/* Emit a self-XOR of SSE register REGNO after AFTER_INSN.  */
+void
+pass_insert_reg_free_zero_idiom::insert_zero_idiom_after (rtx_insn *after_insn,
+                                                         unsigned int regno)
+{
+  if (JUMP_P (after_insn) || CALL_P (after_insn))
+    return;
+
+  machine_mode mode;
+  if (EXT_REX_SSE_REGNO_P (regno) && !TARGET_AVX512VL)
+    mode = V16SImode;
+  else
+    mode = V4SImode;
+
+  rtx reg = gen_rtx_REG (mode, regno);
+  rtx pat = gen_rtx_SET (reg, gen_rtx_XOR (mode, reg, reg));
+  rtx_insn *new_insn = emit_insn_after (pat, after_insn);
+
+  if (dump_file)
+    {
+      fprintf (dump_file,
+              "Inserted reg-free zero idiom for reg %d (insn %d) "
+              "after insn %d\n",
+              regno, INSN_UID (new_insn), INSN_UID (after_insn));
+      print_rtl_single (dump_file, new_insn);
+    }
+}
+
+unsigned int
+pass_insert_reg_free_zero_idiom::execute (function *fun)
+{
+  loop_optimizer_init (AVOID_CFG_MODIFICATIONS);
+  df_note_add_problem ();
+  df_analyze ();
+
+  rtx_insn **dead_insn = XCNEWVEC (rtx_insn *, FIRST_PSEUDO_REGISTER);
+  int *dead_at = XCNEWVEC (int, FIRST_PSEUDO_REGISTER);
+  int *bb_entry_count = XCNEWVEC (int, last_basic_block_for_fn (fun));
+  auto_vec<basic_block> bbs;
+  auto_vec<deferred_insertion> insertions;
+
+  for (auto curr_loop : loops_list (fun, LI_ONLY_INNERMOST))
+    {
+      if (num_loop_insns (curr_loop) < LOOP_INSN_THRESHOLD)
+       continue;
+
+      memset (dead_insn, 0, FIRST_PSEUDO_REGISTER * sizeof (rtx_insn *));
+      memset (dead_at, 0, FIRST_PSEUDO_REGISTER * sizeof (int));
+      memset (bb_entry_count, 0,
+             last_basic_block_for_fn (fun) * sizeof (int));
+      bbs.truncate (0);
+      insertions.truncate (0);
+
+      auto_bitmap visited;
+      bbs.safe_push (curr_loop->header);
+      bitmap_set_bit (visited, curr_loop->header->index);
+      unsigned int bfs_head = 0;
+
+      while (bfs_head < bbs.length ())
+       {
+         basic_block bb = bbs[bfs_head++];
+         int insn_count = bb_entry_count[bb->index];
+
+         rtx_insn *insn;
+         FOR_BB_INSNS (bb, insn)
+           {
+             if (!INSN_P (insn))
+               continue;
+
+             df_ref use;
+             FOR_EACH_INSN_USE (use, insn)
+               {
+                 unsigned int regno = DF_REF_REGNO (use);
+                 if (!SSE_REGNO_P (regno))
+                   continue;
+
+                 if (find_regno_note (insn, REG_DEAD, regno))
+                   {
+                     dead_insn[regno] = insn;
+                     dead_at[regno] = insn_count;
+                   }
+               }
+
+             df_ref def;
+             FOR_EACH_INSN_DEF (def, insn)
+               {
+                 unsigned int regno = DF_REF_REGNO (def);
+                 if (!SSE_REGNO_P (regno))
+                   continue;
+
+                 if (dead_insn[regno])
+                   {
+                     int gap = insn_count - dead_at[regno];
+
+                     if (gap > DEAD_GAP_THRESHOLD)
+                       {
+                         if (dump_file)
+                           fprintf (dump_file,
+                                    "BB %d: reg %d dead at insn %d, "
+                                    "redef at insn %d, gap=%d\n",
+                                    bb->index, regno,
+                                    INSN_UID (dead_insn[regno]),
+                                    INSN_UID (insn), gap);
+                         deferred_insertion d
+                           = { dead_insn[regno], regno };
+                         insertions.safe_push (d);
+                       }
+                     dead_insn[regno] = NULL;
+                   }
+               }
+
+             insn_count++;
+           }
+
+         edge e;
+         edge_iterator ei;
+         FOR_EACH_EDGE (e, ei, bb->succs)
+           {
+             if (!flow_bb_inside_loop_p (curr_loop, e->dest))
+               continue;
+
+             if (bitmap_set_bit (visited, e->dest->index))
+               {
+                 bbs.safe_push (e->dest);
+                 bb_entry_count[e->dest->index] = insn_count;
+               }
+             else if (insn_count > bb_entry_count[e->dest->index])
+               {
+                 bb_entry_count[e->dest->index] = insn_count;
+               }
+           }
+       }
+
+      int loop_zero_count = 0;
+      for (unsigned j = 0; j < insertions.length (); j++)
+       {
+         insert_zero_idiom_after (insertions[j].after, insertions[j].regno);
+         loop_zero_count++;
+       }
+
+      if (dump_file && loop_zero_count > 0)
+       fprintf (dump_file,
+                "Loop %d: inserted %d reg-free zero idiom(s)\n",
+                curr_loop->num, loop_zero_count);
+    }
+
+  free (dead_insn);
+  free (dead_at);
+  free (bb_entry_count);
+  loop_optimizer_finalize ();
+  return 0;
+}
+
+#undef LOOP_INSN_THRESHOLD
+#undef DEAD_GAP_THRESHOLD
+
+} // anon namespace
+
+rtl_opt_pass *
+make_pass_insert_reg_free_zero_idiom (gcc::context *ctxt)
+{
+  return new pass_insert_reg_free_zero_idiom (ctxt);
+}
+
 /* Inserting ENDBR and pseudo patchable-area instructions.  */
 
 static void
diff --git a/gcc/config/i386/i386-passes.def b/gcc/config/i386/i386-passes.def
index 4f461116cac..64e287c86d2 100644
--- a/gcc/config/i386/i386-passes.def
+++ b/gcc/config/i386/i386-passes.def
@@ -38,3 +38,4 @@ along with GCC; see the file COPYING3.  If not see
   INSERT_PASS_AFTER (pass_late_combine, 1, pass_x86_cse);
   INSERT_PASS_AFTER (pass_late_combine, 1, pass_remove_partial_avx_dependency);
   INSERT_PASS_AFTER (pass_rtl_ifcvt, 1, pass_apx_nf_convert);
+  INSERT_PASS_BEFORE (pass_shorten_branches, 1, 
pass_insert_reg_free_zero_idiom);
diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h
index d11fa2df35d..5627c47ac80 100644
--- a/gcc/config/i386/i386-protos.h
+++ b/gcc/config/i386/i386-protos.h
@@ -438,6 +438,7 @@ extern rtl_opt_pass *make_pass_remove_partial_avx_dependency
 extern rtl_opt_pass *make_pass_x86_cse (gcc::context *);
 extern rtl_opt_pass *make_pass_apx_nf_convert (gcc::context *);
 extern rtl_opt_pass *make_pass_align_tight_loops (gcc::context *);
+extern rtl_opt_pass *make_pass_insert_reg_free_zero_idiom (gcc::context *);
 
 extern bool ix86_has_no_direct_extern_access;
 extern bool ix86_rpad_gate ();
diff --git a/gcc/config/i386/x86-tune.def b/gcc/config/i386/x86-tune.def
index d4c27351ad7..d7787f6b984 100644
--- a/gcc/config/i386/x86-tune.def
+++ b/gcc/config/i386/x86-tune.def
@@ -812,3 +812,9 @@ DEF_TUNE (X86_TUNE_SLOW_STC, "slow_stc", m_PENT4)
 
 /* X86_TUNE_USE_RCR: Controls use of rcr 1 instruction instead of shrd.  */
 DEF_TUNE (X86_TUNE_USE_RCR, "use_rcr", m_AMD_MULTIPLE | m_C86_4G)
+
+/* X86_TUNE_INSERT_REG_FREE_ZERO_IDIOM: Insert register-free zero idiom
+   instructions to break false dependencies on vector registers for
+   Zen microarchitectures.  */
+DEF_TUNE (X86_TUNE_INSERT_REG_FREE_ZERO_IDIOM, "insert_reg_free_zero_idiom",
+         m_ZNVER)
diff --git a/gcc/params.opt b/gcc/params.opt
index 90f9943c8cb..bc9b688d059 100644
--- a/gcc/params.opt
+++ b/gcc/params.opt
@@ -1323,4 +1323,12 @@ Maximum number of outgoing edges in a switch before VRP 
does not process it.
 Common Joined UInteger Var(param_vrp_vector_threshold) Init(250) Optimization 
Param
 Maximum number of basic blocks for VRP to use a basic cache vector.
 
+-param=reg-free-zero-loop-threshold=
+Common Joined UInteger Var(param_reg_free_zero_loop_threshold) Init(500) 
IntegerRange(0, 65536) Param Optimization
+Minimum number of RTL instructions in a loop for the 
insert_reg_free_zero_idiom pass to consider it.
+
+-param=reg-free-zero-gap-threshold=
+Common Joined UInteger Var(param_reg_free_zero_gap_threshold) Init(80) 
IntegerRange(0, 65536) Param Optimization
+Minimum instruction gap between last use and redefinition of an SSE register 
for insert_reg_free_zero_idiom to insert a zero idiom.
+
 ; This comment is to ensure we retain the blank line above.
diff --git a/gcc/testsuite/gcc.target/i386/reg-free-zero-1.c 
b/gcc/testsuite/gcc.target/i386/reg-free-zero-1.c
new file mode 100644
index 00000000000..ce27cea8c62
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/reg-free-zero-1.c
@@ -0,0 +1,31 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -mavx2 -mtune-ctrl=insert_reg_free_zero_idiom 
--param=reg-free-zero-loop-threshold=10 --param=reg-free-zero-gap-threshold=5 
-fdump-rtl-insert_reg_free_zero_idiom" } */
+/* { dg-final { scan-rtl-dump-times "Inserted reg-free zero idiom" 1 
"insert_reg_free_zero_idiom" } } */
+/* { dg-final { scan-assembler-times "vpxor\[ \\t\]+\[^\n\]*%xmm" 1 } } */
+
+typedef float v8sf __attribute__((vector_size (32)));
+
+void __attribute__((noipa))
+test (float *__restrict a, float *__restrict b, float *__restrict c,
+      float *__restrict s, int n)
+{
+  for (int i = 0; i < n; i += 15)
+    {
+      v8sf vb = *(v8sf *)(b + i);
+      v8sf vc = *(v8sf *)(c + i);
+      *(v8sf *)(a + i) = vb * vc;
+
+      volatile float *p = a + i;
+      int t = (int) p[0];
+      t = (t >> 1) ^ (t * 5);
+      t = (t >> 2) ^ (t * 9);
+      t = (t >> 1) ^ (t * 3);
+      t = (t >> 3) ^ (t * 7);
+      t = (t >> 2) ^ (t * 11);
+      t = (t >> 1) ^ (t * 13);
+      s[i] = (float) t;
+
+      v8sf vb2 = *(v8sf *)(b + i + 8);
+      *(v8sf *)(a + i + 8) = vb2 + vc;
+    }
+}
diff --git a/gcc/testsuite/gcc.target/i386/reg-free-zero-2.c 
b/gcc/testsuite/gcc.target/i386/reg-free-zero-2.c
new file mode 100644
index 00000000000..b20266c18e7
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/reg-free-zero-2.c
@@ -0,0 +1,31 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -mavx2 -mtune-ctrl=insert_reg_free_zero_idiom 
--param=reg-free-zero-loop-threshold=10 --param=reg-free-zero-gap-threshold=5 
-fdump-rtl-insert_reg_free_zero_idiom" } */
+/* { dg-final { scan-rtl-dump-times "Inserted reg-free zero idiom" 1 
"insert_reg_free_zero_idiom" } } */
+/* { dg-final { scan-assembler-times "vpxor\[ \\t\]+\[^\n\]*%xmm" 1 } } */
+
+typedef float v8sf __attribute__((vector_size (32)));
+
+void __attribute__((noipa))
+test (float *__restrict a, float *__restrict b, float *__restrict c,
+      float *__restrict s, int n)
+{
+  for (int i = 0; i < n; i += 15)
+    {
+      v8sf vb = *(v8sf *)(b + i);
+      *(v8sf *)(a + i) = vb * vb;
+
+      volatile float *p = a + i;
+      int t = (int) p[0];
+      t = (t >> 1) ^ (t * 5);
+      t = (t >> 2) ^ (t * 9);
+      t = (t >> 1) ^ (t * 3);
+      t = (t >> 3) ^ (t * 7);
+      t = (t >> 2) ^ (t * 11);
+      t = (t >> 1) ^ (t * 13);
+      s[i] = (float) t;
+
+      v8sf va = *(v8sf *)(a + i + 8);
+      v8sf vc = *(v8sf *)(c + i + 8);
+      *(v8sf *)(a + i + 8) = va * vc + va;
+    }
+}
diff --git a/gcc/testsuite/gcc.target/i386/reg-free-zero-3.c 
b/gcc/testsuite/gcc.target/i386/reg-free-zero-3.c
new file mode 100644
index 00000000000..aa64ae20b66
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/reg-free-zero-3.c
@@ -0,0 +1,36 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -mavx2 -mtune-ctrl=insert_reg_free_zero_idiom 
--param=reg-free-zero-loop-threshold=10 --param=reg-free-zero-gap-threshold=5 
-fdump-rtl-insert_reg_free_zero_idiom" } */
+/* { dg-final { scan-rtl-dump-times "Inserted reg-free zero idiom" 1 
"insert_reg_free_zero_idiom" } } */
+/* { dg-final { scan-assembler-times "vpxor\[ \\t\]+\[^\n\]*%xmm" 1 } } */
+
+typedef float v8sf __attribute__((vector_size (32)));
+
+void __attribute__((noipa))
+test (float *__restrict a, float *__restrict b, float *__restrict c,
+      float *__restrict s, int n, int cond)
+{
+  for (int i = 0; i < n; i += 15)
+    {
+      v8sf vb = *(v8sf *)(b + i);
+      v8sf vc = *(v8sf *)(c + i);
+      *(v8sf *)(a + i) = vb * vc;
+
+      if (cond)
+        {
+      volatile float *p = a + i;
+      int t = (int) p[0];
+      t = (t >> 1) ^ (t * 5);
+      t = (t >> 2) ^ (t * 9);
+      t = (t >> 1) ^ (t * 3);
+      t = (t >> 3) ^ (t * 7);
+      t = (t >> 2) ^ (t * 11);
+      t = (t >> 1) ^ (t * 13);
+      s[i] = (float) t;
+        }
+      else
+        s[i] = (float) (cond * 2);
+
+      v8sf vb2 = *(v8sf *)(b + i + 8);
+      *(v8sf *)(a + i + 8) = vb2 + vc;
+    }
+}
diff --git a/gcc/testsuite/gcc.target/i386/reg-free-zero-4.c 
b/gcc/testsuite/gcc.target/i386/reg-free-zero-4.c
new file mode 100644
index 00000000000..782ec9994c8
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/reg-free-zero-4.c
@@ -0,0 +1,30 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -mavx2 -mtune-ctrl=insert_reg_free_zero_idiom 
--param=reg-free-zero-loop-threshold=2 
--param=reg-free-zero-gap-threshold=10000 
-fdump-rtl-insert_reg_free_zero_idiom" } */
+/* { dg-final { scan-rtl-dump-not "Inserted reg-free zero idiom" 
"insert_reg_free_zero_idiom" } } */
+
+typedef float v8sf __attribute__((vector_size (32)));
+
+void __attribute__((noipa))
+test (float *__restrict a, float *__restrict b, float *__restrict c,
+      float *__restrict s, int n)
+{
+  for (int i = 0; i < n; i += 15)
+    {
+      v8sf vb = *(v8sf *)(b + i);
+      v8sf vc = *(v8sf *)(c + i);
+      *(v8sf *)(a + i) = vb * vc;
+
+      volatile float *p = a + i;
+      int t = (int) p[0];
+      t = (t >> 1) ^ (t * 5);
+      t = (t >> 2) ^ (t * 9);
+      t = (t >> 1) ^ (t * 3);
+      t = (t >> 3) ^ (t * 7);
+      t = (t >> 2) ^ (t * 11);
+      t = (t >> 1) ^ (t * 13);
+      s[i] = (float) t;
+
+      v8sf vb2 = *(v8sf *)(b + i + 8);
+      *(v8sf *)(a + i + 8) = vb2 + vc;
+    }
+}
diff --git a/gcc/testsuite/gcc.target/i386/reg-free-zero-5.c 
b/gcc/testsuite/gcc.target/i386/reg-free-zero-5.c
new file mode 100644
index 00000000000..9e48950639e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/reg-free-zero-5.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -mavx2 -mtune-ctrl=insert_reg_free_zero_idiom 
--param=reg-free-zero-loop-threshold=10000 
--param=reg-free-zero-gap-threshold=1 -fdump-rtl-insert_reg_free_zero_idiom" } 
*/
+/* { dg-final { scan-rtl-dump-not "Inserted reg-free zero idiom" 
"insert_reg_free_zero_idiom" } } */
+
+typedef float v8sf __attribute__((vector_size (32)));
+
+void __attribute__((noipa))
+test (float *__restrict a, int n)
+{
+  for (int i = 0; i < n; i++)
+    a[i] = a[i] + 1.0f;
+}
diff --git a/gcc/testsuite/gcc.target/i386/reg-free-zero-6.c 
b/gcc/testsuite/gcc.target/i386/reg-free-zero-6.c
new file mode 100644
index 00000000000..57bb9303f28
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/reg-free-zero-6.c
@@ -0,0 +1,71 @@
+/* { dg-do run { target avx_runtime } } */
+/* { dg-options "-O3 -mavx2 -mtune-ctrl=insert_reg_free_zero_idiom 
--param=reg-free-zero-loop-threshold=10 --param=reg-free-zero-gap-threshold=5 
-fdump-rtl-insert_reg_free_zero_idiom -save-temps" } */
+/* { dg-final { scan-rtl-dump-times "Inserted reg-free zero idiom" 2 
"insert_reg_free_zero_idiom" } } */
+/* { dg-final { scan-assembler-times "vpxor\[ \\t\]+\[^\n\]*%xmm" 2 } } */
+
+#include <stdlib.h>
+
+#define N 256
+
+static void __attribute__((noipa))
+work (float *__restrict a, float *__restrict b, float *__restrict c,
+      float *__restrict s, int n)
+{
+  for (int i = 0; i + 16 <= n; i += 15)
+    {
+      for (int k = 0; k < 8; k++)
+       a[i + k] = b[i + k] * c[i + k];
+
+      volatile float *p = a + i;
+      int t = (int) p[0];
+      t = (t >> 1) ^ (t * 5);
+      t = (t >> 2) ^ (t * 9);
+      t = (t >> 1) ^ (t * 3);
+      t = (t >> 3) ^ (t * 7);
+      t = (t >> 2) ^ (t * 11);
+      t = (t >> 1) ^ (t * 13);
+      s[i] = (float) t;
+
+      for (int k = 0; k < 8; k++)
+       a[i + 8 + k] = b[i + 8 + k] + c[i + k];
+    }
+}
+
+int
+main (void)
+{
+  static float a[N], b[N], c[N], s[N];
+  static float a_ref[N], s_ref[N];
+
+  for (int i = 0; i < N; i++)
+    {
+      b[i] = (float) (i + 1);
+      c[i] = (float) ((i * 3) + 2);
+    }
+
+  for (int i = 0; i + 16 <= N; i += 15)
+    {
+      for (int k = 0; k < 8; k++)
+       a_ref[i + k] = b[i + k] * c[i + k];
+
+      int t = (int) a_ref[i];
+      t = (t >> 1) ^ (t * 5);
+      t = (t >> 2) ^ (t * 9);
+      t = (t >> 1) ^ (t * 3);
+      t = (t >> 3) ^ (t * 7);
+      t = (t >> 2) ^ (t * 11);
+      t = (t >> 1) ^ (t * 13);
+      s_ref[i] = (float) t;
+
+      for (int k = 0; k < 8; k++)
+       a_ref[i + 8 + k] = b[i + 8 + k] + c[i + k];
+    }
+
+  work (a, b, c, s, N);
+
+  for (int i = 0; i < N; i++)
+    if (a[i] != a_ref[i] || s[i] != s_ref[i])
+      abort ();
+
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/i386/reg-free-zero-7.c 
b/gcc/testsuite/gcc.target/i386/reg-free-zero-7.c
new file mode 100644
index 00000000000..529a8bcbc3b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/reg-free-zero-7.c
@@ -0,0 +1,29 @@
+/* { dg-do compile { target { ! ia32 } } } */
+/* { dg-options "-O3 -mavx512vl -ffixed-xmm0 -ffixed-xmm1 -ffixed-xmm2 
-ffixed-xmm3 -ffixed-xmm4 -ffixed-xmm5 -ffixed-xmm6 -ffixed-xmm7 -ffixed-xmm8 
-ffixed-xmm9 -ffixed-xmm10 -ffixed-xmm11 -ffixed-xmm12 -ffixed-xmm13 
-ffixed-xmm14 -ffixed-xmm15 -mtune-ctrl=insert_reg_free_zero_idiom 
--param=reg-free-zero-loop-threshold=1 --param=reg-free-zero-gap-threshold=1 
-fdump-rtl-insert_reg_free_zero_idiom" } */
+/* { dg-final { scan-rtl-dump "Inserted reg-free zero idiom" 
"insert_reg_free_zero_idiom" } } */
+/* { dg-final { scan-assembler "vpxord\[ 
\\t\]+%xmm(1\[6-9\]|2\[0-9\]|3\[01\])" } } */
+
+typedef long long v8di __attribute__((vector_size (64)));
+
+void
+test (long long *__restrict a, long long *__restrict b,
+      long long *__restrict c, long long *__restrict s, int n)
+{
+  for (int i = 0; i < n; i += 15)
+    {
+      v8di vb = *(v8di *) (b + i);
+      v8di vc = *(v8di *) (c + i);
+      *(v8di *) (a + i) = vb * vc;
+
+      v8di g = *(v8di *) (a + i);
+      g = (g >> 1) ^ (g + g);
+      g = (g >> 2) ^ (g + g);
+      g = (g >> 1) ^ (g + g);
+      g = (g >> 3) ^ (g + g);
+      g = (g >> 2) ^ (g + g);
+      *(v8di *) (s + i) = g;
+
+      v8di vb2 = *(v8di *) (b + i + 8);
+      *(v8di *) (a + i + 8) = vb2 + vc;
+    }
+}
diff --git a/gcc/testsuite/gcc.target/i386/reg-free-zero-8.c 
b/gcc/testsuite/gcc.target/i386/reg-free-zero-8.c
new file mode 100644
index 00000000000..f568f2b4793
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/reg-free-zero-8.c
@@ -0,0 +1,29 @@
+/* { dg-do compile { target { ! ia32 } } } */
+/* { dg-options "-O3 -mavx512f -mno-avx512vl -ffixed-xmm0 -ffixed-xmm1 
-ffixed-xmm2 -ffixed-xmm3 -ffixed-xmm4 -ffixed-xmm5 -ffixed-xmm6 -ffixed-xmm7 
-ffixed-xmm8 -ffixed-xmm9 -ffixed-xmm10 -ffixed-xmm11 -ffixed-xmm12 
-ffixed-xmm13 -ffixed-xmm14 -ffixed-xmm15 
-mtune-ctrl=insert_reg_free_zero_idiom --param=reg-free-zero-loop-threshold=1 
--param=reg-free-zero-gap-threshold=1 -fdump-rtl-insert_reg_free_zero_idiom" } 
*/
+/* { dg-final { scan-rtl-dump "Inserted reg-free zero idiom" 
"insert_reg_free_zero_idiom" } } */
+/* { dg-final { scan-assembler "vpxord\[ 
\\t\]+%zmm(1\[6-9\]|2\[0-9\]|3\[01\])" } } */
+
+typedef long long v8di __attribute__((vector_size (64)));
+
+void
+test (long long *__restrict a, long long *__restrict b,
+      long long *__restrict c, long long *__restrict s, int n)
+{
+  for (int i = 0; i < n; i += 15)
+    {
+      v8di vb = *(v8di *) (b + i);
+      v8di vc = *(v8di *) (c + i);
+      *(v8di *) (a + i) = vb * vc;
+
+      v8di g = *(v8di *) (a + i);
+      g = (g >> 1) ^ (g + g);
+      g = (g >> 2) ^ (g + g);
+      g = (g >> 1) ^ (g + g);
+      g = (g >> 3) ^ (g + g);
+      g = (g >> 2) ^ (g + g);
+      *(v8di *) (s + i) = g;
+
+      v8di vb2 = *(v8di *) (b + i + 8);
+      *(v8di *) (a + i + 8) = vb2 + vc;
+    }
+}
-- 
2.34.1

Reply via email to