Skip to content

Commit

Permalink
feat(runtime): add marshalling of value structs
Browse files Browse the repository at this point in the history
  • Loading branch information
Wodann committed Mar 7, 2020
1 parent 9dc084e commit 3a716ad
Show file tree
Hide file tree
Showing 17 changed files with 407 additions and 186 deletions.
7 changes: 5 additions & 2 deletions crates/mun/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#[macro_use]
extern crate failure;

use std::cell::RefCell;
use std::rc::Rc;
use std::time::Duration;

use clap::{App, AppSettings, Arg, ArgMatches, SubCommand};
Expand Down Expand Up @@ -84,10 +86,11 @@ fn build(matches: &ArgMatches) -> Result<(), failure::Error> {

/// Starts the runtime with the specified library and invokes function `entry`.
fn start(matches: &ArgMatches) -> Result<(), failure::Error> {
let mut runtime = runtime(matches)?;
let runtime = Rc::new(RefCell::new(runtime(matches)?));

let borrowed = runtime.borrow();
let entry_point = matches.value_of("entry").unwrap_or("main");
let fn_info = runtime.get_function_info(entry_point).ok_or_else(|| {
let fn_info = borrowed.get_function_info(entry_point).ok_or_else(|| {
std::io::Error::new(
std::io::ErrorKind::InvalidInput,
format!("Failed to obtain entry point '{}'", entry_point),
Expand Down
3 changes: 3 additions & 0 deletions crates/mun_codegen/src/intrinsics.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::ir::dispatch_table::FunctionPrototype;
use crate::type_info::TypeInfo;
use inkwell::context::Context;
use inkwell::types::FunctionType;

Expand All @@ -18,4 +19,6 @@ pub trait Intrinsic: Sync {
intrinsics! {
/// Allocates memory from the runtime to use in code.
pub fn malloc(size: u64, alignment: u64) -> *mut u8;
/// Allocates memory for and clones the specified type located at `src` into it.
pub fn clone(src: *const u8, ty: *const TypeInfo) -> *mut u8;
}
2 changes: 1 addition & 1 deletion crates/mun_codegen/src/intrinsics/macros.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
macro_rules! intrinsics{
($($(#[$attr:meta])* pub fn $name:ident($($arg_name:ident:$arg:ty),*) -> $ret:ty;);*) => {
($($(#[$attr:meta])* pub fn $name:ident($($arg_name:ident:$arg:ty),+) -> $ret:ty;)+) => {
$(
paste::item! {
pub struct [<Intrinsic $name>];
Expand Down
8 changes: 8 additions & 0 deletions crates/mun_codegen/src/ir.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::type_info::TypeInfo;
use inkwell::context::Context;
use inkwell::types::{
AnyType, AnyTypeEnum, BasicType, BasicTypeEnum, FunctionType, IntType, PointerType,
Expand Down Expand Up @@ -183,6 +184,13 @@ impl<S: BasicType, T: IsIrType<Type = S>> IsPointerType for *const T {
}
}

// HACK: Manually add `*const TypeInfo`
impl IsPointerType for *const TypeInfo {
fn ir_type(context: &Context) -> PointerType {
context.i8_type().ptr_type(AddressSpace::Const)
}
}

impl<S: BasicType, T: IsIrType<Type = S>> IsPointerType for *mut T {
fn ir_type(context: &Context) -> PointerType {
T::ir_type(context).ptr_type(AddressSpace::Generic)
Expand Down
9 changes: 9 additions & 0 deletions crates/mun_codegen/src/ir/dispatch_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,9 @@ impl<'a, D: IrDatabase> DispatchTableBuilder<'a, D> {
match infer[*callee].as_callable_def() {
Some(hir::CallableDef::Function(def)) => self.collect_fn_def(def),
Some(hir::CallableDef::Struct(s)) => {
// self.collect_intrinsic(&intrinsics::new);
self.collect_intrinsic(&intrinsics::clone);
// self.collect_intrinsic(&intrinsics::drop);
if s.data(self.db).memory_kind == hir::StructMemoryKind::GC {
self.collect_intrinsic(&intrinsics::malloc)
}
Expand All @@ -192,6 +195,9 @@ impl<'a, D: IrDatabase> DispatchTableBuilder<'a, D> {
if let Expr::RecordLit { .. } = expr {
let struct_ty = infer[expr_id].clone();
let hir_struct = struct_ty.as_struct().unwrap(); // Can only really get here if the type is a struct
// self.collect_intrinsic(&intrinsics::new);
self.collect_intrinsic(&intrinsics::clone);
// self.collect_intrinsic(&intrinsics::drop);
if hir_struct.data(self.db).memory_kind == hir::StructMemoryKind::GC {
self.collect_intrinsic(&intrinsics::malloc)
}
Expand All @@ -205,6 +211,9 @@ impl<'a, D: IrDatabase> DispatchTableBuilder<'a, D> {
.expect("unknown path");

if let hir::Resolution::Def(hir::ModuleDef::Struct(s)) = resolution {
// self.collect_intrinsic(&intrinsics::new);
self.collect_intrinsic(&intrinsics::clone);
// self.collect_intrinsic(&intrinsics::drop);
if s.data(self.db).memory_kind == hir::StructMemoryKind::GC {
self.collect_intrinsic(&intrinsics::malloc)
}
Expand Down
4 changes: 2 additions & 2 deletions crates/mun_codegen/src/snapshots/test__field_crash.snap
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ expression: "struct(gc) Foo { a: int };\n\nfn main(c:int):int {\n let b = Foo
; ModuleID = 'main.mun'
source_filename = "main.mun"

%DispatchTable = type { i8* (i64, i64)* }
%DispatchTable = type { i8* (i8 addrspace(4)*, i8 addrspace(4)*)*, i8* (i64, i64)* }
%Foo = type { i64 }

@dispatchTable = global %DispatchTable zeroinitializer
Expand All @@ -18,7 +18,7 @@ body:
%c1 = load i64, i64* %c
%add = add i64 %c1, 5
%init = insertvalue %Foo undef, i64 %add, 0
%malloc_ptr = load i8* (i64, i64)*, i8* (i64, i64)** getelementptr inbounds (%DispatchTable, %DispatchTable* @dispatchTable, i32 0, i32 0)
%malloc_ptr = load i8* (i64, i64)*, i8* (i64, i64)** getelementptr inbounds (%DispatchTable, %DispatchTable* @dispatchTable, i32 0, i32 1)
%malloc = call i8* %malloc_ptr(i64 ptrtoint (i64* getelementptr (i64, i64* null, i32 1) to i64), i64 ptrtoint (i64* getelementptr ({ i1, i64 }, { i1, i64 }* null, i64 0, i32 1) to i64))
%Foo = bitcast i8* %malloc to %Foo*
store %Foo %init, %Foo* %Foo
Expand Down
6 changes: 3 additions & 3 deletions crates/mun_codegen/src/snapshots/test__field_expr.snap
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
---
source: crates/mun_codegen/src/test.rs
expression: "struct Bar(float, Foo);\nstruct Foo { a: int };\n\nfn bar_0(bar: Bar): float {\n bar.0\n}\n\nfn bar_1(bar: Bar): Foo {\n bar.1\n}\n\nfn bar_1_a(bar: Bar): int {\n bar.1.a\n}\n\nfn foo_a(foo: Foo): int {\n foo.a\n}\n\nfn bar_1_foo_a(bar: Bar): int {\n foo_a(bar_1(bar))\n}\n\nfn main(): int {\n let a: Foo = Foo { a: 5 };\n let b: Bar = Bar(1.23, a);\n let aa_lhs = a.a + 2;\n let aa_rhs = 2 + a.a;\n aa_lhs + aa_rhs\n}"
expression: "struct(value) Bar(float, Foo);\nstruct(value) Foo { a: int };\n\nfn bar_0(bar: Bar): float {\n bar.0\n}\n\nfn bar_1(bar: Bar): Foo {\n bar.1\n}\n\nfn bar_1_a(bar: Bar): int {\n bar.1.a\n}\n\nfn foo_a(foo: Foo): int {\n foo.a\n}\n\nfn bar_1_foo_a(bar: Bar): int {\n foo_a(bar_1(bar))\n}\n\nfn main(): int {\n let a: Foo = Foo { a: 5 };\n let b: Bar = Bar(1.23, a);\n let aa_lhs = a.a + 2;\n let aa_rhs = 2 + a.a;\n aa_lhs + aa_rhs\n}"
---
; ModuleID = 'main.mun'
source_filename = "main.mun"

%DispatchTable = type { i64 (%Foo)*, %Foo (%Bar)* }
%DispatchTable = type { i64 (%Foo)*, %Foo (%Bar)*, i8* (i8 addrspace(4)*, i8 addrspace(4)*)* }
%Foo = type { i64 }
%Bar = type { double, %Foo }

@dispatchTable = global %DispatchTable { i64 (%Foo)* @foo_a, %Foo (%Bar)* @bar_1 }
@dispatchTable = global %DispatchTable { i64 (%Foo)* @foo_a, %Foo (%Bar)* @bar_1, i8* (i8 addrspace(4)*, i8 addrspace(4)*)* null }

define double @bar_0(%Bar) {
body:
Expand Down
4 changes: 2 additions & 2 deletions crates/mun_codegen/src/snapshots/test__gc_struct.snap
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ expression: "struct(gc) Foo { a: int, b: int };\n\nfn foo() {\n let a = Foo {
; ModuleID = 'main.mun'
source_filename = "main.mun"

%DispatchTable = type { i8* (i64, i64)* }
%DispatchTable = type { i8* (i8 addrspace(4)*, i8 addrspace(4)*)*, i8* (i64, i64)* }
%Foo = type { i64, i64 }

@dispatchTable = global %DispatchTable zeroinitializer
Expand All @@ -14,7 +14,7 @@ define void @foo() {
body:
%b4 = alloca %Foo*
%a = alloca %Foo*
%malloc_ptr = load i8* (i64, i64)*, i8* (i64, i64)** getelementptr inbounds (%DispatchTable, %DispatchTable* @dispatchTable, i32 0, i32 0)
%malloc_ptr = load i8* (i64, i64)*, i8* (i64, i64)** getelementptr inbounds (%DispatchTable, %DispatchTable* @dispatchTable, i32 0, i32 1)
%malloc = call i8* %malloc_ptr(i64 mul nuw (i64 ptrtoint (i64* getelementptr (i64, i64* null, i32 1) to i64), i64 2), i64 ptrtoint (i64* getelementptr ({ i1, i64 }, { i1, i64 }* null, i64 0, i32 1) to i64))
%Foo = bitcast i8* %malloc to %Foo*
store %Foo { i64 3, i64 4 }, %Foo* %Foo
Expand Down
3 changes: 3 additions & 0 deletions crates/mun_codegen/src/snapshots/test__struct_test.snap
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@ expression: "struct(value) Bar(float, int, bool, Foo);\nstruct(value) Foo { a: i
; ModuleID = 'main.mun'
source_filename = "main.mun"

%DispatchTable = type { i8* (i8 addrspace(4)*, i8 addrspace(4)*)* }
%Baz = type {}
%Bar = type { double, i64, i1, %Foo }
%Foo = type { i64 }

@dispatchTable = global %DispatchTable zeroinitializer

define void @foo() {
body:
%c = alloca %Baz
Expand Down
7 changes: 7 additions & 0 deletions crates/mun_codegen/src/type_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,13 @@ impl<T: HasStaticTypeInfo> HasStaticTypeInfo for *const T {
}
}

// HACK: Manually add `*const TypeInfo`
impl HasStaticTypeInfo for *const TypeInfo {
fn type_info() -> TypeInfo {
TypeInfo::new("*const TypeInfo", TypeGroup::FundamentalTypes)
}
}

/// A trait that statically defines that a type can be used as a return type for a function.
pub trait HasStaticReturnTypeInfo {
fn return_type_info() -> Option<TypeInfo>;
Expand Down
7 changes: 5 additions & 2 deletions crates/mun_runtime/examples/hot_reloading.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use mun_runtime::{invoke_fn, RetryResultExt, RuntimeBuilder};
use std::cell::RefCell;
use std::env;
use std::rc::Rc;

// How to run?
// 1. On the CLI, navigate to the `crates/mun_runtime/examples` directory.
Expand All @@ -9,14 +11,15 @@ fn main() {
let lib_dir = env::args().nth(1).expect("Expected path to a Mun library.");
println!("lib: {}", lib_dir);

let mut runtime = RuntimeBuilder::new(lib_dir)
let runtime = RuntimeBuilder::new(lib_dir)
.spawn()
.expect("Failed to spawn Runtime");

let runtime = Rc::new(RefCell::new(runtime));
loop {
let n: i64 = invoke_fn!(runtime, "nth").wait();
let result: i64 = invoke_fn!(runtime, "fibonacci", n).wait();
println!("fibonacci({}) = {}", n, result);
runtime.update();
runtime.borrow_mut().update();
}
}
30 changes: 26 additions & 4 deletions crates/mun_runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,20 @@ use std::alloc::Layout;
use std::collections::HashMap;
use std::io;
use std::path::{Path, PathBuf};
use std::ptr;
use std::sync::mpsc::{channel, Receiver};
use std::time::Duration;

use abi::{FunctionInfo, Privacy};
use abi::{FunctionInfo, Privacy, TypeInfo};
use failure::Error;
use function::FunctionInfoStorage;
use notify::{DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher};

pub use crate::marshal::MarshalInto;
pub use crate::marshal::Marshal;
pub use crate::reflection::{ArgumentReflection, ReturnTypeReflection};

pub use crate::assembly::Assembly;
pub use crate::r#struct::Struct;
pub use crate::r#struct::StructRef;

/// Options for the construction of a [`Runtime`].
#[derive(Clone, Debug)]
Expand Down Expand Up @@ -116,6 +117,18 @@ extern "C" fn malloc(size: u64, alignment: u64) -> *mut u8 {
}
}

extern "C" fn clone(src: *const u8, ty: *const TypeInfo) -> *mut u8 {
let type_info = unsafe { ty.as_ref().unwrap() };
let struct_info = type_info.as_struct().unwrap();
let size = struct_info.field_offsets().last().cloned().unwrap_or(0)
+ struct_info.field_sizes().last().cloned().unwrap_or(0);
let alignment = 8;

let dest = malloc(size as u64, alignment);
unsafe { ptr::copy_nonoverlapping(src, dest, size as usize) };
dest
}

impl Runtime {
/// Constructs a new `Runtime` that loads the library at `library_path` and its
/// dependencies. The `Runtime` contains a file watcher that is triggered with an interval
Expand All @@ -131,8 +144,17 @@ impl Runtime {
malloc as *const std::ffi::c_void,
);

let (clone_info, clone_storage) = FunctionInfoStorage::new_function(
"clone",
&["*const core::u8".to_string(), "*const TypeInfo".to_string()],
Some("*mut core::u8".to_string()),
Privacy::Public,
clone as *const std::ffi::c_void,
);

let mut dispatch_table = DispatchTable::default();
dispatch_table.insert_fn("malloc", malloc_info);
dispatch_table.insert_fn("clone", clone_info);

let watcher: RecommendedWatcher = Watcher::new(tx, options.delay)?;
let mut runtime = Runtime {
Expand All @@ -141,7 +163,7 @@ impl Runtime {
watcher,
watcher_rx: rx,

_local_fn_storage: vec![malloc_storage],
_local_fn_storage: vec![malloc_storage, clone_storage],
};

runtime.add_assembly(&options.library_path)?;
Expand Down
Loading

0 comments on commit 3a716ad

Please sign in to comment.