We make assumptions in the codebase about when the GC can run, and when
it can't (e.g. within numerous passes) but these aren't captured in a way
that's verifiable.
This patch introduces macros GGC_{BEGIN|END}_ASSERT_NO_COLLECTIONS for
marking regions of code where we assume that a GC can't happen, together
with an assert within ggc_collect to verify that we're not within such
a region.
This lets us both
(A) document code regions where a GC must not happen and
(B) verify in a checked build that these assumptions hold
The patch also adds an example of using the macros, to the JIT.
It all compiles away in a release build.
I'm not fond of the names of the macros, but I can't think of better ones
(suggestions?)
Successfully bootstrapped and regrtested on x86_64-unknown-linux-gnu.
OK for trunk?
gcc/ChangeLog:
* ggc-page.c (ggc_count_of_collection_blocking_assertions): New
variable.
(ggc_collect): Assert that we're not within code regions that are
assuming that the GC isn't going to run.
* ggc.h (GGC_BEGIN_ASSERT_NO_COLLECTIONS): New macro.
(GGC_END_ASSERT_NO_COLLECTIONS): New macro.
(ggc_count_of_collection_blocking_assertions): New variable.
gcc/jit/ChangeLog:
* jit-playback.c (gcc::jit::playback::context::replay): Add
uses of GGC_{BEGIN|END}_ASSERT_NO_COLLECTIONS to clearly
delimit the region of code in which the GC must not run.
---
gcc/ggc-page.c | 13 +++++++++++++
gcc/ggc.h | 39 ++++++++++++++++++++++++++++++++++++++-
gcc/jit/jit-playback.c | 10 +++++++++-
3 files changed, 60 insertions(+), 2 deletions(-)
diff --git a/gcc/ggc-page.c b/gcc/ggc-page.c
index f55c4e9..fd185e4 100644
--- a/gcc/ggc-page.c
+++ b/gcc/ggc-page.c
@@ -2151,11 +2151,24 @@ validate_free_objects (void)
#define validate_free_objects()
#endif
+#ifdef ENABLE_CHECKING
+int ggc_count_of_collection_blocking_assertions;
+#endif
+
/* Top level mark-and-sweep routine. */
void
ggc_collect (void)
{
+ /* Ensure that we don't try to garbage-collect in a code region that
+ assumes the GC won't run (as guarded by
+ GGC_BEGIN_ASSERT_NO_COLLECTIONS).
+
+ If this assertion fails, then either ggc_collect was called in a
+ region that assumed no GC could occur, or we don't have matching pairs
+ of GGC_{BEGIN|END}_ASSERT_NO_COLLECTIONS macros. */
+ gcc_checking_assert (ggc_count_of_collection_blocking_assertions == 0);
+
/* Avoid frequent unnecessary work by skipping collection if the
total allocations haven't expanded much since the last
collection. */
diff --git a/gcc/ggc.h b/gcc/ggc.h
index dc21520..827a8a5 100644
--- a/gcc/ggc.h
+++ b/gcc/ggc.h
@@ -363,4 +363,41 @@ gt_pch_nx (unsigned int)
{
}
-#endif
+/* We don't attempt to track references to GC-allocated entities
+ that are on the stack, so the garbage collectior can only run at
+ specific times.
+
+ The following machinery is available for recording assumptions about
+ code regions in which the GC is expected *not* to collect. */
+
+#if defined ENABLE_CHECKING
+
+/* Begin a region in which it's a bug for a ggc_collect to occur.
+ Such regions can be nested.
+ Each such region must be terminated with a matching
+ GGC_END_ASSERT_NO_COLLECTIONS. */
+
+#define GGC_BEGIN_ASSERT_NO_COLLECTIONS() \
+ do { ggc_count_of_collection_blocking_assertions++; } while (0)
+
+/* Terminate a region in which ggc_collect is forbidden. */
+
+#define GGC_END_ASSERT_NO_COLLECTIONS() \
+ do { \
+ gcc_assert (ggc_count_of_collection_blocking_assertions > 0); \
+ ggc_count_of_collection_blocking_assertions--; \
+ } while (0)
+
+/* How many such assertions are active? */
+
+extern int ggc_count_of_collection_blocking_assertions;
+
+#else /* not defined ENABLE_CHECKING */
+
+/* Do no checking in a release build. */
+#define GGC_BEGIN_ASSERT_NO_COLLECTIONS()
+#define GGC_END_ASSERT_NO_COLLECTIONS()
+
+#endif /* not defined ENABLE_CHECKING */
+
+#endif /* #ifndef GCC_GGC_H */
diff --git a/gcc/jit/jit-playback.c b/gcc/jit/jit-playback.c
index 285a3ef..2998631 100644
--- a/gcc/jit/jit-playback.c
+++ b/gcc/jit/jit-playback.c
@@ -1723,6 +1723,8 @@ void
playback::context::
replay ()
{
+ GGC_BEGIN_ASSERT_NO_COLLECTIONS ();
+
/* Adapted from c-common.c:c_common_nodes_and_builtins. */
tree array_domain_type = build_index_type (size_int (200));
m_char_array_type_node
@@ -1745,7 +1747,11 @@ replay ()
timevar_pop (TV_JIT_REPLAY);
- if (!errors_occurred ())
+ if (errors_occurred ())
+ {
+ GGC_END_ASSERT_NO_COLLECTIONS ();
+ }
+ else
{
int i;
function *func;
@@ -1761,6 +1767,8 @@ replay ()
/* No GC can have happened yet. */
+ GGC_END_ASSERT_NO_COLLECTIONS ();
+
/* Postprocess the functions. This could trigger GC. */
FOR_EACH_VEC_ELT (m_functions, i, func)
{
--
1.8.5.3