From: Kyrylo Tkachov <[email protected]>
The region scheduler initializes one deps_desc for each basic block in a
region. Each initialization allocates and zeroes max_reg deps_reg entries.
The total allocation and zeroing work is
O (max_reg * sum (RGN_NR_BLOCKS (region))).
Before reload, max_reg is max_reg_num (). This work can therefore grow
quadratically with function size.
free_deps now empties each entry that was written. Keep these all-zero
arrays in a pool keyed by max_reg and reuse them in later regions. Reset
the list length fields before returning an array. Under -fchecking=2,
verify that every field is zero. Drain the pool in sched_deps_finish.
Selective scheduling does not return arrays to the pool because
remove_from_deps can leave an untracked control-use list.
Add a selftest that fills every list field and both length fields. It
verifies that free_deps returns the same array through the pool with all
fields cleared. Also add a compile test that drives a debug-only use
through pool release and reuse under -fchecking=2. Disabling the pool makes
the selftest fail. Dropping the reg_last_dirty recording from patch 2 makes
the compile test ICE.
On an aarch64 system, I saw a ~1.5% improvement in compile speed of
gimple-match-6 at -O3.
Generated assembly doesn't change.
Bootstrapped and tested on aarch64-none-linux-gnu and x86_64-linux.
gcc/ChangeLog:
* sched-deps.cc (reg_last_pool, reg_last_pool_max_reg): New.
(alloc_reg_last, release_reg_last): New.
(init_deps, init_deps_reg_last): Use alloc_reg_last.
(free_deps): Reset the list length fields and release reg_last to the
pool.
(sched_deps_finish): Release the pooled arrays.
(selftest::test_reg_last_pool): New.
(selftest::sched_deps_cc_tests): Run it.
gcc/testsuite/ChangeLog:
* gcc.dg/sched-deps-debug-1.c: New test.
Signed-off-by: Kyrylo Tkachov <[email protected]>
---
gcc/sched-deps.cc | 129 +++++++++++++++++++++-
gcc/testsuite/gcc.dg/sched-deps-debug-1.c | 27 +++++
2 files changed, 153 insertions(+), 3 deletions(-)
create mode 100644 gcc/testsuite/gcc.dg/sched-deps-debug-1.c
diff --git a/gcc/sched-deps.cc b/gcc/sched-deps.cc
index 3a170d87b7e..4911b65bba1 100644
--- a/gcc/sched-deps.cc
+++ b/gcc/sched-deps.cc
@@ -3930,6 +3930,61 @@ sched_free_deps (rtx_insn *head, rtx_insn *tail, bool resolved_p)
}
}
+/* Pool of all-zero reg_last arrays. init_deps takes one and free_deps
+ returns it, so the O (MAX_REG) zeroing is paid once per pooled array rather
+ than once per basic block of every region. free_deps empties every entry
+ that was written, so an array coming back is already zero. Selective
+ scheduling is excluded: its remove_from_deps can drop a reg_last_in_use bit
+ while control_uses is still live, which would return a dirty array. */
+static vec<struct deps_reg *> reg_last_pool;
+static int reg_last_pool_max_reg;
+
+/* Return an all-zero array of MAX_REG deps_reg, from the pool if one of the
+ right size is available. */
+
+static struct deps_reg *
+alloc_reg_last (int max_reg)
+{
+ if (max_reg != reg_last_pool_max_reg)
+ {
+ while (!reg_last_pool.is_empty ())
+ free (reg_last_pool.pop ());
+ reg_last_pool_max_reg = max_reg;
+ }
+ else if (!reg_last_pool.is_empty ())
+ return reg_last_pool.pop ();
+
+ return XCNEWVEC (struct deps_reg, max_reg);
+}
+
+/* Give REG_LAST, an array of MAX_REG deps_reg that free_deps has just
+ emptied, back to the pool. */
+
+static void
+release_reg_last (struct deps_reg *reg_last, int max_reg)
+{
+ if (reg_last == NULL)
+ return;
+
+ if (sel_sched_p () || max_reg != reg_last_pool_max_reg)
+ {
+ free (reg_last);
+ return;
+ }
+
+ if (flag_checking > 1)
+ for (int i = 0; i < max_reg; i++)
+ gcc_assert (reg_last[i].uses == NULL
+ && reg_last[i].sets == NULL
+ && reg_last[i].implicit_sets == NULL
+ && reg_last[i].control_uses == NULL
+ && reg_last[i].clobbers == NULL
+ && reg_last[i].uses_length == 0
+ && reg_last[i].clobbers_length == 0);
+
+ reg_last_pool.safe_push (reg_last);
+}
+
/* Initialize variables for region data dependence analysis.
When LAZY_REG_LAST is true, do not allocate reg_last array
of class deps_desc immediately. */
@@ -3943,7 +3998,7 @@ init_deps (class deps_desc *deps, bool lazy_reg_last)
if (lazy_reg_last)
deps->reg_last = NULL;
else
- deps->reg_last = XCNEWVEC (struct deps_reg, max_reg);
+ deps->reg_last = alloc_reg_last (max_reg);
INIT_REG_SET (&deps->reg_last_in_use);
INIT_REG_SET (&deps->reg_last_dirty);
@@ -3978,7 +4033,7 @@ init_deps_reg_last (class deps_desc *deps)
gcc_assert (deps && deps->max_reg > 0);
gcc_assert (deps->reg_last == NULL);
- deps->reg_last = XCNEWVEC (struct deps_reg, deps->max_reg);
+ deps->reg_last = alloc_reg_last (deps->max_reg);
}
@@ -3996,6 +4051,7 @@ free_deps (class deps_desc *deps)
gcc_assert (deps->reg_last == NULL);
return;
}
+ int max_reg = deps->max_reg;
deps->max_reg = 0;
free_INSN_LIST_list (&deps->pending_read_insns);
@@ -4025,13 +4081,15 @@ free_deps (class deps_desc *deps)
free_INSN_LIST_list (®_last->control_uses);
if (reg_last->clobbers)
free_INSN_LIST_list (®_last->clobbers);
+ reg_last->uses_length = 0;
+ reg_last->clobbers_length = 0;
}
CLEAR_REG_SET (&deps->reg_last_in_use);
CLEAR_REG_SET (&deps->reg_last_dirty);
/* As we initialize reg_last lazily, it is possible that we didn't allocate
it at all. */
- free (deps->reg_last);
+ release_reg_last (deps->reg_last, max_reg);
deps->reg_last = NULL;
deps = NULL;
@@ -4183,6 +4241,11 @@ sched_deps_finish (void)
h_d_i_d.release ();
+ while (!reg_last_pool.is_empty ())
+ free (reg_last_pool.pop ());
+ reg_last_pool.release ();
+ reg_last_pool_max_reg = 0;
+
if (true_dependency_cache)
{
int i;
@@ -5079,12 +5142,72 @@ test_dirty_reg_last_release ()
bitmap_obstack_release (&test_obstack);
}
+/* Verify that a pooled reg_last array is empty when it is reused. */
+
+static void
+test_reg_last_pool ()
+{
+ const int max_reg = 3;
+ ASSERT_TRUE (reg_last_pool.is_empty ());
+
+ bitmap_obstack test_obstack;
+ bitmap_obstack_initialize (&test_obstack);
+
+ deps_desc deps = {};
+ deps.max_reg = max_reg;
+ deps.reg_last = alloc_reg_last (max_reg);
+ struct deps_reg *saved_reg_last = deps.reg_last;
+ bitmap_initialize (&deps.reg_last_in_use, &test_obstack);
+ bitmap_initialize (&deps.reg_last_dirty, &test_obstack);
+
+ deps.reg_last[0].uses = alloc_INSN_LIST (NULL_RTX, NULL_RTX);
+ deps.reg_last[0].sets = alloc_INSN_LIST (NULL_RTX, NULL_RTX);
+ deps.reg_last[0].implicit_sets = alloc_INSN_LIST (NULL_RTX, NULL_RTX);
+ deps.reg_last[0].clobbers = alloc_INSN_LIST (NULL_RTX, NULL_RTX);
+ deps.reg_last[0].uses_length = 2;
+ deps.reg_last[0].clobbers_length = 3;
+ SET_REGNO_REG_SET (&deps.reg_last_in_use, 0);
+
+ deps.reg_last[1].control_uses = alloc_INSN_LIST (NULL_RTX, NULL_RTX);
+ SET_REGNO_REG_SET (&deps.reg_last_dirty, 1);
+
+ common_sched_info_def sched_info = {};
+ sched_info.sched_pass_id = SCHED_RGN_PASS;
+ common_sched_info_def *saved_common_sched_info = common_sched_info;
+ common_sched_info = &sched_info;
+ free_deps (&deps);
+
+ ASSERT_EQ (0, deps.max_reg);
+ ASSERT_EQ (NULL, deps.reg_last);
+ ASSERT_EQ (1, reg_last_pool.length ());
+
+ struct deps_reg *reused_reg_last = alloc_reg_last (max_reg);
+ ASSERT_EQ (saved_reg_last, reused_reg_last);
+ ASSERT_TRUE (reg_last_pool.is_empty ());
+ for (int i = 0; i < max_reg; ++i)
+ {
+ ASSERT_EQ (NULL, reused_reg_last[i].uses);
+ ASSERT_EQ (NULL, reused_reg_last[i].sets);
+ ASSERT_EQ (NULL, reused_reg_last[i].implicit_sets);
+ ASSERT_EQ (NULL, reused_reg_last[i].control_uses);
+ ASSERT_EQ (NULL, reused_reg_last[i].clobbers);
+ ASSERT_EQ (0, reused_reg_last[i].uses_length);
+ ASSERT_EQ (0, reused_reg_last[i].clobbers_length);
+ }
+
+ free (reused_reg_last);
+ sched_deps_finish ();
+ common_sched_info = saved_common_sched_info;
+ bitmap_obstack_release (&test_obstack);
+}
+
/* Run the sched-deps.cc selftests. */
void
sched_deps_cc_tests ()
{
test_dirty_reg_last_release ();
+ test_reg_last_pool ();
}
} // namespace selftest
diff --git a/gcc/testsuite/gcc.dg/sched-deps-debug-1.c b/gcc/testsuite/gcc.dg/sched-deps-debug-1.c
new file mode 100644
index 00000000000..2b5cb7b7ad0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/sched-deps-debug-1.c
@@ -0,0 +1,27 @@
+/* A debug insn records a use in a reg_last entry that reg_last_in_use does not
+ cover. free_deps has to release those as well, or the reg_last array it
+ hands back still points at the freed region's insn lists. -fchecking=2
+ checks an array before it is reused. */
+
+/* { dg-do compile } */
+/* { dg-options "-O2 -g -fschedule-insns -fchecking=2" } */
+/* { dg-require-effective-target scheduling } */
+
+void f1 (double);
+void f2 (int);
+
+void
+foo (int type, double xx)
+{
+ /* On the f2 path xx is dead, so what is left of it is a debug insn. */
+ if (type)
+ f1 (xx);
+ else
+ f2 (type);
+}
+
+void
+bar (int type)
+{
+ foo (type, 1.0);
+}
--
2.50.1 (Apple Git-155)
lmpx.com only provides a reader for public news (NNTP) servers. It is not
affiliated with the servers or forums shown here and is not responsible for
the content of articles, which is written by their respective authors.