Skip to content

Commit e85d997

Browse files
authored
Introduce SFTProcessEdges (#542)
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).
1 parent 42262c2 commit e85d997

File tree

28 files changed

+328
-374
lines changed

28 files changed

+328
-374
lines changed
Lines changed: 0 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,14 @@
11
// ANCHOR: imports
22
use super::global::MyGC;
3-
use crate::policy::space::Space;
43
use crate::scheduler::gc_work::*;
5-
use crate::util::copy::CopySemantics;
6-
use crate::util::{Address, ObjectReference};
74
use crate::vm::VMBinding;
8-
use crate::MMTK;
95
use std::ops::{Deref, DerefMut};
106
// ANCHOR_END: imports
117

12-
// ANCHOR: mygc_process_edges
13-
pub struct MyGCProcessEdges<VM: VMBinding> {
14-
plan: &'static MyGC<VM>,
15-
base: ProcessEdgesBase<VM>,
16-
}
17-
// ANCHOR_END: mygc_process_edges
18-
19-
impl<VM: VMBinding> MyGCProcessEdges<VM> {
20-
fn mygc(&self) -> &'static MyGC<VM> {
21-
self.plan
22-
}
23-
}
24-
25-
impl<VM:VMBinding> ProcessEdgesWork for MyGCProcessEdges<VM> {
26-
type VM = VM;
27-
// ANCHOR: mygc_process_edges_new
28-
fn new(edges: Vec<Address>, roots: bool, mmtk: &'static MMTK<VM>) -> Self {
29-
let base = ProcessEdgesBase::new(edges, roots, mmtk);
30-
let plan = base.plan().downcast_ref::<MyGC<VM>>().unwrap();
31-
Self { base, plan }
32-
}
33-
// ANCHOR_END: mygc_process_edges_new
34-
35-
// ANCHOR: trace_object
36-
#[inline]
37-
fn trace_object(&mut self, object: ObjectReference) -> ObjectReference {
38-
if object.is_null() {
39-
return object;
40-
}
41-
if self.mygc().tospace().in_space(object) {
42-
self.mygc().tospace().trace_object::<Self>(
43-
self,
44-
object,
45-
CopySemantics::DefaultCopy,
46-
self.worker(),
47-
)
48-
} else if self.mygc().fromspace().in_space(object) {
49-
self.mygc().fromspace().trace_object::<Self>(
50-
self,
51-
object,
52-
CopySemantics::DefaultCopy,
53-
self.worker(),
54-
)
55-
} else {
56-
self.mygc().common.trace_object::<Self>(self, object)
57-
}
58-
}
59-
// ANCHOR_END: trace_object
60-
}
61-
62-
// ANCHOR: deref
63-
impl<VM: VMBinding> Deref for MyGCProcessEdges<VM> {
64-
type Target = ProcessEdgesBase<VM>;
65-
#[inline]
66-
fn deref(&self) -> &Self::Target {
67-
&self.base
68-
}
69-
}
70-
71-
impl<VM: VMBinding> DerefMut for MyGCProcessEdges<VM> {
72-
#[inline]
73-
fn deref_mut(&mut self) -> &mut Self::Target {
74-
&mut self.base
75-
}
76-
}
77-
// ANCHOR_END: deref
78-
798
// ANCHOR: workcontext
809
pub struct MyGCWorkContext<VM: VMBinding>(std::marker::PhantomData<VM>);
8110
impl<VM: VMBinding> crate::scheduler::GCWorkContext for MyGCWorkContext<VM> {
8211
type VM = VM;
8312
type PlanType = MyGC<VM>;
84-
type ProcessEdgesWorkType = MyGCProcessEdges<VM>;
8513
}
8614
// ANCHOR_END: workcontext

docs/tutorial/code/mygc_semispace/global.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,10 @@ impl<VM: VMBinding> Plan for MyGC<VM> {
116116
let hi = self.hi.load(Ordering::SeqCst);
117117
self.copyspace0.prepare(hi);
118118
self.copyspace1.prepare(!hi);
119+
120+
self.fromspace_mut()
121+
.set_copy_for_sft_trace(Some(CopySemantics::DefaultCopy));
122+
self.tospace_mut().set_copy_for_sft_trace(None);
119123
}
120124
// ANCHOR_END: prepare
121125

