Skip to content

Commit 5c1c8b1

Browse files
authored
Allow configuring each Immix space to be non moving (#1305)
We plan to use non-moving Immix as a non-moving space. In this case, we will have two Immix spaces, one allows moving, and the other does not. We currently use global constants to control whether Immix moves objects or not, and we cannot have two Immix spaces that are configured differently. This PR removes those constants, and adds a flag `never_move_objects` in `ImmixSpaceArgs` to indicate whether this Immix space disallows moving.
1 parent 42a434b commit 5c1c8b1

File tree

6 files changed

+64
-56
lines changed

6 files changed

+64
-56
lines changed

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: false,
257258
},
258259
);
259260

src/plan/immix/global.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ 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+
// If we disable moving in Immix, this is a non-moving plan.
42+
moves_objects: !cfg!(feature = "immix_non_moving"),
4243
// Max immix object size is half of a block.
4344
max_non_los_default_alloc_bytes: crate::policy::immix::MAX_IMMIX_OBJECT_SIZE,
4445
needs_prepare_mutator: false,
@@ -51,7 +52,8 @@ impl<VM: VMBinding> Plan for Immix<VM> {
5152
}
5253

5354
fn last_collection_was_exhaustive(&self) -> bool {
54-
ImmixSpace::<VM>::is_last_gc_exhaustive(self.last_gc_was_defrag.load(Ordering::Relaxed))
55+
self.immix_space
56+
.is_last_gc_exhaustive(self.last_gc_was_defrag.load(Ordering::Relaxed))
5557
}
5658

5759
fn constraints(&self) -> &'static PlanConstraints {
@@ -139,6 +141,7 @@ impl<VM: VMBinding> Immix<VM> {
139141
unlog_object_when_traced: false,
140142
#[cfg(feature = "vo_bit")]
141143
mixed_age: false,
144+
never_move_objects: false,
142145
},
143146
)
144147
}

src/plan/sticky/immix/global.rs

+5-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,8 @@ 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+
// If we disable moving in Immix, this is a non-moving plan.
44+
moves_objects: !cfg!(feature = "immix_non_moving"),
4545
needs_log_bit: true,
4646
barrier: crate::plan::BarrierSelector::ObjectBarrier,
4747
// 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 +164,7 @@ impl<VM: VMBinding> Plan for StickyImmix<VM> {
164164

165165
fn current_gc_may_move_object(&self) -> bool {
166166
if self.is_current_gc_nursery() {
167-
PREFER_COPY_ON_NURSERY_GC
167+
self.get_immix_space().prefer_copy_on_nursery_gc()
168168
} else {
169169
self.get_immix_space().in_defrag()
170170
}
@@ -263,7 +263,7 @@ impl<VM: VMBinding> crate::plan::generational::global::GenerationalPlanExt<VM> f
263263
self.immix
264264
.immix_space
265265
.trace_object_without_moving(queue, object)
266-
} else if crate::policy::immix::PREFER_COPY_ON_NURSERY_GC {
266+
} else if self.immix.immix_space.prefer_copy_on_nursery_gc() {
267267
let ret = self.immix.immix_space.trace_object_with_opportunistic_copy(
268268
queue,
269269
object,
@@ -330,6 +330,7 @@ impl<VM: VMBinding> StickyImmix<VM> {
330330
// In StickyImmix, both young and old objects are allocated in the ImmixSpace.
331331
#[cfg(feature = "vo_bit")]
332332
mixed_age: true,
333+
never_move_objects: false,
333334
},
334335
);
335336
Self {

src/policy/immix/defrag.rs

+4-5
Original file line numberDiff line numberDiff line change
@@ -63,16 +63,18 @@ impl Defrag {
6363
}
6464

6565
/// Determine whether the current GC should do defragmentation.
66+
#[allow(clippy::too_many_arguments)]
6667
pub fn decide_whether_to_defrag(
6768
&self,
69+
defrag_enabled: bool,
6870
emergency_collection: bool,
6971
collect_whole_heap: bool,
7072
collection_attempts: usize,
7173
user_triggered: bool,
7274
exhausted_reusable_space: bool,
7375
full_heap_system_gc: bool,
7476
) {
75-
let in_defrag = super::DEFRAG
77+
let in_defrag = defrag_enabled
7678
&& (emergency_collection
7779
|| (collection_attempts > 1)
7880
|| !exhausted_reusable_space
@@ -116,9 +118,8 @@ impl Defrag {
116118
}
117119

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

124125
// Calculate available free space for defragmentation.
@@ -207,9 +208,7 @@ impl Defrag {
207208
}
208209

209210
/// Reset the in-defrag state.
210-
#[allow(clippy::assertions_on_constants)]
211211
pub fn reset_in_defrag(&self) {
212-
debug_assert!(super::DEFRAG);
213212
self.in_defrag_collection.store(false, Ordering::Release);
214213
}
215214
}

src/policy/immix/immixspace.rs

+46-17
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.space_args.never_move_objects
126128
}
127129

128130
#[cfg(feature = "sanity")]
@@ -276,23 +278,37 @@ impl<VM: VMBinding> ImmixSpace<VM> {
276278

277279
pub fn new(
278280
args: crate::policy::space::PlanCreateSpaceArgs<VM>,
279-
space_args: ImmixSpaceArgs,
281+
mut 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-
);
287-
288283
if space_args.unlog_object_when_traced {
289284
assert!(
290285
args.constraints.needs_log_bit,
291286
"Invalid args when the plan does not use log bit"
292287
);
293288
}
294289

295-
super::validate_features();
290+
// Make sure we override the space args if we force non moving Immix
291+
if cfg!(feature = "immix_non_moving") && !space_args.never_move_objects {
292+
info!(
293+
"Overriding never_moves_objects for Immix Space {}, as the immix_non_moving feature is set. Block size: 2^{}",
294+
args.name,
295+
Block::LOG_BYTES,
296+
);
297+
space_args.never_move_objects = true;
298+
}
299+
300+
// validate features
301+
if super::BLOCK_ONLY {
302+
assert!(
303+
space_args.never_move_objects,
304+
"Block-only immix must not move objects"
305+
);
306+
}
307+
assert!(
308+
Block::LINES / 2 <= u8::MAX as usize - 2,
309+
"Number of lines in a block should not exceed BlockState::MARK_MARKED"
310+
);
311+
296312
#[cfg(feature = "vo_bit")]
297313
vo_bit::helper::validate_config::<VM>();
298314
let vm_map = args.vm_map;
@@ -356,6 +372,7 @@ impl<VM: VMBinding> ImmixSpace<VM> {
356372
full_heap_system_gc: bool,
357373
) -> bool {
358374
self.defrag.decide_whether_to_defrag(
375+
self.is_defrag_enabled(),
359376
emergency_collection,
360377
collect_whole_heap,
361378
collection_attempts,
@@ -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 && !cfg!(feature = "sticky_immix_non_moving_nursery")
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.is_defrag_enabled() {
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)