From 300d2afcab1f596e2cc7287cdae44e52152b058d Mon Sep 17 00:00:00 2001 From: Yi Lin Date: Thu, 7 Apr 2022 14:42:51 +1000 Subject: [PATCH 01/22] Add ImmixProcessEdgesWork. Immix and GenImmix now use this work packet. --- src/plan/generational/immix/gc_work.rs | 86 +--------------- src/plan/generational/immix/global.rs | 25 ++++- src/plan/immix/gc_work.rs | 106 +------------------- src/plan/immix/global.rs | 25 ++++- src/policy/immix/gc_work.rs | 130 +++++++++++++++++++++++++ src/policy/immix/mod.rs | 1 + 6 files changed, 184 insertions(+), 189 deletions(-) create mode 100644 src/policy/immix/gc_work.rs diff --git a/src/plan/generational/immix/gc_work.rs b/src/plan/generational/immix/gc_work.rs index 1ed773c328..6fa7a17c0b 100644 --- a/src/plan/generational/immix/gc_work.rs +++ b/src/plan/generational/immix/gc_work.rs @@ -1,89 +1,9 @@ use super::global::GenImmix; use crate::plan::generational::gc_work::GenNurseryProcessEdges; -use crate::policy::space::Space; -use crate::scheduler::gc_work::*; -use crate::util::{Address, ObjectReference}; use crate::vm::*; -use crate::MMTK; -use std::ops::{Deref, DerefMut}; -use crate::plan::immix::gc_work::{TraceKind, TRACE_KIND_FAST}; - -/// ProcessEdges for a full heap GC for generational immix. The const type parameter -/// defines whether there is copying in the GC. -/// Note that even with TraceKind::Fast, there is no defragmentation, we are still -/// copying from nursery to immix space. So we always need to write new object -/// references in process_edge() (i.e. we do not need to overwrite the default implementation -/// of process_edge() as the immix plan does). -pub(super) struct GenImmixMatureProcessEdges { - plan: &'static GenImmix, - base: ProcessEdgesBase, -} - -impl ProcessEdgesWork - for GenImmixMatureProcessEdges -{ - type VM = VM; - - fn new(edges: Vec
, roots: bool, mmtk: &'static MMTK) -> Self { - let base = ProcessEdgesBase::new(edges, roots, mmtk); - let plan = base.plan().downcast_ref::>().unwrap(); - Self { plan, base } - } - - #[cold] - fn flush(&mut self) { - if self.nodes.is_empty() { - return; - } - - let scan_objects_work = crate::policy::immix::ScanObjectsAndMarkLines::::new( - self.pop_nodes(), - false, - &self.plan.immix, - ); - self.new_scan_work(scan_objects_work); - } - - #[inline] - fn trace_object(&mut self, object: ObjectReference) -> ObjectReference { - if object.is_null() { - return object; - } - - if self.plan.immix.in_space(object) { - if KIND == TRACE_KIND_FAST { - return self.plan.immix.fast_trace_object(self, object); - } else { - return self.plan.immix.trace_object( - self, - object, - crate::util::copy::CopySemantics::Mature, - self.worker(), - ); - } - } - - self.plan - .gen - .trace_object_full_heap::(self, object, self.worker()) - } -} - -impl Deref for GenImmixMatureProcessEdges { - type Target = ProcessEdgesBase; - #[inline] - fn deref(&self) -> &Self::Target { - &self.base - } -} - -impl DerefMut for GenImmixMatureProcessEdges { - #[inline] - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.base - } -} +use crate::policy::immix::gc_work::ImmixProcessEdgesWork; +use crate::policy::immix::gc_work::TraceKind; pub struct GenImmixNurseryGCWorkContext(std::marker::PhantomData); impl crate::scheduler::GCWorkContext for GenImmixNurseryGCWorkContext { @@ -100,5 +20,5 @@ impl crate::scheduler::GCWorkContext { type VM = VM; type PlanType = GenImmix; - type ProcessEdgesWorkType = GenImmixMatureProcessEdges; + type ProcessEdgesWorkType = ImmixProcessEdgesWork, KIND>; } diff --git a/src/plan/generational/immix/global.rs b/src/plan/generational/immix/global.rs index 04c5fb63fe..e0442a968b 100644 --- a/src/plan/generational/immix/global.rs +++ b/src/plan/generational/immix/global.rs @@ -4,13 +4,15 @@ use crate::plan::generational::global::Gen; use crate::plan::global::BasePlan; use crate::plan::global::CommonPlan; use crate::plan::global::GcStatus; -use crate::plan::immix::gc_work::{TRACE_KIND_DEFRAG, TRACE_KIND_FAST}; use crate::plan::AllocationSemantics; use crate::plan::Plan; use crate::plan::PlanConstraints; +use crate::plan::TransitiveClosure; +use crate::policy::immix::gc_work::{TRACE_KIND_DEFRAG, TRACE_KIND_FAST}; use crate::policy::immix::ImmixSpace; use crate::policy::space::Space; use crate::scheduler::GCWorkScheduler; +use crate::scheduler::GCWorker; use crate::util::alloc::allocators::AllocatorSelector; use crate::util::copy::*; use crate::util::heap::layout::heap_layout::Mmapper; @@ -18,6 +20,7 @@ use crate::util::heap::layout::heap_layout::VMMap; use crate::util::heap::layout::vm_layout_constants::{HEAP_END, HEAP_START}; use crate::util::heap::HeapMeta; use crate::util::options::UnsafeOptionsWrapper; +use crate::util::ObjectReference; use crate::util::VMWorkerThread; use crate::vm::*; @@ -197,6 +200,26 @@ impl Plan for GenImmix { } } +impl crate::policy::immix::gc_work::ImmixPlan for GenImmix { + #[inline(always)] + fn get_immix_space(&'static self) -> &'static ImmixSpace { + &self.immix + } + #[inline(always)] + fn get_immix_copy_semantics() -> CopySemantics { + CopySemantics::Mature + } + #[inline(always)] + fn fallback_trace( + &self, + trace: &mut T, + object: ObjectReference, + worker: &mut GCWorker, + ) -> ObjectReference { + self.gen.trace_object_full_heap::(trace, object, worker) + } +} + impl GenImmix { pub fn new( vm_map: &'static VMMap, diff --git a/src/plan/immix/gc_work.rs b/src/plan/immix/gc_work.rs index 903b579ff1..1b8203a894 100644 --- a/src/plan/immix/gc_work.rs +++ b/src/plan/immix/gc_work.rs @@ -1,108 +1,6 @@ use super::global::Immix; -use crate::policy::space::Space; -use crate::scheduler::gc_work::*; -use crate::util::copy::CopySemantics; -use crate::util::{Address, ObjectReference}; +use crate::policy::immix::gc_work::{ImmixProcessEdgesWork, TraceKind}; use crate::vm::VMBinding; -use crate::MMTK; -use std::ops::{Deref, DerefMut}; - -// It would be better if we use an enum for this. However, we use this as -// a constant type parameter, and Rust only accepts integer and bool for -// constant type parameters for now. We need to wait until `adt_const_params` is -// stablized. -pub(in crate::plan) type TraceKind = u8; -pub(in crate::plan) const TRACE_KIND_FAST: TraceKind = 0; -pub(in crate::plan) const TRACE_KIND_DEFRAG: TraceKind = 1; - -/// Object tracing for Immix. -/// Note that it is possible to use [`SFTProcessEdges`](mmtk/scheduler/gc_work/SFTProcessEdges) for immix. -/// We need to: 1. add a plan-specific method to create scan work packets, as most plans use `ScanObjects` while -/// immix uses `ScanObjectsAndMarkLines`, 2. use `ImmixSpace.trace_object()` which has an overhead of checking -/// which trace method to use (with ImmixProcessEdges, we can know which trace method to use by statically checking `TraceKind`). -pub(super) struct ImmixProcessEdges { - // Use a static ref to the specific plan to avoid overhead from dynamic dispatch or - // downcast for each traced object. - plan: &'static Immix, - base: ProcessEdgesBase, -} - -impl ImmixProcessEdges { - fn immix(&self) -> &'static Immix { - self.plan - } -} - -impl ProcessEdgesWork for ImmixProcessEdges { - type VM = VM; - - const OVERWRITE_REFERENCE: bool = crate::policy::immix::DEFRAG; - - fn new(edges: Vec
, roots: bool, mmtk: &'static MMTK) -> Self { - let base = ProcessEdgesBase::new(edges, roots, mmtk); - let plan = base.plan().downcast_ref::>().unwrap(); - Self { plan, base } - } - - #[cold] - fn flush(&mut self) { - if self.nodes.is_empty() { - return; - } - let scan_objects_work = crate::policy::immix::ScanObjectsAndMarkLines::::new( - self.pop_nodes(), - false, - &self.immix().immix_space, - ); - self.new_scan_work(scan_objects_work); - } - - /// Trace and evacuate objects. - #[inline(always)] - fn trace_object(&mut self, object: ObjectReference) -> ObjectReference { - if object.is_null() { - return object; - } - if self.immix().immix_space.in_space(object) { - if KIND == TRACE_KIND_FAST { - self.immix().immix_space.fast_trace_object(self, object) - } else { - self.immix().immix_space.trace_object( - self, - object, - CopySemantics::DefaultCopy, - self.worker(), - ) - } - } else { - self.immix().common.trace_object::(self, object) - } - } - - #[inline] - fn process_edge(&mut self, slot: Address) { - let object = unsafe { slot.load::() }; - let new_object = self.trace_object(object); - if KIND == TRACE_KIND_DEFRAG && Self::OVERWRITE_REFERENCE { - unsafe { slot.store(new_object) }; - } - } -} - -impl Deref for ImmixProcessEdges { - type Target = ProcessEdgesBase; - #[inline] - fn deref(&self) -> &Self::Target { - &self.base - } -} - -impl DerefMut for ImmixProcessEdges { - #[inline] - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.base - } -} pub(super) struct ImmixGCWorkContext( std::marker::PhantomData, @@ -112,5 +10,5 @@ impl crate::scheduler::GCWorkContext { type VM = VM; type PlanType = Immix; - type ProcessEdgesWorkType = ImmixProcessEdges; + type ProcessEdgesWorkType = ImmixProcessEdgesWork, KIND>; } diff --git a/src/plan/immix/global.rs b/src/plan/immix/global.rs index 78008cf3bc..c3263f6c89 100644 --- a/src/plan/immix/global.rs +++ b/src/plan/immix/global.rs @@ -1,5 +1,4 @@ use super::gc_work::ImmixGCWorkContext; -use super::gc_work::{TRACE_KIND_DEFRAG, TRACE_KIND_FAST}; use super::mutator::ALLOCATOR_MAPPING; use crate::plan::global::BasePlan; use crate::plan::global::CommonPlan; @@ -7,7 +6,10 @@ use crate::plan::global::GcStatus; use crate::plan::AllocationSemantics; use crate::plan::Plan; use crate::plan::PlanConstraints; +use crate::plan::TransitiveClosure; +use crate::policy::immix::gc_work::{TRACE_KIND_DEFRAG, TRACE_KIND_FAST}; use crate::policy::space::Space; +use crate::scheduler::GCWorker; use crate::scheduler::*; use crate::util::alloc::allocators::AllocatorSelector; use crate::util::copy::*; @@ -18,6 +20,7 @@ use crate::util::heap::HeapMeta; use crate::util::metadata::side_metadata::SideMetadataContext; use crate::util::metadata::side_metadata::SideMetadataSanity; use crate::util::options::UnsafeOptionsWrapper; +use crate::util::ObjectReference; use crate::vm::VMBinding; use crate::{policy::immix::ImmixSpace, util::opaque_pointer::VMWorkerThread}; use std::sync::atomic::AtomicBool; @@ -127,6 +130,26 @@ impl Plan for Immix { } } +impl crate::policy::immix::gc_work::ImmixPlan for Immix { + #[inline(always)] + fn get_immix_space(&'static self) -> &'static ImmixSpace { + &self.immix_space + } + #[inline(always)] + fn get_immix_copy_semantics() -> CopySemantics { + CopySemantics::DefaultCopy + } + #[inline(always)] + fn fallback_trace( + &self, + trace: &mut T, + object: ObjectReference, + _worker: &mut GCWorker, + ) -> ObjectReference { + self.common.trace_object::(trace, object) + } +} + impl Immix { pub fn new( vm_map: &'static VMMap, diff --git a/src/policy/immix/gc_work.rs b/src/policy/immix/gc_work.rs new file mode 100644 index 0000000000..bcf57a2dac --- /dev/null +++ b/src/policy/immix/gc_work.rs @@ -0,0 +1,130 @@ +use crate::plan::Plan; +use crate::plan::TransitiveClosure; +use crate::policy::space::Space; +use crate::scheduler::gc_work::*; +use crate::scheduler::GCWorker; +use crate::util::copy::CopySemantics; +use crate::util::Address; +use crate::util::ObjectReference; +use crate::vm::VMBinding; +use crate::MMTK; + +use super::ImmixSpace; + +use std::marker::PhantomData; +use std::ops::{Deref, DerefMut}; + +// It would be better if we use an enum for this. However, we use this as +// a constant type parameter, and Rust only accepts integer and bool for +// constant type parameters for now. We need to wait until `adt_const_params` is +// stablized. +pub(crate) type TraceKind = u8; +pub(crate) const TRACE_KIND_FAST: TraceKind = 0; +pub(crate) const TRACE_KIND_DEFRAG: TraceKind = 1; + +/// An Immix plan can use `ImmixProcessEdgesWork`. This assumes there is only one immix space in the plan. +/// For the sake of performance, the implementation of these methods should be marked as `inline(always)`. +pub trait ImmixPlan: Plan + Send { + /// Returns a reference to the immix space. + fn get_immix_space(&'static self) -> &'static ImmixSpace; + /// Returns the copy semantic for the immix space. + fn get_immix_copy_semantics() -> CopySemantics; + /// How to trace objects if the object is not in the immix space. + fn fallback_trace( + &self, + trace: &mut T, + object: ObjectReference, + worker: &mut GCWorker, + ) -> ObjectReference; +} + +/// Object tracing for Immix. +/// Note that it is possible to use [`SFTProcessEdges`](mmtk/scheduler/gc_work/SFTProcessEdges) for immix. +/// We need to: 1. add a plan-specific method to create scan work packets, as most plans use `ScanObjects` while +/// immix uses `ScanObjectsAndMarkLines`, 2. use `ImmixSpace.trace_object()` which has an overhead of checking +/// which trace method to use (with ImmixProcessEdges, we can know which trace method to use by statically checking `TraceKind`). +pub struct ImmixProcessEdgesWork, const KIND: TraceKind> { + plan: &'static P, + base: ProcessEdgesBase, + p: PhantomData

, +} + +impl, const KIND: TraceKind> ProcessEdgesWork + for ImmixProcessEdgesWork +{ + type VM = VM; + + fn new(edges: Vec

, roots: bool, mmtk: &'static MMTK) -> Self { + let base = ProcessEdgesBase::new(edges, roots, mmtk); + let plan = base.plan().downcast_ref::

().unwrap(); + Self { + plan, + base, + p: PhantomData, + } + } + + #[cold] + fn flush(&mut self) { + if self.nodes.is_empty() { + return; + } + let scan_objects_work = crate::policy::immix::ScanObjectsAndMarkLines::::new( + self.pop_nodes(), + false, + self.plan.get_immix_space(), + ); + self.new_scan_work(scan_objects_work); + } + + /// Trace and evacuate objects. + #[inline(always)] + fn trace_object(&mut self, object: ObjectReference) -> ObjectReference { + if object.is_null() { + return object; + } + if self.plan.get_immix_space().in_space(object) { + if KIND == TRACE_KIND_FAST { + self.plan.get_immix_space().fast_trace_object(self, object) + } else { + self.plan.get_immix_space().trace_object( + self, + object, + P::get_immix_copy_semantics(), + self.worker(), + ) + } + } else { + self.plan + .fallback_trace::(self, object, self.worker()) + } + } + + #[inline] + fn process_edge(&mut self, slot: Address) { + let object = unsafe { slot.load::() }; + let new_object = self.trace_object(object); + if KIND == TRACE_KIND_DEFRAG && Self::OVERWRITE_REFERENCE { + unsafe { slot.store(new_object) }; + } + } +} + +impl, const KIND: TraceKind> Deref + for ImmixProcessEdgesWork +{ + type Target = ProcessEdgesBase; + #[inline] + fn deref(&self) -> &Self::Target { + &self.base + } +} + +impl, const KIND: TraceKind> DerefMut + for ImmixProcessEdgesWork +{ + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.base + } +} diff --git a/src/policy/immix/mod.rs b/src/policy/immix/mod.rs index aaaf732cbb..3eba3f6bfd 100644 --- a/src/policy/immix/mod.rs +++ b/src/policy/immix/mod.rs @@ -1,6 +1,7 @@ pub mod block; pub mod chunk; pub mod defrag; +pub mod gc_work; pub mod immixspace; pub mod line; From 65d4c908d325b91fd0793147cb75234bd88ecc1e Mon Sep 17 00:00:00 2001 From: Yi Lin Date: Fri, 8 Apr 2022 12:54:12 +1000 Subject: [PATCH 02/22] Refactor ImmixProcessEdges to PolicyProcessEdges. Mark compact also uses it. --- src/plan/generational/immix/gc_work.rs | 60 ++++++- src/plan/generational/immix/global.rs | 22 +-- src/plan/immix/gc_work.rs | 58 ++++++- src/plan/immix/global.rs | 22 +-- src/plan/markcompact/gc_work.rs | 232 +++++++++++++++---------- src/policy/{immix => }/gc_work.rs | 69 ++++---- src/policy/immix/immixspace.rs | 4 + src/policy/immix/mod.rs | 1 - src/policy/markcompactspace.rs | 4 + src/policy/mod.rs | 2 + src/scheduler/gc_work.rs | 8 +- src/scheduler/work_bucket.rs | 3 + src/scheduler/worker.rs | 4 + 13 files changed, 303 insertions(+), 186 deletions(-) rename src/policy/{immix => }/gc_work.rs (51%) diff --git a/src/plan/generational/immix/gc_work.rs b/src/plan/generational/immix/gc_work.rs index 6fa7a17c0b..b5131dc117 100644 --- a/src/plan/generational/immix/gc_work.rs +++ b/src/plan/generational/immix/gc_work.rs @@ -1,9 +1,61 @@ use super::global::GenImmix; +use crate::policy::gc_work::{PolicyProcessEdges, TraceKind}; +use crate::vm::VMBinding; +use crate::scheduler::GCWork; +use crate::policy::immix::ImmixSpace; +use crate::util::copy::CopySemantics; +use crate::plan::TransitiveClosure; +use crate::policy::immix::{TRACE_KIND_FAST, TRACE_KIND_DEFRAG}; +use crate::util::ObjectReference; +use crate::scheduler::GCWorker; +use crate::scheduler::ProcessEdgesWork; use crate::plan::generational::gc_work::GenNurseryProcessEdges; -use crate::vm::*; -use crate::policy::immix::gc_work::ImmixProcessEdgesWork; -use crate::policy::immix::gc_work::TraceKind; +impl crate::policy::gc_work::UsePolicyProcessEdges for GenImmix { + type SpaceType = ImmixSpace; + + #[inline(always)] + fn get_target_space(&'static self) -> &'static Self::SpaceType { + &self.immix + } + #[inline(always)] + fn get_target_copy_semantics() -> CopySemantics { + CopySemantics::DefaultCopy + } + /// How to trace object in target space + fn target_trace( + &self, + trace: &mut T, + object: ObjectReference, + worker: &mut GCWorker, + ) -> ObjectReference { + if KIND == TRACE_KIND_FAST { + self.immix.fast_trace_object(trace, object) + } else { + self.immix.trace_object(trace, object, CopySemantics::DefaultCopy, worker) + } + } + fn overwrite_reference() -> bool { + KIND == TRACE_KIND_DEFRAG + } + /// How to trace objects if the object is not in the immix space. + fn fallback_trace( + &self, + trace: &mut T, + object: ObjectReference, + worker: &mut GCWorker, + ) -> ObjectReference { + self.gen.trace_object_full_heap::(trace, object, worker) + } + + fn create_scan_work>(&'static self, nodes: Vec) -> Box> { + Box::new(crate::policy::immix::ScanObjectsAndMarkLines::::new( + nodes, + false, + &self.immix, + )) + } +} pub struct GenImmixNurseryGCWorkContext(std::marker::PhantomData); impl crate::scheduler::GCWorkContext for GenImmixNurseryGCWorkContext { @@ -20,5 +72,5 @@ impl crate::scheduler::GCWorkContext { type VM = VM; type PlanType = GenImmix; - type ProcessEdgesWorkType = ImmixProcessEdgesWork, KIND>; + type ProcessEdgesWorkType = PolicyProcessEdges, KIND>; } diff --git a/src/plan/generational/immix/global.rs b/src/plan/generational/immix/global.rs index e0442a968b..ec44f03bb3 100644 --- a/src/plan/generational/immix/global.rs +++ b/src/plan/generational/immix/global.rs @@ -8,7 +8,7 @@ use crate::plan::AllocationSemantics; use crate::plan::Plan; use crate::plan::PlanConstraints; use crate::plan::TransitiveClosure; -use crate::policy::immix::gc_work::{TRACE_KIND_DEFRAG, TRACE_KIND_FAST}; +use crate::policy::immix::{TRACE_KIND_DEFRAG, TRACE_KIND_FAST}; use crate::policy::immix::ImmixSpace; use crate::policy::space::Space; use crate::scheduler::GCWorkScheduler; @@ -200,26 +200,6 @@ impl Plan for GenImmix { } } -impl crate::policy::immix::gc_work::ImmixPlan for GenImmix { - #[inline(always)] - fn get_immix_space(&'static self) -> &'static ImmixSpace { - &self.immix - } - #[inline(always)] - fn get_immix_copy_semantics() -> CopySemantics { - CopySemantics::Mature - } - #[inline(always)] - fn fallback_trace( - &self, - trace: &mut T, - object: ObjectReference, - worker: &mut GCWorker, - ) -> ObjectReference { - self.gen.trace_object_full_heap::(trace, object, worker) - } -} - impl GenImmix { pub fn new( vm_map: &'static VMMap, diff --git a/src/plan/immix/gc_work.rs b/src/plan/immix/gc_work.rs index 1b8203a894..daa39a31b3 100644 --- a/src/plan/immix/gc_work.rs +++ b/src/plan/immix/gc_work.rs @@ -1,6 +1,60 @@ use super::global::Immix; -use crate::policy::immix::gc_work::{ImmixProcessEdgesWork, TraceKind}; +use crate::policy::gc_work::{PolicyProcessEdges, TraceKind}; use crate::vm::VMBinding; +use crate::scheduler::GCWork; +use crate::policy::immix::ImmixSpace; +use crate::util::copy::CopySemantics; +use crate::plan::TransitiveClosure; +use crate::policy::immix::{TRACE_KIND_FAST, TRACE_KIND_DEFRAG}; +use crate::util::ObjectReference; +use crate::scheduler::GCWorker; +use crate::scheduler::ProcessEdgesWork; + +impl crate::policy::gc_work::UsePolicyProcessEdges for Immix { + type SpaceType = ImmixSpace; + + #[inline(always)] + fn get_target_space(&'static self) -> &'static Self::SpaceType { + &self.immix_space + } + #[inline(always)] + fn get_target_copy_semantics() -> CopySemantics { + CopySemantics::DefaultCopy + } + /// How to trace object in target space + fn target_trace( + &self, + trace: &mut T, + object: ObjectReference, + worker: &mut GCWorker, + ) -> ObjectReference { + if KIND == TRACE_KIND_FAST { + self.immix_space.fast_trace_object(trace, object) + } else { + self.immix_space.trace_object(trace, object, CopySemantics::DefaultCopy, worker) + } + } + fn overwrite_reference() -> bool { + KIND == TRACE_KIND_DEFRAG + } + /// How to trace objects if the object is not in the immix space. + fn fallback_trace( + &self, + trace: &mut T, + object: ObjectReference, + worker: &mut GCWorker, + ) -> ObjectReference { + self.common.trace_object::(trace, object) + } + + fn create_scan_work>(&'static self, nodes: Vec) -> Box> { + Box::new(crate::policy::immix::ScanObjectsAndMarkLines::::new( + nodes, + false, + &self.immix_space, + )) + } +} pub(super) struct ImmixGCWorkContext( std::marker::PhantomData, @@ -10,5 +64,5 @@ impl crate::scheduler::GCWorkContext { type VM = VM; type PlanType = Immix; - type ProcessEdgesWorkType = ImmixProcessEdgesWork, KIND>; + type ProcessEdgesWorkType = PolicyProcessEdges, KIND>; } diff --git a/src/plan/immix/global.rs b/src/plan/immix/global.rs index c3263f6c89..de5aa79bfd 100644 --- a/src/plan/immix/global.rs +++ b/src/plan/immix/global.rs @@ -7,7 +7,7 @@ use crate::plan::AllocationSemantics; use crate::plan::Plan; use crate::plan::PlanConstraints; use crate::plan::TransitiveClosure; -use crate::policy::immix::gc_work::{TRACE_KIND_DEFRAG, TRACE_KIND_FAST}; +use crate::policy::immix::{TRACE_KIND_DEFRAG, TRACE_KIND_FAST}; use crate::policy::space::Space; use crate::scheduler::GCWorker; use crate::scheduler::*; @@ -130,26 +130,6 @@ impl Plan for Immix { } } -impl crate::policy::immix::gc_work::ImmixPlan for Immix { - #[inline(always)] - fn get_immix_space(&'static self) -> &'static ImmixSpace { - &self.immix_space - } - #[inline(always)] - fn get_immix_copy_semantics() -> CopySemantics { - CopySemantics::DefaultCopy - } - #[inline(always)] - fn fallback_trace( - &self, - trace: &mut T, - object: ObjectReference, - _worker: &mut GCWorker, - ) -> ObjectReference { - self.common.trace_object::(trace, object) - } -} - impl Immix { pub fn new( vm_map: &'static VMMap, diff --git a/src/plan/markcompact/gc_work.rs b/src/plan/markcompact/gc_work.rs index 848fdd2d8e..8415d4204b 100644 --- a/src/plan/markcompact/gc_work.rs +++ b/src/plan/markcompact/gc_work.rs @@ -10,6 +10,11 @@ use crate::vm::ActivePlan; use crate::vm::Scanning; use crate::vm::VMBinding; use crate::MMTK; +use crate::policy::gc_work::TraceKind; +use crate::policy::gc_work::PolicyProcessEdges; +use crate::policy::markcompactspace::{TRACE_KIND_FORWARD, TRACE_KIND_MARK}; +use crate::plan::TransitiveClosure; +use crate::util::copy::CopySemantics; use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; @@ -83,105 +88,146 @@ impl Compact { } } -// Transitive closure to mark live objects -pub struct MarkingProcessEdges { - plan: &'static MarkCompact, - base: ProcessEdgesBase, -} +impl crate::policy::gc_work::UsePolicyProcessEdges for MarkCompact { + type SpaceType = MarkCompactSpace; -impl MarkingProcessEdges { - fn markcompact(&self) -> &'static MarkCompact { - self.plan + /// Returns a reference to the immix space. + fn get_target_space(&'static self) -> &'static Self::SpaceType { + &self.mc_space } -} - -impl ProcessEdgesWork for MarkingProcessEdges { - type VM = VM; - fn new(edges: Vec

, roots: bool, mmtk: &'static MMTK) -> Self { - let base = ProcessEdgesBase::new(edges, roots, mmtk); - let plan = base.plan().downcast_ref::>().unwrap(); - Self { base, plan } + /// Returns the copy semantic for the immix space. + fn get_target_copy_semantics() -> CopySemantics { + CopySemantics::DefaultCopy } - - #[inline] - fn trace_object(&mut self, object: ObjectReference) -> ObjectReference { - if object.is_null() { - return object; - } - if self.markcompact().mc_space().in_space(object) { - self.markcompact() - .mc_space() - .trace_mark_object::(self, object) + /// How to trace object in target space + fn target_trace( + &self, + trace: &mut T, + object: ObjectReference, + worker: &mut GCWorker, + ) -> ObjectReference { + if KIND == TRACE_KIND_MARK { + self.mc_space.trace_mark_object::(trace, object) } else { - self.markcompact().common.trace_object::(self, object) + self.mc_space.trace_forward_object::(trace, object) } } -} - -impl Deref for MarkingProcessEdges { - type Target = ProcessEdgesBase; - #[inline] - fn deref(&self) -> &Self::Target { - &self.base - } -} - -impl DerefMut for MarkingProcessEdges { - #[inline] - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.base - } -} - -/// Transitive closure to update object references -pub struct ForwardingProcessEdges { - plan: &'static MarkCompact, - base: ProcessEdgesBase, -} - -impl ForwardingProcessEdges { - fn markcompact(&self) -> &'static MarkCompact { - self.plan - } -} - -impl ProcessEdgesWork for ForwardingProcessEdges { - type VM = VM; - fn new(edges: Vec
, roots: bool, mmtk: &'static MMTK) -> Self { - let base = ProcessEdgesBase::new(edges, roots, mmtk); - let plan = base.plan().downcast_ref::>().unwrap(); - Self { base, plan } - } - - #[inline] - fn trace_object(&mut self, object: ObjectReference) -> ObjectReference { - if object.is_null() { - return object; - } - if self.markcompact().mc_space().in_space(object) { - self.markcompact() - .mc_space() - .trace_forward_object::(self, object) - } else { - self.markcompact().common.trace_object::(self, object) - } - } -} - -impl Deref for ForwardingProcessEdges { - type Target = ProcessEdgesBase; - #[inline] - fn deref(&self) -> &Self::Target { - &self.base - } -} - -impl DerefMut for ForwardingProcessEdges { - #[inline] - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.base - } -} + fn overwrite_reference() -> bool { + true + } + /// How to trace objects if the object is not in the immix space. + fn fallback_trace( + &self, + trace: &mut T, + object: ObjectReference, + worker: &mut GCWorker, + ) -> ObjectReference { + self.common.trace_object::(trace, object) + } +} + +pub type MarkingProcessEdges = PolicyProcessEdges, TRACE_KIND_MARK>; +pub type ForwardingProcessEdges = PolicyProcessEdges, TRACE_KIND_FORWARD>; + +// // Transitive closure to mark live objects +// pub struct MarkingProcessEdges { +// plan: &'static MarkCompact, +// base: ProcessEdgesBase, +// } + +// impl MarkingProcessEdges { +// fn markcompact(&self) -> &'static MarkCompact { +// self.plan +// } +// } + +// impl ProcessEdgesWork for MarkingProcessEdges { +// type VM = VM; +// fn new(edges: Vec
, roots: bool, mmtk: &'static MMTK) -> Self { +// let base = ProcessEdgesBase::new(edges, roots, mmtk); +// let plan = base.plan().downcast_ref::>().unwrap(); +// Self { base, plan } +// } + +// #[inline] +// fn trace_object(&mut self, object: ObjectReference) -> ObjectReference { +// if object.is_null() { +// return object; +// } +// if self.markcompact().mc_space().in_space(object) { +// self.markcompact() +// .mc_space() +// .trace_mark_object::(self, object) +// } else { +// self.markcompact().common.trace_object::(self, object) +// } +// } +// } + +// impl Deref for MarkingProcessEdges { +// type Target = ProcessEdgesBase; +// #[inline] +// fn deref(&self) -> &Self::Target { +// &self.base +// } +// } + +// impl DerefMut for MarkingProcessEdges { +// #[inline] +// fn deref_mut(&mut self) -> &mut Self::Target { +// &mut self.base +// } +// } + +// /// Transitive closure to update object references +// pub struct ForwardingProcessEdges { +// plan: &'static MarkCompact, +// base: ProcessEdgesBase, +// } + +// impl ForwardingProcessEdges { +// fn markcompact(&self) -> &'static MarkCompact { +// self.plan +// } +// } + +// impl ProcessEdgesWork for ForwardingProcessEdges { +// type VM = VM; +// fn new(edges: Vec
, roots: bool, mmtk: &'static MMTK) -> Self { +// let base = ProcessEdgesBase::new(edges, roots, mmtk); +// let plan = base.plan().downcast_ref::>().unwrap(); +// Self { base, plan } +// } + +// #[inline] +// fn trace_object(&mut self, object: ObjectReference) -> ObjectReference { +// if object.is_null() { +// return object; +// } +// if self.markcompact().mc_space().in_space(object) { +// self.markcompact() +// .mc_space() +// .trace_forward_object::(self, object) +// } else { +// self.markcompact().common.trace_object::(self, object) +// } +// } +// } + +// impl Deref for ForwardingProcessEdges { +// type Target = ProcessEdgesBase; +// #[inline] +// fn deref(&self) -> &Self::Target { +// &self.base +// } +// } + +// impl DerefMut for ForwardingProcessEdges { +// #[inline] +// fn deref_mut(&mut self) -> &mut Self::Target { +// &mut self.base +// } +// } pub struct MarkCompactGCWorkContext(std::marker::PhantomData); impl crate::scheduler::GCWorkContext for MarkCompactGCWorkContext { diff --git a/src/policy/immix/gc_work.rs b/src/policy/gc_work.rs similarity index 51% rename from src/policy/immix/gc_work.rs rename to src/policy/gc_work.rs index bcf57a2dac..0d4b78a96b 100644 --- a/src/policy/immix/gc_work.rs +++ b/src/policy/gc_work.rs @@ -8,27 +8,30 @@ use crate::util::Address; use crate::util::ObjectReference; use crate::vm::VMBinding; use crate::MMTK; - -use super::ImmixSpace; +use crate::scheduler::GCWork; use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; -// It would be better if we use an enum for this. However, we use this as -// a constant type parameter, and Rust only accepts integer and bool for -// constant type parameters for now. We need to wait until `adt_const_params` is -// stablized. pub(crate) type TraceKind = u8; -pub(crate) const TRACE_KIND_FAST: TraceKind = 0; -pub(crate) const TRACE_KIND_DEFRAG: TraceKind = 1; /// An Immix plan can use `ImmixProcessEdgesWork`. This assumes there is only one immix space in the plan. /// For the sake of performance, the implementation of these methods should be marked as `inline(always)`. -pub trait ImmixPlan: Plan + Send { +pub trait UsePolicyProcessEdges: Plan + Send { + type SpaceType: Space; + /// Returns a reference to the immix space. - fn get_immix_space(&'static self) -> &'static ImmixSpace; + fn get_target_space(&'static self) -> &'static Self::SpaceType; /// Returns the copy semantic for the immix space. - fn get_immix_copy_semantics() -> CopySemantics; + fn get_target_copy_semantics() -> CopySemantics; + /// How to trace object in target space + fn target_trace( + &self, + trace: &mut T, + object: ObjectReference, + worker: &mut GCWorker, + ) -> ObjectReference; + fn overwrite_reference() -> bool; /// How to trace objects if the object is not in the immix space. fn fallback_trace( &self, @@ -36,21 +39,20 @@ pub trait ImmixPlan: Plan + Send { object: ObjectReference, worker: &mut GCWorker, ) -> ObjectReference; + + fn create_scan_work>(&'static self, nodes: Vec) -> Box> { + Box::new(crate::scheduler::gc_work::ScanObjects::::new(nodes, false)) + } } -/// Object tracing for Immix. -/// Note that it is possible to use [`SFTProcessEdges`](mmtk/scheduler/gc_work/SFTProcessEdges) for immix. -/// We need to: 1. add a plan-specific method to create scan work packets, as most plans use `ScanObjects` while -/// immix uses `ScanObjectsAndMarkLines`, 2. use `ImmixSpace.trace_object()` which has an overhead of checking -/// which trace method to use (with ImmixProcessEdges, we can know which trace method to use by statically checking `TraceKind`). -pub struct ImmixProcessEdgesWork, const KIND: TraceKind> { +pub struct PolicyProcessEdges, const KIND: TraceKind> { plan: &'static P, base: ProcessEdgesBase, p: PhantomData

