Skip to content

Commit 74a71de

Browse files
committed
match space and call scan_object() (similar to trace_object())
1 parent b7adb2d commit 74a71de

File tree

7 files changed

+108
-102
lines changed

7 files changed

+108
-102
lines changed

macros/trace_object/src/lib.rs

Lines changed: 22 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ mod util;
2020
/// * add `#[fallback_trace]` to the parent plan if the plan is composed with other plans (or parent plans).
2121
/// For example, `GenImmix` is composed with `Gen`, `Gen` is composed with `CommonPlan`, `CommonPlan` is composed
2222
/// with `BasePlan`.
23-
/// * (optional) add `#[scan_work]` to _one_ space field in the plan. `create_scan_work` will be generated based
24-
/// on the space.
23+
/// * add `#[policy_scan]` to any space field that has some policy-specific scan_object(). For objects in those spaces,
24+
/// `scan_object()` in the policy will be called. For other objects, directly call `VM::VMScanning::scan_object()`.
2525
#[proc_macro_error]
26-
#[proc_macro_derive(PlanTraceObject, attributes(trace, scan_work, copy, fallback_trace))]
26+
#[proc_macro_derive(PlanTraceObject, attributes(trace, policy_scan, copy, fallback_trace))]
2727
pub fn derive_plan_trace_object(input: TokenStream) -> TokenStream {
2828
let input = parse_macro_input!(input as DeriveInput);
2929
let ident = input.ident;
@@ -34,19 +34,19 @@ pub fn derive_plan_trace_object(input: TokenStream) -> TokenStream {
3434
..
3535
}) = input.data {
3636
let spaces = util::get_fields_with_attribute(fields, "trace");
37-
let scan_work = util::get_unique_field_with_attribute(fields, "scan_work");
37+
let scan_spaces = util::get_fields_with_attribute(fields, "policy_scan");
3838
let fallback = util::get_unique_field_with_attribute(fields, "fallback_trace");
3939

4040
let trace_object_function = generate_trace_object(&spaces, &fallback, &ty_generics);
41-
let create_scan_work_function = generate_create_scan_work(&spaces, &scan_work, &ty_generics);
41+
let scan_object_function = generate_scan_object(&scan_spaces, &ty_generics);
4242
let may_move_objects_function = generate_may_move_objects(&spaces, &fallback, &ty_generics);
4343
quote!{
4444
impl #impl_generics crate::plan::transitive_closure::PlanTraceObject #ty_generics for #ident #ty_generics #where_clause {
4545
#[inline(always)]
4646
#trace_object_function
4747

4848
#[inline(always)]
49-
#create_scan_work_function
49+
#scan_object_function
5050

5151
#[inline(always)]
5252
#may_move_objects_function
@@ -121,40 +121,32 @@ fn generate_trace_object<'a>(
121121
}
122122
}
123123

