Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
26 changes: 17 additions & 9 deletions crates/mun_codegen/src/code_gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ pub mod symbols;
enum CodeGenerationError {
#[fail(display = "{}", 0)]
LinkerError(#[fail(cause)] LinkerError),
#[fail(display = "error linking modules: {}", 0)]
ModuleLinkerError(String),
#[fail(display = "unknown target triple: {}", 0)]
UnknownTargetTriple(String),
#[fail(display = "error creating target machine")]
Expand All @@ -40,6 +42,7 @@ impl From<LinkerError> for CodeGenerationError {
}
}

/// A struct that can be used to build an LLVM `Module`.
pub struct ModuleBuilder<'a, D: IrDatabase> {
db: &'a D,
file_id: FileId,
Expand Down Expand Up @@ -79,9 +82,6 @@ impl<'a, D: IrDatabase> ModuleBuilder<'a, D> {
)
.ok_or(CodeGenerationError::CouldNotCreateTargetMachine)?;

// Initialize the module and target data
db.set_module(assembly_module.clone());

Ok(Self {
db,
file_id,
Expand All @@ -93,17 +93,25 @@ impl<'a, D: IrDatabase> ModuleBuilder<'a, D> {

/// Construct a shared object at the specified output file location.
pub fn finalize(&self, out_dir: Option<&Path>) -> Result<PathBuf, failure::Error> {
// Generate IR for the module and clone it so that we can modify it without modifying the
// cached value.
let module = self.db.module_ir(self.file_id);
let group_ir = self.db.group_ir(self.file_id);
let file = self.db.file_ir(self.file_id);

// Clone the LLVM modules so that we can modify it without modifying the cached value.
self.assembly_module
.link_in_module(group_ir.llvm_module.clone())
.map_err(|e| CodeGenerationError::ModuleLinkerError(e.to_string()))?;

self.assembly_module
.link_in_module(file.llvm_module.clone())
.map_err(|e| CodeGenerationError::ModuleLinkerError(e.to_string()))?;

// Generate the `get_info` method.
symbols::gen_reflection_ir(
self.db,
&self.assembly_module,
&module.functions,
&module.dispatch_table,
&module.type_table,
&file.api,
&group_ir.dispatch_table,
&group_ir.type_table,
);

// Optimize the assembly module
Expand Down
78 changes: 44 additions & 34 deletions crates/mun_codegen/src/code_gen/symbols.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,16 @@ use hir::Ty;
use inkwell::{
attributes::Attribute,
module::{Linkage, Module},
values::{FunctionValue, GlobalValue, PointerValue, StructValue},
values::{GlobalValue, PointerValue, StructValue},
AddressSpace,
};
use std::collections::HashMap;
use std::collections::HashSet;

/// Construct a `MunFunctionSignature` struct for the specified HIR function.
fn gen_signature_from_function<D: IrDatabase>(
db: &D,
module: &Module,
types: &AbiTypes,
type_table: &TypeTable,
function: hir::Function,
) -> StructValue {
let name = function.name(db).to_string();
Expand All @@ -33,12 +32,16 @@ fn gen_signature_from_function<D: IrDatabase>(
};

let fn_sig = function.ty(db).callable_sig(db).unwrap();
let ret_type_ir = gen_signature_return_type(db, types, type_table, fn_sig.ret().clone());
let ret_type_ir = gen_signature_return_type(db, module, types, fn_sig.ret().clone());

let param_types: Vec<PointerValue> = fn_sig
.params()
.iter()
.map(|ty| type_table.get(&db.type_info(ty.clone())).unwrap())
.map(|ty| {
TypeTable::get(module, &db.type_info(ty.clone()))
.unwrap()
.as_pointer_value()
})
.collect();

let param_types = gen_struct_ptr_array(
Expand All @@ -63,11 +66,9 @@ fn gen_signature_from_function<D: IrDatabase>(
}

/// Construct a `MunFunctionSignature` struct for the specified dispatch table function.
fn gen_signature_from_dispatch_entry<D: IrDatabase>(
db: &D,
fn gen_signature_from_dispatch_entry(
module: &Module,
types: &AbiTypes,
type_table: &TypeTable,
function: &DispatchableFunction,
) -> StructValue {
let name_str = intern_string(
Expand All @@ -80,16 +81,19 @@ fn gen_signature_from_dispatch_entry<D: IrDatabase>(
// _ => 1,
// };
let ret_type_ir = gen_signature_return_type_from_type_info(
db,
module,
types,
type_table,
function.prototype.ret_type.clone(),
);
let param_types: Vec<PointerValue> = function
.prototype
.arg_types
.iter()
.map(|type_info| type_table.get(type_info).unwrap())
.map(|type_info| {
TypeTable::get(module, type_info)
.unwrap()
.as_pointer_value()
})
.collect();
let param_types = gen_struct_ptr_array(
module,
Expand All @@ -116,14 +120,13 @@ fn gen_signature_from_dispatch_entry<D: IrDatabase>(
/// of the function; or `null` if the return type is empty.
fn gen_signature_return_type<D: IrDatabase>(
db: &D,
module: &Module,
types: &AbiTypes,
type_table: &TypeTable,
ret_type: Ty,
) -> PointerValue {
gen_signature_return_type_from_type_info(
db,
module,
types,
type_table,
if ret_type.is_empty() {
None
} else {
Expand All @@ -134,14 +137,15 @@ fn gen_signature_return_type<D: IrDatabase>(

/// Given a function, construct a pointer to a `MunTypeInfo` global that represents the return type
/// of the function; or `null` if the return type is empty.
fn gen_signature_return_type_from_type_info<D: IrDatabase>(
_db: &D,
fn gen_signature_return_type_from_type_info(
module: &Module,
types: &AbiTypes,
type_table: &TypeTable,
ret_type: Option<TypeInfo>,
) -> PointerValue {
if let Some(ret_type) = ret_type {
type_table.get(&ret_type).unwrap()
TypeTable::get(module, &ret_type)
.unwrap()
.as_pointer_value()
} else {
types
.type_info_type
Expand All @@ -156,19 +160,22 @@ fn gen_function_info_array<'a, D: IrDatabase>(
db: &D,
module: &Module,
types: &AbiTypes,
type_table: &TypeTable,
functions: impl Iterator<Item = (&'a hir::Function, &'a FunctionValue)>,
functions: impl Iterator<Item = &'a hir::Function>,
) -> GlobalValue {
let function_infos: Vec<StructValue> = functions
.map(|(f, value)| {
.map(|f| {
let name = f.name(db).to_string();
// Get the function from the cloned module and modify the linkage of the function.
let value = module
.get_function(value.get_name().to_str().unwrap())
// If a wrapper function exists, use that (required for struct types)
.get_function(&format!("{}_wrapper", name))
// Otherwise, use the normal function
.or_else(|| module.get_function(&name))
.unwrap();
value.set_linkage(Linkage::Private);

// Generate the signature from the function
let signature = gen_signature_from_function(db, module, types, type_table, *f);
let signature = gen_signature_from_function(db, module, types, *f);

// Generate the function info value
types.function_info_type.const_named_struct(&[
Expand All @@ -185,18 +192,16 @@ fn gen_function_info_array<'a, D: IrDatabase>(
/// ```c
/// MunDispatchTable dispatchTable = { ... }
/// ```
fn gen_dispatch_table<D: IrDatabase>(
db: &D,
fn gen_dispatch_table(
module: &Module,
types: &AbiTypes,
dispatch_table: &DispatchTable,
type_table: &TypeTable,
) -> StructValue {
// Generate a vector with all the function signatures
let signatures: Vec<StructValue> = dispatch_table
.entries()
.iter()
.map(|entry| gen_signature_from_dispatch_entry(db, module, types, type_table, entry))
.map(|entry| gen_signature_from_dispatch_entry(module, types, entry))
.collect();

// Construct an IR array from the signatures
Expand Down Expand Up @@ -242,16 +247,21 @@ fn gen_dispatch_table<D: IrDatabase>(
pub(super) fn gen_reflection_ir(
db: &impl IrDatabase,
module: &Module,
function_map: &HashMap<hir::Function, FunctionValue>,
api: &HashSet<hir::Function>,
dispatch_table: &DispatchTable,
type_table: &TypeTable,
) {
// Get all the types
let abi_types = gen_abi_types(module.get_context());
let abi_types = gen_abi_types(&module.get_context());

let num_functions = api.len();
let function_info = gen_function_info_array(db, module, &abi_types, api.iter());

let num_functions = function_map.len();
let function_info =
gen_function_info_array(db, module, &abi_types, type_table, function_map.iter());
let type_table_ir = if let Some(type_table) = module.get_global("type_table_global") {
type_table.as_pointer_value()
} else {
type_table.ty().ptr_type(AddressSpace::Const).const_null()
};

// Construct the module info struct
let module_info = abi_types.module_info_type.const_named_struct(&[
Expand All @@ -262,7 +272,7 @@ pub(super) fn gen_reflection_ir(
.i32_type()
.const_int(num_functions as u64, false)
.into(),
type_table.pointer_value().into(),
type_table_ir.into(),
module
.get_context()
.i32_type()
Expand All @@ -271,7 +281,7 @@ pub(super) fn gen_reflection_ir(
]);

// Construct the dispatch table struct
let dispatch_table = gen_dispatch_table(db, module, &abi_types, dispatch_table, type_table);
let dispatch_table = gen_dispatch_table(module, &abi_types, dispatch_table);

// Construct the actual `get_info` function
gen_get_info_fn(db, module, &abi_types, module_info, dispatch_table);
Expand Down
21 changes: 13 additions & 8 deletions crates/mun_codegen/src/db.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
#![allow(clippy::type_repetition_in_bounds)]

use crate::{ir::module::ModuleIR, type_info::TypeInfo, CodeGenParams, Context};
use crate::{
ir::{file::FileIR, file_group::FileGroupIR},
type_info::TypeInfo,
CodeGenParams, Context,
};
use inkwell::{
module::Module,
targets::TargetData,
types::{AnyTypeEnum, StructType},
OptimizationLevel,
Expand All @@ -18,10 +21,6 @@ pub trait IrDatabase: hir::HirDatabase {
#[salsa::input]
fn context(&self) -> Arc<Context>;

/// Returns the LLVM module that should be used for all generation steps.
#[salsa::input]
fn module(&self) -> Arc<Module>;

/// Gets the optimization level for generation.
#[salsa::input]
fn optimization_lvl(&self) -> OptimizationLevel;
Expand All @@ -42,9 +41,15 @@ pub trait IrDatabase: hir::HirDatabase {
#[salsa::invoke(crate::ir::ty::struct_ty_query)]
fn struct_ty(&self, s: hir::Struct) -> StructType;

/// Given a `hir::FileId` generate code that is shared among the group of files.
/// TODO: Currently, a group always consists of a single file. Need to add support for multiple
/// files using something like `FileGroupId`.
#[salsa::invoke(crate::ir::file_group::ir_query)]
fn group_ir(&self, file: hir::FileId) -> Arc<FileGroupIR>;

/// Given a `hir::FileId` generate code for the module.
#[salsa::invoke(crate::ir::module::ir_query)]
fn module_ir(&self, file: hir::FileId) -> Arc<ModuleIR>;
#[salsa::invoke(crate::ir::file::ir_query)]
fn file_ir(&self, file: hir::FileId) -> Arc<FileIR>;

/// Given a type, return the runtime `TypeInfo` that can be used to reflect the type.
#[salsa::invoke(crate::ir::ty::type_info_query)]
Expand Down
3 changes: 2 additions & 1 deletion crates/mun_codegen/src/ir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ pub mod adt;
pub mod body;
#[macro_use]
pub(crate) mod dispatch_table;
pub mod file;
pub(crate) mod file_group;
pub mod function;
mod intrinsics;
pub mod module;
pub mod ty;
pub(crate) mod type_table;

Expand Down
5 changes: 3 additions & 2 deletions crates/mun_codegen/src/ir/abi_types.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use inkwell::context::ContextRef;
use inkwell::context::Context;
use inkwell::types::{ArrayType, IntType, StructType};
use inkwell::AddressSpace;

#[derive(Debug, PartialEq, Eq)]
pub(crate) struct AbiTypes {
pub guid_type: ArrayType,
pub type_group_type: IntType,
Expand All @@ -16,7 +17,7 @@ pub(crate) struct AbiTypes {
}

/// Returns an `AbiTypes` struct that contains references to all LLVM ABI types.
pub(crate) fn gen_abi_types(context: ContextRef) -> AbiTypes {
pub(crate) fn gen_abi_types(context: &Context) -> AbiTypes {
let str_type = context.i8_type().ptr_type(AddressSpace::Const);

// Construct the `MunGuid` type
Expand Down
Loading