, } -impl, const KIND: TraceKind> ProcessEdgesWork - for ImmixProcessEdgesWork +impl, const KIND: TraceKind> ProcessEdgesWork + for PolicyProcessEdges { type VM = VM; @@ -69,11 +71,7 @@ impl, const KIND: TraceKind> ProcessEdgesWork if self.nodes.is_empty() { return; } - let scan_objects_work = crate::policy::immix::ScanObjectsAndMarkLines::::new( - self.pop_nodes(), - false, - self.plan.get_immix_space(), - ); + let scan_objects_work = self.plan.create_scan_work::(self.pop_nodes()); self.new_scan_work(scan_objects_work); } @@ -83,17 +81,8 @@ impl, const KIND: TraceKind> ProcessEdgesWork if object.is_null() { return object; } - if self.plan.get_immix_space().in_space(object) { - if KIND == TRACE_KIND_FAST { - self.plan.get_immix_space().fast_trace_object(self, object) - } else { - self.plan.get_immix_space().trace_object( - self, - object, - P::get_immix_copy_semantics(), - self.worker(), - ) - } + if self.plan.get_target_space().in_space(object) { + self.plan.target_trace::(self, object, self.worker()) } else { self.plan .fallback_trace::(self, object, self.worker()) @@ -104,14 +93,14 @@ impl, const KIND: TraceKind> ProcessEdgesWork fn process_edge(&mut self, slot: Address) { let object = unsafe { slot.load::() }; let new_object = self.trace_object(object); - if KIND == TRACE_KIND_DEFRAG && Self::OVERWRITE_REFERENCE { + if P::overwrite_reference::() && Self::OVERWRITE_REFERENCE { unsafe { slot.store(new_object) }; } } } -impl, const KIND: TraceKind> Deref - for ImmixProcessEdgesWork +impl, const KIND: TraceKind> Deref + for PolicyProcessEdges { type Target = ProcessEdgesBase; #[inline] @@ -120,8 +109,8 @@ impl, const KIND: TraceKind> Deref } } -impl, const KIND: TraceKind> DerefMut - for ImmixProcessEdgesWork +impl, const KIND: TraceKind> DerefMut + for PolicyProcessEdges { #[inline] fn deref_mut(&mut self) -> &mut Self::Target { diff --git a/src/policy/immix/immixspace.rs b/src/policy/immix/immixspace.rs index a1becee2d5..acf79b806d 100644 --- a/src/policy/immix/immixspace.rs +++ b/src/policy/immix/immixspace.rs @@ -30,9 +30,13 @@ use crate::{ }, MMTK, }; +use crate::policy::gc_work::TraceKind; use atomic::Ordering; use std::sync::{atomic::AtomicU8, Arc}; +pub(crate) const TRACE_KIND_FAST: TraceKind = 0; +pub(crate) const TRACE_KIND_DEFRAG: TraceKind = 1; + pub struct ImmixSpace { common: CommonSpace, pr: FreeListPageResource, diff --git a/src/policy/immix/mod.rs b/src/policy/immix/mod.rs index 3eba3f6bfd..aaaf732cbb 100644 --- a/src/policy/immix/mod.rs +++ b/src/policy/immix/mod.rs @@ -1,7 +1,6 @@ pub mod block; pub mod chunk; pub mod defrag; -pub mod gc_work; pub mod immixspace; pub mod line; diff --git a/src/policy/markcompactspace.rs b/src/policy/markcompactspace.rs index 82bdf269a8..65bdf51eda 100644 --- a/src/policy/markcompactspace.rs +++ b/src/policy/markcompactspace.rs @@ -9,8 +9,12 @@ use crate::util::metadata::side_metadata::{SideMetadataContext, SideMetadataSpec use crate::util::metadata::{compare_exchange_metadata, extract_side_metadata}; use crate::util::{alloc_bit, Address, ObjectReference}; use crate::{vm::*, TransitiveClosure}; +use crate::policy::gc_work::TraceKind; use atomic::Ordering; +pub(crate) const TRACE_KIND_MARK: TraceKind = 0; +pub(crate) const TRACE_KIND_FORWARD: TraceKind = 1; + pub struct MarkCompactSpace { common: CommonSpace, pr: MonotonePageResource, diff --git a/src/policy/mod.rs b/src/policy/mod.rs index 1b6d5f6f5b..3675a294ac 100644 --- a/src/policy/mod.rs +++ b/src/policy/mod.rs @@ -15,6 +15,8 @@ pub mod space; /// Copy context defines the thread local copy allocator for copying policies. pub mod copy_context; +/// Policy specific GC work +pub mod gc_work; pub mod copyspace; pub mod immix; diff --git a/src/scheduler/gc_work.rs b/src/scheduler/gc_work.rs index 2aa53ca731..6aa78aeae0 100644 --- a/src/scheduler/gc_work.rs +++ b/src/scheduler/gc_work.rs @@ -418,16 +418,16 @@ pub trait ProcessEdgesWork: /// Create a new scan work packet. If SCAN_OBJECTS_IMMEDIATELY, the work packet will be executed immediately, in this method. /// Otherwise, the work packet will be added the Closure work bucket and will be dispatched later by the scheduler. #[inline] - fn new_scan_work(&mut self, work_packet: impl GCWork) { + fn new_scan_work(&mut self, work_packet: Box>) { if Self::SCAN_OBJECTS_IMMEDIATELY { // We execute this `scan_objects_work` immediately. // This is expected to be a useful optimization because, // say for _pmd_ with 200M heap, we're likely to have 50000~60000 `ScanObjects` work packets // being dispatched (similar amount to `ProcessEdgesWork`). // Executing these work packets now can remarkably reduce the global synchronization time. - self.worker().do_work(work_packet); + self.worker().do_boxed_work(work_packet); } else { - self.mmtk.scheduler.work_buckets[WorkBucketStage::Closure].add(work_packet); + self.mmtk.scheduler.work_buckets[WorkBucketStage::Closure].add_boxed(work_packet); } } @@ -439,7 +439,7 @@ pub trait ProcessEdgesWork: return; } let scan_objects_work = ScanObjects::::new(self.pop_nodes(), false); - self.new_scan_work(scan_objects_work); + self.new_scan_work(Box::new(scan_objects_work)); } #[inline] diff --git a/src/scheduler/work_bucket.rs b/src/scheduler/work_bucket.rs index db52cd1fed..a4db0f672f 100644 --- a/src/scheduler/work_bucket.rs +++ b/src/scheduler/work_bucket.rs @@ -113,6 +113,9 @@ impl WorkBucket { pub fn add>(&self, work: W) { self.add_with_priority(Self::DEFAULT_PRIORITY, Box::new(work)); } + pub fn add_boxed(&self, work: Box>) { + self.add_with_priority(Self::DEFAULT_PRIORITY, work); + } pub fn bulk_add_with_priority(&self, priority: usize, work_vec: Vec>>) { { let mut queue = self.queue.write(); diff --git a/src/scheduler/worker.rs b/src/scheduler/worker.rs index faca9fa49c..c067d0da41 100644 --- a/src/scheduler/worker.rs +++ b/src/scheduler/worker.rs @@ -140,6 +140,10 @@ impl GCWorker { work.do_work(self, self.mmtk); } + pub fn do_boxed_work(&'static mut self, mut work: Box>) { + work.do_work(self, self.mmtk); + } + pub fn run(&mut self, tls: VMWorkerThread, mmtk: &'static MMTK) { self.tls = tls; self.copy = crate::plan::create_gc_worker_context(tls, mmtk); From 4709b91ef0373d771840bb4accade972467ec147 Mon Sep 17 00:00:00 2001 From: Yi Lin Date: Fri, 8 Apr 2022 14:32:29 +1000 Subject: [PATCH 03/22] Cleanup --- src/plan/generational/immix/gc_work.rs | 39 ++++--- src/plan/generational/immix/global.rs | 5 +- src/plan/immix/gc_work.rs | 39 ++++--- src/plan/immix/global.rs | 3 - src/plan/markcompact/gc_work.rs | 147 ++++--------------------- src/policy/gc_work.rs | 63 +++++++---- src/policy/immix/immixspace.rs | 2 +- src/policy/markcompactspace.rs | 2 +- 8 files changed, 109 insertions(+), 191 deletions(-) diff --git a/src/plan/generational/immix/gc_work.rs b/src/plan/generational/immix/gc_work.rs index b5131dc117..a28a933cc6 100644 --- a/src/plan/generational/immix/gc_work.rs +++ b/src/plan/generational/immix/gc_work.rs @@ -1,28 +1,25 @@ use super::global::GenImmix; +use crate::plan::generational::gc_work::GenNurseryProcessEdges; +use crate::plan::TransitiveClosure; use crate::policy::gc_work::{PolicyProcessEdges, TraceKind}; -use crate::vm::VMBinding; -use crate::scheduler::GCWork; use crate::policy::immix::ImmixSpace; -use crate::util::copy::CopySemantics; -use crate::plan::TransitiveClosure; -use crate::policy::immix::{TRACE_KIND_FAST, TRACE_KIND_DEFRAG}; -use crate::util::ObjectReference; +use crate::policy::immix::{TRACE_KIND_DEFRAG, TRACE_KIND_FAST}; +use crate::scheduler::GCWork; use crate::scheduler::GCWorker; use crate::scheduler::ProcessEdgesWork; -use crate::plan::generational::gc_work::GenNurseryProcessEdges; +use crate::util::copy::CopySemantics; +use crate::util::ObjectReference; +use crate::vm::VMBinding; impl crate::policy::gc_work::UsePolicyProcessEdges for GenImmix { - type SpaceType = ImmixSpace; + type DefaultSpaceType = ImmixSpace; #[inline(always)] - fn get_target_space(&'static self) -> &'static Self::SpaceType { + fn get_target_space(&self) -> &Self::DefaultSpaceType { &self.immix } + #[inline(always)] - fn get_target_copy_semantics() -> CopySemantics { - CopySemantics::DefaultCopy - } - /// How to trace object in target space fn target_trace( &self, trace: &mut T, @@ -32,13 +29,17 @@ impl crate::policy::gc_work::UsePolicyProcessEdges for GenImm if KIND == TRACE_KIND_FAST { self.immix.fast_trace_object(trace, object) } else { - self.immix.trace_object(trace, object, CopySemantics::DefaultCopy, worker) + self.immix + .trace_object(trace, object, CopySemantics::Mature, worker) } } - fn overwrite_reference() -> bool { + + #[inline(always)] + fn may_move_objects() -> bool { KIND == TRACE_KIND_DEFRAG } - /// How to trace objects if the object is not in the immix space. + + #[inline(always)] fn fallback_trace( &self, trace: &mut T, @@ -48,7 +49,11 @@ impl crate::policy::gc_work::UsePolicyProcessEdges for GenImm self.gen.trace_object_full_heap::(trace, object, worker) } - fn create_scan_work>(&'static self, nodes: Vec) -> Box> { + #[inline(always)] + fn create_scan_work>( + &'static self, + nodes: Vec, + ) -> Box> { Box::new(crate::policy::immix::ScanObjectsAndMarkLines::::new( nodes, false, diff --git a/src/plan/generational/immix/global.rs b/src/plan/generational/immix/global.rs index ec44f03bb3..58a0932299 100644 --- a/src/plan/generational/immix/global.rs +++ b/src/plan/generational/immix/global.rs @@ -7,12 +7,10 @@ use crate::plan::global::GcStatus; use crate::plan::AllocationSemantics; use crate::plan::Plan; use crate::plan::PlanConstraints; -use crate::plan::TransitiveClosure; -use crate::policy::immix::{TRACE_KIND_DEFRAG, TRACE_KIND_FAST}; use crate::policy::immix::ImmixSpace; +use crate::policy::immix::{TRACE_KIND_DEFRAG, TRACE_KIND_FAST}; use crate::policy::space::Space; use crate::scheduler::GCWorkScheduler; -use crate::scheduler::GCWorker; use crate::util::alloc::allocators::AllocatorSelector; use crate::util::copy::*; use crate::util::heap::layout::heap_layout::Mmapper; @@ -20,7 +18,6 @@ use crate::util::heap::layout::heap_layout::VMMap; use crate::util::heap::layout::vm_layout_constants::{HEAP_END, HEAP_START}; use crate::util::heap::HeapMeta; use crate::util::options::UnsafeOptionsWrapper; -use crate::util::ObjectReference; use crate::util::VMWorkerThread; use crate::vm::*; diff --git a/src/plan/immix/gc_work.rs b/src/plan/immix/gc_work.rs index daa39a31b3..db51f6f128 100644 --- a/src/plan/immix/gc_work.rs +++ b/src/plan/immix/gc_work.rs @@ -1,27 +1,24 @@ use super::global::Immix; +use crate::plan::TransitiveClosure; use crate::policy::gc_work::{PolicyProcessEdges, TraceKind}; -use crate::vm::VMBinding; -use crate::scheduler::GCWork; use crate::policy::immix::ImmixSpace; -use crate::util::copy::CopySemantics; -use crate::plan::TransitiveClosure; -use crate::policy::immix::{TRACE_KIND_FAST, TRACE_KIND_DEFRAG}; -use crate::util::ObjectReference; +use crate::policy::immix::{TRACE_KIND_DEFRAG, TRACE_KIND_FAST}; +use crate::scheduler::GCWork; use crate::scheduler::GCWorker; use crate::scheduler::ProcessEdgesWork; +use crate::util::copy::CopySemantics; +use crate::util::ObjectReference; +use crate::vm::VMBinding; impl crate::policy::gc_work::UsePolicyProcessEdges for Immix { - type SpaceType = ImmixSpace; + type DefaultSpaceType = ImmixSpace; #[inline(always)] - fn get_target_space(&'static self) -> &'static Self::SpaceType { + fn get_target_space(&self) -> &Self::DefaultSpaceType { &self.immix_space } + #[inline(always)] - fn get_target_copy_semantics() -> CopySemantics { - CopySemantics::DefaultCopy - } - /// How to trace object in target space fn target_trace( &self, trace: &mut T, @@ -31,23 +28,31 @@ impl crate::policy::gc_work::UsePolicyProcessEdges for Immix< if KIND == TRACE_KIND_FAST { self.immix_space.fast_trace_object(trace, object) } else { - self.immix_space.trace_object(trace, object, CopySemantics::DefaultCopy, worker) + self.immix_space + .trace_object(trace, object, CopySemantics::DefaultCopy, worker) } } - fn overwrite_reference() -> bool { + + #[inline(always)] + fn may_move_objects() -> bool { KIND == TRACE_KIND_DEFRAG } - /// How to trace objects if the object is not in the immix space. + + #[inline(always)] fn fallback_trace( &self, trace: &mut T, object: ObjectReference, - worker: &mut GCWorker, + _worker: &mut GCWorker, ) -> ObjectReference { self.common.trace_object::(trace, object) } - fn create_scan_work>(&'static self, nodes: Vec) -> Box> { + #[inline(always)] + fn create_scan_work>( + &'static self, + nodes: Vec, + ) -> Box> { Box::new(crate::policy::immix::ScanObjectsAndMarkLines::::new( nodes, false, diff --git a/src/plan/immix/global.rs b/src/plan/immix/global.rs index de5aa79bfd..bfd7555233 100644 --- a/src/plan/immix/global.rs +++ b/src/plan/immix/global.rs @@ -6,10 +6,8 @@ use crate::plan::global::GcStatus; use crate::plan::AllocationSemantics; use crate::plan::Plan; use crate::plan::PlanConstraints; -use crate::plan::TransitiveClosure; use crate::policy::immix::{TRACE_KIND_DEFRAG, TRACE_KIND_FAST}; use crate::policy::space::Space; -use crate::scheduler::GCWorker; use crate::scheduler::*; use crate::util::alloc::allocators::AllocatorSelector; use crate::util::copy::*; @@ -20,7 +18,6 @@ use crate::util::heap::HeapMeta; use crate::util::metadata::side_metadata::SideMetadataContext; use crate::util::metadata::side_metadata::SideMetadataSanity; use crate::util::options::UnsafeOptionsWrapper; -use crate::util::ObjectReference; use crate::vm::VMBinding; use crate::{policy::immix::ImmixSpace, util::opaque_pointer::VMWorkerThread}; use std::sync::atomic::AtomicBool; diff --git a/src/plan/markcompact/gc_work.rs b/src/plan/markcompact/gc_work.rs index 8415d4204b..35ec987a51 100644 --- a/src/plan/markcompact/gc_work.rs +++ b/src/plan/markcompact/gc_work.rs @@ -1,22 +1,19 @@ use super::global::MarkCompact; +use crate::plan::TransitiveClosure; +use crate::policy::gc_work::PolicyProcessEdges; +use crate::policy::gc_work::TraceKind; use crate::policy::markcompactspace::MarkCompactSpace; -use crate::policy::space::Space; +use crate::policy::markcompactspace::{TRACE_KIND_FORWARD, TRACE_KIND_MARK}; use crate::scheduler::gc_work::*; use crate::scheduler::GCWork; use crate::scheduler::GCWorker; use crate::scheduler::WorkBucketStage; -use crate::util::{Address, ObjectReference}; +use crate::util::ObjectReference; use crate::vm::ActivePlan; use crate::vm::Scanning; use crate::vm::VMBinding; use crate::MMTK; -use crate::policy::gc_work::TraceKind; -use crate::policy::gc_work::PolicyProcessEdges; -use crate::policy::markcompactspace::{TRACE_KIND_FORWARD, TRACE_KIND_MARK}; -use crate::plan::TransitiveClosure; -use crate::util::copy::CopySemantics; use std::marker::PhantomData; -use std::ops::{Deref, DerefMut}; /// iterate through the heap and calculate the new location of live objects pub struct CalculateForwardingAddress { @@ -88,23 +85,25 @@ impl Compact { } } +/// Marking trace +pub type MarkingProcessEdges = PolicyProcessEdges, TRACE_KIND_MARK>; +/// Forwarding trace +pub type ForwardingProcessEdges = PolicyProcessEdges, TRACE_KIND_FORWARD>; + impl crate::policy::gc_work::UsePolicyProcessEdges for MarkCompact { - type SpaceType = MarkCompactSpace; + type DefaultSpaceType = MarkCompactSpace; - /// Returns a reference to the immix space. - fn get_target_space(&'static self) -> &'static Self::SpaceType { + #[inline(always)] + fn get_target_space(&self) -> &Self::DefaultSpaceType { &self.mc_space } - /// Returns the copy semantic for the immix space. - fn get_target_copy_semantics() -> CopySemantics { - CopySemantics::DefaultCopy - } - /// How to trace object in target space + + #[inline(always)] fn target_trace( &self, trace: &mut T, object: ObjectReference, - worker: &mut GCWorker, + _worker: &mut GCWorker, ) -> ObjectReference { if KIND == TRACE_KIND_MARK { self.mc_space.trace_mark_object::(trace, object) @@ -112,123 +111,23 @@ impl crate::policy::gc_work::UsePolicyProcessEdges for MarkCo self.mc_space.trace_forward_object::(trace, object) } } - fn overwrite_reference() -> bool { - true + + #[inline(always)] + fn may_move_objects() -> bool { + KIND == TRACE_KIND_FORWARD } - /// How to trace objects if the object is not in the immix space. + + #[inline(always)] fn fallback_trace( &self, trace: &mut T, object: ObjectReference, - worker: &mut GCWorker, + _worker: &mut GCWorker, ) -> ObjectReference { self.common.trace_object::(trace, object) } } -pub type MarkingProcessEdges = PolicyProcessEdges, TRACE_KIND_MARK>; -pub type ForwardingProcessEdges = PolicyProcessEdges, TRACE_KIND_FORWARD>; - -// // Transitive closure to mark live objects -// pub struct MarkingProcessEdges { -// plan: &'static MarkCompact, -// base: ProcessEdgesBase, -// } - -// impl MarkingProcessEdges { -// fn markcompact(&self) -> &'static MarkCompact { -// self.plan -// } -// } - -// impl ProcessEdgesWork for MarkingProcessEdges { -// type VM = VM; -// fn new(edges: Vec

, roots: bool, mmtk: &'static MMTK) -> Self { -// let base = ProcessEdgesBase::new(edges, roots, mmtk); -// let plan = base.plan().downcast_ref::>().unwrap(); -// Self { base, plan } -// } - -// #[inline] -// fn trace_object(&mut self, object: ObjectReference) -> ObjectReference { -// if object.is_null() { -// return object; -// } -// if self.markcompact().mc_space().in_space(object) { -// self.markcompact() -// .mc_space() -// .trace_mark_object::(self, object) -// } else { -// self.markcompact().common.trace_object::(self, object) -// } -// } -// } - -// impl Deref for MarkingProcessEdges { -// type Target = ProcessEdgesBase; -// #[inline] -// fn deref(&self) -> &Self::Target { -// &self.base -// } -// } - -// impl DerefMut for MarkingProcessEdges { -// #[inline] -// fn deref_mut(&mut self) -> &mut Self::Target { -// &mut self.base -// } -// } - -// /// Transitive closure to update object references -// pub struct ForwardingProcessEdges { -// plan: &'static MarkCompact, -// base: ProcessEdgesBase, -// } - -// impl ForwardingProcessEdges { -// fn markcompact(&self) -> &'static MarkCompact { -// self.plan -// } -// } - -// impl ProcessEdgesWork for ForwardingProcessEdges { -// type VM = VM; -// fn new(edges: Vec
, roots: bool, mmtk: &'static MMTK) -> Self { -// let base = ProcessEdgesBase::new(edges, roots, mmtk); -// let plan = base.plan().downcast_ref::>().unwrap(); -// Self { base, plan } -// } - -// #[inline] -// fn trace_object(&mut self, object: ObjectReference) -> ObjectReference { -// if object.is_null() { -// return object; -// } -// if self.markcompact().mc_space().in_space(object) { -// self.markcompact() -// .mc_space() -// .trace_forward_object::(self, object) -// } else { -// self.markcompact().common.trace_object::(self, object) -// } -// } -// } - -// impl Deref for ForwardingProcessEdges { -// type Target = ProcessEdgesBase; -// #[inline] -// fn deref(&self) -> &Self::Target { -// &self.base -// } -// } - -// impl DerefMut for ForwardingProcessEdges { -// #[inline] -// fn deref_mut(&mut self) -> &mut Self::Target { -// &mut self.base -// } -// } - pub struct MarkCompactGCWorkContext(std::marker::PhantomData); impl crate::scheduler::GCWorkContext for MarkCompactGCWorkContext { type VM = VM; diff --git a/src/policy/gc_work.rs b/src/policy/gc_work.rs index 0d4b78a96b..831d9995af 100644 --- a/src/policy/gc_work.rs +++ b/src/policy/gc_work.rs @@ -2,37 +2,41 @@ use crate::plan::Plan; use crate::plan::TransitiveClosure; use crate::policy::space::Space; use crate::scheduler::gc_work::*; +use crate::scheduler::GCWork; use crate::scheduler::GCWorker; -use crate::util::copy::CopySemantics; use crate::util::Address; use crate::util::ObjectReference; use crate::vm::VMBinding; use crate::MMTK; -use crate::scheduler::GCWork; -use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; +/// Used to identify the trace if a policy has different kinds of traces. For example, defrag vs fast trace for Immix, +/// mark vs forward trace for mark compact. pub(crate) type TraceKind = u8; -/// An Immix plan can use `ImmixProcessEdgesWork`. This assumes there is only one immix space in the plan. -/// For the sake of performance, the implementation of these methods should be marked as `inline(always)`. +/// A plan that uses `PolicyProcessEdges` needs to provide an implementation for this trait. +/// The plan needs to specify a target space. For objects in the target space, `target_trace()` is used to trace the object. +/// Otherwise, `fallback_trace()` is used. +/// For the sake of performance, the implementation of this trait should mark methods as `[inline(always)]`. pub trait UsePolicyProcessEdges: Plan + Send { - type SpaceType: Space; + type DefaultSpaceType: Space; + + /// Returns a reference to the default space. + fn get_target_space(&self) -> &Self::DefaultSpaceType; - /// Returns a reference to the immix space. - fn get_target_space(&'static self) -> &'static Self::SpaceType; - /// Returns the copy semantic for the immix space. - fn get_target_copy_semantics() -> CopySemantics; - /// How to trace object in target space + /// How to trace object in the default space fn target_trace( &self, trace: &mut T, object: ObjectReference, worker: &mut GCWorker, ) -> ObjectReference; - fn overwrite_reference() -> bool; - /// How to trace objects if the object is not in the immix space. + + /// Does this trace move object? + fn may_move_objects() -> bool; + + /// How to trace objects if the object is not in the default space. fn fallback_trace( &self, trace: &mut T, @@ -40,15 +44,27 @@ pub trait UsePolicyProcessEdges: Plan + Send { worker: &mut GCWorker, ) -> ObjectReference; - fn create_scan_work>(&'static self, nodes: Vec) -> Box> { - Box::new(crate::scheduler::gc_work::ScanObjects::::new(nodes, false)) + /// Create a scan work from the nodes. By default, the `ScanObjects` work packet is used. If a policy + /// uses their own scan work packet, they should override this method. + #[inline(always)] + fn create_scan_work>( + &'static self, + nodes: Vec, + ) -> Box> { + Box::new(crate::scheduler::gc_work::ScanObjects::::new( + nodes, false, + )) } } +/// This provides an alternative to [`SFTProcessEdges`](crate::scheduler::gc_work::SFTProcessEdges). For policies that cannot +/// use `SFTProcessEdges`, they could try use this type. One major difference is that `PolicyProcessEdges` allows different +/// traces for a policy. +/// A plan that uses this needs to implement the `UsePolicyProcessEdges` trait, and should choose the policy that has multiple +/// traces as the 'target'. See more details for [`UsePolicyProcessEdges`](crate::policy::gc_work::UsePolicyProcessEdges). pub struct PolicyProcessEdges, const KIND: TraceKind> { plan: &'static P, base: ProcessEdgesBase, - p: PhantomData

, } impl, const KIND: TraceKind> ProcessEdgesWork @@ -59,11 +75,7 @@ impl, const KIND: TraceKind> Process fn new(edges: Vec

, roots: bool, mmtk: &'static MMTK) -> Self { let base = ProcessEdgesBase::new(edges, roots, mmtk); let plan = base.plan().downcast_ref::

().unwrap(); - Self { - plan, - base, - p: PhantomData, - } + Self { plan, base } } #[cold] @@ -75,14 +87,15 @@ impl, const KIND: TraceKind> Process self.new_scan_work(scan_objects_work); } - /// Trace and evacuate objects. + /// Trace object if it is in the target space. Otherwise call fallback_trace(). #[inline(always)] fn trace_object(&mut self, object: ObjectReference) -> ObjectReference { if object.is_null() { return object; } if self.plan.get_target_space().in_space(object) { - self.plan.target_trace::(self, object, self.worker()) + self.plan + .target_trace::(self, object, self.worker()) } else { self.plan .fallback_trace::(self, object, self.worker()) @@ -93,12 +106,14 @@ impl, const KIND: TraceKind> Process fn process_edge(&mut self, slot: Address) { let object = unsafe { slot.load::() }; let new_object = self.trace_object(object); - if P::overwrite_reference::() && Self::OVERWRITE_REFERENCE { + if P::may_move_objects::() { unsafe { slot.store(new_object) }; } } } +// Impl Deref/DerefMut to ProcessEdgesBase for PolicyProcessEdges + impl, const KIND: TraceKind> Deref for PolicyProcessEdges { diff --git a/src/policy/immix/immixspace.rs b/src/policy/immix/immixspace.rs index acf79b806d..1bc48b81da 100644 --- a/src/policy/immix/immixspace.rs +++ b/src/policy/immix/immixspace.rs @@ -5,6 +5,7 @@ use super::{ defrag::Defrag, }; use crate::plan::ObjectsClosure; +use crate::policy::gc_work::TraceKind; use crate::policy::space::SpaceOptions; use crate::policy::space::*; use crate::policy::space::{CommonSpace, Space, SFT}; @@ -30,7 +31,6 @@ use crate::{ }, MMTK, }; -use crate::policy::gc_work::TraceKind; use atomic::Ordering; use std::sync::{atomic::AtomicU8, Arc}; diff --git a/src/policy/markcompactspace.rs b/src/policy/markcompactspace.rs index 65bdf51eda..d37087766f 100644 --- a/src/policy/markcompactspace.rs +++ b/src/policy/markcompactspace.rs @@ -1,4 +1,5 @@ use super::space::{CommonSpace, Space, SpaceOptions, SFT}; +use crate::policy::gc_work::TraceKind; use crate::policy::space::*; use crate::util::alloc::allocator::align_allocation_no_fill; use crate::util::constants::LOG_BYTES_IN_WORD; @@ -9,7 +10,6 @@ use crate::util::metadata::side_metadata::{SideMetadataContext, SideMetadataSpec use crate::util::metadata::{compare_exchange_metadata, extract_side_metadata}; use crate::util::{alloc_bit, Address, ObjectReference}; use crate::{vm::*, TransitiveClosure}; -use crate::policy::gc_work::TraceKind; use atomic::Ordering; pub(crate) const TRACE_KIND_MARK: TraceKind = 0; From 7a99c36b1f88f99379bb1905e38842647ca4dd51 Mon Sep 17 00:00:00 2001 From: Yi Lin Date: Mon, 11 Apr 2022 16:04:44 +1000 Subject: [PATCH 04/22] Add SupportPolicyProcessEdges, and move some code to spaces. --- src/plan/generational/immix/gc_work.rs | 40 ++------------------- src/plan/immix/gc_work.rs | 40 ++------------------- src/plan/markcompact/gc_work.rs | 27 +++------------ src/policy/gc_work.rs | 48 ++++++++++++++++---------- src/policy/immix/immixspace.rs | 32 +++++++++++++++++ src/policy/markcompactspace.rs | 25 ++++++++++++++ 6 files changed, 98 insertions(+), 114 deletions(-) diff --git a/src/plan/generational/immix/gc_work.rs b/src/plan/generational/immix/gc_work.rs index a28a933cc6..b1dc5fa98b 100644 --- a/src/plan/generational/immix/gc_work.rs +++ b/src/plan/generational/immix/gc_work.rs @@ -3,42 +3,20 @@ use crate::plan::generational::gc_work::GenNurseryProcessEdges; use crate::plan::TransitiveClosure; use crate::policy::gc_work::{PolicyProcessEdges, TraceKind}; use crate::policy::immix::ImmixSpace; -use crate::policy::immix::{TRACE_KIND_DEFRAG, TRACE_KIND_FAST}; -use crate::scheduler::GCWork; use crate::scheduler::GCWorker; -use crate::scheduler::ProcessEdgesWork; use crate::util::copy::CopySemantics; use crate::util::ObjectReference; use crate::vm::VMBinding; impl crate::policy::gc_work::UsePolicyProcessEdges for GenImmix { - type DefaultSpaceType = ImmixSpace; + type TargetPolicy = ImmixSpace; + const COPY: CopySemantics = CopySemantics::Mature; #[inline(always)] - fn get_target_space(&self) -> &Self::DefaultSpaceType { + fn get_target_space(&self) -> &Self::TargetPolicy { &self.immix } - #[inline(always)] - fn target_trace( - &self, - trace: &mut T, - object: ObjectReference, - worker: &mut GCWorker, - ) -> ObjectReference { - if KIND == TRACE_KIND_FAST { - self.immix.fast_trace_object(trace, object) - } else { - self.immix - .trace_object(trace, object, CopySemantics::Mature, worker) - } - } - - #[inline(always)] - fn may_move_objects() -> bool { - KIND == TRACE_KIND_DEFRAG - } - #[inline(always)] fn fallback_trace( &self, @@ -48,18 +26,6 @@ impl crate::policy::gc_work::UsePolicyProcessEdges for GenImm ) -> ObjectReference { self.gen.trace_object_full_heap::(trace, object, worker) } - - #[inline(always)] - fn create_scan_work>( - &'static self, - nodes: Vec, - ) -> Box> { - Box::new(crate::policy::immix::ScanObjectsAndMarkLines::::new( - nodes, - false, - &self.immix, - )) - } } pub struct GenImmixNurseryGCWorkContext(std::marker::PhantomData); diff --git a/src/plan/immix/gc_work.rs b/src/plan/immix/gc_work.rs index db51f6f128..c51bfd5efc 100644 --- a/src/plan/immix/gc_work.rs +++ b/src/plan/immix/gc_work.rs @@ -2,42 +2,20 @@ use super::global::Immix; use crate::plan::TransitiveClosure; use crate::policy::gc_work::{PolicyProcessEdges, TraceKind}; use crate::policy::immix::ImmixSpace; -use crate::policy::immix::{TRACE_KIND_DEFRAG, TRACE_KIND_FAST}; -use crate::scheduler::GCWork; use crate::scheduler::GCWorker; -use crate::scheduler::ProcessEdgesWork; use crate::util::copy::CopySemantics; use crate::util::ObjectReference; use crate::vm::VMBinding; impl crate::policy::gc_work::UsePolicyProcessEdges for Immix { - type DefaultSpaceType = ImmixSpace; + type TargetPolicy = ImmixSpace; + const COPY: CopySemantics = CopySemantics::DefaultCopy; #[inline(always)] - fn get_target_space(&self) -> &Self::DefaultSpaceType { + fn get_target_space(&self) -> &Self::TargetPolicy { &self.immix_space } - #[inline(always)] - fn target_trace( - &self, - trace: &mut T, - object: ObjectReference, - worker: &mut GCWorker, - ) -> ObjectReference { - if KIND == TRACE_KIND_FAST { - self.immix_space.fast_trace_object(trace, object) - } else { - self.immix_space - .trace_object(trace, object, CopySemantics::DefaultCopy, worker) - } - } - - #[inline(always)] - fn may_move_objects() -> bool { - KIND == TRACE_KIND_DEFRAG - } - #[inline(always)] fn fallback_trace( &self, @@ -47,18 +25,6 @@ impl crate::policy::gc_work::UsePolicyProcessEdges for Immix< ) -> ObjectReference { self.common.trace_object::(trace, object) } - - #[inline(always)] - fn create_scan_work>( - &'static self, - nodes: Vec, - ) -> Box> { - Box::new(crate::policy::immix::ScanObjectsAndMarkLines::::new( - nodes, - false, - &self.immix_space, - )) - } } pub(super) struct ImmixGCWorkContext( diff --git a/src/plan/markcompact/gc_work.rs b/src/plan/markcompact/gc_work.rs index 35ec987a51..59ff1c997f 100644 --- a/src/plan/markcompact/gc_work.rs +++ b/src/plan/markcompact/gc_work.rs @@ -1,7 +1,6 @@ use super::global::MarkCompact; use crate::plan::TransitiveClosure; use crate::policy::gc_work::PolicyProcessEdges; -use crate::policy::gc_work::TraceKind; use crate::policy::markcompactspace::MarkCompactSpace; use crate::policy::markcompactspace::{TRACE_KIND_FORWARD, TRACE_KIND_MARK}; use crate::scheduler::gc_work::*; @@ -90,33 +89,17 @@ pub type MarkingProcessEdges = PolicyProcessEdges, TRACE /// Forwarding trace pub type ForwardingProcessEdges = PolicyProcessEdges, TRACE_KIND_FORWARD>; +use crate::util::copy::CopySemantics; + impl crate::policy::gc_work::UsePolicyProcessEdges for MarkCompact { - type DefaultSpaceType = MarkCompactSpace; + type TargetPolicy = MarkCompactSpace; + const COPY: CopySemantics = CopySemantics::DefaultCopy; #[inline(always)] - fn get_target_space(&self) -> &Self::DefaultSpaceType { + fn get_target_space(&self) -> &Self::TargetPolicy { &self.mc_space } - #[inline(always)] - fn target_trace( - &self, - trace: &mut T, - object: ObjectReference, - _worker: &mut GCWorker, - ) -> ObjectReference { - if KIND == TRACE_KIND_MARK { - self.mc_space.trace_mark_object::(trace, object) - } else { - self.mc_space.trace_forward_object::(trace, object) - } - } - - #[inline(always)] - fn may_move_objects() -> bool { - KIND == TRACE_KIND_FORWARD - } - #[inline(always)] fn fallback_trace( &self, diff --git a/src/policy/gc_work.rs b/src/policy/gc_work.rs index 831d9995af..4cb54a88e3 100644 --- a/src/policy/gc_work.rs +++ b/src/policy/gc_work.rs @@ -4,6 +4,7 @@ use crate::policy::space::Space; use crate::scheduler::gc_work::*; use crate::scheduler::GCWork; use crate::scheduler::GCWorker; +use crate::util::copy::CopySemantics; use crate::util::Address; use crate::util::ObjectReference; use crate::vm::VMBinding; @@ -16,36 +17,41 @@ use std::ops::{Deref, DerefMut}; pub(crate) type TraceKind = u8; /// A plan that uses `PolicyProcessEdges` needs to provide an implementation for this trait. -/// The plan needs to specify a target space. For objects in the target space, `target_trace()` is used to trace the object. +/// The plan needs to specify a target space (which needs to implement `SupportPolicyProcessEdges`). +/// For objects in the target space, `trace_object_with_tracekind()` is called to trace the object. /// Otherwise, `fallback_trace()` is used. /// For the sake of performance, the implementation of this trait should mark methods as `[inline(always)]`. pub trait UsePolicyProcessEdges: Plan + Send { - type DefaultSpaceType: Space; + type TargetPolicy: SupportPolicyProcessEdges; + /// The copy semantics for objects in the space. + const COPY: CopySemantics; - /// Returns a reference to the default space. - fn get_target_space(&self) -> &Self::DefaultSpaceType; + /// Returns a reference to the target space. + fn get_target_space(&self) -> &Self::TargetPolicy; - /// How to trace object in the default space - fn target_trace( + /// How to trace objects if the object is not in the default space. + fn fallback_trace( &self, trace: &mut T, object: ObjectReference, worker: &mut GCWorker, ) -> ObjectReference; +} - /// Does this trace move object? - fn may_move_objects() -> bool; - - /// How to trace objects if the object is not in the default space. - fn fallback_trace( +/// A policy that allows using `PolicyProcessEdges` needs to provide an implementation for this trait. +/// For the sake of performance, the implementation of this trait should mark methods as `[inline(always)]`. +pub trait SupportPolicyProcessEdges: Space { + /// Trace an object in the policy. + fn trace_object_with_tracekind( &self, trace: &mut T, object: ObjectReference, + copy: CopySemantics, worker: &mut GCWorker, ) -> ObjectReference; - /// Create a scan work from the nodes. By default, the `ScanObjects` work packet is used. If a policy - /// uses their own scan work packet, they should override this method. + /// Create scan work for the policy. By default, we use [`ScanObjects`](crate::scheduler::gc_work::ScanObjects). + /// If a policy has its own scan object work packet, they can override this method. #[inline(always)] fn create_scan_work>( &'static self, @@ -55,13 +61,15 @@ pub trait UsePolicyProcessEdges: Plan + Send { nodes, false, )) } + + /// Does this trace move object? + fn may_move_objects() -> bool; } /// This provides an alternative to [`SFTProcessEdges`](crate::scheduler::gc_work::SFTProcessEdges). For policies that cannot /// use `SFTProcessEdges`, they could try use this type. One major difference is that `PolicyProcessEdges` allows different /// traces for a policy. -/// A plan that uses this needs to implement the `UsePolicyProcessEdges` trait, and should choose the policy that has multiple -/// traces as the 'target'. See more details for [`UsePolicyProcessEdges`](crate::policy::gc_work::UsePolicyProcessEdges). +/// A plan that uses this needs to implement the `UsePolicyProcessEdges` trait, and the policy needs to implement `SupportPolicyProcessEdges`. pub struct PolicyProcessEdges, const KIND: TraceKind> { plan: &'static P, base: ProcessEdgesBase, @@ -83,7 +91,10 @@ impl, const KIND: TraceKind> Process if self.nodes.is_empty() { return; } - let scan_objects_work = self.plan.create_scan_work::(self.pop_nodes()); + let scan_objects_work = self + .plan + .get_target_space() + .create_scan_work::(self.pop_nodes()); self.new_scan_work(scan_objects_work); } @@ -95,7 +106,8 @@ impl, const KIND: TraceKind> Process } if self.plan.get_target_space().in_space(object) { self.plan - .target_trace::(self, object, self.worker()) + .get_target_space() + .trace_object_with_tracekind::(self, object, P::COPY, self.worker()) } else { self.plan .fallback_trace::(self, object, self.worker()) @@ -106,7 +118,7 @@ impl, const KIND: TraceKind> Process fn process_edge(&mut self, slot: Address) { let object = unsafe { slot.load::() }; let new_object = self.trace_object(object); - if P::may_move_objects::() { + if P::TargetPolicy::may_move_objects::() { unsafe { slot.store(new_object) }; } } diff --git a/src/policy/immix/immixspace.rs b/src/policy/immix/immixspace.rs index 1bc48b81da..4f77f5329b 100644 --- a/src/policy/immix/immixspace.rs +++ b/src/policy/immix/immixspace.rs @@ -528,6 +528,38 @@ impl ImmixSpace { } } +impl crate::policy::gc_work::SupportPolicyProcessEdges for ImmixSpace { + #[inline(always)] + fn trace_object_with_tracekind( + &self, + trace: &mut T, + object: ObjectReference, + copy: CopySemantics, + worker: &mut GCWorker, + ) -> ObjectReference { + if KIND == TRACE_KIND_FAST { + self.fast_trace_object(trace, object) + } else { + self.trace_object(trace, object, copy, worker) + } + } + + #[inline(always)] + fn create_scan_work>( + &'static self, + nodes: Vec, + ) -> Box> { + Box::new(crate::policy::immix::ScanObjectsAndMarkLines::::new( + nodes, false, self, + )) + } + + #[inline(always)] + fn may_move_objects() -> bool { + KIND == TRACE_KIND_DEFRAG + } +} + /// A work packet to prepare each block for GC. /// Performs the action on a range of chunks. pub struct PrepareBlockState { diff --git a/src/policy/markcompactspace.rs b/src/policy/markcompactspace.rs index d37087766f..f7510421ed 100644 --- a/src/policy/markcompactspace.rs +++ b/src/policy/markcompactspace.rs @@ -371,6 +371,31 @@ impl MarkCompactSpace { } } +use crate::scheduler::GCWorker; +use crate::util::copy::CopySemantics; + +impl crate::policy::gc_work::SupportPolicyProcessEdges for MarkCompactSpace { + #[inline(always)] + fn trace_object_with_tracekind( + &self, + trace: &mut T, + object: ObjectReference, + _copy: CopySemantics, + _worker: &mut GCWorker, + ) -> ObjectReference { + if KIND == TRACE_KIND_MARK { + self.trace_mark_object::(trace, object) + } else { + self.trace_forward_object::(trace, object) + } + } + + #[inline(always)] + fn may_move_objects() -> bool { + KIND == TRACE_KIND_FORWARD + } +} + struct MarkCompactObjectSize(std::marker::PhantomData); impl crate::util::linear_scan::LinearScanObjectSize for MarkCompactObjectSize { #[inline(always)] From 0d470e6caf87b2acaf50126a38c81232e47236d9 Mon Sep 17 00:00:00 2001 From: Yi Lin Date: Wed, 13 Apr 2022 10:28:40 +1000 Subject: [PATCH 05/22] Add create_scan_work to ProcessEdgesWork --- src/policy/gc_work.rs | 13 +++---------- src/scheduler/gc_work.rs | 17 +++++++++++++---- src/scheduler/work_bucket.rs | 1 + 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/policy/gc_work.rs b/src/policy/gc_work.rs index 4cb54a88e3..ef1fa91231 100644 --- a/src/policy/gc_work.rs +++ b/src/policy/gc_work.rs @@ -86,16 +86,9 @@ impl, const KIND: TraceKind> Process Self { plan, base } } - #[cold] - fn flush(&mut self) { - if self.nodes.is_empty() { - return; - } - let scan_objects_work = self - .plan - .get_target_space() - .create_scan_work::(self.pop_nodes()); - self.new_scan_work(scan_objects_work); + #[inline(always)] + fn create_scan_work(&self, nodes: Vec) -> Box> { + self.plan.get_target_space().create_scan_work::(nodes) } /// Trace object if it is in the target space. Otherwise call fallback_trace(). diff --git a/src/scheduler/gc_work.rs b/src/scheduler/gc_work.rs index 6aa78aeae0..91d15a41fb 100644 --- a/src/scheduler/gc_work.rs +++ b/src/scheduler/gc_work.rs @@ -415,10 +415,10 @@ pub trait ProcessEdgesWork: // So maximum 1 `ScanObjects` work can be created from `nodes` buffer } - /// Create a new scan work packet. If SCAN_OBJECTS_IMMEDIATELY, the work packet will be executed immediately, in this method. + /// Start the a scan work packet. If SCAN_OBJECTS_IMMEDIATELY, the work packet will be executed immediately, in this method. /// Otherwise, the work packet will be added the Closure work bucket and will be dispatched later by the scheduler. #[inline] - fn new_scan_work(&mut self, work_packet: Box>) { + fn start_scan_work(&mut self, work_packet: Box>) { if Self::SCAN_OBJECTS_IMMEDIATELY { // We execute this `scan_objects_work` immediately. // This is expected to be a useful optimization because, @@ -431,6 +431,15 @@ pub trait ProcessEdgesWork: } } + /// Create scan work for the policy. By default, we use [`ScanObjects`](crate::scheduler::gc_work::ScanObjects). + /// If a policy has its own scan object work packet, they can override this method. + #[inline(always)] + fn create_scan_work(&self, nodes: Vec) -> Box> { + Box::new(crate::scheduler::gc_work::ScanObjects::::new( + nodes, false, + )) + } + /// Flush the nodes in ProcessEdgesBase, and create a ScanObjects work packet for it. If the node set is empty, /// this method will simply return with no work packet created. #[cold] @@ -438,8 +447,8 @@ pub trait ProcessEdgesWork: if self.nodes.is_empty() { return; } - let scan_objects_work = ScanObjects::::new(self.pop_nodes(), false); - self.new_scan_work(Box::new(scan_objects_work)); + let nodes = self.pop_nodes(); + self.start_scan_work(self.create_scan_work(nodes)); } #[inline] diff --git a/src/scheduler/work_bucket.rs b/src/scheduler/work_bucket.rs index a4db0f672f..6b69597d8f 100644 --- a/src/scheduler/work_bucket.rs +++ b/src/scheduler/work_bucket.rs @@ -113,6 +113,7 @@ impl WorkBucket { pub fn add>(&self, work: W) { self.add_with_priority(Self::DEFAULT_PRIORITY, Box::new(work)); } + /// Add a boxed work packet to this bucket, with a default priority (1000) pub fn add_boxed(&self, work: Box>) { self.add_with_priority(Self::DEFAULT_PRIORITY, work); } From 6d323eeac8f4cc55b7380228a851822b3e35d471 Mon Sep 17 00:00:00 2001 From: Yi Lin Date: Wed, 20 Apr 2022 10:27:24 +1000 Subject: [PATCH 06/22] Add trait PolicyTraceObject and PlanTraceObject --- src/plan/mod.rs | 2 +- src/plan/transitive_closure.rs | 12 ++++++++++++ src/policy/copyspace.rs | 6 ++++++ src/policy/immix/immixspace.rs | 12 ++++++++++++ src/policy/immortalspace.rs | 9 +++++++++ src/policy/largeobjectspace.rs | 9 +++++++++ src/policy/lockfreeimmortalspace.rs | 10 ++++++++++ src/policy/mallocspace/global.rs | 9 +++++++++ src/policy/markcompactspace.rs | 12 ++++++++++++ 9 files changed, 80 insertions(+), 1 deletion(-) diff --git a/src/plan/mod.rs b/src/plan/mod.rs index 3abc62916e..6a58eceef4 100644 --- a/src/plan/mod.rs +++ b/src/plan/mod.rs @@ -37,7 +37,7 @@ pub use plan_constraints::DEFAULT_PLAN_CONSTRAINTS; mod tracelocal; pub use tracelocal::TraceLocal; -mod transitive_closure; +pub(crate) mod transitive_closure; pub use transitive_closure::{ObjectsClosure, TransitiveClosure}; mod generational; diff --git a/src/plan/transitive_closure.rs b/src/plan/transitive_closure.rs index 7a49d1260c..108929b7f9 100644 --- a/src/plan/transitive_closure.rs +++ b/src/plan/transitive_closure.rs @@ -80,3 +80,15 @@ impl<'a, E: ProcessEdgesWork> Drop for ObjectsClosure<'a, E> { ); } } + +use crate::vm::VMBinding; +use crate::util::copy::CopySemantics; +use crate::policy::gc_work::TraceKind; + +pub trait PlanTraceObject { + fn trace_object(&self, trace: &mut T, object: ObjectReference, worker: &mut GCWorker) -> ObjectReference; +} + +pub trait PolicyTraceObject { + fn trace_object(&self, trace: &mut T, object: ObjectReference, copy: Option, worker: &mut GCWorker) -> ObjectReference; +} diff --git a/src/policy/copyspace.rs b/src/policy/copyspace.rs index 7bdec29a17..8914bd36d2 100644 --- a/src/policy/copyspace.rs +++ b/src/policy/copyspace.rs @@ -108,6 +108,12 @@ impl Space for CopySpace { } } +impl crate::plan::transitive_closure::PolicyTraceObject for CopySpace { + fn trace_object(&self, trace: &mut T, object: ObjectReference, copy: Option, worker: &mut GCWorker) -> ObjectReference { + self.trace_object(trace, object, copy, worker) + } +} + impl CopySpace { #[allow(clippy::too_many_arguments)] pub fn new( diff --git a/src/policy/immix/immixspace.rs b/src/policy/immix/immixspace.rs index 4f77f5329b..40ca0a2ab3 100644 --- a/src/policy/immix/immixspace.rs +++ b/src/policy/immix/immixspace.rs @@ -117,6 +117,18 @@ impl Space for ImmixSpace { } } +impl crate::plan::transitive_closure::PolicyTraceObject for ImmixSpace { + fn trace_object(&self, trace: &mut T, object: ObjectReference, copy: Option, worker: &mut GCWorker) -> ObjectReference { + if KIND == TRACE_KIND_DEFRAG { + self.trace_object_with_opportunistic_copy(trace, object, copy.unwrap(), worker) + } else if KIND == TRACE_KIND_FAST { + self.trace_object_without_moving(trace, object) + } else { + unreachable!() + } + } +} + impl ImmixSpace { const UNMARKED_STATE: u8 = 0; const MARKED_STATE: u8 = 1; diff --git a/src/policy/immortalspace.rs b/src/policy/immortalspace.rs index fbc344ff70..2ae9e6e829 100644 --- a/src/policy/immortalspace.rs +++ b/src/policy/immortalspace.rs @@ -111,6 +111,15 @@ impl Space for ImmortalSpace { } } +use crate::util::copy::CopySemantics; +use crate::scheduler::GCWorker; + +impl crate::plan::transitive_closure::PolicyTraceObject for ImmortalSpace { + fn trace_object(&self, trace: &mut T, object: ObjectReference, _copy: Option, _worker: &mut GCWorker) -> ObjectReference { + self.trace_object(trace, object) + } +} + impl ImmortalSpace { #[allow(clippy::too_many_arguments)] pub fn new( diff --git a/src/policy/largeobjectspace.rs b/src/policy/largeobjectspace.rs index 0884efeb97..6fedae817c 100644 --- a/src/policy/largeobjectspace.rs +++ b/src/policy/largeobjectspace.rs @@ -115,6 +115,15 @@ impl Space for LargeObjectSpace { } } +use crate::util::copy::CopySemantics; +use crate::scheduler::GCWorker; + +impl crate::plan::transitive_closure::PolicyTraceObject for LargeObjectSpace { + fn trace_object(&self, trace: &mut T, object: ObjectReference, _copy: Option, _worker: &mut GCWorker) -> ObjectReference { + self.trace_object(trace, object) + } +} + impl LargeObjectSpace { #[allow(clippy::too_many_arguments)] pub fn new( diff --git a/src/policy/lockfreeimmortalspace.rs b/src/policy/lockfreeimmortalspace.rs index 0c6ce2d2b1..48d381887e 100644 --- a/src/policy/lockfreeimmortalspace.rs +++ b/src/policy/lockfreeimmortalspace.rs @@ -149,6 +149,16 @@ impl Space for LockFreeImmortalSpace { } } +use crate::util::copy::CopySemantics; +use crate::scheduler::GCWorker; +use crate::plan::TransitiveClosure; + +impl crate::plan::transitive_closure::PolicyTraceObject for LockFreeImmortalSpace { + fn trace_object(&self, _trace: &mut T, _object: ObjectReference, _copy: Option, _worker: &mut GCWorker) -> ObjectReference { + unreachable!() + } +} + impl LockFreeImmortalSpace { #[allow(dead_code)] // Only used with certain features. pub fn new( diff --git a/src/policy/mallocspace/global.rs b/src/policy/mallocspace/global.rs index 136a212ed8..927e43e726 100644 --- a/src/policy/mallocspace/global.rs +++ b/src/policy/mallocspace/global.rs @@ -189,6 +189,15 @@ impl Space for MallocSpace { } } +use crate::util::copy::CopySemantics; +use crate::scheduler::GCWorker; + +impl crate::plan::transitive_closure::PolicyTraceObject for MallocSpace { + fn trace_object(&self, trace: &mut T, object: ObjectReference, _copy: Option, _worker: &mut GCWorker) -> ObjectReference { + self.trace_object(trace, object) + } +} + impl MallocSpace { pub fn new(global_side_metadata_specs: Vec) -> Self { MallocSpace { diff --git a/src/policy/markcompactspace.rs b/src/policy/markcompactspace.rs index f7510421ed..1221c9fb8e 100644 --- a/src/policy/markcompactspace.rs +++ b/src/policy/markcompactspace.rs @@ -101,6 +101,18 @@ impl Space for MarkCompactSpace { } } +impl crate::plan::transitive_closure::PolicyTraceObject for MarkCompactSpace { + fn trace_object(&self, trace: &mut T, object: ObjectReference, _copy: Option, _worker: &mut GCWorker) -> ObjectReference { + if KIND == TRACE_KIND_MARK { + self.trace_mark_object(trace, object) + } else if KIND == TRACE_KIND_FORWARD { + self.trace_forward_object(trace, object) + } else { + unreachable!() + } + } +} + impl MarkCompactSpace { /// We need one extra header word for each object. Considering the alignment requirement, this is /// the actual bytes we need to reserve for each allocation. From 7f1b1dfe52973ffd97d465a24931355ef277e4b4 Mon Sep 17 00:00:00 2001 From: Yi Lin Date: Tue, 26 Apr 2022 10:19:27 +1000 Subject: [PATCH 07/22] Use macro to trace object for plans --- Cargo.toml | 2 + macros/trace_object/Cargo.toml | 13 ++ macros/trace_object/src/lib.rs | 163 +++++++++++++++++++++++ macros/trace_object/src/util.rs | 47 +++++++ src/plan/generational/copying/gc_work.rs | 13 +- src/plan/generational/copying/global.rs | 16 ++- src/plan/generational/global.rs | 7 + src/plan/generational/immix/gc_work.rs | 8 +- src/plan/generational/immix/global.rs | 6 + src/plan/global.rs | 11 ++ src/plan/immix/gc_work.rs | 8 +- src/plan/immix/global.rs | 6 + src/plan/markcompact/gc_work.rs | 6 +- src/plan/markcompact/global.rs | 8 ++ src/plan/marksweep/gc_work.rs | 6 +- src/plan/marksweep/global.rs | 6 + src/plan/pageprotect/gc_work.rs | 4 +- src/plan/pageprotect/global.rs | 5 + src/plan/semispace/gc_work.rs | 4 +- src/plan/semispace/global.rs | 7 + src/plan/transitive_closure.rs | 15 ++- src/policy/copyspace.rs | 6 +- src/policy/gc_work.rs | 99 +++++++++++--- src/policy/immix/immixspace.rs | 26 +++- src/policy/immortalspace.rs | 4 + src/policy/largeobjectspace.rs | 4 + src/policy/lockfreeimmortalspace.rs | 4 + src/policy/mallocspace/global.rs | 5 + src/policy/markcompactspace.rs | 10 ++ 29 files changed, 484 insertions(+), 35 deletions(-) create mode 100644 macros/trace_object/Cargo.toml create mode 100644 macros/trace_object/src/lib.rs create mode 100644 macros/trace_object/src/util.rs diff --git a/Cargo.toml b/Cargo.toml index 51cb2cc69a..f0349a4f92 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,8 @@ crate-type = ["rlib"] doctest = false [dependencies] +macro-trace-object = { path = "macros/trace_object"} + custom_derive = "0.1" enum_derive = "0.1" libc = "0.2" diff --git a/macros/trace_object/Cargo.toml b/macros/trace_object/Cargo.toml new file mode 100644 index 0000000000..b62159f79c --- /dev/null +++ b/macros/trace_object/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "macro-trace-object" +version = "0.1.0" +edition = "2021" + +[lib] +proc-macro = true + +[dependencies] +proc-macro2 = "1.0.37" +syn = { version = "1.0.91", features = ["extra-traits"] } +quote = "1.0.18" +proc-macro-error = "1.0.4" diff --git a/macros/trace_object/src/lib.rs b/macros/trace_object/src/lib.rs new file mode 100644 index 0000000000..6c7f0aaacd --- /dev/null +++ b/macros/trace_object/src/lib.rs @@ -0,0 +1,163 @@ +extern crate proc_macro; +extern crate syn; +extern crate proc_macro_error; +extern crate quote; + +use proc_macro::TokenStream; +use proc_macro_error::proc_macro_error; +use syn::{parse_macro_input}; +use proc_macro_error::{abort, abort_call_site}; +use quote::quote; +use syn::{spanned::Spanned, Attribute, DeriveInput, Field, FieldsNamed, TypeGenerics}; +use quote::ToTokens; +use syn::__private::TokenStream2; +use syn::token::Token; + +mod util; + +#[proc_macro_error] +#[proc_macro_derive(PlanTraceObject, attributes(trace, main_policy, copy, fallback_trace))] +pub fn derive_plan_trace_object(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + let ident = input.ident; + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + + let output = if let syn::Data::Struct(syn::DataStruct { + fields: syn::Fields::Named(ref fields), + .. + }) = input.data { + let spaces = util::get_fields_with_attribute(fields, "trace"); + let main_policy = util::get_unique_field_with_attribute(fields, "main_policy"); + let fallback = util::get_unique_field_with_attribute(fields, "fallback_trace"); + + let trace_object_function = generate_trace_object(&spaces, &fallback, &ty_generics); + let create_scan_work_function = generate_create_scan_work(&main_policy, &ty_generics); + let may_move_objects_function = generate_may_move_objects(&main_policy, &ty_generics); + quote!{ + impl #impl_generics crate::plan::transitive_closure::PlanTraceObject #ty_generics for #ident #ty_generics #where_clause { + #[inline(always)] + #trace_object_function + + #[inline(always)] + #create_scan_work_function + + #[inline(always)] + #may_move_objects_function + } + } + } else { + abort_call_site!("`#[derive(PlanTraceObject)]` only supports structs with named fields.") + }; + + // Debug the output + // println!("{}", output.to_token_stream()); + + output.into() +} + +fn generate_trace_object<'a>( + space_fields: &[&'a Field], + parent_field: &Option<&'a Field>, + ty_generics: &TypeGenerics, +) -> TokenStream2 { + let space_field_handler = space_fields.iter().map(|f| { + let f_ident = f.ident.as_ref().unwrap(); + let ref f_ty = f.ty; + + // Figure out copy + let trace_attr = util::get_field_attribute(f, "trace").unwrap(); + // parse tts + let copy = if !trace_attr.tokens.is_empty() { + // let copy = trace_attr.parse_meta().unwrap(); + // println!("copy {:?}", copy.name()); + use syn::AttributeArgs; + use syn::Token; + use syn::NestedMeta; + use syn::punctuated::Punctuated; + + let attr_tokens: TokenStream = trace_attr.tokens.clone().into(); + let args = trace_attr.parse_args_with(Punctuated::::parse_terminated).unwrap(); + if let Some(NestedMeta::Meta(syn::Meta::Path(p))) = args.first() { + quote!{ Some(#p) } + } else { + quote!{ None } + } + } else { + quote!{ None } + }; + + quote! { + if self.#f_ident.in_space(__mmtk_objref) { + return <#f_ty as PolicyTraceObject #ty_generics>::trace_object::(&self.#f_ident, __mmtk_trace, __mmtk_objref, #copy, __mmtk_worker); + } + } + }); + + let parent_field_delegator = if let Some(f) = parent_field { + let f_ident = f.ident.as_ref().unwrap(); + let ref f_ty = f.ty; + quote! { + <#f_ty as PlanTraceObject #ty_generics>::trace_object::(&self.#f_ident, __mmtk_trace, __mmtk_objref, __mmtk_worker) + } + } else { + quote! { + panic!("No more spaces to try") + } + }; + + quote! { + fn trace_object(&self, __mmtk_trace: &mut T, __mmtk_objref: crate::util::ObjectReference, __mmtk_worker: &mut crate::scheduler::GCWorker) -> crate::util::ObjectReference { + use crate::policy::space::Space; + use crate::plan::transitive_closure::PolicyTraceObject; + use crate::plan::transitive_closure::PlanTraceObject; + #(#space_field_handler)* + #parent_field_delegator + } + } +} + +fn generate_create_scan_work<'a>( + scan_work: &Option<&'a Field>, + ty_generics: &TypeGenerics, +) -> TokenStream2 { + if let Some(f) = scan_work { + let f_ident = f.ident.as_ref().unwrap(); + let ref f_ty = f.ty; + + quote! { + fn create_scan_work>(&'static self, nodes: Vec) -> Box> { + use crate::plan::transitive_closure::PolicyTraceObject; + <#f_ty as PolicyTraceObject #ty_generics>::create_scan_work::(&self.#f_ident, nodes) + } + } + } else { + quote! { + fn create_scan_work>(&'static self, nodes: Vec) -> Box> { + unreachable!() + } + } + } +} + +fn generate_may_move_objects<'a>( + scan_work: &Option<&'a Field>, + ty_generics: &TypeGenerics, +) -> TokenStream2 { + if let Some(f) = scan_work { + let f_ident = f.ident.as_ref().unwrap(); + let ref f_ty = f.ty; + + quote! { + fn may_move_objects() -> bool { + use crate::plan::transitive_closure::PolicyTraceObject; + <#f_ty as PolicyTraceObject #ty_generics>::may_move_objects::() + } + } + } else { + quote! { + fn may_move_objects() -> bool { + unreachable!() + } + } + } +} diff --git a/macros/trace_object/src/util.rs b/macros/trace_object/src/util.rs new file mode 100644 index 0000000000..4bb8d2417f --- /dev/null +++ b/macros/trace_object/src/util.rs @@ -0,0 +1,47 @@ +use proc_macro2::TokenStream; +use proc_macro_error::{abort, abort_call_site}; +use quote::quote; +use syn::{spanned::Spanned, Attribute, DeriveInput, Field, FieldsNamed}; + +pub fn get_field_attribute<'f>(field: &'f Field, attr_name: &str) -> Option<&'f Attribute> { + let attrs = field + .attrs + .iter() + .filter(|a| a.path.is_ident(attr_name)) + .collect::>(); + if attrs.len() > 1 { + let second_attr = attrs.get(1).unwrap(); + abort! { second_attr.path.span(), "Duplicated attribute: #[{}]", attr_name } + }; + + attrs.get(0).cloned() +} + +pub fn get_fields_with_attribute<'f>(fields: &'f FieldsNamed, attr_name: &str) -> Vec<&'f Field> { + fields + .named + .iter() + .filter(|f| get_field_attribute(f, attr_name).is_some()) + .collect::>() +} + +pub fn get_unique_field_with_attribute<'f>( + fields: &'f FieldsNamed, + attr_name: &str, +) -> Option<&'f Field> { + let mut result = None; + + 'each_field: for field in fields.named.iter() { + if let Some(attr) = get_field_attribute(field, attr_name) { + if result.is_none() { + result = Some(field); + continue 'each_field; + } else { + let span = attr.path.span(); + abort! { span, "At most one field in a struct can have the #[{}] attribute.", attr_name }; + } + } + } + + result +} diff --git a/src/plan/generational/copying/gc_work.rs b/src/plan/generational/copying/gc_work.rs index 867b09b1fc..3a27d787dc 100644 --- a/src/plan/generational/copying/gc_work.rs +++ b/src/plan/generational/copying/gc_work.rs @@ -1,10 +1,21 @@ use super::global::GenCopy; use crate::scheduler::gc_work::SFTProcessEdges; use crate::vm::*; +use crate::plan::generational::gc_work::GenNurseryProcessEdges; + +use crate::policy::gc_work::PlanProcessEdges; +use crate::policy::gc_work::DEFAULT_TRACE; + +pub struct GenCopyNurseryGCWorkContext(std::marker::PhantomData); +impl crate::scheduler::GCWorkContext for GenCopyNurseryGCWorkContext { + type VM = VM; + type PlanType = GenCopy; + type ProcessEdgesWorkType = GenNurseryProcessEdges; +} pub struct GenCopyGCWorkContext(std::marker::PhantomData); impl crate::scheduler::GCWorkContext for GenCopyGCWorkContext { type VM = VM; type PlanType = GenCopy; - type ProcessEdgesWorkType = SFTProcessEdges; + type ProcessEdgesWorkType = PlanProcessEdges, DEFAULT_TRACE>; } diff --git a/src/plan/generational/copying/global.rs b/src/plan/generational/copying/global.rs index 9ce19e2d79..e17091461d 100644 --- a/src/plan/generational/copying/global.rs +++ b/src/plan/generational/copying/global.rs @@ -1,4 +1,5 @@ use super::gc_work::GenCopyGCWorkContext; +use super::gc_work::GenCopyNurseryGCWorkContext; use super::mutator::ALLOCATOR_MAPPING; use crate::plan::generational::global::Gen; use crate::plan::global::BasePlan; @@ -25,10 +26,17 @@ use enum_map::EnumMap; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; +use macro_trace_object::PlanTraceObject; + +#[derive(PlanTraceObject)] pub struct GenCopy { + #[fallback_trace] pub gen: Gen, pub hi: AtomicBool, + #[main_policy] + #[trace(CopySemantics::Mature)] pub copyspace0: CopySpace, + #[trace(CopySemantics::Mature)] pub copyspace1: CopySpace, } @@ -79,10 +87,14 @@ impl Plan for GenCopy { } fn schedule_collection(&'static self, scheduler: &GCWorkScheduler) { - let _ = self.request_full_heap_collection(); + let is_full_heap = self.request_full_heap_collection(); self.base().set_collection_kind::(self); self.base().set_gc_status(GcStatus::GcPrepare); - scheduler.schedule_common_work::>(self); + if is_full_heap { + scheduler.schedule_common_work::>(self); + } else { + scheduler.schedule_common_work::>(self); + } } fn get_allocator_mapping(&self) -> &'static EnumMap { diff --git a/src/plan/generational/global.rs b/src/plan/generational/global.rs index cbd15b9cc6..3a0002977b 100644 --- a/src/plan/generational/global.rs +++ b/src/plan/generational/global.rs @@ -21,12 +21,17 @@ use std::sync::atomic::AtomicBool; use std::sync::atomic::Ordering; use std::sync::Arc; +use macro_trace_object::PlanTraceObject; + /// Common implementation for generational plans. Each generational plan /// should include this type, and forward calls to it where possible. +#[derive(PlanTraceObject)] pub struct Gen { /// The nursery space. Its type depends on the actual plan. + #[trace(CopySemantics::PromoteToMature)] pub nursery: CopySpace, /// The common plan. + #[fallback_trace] pub common: CommonPlan, /// Is this GC full heap? pub gc_full_heap: AtomicBool, @@ -160,6 +165,8 @@ impl Gen { self.gc_full_heap.store(is_full_heap, Ordering::SeqCst); + info!("{}", if is_full_heap { "Full heap GC" } else { "nursery GC"} ); + is_full_heap } diff --git a/src/plan/generational/immix/gc_work.rs b/src/plan/generational/immix/gc_work.rs index b1dc5fa98b..4e1580438b 100644 --- a/src/plan/generational/immix/gc_work.rs +++ b/src/plan/generational/immix/gc_work.rs @@ -1,7 +1,8 @@ use super::global::GenImmix; use crate::plan::generational::gc_work::GenNurseryProcessEdges; use crate::plan::TransitiveClosure; -use crate::policy::gc_work::{PolicyProcessEdges, TraceKind}; +// use crate::policy::gc_work::{PolicyProcessEdges, TraceKind}; +use crate::policy::gc_work::TraceKind; use crate::policy::immix::ImmixSpace; use crate::scheduler::GCWorker; use crate::util::copy::CopySemantics; @@ -28,6 +29,9 @@ impl crate::policy::gc_work::UsePolicyProcessEdges for GenImm } } +use crate::policy::gc_work::PlanProcessEdges; +use crate::policy::gc_work::DEFAULT_TRACE; + pub struct GenImmixNurseryGCWorkContext(std::marker::PhantomData); impl crate::scheduler::GCWorkContext for GenImmixNurseryGCWorkContext { type VM = VM; @@ -43,5 +47,5 @@ impl crate::scheduler::GCWorkContext { type VM = VM; type PlanType = GenImmix; - type ProcessEdgesWorkType = PolicyProcessEdges, KIND>; + type ProcessEdgesWorkType = PlanProcessEdges, KIND>; } diff --git a/src/plan/generational/immix/global.rs b/src/plan/generational/immix/global.rs index 58a0932299..df5360e46a 100644 --- a/src/plan/generational/immix/global.rs +++ b/src/plan/generational/immix/global.rs @@ -26,14 +26,20 @@ use std::sync::atomic::AtomicBool; use std::sync::atomic::Ordering; use std::sync::Arc; +use macro_trace_object::PlanTraceObject; + /// Generational immix. This implements the functionality of a two-generation copying /// collector where the higher generation is an immix space. /// See the PLDI'08 paper by Blackburn and McKinley for a description /// of the algorithm: http://doi.acm.org/10.1145/1375581.137558. +#[derive(PlanTraceObject)] pub struct GenImmix { /// Generational plan, which includes a nursery space and operations related with nursery. + #[fallback_trace] pub gen: Gen, /// An immix space as the mature space. + #[main_policy] + #[trace(CopySemantics::Mature)] pub immix: ImmixSpace, /// Whether the last GC was a defrag GC for the immix space. pub last_gc_was_defrag: AtomicBool, diff --git a/src/plan/global.rs b/src/plan/global.rs index 2c990cd115..fe24e8577f 100644 --- a/src/plan/global.rs +++ b/src/plan/global.rs @@ -33,6 +33,8 @@ use enum_map::EnumMap; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use std::sync::{Arc, Mutex}; +use macro_trace_object::PlanTraceObject; + pub fn create_mutator( tls: VMMutatorThread, mmtk: &'static MMTK, @@ -338,6 +340,7 @@ pub enum GcStatus { /** BasePlan should contain all plan-related state and functions that are _fundamental_ to _all_ plans. These include VM-specific (but not plan-specific) features such as a code space or vm space, which are fundamental to all plans for a given VM. Features that are common to _many_ (but not intrinsically _all_) plans should instead be included in CommonPlan. */ +#[derive(PlanTraceObject)] pub struct BasePlan { /// Whether MMTk is now ready for collection. This is set to true when initialize_collection() is called. pub initialized: AtomicBool, @@ -377,10 +380,13 @@ pub struct BasePlan { // Spaces in base plan #[cfg(feature = "code_space")] + #[trace] pub code_space: ImmortalSpace, #[cfg(feature = "code_space")] + #[trace] pub code_lo_space: ImmortalSpace, #[cfg(feature = "ro_space")] + #[trace] pub ro_space: ImmortalSpace, /// A VM space is a space allocated and populated by the VM. Currently it is used by JikesRVM @@ -396,6 +402,7 @@ pub struct BasePlan { /// - The `is_in_mmtk_spaces` currently returns `true` if the given object reference is in /// the VM space. #[cfg(feature = "vm_space")] + #[trace] pub vm_space: ImmortalSpace, } @@ -834,9 +841,13 @@ impl BasePlan { /** CommonPlan is for representing state and features used by _many_ plans, but that are not fundamental to _all_ plans. Examples include the Large Object Space and an Immortal space. Features that are fundamental to _all_ plans must be included in BasePlan. */ +#[derive(PlanTraceObject)] pub struct CommonPlan { + #[trace] pub immortal: ImmortalSpace, + #[trace] pub los: LargeObjectSpace, + #[fallback_trace] pub base: BasePlan, } diff --git a/src/plan/immix/gc_work.rs b/src/plan/immix/gc_work.rs index c51bfd5efc..3c8d7185f1 100644 --- a/src/plan/immix/gc_work.rs +++ b/src/plan/immix/gc_work.rs @@ -1,12 +1,16 @@ use super::global::Immix; use crate::plan::TransitiveClosure; -use crate::policy::gc_work::{PolicyProcessEdges, TraceKind}; +// use crate::policy::gc_work::{PolicyProcessEdges, TraceKind}; +use crate::policy::gc_work::TraceKind; use crate::policy::immix::ImmixSpace; use crate::scheduler::GCWorker; use crate::util::copy::CopySemantics; use crate::util::ObjectReference; use crate::vm::VMBinding; +use crate::policy::gc_work::PlanProcessEdges; +use crate::policy::gc_work::DEFAULT_TRACE; + impl crate::policy::gc_work::UsePolicyProcessEdges for Immix { type TargetPolicy = ImmixSpace; const COPY: CopySemantics = CopySemantics::DefaultCopy; @@ -35,5 +39,5 @@ impl crate::scheduler::GCWorkContext { type VM = VM; type PlanType = Immix; - type ProcessEdgesWorkType = PolicyProcessEdges, KIND>; + type ProcessEdgesWorkType = PlanProcessEdges, KIND>; } diff --git a/src/plan/immix/global.rs b/src/plan/immix/global.rs index bfd7555233..752226af87 100644 --- a/src/plan/immix/global.rs +++ b/src/plan/immix/global.rs @@ -26,8 +26,14 @@ use std::sync::Arc; use atomic::Ordering; use enum_map::EnumMap; +use macro_trace_object::PlanTraceObject; + +#[derive(PlanTraceObject)] pub struct Immix { + #[main_policy] + #[trace(CopySemantics::DefaultCopy)] pub immix_space: ImmixSpace, + #[fallback_trace] pub common: CommonPlan, last_gc_was_defrag: AtomicBool, } diff --git a/src/plan/markcompact/gc_work.rs b/src/plan/markcompact/gc_work.rs index 59ff1c997f..7a81f38661 100644 --- a/src/plan/markcompact/gc_work.rs +++ b/src/plan/markcompact/gc_work.rs @@ -1,6 +1,6 @@ use super::global::MarkCompact; use crate::plan::TransitiveClosure; -use crate::policy::gc_work::PolicyProcessEdges; +use crate::policy::gc_work::PlanProcessEdges; use crate::policy::markcompactspace::MarkCompactSpace; use crate::policy::markcompactspace::{TRACE_KIND_FORWARD, TRACE_KIND_MARK}; use crate::scheduler::gc_work::*; @@ -85,9 +85,9 @@ impl Compact { } /// Marking trace -pub type MarkingProcessEdges = PolicyProcessEdges, TRACE_KIND_MARK>; +pub type MarkingProcessEdges = PlanProcessEdges, TRACE_KIND_MARK>; /// Forwarding trace -pub type ForwardingProcessEdges = PolicyProcessEdges, TRACE_KIND_FORWARD>; +pub type ForwardingProcessEdges = PlanProcessEdges, TRACE_KIND_FORWARD>; use crate::util::copy::CopySemantics; diff --git a/src/plan/markcompact/global.rs b/src/plan/markcompact/global.rs index 6562b75e72..a0ca295234 100644 --- a/src/plan/markcompact/global.rs +++ b/src/plan/markcompact/global.rs @@ -26,11 +26,19 @@ use crate::util::metadata::side_metadata::{SideMetadataContext, SideMetadataSani use crate::util::opaque_pointer::*; use crate::util::options::UnsafeOptionsWrapper; use crate::vm::VMBinding; +use crate::util::copy::CopySemantics; + use enum_map::EnumMap; use std::sync::Arc; +use macro_trace_object::PlanTraceObject; + +#[derive(PlanTraceObject)] pub struct MarkCompact { + #[main_policy] + #[trace(CopySemantics::DefaultCopy)] pub mc_space: MarkCompactSpace, + #[fallback_trace] pub common: CommonPlan, } diff --git a/src/plan/marksweep/gc_work.rs b/src/plan/marksweep/gc_work.rs index 6665d1046b..248b547413 100644 --- a/src/plan/marksweep/gc_work.rs +++ b/src/plan/marksweep/gc_work.rs @@ -68,10 +68,12 @@ impl GCWork for MSSweepChunks { } } -use crate::scheduler::gc_work::SFTProcessEdges; pub struct MSGCWorkContext(std::marker::PhantomData); +use crate::policy::gc_work::PlanProcessEdges; +use crate::policy::gc_work::DEFAULT_TRACE; + impl crate::scheduler::GCWorkContext for MSGCWorkContext { type VM = VM; type PlanType = MarkSweep; - type ProcessEdgesWorkType = SFTProcessEdges; + type ProcessEdgesWorkType = PlanProcessEdges, DEFAULT_TRACE>; } diff --git a/src/plan/marksweep/global.rs b/src/plan/marksweep/global.rs index 1bf8ef0611..985ca680af 100644 --- a/src/plan/marksweep/global.rs +++ b/src/plan/marksweep/global.rs @@ -25,8 +25,14 @@ use std::sync::Arc; use enum_map::EnumMap; +use macro_trace_object::PlanTraceObject; + +#[derive(PlanTraceObject)] pub struct MarkSweep { + #[fallback_trace] common: CommonPlan, + #[main_policy] + #[trace] ms: MallocSpace, } diff --git a/src/plan/pageprotect/gc_work.rs b/src/plan/pageprotect/gc_work.rs index c95d9e6f21..0c09c927df 100644 --- a/src/plan/pageprotect/gc_work.rs +++ b/src/plan/pageprotect/gc_work.rs @@ -1,10 +1,12 @@ use super::global::PageProtect; use crate::scheduler::gc_work::SFTProcessEdges; use crate::vm::VMBinding; +use crate::policy::gc_work::PlanProcessEdges; +use crate::policy::gc_work::DEFAULT_TRACE; pub struct PPGCWorkContext(std::marker::PhantomData); impl crate::scheduler::GCWorkContext for PPGCWorkContext { type VM = VM; type PlanType = PageProtect; - type ProcessEdgesWorkType = SFTProcessEdges; + type ProcessEdgesWorkType = PlanProcessEdges, DEFAULT_TRACE>; } diff --git a/src/plan/pageprotect/global.rs b/src/plan/pageprotect/global.rs index 08ecff151e..0c8dc48ec1 100644 --- a/src/plan/pageprotect/global.rs +++ b/src/plan/pageprotect/global.rs @@ -22,7 +22,12 @@ use crate::{ use enum_map::EnumMap; use std::sync::Arc; +use macro_trace_object::PlanTraceObject; + +#[derive(PlanTraceObject)] pub struct PageProtect { + #[main_policy] + #[trace] pub space: LargeObjectSpace, pub common: CommonPlan, } diff --git a/src/plan/semispace/gc_work.rs b/src/plan/semispace/gc_work.rs index 0868e61b7f..b24e101f61 100644 --- a/src/plan/semispace/gc_work.rs +++ b/src/plan/semispace/gc_work.rs @@ -1,10 +1,12 @@ use super::global::SemiSpace; use crate::scheduler::gc_work::SFTProcessEdges; use crate::vm::VMBinding; +use crate::policy::gc_work::PlanProcessEdges; +use crate::policy::gc_work::DEFAULT_TRACE; pub struct SSGCWorkContext(std::marker::PhantomData); impl crate::scheduler::GCWorkContext for SSGCWorkContext { type VM = VM; type PlanType = SemiSpace; - type ProcessEdgesWorkType = SFTProcessEdges; + type ProcessEdgesWorkType = PlanProcessEdges, DEFAULT_TRACE>; } diff --git a/src/plan/semispace/global.rs b/src/plan/semispace/global.rs index 4de2526874..0df724f7c1 100644 --- a/src/plan/semispace/global.rs +++ b/src/plan/semispace/global.rs @@ -22,12 +22,19 @@ use crate::{plan::global::BasePlan, vm::VMBinding}; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; +use macro_trace_object::PlanTraceObject; + use enum_map::EnumMap; +#[derive(PlanTraceObject)] pub struct SemiSpace { pub hi: AtomicBool, + #[main_policy] + #[trace(CopySemantics::DefaultCopy)] pub copyspace0: CopySpace, + #[trace(CopySemantics::DefaultCopy)] pub copyspace1: CopySpace, + #[fallback_trace] pub common: CommonPlan, } diff --git a/src/plan/transitive_closure.rs b/src/plan/transitive_closure.rs index 108929b7f9..04eea3ecd3 100644 --- a/src/plan/transitive_closure.rs +++ b/src/plan/transitive_closure.rs @@ -84,11 +84,24 @@ impl<'a, E: ProcessEdgesWork> Drop for ObjectsClosure<'a, E> { use crate::vm::VMBinding; use crate::util::copy::CopySemantics; use crate::policy::gc_work::TraceKind; +use crate::scheduler::GCWork; pub trait PlanTraceObject { - fn trace_object(&self, trace: &mut T, object: ObjectReference, worker: &mut GCWorker) -> ObjectReference; + fn trace_object(&self, trace: &mut T, object: ObjectReference, worker: &mut GCWorker) -> ObjectReference; + fn create_scan_work>(&'static self, nodes: Vec) -> Box>; + fn may_move_objects() -> bool; } pub trait PolicyTraceObject { fn trace_object(&self, trace: &mut T, object: ObjectReference, copy: Option, worker: &mut GCWorker) -> ObjectReference; + #[inline(always)] + fn create_scan_work>( + &'static self, + nodes: Vec, + ) -> Box> { + Box::new(crate::scheduler::gc_work::ScanObjects::::new( + nodes, false, + )) + } + fn may_move_objects() -> bool; } diff --git a/src/policy/copyspace.rs b/src/policy/copyspace.rs index 8914bd36d2..a346fa4f5f 100644 --- a/src/policy/copyspace.rs +++ b/src/policy/copyspace.rs @@ -112,6 +112,11 @@ impl crate::plan::transitive_closure::PolicyTraceObject for C fn trace_object(&self, trace: &mut T, object: ObjectReference, copy: Option, worker: &mut GCWorker) -> ObjectReference { self.trace_object(trace, object, copy, worker) } + + #[inline(always)] + fn may_move_objects() -> bool { + true + } } impl CopySpace { @@ -219,7 +224,6 @@ impl CopySpace { // If this is not from space, we do not need to trace it (the object has been copied to the tosapce) if !self.is_from_space() { // The copy semantics for tospace should be none. - debug_assert!(semantics.is_none()); return object; } diff --git a/src/policy/gc_work.rs b/src/policy/gc_work.rs index ef1fa91231..b220fe6caa 100644 --- a/src/policy/gc_work.rs +++ b/src/policy/gc_work.rs @@ -16,6 +16,8 @@ use std::ops::{Deref, DerefMut}; /// mark vs forward trace for mark compact. pub(crate) type TraceKind = u8; +pub const DEFAULT_TRACE: u8 = u8::MAX; + /// A plan that uses `PolicyProcessEdges` needs to provide an implementation for this trait. /// The plan needs to specify a target space (which needs to implement `SupportPolicyProcessEdges`). /// For objects in the target space, `trace_object_with_tracekind()` is called to trace the object. @@ -70,13 +72,83 @@ pub trait SupportPolicyProcessEdges: Space { /// use `SFTProcessEdges`, they could try use this type. One major difference is that `PolicyProcessEdges` allows different /// traces for a policy. /// A plan that uses this needs to implement the `UsePolicyProcessEdges` trait, and the policy needs to implement `SupportPolicyProcessEdges`. -pub struct PolicyProcessEdges, const KIND: TraceKind> { +// pub struct PolicyProcessEdges, const KIND: TraceKind> { +// plan: &'static P, +// base: ProcessEdgesBase, +// } + +// impl, const KIND: TraceKind> ProcessEdgesWork +// for PolicyProcessEdges +// { +// type VM = VM; + +// fn new(edges: Vec

, roots: bool, mmtk: &'static MMTK) -> Self { +// let base = ProcessEdgesBase::new(edges, roots, mmtk); +// let plan = base.plan().downcast_ref::

().unwrap(); +// Self { plan, base } +// } + +// #[inline(always)] +// fn create_scan_work(&self, nodes: Vec) -> Box> { +// self.plan.get_target_space().create_scan_work::(nodes) +// } + +// /// Trace object if it is in the target space. Otherwise call fallback_trace(). +// #[inline(always)] +// fn trace_object(&mut self, object: ObjectReference) -> ObjectReference { +// if object.is_null() { +// return object; +// } +// if self.plan.get_target_space().in_space(object) { +// self.plan +// .get_target_space() +// .trace_object_with_tracekind::(self, object, P::COPY, self.worker()) +// } else { +// self.plan +// .fallback_trace::(self, object, self.worker()) +// } +// } + +// #[inline] +// fn process_edge(&mut self, slot: Address) { +// let object = unsafe { slot.load::() }; +// let new_object = self.trace_object(object); +// if P::TargetPolicy::may_move_objects::() { +// unsafe { slot.store(new_object) }; +// } +// } +// } + +// // Impl Deref/DerefMut to ProcessEdgesBase for PolicyProcessEdges + +// impl, const KIND: TraceKind> Deref +// for PolicyProcessEdges +// { +// type Target = ProcessEdgesBase; +// #[inline] +// fn deref(&self) -> &Self::Target { +// &self.base +// } +// } + +// impl, const KIND: TraceKind> DerefMut +// for PolicyProcessEdges +// { +// #[inline] +// fn deref_mut(&mut self) -> &mut Self::Target { +// &mut self.base +// } +// } + +use crate::plan::transitive_closure::PlanTraceObject; + +pub struct PlanProcessEdges + PlanTraceObject + Sync, const KIND: TraceKind> { plan: &'static P, base: ProcessEdgesBase, } -impl, const KIND: TraceKind> ProcessEdgesWork - for PolicyProcessEdges +impl + Plan + Sync, const KIND: TraceKind> ProcessEdgesWork + for PlanProcessEdges { type VM = VM; @@ -88,7 +160,7 @@ impl, const KIND: TraceKind> Process #[inline(always)] fn create_scan_work(&self, nodes: Vec) -> Box> { - self.plan.get_target_space().create_scan_work::(nodes) + self.plan.create_scan_work::(nodes) } /// Trace object if it is in the target space. Otherwise call fallback_trace(). @@ -97,21 +169,14 @@ impl, const KIND: TraceKind> Process if object.is_null() { return object; } - if self.plan.get_target_space().in_space(object) { - self.plan - .get_target_space() - .trace_object_with_tracekind::(self, object, P::COPY, self.worker()) - } else { - self.plan - .fallback_trace::(self, object, self.worker()) - } + self.plan.trace_object::(self, object, self.worker()) } #[inline] fn process_edge(&mut self, slot: Address) { let object = unsafe { slot.load::() }; let new_object = self.trace_object(object); - if P::TargetPolicy::may_move_objects::() { + if P::may_move_objects::() { unsafe { slot.store(new_object) }; } } @@ -119,8 +184,8 @@ impl, const KIND: TraceKind> Process // Impl Deref/DerefMut to ProcessEdgesBase for PolicyProcessEdges -impl, const KIND: TraceKind> Deref - for PolicyProcessEdges +impl + Plan + Sync, const KIND: TraceKind> Deref + for PlanProcessEdges { type Target = ProcessEdgesBase; #[inline] @@ -129,8 +194,8 @@ impl, const KIND: TraceKind> Deref } } -impl, const KIND: TraceKind> DerefMut - for PolicyProcessEdges +impl + Plan + Sync, const KIND: TraceKind> DerefMut + for PlanProcessEdges { #[inline] fn deref_mut(&mut self) -> &mut Self::Target { diff --git a/src/policy/immix/immixspace.rs b/src/policy/immix/immixspace.rs index 40ca0a2ab3..36d1d13e18 100644 --- a/src/policy/immix/immixspace.rs +++ b/src/policy/immix/immixspace.rs @@ -118,11 +118,33 @@ impl Space for ImmixSpace { } impl crate::plan::transitive_closure::PolicyTraceObject for ImmixSpace { + #[inline(always)] fn trace_object(&self, trace: &mut T, object: ObjectReference, copy: Option, worker: &mut GCWorker) -> ObjectReference { if KIND == TRACE_KIND_DEFRAG { - self.trace_object_with_opportunistic_copy(trace, object, copy.unwrap(), worker) + self.trace_object(trace, object, copy.unwrap(), worker) } else if KIND == TRACE_KIND_FAST { - self.trace_object_without_moving(trace, object) + self.fast_trace_object(trace, object) + } else { + unreachable!() + } + } + + #[inline(always)] + fn create_scan_work>( + &'static self, + nodes: Vec, + ) -> Box> { + Box::new(crate::policy::immix::ScanObjectsAndMarkLines::::new( + nodes, false, self, + )) + } + + #[inline(always)] + fn may_move_objects() -> bool { + if KIND == TRACE_KIND_DEFRAG { + true + } else if KIND == TRACE_KIND_FAST { + false } else { unreachable!() } diff --git a/src/policy/immortalspace.rs b/src/policy/immortalspace.rs index 2ae9e6e829..a23f457825 100644 --- a/src/policy/immortalspace.rs +++ b/src/policy/immortalspace.rs @@ -118,6 +118,10 @@ impl crate::plan::transitive_closure::PolicyTraceObject for I fn trace_object(&self, trace: &mut T, object: ObjectReference, _copy: Option, _worker: &mut GCWorker) -> ObjectReference { self.trace_object(trace, object) } + #[inline(always)] + fn may_move_objects() -> bool { + false + } } impl ImmortalSpace { diff --git a/src/policy/largeobjectspace.rs b/src/policy/largeobjectspace.rs index 6fedae817c..4e25d63356 100644 --- a/src/policy/largeobjectspace.rs +++ b/src/policy/largeobjectspace.rs @@ -122,6 +122,10 @@ impl crate::plan::transitive_closure::PolicyTraceObject for L fn trace_object(&self, trace: &mut T, object: ObjectReference, _copy: Option, _worker: &mut GCWorker) -> ObjectReference { self.trace_object(trace, object) } + #[inline(always)] + fn may_move_objects() -> bool { + false + } } impl LargeObjectSpace { diff --git a/src/policy/lockfreeimmortalspace.rs b/src/policy/lockfreeimmortalspace.rs index 48d381887e..52da24a219 100644 --- a/src/policy/lockfreeimmortalspace.rs +++ b/src/policy/lockfreeimmortalspace.rs @@ -157,6 +157,10 @@ impl crate::plan::transitive_closure::PolicyTraceObject for L fn trace_object(&self, _trace: &mut T, _object: ObjectReference, _copy: Option, _worker: &mut GCWorker) -> ObjectReference { unreachable!() } + #[inline(always)] + fn may_move_objects() -> bool { + unreachable!() + } } impl LockFreeImmortalSpace { diff --git a/src/policy/mallocspace/global.rs b/src/policy/mallocspace/global.rs index 927e43e726..9fe00081da 100644 --- a/src/policy/mallocspace/global.rs +++ b/src/policy/mallocspace/global.rs @@ -196,6 +196,11 @@ impl crate::plan::transitive_closure::PolicyTraceObject for M fn trace_object(&self, trace: &mut T, object: ObjectReference, _copy: Option, _worker: &mut GCWorker) -> ObjectReference { self.trace_object(trace, object) } + + #[inline(always)] + fn may_move_objects() -> bool { + false + } } impl MallocSpace { diff --git a/src/policy/markcompactspace.rs b/src/policy/markcompactspace.rs index 1221c9fb8e..a63c2fa440 100644 --- a/src/policy/markcompactspace.rs +++ b/src/policy/markcompactspace.rs @@ -111,6 +111,16 @@ impl crate::plan::transitive_closure::PolicyTraceObject for M unreachable!() } } + #[inline(always)] + fn may_move_objects() -> bool { + if KIND == TRACE_KIND_MARK { + false + } else if KIND == TRACE_KIND_FORWARD { + true + } else { + unreachable!() + } + } } impl MarkCompactSpace { From 9c17551bb2a9869c02360ed5a2af6210d6d7e5a1 Mon Sep 17 00:00:00 2001 From: Yi Lin Date: Tue, 26 Apr 2022 13:20:51 +1000 Subject: [PATCH 08/22] may_move_objects() will check parent --- macros/trace_object/src/lib.rs | 36 ++++++++++++++++++++++++--------- src/plan/generational/global.rs | 3 ++- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/macros/trace_object/src/lib.rs b/macros/trace_object/src/lib.rs index 6c7f0aaacd..ffd565cc61 100644 --- a/macros/trace_object/src/lib.rs +++ b/macros/trace_object/src/lib.rs @@ -32,7 +32,7 @@ pub fn derive_plan_trace_object(input: TokenStream) -> TokenStream { let trace_object_function = generate_trace_object(&spaces, &fallback, &ty_generics); let create_scan_work_function = generate_create_scan_work(&main_policy, &ty_generics); - let may_move_objects_function = generate_may_move_objects(&main_policy, &ty_generics); + let may_move_objects_function = generate_may_move_objects(&main_policy, &fallback, &ty_generics); quote!{ impl #impl_generics crate::plan::transitive_closure::PlanTraceObject #ty_generics for #ident #ty_generics #where_clause { #[inline(always)] @@ -117,10 +117,10 @@ fn generate_trace_object<'a>( } fn generate_create_scan_work<'a>( - scan_work: &Option<&'a Field>, + main_policy_field: &Option<&'a Field>, ty_generics: &TypeGenerics, ) -> TokenStream2 { - if let Some(f) = scan_work { + if let Some(f) = main_policy_field { let f_ident = f.ident.as_ref().unwrap(); let ref f_ty = f.ty; @@ -139,24 +139,40 @@ fn generate_create_scan_work<'a>( } } +// The function generated needs to be inlined and constant folded. Otherwise, there will be a huge +// performance penalty. fn generate_may_move_objects<'a>( - scan_work: &Option<&'a Field>, + main_policy_field: &Option<&'a Field>, + parent_field: &Option<&'a Field>, ty_generics: &TypeGenerics, ) -> TokenStream2 { - if let Some(f) = scan_work { + if let Some(f) = main_policy_field { let f_ident = f.ident.as_ref().unwrap(); let ref f_ty = f.ty; - quote! { - fn may_move_objects() -> bool { - use crate::plan::transitive_closure::PolicyTraceObject; - <#f_ty as PolicyTraceObject #ty_generics>::may_move_objects::() + if let Some(p) = parent_field { + let p_ident = p.ident.as_ref().unwrap(); + let ref p_ty = p.ty; + quote! { + fn may_move_objects() -> bool { + use crate::plan::transitive_closure::PolicyTraceObject; + use crate::plan::transitive_closure::PlanTraceObject; + <#f_ty as PolicyTraceObject #ty_generics>::may_move_objects::() || <#p_ty as PlanTraceObject #ty_generics>::may_move_objects::() + } + } + } else { + quote! { + fn may_move_objects() -> bool { + use crate::plan::transitive_closure::PolicyTraceObject; + <#f_ty as PolicyTraceObject #ty_generics>::may_move_objects::() + } } } } else { + // If the plan has no main policy, by default we assume it does not move objects. quote! { fn may_move_objects() -> bool { - unreachable!() + false } } } diff --git a/src/plan/generational/global.rs b/src/plan/generational/global.rs index 3a0002977b..c41e8f87be 100644 --- a/src/plan/generational/global.rs +++ b/src/plan/generational/global.rs @@ -27,7 +27,8 @@ use macro_trace_object::PlanTraceObject; /// should include this type, and forward calls to it where possible. #[derive(PlanTraceObject)] pub struct Gen { - /// The nursery space. Its type depends on the actual plan. + /// The nursery space. + #[main_policy] #[trace(CopySemantics::PromoteToMature)] pub nursery: CopySpace, /// The common plan. From 9ba05e61f8a659286f42c5e754e96b029015f913 Mon Sep 17 00:00:00 2001 From: Yi Lin Date: Tue, 26 Apr 2022 14:55:08 +1000 Subject: [PATCH 09/22] Remove warnings --- macros/trace_object/src/lib.rs | 15 +++--------- macros/trace_object/src/util.rs | 6 ++--- src/plan/generational/copying/gc_work.rs | 3 +-- src/plan/generational/global.rs | 9 ++++++- src/plan/generational/immix/gc_work.rs | 2 -- src/plan/immix/gc_work.rs | 1 - src/plan/markcompact/global.rs | 2 +- src/plan/pageprotect/gc_work.rs | 3 +-- src/plan/semispace/gc_work.rs | 3 +-- src/plan/transitive_closure.rs | 24 ++++++++++++++---- src/policy/copyspace.rs | 8 +++++- src/policy/gc_work.rs | 31 +++++++++++++++++------- src/policy/immix/immixspace.rs | 8 +++++- src/policy/immortalspace.rs | 10 ++++++-- src/policy/largeobjectspace.rs | 14 ++++++++--- src/policy/lockfreeimmortalspace.rs | 16 +++++++++--- src/policy/mallocspace/global.rs | 10 ++++++-- src/policy/markcompactspace.rs | 12 +++++++-- 18 files changed, 121 insertions(+), 56 deletions(-) diff --git a/macros/trace_object/src/lib.rs b/macros/trace_object/src/lib.rs index ffd565cc61..11fc39b421 100644 --- a/macros/trace_object/src/lib.rs +++ b/macros/trace_object/src/lib.rs @@ -6,12 +6,10 @@ extern crate quote; use proc_macro::TokenStream; use proc_macro_error::proc_macro_error; use syn::{parse_macro_input}; -use proc_macro_error::{abort, abort_call_site}; +use proc_macro_error::abort_call_site; use quote::quote; -use syn::{spanned::Spanned, Attribute, DeriveInput, Field, FieldsNamed, TypeGenerics}; -use quote::ToTokens; +use syn::{DeriveInput, Field, TypeGenerics}; use syn::__private::TokenStream2; -use syn::token::Token; mod util; @@ -66,16 +64,11 @@ fn generate_trace_object<'a>( // Figure out copy let trace_attr = util::get_field_attribute(f, "trace").unwrap(); - // parse tts let copy = if !trace_attr.tokens.is_empty() { - // let copy = trace_attr.parse_meta().unwrap(); - // println!("copy {:?}", copy.name()); - use syn::AttributeArgs; use syn::Token; use syn::NestedMeta; use syn::punctuated::Punctuated; - let attr_tokens: TokenStream = trace_attr.tokens.clone().into(); let args = trace_attr.parse_args_with(Punctuated::::parse_terminated).unwrap(); if let Some(NestedMeta::Meta(syn::Meta::Path(p))) = args.first() { quote!{ Some(#p) } @@ -139,7 +132,7 @@ fn generate_create_scan_work<'a>( } } -// The function generated needs to be inlined and constant folded. Otherwise, there will be a huge +// The generated function needs to be inlined and constant folded. Otherwise, there will be a huge // performance penalty. fn generate_may_move_objects<'a>( main_policy_field: &Option<&'a Field>, @@ -147,11 +140,9 @@ fn generate_may_move_objects<'a>( ty_generics: &TypeGenerics, ) -> TokenStream2 { if let Some(f) = main_policy_field { - let f_ident = f.ident.as_ref().unwrap(); let ref f_ty = f.ty; if let Some(p) = parent_field { - let p_ident = p.ident.as_ref().unwrap(); let ref p_ty = p.ty; quote! { fn may_move_objects() -> bool { diff --git a/macros/trace_object/src/util.rs b/macros/trace_object/src/util.rs index 4bb8d2417f..a45a82dff0 100644 --- a/macros/trace_object/src/util.rs +++ b/macros/trace_object/src/util.rs @@ -1,7 +1,5 @@ -use proc_macro2::TokenStream; -use proc_macro_error::{abort, abort_call_site}; -use quote::quote; -use syn::{spanned::Spanned, Attribute, DeriveInput, Field, FieldsNamed}; +use proc_macro_error::abort; +use syn::{spanned::Spanned, Attribute, Field, FieldsNamed}; pub fn get_field_attribute<'f>(field: &'f Field, attr_name: &str) -> Option<&'f Attribute> { let attrs = field diff --git a/src/plan/generational/copying/gc_work.rs b/src/plan/generational/copying/gc_work.rs index 3a27d787dc..961e08b8c2 100644 --- a/src/plan/generational/copying/gc_work.rs +++ b/src/plan/generational/copying/gc_work.rs @@ -1,7 +1,6 @@ use super::global::GenCopy; -use crate::scheduler::gc_work::SFTProcessEdges; -use crate::vm::*; use crate::plan::generational::gc_work::GenNurseryProcessEdges; +use crate::vm::*; use crate::policy::gc_work::PlanProcessEdges; use crate::policy::gc_work::DEFAULT_TRACE; diff --git a/src/plan/generational/global.rs b/src/plan/generational/global.rs index c41e8f87be..82422ef2f3 100644 --- a/src/plan/generational/global.rs +++ b/src/plan/generational/global.rs @@ -166,7 +166,14 @@ impl Gen { self.gc_full_heap.store(is_full_heap, Ordering::SeqCst); - info!("{}", if is_full_heap { "Full heap GC" } else { "nursery GC"} ); + info!( + "{}", + if is_full_heap { + "Full heap GC" + } else { + "nursery GC" + } + ); is_full_heap } diff --git a/src/plan/generational/immix/gc_work.rs b/src/plan/generational/immix/gc_work.rs index 4e1580438b..2d0a6b7a02 100644 --- a/src/plan/generational/immix/gc_work.rs +++ b/src/plan/generational/immix/gc_work.rs @@ -1,7 +1,6 @@ use super::global::GenImmix; use crate::plan::generational::gc_work::GenNurseryProcessEdges; use crate::plan::TransitiveClosure; -// use crate::policy::gc_work::{PolicyProcessEdges, TraceKind}; use crate::policy::gc_work::TraceKind; use crate::policy::immix::ImmixSpace; use crate::scheduler::GCWorker; @@ -30,7 +29,6 @@ impl crate::policy::gc_work::UsePolicyProcessEdges for GenImm } use crate::policy::gc_work::PlanProcessEdges; -use crate::policy::gc_work::DEFAULT_TRACE; pub struct GenImmixNurseryGCWorkContext(std::marker::PhantomData); impl crate::scheduler::GCWorkContext for GenImmixNurseryGCWorkContext { diff --git a/src/plan/immix/gc_work.rs b/src/plan/immix/gc_work.rs index 3c8d7185f1..16251afa7e 100644 --- a/src/plan/immix/gc_work.rs +++ b/src/plan/immix/gc_work.rs @@ -9,7 +9,6 @@ use crate::util::ObjectReference; use crate::vm::VMBinding; use crate::policy::gc_work::PlanProcessEdges; -use crate::policy::gc_work::DEFAULT_TRACE; impl crate::policy::gc_work::UsePolicyProcessEdges for Immix { type TargetPolicy = ImmixSpace; diff --git a/src/plan/markcompact/global.rs b/src/plan/markcompact/global.rs index a0ca295234..8a6f773812 100644 --- a/src/plan/markcompact/global.rs +++ b/src/plan/markcompact/global.rs @@ -17,6 +17,7 @@ use crate::scheduler::*; use crate::util::alloc::allocators::AllocatorSelector; #[cfg(not(feature = "global_alloc_bit"))] use crate::util::alloc_bit::ALLOC_SIDE_METADATA_SPEC; +use crate::util::copy::CopySemantics; use crate::util::heap::layout::heap_layout::Mmapper; use crate::util::heap::layout::heap_layout::VMMap; use crate::util::heap::layout::vm_layout_constants::{HEAP_END, HEAP_START}; @@ -26,7 +27,6 @@ use crate::util::metadata::side_metadata::{SideMetadataContext, SideMetadataSani use crate::util::opaque_pointer::*; use crate::util::options::UnsafeOptionsWrapper; use crate::vm::VMBinding; -use crate::util::copy::CopySemantics; use enum_map::EnumMap; use std::sync::Arc; diff --git a/src/plan/pageprotect/gc_work.rs b/src/plan/pageprotect/gc_work.rs index 0c09c927df..8f61a21f8e 100644 --- a/src/plan/pageprotect/gc_work.rs +++ b/src/plan/pageprotect/gc_work.rs @@ -1,8 +1,7 @@ use super::global::PageProtect; -use crate::scheduler::gc_work::SFTProcessEdges; -use crate::vm::VMBinding; use crate::policy::gc_work::PlanProcessEdges; use crate::policy::gc_work::DEFAULT_TRACE; +use crate::vm::VMBinding; pub struct PPGCWorkContext(std::marker::PhantomData); impl crate::scheduler::GCWorkContext for PPGCWorkContext { diff --git a/src/plan/semispace/gc_work.rs b/src/plan/semispace/gc_work.rs index b24e101f61..3171c10689 100644 --- a/src/plan/semispace/gc_work.rs +++ b/src/plan/semispace/gc_work.rs @@ -1,8 +1,7 @@ use super::global::SemiSpace; -use crate::scheduler::gc_work::SFTProcessEdges; -use crate::vm::VMBinding; use crate::policy::gc_work::PlanProcessEdges; use crate::policy::gc_work::DEFAULT_TRACE; +use crate::vm::VMBinding; pub struct SSGCWorkContext(std::marker::PhantomData); impl crate::scheduler::GCWorkContext for SSGCWorkContext { diff --git a/src/plan/transitive_closure.rs b/src/plan/transitive_closure.rs index 04eea3ecd3..48461370d2 100644 --- a/src/plan/transitive_closure.rs +++ b/src/plan/transitive_closure.rs @@ -81,19 +81,33 @@ impl<'a, E: ProcessEdgesWork> Drop for ObjectsClosure<'a, E> { } } -use crate::vm::VMBinding; -use crate::util::copy::CopySemantics; use crate::policy::gc_work::TraceKind; use crate::scheduler::GCWork; +use crate::util::copy::CopySemantics; +use crate::vm::VMBinding; pub trait PlanTraceObject { - fn trace_object(&self, trace: &mut T, object: ObjectReference, worker: &mut GCWorker) -> ObjectReference; - fn create_scan_work>(&'static self, nodes: Vec) -> Box>; + fn trace_object( + &self, + trace: &mut T, + object: ObjectReference, + worker: &mut GCWorker, + ) -> ObjectReference; + fn create_scan_work>( + &'static self, + nodes: Vec, + ) -> Box>; fn may_move_objects() -> bool; } pub trait PolicyTraceObject { - fn trace_object(&self, trace: &mut T, object: ObjectReference, copy: Option, worker: &mut GCWorker) -> ObjectReference; + fn trace_object( + &self, + trace: &mut T, + object: ObjectReference, + copy: Option, + worker: &mut GCWorker, + ) -> ObjectReference; #[inline(always)] fn create_scan_work>( &'static self, diff --git a/src/policy/copyspace.rs b/src/policy/copyspace.rs index a346fa4f5f..65d449f663 100644 --- a/src/policy/copyspace.rs +++ b/src/policy/copyspace.rs @@ -109,7 +109,13 @@ impl Space for CopySpace { } impl crate::plan::transitive_closure::PolicyTraceObject for CopySpace { - fn trace_object(&self, trace: &mut T, object: ObjectReference, copy: Option, worker: &mut GCWorker) -> ObjectReference { + fn trace_object( + &self, + trace: &mut T, + object: ObjectReference, + copy: Option, + worker: &mut GCWorker, + ) -> ObjectReference { self.trace_object(trace, object, copy, worker) } diff --git a/src/policy/gc_work.rs b/src/policy/gc_work.rs index b220fe6caa..1b1f820a8b 100644 --- a/src/policy/gc_work.rs +++ b/src/policy/gc_work.rs @@ -139,16 +139,22 @@ pub trait SupportPolicyProcessEdges: Space { // &mut self.base // } // } - use crate::plan::transitive_closure::PlanTraceObject; -pub struct PlanProcessEdges + PlanTraceObject + Sync, const KIND: TraceKind> { +pub struct PlanProcessEdges< + VM: VMBinding, + P: 'static + Plan + PlanTraceObject + Sync, + const KIND: TraceKind, +> { plan: &'static P, base: ProcessEdgesBase, } -impl + Plan + Sync, const KIND: TraceKind> ProcessEdgesWork - for PlanProcessEdges +impl< + VM: VMBinding, + P: 'static + PlanTraceObject + Plan + Sync, + const KIND: TraceKind, + > ProcessEdgesWork for PlanProcessEdges { type VM = VM; @@ -169,7 +175,8 @@ impl + Plan + Sync, con if object.is_null() { return object; } - self.plan.trace_object::(self, object, self.worker()) + self.plan + .trace_object::(self, object, self.worker()) } #[inline] @@ -184,8 +191,11 @@ impl + Plan + Sync, con // Impl Deref/DerefMut to ProcessEdgesBase for PolicyProcessEdges -impl + Plan + Sync, const KIND: TraceKind> Deref - for PlanProcessEdges +impl< + VM: VMBinding, + P: 'static + PlanTraceObject + Plan + Sync, + const KIND: TraceKind, + > Deref for PlanProcessEdges { type Target = ProcessEdgesBase; #[inline] @@ -194,8 +204,11 @@ impl + Plan + Sync, con } } -impl + Plan + Sync, const KIND: TraceKind> DerefMut - for PlanProcessEdges +impl< + VM: VMBinding, + P: 'static + PlanTraceObject + Plan + Sync, + const KIND: TraceKind, + > DerefMut for PlanProcessEdges { #[inline] fn deref_mut(&mut self) -> &mut Self::Target { diff --git a/src/policy/immix/immixspace.rs b/src/policy/immix/immixspace.rs index 36d1d13e18..1645102181 100644 --- a/src/policy/immix/immixspace.rs +++ b/src/policy/immix/immixspace.rs @@ -119,7 +119,13 @@ impl Space for ImmixSpace { impl crate::plan::transitive_closure::PolicyTraceObject for ImmixSpace { #[inline(always)] - fn trace_object(&self, trace: &mut T, object: ObjectReference, copy: Option, worker: &mut GCWorker) -> ObjectReference { + fn trace_object( + &self, + trace: &mut T, + object: ObjectReference, + copy: Option, + worker: &mut GCWorker, + ) -> ObjectReference { if KIND == TRACE_KIND_DEFRAG { self.trace_object(trace, object, copy.unwrap(), worker) } else if KIND == TRACE_KIND_FAST { diff --git a/src/policy/immortalspace.rs b/src/policy/immortalspace.rs index a23f457825..efeec40851 100644 --- a/src/policy/immortalspace.rs +++ b/src/policy/immortalspace.rs @@ -111,11 +111,17 @@ impl Space for ImmortalSpace { } } -use crate::util::copy::CopySemantics; use crate::scheduler::GCWorker; +use crate::util::copy::CopySemantics; impl crate::plan::transitive_closure::PolicyTraceObject for ImmortalSpace { - fn trace_object(&self, trace: &mut T, object: ObjectReference, _copy: Option, _worker: &mut GCWorker) -> ObjectReference { + fn trace_object( + &self, + trace: &mut T, + object: ObjectReference, + _copy: Option, + _worker: &mut GCWorker, + ) -> ObjectReference { self.trace_object(trace, object) } #[inline(always)] diff --git a/src/policy/largeobjectspace.rs b/src/policy/largeobjectspace.rs index 4e25d63356..50889e332b 100644 --- a/src/policy/largeobjectspace.rs +++ b/src/policy/largeobjectspace.rs @@ -115,11 +115,19 @@ impl Space for LargeObjectSpace { } } -use crate::util::copy::CopySemantics; use crate::scheduler::GCWorker; +use crate::util::copy::CopySemantics; -impl crate::plan::transitive_closure::PolicyTraceObject for LargeObjectSpace { - fn trace_object(&self, trace: &mut T, object: ObjectReference, _copy: Option, _worker: &mut GCWorker) -> ObjectReference { +impl crate::plan::transitive_closure::PolicyTraceObject + for LargeObjectSpace +{ + fn trace_object( + &self, + trace: &mut T, + object: ObjectReference, + _copy: Option, + _worker: &mut GCWorker, + ) -> ObjectReference { self.trace_object(trace, object) } #[inline(always)] diff --git a/src/policy/lockfreeimmortalspace.rs b/src/policy/lockfreeimmortalspace.rs index 52da24a219..03a2d299c2 100644 --- a/src/policy/lockfreeimmortalspace.rs +++ b/src/policy/lockfreeimmortalspace.rs @@ -149,12 +149,20 @@ impl Space for LockFreeImmortalSpace { } } -use crate::util::copy::CopySemantics; -use crate::scheduler::GCWorker; use crate::plan::TransitiveClosure; +use crate::scheduler::GCWorker; +use crate::util::copy::CopySemantics; -impl crate::plan::transitive_closure::PolicyTraceObject for LockFreeImmortalSpace { - fn trace_object(&self, _trace: &mut T, _object: ObjectReference, _copy: Option, _worker: &mut GCWorker) -> ObjectReference { +impl crate::plan::transitive_closure::PolicyTraceObject + for LockFreeImmortalSpace +{ + fn trace_object( + &self, + _trace: &mut T, + _object: ObjectReference, + _copy: Option, + _worker: &mut GCWorker, + ) -> ObjectReference { unreachable!() } #[inline(always)] diff --git a/src/policy/mallocspace/global.rs b/src/policy/mallocspace/global.rs index 9fe00081da..ef6c09c009 100644 --- a/src/policy/mallocspace/global.rs +++ b/src/policy/mallocspace/global.rs @@ -189,11 +189,17 @@ impl Space for MallocSpace { } } -use crate::util::copy::CopySemantics; use crate::scheduler::GCWorker; +use crate::util::copy::CopySemantics; impl crate::plan::transitive_closure::PolicyTraceObject for MallocSpace { - fn trace_object(&self, trace: &mut T, object: ObjectReference, _copy: Option, _worker: &mut GCWorker) -> ObjectReference { + fn trace_object( + &self, + trace: &mut T, + object: ObjectReference, + _copy: Option, + _worker: &mut GCWorker, + ) -> ObjectReference { self.trace_object(trace, object) } diff --git a/src/policy/markcompactspace.rs b/src/policy/markcompactspace.rs index a63c2fa440..bd3189d11f 100644 --- a/src/policy/markcompactspace.rs +++ b/src/policy/markcompactspace.rs @@ -101,8 +101,16 @@ impl Space for MarkCompactSpace { } } -impl crate::plan::transitive_closure::PolicyTraceObject for MarkCompactSpace { - fn trace_object(&self, trace: &mut T, object: ObjectReference, _copy: Option, _worker: &mut GCWorker) -> ObjectReference { +impl crate::plan::transitive_closure::PolicyTraceObject + for MarkCompactSpace +{ + fn trace_object( + &self, + trace: &mut T, + object: ObjectReference, + _copy: Option, + _worker: &mut GCWorker, + ) -> ObjectReference { if KIND == TRACE_KIND_MARK { self.trace_mark_object(trace, object) } else if KIND == TRACE_KIND_FORWARD { From 06b2f0c08101717d7312d94fd7feab67546443cc Mon Sep 17 00:00:00 2001 From: Yi Lin Date: Wed, 27 Apr 2022 10:20:35 +1000 Subject: [PATCH 10/22] Some cleanup --- macros/trace_object/src/lib.rs | 16 +- src/plan/generational/copying/gc_work.rs | 4 +- src/plan/generational/immix/gc_work.rs | 28 +--- src/plan/immix/gc_work.rs | 29 +--- src/plan/markcompact/gc_work.rs | 26 +-- src/plan/marksweep/gc_work.rs | 2 +- src/plan/pageprotect/gc_work.rs | 4 +- src/plan/semispace/gc_work.rs | 4 +- src/plan/transitive_closure.rs | 108 ++++++++++-- src/policy/copyspace.rs | 3 +- src/policy/gc_work.rs | 204 ++--------------------- src/policy/immix/immixspace.rs | 34 +--- src/policy/immortalspace.rs | 3 +- src/policy/largeobjectspace.rs | 3 +- src/policy/lockfreeimmortalspace.rs | 3 +- src/policy/mallocspace/global.rs | 3 +- src/policy/markcompactspace.rs | 30 +--- 17 files changed, 138 insertions(+), 366 deletions(-) diff --git a/macros/trace_object/src/lib.rs b/macros/trace_object/src/lib.rs index 11fc39b421..b50e7982bc 100644 --- a/macros/trace_object/src/lib.rs +++ b/macros/trace_object/src/lib.rs @@ -13,6 +13,14 @@ use syn::__private::TokenStream2; mod util; +/// Generally a plan needs to add these attributes in order for the macro to work: +/// * add `#[derive(PlanTraceObject)]` to the plan struct. +/// * add `#[trace]` to each space field the plan struct has. If the policy is a copying policy, +/// it needs to further specify the copy semantic (`#[trace(CopySemantics::X)]`) +/// * add `#[fallback_trace]` to the parent plan if the plan is composed with other plans (or parent plans). +/// For example, `GenImmix` is composed with `Gen`, `Gen` is composed with `CommonPlan`, `CommonPlan` is composed +/// with `BasePlan`. +/// * (optional) add `#[main_policy]` to _one_ space field in the plan. #[proc_macro_error] #[proc_macro_derive(PlanTraceObject, attributes(trace, main_policy, copy, fallback_trace))] pub fn derive_plan_trace_object(input: TokenStream) -> TokenStream { @@ -101,7 +109,7 @@ fn generate_trace_object<'a>( quote! { fn trace_object(&self, __mmtk_trace: &mut T, __mmtk_objref: crate::util::ObjectReference, __mmtk_worker: &mut crate::scheduler::GCWorker) -> crate::util::ObjectReference { use crate::policy::space::Space; - use crate::plan::transitive_closure::PolicyTraceObject; + use crate::policy::gc_work::PolicyTraceObject; use crate::plan::transitive_closure::PlanTraceObject; #(#space_field_handler)* #parent_field_delegator @@ -119,7 +127,7 @@ fn generate_create_scan_work<'a>( quote! { fn create_scan_work>(&'static self, nodes: Vec) -> Box> { - use crate::plan::transitive_closure::PolicyTraceObject; + use crate::policy::gc_work::PolicyTraceObject; <#f_ty as PolicyTraceObject #ty_generics>::create_scan_work::(&self.#f_ident, nodes) } } @@ -146,7 +154,7 @@ fn generate_may_move_objects<'a>( let ref p_ty = p.ty; quote! { fn may_move_objects() -> bool { - use crate::plan::transitive_closure::PolicyTraceObject; + use crate::policy::gc_work::PolicyTraceObject; use crate::plan::transitive_closure::PlanTraceObject; <#f_ty as PolicyTraceObject #ty_generics>::may_move_objects::() || <#p_ty as PlanTraceObject #ty_generics>::may_move_objects::() } @@ -154,7 +162,7 @@ fn generate_may_move_objects<'a>( } else { quote! { fn may_move_objects() -> bool { - use crate::plan::transitive_closure::PolicyTraceObject; + use crate::policy::gc_work::PolicyTraceObject; <#f_ty as PolicyTraceObject #ty_generics>::may_move_objects::() } } diff --git a/src/plan/generational/copying/gc_work.rs b/src/plan/generational/copying/gc_work.rs index 961e08b8c2..ea7ff14c52 100644 --- a/src/plan/generational/copying/gc_work.rs +++ b/src/plan/generational/copying/gc_work.rs @@ -1,8 +1,8 @@ use super::global::GenCopy; -use crate::plan::generational::gc_work::GenNurseryProcessEdges; use crate::vm::*; +use crate::plan::generational::gc_work::GenNurseryProcessEdges; -use crate::policy::gc_work::PlanProcessEdges; +use crate::plan::transitive_closure::PlanProcessEdges; use crate::policy::gc_work::DEFAULT_TRACE; pub struct GenCopyNurseryGCWorkContext(std::marker::PhantomData); diff --git a/src/plan/generational/immix/gc_work.rs b/src/plan/generational/immix/gc_work.rs index 2d0a6b7a02..b929b2a8ad 100644 --- a/src/plan/generational/immix/gc_work.rs +++ b/src/plan/generational/immix/gc_work.rs @@ -1,34 +1,8 @@ use super::global::GenImmix; use crate::plan::generational::gc_work::GenNurseryProcessEdges; -use crate::plan::TransitiveClosure; use crate::policy::gc_work::TraceKind; -use crate::policy::immix::ImmixSpace; -use crate::scheduler::GCWorker; -use crate::util::copy::CopySemantics; -use crate::util::ObjectReference; use crate::vm::VMBinding; - -impl crate::policy::gc_work::UsePolicyProcessEdges for GenImmix { - type TargetPolicy = ImmixSpace; - const COPY: CopySemantics = CopySemantics::Mature; - - #[inline(always)] - fn get_target_space(&self) -> &Self::TargetPolicy { - &self.immix - } - - #[inline(always)] - fn fallback_trace( - &self, - trace: &mut T, - object: ObjectReference, - worker: &mut GCWorker, - ) -> ObjectReference { - self.gen.trace_object_full_heap::(trace, object, worker) - } -} - -use crate::policy::gc_work::PlanProcessEdges; +use crate::plan::transitive_closure::PlanProcessEdges; pub struct GenImmixNurseryGCWorkContext(std::marker::PhantomData); impl crate::scheduler::GCWorkContext for GenImmixNurseryGCWorkContext { diff --git a/src/plan/immix/gc_work.rs b/src/plan/immix/gc_work.rs index 16251afa7e..ea43bb27c1 100644 --- a/src/plan/immix/gc_work.rs +++ b/src/plan/immix/gc_work.rs @@ -1,34 +1,7 @@ use super::global::Immix; -use crate::plan::TransitiveClosure; -// use crate::policy::gc_work::{PolicyProcessEdges, TraceKind}; use crate::policy::gc_work::TraceKind; -use crate::policy::immix::ImmixSpace; -use crate::scheduler::GCWorker; -use crate::util::copy::CopySemantics; -use crate::util::ObjectReference; use crate::vm::VMBinding; - -use crate::policy::gc_work::PlanProcessEdges; - -impl crate::policy::gc_work::UsePolicyProcessEdges for Immix { - type TargetPolicy = ImmixSpace; - const COPY: CopySemantics = CopySemantics::DefaultCopy; - - #[inline(always)] - fn get_target_space(&self) -> &Self::TargetPolicy { - &self.immix_space - } - - #[inline(always)] - fn fallback_trace( - &self, - trace: &mut T, - object: ObjectReference, - _worker: &mut GCWorker, - ) -> ObjectReference { - self.common.trace_object::(trace, object) - } -} +use crate::plan::transitive_closure::PlanProcessEdges; pub(super) struct ImmixGCWorkContext( std::marker::PhantomData, diff --git a/src/plan/markcompact/gc_work.rs b/src/plan/markcompact/gc_work.rs index 7a81f38661..40578c612d 100644 --- a/src/plan/markcompact/gc_work.rs +++ b/src/plan/markcompact/gc_work.rs @@ -1,13 +1,11 @@ use super::global::MarkCompact; -use crate::plan::TransitiveClosure; -use crate::policy::gc_work::PlanProcessEdges; +use crate::plan::transitive_closure::PlanProcessEdges; use crate::policy::markcompactspace::MarkCompactSpace; use crate::policy::markcompactspace::{TRACE_KIND_FORWARD, TRACE_KIND_MARK}; use crate::scheduler::gc_work::*; use crate::scheduler::GCWork; use crate::scheduler::GCWorker; use crate::scheduler::WorkBucketStage; -use crate::util::ObjectReference; use crate::vm::ActivePlan; use crate::vm::Scanning; use crate::vm::VMBinding; @@ -89,28 +87,6 @@ pub type MarkingProcessEdges = PlanProcessEdges, TRACE_K /// Forwarding trace pub type ForwardingProcessEdges = PlanProcessEdges, TRACE_KIND_FORWARD>; -use crate::util::copy::CopySemantics; - -impl crate::policy::gc_work::UsePolicyProcessEdges for MarkCompact { - type TargetPolicy = MarkCompactSpace; - const COPY: CopySemantics = CopySemantics::DefaultCopy; - - #[inline(always)] - fn get_target_space(&self) -> &Self::TargetPolicy { - &self.mc_space - } - - #[inline(always)] - fn fallback_trace( - &self, - trace: &mut T, - object: ObjectReference, - _worker: &mut GCWorker, - ) -> ObjectReference { - self.common.trace_object::(trace, object) - } -} - pub struct MarkCompactGCWorkContext(std::marker::PhantomData); impl crate::scheduler::GCWorkContext for MarkCompactGCWorkContext { type VM = VM; diff --git a/src/plan/marksweep/gc_work.rs b/src/plan/marksweep/gc_work.rs index 248b547413..1c667ff967 100644 --- a/src/plan/marksweep/gc_work.rs +++ b/src/plan/marksweep/gc_work.rs @@ -69,7 +69,7 @@ impl GCWork for MSSweepChunks { } pub struct MSGCWorkContext(std::marker::PhantomData); -use crate::policy::gc_work::PlanProcessEdges; +use crate::plan::transitive_closure::PlanProcessEdges; use crate::policy::gc_work::DEFAULT_TRACE; impl crate::scheduler::GCWorkContext for MSGCWorkContext { diff --git a/src/plan/pageprotect/gc_work.rs b/src/plan/pageprotect/gc_work.rs index 8f61a21f8e..1fe2ef4304 100644 --- a/src/plan/pageprotect/gc_work.rs +++ b/src/plan/pageprotect/gc_work.rs @@ -1,7 +1,7 @@ use super::global::PageProtect; -use crate::policy::gc_work::PlanProcessEdges; -use crate::policy::gc_work::DEFAULT_TRACE; use crate::vm::VMBinding; +use crate::plan::transitive_closure::PlanProcessEdges; +use crate::policy::gc_work::DEFAULT_TRACE; pub struct PPGCWorkContext(std::marker::PhantomData); impl crate::scheduler::GCWorkContext for PPGCWorkContext { diff --git a/src/plan/semispace/gc_work.rs b/src/plan/semispace/gc_work.rs index 3171c10689..1ddec3dd93 100644 --- a/src/plan/semispace/gc_work.rs +++ b/src/plan/semispace/gc_work.rs @@ -1,7 +1,7 @@ use super::global::SemiSpace; -use crate::policy::gc_work::PlanProcessEdges; -use crate::policy::gc_work::DEFAULT_TRACE; use crate::vm::VMBinding; +use crate::plan::transitive_closure::PlanProcessEdges; +use crate::policy::gc_work::DEFAULT_TRACE; pub struct SSGCWorkContext(std::marker::PhantomData); impl crate::scheduler::GCWorkContext for SSGCWorkContext { diff --git a/src/plan/transitive_closure.rs b/src/plan/transitive_closure.rs index 48461370d2..130131c7da 100644 --- a/src/plan/transitive_closure.rs +++ b/src/plan/transitive_closure.rs @@ -83,39 +83,113 @@ impl<'a, E: ProcessEdgesWork> Drop for ObjectsClosure<'a, E> { use crate::policy::gc_work::TraceKind; use crate::scheduler::GCWork; -use crate::util::copy::CopySemantics; use crate::vm::VMBinding; +/// A plan that uses `PlanProcessEdges` needs to provide an implementation for this trait. +/// Generally a plan does not need to manually implement this trait. Instead, we provide +/// a procedural macro that helps generate an implementation. Please check `macros/trace_object`. +/// +/// A plan could also manually implement this trait. For the sake of performance, the implementation +/// of this trait should mark methods as `[inline(always)]`. pub trait PlanTraceObject { + /// Trace objects in the plan. Generally one needs to figure out + /// which space an object resides in, and invokes the corresponding policy + /// trace object method. fn trace_object( &self, trace: &mut T, object: ObjectReference, worker: &mut GCWorker, ) -> ObjectReference; + + /// Create a scan objects work packet for the plan. Usually [`ScanObjects`](scheduler/gc_work/ScanObjects) + /// is used. If a plan or any policy in the plan uses a specific scan work packet, the work packet is required + /// to handle objects that is in any space in the plan. fn create_scan_work>( &'static self, nodes: Vec, ) -> Box>; + + /// Whether objects in this plan may move. If any of the spaces used by the plan may move objects, this should + /// return true. fn may_move_objects() -> bool; } -pub trait PolicyTraceObject { - fn trace_object( - &self, - trace: &mut T, - object: ObjectReference, - copy: Option, - worker: &mut GCWorker, - ) -> ObjectReference; +use crate::plan::Plan; +use crate::scheduler::gc_work::ProcessEdgesBase; +use std::ops::{Deref, DerefMut}; + +/// This provides an implementation of [`ProcessEdgesWork`](scheduler/gc_work/ProcessEdgesWork). A plan that implements +/// `PlanTraceObject` can use this work packet for tracing objects. +pub struct PlanProcessEdges< + VM: VMBinding, + P: 'static + Plan + PlanTraceObject + Sync, + const KIND: TraceKind, +> { + plan: &'static P, + base: ProcessEdgesBase, +} + +impl< + VM: VMBinding, + P: 'static + PlanTraceObject + Plan + Sync, + const KIND: TraceKind, + > ProcessEdgesWork for PlanProcessEdges +{ + type VM = VM; + + fn new(edges: Vec

, roots: bool, mmtk: &'static MMTK) -> Self { + let base = ProcessEdgesBase::new(edges, roots, mmtk); + let plan = base.plan().downcast_ref::

().unwrap(); + Self { plan, base } + } + #[inline(always)] - fn create_scan_work>( - &'static self, - nodes: Vec, - ) -> Box> { - Box::new(crate::scheduler::gc_work::ScanObjects::::new( - nodes, false, - )) + fn create_scan_work(&self, nodes: Vec) -> Box> { + self.plan.create_scan_work::(nodes) + } + + #[inline(always)] + fn trace_object(&mut self, object: ObjectReference) -> ObjectReference { + if object.is_null() { + return object; + } + self.plan + .trace_object::(self, object, self.worker()) + } + + #[inline] + fn process_edge(&mut self, slot: Address) { + let object = unsafe { slot.load::() }; + let new_object = self.trace_object(object); + if P::may_move_objects::() { + unsafe { slot.store(new_object) }; + } + } +} + +// Impl Deref/DerefMut to ProcessEdgesBase for PlanProcessEdges +impl< + VM: VMBinding, + P: 'static + PlanTraceObject + Plan + Sync, + const KIND: TraceKind, + > Deref for PlanProcessEdges +{ + type Target = ProcessEdgesBase; + #[inline] + fn deref(&self) -> &Self::Target { + &self.base + } +} + +impl< + VM: VMBinding, + P: 'static + PlanTraceObject + Plan + Sync, + const KIND: TraceKind, + > DerefMut for PlanProcessEdges +{ + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.base } - fn may_move_objects() -> bool; } diff --git a/src/policy/copyspace.rs b/src/policy/copyspace.rs index 65d449f663..006aac523b 100644 --- a/src/policy/copyspace.rs +++ b/src/policy/copyspace.rs @@ -108,7 +108,8 @@ impl Space for CopySpace { } } -impl crate::plan::transitive_closure::PolicyTraceObject for CopySpace { +impl crate::policy::gc_work::PolicyTraceObject for CopySpace { + #[inline(always)] fn trace_object( &self, trace: &mut T, diff --git a/src/policy/gc_work.rs b/src/policy/gc_work.rs index 1b1f820a8b..65e0eb5466 100644 --- a/src/policy/gc_work.rs +++ b/src/policy/gc_work.rs @@ -1,59 +1,25 @@ -use crate::plan::Plan; -use crate::plan::TransitiveClosure; -use crate::policy::space::Space; -use crate::scheduler::gc_work::*; -use crate::scheduler::GCWork; -use crate::scheduler::GCWorker; -use crate::util::copy::CopySemantics; -use crate::util::Address; -use crate::util::ObjectReference; -use crate::vm::VMBinding; -use crate::MMTK; - -use std::ops::{Deref, DerefMut}; - /// Used to identify the trace if a policy has different kinds of traces. For example, defrag vs fast trace for Immix, /// mark vs forward trace for mark compact. pub(crate) type TraceKind = u8; pub const DEFAULT_TRACE: u8 = u8::MAX; -/// A plan that uses `PolicyProcessEdges` needs to provide an implementation for this trait. -/// The plan needs to specify a target space (which needs to implement `SupportPolicyProcessEdges`). -/// For objects in the target space, `trace_object_with_tracekind()` is called to trace the object. -/// Otherwise, `fallback_trace()` is used. -/// For the sake of performance, the implementation of this trait should mark methods as `[inline(always)]`. -pub trait UsePolicyProcessEdges: Plan + Send { - type TargetPolicy: SupportPolicyProcessEdges; - /// The copy semantics for objects in the space. - const COPY: CopySemantics; - - /// Returns a reference to the target space. - fn get_target_space(&self) -> &Self::TargetPolicy; - - /// How to trace objects if the object is not in the default space. - fn fallback_trace( - &self, - trace: &mut T, - object: ObjectReference, - worker: &mut GCWorker, - ) -> ObjectReference; -} +use crate::plan::TransitiveClosure; +use crate::scheduler::GCWork; +use crate::util::copy::CopySemantics; +use crate::vm::VMBinding; +use crate::util::ObjectReference; +use crate::scheduler::gc_work::ProcessEdgesWork; +use crate::scheduler::GCWorker; -/// A policy that allows using `PolicyProcessEdges` needs to provide an implementation for this trait. -/// For the sake of performance, the implementation of this trait should mark methods as `[inline(always)]`. -pub trait SupportPolicyProcessEdges: Space { - /// Trace an object in the policy. - fn trace_object_with_tracekind( +pub trait PolicyTraceObject { + fn trace_object( &self, trace: &mut T, object: ObjectReference, - copy: CopySemantics, + copy: Option, worker: &mut GCWorker, ) -> ObjectReference; - - /// Create scan work for the policy. By default, we use [`ScanObjects`](crate::scheduler::gc_work::ScanObjects). - /// If a policy has its own scan object work packet, they can override this method. #[inline(always)] fn create_scan_work>( &'static self, @@ -63,155 +29,5 @@ pub trait SupportPolicyProcessEdges: Space { nodes, false, )) } - - /// Does this trace move object? fn may_move_objects() -> bool; } - -/// This provides an alternative to [`SFTProcessEdges`](crate::scheduler::gc_work::SFTProcessEdges). For policies that cannot -/// use `SFTProcessEdges`, they could try use this type. One major difference is that `PolicyProcessEdges` allows different -/// traces for a policy. -/// A plan that uses this needs to implement the `UsePolicyProcessEdges` trait, and the policy needs to implement `SupportPolicyProcessEdges`. -// pub struct PolicyProcessEdges, const KIND: TraceKind> { -// plan: &'static P, -// base: ProcessEdgesBase, -// } - -// impl, const KIND: TraceKind> ProcessEdgesWork -// for PolicyProcessEdges -// { -// type VM = VM; - -// fn new(edges: Vec

