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: use extern functions in dispatch table #90

Merged
Merged
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
Next Next commit
feat: malloc is runtime linked
baszalmstra committed Jan 24, 2020
commit 7a0c60af70b72204fe35c64f201959edef25c46a
3 changes: 1 addition & 2 deletions crates/mun_codegen/Cargo.toml
Original file line number Diff line number Diff line change
@@ -17,7 +17,7 @@ salsa="0.12"
md5="0.6.1"
array-init="0.1.0"
tempfile = "3"
lazy_static = "1.4.0"
paste = "0.1.6"

[dependencies.inkwell]
git = "https://github.com/mun-lang/inkwell"
@@ -28,6 +28,5 @@ features = ["llvm7-0"]
insta = "0.12.0"

[build-dependencies]
lazy_static = "1.4.0"
semver = "0.9.0"
regex = "1.3.1"
31 changes: 1 addition & 30 deletions crates/mun_codegen/src/code_gen/symbols.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use super::abi_types::{gen_abi_types, AbiTypes};
use crate::ir::dispatch_table::{DispatchTable, DispatchableFunction};
use crate::ir::function;
use crate::type_info::TypeInfo;
use crate::values::{BasicValue, GlobalValue};
use crate::IrDatabase;
use hir::{Ty, TypeCtor};
@@ -12,36 +13,6 @@ use inkwell::{
AddressSpace,
};
use std::collections::HashMap;
use std::hash::{Hash, Hasher};

pub type Guid = [u8; 16];

#[derive(Clone, Eq, Ord, PartialOrd, Debug)]
pub struct TypeInfo {
pub guid: Guid,
pub name: String,
}

impl Hash for TypeInfo {
fn hash<H: Hasher>(&self, state: &mut H) {
state.write(&self.guid)
}
}

impl PartialEq for TypeInfo {
fn eq(&self, other: &Self) -> bool {
self.guid == other.guid
}
}

impl TypeInfo {
fn from_name<S: AsRef<str>>(name: S) -> TypeInfo {
TypeInfo {
name: name.as_ref().to_string(),
guid: md5::compute(name.as_ref()).0,
}
}
}

