Skip to content

Commit

Permalink
Introduce SFTProcessEdges (#542)
Browse files Browse the repository at this point in the history
This PR adds a general implementation for `ProcessEdges`, called `SFTProcessEdges`. It uses `SFT.sft_trace_object()` for each policy. A plan does not need to implement their own process edges work packet if they use `SFTProcessEdges`. This PR greatly simplifies the GC work implementation for most GC plans. This PR closes #110, and it is an important step towards #258.

Currently only Immix (including GenImmix) and mark compact uses custom process edges. All the other plans use `SFTProcessEdges`.

This PR
* adds `SFT.sft_trace_object()`.
* adds `Space.set_copy_for_sft_trace()` to set copy context for each space, which is used when we invoke `sft_trace_object()`.
* adds `SFTProcessEdges`, and use it for most plans (except immix/genimmix and mark compact).
  • Loading branch information
qinsoon authored Feb 17, 2022
1 parent 42262c2 commit e85d997
Show file tree
Hide file tree
Showing 28 changed files with 328 additions and 374 deletions.
72 changes: 0 additions & 72 deletions docs/tutorial/code/mygc_semispace/gc_work.rs
Original file line number Diff line number Diff line change
@@ -1,86 +1,14 @@
// ANCHOR: imports
use super::global::MyGC;
use crate::policy::space::Space;
use crate::scheduler::gc_work::*;
use crate::util::copy::CopySemantics;
use crate::util::{Address, ObjectReference};
use crate::vm::VMBinding;
use crate::MMTK;
use std::ops::{Deref, DerefMut};
// ANCHOR_END: imports

// ANCHOR: mygc_process_edges
pub struct MyGCProcessEdges<VM: VMBinding> {
plan: &'static MyGC<VM>,
base: ProcessEdgesBase<VM>,
}
// ANCHOR_END: mygc_process_edges

impl<VM: VMBinding> MyGCProcessEdges<VM> {
fn mygc(&self) -> &'static MyGC<VM> {
self.plan
}
}

impl<VM:VMBinding> ProcessEdgesWork for MyGCProcessEdges<VM> {
type VM = VM;
// ANCHOR: mygc_process_edges_new
fn new(edges: Vec<Address>, roots: bool, mmtk: &'static MMTK<VM>) -> Self {
let base = ProcessEdgesBase::new(edges, roots, mmtk);
let plan = base.plan().downcast_ref::<MyGC<VM>>().unwrap();
Self { base, plan }
}
// ANCHOR_END: mygc_process_edges_new

// ANCHOR: trace_object
#[inline]
fn trace_object(&mut self, object: ObjectReference) -> ObjectReference {
if object.is_null() {
return object;
}
if self.mygc().tospace().in_space(object) {
self.mygc().tospace().trace_object::<Self>(
self,
object,
CopySemantics::DefaultCopy,
self.worker(),
)
} else if self.mygc().fromspace().in_space(object) {
self.mygc().fromspace().trace_object::<Self>(
self,
object,
CopySemantics::DefaultCopy,
self.worker(),
)
} else {
self.mygc().common.trace_object::<Self>(self, object)
}
}
// ANCHOR_END: trace_object
}

// ANCHOR: deref
impl<VM: VMBinding> Deref for MyGCProcessEdges<VM> {
type Target = ProcessEdgesBase<VM>;
#[inline]
fn deref(&self) -> &Self::Target {
&self.base
}
}

impl<VM: VMBinding> DerefMut for MyGCProcessEdges<VM> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.base
}
}
// ANCHOR_END: deref

// ANCHOR: workcontext
pub struct MyGCWorkContext<VM: VMBinding>(std::marker::PhantomData<VM>);
impl<VM: VMBinding> crate::scheduler::GCWorkContext for MyGCWorkContext<VM> {
type VM = VM;
type PlanType = MyGC<VM>;
type ProcessEdgesWorkType = MyGCProcessEdges<VM>;
}
// ANCHOR_END: workcontext
20 changes: 20 additions & 0 deletions docs/tutorial/code/mygc_semispace/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@ impl<VM: VMBinding> Plan for MyGC<VM> {
let hi = self.hi.load(Ordering::SeqCst);
self.copyspace0.prepare(hi);
self.copyspace1.prepare(!hi);

self.fromspace_mut()
.set_copy_for_sft_trace(Some(CopySemantics::DefaultCopy));
self.tospace_mut().set_copy_for_sft_trace(None);
}
// ANCHOR_END: prepare

