Skip to content

Commit

Permalink
Rust side field scanning (#18)
Browse files Browse the repository at this point in the history
* Rust implementation of field scanning, except `InstanceRefKlass` objects

* Move structs to a separate module

* Refactor

* Minor performance fix

* Support java.lang.ref.Reference scanning

* Move `validate_memory_layouts` to `abi` module
  • Loading branch information
wenyuzhao authored Sep 2, 2020
1 parent 7071092 commit 038d0b4
Show file tree
Hide file tree
Showing 8 changed files with 535 additions and 7 deletions.
320 changes: 320 additions & 0 deletions mmtk/src/abi.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,320 @@
use mmtk::util::{OpaquePointer, Address};
use mmtk::util::constants::*;
use mmtk::util::conversions;
use std::{mem, slice};
use std::marker::PhantomData;
use super::UPCALLS;
use std::fmt;
use std::ffi::CStr;

trait EqualTo<T> {
const VALUE: bool;
}

impl <T, U> EqualTo<U> for T {
default const VALUE: bool = false;
}

impl <T> EqualTo<T> for T {
const VALUE: bool = true;
}

pub const fn type_equal<T, U>() -> bool {
<T as EqualTo<U>>::VALUE
}

#[repr(i32)]
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[allow(dead_code)]
pub enum KlassID {
Instance,
InstanceRef,
InstanceMirror,
InstanceClassLoader,
TypeArray,
ObjArray,
}

#[repr(C)]
pub struct Klass {
vptr: OpaquePointer,
#[cfg(debug_assertions)]
valid: i32,
pub layout_helper: i32,
pub id: KlassID,
pub super_check_offset: u32,
pub name: OpaquePointer, // Symbol*
pub secondary_super_cache: &'static Klass,
pub secondary_supers: OpaquePointer, // Array<Klass*>*
pub primary_supers: [&'static Klass; 8],
pub java_mirror: &'static Oop, // OopHandle
pub super_: &'static Klass,
pub subklass: &'static Klass,
pub next_sibling: &'static Klass,
pub next_link: &'static Klass,
pub class_loader_data: OpaquePointer, // ClassLoaderData*
pub modifier_flags: i32,
pub access_flags: i32, // AccessFlags
pub trace_id: u64, // JFR_ONLY(traceid _trace_id;)
pub last_biased_lock_bulk_revocation_time: i64,
pub prototype_header: Oop, // markOop,
pub biased_lock_revocation_count: i32,
pub vtable_len: i32,
pub shared_class_path_index: i16,
}

impl Klass {
pub unsafe fn cast<'a, T>(&self) -> &'a T {
&*(self as *const _ as usize as *const T)
}
}

#[repr(C)]
pub struct InstanceKlass {
pub klass: Klass,
pub annotations: OpaquePointer, // Annotations*
pub package_entry: OpaquePointer, // PackageEntry*
pub array_klasses: &'static Klass,
pub constants: OpaquePointer, // ConstantPool*
pub inner_classes: OpaquePointer, // Array<jushort>*
pub nest_members: OpaquePointer, // Array<jushort>*
pub nest_host_index: u16,
pub nest_host: &'static InstanceKlass,
pub source_debug_extension: OpaquePointer, // const char*
pub array_name: OpaquePointer, // Symbol*
pub nonstatic_field_size: i32,
pub static_field_size: i32,
pub generic_signature_index: u16,
pub source_file_name_index: u16,
pub static_oop_field_count: u16,
pub java_fields_count: u16,
pub nonstatic_oop_map_size: i32,
pub itable_len: i32,
pub is_marked_dependent: bool, // bool
pub is_being_redefined: bool, // bool
pub misc_flags: u16,
pub minor_version: u16,
pub major_version: u16,
pub init_thread: OpaquePointer, // Thread*
pub oop_map_cache: OpaquePointer, // OopMapCache*
pub jni_ids: OpaquePointer, // JNIid*
pub methods_jmethod_ids: OpaquePointer, // jmethodID*
pub dep_context: usize, // intptr_t
pub osr_nmethods_head: OpaquePointer, // nmethod*
// #if INCLUDE_JVMTI
pub breakpoints: OpaquePointer, // BreakpointInfo*
pub previous_versions: OpaquePointer, // InstanceKlass*
pub cached_class_file: OpaquePointer, // JvmtiCachedClassFileData*
// #endif
pub idnum_allocated_count: u16,
pub init_state: u8,
pub reference_type: u8,
pub this_class_index: u16,
// #if INCLUDE_JVMTI
pub jvmti_cached_class_field_map: OpaquePointer, // JvmtiCachedClassFieldMap*
// #endif
#[cfg(debug_assertions)]
verify_count: i32,
pub methods: OpaquePointer, // Array<Method*>*
pub default_methods: OpaquePointer, // Array<Method*>*
pub local_interfaces: OpaquePointer, // Array<Klass*>*
pub transitive_interfaces: OpaquePointer, // Array<Klass*>*
pub method_ordering: OpaquePointer, // Array<int>*
pub default_vtable_indices: OpaquePointer, // Array<int>*
pub fields: OpaquePointer, // Array<u2>*
}

impl InstanceKlass {
const HEADER_SIZE: usize = mem::size_of::<Self>() / BYTES_IN_WORD;
const VTABLE_START_OFFSET: usize = Self::HEADER_SIZE * BYTES_IN_WORD;

fn start_of_vtable(&self) -> *const usize {
unsafe { (self as *const _ as *const u8).add(Self::VTABLE_START_OFFSET) as _ }
}

fn start_of_itable(&self) -> *const usize {
unsafe { self.start_of_vtable().add(self.klass.vtable_len as _) }
}

fn nonstatic_oop_map_count(&self) -> usize {
let oop_map_block_size = mem::size_of::<OopMapBlock>();
let oop_map_block_size_up = mmtk::util::conversions::raw_align_up(oop_map_block_size, BYTES_IN_WORD);
self.nonstatic_oop_map_size as usize / (oop_map_block_size_up >> LOG_BYTES_IN_WORD)
}

pub fn nonstatic_oop_maps(&self) -> &'static [OopMapBlock] {
let start_of_itable = self.start_of_itable();
let start = unsafe { start_of_itable.add(self.itable_len as _) as *const OopMapBlock };
let count = self.nonstatic_oop_map_count();
unsafe { slice::from_raw_parts(start, count) }
}
}

#[repr(C)]
pub struct InstanceMirrorKlass {
pub instance_klass: InstanceKlass,
}

impl InstanceMirrorKlass {
fn offset_of_static_fields() -> usize {
lazy_static! {
pub static ref OFFSET_OF_STATIC_FIELDS: usize = unsafe {
((*UPCALLS).offset_of_static_fields)() as usize
};
}
*OFFSET_OF_STATIC_FIELDS
}
fn static_oop_field_count_offset() -> i32 {
lazy_static! {
pub static ref STATIC_OOP_FIELD_COUNT_OFFSET: i32 = unsafe {
((*UPCALLS).static_oop_field_count_offset)()
};
}
*STATIC_OOP_FIELD_COUNT_OFFSET
}
pub fn start_of_static_fields(oop: Oop) -> Address {
Address::from_ref(oop) + Self::offset_of_static_fields()
}
pub fn static_oop_field_count(oop: Oop) -> usize {
let offset = Self::static_oop_field_count_offset();
unsafe { oop.get_field_address(offset).load::<i32>() as _ }
}
}

#[repr(C)]
pub struct ArrayKlass {
pub klass: Klass,
pub dimension: i32,
pub higher_dimension: &'static Klass,
pub lower_dimension: &'static Klass,
}

#[repr(C)]
pub struct ObjArrayKlass {
pub array_klass: ArrayKlass,
pub element_klass: &'static Klass,
pub bottom_klass: &'static Klass,
}

#[repr(C)]
pub struct TypeArrayKlass {
pub array_klass: ArrayKlass,
pub max_length: i32,
}

#[repr(C)]
pub struct InstanceClassLoaderKlass {
pub instance_klass: InstanceKlass,
}

#[repr(C)]
pub struct InstanceRefKlass {
pub instance_klass: InstanceKlass,
}

impl InstanceRefKlass {
fn referent_offset() -> i32 {
lazy_static! {
pub static ref REFERENT_OFFSET: i32 = unsafe {
((*UPCALLS).referent_offset)()
};
}
*REFERENT_OFFSET
}
fn discovered_offset() -> i32 {
lazy_static! {
pub static ref DISCOVERED_OFFSET: i32 = unsafe {
((*UPCALLS).discovered_offset)()
};
}
*DISCOVERED_OFFSET
}
pub fn referent_address(oop: Oop) -> Address {
oop.get_field_address(Self::referent_offset())
}
pub fn discovered_address(oop: Oop) -> Address {
oop.get_field_address(Self::discovered_offset())
}
}

#[repr(C)]
pub struct OopDesc {
pub mark: usize,
pub klass: &'static Klass,
}

impl fmt::Debug for OopDesc {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let c_string = unsafe {
((*UPCALLS).dump_object_string)(mem::transmute(self))
};
let c_str: &CStr = unsafe { CStr::from_ptr(c_string) };
let s: &str = c_str.to_str().unwrap();
write!(f, "{}", s)
}
}

