ca94aff
From 4d9cc9afa47981520d991d19fd78b322f1ba9f2a Mon Sep 17 00:00:00 2001
ca94aff
From: Jarek Prokop <jprokop@redhat.com>
ca94aff
Date: Wed, 22 Jun 2022 01:03:49 +0200
ca94aff
Subject: [PATCH] Detect compaction support during runtime.
ca94aff
ca94aff
The patch is created by backporting 3 commits from
ca94aff
the upstream Ruby master branch in the chronological order below.
ca94aff
ca94aff
https://github.com/ruby/ruby/commit/52d42e702375446746164a0251e1a10bce813b78
ca94aff
https://github.com/ruby/ruby/commit/79eaaf2d0b641710613f16525e4b4c439dfe854e
ca94aff
https://github.com/ruby/ruby/commit/2c190863239bee3f54cfb74b16bb6ea4cae6ed20
ca94aff
ca94aff
== How to create this patch ==
ca94aff
ca94aff
Download Ruby source code.
ca94aff
```
ca94aff
$ git clone https://github.com/ruby/ruby.git
ca94aff
$ cd ruby
ca94aff
```
ca94aff
ca94aff
First create a commit squashed from the 3 commits above.
ca94aff
Checkout the second commmit above, and create a temporary branch.
ca94aff
```
ca94aff
$ git checkout 79eaaf2d0b641710613f16525e4b4c439dfe854e
ca94aff
$ git checkout -b wip/detect-compaction-runtime-tmp
ca94aff
```
ca94aff
ca94aff
Cherry pick the third commit on the second commit.
ca94aff
```
ca94aff
$ git cherry-pick 2c190863239bee3f54cfb74b16bb6ea4cae6ed20
ca94aff
```
ca94aff
ca94aff
Squash the last 3 commits on the branch.
ca94aff
```
ca94aff
$ git rebase -i 2223eb082afa6d05321b69df783d4133b9aacba6
ca94aff
```
ca94aff
ca94aff
Then checkout Ruby 3.1.2 branch.
ca94aff
Create a new branch.
ca94aff
Merge the Fedora Ruby's
ca94aff
ruby-3.2.0-define-unsupported-gc-compaction-methods-as-rb_f_notimplement.patch.
ca94aff
```
ca94aff
$ git checkout v3_1_2
ca94aff
$ git checkout -b wip/detect-compaction-runtime
ca94aff
$ patch -p1 <
ca94aff
~/fed/ruby/ruby-3.2.0-define-unsupported-gc-compaction-methods-as-rb_f_notimplement.patch
ca94aff
$ git add gc.c gc.rb test/ruby/test_gc_compact.rb
ca94aff
$ git commit
ca94aff
```
ca94aff
ca94aff
Merge the squashed one commit on the
ca94aff
`wip/detect-compaction-runtime-tmp` branch
ca94aff
into the `wip/detect-compaction-runtime` branch.
ca94aff
```
ca94aff
$ git cherry-pick <the squashed commit above>
ca94aff
```
ca94aff
ca94aff
Fix conflicts seeing the difference by `git show 
ca94aff
above>`
ca94aff
on another terminal.
ca94aff
```
ca94aff
$ vi gc.c
ca94aff
$ git add gc.c
ca94aff
$ git commit
ca94aff
```
ca94aff
ca94aff
== Notes for the patch ==
ca94aff
ca94aff
```
ca94aff
+# define GC_COMPACTION_SUPPORTED (GC_CAN_COMPILE_COMPACTION && USE_MMAP_ALIGNED_ALLOC)
ca94aff
```
ca94aff
ca94aff
We use the USE_MMAP_ALIGNED_ALLOC instead of HEAP_PAGE_ALLOC_USE_MMAP on
ca94aff
the line above. Because while the Ruby on the master branch replaced the
ca94aff
USE_MMAP_ALIGNED_ALLOC with HEAP_PAGE_ALLOC_USE_MMAP, Ruby 3.1.2 doesn't.
ca94aff
See <https://github.com/ruby/ruby/commit/fe21b7794af0cdb7ebd502e2c0da38c68fd89839>.
ca94aff
ca94aff
ca94aff
```
ca94aff
+        rb_define_singleton_method(rb_mGC, "verify_compaction_references", gc_verify_compaction_references, -1);
ca94aff
```
ca94aff
ca94aff
We added the line in the case that GC_COMPACTION_SUPPORTED is true.
ca94aff
Because while the Ruby on the master branch defines the
ca94aff
GC.verify_compaction_references in the gc.rb in
ca94aff
the case that GC_COMPACTION_SUPPORTED is true, Ruby 3.1.2
ca94aff
doesn't define it in the gc.rb.
ca94aff
See <https://github.com/ruby/ruby/commit/b96a3a6fd2093e1dbea5491c002da515652dd347>.
ca94aff
ca94aff
ca94aff
```
ca94aff
+	OPT(GC_COMPACTION_SUPPORTED);
ca94aff
```
ca94aff
ca94aff
We added the line to expose the C macro to Ruby level.
ca94aff
In Ruby the macro existance can then be checked like so:
ca94aff
```Ruby
ca94aff
GC::OPTS.include?("GC_COMPACTION_SUPPORTED")
ca94aff
```
ca94aff
It will return `true` if the GC_COMPACTION_SUPPORTED evaluates to `true` on the
ca94aff
C level, `false` otherwise.
ca94aff
See <https://github.com/ruby/ruby/blob/b96a3a6fd2093e1dbea5491c002da515652dd347/gc.c#L14091>
ca94aff
ca94aff
== Original commit messages ==
ca94aff
ca94aff
This is a combination of 3 commits.
ca94aff
This is the 1st commit message:
ca94aff
~~~
ca94aff
Rename GC_COMPACTION_SUPPORTED
ca94aff
ca94aff
Naming this macro GC_COMPACTION_SUPPORTED is misleading because it
ca94aff
only checks whether compaction is supported at compile time.
ca94aff
ca94aff
[Bug #18829]
ca94aff
~~~
ca94aff
ca94aff
This is the commit message #2:
ca94aff
~~~
ca94aff
Include runtime checks for compaction support
ca94aff
ca94aff
Commit 0c36ba53192c5a0d245c9b626e4346a32d7d144e changed GC compaction
ca94aff
methods to not be implemented when not supported. However, that commit
ca94aff
only does compile time checks (which currently only checks for WASM),
ca94aff
but there are additional compaction support checks during run time.
ca94aff
ca94aff
This commit changes it so that GC compaction methods aren't defined
ca94aff
during run time if the platform does not support GC compaction.
ca94aff
ca94aff
[Bug #18829]
ca94aff
~~~
ca94aff
ca94aff
This is the commit message #3:
ca94aff
~~~
ca94aff
Suppress code unused unless GC_CAN_COMPILE_COMPACTION
ca94aff
~~~
ca94aff
---
ca94aff
 gc.c | 63 +++++++++++++++++++++++++++++++++++++++++-------------------
ca94aff
 1 file changed, 43 insertions(+), 20 deletions(-)
ca94aff
ca94aff
diff --git a/gc.c b/gc.c
ca94aff
index 1c35856c44..bff0666a17 100644
ca94aff
--- a/gc.c
ca94aff
+++ b/gc.c
ca94aff
@@ -4980,6 +4980,23 @@ gc_unprotect_pages(rb_objspace_t *objspace, rb_heap_t *heap)
ca94aff
 static void gc_update_references(rb_objspace_t * objspace);
ca94aff
 static void invalidate_moved_page(rb_objspace_t *objspace, struct heap_page *page);
ca94aff
 
ca94aff
+#ifndef GC_CAN_COMPILE_COMPACTION
ca94aff
+#if defined(__wasi__) /* WebAssembly doesn't support signals */
ca94aff
+# define GC_CAN_COMPILE_COMPACTION 0
ca94aff
+#else
ca94aff
+# define GC_CAN_COMPILE_COMPACTION 1
ca94aff
+#endif
ca94aff
+#endif
ca94aff
+
ca94aff
+#if defined(__MINGW32__) || defined(_WIN32)
ca94aff
+# define GC_COMPACTION_SUPPORTED 1
ca94aff
+#else
ca94aff
+/* If not MinGW, Windows, or does not have mmap, we cannot use mprotect for
ca94aff
+ * the read barrier, so we must disable compaction. */
ca94aff
+# define GC_COMPACTION_SUPPORTED (GC_CAN_COMPILE_COMPACTION && USE_MMAP_ALIGNED_ALLOC)
ca94aff
+#endif
ca94aff
+
ca94aff
+#if GC_CAN_COMPILE_COMPACTION
ca94aff
 static void
ca94aff
 read_barrier_handler(uintptr_t address)
ca94aff
 {
ca94aff
@@ -5000,6 +5017,7 @@ read_barrier_handler(uintptr_t address)
ca94aff
     }
ca94aff
     RB_VM_LOCK_LEAVE();
ca94aff
 }
ca94aff
+#endif
ca94aff
 
ca94aff
 #if defined(_WIN32)
ca94aff
 static LPTOP_LEVEL_EXCEPTION_FILTER old_handler;
ca94aff
@@ -9250,13 +9268,7 @@ gc_start_internal(rb_execution_context_t *ec, VALUE self, VALUE full_mark, VALUE
ca94aff
 
ca94aff
     /* For now, compact implies full mark / sweep, so ignore other flags */
ca94aff
     if (RTEST(compact)) {
ca94aff
-        /* If not MinGW, Windows, or does not have mmap, we cannot use mprotect for
ca94aff
-         * the read barrier, so we must disable compaction. */
ca94aff
-#if !defined(__MINGW32__) && !defined(_WIN32)
ca94aff
-        if (!USE_MMAP_ALIGNED_ALLOC) {
ca94aff
-            rb_raise(rb_eNotImpError, "Compaction isn't available on this platform");
ca94aff
-        }
ca94aff
-#endif
ca94aff
+        GC_ASSERT(GC_COMPACTION_SUPPORTED);
ca94aff
 
ca94aff
         reason |= GPR_FLAG_COMPACT;
ca94aff
     }
ca94aff
@@ -9421,7 +9433,7 @@ gc_move(rb_objspace_t *objspace, VALUE scan, VALUE free, size_t slot_size)
ca94aff
     return (VALUE)src;
ca94aff
 }
ca94aff
 
ca94aff
-#if GC_COMPACTION_SUPPORTED
ca94aff
+#if GC_CAN_COMPILE_COMPACTION
ca94aff
 static int
ca94aff
 compare_free_slots(const void *left, const void *right, void *dummy)
ca94aff
 {
ca94aff
@@ -10149,7 +10161,7 @@ gc_update_references(rb_objspace_t *objspace)
ca94aff
     gc_update_table_refs(objspace, finalizer_table);
ca94aff
 }
ca94aff
 
ca94aff
-#if GC_COMPACTION_SUPPORTED
ca94aff
+#if GC_CAN_COMPILE_COMPACTION
ca94aff
 /*
ca94aff
  *  call-seq:
ca94aff
  *     GC.latest_compact_info -> {:considered=>{:T_CLASS=>11}, :moved=>{:T_CLASS=>11}}
ca94aff
@@ -10190,7 +10202,7 @@ gc_compact_stats(VALUE self)
ca94aff
 #  define gc_compact_stats rb_f_notimplement
ca94aff
 #endif
ca94aff
 
ca94aff
-#if GC_COMPACTION_SUPPORTED
ca94aff
+#if GC_CAN_COMPILE_COMPACTION
ca94aff
 static void
ca94aff
 root_obj_check_moved_i(const char *category, VALUE obj, void *data)
ca94aff
 {
ca94aff
@@ -10269,7 +10281,7 @@ gc_compact(VALUE self)
ca94aff
 #  define gc_compact rb_f_notimplement
ca94aff
 #endif
ca94aff
 
ca94aff
-#if GC_COMPACTION_SUPPORTED
ca94aff
+#if GC_CAN_COMPILE_COMPACTION
ca94aff
 /*
ca94aff
  * call-seq:
ca94aff
  *    GC.verify_compaction_references(toward: nil, double_heap: false) -> hash
ca94aff
@@ -10800,7 +10812,7 @@ gc_disable(rb_execution_context_t *ec, VALUE _)
ca94aff
     return rb_gc_disable();
ca94aff
 }
ca94aff
 
ca94aff
-#if GC_COMPACTION_SUPPORTED
ca94aff
+#if GC_CAN_COMPILE_COMPACTION
ca94aff
 /*
ca94aff
  *  call-seq:
ca94aff
  *     GC.auto_compact = flag
ca94aff
@@ -10814,8 +10826,7 @@ gc_disable(rb_execution_context_t *ec, VALUE _)
ca94aff
 static VALUE
ca94aff
 gc_set_auto_compact(VALUE _, VALUE v)
ca94aff
 {
ca94aff
-    /* If not MinGW, Windows, or does not have mmap, we cannot use mprotect for
ca94aff
-     * the read barrier, so we must disable automatic compaction. */
ca94aff
+    GC_ASSERT(GC_COMPACTION_SUPPORTED);
ca94aff
 
ca94aff
     ruby_enable_autocompact = RTEST(v);
ca94aff
     return v;
ca94aff
@@ -10824,7 +10835,8 @@ gc_set_auto_compact(VALUE _, VALUE v)
ca94aff
 #  define gc_set_auto_compact rb_f_notimplement
ca94aff
 #endif
ca94aff
 
ca94aff
-#if GC_COMPACTION_SUPPORTED
ca94aff
+
ca94aff
+#if GC_CAN_COMPILE_COMPACTION
ca94aff
 /*
ca94aff
  *  call-seq:
ca94aff
  *     GC.auto_compact    -> true or false
ca94aff
@@ -13696,11 +13708,21 @@ Init_GC(void)
ca94aff
     rb_define_singleton_method(rb_mGC, "malloc_allocated_size", gc_malloc_allocated_size, 0);
ca94aff
     rb_define_singleton_method(rb_mGC, "malloc_allocations", gc_malloc_allocations, 0);
ca94aff
 #endif
ca94aff
-    rb_define_singleton_method(rb_mGC, "compact", gc_compact, 0);
ca94aff
-    rb_define_singleton_method(rb_mGC, "auto_compact", gc_get_auto_compact, 0);
ca94aff
-    rb_define_singleton_method(rb_mGC, "auto_compact=", gc_set_auto_compact, 1);
ca94aff
-    rb_define_singleton_method(rb_mGC, "latest_compact_info", gc_compact_stats, 0);
ca94aff
-    rb_define_singleton_method(rb_mGC, "verify_compaction_references", gc_verify_compaction_references, -1);
ca94aff
+    if (GC_COMPACTION_SUPPORTED) {
ca94aff
+        rb_define_singleton_method(rb_mGC, "compact", gc_compact, 0);
ca94aff
+        rb_define_singleton_method(rb_mGC, "auto_compact", gc_get_auto_compact, 0);
ca94aff
+        rb_define_singleton_method(rb_mGC, "auto_compact=", gc_set_auto_compact, 1);
ca94aff
+        rb_define_singleton_method(rb_mGC, "latest_compact_info", gc_compact_stats, 0);
ca94aff
+        rb_define_singleton_method(rb_mGC, "verify_compaction_references", gc_verify_compaction_references, -1);
ca94aff
+    }
ca94aff
+    else {
ca94aff
+        rb_define_singleton_method(rb_mGC, "compact", rb_f_notimplement, 0);
ca94aff
+        rb_define_singleton_method(rb_mGC, "auto_compact", rb_f_notimplement, 0);
ca94aff
+        rb_define_singleton_method(rb_mGC, "auto_compact=", rb_f_notimplement, 1);
ca94aff
+        rb_define_singleton_method(rb_mGC, "latest_compact_info", rb_f_notimplement, 0);
ca94aff
+        /* When !GC_COMPACTION_SUPPORTED, this method is not defined in gc.rb */
ca94aff
+        rb_define_singleton_method(rb_mGC, "verify_compaction_references", rb_f_notimplement, -1);
ca94aff
+    }
ca94aff
 
ca94aff
 #if GC_DEBUG_STRESS_TO_CLASS
ca94aff
     rb_define_singleton_method(rb_mGC, "add_stress_to_class", rb_gcdebug_add_stress_to_class, -1);
ca94aff
@@ -13724,6 +13746,7 @@ Init_GC(void)
ca94aff
 	OPT(MALLOC_ALLOCATED_SIZE);
ca94aff
 	OPT(MALLOC_ALLOCATED_SIZE_CHECK);
ca94aff
 	OPT(GC_PROFILE_DETAIL_MEMORY);
ca94aff
+	OPT(GC_COMPACTION_SUPPORTED);
ca94aff
 #undef OPT
ca94aff
 	OBJ_FREEZE(opts);
ca94aff
     }
ca94aff
-- 
ca94aff
2.36.1
ca94aff