, roots: bool, mmtk: &'static MMTK) -> Self { -// let base = ProcessEdgesBase::new(edges, roots, mmtk); -// let plan = base.plan().downcast_ref::

().unwrap(); -// Self { plan, base } -// } - -// #[inline(always)] -// fn create_scan_work(&self, nodes: Vec) -> Box> { -// self.plan.get_target_space().create_scan_work::(nodes) -// } - -// /// Trace object if it is in the target space. Otherwise call fallback_trace(). -// #[inline(always)] -// fn trace_object(&mut self, object: ObjectReference) -> ObjectReference { -// if object.is_null() { -// return object; -// } -// if self.plan.get_target_space().in_space(object) { -// self.plan -// .get_target_space() -// .trace_object_with_tracekind::(self, object, P::COPY, self.worker()) -// } else { -// self.plan -// .fallback_trace::(self, object, self.worker()) -// } -// } - -// #[inline] -// fn process_edge(&mut self, slot: Address) { -// let object = unsafe { slot.load::() }; -// let new_object = self.trace_object(object); -// if P::TargetPolicy::may_move_objects::() { -// unsafe { slot.store(new_object) }; -// } -// } -// } - -// // Impl Deref/DerefMut to ProcessEdgesBase for PolicyProcessEdges - -// impl, const KIND: TraceKind> Deref -// for PolicyProcessEdges -// { -// type Target = ProcessEdgesBase; -// #[inline] -// fn deref(&self) -> &Self::Target { -// &self.base -// } -// } - -// impl, const KIND: TraceKind> DerefMut -// for PolicyProcessEdges -// { -// #[inline] -// fn deref_mut(&mut self) -> &mut Self::Target { -// &mut self.base -// } -// } -use crate::plan::transitive_closure::PlanTraceObject; - -pub struct PlanProcessEdges< - VM: VMBinding, - P: 'static + Plan + PlanTraceObject + Sync, - const KIND: TraceKind, -> { - plan: &'static P, - base: ProcessEdgesBase, -} - -impl< - VM: VMBinding, - P: 'static + PlanTraceObject + Plan + Sync, - const KIND: TraceKind, - > ProcessEdgesWork for PlanProcessEdges -{ - type VM = VM; - - fn new(edges: Vec