pub type Oop = &'static OopDesc;

impl OopDesc {
pub unsafe fn as_array_oop<T>(&self) -> ArrayOop<T> {
mem::transmute(self)
}

pub fn get_field_address(&self, offset: i32) -> Address {
Address::from_ref(self) + offset as isize
}
}

#[repr(C)]
pub struct ArrayOopDesc<T>(OopDesc, PhantomData<T>);

pub type ArrayOop<T> = &'static ArrayOopDesc<T>;

impl <T> ArrayOopDesc<T> {
const ELEMENT_TYPE_SHOULD_BE_ALIGNED: bool = type_equal::<T, f64>() || type_equal::<T, i64>();
const LENGTH_OFFSET: usize = mem::size_of::<Self>();
fn header_size() -> usize {
let typesize_in_bytes = conversions::raw_align_up(Self::LENGTH_OFFSET + BYTES_IN_INT, BYTES_IN_LONG);
if Self::ELEMENT_TYPE_SHOULD_BE_ALIGNED {
conversions::raw_align_up(typesize_in_bytes / BYTES_IN_WORD, BYTES_IN_LONG)
} else {
typesize_in_bytes / BYTES_IN_WORD
}
}
fn length(&self) -> i32 {
unsafe { *((self as *const _ as *const u8).add(Self::LENGTH_OFFSET) as *const i32) }
}
fn base(&self) -> *const T {
let base_offset_in_bytes = Self::header_size() * BYTES_IN_WORD;
unsafe { (self as *const _ as *const u8).add(base_offset_in_bytes) as _ }
}
pub fn data(&self) -> &[T] {
unsafe { slice::from_raw_parts(self.base(), self.length() as _) }
}
}

