Skip to content

Commit

Permalink
Backporting conservative stack scanning to dev
Browse files Browse the repository at this point in the history
  • Loading branch information
udesou committed Dec 11, 2024
1 parent 19a180c commit c6d752c
Show file tree
Hide file tree
Showing 8 changed files with 205 additions and 61 deletions.
13 changes: 7 additions & 6 deletions mmtk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ edition = "2018"
[package.metadata.julia]
# Our CI matches the following line and extract mmtk/julia. If this line is updated, please check ci yaml files and make sure it works.
julia_repo = "https://github.com/udesou/julia.git"
julia_version = "118d962ee2bda4f18060fc6ff726c739acb3fa45"
julia_version = "b5e7a9ddc28c145a025c38b41bc9492f4ad534e8"

[lib]
crate-type = ["cdylib"]
Expand Down Expand Up @@ -46,16 +46,17 @@ memoffset = "*"
# ykstackmaps = { git = "https://github.com/udesou/ykstackmaps.git", branch = "udesou-master", version = "*" }

[features]
default = ["mmtk/vm_space", "julia_copy_stack", "object_pinning", "is_mmtk_object", "mmtk/vo_bit_access"]
# We must build with default features
default = ["mmtk/vm_space", "julia_copy_stack", "mmtk/object_pinning", "mmtk/is_mmtk_object", "mmtk/vo_bit_access"]

# Plans
# Default features
julia_copy_stack = []

# Plans: choose one
nogc = []
immix = []
stickyimmix = ["mmtk/sticky_immix_non_moving_nursery", "mmtk/immix_smaller_block"]
marksweep = []
object_pinning = ["mmtk/object_pinning"]
is_mmtk_object = ["mmtk/is_mmtk_object"]