pub fn type_info_query(db: &impl IrDatabase, ty: Ty) -> TypeInfo {
match ty {
2 changes: 1 addition & 1 deletion crates/mun_codegen/src/db.rs
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@



use crate::{code_gen::symbols::TypeInfo, ir::module::ModuleIR, Context};
use crate::{ir::module::ModuleIR, type_info::TypeInfo, Context};
use inkwell::types::StructType;
use inkwell::{types::AnyTypeEnum, OptimizationLevel};
use mun_target::spec::Target;
21 changes: 21 additions & 0 deletions crates/mun_codegen/src/intrinsics.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use crate::ir::dispatch_table::FunctionPrototype;
use inkwell::context::Context;
use inkwell::types::FunctionType;

#[macro_use]
mod r#macro;

/// Defines the properties of an intrinsic function that can be called from Mun. These functions
/// are mostly used internally.
pub trait Intrinsic: Sync {
/// Returns the prototype of the intrinsic
fn prototype(&self) -> FunctionPrototype;

/// Returns the IR type for the function
fn ir_type(&self, context: &Context) -> FunctionType;
}

intrinsics! {
/// Allocates memory from the runtime to use in code.
pub fn malloc(size: u64, alignment: u64) -> *mut u8;
}
30 changes: 30 additions & 0 deletions crates/mun_codegen/src/intrinsics/macro.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
macro_rules! intrinsics{
($($(#[$attr:meta])* pub fn $name:ident($($arg_name:ident:$arg:ty),*) -> $ret:ty;);*) => {
$(
paste::item! {
pub struct [<Intrinsic $name>];
}
paste::item! {
impl Intrinsic for [<Intrinsic $name>] {
fn prototype(&self) -> FunctionPrototype {
FunctionPrototype {
name: stringify!($name).to_owned(),
arg_types: vec![$(<$arg as crate::type_info::HasStaticTypeInfo>::type_info()),*],
ret_type: <$ret as crate::type_info::HasStaticReturnTypeInfo>::return_type_info()
}
}

fn ir_type(&self, context: &Context) -> FunctionType {
let args = vec![$(<$arg as crate::ir::IsBasicIrType>::ir_type(context)),*];
<$ret as crate::ir::IsFunctionReturnType>::fn_type(context, &args, false)
}
}
}
paste::item! {
#[allow(non_upper_case_globals)]
$(#[$attr])* pub const $name:[<Intrinsic $name>] = [<Intrinsic $name>];
}
)*
};
($(#[$attr:meta])*) => {}
}
188 changes: 187 additions & 1 deletion crates/mun_codegen/src/ir.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
use inkwell::types::{AnyTypeEnum, BasicTypeEnum};
use failure::_core::marker::PhantomData;
use inkwell::context::Context;
use inkwell::types::{
AnyType, AnyTypeEnum, BasicType, BasicTypeEnum, FunctionType, IntType, PointerType,
};
use inkwell::AddressSpace;

pub mod adt;
pub mod body;
#[macro_use]
pub(crate) mod dispatch_table;
pub mod function;
pub mod module;
@@ -19,3 +25,183 @@ fn try_convert_any_to_basic(ty: AnyTypeEnum) -> Option<BasicTypeEnum> {
_ => None,
}
}

/// Defines that a type has a static representation in inkwell
pub trait IsIrType {
type Type: AnyType;

fn ir_type(context: &Context) -> Self::Type;
}

pub trait IsBasicIrType {
fn ir_type(context: &Context) -> BasicTypeEnum;
}

impl<S: BasicType, T: IsIrType<Type = S>> IsBasicIrType for T {
fn ir_type(context: &Context) -> BasicTypeEnum {
Self::ir_type(context).as_basic_type_enum()
}
}

/// Defines that a type can statically be used as a return type for a function
pub trait IsFunctionReturnType {
fn fn_type(context: &Context, arg_types: &[BasicTypeEnum], is_var_args: bool) -> FunctionType;
}

/// All types that statically have a BasicTypeEnum can also be used as a function return type
impl<T: IsBasicIrType> IsFunctionReturnType for T {
fn fn_type(context: &Context, arg_types: &[BasicTypeEnum], is_var_args: bool) -> FunctionType {
T::ir_type(context).fn_type(arg_types, is_var_args)
}
}

impl IsFunctionReturnType for () {
fn fn_type(context: &Context, arg_types: &[BasicTypeEnum], is_var_args: bool) -> FunctionType {
context.void_type().fn_type(arg_types, is_var_args)
}
}

/// Defines that a value can be converted to an inkwell type
pub trait AsIrType {
type Type: AnyType;

fn as_ir_type(&self, context: &Context) -> Self::Type;
}

impl<S: AnyType, T: IsIrType<Type = S>> AsIrType for PhantomData<T> {
type Type = S;

fn as_ir_type(&self, context: &Context) -> Self::Type {
T::ir_type(context)
}
}

pub trait AsBasicIrType {
fn as_ir_type(&self, context: &Context) -> BasicTypeEnum;
}

impl<S: BasicType, T: AsIrType<Type = S>> AsBasicIrType for T {
fn as_ir_type(&self, context: &Context) -> BasicTypeEnum {
self.as_ir_type(context).as_basic_type_enum()
}
}

/// Defines that a value can be used to construct a function type.
pub trait AsFunctionReturnType {
fn as_fn_type(
&self,
context: &Context,
arg_types: &[BasicTypeEnum],
is_var_args: bool,
) -> FunctionType;
}

impl<T: AsBasicIrType> AsFunctionReturnType for T {
fn as_fn_type(
&self,
context: &Context,
arg_types: &[BasicTypeEnum],
is_var_args: bool,
) -> FunctionType {
self.as_ir_type(context).fn_type(arg_types, is_var_args)
}
}

impl AsFunctionReturnType for () {
fn as_fn_type(
&self,
context: &Context,
arg_types: &[BasicTypeEnum],
is_var_args: bool,
) -> FunctionType {
context.void_type().fn_type(arg_types, is_var_args)
}
}

impl IsIrType for u8 {
type Type = IntType;

fn ir_type(context: &Context) -> Self::Type {
context.i8_type()
}
}

impl IsIrType for u16 {
type Type = IntType;

fn ir_type(context: &Context) -> Self::Type {
context.i16_type()
}
}

impl IsIrType for u32 {
type Type = IntType;

fn ir_type(context: &Context) -> Self::Type {
context.i32_type()
}
}

impl IsIrType for u64 {
type Type = IntType;

fn ir_type(context: &Context) -> Self::Type {
context.i64_type()
}
}

impl IsIrType for i8 {
type Type = IntType;

fn ir_type(context: &Context) -> Self::Type {
context.i8_type()
}
}

impl IsIrType for i16 {
type Type = IntType;

fn ir_type(context: &Context) -> Self::Type {
context.i16_type()
}
}

impl IsIrType for i32 {
type Type = IntType;

fn ir_type(context: &Context) -> Self::Type {
context.i32_type()
}
}

impl IsIrType for i64 {
type Type = IntType;

fn ir_type(context: &Context) -> Self::Type {
context.i64_type()
}
}

pub trait IsPointerType {
fn ir_type(context: &Context) -> PointerType;
}

impl<S: BasicType, T: IsIrType<Type = S>> IsPointerType for *const T {
fn ir_type(context: &Context) -> PointerType {
T::ir_type(context).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)
}
}

impl<T: IsPointerType> IsIrType for T {
type Type = PointerType;

fn ir_type(context: &Context) -> Self::Type {
T::ir_type(context)
}
}
31 changes: 28 additions & 3 deletions crates/mun_codegen/src/ir/body.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::intrinsics;
use crate::{ir::dispatch_table::DispatchTable, ir::try_convert_any_to_basic, IrDatabase};
use hir::{
ArenaId, ArithOp, BinaryOp, Body, CmpOp, Expr, ExprId, HirDisplay, InferenceResult, Literal,
@@ -7,7 +8,7 @@ use inkwell::{
builder::Builder,
module::Module,
values::{BasicValueEnum, CallSiteValue, FloatValue, FunctionValue, IntValue},
FloatPredicate, IntPredicate,
AddressSpace, FloatPredicate, IntPredicate,
};
use std::{collections::HashMap, mem, sync::Arc};

@@ -235,9 +236,33 @@ impl<'a, 'b, D: IrDatabase> BodyIrGenerator<'a, 'b, D> {
hir::StructMemoryKind::GC => {
// TODO: Root memory in GC
let struct_ir_ty = self.db.struct_ty(hir_struct);
let struct_ptr: PointerValue = self
let malloc_fn_ptr = self
.dispatch_table
.get_intrinsic_lookup(&self.builder, &intrinsics::malloc);
let mem_ptr = self
.builder
.build_malloc(struct_ir_ty, &hir_struct.name(self.db).to_string());
.build_call(
malloc_fn_ptr,
&[
struct_ir_ty.size_of().unwrap().into(),
struct_ir_ty.get_alignment().into(),
],
"malloc",
)
.try_as_basic_value()
.left()
.unwrap();
let struct_ptr = self
.builder
.build_bitcast(
mem_ptr,
struct_ir_ty.ptr_type(AddressSpace::Generic),
&hir_struct.name(self.db).to_string(),
)
.into_pointer_value();
// let struct_ptr: PointerValue = self
// .builder
// .build_malloc(struct_ir_ty, &hir_struct.name(self.db).to_string());
self.builder.build_store(struct_ptr, struct_lit);
struct_ptr.into()
}
110 changes: 99 additions & 11 deletions crates/mun_codegen/src/ir/dispatch_table.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
use crate::intrinsics;
use crate::values::FunctionValue;
use crate::IrDatabase;
use inkwell::module::Module;
use inkwell::types::{BasicTypeEnum, FunctionType};
use inkwell::values::{BasicValueEnum, PointerValue};

use crate::code_gen::symbols::TypeInfo;
use crate::intrinsics::Intrinsic;
use crate::type_info::TypeInfo;
use hir::{Body, Expr, ExprId, InferenceResult};
use std::collections::HashMap;
use std::sync::Arc;

/// A dispatch table in IR is a struct that contains pointers to all functions that are called from
/// code. In C terms it looks something like this:
@@ -24,6 +27,8 @@ use std::collections::HashMap;
pub struct DispatchTable {
// This contains the function that map to the DispatchTable struct fields
function_to_idx: HashMap<hir::Function, usize>,
// Prototype to function index
prototype_to_idx: HashMap<FunctionPrototype, usize>,
// This contains an ordered list of all the function in the dispatch table
entries: Vec<DispatchableFunction>,
// Contains a reference to the global value containing the DispatchTable
@@ -69,6 +74,36 @@ impl DispatchTable {
.get(&function)
.expect("unknown function");

self.gen_function_loopup_by_index(builder, &function_name, index)
}

/// Generates a function lookup through the DispatchTable, equivalent to something alone the
/// lines of: `dispatchTable[i]`, where i is the index of the intrinsic and `dispatchTable` is a
/// struct
pub fn get_intrinsic_lookup(
&self,
builder: &inkwell::builder::Builder,
intrinsic: &impl Intrinsic,
) -> PointerValue {
let prototype = intrinsic.prototype();

// Get the index of the intrinsic
let index = *self
.prototype_to_idx
.get(&prototype)
.expect("unknown function");

self.gen_function_loopup_by_index(builder, &prototype.name, index)
}

/// Generates a function lookup through the DispatchTable, equivalent to something alone the
/// lines of: `dispatchTable[i]`, where i is the index and `dispatchTable` is a struct
fn gen_function_loopup_by_index(
&self,
builder: &inkwell::builder::Builder,
function_name: &str,
index: usize,
) -> PointerValue {
// Get the internal table reference
let table_ref = self.table_ref.expect("no dispatch table defined");

@@ -80,7 +115,6 @@ impl DispatchTable {
&format!("{0}_ptr_ptr", function_name),
)
};

builder
.build_load(ptr_to_function_ptr, &format!("{0}_ptr", function_name))
.into_pointer_value()
@@ -99,6 +133,8 @@ pub(crate) struct DispatchTableBuilder<'a, D: IrDatabase> {
module: &'a Module,
// This contains the functions that map to the DispatchTable struct fields
function_to_idx: HashMap<hir::Function, usize>,
// Prototype to function index
prototype_to_idx: HashMap<FunctionPrototype, usize>,
// These are *all* called functions in the modules
entries: Vec<TypedDispatchableFunction>,
// Contains a reference to the global value containing the DispatchTable
@@ -119,6 +155,7 @@ impl<'a, D: IrDatabase> DispatchTableBuilder<'a, D> {
db,
module,
function_to_idx: Default::default(),
prototype_to_idx: Default::default(),
entries: Default::default(),
table_ref: None,
table_type: module.get_context().opaque_struct_type("DispatchTable"),
@@ -136,18 +173,44 @@ impl<'a, D: IrDatabase> DispatchTableBuilder<'a, D> {
}

/// Collects call expression from the given expression and sub expressions.
fn collect_expr(&mut self, expr: ExprId, body: &Body, infer: &InferenceResult) {
let expr = &body[expr];
fn collect_expr(&mut self, expr_id: ExprId, body: &Arc<Body>, infer: &InferenceResult) {
let expr = &body[expr_id];

// If this expression is a call, store it in the dispatch table
if let Expr::Call { callee, .. } = expr {
match infer[*callee].as_callable_def() {
Some(hir::CallableDef::Function(def)) => self.collect_fn_def(def),
Some(hir::CallableDef::Struct(_)) => (),
Some(hir::CallableDef::Struct(s)) => {
if s.data(self.db).memory_kind == hir::StructMemoryKind::GC {
self.collect_intrinsic(&intrinsics::malloc)
}
}
None => panic!("expected a callable expression"),
}
}

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
if hir_struct.data(self.db).memory_kind == hir::StructMemoryKind::GC {
self.collect_intrinsic(&intrinsics::malloc)
}
}

if let Expr::Path(path) = expr {
let resolver = hir::resolver_for_expr(body.clone(), self.db, expr_id);
let resolution = resolver
.resolve_path_without_assoc_items(self.db, path)
.take_values()
.expect("unknown path");

if let hir::Resolution::Def(hir::ModuleDef::Struct(s)) = resolution {
if s.data(self.db).memory_kind == hir::StructMemoryKind::GC {
self.collect_intrinsic(&intrinsics::malloc)
}
}
}

// Recurse further
expr.walk_child_exprs(|expr_id| self.collect_expr(expr_id, body, infer))
}
@@ -174,25 +237,49 @@ impl<'a, D: IrDatabase> DispatchTableBuilder<'a, D> {
None
};

let prototype = FunctionPrototype {
name,
arg_types,
ret_type,
};
self.entries.push(TypedDispatchableFunction {
function: DispatchableFunction {
prototype: FunctionPrototype {
name,
arg_types,
ret_type,
},
prototype: prototype.clone(),
hir: Some(function),
},
ir_type,
});
self.prototype_to_idx
.insert(prototype, self.function_to_idx.len());
self.function_to_idx
.insert(function, self.function_to_idx.len());
}
}

/// Collects a call to an intrinsic function.
#[allow(clippy::map_entry)]
fn collect_intrinsic(&mut self, intrinsic: &impl Intrinsic) {
self.ensure_table_ref();

// If the function is not yet contained in the table add it
let prototype = intrinsic.prototype();
if !self.prototype_to_idx.contains_key(&prototype) {
self.entries.push(TypedDispatchableFunction {
function: DispatchableFunction {
prototype: prototype.clone(),
hir: None,
},
ir_type: intrinsic.ir_type(&self.module.get_context()),
});

self.prototype_to_idx
.insert(prototype, self.function_to_idx.len());
}
}

/// Collect all the call expressions from the specified body with the given type inference
/// result.
pub fn collect_body(&mut self, body: &Body, infer: &InferenceResult) {
pub fn collect_body(&mut self, body: &Arc<Body>, infer: &InferenceResult) {
self.collect_expr(body.body_expr(), body, infer);
}

@@ -235,6 +322,7 @@ impl<'a, D: IrDatabase> DispatchTableBuilder<'a, D> {

DispatchTable {
function_to_idx: self.function_to_idx,
prototype_to_idx: self.prototype_to_idx,
table_ref: self.table_ref,
entries: self
.entries
4 changes: 4 additions & 0 deletions crates/mun_codegen/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/// This library generates machine code from HIR using inkwell which is a safe wrapper around LLVM.
mod code_gen;
mod db;
#[macro_use]
mod ir;
pub(crate) mod symbols;

@@ -9,6 +10,9 @@ mod mock;
#[cfg(test)]
mod test;

pub(crate) mod intrinsics;
pub(crate) mod type_info;

pub use inkwell::{builder, context::Context, module::Module, values, OptimizationLevel};

pub use crate::{
10 changes: 6 additions & 4 deletions crates/mun_codegen/src/snapshots/test__field_crash.snap
Original file line number Diff line number Diff line change
@@ -5,8 +5,11 @@ 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)* }
%Foo = type { i64 }

@dispatchTable = global %DispatchTable zeroinitializer

define i64 @main(i64) {
body:
%b = alloca %Foo*
@@ -15,8 +18,9 @@ body:
%c1 = load i64, i64* %c
%add = add i64 %c1, 5
%init = insertvalue %Foo undef, i64 %add, 0
%malloccall = tail call i8* @malloc(i32 ptrtoint (i64* getelementptr (i64, i64* null, i32 1) to i32))
%Foo = bitcast i8* %malloccall to %Foo*
%malloc_ptr = load i8* (i64, i64)*, i8* (i64, i64)** getelementptr inbounds (%DispatchTable, %DispatchTable* @dispatchTable, i32 0, i32 0)
%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
store %Foo* %Foo, %Foo** %b
%deref = load %Foo*, %Foo** %b
@@ -25,5 +29,3 @@ body:
ret i64 %a
}

declare noalias i8* @malloc(i32)

10 changes: 6 additions & 4 deletions crates/mun_codegen/src/snapshots/test__gc_struct.snap
Original file line number Diff line number Diff line change
@@ -5,14 +5,18 @@ 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)* }
%Foo = type { i64, i64 }

@dispatchTable = global %DispatchTable zeroinitializer

define void @foo() {
body:
%b4 = alloca %Foo*
%a = alloca %Foo*
%malloccall = tail call i8* @malloc(i32 trunc (i64 mul nuw (i64 ptrtoint (i64* getelementptr (i64, i64* null, i32 1) to i64), i64 2) to i32))
%Foo = bitcast i8* %malloccall to %Foo*
%malloc_ptr = load i8* (i64, i64)*, i8* (i64, i64)** getelementptr inbounds (%DispatchTable, %DispatchTable* @dispatchTable, i32 0, i32 0)
%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
store %Foo* %Foo, %Foo** %a
%deref = load %Foo*, %Foo** %a
@@ -27,5 +31,3 @@ body:
ret void
}

declare noalias i8* @malloc(i32)

138 changes: 138 additions & 0 deletions crates/mun_codegen/src/type_info.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
use std::hash::{Hash, Hasher};
use std::marker::PhantomData;
use std::mem::size_of;

pub type Guid = [u8; 16];

#[derive(Clone, Eq, Ord, PartialOrd, Debug)]
pub struct TypeInfo {
pub guid: Guid,
pub name: String,
}

impl Hash for TypeInfo {
fn hash<H: Hasher>(&self, state: &mut H) {
state.write(&self.guid)
}
}

impl PartialEq for TypeInfo {
fn eq(&self, other: &Self) -> bool {
self.guid == other.guid
}
}

impl TypeInfo {
pub fn from_name<S: AsRef<str>>(name: S) -> TypeInfo {
TypeInfo {
name: name.as_ref().to_string(),
guid: md5::compute(name.as_ref()).0,
}
}
}

/// A trait that statically defines that a type can be used as an argument.
pub trait HasStaticTypeInfo {
fn type_info() -> TypeInfo;
}

pub trait HasTypeInfo {
fn type_info(&self) -> TypeInfo;
}

impl<T: HasStaticTypeInfo> HasTypeInfo for PhantomData<T> {
fn type_info(&self) -> TypeInfo {
T::type_info()
}
}

impl<T: HasStaticTypeInfo> HasTypeInfo for T {
fn type_info(&self) -> TypeInfo {
T::type_info()
}
}

impl HasStaticTypeInfo for u8 {
fn type_info() -> TypeInfo {
TypeInfo::from_name("core::u8")
}
}

impl HasStaticTypeInfo for u64 {
fn type_info() -> TypeInfo {
TypeInfo::from_name("core::u64")
}
}

impl HasStaticTypeInfo for i64 {
fn type_info() -> TypeInfo {
TypeInfo::from_name("core::i64")
}
}

impl HasStaticTypeInfo for f32 {
fn type_info() -> TypeInfo {
TypeInfo::from_name("core::f32")
}
}

impl HasStaticTypeInfo for bool {
fn type_info() -> TypeInfo {
TypeInfo::from_name("core::bool")
}
}

impl<T: HasStaticTypeInfo> HasStaticTypeInfo for *mut T {
fn type_info() -> TypeInfo {
TypeInfo::from_name(format!("{}*", T::type_info().name))
}
}

impl HasStaticTypeInfo for usize {
fn type_info() -> TypeInfo {
match size_of::<usize>() {
64 => <u64 as HasStaticTypeInfo>::type_info(),
_ => panic!("unsupported usize size"),
}
}
}

impl<T: HasStaticTypeInfo> HasStaticTypeInfo for *const T {
fn type_info() -> TypeInfo {
TypeInfo::from_name(format!("{}*", T::type_info().name))
}
}

/// 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>;
}

/// A trait that defines that a type can be used as a return type for a function.
pub trait HasReturnTypeInfo {
fn return_type_info(&self) -> Option<TypeInfo>;
}

impl<T: HasStaticReturnTypeInfo> HasReturnTypeInfo for PhantomData<T> {
fn return_type_info(&self) -> Option<TypeInfo> {
T::return_type_info()
}
}

impl<T: HasStaticReturnTypeInfo> HasReturnTypeInfo for T {
fn return_type_info(&self) -> Option<TypeInfo> {
T::return_type_info()
}
}

impl<T: HasStaticTypeInfo> HasStaticReturnTypeInfo for T {
fn return_type_info() -> Option<TypeInfo> {
Some(T::type_info())
}
}

impl HasStaticReturnTypeInfo for () {
fn return_type_info() -> Option<TypeInfo> {
None
}
}
3 changes: 2 additions & 1 deletion crates/mun_runtime/Cargo.toml
Original file line number Diff line number Diff line change
@@ -11,10 +11,11 @@ description = "A runtime for hot reloading and invoking Mun from Rust"
[dependencies]
failure = "0.1.5"
libloading = "0.5"
mun_abi = { path = "../mun_abi" }
abi = { path = "../mun_abi", package = "mun_abi" }
notify = "4.0.12"
parking_lot = "0.9"
tempfile = "3"
md5="0.7.0"

[dev-dependencies]
mun_compiler = { path="../mun_compiler" }
7 changes: 2 additions & 5 deletions crates/mun_runtime/src/assembly.rs
Original file line number Diff line number Diff line change
@@ -2,9 +2,9 @@ use std::io;
use std::path::{Path, PathBuf};

use crate::DispatchTable;
use abi::AssemblyInfo;
use failure::Error;
use libloading::Symbol;
use mun_abi::AssemblyInfo;

mod temp_library;

@@ -55,10 +55,7 @@ impl Assembly {
.ok_or_else(|| {
io::Error::new(
io::ErrorKind::NotFound,
format!(
"Failed to link: function '{}' is missing.",
fn_signature.name()
),
format!("Failed to link: function '{}' is missing.", fn_signature),
)
})?;

66 changes: 63 additions & 3 deletions crates/mun_runtime/src/lib.rs
Original file line number Diff line number Diff line change
@@ -17,11 +17,13 @@ use std::path::{Path, PathBuf};
use std::sync::mpsc::{channel, Receiver};
use std::time::Duration;

use abi::{FunctionInfo, FunctionSignature, Guid, Privacy, Reflection, StructInfo, TypeInfo};
use failure::Error;
use mun_abi::{FunctionInfo, Reflection, StructInfo};
use notify::{DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher};

pub use crate::assembly::Assembly;
use std::alloc::Layout;
use std::ffi::CString;

/// Options for the construction of a [`Runtime`].
#[derive(Clone, Debug)]
@@ -68,7 +70,7 @@ pub struct DispatchTable {
}

impl DispatchTable {
/// Retrieves the [`FunctionInfo`] corresponding to `fn_path`, if it exists.
/// Retrieves the [`abi::FunctionInfo`] corresponding to `fn_path`, if it exists.
pub fn get_fn(&self, fn_path: &str) -> Option<&FunctionInfo> {
self.functions.get(fn_path)
}
@@ -119,6 +121,18 @@ pub struct Runtime {
dispatch_table: DispatchTable,
watcher: RecommendedWatcher,
watcher_rx: Receiver<DebouncedEvent>,

_name: CString,
_u64_type: CString,
_ptr_mut_u8_type: CString,
_arg_types: Vec<abi::TypeInfo>,
_ret_type: Box<abi::TypeInfo>,
}

extern "C" fn malloc(size: u64, alignment: u64) -> *mut u8 {
unsafe {
std::alloc::alloc(Layout::from_size_align(size as usize, alignment as usize).unwrap())
}
}

impl Runtime {
@@ -128,12 +142,58 @@ impl Runtime {
pub fn new(options: RuntimeOptions) -> Result<Runtime, Error> {
let (tx, rx) = channel();

let name = CString::new("malloc").unwrap();
let u64_type = CString::new("core::u64").unwrap();
let ptr_mut_u8_type = CString::new("core::u8*").unwrap();

let arg_types = vec![
TypeInfo {
guid: Guid {
b: md5::compute("core::u64").0,
},
name: u64_type.as_ptr(),
},
TypeInfo {
guid: Guid {
b: md5::compute("core::u64").0,
},
name: u64_type.as_ptr(),
},
];

let ret_type = Box::new(TypeInfo {
guid: Guid {
b: md5::compute("core::u8*").0,
},
name: ptr_mut_u8_type.as_ptr(),
});

let fn_info = FunctionInfo {
signature: FunctionSignature {
name: name.as_ptr(),
arg_types: arg_types.as_ptr(),
return_type: ret_type.as_ref(),
num_arg_types: 2,
privacy: Privacy::Public,
},
fn_ptr: malloc as *const std::ffi::c_void,
};

let mut dispatch_table = DispatchTable::default();
dispatch_table.insert_fn("malloc", fn_info);

let watcher: RecommendedWatcher = Watcher::new(tx, options.delay)?;
let mut runtime = Runtime {
assemblies: HashMap::new(),
dispatch_table: DispatchTable::default(),
dispatch_table,
watcher,
watcher_rx: rx,

_name: name,
_u64_type: u64_type,
_ptr_mut_u8_type: ptr_mut_u8_type,
_arg_types: arg_types,
_ret_type: ret_type,
};

runtime.add_assembly(&options.library_path)?;
2 changes: 1 addition & 1 deletion crates/mun_runtime/src/macros.rs
Original file line number Diff line number Diff line change
@@ -87,7 +87,7 @@ macro_rules! invoke_fn_impl {
let function: core::result::Result<fn($($T),*) -> Output, String> = runtime
.get_function_info(function_name)
.ok_or(format!("Failed to obtain function '{}'", function_name))
.and_then(|function| mun_abi::downcast_fn!(function, fn($($T),*) -> Output));
.and_then(|function| abi::downcast_fn!(function, fn($($T),*) -> Output));

match function {
Ok(function) => Ok(function($($Arg),*)),
2 changes: 1 addition & 1 deletion crates/mun_runtime/src/test.rs
Original file line number Diff line number Diff line change
@@ -337,7 +337,7 @@ fn compiler_valid_utf8() {

#[test]
fn struct_info() {
use mun_abi::{Guid, Reflection};
use abi::{Guid, Reflection};

let driver = TestDriver::new(
r"