diff --git a/docs/userguide/src/tutorial/mygc/ss/collection.md b/docs/userguide/src/tutorial/mygc/ss/collection.md index af5e123e65..c0a66256e1 100644 --- a/docs/userguide/src/tutorial/mygc/ss/collection.md +++ b/docs/userguide/src/tutorial/mygc/ss/collection.md @@ -104,13 +104,19 @@ the new `tospace`. ### Prepare mutator -Going back to `mutator.rs`, create a new function called -`mygc_mutator_prepare(_mutator: &mut Mutator >, _tls: OpaquePointer,)`. -This function will be called at the preparation stage of a collection -(at the start of a collection) for each mutator. Its body can stay empty, as -there aren't any preparation steps for the mutator in this GC. -In `create_mygc_mutator()`, find the field `prep_func` and change it from -`mygc_mutator_noop()` to `mygc_mutator_prepare()`. +Going back to `mutator.rs`, create a new function called +`mygc_mutator_prepare(_mutator: &mut Mutator, _tls: VMWorkerThread)`. +This function will be called at the preparation stage of a collection (at the start of a +collection) for each mutator. Its body can stay empty, as there aren't any preparation steps for +the mutator in this GC. In `create_mygc_mutator()`, find the field `prepare_func` and change it +from `&unreachable_prepare_func` to `&mygc_mutator_prepare`. + +> 💡 Hint: If your plan does nothing when preparing mutators, there is an optimization you can do. +You may set the plan constraints field `PlanConstraints::needs_prepare_mutator` to `false` so that +the `PrepareMutator` work packets which call `prepare_func` will not be created in the first place. +This optimization is helpful for VMs that run with a large number of mutator threads. If you do +this optimization, you may also leave the `MutatorConfig::prepare_func` field as +`&unreachable_prepare_func` to indicate it should not be called. ## Release @@ -131,24 +137,18 @@ routines for the common plan spaces and the fromspace. ### Release in mutator -Go back to `mutator.rs`. In `create_mygc_mutator()`, replace -`mygc_mutator_noop()` in the `release_func` field with `mygc_mutator_release()`. -Leave the `release()` function in the `CopyContext` empty. There are no -release steps for `CopyContext` in this collector. - -Create a new function called `mygc_mutator_release()` that takes the same -inputs as the `prepare()` function above. This function will be called at the -release stage of a collection (at the end of a collection) for each mutator. -It rebinds the allocator for the `Default` allocation semantics to the new -tospace. When the mutator threads resume, any new allocations for `Default` -will then go to the new tospace. - +Go back to `mutator.rs`. Create a new function called `mygc_mutator_release()` that takes the same +inputs as the `mygc_mutator_prepare()` function above. + ```rust {{#include ../../code/mygc_semispace/mutator.rs:release}} ``` -Delete `mygc_mutator_noop()`. It was a placeholder for the prepare and -release functions that you have now added, so it is now dead code. +Then go to `create_mygc_mutator()`, replace `&unreachable_release_func` in the `release_func` field +with `&mygc_mutator_release`. This function will be called at the release stage of a collection +(at the end of a collection) for each mutator. It rebinds the allocator for the `Default` +allocation semantics to the new tospace. When the mutator threads resume, any new allocations for +`Default` will then go to the new tospace. ## ProcessEdgesWork for MyGC diff --git a/src/plan/generational/copying/mutator.rs b/src/plan/generational/copying/mutator.rs index dc8ae7025b..fc5a5a7384 100644 --- a/src/plan/generational/copying/mutator.rs +++ b/src/plan/generational/copying/mutator.rs @@ -3,6 +3,7 @@ use super::GenCopy; use crate::plan::barriers::ObjectBarrier; use crate::plan::generational::barrier::GenObjectBarrierSemantics; use crate::plan::generational::create_gen_space_mapping; +use crate::plan::mutator_context::unreachable_prepare_func; use crate::plan::mutator_context::Mutator; use crate::plan::mutator_context::MutatorConfig; use crate::plan::AllocationSemantics; @@ -12,10 +13,6 @@ use crate::util::{VMMutatorThread, VMWorkerThread}; use crate::vm::VMBinding; use crate::MMTK; -pub fn gencopy_mutator_prepare(_mutator: &mut Mutator, _tls: VMWorkerThread) { - // Do nothing -} - pub fn gencopy_mutator_release(mutator: &mut Mutator, _tls: VMWorkerThread) { // reset nursery allocator let bump_allocator = unsafe { @@ -39,7 +36,7 @@ pub fn create_gencopy_mutator( mmtk.get_plan(), &gencopy.gen.nursery, )), - prepare_func: &gencopy_mutator_prepare, + prepare_func: &unreachable_prepare_func, release_func: &gencopy_mutator_release, }; diff --git a/src/plan/generational/immix/mutator.rs b/src/plan/generational/immix/mutator.rs index 892d430d03..0f0841ac28 100644 --- a/src/plan/generational/immix/mutator.rs +++ b/src/plan/generational/immix/mutator.rs @@ -3,6 +3,7 @@ use crate::plan::barriers::ObjectBarrier; use crate::plan::generational::barrier::GenObjectBarrierSemantics; use crate::plan::generational::create_gen_space_mapping; use crate::plan::generational::immix::GenImmix; +use crate::plan::mutator_context::unreachable_prepare_func; use crate::plan::mutator_context::Mutator; use crate::plan::mutator_context::MutatorConfig; use crate::plan::AllocationSemantics; @@ -12,8 +13,6 @@ use crate::util::{VMMutatorThread, VMWorkerThread}; use crate::vm::VMBinding; use crate::MMTK; -pub fn genimmix_mutator_prepare(_mutator: &mut Mutator, _tls: VMWorkerThread) {} - pub fn genimmix_mutator_release(mutator: &mut Mutator, _tls: VMWorkerThread) { // reset nursery allocator let bump_allocator = unsafe { @@ -37,7 +36,7 @@ pub fn create_genimmix_mutator( mmtk.get_plan(), &genimmix.gen.nursery, )), - prepare_func: &genimmix_mutator_prepare, + prepare_func: &unreachable_prepare_func, release_func: &genimmix_mutator_release, }; diff --git a/src/plan/generational/mod.rs b/src/plan/generational/mod.rs index 96c25c1e0f..5623cc8ba8 100644 --- a/src/plan/generational/mod.rs +++ b/src/plan/generational/mod.rs @@ -55,6 +55,7 @@ pub const GEN_CONSTRAINTS: PlanConstraints = PlanConstraints { crate::plan::plan_constraints::MAX_NON_LOS_ALLOC_BYTES_COPYING_PLAN, crate::util::options::NURSERY_SIZE, ), + needs_prepare_mutator: false, ..PlanConstraints::default() }; diff --git a/src/plan/immix/global.rs b/src/plan/immix/global.rs index 23e2b0402e..1eec4155f1 100644 --- a/src/plan/immix/global.rs +++ b/src/plan/immix/global.rs @@ -43,6 +43,7 @@ pub const IMMIX_CONSTRAINTS: PlanConstraints = PlanConstraints { num_specialized_scans: 1, /// Max immix object size is half of a block. max_non_los_default_alloc_bytes: crate::policy::immix::MAX_IMMIX_OBJECT_SIZE, + needs_prepare_mutator: false, ..PlanConstraints::default() }; diff --git a/src/plan/immix/mutator.rs b/src/plan/immix/mutator.rs index 9dfa8cb460..7eca77c1d5 100644 --- a/src/plan/immix/mutator.rs +++ b/src/plan/immix/mutator.rs @@ -1,6 +1,7 @@ use super::Immix; use crate::plan::mutator_context::create_allocator_mapping; use crate::plan::mutator_context::create_space_mapping; +use crate::plan::mutator_context::unreachable_prepare_func; use crate::plan::mutator_context::Mutator; use crate::plan::mutator_context::MutatorConfig; use crate::plan::mutator_context::ReservedAllocators; @@ -15,17 +16,6 @@ use crate::{ }; use enum_map::EnumMap; -pub fn immix_mutator_prepare(mutator: &mut Mutator, _tls: VMWorkerThread) { - let immix_allocator = unsafe { - mutator - .allocators - .get_allocator_mut(mutator.config.allocator_mapping[AllocationSemantics::Default]) - } - .downcast_mut::>() - .unwrap(); - immix_allocator.reset(); -} - pub fn immix_mutator_release(mutator: &mut Mutator, _tls: VMWorkerThread) { let immix_allocator = unsafe { mutator @@ -62,7 +52,7 @@ pub fn create_immix_mutator( vec.push((AllocatorSelector::Immix(0), &immix.immix_space)); vec }), - prepare_func: &immix_mutator_prepare, + prepare_func: &unreachable_prepare_func, release_func: &immix_mutator_release, }; diff --git a/src/plan/markcompact/global.rs b/src/plan/markcompact/global.rs index 32da425ea3..a04b891f28 100644 --- a/src/plan/markcompact/global.rs +++ b/src/plan/markcompact/global.rs @@ -44,6 +44,7 @@ pub const MARKCOMPACT_CONSTRAINTS: PlanConstraints = PlanConstraints { needs_forward_after_liveness: true, max_non_los_default_alloc_bytes: crate::plan::plan_constraints::MAX_NON_LOS_ALLOC_BYTES_COPYING_PLAN, + needs_prepare_mutator: false, ..PlanConstraints::default() }; diff --git a/src/plan/markcompact/mutator.rs b/src/plan/markcompact/mutator.rs index c52fa7387d..64a071abcf 100644 --- a/src/plan/markcompact/mutator.rs +++ b/src/plan/markcompact/mutator.rs @@ -2,6 +2,7 @@ use super::MarkCompact; // Add use crate::plan::barriers::NoBarrier; use crate::plan::mutator_context::create_allocator_mapping; use crate::plan::mutator_context::create_space_mapping; +use crate::plan::mutator_context::unreachable_prepare_func; use crate::plan::mutator_context::Mutator; use crate::plan::mutator_context::MutatorConfig; use crate::plan::mutator_context::ReservedAllocators; @@ -38,7 +39,7 @@ pub fn create_markcompact_mutator( vec.push((AllocatorSelector::MarkCompact(0), markcompact.mc_space())); vec }), - prepare_func: &markcompact_mutator_prepare, + prepare_func: &unreachable_prepare_func, release_func: &markcompact_mutator_release, }; @@ -51,12 +52,6 @@ pub fn create_markcompact_mutator( } } -pub fn markcompact_mutator_prepare( - _mutator: &mut Mutator, - _tls: VMWorkerThread, -) { -} - pub fn markcompact_mutator_release( _mutator: &mut Mutator, _tls: VMWorkerThread, diff --git a/src/plan/marksweep/global.rs b/src/plan/marksweep/global.rs index 34a59f5a10..30b16fcc3c 100644 --- a/src/plan/marksweep/global.rs +++ b/src/plan/marksweep/global.rs @@ -43,6 +43,8 @@ pub const MS_CONSTRAINTS: PlanConstraints = PlanConstraints { num_specialized_scans: 1, max_non_los_default_alloc_bytes: MAX_OBJECT_SIZE, may_trace_duplicate_edges: true, + needs_prepare_mutator: !cfg!(feature = "malloc_mark_sweep") + && !cfg!(feature = "eager_sweeping"), ..PlanConstraints::default() }; diff --git a/src/plan/mutator_context.rs b/src/plan/mutator_context.rs index b221f3003f..ad434f9987 100644 --- a/src/plan/mutator_context.rs +++ b/src/plan/mutator_context.rs @@ -13,6 +13,28 @@ use enum_map::EnumMap; pub(crate) type SpaceMapping = Vec<(AllocatorSelector, &'static dyn Space)>; +/// A place-holder implementation for `MutatorConfig::prepare_func` that should not be called. +/// It is the most often used by plans that sets `PlanConstraints::needs_prepare_mutator` to +/// `false`. It is also used by `NoGC` because it must not trigger GC. +pub(crate) fn unreachable_prepare_func( + _mutator: &mut Mutator, + _tls: VMWorkerThread, +) { + unreachable!("`MutatorConfig::prepare_func` must not be called for the current plan.") +} + +/// A place-holder implementation for `MutatorConfig::release_func` that should not be called. +/// Currently only used by `NoGC`. +pub(crate) fn unreachable_release_func( + _mutator: &mut Mutator, + _tls: VMWorkerThread, +) { + unreachable!("`MutatorConfig::release_func` must not be called for the current plan.") +} + +/// A place-holder implementation for `MutatorConfig::release_func` that does nothing. +pub(crate) fn no_op_release_func(_mutator: &mut Mutator, _tls: VMWorkerThread) {} + // This struct is part of the Mutator struct. // We are trying to make it fixed-sized so that VM bindings can easily define a Mutator type to have the exact same layout as our Mutator struct. #[repr(C)] diff --git a/src/plan/nogc/mutator.rs b/src/plan/nogc/mutator.rs index 197aa64ade..f71a401b05 100644 --- a/src/plan/nogc/mutator.rs +++ b/src/plan/nogc/mutator.rs @@ -1,4 +1,6 @@ use crate::plan::barriers::NoBarrier; +use crate::plan::mutator_context::unreachable_prepare_func; +use crate::plan::mutator_context::unreachable_release_func; use crate::plan::mutator_context::Mutator; use crate::plan::mutator_context::MutatorConfig; use crate::plan::mutator_context::{ @@ -8,7 +10,7 @@ use crate::plan::nogc::NoGC; use crate::plan::AllocationSemantics; use crate::plan::Plan; use crate::util::alloc::allocators::{AllocatorSelector, Allocators}; -use crate::util::{VMMutatorThread, VMWorkerThread}; +use crate::util::VMMutatorThread; use crate::vm::VMBinding; use enum_map::{enum_map, EnumMap}; @@ -36,10 +38,6 @@ lazy_static! { }; } -pub fn nogc_mutator_noop(_mutator: &mut Mutator, _tls: VMWorkerThread) { - unreachable!(); -} - pub fn create_nogc_mutator( mutator_tls: VMMutatorThread, plan: &'static dyn Plan, @@ -62,8 +60,8 @@ pub fn create_nogc_mutator( )); vec }), - prepare_func: &nogc_mutator_noop, - release_func: &nogc_mutator_noop, + prepare_func: &unreachable_prepare_func, + release_func: &unreachable_release_func, }; Mutator { diff --git a/src/plan/pageprotect/global.rs b/src/plan/pageprotect/global.rs index 1e8021fae8..d1641e18e0 100644 --- a/src/plan/pageprotect/global.rs +++ b/src/plan/pageprotect/global.rs @@ -30,6 +30,7 @@ pub struct PageProtect { pub const CONSTRAINTS: PlanConstraints = PlanConstraints { moves_objects: false, + needs_prepare_mutator: false, ..PlanConstraints::default() }; diff --git a/src/plan/pageprotect/mutator.rs b/src/plan/pageprotect/mutator.rs index 647133a631..513c9fe24f 100644 --- a/src/plan/pageprotect/mutator.rs +++ b/src/plan/pageprotect/mutator.rs @@ -1,4 +1,6 @@ use super::PageProtect; +use crate::plan::mutator_context::no_op_release_func; +use crate::plan::mutator_context::unreachable_prepare_func; use crate::plan::mutator_context::Mutator; use crate::plan::mutator_context::MutatorConfig; use crate::plan::mutator_context::{ @@ -8,18 +10,9 @@ use crate::plan::AllocationSemantics; use crate::plan::Plan; use crate::util::alloc::allocators::{AllocatorSelector, Allocators}; use crate::vm::VMBinding; -use crate::{ - plan::barriers::NoBarrier, - util::opaque_pointer::{VMMutatorThread, VMWorkerThread}, -}; +use crate::{plan::barriers::NoBarrier, util::opaque_pointer::VMMutatorThread}; use enum_map::EnumMap; -/// Prepare mutator. Do nothing. -fn pp_mutator_prepare(_mutator: &mut Mutator, _tls: VMWorkerThread) {} - -/// Release mutator. Do nothing. -fn pp_mutator_release(_mutator: &mut Mutator, _tls: VMWorkerThread) {} - const RESERVED_ALLOCATORS: ReservedAllocators = ReservedAllocators { n_large_object: 1, ..ReservedAllocators::DEFAULT @@ -47,8 +40,8 @@ pub fn create_pp_mutator( vec.push((AllocatorSelector::LargeObject(0), &page.space)); vec }), - prepare_func: &pp_mutator_prepare, - release_func: &pp_mutator_release, + prepare_func: &unreachable_prepare_func, + release_func: &no_op_release_func, }; Mutator { diff --git a/src/plan/plan_constraints.rs b/src/plan/plan_constraints.rs index 700f379d56..f2ab81f7c1 100644 --- a/src/plan/plan_constraints.rs +++ b/src/plan/plan_constraints.rs @@ -33,6 +33,10 @@ pub struct PlanConstraints { /// Some policies do object forwarding after the first liveness transitive closure, such as mark compact. /// For plans that use those policies, they should set this as true. pub needs_forward_after_liveness: bool, + /// Some (in fact, most) plans do nothing when preparing mutators before tracing (i.e. in + /// `MutatorConfig::prepare_func`). Those plans can set this to `false` so that the + /// `PrepareMutator` work packets will not be created at all. + pub needs_prepare_mutator: bool, } impl PlanConstraints { @@ -51,6 +55,7 @@ impl PlanConstraints { needs_forward_after_liveness: false, needs_log_bit: false, barrier: BarrierSelector::NoBarrier, + needs_prepare_mutator: true, } } } diff --git a/src/plan/semispace/global.rs b/src/plan/semispace/global.rs index ec74ec6c4c..9745b07bc5 100644 --- a/src/plan/semispace/global.rs +++ b/src/plan/semispace/global.rs @@ -42,6 +42,7 @@ pub const SS_CONSTRAINTS: PlanConstraints = PlanConstraints { num_specialized_scans: 1, max_non_los_default_alloc_bytes: crate::plan::plan_constraints::MAX_NON_LOS_ALLOC_BYTES_COPYING_PLAN, + needs_prepare_mutator: false, ..PlanConstraints::default() }; diff --git a/src/plan/semispace/mutator.rs b/src/plan/semispace/mutator.rs index 26f49e283e..3d80df4207 100644 --- a/src/plan/semispace/mutator.rs +++ b/src/plan/semispace/mutator.rs @@ -1,5 +1,6 @@ use super::SemiSpace; use crate::plan::barriers::NoBarrier; +use crate::plan::mutator_context::unreachable_prepare_func; use crate::plan::mutator_context::Mutator; use crate::plan::mutator_context::MutatorConfig; use crate::plan::mutator_context::{ @@ -13,10 +14,6 @@ use crate::util::{VMMutatorThread, VMWorkerThread}; use crate::vm::VMBinding; use enum_map::EnumMap; -pub fn ss_mutator_prepare(_mutator: &mut Mutator, _tls: VMWorkerThread) { - // Do nothing -} - pub fn ss_mutator_release(mutator: &mut Mutator, _tls: VMWorkerThread) { // rebind the allocation bump pointer to the appropriate semispace let bump_allocator = unsafe { @@ -60,7 +57,7 @@ pub fn create_ss_mutator( vec.push((AllocatorSelector::BumpPointer(0), ss.tospace())); vec }), - prepare_func: &ss_mutator_prepare, + prepare_func: &unreachable_prepare_func, release_func: &ss_mutator_release, }; diff --git a/src/plan/sticky/immix/mutator.rs b/src/plan/sticky/immix/mutator.rs index c5b7b17721..4f29c8ac51 100644 --- a/src/plan/sticky/immix/mutator.rs +++ b/src/plan/sticky/immix/mutator.rs @@ -1,7 +1,7 @@ use crate::plan::barriers::ObjectBarrier; use crate::plan::generational::barrier::GenObjectBarrierSemantics; use crate::plan::immix; -use crate::plan::mutator_context::{create_space_mapping, MutatorConfig}; +use crate::plan::mutator_context::{create_space_mapping, unreachable_prepare_func, MutatorConfig}; use crate::plan::sticky::immix::global::StickyImmix; use crate::util::alloc::allocators::Allocators; use crate::util::alloc::AllocatorSelector; @@ -10,10 +10,6 @@ use crate::util::VMMutatorThread; use crate::vm::VMBinding; use crate::{Mutator, MMTK}; -pub fn stickyimmix_mutator_prepare(mutator: &mut Mutator, tls: VMWorkerThread) { - immix::mutator::immix_mutator_prepare(mutator, tls) -} - pub fn stickyimmix_mutator_release(mutator: &mut Mutator, tls: VMWorkerThread) { immix::mutator::immix_mutator_release(mutator, tls) } @@ -33,7 +29,7 @@ pub fn create_stickyimmix_mutator( vec.push((AllocatorSelector::Immix(0), stickyimmix.get_immix_space())); vec }), - prepare_func: &stickyimmix_mutator_prepare, + prepare_func: &unreachable_prepare_func, release_func: &stickyimmix_mutator_release, }; diff --git a/src/scheduler/gc_work.rs b/src/scheduler/gc_work.rs index ebf45d5c77..2aa0607445 100644 --- a/src/scheduler/gc_work.rs +++ b/src/scheduler/gc_work.rs @@ -51,9 +51,11 @@ impl GCWork for Prepare { let plan_mut: &mut C::PlanType = unsafe { &mut *(self.plan as *const _ as *mut _) }; plan_mut.prepare(worker.tls); - for mutator in ::VMActivePlan::mutators() { - mmtk.scheduler.work_buckets[WorkBucketStage::Prepare] - .add(PrepareMutator::::new(mutator)); + if plan_mut.constraints().needs_prepare_mutator { + for mutator in ::VMActivePlan::mutators() { + mmtk.scheduler.work_buckets[WorkBucketStage::Prepare] + .add(PrepareMutator::::new(mutator)); + } } for w in &mmtk.scheduler.worker_group.workers_shared { let result = w.designated_work.push(Box::new(PrepareCollector));