# This feature disables moving
non_moving = ["mmtk/immix_non_moving", "mmtk/immix_smaller_block"]
julia_copy_stack = []
2 changes: 2 additions & 0 deletions mmtk/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ fn main() {
let bindings = bindgen::Builder::default()
.header(format!("{}/src/julia.h", julia_dir))
.header(format!("{}/src/julia_internal.h", julia_dir))
.header(format!("{}/src/gc-common.h", julia_dir))
// Including the paths to depending .h files
.clang_arg("-I")
.clang_arg(format!("{}/mmtk/api", mmtk_dir))
Expand All @@ -51,6 +52,7 @@ fn main() {
.allowlist_item("jl_bt_element_t")
.allowlist_item("jl_taggedvalue_t")
.allowlist_item("_jl_module_using")
.allowlist_item("_bigval_t")
.allowlist_item("MMTkMutatorContext")
// --opaque-type MMTkMutatorContext
.opaque_type("MMTkMutatorContext")
Expand Down
34 changes: 9 additions & 25 deletions mmtk/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,6 @@ pub extern "C" fn mmtk_set_vm_space(start: Address, size: usize) {

#[cfg(feature = "stickyimmix")]
set_side_log_bit_for_region(start, size);
#[cfg(feature = "is_mmtk_object")]
set_side_vo_bit_for_region(start, size);
}

Expand Down Expand Up @@ -371,7 +370,7 @@ pub extern "C" fn mmtk_memory_region_copy(
pub extern "C" fn mmtk_immortal_region_post_alloc(start: Address, size: usize) {
#[cfg(feature = "stickyimmix")]
set_side_log_bit_for_region(start, size);
#[cfg(feature = "is_mmtk_object")]

set_side_vo_bit_for_region(start, size);
}

Expand All @@ -385,7 +384,8 @@ fn set_side_log_bit_for_region(start: Address, size: usize) {
}
}

#[cfg(feature = "is_mmtk_object")]
// We have to set VO bit even if this is a non_moving build. Otherwise, assertions in mmtk-core
// will complain about seeing objects without VO bit.
fn set_side_vo_bit_for_region(start: Address, size: usize) {
debug!(
"Bulk set VO bit {} to {} ({} bytes)",
Expand Down Expand Up @@ -473,9 +473,10 @@ pub extern "C" fn mmtk_get_obj_size(obj: ObjectReference) -> usize {
}
}

#[cfg(all(feature = "object_pinning", not(feature = "non_moving")))]
#[no_mangle]
pub extern "C" fn mmtk_pin_object(object: ObjectReference) -> bool {
crate::early_return_for_non_moving!(false);

// We may in the future replace this with a check for the immix space (bound check), which should be much cheaper.
if mmtk_object_is_managed_by_mmtk(object.to_raw_address().as_usize()) {
memory_manager::pin_object(object)
Expand All @@ -485,9 +486,10 @@ pub extern "C" fn mmtk_pin_object(object: ObjectReference) -> bool {
}
}

#[cfg(all(feature = "object_pinning", not(feature = "non_moving")))]
#[no_mangle]
pub extern "C" fn mmtk_unpin_object(object: ObjectReference) -> bool {
crate::early_return_for_non_moving!(false);

if mmtk_object_is_managed_by_mmtk(object.to_raw_address().as_usize()) {
memory_manager::unpin_object(object)
} else {
Expand All @@ -496,9 +498,10 @@ pub extern "C" fn mmtk_unpin_object(object: ObjectReference) -> bool {
}
}

#[cfg(all(feature = "object_pinning", not(feature = "non_moving")))]
#[no_mangle]
pub extern "C" fn mmtk_is_pinned(object: ObjectReference) -> bool {
crate::early_return_for_non_moving!(false);

if mmtk_object_is_managed_by_mmtk(object.to_raw_address().as_usize()) {
memory_manager::is_pinned(object)
} else {
Expand All @@ -507,25 +510,6 @@ pub extern "C" fn mmtk_is_pinned(object: ObjectReference) -> bool {
}
}

// If the `non-moving` feature is selected, pinning/unpinning is a noop and simply returns false
#[cfg(all(feature = "object_pinning", feature = "non_moving"))]
#[no_mangle]
pub extern "C" fn mmtk_pin_object(_object: ObjectReference) -> bool {
false
}

#[cfg(all(feature = "object_pinning", feature = "non_moving"))]
#[no_mangle]
pub extern "C" fn mmtk_unpin_object(_object: ObjectReference) -> bool {
false
}

#[cfg(all(feature = "object_pinning", feature = "non_moving"))]
#[no_mangle]
pub extern "C" fn mmtk_is_pinned(_object: ObjectReference) -> bool {
false
}

#[no_mangle]
pub extern "C" fn get_mmtk_version() -> *const c_char {
crate::build_info::MMTK_JULIA_FULL_VERSION_STRING
Expand Down
27 changes: 16 additions & 11 deletions mmtk/src/collection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ impl Collection<JuliaVM> for VMCollection {
}

fn resume_mutators(_tls: VMWorkerThread) {
// unpin conservative roots
crate::conservative::unpin_conservative_roots();

// Get the end time of the GC
let end = unsafe { jl_hrtime() };
trace!("gc_end = {}", end);
Expand Down Expand Up @@ -83,18 +86,20 @@ impl Collection<JuliaVM> for VMCollection {

fn spawn_gc_thread(_tls: VMThread, ctx: GCThreadContext<JuliaVM>) {
// Just drop the join handle. The thread will run until the process quits.
let _ = std::thread::spawn(move || {
use mmtk::util::opaque_pointer::*;
use mmtk::util::Address;
let worker_tls = VMWorkerThread(VMThread(OpaquePointer::from_address(unsafe {
Address::from_usize(thread_id::get())
})));
match ctx {
GCThreadContext::Worker(w) => {
mmtk::memory_manager::start_worker(&SINGLETON, worker_tls, w)
let _ = std::thread::Builder::new()
.name("MMTk Worker".to_string())
.spawn(move || {
use mmtk::util::opaque_pointer::*;
use mmtk::util::Address;
let worker_tls = VMWorkerThread(VMThread(OpaquePointer::from_address(unsafe {
Address::from_usize(thread_id::get())
})));
match ctx {
GCThreadContext::Worker(w) => {
mmtk::memory_manager::start_worker(&SINGLETON, worker_tls, w)
}
}
}
});
});
}

fn schedule_finalization(_tls: VMWorkerThread) {}
Expand Down
102 changes: 102 additions & 0 deletions mmtk/src/conservative.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
use crate::jl_task_stack_buffer;
use crate::julia_types::*;
use mmtk::memory_manager;
use mmtk::util::constants::BYTES_IN_ADDRESS;
use mmtk::util::{Address, ObjectReference};
use std::collections::HashSet;
use std::sync::Mutex;
lazy_static! {
pub static ref CONSERVATIVE_ROOTS: Mutex<HashSet<ObjectReference>> = Mutex::new(HashSet::new());
}
pub fn pin_conservative_roots() {
crate::early_return_for_non_moving!(());
let mut roots = CONSERVATIVE_ROOTS.lock().unwrap();
let n_roots = roots.len();
roots.retain(|obj| mmtk::memory_manager::pin_object(*obj));
let n_pinned = roots.len();
log::debug!("Conservative roots: {}, pinned: {}", n_roots, n_pinned);
}
pub fn unpin_conservative_roots() {
crate::early_return_for_non_moving!(());
let mut roots = CONSERVATIVE_ROOTS.lock().unwrap();
let n_pinned = roots.len();
let mut n_live = 0;
roots.drain().for_each(|obj| {
if mmtk::memory_manager::is_live_object(obj) {
n_live += 1;
mmtk::memory_manager::unpin_object(obj);
}
});
log::debug!(
"Conservative roots: pinned: {}, unpinned/live {}",
n_pinned,
n_live
);
}
pub fn mmtk_conservative_scan_task_stack(ta: *const jl_task_t) {
crate::early_return_for_non_moving!(());
let mut size: u64 = 0;
let mut ptid: i32 = 0;
log::info!("mmtk_conservative_scan_native_stack begin ta = {:?}", ta);
let stk = unsafe { jl_task_stack_buffer(ta, &mut size as *mut _, &mut ptid as *mut _) };
log::info!(
"mmtk_conservative_scan_native_stack continue stk = {}, size = {}, ptid = {:x}",
stk,
size,
ptid
);
if !stk.is_zero() {
log::info!("Conservatively scan the stack");
// See jl_guard_size
// TODO: Are we sure there are always guard pages we need to skip?
const JL_GUARD_PAGE: usize = 4096 * 8;
let guard_page_start = stk + JL_GUARD_PAGE;
log::info!("Skip guard page: {}, {}", stk, guard_page_start);
conservative_scan_range(guard_page_start, stk + size as usize);
} else {
log::warn!("Skip stack for {:?}", ta);
}
}
pub fn mmtk_conservative_scan_task_registers(ta: *const jl_task_t) {
crate::early_return_for_non_moving!(());
let (lo, hi) = get_range(&unsafe { &*ta }.ctx);
conservative_scan_range(lo, hi);
}
pub fn mmtk_conservative_scan_ptls_registers(ptls: &mut _jl_tls_states_t) {
crate::early_return_for_non_moving!(());
let (lo, hi) = get_range(&((*ptls).gc_tls.ctx_at_the_time_gc_started));
conservative_scan_range(lo, hi);
}
// TODO: This scans the entire context type, which is slower.
// We actually only need to scan registers.
fn get_range<T>(ctx: &T) -> (Address, Address) {
let start = Address::from_ptr(ctx);
let ty_size = std::mem::size_of::<T>();
(start, start + ty_size)
}
fn conservative_scan_range(lo: Address, hi: Address) {
// The high address is exclusive
let hi = if hi.is_aligned_to(BYTES_IN_ADDRESS) {
hi - BYTES_IN_ADDRESS
} else {
hi.align_down(BYTES_IN_ADDRESS)
};
let lo = lo.align_up(BYTES_IN_ADDRESS);
log::info!("Scan {} (lo) {} (hi)", lo, hi);
let mut cursor = hi;
while cursor >= lo {
let addr = unsafe { cursor.load::<Address>() };
if let Some(obj) = is_potential_mmtk_object(addr) {
CONSERVATIVE_ROOTS.lock().unwrap().insert(obj);
}
cursor -= BYTES_IN_ADDRESS;
}
}
fn is_potential_mmtk_object(addr: Address) -> Option<ObjectReference> {
if crate::object_model::is_addr_in_immixspace(addr) {
// We only care about immix space. If the object is in other spaces, we won't move them, and we don't need to pin them.
memory_manager::find_object_from_internal_pointer(addr, usize::MAX)
} else {
None
}
}
15 changes: 15 additions & 0 deletions mmtk/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pub mod active_plan;
pub mod api;
mod build_info;
pub mod collection;
pub mod conservative;
pub mod object_model;
pub mod reference_glue;
pub mod scanning;
Expand Down Expand Up @@ -116,4 +117,18 @@ extern "C" {
pub fn arraylist_grow(a: Address, n: usize);
pub fn jl_get_gc_disable_counter() -> u32;
pub fn jl_hrtime() -> u64;
pub fn jl_task_stack_buffer(
task: *const crate::julia_types::jl_task_t,
size: *mut u64,
ptid: *mut i32,
) -> Address;
}

#[macro_export]
macro_rules! early_return_for_non_moving {
($ret_val:expr) => {
if cfg!(feature = "non_moving") {
return $ret_val;
}
};
}
Loading

0 comments on commit c6d752c

Please sign in to comment.