Skip to content

Commit

Permalink
feat: adds the ArrayRef return type
Browse files Browse the repository at this point in the history
  • Loading branch information
baszalmstra committed Aug 24, 2021
1 parent bb5974f commit 0f5d148
Show file tree
Hide file tree
Showing 15 changed files with 261 additions and 74 deletions.
2 changes: 1 addition & 1 deletion crates/mun_codegen/src/ir/body.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1410,7 +1410,7 @@ impl<'db, 'ink, 't> BodyIrGenerator<'db, 'ink, 't> {
let type_info_ptr = self.type_table.gen_type_info_lookup(
self.context,
&self.builder,
&self.hir_types.type_info(&array_ty),
&self.hir_types.type_info(array_ty),
self.external_globals.type_table,
);

Expand Down
2 changes: 1 addition & 1 deletion crates/mun_codegen/src/ir/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ fn collect_expr<'db, 'ink>(
}

if let Expr::Array(_) = expr {
collect_intrinsic(context, &target, &intrinsics::new_array, intrinsics);
collect_intrinsic(context, target, &intrinsics::new_array, intrinsics);
*needs_alloc = true;
}

Expand Down
2 changes: 1 addition & 1 deletion crates/mun_codegen/src/ir/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ impl<'db, 'ink> HirTypeCache<'db, 'ink> {
let length_ir_type = self.context.ptr_sized_int_type(&self.target_data, None);
let capacity_ir_type = self.context.ptr_sized_int_type(&self.target_data, None);
let element_ir_type = self
.get_basic_type(&element_ty)
.get_basic_type(element_ty)
.expect("could not convert array element type to basic type");

let array_value_type = self.context.struct_type(
Expand Down
17 changes: 5 additions & 12 deletions crates/mun_memory/src/gc/ptr.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::ptr::NonNull;

/// A `GcPtr` is what you interact with outside of the allocator. It is a pointer to a piece of
/// memory that points to the actual data stored in memory.
///
Expand All @@ -22,21 +24,12 @@ pub trait HasIndirectionPtr {
/// # Safety
///
/// This is an unsafe method because derefencing could result in an access violation.
unsafe fn deref<T: Sized>(&self) -> *const T;

/// Returns a mutable pointer to the referenced memory.
///
/// # Safety
///
/// This is an unsafe method because derefencing could result in an access violation.
unsafe fn deref_mut<T: Sized>(&mut self) -> *mut T {
self.deref::<T>() as *mut _
}
unsafe fn deref<T: Sized>(&self) -> NonNull<T>;
}

impl HasIndirectionPtr for GcPtr {
unsafe fn deref<T: Sized>(&self) -> *const T {
(*self.0).cast()
unsafe fn deref<T: Sized>(&self) -> NonNull<T> {
NonNull::new_unchecked(*self.0 as *mut T)
}
}

Expand Down
3 changes: 2 additions & 1 deletion crates/mun_memory/src/gc/root_ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use crate::{
HasCompileTimeMemoryLayout,
};
use std::marker::PhantomData;
use std::ptr::NonNull;
use std::sync::{Arc, Weak};

/// A `GcPtr` that automatically roots and unroots its internal `GcPtr`.
Expand Down Expand Up @@ -64,7 +65,7 @@ impl<T: HasCompileTimeMemoryLayout + TypeTrace, G: GcRuntime<T>> Drop for GcRoot
impl<T: HasCompileTimeMemoryLayout + TypeTrace, G: GcRuntime<T>> HasIndirectionPtr
for GcRootPtr<T, G>
{
unsafe fn deref<R: Sized>(&self) -> *const R {
unsafe fn deref<R: Sized>(&self) -> NonNull<R> {
self.handle.deref()
}
}
12 changes: 6 additions & 6 deletions crates/mun_memory/tests/gc/structs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ impl_struct_ty!(Foo);
#[test]
fn test_trace() {
let runtime = MarkSweep::<&'static TypeInfo, EventAggregator<Event>>::default();
let mut foo_handle = runtime.alloc(Foo::type_info());
let foo_handle = runtime.alloc(Foo::type_info());
let bar_handle = runtime.alloc(i64::type_info());

// Assign bar to foo.bar
unsafe {
(*foo_handle.deref_mut::<Foo>()).bar = bar_handle;
foo_handle.deref::<Foo>().as_mut().bar = bar_handle;
}

// Trace foo to see if we get bar back
Expand All @@ -38,12 +38,12 @@ fn test_trace() {
#[test]
fn trace_collect() {
let runtime = Arc::new(MarkSweep::<&'static TypeInfo, EventAggregator<Event>>::default());
let mut foo = GcRootPtr::new(&runtime, runtime.alloc(Foo::type_info()));
let foo = GcRootPtr::new(&runtime, runtime.alloc(Foo::type_info()));
let bar = runtime.alloc(i64::type_info());

// Assign bar to foo.bar
unsafe {
(*foo.deref_mut::<Foo>()).bar = bar;
foo.deref::<Foo>().as_mut().bar = bar;
}

// Collect garbage, bar should not be collected
Expand All @@ -70,11 +70,11 @@ fn trace_collect() {
#[test]
fn trace_cycle() {
let runtime = Arc::new(MarkSweep::<&'static TypeInfo, EventAggregator<Event>>::default());
let mut foo = GcRootPtr::new(&runtime, runtime.alloc(Foo::type_info()));
let foo = GcRootPtr::new(&runtime, runtime.alloc(Foo::type_info()));

// Assign bar to foo.bar
unsafe {
(*foo.deref_mut::<Foo>()).bar = foo.handle();
foo.deref::<Foo>().as_mut().bar = foo.handle();
}

// Collect garbage, nothing should be collected since foo is rooted
Expand Down
2 changes: 1 addition & 1 deletion crates/mun_memory/tests/gc/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ macro_rules! impl_struct_ty {
#[allow(non_upper_case_globals, non_snake_case)]
fn [<trace_ $ty>](obj:GcPtr) -> Vec<GcPtr> {
let mut result = Vec::new();
let foo = unsafe { &(*obj.deref::<$ty>()) };
let foo = unsafe { &(obj.deref::<$ty>().as_ref()) };
foo.trace(&mut result);
result
}
Expand Down
39 changes: 23 additions & 16 deletions crates/mun_runtime/src/adt.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
use crate::garbage_collector::{GcPtr, GcRootPtr, UnsafeTypeInfo};
use crate::{
marshal::Marshal,
reflection::{
equals_argument_type, equals_return_type, ArgumentReflection, ReturnTypeReflection,
},
reflection::{equals_argument_type, ArgumentReflection, ReturnTypeReflection},
Runtime,
};
use abi::TypeInfo;
use memory::gc::{GcRuntime, HasIndirectionPtr};
use once_cell::sync::OnceCell;
use std::cell::{Ref, RefCell};
Expand All @@ -25,7 +24,7 @@ pub struct RawStruct(GcPtr);

impl RawStruct {
/// Returns a pointer to the struct memory.
pub unsafe fn get_ptr(&self) -> *const u8 {
pub unsafe fn get_ptr(&self) -> NonNull<u8> {
self.0.deref()
}
}
Expand Down Expand Up @@ -77,7 +76,9 @@ impl<'s> StructRef<'s> {
) -> NonNull<T> {
let offset = *struct_info.field_offsets().get_unchecked(field_idx);
// Safety: self.raw's memory pointer is never null
NonNull::new_unchecked(self.raw.get_ptr().add(offset as usize).cast::<T>() as *mut _)
NonNull::new_unchecked(
self.raw.get_ptr().as_ptr().add(offset as usize).cast::<T>() as *mut _
)
}

/// Retrieves the value of the field corresponding to the specified `field_name`.
Expand All @@ -95,15 +96,15 @@ impl<'s> StructRef<'s> {
// Safety: If we found the `field_idx`, we are guaranteed to also have the `field_type` and
// `field_offset`.
let field_type = unsafe { struct_info.field_types().get_unchecked(field_idx) };
equals_return_type::<T>(field_type).map_err(|(expected, found)| {
format!(
"Mismatched types for `{}::{}`. Expected: `{}`. Found: `{}`.",
if !T::equals_type(field_type) {
return Err(format!(
"mismatched types for `{}::{}`. Expected: `{}`. Found: `{}`.",
type_info.name(),
field_name,
expected,
found,
)
})?;
T::type_name(),
type_info.name(),
));
};

// If we found the `field_idx`, we are guaranteed to also have the `field_offset`
let field_ptr =
Expand Down Expand Up @@ -209,6 +210,10 @@ impl<'r> ReturnTypeReflection for StructRef<'r> {
static GUID: OnceCell<abi::Guid> = OnceCell::new();
*GUID.get_or_init(|| abi::Guid(md5::compute(<Self as ReturnTypeReflection>::type_name()).0))
}

fn equals_type(type_info: &TypeInfo) -> bool {
type_info.as_struct().is_some()
}
}

impl<'s> Marshal<'s> for StructRef<'s> {
Expand Down Expand Up @@ -244,7 +249,7 @@ impl<'s> Marshal<'s> for StructRef<'s> {
// For a value struct, `ptr` points to a struct value.

// Create a new object using the runtime's intrinsic
let mut gc_handle = {
let gc_handle = {
runtime.gc().alloc(
// Safety: `ty` is a shared reference, so is guaranteed to not be `ptr::null()`.
UnsafeTypeInfo::new(unsafe {
Expand All @@ -255,9 +260,9 @@ impl<'s> Marshal<'s> for StructRef<'s> {

// Construct
let src = ptr.cast::<u8>().as_ptr() as *const _;
let dest = unsafe { gc_handle.deref_mut::<u8>() };
let dest = unsafe { gc_handle.deref::<u8>() };
let size = type_info.size_in_bytes();
unsafe { ptr::copy_nonoverlapping(src, dest, size) };
unsafe { ptr::copy_nonoverlapping(src, dest.as_ptr(), size) };

gc_handle
} else {
Expand All @@ -280,7 +285,9 @@ impl<'s> Marshal<'s> for StructRef<'s> {
if struct_info.memory_kind == abi::StructMemoryKind::Value {
let dest = ptr.cast::<u8>().as_ptr();
let size = type_info.size_in_bytes();
unsafe { ptr::copy_nonoverlapping(value.into_raw().get_ptr(), dest, size as usize) };
unsafe {
ptr::copy_nonoverlapping(value.into_raw().get_ptr().as_ptr(), dest, size as usize)
};
} else {
unsafe { *ptr.as_mut() = value.into_raw() };
}
Expand Down
153 changes: 153 additions & 0 deletions crates/mun_runtime/src/array.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
use crate::garbage_collector::GcPtr;
use crate::{Marshal, ReturnTypeReflection, Runtime};
use abi::TypeInfo;
use memory::gc::{GcRuntime, HasIndirectionPtr};
use memory::{ArrayMemoryLayout, ArrayType, CompositeType};
use once_cell::sync::OnceCell;
use std::marker::PhantomData;
use std::ptr::NonNull;

/// Represents a Mun array pointer.
#[repr(transparent)]
#[derive(Clone)]
pub struct RawArray(GcPtr);

impl RawArray {
/// Returns a pointer to the array memory.
///
/// # Safety
///
/// Dereferencing might cause undefined behavior
pub unsafe fn get_ptr(&self) -> NonNull<u8> {
self.0.deref()
}
}

/// Type-agnostic wrapper for interoperability with a Mun array. This is merely a reference to the
/// Mun array, that will be garbage collected unless it is rooted.
#[derive(Clone)]
pub struct ArrayRef<'a, T: Marshal<'a>> {
raw: RawArray,
runtime: &'a Runtime,
_phantom: PhantomData<T>,
}

impl<'array, T: Marshal<'array> + 'array> ArrayRef<'array, T> {
/// Creates a `StructRef` that wraps a raw Mun struct.
fn new<'runtime>(raw: RawArray, runtime: &'runtime Runtime) -> Self
where
'runtime: 'array,
{
Self {
raw,
runtime,
_phantom: Default::default(),
}
}

/// Consumes the `ArrayRef`, returning a raw Mun array.
pub fn into_raw(self) -> RawArray {
self.raw
}

/// Returns the number of elements stored in the array
pub fn len(&self) -> usize {
unsafe {
let value_ty = self.runtime.gc.ptr_type(self.raw.0);
let array_ty = value_ty
.as_array()
.expect("type of the array value must be an array");
let value_ptr = self.raw.get_ptr();
array_ty.retrieve_length(value_ptr)
}
}

/// Returns true if this array does not contain a single element.
pub fn is_empty(&self) -> bool {
self.len() == 0
}

/// Returns the length of the array
pub fn capacity(&self) -> usize {
unsafe {
let value_ty = self.runtime.gc.ptr_type(self.raw.0);
let array_ty = value_ty
.as_array()
.expect("type of the array value must be an array");
let value_ptr = self.raw.get_ptr();
array_ty.retrieve_capacity(value_ptr)
}
}

/// Returns an iterator to iterate over the elements of the array.
pub fn iter(&self) -> impl Iterator<Item = T> + 'array
where
T: 'array,
{
let value_ty = self.runtime.gc.ptr_type(self.raw.0);
let array_ty = value_ty
.as_array()
.expect("type of the array value must be an array");
let value_ptr = unsafe { self.raw.get_ptr() };
let iter = unsafe { array_ty.elements(value_ptr) };
let element_ty = unsafe { array_ty.element_type().into_inner().as_ref() };
let runtime = self.runtime;
iter.map(move |element_ptr| {
T::marshal_from_ptr(element_ptr.cast(), runtime, Some(element_ty))
})
}
}

impl<'a, T: Marshal<'a> + ReturnTypeReflection> ReturnTypeReflection for ArrayRef<'a, T> {
fn type_guid() -> abi::Guid {
// TODO: Once `const_fn` lands, replace this with a const md5 hash
static GUID: OnceCell<abi::Guid> = OnceCell::new();
*GUID.get_or_init(|| abi::Guid(md5::compute(<Self as ReturnTypeReflection>::type_name()).0))
}

fn type_name() -> &'static str {
static NAME: OnceCell<String> = OnceCell::new();
NAME.get_or_init(|| format!("[{}]", T::type_name()))
}

/// Returns true if this type equals the given type information
fn equals_type(type_info: &abi::TypeInfo) -> bool {
type_info
.as_array()
.map(|arr| T::equals_type(arr.element_type()))
.unwrap_or(false)
}
}

impl<'a, T: Marshal<'a> + 'a> Marshal<'a> for ArrayRef<'a, T> {
type MunType = RawArray;

fn marshal_from<'runtime>(value: Self::MunType, runtime: &'runtime Runtime) -> Self
where
Self: 'a,
'runtime: 'a,
{
ArrayRef::new(value, runtime)
}

fn marshal_into(self) -> Self::MunType {
self.raw
}

fn marshal_from_ptr<'runtime>(
ptr: NonNull<Self::MunType>,
runtime: &'runtime Runtime,
_type_info: Option<&TypeInfo>,
) -> Self
where
Self: 'a,
'runtime: 'a,
{
let handle = unsafe { *ptr.cast::<GcPtr>().as_ptr() };
ArrayRef::new(RawArray(handle), runtime)
}

fn marshal_to_ptr(value: Self, mut ptr: NonNull<Self::MunType>, _type_info: Option<&TypeInfo>) {
unsafe { *ptr.as_mut() = value.into_raw() };
}
}
Loading

0 comments on commit 0f5d148

Please sign in to comment.