Skip to content

Commit

Permalink
Merge pull request #300 from baszalmstra/feat/runtime_linking
Browse files Browse the repository at this point in the history
feat: runtime linking
  • Loading branch information
Wodann authored Mar 9, 2021
2 parents 4ca2f58 + 8d65f0c commit 1663f99
Show file tree
Hide file tree
Showing 182 changed files with 2,286 additions and 1,679 deletions.
39 changes: 18 additions & 21 deletions crates/mun/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,12 @@ where
Arg::with_name("manifest-path")
.long("manifest-path")
.takes_value(true)
.help(&format!("Path to {}", MANIFEST_FILENAME))
)
.arg(
Arg::with_name("watch")
.long("watch")
.help("Run the compiler in watch mode.\
Watch input files and trigger recompilation on changes.",)
.help(&format!("Path to {}", MANIFEST_FILENAME)),
)
.arg(Arg::with_name("watch").long("watch").help(
"Run the compiler in watch mode.\
Watch input files and trigger recompilation on changes.",
))
.arg(
Arg::with_name("opt-level")
.short("O")
Expand All @@ -71,7 +69,7 @@ where
.arg(
Arg::with_name("emit-ir")
.long("emit-ir")
.help("emits IR instead of a *.munlib")
.help("emits IR instead of a *.munlib"),
)
.about("Compiles a local Mun file into a module"),
)
Expand All @@ -88,25 +86,24 @@ where
.long("entry")
.takes_value(true)
.help("the function entry point to call on startup"),
)
.arg(
Arg::with_name("delay")
.long("delay")
.takes_value(true)
.help("how much to delay received filesystem events (in ms). This allows bundling of identical events, e.g. when several writes to the same file are detected. A high delay will make hot reloading less responsive. (defaults to 10 ms)"),
),
)
.subcommand(
SubCommand::with_name("new")
.arg(Arg::with_name("path").help("the path to create a new project").required(true).index(1))
)
.subcommand(
SubCommand::with_name("init")
.arg(Arg::with_name("path").help("the path to create a new project [default: .]").index(1))
SubCommand::with_name("new").arg(
Arg::with_name("path")
.help("the path to create a new project")
.required(true)
.index(1),
),
)
.subcommand(
SubCommand::with_name("language-server")
SubCommand::with_name("init").arg(
Arg::with_name("path")
.help("the path to create a new project [default: .]")
.index(1),
),
)
.subcommand(SubCommand::with_name("language-server"))
.get_matches_from_safe(args);

match matches {
Expand Down
11 changes: 1 addition & 10 deletions crates/mun/src/ops/start.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
use std::cell::RefCell;
use std::rc::Rc;
use std::time::Duration;
use std::{cell::RefCell, rc::Rc};

use anyhow::anyhow;
use clap::ArgMatches;
Expand Down Expand Up @@ -57,12 +55,5 @@ fn runtime(matches: &ArgMatches) -> Result<Rc<RefCell<Runtime>>, anyhow::Error>
matches.value_of("LIBRARY").unwrap(), // Safe because its a required arg
);

let builder = if let Some(delay) = matches.value_of("delay") {
let delay: u64 = delay.parse()?;
builder.set_delay(Duration::from_millis(delay))
} else {
builder
};

builder.spawn()
}
3 changes: 2 additions & 1 deletion crates/mun_codegen/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ keywords = ["game", "hot-reloading", "language", "mun", "scripting"]
categories = ["Game development", "Mun"]

[dependencies]
rustc-hash = "1.1.0"
abi = { version = "=0.2.0", path = "../mun_abi", package = "mun_abi" }
bytemuck = "1.4.1"
hir = { version = "=0.2.0", path = "../mun_hir", package = "mun_hir" }
Expand All @@ -30,14 +31,14 @@ paste = "0.1.6"
parking_lot = "0.10"
inkwell = { version = "=0.1.0-beta.2", features = ["llvm11-0", "no-libffi-linking"]}
by_address = "1.0.4"
paths = {path="../mun_paths", package="mun_paths"}

