proc-macro
This commit is contained in:
parent
32662e9fc8
commit
6d565ad836
9 changed files with 982 additions and 16 deletions
0
.editorconfig
Normal file
0
.editorconfig
Normal file
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -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]]
|
||||
|
|
|
@ -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
|
|
@ -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<syn::Ident>,
|
||||
/// Getter for length field.
|
||||
pub length_getter: Option<syn::Expr>,
|
||||
/// Setter for length field.
|
||||
pub length_setter: Option<syn::Expr>,
|
||||
pub element_type: syn::Type,
|
||||
pub data_field: Option<syn::Ident>,
|
||||
pub data_getter: Option<syn::Expr>,
|
||||
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<Option<ArrayLike>> {
|
||||
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::<syn::LitBool>()?;
|
||||
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::<syn::Token![=]>()?;
|
||||
let field = meta.input.parse::<syn::Ident>()?;
|
||||
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::<syn::Expr>()?;
|
||||
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::<syn::Expr>()?;
|
||||
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::<syn::Expr>()?;
|
||||
data_getter = Some(field);
|
||||
} else {
|
||||
return Err(meta.error("unknown data field"));
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
} else {
|
||||
let field = meta.input.parse::<syn::Ident>()?;
|
||||
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::<syn::Token![=]>()?;
|
||||
let field = meta.input.parse::<syn::Type>()?;
|
||||
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<GCMetadata> {
|
||||
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::<syn::Token![=]>()?;
|
||||
let field = meta.input.parse::<syn::Type>()?;
|
||||
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::<syn::Expr>()?;
|
||||
alignment = Some(ObjectAlignment::Const(constant));
|
||||
} else {
|
||||
let _ = meta.input.parse::<syn::Token![=]>()?;
|
||||
let field = meta.input.parse::<syn::Expr>()?;
|
||||
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::<syn::Expr>()?;
|
||||
size = Some(ObjectSize::Size(constant));
|
||||
} else {
|
||||
let _ = meta.input.parse::<syn::Token![=]>()?;
|
||||
let field = meta.input.parse::<syn::Expr>()?;
|
||||
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::<syn::Token![=]>()?;
|
||||
let field = meta.input.parse::<syn::Expr>()?;
|
||||
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::<syn::Token![=]>()?;
|
||||
let field = meta.input.parse::<syn::Expr>()?;
|
||||
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: ::vmkit::prelude::Scan<#full_path>>(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: ::vmkit::prelude::Trace>(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()
|
||||
}
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
fn main() {
|
||||
println!("Hello, world!");
|
||||
}
|
|
@ -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"]
|
||||
|
|
|
@ -216,3 +216,15 @@ impl<VM: VirtualMachine> VMKit<VM> {
|
|||
&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;
|
||||
}
|
|
@ -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<BenchVM> = GCMetadata {
|
||||
trace: TraceCallback::TraceObject(|object, tracer| unsafe {
|
||||
let node = object.as_address().as_mut_ref::<Node>();
|
||||
node.left.0.trace_object(tracer);
|
||||
node.right.0.trace_object(tracer);
|
||||
}),
|
||||
instance_size: size_of::<Node>(),
|
||||
compute_size: None,
|
||||
alignment: 16,
|
||||
compute_alignment: None,
|
||||
};
|
||||
|
||||
struct BenchVM {
|
||||
vmkit: VMKit<Self>,
|
||||
}
|
||||
|
||||
static VM: OnceLock<BenchVM> = OnceLock::new();
|
||||
|
||||
struct ThreadBenchContext;
|
||||
|
||||
impl ThreadContext<BenchVM> for ThreadBenchContext {
|
||||
fn new(_: bool) -> Self {
|
||||
Self
|
||||
}
|
||||
fn save_thread_state(&self) {}
|
||||
|
||||
fn scan_roots(
|
||||
&self,
|
||||
_factory: impl mmtk::vm::RootsWorkFactory<<BenchVM as VirtualMachine>::Slot>,
|
||||
) {
|
||||
}
|
||||
|
||||
fn scan_conservative_roots(
|
||||
&self,
|
||||
_croots: &mut vmkit::mm::conservative_roots::ConservativeRoots,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
impl VirtualMachine for BenchVM {
|
||||
type BlockAdapterList = (GCBlockAdapter, ());
|
||||
type Metadata = &'static GCMetadata<Self>;
|
||||
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> {
|
||||
&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<vmkit::mm::MemoryManager<Self>>,
|
||||
_tracer_context: impl mmtk::vm::ObjectTracerContext<vmkit::mm::MemoryManager<Self>>,
|
||||
) {
|
||||
}
|
||||
|
||||
fn scan_roots_in_mutator_thread(
|
||||
_tls: mmtk::util::VMWorkerThread,
|
||||
_mutator: &'static mut mmtk::Mutator<vmkit::mm::MemoryManager<Self>>,
|
||||
_factory: impl mmtk::vm::RootsWorkFactory<
|
||||
<vmkit::mm::MemoryManager<Self> as mmtk::vm::VMBinding>::VMSlot,
|
||||
>,
|
||||
) {
|
||||
}
|
||||
|
||||
fn scan_vm_specific_roots(
|
||||
_tls: mmtk::util::VMWorkerThread,
|
||||
_factory: impl mmtk::vm::RootsWorkFactory<
|
||||
<vmkit::mm::MemoryManager<Self> as mmtk::vm::VMBinding>::VMSlot,
|
||||
>,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
struct NodeRef(VMKitObject);
|
||||
|
||||
impl NodeRef {
|
||||
pub fn new(thread: &Thread<BenchVM>, left: NodeRef, right: NodeRef) -> Self {
|
||||
let node = MemoryManager::<BenchVM>::allocate(
|
||||
thread,
|
||||
size_of::<Node>(),
|
||||
32,
|
||||
&METADATA,
|
||||
AllocationSemantics::Default,
|
||||
);
|
||||
//node.hashcode::<BenchVM>();
|
||||
node.set_field_object::<BenchVM, false>(offset_of!(Node, left), left.0);
|
||||
node.set_field_object::<BenchVM, false>(offset_of!(Node, right), right.0);
|
||||
|
||||
Self(node)
|
||||
}
|
||||
|
||||
pub fn left(self) -> NodeRef {
|
||||
unsafe {
|
||||
let node = self.0.as_address().as_ref::<Node>();
|
||||
node.left
|
||||
}
|
||||
}
|
||||
|
||||
pub fn right(self) -> NodeRef {
|
||||
unsafe {
|
||||
let node = self.0.as_address().as_ref::<Node>();
|
||||
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<BenchVM>) -> Self {
|
||||
Self::new(thread, NodeRef::null(), NodeRef::null())
|
||||
}
|
||||
}
|
||||
|
||||
fn bottom_up_tree(thread: &Thread<BenchVM>, depth: usize) -> NodeRef {
|
||||
if thread.take_yieldpoint() != 0 {
|
||||
Thread::<BenchVM>::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::<usize>()
|
||||
.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::<BenchVM>::main(ThreadBenchContext, || {
|
||||
let thread = Thread::<BenchVM>::current();
|
||||
let start = std::time::Instant::now();
|
||||
let n = std::env::var("DEPTH")
|
||||
.unwrap_or("18".to_string())
|
||||
.parse::<usize>()
|
||||
.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::<BenchVM>::for_mutator(ThreadBenchContext);
|
||||
let results = results.clone();
|
||||
let handle = thread.start(move || {
|
||||
let thread = Thread::<BenchVM>::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::<BenchVM>()[(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::<BenchVM>().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:?}");
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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<SL: SlotExtra> MemorySlice for SimpleMemorySlice<SL> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GcPtr<T> {
|
||||
ptr: NonNull<T>,
|
||||
}
|
||||
|
||||
impl<T> GcPtr<T> {
|
||||
pub fn new(ptr: NonNull<T>) -> 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<T> GcPtr<MaybeUninit<T>> {
|
||||
pub unsafe fn assume_init(self) -> GcPtr<T> {
|
||||
GcPtr::new(self.ptr.cast())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> std::ops::Deref for GcPtr<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> std::ops::DerefMut for GcPtr<T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug> fmt::Debug for GcPtr<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{:?}", **self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Hash> Hash for GcPtr<T> {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
(**self).hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PartialEq> PartialEq for GcPtr<T> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
**self == **other
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Eq> Eq for GcPtr<T> {}
|
||||
|
||||
impl<T: PartialOrd> PartialOrd for GcPtr<T> {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
(**self).partial_cmp(&**other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Ord> Ord for GcPtr<T> {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
(**self).cmp(&**other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for GcPtr<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self::new(self.ptr)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Copy for GcPtr<T> {}
|
||||
|
|
Loading…
Add table
Reference in a new issue