@@ -229,5 +233,21 @@ impl<VM: VMBinding> MyGC<VM> {
229233
&self.copyspace1
230234
}
231235
}
236+
237+
pub fn tospace_mut(&mut self) -> &mut CopySpace<VM> {
238+
if self.hi.load(Ordering::SeqCst) {
239+
&mut self.copyspace1
240+
} else {
241+
&mut self.copyspace0
242+
}
243+
}
244+
245+
pub fn fromspace_mut(&mut self) -> &mut CopySpace<VM> {
246+
if self.hi.load(Ordering::SeqCst) {
247+
&mut self.copyspace0
248+
} else {
249+
&mut self.copyspace1
250+
}
251+
}
232252
// ANCHOR_END: plan_space_access
233253
}

docs/tutorial/src/mygc/ss/alloc.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,10 @@ and return a reference to the tospace and fromspace respectively.
113113
`tospace()` (see below) returns a reference to the tospace,
114114
and `fromspace()` returns a reference to the fromspace.
115115

116+
We also add another two helper methods to get `tospace_mut(&mut self)`
117+
and `fromspace_mut(&mut self)`. Those will be used later when we implement
118+
collection for our GC plan.
119+
116120
```rust
117121
{{#include ../../../code/mygc_semispace/global.rs:plan_space_access}}
118122
```

docs/tutorial/src/mygc/ss/collection.md