, roots: bool, mmtk: &'static MMTK) -> Self { - let base = ProcessEdgesBase::new(edges, roots, mmtk); - let plan = base.plan().downcast_ref::

().unwrap(); - Self { plan, base } - } - - #[inline(always)] - fn create_scan_work(&self, nodes: Vec) -> Box> { - self.plan.create_scan_work::(nodes) - } - - /// Trace object if it is in the target space. Otherwise call fallback_trace(). - #[inline(always)] - fn trace_object(&mut self, object: ObjectReference) -> ObjectReference { - if object.is_null() { - return object; - } - self.plan - .trace_object::(self, object, self.worker()) - } - - #[inline] - fn process_edge(&mut self, slot: Address) { - let object = unsafe { slot.load::() }; - let new_object = self.trace_object(object); - if P::may_move_objects::() { - unsafe { slot.store(new_object) }; - } - } -} - -// Impl Deref/DerefMut to ProcessEdgesBase for PolicyProcessEdges - -impl< - VM: VMBinding, - P: 'static + PlanTraceObject + Plan + Sync, - const KIND: TraceKind, - > Deref for PlanProcessEdges -{ - type Target = ProcessEdgesBase; - #[inline] - fn deref(&self) -> &Self::Target { - &self.base - } -} - -impl< - VM: VMBinding, - P: 'static + PlanTraceObject + Plan + Sync, - const KIND: TraceKind, - > DerefMut for PlanProcessEdges -{ - #[inline] - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.base - } -} diff --git a/src/policy/immix/immixspace.rs b/src/policy/immix/immixspace.rs index 1645102181..3522e00614 100644 --- a/src/policy/immix/immixspace.rs +++ b/src/policy/immix/immixspace.rs @@ -117,7 +117,7 @@ impl Space for ImmixSpace { } } -impl crate::plan::transitive_closure::PolicyTraceObject for ImmixSpace { +impl crate::policy::gc_work::PolicyTraceObject for ImmixSpace { #[inline(always)] fn trace_object( &self, @@ -568,38 +568,6 @@ impl ImmixSpace { } } -impl crate::policy::gc_work::SupportPolicyProcessEdges for ImmixSpace { - #[inline(always)] - fn trace_object_with_tracekind( - &self, - trace: &mut T, - object: ObjectReference, - copy: CopySemantics, - worker: &mut GCWorker, - ) -> ObjectReference { - if KIND == TRACE_KIND_FAST { - self.fast_trace_object(trace, object) - } else { - self.trace_object(trace, object, copy, worker) - } - } - - #[inline(always)] - fn create_scan_work>( - &'static self, - nodes: Vec, - ) -> Box> { - Box::new(crate::policy::immix::ScanObjectsAndMarkLines::::new( - nodes, false, self, - )) - } - - #[inline(always)] - fn may_move_objects() -> bool { - KIND == TRACE_KIND_DEFRAG - } -} - /// A work packet to prepare each block for GC. /// Performs the action on a range of chunks. pub struct PrepareBlockState { diff --git a/src/policy/immortalspace.rs b/src/policy/immortalspace.rs index efeec40851..214358b597 100644 --- a/src/policy/immortalspace.rs +++ b/src/policy/immortalspace.rs @@ -114,7 +114,8 @@ impl Space for ImmortalSpace { use crate::scheduler::GCWorker; use crate::util::copy::CopySemantics; -impl crate::plan::transitive_closure::PolicyTraceObject for ImmortalSpace { +impl crate::policy::gc_work::PolicyTraceObject for ImmortalSpace { + #[inline(always)] fn trace_object( &self, trace: &mut T, diff --git a/src/policy/largeobjectspace.rs b/src/policy/largeobjectspace.rs index 50889e332b..8439615376 100644 --- a/src/policy/largeobjectspace.rs +++ b/src/policy/largeobjectspace.rs @@ -118,9 +118,10 @@ impl Space for LargeObjectSpace { use crate::scheduler::GCWorker; use crate::util::copy::CopySemantics; -impl crate::plan::transitive_closure::PolicyTraceObject +impl crate::policy::gc_work::PolicyTraceObject for LargeObjectSpace { + #[inline(always)] fn trace_object( &self, trace: &mut T, diff --git a/src/policy/lockfreeimmortalspace.rs b/src/policy/lockfreeimmortalspace.rs index 03a2d299c2..52c713eaaf 100644 --- a/src/policy/lockfreeimmortalspace.rs +++ b/src/policy/lockfreeimmortalspace.rs @@ -153,9 +153,10 @@ use crate::plan::TransitiveClosure; use crate::scheduler::GCWorker; use crate::util::copy::CopySemantics; -impl crate::plan::transitive_closure::PolicyTraceObject +impl crate::policy::gc_work::PolicyTraceObject for LockFreeImmortalSpace { + #[inline(always)] fn trace_object( &self, _trace: &mut T, diff --git a/src/policy/mallocspace/global.rs b/src/policy/mallocspace/global.rs index ef6c09c009..e36318a46a 100644 --- a/src/policy/mallocspace/global.rs +++ b/src/policy/mallocspace/global.rs @@ -192,7 +192,8 @@ impl Space for MallocSpace { use crate::scheduler::GCWorker; use crate::util::copy::CopySemantics; -impl crate::plan::transitive_closure::PolicyTraceObject for MallocSpace { +impl crate::policy::gc_work::PolicyTraceObject for MallocSpace { + #[inline(always)] fn trace_object( &self, trace: &mut T, diff --git a/src/policy/markcompactspace.rs b/src/policy/markcompactspace.rs index bd3189d11f..451deb98d9 100644 --- a/src/policy/markcompactspace.rs +++ b/src/policy/markcompactspace.rs @@ -10,6 +10,8 @@ use crate::util::metadata::side_metadata::{SideMetadataContext, SideMetadataSpec use crate::util::metadata::{compare_exchange_metadata, extract_side_metadata}; use crate::util::{alloc_bit, Address, ObjectReference}; use crate::{vm::*, TransitiveClosure}; +use crate::util::copy::CopySemantics; +use crate::scheduler::GCWorker; use atomic::Ordering; pub(crate) const TRACE_KIND_MARK: TraceKind = 0; @@ -101,9 +103,10 @@ impl Space for MarkCompactSpace { } } -impl crate::plan::transitive_closure::PolicyTraceObject +impl crate::policy::gc_work::PolicyTraceObject for MarkCompactSpace { + #[inline(always)] fn trace_object( &self, trace: &mut T, @@ -401,31 +404,6 @@ impl MarkCompactSpace { } } -use crate::scheduler::GCWorker; -use crate::util::copy::CopySemantics; - -impl crate::policy::gc_work::SupportPolicyProcessEdges for MarkCompactSpace { - #[inline(always)] - fn trace_object_with_tracekind( - &self, - trace: &mut T, - object: ObjectReference, - _copy: CopySemantics, - _worker: &mut GCWorker, - ) -> ObjectReference { - if KIND == TRACE_KIND_MARK { - self.trace_mark_object::(trace, object) - } else { - self.trace_forward_object::(trace, object) - } - } - - #[inline(always)] - fn may_move_objects() -> bool { - KIND == TRACE_KIND_FORWARD - } -} - struct MarkCompactObjectSize(std::marker::PhantomData); impl crate::util::linear_scan::LinearScanObjectSize for MarkCompactObjectSize { #[inline(always)] From 3d00882a8887bd493c4015e3f5fa61fb7b939c35 Mon Sep 17 00:00:00 2001 From: Yi Lin Date: Wed, 27 Apr 2022 11:30:10 +1000 Subject: [PATCH 11/22] Generate may_move_objects() based on all spaces --- macros/trace_object/src/lib.rs | 54 ++++++++++++------------- src/plan/generational/copying/global.rs | 1 - src/plan/generational/global.rs | 1 - src/plan/generational/immix/global.rs | 2 +- src/plan/immix/global.rs | 2 +- src/plan/markcompact/global.rs | 1 - src/plan/marksweep/global.rs | 1 - src/plan/pageprotect/global.rs | 1 - src/plan/semispace/global.rs | 1 - 9 files changed, 28 insertions(+), 36 deletions(-) diff --git a/macros/trace_object/src/lib.rs b/macros/trace_object/src/lib.rs index b50e7982bc..ccae2192e9 100644 --- a/macros/trace_object/src/lib.rs +++ b/macros/trace_object/src/lib.rs @@ -20,9 +20,9 @@ mod util; /// * add `#[fallback_trace]` to the parent plan if the plan is composed with other plans (or parent plans). /// For example, `GenImmix` is composed with `Gen`, `Gen` is composed with `CommonPlan`, `CommonPlan` is composed /// with `BasePlan`. -/// * (optional) add `#[main_policy]` to _one_ space field in the plan. +/// * (optional) add `#[scan_work]` to _one_ space field in the plan. #[proc_macro_error] -#[proc_macro_derive(PlanTraceObject, attributes(trace, main_policy, copy, fallback_trace))] +#[proc_macro_derive(PlanTraceObject, attributes(trace, scan_work, copy, fallback_trace))] pub fn derive_plan_trace_object(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); let ident = input.ident; @@ -33,12 +33,12 @@ pub fn derive_plan_trace_object(input: TokenStream) -> TokenStream { .. }) = input.data { let spaces = util::get_fields_with_attribute(fields, "trace"); - let main_policy = util::get_unique_field_with_attribute(fields, "main_policy"); + let scan_work = util::get_unique_field_with_attribute(fields, "scan_work"); let fallback = util::get_unique_field_with_attribute(fields, "fallback_trace"); let trace_object_function = generate_trace_object(&spaces, &fallback, &ty_generics); - let create_scan_work_function = generate_create_scan_work(&main_policy, &ty_generics); - let may_move_objects_function = generate_may_move_objects(&main_policy, &fallback, &ty_generics); + let create_scan_work_function = generate_create_scan_work(&scan_work, &ty_generics); + let may_move_objects_function = generate_may_move_objects(&spaces, &fallback, &ty_generics); quote!{ impl #impl_generics crate::plan::transitive_closure::PlanTraceObject #ty_generics for #ident #ty_generics #where_clause { #[inline(always)] @@ -143,36 +143,34 @@ fn generate_create_scan_work<'a>( // The generated function needs to be inlined and constant folded. Otherwise, there will be a huge // performance penalty. fn generate_may_move_objects<'a>( - main_policy_field: &Option<&'a Field>, + space_fields: &[&'a Field], parent_field: &Option<&'a Field>, ty_generics: &TypeGenerics, ) -> TokenStream2 { - if let Some(f) = main_policy_field { + let space_handlers = space_fields.iter().map(|f| { let ref f_ty = f.ty; - if let Some(p) = parent_field { - let ref p_ty = p.ty; - quote! { - fn may_move_objects() -> bool { - use crate::policy::gc_work::PolicyTraceObject; - use crate::plan::transitive_closure::PlanTraceObject; - <#f_ty as PolicyTraceObject #ty_generics>::may_move_objects::() || <#p_ty as PlanTraceObject #ty_generics>::may_move_objects::() - } - } - } else { - quote! { - fn may_move_objects() -> bool { - use crate::policy::gc_work::PolicyTraceObject; - <#f_ty as PolicyTraceObject #ty_generics>::may_move_objects::() - } - } + quote! { + || <#f_ty as PolicyTraceObject #ty_generics>::may_move_objects::() } - } else { - // If the plan has no main policy, by default we assume it does not move objects. + }); + + let parent_handler = if let Some(p) = parent_field { + let ref p_ty = p.ty; + quote! { - fn may_move_objects() -> bool { - false - } + || <#p_ty as PlanTraceObject #ty_generics>::may_move_objects::() + } + } else { + TokenStream2::new() + }; + + quote! { + fn may_move_objects() -> bool { + use crate::policy::gc_work::PolicyTraceObject; + use crate::plan::transitive_closure::PlanTraceObject; + + false #(#space_handlers)* #parent_handler } } } diff --git a/src/plan/generational/copying/global.rs b/src/plan/generational/copying/global.rs index e17091461d..b0ba77ed52 100644 --- a/src/plan/generational/copying/global.rs +++ b/src/plan/generational/copying/global.rs @@ -33,7 +33,6 @@ pub struct GenCopy { #[fallback_trace] pub gen: Gen, pub hi: AtomicBool, - #[main_policy] #[trace(CopySemantics::Mature)] pub copyspace0: CopySpace, #[trace(CopySemantics::Mature)] diff --git a/src/plan/generational/global.rs b/src/plan/generational/global.rs index 82422ef2f3..b741583d5c 100644 --- a/src/plan/generational/global.rs +++ b/src/plan/generational/global.rs @@ -28,7 +28,6 @@ use macro_trace_object::PlanTraceObject; #[derive(PlanTraceObject)] pub struct Gen { /// The nursery space. - #[main_policy] #[trace(CopySemantics::PromoteToMature)] pub nursery: CopySpace, /// The common plan. diff --git a/src/plan/generational/immix/global.rs b/src/plan/generational/immix/global.rs index df5360e46a..9c2eab0b37 100644 --- a/src/plan/generational/immix/global.rs +++ b/src/plan/generational/immix/global.rs @@ -38,7 +38,7 @@ pub struct GenImmix { #[fallback_trace] pub gen: Gen, /// An immix space as the mature space. - #[main_policy] + #[scan_work] #[trace(CopySemantics::Mature)] pub immix: ImmixSpace, /// Whether the last GC was a defrag GC for the immix space. diff --git a/src/plan/immix/global.rs b/src/plan/immix/global.rs index 752226af87..825eed2e0d 100644 --- a/src/plan/immix/global.rs +++ b/src/plan/immix/global.rs @@ -30,7 +30,7 @@ use macro_trace_object::PlanTraceObject; #[derive(PlanTraceObject)] pub struct Immix { - #[main_policy] + #[scan_work] #[trace(CopySemantics::DefaultCopy)] pub immix_space: ImmixSpace, #[fallback_trace] diff --git a/src/plan/markcompact/global.rs b/src/plan/markcompact/global.rs index 8a6f773812..a2017e1d38 100644 --- a/src/plan/markcompact/global.rs +++ b/src/plan/markcompact/global.rs @@ -35,7 +35,6 @@ use macro_trace_object::PlanTraceObject; #[derive(PlanTraceObject)] pub struct MarkCompact { - #[main_policy] #[trace(CopySemantics::DefaultCopy)] pub mc_space: MarkCompactSpace, #[fallback_trace] diff --git a/src/plan/marksweep/global.rs b/src/plan/marksweep/global.rs index 985ca680af..a3229dddd9 100644 --- a/src/plan/marksweep/global.rs +++ b/src/plan/marksweep/global.rs @@ -31,7 +31,6 @@ use macro_trace_object::PlanTraceObject; pub struct MarkSweep { #[fallback_trace] common: CommonPlan, - #[main_policy] #[trace] ms: MallocSpace, } diff --git a/src/plan/pageprotect/global.rs b/src/plan/pageprotect/global.rs index 0c8dc48ec1..13f24075d6 100644 --- a/src/plan/pageprotect/global.rs +++ b/src/plan/pageprotect/global.rs @@ -26,7 +26,6 @@ use macro_trace_object::PlanTraceObject; #[derive(PlanTraceObject)] pub struct PageProtect { - #[main_policy] #[trace] pub space: LargeObjectSpace, pub common: CommonPlan, diff --git a/src/plan/semispace/global.rs b/src/plan/semispace/global.rs index 0df724f7c1..c537ad8061 100644 --- a/src/plan/semispace/global.rs +++ b/src/plan/semispace/global.rs @@ -29,7 +29,6 @@ use enum_map::EnumMap; #[derive(PlanTraceObject)] pub struct SemiSpace { pub hi: AtomicBool, - #[main_policy] #[trace(CopySemantics::DefaultCopy)] pub copyspace0: CopySpace, #[trace(CopySemantics::DefaultCopy)] From 6b6bb0d998b3d84e77b7aba12eec49f16bb204b9 Mon Sep 17 00:00:00 2001 From: Yi Lin Date: Wed, 27 Apr 2022 12:24:47 +1000 Subject: [PATCH 12/22] Cleanup --- macros/trace_object/src/lib.rs | 30 ++++++++++++++++++++---- src/plan/generational/copying/gc_work.rs | 2 +- src/plan/generational/immix/gc_work.rs | 2 +- src/plan/immix/gc_work.rs | 2 +- src/plan/pageprotect/gc_work.rs | 2 +- src/plan/semispace/gc_work.rs | 2 +- src/policy/gc_work.rs | 20 +++++++++++++--- src/policy/largeobjectspace.rs | 4 +--- src/policy/lockfreeimmortalspace.rs | 4 +--- src/policy/markcompactspace.rs | 8 +++---- 10 files changed, 52 insertions(+), 24 deletions(-) diff --git a/macros/trace_object/src/lib.rs b/macros/trace_object/src/lib.rs index ccae2192e9..80ba04ce6e 100644 --- a/macros/trace_object/src/lib.rs +++ b/macros/trace_object/src/lib.rs @@ -20,7 +20,8 @@ mod util; /// * add `#[fallback_trace]` to the parent plan if the plan is composed with other plans (or parent plans). /// For example, `GenImmix` is composed with `Gen`, `Gen` is composed with `CommonPlan`, `CommonPlan` is composed /// with `BasePlan`. -/// * (optional) add `#[scan_work]` to _one_ space field in the plan. +/// * (optional) add `#[scan_work]` to _one_ space field in the plan. `create_scan_work` will be generated based +/// on the space. #[proc_macro_error] #[proc_macro_derive(PlanTraceObject, attributes(trace, scan_work, copy, fallback_trace))] pub fn derive_plan_trace_object(input: TokenStream) -> TokenStream { @@ -37,7 +38,7 @@ pub fn derive_plan_trace_object(input: TokenStream) -> TokenStream { let fallback = util::get_unique_field_with_attribute(fields, "fallback_trace"); let trace_object_function = generate_trace_object(&spaces, &fallback, &ty_generics); - let create_scan_work_function = generate_create_scan_work(&scan_work, &ty_generics); + let create_scan_work_function = generate_create_scan_work(&spaces, &scan_work, &ty_generics); let may_move_objects_function = generate_may_move_objects(&spaces, &fallback, &ty_generics); quote!{ impl #impl_generics crate::plan::transitive_closure::PlanTraceObject #ty_generics for #ident #ty_generics #where_clause { @@ -66,6 +67,7 @@ fn generate_trace_object<'a>( parent_field: &Option<&'a Field>, ty_generics: &TypeGenerics, ) -> TokenStream2 { + // Generate a check with early return for each space let space_field_handler = space_fields.iter().map(|f| { let f_ident = f.ident.as_ref().unwrap(); let ref f_ty = f.ty; @@ -78,6 +80,7 @@ fn generate_trace_object<'a>( use syn::punctuated::Punctuated; let args = trace_attr.parse_args_with(Punctuated::::parse_terminated).unwrap(); + // CopySemantics::X is a path. if let Some(NestedMeta::Meta(syn::Meta::Path(p))) = args.first() { quote!{ Some(#p) } } else { @@ -94,6 +97,7 @@ fn generate_trace_object<'a>( } }); + // Generate a fallback to the parent plan let parent_field_delegator = if let Some(f) = parent_field { let f_ident = f.ident.as_ref().unwrap(); let ref f_ty = f.ty; @@ -118,10 +122,24 @@ fn generate_trace_object<'a>( } fn generate_create_scan_work<'a>( - main_policy_field: &Option<&'a Field>, + space_fields: &[&'a Field], + scan_work_field: &Option<&'a Field>, ty_generics: &TypeGenerics, ) -> TokenStream2 { - if let Some(f) = main_policy_field { + if let Some(f) = scan_work_field { + // If the plan names a field for scan work, use it + let f_ident = f.ident.as_ref().unwrap(); + let ref f_ty = f.ty; + + quote! { + fn create_scan_work>(&'static self, nodes: Vec) -> Box> { + use crate::policy::gc_work::PolicyTraceObject; + <#f_ty as PolicyTraceObject #ty_generics>::create_scan_work::(&self.#f_ident, nodes) + } + } + } else if !space_fields.is_empty() { + // If the plan does not name a specific field for scan work, just use the first space for scan work + let f = space_fields[0]; let f_ident = f.ident.as_ref().unwrap(); let ref f_ty = f.ty; @@ -132,9 +150,10 @@ fn generate_create_scan_work<'a>( } } } else { + // Otherwise, just panic quote! { fn create_scan_work>(&'static self, nodes: Vec) -> Box> { - unreachable!() + panic!("Unable to create a scan work packet for the plan (the plan does not name a #[scan_work] field, or a #[trace] field") } } } @@ -147,6 +166,7 @@ fn generate_may_move_objects<'a>( parent_field: &Option<&'a Field>, ty_generics: &TypeGenerics, ) -> TokenStream2 { + // If any space or the parent may move objects, the plan may move objects let space_handlers = space_fields.iter().map(|f| { let ref f_ty = f.ty; diff --git a/src/plan/generational/copying/gc_work.rs b/src/plan/generational/copying/gc_work.rs index ea7ff14c52..7b1bca1007 100644 --- a/src/plan/generational/copying/gc_work.rs +++ b/src/plan/generational/copying/gc_work.rs @@ -1,6 +1,6 @@ use super::global::GenCopy; -use crate::vm::*; use crate::plan::generational::gc_work::GenNurseryProcessEdges; +use crate::vm::*; use crate::plan::transitive_closure::PlanProcessEdges; use crate::policy::gc_work::DEFAULT_TRACE; diff --git a/src/plan/generational/immix/gc_work.rs b/src/plan/generational/immix/gc_work.rs index b929b2a8ad..801cc59e4a 100644 --- a/src/plan/generational/immix/gc_work.rs +++ b/src/plan/generational/immix/gc_work.rs @@ -1,8 +1,8 @@ use super::global::GenImmix; use crate::plan::generational::gc_work::GenNurseryProcessEdges; +use crate::plan::transitive_closure::PlanProcessEdges; use crate::policy::gc_work::TraceKind; use crate::vm::VMBinding; -use crate::plan::transitive_closure::PlanProcessEdges; pub struct GenImmixNurseryGCWorkContext(std::marker::PhantomData); impl crate::scheduler::GCWorkContext for GenImmixNurseryGCWorkContext { diff --git a/src/plan/immix/gc_work.rs b/src/plan/immix/gc_work.rs index ea43bb27c1..dfbdcc4fd8 100644 --- a/src/plan/immix/gc_work.rs +++ b/src/plan/immix/gc_work.rs @@ -1,7 +1,7 @@ use super::global::Immix; +use crate::plan::transitive_closure::PlanProcessEdges; use crate::policy::gc_work::TraceKind; use crate::vm::VMBinding; -use crate::plan::transitive_closure::PlanProcessEdges; pub(super) struct ImmixGCWorkContext( std::marker::PhantomData, diff --git a/src/plan/pageprotect/gc_work.rs b/src/plan/pageprotect/gc_work.rs index 1fe2ef4304..46a507795d 100644 --- a/src/plan/pageprotect/gc_work.rs +++ b/src/plan/pageprotect/gc_work.rs @@ -1,7 +1,7 @@ use super::global::PageProtect; -use crate::vm::VMBinding; use crate::plan::transitive_closure::PlanProcessEdges; use crate::policy::gc_work::DEFAULT_TRACE; +use crate::vm::VMBinding; pub struct PPGCWorkContext(std::marker::PhantomData); impl crate::scheduler::GCWorkContext for PPGCWorkContext { diff --git a/src/plan/semispace/gc_work.rs b/src/plan/semispace/gc_work.rs index 1ddec3dd93..5ee6b184c8 100644 --- a/src/plan/semispace/gc_work.rs +++ b/src/plan/semispace/gc_work.rs @@ -1,7 +1,7 @@ use super::global::SemiSpace; -use crate::vm::VMBinding; use crate::plan::transitive_closure::PlanProcessEdges; use crate::policy::gc_work::DEFAULT_TRACE; +use crate::vm::VMBinding; pub struct SSGCWorkContext(std::marker::PhantomData); impl crate::scheduler::GCWorkContext for SSGCWorkContext { diff --git a/src/policy/gc_work.rs b/src/policy/gc_work.rs index 65e0eb5466..b2cd0256d5 100644 --- a/src/policy/gc_work.rs +++ b/src/policy/gc_work.rs @@ -5,14 +5,21 @@ pub(crate) type TraceKind = u8; pub const DEFAULT_TRACE: u8 = u8::MAX; use crate::plan::TransitiveClosure; +use crate::scheduler::gc_work::ProcessEdgesWork; use crate::scheduler::GCWork; +use crate::scheduler::GCWorker; use crate::util::copy::CopySemantics; -use crate::vm::VMBinding; use crate::util::ObjectReference; -use crate::scheduler::gc_work::ProcessEdgesWork; -use crate::scheduler::GCWorker; +use crate::vm::VMBinding; +/// This trait defines policy-specific behavior for tracing objects. +/// The procedural macro #[derive(PlanTraceObject)] will generate code +/// that uses this trait. We expect any policy to implement this trait. +/// For the sake of performance, the implementation +/// of this trait should mark methods as `[inline(always)]`. pub trait PolicyTraceObject { + /// Trace object in the policy. If the policy copies objects, we should + /// expect `copy` to be a `Some` value. fn trace_object( &self, trace: &mut T, @@ -20,6 +27,11 @@ pub trait PolicyTraceObject { copy: Option, worker: &mut GCWorker, ) -> ObjectReference; + + /// Create a scan work packet. Note that a plan currently only uses one type + /// of the scan work packet. So a policy either uses the general `ScanObjects` + /// work, or implement their own packet. Their implementation needs to handle + /// cases that objects are not in this current space. #[inline(always)] fn create_scan_work>( &'static self, @@ -29,5 +41,7 @@ pub trait PolicyTraceObject { nodes, false, )) } + + /// Return whether the policy moves objects. fn may_move_objects() -> bool; } diff --git a/src/policy/largeobjectspace.rs b/src/policy/largeobjectspace.rs index 8439615376..dc016d036c 100644 --- a/src/policy/largeobjectspace.rs +++ b/src/policy/largeobjectspace.rs @@ -118,9 +118,7 @@ impl Space for LargeObjectSpace { use crate::scheduler::GCWorker; use crate::util::copy::CopySemantics; -impl crate::policy::gc_work::PolicyTraceObject - for LargeObjectSpace -{ +impl crate::policy::gc_work::PolicyTraceObject for LargeObjectSpace { #[inline(always)] fn trace_object( &self, diff --git a/src/policy/lockfreeimmortalspace.rs b/src/policy/lockfreeimmortalspace.rs index 52c713eaaf..924be8a294 100644 --- a/src/policy/lockfreeimmortalspace.rs +++ b/src/policy/lockfreeimmortalspace.rs @@ -153,9 +153,7 @@ use crate::plan::TransitiveClosure; use crate::scheduler::GCWorker; use crate::util::copy::CopySemantics; -impl crate::policy::gc_work::PolicyTraceObject - for LockFreeImmortalSpace -{ +impl crate::policy::gc_work::PolicyTraceObject for LockFreeImmortalSpace { #[inline(always)] fn trace_object( &self, diff --git a/src/policy/markcompactspace.rs b/src/policy/markcompactspace.rs index 451deb98d9..b7c8ccec3f 100644 --- a/src/policy/markcompactspace.rs +++ b/src/policy/markcompactspace.rs @@ -1,8 +1,10 @@ use super::space::{CommonSpace, Space, SpaceOptions, SFT}; use crate::policy::gc_work::TraceKind; use crate::policy::space::*; +use crate::scheduler::GCWorker; use crate::util::alloc::allocator::align_allocation_no_fill; use crate::util::constants::LOG_BYTES_IN_WORD; +use crate::util::copy::CopySemantics; use crate::util::heap::layout::heap_layout::{Mmapper, VMMap}; use crate::util::heap::{HeapMeta, MonotonePageResource, PageResource, VMRequest}; use crate::util::metadata::load_metadata; @@ -10,8 +12,6 @@ use crate::util::metadata::side_metadata::{SideMetadataContext, SideMetadataSpec use crate::util::metadata::{compare_exchange_metadata, extract_side_metadata}; use crate::util::{alloc_bit, Address, ObjectReference}; use crate::{vm::*, TransitiveClosure}; -use crate::util::copy::CopySemantics; -use crate::scheduler::GCWorker; use atomic::Ordering; pub(crate) const TRACE_KIND_MARK: TraceKind = 0; @@ -103,9 +103,7 @@ impl Space for MarkCompactSpace { } } -impl crate::policy::gc_work::PolicyTraceObject - for MarkCompactSpace -{ +impl crate::policy::gc_work::PolicyTraceObject for MarkCompactSpace { #[inline(always)] fn trace_object( &self, From b7adb2d4e9a703fe1dd2d796b1cd60fa70fb9362 Mon Sep 17 00:00:00 2001 From: Yi Lin Date: Wed, 27 Apr 2022 13:07:57 +1000 Subject: [PATCH 13/22] Fix missing use --- src/plan/transitive_closure.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plan/transitive_closure.rs b/src/plan/transitive_closure.rs index b5dbb3a395..c36958a604 100644 --- a/src/plan/transitive_closure.rs +++ b/src/plan/transitive_closure.rs @@ -106,6 +106,7 @@ pub trait PlanTraceObject { fn may_move_objects() -> bool; } +use crate::mmtk::MMTK; use crate::plan::Plan; use crate::scheduler::gc_work::ProcessEdgesBase; use std::ops::{Deref, DerefMut}; From 74a71de483a73bc7b7764022fc46b3cb41417d8c Mon Sep 17 00:00:00 2001 From: Yi Lin Date: Fri, 29 Apr 2022 11:38:55 +1000 Subject: [PATCH 14/22] match space and call scan_object() (similar to trace_object()) --- macros/trace_object/src/lib.rs | 52 ++++++++++------------ src/plan/generational/immix/global.rs | 2 +- src/plan/immix/global.rs | 2 +- src/plan/transitive_closure.rs | 63 +++++++++++++++++++++++---- src/policy/gc_work.rs | 25 +++++------ src/policy/immix/immixspace.rs | 60 +++++-------------------- src/scheduler/gc_work.rs | 6 ++- 7 files changed, 108 insertions(+), 102 deletions(-) diff --git a/macros/trace_object/src/lib.rs b/macros/trace_object/src/lib.rs index 80ba04ce6e..2387b78222 100644 --- a/macros/trace_object/src/lib.rs +++ b/macros/trace_object/src/lib.rs @@ -20,10 +20,10 @@ mod util; /// * add `#[fallback_trace]` to the parent plan if the plan is composed with other plans (or parent plans). /// For example, `GenImmix` is composed with `Gen`, `Gen` is composed with `CommonPlan`, `CommonPlan` is composed /// with `BasePlan`. -/// * (optional) add `#[scan_work]` to _one_ space field in the plan. `create_scan_work` will be generated based -/// on the space. +/// * add `#[policy_scan]` to any space field that has some policy-specific scan_object(). For objects in those spaces, +/// `scan_object()` in the policy will be called. For other objects, directly call `VM::VMScanning::scan_object()`. #[proc_macro_error] -#[proc_macro_derive(PlanTraceObject, attributes(trace, scan_work, copy, fallback_trace))] +#[proc_macro_derive(PlanTraceObject, attributes(trace, policy_scan, copy, fallback_trace))] pub fn derive_plan_trace_object(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); let ident = input.ident; @@ -34,11 +34,11 @@ pub fn derive_plan_trace_object(input: TokenStream) -> TokenStream { .. }) = input.data { let spaces = util::get_fields_with_attribute(fields, "trace"); - let scan_work = util::get_unique_field_with_attribute(fields, "scan_work"); + let scan_spaces = util::get_fields_with_attribute(fields, "policy_scan"); let fallback = util::get_unique_field_with_attribute(fields, "fallback_trace"); let trace_object_function = generate_trace_object(&spaces, &fallback, &ty_generics); - let create_scan_work_function = generate_create_scan_work(&spaces, &scan_work, &ty_generics); + let scan_object_function = generate_scan_object(&scan_spaces, &ty_generics); let may_move_objects_function = generate_may_move_objects(&spaces, &fallback, &ty_generics); quote!{ impl #impl_generics crate::plan::transitive_closure::PlanTraceObject #ty_generics for #ident #ty_generics #where_clause { @@ -46,7 +46,7 @@ pub fn derive_plan_trace_object(input: TokenStream) -> TokenStream { #trace_object_function #[inline(always)] - #create_scan_work_function + #scan_object_function #[inline(always)] #may_move_objects_function @@ -121,40 +121,32 @@ fn generate_trace_object<'a>( } } -fn generate_create_scan_work<'a>( - space_fields: &[&'a Field], - scan_work_field: &Option<&'a Field>, +fn generate_scan_object<'a>( + scan_object_fields: &[&'a Field], ty_generics: &TypeGenerics, ) -> TokenStream2 { - if let Some(f) = scan_work_field { - // If the plan names a field for scan work, use it + let scan_field_handler = scan_object_fields.iter().map(|f| { let f_ident = f.ident.as_ref().unwrap(); let ref f_ty = f.ty; quote! { - fn create_scan_work>(&'static self, nodes: Vec) -> Box> { + if self.#f_ident.in_space(__mmtk_objref) { use crate::policy::gc_work::PolicyTraceObject; - <#f_ty as PolicyTraceObject #ty_generics>::create_scan_work::(&self.#f_ident, nodes) + <#f_ty as PolicyTraceObject #ty_generics>::scan_object::(&self.#f_ident, __mmtk_worker_tls, __mmtk_objref, __mmtk_ev); + return; } } - } else if !space_fields.is_empty() { - // If the plan does not name a specific field for scan work, just use the first space for scan work - let f = space_fields[0]; - let f_ident = f.ident.as_ref().unwrap(); - let ref f_ty = f.ty; + }); - quote! { - fn create_scan_work>(&'static self, nodes: Vec) -> Box> { - use crate::policy::gc_work::PolicyTraceObject; - <#f_ty as PolicyTraceObject #ty_generics>::create_scan_work::(&self.#f_ident, nodes) - } - } - } else { - // Otherwise, just panic - quote! { - fn create_scan_work>(&'static self, nodes: Vec) -> Box> { - panic!("Unable to create a scan work packet for the plan (the plan does not name a #[scan_work] field, or a #[trace] field") - } + quote! { + fn scan_object(&self, __mmtk_worker_tls: crate::util::opaque_pointer::VMWorkerThread, __mmtk_objref: crate::util::ObjectReference, __mmtk_ev: &mut EV) { + use crate::vm::Scanning; + + // Plan specific + #(#scan_field_handler)* + + // Default + VM::VMScanning::scan_object(__mmtk_worker_tls, __mmtk_objref, __mmtk_ev); } } } diff --git a/src/plan/generational/immix/global.rs b/src/plan/generational/immix/global.rs index 9c2eab0b37..0e820b4eba 100644 --- a/src/plan/generational/immix/global.rs +++ b/src/plan/generational/immix/global.rs @@ -38,7 +38,7 @@ pub struct GenImmix { #[fallback_trace] pub gen: Gen, /// An immix space as the mature space. - #[scan_work] + #[policy_scan] #[trace(CopySemantics::Mature)] pub immix: ImmixSpace, /// Whether the last GC was a defrag GC for the immix space. diff --git a/src/plan/immix/global.rs b/src/plan/immix/global.rs index 825eed2e0d..1d5d35cfd7 100644 --- a/src/plan/immix/global.rs +++ b/src/plan/immix/global.rs @@ -30,7 +30,7 @@ use macro_trace_object::PlanTraceObject; #[derive(PlanTraceObject)] pub struct Immix { - #[scan_work] + #[policy_scan] #[trace(CopySemantics::DefaultCopy)] pub immix_space: ImmixSpace, #[fallback_trace] diff --git a/src/plan/transitive_closure.rs b/src/plan/transitive_closure.rs index c36958a604..42f6f9151b 100644 --- a/src/plan/transitive_closure.rs +++ b/src/plan/transitive_closure.rs @@ -74,6 +74,7 @@ impl<'a, E: ProcessEdgesWork> Drop for ObjectsClosure<'a, E> { use crate::policy::gc_work::TraceKind; use crate::scheduler::GCWork; +use crate::util::VMWorkerThread; use crate::vm::VMBinding; /// A plan that uses `PlanProcessEdges` needs to provide an implementation for this trait. @@ -93,13 +94,15 @@ pub trait PlanTraceObject { worker: &mut GCWorker, ) -> ObjectReference; - /// Create a scan objects work packet for the plan. Usually [`ScanObjects`](scheduler/gc_work/ScanObjects) - /// is used. If a plan or any policy in the plan uses a specific scan work packet, the work packet is required - /// to handle objects that is in any space in the plan. - fn create_scan_work>( - &'static self, - nodes: Vec, - ) -> Box>; + /// Scan objects in the plan. It is expected that each object will be scanned by `VM::VMScanning::scan_object()`. + /// If the object is in a policy that has some policy specific behaviors for scanning (e.g. mark lines in Immix), + /// this method should also invoke those policy specific methods. + fn scan_object( + &self, + tls: VMWorkerThread, + object: ObjectReference, + edge_visitor: &mut EV, + ); /// Whether objects in this plan may move. If any of the spaces used by the plan may move objects, this should /// return true. @@ -138,7 +141,7 @@ impl< #[inline(always)] fn create_scan_work(&self, nodes: Vec) -> Box> { - self.plan.create_scan_work::(nodes) + Box::new(PlanScanObjects::::new(self.plan, nodes, false)) } #[inline(always)] @@ -185,3 +188,47 @@ impl< &mut self.base } } + +use std::marker::PhantomData; + +/// This provides an implementation of scanning objects work. Each object will be scanned by calling `scan_object()` +/// in `PlanTraceObject`. +pub struct PlanScanObjects< + E: ProcessEdgesWork, + P: 'static + Plan + PlanTraceObject + Sync, +> { + plan: &'static P, + buffer: Vec, + #[allow(dead_code)] + concurrent: bool, + phantom: PhantomData, +} + +impl + PlanTraceObject + Sync> + PlanScanObjects +{ + pub fn new(plan: &'static P, buffer: Vec, concurrent: bool) -> Self { + Self { + plan, + buffer, + concurrent, + phantom: PhantomData, + } + } +} + +impl + PlanTraceObject + Sync> + GCWork for PlanScanObjects +{ + fn do_work(&mut self, worker: &mut GCWorker, _mmtk: &'static MMTK) { + trace!("PlanScanObjects"); + { + let tls = worker.tls; + let mut closure = ObjectsClosure::::new(worker); + for object in &self.buffer { + self.plan.scan_object(tls, *object, &mut closure); + } + } + trace!("PlanScanObjects End"); + } +} diff --git a/src/policy/gc_work.rs b/src/policy/gc_work.rs index b2cd0256d5..756e9f2d03 100644 --- a/src/policy/gc_work.rs +++ b/src/policy/gc_work.rs @@ -5,11 +5,11 @@ pub(crate) type TraceKind = u8; pub const DEFAULT_TRACE: u8 = u8::MAX; use crate::plan::TransitiveClosure; -use crate::scheduler::gc_work::ProcessEdgesWork; -use crate::scheduler::GCWork; use crate::scheduler::GCWorker; use crate::util::copy::CopySemantics; +use crate::util::opaque_pointer::VMWorkerThread; use crate::util::ObjectReference; +use crate::vm::EdgeVisitor; use crate::vm::VMBinding; /// This trait defines policy-specific behavior for tracing objects. @@ -28,18 +28,17 @@ pub trait PolicyTraceObject { worker: &mut GCWorker, ) -> ObjectReference; - /// Create a scan work packet. Note that a plan currently only uses one type - /// of the scan work packet. So a policy either uses the general `ScanObjects` - /// work, or implement their own packet. Their implementation needs to handle - /// cases that objects are not in this current space. + /// Policy-specific scan object. The implementation needs to guarantee that + /// they will call `VM::VMScanning::scan_object()` (or `Self::vm_scan_object()`) besides any space-specific work for the object. #[inline(always)] - fn create_scan_work>( - &'static self, - nodes: Vec, - ) -> Box> { - Box::new(crate::scheduler::gc_work::ScanObjects::::new( - nodes, false, - )) + fn scan_object( + &self, + tls: VMWorkerThread, + object: ObjectReference, + edge_visitor: &mut EV, + ) { + use crate::vm::Scanning; + VM::VMScanning::scan_object(tls, object, edge_visitor) } /// Return whether the policy moves objects. diff --git a/src/policy/immix/immixspace.rs b/src/policy/immix/immixspace.rs index 390725fbf9..503680df91 100644 --- a/src/policy/immix/immixspace.rs +++ b/src/policy/immix/immixspace.rs @@ -4,7 +4,6 @@ use super::{ chunk::{Chunk, ChunkMap, ChunkState}, defrag::Defrag, }; -use crate::plan::ObjectsClosure; use crate::policy::gc_work::TraceKind; use crate::policy::space::SpaceOptions; use crate::policy::space::*; @@ -24,7 +23,7 @@ use crate::util::{Address, ObjectReference}; use crate::vm::*; use crate::{ plan::TransitiveClosure, - scheduler::{gc_work::ProcessEdgesWork, GCWork, GCWorkScheduler, GCWorker, WorkBucketStage}, + scheduler::{GCWork, GCWorkScheduler, GCWorker, WorkBucketStage}, util::{ heap::FreeListPageResource, opaque_pointer::{VMThread, VMWorkerThread}, @@ -136,13 +135,17 @@ impl crate::policy::gc_work::PolicyTraceObject for ImmixSpace } #[inline(always)] - fn create_scan_work>( - &'static self, - nodes: Vec, - ) -> Box> { - Box::new(crate::policy::immix::ScanObjectsAndMarkLines::::new( - nodes, false, self, - )) + fn scan_object( + &self, + tls: VMWorkerThread, + object: ObjectReference, + edge_visitor: &mut EV, + ) { + VM::VMScanning::scan_object(tls, object, edge_visitor); + if super::MARK_LINE_AT_SCAN_TIME && !super::BLOCK_ONLY { + debug_assert!(self.in_space(object)); + self.mark_lines(object); + } } #[inline(always)] @@ -613,45 +616,6 @@ impl GCWork for PrepareBlockState { } } -/// A work packet to scan the fields of each objects and mark lines. -pub struct ScanObjectsAndMarkLines { - buffer: Vec, - #[allow(unused)] - concurrent: bool, - immix_space: &'static ImmixSpace, -} - -impl ScanObjectsAndMarkLines { - pub fn new( - buffer: Vec, - concurrent: bool, - immix_space: &'static ImmixSpace, - ) -> Self { - Self { - buffer, - concurrent, - immix_space, - } - } -} - -impl GCWork for ScanObjectsAndMarkLines { - fn do_work(&mut self, worker: &mut GCWorker, _mmtk: &'static MMTK) { - trace!("ScanObjectsAndMarkLines"); - let tls = worker.tls; - let mut closure = ObjectsClosure::::new(worker); - for object in &self.buffer { - ::VMScanning::scan_object(tls, *object, &mut closure); - if super::MARK_LINE_AT_SCAN_TIME - && !super::BLOCK_ONLY - && self.immix_space.in_space(*object) - { - self.immix_space.mark_lines(*object); - } - } - } -} - use crate::plan::Plan; use crate::policy::copy_context::PolicyCopyContext; use crate::util::alloc::Allocator; diff --git a/src/scheduler/gc_work.rs b/src/scheduler/gc_work.rs index 1a918306e6..cb55a47c58 100644 --- a/src/scheduler/gc_work.rs +++ b/src/scheduler/gc_work.rs @@ -492,6 +492,7 @@ impl GCWork for E { /// (such as `Space.set_copy_for_sft_trace()`, `SFT.sft_trace_object()`). /// Some plans are not using this type, mostly due to more complex tracing. Either it is impossible to use this type, or /// there is performance overheads for using this general trace type. In such cases, they implement their specific process edges. +// TODO: This is not used any more. Should we remove it? pub struct SFTProcessEdges { pub base: ProcessEdgesBase, } @@ -540,7 +541,10 @@ impl DerefMut for SFTProcessEdges { } } -/// Scan & update a list of object slots +/// Scan & update a list of object slots. +/// Note that this work packet does not do any policy-specific scan +/// object work (it won't call `scan_object()` in [`policy::gc_work::PolicytraceObject`]). +/// It should be used only for policies that do not have policy-specific scan_object(). pub struct ScanObjects { buffer: Vec, #[allow(unused)] From 70896deab5fd7d4712929872b8bf026b93380213 Mon Sep 17 00:00:00 2001 From: Yi Lin Date: Fri, 29 Apr 2022 11:46:57 +1000 Subject: [PATCH 15/22] Rename macro with a mmtk prefix. Move macro impl to a separate module. --- Cargo.toml | 3 +- macros/trace_object/Cargo.toml | 2 +- macros/trace_object/src/derive_impl.rs | 130 ++++++++++++++++++++++ macros/trace_object/src/lib.rs | 137 ++---------------------- src/plan/generational/copying/global.rs | 2 +- src/plan/generational/global.rs | 2 +- src/plan/generational/immix/global.rs | 2 +- src/plan/global.rs | 2 +- src/plan/immix/global.rs | 2 +- src/plan/markcompact/global.rs | 2 +- src/plan/marksweep/global.rs | 2 +- src/plan/pageprotect/global.rs | 2 +- src/plan/semispace/global.rs | 2 +- 13 files changed, 148 insertions(+), 142 deletions(-) create mode 100644 macros/trace_object/src/derive_impl.rs diff --git a/Cargo.toml b/Cargo.toml index f0349a4f92..ed34c25e92 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,8 @@ crate-type = ["rlib"] doctest = false [dependencies] -macro-trace-object = { path = "macros/trace_object"} +# MMTk macros +mmtk-macro-trace-object = { path = "macros/trace_object"} custom_derive = "0.1" enum_derive = "0.1" diff --git a/macros/trace_object/Cargo.toml b/macros/trace_object/Cargo.toml index b62159f79c..4f58ce29ba 100644 --- a/macros/trace_object/Cargo.toml +++ b/macros/trace_object/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "macro-trace-object" +name = "mmtk-macro-trace-object" version = "0.1.0" edition = "2021" diff --git a/macros/trace_object/src/derive_impl.rs b/macros/trace_object/src/derive_impl.rs new file mode 100644 index 0000000000..a082a6663c --- /dev/null +++ b/macros/trace_object/src/derive_impl.rs @@ -0,0 +1,130 @@ +use quote::quote; +use syn::{Field, TypeGenerics}; +use proc_macro2::TokenStream as TokenStream2; + +use crate::util; + +pub fn generate_trace_object<'a>( + space_fields: &[&'a Field], + parent_field: &Option<&'a Field>, + ty_generics: &TypeGenerics, +) -> TokenStream2 { + // Generate a check with early return for each space + let space_field_handler = space_fields.iter().map(|f| { + let f_ident = f.ident.as_ref().unwrap(); + let ref f_ty = f.ty; + + // Figure out copy + let trace_attr = util::get_field_attribute(f, "trace").unwrap(); + let copy = if !trace_attr.tokens.is_empty() { + use syn::Token; + use syn::NestedMeta; + use syn::punctuated::Punctuated; + + let args = trace_attr.parse_args_with(Punctuated::::parse_terminated).unwrap(); + // CopySemantics::X is a path. + if let Some(NestedMeta::Meta(syn::Meta::Path(p))) = args.first() { + quote!{ Some(#p) } + } else { + quote!{ None } + } + } else { + quote!{ None } + }; + + quote! { + if self.#f_ident.in_space(__mmtk_objref) { + return <#f_ty as PolicyTraceObject #ty_generics>::trace_object::(&self.#f_ident, __mmtk_trace, __mmtk_objref, #copy, __mmtk_worker); + } + } + }); + + // Generate a fallback to the parent plan + let parent_field_delegator = if let Some(f) = parent_field { + let f_ident = f.ident.as_ref().unwrap(); + let ref f_ty = f.ty; + quote! { + <#f_ty as PlanTraceObject #ty_generics>::trace_object::(&self.#f_ident, __mmtk_trace, __mmtk_objref, __mmtk_worker) + } + } else { + quote! { + panic!("No more spaces to try") + } + }; + + quote! { + fn trace_object(&self, __mmtk_trace: &mut T, __mmtk_objref: crate::util::ObjectReference, __mmtk_worker: &mut crate::scheduler::GCWorker) -> crate::util::ObjectReference { + use crate::policy::space::Space; + use crate::policy::gc_work::PolicyTraceObject; + use crate::plan::transitive_closure::PlanTraceObject; + #(#space_field_handler)* + #parent_field_delegator + } + } +} + +pub fn generate_scan_object<'a>( + scan_object_fields: &[&'a Field], + ty_generics: &TypeGenerics, +) -> TokenStream2 { + let scan_field_handler = scan_object_fields.iter().map(|f| { + let f_ident = f.ident.as_ref().unwrap(); + let ref f_ty = f.ty; + + quote! { + if self.#f_ident.in_space(__mmtk_objref) { + use crate::policy::gc_work::PolicyTraceObject; + <#f_ty as PolicyTraceObject #ty_generics>::scan_object::(&self.#f_ident, __mmtk_worker_tls, __mmtk_objref, __mmtk_ev); + return; + } + } + }); + + quote! { + fn scan_object(&self, __mmtk_worker_tls: crate::util::opaque_pointer::VMWorkerThread, __mmtk_objref: crate::util::ObjectReference, __mmtk_ev: &mut EV) { + use crate::vm::Scanning; + + // Plan specific + #(#scan_field_handler)* + + // Default + VM::VMScanning::scan_object(__mmtk_worker_tls, __mmtk_objref, __mmtk_ev); + } + } +} + +// The generated function needs to be inlined and constant folded. Otherwise, there will be a huge +// performance penalty. +pub fn generate_may_move_objects<'a>( + space_fields: &[&'a Field], + parent_field: &Option<&'a Field>, + ty_generics: &TypeGenerics, +) -> TokenStream2 { + // If any space or the parent may move objects, the plan may move objects + let space_handlers = space_fields.iter().map(|f| { + let ref f_ty = f.ty; + + quote! { + || <#f_ty as PolicyTraceObject #ty_generics>::may_move_objects::() + } + }); + + let parent_handler = if let Some(p) = parent_field { + let ref p_ty = p.ty; + + quote! { + || <#p_ty as PlanTraceObject #ty_generics>::may_move_objects::() + } + } else { + TokenStream2::new() + }; + + quote! { + fn may_move_objects() -> bool { + use crate::policy::gc_work::PolicyTraceObject; + use crate::plan::transitive_closure::PlanTraceObject; + + false #(#space_handlers)* #parent_handler + } + } +} diff --git a/macros/trace_object/src/lib.rs b/macros/trace_object/src/lib.rs index 2387b78222..c526c0c3af 100644 --- a/macros/trace_object/src/lib.rs +++ b/macros/trace_object/src/lib.rs @@ -8,10 +8,10 @@ use proc_macro_error::proc_macro_error; use syn::{parse_macro_input}; use proc_macro_error::abort_call_site; use quote::quote; -use syn::{DeriveInput, Field, TypeGenerics}; -use syn::__private::TokenStream2; +use syn::DeriveInput; mod util; +mod derive_impl; /// Generally a plan needs to add these attributes in order for the macro to work: /// * add `#[derive(PlanTraceObject)]` to the plan struct. @@ -23,7 +23,7 @@ mod util; /// * add `#[policy_scan]` to any space field that has some policy-specific scan_object(). For objects in those spaces, /// `scan_object()` in the policy will be called. For other objects, directly call `VM::VMScanning::scan_object()`. #[proc_macro_error] -#[proc_macro_derive(PlanTraceObject, attributes(trace, policy_scan, copy, fallback_trace))] +#[proc_macro_derive(PlanTraceObject, attributes(trace, policy_scan, fallback_trace))] pub fn derive_plan_trace_object(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); let ident = input.ident; @@ -37,9 +37,9 @@ pub fn derive_plan_trace_object(input: TokenStream) -> TokenStream { let scan_spaces = util::get_fields_with_attribute(fields, "policy_scan"); let fallback = util::get_unique_field_with_attribute(fields, "fallback_trace"); - let trace_object_function = generate_trace_object(&spaces, &fallback, &ty_generics); - let scan_object_function = generate_scan_object(&scan_spaces, &ty_generics); - let may_move_objects_function = generate_may_move_objects(&spaces, &fallback, &ty_generics); + let trace_object_function = derive_impl::generate_trace_object(&spaces, &fallback, &ty_generics); + let scan_object_function = derive_impl::generate_scan_object(&scan_spaces, &ty_generics); + let may_move_objects_function = derive_impl::generate_may_move_objects(&spaces, &fallback, &ty_generics); quote!{ impl #impl_generics crate::plan::transitive_closure::PlanTraceObject #ty_generics for #ident #ty_generics #where_clause { #[inline(always)] @@ -61,128 +61,3 @@ pub fn derive_plan_trace_object(input: TokenStream) -> TokenStream { output.into() } - -fn generate_trace_object<'a>( - space_fields: &[&'a Field], - parent_field: &Option<&'a Field>, - ty_generics: &TypeGenerics, -) -> TokenStream2 { - // Generate a check with early return for each space - let space_field_handler = space_fields.iter().map(|f| { - let f_ident = f.ident.as_ref().unwrap(); - let ref f_ty = f.ty; - - // Figure out copy - let trace_attr = util::get_field_attribute(f, "trace").unwrap(); - let copy = if !trace_attr.tokens.is_empty() { - use syn::Token; - use syn::NestedMeta; - use syn::punctuated::Punctuated; - - let args = trace_attr.parse_args_with(Punctuated::::parse_terminated).unwrap(); - // CopySemantics::X is a path. - if let Some(NestedMeta::Meta(syn::Meta::Path(p))) = args.first() { - quote!{ Some(#p) } - } else { - quote!{ None } - } - } else { - quote!{ None } - }; - - quote! { - if self.#f_ident.in_space(__mmtk_objref) { - return <#f_ty as PolicyTraceObject #ty_generics>::trace_object::(&self.#f_ident, __mmtk_trace, __mmtk_objref, #copy, __mmtk_worker); - } - } - }); - - // Generate a fallback to the parent plan - let parent_field_delegator = if let Some(f) = parent_field { - let f_ident = f.ident.as_ref().unwrap(); - let ref f_ty = f.ty; - quote! { - <#f_ty as PlanTraceObject #ty_generics>::trace_object::(&self.#f_ident, __mmtk_trace, __mmtk_objref, __mmtk_worker) - } - } else { - quote! { - panic!("No more spaces to try") - } - }; - - quote! { - fn trace_object(&self, __mmtk_trace: &mut T, __mmtk_objref: crate::util::ObjectReference, __mmtk_worker: &mut crate::scheduler::GCWorker) -> crate::util::ObjectReference { - use crate::policy::space::Space; - use crate::policy::gc_work::PolicyTraceObject; - use crate::plan::transitive_closure::PlanTraceObject; - #(#space_field_handler)* - #parent_field_delegator - } - } -} - -fn generate_scan_object<'a>( - scan_object_fields: &[&'a Field], - ty_generics: &TypeGenerics, -) -> TokenStream2 { - let scan_field_handler = scan_object_fields.iter().map(|f| { - let f_ident = f.ident.as_ref().unwrap(); - let ref f_ty = f.ty; - - quote! { - if self.#f_ident.in_space(__mmtk_objref) { - use crate::policy::gc_work::PolicyTraceObject; - <#f_ty as PolicyTraceObject #ty_generics>::scan_object::(&self.#f_ident, __mmtk_worker_tls, __mmtk_objref, __mmtk_ev); - return; - } - } - }); - - quote! { - fn scan_object(&self, __mmtk_worker_tls: crate::util::opaque_pointer::VMWorkerThread, __mmtk_objref: crate::util::ObjectReference, __mmtk_ev: &mut EV) { - use crate::vm::Scanning; - - // Plan specific - #(#scan_field_handler)* - - // Default - VM::VMScanning::scan_object(__mmtk_worker_tls, __mmtk_objref, __mmtk_ev); - } - } -} - -// The generated function needs to be inlined and constant folded. Otherwise, there will be a huge -// performance penalty. -fn generate_may_move_objects<'a>( - space_fields: &[&'a Field], - parent_field: &Option<&'a Field>, - ty_generics: &TypeGenerics, -) -> TokenStream2 { - // If any space or the parent may move objects, the plan may move objects - let space_handlers = space_fields.iter().map(|f| { - let ref f_ty = f.ty; - - quote! { - || <#f_ty as PolicyTraceObject #ty_generics>::may_move_objects::() - } - }); - - let parent_handler = if let Some(p) = parent_field { - let ref p_ty = p.ty; - - quote! { - || <#p_ty as PlanTraceObject #ty_generics>::may_move_objects::() - } - } else { - TokenStream2::new() - }; - - quote! { - fn may_move_objects() -> bool { - use crate::policy::gc_work::PolicyTraceObject; - use crate::plan::transitive_closure::PlanTraceObject; - - false #(#space_handlers)* #parent_handler - } - } -} diff --git a/src/plan/generational/copying/global.rs b/src/plan/generational/copying/global.rs index b0ba77ed52..f8bd4d53b2 100644 --- a/src/plan/generational/copying/global.rs +++ b/src/plan/generational/copying/global.rs @@ -26,7 +26,7 @@ use enum_map::EnumMap; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; -use macro_trace_object::PlanTraceObject; +use mmtk_macro_trace_object::PlanTraceObject; #[derive(PlanTraceObject)] pub struct GenCopy { diff --git a/src/plan/generational/global.rs b/src/plan/generational/global.rs index b741583d5c..dd64d995b5 100644 --- a/src/plan/generational/global.rs +++ b/src/plan/generational/global.rs @@ -21,7 +21,7 @@ use std::sync::atomic::AtomicBool; use std::sync::atomic::Ordering; use std::sync::Arc; -use macro_trace_object::PlanTraceObject; +use mmtk_macro_trace_object::PlanTraceObject; /// Common implementation for generational plans. Each generational plan /// should include this type, and forward calls to it where possible. diff --git a/src/plan/generational/immix/global.rs b/src/plan/generational/immix/global.rs index 0e820b4eba..583408334e 100644 --- a/src/plan/generational/immix/global.rs +++ b/src/plan/generational/immix/global.rs @@ -26,7 +26,7 @@ use std::sync::atomic::AtomicBool; use std::sync::atomic::Ordering; use std::sync::Arc; -use macro_trace_object::PlanTraceObject; +use mmtk_macro_trace_object::PlanTraceObject; /// Generational immix. This implements the functionality of a two-generation copying /// collector where the higher generation is an immix space. diff --git a/src/plan/global.rs b/src/plan/global.rs index fe24e8577f..e473971bb4 100644 --- a/src/plan/global.rs +++ b/src/plan/global.rs @@ -33,7 +33,7 @@ use enum_map::EnumMap; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use std::sync::{Arc, Mutex}; -use macro_trace_object::PlanTraceObject; +use mmtk_macro_trace_object::PlanTraceObject; pub fn create_mutator( tls: VMMutatorThread, diff --git a/src/plan/immix/global.rs b/src/plan/immix/global.rs index 1d5d35cfd7..7fb50909ca 100644 --- a/src/plan/immix/global.rs +++ b/src/plan/immix/global.rs @@ -26,7 +26,7 @@ use std::sync::Arc; use atomic::Ordering; use enum_map::EnumMap; -use macro_trace_object::PlanTraceObject; +use mmtk_macro_trace_object::PlanTraceObject; #[derive(PlanTraceObject)] pub struct Immix { diff --git a/src/plan/markcompact/global.rs b/src/plan/markcompact/global.rs index a2017e1d38..d29f328cef 100644 --- a/src/plan/markcompact/global.rs +++ b/src/plan/markcompact/global.rs @@ -31,7 +31,7 @@ use crate::vm::VMBinding; use enum_map::EnumMap; use std::sync::Arc; -use macro_trace_object::PlanTraceObject; +use mmtk_macro_trace_object::PlanTraceObject; #[derive(PlanTraceObject)] pub struct MarkCompact { diff --git a/src/plan/marksweep/global.rs b/src/plan/marksweep/global.rs index a3229dddd9..4cbc4dcb68 100644 --- a/src/plan/marksweep/global.rs +++ b/src/plan/marksweep/global.rs @@ -25,7 +25,7 @@ use std::sync::Arc; use enum_map::EnumMap; -use macro_trace_object::PlanTraceObject; +use mmtk_macro_trace_object::PlanTraceObject; #[derive(PlanTraceObject)] pub struct MarkSweep { diff --git a/src/plan/pageprotect/global.rs b/src/plan/pageprotect/global.rs index 13f24075d6..41ebc221bd 100644 --- a/src/plan/pageprotect/global.rs +++ b/src/plan/pageprotect/global.rs @@ -22,7 +22,7 @@ use crate::{ use enum_map::EnumMap; use std::sync::Arc; -use macro_trace_object::PlanTraceObject; +use mmtk_macro_trace_object::PlanTraceObject; #[derive(PlanTraceObject)] pub struct PageProtect { diff --git a/src/plan/semispace/global.rs b/src/plan/semispace/global.rs index c537ad8061..3aedb03b25 100644 --- a/src/plan/semispace/global.rs +++ b/src/plan/semispace/global.rs @@ -22,7 +22,7 @@ use crate::{plan::global::BasePlan, vm::VMBinding}; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; -use macro_trace_object::PlanTraceObject; +use mmtk_macro_trace_object::PlanTraceObject; use enum_map::EnumMap; From d09c58af475560da5cd827d3bc3c19e77a56209e Mon Sep 17 00:00:00 2001 From: Yi Lin Date: Fri, 29 Apr 2022 16:12:23 +1000 Subject: [PATCH 16/22] Update tutorial --- docs/tutorial/code/mygc_semispace/gc_work.rs | 88 +++++++++++++- docs/tutorial/code/mygc_semispace/global.rs | 6 + docs/tutorial/src/mygc/ss/collection.md | 121 ++++++++++++++----- 3 files changed, 181 insertions(+), 34 deletions(-) diff --git a/docs/tutorial/code/mygc_semispace/gc_work.rs b/docs/tutorial/code/mygc_semispace/gc_work.rs index 02b122e29e..ffc7ae396c 100644 --- a/docs/tutorial/code/mygc_semispace/gc_work.rs +++ b/docs/tutorial/code/mygc_semispace/gc_work.rs @@ -5,11 +5,95 @@ use crate::vm::VMBinding; use std::ops::{Deref, DerefMut}; // ANCHOR_END: imports -// ANCHOR: workcontext +// ANCHOR: workcontext_sft pub struct MyGCWorkContext(std::marker::PhantomData); impl crate::scheduler::GCWorkContext for MyGCWorkContext { type VM = VM; type PlanType = MyGC; type ProcessEdgesWorkType = SFTProcessEdges; } -// ANCHOR_END: workcontext +// ANCHOR_END: workcontext_sft + +// ANCHOR: workcontext_plan +use crate::plan::transitive_closure::PlanProcessEdges; +use crate::policy::gc_work::DEFAULT_TRACE; +pub struct MyGCWorkContext2(std::marker::PhantomData); +impl crate::scheduler::GCWorkContext for MyGCWorkContext2 { + type VM = VM; + type PlanType = MyGC; + type ProcessEdgesWorkType = PlanProcessEdges, DEFAULT_TRACE>; +} +// ANCHOR: workcontext_plan + +use crate::util::{Address, ObjectReference}; +use crate::util::copy::CopySemantics; +use crate::MMTK; +use crate::policy::space::Space; + +// ANCHOR: mygc_process_edges +pub struct MyGCProcessEdges { + plan: &'static MyGC, + base: ProcessEdgesBase, +} +// ANCHOR_END: mygc_process_edges + +// ANCHOR: mygc_process_edges_impl +impl ProcessEdgesWork for MyGCProcessEdges { + type VM = VM; + fn new(edges: Vec