124-
fn generate_create_scan_work<'a>(
125-
space_fields: &[&'a Field],
126-
scan_work_field: &Option<&'a Field>,
124+
fn generate_scan_object<'a>(
125+
scan_object_fields: &[&'a Field],
127126
ty_generics: &TypeGenerics,
128127
) -> TokenStream2 {
129-
if let Some(f) = scan_work_field {
130-
// If the plan names a field for scan work, use it
128+
let scan_field_handler = scan_object_fields.iter().map(|f| {
131129
let f_ident = f.ident.as_ref().unwrap();
132130
let ref f_ty = f.ty;
133131

134132
quote! {
135-
fn create_scan_work<E: crate::scheduler::gc_work::ProcessEdgesWork<VM = VM>>(&'static self, nodes: Vec<crate::util::ObjectReference>) -> Box<dyn crate::scheduler::GCWork<VM>> {
133+
if self.#f_ident.in_space(__mmtk_objref) {
136134
use crate::policy::gc_work::PolicyTraceObject;
137-
<#f_ty as PolicyTraceObject #ty_generics>::create_scan_work::<E>(&self.#f_ident, nodes)
135+
<#f_ty as PolicyTraceObject #ty_generics>::scan_object::<EV>(&self.#f_ident, __mmtk_worker_tls, __mmtk_objref, __mmtk_ev);
136+
return;
138137
}
139138
}
140-
} else if !space_fields.is_empty() {
141-
// If the plan does not name a specific field for scan work, just use the first space for scan work
142-
let f = space_fields[0];
143-
let f_ident = f.ident.as_ref().unwrap();
144-
let ref f_ty = f.ty;
139+
});
145140

146-
quote! {
147-
fn create_scan_work<E: crate::scheduler::gc_work::ProcessEdgesWork<VM = VM>>(&'static self, nodes: Vec<crate::util::ObjectReference>) -> Box<dyn crate::scheduler::GCWork<VM>> {
148-
use crate::policy::gc_work::PolicyTraceObject;
149-
<#f_ty as PolicyTraceObject #ty_generics>::create_scan_work::<E>(&self.#f_ident, nodes)
150-
}
151-
}
152-
} else {
153-
// Otherwise, just panic
154-
quote! {
155-
fn create_scan_work<E: crate::scheduler::gc_work::ProcessEdgesWork<VM = VM>>(&'static self, nodes: Vec<crate::util::ObjectReference>) -> Box<dyn crate::scheduler::GCWork<VM>> {
156-
panic!("Unable to create a scan work packet for the plan (the plan does not name a #[scan_work] field, or a #[trace] field")
157-
}
141+
quote! {
142+
fn scan_object<EV: crate::vm::EdgeVisitor>(&self, __mmtk_worker_tls: crate::util::opaque_pointer::VMWorkerThread, __mmtk_objref: crate::util::ObjectReference, __mmtk_ev: &mut EV) {
143+
use crate::vm::Scanning;
144+
145+
// Plan specific
146+
#(#scan_field_handler)*
147+
148+
// Default
149+
VM::VMScanning::scan_object(__mmtk_worker_tls, __mmtk_objref, __mmtk_ev);
158150
}
159151
}
160152
}

src/plan/generational/immix/global.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ pub struct GenImmix<VM: VMBinding> {
3838
#[fallback_trace]
3939
pub gen: Gen<VM>,
4040
/// An immix space as the mature space.
41-
#[scan_work]
41+
#[policy_scan]
4242
#[trace(CopySemantics::Mature)]
4343
pub immix: ImmixSpace<VM>,
4444
/// Whether the last GC was a defrag GC for the immix space.

src/plan/immix/global.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ use macro_trace_object::PlanTraceObject;
3030

3131
#[derive(PlanTraceObject)]
3232
pub struct Immix<VM: VMBinding> {
33-
#[scan_work]
33+
#[policy_scan]
3434
#[trace(CopySemantics::DefaultCopy)]
3535
pub immix_space: ImmixSpace<VM>,
3636
#[fallback_trace]

src/plan/transitive_closure.rs

Lines changed: 55 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ impl<'a, E: ProcessEdgesWork> Drop for ObjectsClosure<'a, E> {
7474

7575
use crate::policy::gc_work::TraceKind;
7676
use crate::scheduler::GCWork;
77+
use crate::util::VMWorkerThread;
7778
use crate::vm::VMBinding;
7879

7980
/// A plan that uses `PlanProcessEdges` needs to provide an implementation for this trait.
@@ -93,13 +94,15 @@ pub trait PlanTraceObject<VM: VMBinding> {
9394
worker: &mut GCWorker<VM>,
9495
) -> ObjectReference;
9596

96-
/// Create a scan objects work packet for the plan. Usually [`ScanObjects`](scheduler/gc_work/ScanObjects)
97-
/// is used. If a plan or any policy in the plan uses a specific scan work packet, the work packet is required
98-
/// to handle objects that is in any space in the plan.
99-
fn create_scan_work<E: ProcessEdgesWork<VM = VM>>(
100-
&'static self,
101-
nodes: Vec<ObjectReference>,
102-
) -> Box<dyn GCWork<VM>>;
97+
/// Scan objects in the plan. It is expected that each object will be scanned by `VM::VMScanning::scan_object()`.
98+
/// If the object is in a policy that has some policy specific behaviors for scanning (e.g. mark lines in Immix),
99+
/// this method should also invoke those policy specific methods.
100+
fn scan_object<EV: EdgeVisitor>(
101+
&self,
102+
tls: VMWorkerThread,
103+
object: ObjectReference,
104+
edge_visitor: &mut EV,
105+
);
103106

104107
/// Whether objects in this plan may move. If any of the spaces used by the plan may move objects, this should
105108
/// return true.
@@ -138,7 +141,7 @@ impl<
138141

139142
#[inline(always)]
140143
fn create_scan_work(&self, nodes: Vec<ObjectReference>) -> Box<dyn GCWork<Self::VM>> {
141-
self.plan.create_scan_work::<Self>(nodes)
144+
Box::new(PlanScanObjects::<Self, P>::new(self.plan, nodes, false))
142145
}
143146

144147
#[inline(always)]
@@ -185,3 +188,47 @@ impl<
185188
&mut self.base
186189
}
187190
}
191+
192+
use std::marker::PhantomData;
193+
194+
/// This provides an implementation of scanning objects work. Each object will be scanned by calling `scan_object()`
195+
/// in `PlanTraceObject`.
196+
pub struct PlanScanObjects<
197+
E: ProcessEdgesWork,
198+
P: 'static + Plan<VM = E::VM> + PlanTraceObject<E::VM> + Sync,
199+
> {
200+
plan: &'static P,
201+
buffer: Vec<ObjectReference>,
202+
#[allow(dead_code)]
203+
concurrent: bool,
204+
phantom: PhantomData<E>,
205+
}
206+
207+
impl<E: ProcessEdgesWork, P: 'static + Plan<VM = E::VM> + PlanTraceObject<E::VM> + Sync>
208+
PlanScanObjects<E, P>
209+
{
210+
pub fn new(plan: &'static P, buffer: Vec<ObjectReference>, concurrent: bool) -> Self {
211+
Self {
212+
plan,
213+
buffer,
214+
concurrent,
215+
phantom: PhantomData,
216+
}
217+
}
218+
}
219+
220+
impl<E: ProcessEdgesWork, P: 'static + Plan<VM = E::VM> + PlanTraceObject<E::VM> + Sync>
221+
GCWork<E::VM> for PlanScanObjects<E, P>
222+
{
223+
fn do_work(&mut self, worker: &mut GCWorker<E::VM>, _mmtk: &'static MMTK<E::VM>) {
224+
trace!("PlanScanObjects");
225+
{
226+
let tls = worker.tls;
227+
let mut closure = ObjectsClosure::<E>::new(worker);
228+
for object in &self.buffer {
229+
self.plan.scan_object(tls, *object, &mut closure);
230+
}
231+
}
232+
trace!("PlanScanObjects End");
233+
}
234+
}

src/policy/gc_work.rs

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ pub(crate) type TraceKind = u8;
55
pub const DEFAULT_TRACE: u8 = u8::MAX;
66

77
use crate::plan::TransitiveClosure;
8-
use crate::scheduler::gc_work::ProcessEdgesWork;
9-
use crate::scheduler::GCWork;
108
use crate::scheduler::GCWorker;
119
use crate::util::copy::CopySemantics;
10+
use crate::util::opaque_pointer::VMWorkerThread;
1211
use crate::util::ObjectReference;
12+
use crate::vm::EdgeVisitor;
1313
use crate::vm::VMBinding;
1414

1515
/// This trait defines policy-specific behavior for tracing objects.
@@ -28,18 +28,17 @@ pub trait PolicyTraceObject<VM: VMBinding> {
2828
worker: &mut GCWorker<VM>,
2929
) -> ObjectReference;
3030

31-
/// Create a scan work packet. Note that a plan currently only uses one type
32-
/// of the scan work packet. So a policy either uses the general `ScanObjects`
33-
/// work, or implement their own packet. Their implementation needs to handle
34-
/// cases that objects are not in this current space.
31+
/// Policy-specific scan object. The implementation needs to guarantee that
32+
/// they will call `VM::VMScanning::scan_object()` (or `Self::vm_scan_object()`) besides any space-specific work for the object.
3533
#[inline(always)]
36-
fn create_scan_work<E: ProcessEdgesWork<VM = VM>>(
37-
&'static self,
38-
nodes: Vec<ObjectReference>,
39-
) -> Box<dyn GCWork<VM>> {
40-
Box::new(crate::scheduler::gc_work::ScanObjects::<E>::new(
41-
nodes, false,
42-
))
34+
fn scan_object<EV: EdgeVisitor>(
35+
&self,
36+
tls: VMWorkerThread,
37+
object: ObjectReference,
38+
edge_visitor: &mut EV,
39+
) {
40+
use crate::vm::Scanning;
41+
VM::VMScanning::scan_object(tls, object, edge_visitor)
4342
}
4443

4544
/// Return whether the policy moves objects.

src/policy/immix/immixspace.rs

Lines changed: 12 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ use super::{
44
chunk::{Chunk, ChunkMap, ChunkState},
55
defrag::Defrag,
66
};
7-
use crate::plan::ObjectsClosure;
87
use crate::policy::gc_work::TraceKind;
98
use crate::policy::space::SpaceOptions;
109
use crate::policy::space::*;
@@ -24,7 +23,7 @@ use crate::util::{Address, ObjectReference};
2423
use crate::vm::*;
2524
use crate::{
2625
plan::TransitiveClosure,
27-
scheduler::{gc_work::ProcessEdgesWork, GCWork, GCWorkScheduler, GCWorker, WorkBucketStage},
26+
scheduler::{GCWork, GCWorkScheduler, GCWorker, WorkBucketStage},
2827
util::{
2928
heap::FreeListPageResource,
3029
opaque_pointer::{VMThread, VMWorkerThread},
@@ -136,13 +135,17 @@ impl<VM: VMBinding> crate::policy::gc_work::PolicyTraceObject<VM> for ImmixSpace
136135
}
137136

138137
#[inline(always)]
139-
fn create_scan_work<E: ProcessEdgesWork<VM = VM>>(
140-
&'static self,
141-
nodes: Vec<ObjectReference>,
142-
) -> Box<dyn GCWork<VM>> {
143-
Box::new(crate::policy::immix::ScanObjectsAndMarkLines::<E>::new(
144-
nodes, false, self,
145-
))
138+
fn scan_object<EV: EdgeVisitor>(
139+
&self,
140+
tls: VMWorkerThread,
141+
object: ObjectReference,
142+
edge_visitor: &mut EV,
143+
) {
144+
VM::VMScanning::scan_object(tls, object, edge_visitor);
145+
if super::MARK_LINE_AT_SCAN_TIME && !super::BLOCK_ONLY {
146+
debug_assert!(self.in_space(object));
147+
self.mark_lines(object);
148+
}
146149
}
147150

148151
#[inline(always)]
@@ -613,45 +616,6 @@ impl<VM: VMBinding> GCWork<VM> for PrepareBlockState<VM> {
613616
}
614617
}
615618

616-
/// A work packet to scan the fields of each objects and mark lines.
617-
pub struct ScanObjectsAndMarkLines<Edges: ProcessEdgesWork> {
618-
buffer: Vec<ObjectReference>,
619-
#[allow(unused)]
620-
concurrent: bool,
621-
immix_space: &'static ImmixSpace<Edges::VM>,
622-
}
623-
624-
impl<Edges: ProcessEdgesWork> ScanObjectsAndMarkLines<Edges> {
625-
pub fn new(
626-
buffer: Vec<ObjectReference>,
627-
concurrent: bool,
628-
immix_space: &'static ImmixSpace<Edges::VM>,
629-
) -> Self {
630-
Self {
631-
buffer,
632-
concurrent,
633-
immix_space,
634-
}
635-
}
636-
}
637-
638-
impl<E: ProcessEdgesWork> GCWork<E::VM> for ScanObjectsAndMarkLines<E> {
639-
fn do_work(&mut self, worker: &mut GCWorker<E::VM>, _mmtk: &'static MMTK<E::VM>) {
640-
trace!("ScanObjectsAndMarkLines");
641-
let tls = worker.tls;
642-
let mut closure = ObjectsClosure::<E>::new(worker);
643-
for object in &self.buffer {
644-
<E::VM as VMBinding>::VMScanning::scan_object(tls, *object, &mut closure);
645-
if super::MARK_LINE_AT_SCAN_TIME
646-
&& !super::BLOCK_ONLY
647-
&& self.immix_space.in_space(*object)
648-
{
649-
self.immix_space.mark_lines(*object);
650-
}
651-
}
652-
}
653-
}
654-
655619
use crate::plan::Plan;
656620
use crate::policy::copy_context::PolicyCopyContext;
657621
use crate::util::alloc::Allocator;

src/scheduler/gc_work.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,7 @@ impl<E: ProcessEdgesWork> GCWork<E::VM> for E {
492492
/// (such as `Space.set_copy_for_sft_trace()`, `SFT.sft_trace_object()`).
493493
/// Some plans are not using this type, mostly due to more complex tracing. Either it is impossible to use this type, or
494494
/// there is performance overheads for using this general trace type. In such cases, they implement their specific process edges.
495+
// TODO: This is not used any more. Should we remove it?
495496
pub struct SFTProcessEdges<VM: VMBinding> {
496497
pub base: ProcessEdgesBase<VM>,
497498
}
@@ -540,7 +541,10 @@ impl<VM: VMBinding> DerefMut for SFTProcessEdges<VM> {
540541
}
541542
}
542543

543-
/// Scan & update a list of object slots
544+
/// Scan & update a list of object slots.
545+
/// Note that this work packet does not do any policy-specific scan
546+
/// object work (it won't call `scan_object()` in [`policy::gc_work::PolicytraceObject`]).
547+
/// It should be used only for policies that do not have policy-specific scan_object().
544548
pub struct ScanObjects<Edges: ProcessEdgesWork> {
545549
buffer: Vec<ObjectReference>,
546550
#[allow(unused)]

0 commit comments

Comments
 (0)