Skip to content

Commit

Permalink
Merge pull request #101 from Wodann/fix/incremental-compilation
Browse files Browse the repository at this point in the history
fix(code_gen): incremental compilation
  • Loading branch information
baszalmstra authored Mar 23, 2020
2 parents 0400406 + 8d5f1d5 commit 00cb85a
Show file tree
Hide file tree
Showing 72 changed files with 905 additions and 511 deletions.
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

0 comments on commit 00cb85a

Please sign in to comment.