Skip to content

Experimental: Delegating trace_object to a dedicated struct #1278

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/policy/space.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ pub trait SFT {
// In this way, we can store the refs with <VM> in SFT (which cannot have parameters with generic type parameters)

use crate::util::erase_vm::define_erased_vm_mut_ref;
define_erased_vm_mut_ref!(SFTProcessEdgesMutRef = SFTProcessEdges<VM>);
define_erased_vm_mut_ref!(SFTProcessEdgesMutRef = Vec<ObjectReference>);
define_erased_vm_mut_ref!(GCWorkerMutRef = GCWorker<VM>);

/// Print debug info for SFT. Should be false when committed.
Expand Down
184 changes: 165 additions & 19 deletions src/scheduler/gc_work.rs
Original file line number Diff line number Diff line change
Expand Up @@ -525,7 +525,7 @@ impl<VM: VMBinding> ProcessEdgesWork for SFTProcessEdges<VM> {

// Erase <VM> type parameter
let worker = GCWorkerMutRef::new(self.worker());
let trace = SFTProcessEdgesMutRef::new(self);
let trace = SFTProcessEdgesMutRef::new::<VM>(&mut self.base.nodes);

// Invoke trace object on sft
let sft = crate::mmtk::SFT_MAP.get(object.to_address());
Expand Down Expand Up @@ -629,7 +629,7 @@ pub struct PlanProcessEdges<
P: Plan<VM = VM> + PlanTraceObject<VM>,
const KIND: TraceKind,
> {
plan: &'static P,
delegate: PlanTracingDelegate<P, KIND>,
base: ProcessEdgesBase<VM>,
}

Expand All @@ -641,21 +641,28 @@ impl<VM: VMBinding, P: PlanTraceObject<VM> + Plan<VM = VM>, const KIND: TraceKin
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::<P>().unwrap();
Self { plan, base }
let delegate = PlanTracingDelegate::new(plan);
Self { delegate, base }
}

#[inline(always)]
fn create_scan_work(&self, nodes: Vec<ObjectReference>) -> Box<dyn GCWork<Self::VM>> {
Box::new(PlanScanObjects::<Self, P>::new(self.plan, nodes, false))
Box::new(PlanScanObjects::<P, KIND>::new(
self.delegate.clone(),
nodes,
false,
))
}

#[inline(always)]
fn trace_object(&mut self, object: ObjectReference) -> ObjectReference {
if object.is_null() {
return object;
}
self.plan
.trace_object::<Self, KIND>(self, object, self.worker())
let worker = self.worker();
let queue = &mut self.base.nodes;
let delegate = &self.delegate;
delegate.trace_object(queue, object, worker)
}

#[inline]
Expand Down Expand Up @@ -690,38 +697,177 @@ impl<VM: VMBinding, P: PlanTraceObject<VM> + Plan<VM = VM>, const KIND: TraceKin

/// 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: Plan<VM = E::VM> + PlanTraceObject<E::VM>> {
plan: &'static P,
pub struct PlanScanObjects<P: Plan + PlanTraceObject<P::VM>, const KIND: TraceKind> {
delegate: PlanTracingDelegate<P, KIND>,
buffer: Vec<ObjectReference>,
#[allow(dead_code)]
concurrent: bool,
phantom: PhantomData<E>,
}

impl<E: ProcessEdgesWork, P: Plan<VM = E::VM> + PlanTraceObject<E::VM>> PlanScanObjects<E, P> {
pub fn new(plan: &'static P, buffer: Vec<ObjectReference>, concurrent: bool) -> Self {
impl<P: Plan + PlanTraceObject<P::VM>, const KIND: TraceKind> PlanScanObjects<P, KIND> {
pub fn new(
delegate: PlanTracingDelegate<P, KIND>,
buffer: Vec<ObjectReference>,
concurrent: bool,
) -> Self {
Self {
plan,
delegate,
buffer,
concurrent,
phantom: PhantomData,
}
}
}

impl<E: ProcessEdgesWork, P: Plan<VM = E::VM> + PlanTraceObject<E::VM>> GCWork<E::VM>
for PlanScanObjects<E, P>
impl<P: Plan + PlanTraceObject<P::VM>, const KIND: TraceKind> GCWork<P::VM>
for PlanScanObjects<P, KIND>
{
fn do_work(&mut self, worker: &mut GCWorker<E::VM>, _mmtk: &'static MMTK<E::VM>) {
fn do_work(&mut self, worker: &mut GCWorker<P::VM>, _mmtk: &'static MMTK<P::VM>) {
trace!("PlanScanObjects");
{
let tls = worker.tls;
let mut closure = ObjectsClosure::<E>::new(worker);
let mut closure = ObjectsClosure::<PlanProcessEdges<P::VM, P, KIND>>::new(worker);
for object in &self.buffer {
<E::VM as VMBinding>::VMScanning::scan_object(tls, *object, &mut closure);
self.plan.post_scan_object(*object);
<P::VM as VMBinding>::VMScanning::scan_object(tls, *object, &mut closure);
self.delegate.post_scan_object(*object);
}
}
trace!("PlanScanObjects End");
}
}

pub trait TracingDelegate: 'static + Send + Clone {
type VM: VMBinding;

fn trace_object<T: TransitiveClosure>(
&self,
trace: &mut T,
object: ObjectReference,
worker: &mut GCWorker<Self::VM>,
) -> ObjectReference;

fn may_move_objects() -> bool;

fn post_scan_object(&self, object: ObjectReference);
}

pub struct PlanTracingDelegate<P: Plan + PlanTraceObject<P::VM>, const KIND: TraceKind> {
plan: &'static P,
}

impl<P: Plan + PlanTraceObject<P::VM>, const KIND: TraceKind> PlanTracingDelegate<P, KIND> {
pub fn new(plan: &'static P) -> Self {
Self { plan }
}
}

impl<P: Plan + PlanTraceObject<P::VM>, const KIND: TraceKind> Clone
for PlanTracingDelegate<P, KIND>
{
fn clone(&self) -> Self {
Self {
plan: self.plan.clone(),
}
}
}

impl<P: Plan + PlanTraceObject<P::VM>, const KIND: TraceKind> TracingDelegate
for PlanTracingDelegate<P, KIND>
{
type VM = P::VM;

#[inline]
fn trace_object<T: TransitiveClosure>(
&self,
trace: &mut T,
object: ObjectReference,
worker: &mut GCWorker<P::VM>,
) -> ObjectReference {
self.plan.trace_object::<T, KIND>(trace, object, worker)
}

#[inline(always)]
fn may_move_objects() -> bool {
P::may_move_objects::<KIND>()
}

#[inline]
fn post_scan_object(&self, object: ObjectReference) {
self.plan.post_scan_object(object);
}
}


const OBJECT_QUEUE_CAPACITY: usize = 4096;

impl TransitiveClosure for Vec<ObjectReference> {
#[inline]
fn process_node(&mut self, object: ObjectReference) {
if self.is_empty() {
self.reserve(OBJECT_QUEUE_CAPACITY);
}
self.push(object);
}
}

pub struct SFTTracingDelegate<VM: VMBinding> {
phantom_data: PhantomData<VM>,
}

impl<VM: VMBinding> SFTTracingDelegate<VM> {
pub fn new() -> Self {
Self { phantom_data: PhantomData }
}
}

impl<VM: VMBinding> Clone for SFTTracingDelegate<VM> {
fn clone(&self) -> Self {
Self { phantom_data: PhantomData }
}
}

impl<VM: VMBinding> TracingDelegate
for SFTTracingDelegate<VM>
{
type VM = VM;

#[inline]
fn trace_object<T: TransitiveClosure>(
&self,
trace: &mut T,
object: ObjectReference,
worker: &mut GCWorker<VM>,
) -> ObjectReference {
// SFT only supports Vec<ObjectReference>. Currently, Rust doesn't have a way to check
// if T is Vec<ObjectReference>.
let vec_object_reference: &mut Vec<ObjectReference> = unsafe { std::mem::transmute(trace) };

use crate::policy::space::*;

if object.is_null() {
return object;
}

// Make sure we have valid SFT entries for the object.
#[cfg(debug_assertions)]
crate::mmtk::SFT_MAP.assert_valid_entries_for_object::<VM>(object);

// Erase <VM> type parameter
let worker = GCWorkerMutRef::new(worker);
let trace = SFTProcessEdgesMutRef::new::<VM>(vec_object_reference);

// Invoke trace object on sft
let sft = crate::mmtk::SFT_MAP.get(object.to_address());
sft.sft_trace_object(trace, object, worker)
}

#[inline(always)]
fn may_move_objects() -> bool {
true
}

#[inline]
fn post_scan_object(&self, object: ObjectReference) {
// Do nothing. If a plan needs to do anything, it cannot use
// SFTTracingDelegate
}
}
1 change: 1 addition & 0 deletions src/scheduler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ pub use controller::GCController;

pub(crate) mod gc_work;
pub use gc_work::ProcessEdgesWork;
pub use gc_work::TracingDelegate;
// TODO: We shouldn't need to expose ScanStackRoot. However, OpenJDK uses it.
// We should do some refactoring related to Scanning::SCAN_MUTATORS_IN_SAFEPOINT
// to make sure this type is not exposed to the bindings.
Expand Down
Loading