Skip to content

Commit c540854

Browse files
committed
Edge trait
We make the representation of edges VM-specific. The MMTk core only depends on the abstract Edge trait. This allows VM bindings to custom the behaviour of loading/storing of edges, and support features like compressed OOPs. Binding migration advices: - The type of edges is now defined by the VM, and is no longer limited to Address. To transition from the old API, the VM can start by defining its vm-specific edge type as an alias of Address, like: type OpenJDKEdge = Address; and gradually transition to mmtk::vm::edge_shape::SimpleEdge, or define a more appropriate type for the VM. - The VMBinding trait now requires a VMEdge member type. Implement it with the VM-specific edge type, like: type VMEdge = OpenJDKEdge; - EdgeVisitor and RootsWorkFactory now become EdgeVisitor<T> and RootsWorkFactory<T>. If an old API function passes an EdgeVisitor or RootsWorkFactory object, add the VM's edge type as T. For example, change fn scan_object<EV: EdgeVisitor>(...) { ... } to fn scan_object<EV: EdgeVisitor<OpenJDKEdge>>(...) { ... }
1 parent d8bcf90 commit c540854

File tree

19 files changed

+565
-76
lines changed

19 files changed

+565
-76
lines changed

docs/tutorial/code/mygc_semispace/gc_work.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ impl<VM:VMBinding> ProcessEdgesWork for MyGCProcessEdges<VM> {
4242
type VM = VM;
4343
type ScanObjectsWorkType = ScanObjects<Self>;
4444

45-
fn new(edges: Vec<Address>, roots: bool, mmtk: &'static MMTK<VM>) -> Self {
45+
fn new(edges: Vec<EdgeOf<Self>>, roots: bool, mmtk: &'static MMTK<VM>) -> Self {
4646
let base = ProcessEdgesBase::new(edges, roots, mmtk);
4747
let plan = base.plan().downcast_ref::<MyGC<VM>>().unwrap();
4848
Self { base, plan }

src/mmtk.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
use crate::plan::Plan;
33
use crate::policy::space::SFTMap;
44
use crate::scheduler::GCWorkScheduler;
5+
6+
#[cfg(feature = "extreme_assertions")]
7+
use crate::util::edge_logger::EdgeLogger;
58
use crate::util::finalizable_processor::FinalizableProcessor;
69
use crate::util::heap::layout::heap_layout::Mmapper;
710
use crate::util::heap::layout::heap_layout::VMMap;
@@ -86,7 +89,9 @@ pub struct MMTK<VM: VMBinding> {
8689
Mutex<FinalizableProcessor<<VM::VMReferenceGlue as ReferenceGlue<VM>>::FinalizableType>>,
8790
pub(crate) scheduler: Arc<GCWorkScheduler<VM>>,
8891
#[cfg(feature = "sanity")]
89-
pub(crate) sanity_checker: Mutex<SanityChecker>,
92+
pub(crate) sanity_checker: Mutex<SanityChecker<VM::VMEdge>>,
93+
#[cfg(feature = "extreme_assertions")]
94+
pub(crate) edge_logger: EdgeLogger<VM::VMEdge>,
9095
inside_harness: AtomicBool,
9196
}
9297

@@ -130,6 +135,8 @@ impl<VM: VMBinding> MMTK<VM> {
130135
#[cfg(feature = "sanity")]
131136
sanity_checker: Mutex::new(SanityChecker::new()),
132137
inside_harness: AtomicBool::new(false),
138+
#[cfg(feature = "extreme_assertions")]
139+
edge_logger: EdgeLogger::new(),
133140
}
134141
}
135142

src/plan/generational/gc_work.rs

+6-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
use crate::plan::generational::global::Gen;
22
use crate::policy::space::Space;
33
use crate::scheduler::gc_work::*;
4-
use crate::util::{Address, ObjectReference};
4+
use crate::util::ObjectReference;
5+
use crate::vm::edge_shape::Edge;
56
use crate::vm::*;
67
use crate::MMTK;
78
use std::ops::{Deref, DerefMut};
@@ -18,7 +19,7 @@ impl<VM: VMBinding> ProcessEdgesWork for GenNurseryProcessEdges<VM> {
1819
type VM = VM;
1920
type ScanObjectsWorkType = ScanObjects<Self>;
2021

21-
fn new(edges: Vec<Address>, roots: bool, mmtk: &'static MMTK<VM>) -> Self {
22+
fn new(edges: Vec<EdgeOf<Self>>, roots: bool, mmtk: &'static MMTK<VM>) -> Self {
2223
let base = ProcessEdgesBase::new(edges, roots, mmtk);
2324
let gen = base.plan().generational();
2425
Self { gen, base }
@@ -34,11 +35,11 @@ impl<VM: VMBinding> ProcessEdgesWork for GenNurseryProcessEdges<VM> {
3435
.trace_object_nursery(&mut self.base.nodes, object, worker)
3536
}
3637
#[inline]
37-
fn process_edge(&mut self, slot: Address) {
38-
let object = unsafe { slot.load::<ObjectReference>() };
38+
fn process_edge(&mut self, slot: EdgeOf<Self>) {
39+
let object = slot.load();
3940
let new_object = self.trace_object(object);
4041
debug_assert!(!self.gen.nursery.in_space(new_object));
41-
unsafe { slot.store(new_object) };
42+
slot.store(new_object);
4243
}
4344

4445
#[inline(always)]

src/plan/markcompact/gc_work.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ impl<VM: VMBinding> GCWork<VM> for UpdateReferences<VM> {
4343
VM::VMScanning::prepare_for_roots_re_scanning();
4444
mmtk.plan.base().prepare_for_stack_scanning();
4545
#[cfg(feature = "extreme_assertions")]
46-
crate::util::edge_logger::reset();
46+
mmtk.edge_logger.reset();
4747

4848
// TODO investigate why the following will create duplicate edges
4949
// scheduler.work_buckets[WorkBucketStage::RefForwarding]

src/plan/tracing.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
44
use std::mem;
55

6-
use crate::scheduler::gc_work::ProcessEdgesWork;
6+
use crate::scheduler::gc_work::{EdgeOf, ProcessEdgesWork};
77
use crate::scheduler::{GCWorker, WorkBucketStage};
8-
use crate::util::{Address, ObjectReference};
8+
use crate::util::ObjectReference;
99
use crate::vm::EdgeVisitor;
1010

1111
/// This trait represents an object queue to enqueue objects during tracing.
@@ -63,7 +63,7 @@ impl ObjectQueue for VectorObjectQueue {
6363

6464
/// A transitive closure visitor to collect all the edges of an object.
6565
pub struct ObjectsClosure<'a, E: ProcessEdgesWork> {
66-
buffer: Vec<Address>,
66+
buffer: Vec<EdgeOf<E>>,
6767
worker: &'a mut GCWorker<E::VM>,
6868
}
6969

@@ -85,9 +85,9 @@ impl<'a, E: ProcessEdgesWork> ObjectsClosure<'a, E> {
8585
}
8686
}
8787

88-
impl<'a, E: ProcessEdgesWork> EdgeVisitor for ObjectsClosure<'a, E> {
88+
impl<'a, E: ProcessEdgesWork> EdgeVisitor<EdgeOf<E>> for ObjectsClosure<'a, E> {
8989
#[inline(always)]
90-
fn visit_edge(&mut self, slot: Address) {
90+
fn visit_edge(&mut self, slot: EdgeOf<E>) {
9191
if self.buffer.is_empty() {
9292
self.buffer.reserve(E::CAPACITY);
9393
}

src/scheduler/gc_work.rs

+20-15
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use crate::plan::ObjectsClosure;
55
use crate::plan::VectorObjectQueue;
66
use crate::util::metadata::*;
77
use crate::util::*;
8+
use crate::vm::edge_shape::Edge;
89
use crate::vm::*;
910
use crate::*;
1011
use std::marker::PhantomData;
@@ -227,7 +228,7 @@ impl<VM: VMBinding> GCWork<VM> for EndOfGC {
227228
#[cfg(feature = "extreme_assertions")]
228229
if crate::util::edge_logger::should_check_duplicate_edges(&*mmtk.plan) {
229230
// reset the logging info at the end of each GC
230-
crate::util::edge_logger::reset();
231+
mmtk.edge_logger.reset();
231232
}
232233

233234
if <VM as VMBinding>::VMCollection::COORDINATOR_ONLY_STW {
@@ -331,7 +332,7 @@ impl<E: ProcessEdgesWork> GCWork<E::VM> for ScanVMSpecificRoots<E> {
331332
}
332333

333334
pub struct ProcessEdgesBase<VM: VMBinding> {
334-
pub edges: Vec<Address>,
335+
pub edges: Vec<VM::VMEdge>,
335336
pub nodes: VectorObjectQueue,
336337
mmtk: &'static MMTK<VM>,
337338
// Use raw pointer for fast pointer dereferencing, instead of using `Option<&'static mut GCWorker<E::VM>>`.
@@ -345,12 +346,12 @@ unsafe impl<VM: VMBinding> Send for ProcessEdgesBase<VM> {}
345346
impl<VM: VMBinding> ProcessEdgesBase<VM> {
346347
// Requires an MMTk reference. Each plan-specific type that uses ProcessEdgesBase can get a static plan reference
347348
// at creation. This avoids overhead for dynamic dispatch or downcasting plan for each object traced.
348-
pub fn new(edges: Vec<Address>, roots: bool, mmtk: &'static MMTK<VM>) -> Self {
349+
pub fn new(edges: Vec<VM::VMEdge>, roots: bool, mmtk: &'static MMTK<VM>) -> Self {
349350
#[cfg(feature = "extreme_assertions")]
350351
if crate::util::edge_logger::should_check_duplicate_edges(&*mmtk.plan) {
351352
for edge in &edges {
352353
// log edge, panic if already logged
353-
crate::util::edge_logger::log_edge(*edge);
354+
mmtk.edge_logger.log_edge(*edge);
354355
}
355356
}
356357
Self {
@@ -388,6 +389,9 @@ impl<VM: VMBinding> ProcessEdgesBase<VM> {
388389
}
389390
}
390391

392+
/// A short-hand for `<E::VM as VMBinding>::VMEdge`.
393+
pub type EdgeOf<E> = <<E as ProcessEdgesWork>::VM as VMBinding>::VMEdge;
394+
391395
/// Scan & update a list of object slots
392396
//
393397
// Note: be very careful when using this trait. process_node() will push objects
@@ -407,7 +411,8 @@ pub trait ProcessEdgesWork:
407411
const CAPACITY: usize = 4096;
408412
const OVERWRITE_REFERENCE: bool = true;
409413
const SCAN_OBJECTS_IMMEDIATELY: bool = true;
410-
fn new(edges: Vec<Address>, roots: bool, mmtk: &'static MMTK<Self::VM>) -> Self;
414+
415+
fn new(edges: Vec<EdgeOf<Self>>, roots: bool, mmtk: &'static MMTK<Self::VM>) -> Self;
411416

412417
/// Trace an MMTk object. The implementation should forward this call to the policy-specific
413418
/// `trace_object()` methods, depending on which space this object is in.
@@ -463,11 +468,11 @@ pub trait ProcessEdgesWork:
463468
}
464469

465470
#[inline]
466-
fn process_edge(&mut self, slot: Address) {
467-
let object = unsafe { slot.load::<ObjectReference>() };
471+
fn process_edge(&mut self, slot: EdgeOf<Self>) {
472+
let object = slot.load();
468473
let new_object = self.trace_object(object);
469474
if Self::OVERWRITE_REFERENCE {
470-
unsafe { slot.store(new_object) };
475+
slot.store(new_object);
471476
}
472477
}
473478

@@ -511,7 +516,7 @@ impl<VM: VMBinding> ProcessEdgesWork for SFTProcessEdges<VM> {
511516
type VM = VM;
512517
type ScanObjectsWorkType = ScanObjects<Self>;
513518

514-
fn new(edges: Vec<Address>, roots: bool, mmtk: &'static MMTK<VM>) -> Self {
519+
fn new(edges: Vec<EdgeOf<Self>>, roots: bool, mmtk: &'static MMTK<VM>) -> Self {
515520
let base = ProcessEdgesBase::new(edges, roots, mmtk);
516521
Self { base }
517522
}
@@ -552,8 +557,8 @@ impl<E: ProcessEdgesWork> Clone for ProcessEdgesWorkRootsWorkFactory<E> {
552557
}
553558
}
554559

555-
impl<E: ProcessEdgesWork> RootsWorkFactory for ProcessEdgesWorkRootsWorkFactory<E> {
556-
fn create_process_edge_roots_work(&mut self, edges: Vec<Address>) {
560+
impl<E: ProcessEdgesWork> RootsWorkFactory<EdgeOf<E>> for ProcessEdgesWorkRootsWorkFactory<E> {
561+
fn create_process_edge_roots_work(&mut self, edges: Vec<EdgeOf<E>>) {
557562
crate::memory_manager::add_work_packet(
558563
self.mmtk,
559564
WorkBucketStage::Closure,
@@ -827,7 +832,7 @@ impl<VM: VMBinding, P: PlanTraceObject<VM> + Plan<VM = VM>, const KIND: TraceKin
827832
type VM = VM;
828833
type ScanObjectsWorkType = PlanScanObjects<Self, P>;
829834

830-
fn new(edges: Vec<Address>, roots: bool, mmtk: &'static MMTK<VM>) -> Self {
835+
fn new(edges: Vec<EdgeOf<Self>>, roots: bool, mmtk: &'static MMTK<VM>) -> Self {
831836
let base = ProcessEdgesBase::new(edges, roots, mmtk);
832837
let plan = base.plan().downcast_ref::<P>().unwrap();
833838
Self { plan, base }
@@ -854,11 +859,11 @@ impl<VM: VMBinding, P: PlanTraceObject<VM> + Plan<VM = VM>, const KIND: TraceKin
854859
}
855860

856861
#[inline]
857-
fn process_edge(&mut self, slot: Address) {
858-
let object = unsafe { slot.load::<ObjectReference>() };
862+
fn process_edge(&mut self, slot: EdgeOf<Self>) {
863+
let object = slot.load();
859864
let new_object = self.trace_object(object);
860865
if P::may_move_objects::<KIND>() {
861-
unsafe { slot.store(new_object) };
866+
slot.store(new_object);
862867
}
863868
}
864869
}

src/util/edge_logger.rs

+38-24
Original file line numberDiff line numberDiff line change
@@ -5,39 +5,53 @@
55
//!
66
77
use crate::plan::Plan;
8-
use crate::util::Address;
8+
use crate::vm::edge_shape::Edge;
99
use crate::vm::VMBinding;
1010
use std::collections::HashSet;
1111
use std::sync::RwLock;
1212

13-
lazy_static! {
13+
pub struct EdgeLogger<ES: Edge> {
1414
// A private hash-set to keep track of edges.
15-
static ref EDGE_LOG: RwLock<HashSet<Address>> = RwLock::new(HashSet::new());
15+
edge_log: RwLock<HashSet<ES>>,
16+
}
17+
18+
unsafe impl<ES: Edge> Sync for EdgeLogger<ES> {}
19+
20+
impl<ES: Edge> EdgeLogger<ES> {
21+
pub fn new() -> Self {
22+
Self {
23+
edge_log: Default::default(),
24+
}
25+
}
26+
27+
/// Logs an edge.
28+
/// Panics if the edge was already logged.
29+
///
30+
/// # Arguments
31+
///
32+
/// * `edge` - The edge to log.
33+
///
34+
pub fn log_edge(&self, edge: ES) {
35+
trace!("log_edge({:?})", edge);
36+
let mut edge_log = self.edge_log.write().unwrap();
37+
assert!(
38+
edge_log.insert(edge),
39+
"duplicate edge ({:?}) detected",
40+
edge
41+
);
42+
}
43+
44+
/// Reset the edge logger by clearing the hash-set of edges.
45+
/// This function is called at the end of each GC iteration.
46+
///
47+
pub fn reset(&self) {
48+
let mut edge_log = self.edge_log.write().unwrap();
49+
edge_log.clear();
50+
}
1651
}
1752

1853
/// Whether we should check duplicate edges. This depends on the actual plan.
1954
pub fn should_check_duplicate_edges<VM: VMBinding>(plan: &dyn Plan<VM = VM>) -> bool {
2055
// If a plan allows tracing duplicate edges, we should not run this check.
2156
!plan.constraints().may_trace_duplicate_edges
2257
}
23-
24-
/// Logs an edge.
25-
/// Panics if the edge was already logged.
26-
///
27-
/// # Arguments
28-
///
29-
/// * `edge` - The edge to log.
30-
///
31-
pub fn log_edge(edge: Address) {
32-
trace!("log_edge({})", edge);
33-
let mut edge_log = EDGE_LOG.write().unwrap();
34-
assert!(edge_log.insert(edge), "duplicate edge ({}) detected", edge);
35-
}
36-
37-
/// Reset the edge logger by clearing the hash-set of edges.
38-
/// This function is called at the end of each GC iteration.
39-
///
40-
pub fn reset() {
41-
let mut edge_log = EDGE_LOG.write().unwrap();
42-
edge_log.clear();
43-
}

src/util/sanity/sanity_checker.rs

+8-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::plan::Plan;
22
use crate::scheduler::gc_work::*;
3-
use crate::util::{Address, ObjectReference};
3+
use crate::util::ObjectReference;
4+
use crate::vm::edge_shape::Edge;
45
use crate::vm::*;
56
use crate::MMTK;
67
use crate::{scheduler::*, ObjectQueue};
@@ -9,20 +10,20 @@ use std::ops::{Deref, DerefMut};
910
use std::sync::atomic::Ordering;
1011

1112
#[allow(dead_code)]
12-
pub struct SanityChecker {
13+
pub struct SanityChecker<ES: Edge> {
1314
/// Visited objects
1415
refs: HashSet<ObjectReference>,
1516
/// Cached root edges for sanity root scanning
16-
roots: Vec<Vec<Address>>,
17+
roots: Vec<Vec<ES>>,
1718
}
1819

19-
impl Default for SanityChecker {
20+
impl<ES: Edge> Default for SanityChecker<ES> {
2021
fn default() -> Self {
2122
Self::new()
2223
}
2324
}
2425

25-
impl SanityChecker {
26+
impl<ES: Edge> SanityChecker<ES> {
2627
pub fn new() -> Self {
2728
Self {
2829
refs: HashSet::new(),
@@ -31,7 +32,7 @@ impl SanityChecker {
3132
}
3233

3334
/// Cache a list of root edges to the sanity checker.
34-
pub fn add_roots(&mut self, roots: Vec<Address>) {
35+
pub fn add_roots(&mut self, roots: Vec<ES>) {
3536
self.roots.push(roots)
3637
}
3738

@@ -164,7 +165,7 @@ impl<VM: VMBinding> ProcessEdgesWork for SanityGCProcessEdges<VM> {
164165
type ScanObjectsWorkType = ScanObjects<Self>;
165166

166167
const OVERWRITE_REFERENCE: bool = false;
167-
fn new(edges: Vec<Address>, roots: bool, mmtk: &'static MMTK<VM>) -> Self {
168+
fn new(edges: Vec<EdgeOf<Self>>, roots: bool, mmtk: &'static MMTK<VM>) -> Self {
168169
Self {
169170
base: ProcessEdgesBase::new(edges, roots, mmtk),
170171
// ..Default::default()

0 commit comments

Comments
 (0)