Skip to content

Commit a9b619a

Browse files
authored
Clear side forwarding bits properly (#1138)
This PR fixes bugs related to the clearing of side forwarding bits. The side forwarding bits of ImmixSpace are now cleared after moving GCs instead of before every major GC. This eliminates redundant clearing operations during non-moving full-heap GCs (i.e. when not doing defrag), and also ensures the subsequent nursery GCs do not see stale forwarding bits when using StickyImmix in which case young objects are also allocated in the ImmixSpace. Also fixed the code for clearing side forwarding bits in discontiguous CopySpace.
1 parent 170630f commit a9b619a

File tree

5 files changed

+51
-29
lines changed

5 files changed

+51
-29
lines changed

src/policy/copyspace.rs

+13-19
Original file line numberDiff line numberDiff line change
@@ -168,32 +168,26 @@ impl<VM: VMBinding> CopySpace<VM> {
168168

169169
pub fn prepare(&self, from_space: bool) {
170170
self.from_space.store(from_space, Ordering::SeqCst);
171-
// Clear the metadata if we are using side forwarding status table. Otherwise
172-
// objects may inherit forwarding status from the previous GC.
173-
// TODO: Fix performance.
174-
if let MetadataSpec::OnSide(side_forwarding_status_table) =
175-
*<VM::VMObjectModel as ObjectModel<VM>>::LOCAL_FORWARDING_BITS_SPEC
176-
{
177-
side_forwarding_status_table
178-
.bzero_metadata(self.common.start, self.pr.cursor() - self.common.start);
179-
}
180171
}
181172

182173
pub fn release(&self) {
183-
unsafe {
174+
for (start, size) in self.pr.iterate_allocated_regions() {
175+
// Clear the forwarding bits if it is on the side.
176+
if let MetadataSpec::OnSide(side_forwarding_status_table) =
177+
*<VM::VMObjectModel as ObjectModel<VM>>::LOCAL_FORWARDING_BITS_SPEC
178+
{
179+
side_forwarding_status_table.bzero_metadata(start, size);
180+
}
181+
182+
// Clear VO bits because all objects in the space are dead.
184183
#[cfg(feature = "vo_bit")]
185-
self.reset_vo_bit();
186-
self.pr.reset();
184+
crate::util::metadata::vo_bit::bzero_vo_bit(start, size);
187185
}
188-
self.common.metadata.reset();
189-
self.from_space.store(false, Ordering::SeqCst);
190-
}
191186

192-
#[cfg(feature = "vo_bit")]
193-
unsafe fn reset_vo_bit(&self) {
194-
for (start, size) in self.pr.iterate_allocated_regions() {
195-
crate::util::metadata::vo_bit::bzero_vo_bit(start, size);
187+
unsafe {
188+
self.pr.reset();
196189
}
190+
self.from_space.store(false, Ordering::SeqCst);
197191
}
198192

199193
fn is_from_space(&self) -> bool {

src/policy/immix/defrag.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ impl Defrag {
7878
|| !exhausted_reusable_space
7979
|| super::STRESS_DEFRAG
8080
|| (collect_whole_heap && user_triggered && full_heap_system_gc));
81-
// println!("Defrag: {}", in_defrag);
81+
info!("Defrag: {}", in_defrag);
8282
self.in_defrag_collection
8383
.store(in_defrag, Ordering::Release)
8484
}

src/policy/immix/immixspace.rs

+28-5
Original file line numberDiff line numberDiff line change
@@ -841,10 +841,6 @@ impl<VM: VMBinding> PrepareBlockState<VM> {
841841
unimplemented!("We cannot bulk zero unlogged bit.")
842842
}
843843
}
844-
// If the forwarding bits are on the side, we need to clear them, too.
845-
if let MetadataSpec::OnSide(side) = *VM::VMObjectModel::LOCAL_FORWARDING_BITS_SPEC {
846-
side.bzero_metadata(self.chunk.start(), Chunk::BYTES);
847-
}
848844
}
849845
}
850846

@@ -891,14 +887,17 @@ struct SweepChunk<VM: VMBinding> {
891887
}
892888

893889
impl<VM: VMBinding> GCWork<VM> for SweepChunk<VM> {
894-
fn do_work(&mut self, _worker: &mut GCWorker<VM>, _mmtk: &'static MMTK<VM>) {
890+
fn do_work(&mut self, _worker: &mut GCWorker<VM>, mmtk: &'static MMTK<VM>) {
895891
let mut histogram = self.space.defrag.new_histogram();
896892
if self.space.chunk_map.get(self.chunk) == ChunkState::Allocated {
897893
let line_mark_state = if super::BLOCK_ONLY {
898894
None
899895
} else {
900896
Some(self.space.line_mark_state.load(Ordering::Acquire))
901897
};
898+
// Hints for clearing side forwarding bits.
899+
let is_moving_gc = mmtk.get_plan().current_gc_may_move_object();
900+
let is_defrag_gc = self.space.defrag.in_defrag();
902901
// number of allocated blocks.
903902
let mut allocated_blocks = 0;
904903
// Iterate over all allocated blocks in this chunk.
@@ -907,6 +906,30 @@ impl<VM: VMBinding> GCWork<VM> for SweepChunk<VM> {
907906
.iter_region::<Block>()
908907
.filter(|block| block.get_state() != BlockState::Unallocated)
909908
{
909+
// Clear side forwarding bits.
910+
// In the beginning of the next GC, no side forwarding bits shall be set.
911+
// In this way, we can omit clearing forwarding bits when copying object.
912+
// See `GCWorkerCopyContext::post_copy`.
913+
// Note, `block.sweep()` overwrites `DEFRAG_STATE_TABLE` with the number of holes,
914+
// but we need it to know if a block is a defrag source.
915+
// We clear forwarding bits before `block.sweep()`.
916+
if let MetadataSpec::OnSide(side) = *VM::VMObjectModel::LOCAL_FORWARDING_BITS_SPEC {
917+
if is_moving_gc {
918+
let objects_may_move = if is_defrag_gc {
919+
// If it is a defrag GC, we only clear forwarding bits for defrag sources.
920+
block.is_defrag_source()
921+
} else {
922+
// Otherwise, it must be a nursery GC of StickyImmix with copying nursery.
923+
// We don't have information about which block contains moved objects,
924+
// so we have to clear forwarding bits for all blocks.
925+
true
926+
};
927+
if objects_may_move {
928+
side.bzero_metadata(block.start(), Block::BYTES);
929+
}
930+
}
931+
}
932+
910933
if !block.sweep(self.space, &mut histogram, line_mark_state) {
911934
// Block is live. Increment the allocated block count.
912935
allocated_blocks += 1;

src/util/copy/mod.rs

+9-2
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,15 @@ impl<VM: VMBinding> GCWorkerCopyContext<VM> {
110110
/// * `bytes`: The size of the object in bytes.
111111
/// * `semantics`: The copy semantic used for the copying.
112112
pub fn post_copy(&mut self, object: ObjectReference, bytes: usize, semantics: CopySemantics) {
113-
// Clear forwarding bits.
114-
object_forwarding::clear_forwarding_bits::<VM>(object);
113+
if VM::VMObjectModel::LOCAL_FORWARDING_BITS_SPEC.is_in_header() {
114+
// Clear forwarding bits if the forwarding bits are in the header.
115+
object_forwarding::clear_forwarding_bits::<VM>(object);
116+
} else {
117+
// We ensure no stale side forwarding bits exist before tracing.
118+
debug_assert!(!object_forwarding::is_forwarded_or_being_forwarded::<VM>(
119+
object
120+
));
121+
}
115122
// If we are copying objects in mature space, we would need to mark the object as mature.
116123
if semantics.is_mature() && self.config.constraints.needs_log_bit {
117124
// If the plan uses unlogged bit, we set the unlogged bit (the object is unlogged/mature)

src/util/metadata/side_metadata/global.rs

-2
Original file line numberDiff line numberDiff line change
@@ -1080,8 +1080,6 @@ impl SideMetadataContext {
10801080
total
10811081
}
10821082

1083-
pub fn reset(&self) {}
1084-
10851083
// ** NOTE: **
10861084
// Regardless of the number of bits in a metadata unit, we always represent its content as a word.
10871085

0 commit comments

Comments
 (0)