, roots: bool, mmtk: &'static MMTK) -> Self { + let base = ProcessEdgesBase::new(edges, roots, mmtk); + let plan = base.plan().downcast_ref::>().unwrap(); + Self { base, plan } + } + + #[inline] + fn trace_object(&mut self, object: ObjectReference) -> ObjectReference { + if object.is_null() { + return object; + } + if self.plan.tospace().in_space(object) { + self.plan.tospace().trace_object::( + self, + object, + Some(CopySemantics::DefaultCopy), + self.worker(), + ) + } else if self.plan.fromspace().in_space(object) { + self.plan.fromspace().trace_object::( + self, + object, + Some(CopySemantics::DefaultCopy), + self.worker(), + ) + } else { + self.plan.common.trace_object::(self, object) + } + } +} +// ANCHOR_END: mygc_process_edges_impl + +// ANCHOR: mygc_process_edges_deref +impl Deref for MyGCProcessEdges { + type Target = ProcessEdgesBase; + #[inline] + fn deref(&self) -> &Self::Target { + &self.base + } +} + +impl DerefMut for MyGCProcessEdges { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.base + } +} +// ANCHOR_END: mygc_process_edges_deref + +// ANCHOR: workcontext_mygc +pub struct MyGCWorkContext3(std::marker::PhantomData); +impl crate::scheduler::GCWorkContext for MyGCWorkContext3 { + type VM = VM; + type PlanType = MyGC; + type ProcessEdgesWorkType = MyGCProcessEdges; +} +// ANCHOR: workcontext_mygc diff --git a/docs/tutorial/code/mygc_semispace/global.rs b/docs/tutorial/code/mygc_semispace/global.rs index bbb0dccd64..7b87cf7638 100644 --- a/docs/tutorial/code/mygc_semispace/global.rs +++ b/docs/tutorial/code/mygc_semispace/global.rs @@ -29,12 +29,18 @@ use std::sync::Arc; // Remove #[allow(unused_imports)]. // Remove handle_user_collection_request(). +use mmtk_macro_trace_object::PlanTraceObject; + // Modify // ANCHOR: plan_def +#[derive(PlanTraceObject)] pub struct MyGC { pub hi: AtomicBool, + #[trace(CopySemantics::DefaultCopy)] pub copyspace0: CopySpace, + #[trace(CopySemantics::DefaultCopy)] pub copyspace1: CopySpace, + #[fallback_trace] pub common: CommonPlan, } // ANCHOR_END: plan_def diff --git a/docs/tutorial/src/mygc/ss/collection.md b/docs/tutorial/src/mygc/ss/collection.md index 7a1a70b246..85e6fa10e2 100644 --- a/docs/tutorial/src/mygc/ss/collection.md +++ b/docs/tutorial/src/mygc/ss/collection.md @@ -43,14 +43,15 @@ method `schedule_common_work()` that will add common work packets for you. To use `schedule_common_work()`, first we need to create a type `MyGCWorkContext` and implement the trait `GCWorkContext` for it. We create `gc_work.rs` and add the -following implementation. Note that we do not set a specific `ProcessEdgesWorkType` -and we will use the default [`SFTProcessEdges`](https://www.mmtk.io/mmtk-core/mmtk/scheduler/gc_work/struct.SFTProcessEdges.html), +following implementation. Note that we will use the default +[`SFTProcessEdges`](https://www.mmtk.io/mmtk-core/mmtk/scheduler/gc_work/struct.SFTProcessEdges.html), which is a general work packet that a plan can use to trace objects. For plans like semispace, `SFTProcessEdges` is sufficient. For more complex GC plans, one can create and write their own work packet that implements the `ProcessEdgesWork` trait. +We will discuss about this later, and discuss the alternatives. ```rust -{{#include ../../../code/mygc_semispace/gc_work.rs:workcontext}} +{{#include ../../../code/mygc_semispace/gc_work.rs:workcontext_sft}} ``` Then we implement `schedule_collection()` using `MyGCWorkContext` and `schedule_common_work()`. @@ -111,35 +112,6 @@ 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()`. - -## Scan objects - -Next, we'll add the code to allow the plan to collect garbage - filling out -functions for work packets. - -In `gc_work.rs`, add a new method to `ProcessEdgesWork for MyGCProcessEdges`, -`trace_object(&mut self, object: ObjectReference)`. -This method should return an ObjectReference, and use the -inline attribute. -Check if the object passed into the function is null -(`object.is_null()`). If it is, return the object. -Otherwise, check which space the object is in, and forward the call to the -policy-specific object tracing code. If it is in neither space, forward the -call to the common space and let the common space to handle object tracing in -its spaces (e.g. immortal or large object space): - -```rust -{{#include ../../../code/mygc_semispace/gc_work.rs:trace_object}} -``` - -Add two new implementation blocks, `Deref` and `DerefMut` for -`MyGCProcessEdges`. These allow `MyGCProcessEdges` to be dereferenced to -`ProcessEdgesBase`, and allows easy access to fields in `ProcessEdgesBase`. - -```rust -{{#include ../../../code/mygc_semispace/gc_work.rs:deref}} -``` - ## Release Finally, we need to fill out the functions that are, roughly speaking, @@ -178,6 +150,91 @@ will then go to the new tospace. 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. +## ProcessEdgesWork for MyGC + +[`ProcessEdgesWork`](https://www.mmtk.io/mmtk-core/mmtk/scheduler/gc_work/trait.ProcessEdgesWork.html) +is the key work packet for tracing objects in a GC. A `ProcessEdgesWork` implementation +defines how to trace objects, and how to generate more work packets based on the current tracing +to finish the object closure. + +`GCWorkContext` specifies a type +that implements `ProcessEdgesWork`, and we used `SFTProcessEdges` earlier. In +this section, we discuss what `SFTProcessEdges` does, and what the alternatives +are. + +### Approach 1: Use `SFTProcessEdges` + +[`SFTProcessEdges`](https://www.mmtk.io/mmtk-core/mmtk/scheduler/gc_work/struct.SFTProcessEdges.html) dispatches +trace objects to each space through [Space Function Table (SFT)](https://www.mmtk.io/mmtk-core/mmtk/policy/space/trait.SFT.html). +As long as all the policies in a plan provides an implementation of `sft_trace_object()` in their SFT implementation, +the plan can use `SFTProcessEdges`. Currently most policies provide an implementation for `sft_trace_object()`, except +mark compact and immix. Those two policies use multiple GC traces, and due to the limitation of SFT, SFT does not allow +multiple `sft_trace_object()` for a policy. + +`SFTProcessEdges` is the simplest approach when all the policies support it. Fortunately, we can use it for our GC, semispace. + +### Approach 2: Derive `PlanTraceObject` and use `PlanProcessEdges` + +`PlanProcessEdges` is another general `ProcessEdgesWork` implementation that can be used by most plans. When a plan +implements the [`PlanTraceObject`](https://www.mmtk.io/mmtk-core/mmtk/plan/transitive_closure/trait.PlanTraceObject.html), +they can use `PlanProcessEdges`. + +You can manually provide an implementation of `PlanTraceObject` for `MyGC`. But you can also use the derive macro MMTK provides, +and the macro will generate an implementation of `PlanTraceObject`: +* add `#[derive(PlanTraceObject)]` for `MyGC` (import the macro properly: `use mmtk_macro_trace_object::PlanTraceObject`) +* add `#[trace(CopySemantics::Default)]` to both copy space fields, `copyspace0` and `copyspace1`. This tells the macro to generate + trace code for both spaces, and for any copying in the spaces, use `CopySemantics::DefaultCopy` that we have configured early. +* add `#[fallback_trace]` to `common`. This tells the macro that if an object is not found in any space with `#[trace]` in ths plan, + try find the space for the object in the 'parent' plan. In our case, we fall back to the `CommonPlan`, as the object may be + in large object space or immortal space in the common plan. `CommonPlan` also implements `PlanTraceObject`, so it knows how to + find a space for the object and trace it in the same way. + +With the derive macro, your `MyGC` struct should look like this: +```rust +{{#include ../../../code/mygc_semispace/global.rs:plan_def}} +``` + +Once this is done, you can specify `PlanProcessEdges` as the `ProcessEdgesWorkType` in your GC work context: +```rust +{{#include ../../../code/mygc_semispace/gc_work.rs:workcontext_plan}} +``` + +### Approach 3: Implement your own `ProcessEdgesWork` + +Apart from the two approaches above, you can always implement your own `ProcessEdgesWork`. This is +an overkill for simple plans like semi space, but is probably necessary for more complex plans. +We discuss how to implement it for `MyGC`. + +Create a struct `MyGCProcessEdges` in the `gc_work` module. It includes a reference +back to the plan, and a `ProcessEdgesBase` field: +```rust +{{#include ../../../code/mygc_semispace/gc_work.rs:mygc_process_edges}} +``` + +Implement `ProcessEdgesWork` for `MyGCProcessEdges`. As most methods in the trait have a default +implemetation, we only need to implement `new()` and `trace_object()` for our plan. However, this +may not be true when you are implement for other GC plans. It would be better to check the default +implementation of `ProcessEdgesWork`. + +For `trace_object()`, what we do is similar to the approach above (except that we need to write the code +ourselves rather than letting the macro to generate it for us). We try figure out +which space the object is in, and invoke `trace_object()` for the object on that space. If the +object is not in any of the semi spaces in the plan, we forward the call to `CommonPlan`. +```rust +{{#include ../../../code/mygc_semispace/gc_work.rs:mygc_process_edges_impl}} +``` + +We would also need to implement `Deref` and `DerefMut` to our `ProcessEdgesWork` impl to be +dereferenced as `ProcessEdgesBase`. +```rust +{{#include ../../../code/mygc_semispace/gc_work.rs:mygc_process_edges_deref}} +``` + +In the end, use `MyGCProcessEdges` as `ProcessEdgesWorkType` in the `GCWorkContext`: +```rust +{{#include ../../../code/mygc_semispace/gc_work.rs:workcontext_mygc}} +``` + ## Summary You should now have MyGC working and able to collect garbage. All three From 158878d65a8220732011bfa28f58b4aa35d74519 Mon Sep 17 00:00:00 2001 From: Kunshan Wang Date: Thu, 5 May 2022 19:22:35 +0800 Subject: [PATCH 17/22] Change to PolicyTraceObject::post_scan_object Now it is provided as a hook **after** scan_object. It no longer customises how to do scan_object. --- macros/trace_object/src/derive_impl.rs | 17 ++++++----------- macros/trace_object/src/lib.rs | 12 ++++++------ src/plan/generational/immix/global.rs | 2 +- src/plan/immix/global.rs | 2 +- src/plan/transitive_closure.rs | 13 +++++-------- src/policy/gc_work.rs | 18 ++++++------------ src/policy/immix/immixspace.rs | 8 +------- 7 files changed, 26 insertions(+), 46 deletions(-) diff --git a/macros/trace_object/src/derive_impl.rs b/macros/trace_object/src/derive_impl.rs index a082a6663c..9114f28029 100644 --- a/macros/trace_object/src/derive_impl.rs +++ b/macros/trace_object/src/derive_impl.rs @@ -63,32 +63,27 @@ pub fn generate_trace_object<'a>( } } -pub fn generate_scan_object<'a>( - scan_object_fields: &[&'a Field], +pub fn generate_post_scan_object<'a>( + post_scan_object_fields: &[&'a Field], ty_generics: &TypeGenerics, ) -> TokenStream2 { - let scan_field_handler = scan_object_fields.iter().map(|f| { + let scan_field_handler = post_scan_object_fields.iter().map(|f| { let f_ident = f.ident.as_ref().unwrap(); let ref f_ty = f.ty; quote! { if self.#f_ident.in_space(__mmtk_objref) { use crate::policy::gc_work::PolicyTraceObject; - <#f_ty as PolicyTraceObject #ty_generics>::scan_object::(&self.#f_ident, __mmtk_worker_tls, __mmtk_objref, __mmtk_ev); + <#f_ty as PolicyTraceObject #ty_generics>::post_scan_object(&self.#f_ident, __mmtk_objref); return; } } }); quote! { - fn scan_object(&self, __mmtk_worker_tls: crate::util::opaque_pointer::VMWorkerThread, __mmtk_objref: crate::util::ObjectReference, __mmtk_ev: &mut EV) { - use crate::vm::Scanning; - - // Plan specific + #[inline(always)] + fn post_scan_object(&self, __mmtk_objref: crate::util::ObjectReference) { #(#scan_field_handler)* - - // Default - VM::VMScanning::scan_object(__mmtk_worker_tls, __mmtk_objref, __mmtk_ev); } } } diff --git a/macros/trace_object/src/lib.rs b/macros/trace_object/src/lib.rs index c526c0c3af..c3db273b5c 100644 --- a/macros/trace_object/src/lib.rs +++ b/macros/trace_object/src/lib.rs @@ -20,10 +20,10 @@ mod derive_impl; /// * add `#[fallback_trace]` to the parent plan if the plan is composed with other plans (or parent plans). /// For example, `GenImmix` is composed with `Gen`, `Gen` is composed with `CommonPlan`, `CommonPlan` is composed /// with `BasePlan`. -/// * add `#[policy_scan]` to any space field that has some policy-specific scan_object(). For objects in those spaces, -/// `scan_object()` in the policy will be called. For other objects, directly call `VM::VMScanning::scan_object()`. +/// * add `#[post_scan_hook]` to any space field that has some policy-specific post_scan_object(). For objects in those spaces, +/// `post_scan_object()` in the policy will be called after `VM::VMScanning::scan_object()`. #[proc_macro_error] -#[proc_macro_derive(PlanTraceObject, attributes(trace, policy_scan, fallback_trace))] +#[proc_macro_derive(PlanTraceObject, attributes(trace, post_scan_hook, fallback_trace))] pub fn derive_plan_trace_object(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); let ident = input.ident; @@ -34,11 +34,11 @@ pub fn derive_plan_trace_object(input: TokenStream) -> TokenStream { .. }) = input.data { let spaces = util::get_fields_with_attribute(fields, "trace"); - let scan_spaces = util::get_fields_with_attribute(fields, "policy_scan"); + let post_scan_hook_spaces = util::get_fields_with_attribute(fields, "post_scan_hook"); let fallback = util::get_unique_field_with_attribute(fields, "fallback_trace"); let trace_object_function = derive_impl::generate_trace_object(&spaces, &fallback, &ty_generics); - let scan_object_function = derive_impl::generate_scan_object(&scan_spaces, &ty_generics); + let post_scan_object_function = derive_impl::generate_post_scan_object(&post_scan_hook_spaces, &ty_generics); let may_move_objects_function = derive_impl::generate_may_move_objects(&spaces, &fallback, &ty_generics); quote!{ impl #impl_generics crate::plan::transitive_closure::PlanTraceObject #ty_generics for #ident #ty_generics #where_clause { @@ -46,7 +46,7 @@ pub fn derive_plan_trace_object(input: TokenStream) -> TokenStream { #trace_object_function #[inline(always)] - #scan_object_function + #post_scan_object_function #[inline(always)] #may_move_objects_function diff --git a/src/plan/generational/immix/global.rs b/src/plan/generational/immix/global.rs index 583408334e..6c27b811df 100644 --- a/src/plan/generational/immix/global.rs +++ b/src/plan/generational/immix/global.rs @@ -38,7 +38,7 @@ pub struct GenImmix { #[fallback_trace] pub gen: Gen, /// An immix space as the mature space. - #[policy_scan] + #[post_scan_hook] #[trace(CopySemantics::Mature)] pub immix: ImmixSpace, /// Whether the last GC was a defrag GC for the immix space. diff --git a/src/plan/immix/global.rs b/src/plan/immix/global.rs index 7fb50909ca..482230ba54 100644 --- a/src/plan/immix/global.rs +++ b/src/plan/immix/global.rs @@ -30,7 +30,7 @@ use mmtk_macro_trace_object::PlanTraceObject; #[derive(PlanTraceObject)] pub struct Immix { - #[policy_scan] + #[post_scan_hook] #[trace(CopySemantics::DefaultCopy)] pub immix_space: ImmixSpace, #[fallback_trace] diff --git a/src/plan/transitive_closure.rs b/src/plan/transitive_closure.rs index 42f6f9151b..07b5f280e6 100644 --- a/src/plan/transitive_closure.rs +++ b/src/plan/transitive_closure.rs @@ -74,7 +74,7 @@ impl<'a, E: ProcessEdgesWork> Drop for ObjectsClosure<'a, E> { use crate::policy::gc_work::TraceKind; use crate::scheduler::GCWork; -use crate::util::VMWorkerThread; + use crate::vm::VMBinding; /// A plan that uses `PlanProcessEdges` needs to provide an implementation for this trait. @@ -97,12 +97,7 @@ pub trait PlanTraceObject { /// Scan objects in the plan. It is expected that each object will be scanned by `VM::VMScanning::scan_object()`. /// If the object is in a policy that has some policy specific behaviors for scanning (e.g. mark lines in Immix), /// this method should also invoke those policy specific methods. - fn scan_object( - &self, - tls: VMWorkerThread, - object: ObjectReference, - edge_visitor: &mut EV, - ); + fn post_scan_object(&self, object: ObjectReference); /// Whether objects in this plan may move. If any of the spaces used by the plan may move objects, this should /// return true. @@ -226,7 +221,9 @@ impl + PlanTraceObject let tls = worker.tls; let mut closure = ObjectsClosure::::new(worker); for object in &self.buffer { - self.plan.scan_object(tls, *object, &mut closure); + use crate::vm::Scanning; + ::VMScanning::scan_object(tls, *object, &mut closure); + self.plan.post_scan_object(*object); } } trace!("PlanScanObjects End"); diff --git a/src/policy/gc_work.rs b/src/policy/gc_work.rs index 756e9f2d03..8520c437bd 100644 --- a/src/policy/gc_work.rs +++ b/src/policy/gc_work.rs @@ -7,9 +7,9 @@ pub const DEFAULT_TRACE: u8 = u8::MAX; use crate::plan::TransitiveClosure; use crate::scheduler::GCWorker; use crate::util::copy::CopySemantics; -use crate::util::opaque_pointer::VMWorkerThread; + use crate::util::ObjectReference; -use crate::vm::EdgeVisitor; + use crate::vm::VMBinding; /// This trait defines policy-specific behavior for tracing objects. @@ -28,17 +28,11 @@ pub trait PolicyTraceObject { worker: &mut GCWorker, ) -> ObjectReference; - /// Policy-specific scan object. The implementation needs to guarantee that - /// they will call `VM::VMScanning::scan_object()` (or `Self::vm_scan_object()`) besides any space-specific work for the object. + /// Policy-specific post-scan-object hook. It is called after scanning + /// each object in this space. #[inline(always)] - fn scan_object( - &self, - tls: VMWorkerThread, - object: ObjectReference, - edge_visitor: &mut EV, - ) { - use crate::vm::Scanning; - VM::VMScanning::scan_object(tls, object, edge_visitor) + fn post_scan_object(&self, _object: ObjectReference) { + // Do nothing. } /// Return whether the policy moves objects. diff --git a/src/policy/immix/immixspace.rs b/src/policy/immix/immixspace.rs index 503680df91..d3f1c7ba5b 100644 --- a/src/policy/immix/immixspace.rs +++ b/src/policy/immix/immixspace.rs @@ -135,13 +135,7 @@ impl crate::policy::gc_work::PolicyTraceObject for ImmixSpace } #[inline(always)] - fn scan_object( - &self, - tls: VMWorkerThread, - object: ObjectReference, - edge_visitor: &mut EV, - ) { - VM::VMScanning::scan_object(tls, object, edge_visitor); + fn post_scan_object(&self, object: ObjectReference) { if super::MARK_LINE_AT_SCAN_TIME && !super::BLOCK_ONLY { debug_assert!(self.in_space(object)); self.mark_lines(object); From d5bcfa42e6eeff29b0c5363c077fb4609592862d Mon Sep 17 00:00:00 2001 From: Yi Lin Date: Mon, 9 May 2022 10:23:19 +1000 Subject: [PATCH 18/22] post_scan_hook -> post_scan. Update comments. --- macros/trace_object/src/lib.rs | 8 ++++---- src/plan/generational/immix/global.rs | 2 +- src/plan/immix/global.rs | 2 +- src/plan/transitive_closure.rs | 8 +++++--- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/macros/trace_object/src/lib.rs b/macros/trace_object/src/lib.rs index c3db273b5c..9d11652f4c 100644 --- a/macros/trace_object/src/lib.rs +++ b/macros/trace_object/src/lib.rs @@ -20,10 +20,10 @@ mod derive_impl; /// * add `#[fallback_trace]` to the parent plan if the plan is composed with other plans (or parent plans). /// For example, `GenImmix` is composed with `Gen`, `Gen` is composed with `CommonPlan`, `CommonPlan` is composed /// with `BasePlan`. -/// * add `#[post_scan_hook]` to any space field that has some policy-specific post_scan_object(). For objects in those spaces, +/// * add `#[post_scan]` to any space field that has some policy-specific post_scan_object(). For objects in those spaces, /// `post_scan_object()` in the policy will be called after `VM::VMScanning::scan_object()`. #[proc_macro_error] -#[proc_macro_derive(PlanTraceObject, attributes(trace, post_scan_hook, fallback_trace))] +#[proc_macro_derive(PlanTraceObject, attributes(trace, post_scan, fallback_trace))] pub fn derive_plan_trace_object(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); let ident = input.ident; @@ -34,11 +34,11 @@ pub fn derive_plan_trace_object(input: TokenStream) -> TokenStream { .. }) = input.data { let spaces = util::get_fields_with_attribute(fields, "trace"); - let post_scan_hook_spaces = util::get_fields_with_attribute(fields, "post_scan_hook"); + let post_scan_spaces = util::get_fields_with_attribute(fields, "post_scan"); let fallback = util::get_unique_field_with_attribute(fields, "fallback_trace"); let trace_object_function = derive_impl::generate_trace_object(&spaces, &fallback, &ty_generics); - let post_scan_object_function = derive_impl::generate_post_scan_object(&post_scan_hook_spaces, &ty_generics); + let post_scan_object_function = derive_impl::generate_post_scan_object(&post_scan_spaces, &ty_generics); let may_move_objects_function = derive_impl::generate_may_move_objects(&spaces, &fallback, &ty_generics); quote!{ impl #impl_generics crate::plan::transitive_closure::PlanTraceObject #ty_generics for #ident #ty_generics #where_clause { diff --git a/src/plan/generational/immix/global.rs b/src/plan/generational/immix/global.rs index 6c27b811df..6470c1ff95 100644 --- a/src/plan/generational/immix/global.rs +++ b/src/plan/generational/immix/global.rs @@ -38,7 +38,7 @@ pub struct GenImmix { #[fallback_trace] pub gen: Gen, /// An immix space as the mature space. - #[post_scan_hook] + #[post_scan] #[trace(CopySemantics::Mature)] pub immix: ImmixSpace, /// Whether the last GC was a defrag GC for the immix space. diff --git a/src/plan/immix/global.rs b/src/plan/immix/global.rs index 482230ba54..d852424f5f 100644 --- a/src/plan/immix/global.rs +++ b/src/plan/immix/global.rs @@ -30,7 +30,7 @@ use mmtk_macro_trace_object::PlanTraceObject; #[derive(PlanTraceObject)] pub struct Immix { - #[post_scan_hook] + #[post_scan] #[trace(CopySemantics::DefaultCopy)] pub immix_space: ImmixSpace, #[fallback_trace] diff --git a/src/plan/transitive_closure.rs b/src/plan/transitive_closure.rs index 07b5f280e6..ee9d74bedc 100644 --- a/src/plan/transitive_closure.rs +++ b/src/plan/transitive_closure.rs @@ -94,9 +94,11 @@ pub trait PlanTraceObject { worker: &mut GCWorker, ) -> ObjectReference; - /// Scan objects in the plan. It is expected that each object will be scanned by `VM::VMScanning::scan_object()`. - /// If the object is in a policy that has some policy specific behaviors for scanning (e.g. mark lines in Immix), - /// this method should also invoke those policy specific methods. + /// Post-scan objects in the plan. Each object is scanned by `VM::VMScanning::scan_object()`, and this function + /// will be called after the `VM::VMScanning::scan_object()` as a hook to invoke possible policy post scan method. + /// If a plan does not have any policy that needs post scan, this method can be implemented as empty. + /// If a plan has a policy that has some policy specific behaviors for scanning (e.g. mark lines in Immix), + /// this method should also invoke those policy specific methods for objects in that space. fn post_scan_object(&self, object: ObjectReference); /// Whether objects in this plan may move. If any of the spaces used by the plan may move objects, this should From f15bc9c5b6388961ec99263f49e17953be7aef03 Mon Sep 17 00:00:00 2001 From: Yi Lin Date: Wed, 11 May 2022 16:50:40 +1000 Subject: [PATCH 19/22] Minor cleanup based on reviews. --- docs/tutorial/src/mygc/ss/collection.md | 12 ++++---- macros/trace_object/src/derive_impl.rs | 8 +++-- macros/trace_object/src/lib.rs | 15 +++++---- macros/trace_object/src/util.rs | 4 +-- src/plan/transitive_closure.rs | 41 ++++++++++--------------- src/policy/immix/immixspace.rs | 4 +-- 6 files changed, 40 insertions(+), 44 deletions(-) diff --git a/docs/tutorial/src/mygc/ss/collection.md b/docs/tutorial/src/mygc/ss/collection.md index 85e6fa10e2..fb9c5e53ca 100644 --- a/docs/tutorial/src/mygc/ss/collection.md +++ b/docs/tutorial/src/mygc/ss/collection.md @@ -165,8 +165,8 @@ are. ### Approach 1: Use `SFTProcessEdges` [`SFTProcessEdges`](https://www.mmtk.io/mmtk-core/mmtk/scheduler/gc_work/struct.SFTProcessEdges.html) dispatches -trace objects to each space through [Space Function Table (SFT)](https://www.mmtk.io/mmtk-core/mmtk/policy/space/trait.SFT.html). -As long as all the policies in a plan provides an implementation of `sft_trace_object()` in their SFT implementation, +the tracing of objects to their respective spaces through [Space Function Table (SFT)](https://www.mmtk.io/mmtk-core/mmtk/policy/space/trait.SFT.html). +As long as all the policies in a plan provide an implementation of `sft_trace_object()` in their SFT implementations, the plan can use `SFTProcessEdges`. Currently most policies provide an implementation for `sft_trace_object()`, except mark compact and immix. Those two policies use multiple GC traces, and due to the limitation of SFT, SFT does not allow multiple `sft_trace_object()` for a policy. @@ -177,7 +177,7 @@ multiple `sft_trace_object()` for a policy. `PlanProcessEdges` is another general `ProcessEdgesWork` implementation that can be used by most plans. When a plan implements the [`PlanTraceObject`](https://www.mmtk.io/mmtk-core/mmtk/plan/transitive_closure/trait.PlanTraceObject.html), -they can use `PlanProcessEdges`. +it can use `PlanProcessEdges`. You can manually provide an implementation of `PlanTraceObject` for `MyGC`. But you can also use the derive macro MMTK provides, and the macro will generate an implementation of `PlanTraceObject`: @@ -202,7 +202,7 @@ Once this is done, you can specify `PlanProcessEdges` as the `ProcessEdgesWorkTy ### Approach 3: Implement your own `ProcessEdgesWork` Apart from the two approaches above, you can always implement your own `ProcessEdgesWork`. This is -an overkill for simple plans like semi space, but is probably necessary for more complex plans. +an overkill for simple plans like semi space, but might be necessary for more complex plans. We discuss how to implement it for `MyGC`. Create a struct `MyGCProcessEdges` in the `gc_work` module. It includes a reference @@ -213,11 +213,11 @@ back to the plan, and a `ProcessEdgesBase` field: Implement `ProcessEdgesWork` for `MyGCProcessEdges`. As most methods in the trait have a default implemetation, we only need to implement `new()` and `trace_object()` for our plan. However, this -may not be true when you are implement for other GC plans. It would be better to check the default +may not be true when you implement it for other GC plans. It would be better to check the default implementation of `ProcessEdgesWork`. For `trace_object()`, what we do is similar to the approach above (except that we need to write the code -ourselves rather than letting the macro to generate it for us). We try figure out +ourselves rather than letting the macro to generate it for us). We try to figure out which space the object is in, and invoke `trace_object()` for the object on that space. If the object is not in any of the semi spaces in the plan, we forward the call to `CommonPlan`. ```rust diff --git a/macros/trace_object/src/derive_impl.rs b/macros/trace_object/src/derive_impl.rs index 9114f28029..2c7fb889f1 100644 --- a/macros/trace_object/src/derive_impl.rs +++ b/macros/trace_object/src/derive_impl.rs @@ -4,7 +4,7 @@ use proc_macro2::TokenStream as TokenStream2; use crate::util; -pub fn generate_trace_object<'a>( +pub(crate) fn generate_trace_object<'a>( space_fields: &[&'a Field], parent_field: &Option<&'a Field>, ty_generics: &TypeGenerics, @@ -53,6 +53,7 @@ pub fn generate_trace_object<'a>( }; quote! { + #[inline(always)] fn trace_object(&self, __mmtk_trace: &mut T, __mmtk_objref: crate::util::ObjectReference, __mmtk_worker: &mut crate::scheduler::GCWorker) -> crate::util::ObjectReference { use crate::policy::space::Space; use crate::policy::gc_work::PolicyTraceObject; @@ -63,7 +64,7 @@ pub fn generate_trace_object<'a>( } } -pub fn generate_post_scan_object<'a>( +pub(crate) fn generate_post_scan_object<'a>( post_scan_object_fields: &[&'a Field], ty_generics: &TypeGenerics, ) -> TokenStream2 { @@ -90,7 +91,7 @@ pub fn generate_post_scan_object<'a>( // The generated function needs to be inlined and constant folded. Otherwise, there will be a huge // performance penalty. -pub fn generate_may_move_objects<'a>( +pub(crate) fn generate_may_move_objects<'a>( space_fields: &[&'a Field], parent_field: &Option<&'a Field>, ty_generics: &TypeGenerics, @@ -115,6 +116,7 @@ pub fn generate_may_move_objects<'a>( }; quote! { + #[inline(always)] fn may_move_objects() -> bool { use crate::policy::gc_work::PolicyTraceObject; use crate::plan::transitive_closure::PlanTraceObject; diff --git a/macros/trace_object/src/lib.rs b/macros/trace_object/src/lib.rs index 9d11652f4c..0d42d15d10 100644 --- a/macros/trace_object/src/lib.rs +++ b/macros/trace_object/src/lib.rs @@ -13,7 +13,10 @@ use syn::DeriveInput; mod util; mod derive_impl; -/// Generally a plan needs to add these attributes in order for the macro to work: +/// Generally a plan needs to add these attributes in order for the macro to work. The macro will +/// generate an implementation of `PlanTraceObject` for the plan. With `PlanTraceObject`, the plan use +/// `PlanProcessEdges` for GC tracing. The attributes only affects code generation in the macro, thus +/// only affects the generated `PlanTraceObject` implementation. /// * add `#[derive(PlanTraceObject)]` to the plan struct. /// * add `#[trace]` to each space field the plan struct has. If the policy is a copying policy, /// it needs to further specify the copy semantic (`#[trace(CopySemantics::X)]`) @@ -42,13 +45,10 @@ pub fn derive_plan_trace_object(input: TokenStream) -> TokenStream { let may_move_objects_function = derive_impl::generate_may_move_objects(&spaces, &fallback, &ty_generics); quote!{ impl #impl_generics crate::plan::transitive_closure::PlanTraceObject #ty_generics for #ident #ty_generics #where_clause { - #[inline(always)] #trace_object_function - #[inline(always)] #post_scan_object_function - #[inline(always)] #may_move_objects_function } } @@ -56,8 +56,11 @@ pub fn derive_plan_trace_object(input: TokenStream) -> TokenStream { abort_call_site!("`#[derive(PlanTraceObject)]` only supports structs with named fields.") }; - // Debug the output - // println!("{}", output.to_token_stream()); + // Debug the output - use the following code to debug the generated code (when cargo exapand is not working) + // { + // use quote::ToTokens; + // println!("{}", output.to_token_stream()); + // } output.into() } diff --git a/macros/trace_object/src/util.rs b/macros/trace_object/src/util.rs index a45a82dff0..ed6f53d880 100644 --- a/macros/trace_object/src/util.rs +++ b/macros/trace_object/src/util.rs @@ -29,11 +29,11 @@ pub fn get_unique_field_with_attribute<'f>( ) -> Option<&'f Field> { let mut result = None; - 'each_field: for field in fields.named.iter() { + for field in fields.named.iter() { if let Some(attr) = get_field_attribute(field, attr_name) { if result.is_none() { result = Some(field); - continue 'each_field; + continue; } else { let span = attr.path.span(); abort! { span, "At most one field in a struct can have the #[{}] attribute.", attr_name }; diff --git a/src/plan/transitive_closure.rs b/src/plan/transitive_closure.rs index ee9d74bedc..d36b5cab74 100644 --- a/src/plan/transitive_closure.rs +++ b/src/plan/transitive_closure.rs @@ -87,6 +87,11 @@ pub trait PlanTraceObject { /// Trace objects in the plan. Generally one needs to figure out /// which space an object resides in, and invokes the corresponding policy /// trace object method. + /// + /// Arguments: + /// * `trace`: the current transitive closure + /// * `object`: the object to trace. This is a non-nullable object reference. + /// * `worker`: the GC worker that is tracing this object. fn trace_object( &self, trace: &mut T, @@ -115,18 +120,15 @@ use std::ops::{Deref, DerefMut}; /// `PlanTraceObject` can use this work packet for tracing objects. pub struct PlanProcessEdges< VM: VMBinding, - P: 'static + Plan + PlanTraceObject + Sync, + P: Plan + PlanTraceObject, const KIND: TraceKind, > { plan: &'static P, base: ProcessEdgesBase, } -impl< - VM: VMBinding, - P: 'static + PlanTraceObject + Plan + Sync, - const KIND: TraceKind, - > ProcessEdgesWork for PlanProcessEdges +impl + Plan, const KIND: TraceKind> ProcessEdgesWork + for PlanProcessEdges { type VM = VM; @@ -161,11 +163,8 @@ impl< } // Impl Deref/DerefMut to ProcessEdgesBase for PlanProcessEdges -impl< - VM: VMBinding, - P: 'static + PlanTraceObject + Plan + Sync, - const KIND: TraceKind, - > Deref for PlanProcessEdges +impl + Plan, const KIND: TraceKind> Deref + for PlanProcessEdges { type Target = ProcessEdgesBase; #[inline] @@ -174,11 +173,8 @@ impl< } } -impl< - VM: VMBinding, - P: 'static + PlanTraceObject + Plan + Sync, - const KIND: TraceKind, - > DerefMut for PlanProcessEdges +impl + Plan, const KIND: TraceKind> DerefMut + for PlanProcessEdges { #[inline] fn deref_mut(&mut self) -> &mut Self::Target { @@ -190,10 +186,7 @@ use std::marker::PhantomData; /// This provides an implementation of scanning objects work. Each object will be scanned by calling `scan_object()` /// in `PlanTraceObject`. -pub struct PlanScanObjects< - E: ProcessEdgesWork, - P: 'static + Plan + PlanTraceObject + Sync, -> { +pub struct PlanScanObjects + PlanTraceObject> { plan: &'static P, buffer: Vec, #[allow(dead_code)] @@ -201,9 +194,7 @@ pub struct PlanScanObjects< phantom: PhantomData, } -impl + PlanTraceObject + Sync> - PlanScanObjects -{ +impl + PlanTraceObject> PlanScanObjects { pub fn new(plan: &'static P, buffer: Vec, concurrent: bool) -> Self { Self { plan, @@ -214,8 +205,8 @@ impl + PlanTraceObject } } -impl + PlanTraceObject + Sync> - GCWork for PlanScanObjects +impl + PlanTraceObject> GCWork + for PlanScanObjects { fn do_work(&mut self, worker: &mut GCWorker, _mmtk: &'static MMTK) { trace!("PlanScanObjects"); diff --git a/src/policy/immix/immixspace.rs b/src/policy/immix/immixspace.rs index 5fca31d21b..a40df63cb5 100644 --- a/src/policy/immix/immixspace.rs +++ b/src/policy/immix/immixspace.rs @@ -118,7 +118,7 @@ impl Space for ImmixSpace { impl crate::policy::gc_work::PolicyTraceObject for ImmixSpace { #[inline(always)] - fn trace_object( + fn trace_object( &self, trace: &mut T, object: ObjectReference, @@ -143,7 +143,7 @@ impl crate::policy::gc_work::PolicyTraceObject for ImmixSpace } #[inline(always)] - fn may_move_objects() -> bool { + fn may_move_objects() -> bool { if KIND == TRACE_KIND_DEFRAG { true } else if KIND == TRACE_KIND_FAST { From d2ef949b9caf9e3a4cb90f6949838a93b1aff3ba Mon Sep 17 00:00:00 2001 From: Yi Lin Date: Thu, 12 May 2022 09:46:49 +1000 Subject: [PATCH 20/22] Move PlanTraceObject to plan::global, move PlanProcessEdge/PlanScanObjects to scheduler::gc_work. --- docs/tutorial/code/mygc_semispace/gc_work.rs | 2 +- macros/trace_object/src/derive_impl.rs | 4 +- macros/trace_object/src/lib.rs | 12 +- src/plan/generational/copying/gc_work.rs | 2 +- src/plan/generational/immix/gc_work.rs | 2 +- src/plan/global.rs | 37 +++++ src/plan/immix/gc_work.rs | 2 +- src/plan/markcompact/gc_work.rs | 2 +- src/plan/marksweep/gc_work.rs | 2 +- src/plan/mod.rs | 3 +- src/plan/pageprotect/gc_work.rs | 2 +- src/plan/semispace/gc_work.rs | 2 +- src/plan/transitive_closure.rs | 151 ------------------- src/scheduler/gc_work.rs | 113 +++++++++++++- 14 files changed, 167 insertions(+), 169 deletions(-) diff --git a/docs/tutorial/code/mygc_semispace/gc_work.rs b/docs/tutorial/code/mygc_semispace/gc_work.rs index ffc7ae396c..0a36583bf4 100644 --- a/docs/tutorial/code/mygc_semispace/gc_work.rs +++ b/docs/tutorial/code/mygc_semispace/gc_work.rs @@ -15,7 +15,7 @@ impl crate::scheduler::GCWorkContext for MyGCWorkContext { // ANCHOR_END: workcontext_sft // ANCHOR: workcontext_plan -use crate::plan::transitive_closure::PlanProcessEdges; +use crate::scheduler::gc_work::PlanProcessEdges; use crate::policy::gc_work::DEFAULT_TRACE; pub struct MyGCWorkContext2(std::marker::PhantomData); impl crate::scheduler::GCWorkContext for MyGCWorkContext2 { diff --git a/macros/trace_object/src/derive_impl.rs b/macros/trace_object/src/derive_impl.rs index 2c7fb889f1..13914efd0e 100644 --- a/macros/trace_object/src/derive_impl.rs +++ b/macros/trace_object/src/derive_impl.rs @@ -57,7 +57,7 @@ pub(crate) fn generate_trace_object<'a>( fn trace_object(&self, __mmtk_trace: &mut T, __mmtk_objref: crate::util::ObjectReference, __mmtk_worker: &mut crate::scheduler::GCWorker) -> crate::util::ObjectReference { use crate::policy::space::Space; use crate::policy::gc_work::PolicyTraceObject; - use crate::plan::transitive_closure::PlanTraceObject; + use crate::plan::PlanTraceObject; #(#space_field_handler)* #parent_field_delegator } @@ -119,7 +119,7 @@ pub(crate) fn generate_may_move_objects<'a>( #[inline(always)] fn may_move_objects() -> bool { use crate::policy::gc_work::PolicyTraceObject; - use crate::plan::transitive_closure::PlanTraceObject; + use crate::plan::PlanTraceObject; false #(#space_handlers)* #parent_handler } diff --git a/macros/trace_object/src/lib.rs b/macros/trace_object/src/lib.rs index 0d42d15d10..dd23ce6952 100644 --- a/macros/trace_object/src/lib.rs +++ b/macros/trace_object/src/lib.rs @@ -13,6 +13,8 @@ use syn::DeriveInput; mod util; mod derive_impl; +const DEBUG_MACRO_OUTPUT: bool = false; + /// Generally a plan needs to add these attributes in order for the macro to work. The macro will /// generate an implementation of `PlanTraceObject` for the plan. With `PlanTraceObject`, the plan use /// `PlanProcessEdges` for GC tracing. The attributes only affects code generation in the macro, thus @@ -44,7 +46,7 @@ pub fn derive_plan_trace_object(input: TokenStream) -> TokenStream { let post_scan_object_function = derive_impl::generate_post_scan_object(&post_scan_spaces, &ty_generics); let may_move_objects_function = derive_impl::generate_may_move_objects(&spaces, &fallback, &ty_generics); quote!{ - impl #impl_generics crate::plan::transitive_closure::PlanTraceObject #ty_generics for #ident #ty_generics #where_clause { + impl #impl_generics crate::plan::PlanTraceObject #ty_generics for #ident #ty_generics #where_clause { #trace_object_function #post_scan_object_function @@ -57,10 +59,10 @@ pub fn derive_plan_trace_object(input: TokenStream) -> TokenStream { }; // Debug the output - use the following code to debug the generated code (when cargo exapand is not working) - // { - // use quote::ToTokens; - // println!("{}", output.to_token_stream()); - // } + if DEBUG_MACRO_OUTPUT { + use quote::ToTokens; + println!("{}", output.to_token_stream()); + } output.into() } diff --git a/src/plan/generational/copying/gc_work.rs b/src/plan/generational/copying/gc_work.rs index 7b1bca1007..d8f245d3fd 100644 --- a/src/plan/generational/copying/gc_work.rs +++ b/src/plan/generational/copying/gc_work.rs @@ -2,8 +2,8 @@ use super::global::GenCopy; use crate::plan::generational::gc_work::GenNurseryProcessEdges; use crate::vm::*; -use crate::plan::transitive_closure::PlanProcessEdges; use crate::policy::gc_work::DEFAULT_TRACE; +use crate::scheduler::gc_work::PlanProcessEdges; pub struct GenCopyNurseryGCWorkContext(std::marker::PhantomData); impl crate::scheduler::GCWorkContext for GenCopyNurseryGCWorkContext { diff --git a/src/plan/generational/immix/gc_work.rs b/src/plan/generational/immix/gc_work.rs index 801cc59e4a..1b4af605e0 100644 --- a/src/plan/generational/immix/gc_work.rs +++ b/src/plan/generational/immix/gc_work.rs @@ -1,7 +1,7 @@ use super::global::GenImmix; use crate::plan::generational::gc_work::GenNurseryProcessEdges; -use crate::plan::transitive_closure::PlanProcessEdges; use crate::policy::gc_work::TraceKind; +use crate::scheduler::gc_work::PlanProcessEdges; use crate::vm::VMBinding; pub struct GenImmixNurseryGCWorkContext(std::marker::PhantomData); diff --git a/src/plan/global.rs b/src/plan/global.rs index e473971bb4..bdd6bc6b1d 100644 --- a/src/plan/global.rs +++ b/src/plan/global.rs @@ -956,6 +956,43 @@ impl CommonPlan { } } +use crate::policy::gc_work::TraceKind; +use crate::vm::VMBinding; + +/// A plan that uses `PlanProcessEdges` needs to provide an implementation for this trait. +/// Generally a plan does not need to manually implement this trait. Instead, we provide +/// a procedural macro that helps generate an implementation. Please check `macros/trace_object`. +/// +/// A plan could also manually implement this trait. For the sake of performance, the implementation +/// of this trait should mark methods as `[inline(always)]`. +pub trait PlanTraceObject { + /// Trace objects in the plan. Generally one needs to figure out + /// which space an object resides in, and invokes the corresponding policy + /// trace object method. + /// + /// Arguments: + /// * `trace`: the current transitive closure + /// * `object`: the object to trace. This is a non-nullable object reference. + /// * `worker`: the GC worker that is tracing this object. + fn trace_object( + &self, + trace: &mut T, + object: ObjectReference, + worker: &mut GCWorker, + ) -> ObjectReference; + + /// Post-scan objects in the plan. Each object is scanned by `VM::VMScanning::scan_object()`, and this function + /// will be called after the `VM::VMScanning::scan_object()` as a hook to invoke possible policy post scan method. + /// If a plan does not have any policy that needs post scan, this method can be implemented as empty. + /// If a plan has a policy that has some policy specific behaviors for scanning (e.g. mark lines in Immix), + /// this method should also invoke those policy specific methods for objects in that space. + fn post_scan_object(&self, object: ObjectReference); + + /// Whether objects in this plan may move. If any of the spaces used by the plan may move objects, this should + /// return true. + fn may_move_objects() -> bool; +} + use enum_map::Enum; /// Allocation semantics that MMTk provides. /// Each allocation request requires a desired semantic for the object to allocate. diff --git a/src/plan/immix/gc_work.rs b/src/plan/immix/gc_work.rs index dfbdcc4fd8..0696086fc1 100644 --- a/src/plan/immix/gc_work.rs +++ b/src/plan/immix/gc_work.rs @@ -1,6 +1,6 @@ use super::global::Immix; -use crate::plan::transitive_closure::PlanProcessEdges; use crate::policy::gc_work::TraceKind; +use crate::scheduler::gc_work::PlanProcessEdges; use crate::vm::VMBinding; pub(super) struct ImmixGCWorkContext( diff --git a/src/plan/markcompact/gc_work.rs b/src/plan/markcompact/gc_work.rs index 40578c612d..d7f76df34e 100644 --- a/src/plan/markcompact/gc_work.rs +++ b/src/plan/markcompact/gc_work.rs @@ -1,7 +1,7 @@ use super::global::MarkCompact; -use crate::plan::transitive_closure::PlanProcessEdges; use crate::policy::markcompactspace::MarkCompactSpace; use crate::policy::markcompactspace::{TRACE_KIND_FORWARD, TRACE_KIND_MARK}; +use crate::scheduler::gc_work::PlanProcessEdges; use crate::scheduler::gc_work::*; use crate::scheduler::GCWork; use crate::scheduler::GCWorker; diff --git a/src/plan/marksweep/gc_work.rs b/src/plan/marksweep/gc_work.rs index 1c667ff967..01933edebd 100644 --- a/src/plan/marksweep/gc_work.rs +++ b/src/plan/marksweep/gc_work.rs @@ -69,8 +69,8 @@ impl GCWork for MSSweepChunks { } pub struct MSGCWorkContext(std::marker::PhantomData); -use crate::plan::transitive_closure::PlanProcessEdges; use crate::policy::gc_work::DEFAULT_TRACE; +use crate::scheduler::gc_work::PlanProcessEdges; impl crate::scheduler::GCWorkContext for MSGCWorkContext { type VM = VM; diff --git a/src/plan/mod.rs b/src/plan/mod.rs index 6a58eceef4..cd667c7c99 100644 --- a/src/plan/mod.rs +++ b/src/plan/mod.rs @@ -25,6 +25,7 @@ pub(crate) use global::create_plan; pub use global::AllocationSemantics; pub(crate) use global::GcStatus; pub use global::Plan; +pub(crate) use global::PlanTraceObject; mod mutator_context; pub use mutator_context::Mutator; @@ -37,7 +38,7 @@ pub use plan_constraints::DEFAULT_PLAN_CONSTRAINTS; mod tracelocal; pub use tracelocal::TraceLocal; -pub(crate) mod transitive_closure; +mod transitive_closure; pub use transitive_closure::{ObjectsClosure, TransitiveClosure}; mod generational; diff --git a/src/plan/pageprotect/gc_work.rs b/src/plan/pageprotect/gc_work.rs index 46a507795d..c962eae190 100644 --- a/src/plan/pageprotect/gc_work.rs +++ b/src/plan/pageprotect/gc_work.rs @@ -1,6 +1,6 @@ use super::global::PageProtect; -use crate::plan::transitive_closure::PlanProcessEdges; use crate::policy::gc_work::DEFAULT_TRACE; +use crate::scheduler::gc_work::PlanProcessEdges; use crate::vm::VMBinding; pub struct PPGCWorkContext(std::marker::PhantomData); diff --git a/src/plan/semispace/gc_work.rs b/src/plan/semispace/gc_work.rs index 5ee6b184c8..9f02c73eea 100644 --- a/src/plan/semispace/gc_work.rs +++ b/src/plan/semispace/gc_work.rs @@ -1,6 +1,6 @@ use super::global::SemiSpace; -use crate::plan::transitive_closure::PlanProcessEdges; use crate::policy::gc_work::DEFAULT_TRACE; +use crate::scheduler::gc_work::PlanProcessEdges; use crate::vm::VMBinding; pub struct SSGCWorkContext(std::marker::PhantomData); diff --git a/src/plan/transitive_closure.rs b/src/plan/transitive_closure.rs index d36b5cab74..de7492f909 100644 --- a/src/plan/transitive_closure.rs +++ b/src/plan/transitive_closure.rs @@ -71,154 +71,3 @@ impl<'a, E: ProcessEdgesWork> Drop for ObjectsClosure<'a, E> { self.flush(); } } - -use crate::policy::gc_work::TraceKind; -use crate::scheduler::GCWork; - -use crate::vm::VMBinding; - -/// A plan that uses `PlanProcessEdges` needs to provide an implementation for this trait. -/// Generally a plan does not need to manually implement this trait. Instead, we provide -/// a procedural macro that helps generate an implementation. Please check `macros/trace_object`. -/// -/// A plan could also manually implement this trait. For the sake of performance, the implementation -/// of this trait should mark methods as `[inline(always)]`. -pub trait PlanTraceObject { - /// Trace objects in the plan. Generally one needs to figure out - /// which space an object resides in, and invokes the corresponding policy - /// trace object method. - /// - /// Arguments: - /// * `trace`: the current transitive closure - /// * `object`: the object to trace. This is a non-nullable object reference. - /// * `worker`: the GC worker that is tracing this object. - fn trace_object( - &self, - trace: &mut T, - object: ObjectReference, - worker: &mut GCWorker, - ) -> ObjectReference; - - /// Post-scan objects in the plan. Each object is scanned by `VM::VMScanning::scan_object()`, and this function - /// will be called after the `VM::VMScanning::scan_object()` as a hook to invoke possible policy post scan method. - /// If a plan does not have any policy that needs post scan, this method can be implemented as empty. - /// If a plan has a policy that has some policy specific behaviors for scanning (e.g. mark lines in Immix), - /// this method should also invoke those policy specific methods for objects in that space. - fn post_scan_object(&self, object: ObjectReference); - - /// Whether objects in this plan may move. If any of the spaces used by the plan may move objects, this should - /// return true. - fn may_move_objects() -> bool; -} - -use crate::mmtk::MMTK; -use crate::plan::Plan; -use crate::scheduler::gc_work::ProcessEdgesBase; -use std::ops::{Deref, DerefMut}; - -/// This provides an implementation of [`ProcessEdgesWork`](scheduler/gc_work/ProcessEdgesWork). A plan that implements -/// `PlanTraceObject` can use this work packet for tracing objects. -pub struct PlanProcessEdges< - VM: VMBinding, - P: Plan + PlanTraceObject, - const KIND: TraceKind, -> { - plan: &'static P, - base: ProcessEdgesBase, -} - -impl + Plan, const KIND: TraceKind> ProcessEdgesWork - for PlanProcessEdges -{ - type VM = VM; - - fn new(edges: Vec
, roots: bool, mmtk: &'static MMTK) -> Self { - let base = ProcessEdgesBase::new(edges, roots, mmtk); - let plan = base.plan().downcast_ref::

