Skip to content

Commit 836cac5

Browse files
committed
Allow configuring each Immix space to be non moving
1 parent 5f48a8a commit 836cac5

File tree

7 files changed

+62
-64
lines changed

7 files changed

+62
-64
lines changed

Cargo.toml

-5
Original file line numberDiff line numberDiff line change
@@ -152,11 +152,6 @@ object_pinning = []
152152
# Disable any object copying in Immix. This makes Immix a non-moving policy.
153153
immix_non_moving = []
154154

155-
# Disable any object copying in nursery GC for Sticky Immix while allowing other kinds of copying.
156-
# `immix_non_moving` disables all kinds of copying in Immix, so this feature is not needed
157-
# if `immix_non_moving` is in use.
158-
sticky_immix_non_moving_nursery = []
159-
160155
# Turn on stress copying for Immix. This is a debug feature to test copying for Immix plans.
161156
immix_stress_copying = []
162157
# Reduce block size for ImmixSpace. This mitigates fragmentation when defrag is disabled.

src/plan/generational/immix/global.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,9 @@ impl<VM: VMBinding> Plan for GenImmix<VM> {
8787

8888
fn last_collection_was_exhaustive(&self) -> bool {
8989
self.last_gc_was_full_heap.load(Ordering::Relaxed)
90-
&& ImmixSpace::<VM>::is_last_gc_exhaustive(
91-
self.last_gc_was_defrag.load(Ordering::Relaxed),
92-
)
90+
&& self
91+
.immix_space
92+
.is_last_gc_exhaustive(self.last_gc_was_defrag.load(Ordering::Relaxed))
9393
}
9494

9595
fn collection_required(&self, space_full: bool, space: Option<SpaceStats<Self::VM>>) -> bool
@@ -254,6 +254,7 @@ impl<VM: VMBinding> GenImmix<VM> {
254254
// In GenImmix, young objects are not allocated in ImmixSpace directly.
255255
#[cfg(feature = "vo_bit")]
256256
mixed_age: false,
257+
never_move_objects: cfg!(feature = "immix_non_moving"),
257258
},
258259
);
259260

src/plan/immix/global.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ pub struct Immix<VM: VMBinding> {
3838

3939
/// The plan constraints for the immix plan.
4040
pub const IMMIX_CONSTRAINTS: PlanConstraints = PlanConstraints {
41-
moves_objects: crate::policy::immix::DEFRAG,
41+
moves_objects: true,
4242
// Max immix object size is half of a block.
4343
max_non_los_default_alloc_bytes: crate::policy::immix::MAX_IMMIX_OBJECT_SIZE,
4444
needs_prepare_mutator: false,
@@ -51,7 +51,8 @@ impl<VM: VMBinding> Plan for Immix<VM> {
5151
}
5252

5353
fn last_collection_was_exhaustive(&self) -> bool {
54-
ImmixSpace::<VM>::is_last_gc_exhaustive(self.last_gc_was_defrag.load(Ordering::Relaxed))
54+
self.immix_space
55+
.is_last_gc_exhaustive(self.last_gc_was_defrag.load(Ordering::Relaxed))
5556
}
5657

5758
fn constraints(&self) -> &'static PlanConstraints {
@@ -139,6 +140,7 @@ impl<VM: VMBinding> Immix<VM> {
139140
unlog_object_when_traced: false,
140141
#[cfg(feature = "vo_bit")]
141142
mixed_age: false,
143+
never_move_objects: cfg!(feature = "immix_non_moving"),
142144
},
143145
)
144146
}