[dev-dependencies]
abi = { path = "../mun_abi", package = "mun_abi" }
insta = "0.16"
libloader = { path = "../mun_libloader", package = "mun_libloader" }
mun_test = { path = "../mun_test" }
runtime = { path = "../mun_runtime", package = "mun_runtime" }
paths = {path="../mun_paths", package="mun_paths"}

[build-dependencies]
semver = "0.9.0"
Expand Down
26 changes: 16 additions & 10 deletions crates/mun_codegen/src/assembly.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::{
code_gen::{CodeGenContext, ModuleBuilder, ObjectFile},
code_gen::{AssemblyBuilder, CodeGenContext, ObjectFile},
db::CodeGenDatabase,
ModuleGroupId,
};
use anyhow::anyhow;
use inkwell::context::Context;
Expand Down Expand Up @@ -41,12 +42,14 @@ impl<'db, 'ink, 'ctx> Assembly<'db, 'ink, 'ctx> {

/// Builds an assembly for the specified file
fn build_assembly<'db, 'ink, 'ctx>(
code_gen_context: &'ctx CodeGenContext<'db, 'ink>,
module: hir::Module,
db: &'db dyn CodeGenDatabase,
code_gen: &'ctx CodeGenContext<'db, 'ink>,
module_group_id: ModuleGroupId,
) -> Assembly<'db, 'ink, 'ctx> {
let module_builder =
ModuleBuilder::new(code_gen_context, module).expect("could not create ModuleBuilder");
// Setup the code generation context
let module_partition = db.module_partition();

let module_builder = AssemblyBuilder::new(&code_gen, &module_partition, module_group_id);
module_builder.build().expect("unable to create assembly")
}

