Skip to content

Commit

Permalink
feat(marshal): create marshallable wrapper functions for functions th…
Browse files Browse the repository at this point in the history
…at contain value struct parameters or return types
  • Loading branch information
Wodann committed Mar 1, 2020
1 parent e74595e commit d1868b4
Show file tree
Hide file tree
Showing 10 changed files with 92 additions and 35 deletions.
8 changes: 4 additions & 4 deletions crates/mun_codegen/src/code_gen/symbols.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::ir::{
};
use crate::type_info::{TypeGroup, TypeInfo};
use crate::values::{BasicValue, GlobalValue};
use crate::IrDatabase;
use crate::{CodeGenParams, IrDatabase};
use hir::Ty;
use inkwell::{
attributes::Attribute,
Expand Down Expand Up @@ -318,9 +318,9 @@ fn gen_struct_info<D: IrDatabase>(
(0..fields.len()).map(|idx| target_data.offset_of_element(&t, idx as u32).unwrap());
let (field_offsets, _) = gen_u16_array(module, field_offsets);

let field_sizes = fields
.iter()
.map(|field| target_data.get_store_size(&db.type_ir(field.ty(db))));
let field_sizes = fields.iter().map(|field| {
target_data.get_store_size(&db.type_ir(field.ty(db), CodeGenParams { is_extern: false }))
});
let (field_sizes, _) = gen_u16_array(module, field_sizes);

types.struct_info_type.const_named_struct(&[
Expand Down
6 changes: 3 additions & 3 deletions crates/mun_codegen/src/db.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#![allow(clippy::type_repetition_in_bounds)]

use crate::{ir::module::ModuleIR, type_info::TypeInfo, Context};
use crate::{ir::module::ModuleIR, type_info::TypeInfo, CodeGenParams, Context};
use inkwell::types::StructType;
use inkwell::{types::AnyTypeEnum, OptimizationLevel};
use mun_target::spec::Target;
Expand All @@ -22,9 +22,9 @@ pub trait IrDatabase: hir::HirDatabase {
#[salsa::input]
fn target(&self) -> Target;

/// Given a type, return the corresponding IR type.
/// Given a type and code generation parameters, return the corresponding IR type.
#[salsa::invoke(crate::ir::ty::ir_query)]
fn type_ir(&self, ty: hir::Ty) -> AnyTypeEnum;
fn type_ir(&self, ty: hir::Ty, params: CodeGenParams) -> AnyTypeEnum;

/// Given a struct, return the corresponding IR type.
#[salsa::invoke(crate::ir::ty::struct_ty_query)]
Expand Down
4 changes: 2 additions & 2 deletions crates/mun_codegen/src/ir/adt.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//use crate::ir::module::Types;
use crate::ir::try_convert_any_to_basic;
use crate::IrDatabase;
use crate::{CodeGenParams, IrDatabase};
use inkwell::types::{BasicTypeEnum, StructType};

pub(super) fn gen_struct_decl(db: &impl IrDatabase, s: hir::Struct) -> StructType {
Expand All @@ -11,7 +11,7 @@ pub(super) fn gen_struct_decl(db: &impl IrDatabase, s: hir::Struct) -> StructTyp
.iter()
.map(|field| {
let field_type = field.ty(db);
try_convert_any_to_basic(db.type_ir(field_type))
try_convert_any_to_basic(db.type_ir(field_type, CodeGenParams { is_extern: false }))
.expect("could not convert field type")
})
.collect();
Expand Down
11 changes: 8 additions & 3 deletions crates/mun_codegen/src/ir/body.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use crate::intrinsics;
use crate::{ir::dispatch_table::DispatchTable, ir::try_convert_any_to_basic, IrDatabase};
use crate::{
ir::dispatch_table::DispatchTable, ir::try_convert_any_to_basic, CodeGenParams, IrDatabase,
};
use hir::{
ArenaId, ArithOp, BinaryOp, Body, CmpOp, Expr, ExprId, HirDisplay, InferenceResult, Literal,
Name, Ordering, Pat, PatId, Path, Resolution, Resolver, Statement, TypeCtor,
Expand Down Expand Up @@ -349,8 +351,11 @@ impl<'a, 'b, D: IrDatabase> BodyIrGenerator<'a, 'b, D> {
Pat::Bind { name } => {
let builder = self.new_alloca_builder();
let pat_ty = self.infer[pat].clone();
let ty = try_convert_any_to_basic(self.db.type_ir(pat_ty.clone()))
.expect("expected basic type");
let ty = try_convert_any_to_basic(
self.db
.type_ir(pat_ty.clone(), CodeGenParams { is_extern: false }),
)
.expect("expected basic type");
let ptr = builder.build_alloca(ty, &name.to_string());
self.pat_to_local.insert(pat, ptr);
self.pat_to_name.insert(pat, name.to_string());
Expand Down
27 changes: 19 additions & 8 deletions crates/mun_codegen/src/ir/dispatch_table.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::intrinsics;
use crate::values::FunctionValue;
use crate::IrDatabase;
use crate::{CodeGenParams, IrDatabase};
use inkwell::module::Module;
use inkwell::types::{BasicTypeEnum, FunctionType};
use inkwell::values::{BasicValueEnum, PointerValue};
Expand Down Expand Up @@ -173,13 +173,19 @@ impl<'a, D: IrDatabase> DispatchTableBuilder<'a, D> {
}

/// Collects call expression from the given expression and sub expressions.
fn collect_expr(&mut self, expr_id: ExprId, body: &Arc<Body>, infer: &InferenceResult) {
fn collect_expr(
&mut self,
expr_id: ExprId,
body: &Arc<Body>,
params: CodeGenParams,
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::Function(def)) => self.collect_fn_def(def, params.clone()),
Some(hir::CallableDef::Struct(s)) => {
if s.data(self.db).memory_kind == hir::StructMemoryKind::GC {
self.collect_intrinsic(&intrinsics::malloc)
Expand Down Expand Up @@ -212,20 +218,20 @@ impl<'a, D: IrDatabase> DispatchTableBuilder<'a, D> {
}

// Recurse further
expr.walk_child_exprs(|expr_id| self.collect_expr(expr_id, body, infer))
expr.walk_child_exprs(|expr_id| self.collect_expr(expr_id, body, params.clone(), infer))
}

/// Collects function call expression from the given expression.
#[allow(clippy::map_entry)]
fn collect_fn_def(&mut self, function: hir::Function) {
fn collect_fn_def(&mut self, function: hir::Function, params: CodeGenParams) {
self.ensure_table_ref();

// If the function is not yet contained in the table, add it
if !self.function_to_idx.contains_key(&function) {
let name = function.name(self.db).to_string();
let hir_type = function.ty(self.db);
let sig = hir_type.callable_sig(self.db).unwrap();
let ir_type = self.db.type_ir(hir_type).into_function_type();
let ir_type = self.db.type_ir(hir_type, params).into_function_type();
let arg_types = sig
.params()
.iter()
Expand Down Expand Up @@ -279,8 +285,13 @@ impl<'a, D: IrDatabase> DispatchTableBuilder<'a, D> {

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

/// This creates the final DispatchTable with all *called* functions from within the module
Expand Down
5 changes: 3 additions & 2 deletions crates/mun_codegen/src/ir/function.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::ir::body::BodyIrGenerator;
use crate::ir::dispatch_table::DispatchTable;
use crate::values::FunctionValue;
use crate::{IrDatabase, Module, OptimizationLevel};
use crate::{CodeGenParams, IrDatabase, Module, OptimizationLevel};
use inkwell::passes::{PassManager, PassManagerBuilder};
use inkwell::types::AnyTypeEnum;

Expand Down Expand Up @@ -30,9 +30,10 @@ pub(crate) fn gen_signature(
db: &impl IrDatabase,
f: hir::Function,
module: &Module,
params: CodeGenParams,
) -> FunctionValue {
let name = f.name(db).to_string();
if let AnyTypeEnum::FunctionType(ty) = db.type_ir(f.ty(db)) {
if let AnyTypeEnum::FunctionType(ty) = db.type_ir(f.ty(db), params) {
module.add_function(&name, ty, None)
} else {
panic!("not a function type")
Expand Down
14 changes: 11 additions & 3 deletions crates/mun_codegen/src/ir/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use super::adt;
use crate::ir::dispatch_table::{DispatchTable, DispatchTableBuilder};
use crate::ir::function;
use crate::type_info::TypeInfo;
use crate::IrDatabase;
use crate::{CodeGenParams, IrDatabase};
use hir::{FileId, ModuleDef};
use inkwell::{module::Module, values::FunctionValue};
use std::collections::{HashMap, HashSet};
Expand Down Expand Up @@ -64,14 +64,22 @@ pub(crate) fn ir_query(db: &impl IrDatabase, file_id: FileId) -> Arc<ModuleIR> {
types.insert(db.type_info(ret_ty.clone()));
}

let params = CodeGenParams { is_extern: false };

// Construct the function signature
let fun = function::gen_signature(db, *f, &llvm_module);
let fun = function::gen_signature(db, *f, &llvm_module, params.clone());
functions.insert(*f, fun);

// Add calls to the dispatch table
let body = f.body(db);
let infer = f.infer(db);
dispatch_table_builder.collect_body(&body, &infer);
dispatch_table_builder.collect_body(&body, params, &infer);

if f.data(db).visibility() != hir::Visibility::Private && !fn_sig.marshallable(db) {
// TODO: Generate a wrapper function that does:
// let value_result = fn_call(gc_param, *value_param, ...);
// return INTRINSIC.alloc(value_result);
}
}
_ => {}
}
Expand Down
26 changes: 17 additions & 9 deletions crates/mun_codegen/src/ir/ty.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
use super::try_convert_any_to_basic;
use crate::{
type_info::{TypeGroup, TypeInfo},
IrDatabase,
CodeGenParams, IrDatabase,
};
use hir::{ApplicationTy, CallableDef, Ty, TypeCtor};
use inkwell::types::{AnyTypeEnum, BasicType, BasicTypeEnum, StructType};
use inkwell::AddressSpace;

/// Given a mun type, construct an LLVM IR type
pub(crate) fn ir_query(db: &impl IrDatabase, ty: Ty) -> AnyTypeEnum {
pub(crate) fn ir_query(db: &impl IrDatabase, ty: Ty, params: CodeGenParams) -> AnyTypeEnum {
let context = db.context();
match ty {
Ty::Empty => AnyTypeEnum::StructType(context.struct_type(&[], false)),
Expand All @@ -18,17 +18,19 @@ pub(crate) fn ir_query(db: &impl IrDatabase, ty: Ty) -> AnyTypeEnum {
TypeCtor::Bool => AnyTypeEnum::IntType(context.bool_type()),
TypeCtor::FnDef(def @ CallableDef::Function(_)) => {
let ty = db.callable_sig(def);
let params: Vec<BasicTypeEnum> = ty
let param_tys: Vec<BasicTypeEnum> = ty
.params()
.iter()
.map(|p| try_convert_any_to_basic(db.type_ir(p.clone())).unwrap())
.map(|p| {
try_convert_any_to_basic(db.type_ir(p.clone(), params.clone())).unwrap()
})
.collect();

let fn_type = match ty.ret() {
Ty::Empty => context.void_type().fn_type(&params, false),
ty => try_convert_any_to_basic(db.type_ir(ty.clone()))
Ty::Empty => context.void_type().fn_type(&param_tys, false),
ty => try_convert_any_to_basic(db.type_ir(ty.clone(), params))
.expect("could not convert return value")
.fn_type(&params, false),
.fn_type(&param_tys, false),
};

AnyTypeEnum::FunctionType(fn_type)
Expand All @@ -37,7 +39,13 @@ pub(crate) fn ir_query(db: &impl IrDatabase, ty: Ty) -> AnyTypeEnum {
let struct_ty = db.struct_ty(s);
match s.data(db).memory_kind {
hir::StructMemoryKind::GC => struct_ty.ptr_type(AddressSpace::Generic).into(),
hir::StructMemoryKind::Value => struct_ty.into(),
hir::StructMemoryKind::Value => {
if params.is_extern() {
struct_ty.ptr_type(AddressSpace::Generic).into()
} else {
struct_ty.into()
}
}
}
}
_ => unreachable!(),
Expand All @@ -51,7 +59,7 @@ pub fn struct_ty_query(db: &impl IrDatabase, s: hir::Struct) -> StructType {
let name = s.name(db).to_string();
for field in s.fields(db).iter() {
// Ensure that salsa's cached value incorporates the struct fields
let _field_type_ir = db.type_ir(field.ty(db));
let _field_type_ir = db.type_ir(field.ty(db), CodeGenParams { is_extern: false });
}

db.context().opaque_struct_type(&name)
Expand Down
13 changes: 13 additions & 0 deletions crates/mun_codegen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,16 @@ pub use crate::{
code_gen::write_module_shared_object,
db::{IrDatabase, IrDatabaseStorage},
};

#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
pub struct CodeGenParams {
/// Whether generated code should support extern function calls.
/// This allows function parameters with `struct(value)` types to be marshalled.
is_extern: bool,
}

impl CodeGenParams {
pub fn is_extern(&self) -> bool {
self.is_extern
}
}
13 changes: 12 additions & 1 deletion crates/mun_hir/src/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ mod op;
use crate::display::{HirDisplay, HirFormatter};
use crate::ty::infer::TypeVarId;
use crate::ty::lower::fn_sig_for_struct_constructor;
use crate::{HirDatabase, Struct};
use crate::{HirDatabase, Struct, StructMemoryKind};
pub(crate) use infer::infer_query;
pub use infer::InferenceResult;
pub(crate) use lower::{callable_item_sig, fn_sig_for_fn, type_for_def, CallableDef, TypableDef};
Expand Down Expand Up @@ -172,6 +172,17 @@ impl FnSig {
pub fn ret(&self) -> &Ty {
&self.params_and_return[self.params_and_return.len() - 1]
}

pub fn marshallable(&self, db: &impl HirDatabase) -> bool {
for ty in self.params_and_return.iter() {
if let Some(s) = ty.as_struct() {
if s.data(db).memory_kind == StructMemoryKind::Value {
return false;
}
}
}
true
}
}

impl HirDisplay for Ty {
Expand Down

1 comment on commit d1868b4

@baszalmstra
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the idea of generating a wrapper is a good idea.

Please sign in to comment.