src/plan/sticky/immix/global.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ use crate::plan::PlanConstraints;
77
use crate::policy::gc_work::TraceKind;
88
use crate::policy::gc_work::TRACE_KIND_TRANSITIVE_PIN;
99
use crate::policy::immix::ImmixSpace;
10-
use crate::policy::immix::PREFER_COPY_ON_NURSERY_GC;
1110
use crate::policy::immix::TRACE_KIND_FAST;
1211
use crate::policy::sft::SFT;
1312
use crate::policy::space::Space;
@@ -41,7 +40,7 @@ pub struct StickyImmix<VM: VMBinding> {
4140

4241
/// The plan constraints for the sticky immix plan.
4342
pub const STICKY_IMMIX_CONSTRAINTS: PlanConstraints = PlanConstraints {
44-
moves_objects: crate::policy::immix::DEFRAG || crate::policy::immix::PREFER_COPY_ON_NURSERY_GC,
43+
moves_objects: true,
4544
needs_log_bit: true,
4645
barrier: crate::plan::BarrierSelector::ObjectBarrier,
4746
// We may trace duplicate edges in sticky immix (or any plan that uses object remembering barrier). See https://github.com/mmtk/mmtk-core/issues/743.
@@ -164,7 +163,7 @@ impl<VM: VMBinding> Plan for StickyImmix<VM> {
164163

165164
fn current_gc_may_move_object(&self) -> bool {
166165
if self.is_current_gc_nursery() {
167-
PREFER_COPY_ON_NURSERY_GC
166+
self.get_immix_space().prefer_copy_on_nursery_gc()
168167
} else {
169168
self.get_immix_space().in_defrag()
170169
}
@@ -263,7 +262,7 @@ impl<VM: VMBinding> crate::plan::generational::global::GenerationalPlanExt<VM> f
263262
self.immix
264263
.immix_space
265264
.trace_object_without_moving(queue, object)
266-
} else if crate::policy::immix::PREFER_COPY_ON_NURSERY_GC {
265+
} else if self.immix.immix_space.is_nursery_copy_enabled() {
267266
let ret = self.immix.immix_space.trace_object_with_opportunistic_copy(
268267
queue,
269268
object,
@@ -330,6 +329,7 @@ impl<VM: VMBinding> StickyImmix<VM> {
330329
// In StickyImmix, both young and old objects are allocated in the ImmixSpace.
331330
#[cfg(feature = "vo_bit")]
332331
mixed_age: true,
332+
never_move_objects: cfg!(feature = "immix_non_moving"),
333333
},
334334
);
335335
Self {

src/policy/immix/defrag.rs

+6-10
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,11 @@ impl Defrag {
7272
exhausted_reusable_space: bool,
7373
full_heap_system_gc: bool,
7474
) {
75-
let in_defrag = super::DEFRAG
76-
&& (emergency_collection
77-
|| (collection_attempts > 1)
78-
|| !exhausted_reusable_space
79-
|| super::STRESS_DEFRAG
80-
|| (collect_whole_heap && user_triggered && full_heap_system_gc));
75+
let in_defrag = emergency_collection
76+
|| (collection_attempts > 1)
77+
|| !exhausted_reusable_space
78+
|| super::STRESS_DEFRAG
79+
|| (collect_whole_heap && user_triggered && full_heap_system_gc);
8180
info!("Defrag: {}", in_defrag);
8281
probe!(mmtk, immix_defrag, in_defrag);
8382
self.in_defrag_collection
@@ -116,9 +115,8 @@ impl Defrag {
116115
}
117116

118117
/// Prepare work. Should be called in ImmixSpace::prepare.
119-
#[allow(clippy::assertions_on_constants)]
120118
pub fn prepare<VM: VMBinding>(&self, space: &ImmixSpace<VM>, plan_stats: StatsForDefrag) {
121-
debug_assert!(super::DEFRAG);
119+
debug_assert!(space.is_defrag_enabled());
122120
self.defrag_space_exhausted.store(false, Ordering::Release);
123121

124122
// Calculate available free space for defragmentation.
@@ -207,9 +205,7 @@ impl Defrag {
207205
}
208206

209207
/// Reset the in-defrag state.
210-
#[allow(clippy::assertions_on_constants)]
211208
pub fn reset_in_defrag(&self) {
212-
debug_assert!(super::DEFRAG);
213209
self.in_defrag_collection.store(false, Ordering::Release);
214210
}
215211
}

src/policy/immix/immixspace.rs

+44-15
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ pub struct ImmixSpaceArgs {
7373
// Currently only used when "vo_bit" is enabled. Using #[cfg(...)] to eliminate dead code warning.
7474
#[cfg(feature = "vo_bit")]
7575
pub mixed_age: bool,
76+
/// Disable copying for this Immix space.
77+
pub never_move_objects: bool,
7678
}
7779

7880
unsafe impl<VM: VMBinding> Sync for ImmixSpace<VM> {}
@@ -84,7 +86,7 @@ impl<VM: VMBinding> SFT for ImmixSpace<VM> {
8486

8587
fn get_forwarded_object(&self, object: ObjectReference) -> Option<ObjectReference> {
8688
// If we never move objects, look no further.
87-
if super::NEVER_MOVE_OBJECTS {
89+
if !self.is_movable() {
8890
return None;
8991
}
9092

@@ -102,7 +104,7 @@ impl<VM: VMBinding> SFT for ImmixSpace<VM> {
102104
}
103105

104106
// If we never move objects, look no further.
105-
if super::NEVER_MOVE_OBJECTS {
107+
if !self.is_movable() {
106108
return false;
107109
}
108110

@@ -122,7 +124,7 @@ impl<VM: VMBinding> SFT for ImmixSpace<VM> {
122124
VM::VMObjectModel::LOCAL_PINNING_BIT_SPEC.is_object_pinned::<VM>(object)
123125
}
124126
fn is_movable(&self) -> bool {
125-
!super::NEVER_MOVE_OBJECTS
127+
!self.is_defrag_enabled() && !self.is_nursery_copy_enabled()
126128
}
127129

128130
#[cfg(feature = "sanity")]
@@ -278,12 +280,13 @@ impl<VM: VMBinding> ImmixSpace<VM> {
278280
args: crate::policy::space::PlanCreateSpaceArgs<VM>,
279281
space_args: ImmixSpaceArgs,
280282
) -> Self {
281-
#[cfg(feature = "immix_non_moving")]
282-
info!(
283-
"Creating non-moving ImmixSpace: {}. Block size: 2^{}",
284-
args.name,
285-
Block::LOG_BYTES
286-
);
283+
if space_args.never_move_objects {
284+
info!(
285+
"Creating non-moving ImmixSpace: {}. Block size: 2^{}",
286+
args.name,
287+
Block::LOG_BYTES
288+
);
289+
}
287290

288291
if space_args.unlog_object_when_traced {
289292
assert!(
@@ -292,7 +295,18 @@ impl<VM: VMBinding> ImmixSpace<VM> {
292295
);
293296
}
294297

295-
super::validate_features();
298+
// validate features
299+
if super::BLOCK_ONLY {
300+
assert!(
301+
space_args.never_move_objects,
302+
"Block-only immix must not move objects"
303+
);
304+
}
305+
assert!(
306+
Block::LINES / 2 <= u8::MAX as usize - 2,
307+
"Number of lines in a block should not exceed BlockState::MARK_MARKED"
308+
);
309+
296310
#[cfg(feature = "vo_bit")]
297311
vo_bit::helper::validate_config::<VM>();
298312
let vm_map = args.vm_map;
@@ -355,6 +369,9 @@ impl<VM: VMBinding> ImmixSpace<VM> {
355369
user_triggered_collection: bool,
356370
full_heap_system_gc: bool,
357371
) -> bool {
372+
if !self.is_defrag_enabled() {
373+
return false;
374+
}
358375
self.defrag.decide_whether_to_defrag(
359376
emergency_collection,
360377
collect_whole_heap,
@@ -390,7 +407,7 @@ impl<VM: VMBinding> ImmixSpace<VM> {
390407
}
391408

392409
// Prepare defrag info
393-
if super::DEFRAG {
410+
if self.is_defrag_enabled() {
394411
self.defrag.prepare(self, plan_stats);
395412
}
396413

@@ -483,7 +500,7 @@ impl<VM: VMBinding> ImmixSpace<VM> {
483500
/// Return whether this GC was a defrag GC, as a plan may want to know this.
484501
pub fn end_of_gc(&mut self) -> bool {
485502
let did_defrag = self.defrag.in_defrag();
486-
if super::DEFRAG {
503+
if self.is_defrag_enabled() {
487504
self.defrag.reset_in_defrag();
488505
}
489506
did_defrag
@@ -806,8 +823,8 @@ impl<VM: VMBinding> ImmixSpace<VM> {
806823
Some((start, end))
807824
}
808825

809-
pub fn is_last_gc_exhaustive(did_defrag_for_last_gc: bool) -> bool {
810-
if super::DEFRAG {
826+
pub fn is_last_gc_exhaustive(&self, did_defrag_for_last_gc: bool) -> bool {
827+
if self.is_defrag_enabled() {
811828
did_defrag_for_last_gc
812829
} else {
813830
// If defrag is disabled, every GC is exhaustive.
@@ -833,6 +850,18 @@ impl<VM: VMBinding> ImmixSpace<VM> {
833850
self.mark_lines(object);
834851
}
835852
}
853+
854+
pub(crate) fn prefer_copy_on_nursery_gc(&self) -> bool {
855+
self.is_nursery_copy_enabled()
856+
}
857+
858+
pub(crate) fn is_nursery_copy_enabled(&self) -> bool {
859+
!self.space_args.never_move_objects
860+
}
861+
862+
pub(crate) fn is_defrag_enabled(&self) -> bool {
863+
!self.space_args.never_move_objects
864+
}
836865
}
837866

838867
/// A work packet to prepare each block for a major GC.
@@ -867,7 +896,7 @@ impl<VM: VMBinding> GCWork<VM> for PrepareBlockState<VM> {
867896
continue;
868897
}
869898
// Check if this block needs to be defragmented.
870-
let is_defrag_source = if !super::DEFRAG {
899+
let is_defrag_source = if self.space.space_args.never_move_objects {
871900
// Do not set any block as defrag source if defrag is disabled.
872901
false
873902
} else if super::DEFRAG_EVERY_BLOCK {

src/policy/immix/mod.rs

-25
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,6 @@ pub const MAX_IMMIX_OBJECT_SIZE: usize = Block::BYTES >> 1;
1414
/// Mark/sweep memory for block-level only
1515
pub const BLOCK_ONLY: bool = false;
1616

17-
/// Do we allow Immix to do defragmentation?
18-
pub const DEFRAG: bool = !cfg!(feature = "immix_non_moving"); // defrag if we are allowed to move.
19-
2017
// STRESS COPYING: Set the feature 'immix_stress_copying' so that Immix will copy as many objects as possible.
2118
// Useful for debugging copying GC if you cannot use SemiSpace.
2219
//
@@ -46,28 +43,6 @@ pub const DEFRAG_HEADROOM_PERCENT: usize = if cfg!(feature = "immix_stress_copyi
4643
2
4744
};
4845

49-
/// If Immix is used as a nursery space, do we prefer copy?
50-
pub const PREFER_COPY_ON_NURSERY_GC: bool =
51-
!cfg!(feature = "immix_non_moving") && !cfg!(feature = "sticky_immix_non_moving_nursery"); // copy nursery objects if we are allowed to move.
52-
53-
/// In some cases/settings, Immix may never move objects.
54-
/// Currently we only have two cases where we move objects: 1. defrag, 2. nursery copy.
55-
/// If we do neither, we will not move objects.
56-
/// If we have other reasons to move objects, we need to add them here.
57-
pub const NEVER_MOVE_OBJECTS: bool = !DEFRAG && !PREFER_COPY_ON_NURSERY_GC;
58-
5946
/// Mark lines when scanning objects.
6047
/// Otherwise, do it at mark time.
6148
pub const MARK_LINE_AT_SCAN_TIME: bool = true;
62-
63-
macro_rules! validate {
64-
($x: expr) => { assert!($x, stringify!($x)) };
65-
($x: expr => $y: expr) => { if $x { assert!($y, stringify!($x implies $y)) } };
66-
}
67-
68-
fn validate_features() {
69-
// Block-only immix cannot do defragmentation
70-
validate!(DEFRAG => !BLOCK_ONLY);
71-
// Number of lines in a block should not exceed BlockState::MARK_MARKED
72-
assert!(Block::LINES / 2 <= u8::MAX as usize - 2);
73-
}

0 commit comments

Comments
 (0)