Expand Down Expand Up @@ -229,5 +233,21 @@ impl<VM: VMBinding> MyGC<VM> {
&self.copyspace1
}
}

pub fn tospace_mut(&mut self) -> &mut CopySpace<VM> {
if self.hi.load(Ordering::SeqCst) {
&mut self.copyspace1
} else {
&mut self.copyspace0
}
}

pub fn fromspace_mut(&mut self) -> &mut CopySpace<VM> {
if self.hi.load(Ordering::SeqCst) {
&mut self.copyspace0
} else {
&mut self.copyspace1
}
}
// ANCHOR_END: plan_space_access
}
4 changes: 4 additions & 0 deletions docs/tutorial/src/mygc/ss/alloc.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ and return a reference to the tospace and fromspace respectively.
`tospace()` (see below) returns a reference to the tospace,
and `fromspace()` returns a reference to the fromspace.

We also add another two helper methods to get `tospace_mut(&mut self)`
and `fromspace_mut(&mut self)`. Those will be used later when we implement
collection for our GC plan.

```rust
{{#include ../../../code/mygc_semispace/global.rs:plan_space_access}}
```
Expand Down
46 changes: 14 additions & 32 deletions docs/tutorial/src/mygc/ss/collection.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,37 +29,6 @@ space here.
{{#include ../../../code/mygc_semispace/global.rs:create_copy_config}}
```

## MyGCProcessEdges

We will create the tracing work packet for our GC.
At the moment, none of the files in the plan are suited for implementing this
GC packet. So, we need to add a new file to hold the `MyGCProcessEdges`.

Make a new file under `mygc`, called `gc_work.rs`.
In `mod.rs`, import `gc_work` as a module by adding the line `mod gc_work`.
In `gc_work.rs`, add the following import statements:
```rust
{{#include ../../../code/mygc_semispace/gc_work.rs:imports}}
```

Add a new public structure, `MyGCProcessEdges`, with the type parameter
`<VM:VMBinding>`. It will hold an instance of `ProcessEdgesBase` and
`MyGC`. This is the core part for tracing objects in the `MyGC` plan.

```rust
{{#include ../../../code/mygc_semispace/gc_work.rs:mygc_process_edges}}
```

Add a new implementations block
`impl<VM:VMBinding> ProcessEdgesWork for MyGCProcessEdges<VM>`.
Similarly to before, set `ProcessEdgesWork`'s associate type `VM` to
the type parameter of `MyGCProcessEdges`, `VM`: `type VM:VM`.
Add a new constructor, `new()`.

```rust
{{#include ../../../code/mygc_semispace/gc_work.rs:mygc_process_edges_new}}
```

## Introduce collection to MyGC plan

Add a new method to `Plan for MyGC`, `schedule_collection()`. This function
Expand All @@ -73,7 +42,12 @@ Though you can add those work packets by yourself, `GCWorkScheduler` provides a
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 this type in `gc_work.rs`.
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),
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.

```rust
{{#include ../../../code/mygc_semispace/gc_work.rs:workcontext}}
Expand Down Expand Up @@ -107,6 +81,14 @@ This function is called at the start of a collection. It prepares the two
spaces in the common plan, flips the definitions for which space is 'to'
and which is 'from', then prepares the copyspaces with the new definition.

Note that we call `set_copy_for_sft_trace()` for both spaces. This step is required
when using `SFTProcessEdges` to tell the spaces which copy semantic to use for copying.
For fromspace, we use the `DefaultCopy` semantic, which we have defined earlier in our `CopyConfig`.
So for objects in fromspace that need to be copied, the policy will use the copy context that binds with
`DefaultCopy` (which allocates to the tospace) in the GC worker. For tospace, we set its
copy semantics to `None`, as we do not expect to copy objects from tospace, and if that ever happens,
we will simply panic.

### Prepare worker

As we flip tospace for the plan, we also need to rebind the copy context
Expand Down
74 changes: 2 additions & 72 deletions src/plan/generational/copying/gc_work.rs
Original file line number Diff line number Diff line change
@@ -1,78 +1,8 @@
use super::global::GenCopy;
use crate::plan::generational::gc_work::GenNurseryProcessEdges;
use crate::policy::space::Space;
use crate::scheduler::gc_work::*;
use crate::util::copy::*;
use crate::util::{Address, ObjectReference};
use crate::vm::*;
use crate::MMTK;
use std::ops::{Deref, DerefMut};

pub struct GenCopyMatureProcessEdges<VM: VMBinding> {
plan: &'static GenCopy<VM>,
base: ProcessEdgesBase<VM>,
}

impl<VM: VMBinding> GenCopyMatureProcessEdges<VM> {
fn gencopy(&self) -> &'static GenCopy<VM> {
self.plan
}
}

impl<VM: VMBinding> ProcessEdgesWork for GenCopyMatureProcessEdges<VM> {
type VM = VM;

fn new(edges: Vec<Address>, roots: bool, mmtk: &'static MMTK<VM>) -> Self {
let base = ProcessEdgesBase::new(edges, roots, mmtk);
let plan = base.plan().downcast_ref::<GenCopy<VM>>().unwrap();
Self { plan, base }
}
#[inline]
fn trace_object(&mut self, object: ObjectReference) -> ObjectReference {
if object.is_null() {
return object;
}
// Evacuate mature objects; don't trace objects if they are in to-space
if self.gencopy().tospace().in_space(object) {
return object;
} else if self.gencopy().fromspace().in_space(object) {
return self.gencopy().fromspace().trace_object::<Self>(
self,
object,
CopySemantics::Mature,
self.worker(),
);
}

self.gencopy()
.gen
.trace_object_full_heap::<Self>(self, object, self.worker())
}
}

impl<VM: VMBinding> Deref for GenCopyMatureProcessEdges<VM> {
type Target = ProcessEdgesBase<VM>;
fn deref(&self) -> &Self::Target {
&self.base
}
}

impl<VM: VMBinding> DerefMut for GenCopyMatureProcessEdges<VM> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.base
}
}

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<VM>;
}

pub(super) struct GenCopyMatureGCWorkContext<VM: VMBinding>(std::marker::PhantomData<VM>);
impl<VM: VMBinding> crate::scheduler::GCWorkContext for GenCopyMatureGCWorkContext<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 = GenCopyMatureProcessEdges<VM>;
}
34 changes: 24 additions & 10 deletions src/plan/generational/copying/global.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::gc_work::{GenCopyMatureGCWorkContext, GenCopyNurseryGCWorkContext};
use super::gc_work::GenCopyGCWorkContext;
use super::mutator::ALLOCATOR_MAPPING;
use crate::plan::generational::global::Gen;
use crate::plan::global::BasePlan;
Expand Down Expand Up @@ -46,7 +46,7 @@ impl<VM: VMBinding> Plan for GenCopy<VM> {
CopyConfig {
copy_mapping: enum_map! {
CopySemantics::Mature => CopySelector::CopySpace(0),
CopySemantics::PromoteMature => CopySelector::CopySpace(0),
CopySemantics::PromoteToMature => CopySelector::CopySpace(0),
_ => CopySelector::Unused,
},
space_mapping: vec![
Expand Down Expand Up @@ -79,16 +79,10 @@ impl<VM: VMBinding> Plan for GenCopy<VM> {
}

fn schedule_collection(&'static self, scheduler: &GCWorkScheduler<VM>) {
let is_full_heap = self.request_full_heap_collection();
let _ = self.request_full_heap_collection();
self.base().set_collection_kind::<Self>(self);
self.base().set_gc_status(GcStatus::GcPrepare);
if !is_full_heap {
debug!("Nursery GC");
scheduler.schedule_common_work::<GenCopyNurseryGCWorkContext<VM>>(self);
} else {
debug!("Full heap GC");
scheduler.schedule_common_work::<GenCopyMatureGCWorkContext<VM>>(self);
}
scheduler.schedule_common_work::<GenCopyGCWorkContext<VM>>(self);
}

fn get_allocator_mapping(&self) -> &'static EnumMap<AllocationSemantics, AllocatorSelector> {
Expand All @@ -105,6 +99,10 @@ impl<VM: VMBinding> Plan for GenCopy<VM> {
let hi = self.hi.load(Ordering::SeqCst);
self.copyspace0.prepare(hi);
self.copyspace1.prepare(!hi);

self.fromspace_mut()
.set_copy_for_sft_trace(Some(CopySemantics::Mature));
self.tospace_mut().set_copy_for_sft_trace(None);
}

fn prepare_worker(&self, worker: &mut GCWorker<Self::VM>) {
Expand Down Expand Up @@ -230,11 +228,27 @@ impl<VM: VMBinding> GenCopy<VM> {
}
}

pub fn tospace_mut(&mut self) -> &mut CopySpace<VM> {
if self.hi.load(Ordering::SeqCst) {
&mut self.copyspace1
} else {
&mut self.copyspace0
}
}

pub fn fromspace(&self) -> &CopySpace<VM> {
if self.hi.load(Ordering::SeqCst) {
&self.copyspace0
} else {
&self.copyspace1
}
}

pub fn fromspace_mut(&mut self) -> &mut CopySpace<VM> {
if self.hi.load(Ordering::SeqCst) {
&mut self.copyspace0
} else {
&mut self.copyspace1
}
}
}
4 changes: 3 additions & 1 deletion src/plan/generational/gc_work.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ use crate::vm::*;
use crate::MMTK;
use std::ops::{Deref, DerefMut};

/// Process edges for a nursery GC. A generatinoal plan should use this type for a nursery GC.
/// Process edges for a nursery GC. This type is provided if a generational plan does not use
/// [`crate::scheduler::gc_work::SFTProcessEdges`]. If a plan uses `SFTProcessEdges`,
/// it does not need to use this type.
pub struct GenNurseryProcessEdges<VM: VMBinding> {
gen: &'static Gen<VM>,
base: ProcessEdgesBase<VM>,
Expand Down
6 changes: 4 additions & 2 deletions src/plan/generational/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ impl<VM: VMBinding> Gen<VM> {
let full_heap = !self.is_current_gc_nursery();
self.common.prepare(tls, full_heap);
self.nursery.prepare(true);
self.nursery
.set_copy_for_sft_trace(Some(CopySemantics::PromoteToMature));
}

/// Release Gen. This should be called by a single thread in GC release work.
Expand Down Expand Up @@ -172,7 +174,7 @@ impl<VM: VMBinding> Gen<VM> {
return self.nursery.trace_object::<T>(
trace,
object,
CopySemantics::PromoteMature,
Some(CopySemantics::PromoteToMature),
worker,
);
}
Expand All @@ -191,7 +193,7 @@ impl<VM: VMBinding> Gen<VM> {
return self.nursery.trace_object::<T>(
trace,
object,
CopySemantics::PromoteMature,
Some(CopySemantics::PromoteToMature),
worker,
);
}
Expand Down
2 changes: 1 addition & 1 deletion src/plan/generational/immix/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ impl<VM: VMBinding> Plan for GenImmix<VM> {
use enum_map::enum_map;
CopyConfig {
copy_mapping: enum_map! {
CopySemantics::PromoteMature => CopySelector::Immix(0),
CopySemantics::PromoteToMature => CopySelector::Immix(0),
CopySemantics::Mature => CopySelector::Immix(0),
_ => CopySelector::Unused,
},
Expand Down
Loading

0 comments on commit e85d997

Please sign in to comment.