Skip to content

Commit

Permalink
refactor(codegen): introduces Inkwell lifetimes
Browse files Browse the repository at this point in the history
  • Loading branch information
baszalmstra committed Aug 22, 2020
1 parent 3a6b437 commit 21e17c0
Show file tree
Hide file tree
Showing 162 changed files with 2,348 additions and 2,028 deletions.
12 changes: 6 additions & 6 deletions .github/actions/install-llvm/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,18 @@ export async function execute(cmd) {
(async () => {
try {
if(isLinux) {
await exec.exec("sudo apt install llvm-7 llvm-7-* liblld-7*");
await exec.exec("sudo apt install llvm-8 llvm-8-* liblld-8*");
} else if(isMacOS) {
await exec.exec("brew install llvm@7")
let llvmPath = await execute("brew --prefix llvm@7");
await exec.exec("brew install llvm@8")
let llvmPath = await execute("brew --prefix llvm@8");
core.addPath(`${llvmPath}/bin`)
} else if(isWindows) {
let llvmCachedPath = tc.find("llvm", "7.1.0", "windows-x64");
let llvmCachedPath = tc.find("llvm", "8.0.1", "windows-x64");
if(!llvmCachedPath) {
let _7zPath = path.join(__dirname, '..', 'externals', '7zr.exe');
llvmCachedPath = await tc.downloadTool("https://github.com/mun-lang/llvm-package-windows/releases/download/v7.1.0/llvm-7.1.0-windows-x64-msvc15.7z")
llvmCachedPath = await tc.downloadTool("https://github.com/mun-lang/llvm-package-windows/releases/download/v8.0.1/llvm-8.0.1-windows-x64-msvc15.7z")
.then(downloadPath => tc.extract7z(downloadPath, null, _7zPath))
.then(extractPath => tc.cacheDir(extractPath, "llvm", "7.1.0", "windows-x64"));
.then(extractPath => tc.cacheDir(extractPath, "llvm", "8.0.1", "windows-x64"));
}
core.addPath(`${llvmCachedPath}/bin`)
core.exportVariable('LIBCLANG_PATH', `${llvmCachedPath}/bin`)
Expand Down
10 changes: 3 additions & 7 deletions crates/mun_codegen/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ abi = { version = "=0.2.0", path = "../mun_abi", package = "mun_abi" }
hir = { version = "=0.2.0", path = "../mun_hir", package = "mun_hir" }
mun_codegen_macros = { path = "../mun_codegen_macros", package = "mun_codegen_macros" }
mun_target = { version = "=0.2.0", path = "../mun_target" }
mun_lld = { version = "=70.2.0", path = "../mun_lld" }
mun_lld = { version = "=80.0.0", path = "../mun_lld" }
anyhow = "1.0.31"
thiserror = "1.0.19"
salsa = "0.15.0"
Expand All @@ -26,12 +26,8 @@ array-init="0.1.0"
tempfile = "3"
paste = "0.1.6"
parking_lot = "0.10"
by_address = "1.0"

[dependencies.inkwell]
git = "https://github.com/mun-lang/inkwell"
rev = "4448bcd"
features = ["llvm7-0"]
inkwell = { version = "=0.1.0-llvm8sample", features = ["llvm8-0"]}
by_address = "1.0.4"

[dev-dependencies]
insta = "0.16"
Expand Down
20 changes: 16 additions & 4 deletions crates/mun_codegen/src/assembly.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
use crate::{IrDatabase, ModuleBuilder};
use crate::db::{CodeGenContext, CodeGenDatabase};
use crate::ModuleBuilder;
use inkwell::context::Context;
use std::path::Path;
use std::sync::Arc;
use tempfile::NamedTempFile;

/// An `Assembly` is a reference to a Mun library stored on disk.
#[derive(Debug)]
pub struct Assembly {
file: NamedTempFile,
Expand Down Expand Up @@ -30,14 +33,23 @@ impl Assembly {
}
}

/// Create a new temporary file that contains the linked object
pub fn assembly_query(db: &dyn IrDatabase, file_id: hir::FileId) -> Arc<Assembly> {
/// Builds an assembly for the specified file
pub fn build_assembly(db: &dyn CodeGenDatabase, file_id: hir::FileId) -> Arc<Assembly> {
// Construct a for the assembly
let file = NamedTempFile::new().expect("could not create temp file for shared object");

let module_builder = ModuleBuilder::new(db, file_id).expect("could not create ModuleBuilder");
// Setup the code generation context
let inkwell_context = Context::create();
let code_gen_context = CodeGenContext::new(&inkwell_context, db);

// Construct the module
let module_builder =
ModuleBuilder::new(&code_gen_context, file_id).expect("could not create ModuleBuilder");
let obj_file = module_builder
.build()
.expect("unable to create object file");

// Translate the object file into a shared object
obj_file
.into_shared_object(file.path())
.expect("could not link object file");
Expand Down
113 changes: 39 additions & 74 deletions crates/mun_codegen/src/code_gen.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
use crate::code_gen::linker::LinkerError;
use crate::db::StructMapping;
use crate::db::CodeGenContext;
use crate::ir::file::gen_file_ir;
use crate::ir::file_group::gen_file_group_ir;
use crate::value::{IrTypeContext, IrValueContext};
use crate::IrDatabase;
use hir::FileId;
use inkwell::targets::TargetData;
use inkwell::module::Linkage;
use inkwell::{
module::Module,
passes::{PassManager, PassManagerBuilder},
targets::{CodeModel, FileType, InitializationConfig, RelocMode, Target, TargetMachine},
targets::{FileType, TargetMachine},
OptimizationLevel,
};
use mun_target::spec;
use parking_lot::RwLock;
use std::collections::HashMap;
use std::io::{self, Write};
use std::{path::Path, sync::Arc};
use std::path::Path;
use tempfile::NamedTempFile;
use thiserror::Error;

Expand All @@ -27,10 +26,6 @@ enum CodeGenerationError {
LinkerError(#[source] LinkerError),
#[error("error linking modules: {0}")]
ModuleLinkerError(String),
#[error("unknown target triple: {0}")]
UnknownTargetTriple(String),
#[error("error creating target machine")]
CouldNotCreateTargetMachine,
#[error("error creating object file")]
CouldNotCreateObjectFile(io::Error),
#[error("error generating machine code")]
Expand All @@ -53,7 +48,7 @@ impl ObjectFile {
pub fn new(
target: &spec::Target,
target_machine: &TargetMachine,
module: Arc<inkwell::module::Module>,
module: &inkwell::module::Module,
) -> Result<Self, anyhow::Error> {
let obj = target_machine
.write_to_memory_buffer(&module, FileType::Object)
Expand Down Expand Up @@ -86,58 +81,33 @@ impl ObjectFile {
}

/// A struct that can be used to build an LLVM `Module`.
pub struct ModuleBuilder<'a> {
db: &'a dyn IrDatabase,
pub struct ModuleBuilder<'db, 'ink, 'ctx> {
code_gen: &'ctx CodeGenContext<'db, 'ink>,
file_id: FileId,
_target: inkwell::targets::Target,
target_machine: inkwell::targets::TargetMachine,
assembly_module: Arc<inkwell::module::Module>,
assembly_module: Module<'ink>,
}

impl<'a> ModuleBuilder<'a> {
impl<'db, 'ink, 'ctx> ModuleBuilder<'db, 'ink, 'ctx> {
/// Constructs module for the given `hir::FileId` at the specified output file location.
pub fn new(db: &'a dyn IrDatabase, file_id: FileId) -> Result<Self, anyhow::Error> {
let target = db.target();

pub fn new(
code_gen: &'ctx CodeGenContext<'db, 'ink>,
file_id: FileId,
) -> Result<Self, anyhow::Error> {
// Construct a module for the assembly
let assembly_module = Arc::new(
db.context()
.create_module(db.file_relative_path(file_id).as_str()),
);

// Initialize the x86 target
Target::initialize_x86(&InitializationConfig::default());

// Retrieve the LLVM target using the specified target.
let llvm_target = Target::from_triple(&target.llvm_target)
.map_err(|e| CodeGenerationError::UnknownTargetTriple(e.to_string()))?;
assembly_module.set_target(&llvm_target);

// Construct target machine for machine code generation
let target_machine = llvm_target
.create_target_machine(
&target.llvm_target,
&target.options.cpu,
&target.options.features,
db.optimization_lvl(),
RelocMode::PIC,
CodeModel::Default,
)
.ok_or(CodeGenerationError::CouldNotCreateTargetMachine)?;
let assembly_name = code_gen.db.file_relative_path(file_id);
let assembly_module = code_gen.create_module(assembly_name);

Ok(Self {
db,
code_gen,
file_id,
_target: llvm_target,
target_machine,
assembly_module,
})
}

/// Constructs an object file.
pub fn build(self) -> Result<ObjectFile, anyhow::Error> {
let group_ir = self.db.group_ir(self.file_id);
let file = self.db.file_ir(self.file_id);
let group_ir = gen_file_group_ir(self.code_gen, self.file_id);
let file = gen_file_ir(self.code_gen, &group_ir, self.file_id);

// Clone the LLVM modules so that we can modify it without modifying the cached value.
self.assembly_module
Expand All @@ -148,12 +118,19 @@ impl<'a> ModuleBuilder<'a> {
.link_in_module(file.llvm_module.clone())
.map_err(|e| CodeGenerationError::ModuleLinkerError(e.to_string()))?;

let target_data = self.db.target_data();
let struct_types = self.db.type_to_struct_mapping();
// Add the useless `_fltused` symbol to indicate that the object file supports
// floating-point values. This is required for Windows.
let fltused =
self.assembly_module
.add_global(self.code_gen.context.i32_type(), None, "_fltused");
fltused.set_initializer(&self.code_gen.context.i32_type().const_int(1, false));
fltused.set_linkage(Linkage::External);

let target_data = self.code_gen.target_machine.get_target_data();
let type_context = IrTypeContext {
context: &self.assembly_module.get_context(),
target_data: target_data.as_ref(),
struct_types: struct_types.as_ref(),
context: &self.code_gen.context,
target_data: &target_data,
struct_types: &self.code_gen.rust_types,
};

let value_context = IrValueContext {
Expand All @@ -164,23 +141,25 @@ impl<'a> ModuleBuilder<'a> {

// Generate the `get_info` method.
symbols::gen_reflection_ir(
self.db,
self.code_gen.db,
&value_context,
&file.api,
&group_ir.dispatch_table,
&group_ir.type_table,
&self.code_gen.hir_types,
self.code_gen.optimization_level,
);

// Optimize the assembly module
optimize_module(&self.assembly_module, self.db.optimization_lvl());
optimize_module(&self.assembly_module, self.code_gen.optimization_level);

// Debug print the IR
//println!("{}", assembly_module.print_to_string().to_string());

ObjectFile::new(
&self.db.target(),
&self.target_machine,
self.assembly_module,
&self.code_gen.db.target(),
&self.code_gen.target_machine,
&self.assembly_module,
)
}
}
Expand All @@ -195,17 +174,3 @@ fn optimize_module(module: &Module, optimization_lvl: OptimizationLevel) {
pass_builder.populate_module_pass_manager(&module_pass_manager);
module_pass_manager.run_on(module);
}

/// Create an inkwell TargetData from the target in the database
pub(crate) fn target_data_query(db: &dyn IrDatabase) -> Arc<TargetData> {
Arc::new(TargetData::create(&db.target().data_layout))
}

/// Returns a mapping from struct type to a struct type in the context. This is a query because the
/// value of struct type depends on the target we compile for.
pub(crate) fn type_to_struct_mapping_query(
db: &dyn IrDatabase,
) -> by_address::ByAddress<Arc<StructMapping>> {
let _ = db.target_data();
by_address::ByAddress(Arc::new(RwLock::new(HashMap::default())))
}
Loading

0 comments on commit 21e17c0

Please sign in to comment.