Skip to content

Commit 0621d99

Browse files
committed
HasSpaces trait and derive macro
1 parent a2c6012 commit 0621d99

File tree

17 files changed

+152
-27
lines changed

17 files changed

+152
-27
lines changed

docs/userguide/src/tutorial/code/mygc_semispace/global.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,11 @@ use std::sync::atomic::{AtomicBool, Ordering}; // Add
2424
// Remove #[allow(unused_imports)].
2525
// Remove handle_user_collection_request().
2626

27-
use mmtk_macros::PlanTraceObject;
27+
use mmtk_macros::{HasSpaces, PlanTraceObject};
2828

2929
// Modify
3030
// ANCHOR: plan_def
31-
#[derive(PlanTraceObject)]
31+
#[derive(HasSpaces, PlanTraceObject)]
3232
pub struct MyGC<VM: VMBinding> {
3333
pub hi: AtomicBool,
3434
#[space]

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ you can implement object tracing, in this tutorial we use the macros, as it is t
7272
Make sure you import the macros. We will discuss on what those attributes mean in later sections.
7373

7474
```rust
75-
use mmtk_macros::PlanTraceObject;
75+
use mmtk_macros::{HasSpaces, PlanTraceObject};
7676
```
7777

7878
### Implement the Plan trait for MyGC
@@ -230,4 +230,4 @@ by the common plan:
230230

231231
With this, you should have the allocation working, but not garbage collection.
232232
Try building again. If you run HelloWorld or Fannkunchredux, they should
233-
work. DaCapo's lusearch should fail, as it requires garbage to be collected.
233+
work. DaCapo's lusearch should fail, as it requires garbage to be collected.

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ it can use `PlanProcessEdges`.
181181

182182
You can manually provide an implementation of `PlanTraceObject` for `MyGC`. But you can also use the derive macro MMTK provides,
183183
and the macro will generate an implementation of `PlanTraceObject`:
184-
* add `#[derive(PlanTraceObject)]` for `MyGC` (import the macro properly: `use mmtk_macros::PlanTraceObject`)
184+
* add `#[derive(HasSpaces, PlanTraceObject)]` for `MyGC` (import the macro properly: `use mmtk_macros::PlanTraceObject`)
185185
* add `#[space]
186186
#[copy_semantics(CopySemantics::Default)]` to both copy space fields, `copyspace0` and `copyspace1`. This tells the macro to generate
187187
trace code for both spaces, and for any copying in the spaces, use `CopySemantics::DefaultCopy` that we have configured early.

macros/src/has_spaces_impl.rs

+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
use proc_macro2::TokenStream as TokenStream2;
2+
use proc_macro_error::abort_call_site;
3+
use quote::quote;
4+
use syn::{DeriveInput, Field};
5+
6+
use crate::util;
7+
8+
pub fn derive(input: DeriveInput) -> TokenStream2 {
9+
let ident = input.ident;
10+
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
11+
12+
let syn::Data::Struct(syn::DataStruct {
13+
fields: syn::Fields::Named(ref fields),
14+
..
15+
}) = input.data else {
16+
abort_call_site!("`#[derive(HasSpaces)]` only supports structs with named fields.");
17+
};
18+
19+
let spaces = util::get_fields_with_attribute(fields, "space");
20+
let parent = util::get_unique_field_with_attribute(fields, "parent");
21+
22+
let items = generate_impl_items(&spaces, &parent);
23+
24+
let output = quote! {
25+
impl #impl_generics crate::plan::HasSpaces #ty_generics for #ident #ty_generics #where_clause {
26+
#items
27+
}
28+
};
29+
30+
output.into()
31+
}
32+
33+
pub(crate) fn generate_impl_items<'a>(
34+
space_fields: &[&'a Field],
35+
parent_field: &Option<&'a Field>,
36+
) -> TokenStream2 {
37+
// Currently we implement callback-style visitor methods.
38+
// Iterators should be more powerful, but is more difficult to implement.
39+
40+
let mut space_visitors = vec![];
41+
let mut space_visitors_mut = vec![];
42+
43+
for f in space_fields {
44+
let f_ident = f.ident.as_ref().unwrap();
45+
46+
let visitor = quote! {
47+
__func(&self.#f_ident);
48+
};
49+
50+
let visitor_mut = quote! {
51+
__func(&mut self.#f_ident);
52+
};
53+
54+
space_visitors.push(visitor);
55+
space_visitors_mut.push(visitor_mut);
56+
}
57+
58+
let (parent_visitor, parent_visitor_mut) = if let Some(f) = parent_field {
59+
let f_ident = f.ident.as_ref().unwrap();
60+
let visitor = quote! {
61+
self.#f_ident.for_each_space(&mut __func)
62+
};
63+
let visitor_mut = quote! {
64+
self.#f_ident.for_each_space_mut(&mut __func)
65+
};
66+
(visitor, visitor_mut)
67+
} else {
68+
(quote! {}, quote! {})
69+
};
70+
71+
let foo: Option<TokenStream2> = None;
72+
let bar: Option<TokenStream2> = Some(quote! { /* do nothing */});
73+
74+
quote! {
75+
fn for_each_space(&self, mut __func: impl FnMut(&dyn Space<VM>)) {
76+
#(#space_visitors)*
77+
#parent_visitor
78+
#foo
79+
}
80+
81+
fn for_each_space_mut(&mut self, mut __func: impl FnMut(&mut dyn Space<VM>)) {
82+
#(#space_visitors_mut)*
83+
#parent_visitor_mut
84+
#bar
85+
}
86+
}
87+
}

macros/src/lib.rs

+10
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,21 @@ use quote::quote;
1010
use syn::parse_macro_input;
1111
use syn::DeriveInput;
1212

13+
mod has_spaces_impl;
1314
mod plan_trace_object_impl;
1415
mod util;
1516

1617
const DEBUG_MACRO_OUTPUT: bool = false;
1718

19+
#[proc_macro_error]
20+
#[proc_macro_derive(HasSpaces, attributes(space, parent))]
21+
pub fn derive_has_spaces(input: TokenStream) -> TokenStream {
22+
let input = parse_macro_input!(input as DeriveInput);
23+
let output = has_spaces_impl::derive(input);
24+
25+
output.into()
26+
}
27+
1828
/// Generally a plan needs to add these attributes in order for the macro to work. The macro will
1929
/// generate an implementation of `PlanTraceObject` for the plan. With `PlanTraceObject`, the plan use
2030
/// `PlanProcessEdges` for GC tracing. The attributes only affects code generation in the macro, thus

src/plan/generational/copying/global.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ use crate::ObjectQueue;
2727
use enum_map::EnumMap;
2828
use std::sync::atomic::{AtomicBool, Ordering};
2929

30-
use mmtk_macros::PlanTraceObject;
30+
use mmtk_macros::{HasSpaces, PlanTraceObject};
3131

32-
#[derive(PlanTraceObject)]
32+
#[derive(HasSpaces, PlanTraceObject)]
3333
pub struct GenCopy<VM: VMBinding> {
3434
#[parent]
3535
pub gen: CommonGenPlan<VM>,

src/plan/generational/global.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@ use std::sync::atomic::AtomicBool;
1717
use std::sync::atomic::Ordering;
1818
use std::sync::{Arc, Mutex};
1919

20-
use mmtk_macros::PlanTraceObject;
20+
use mmtk_macros::{HasSpaces, PlanTraceObject};
2121

2222
/// Common implementation for generational plans. Each generational plan
2323
/// should include this type, and forward calls to it where possible.
24-
#[derive(PlanTraceObject)]
24+
#[derive(HasSpaces, PlanTraceObject)]
2525
pub struct CommonGenPlan<VM: VMBinding> {
2626
/// The nursery space.
2727
#[space]

src/plan/generational/immix/global.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,13 @@ use enum_map::EnumMap;
2929
use std::sync::atomic::AtomicBool;
3030
use std::sync::atomic::Ordering;
3131

32-
use mmtk_macros::PlanTraceObject;
32+
use mmtk_macros::{HasSpaces, PlanTraceObject};
3333

3434
/// Generational immix. This implements the functionality of a two-generation copying
3535
/// collector where the higher generation is an immix space.
3636
/// See the PLDI'08 paper by Blackburn and McKinley for a description
3737
/// of the algorithm: <http://doi.acm.org/10.1145/1375581.1375586>.
38-
#[derive(PlanTraceObject)]
38+
#[derive(HasSpaces, PlanTraceObject)]
3939
pub struct GenImmix<VM: VMBinding> {
4040
/// Generational plan, which includes a nursery space and operations related with nursery.
4141
#[parent]

src/plan/global.rs

+30-3
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ use enum_map::EnumMap;
3333
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
3434
use std::sync::{Arc, Mutex};
3535

36-
use mmtk_macros::PlanTraceObject;
36+
use mmtk_macros::{HasSpaces, PlanTraceObject};
3737

3838
pub fn create_mutator<VM: VMBinding>(
3939
tls: VMMutatorThread,
@@ -385,7 +385,7 @@ pub enum GcStatus {
385385
/**
386386
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.
387387
*/
388-
#[derive(PlanTraceObject)]
388+
#[derive(HasSpaces, PlanTraceObject)]
389389
pub struct BasePlan<VM: VMBinding> {
390390
/// Whether MMTk is now ready for collection. This is set to true when initialize_collection() is called.
391391
pub initialized: AtomicBool,
@@ -896,7 +896,7 @@ impl<VM: VMBinding> BasePlan<VM> {
896896
/**
897897
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.
898898
*/
899-
#[derive(PlanTraceObject)]
899+
#[derive(HasSpaces, PlanTraceObject)]
900900
pub struct CommonPlan<VM: VMBinding> {
901901
#[space]
902902
pub immortal: ImmortalSpace<VM>,
@@ -1014,6 +1014,33 @@ impl<VM: VMBinding> CommonPlan<VM> {
10141014
use crate::policy::gc_work::TraceKind;
10151015
use crate::vm::VMBinding;
10161016

1017+
/// A trait for anything that contains spaces.
1018+
/// Examples include concrete plans as well as `Gen`, `CommonPlan` and `BasePlan`.
1019+
///
1020+
/// This trait provides methods for enumerating spaces in a struct, including nested struct such as
1021+
/// `Gen`, `CommonPlan` and `BasePlan`.
1022+
///
1023+
/// This trait can be implemented automatically using `#[derive(HasSpaces)]` defined in the
1024+
/// `mmtk-macros` crate.
1025+
///
1026+
/// This trait visits spaces as `dyn`, so it should only be used when performance is not critical.
1027+
/// For performance critical methods that visit spaces in a plan, such as `trace_object`, it is
1028+
/// recommended to define a trait (such as `PlanTraceObject`) for concrete plans to implement, and
1029+
/// implement (by hand or automatically) the method without `dyn`.
1030+
pub trait HasSpaces<VM: VMBinding> {
1031+
/// Visit each space field immutably.
1032+
///
1033+
/// If `Self` contains a parent field that contain more spaces, this method will visit spaces
1034+
/// in the outer struct first.
1035+
fn for_each_space(&self, func: impl FnMut(&dyn Space<VM>));
1036+
1037+
/// Visit each space field mutably.
1038+
///
1039+
/// If `Self` contains a parent field that contain more spaces, this method will visit spaces
1040+
/// in the outer struct first.
1041+
fn for_each_space_mut(&mut self, func: impl FnMut(&mut dyn Space<VM>));
1042+
}
1043+
10171044
/// A plan that uses `PlanProcessEdges` needs to provide an implementation for this trait.
10181045
/// Generally a plan does not need to manually implement this trait. Instead, we provide
10191046
/// a procedural macro that helps generate an implementation. Please check `macros/trace_object`.

src/plan/immix/global.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ use std::sync::atomic::AtomicBool;
2424
use atomic::Ordering;
2525
use enum_map::EnumMap;
2626

27-
use mmtk_macros::PlanTraceObject;
27+
use mmtk_macros::{HasSpaces, PlanTraceObject};
2828

29-
#[derive(PlanTraceObject)]
29+
#[derive(HasSpaces, PlanTraceObject)]
3030
pub struct Immix<VM: VMBinding> {
3131
#[post_scan]
3232
#[space]

src/plan/markcompact/global.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ use crate::vm::VMBinding;
2525

2626
use enum_map::EnumMap;
2727

28-
use mmtk_macros::PlanTraceObject;
28+
use mmtk_macros::{HasSpaces, PlanTraceObject};
2929

30-
#[derive(PlanTraceObject)]
30+
#[derive(HasSpaces, PlanTraceObject)]
3131
pub struct MarkCompact<VM: VMBinding> {
3232
#[space]
3333
#[copy_semantics(CopySemantics::DefaultCopy)]

src/plan/marksweep/global.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use crate::util::metadata::side_metadata::{SideMetadataContext, SideMetadataSani
1616
use crate::util::VMWorkerThread;
1717
use crate::vm::VMBinding;
1818
use enum_map::EnumMap;
19-
use mmtk_macros::PlanTraceObject;
19+
use mmtk_macros::{HasSpaces, PlanTraceObject};
2020

2121
#[cfg(feature = "malloc_mark_sweep")]
2222
pub type MarkSweepSpace<VM> = crate::policy::marksweepspace::malloc_ms::MallocSpace<VM>;
@@ -28,7 +28,7 @@ pub type MarkSweepSpace<VM> = crate::policy::marksweepspace::native_ms::MarkSwee
2828
#[cfg(not(feature = "malloc_mark_sweep"))]
2929
use crate::policy::marksweepspace::native_ms::MAX_OBJECT_SIZE;
3030

31-
#[derive(PlanTraceObject)]
31+
#[derive(HasSpaces, PlanTraceObject)]
3232
pub struct MarkSweep<VM: VMBinding> {
3333
#[parent]
3434
common: CommonPlan<VM>,

src/plan/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ pub(crate) use global::create_mutator;
2424
pub(crate) use global::create_plan;
2525
pub use global::AllocationSemantics;
2626
pub(crate) use global::GcStatus;
27+
pub(crate) use global::HasSpaces;
2728
pub use global::Plan;
2829
pub(crate) use global::PlanTraceObject;
2930

src/plan/pageprotect/global.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ use crate::{
1818
};
1919
use enum_map::EnumMap;
2020

21-
use mmtk_macros::PlanTraceObject;
21+
use mmtk_macros::{HasSpaces, PlanTraceObject};
2222

23-
#[derive(PlanTraceObject)]
23+
#[derive(HasSpaces, PlanTraceObject)]
2424
pub struct PageProtect<VM: VMBinding> {
2525
#[space]
2626
pub space: LargeObjectSpace<VM>,

src/plan/semispace/global.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@ use crate::util::opaque_pointer::VMWorkerThread;
1818
use crate::{plan::global::BasePlan, vm::VMBinding};
1919
use std::sync::atomic::{AtomicBool, Ordering};
2020

21-
use mmtk_macros::PlanTraceObject;
21+
use mmtk_macros::{HasSpaces, PlanTraceObject};
2222

2323
use enum_map::EnumMap;
2424

25-
#[derive(PlanTraceObject)]
25+
#[derive(HasSpaces, PlanTraceObject)]
2626
pub struct SemiSpace<VM: VMBinding> {
2727
pub hi: AtomicBool,
2828
#[space]

src/plan/sticky/immix/global.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,12 @@ use atomic::Ordering;
2121
use std::sync::atomic::AtomicBool;
2222
use std::sync::{Arc, Mutex};
2323

24-
use mmtk_macros::PlanTraceObject;
24+
use mmtk_macros::{HasSpaces, PlanTraceObject};
2525

2626
use super::gc_work::StickyImmixMatureGCWorkContext;
2727
use super::gc_work::StickyImmixNurseryGCWorkContext;
2828

29-
#[derive(PlanTraceObject)]
29+
#[derive(HasSpaces, PlanTraceObject)]
3030
pub struct StickyImmix<VM: VMBinding> {
3131
#[parent]
3232
immix: immix::Immix<VM>,

src/policy/gc_work.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use crate::util::ObjectReference;
1313
use crate::vm::VMBinding;
1414

1515
/// This trait defines policy-specific behavior for tracing objects.
16-
/// The procedural macro #[derive(PlanTraceObject)] will generate code
16+
/// The procedural macro #[derive(HasSpaces, PlanTraceObject)] will generate code
1717
/// that uses this trait. We expect any policy to implement this trait.
1818
/// For the sake of performance, the implementation
1919
/// of this trait should mark methods as `[inline(always)]`.

0 commit comments

Comments
 (0)