Expand Down Expand Up @@ -81,14 +84,14 @@ impl TargetAssembly {
/// Builds an assembly for the specified module.
pub(crate) fn build_target_assembly(
db: &dyn CodeGenDatabase,
module: hir::Module,
module_group: ModuleGroupId,
) -> Arc<TargetAssembly> {
// Setup the code generation context
let inkwell_context = Context::create();
let code_gen_context = CodeGenContext::new(&inkwell_context, db);

// Build an assembly for the module
let assembly = build_assembly(&code_gen_context, module);
let assembly = build_assembly(db, &code_gen_context, module_group);

// Convert the assembly into an object file
let obj_file = assembly
Expand Down Expand Up @@ -135,13 +138,16 @@ impl AssemblyIR {
}

/// Builds an IR file for the specified module.
pub(crate) fn build_assembly_ir(db: &dyn CodeGenDatabase, module: hir::Module) -> Arc<AssemblyIR> {
pub(crate) fn build_assembly_ir(
db: &dyn CodeGenDatabase,
module_group: ModuleGroupId,
) -> Arc<AssemblyIR> {
// Setup the code generation context
let inkwell_context = Context::create();
let code_gen_context = CodeGenContext::new(&inkwell_context, db);

// Build an assembly for the file
let assembly = build_assembly(&code_gen_context, module);
// Build an assembly for the module
let assembly = build_assembly(db, &code_gen_context, module_group);

// Construct a temporary file for the assembly
let file = NamedTempFile::new().expect("could not create temp file for shared object");
Expand Down
12 changes: 6 additions & 6 deletions crates/mun_codegen/src/code_gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@ use inkwell::{
OptimizationLevel,
};

pub use assembly_builder::AssemblyBuilder;
pub use context::CodeGenContext;
pub use error::CodeGenerationError;
pub(crate) use object_file::ObjectFile;

mod assembly_builder;
mod context;
mod error;
mod module_builder;
mod object_file;
pub mod symbols;

pub use context::CodeGenContext;
pub use error::CodeGenerationError;
pub use module_builder::ModuleBuilder;
pub(crate) use object_file::ObjectFile;

/// Optimizes the specified LLVM `Module` using the default passes for the given
/// `OptimizationLevel`.
fn optimize_module(module: &Module, optimization_lvl: OptimizationLevel) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,40 +3,43 @@ use crate::{
code_gen::{optimize_module, symbols, CodeGenContext, CodeGenerationError},
ir::{file::gen_file_ir, file_group::gen_file_group_ir},
value::{IrTypeContext, IrValueContext},
ModuleGroupId, ModulePartition,
};
use inkwell::module::{Linkage, Module};
use rustc_hash::FxHashSet;

/// A struct that can be used to build an LLVM `Module`.
pub struct ModuleBuilder<'db, 'ink, 'ctx> {
/// A struct that can be used to build an `Assembly<'db, 'ink', ctx>`
pub struct AssemblyBuilder<'db, 'ink, 'ctx, 't> {
code_gen: &'ctx CodeGenContext<'db, 'ink>,
module: hir::Module,
module_group_partition: &'t ModulePartition,
module_group_id: ModuleGroupId,
assembly_module: Module<'ink>,
}

impl<'db, 'ink, 'ctx> ModuleBuilder<'db, 'ink, 'ctx> {
/// Constructs a module for the given `hir::FileId` using the provided `CodeGenContext`.
pub fn new(
impl<'db, 'ink, 'ctx, 't> AssemblyBuilder<'db, 'ink, 'ctx, 't> {
/// Constructs a new `AssemblyBuilder` for the given module group.
pub(crate) fn new(
code_gen: &'ctx CodeGenContext<'db, 'ink>,
module: hir::Module,
) -> Result<Self, anyhow::Error> {
module_group_partition: &'t ModulePartition,
module_group_id: ModuleGroupId,
) -> Self {
// Construct a module for the assembly
let file_id = module
.file_id(code_gen.db)
.expect("module must have a file");
let assembly_module =
code_gen.create_module(code_gen.db.file_relative_path(file_id).as_str());
let module_group = &module_group_partition[module_group_id];
let assembly_module = code_gen.create_module(&module_group.name);

Ok(Self {
Self {
code_gen,
module,
module_group_id,
assembly_module,
})
module_group_partition,
}
}

/// Constructs an object file.
pub fn build(self) -> Result<Assembly<'db, 'ink, 'ctx>, anyhow::Error> {
let group_ir = gen_file_group_ir(self.code_gen, self.module);
let file = gen_file_ir(self.code_gen, &group_ir, self.module);
let module_group = &self.module_group_partition[self.module_group_id];
let group_ir = gen_file_group_ir(self.code_gen, module_group);
let file = gen_file_ir(self.code_gen, &group_ir, module_group);

// Clone the LLVM modules so that we can modify it without modifying the cached value.
self.assembly_module
Expand Down Expand Up @@ -70,6 +73,20 @@ impl<'db, 'ink, 'ctx> ModuleBuilder<'db, 'ink, 'ctx> {
module: &self.assembly_module,
};

// Build the set of dependencies
let dependencies = group_ir
.referenced_modules
.iter()
.filter_map(|&module| self.module_group_partition.group_for_module(module))
.collect::<FxHashSet<_>>()
.into_iter()
.map(|group_id| {
self.module_group_partition[group_id]
.relative_file_path()
.to_string()
})
.collect();

// Generate the `get_info` method.
symbols::gen_reflection_ir(
self.code_gen.db,
Expand All @@ -79,6 +96,7 @@ impl<'db, 'ink, 'ctx> ModuleBuilder<'db, 'ink, 'ctx> {
&group_ir.type_table,
&self.code_gen.hir_types,
self.code_gen.optimization_level,
dependencies,
);

// Optimize the assembly module
Expand Down
43 changes: 35 additions & 8 deletions crates/mun_codegen/src/code_gen/symbols.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ use crate::{
value::{AsValue, CanInternalize, Global, IrValueContext, IterAsIrValue, Value},
};
use hir::{HirDatabase, Ty};
use inkwell::{attributes::Attribute, module::Linkage, AddressSpace};
use inkwell::{attributes::Attribute, module::Linkage};
use std::convert::TryFrom;
use std::{collections::HashSet, ffi::CString};

/// Construct a `MunFunctionPrototype` struct for the specified HIR function.
Expand All @@ -21,7 +22,7 @@ fn gen_prototype_from_function<'ink>(
hir_types: &HirTypeCache,
) -> ir::FunctionPrototype<'ink> {
let module = context.module;
let name = function.name(db).to_string();
let name = function.full_name(db);

// Internalize the name of the function prototype
let name_str = CString::new(name.clone())
Expand Down Expand Up @@ -120,8 +121,12 @@ fn gen_signature_return_type_from_type_info<'ink>(
) -> Value<'ink, *const ir::TypeInfo<'ink>> {
ret_type
.map(|info| {
TypeTable::get(context.module, &info, context)
.expect("could not find TypeInfo that should definitely be there")
TypeTable::get(context.module, &info, context).unwrap_or_else(|| {
panic!(
"could not find TypeInfo that should definitely be there: {}",
info.name
)
})
})
.unwrap_or_else(|| Value::null(context))
}
Expand Down Expand Up @@ -200,6 +205,7 @@ fn gen_dispatch_table<'ink>(
/// Constructs IR that exposes the types and symbols in the specified module. A function called
/// `get_info` is constructed that returns a struct `MunAssemblyInfo`. See the `mun_abi` crate
/// for the ABI that `get_info` exposes.
#[allow(clippy::too_many_arguments)]
pub(super) fn gen_reflection_ir<'db, 'ink>(
db: &'db dyn HirDatabase,
context: &IrValueContext<'ink, '_, '_>,
Expand All @@ -208,6 +214,7 @@ pub(super) fn gen_reflection_ir<'db, 'ink>(
type_table: &TypeTable<'ink>,
hir_types: &HirTypeCache<'db, 'ink>,
optimization_level: inkwell::OptimizationLevel,
dependencies: Vec<String>,
) {
let module = context.module;

Expand Down Expand Up @@ -235,7 +242,14 @@ pub(super) fn gen_reflection_ir<'db, 'ink>(
let dispatch_table = gen_dispatch_table(context, dispatch_table);

// Construct the actual `get_info` function
gen_get_info_fn(db, context, module_info, dispatch_table, optimization_level);
gen_get_info_fn(
db,
context,
module_info,
dispatch_table,
optimization_level,
dependencies,
);
gen_set_allocator_handle_fn(context);
gen_get_version_fn(context);
}
Expand All @@ -247,9 +261,9 @@ fn gen_get_info_fn<'ink>(
module_info: ir::ModuleInfo<'ink>,
dispatch_table: ir::DispatchTable<'ink>,
optimization_level: inkwell::OptimizationLevel,
dependencies: Vec<String>,
) {
let target = db.target();
let str_type = context.context.i8_type().ptr_type(AddressSpace::Generic);

// Construct the return type of the `get_info` method. Depending on the C ABI this is either the
// `MunAssemblyInfo` struct or void. On windows the return argument is passed back to the caller
Expand Down Expand Up @@ -319,11 +333,24 @@ fn gen_get_info_fn<'ink>(
builder.build_store(dispatch_table_addr, dispatch_table.as_value(context).value);
builder.build_store(
dependencies_addr,
str_type.ptr_type(AddressSpace::Generic).const_null(),
dependencies
.iter()
.enumerate()
.map(|(idx, name)| {
CString::new(name.as_str())
.expect("could not convert dependency name to string")
.intern(format!("dependency{}", idx), context)
.as_value(context)
})
.into_const_private_pointer_or_null("dependencies", context)
.value,
);
builder.build_store(
num_dependencies_addr,
context.context.i32_type().const_int(0u64, false),
context.context.i32_type().const_int(
u32::try_from(dependencies.len()).expect("too many dependencies") as u64,
false,
),
);

// Construct the return statement of the function.
Expand Down
Loading

0 comments on commit 1663f99

Please sign in to comment.