[gcc r17-2920] sched-deps: release reg_last lists outside reg_last_in_use
Kyrylo Tkachov via Gcc-cvs <[email protected]> Tue, 4 Aug 2026 08:15:17 +0000 (GMT)
| Newsgroups | gmane.comp.gcc.cvs |
|---|---|
| Message-ID | <[email protected]> |
https://gcc.gnu.org/g:ce720fc2b7e70fe284f5b6159e4088dc6e8844c0 commit r17-2920-gce720fc2b7e70fe284f5b6159e4088dc6e8844c0 Author: Kyrylo Tkachov <[email protected]> Date: Mon Jul 27 20:04:22 2026 +0200 sched-deps: release reg_last lists outside reg_last_in_use reg_last_in_use does not record every reg_last entry that contains a list. The debug-insn path adds uses and then clears reg_pending_uses before the normal update of reg_last_in_use. The control-use path also adds lists without updating reg_last_in_use. free_deps only walks reg_last_in_use, so these INSN_LIST nodes are not returned to the recycler. Add a teardown-only reg_last_dirty regset for these entries. Merge it into reg_last_in_use in free_deps, then release all lists in the existing loop. Do not use reg_last_dirty for dependence generation. Adding these entries to reg_last_in_use during analysis would make barrier handling create new anti-dependences for debug uses and control dependences for control uses. Add a selftest that puts a use list and a control-use list in a dirty-only entry. It calls free_deps and verifies that the INSN_LIST recycler returns both nodes. Ignoring the dirty-only entry makes this selftest fail. Bootstrapped and regtested on aarch64-none-linux-gnu and x86_64-linux. gcc/ChangeLog: * sched-int.h (struct deps_desc): Add reg_last_dirty. * sched-deps.cc: Include selftest.h. (sched_analyze_insn): Record debug-insn uses and control uses in reg_last_dirty. (init_deps): Initialize reg_last_dirty. (free_deps): Merge reg_last_dirty into reg_last_in_use for teardown, then clear it. (selftest::test_dirty_reg_last_release): New. (selftest::sched_deps_cc_tests): New. Provide an empty definition when INSN_SCHEDULING is not defined. * selftest-run-tests.cc (selftest::run_tests): Call sched_deps_cc_tests. * selftest.h (selftest::sched_deps_cc_tests): Declare. Signed-off-by: Kyrylo Tkachov <[email protected]> Diff: --- gcc/sched-deps.cc | 81 +++++++++++++++++++++++++++++++++++++++++++++++ gcc/sched-int.h | 6 ++++ gcc/selftest-run-tests.cc | 1 + gcc/selftest.h | 1 + 4 files changed, 89 insertions(+) diff --git a/gcc/sched-deps.cc b/gcc/sched-deps.cc index 23f3545b54c9..3a170d87b7e1 100644 --- a/gcc/sched-deps.cc +++ b/gcc/sched-deps.cc @@ -38,6 +38,7 @@ along with GCC; see the file COPYING3. If not see #include "sched-int.h" #include "cselib.h" #include "function-abi.h" +#include "selftest.h" #ifdef INSN_SCHEDULING @@ -3115,6 +3116,8 @@ sched_analyze_insn (class deps_desc *deps, rtx x, rtx_insn *insn) if (!deps->readonly) reg_last->uses = alloc_INSN_LIST (insn, reg_last->uses); } + if (!deps->readonly) + IOR_REG_SET (&deps->reg_last_dirty, reg_pending_uses); CLEAR_REG_SET (reg_pending_uses); /* Quite often, a debug insn will refer to stuff in the @@ -3306,6 +3309,7 @@ sched_analyze_insn (class deps_desc *deps, rtx x, rtx_insn *insn) reg_last->control_uses = alloc_INSN_LIST (insn, reg_last->control_uses); } + IOR_REG_SET (&deps->reg_last_dirty, reg_pending_control_uses); } } @@ -3941,6 +3945,7 @@ init_deps (class deps_desc *deps, bool lazy_reg_last) else deps->reg_last = XCNEWVEC (struct deps_reg, max_reg); INIT_REG_SET (&deps->reg_last_in_use); + INIT_REG_SET (&deps->reg_last_dirty); deps->pending_read_insns = 0; deps->pending_read_mems = 0; @@ -3999,6 +4004,11 @@ free_deps (class deps_desc *deps) free_EXPR_LIST_list (&deps->pending_write_mems); free_INSN_LIST_list (&deps->last_pending_memory_flush); + /* Teardown only: fold the entries recorded solely in reg_last_dirty into the + live set, so that one loop releases everything. free_deps creates no + dependences, so this merge cannot add one. */ + IOR_REG_SET (&deps->reg_last_in_use, &deps->reg_last_dirty); + /* Without the EXECUTE_IF_SET, this loop is executed max_reg * nr_regions times. For a testcase with 42000 regs and 8000 small basic blocks, this loop accounted for nearly 60% (84 sec) of the total -O2 runtime. */ @@ -4017,6 +4027,7 @@ free_deps (class deps_desc *deps) free_INSN_LIST_list (®_last->clobbers); } 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. */ @@ -5023,4 +5034,74 @@ find_modifiable_mems (rtx_insn *head, rtx_insn *tail) success_in_block); } +#if CHECKING_P + +namespace selftest { + +/* Verify that free_deps releases entries recorded only in reg_last_dirty. */ + +static void +test_dirty_reg_last_release () +{ + bitmap_obstack test_obstack; + bitmap_obstack_initialize (&test_obstack); + + deps_desc deps = {}; + deps.max_reg = 2; + deps.reg_last = XCNEWVEC (deps_reg, deps.max_reg); + bitmap_initialize (&deps.reg_last_in_use, &test_obstack); + bitmap_initialize (&deps.reg_last_dirty, &test_obstack); + + rtx_insn_list *uses = alloc_INSN_LIST (NULL_RTX, NULL_RTX); + rtx_insn_list *control_uses = alloc_INSN_LIST (NULL_RTX, NULL_RTX); + deps.reg_last[1].uses = uses; + deps.reg_last[1].control_uses = control_uses; + 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); + common_sched_info = saved_common_sched_info; + + ASSERT_EQ (0, deps.max_reg); + ASSERT_EQ (NULL, deps.reg_last); + + rtx_insn_list *first = alloc_INSN_LIST (NULL_RTX, NULL_RTX); + rtx_insn_list *second = alloc_INSN_LIST (NULL_RTX, NULL_RTX); + ASSERT_TRUE ((first == control_uses && second == uses) + || (first == uses && second == control_uses)); + free_INSN_LIST_list (&first); + free_INSN_LIST_list (&second); + + sched_deps_finish (); + bitmap_obstack_release (&test_obstack); +} + +/* Run the sched-deps.cc selftests. */ + +void +sched_deps_cc_tests () +{ + test_dirty_reg_last_release (); +} + +} // namespace selftest + +#endif + #endif /* INSN_SCHEDULING */ + +#if CHECKING_P && !defined (INSN_SCHEDULING) + +namespace selftest { + +void +sched_deps_cc_tests () +{ +} + +} // namespace selftest + +#endif diff --git a/gcc/sched-int.h b/gcc/sched-int.h index 4e7553329df9..0c7c05abcf27 100644 --- a/gcc/sched-int.h +++ b/gcc/sched-int.h @@ -567,6 +567,12 @@ public: in reg_last[N].{uses,sets,clobbers}. */ regset_head reg_last_in_use; + /* Element N is set for each register whose reg_last[N] was written on a + path that does not record it in reg_last_in_use, namely debug insn uses + and control uses. Used only to release those lists in free_deps; it + takes no part in dependence generation. */ + regset_head reg_last_dirty; + /* Shows the last value of reg_pending_barrier associated with the insn. */ enum reg_pending_barrier_mode last_reg_pending_barrier; diff --git a/gcc/selftest-run-tests.cc b/gcc/selftest-run-tests.cc index e39a94f8688a..1d21b8751b4a 100644 --- a/gcc/selftest-run-tests.cc +++ b/gcc/selftest-run-tests.cc @@ -114,6 +114,7 @@ selftest::run_tests () /* This one relies on most of the above. */ function_tests_cc_tests (); + sched_deps_cc_tests (); /* Run any target-specific selftests. */ if (targetm.run_target_selftests) diff --git a/gcc/selftest.h b/gcc/selftest.h index 8891d0b7b6f7..f2e2b49fc20d 100644 --- a/gcc/selftest.h +++ b/gcc/selftest.h @@ -255,6 +255,7 @@ extern void read_rtl_function_cc_tests (); extern void relation_tests (); extern void rtl_tests_cc_tests (); extern void sbitmap_cc_tests (); +extern void sched_deps_cc_tests (); extern void selftest_cc_tests (); extern void simple_diagnostic_path_cc_tests (); extern void simplify_rtx_cc_tests ();