Skip to content

Commit

Permalink
This PR enables transitively pinning (TP) objects from particular roo…
Browse files Browse the repository at this point in the history
…ts for Immix/StickyImmix (#897)

- A new bucket `ImmovableClosure` has been created to trace and
transitively pin roots, i.e., roots in which no object in its transitive
closure is allowed to move.
- `RootsWorkFactory` has two new functions
`create_process_tp_edge_roots_work` and
`create_process_tp_node_roots_work` to create work to process these
roots.
- `GCWorkContext` expects an `TPProcessEdges` type, which performs the
transitively pinning trace (and is unimplemented for unsupported plans).
- `create_process_node_roots_work` creates work to process roots in the
`NodeRootsTrace` bucket, which is executed after `TPClosure`, but any
work derived from it is put into the regular `Closure` bucket, meaning
that the binding shouldn't need to pin root nodes.
- For sticky immix, currently we only support non-moving nursery
collections (`sticky_immix_non_moving_nursery`), but the solution here
is to use the `TPClosure` on the modified buffer constructed from the
write barrier (since during nursery collection we do not know if these
objects should be transitively pinned or not).

---------

Co-authored-by: Yi Lin <[email protected]>
Co-authored-by: Kunshan Wang <[email protected]>
  • Loading branch information
3 people authored Sep 11, 2023
1 parent 62f1dc9 commit 61d20e2
Show file tree
Hide file tree
Showing 29 changed files with 415 additions and 158 deletions.
13 changes: 9 additions & 4 deletions .github/scripts/replace-mmtk-dep.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,15 @@

mmtk_node = toml_data["dependencies"]["mmtk"]

print("Deleting dependencies.mmtk.git")
if "git" in mmtk_node:
del mmtk_node["git"]

# These keys may specify the locations of the dependency. Remove them.
for key in ["git", "branch", "version", "registry"]:
if key in mmtk_node:
print("Deleting dependencies.mmtk.{}".format(key))
del mmtk_node[key]
else:
print("Key dependencies.mmtk.{} does not exist. Ignored.".format(key))

# Use mmtk-core from the specified local directory.
mmtk_repo_path = os.path.realpath(args.mmtk_core_path)
print("Setting dependencies.mmtk.path to {}".format(mmtk_repo_path))
mmtk_node["path"] = mmtk_repo_path
Expand Down
6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,12 @@ object_pinning = []
# Disable any object copying in Immix. This makes Immix a non-moving policy.
immix_non_moving = []

# Disable any object copying in nursery GC for Sticky Immix while allowing other kinds of copying.
# `immix_non_moving` disables all kinds of copying in Immix, so this feature is not needed
# if `immix_non_moving` is in use.
sticky_immix_non_moving_nursery = []


# Reduce block size for ImmixSpace. This mitigates fragmentation when defrag is disabled.
immix_smaller_block = []
# Zero the unmarked lines after a GC cycle in immix. This helps debug untraced objects.
Expand Down
24 changes: 16 additions & 8 deletions docs/userguide/src/tutorial/code/mygc_semispace/gc_work.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// ANCHOR: imports
use super::global::MyGC;
use crate::scheduler::gc_work::*;
use crate::scheduler::{gc_work::*, WorkBucketStage};
use crate::vm::VMBinding;
use std::ops::{Deref, DerefMut};
// ANCHOR_END: imports
Expand All @@ -11,24 +11,26 @@ impl<VM: VMBinding> crate::scheduler::GCWorkContext for MyGCWorkContext<VM> {
type VM = VM;
type PlanType = MyGC<VM>;
type ProcessEdgesWorkType = SFTProcessEdges<Self::VM>;
type TPProcessEdges = UnsupportedProcessEdges<Self::VM>;
}
// ANCHOR_END: workcontext_sft

// ANCHOR: workcontext_plan
use crate::scheduler::gc_work::PlanProcessEdges;
use crate::policy::gc_work::DEFAULT_TRACE;
use crate::scheduler::gc_work::PlanProcessEdges;
pub struct MyGCWorkContext2<VM: VMBinding>(std::marker::PhantomData<VM>);
impl<VM: VMBinding> crate::scheduler::GCWorkContext for MyGCWorkContext2<VM> {
type VM = VM;
type PlanType = MyGC<VM>;
type ProcessEdgesWorkType = PlanProcessEdges<Self::VM, MyGC<VM>, DEFAULT_TRACE>;
type TPProcessEdges = UnsupportedProcessEdges<Self::VM>;
}
// ANCHOR_END: workcontext_plan

use crate::util::ObjectReference;
use crate::policy::space::Space;
use crate::util::copy::CopySemantics;
use crate::util::ObjectReference;
use crate::MMTK;
use crate::policy::space::Space;

// ANCHOR: mygc_process_edges
pub struct MyGCProcessEdges<VM: VMBinding> {
Expand All @@ -38,12 +40,17 @@ pub struct MyGCProcessEdges<VM: VMBinding> {
// ANCHOR_END: mygc_process_edges

// ANCHOR: mygc_process_edges_impl
impl<VM:VMBinding> ProcessEdgesWork for MyGCProcessEdges<VM> {
impl<VM: VMBinding> ProcessEdgesWork for MyGCProcessEdges<VM> {
type VM = VM;
type ScanObjectsWorkType = ScanObjects<Self>;

fn new(edges: Vec<EdgeOf<Self>>, roots: bool, mmtk: &'static MMTK<VM>) -> Self {
let base = ProcessEdgesBase::new(edges, roots, mmtk);
fn new(
edges: Vec<EdgeOf<Self>>,
roots: bool,
mmtk: &'static MMTK<VM>,
bucket: WorkBucketStage,
) -> Self {
let base = ProcessEdgesBase::new(edges, roots, mmtk, bucket);
let plan = base.plan().downcast_ref::<MyGC<VM>>().unwrap();
Self { base, plan }
}
Expand Down Expand Up @@ -74,7 +81,7 @@ impl<VM:VMBinding> ProcessEdgesWork for MyGCProcessEdges<VM> {
}

fn create_scan_work(&self, nodes: Vec<ObjectReference>, roots: bool) -> ScanObjects<Self> {
ScanObjects::<Self>::new(nodes, false, roots)
ScanObjects::<Self>::new(nodes, false, roots, self.bucket)
}
}
// ANCHOR_END: mygc_process_edges_impl
Expand All @@ -100,5 +107,6 @@ impl<VM: VMBinding> crate::scheduler::GCWorkContext for MyGCWorkContext3<VM> {
type VM = VM;
type PlanType = MyGC<VM>;
type ProcessEdgesWorkType = MyGCProcessEdges<Self::VM>;
type TPProcessEdges = UnsupportedProcessEdges<Self::VM>;
}
// ANCHOR: workcontext_mygc
4 changes: 3 additions & 1 deletion src/plan/generational/copying/gc_work.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,20 @@ use crate::plan::generational::gc_work::GenNurseryProcessEdges;
use crate::vm::*;

use crate::policy::gc_work::DEFAULT_TRACE;
use crate::scheduler::gc_work::PlanProcessEdges;
use crate::scheduler::gc_work::{PlanProcessEdges, UnsupportedProcessEdges};

pub struct GenCopyNurseryGCWorkContext<VM: VMBinding>(std::marker::PhantomData<VM>);
impl<VM: VMBinding> crate::scheduler::GCWorkContext for GenCopyNurseryGCWorkContext<VM> {
type VM = VM;
type PlanType = GenCopy<VM>;
type ProcessEdgesWorkType = GenNurseryProcessEdges<Self::VM, Self::PlanType>;
type TPProcessEdges = UnsupportedProcessEdges<VM>;
}

pub struct GenCopyGCWorkContext<VM: VMBinding>(std::marker::PhantomData<VM>);
impl<VM: VMBinding> crate::scheduler::GCWorkContext for GenCopyGCWorkContext<VM> {
type VM = VM;
type PlanType = GenCopy<VM>;
type ProcessEdgesWorkType = PlanProcessEdges<Self::VM, GenCopy<VM>, DEFAULT_TRACE>;
type TPProcessEdges = UnsupportedProcessEdges<VM>;
}
21 changes: 15 additions & 6 deletions src/plan/generational/gc_work.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use atomic::Ordering;

use crate::plan::PlanTraceObject;
use crate::scheduler::{gc_work::*, GCWork, GCWorker};
use crate::scheduler::{gc_work::*, GCWork, GCWorker, WorkBucketStage};
use crate::util::ObjectReference;
use crate::vm::edge_shape::{Edge, MemorySlice};
use crate::vm::*;
Expand All @@ -25,8 +25,13 @@ impl<VM: VMBinding, P: GenerationalPlanExt<VM> + PlanTraceObject<VM>> ProcessEdg
type VM = VM;
type ScanObjectsWorkType = PlanScanObjects<Self, P>;

fn new(edges: Vec<EdgeOf<Self>>, roots: bool, mmtk: &'static MMTK<VM>) -> Self {
let base = ProcessEdgesBase::new(edges, roots, mmtk);
fn new(
edges: Vec<EdgeOf<Self>>,
roots: bool,
mmtk: &'static MMTK<VM>,
bucket: WorkBucketStage,
) -> Self {
let base = ProcessEdgesBase::new(edges, roots, mmtk, bucket);
let plan = base.plan().downcast_ref().unwrap();
Self { plan, base }
}
Expand All @@ -51,7 +56,7 @@ impl<VM: VMBinding, P: GenerationalPlanExt<VM> + PlanTraceObject<VM>> ProcessEdg
nodes: Vec<ObjectReference>,
roots: bool,
) -> Self::ScanObjectsWorkType {
PlanScanObjects::new(self.plan, nodes, false, roots)
PlanScanObjects::new(self.plan, nodes, false, roots, self.bucket)
}
}

Expand Down Expand Up @@ -111,7 +116,7 @@ impl<E: ProcessEdgesWork> GCWork<E::VM> for ProcessModBuf<E> {
// Scan objects in the modbuf and forward pointers
let modbuf = std::mem::take(&mut self.modbuf);
GCWork::do_work(
&mut ScanObjects::<E>::new(modbuf, false, false),
&mut ScanObjects::<E>::new(modbuf, false, false, WorkBucketStage::Closure),
worker,
mmtk,
)
Expand Down Expand Up @@ -154,7 +159,11 @@ impl<E: ProcessEdgesWork> GCWork<E::VM> for ProcessRegionModBuf<E> {
}
}
// Forward entries
GCWork::do_work(&mut E::new(edges, false, mmtk), worker, mmtk)
GCWork::do_work(
&mut E::new(edges, false, mmtk, WorkBucketStage::Closure),
worker,
mmtk,
)
}
}
}
3 changes: 3 additions & 0 deletions src/plan/generational/immix/gc_work.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ use super::global::GenImmix;
use crate::plan::generational::gc_work::GenNurseryProcessEdges;
use crate::policy::gc_work::TraceKind;
use crate::scheduler::gc_work::PlanProcessEdges;
use crate::scheduler::gc_work::UnsupportedProcessEdges;
use crate::vm::VMBinding;

pub struct GenImmixNurseryGCWorkContext<VM: VMBinding>(std::marker::PhantomData<VM>);
impl<VM: VMBinding> crate::scheduler::GCWorkContext for GenImmixNurseryGCWorkContext<VM> {
type VM = VM;
type PlanType = GenImmix<VM>;
type ProcessEdgesWorkType = GenNurseryProcessEdges<VM, Self::PlanType>;
type TPProcessEdges = UnsupportedProcessEdges<VM>;
}

pub(super) struct GenImmixMatureGCWorkContext<VM: VMBinding, const KIND: TraceKind>(
Expand All @@ -20,4 +22,5 @@ impl<VM: VMBinding, const KIND: TraceKind> crate::scheduler::GCWorkContext
type VM = VM;
type PlanType = GenImmix<VM>;
type ProcessEdgesWorkType = PlanProcessEdges<VM, GenImmix<VM>, KIND>;
type TPProcessEdges = UnsupportedProcessEdges<VM>;
}
2 changes: 2 additions & 0 deletions src/plan/immix/gc_work.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use super::global::Immix;
use crate::policy::gc_work::TraceKind;
use crate::policy::gc_work::TRACE_KIND_TRANSITIVE_PIN;
use crate::scheduler::gc_work::PlanProcessEdges;
use crate::vm::VMBinding;

Expand All @@ -12,4 +13,5 @@ impl<VM: VMBinding, const KIND: TraceKind> crate::scheduler::GCWorkContext
type VM = VM;
type PlanType = Immix<VM>;
type ProcessEdgesWorkType = PlanProcessEdges<VM, Immix<VM>, KIND>;
type TPProcessEdges = PlanProcessEdges<VM, Immix<VM>, TRACE_KIND_TRANSITIVE_PIN>;
}
4 changes: 2 additions & 2 deletions src/plan/immix/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,8 @@ impl<VM: VMBinding> Immix<VM> {
/// to schedule a full heap collection. A plan must call set_collection_kind and set_gc_status before this method.
pub(crate) fn schedule_immix_full_heap_collection<
PlanType: Plan<VM = VM>,
FastContext: 'static + GCWorkContext<VM = VM, PlanType = PlanType>,
DefragContext: 'static + GCWorkContext<VM = VM, PlanType = PlanType>,
FastContext: GCWorkContext<VM = VM, PlanType = PlanType>,
DefragContext: GCWorkContext<VM = VM, PlanType = PlanType>,
>(
plan: &'static DefragContext::PlanType,
immix_space: &ImmixSpace<VM>,
Expand Down
16 changes: 13 additions & 3 deletions src/plan/markcompact/gc_work.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,13 @@ impl<VM: VMBinding> GCWork<VM> for UpdateReferences<VM> {
.get_and_clear_worker_live_bytes();

for mutator in VM::VMActivePlan::mutators() {
mmtk.scheduler.work_buckets[WorkBucketStage::SecondRoots]
.add(ScanMutatorRoots::<ForwardingProcessEdges<VM>>(mutator));
mmtk.scheduler.work_buckets[WorkBucketStage::SecondRoots].add(ScanMutatorRoots::<
MarkCompactForwardingGCWorkContext<VM>,
>(mutator));
}

mmtk.scheduler.work_buckets[WorkBucketStage::SecondRoots]
.add(ScanVMSpecificRoots::<ForwardingProcessEdges<VM>>::new());
.add(ScanVMSpecificRoots::<MarkCompactForwardingGCWorkContext<VM>>::new());
}
}

Expand Down Expand Up @@ -102,4 +103,13 @@ impl<VM: VMBinding> crate::scheduler::GCWorkContext for MarkCompactGCWorkContext
type VM = VM;
type PlanType = MarkCompact<VM>;
type ProcessEdgesWorkType = MarkingProcessEdges<VM>;
type TPProcessEdges = UnsupportedProcessEdges<VM>;
}