#[repr(C)]
#[derive(Debug)]
pub struct OopMapBlock {
pub offset: i32,
pub count: u32,
}



pub fn validate_memory_layouts() {
let vm_checksum = unsafe {
((*UPCALLS).compute_klass_mem_layout_checksum)()
};
let binding_checksum = {
mem::size_of::<Klass>()
^ mem::size_of::<InstanceKlass>()
^ mem::size_of::<InstanceRefKlass>()
^ mem::size_of::<InstanceMirrorKlass>()
^ mem::size_of::<InstanceClassLoaderKlass>()
^ mem::size_of::<TypeArrayKlass>()
^ mem::size_of::<ObjArrayKlass>()
};
assert_eq!(vm_checksum, binding_checksum);
}
1 change: 1 addition & 0 deletions mmtk/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use crate::SINGLETON;
#[no_mangle]
pub extern "C" fn openjdk_gc_init(calls: *const OpenJDK_Upcalls, heap_size: usize) {
unsafe { UPCALLS = calls };
crate::abi::validate_memory_layouts();
memory_manager::gc_init(&SINGLETON, heap_size);
}

Expand Down
14 changes: 12 additions & 2 deletions mmtk/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![feature(specialization)]
#![feature(const_fn)]
extern crate mmtk;
extern crate libc;
#[macro_use]
Expand All @@ -8,15 +10,17 @@ use std::ptr::null_mut;
use mmtk::vm::VMBinding;
use mmtk::util::OpaquePointer;
use mmtk::MMTK;
use mmtk::util::ObjectReference;
use mmtk::util::{Address, ObjectReference};
use mmtk::{Plan, SelectedPlan};
use libc::c_void;
use libc::{c_void, c_char};
pub mod scanning;
pub mod collection;
pub mod object_model;
pub mod active_plan;
pub mod reference_glue;
pub mod api;
mod abi;
mod object_scanning;

#[repr(C)]
pub struct OpenJDK_Upcalls {
Expand All @@ -37,6 +41,12 @@ pub struct OpenJDK_Upcalls {
pub is_mutator: extern "C" fn(tls: OpaquePointer) -> bool,
pub enter_vm: extern "C" fn() -> i32,
pub leave_vm: extern "C" fn(st: i32),
pub compute_klass_mem_layout_checksum: extern "C" fn() -> usize,
pub offset_of_static_fields: extern "C" fn() -> i32,
pub static_oop_field_count_offset: extern "C" fn() -> i32,
pub referent_offset: extern "C" fn() -> i32,
pub discovered_offset: extern "C" fn() -> i32,
pub dump_object_string: extern "C" fn(object: ObjectReference) -> *const c_char,
}

pub static mut UPCALLS: *const OpenJDK_Upcalls = null_mut();
Expand Down
Loading

0 comments on commit 038d0b4

Please sign in to comment.