().unwrap(); - Self { plan, base } - } - - #[inline(always)] - fn create_scan_work(&self, nodes: Vec) -> Box> { - Box::new(PlanScanObjects::::new(self.plan, nodes, false)) - } - - #[inline(always)] - fn trace_object(&mut self, object: ObjectReference) -> ObjectReference { - if object.is_null() { - return object; - } - self.plan - .trace_object::(self, object, self.worker()) - } - - #[inline] - fn process_edge(&mut self, slot: Address) { - let object = unsafe { slot.load::() }; - let new_object = self.trace_object(object); - if P::may_move_objects::() { - unsafe { slot.store(new_object) }; - } - } -} - -// Impl Deref/DerefMut to ProcessEdgesBase for PlanProcessEdges -impl + Plan, const KIND: TraceKind> Deref - for PlanProcessEdges -{ - type Target = ProcessEdgesBase; - #[inline] - fn deref(&self) -> &Self::Target { - &self.base - } -} - -impl + Plan, const KIND: TraceKind> DerefMut - for PlanProcessEdges -{ - #[inline] - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.base - } -} - -use std::marker::PhantomData; - -/// This provides an implementation of scanning objects work. Each object will be scanned by calling `scan_object()` -/// in `PlanTraceObject`. -pub struct PlanScanObjects + PlanTraceObject> { - plan: &'static P, - buffer: Vec, - #[allow(dead_code)] - concurrent: bool, - phantom: PhantomData, -} - -impl + PlanTraceObject> PlanScanObjects { - pub fn new(plan: &'static P, buffer: Vec, concurrent: bool) -> Self { - Self { - plan, - buffer, - concurrent, - phantom: PhantomData, - } - } -} - -impl + PlanTraceObject> GCWork - for PlanScanObjects -{ - fn do_work(&mut self, worker: &mut GCWorker, _mmtk: &'static MMTK) { - trace!("PlanScanObjects"); - { - let tls = worker.tls; - let mut closure = ObjectsClosure::::new(worker); - for object in &self.buffer { - use crate::vm::Scanning; - ::VMScanning::scan_object(tls, *object, &mut closure); - self.plan.post_scan_object(*object); - } - } - trace!("PlanScanObjects End"); - } -} diff --git a/src/scheduler/gc_work.rs b/src/scheduler/gc_work.rs index 3cfcee70d2..71b137faa0 100644 --- a/src/scheduler/gc_work.rs +++ b/src/scheduler/gc_work.rs @@ -424,7 +424,7 @@ pub trait ProcessEdgesWork: /// Start the a scan work packet. If SCAN_OBJECTS_IMMEDIATELY, the work packet will be executed immediately, in this method. /// Otherwise, the work packet will be added the Closure work bucket and will be dispatched later by the scheduler. #[inline] - fn start_scan_work(&mut self, work_packet: Box>) { + fn start_or_dispatch_scan_work(&mut self, work_packet: Box>) { if Self::SCAN_OBJECTS_IMMEDIATELY { // We execute this `scan_objects_work` immediately. // This is expected to be a useful optimization because, @@ -454,7 +454,7 @@ pub trait ProcessEdgesWork: return; } let nodes = self.pop_nodes(); - self.start_scan_work(self.create_scan_work(nodes)); + self.start_or_dispatch_scan_work(self.create_scan_work(nodes)); } #[inline] @@ -614,3 +614,112 @@ impl GCWork for ProcessModBuf { } } } + +use crate::mmtk::MMTK; +use crate::plan::Plan; +use crate::plan::PlanTraceObject; +use crate::policy::gc_work::TraceKind; + +/// This provides an implementation of [`ProcessEdgesWork`](scheduler/gc_work/ProcessEdgesWork). A plan that implements +/// `PlanTraceObject` can use this work packet for tracing objects. +pub struct PlanProcessEdges< + VM: VMBinding, + P: Plan + PlanTraceObject, + const KIND: TraceKind, +> { + plan: &'static P, + base: ProcessEdgesBase, +} + +impl + Plan, const KIND: TraceKind> ProcessEdgesWork + for PlanProcessEdges +{ + type VM = VM; + + fn new(edges: Vec