Lines changed: 14 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -29,37 +29,6 @@ space here.
2929
{{#include ../../../code/mygc_semispace/global.rs:create_copy_config}}
3030
```
3131

32-
## MyGCProcessEdges
33-
34-
We will create the tracing work packet for our GC.
35-
At the moment, none of the files in the plan are suited for implementing this
36-
GC packet. So, we need to add a new file to hold the `MyGCProcessEdges`.
37-
38-
Make a new file under `mygc`, called `gc_work.rs`.
39-
In `mod.rs`, import `gc_work` as a module by adding the line `mod gc_work`.
40-
In `gc_work.rs`, add the following import statements:
41-
```rust
42-
{{#include ../../../code/mygc_semispace/gc_work.rs:imports}}
43-
```
44-
45-
Add a new public structure, `MyGCProcessEdges`, with the type parameter
46-
`<VM:VMBinding>`. It will hold an instance of `ProcessEdgesBase` and
47-
`MyGC`. This is the core part for tracing objects in the `MyGC` plan.
48-
49-
```rust
50-
{{#include ../../../code/mygc_semispace/gc_work.rs:mygc_process_edges}}
51-
```
52-
53-
Add a new implementations block
54-
`impl<VM:VMBinding> ProcessEdgesWork for MyGCProcessEdges<VM>`.
55-
Similarly to before, set `ProcessEdgesWork`'s associate type `VM` to
56-
the type parameter of `MyGCProcessEdges`, `VM`: `type VM:VM`.
57-
Add a new constructor, `new()`.
58-
59-
```rust
60-
{{#include ../../../code/mygc_semispace/gc_work.rs:mygc_process_edges_new}}
61-
```
62-
6332
## Introduce collection to MyGC plan
6433

6534
Add a new method to `Plan for MyGC`, `schedule_collection()`. This function
@@ -73,7 +42,12 @@ Though you can add those work packets by yourself, `GCWorkScheduler` provides a
7342
method `schedule_common_work()` that will add common work packets for you.
7443

7544
To use `schedule_common_work()`, first we need to create a type `MyGCWorkContext`
76-
and implement the trait `GCWorkContext` for it. We create this type in `gc_work.rs`.
45+
and implement the trait `GCWorkContext` for it. We create `gc_work.rs` and add the
46+
following implementation. Note that we do not set a specific `ProcessEdgesWorkType`
47+
and we will use the default [`SFTProcessEdges`](https://www.mmtk.io/mmtk-core/mmtk/scheduler/gc_work/struct.SFTProcessEdges.html),
48+
which is a general work packet that a plan can use to trace objects. For plans
49+
like semispace, `SFTProcessEdges` is sufficient. For more complex GC plans,
50+
one can create and write their own work packet that implements the `ProcessEdgesWork` trait.
7751

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

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

11294
As we flip tospace for the plan, we also need to rebind the copy context
Lines changed: 2 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,8 @@
11
use super::global::GenCopy;
2-
use crate::plan::generational::gc_work::GenNurseryProcessEdges;
3-
use crate::policy::space::Space;
4-
use crate::scheduler::gc_work::*;
5-
use crate::util::copy::*;
6-
use crate::util::{Address, ObjectReference};
72
use crate::vm::*;
8-
use crate::MMTK;
9-
use std::ops::{Deref, DerefMut};
103

11-
pub struct GenCopyMatureProcessEdges<VM: VMBinding> {
12-
plan: &'static GenCopy<VM>,
13-
base: ProcessEdgesBase<VM>,
14-
}
15-
16-
impl<VM: VMBinding> GenCopyMatureProcessEdges<VM> {
17-
fn gencopy(&self) -> &'static GenCopy<VM> {
18-
self.plan
19-
}
20-
}
21-
22-
impl<VM: VMBinding> ProcessEdgesWork for GenCopyMatureProcessEdges<VM> {
23-
type VM = VM;
24-
25-
fn new(edges: Vec<Address>, roots: bool, mmtk: &'static MMTK<VM>) -> Self {
26-
let base = ProcessEdgesBase::new(edges, roots, mmtk);
27-
let plan = base.plan().downcast_ref::<GenCopy<VM>>().unwrap();
28-
Self { plan, base }
29-
}
30-
#[inline]
31-
fn trace_object(&mut self, object: ObjectReference) -> ObjectReference {
32-
if object.is_null() {
33-
return object;
34-
}
35-
// Evacuate mature objects; don't trace objects if they are in to-space
36-
if self.gencopy().tospace().in_space(object) {
37-
return object;
38-
} else if self.gencopy().fromspace().in_space(object) {
39-
return self.gencopy().fromspace().trace_object::<Self>(
40-
self,
41-
object,
42-
CopySemantics::Mature,
43-
self.worker(),
44-
);
45-
}
46-
47-
self.gencopy()
48-
.gen
49-
.trace_object_full_heap::<Self>(self, object, self.worker())
50-
}
51-
}
52-
53-
impl<VM: VMBinding> Deref for GenCopyMatureProcessEdges<VM> {
54-
type Target = ProcessEdgesBase<VM>;
55-
fn deref(&self) -> &Self::Target {
56-
&self.base
57-
}
58-
}
59-
60-
impl<VM: VMBinding> DerefMut for GenCopyMatureProcessEdges<VM> {
61-
fn deref_mut(&mut self) -> &mut Self::Target {
62-
&mut self.base
63-
}
64-
}
65-
66-
pub struct GenCopyNurseryGCWorkContext<VM: VMBinding>(std::marker::PhantomData<VM>);
67-
impl<VM: VMBinding> crate::scheduler::GCWorkContext for GenCopyNurseryGCWorkContext<VM> {
68-
type VM = VM;
69-
type PlanType = GenCopy<VM>;
70-
type ProcessEdgesWorkType = GenNurseryProcessEdges<VM>;
71-
}
72-
73-
pub(super) struct GenCopyMatureGCWorkContext<VM: VMBinding>(std::marker::PhantomData<VM>);
74-
impl<VM: VMBinding> crate::scheduler::GCWorkContext for GenCopyMatureGCWorkContext<VM> {
4+
pub struct GenCopyGCWorkContext<VM: VMBinding>(std::marker::PhantomData<VM>);
5+
impl<VM: VMBinding> crate::scheduler::GCWorkContext for GenCopyGCWorkContext<VM> {
756
type VM = VM;
767
type PlanType = GenCopy<VM>;
77-
type ProcessEdgesWorkType = GenCopyMatureProcessEdges<VM>;
788
}

src/plan/generational/copying/global.rs

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use super::gc_work::{GenCopyMatureGCWorkContext, GenCopyNurseryGCWorkContext};
1+
use super::gc_work::GenCopyGCWorkContext;
22
use super::mutator::ALLOCATOR_MAPPING;
33
use crate::plan::generational::global::Gen;
44
use crate::plan::global::BasePlan;
@@ -46,7 +46,7 @@ impl<VM: VMBinding> Plan for GenCopy<VM> {
4646
CopyConfig {
4747
copy_mapping: enum_map! {
4848
CopySemantics::Mature => CopySelector::CopySpace(0),
49-
CopySemantics::PromoteMature => CopySelector::CopySpace(0),
49+
CopySemantics::PromoteToMature => CopySelector::CopySpace(0),
5050
_ => CopySelector::Unused,
5151
},
5252
space_mapping: vec![
@@ -79,16 +79,10 @@ impl<VM: VMBinding> Plan for GenCopy<VM> {
7979
}
8080

8181
fn schedule_collection(&'static self, scheduler: &GCWorkScheduler<VM>) {
82-
let is_full_heap = self.request_full_heap_collection();
82+
let _ = self.request_full_heap_collection();
8383
self.base().set_collection_kind::<Self>(self);
8484
self.base().set_gc_status(GcStatus::GcPrepare);
85-
if !is_full_heap {
86-
debug!("Nursery GC");
87-
scheduler.schedule_common_work::<GenCopyNurseryGCWorkContext<VM>>(self);
88-
} else {
89-
debug!("Full heap GC");
90-
scheduler.schedule_common_work::<GenCopyMatureGCWorkContext<VM>>(self);
91-
}
85+
scheduler.schedule_common_work::<GenCopyGCWorkContext<VM>>(self);
9286
}
9387

9488
fn get_allocator_mapping(&self) -> &'static EnumMap<AllocationSemantics, AllocatorSelector> {
@@ -105,6 +99,10 @@ impl<VM: VMBinding> Plan for GenCopy<VM> {
10599
let hi = self.hi.load(Ordering::SeqCst);
106100
self.copyspace0.prepare(hi);
107101
self.copyspace1.prepare(!hi);
102+
103+
self.fromspace_mut()
104+
.set_copy_for_sft_trace(Some(CopySemantics::Mature));
105+
self.tospace_mut().set_copy_for_sft_trace(None);
108106
}
109107

110108
fn prepare_worker(&self, worker: &mut GCWorker<Self::VM>) {
@@ -230,11 +228,27 @@ impl<VM: VMBinding> GenCopy<VM> {
230228
}
231229
}
232230

231+
pub fn tospace_mut(&mut self) -> &mut CopySpace<VM> {
232+
if self.hi.load(Ordering::SeqCst) {
233+
&mut self.copyspace1
234+
} else {
235+
&mut self.copyspace0
236+
}
237+
}
238+
233239
pub fn fromspace(&self) -> &CopySpace<VM> {
234240
if self.hi.load(Ordering::SeqCst) {
235241
&self.copyspace0
236242
} else {
237243
&self.copyspace1
238244
}
239245
}
246+
247+
pub fn fromspace_mut(&mut self) -> &mut CopySpace<VM> {
248+
if self.hi.load(Ordering::SeqCst) {
249+
&mut self.copyspace0
250+
} else {
251+
&mut self.copyspace1
252+
}
253+
}
240254
}

src/plan/generational/gc_work.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ use crate::vm::*;
66
use crate::MMTK;
77
use std::ops::{Deref, DerefMut};
88

9-
/// Process edges for a nursery GC. A generatinoal plan should use this type for a nursery GC.
9+
/// Process edges for a nursery GC. This type is provided if a generational plan does not use
10+
/// [`crate::scheduler::gc_work::SFTProcessEdges`]. If a plan uses `SFTProcessEdges`,
11+
/// it does not need to use this type.
1012
pub struct GenNurseryProcessEdges<VM: VMBinding> {
1113
gen: &'static Gen<VM>,
1214
base: ProcessEdgesBase<VM>,

src/plan/generational/global.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ impl<VM: VMBinding> Gen<VM> {
8484
let full_heap = !self.is_current_gc_nursery();
8585
self.common.prepare(tls, full_heap);
8686
self.nursery.prepare(true);
87+
self.nursery
88+
.set_copy_for_sft_trace(Some(CopySemantics::PromoteToMature));
8789
}
8890

8991
/// Release Gen. This should be called by a single thread in GC release work.
@@ -172,7 +174,7 @@ impl<VM: VMBinding> Gen<VM> {
172174
return self.nursery.trace_object::<T>(
173175
trace,
174176
object,
175-
CopySemantics::PromoteMature,
177+
Some(CopySemantics::PromoteToMature),
176178
worker,
177179
);
178180
}
@@ -191,7 +193,7 @@ impl<VM: VMBinding> Gen<VM> {
191193
return self.nursery.trace_object::<T>(
192194
trace,
193195
object,
194-
CopySemantics::PromoteMature,
196+
Some(CopySemantics::PromoteToMature),
195197
worker,
196198
);
197199
}

src/plan/generational/immix/global.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ impl<VM: VMBinding> Plan for GenImmix<VM> {
6666
use enum_map::enum_map;
6767
CopyConfig {
6868
copy_mapping: enum_map! {
69-
CopySemantics::PromoteMature => CopySelector::Immix(0),
69+
CopySemantics::PromoteToMature => CopySelector::Immix(0),
7070
CopySemantics::Mature => CopySelector::Immix(0),
7171
_ => CopySelector::Unused,
7272
},

0 commit comments

Comments
 (0)