From 6d565ad8369a532aeb67f56316af29ce952ffe1c Mon Sep 17 00:00:00 2001 From: playX18 Date: Thu, 20 Feb 2025 20:46:10 +0700 Subject: [PATCH] proc-macro --- .editorconfig | 0 Cargo.lock | 2 + vmkit-proc/Cargo.toml | 2 +- vmkit-proc/src/lib.rs | 593 ++++++++++++++++++++++++++++++- vmkit-proc/src/main.rs | 3 - vmkit/Cargo.toml | 5 +- vmkit/src/lib.rs | 12 + vmkit/src/main.rs | 279 ++++++++++++++- vmkit/src/object_model/object.rs | 102 ++++++ 9 files changed, 982 insertions(+), 16 deletions(-) create mode 100644 .editorconfig delete mode 100644 vmkit-proc/src/main.rs diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..e69de29 diff --git a/Cargo.lock b/Cargo.lock index 2677185..671f204 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1233,6 +1233,7 @@ dependencies = [ "parking_lot", "rand", "sysinfo 0.33.1", + "vmkit-proc", "winapi", ] @@ -1243,6 +1244,7 @@ dependencies = [ "proc-macro2", "quote", "syn 2.0.98", + "synstructure", ] [[package]] diff --git a/vmkit-proc/Cargo.toml b/vmkit-proc/Cargo.toml index 882cdea..3f25296 100644 --- a/vmkit-proc/Cargo.toml +++ b/vmkit-proc/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" proc-macro2 = "1.0.93" quote = "1.0.38" syn = { version = "2.0.98", features = ["full"] } - +synstructure = { version = "0.13.1" } [lib] proc-macro = true \ No newline at end of file diff --git a/vmkit-proc/src/lib.rs b/vmkit-proc/src/lib.rs index 8333ee2..d96bb66 100644 --- a/vmkit-proc/src/lib.rs +++ b/vmkit-proc/src/lib.rs @@ -1,6 +1,591 @@ -#[proc_macro] -pub fn define_options(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let _input = proc_macro2::TokenStream::from(input); +//! Proc macro to derive GCMetadata implementation for types. - proc_macro::TokenStream::new() +extern crate proc_macro; + +use std::collections::HashSet; + +use proc_macro::TokenStream; +use proc_macro2::{extra, Span}; +use quote::quote; +use syn::{parse_macro_input, Arm, Data, DeriveInput}; +use synstructure::{decl_derive, AddBounds}; + +struct ArrayLike { + pub length_field: Option, + /// Getter for length field. + pub length_getter: Option, + /// Setter for length field. + pub length_setter: Option, + pub element_type: syn::Type, + pub data_field: Option, + pub data_getter: Option, + pub trace: bool, +} + +impl Default for ArrayLike { + fn default() -> Self { + Self { + length_field: None, + length_getter: None, + length_setter: None, + element_type: syn::parse_str("u8").unwrap(), + data_field: None, + data_getter: None, + trace: true, + } + } +} + +fn find_arraylike(attrs: &[syn::Attribute]) -> syn::Result> { + let mut length_field = None; + let mut length_getter = None; + let mut length_setter = None; + let mut element_type = None; + let mut data_field = None; + let mut data_getter = None; + let mut trace = None; + + let mut has = false; + for attr in attrs.iter() { + if attr.meta.path().is_ident("arraylike") { + has = true; + attr.parse_nested_meta(|meta| { + if meta.path.is_ident("trace") { + let value = meta.value()?.parse::()?; + if value.value() { + trace = Some(true); + } else { + trace = Some(false); + } + } + + if meta.path.is_ident("len") { + if length_field.is_some() || length_getter.is_some() || length_setter.is_some() + { + return Err(meta.error("multiple length fields found")); + } + + // parse `len = field` or `len(getter = field)` or `len(setter = field)` + if meta.input.peek(syn::Token![=]) { + meta.input.parse::()?; + let field = meta.input.parse::()?; + if length_field.is_some() { + return Err(meta.error("multiple length fields found")); + } + length_field = Some(field); + } else if meta.input.peek(syn::token::Paren) { + meta.parse_nested_meta(|meta| { + if meta.path.is_ident("get") { + if length_getter.is_some() { + return Err(meta.error("multiple length getters found")); + } + + let field = meta.input.parse::()?; + length_getter = Some(field); + } else if meta.path.is_ident("set") { + if length_setter.is_some() { + return Err(meta.error("multiple length setters found")); + } + let field = meta.input.parse::()?; + length_setter = Some(field); + } else { + return Err(meta.error("unknown length field")); + } + Ok(()) + })?; + } else { + return Err(meta.error("unknown length attribute for #[arraylike]")); + } + } + + if meta.path.is_ident("data") { + if data_field.is_some() || data_getter.is_some() { + return Err(meta.error("multiple data fields found")); + } + if meta.input.peek(syn::token::Paren) { + meta.parse_nested_meta(|meta| { + if meta.path.is_ident("get") { + if data_getter.is_some() { + return Err(meta.error("multiple data getters found")); + } + + let field = meta.input.parse::()?; + data_getter = Some(field); + } else { + return Err(meta.error("unknown data field")); + } + Ok(()) + })?; + } else { + let field = meta.input.parse::()?; + data_field = Some(field); + } + } + + if meta.path.is_ident("data_type") { + if element_type.is_some() { + return Err(meta.error("multiple data types found")); + } + meta.input.parse::()?; + let field = meta.input.parse::()?; + element_type = Some(field); + } + + Ok(()) + })?; + } + } + + if has { + Ok(Some(ArrayLike { + length_field, + length_getter, + length_setter, + element_type: element_type + .ok_or_else(|| syn::Error::new(Span::call_site(), "missing data_type"))?, + data_field, + data_getter, + trace: trace.unwrap_or(true), + })) + } else { + Ok(None) + } +} + +enum ObjectAlignment { + Auto, + Const(syn::Expr), + Compute(syn::Expr), +} + +enum ObjectSize { + Auto, + Size(syn::Expr), + Compute(syn::Expr), +} + +enum ObjectTrace { + NoTrace, + Auto(bool), + Trace(bool, syn::Expr), +} + +struct GCMetadata { + vm: syn::Type, + alignment: ObjectAlignment, + size: ObjectSize, + trace: ObjectTrace, +} + +fn find_gcmetadata(attrs: &[syn::Attribute]) -> syn::Result { + let mut vm = None; + let mut alignment = None; + let mut size = None; + let mut trace = None; + + let mut has = false; + for attr in attrs.iter() { + if attr.meta.path().is_ident("gcmetadata") { + has = true; + + attr.parse_nested_meta(|meta| { + if meta.path.is_ident("vm") { + if vm.is_some() { + return Err(meta.error("multiple vm fields found")); + } + meta.input.parse::()?; + let field = meta.input.parse::()?; + vm = Some(field); + } + + if meta.path.is_ident("align") { + if alignment.is_some() { + return Err(meta.error("multiple alignment fields found")); + } + if meta.input.peek(syn::token::Paren) { + let constant = meta.input.parse::()?; + alignment = Some(ObjectAlignment::Const(constant)); + } else { + let _ = meta.input.parse::()?; + let field = meta.input.parse::()?; + alignment = Some(ObjectAlignment::Compute(field)); + } + } + + if meta.path.is_ident("size") { + if size.is_some() { + return Err(meta.error("multiple size fields found")); + } + if meta.input.peek(syn::token::Paren) { + let constant = meta.input.parse::()?; + size = Some(ObjectSize::Size(constant)); + } else { + let _ = meta.input.parse::()?; + let field = meta.input.parse::()?; + size = Some(ObjectSize::Compute(field)); + } + } + + if meta.path.is_ident("trace") { + if trace.is_some() { + return Err(meta.error("multiple trace fields found")); + } + if meta.input.is_empty() { + trace = Some(ObjectTrace::Auto(false)); + } else { + let _ = meta.input.parse::()?; + let field = meta.input.parse::()?; + trace = Some(ObjectTrace::Trace(false, field)); + } + } else if meta.path.is_ident("notrace") { + if trace.is_some() { + return Err(meta.error("multiple trace fields found")); + } + trace = Some(ObjectTrace::NoTrace); + } else if meta.path.is_ident("scan") { + if trace.is_some() { + return Err(meta.error("multiple trace fields found")); + } + if meta.input.is_empty() { + trace = Some(ObjectTrace::Auto(true)); + } else { + let _ = meta.input.parse::()?; + let field = meta.input.parse::()?; + trace = Some(ObjectTrace::Trace(true, field)); + } + } + + Ok(()) + })?; + } + } + if !has { + return Err(syn::Error::new(Span::call_site(), "missing gcmetadata")); + } + + let vm = vm.ok_or_else(|| syn::Error::new(Span::call_site(), "VM for object not specified"))?; + + Ok(GCMetadata { + vm, + alignment: alignment.unwrap_or(ObjectAlignment::Auto), + size: size.unwrap_or(ObjectSize::Auto), + trace: trace.unwrap_or(ObjectTrace::Auto(false)), + }) +} +/* +#[proc_macro_derive(GCMetadata, attributes(arraylike, gcmetadata))] +pub fn derive_gcmetadata(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + let name = input.ident; + + let arraylike = match find_arraylike(&input.attrs) { + Ok(arraylike) => arraylike, + Err(err) => return err.to_compile_error().into(), + }; + + let gc_meta = match find_gcmetadata(&input.attrs) { + Ok(gc_meta) => gc_meta, + Err(err) => return err.to_compile_error().into(), + }; + + + let mut tracer = match gc_meta.trace { + ObjectTrace::NoTrace => quote! { TraceCallback::None }, + ObjectTrace::Auto(scan_slots) => { + let call = |typ: syn::Type, field: syn::Expr| { + if scan_slots { + quote! { + <#typ as vmkit::mm::traits::Scan<_>>::scan_object(&#field, visitor); + } + } else { + quote! { + <#typ as vmkit::mm::traits::Trace>::trace_object(&mut #field, visitor) + } + } + }; + + + let mut body = TokenStream::new(); + match input.data { + Data::Enum(enumeration) => { + let mut match_: syn::ExprMatch = syn::parse_quote!( + match object.as_address().as_mut_ref::<#name>() {} + ); + + for variant in enumeration.variants { + let ident = variant.ident.clone(); + let path = quote! { #name::#ident } + } + } + + Data::Struct(structure) => { + + } + } + } + } + + TokenStream::new() +} +*/ + +decl_derive!([GCMetadata,attributes(gcmetadata, arraylike, ignore_trace)] => derive_gcmetadata); + +fn derive_gcmetadata(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { + let gc_metadata = match find_gcmetadata(&s.ast().attrs) { + Ok(gc) => gc, + Err(err) => return err.to_compile_error(), + }; + let arraylike = match find_arraylike(&s.ast().attrs) { + Ok(arraylike) => arraylike, + Err(err) => return err.to_compile_error(), + }; + + let vm = &gc_metadata.vm; + + let name = s.ast().ident.clone(); + + let mut fields_to_skip = HashSet::new(); + if let Some(arraylike) = arraylike.as_ref() { + if let Some(field) = &arraylike.data_field { + fields_to_skip.insert(field.clone()); + } + if let Some(field) = &arraylike.length_field { + fields_to_skip.insert(field.clone()); + } + } + + let (trace_impl, trace_callback) = match gc_metadata.trace { + ObjectTrace::NoTrace => (None, quote! { ::vmkit::prelude::TraceCallback::None }), + ObjectTrace::Auto(scan_slots) => { + let mut filtered = s.clone(); + filtered.filter(|bi| { + !bi.ast() + .attrs + .iter() + .any(|attr| attr.path().is_ident("ignore_trace")) + && (bi.ast().ident.is_none() + || !fields_to_skip.contains(bi.ast().ident.as_ref().unwrap())) + }); + + filtered.add_bounds(AddBounds::Fields); + + let trace_impl = if scan_slots { + let trace_body = filtered.each(|bi| { + + quote! { + mark(#bi, visitor); + } + }); + + let full_path = quote! { <#vm as vmkit::VirtualMachine>::Slot }; + + let mut extra = quote! { + fn mark>(t: &T, visitor: &mut dyn ::vmkit::prelude::SlotVisitor<#full_path>) { + ::vmkit::prelude::Scan::<#full_path>::scan_object(t, visitor); + } + }; + if let Some(arraylike) = arraylike.as_ref().filter(|x| x.trace) { + let element = &arraylike.element_type; + let length = if let Some(getter) = &arraylike.length_getter { + quote! { #getter } + } else if let Some(field) = &arraylike.length_field { + quote! { self.#field as usize } + } else { + panic!("length field not found"); + }; + + let data_ptr = if let Some(getter) = &arraylike.data_getter { + quote! { #getter } + } else if let Some(field) = &arraylike.data_field { + quote! { self.#field.as_mut_ptr().cast::<#element>() } + } else { + panic!("data field not found"); + }; + + extra.extend(quote! { + let length = #length; + let data = #data_ptr; + + for i in 0..length { + unsafe { + let ptr = data.add(i).as_ref().unwrap(); + ::vmkit::prelude::Scan::<#full_path>::scan_object(ptr, visitor); + } + } + }); + } + + filtered.bound_impl( + quote! {::vmkit::prelude::Scan<#full_path> }, + quote! { + #[inline] + fn scan_object(&self, visitor: &mut dyn ::vmkit::prelude::SlotVisitor<#full_path>) { + let this = self; + #extra + match *self { #trace_body } + } + } + ) + } else { + filtered.bind_with(|_| synstructure::BindStyle::RefMut); + let trace_body = filtered.each(|bi| { + quote! { + mark(#bi, visitor); + } + }); + let mut extra = quote! { + #[inline] + fn mark(t: &mut T, visitor: &mut dyn ::vmkit::prelude::ObjectTracer) { + ::vmkit::prelude::Trace::trace_object(t, visitor); + } + }; + if let Some(arraylike) = arraylike.as_ref().filter(|x| x.trace) { + let element = &arraylike.element_type; + let length = if let Some(getter) = &arraylike.length_getter { + quote! { #getter } + } else if let Some(field) = &arraylike.length_field { + quote! { self.#field as usize } + } else { + panic!("length field not found"); + }; + + let data_ptr = if let Some(getter) = &arraylike.data_getter { + quote! { #getter } + } else if let Some(field) = &arraylike.data_field { + quote! { self.#field.as_mut_ptr().cast::<#element>() } + } else { + panic!("data field not found"); + }; + + extra.extend(quote! { + let length = #length; + let data = #data_ptr; + + for i in 0..length { + unsafe { + let ptr = data.add(i).as_mut().unwrap(); + ::vmkit::prelude::Trace::trace_object(ptr, visitor); + } + } + }); + } + + filtered.bound_impl( + quote! {::vmkit::prelude::Trace }, + quote! { + #[inline] + fn trace_object(&mut self, visitor: &mut dyn ::vmkit::prelude::ObjectTracer) { + match *self { #trace_body }; + let this = self; + #extra + } + } + ) + }; + + (Some(trace_impl), if scan_slots { + let full_path = quote! { <#vm as vmkit::VirtualMachine>::Slot }; + quote! { ::vmkit::prelude::TraceCallback::ScanSlots(|object, visitor| unsafe { + let object = object.as_address().as_ref::<#name>(); + ::vmkit::prelude::Scan::<#full_path>::scan_object(object, visitor); + }) } + } else { + quote! { ::vmkit::prelude::TraceCallback::TraceObject(|object, visitor| unsafe { + let object = object.as_address().as_mut_ref::<#name>(); + ::vmkit::prelude::Trace::trace_object(object, visitor); + }) } + }) + } + + ObjectTrace::Trace(scan_slots, expr) => { + if scan_slots { + (None, quote! { + ::vmkit::prelude::TraceCallback::ScanSlots(|object, visitor| unsafe { + let this = object.as_address().as_ref::<#name>(); + #expr + }) + }) + } else { + (None, quote! { + ::vmkit::prelude::TraceCallback::TraceObject(|object, visitor| unsafe { + let this = object.as_address().as_mut_ref::<#name>(); + #expr + }) + }) + } + } + , + }; + + let instance_size = match &gc_metadata.size { + ObjectSize::Auto if arraylike.is_some() => quote! { 0 }, + ObjectSize::Size(_) if arraylike.is_some() => quote! { 0 }, + ObjectSize::Compute(_) => quote! { 0 }, + ObjectSize::Auto => quote! { ::std::mem::size_of::<#name>() }, + ObjectSize::Size(size) => quote! { #size }, + }; + + let alignment = match &gc_metadata.alignment { + ObjectAlignment::Auto => quote! { ::std::mem::align_of::<#name>() }, + ObjectAlignment::Const(expr) => quote! { #expr }, + ObjectAlignment::Compute(_) => quote! { 0 }, + }; + + let compute_size = if let Some(arraylike) = arraylike.as_ref() { + match gc_metadata.size { + ObjectSize::Compute(expr) => quote! { Some(|object| unsafe { + let this = object.as_address().as_ref::<#name>(); + #expr + }) }, + _ => { + let length = if let Some(getter) = &arraylike.length_getter { + quote! { #getter } + } else if let Some(field) = &arraylike.length_field { + quote! { this.#field as usize } + } else { + panic!("length field not found"); + }; + + + let element = &arraylike.element_type; + quote! { Some(|object| unsafe { + let this = object.as_address().as_ref::<#name>(); + let length = #length; + + + size_of::<#name>() + (length * ::std::mem::size_of::<#element>()) + }) } + } + } + } else { + match gc_metadata.size { + ObjectSize::Compute(expr) => quote! { Some(|object| unsafe { + let this = object.as_address().as_ref::<#name>(); + #expr + }) }, + _ => quote! { None }, + } + }; + + let mut output = quote! { + impl #name { + pub fn gc_metadata() -> &'static ::vmkit::prelude::GCMetadata<#vm> { + static METADATA: ::vmkit::prelude::GCMetadata<#vm> = ::vmkit::prelude::GCMetadata { + trace: #trace_callback, + instance_size: #instance_size, + compute_size: #compute_size, + alignment: #alignment, + compute_alignment: None, + }; + &METADATA + } + } + }; + + if let Some(trace_impl) = trace_impl { + output.extend(trace_impl); + } + + output.into() } diff --git a/vmkit-proc/src/main.rs b/vmkit-proc/src/main.rs deleted file mode 100644 index e7a11a9..0000000 --- a/vmkit-proc/src/main.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - println!("Hello, world!"); -} diff --git a/vmkit/Cargo.toml b/vmkit/Cargo.toml index 24c80c5..ee201d8 100644 --- a/vmkit/Cargo.toml +++ b/vmkit/Cargo.toml @@ -17,14 +17,15 @@ mmtk = { git = "https://github.com/mmtk/mmtk-core" } parking_lot = "0.12.3" rand = "0.9.0" sysinfo = "0.33.1" - +vmkit-proc = { path = "../vmkit-proc", optional = true } [features] -default = ["cooperative", "address_based_hashing", "object_pinning"] +default = ["cooperative", "address_based_hashing", "object_pinning", "derive"] vmside_forwarding = [] +derive = ["vmkit-proc"] object_pinning = ["mmtk/object_pinning"] address_based_hashing = [] uncooperative = ["cooperative", "mmtk/immix_non_moving", "mmtk/immix_zero_on_release"] diff --git a/vmkit/src/lib.rs b/vmkit/src/lib.rs index 8ab3633..07a9756 100644 --- a/vmkit/src/lib.rs +++ b/vmkit/src/lib.rs @@ -216,3 +216,15 @@ impl VMKit { &self.thread_manager } } + +#[cfg(feature="derive")] +pub use vmkit_proc::GCMetadata; + +pub mod prelude { + pub use super::GCMetadata; + pub use super::mm::traits::*; + pub use super::object_model::object::*; + pub use super::object_model::metadata::*; + pub use mmtk::vm::ObjectTracer; + pub use mmtk::vm::SlotVisitor; +} \ No newline at end of file diff --git a/vmkit/src/main.rs b/vmkit/src/main.rs index 35f4ae6..b86d3d8 100644 --- a/vmkit/src/main.rs +++ b/vmkit/src/main.rs @@ -1,9 +1,276 @@ -use easy_bitfield::BitFieldTrait; -use vmkit::object_model::header::{HashState, HashStateField}; +use mmtk::util::Address; +use mmtk::vm::slot::UnimplementedMemorySlice; +use mmtk::{util::options::PlanSelector, vm::slot::SimpleSlot, AllocationSemantics, MMTKBuilder}; +use vmkit::GCMetadata; +use std::cell::RefCell; +use std::mem::offset_of; +use std::sync::Arc; +use std::sync::OnceLock; +use vmkit::threading::parked_scope; +use vmkit::{ + mm::{traits::Trace, MemoryManager}, + object_model::{ + metadata::{GCMetadata, TraceCallback}, + object::VMKitObject, + }, + sync::Monitor, + threading::{GCBlockAdapter, Thread, ThreadContext}, + VMKit, VirtualMachine, +}; + +#[repr(C)] +struct Node { + left: NodeRef, + right: NodeRef, +} + +static METADATA: GCMetadata = GCMetadata { + trace: TraceCallback::TraceObject(|object, tracer| unsafe { + let node = object.as_address().as_mut_ref::(); + node.left.0.trace_object(tracer); + node.right.0.trace_object(tracer); + }), + instance_size: size_of::(), + compute_size: None, + alignment: 16, + compute_alignment: None, +}; + +struct BenchVM { + vmkit: VMKit, +} + +static VM: OnceLock = OnceLock::new(); + +struct ThreadBenchContext; + +impl ThreadContext for ThreadBenchContext { + fn new(_: bool) -> Self { + Self + } + fn save_thread_state(&self) {} + + fn scan_roots( + &self, + _factory: impl mmtk::vm::RootsWorkFactory<::Slot>, + ) { + } + + fn scan_conservative_roots( + &self, + _croots: &mut vmkit::mm::conservative_roots::ConservativeRoots, + ) { + } +} + +impl VirtualMachine for BenchVM { + type BlockAdapterList = (GCBlockAdapter, ()); + type Metadata = &'static GCMetadata; + type Slot = SimpleSlot; + type ThreadContext = ThreadBenchContext; + type MemorySlice = UnimplementedMemorySlice; + + #[cfg(feature="vmside_forwarding")] + const LOCAL_FORWARDING_POINTER_SPEC: mmtk::vm::VMLocalForwardingPointerSpec = mmtk::vm::VMLocalForwardingPointerSpec::in_header(0); + #[cfg(feature="vmside_forwarding")] + const LOCAL_FORWARDING_BITS_SPEC: mmtk::vm::VMLocalForwardingBitsSpec = mmtk::vm::VMLocalForwardingBitsSpec::in_header(62); + + fn get() -> &'static Self { + VM.get().unwrap() + } + + fn vmkit(&self) -> &VMKit { + &self.vmkit + } + + fn prepare_for_roots_re_scanning() {} + + fn notify_initial_thread_scan_complete(partial_scan: bool, tls: mmtk::util::VMWorkerThread) { + let _ = partial_scan; + let _ = tls; + } + + fn forward_weak_refs( + _worker: &mut mmtk::scheduler::GCWorker>, + _tracer_context: impl mmtk::vm::ObjectTracerContext>, + ) { + } + + fn scan_roots_in_mutator_thread( + _tls: mmtk::util::VMWorkerThread, + _mutator: &'static mut mmtk::Mutator>, + _factory: impl mmtk::vm::RootsWorkFactory< + as mmtk::vm::VMBinding>::VMSlot, + >, + ) { + } + + fn scan_vm_specific_roots( + _tls: mmtk::util::VMWorkerThread, + _factory: impl mmtk::vm::RootsWorkFactory< + as mmtk::vm::VMBinding>::VMSlot, + >, + ) { + } +} + +#[repr(transparent)] +#[derive(Clone, Copy, PartialEq, Eq)] +struct NodeRef(VMKitObject); + +impl NodeRef { + pub fn new(thread: &Thread, left: NodeRef, right: NodeRef) -> Self { + let node = MemoryManager::::allocate( + thread, + size_of::(), + 32, + &METADATA, + AllocationSemantics::Default, + ); + //node.hashcode::(); + node.set_field_object::(offset_of!(Node, left), left.0); + node.set_field_object::(offset_of!(Node, right), right.0); + + Self(node) + } + + pub fn left(self) -> NodeRef { + unsafe { + let node = self.0.as_address().as_ref::(); + node.left + } + } + + pub fn right(self) -> NodeRef { + unsafe { + let node = self.0.as_address().as_ref::(); + node.right + } + } + + pub fn null() -> Self { + Self(VMKitObject::NULL) + } + + pub fn item_check(&self) -> usize { + if self.left() == NodeRef::null() { + 1 + } else { + 1 + self.left().item_check() + self.right().item_check() + } + } + + pub fn leaf(thread: &Thread) -> Self { + Self::new(thread, NodeRef::null(), NodeRef::null()) + } +} + +fn bottom_up_tree(thread: &Thread, depth: usize) -> NodeRef { + if thread.take_yieldpoint() != 0 { + Thread::::yieldpoint(0, Address::ZERO); + } + if depth > 0 { + NodeRef::new( + thread, + bottom_up_tree(thread, depth - 1), + bottom_up_tree(thread, depth - 1), + ) + } else { + NodeRef::leaf(thread) + } +} + +const MIN_DEPTH: usize = 4; + +#[derive(GCMetadata)] +#[gcmetadata( + vm = BenchVM, + scan +)] +enum Object { + + Fixnum(#[ignore_trace] i32), + Boxed(VMKitObject) +} + + fn main() { - let encoded = HashStateField::encode(HashState::HashedAndMoved); - println!("{:x}", encoded); - println!("{}", HashStateField::NEXT_BIT); - println!("{:?}", HashStateField::decode(encoded)); + env_logger::init(); + let nthreads = std::env::var("THREADS") + .unwrap_or("4".to_string()) + .parse::() + .unwrap(); + let mut builder = MMTKBuilder::new(); + builder.options.plan.set(PlanSelector::StickyImmix); + builder.options.threads.set(nthreads); + VM.set(BenchVM { + vmkit: VMKit::new(&mut builder), + }) + .unwrap_or_else(|_| panic!()); + + Thread::::main(ThreadBenchContext, || { + let thread = Thread::::current(); + let start = std::time::Instant::now(); + let n = std::env::var("DEPTH") + .unwrap_or("18".to_string()) + .parse::() + .unwrap(); + let max_depth = if n < MIN_DEPTH + 2 { MIN_DEPTH + 2 } else { n }; + + let stretch_depth = max_depth + 1; + + println!("stretch tree of depth {stretch_depth}"); + + let _ = bottom_up_tree(&thread, stretch_depth); + let duration = start.elapsed(); + println!("time: {duration:?}"); + + let results = Arc::new(Monitor::new(vec![ + RefCell::new(String::new()); + (max_depth - MIN_DEPTH) / 2 + 1 + ])); + + let mut handles = Vec::new(); + + for d in (MIN_DEPTH..=max_depth).step_by(2) { + let depth = d; + + let thread = Thread::::for_mutator(ThreadBenchContext); + let results = results.clone(); + let handle = thread.start(move || { + let thread = Thread::::current(); + let mut check = 0; + + let iterations = 1 << (max_depth - depth + MIN_DEPTH); + for _ in 1..=iterations { + let tree_node = bottom_up_tree(&thread, depth); + check += tree_node.item_check(); + } + + *results.lock_with_handshake::()[(depth - MIN_DEPTH) / 2].borrow_mut() = + format!("{iterations}\t trees of depth {depth}\t check: {check}"); + }); + handles.push(handle); + } + println!("created {} threads", handles.len()); + + parked_scope::<(), BenchVM>(|| { + while let Some(handle) = handles.pop() { + handle.join().unwrap(); + } + }); + + for result in results.lock_with_handshake::().iter() { + println!("{}", result.borrow()); + } + + println!( + "long lived tree of depth {max_depth}\t check: {}", + bottom_up_tree(&thread, max_depth).item_check() + ); + + let duration = start.elapsed(); + println!("time: {duration:?}"); + }); } diff --git a/vmkit/src/object_model/object.rs b/vmkit/src/object_model/object.rs index 8efdd65..4874975 100644 --- a/vmkit/src/object_model/object.rs +++ b/vmkit/src/object_model/object.rs @@ -3,6 +3,8 @@ use crate::threading::Thread; use crate::{mm::MemoryManager, VirtualMachine}; use atomic::Atomic; use core::ops::Range; +use std::mem::MaybeUninit; +use std::ptr::NonNull; use mmtk::util::{ constants::LOG_BYTES_IN_ADDRESS, conversions::raw_align_up, Address, ObjectReference, }; @@ -738,3 +740,103 @@ impl MemorySlice for SimpleMemorySlice { } } } + +pub struct GcPtr { + ptr: NonNull, +} + +impl GcPtr { + pub fn new(ptr: NonNull) -> Self { + Self { ptr } + } + + pub fn as_ptr(&self) -> *mut T { + self.ptr.as_ptr() + } + + pub fn as_ref(&self) -> &T { + unsafe { self.ptr.as_ref() } + } + + pub fn as_mut(&mut self) -> &mut T { + unsafe { self.ptr.as_mut() } + } + + pub fn as_address(&self) -> Address { + Address::from_mut_ptr(self.ptr.as_ptr()) + } + + pub fn from_address(address: Address) -> Self { + assert!(!address.is_zero()); + Self { + ptr: NonNull::new(address.to_mut_ptr()).unwrap(), + } + } + + pub fn from_ptr(ptr: *mut T) -> Self { + assert!(!ptr.is_null()); + Self { + ptr: NonNull::new(ptr).unwrap(), + } + } +} + +impl GcPtr> { + pub unsafe fn assume_init(self) -> GcPtr { + GcPtr::new(self.ptr.cast()) + } +} + +impl std::ops::Deref for GcPtr { + type Target = T; + + fn deref(&self) -> &Self::Target { + self.as_ref() + } +} + +impl std::ops::DerefMut for GcPtr { + fn deref_mut(&mut self) -> &mut Self::Target { + self.as_mut() + } +} + +impl fmt::Debug for GcPtr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", **self) + } +} + +impl Hash for GcPtr { + fn hash(&self, state: &mut H) { + (**self).hash(state); + } +} + +impl PartialEq for GcPtr { + fn eq(&self, other: &Self) -> bool { + **self == **other + } +} + +impl Eq for GcPtr {} + +impl PartialOrd for GcPtr { + fn partial_cmp(&self, other: &Self) -> Option { + (**self).partial_cmp(&**other) + } +} + +impl Ord for GcPtr { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + (**self).cmp(&**other) + } +} + +impl Clone for GcPtr { + fn clone(&self) -> Self { + Self::new(self.ptr) + } +} + +impl Copy for GcPtr {}