, roots: bool, mmtk: &'static MMTK) -> Self { + let base = ProcessEdgesBase::new(edges, roots, mmtk); + let plan = base.plan().downcast_ref::

().unwrap(); + Self { plan, base } + } + + #[inline(always)] + fn create_scan_work(&self, nodes: Vec) -> Box> { + Box::new(PlanScanObjects::::new(self.plan, nodes, false)) + } + + #[inline(always)] + fn trace_object(&mut self, object: ObjectReference) -> ObjectReference { + if object.is_null() { + return object; + } + self.plan + .trace_object::(self, object, self.worker()) + } + + #[inline] + fn process_edge(&mut self, slot: Address) { + let object = unsafe { slot.load::() }; + let new_object = self.trace_object(object); + if P::may_move_objects::() { + unsafe { slot.store(new_object) }; + } + } +} + +// Impl Deref/DerefMut to ProcessEdgesBase for PlanProcessEdges +impl + Plan, const KIND: TraceKind> Deref + for PlanProcessEdges +{ + type Target = ProcessEdgesBase; + #[inline] + fn deref(&self) -> &Self::Target { + &self.base + } +} + +impl + Plan, const KIND: TraceKind> DerefMut + for PlanProcessEdges +{ + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.base + } +} + +/// This provides an implementation of scanning objects work. Each object will be scanned by calling `scan_object()` +/// in `PlanTraceObject`. +pub struct PlanScanObjects + PlanTraceObject> { + plan: &'static P, + buffer: Vec, + #[allow(dead_code)] + concurrent: bool, + phantom: PhantomData, +} + +impl + PlanTraceObject> PlanScanObjects { + pub fn new(plan: &'static P, buffer: Vec, concurrent: bool) -> Self { + Self { + plan, + buffer, + concurrent, + phantom: PhantomData, + } + } +} + +impl + PlanTraceObject> GCWork + for PlanScanObjects +{ + fn do_work(&mut self, worker: &mut GCWorker, _mmtk: &'static MMTK) { + trace!("PlanScanObjects"); + { + let tls = worker.tls; + let mut closure = ObjectsClosure::::new(worker); + for object in &self.buffer { + ::VMScanning::scan_object(tls, *object, &mut closure); + self.plan.post_scan_object(*object); + } + } + trace!("PlanScanObjects End"); + } +} From c94fcaa5ad35931fe87a330432c30f601901fc39 Mon Sep 17 00:00:00 2001 From: Yi Lin Date: Thu, 12 May 2022 09:53:51 +1000 Subject: [PATCH 21/22] Rename mmtk_macro_trace_object to mmtk_macros. Move macros/trace_object to macros/. --- Cargo.toml | 2 +- docs/tutorial/code/mygc_semispace/global.rs | 2 +- docs/tutorial/src/mygc/ss/collection.md | 2 +- macros/{trace_object => }/Cargo.toml | 5 +++-- macros/{trace_object => }/src/lib.rs | 8 ++++---- .../src/derive_impl.rs => src/plan_trace_object_impl.rs} | 0 macros/{trace_object => }/src/util.rs | 0 src/plan/generational/copying/global.rs | 2 +- src/plan/generational/global.rs | 2 +- src/plan/generational/immix/global.rs | 2 +- src/plan/global.rs | 2 +- src/plan/immix/global.rs | 2 +- src/plan/markcompact/global.rs | 2 +- src/plan/marksweep/global.rs | 2 +- src/plan/pageprotect/global.rs | 2 +- src/plan/semispace/global.rs | 2 +- 16 files changed, 19 insertions(+), 18 deletions(-) rename macros/{trace_object => }/Cargo.toml (67%) rename macros/{trace_object => }/src/lib.rs (87%) rename macros/{trace_object/src/derive_impl.rs => src/plan_trace_object_impl.rs} (100%) rename macros/{trace_object => }/src/util.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index ed34c25e92..826c6aca57 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ doctest = false [dependencies] # MMTk macros -mmtk-macro-trace-object = { path = "macros/trace_object"} +mmtk-macros = { path = "macros/"} custom_derive = "0.1" enum_derive = "0.1" diff --git a/docs/tutorial/code/mygc_semispace/global.rs b/docs/tutorial/code/mygc_semispace/global.rs index 7b87cf7638..c0c09d0b00 100644 --- a/docs/tutorial/code/mygc_semispace/global.rs +++ b/docs/tutorial/code/mygc_semispace/global.rs @@ -29,7 +29,7 @@ use std::sync::Arc; // Remove #[allow(unused_imports)]. // Remove handle_user_collection_request(). -use mmtk_macro_trace_object::PlanTraceObject; +use mmtk_macros::PlanTraceObject; // Modify // ANCHOR: plan_def diff --git a/docs/tutorial/src/mygc/ss/collection.md b/docs/tutorial/src/mygc/ss/collection.md index fb9c5e53ca..985e5e1a9c 100644 --- a/docs/tutorial/src/mygc/ss/collection.md +++ b/docs/tutorial/src/mygc/ss/collection.md @@ -181,7 +181,7 @@ it can use `PlanProcessEdges`. You can manually provide an implementation of `PlanTraceObject` for `MyGC`. But you can also use the derive macro MMTK provides, and the macro will generate an implementation of `PlanTraceObject`: -* add `#[derive(PlanTraceObject)]` for `MyGC` (import the macro properly: `use mmtk_macro_trace_object::PlanTraceObject`) +* add `#[derive(PlanTraceObject)]` for `MyGC` (import the macro properly: `use mmtk_macros::PlanTraceObject`) * add `#[trace(CopySemantics::Default)]` to both copy space fields, `copyspace0` and `copyspace1`. This tells the macro to generate trace code for both spaces, and for any copying in the spaces, use `CopySemantics::DefaultCopy` that we have configured early. * add `#[fallback_trace]` to `common`. This tells the macro that if an object is not found in any space with `#[trace]` in ths plan, diff --git a/macros/trace_object/Cargo.toml b/macros/Cargo.toml similarity index 67% rename from macros/trace_object/Cargo.toml rename to macros/Cargo.toml index 4f58ce29ba..a6668ce02a 100644 --- a/macros/trace_object/Cargo.toml +++ b/macros/Cargo.toml @@ -1,6 +1,7 @@ [package] -name = "mmtk-macro-trace-object" -version = "0.1.0" +name = "mmtk-macros" +# the macro crate uses the same version as mmtk-core +version = "0.11.0" edition = "2021" [lib] diff --git a/macros/trace_object/src/lib.rs b/macros/src/lib.rs similarity index 87% rename from macros/trace_object/src/lib.rs rename to macros/src/lib.rs index dd23ce6952..40ee8f9b01 100644 --- a/macros/trace_object/src/lib.rs +++ b/macros/src/lib.rs @@ -11,7 +11,7 @@ use quote::quote; use syn::DeriveInput; mod util; -mod derive_impl; +mod plan_trace_object_impl; const DEBUG_MACRO_OUTPUT: bool = false; @@ -42,9 +42,9 @@ pub fn derive_plan_trace_object(input: TokenStream) -> TokenStream { let post_scan_spaces = util::get_fields_with_attribute(fields, "post_scan"); let fallback = util::get_unique_field_with_attribute(fields, "fallback_trace"); - let trace_object_function = derive_impl::generate_trace_object(&spaces, &fallback, &ty_generics); - let post_scan_object_function = derive_impl::generate_post_scan_object(&post_scan_spaces, &ty_generics); - let may_move_objects_function = derive_impl::generate_may_move_objects(&spaces, &fallback, &ty_generics); + let trace_object_function = plan_trace_object_impl::generate_trace_object(&spaces, &fallback, &ty_generics); + let post_scan_object_function = plan_trace_object_impl::generate_post_scan_object(&post_scan_spaces, &ty_generics); + let may_move_objects_function = plan_trace_object_impl::generate_may_move_objects(&spaces, &fallback, &ty_generics); quote!{ impl #impl_generics crate::plan::PlanTraceObject #ty_generics for #ident #ty_generics #where_clause { #trace_object_function diff --git a/macros/trace_object/src/derive_impl.rs b/macros/src/plan_trace_object_impl.rs similarity index 100% rename from macros/trace_object/src/derive_impl.rs rename to macros/src/plan_trace_object_impl.rs diff --git a/macros/trace_object/src/util.rs b/macros/src/util.rs similarity index 100% rename from macros/trace_object/src/util.rs rename to macros/src/util.rs diff --git a/src/plan/generational/copying/global.rs b/src/plan/generational/copying/global.rs index f8bd4d53b2..3384760827 100644 --- a/src/plan/generational/copying/global.rs +++ b/src/plan/generational/copying/global.rs @@ -26,7 +26,7 @@ use enum_map::EnumMap; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; -use mmtk_macro_trace_object::PlanTraceObject; +use mmtk_macros::PlanTraceObject; #[derive(PlanTraceObject)] pub struct GenCopy { diff --git a/src/plan/generational/global.rs b/src/plan/generational/global.rs index dd64d995b5..e2ffe4a3b1 100644 --- a/src/plan/generational/global.rs +++ b/src/plan/generational/global.rs @@ -21,7 +21,7 @@ use std::sync::atomic::AtomicBool; use std::sync::atomic::Ordering; use std::sync::Arc; -use mmtk_macro_trace_object::PlanTraceObject; +use mmtk_macros::PlanTraceObject; /// Common implementation for generational plans. Each generational plan /// should include this type, and forward calls to it where possible. diff --git a/src/plan/generational/immix/global.rs b/src/plan/generational/immix/global.rs index 6470c1ff95..eb0714adf3 100644 --- a/src/plan/generational/immix/global.rs +++ b/src/plan/generational/immix/global.rs @@ -26,7 +26,7 @@ use std::sync::atomic::AtomicBool; use std::sync::atomic::Ordering; use std::sync::Arc; -use mmtk_macro_trace_object::PlanTraceObject; +use mmtk_macros::PlanTraceObject; /// Generational immix. This implements the functionality of a two-generation copying /// collector where the higher generation is an immix space. diff --git a/src/plan/global.rs b/src/plan/global.rs index bdd6bc6b1d..515023d576 100644 --- a/src/plan/global.rs +++ b/src/plan/global.rs @@ -33,7 +33,7 @@ use enum_map::EnumMap; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use std::sync::{Arc, Mutex}; -use mmtk_macro_trace_object::PlanTraceObject; +use mmtk_macros::PlanTraceObject; pub fn create_mutator( tls: VMMutatorThread, diff --git a/src/plan/immix/global.rs b/src/plan/immix/global.rs index d852424f5f..2d40d32a8b 100644 --- a/src/plan/immix/global.rs +++ b/src/plan/immix/global.rs @@ -26,7 +26,7 @@ use std::sync::Arc; use atomic::Ordering; use enum_map::EnumMap; -use mmtk_macro_trace_object::PlanTraceObject; +use mmtk_macros::PlanTraceObject; #[derive(PlanTraceObject)] pub struct Immix { diff --git a/src/plan/markcompact/global.rs b/src/plan/markcompact/global.rs index f5af3616de..c7e3f451d8 100644 --- a/src/plan/markcompact/global.rs +++ b/src/plan/markcompact/global.rs @@ -31,7 +31,7 @@ use crate::vm::VMBinding; use enum_map::EnumMap; use std::sync::Arc; -use mmtk_macro_trace_object::PlanTraceObject; +use mmtk_macros::PlanTraceObject; #[derive(PlanTraceObject)] pub struct MarkCompact { diff --git a/src/plan/marksweep/global.rs b/src/plan/marksweep/global.rs index 4cbc4dcb68..c06ac5f5c8 100644 --- a/src/plan/marksweep/global.rs +++ b/src/plan/marksweep/global.rs @@ -25,7 +25,7 @@ use std::sync::Arc; use enum_map::EnumMap; -use mmtk_macro_trace_object::PlanTraceObject; +use mmtk_macros::PlanTraceObject; #[derive(PlanTraceObject)] pub struct MarkSweep { diff --git a/src/plan/pageprotect/global.rs b/src/plan/pageprotect/global.rs index 41ebc221bd..6f5b8b47e2 100644 --- a/src/plan/pageprotect/global.rs +++ b/src/plan/pageprotect/global.rs @@ -22,7 +22,7 @@ use crate::{ use enum_map::EnumMap; use std::sync::Arc; -use mmtk_macro_trace_object::PlanTraceObject; +use mmtk_macros::PlanTraceObject; #[derive(PlanTraceObject)] pub struct PageProtect { diff --git a/src/plan/semispace/global.rs b/src/plan/semispace/global.rs index 3aedb03b25..6b8db36328 100644 --- a/src/plan/semispace/global.rs +++ b/src/plan/semispace/global.rs @@ -22,7 +22,7 @@ use crate::{plan::global::BasePlan, vm::VMBinding}; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; -use mmtk_macro_trace_object::PlanTraceObject; +use mmtk_macros::PlanTraceObject; use enum_map::EnumMap; From 0505dbea62c90be659aee84e15982e6f355e843f Mon Sep 17 00:00:00 2001 From: Yi Lin Date: Thu, 12 May 2022 10:09:32 +1000 Subject: [PATCH 22/22] Add crate metadata for macros. Add mmtk-macro publish in github action. --- .github/workflows/cargo-publish.yml | 8 ++++++-- Cargo.toml | 2 +- macros/Cargo.toml | 4 ++++ 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.github/workflows/cargo-publish.yml b/.github/workflows/cargo-publish.yml index 5b7e9f8956..5a10621747 100644 --- a/.github/workflows/cargo-publish.yml +++ b/.github/workflows/cargo-publish.yml @@ -19,5 +19,9 @@ jobs: override: true - name: Cargo login run: cargo login ${{ secrets.CI_CARGO_LOGIN }} - - name: Cargo publish - run: cargo publish + - name: Publish sub crates + run: | + cargo publish --manifest-path=macros/Cargo.toml + - name: Public mmtk-core + run: | + cargo publish diff --git a/Cargo.toml b/Cargo.toml index 826c6aca57..ee9d6d5b56 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ doctest = false [dependencies] # MMTk macros -mmtk-macros = { path = "macros/"} +mmtk-macros = { version = "0.11.0", path = "macros/" } custom_derive = "0.1" enum_derive = "0.1" diff --git a/macros/Cargo.toml b/macros/Cargo.toml index a6668ce02a..efb0839006 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -3,6 +3,10 @@ name = "mmtk-macros" # the macro crate uses the same version as mmtk-core version = "0.11.0" edition = "2021" +license = "MIT OR Apache-2.0" +description = "MMTk macros provides procedural macros used by mmtk-core." +homepage = "https://www.mmtk.io" +repository = "https://github.com/mmtk/mmtk-core/tree/master/macros" [lib] proc-macro = true