pub struct MarkCompactForwardingGCWorkContext<VM: VMBinding>(std::marker::PhantomData<VM>);
impl<VM: VMBinding> crate::scheduler::GCWorkContext for MarkCompactForwardingGCWorkContext<VM> {
type VM = VM;
type PlanType = MarkCompact<VM>;
type ProcessEdgesWorkType = ForwardingProcessEdges<VM>;
type TPProcessEdges = UnsupportedProcessEdges<VM>;
}
2 changes: 1 addition & 1 deletion src/plan/markcompact/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ impl<VM: VMBinding> Plan for MarkCompact<VM> {

// Stop & scan mutators (mutator scanning can happen before STW)
scheduler.work_buckets[WorkBucketStage::Unconstrained]
.add(StopMutators::<MarkingProcessEdges<VM>>::new());
.add(StopMutators::<MarkCompactGCWorkContext<VM>>::new());

// Prepare global/collectors/mutators
scheduler.work_buckets[WorkBucketStage::Prepare]
Expand Down
1 change: 1 addition & 0 deletions src/plan/marksweep/gc_work.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ impl<VM: VMBinding> crate::scheduler::GCWorkContext for MSGCWorkContext<VM> {
type VM = VM;
type PlanType = MarkSweep<VM>;
type ProcessEdgesWorkType = PlanProcessEdges<Self::VM, MarkSweep<VM>, DEFAULT_TRACE>;
type TPProcessEdges = PlanProcessEdges<Self::VM, MarkSweep<VM>, DEFAULT_TRACE>;
}
1 change: 1 addition & 0 deletions src/plan/pageprotect/gc_work.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ impl<VM: VMBinding> crate::scheduler::GCWorkContext for PPGCWorkContext<VM> {
type VM = VM;
type PlanType = PageProtect<VM>;
type ProcessEdgesWorkType = PlanProcessEdges<Self::VM, PageProtect<VM>, DEFAULT_TRACE>;
type TPProcessEdges = PlanProcessEdges<Self::VM, PageProtect<VM>, DEFAULT_TRACE>;
}
3 changes: 2 additions & 1 deletion src/plan/semispace/gc_work.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use super::global::SemiSpace;
use crate::policy::gc_work::DEFAULT_TRACE;
use crate::scheduler::gc_work::PlanProcessEdges;
use crate::scheduler::gc_work::{PlanProcessEdges, UnsupportedProcessEdges};
use crate::vm::VMBinding;

pub struct SSGCWorkContext<VM: VMBinding>(std::marker::PhantomData<VM>);
impl<VM: VMBinding> crate::scheduler::GCWorkContext for SSGCWorkContext<VM> {
type VM = VM;
type PlanType = SemiSpace<VM>;
type ProcessEdgesWorkType = PlanProcessEdges<Self::VM, SemiSpace<VM>, DEFAULT_TRACE>;
type TPProcessEdges = UnsupportedProcessEdges<VM>;
}
3 changes: 3 additions & 0 deletions src/plan/sticky/immix/gc_work.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::policy::gc_work::TraceKind;
use crate::policy::gc_work::TRACE_KIND_TRANSITIVE_PIN;
use crate::scheduler::gc_work::PlanProcessEdges;
use crate::{plan::generational::gc_work::GenNurseryProcessEdges, vm::VMBinding};

Expand All @@ -9,6 +10,7 @@ impl<VM: VMBinding> crate::scheduler::GCWorkContext for StickyImmixNurseryGCWork
type VM = VM;
type PlanType = StickyImmix<VM>;
type ProcessEdgesWorkType = GenNurseryProcessEdges<VM, Self::PlanType>;
type TPProcessEdges = GenNurseryProcessEdges<VM, Self::PlanType>;
}

