From 74a71de483a73bc7b7764022fc46b3cb41417d8c Mon Sep 17 00:00:00 2001 From: Yi Lin Date: Fri, 29 Apr 2022 11:38:55 +1000 Subject: [PATCH] match space and call scan_object() (similar to trace_object()) --- macros/trace_object/src/lib.rs | 52 ++++++++++------------ src/plan/generational/immix/global.rs | 2 +- src/plan/immix/global.rs | 2 +- src/plan/transitive_closure.rs | 63 +++++++++++++++++++++++---- src/policy/gc_work.rs | 25 +++++------ src/policy/immix/immixspace.rs | 60 +++++-------------------- src/scheduler/gc_work.rs | 6 ++- 7 files changed, 108 insertions(+), 102 deletions(-) diff --git a/macros/trace_object/src/lib.rs b/macros/trace_object/src/lib.rs index 80ba04ce6e..2387b78222 100644 --- a/macros/trace_object/src/lib.rs +++ b/macros/trace_object/src/lib.rs @@ -20,10 +20,10 @@ mod util; /// * add `#[fallback_trace]` to the parent plan if the plan is composed with other plans (or parent plans). /// For example, `GenImmix` is composed with `Gen`, `Gen` is composed with `CommonPlan`, `CommonPlan` is composed /// with `BasePlan`. -/// * (optional) add `#[scan_work]` to _one_ space field in the plan. `create_scan_work` will be generated based -/// on the space. +/// * add `#[policy_scan]` to any space field that has some policy-specific scan_object(). For objects in those spaces, +/// `scan_object()` in the policy will be called. For other objects, directly call `VM::VMScanning::scan_object()`. #[proc_macro_error] -#[proc_macro_derive(PlanTraceObject, attributes(trace, scan_work, copy, fallback_trace))] +#[proc_macro_derive(PlanTraceObject, attributes(trace, policy_scan, copy, fallback_trace))] pub fn derive_plan_trace_object(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); let ident = input.ident; @@ -34,11 +34,11 @@ pub fn derive_plan_trace_object(input: TokenStream) -> TokenStream { .. }) = input.data { let spaces = util::get_fields_with_attribute(fields, "trace"); - let scan_work = util::get_unique_field_with_attribute(fields, "scan_work"); + let scan_spaces = util::get_fields_with_attribute(fields, "policy_scan"); let fallback = util::get_unique_field_with_attribute(fields, "fallback_trace"); let trace_object_function = generate_trace_object(&spaces, &fallback, &ty_generics); - let create_scan_work_function = generate_create_scan_work(&spaces, &scan_work, &ty_generics); + let scan_object_function = generate_scan_object(&scan_spaces, &ty_generics); let may_move_objects_function = generate_may_move_objects(&spaces, &fallback, &ty_generics); quote!{ impl #impl_generics crate::plan::transitive_closure::PlanTraceObject #ty_generics for #ident #ty_generics #where_clause { @@ -46,7 +46,7 @@ pub fn derive_plan_trace_object(input: TokenStream) -> TokenStream { #trace_object_function #[inline(always)] - #create_scan_work_function + #scan_object_function #[inline(always)] #may_move_objects_function @@ -121,40 +121,32 @@ fn generate_trace_object<'a>( } } -fn generate_create_scan_work<'a>( - space_fields: &[&'a Field], - scan_work_field: &Option<&'a Field>, +fn generate_scan_object<'a>( + scan_object_fields: &[&'a Field], ty_generics: &TypeGenerics, ) -> TokenStream2 { - if let Some(f) = scan_work_field { - // If the plan names a field for scan work, use it + let scan_field_handler = scan_object_fields.iter().map(|f| { let f_ident = f.ident.as_ref().unwrap(); let ref f_ty = f.ty; quote! { - fn create_scan_work>(&'static self, nodes: Vec) -> Box> { + if self.#f_ident.in_space(__mmtk_objref) { use crate::policy::gc_work::PolicyTraceObject; - <#f_ty as PolicyTraceObject #ty_generics>::create_scan_work::(&self.#f_ident, nodes) + <#f_ty as PolicyTraceObject #ty_generics>::scan_object::(&self.#f_ident, __mmtk_worker_tls, __mmtk_objref, __mmtk_ev); + return; } } - } else if !space_fields.is_empty() { - // If the plan does not name a specific field for scan work, just use the first space for scan work - let f = space_fields[0]; - let f_ident = f.ident.as_ref().unwrap(); - let ref f_ty = f.ty; + }); - quote! { - fn create_scan_work>(&'static self, nodes: Vec) -> Box> { - use crate::policy::gc_work::PolicyTraceObject; - <#f_ty as PolicyTraceObject #ty_generics>::create_scan_work::(&self.#f_ident, nodes) - } - } - } else { - // Otherwise, just panic - quote! { - fn create_scan_work>(&'static self, nodes: Vec) -> Box> { - panic!("Unable to create a scan work packet for the plan (the plan does not name a #[scan_work] field, or a #[trace] field") - } + quote! { + fn scan_object(&self, __mmtk_worker_tls: crate::util::opaque_pointer::VMWorkerThread, __mmtk_objref: crate::util::ObjectReference, __mmtk_ev: &mut EV) { + use crate::vm::Scanning; + + // Plan specific + #(#scan_field_handler)* + + // Default + VM::VMScanning::scan_object(__mmtk_worker_tls, __mmtk_objref, __mmtk_ev); } } } diff --git a/src/plan/generational/immix/global.rs b/src/plan/generational/immix/global.rs index 9c2eab0b37..0e820b4eba 100644 --- a/src/plan/generational/immix/global.rs +++ b/src/plan/generational/immix/global.rs @@ -38,7 +38,7 @@ pub struct GenImmix { #[fallback_trace] pub gen: Gen, /// An immix space as the mature space. - #[scan_work] + #[policy_scan] #[trace(CopySemantics::Mature)] pub immix: ImmixSpace, /// Whether the last GC was a defrag GC for the immix space. diff --git a/src/plan/immix/global.rs b/src/plan/immix/global.rs index 825eed2e0d..1d5d35cfd7 100644 --- a/src/plan/immix/global.rs +++ b/src/plan/immix/global.rs @@ -30,7 +30,7 @@ use macro_trace_object::PlanTraceObject; #[derive(PlanTraceObject)] pub struct Immix { - #[scan_work] + #[policy_scan] #[trace(CopySemantics::DefaultCopy)] pub immix_space: ImmixSpace, #[fallback_trace] diff --git a/src/plan/transitive_closure.rs b/src/plan/transitive_closure.rs index c36958a604..42f6f9151b 100644 --- a/src/plan/transitive_closure.rs +++ b/src/plan/transitive_closure.rs @@ -74,6 +74,7 @@ impl<'a, E: ProcessEdgesWork> Drop for ObjectsClosure<'a, E> { use crate::policy::gc_work::TraceKind; use crate::scheduler::GCWork; +use crate::util::VMWorkerThread; use crate::vm::VMBinding; /// A plan that uses `PlanProcessEdges` needs to provide an implementation for this trait. @@ -93,13 +94,15 @@ pub trait PlanTraceObject { worker: &mut GCWorker, ) -> ObjectReference; - /// Create a scan objects work packet for the plan. Usually [`ScanObjects`](scheduler/gc_work/ScanObjects) - /// is used. If a plan or any policy in the plan uses a specific scan work packet, the work packet is required - /// to handle objects that is in any space in the plan. - fn create_scan_work>( - &'static self, - nodes: Vec, - ) -> Box>; + /// Scan objects in the plan. It is expected that each object will be scanned by `VM::VMScanning::scan_object()`. + /// If the object is in a policy that has some policy specific behaviors for scanning (e.g. mark lines in Immix), + /// this method should also invoke those policy specific methods. + fn scan_object( + &self, + tls: VMWorkerThread, + object: ObjectReference, + edge_visitor: &mut EV, + ); /// Whether objects in this plan may move. If any of the spaces used by the plan may move objects, this should /// return true. @@ -138,7 +141,7 @@ impl< #[inline(always)] fn create_scan_work(&self, nodes: Vec) -> Box> { - self.plan.create_scan_work::(nodes) + Box::new(PlanScanObjects::::new(self.plan, nodes, false)) } #[inline(always)] @@ -185,3 +188,47 @@ impl< &mut self.base } } + +use std::marker::PhantomData; + +/// This provides an implementation of scanning objects work. Each object will be scanned by calling `scan_object()` +/// in `PlanTraceObject`. +pub struct PlanScanObjects< + E: ProcessEdgesWork, + P: 'static + Plan + PlanTraceObject + Sync, +> { + plan: &'static P, + buffer: Vec, + #[allow(dead_code)] + concurrent: bool, + phantom: PhantomData, +} + +impl + PlanTraceObject + Sync> + PlanScanObjects +{ + pub fn new(plan: &'static P, buffer: Vec, concurrent: bool) -> Self { + Self { + plan, + buffer, + concurrent, + phantom: PhantomData, + } + } +} + +impl + PlanTraceObject + Sync> + GCWork for PlanScanObjects +{ + fn do_work(&mut self, worker: &mut GCWorker, _mmtk: &'static MMTK) { + trace!("PlanScanObjects"); + { + let tls = worker.tls; + let mut closure = ObjectsClosure::::new(worker); + for object in &self.buffer { + self.plan.scan_object(tls, *object, &mut closure); + } + } + trace!("PlanScanObjects End"); + } +} diff --git a/src/policy/gc_work.rs b/src/policy/gc_work.rs index b2cd0256d5..756e9f2d03 100644 --- a/src/policy/gc_work.rs +++ b/src/policy/gc_work.rs @@ -5,11 +5,11 @@ pub(crate) type TraceKind = u8; pub const DEFAULT_TRACE: u8 = u8::MAX; use crate::plan::TransitiveClosure; -use crate::scheduler::gc_work::ProcessEdgesWork; -use crate::scheduler::GCWork; use crate::scheduler::GCWorker; use crate::util::copy::CopySemantics; +use crate::util::opaque_pointer::VMWorkerThread; use crate::util::ObjectReference; +use crate::vm::EdgeVisitor; use crate::vm::VMBinding; /// This trait defines policy-specific behavior for tracing objects. @@ -28,18 +28,17 @@ pub trait PolicyTraceObject { worker: &mut GCWorker, ) -> ObjectReference; - /// Create a scan work packet. Note that a plan currently only uses one type - /// of the scan work packet. So a policy either uses the general `ScanObjects` - /// work, or implement their own packet. Their implementation needs to handle - /// cases that objects are not in this current space. + /// Policy-specific scan object. The implementation needs to guarantee that + /// they will call `VM::VMScanning::scan_object()` (or `Self::vm_scan_object()`) besides any space-specific work for the object. #[inline(always)] - fn create_scan_work>( - &'static self, - nodes: Vec, - ) -> Box> { - Box::new(crate::scheduler::gc_work::ScanObjects::::new( - nodes, false, - )) + fn scan_object( + &self, + tls: VMWorkerThread, + object: ObjectReference, + edge_visitor: &mut EV, + ) { + use crate::vm::Scanning; + VM::VMScanning::scan_object(tls, object, edge_visitor) } /// Return whether the policy moves objects. diff --git a/src/policy/immix/immixspace.rs b/src/policy/immix/immixspace.rs index 390725fbf9..503680df91 100644 --- a/src/policy/immix/immixspace.rs +++ b/src/policy/immix/immixspace.rs @@ -4,7 +4,6 @@ use super::{ chunk::{Chunk, ChunkMap, ChunkState}, defrag::Defrag, }; -use crate::plan::ObjectsClosure; use crate::policy::gc_work::TraceKind; use crate::policy::space::SpaceOptions; use crate::policy::space::*; @@ -24,7 +23,7 @@ use crate::util::{Address, ObjectReference}; use crate::vm::*; use crate::{ plan::TransitiveClosure, - scheduler::{gc_work::ProcessEdgesWork, GCWork, GCWorkScheduler, GCWorker, WorkBucketStage}, + scheduler::{GCWork, GCWorkScheduler, GCWorker, WorkBucketStage}, util::{ heap::FreeListPageResource, opaque_pointer::{VMThread, VMWorkerThread}, @@ -136,13 +135,17 @@ impl crate::policy::gc_work::PolicyTraceObject for ImmixSpace } #[inline(always)] - fn create_scan_work>( - &'static self, - nodes: Vec, - ) -> Box> { - Box::new(crate::policy::immix::ScanObjectsAndMarkLines::::new( - nodes, false, self, - )) + fn scan_object( + &self, + tls: VMWorkerThread, + object: ObjectReference, + edge_visitor: &mut EV, + ) { + VM::VMScanning::scan_object(tls, object, edge_visitor); + if super::MARK_LINE_AT_SCAN_TIME && !super::BLOCK_ONLY { + debug_assert!(self.in_space(object)); + self.mark_lines(object); + } } #[inline(always)] @@ -613,45 +616,6 @@ impl GCWork for PrepareBlockState { } } -/// A work packet to scan the fields of each objects and mark lines. -pub struct ScanObjectsAndMarkLines { - buffer: Vec, - #[allow(unused)] - concurrent: bool, - immix_space: &'static ImmixSpace, -} - -impl ScanObjectsAndMarkLines { - pub fn new( - buffer: Vec, - concurrent: bool, - immix_space: &'static ImmixSpace, - ) -> Self { - Self { - buffer, - concurrent, - immix_space, - } - } -} - -impl GCWork for ScanObjectsAndMarkLines { - fn do_work(&mut self, worker: &mut GCWorker, _mmtk: &'static MMTK) { - trace!("ScanObjectsAndMarkLines"); - let tls = worker.tls; - let mut closure = ObjectsClosure::::new(worker); - for object in &self.buffer { - ::VMScanning::scan_object(tls, *object, &mut closure); - if super::MARK_LINE_AT_SCAN_TIME - && !super::BLOCK_ONLY - && self.immix_space.in_space(*object) - { - self.immix_space.mark_lines(*object); - } - } - } -} - use crate::plan::Plan; use crate::policy::copy_context::PolicyCopyContext; use crate::util::alloc::Allocator; diff --git a/src/scheduler/gc_work.rs b/src/scheduler/gc_work.rs index 1a918306e6..cb55a47c58 100644 --- a/src/scheduler/gc_work.rs +++ b/src/scheduler/gc_work.rs @@ -492,6 +492,7 @@ impl GCWork for E { /// (such as `Space.set_copy_for_sft_trace()`, `SFT.sft_trace_object()`). /// Some plans are not using this type, mostly due to more complex tracing. Either it is impossible to use this type, or /// there is performance overheads for using this general trace type. In such cases, they implement their specific process edges. +// TODO: This is not used any more. Should we remove it? pub struct SFTProcessEdges { pub base: ProcessEdgesBase, } @@ -540,7 +541,10 @@ impl DerefMut for SFTProcessEdges { } } -/// Scan & update a list of object slots +/// Scan & update a list of object slots. +/// Note that this work packet does not do any policy-specific scan +/// object work (it won't call `scan_object()` in [`policy::gc_work::PolicytraceObject`]). +/// It should be used only for policies that do not have policy-specific scan_object(). pub struct ScanObjects { buffer: Vec, #[allow(unused)]