From b803dca836e7436baf4a899787777842a0036fc4 Mon Sep 17 00:00:00 2001 From: Chris Seaton Date: Thu, 14 Jul 2022 13:31:12 +0000 Subject: [PATCH 1/7] Use defined?(GC::MMTk) for feature detection --- test/lib/jit_support.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/lib/jit_support.rb b/test/lib/jit_support.rb index 33e289b9b62781..b68f8b3455f74d 100644 --- a/test/lib/jit_support.rb +++ b/test/lib/jit_support.rb @@ -56,7 +56,7 @@ def eval_with_jit_without_retry(env = nil, script, verbose: 0, min_calls: 5, sav end def supported? - return false if RbConfig::CONFIG["CFLAGS"].include?("USE_THIRD_PARTY_HEAP") + return false if defined?(GC::MMTk) return @supported if defined?(@supported) @supported = RbConfig::CONFIG["MJIT_SUPPORT"] != 'no' && UNSUPPORTED_COMPILERS.all? do |regexp| !regexp.match?(RbConfig::CONFIG['MJIT_CC']) From b32b4a53e10d94c1dba63c4a94381f4c91ce84c5 Mon Sep 17 00:00:00 2001 From: Chris Seaton Date: Thu, 14 Jul 2022 13:31:38 +0000 Subject: [PATCH 2/7] Report MMTk plan in version string and crash --- version.c | 14 ++++++++++---- vm_dump.c | 1 + 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/version.c b/version.c index cd3a322d525420..a7485007a701d0 100644 --- a/version.c +++ b/version.c @@ -16,6 +16,10 @@ #include "yjit.h" #include +#ifdef USE_THIRD_PARTY_HEAP +#include "mmtk.h" +#endif + #ifndef EXIT_SUCCESS #define EXIT_SUCCESS 0 #endif @@ -48,7 +52,7 @@ const char ruby_copyright[] = RUBY_COPYRIGHT; const char ruby_engine[] = "ruby"; // Enough space for any combination of option flags -static char ruby_dynamic_description_buffer[sizeof(ruby_description) + sizeof("+MJIT +YJIT +MMTk") - 1]; +static char ruby_dynamic_description_buffer[sizeof(ruby_description) + sizeof("+MJIT +YJIT +MMTk(XXXXXXXXXXXXXXXX)") - 1]; // Might change after initialization const char *rb_dynamic_description = ruby_description; @@ -107,14 +111,16 @@ Init_version(void) void Init_ruby_description(void) { - if (snprintf(ruby_dynamic_description_buffer, sizeof(ruby_dynamic_description_buffer), "%s%s%s%s%s", + if (snprintf(ruby_dynamic_description_buffer, sizeof(ruby_dynamic_description_buffer), "%s%s%s%s%s%s%s", ruby_description_pre, MJIT_OPTS_ON ? " +MJIT" : "", rb_yjit_enabled_p() ? " +YJIT" : "", #ifdef USE_THIRD_PARTY_HEAP - " +MMTk", + " +MMTk(", + mmtk_plan_name(), + ")", #else - "", + "", "", "", #endif ruby_description_post) < 0) { rb_bug("could not format dynamic description string"); diff --git a/vm_dump.c b/vm_dump.c index e75367314a54d1..f7dcc73133d3c2 100644 --- a/vm_dump.c +++ b/vm_dump.c @@ -1196,6 +1196,7 @@ rb_vm_bugreport(const void *ctx) #ifdef USE_THIRD_PARTY_HEAP fprintf(stderr, "* MMTk:\n\n"); + fprintf(stderr, " mmtk_plan_name: %s\n", mmtk_plan_name()); fprintf(stderr, " mmtk_free_bytes: %zu\n", mmtk_free_bytes()); fprintf(stderr, " mmtk_total_bytes: %zu\n", mmtk_total_bytes()); fprintf(stderr, " mmtk_used_bytes: %zu\n", mmtk_used_bytes()); From 8b5c9ee923736270c1e9f55db7fd6e52733e5f48 Mon Sep 17 00:00:00 2001 From: Chris Seaton Date: Thu, 14 Jul 2022 16:49:00 +0000 Subject: [PATCH 3/7] Support setting the MMTk plan as a flag --- gc.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ main.c | 7 +++++ mmtk.h | 6 ++++ ruby.c | 27 +++++++++++++++++ 4 files changed, 133 insertions(+) diff --git a/gc.c b/gc.c index 002abfe81309bc..708d0e90aafedc 100644 --- a/gc.c +++ b/gc.c @@ -42,6 +42,7 @@ #error MMTk does not use transient heap. #endif // USE_TRANSIENT_HEAP #include "mmtk.h" +#include "internal/cmdlineopt.h" RubyUpcalls ruby_upcalls; #endif @@ -156,6 +157,13 @@ RubyUpcalls ruby_upcalls; #define MAP_ANONYMOUS MAP_ANON #endif +#ifdef USE_THIRD_PARTY_HEAP +static char *mmtk_env_plan = NULL; +static char *mmtk_pre_arg_plan = NULL; +static char *mmtk_post_arg_plan = NULL; +static char *mmtk_chosen_plan = NULL; +#endif + static inline struct rbimpl_size_mul_overflow_tag size_add_overflow(size_t x, size_t y) { @@ -1863,10 +1871,20 @@ rb_objspace_alloc(void) dont_gc_on(); #ifdef USE_THIRD_PARTY_HEAP + if (!mmtk_env_plan && setenv("MMTK_PLAN", mmtk_chosen_plan, 0) != 0) { + fputs("[FATAL] could not set MMTK_PLAN\n", stderr); + exit(EXIT_FAILURE); + } + // Note: the limit is currently broken for NoGC, but we still attempt to // initialise it properly regardless. // See https://github.com/mmtk/mmtk-core/issues/214 mmtk_init_binding(rb_mmtk_heap_limit(), &ruby_upcalls); + + if (!mmtk_env_plan && unsetenv("MMTK_PLAN") != 0) { + fputs("[FATAL] could not unset MMTK_PLAN\n", stderr); + exit(EXIT_FAILURE); + } #endif return objspace; @@ -15092,4 +15110,79 @@ size_t rb_mmtk_heap_limit(void) { } } +void rb_mmtk_pre_process_opts(int argc, char **argv) { + mmtk_env_plan = getenv("MMTK_PLAN"); + + for (int n = 1; n < argc; n++) { + if (strcmp(argv[n], "--") == 0) { + break; + } + else if (strncmp(argv[n], "--mmtk-plan=", strlen("--mmtk-plan=")) == 0) { + mmtk_pre_arg_plan = argv[n] + strlen("--mmtk-plan="); + } + } + + char *env_args = getenv("RUBYOPT"); + if (env_args != NULL) { + while (*env_args != '\0') { + while (ISSPACE(*env_args)) { + env_args++; + } + + if (strncmp(env_args, "--mmtk-plan=", strlen("--mmtk-plan=")) == 0) { + int length = 0; + while (env_args[length] != '\0' && !ISSPACE(env_args[length])) { + length++; + } + + mmtk_pre_arg_plan = strndup(env_args + strlen("--mmtk-plan="), length - strlen("--mmtk-plan=")); + if (mmtk_pre_arg_plan == NULL) { + rb_bug("could not allocate space for argument"); + } + env_args += length; + } + } + } + + if (mmtk_env_plan && mmtk_pre_arg_plan && strcmp(mmtk_env_plan, mmtk_pre_arg_plan) != 0) { + fputs("[FATAL] MMTK_PLAN and --mmtk-plan do not agree\n", stderr); + exit(EXIT_FAILURE); + } + + if (mmtk_env_plan) { + mmtk_chosen_plan = mmtk_env_plan; + } + else if (mmtk_pre_arg_plan) { + mmtk_chosen_plan = mmtk_pre_arg_plan; + } + else { + mmtk_chosen_plan = MMTK_DEFAULT_PLAN; + } +} + +#define opt_match_noarg(s, l, name) \ + opt_match(s, l, name) && (*(s) ? (rb_warn("argument to --mmtk-" name " is ignored"), 1) : 1) +#define opt_match_arg(s, l, name) \ + opt_match(s, l, name) && (*(s) ? 1 : (rb_raise(rb_eRuntimeError, "--mmtk-" name " needs an argument"), 0)) + +void rb_mmtk_post_process_opts(char *s) { + const size_t l = strlen(s); + if (l == 0) { + return; + } + if (opt_match_arg(s, l, "plan")) { + mmtk_post_arg_plan = s + 1; + } + else { + rb_raise(rb_eRuntimeError, + "invalid MMTk option `%s' (--help will show valid MMTk options)", s); + } +} + +void rb_mmtk_post_process_opts_finish(void) { + if (strcmp(mmtk_pre_arg_plan ? mmtk_pre_arg_plan : "", mmtk_post_arg_plan ? mmtk_post_arg_plan : "") != 0) { + rb_raise(rb_eRuntimeError, "--mmtk-plan values disagree"); + } +} + #endif diff --git a/main.c b/main.c index e76228713f4af7..f338fd8ca056a7 100644 --- a/main.c +++ b/main.c @@ -30,10 +30,17 @@ # undef RUBY_DEBUG_ENV #endif +#ifdef USE_THIRD_PARTY_HEAP +#include "mmtk.h" +#endif + static int rb_main(int argc, char **argv) { RUBY_INIT_STACK; +#ifdef USE_THIRD_PARTY_HEAP + rb_mmtk_pre_process_opts(argc, argv); +#endif ruby_init(); return ruby_run_node(ruby_options(argc, argv)); } diff --git a/mmtk.h b/mmtk.h index 654a818f9b2e11..5f727d66aa0eb5 100644 --- a/mmtk.h +++ b/mmtk.h @@ -22,6 +22,8 @@ typedef void* MMTk_VMMutatorThread; #define MMTK_GC_THREAD_KIND_CONTROLLER 0 #define MMTK_GC_THREAD_KIND_WORKER 1 +#define MMTK_DEFAULT_PLAN "MarkSweep" + typedef struct { void (*init_gc_worker_thread)(MMTk_VMWorkerThread worker_tls); MMTk_VMWorkerThread (*get_gc_thread_tls)(void); @@ -104,6 +106,10 @@ extern void mmtk_harness_end(void *tls); extern void mmtk_register_finalizable(void *reff); extern void* mmtk_poll_finalizable(bool include_live); +void rb_mmtk_pre_process_opts(int argc, char **argv); +void rb_mmtk_post_process_opts(char *arg); +void rb_mmtk_post_process_opts_finish(void); + #ifdef __cplusplus } #endif diff --git a/ruby.c b/ruby.c index 311e92a235e709..28a46506ffdbca 100644 --- a/ruby.c +++ b/ruby.c @@ -61,6 +61,10 @@ #include "ruby/version.h" #include "ruby/internal/error.h" +#ifdef USE_THIRD_PARTY_HEAP +#include "mmtk.h" +#endif + #ifndef MAXPATHLEN # define MAXPATHLEN 1024 #endif @@ -333,6 +337,11 @@ usage(const char *name, int help, int highlight, int columns) M("--yjit-max-versions=num", "", "Maximum number of versions per basic block (default: 4)"), M("--yjit-greedy-versioning", "", "Greedy versioning mode (default: disabled)"), }; +#endif +#if USE_THIRD_PARTY_HEAP + static const struct ruby_opt_message mmtk_options[] = { + M("--mmtk-plan=name", "", "MMTk garbage collection plan to use (default: " MMTK_DEFAULT_PLAN ")"), + }; #endif int i; const char *sb = highlight ? esc_standout+1 : esc_none; @@ -370,6 +379,11 @@ usage(const char *name, int help, int highlight, int columns) for (i = 0; i < numberof(yjit_options); ++i) SHOW(yjit_options[i]); #endif +#ifdef USE_THIRD_PARTY_HEAP + printf("%s""MMTk options (experimental):%s\n", sb, se); + for (i = 0; i < numberof(mmtk_options); ++i) + SHOW(mmtk_options[i]); +#endif } #define rubylib_path_new rb_str_new @@ -1439,6 +1453,15 @@ proc_options(long argc, char **argv, ruby_cmdline_options_t *opt, int envopt) setup_yjit_options(s); #else rb_warn("Ruby was built without YJIT support"); +#endif + } + else if (is_option_with_optarg("mmtk", '-', true, false, false)) { +#ifdef USE_THIRD_PARTY_HEAP + rb_mmtk_post_process_opts(s); +#undef opt_match_noarg +#undef opt_match_arg +#else + rb_warn("Ruby was built without MMTk support"); #endif } else if (strcmp("yydebug", s) == 0) { @@ -1805,6 +1828,10 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt) FEATURE_SET_RESTORE(opt->warn, warn); } +#ifdef USE_THIRD_PARTY_HEAP + rb_mmtk_post_process_opts_finish(); +#endif + if (opt->src.enc.name) /* cannot set deprecated category, as enabling deprecation warnings based on flags * has not happened yet. From a99648390562fd2b9e57f51ae12e6ae10350f100 Mon Sep 17 00:00:00 2001 From: Chris Seaton Date: Thu, 14 Jul 2022 22:49:43 +0000 Subject: [PATCH 4/7] Fix build --- gc.c | 2 +- ruby.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gc.c b/gc.c index 708d0e90aafedc..a313731b895881 100644 --- a/gc.c +++ b/gc.c @@ -1450,7 +1450,7 @@ asan_poison_object_restore(VALUE obj, void *ptr) #define FL_SET2(x,f) FL_CHECK2("FL_SET2", x, RBASIC(x)->flags |= (f)) #define FL_UNSET2(x,f) FL_CHECK2("FL_UNSET2", x, RBASIC(x)->flags &= ~(f)) -// Comment for easy location +// Comment for easy location #ifdef USE_THIRD_PARTY_HEAP #define RVALUE_MARK_BITMAP(obj) 0 #define RVALUE_PIN_BITMAP(obj) 0 diff --git a/ruby.c b/ruby.c index 28a46506ffdbca..5b35a81e539fec 100644 --- a/ruby.c +++ b/ruby.c @@ -338,7 +338,7 @@ usage(const char *name, int help, int highlight, int columns) M("--yjit-greedy-versioning", "", "Greedy versioning mode (default: disabled)"), }; #endif -#if USE_THIRD_PARTY_HEAP +#ifdef USE_THIRD_PARTY_HEAP static const struct ruby_opt_message mmtk_options[] = { M("--mmtk-plan=name", "", "MMTk garbage collection plan to use (default: " MMTK_DEFAULT_PLAN ")"), }; From e895c513633c8d7b90d82cc80fb35960e44998c9 Mon Sep 17 00:00:00 2001 From: Chris Seaton Date: Thu, 14 Jul 2022 22:51:45 +0000 Subject: [PATCH 5/7] Fix --- vm_dump.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm_dump.c b/vm_dump.c index f7dcc73133d3c2..76d4184aa4672a 100644 --- a/vm_dump.c +++ b/vm_dump.c @@ -1196,7 +1196,7 @@ rb_vm_bugreport(const void *ctx) #ifdef USE_THIRD_PARTY_HEAP fprintf(stderr, "* MMTk:\n\n"); - fprintf(stderr, " mmtk_plan_name: %s\n", mmtk_plan_name()); + fprintf(stderr, " mmtk_plan_name: %s\n", mmtk_plan_name()); fprintf(stderr, " mmtk_free_bytes: %zu\n", mmtk_free_bytes()); fprintf(stderr, " mmtk_total_bytes: %zu\n", mmtk_total_bytes()); fprintf(stderr, " mmtk_used_bytes: %zu\n", mmtk_used_bytes()); From a4ad145e83a6b580855bb3da025c41dc40f8a697 Mon Sep 17 00:00:00 2001 From: Chris Seaton Date: Tue, 19 Jul 2022 16:53:52 +0000 Subject: [PATCH 6/7] Move GC argument declarations --- gc.h | 4 ++++ main.c | 2 +- mmtk.h | 6 ------ ruby.c | 2 +- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/gc.h b/gc.h index 02e6b1311bd930..ad3296d457a49b 100644 --- a/gc.h +++ b/gc.h @@ -119,7 +119,11 @@ VALUE rb_gc_disable_no_rest(void); struct rb_thread_struct; #ifdef USE_THIRD_PARTY_HEAP +#define MMTK_DEFAULT_PLAN "MarkSweep" void rb_gc_init_collection(); +void rb_mmtk_pre_process_opts(int argc, char **argv); +void rb_mmtk_post_process_opts(char *arg); +void rb_mmtk_post_process_opts_finish(void); #endif // USE_THIRD_PARTY_HEAP RUBY_SYMBOL_EXPORT_BEGIN diff --git a/main.c b/main.c index f338fd8ca056a7..4d7a380e531fa0 100644 --- a/main.c +++ b/main.c @@ -31,7 +31,7 @@ #endif #ifdef USE_THIRD_PARTY_HEAP -#include "mmtk.h" +#include "gc.h" #endif static int diff --git a/mmtk.h b/mmtk.h index 5f727d66aa0eb5..654a818f9b2e11 100644 --- a/mmtk.h +++ b/mmtk.h @@ -22,8 +22,6 @@ typedef void* MMTk_VMMutatorThread; #define MMTK_GC_THREAD_KIND_CONTROLLER 0 #define MMTK_GC_THREAD_KIND_WORKER 1 -#define MMTK_DEFAULT_PLAN "MarkSweep" - typedef struct { void (*init_gc_worker_thread)(MMTk_VMWorkerThread worker_tls); MMTk_VMWorkerThread (*get_gc_thread_tls)(void); @@ -106,10 +104,6 @@ extern void mmtk_harness_end(void *tls); extern void mmtk_register_finalizable(void *reff); extern void* mmtk_poll_finalizable(bool include_live); -void rb_mmtk_pre_process_opts(int argc, char **argv); -void rb_mmtk_post_process_opts(char *arg); -void rb_mmtk_post_process_opts_finish(void); - #ifdef __cplusplus } #endif diff --git a/ruby.c b/ruby.c index 5b35a81e539fec..5c53e5fc7327ea 100644 --- a/ruby.c +++ b/ruby.c @@ -62,7 +62,7 @@ #include "ruby/internal/error.h" #ifdef USE_THIRD_PARTY_HEAP -#include "mmtk.h" +#include "gc.h" #endif #ifndef MAXPATHLEN From 0c5b4363ca9324e7eb9b0b4425c0a8351b7ca07b Mon Sep 17 00:00:00 2001 From: Chris Seaton Date: Tue, 19 Jul 2022 16:54:05 +0000 Subject: [PATCH 7/7] Support rubyopt disabling for GC arguments --- gc.c | 48 +++++++++++++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/gc.c b/gc.c index a313731b895881..7fcfdbcccbec4b 100644 --- a/gc.c +++ b/gc.c @@ -15111,45 +15111,59 @@ size_t rb_mmtk_heap_limit(void) { } void rb_mmtk_pre_process_opts(int argc, char **argv) { + bool enable_rubyopt = true; + mmtk_env_plan = getenv("MMTK_PLAN"); for (int n = 1; n < argc; n++) { if (strcmp(argv[n], "--") == 0) { break; } + else if (strcmp(argv[n], "--enable-rubyopt") == 0 + || strcmp(argv[n], "--enable=rubyopt") == 0) { + enable_rubyopt = true; + } + else if (strcmp(argv[n], "--disable-rubyopt") == 0 + || strcmp(argv[n], "--disable=rubyopt") == 0) { + enable_rubyopt = false; + } else if (strncmp(argv[n], "--mmtk-plan=", strlen("--mmtk-plan=")) == 0) { mmtk_pre_arg_plan = argv[n] + strlen("--mmtk-plan="); } } - char *env_args = getenv("RUBYOPT"); - if (env_args != NULL) { - while (*env_args != '\0') { - while (ISSPACE(*env_args)) { - env_args++; - } - - if (strncmp(env_args, "--mmtk-plan=", strlen("--mmtk-plan=")) == 0) { - int length = 0; - while (env_args[length] != '\0' && !ISSPACE(env_args[length])) { - length++; + if (enable_rubyopt) { + char *env_args = getenv("RUBYOPT"); + if (env_args != NULL) { + while (*env_args != '\0') { + if (ISSPACE(*env_args)) { + env_args++; } + else { + size_t length = 0; + while (env_args[length] != '\0' && !ISSPACE(env_args[length])) { + length++; + } + + if (strncmp(env_args, "--mmtk-plan=", strlen("--mmtk-plan=")) == 0) { + mmtk_pre_arg_plan = strndup(env_args + strlen("--mmtk-plan="), length - strlen("--mmtk-plan=")); + if (mmtk_pre_arg_plan == NULL) { + rb_bug("could not allocate space for argument"); + } + } - mmtk_pre_arg_plan = strndup(env_args + strlen("--mmtk-plan="), length - strlen("--mmtk-plan=")); - if (mmtk_pre_arg_plan == NULL) { - rb_bug("could not allocate space for argument"); + env_args += length; } - env_args += length; } } } - if (mmtk_env_plan && mmtk_pre_arg_plan && strcmp(mmtk_env_plan, mmtk_pre_arg_plan) != 0) { + if (enable_rubyopt && mmtk_env_plan && mmtk_pre_arg_plan && strcmp(mmtk_env_plan, mmtk_pre_arg_plan) != 0) { fputs("[FATAL] MMTK_PLAN and --mmtk-plan do not agree\n", stderr); exit(EXIT_FAILURE); } - if (mmtk_env_plan) { + if (enable_rubyopt && mmtk_env_plan) { mmtk_chosen_plan = mmtk_env_plan; } else if (mmtk_pre_arg_plan) {