pub struct StickyImmixMatureGCWorkContext<VM: VMBinding, const KIND: TraceKind>(
Expand All @@ -20,4 +22,5 @@ impl<VM: VMBinding, const KIND: TraceKind> crate::scheduler::GCWorkContext
type VM = VM;
type PlanType = StickyImmix<VM>;
type ProcessEdgesWorkType = PlanProcessEdges<VM, Self::PlanType, KIND>;
type TPProcessEdges = PlanProcessEdges<VM, Self::PlanType, TRACE_KIND_TRANSITIVE_PIN>;
}
8 changes: 5 additions & 3 deletions src/plan/tracing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,22 +75,24 @@ impl ObjectQueue for VectorQueue<ObjectReference> {
pub struct ObjectsClosure<'a, E: ProcessEdgesWork> {
buffer: VectorQueue<EdgeOf<E>>,
pub(crate) worker: &'a mut GCWorker<E::VM>,
bucket: WorkBucketStage,
}

impl<'a, E: ProcessEdgesWork> ObjectsClosure<'a, E> {
pub fn new(worker: &'a mut GCWorker<E::VM>) -> Self {
pub fn new(worker: &'a mut GCWorker<E::VM>, bucket: WorkBucketStage) -> Self {
Self {
buffer: VectorQueue::new(),
worker,
bucket,
}
}

fn flush(&mut self) {
let buf = self.buffer.take();
if !buf.is_empty() {
self.worker.add_work(
WorkBucketStage::Closure,
E::new(buf, false, self.worker.mmtk),
self.bucket,
E::new(buf, false, self.worker.mmtk, self.bucket),
);
}
}
Expand Down
5 changes: 5 additions & 0 deletions src/policy/copyspace.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::plan::{ObjectQueue, VectorObjectQueue};
use crate::policy::copy_context::PolicyCopyContext;
use crate::policy::gc_work::TRACE_KIND_TRANSITIVE_PIN;
use crate::policy::sft::GCWorkerMutRef;
use crate::policy::sft::SFT;
use crate::policy::space::{CommonSpace, Space};
Expand Down Expand Up @@ -126,6 +127,10 @@ impl<VM: VMBinding> crate::policy::gc_work::PolicyTraceObject<VM> for CopySpace<
copy: Option<CopySemantics>,
worker: &mut GCWorker<VM>,
) -> ObjectReference {
debug_assert!(
KIND != TRACE_KIND_TRANSITIVE_PIN,
"Copyspace does not support transitive pin trace."
);
self.trace_object(queue, object, copy, worker)
}

Expand Down
1 change: 1 addition & 0 deletions src/policy/gc_work.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
pub(crate) type TraceKind = u8;

pub const DEFAULT_TRACE: u8 = u8::MAX;
pub const TRACE_KIND_TRANSITIVE_PIN: u8 = DEFAULT_TRACE - 1;

use crate::plan::ObjectQueue;
use crate::scheduler::GCWorker;
Expand Down
Loading

0 comments on commit 61d20e2

Please sign in to comment.