From d1868b44af3151f67e4f7e6005f82d6e38395fa9 Mon Sep 17 00:00:00 2001 From: Wodann Date: Sun, 1 Mar 2020 14:52:57 +0100 Subject: [PATCH] feat(marshal): create marshallable wrapper functions for functions that contain value struct parameters or return types --- crates/mun_codegen/src/code_gen/symbols.rs | 8 +++--- crates/mun_codegen/src/db.rs | 6 ++--- crates/mun_codegen/src/ir/adt.rs | 4 +-- crates/mun_codegen/src/ir/body.rs | 11 ++++++--- crates/mun_codegen/src/ir/dispatch_table.rs | 27 +++++++++++++++------ crates/mun_codegen/src/ir/function.rs | 5 ++-- crates/mun_codegen/src/ir/module.rs | 14 ++++++++--- crates/mun_codegen/src/ir/ty.rs | 26 +++++++++++++------- crates/mun_codegen/src/lib.rs | 13 ++++++++++ crates/mun_hir/src/ty.rs | 13 +++++++++- 10 files changed, 92 insertions(+), 35 deletions(-) diff --git a/crates/mun_codegen/src/code_gen/symbols.rs b/crates/mun_codegen/src/code_gen/symbols.rs index cde1dfa91..52395ed26 100644 --- a/crates/mun_codegen/src/code_gen/symbols.rs +++ b/crates/mun_codegen/src/code_gen/symbols.rs @@ -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, @@ -318,9 +318,9 @@ fn gen_struct_info( (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(&[ diff --git a/crates/mun_codegen/src/db.rs b/crates/mun_codegen/src/db.rs index 5b911aebd..332c4ee73 100644 --- a/crates/mun_codegen/src/db.rs +++ b/crates/mun_codegen/src/db.rs @@ -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; @@ -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)] diff --git a/crates/mun_codegen/src/ir/adt.rs b/crates/mun_codegen/src/ir/adt.rs index b8c00b271..66105d831 100644 --- a/crates/mun_codegen/src/ir/adt.rs +++ b/crates/mun_codegen/src/ir/adt.rs @@ -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 { @@ -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(); diff --git a/crates/mun_codegen/src/ir/body.rs b/crates/mun_codegen/src/ir/body.rs index 3bef260ce..6e57fc1d4 100644 --- a/crates/mun_codegen/src/ir/body.rs +++ b/crates/mun_codegen/src/ir/body.rs @@ -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, @@ -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()); diff --git a/crates/mun_codegen/src/ir/dispatch_table.rs b/crates/mun_codegen/src/ir/dispatch_table.rs index 0e6af3387..ce19afde8 100644 --- a/crates/mun_codegen/src/ir/dispatch_table.rs +++ b/crates/mun_codegen/src/ir/dispatch_table.rs @@ -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}; @@ -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, infer: &InferenceResult) { + fn collect_expr( + &mut self, + expr_id: ExprId, + body: &Arc, + 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) @@ -212,12 +218,12 @@ 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 @@ -225,7 +231,7 @@ impl<'a, D: IrDatabase> DispatchTableBuilder<'a, D> { 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() @@ -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, infer: &InferenceResult) { - self.collect_expr(body.body_expr(), body, infer); + pub fn collect_body( + &mut self, + body: &Arc, + 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 diff --git a/crates/mun_codegen/src/ir/function.rs b/crates/mun_codegen/src/ir/function.rs index 357d0b92d..27081e697 100644 --- a/crates/mun_codegen/src/ir/function.rs +++ b/crates/mun_codegen/src/ir/function.rs @@ -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; @@ -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") diff --git a/crates/mun_codegen/src/ir/module.rs b/crates/mun_codegen/src/ir/module.rs index d3d54578a..75ae58e7a 100644 --- a/crates/mun_codegen/src/ir/module.rs +++ b/crates/mun_codegen/src/ir/module.rs @@ -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}; @@ -64,14 +64,22 @@ pub(crate) fn ir_query(db: &impl IrDatabase, file_id: FileId) -> Arc { 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); + } } _ => {} } diff --git a/crates/mun_codegen/src/ir/ty.rs b/crates/mun_codegen/src/ir/ty.rs index 280fe0595..08b6e54f5 100644 --- a/crates/mun_codegen/src/ir/ty.rs +++ b/crates/mun_codegen/src/ir/ty.rs @@ -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)), @@ -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 = ty + let param_tys: Vec = 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(¶ms, false), - ty => try_convert_any_to_basic(db.type_ir(ty.clone())) + Ty::Empty => context.void_type().fn_type(¶m_tys, false), + ty => try_convert_any_to_basic(db.type_ir(ty.clone(), params)) .expect("could not convert return value") - .fn_type(¶ms, false), + .fn_type(¶m_tys, false), }; AnyTypeEnum::FunctionType(fn_type) @@ -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!(), @@ -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) diff --git a/crates/mun_codegen/src/lib.rs b/crates/mun_codegen/src/lib.rs index f674be161..59df4219d 100644 --- a/crates/mun_codegen/src/lib.rs +++ b/crates/mun_codegen/src/lib.rs @@ -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 + } +} diff --git a/crates/mun_hir/src/ty.rs b/crates/mun_hir/src/ty.rs index 6f3f84cf3..0c253455a 100644 --- a/crates/mun_hir/src/ty.rs +++ b/crates/mun_hir/src/ty.rs @@ -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}; @@ -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 {