Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(runtime): add marshalling of value structs #93

Merged
merged 6 commits into from
Mar 7, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
feat(runtime): add marshalling of value structs
  • Loading branch information
Wodann committed Mar 7, 2020
commit 3a716add8fe7dd31ba0c4aceaf4a4085362d7f5a
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