mirror of
https://github.com/roc-lang/roc.git
synced 2024-11-10 18:08:55 +03:00
code to link LLVM IR
This commit is contained in:
parent
c8018a12fc
commit
2b13bdfd52
106
cli/src/build.rs
106
cli/src/build.rs
@ -44,7 +44,7 @@ pub fn build_file<'a>(
|
||||
let compilation_start = SystemTime::now();
|
||||
let ptr_bytes = target.pointer_width().unwrap().bytes() as u32;
|
||||
|
||||
// Step 1: compile the app and generate the .o file
|
||||
// Step 1: compile the app
|
||||
let subs_by_module = MutMap::default();
|
||||
|
||||
// Release builds use uniqueness optimizations
|
||||
@ -64,6 +64,51 @@ pub fn build_file<'a>(
|
||||
)?;
|
||||
|
||||
let path_to_platform = loaded.platform_path.clone();
|
||||
|
||||
// Step 2: build the host
|
||||
let cwd = roc_file_path.parent().unwrap();
|
||||
let mut host_input_path = PathBuf::from(cwd);
|
||||
|
||||
host_input_path.push(&*path_to_platform);
|
||||
use roc_build::link::HostBuildMode;
|
||||
let host_build_mode = match opt_level {
|
||||
OptLevel::Normal => {
|
||||
if true {
|
||||
host_input_path.push("host.o");
|
||||
HostBuildMode::ObjectFile
|
||||
} else {
|
||||
host_input_path.push("host.bc");
|
||||
HostBuildMode::LLVMBitcode
|
||||
}
|
||||
}
|
||||
OptLevel::Optimize => {
|
||||
if false {
|
||||
host_input_path.push("host.o");
|
||||
HostBuildMode::ObjectFile
|
||||
} else {
|
||||
host_input_path.push("host.bc");
|
||||
HostBuildMode::LLVMBitcode
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// TODO we should no longer need to do this once we have platforms on
|
||||
// a package repository, as we can then get precompiled hosts from there.
|
||||
let rebuild_host_start = SystemTime::now();
|
||||
rebuild_host(host_input_path.as_path(), host_build_mode);
|
||||
let rebuild_host_end = rebuild_host_start.elapsed().unwrap();
|
||||
|
||||
if emit_debug_info {
|
||||
println!(
|
||||
"Finished rebuilding the host in {} ms\n",
|
||||
rebuild_host_end.as_millis()
|
||||
);
|
||||
}
|
||||
|
||||
dbg!("built host");
|
||||
|
||||
// step 3: generate the .o file
|
||||
|
||||
let app_o_file = Builder::new()
|
||||
.prefix("roc_app")
|
||||
.suffix(".o")
|
||||
@ -114,7 +159,6 @@ pub fn build_file<'a>(
|
||||
}
|
||||
}
|
||||
|
||||
let cwd = roc_file_path.parent().unwrap();
|
||||
let binary_path = cwd.join(&*loaded.output_path); // TODO should join ".exe" on Windows
|
||||
let code_gen_timing = program::gen_from_mono_module(
|
||||
&arena,
|
||||
@ -122,9 +166,14 @@ pub fn build_file<'a>(
|
||||
&roc_file_path,
|
||||
Triple::host(),
|
||||
&app_o_file,
|
||||
match host_build_mode {
|
||||
HostBuildMode::ObjectFile => None,
|
||||
HostBuildMode::LLVMBitcode => Some(host_input_path.as_path()),
|
||||
},
|
||||
opt_level,
|
||||
emit_debug_info,
|
||||
);
|
||||
dbg!("code gen done");
|
||||
|
||||
buf.push('\n');
|
||||
buf.push_str(" ");
|
||||
@ -158,38 +207,35 @@ pub fn build_file<'a>(
|
||||
);
|
||||
}
|
||||
|
||||
// Step 2: link the precompiled host and compiled app
|
||||
let mut host_input_path = PathBuf::from(cwd);
|
||||
|
||||
host_input_path.push(&*path_to_platform);
|
||||
host_input_path.push("host.o");
|
||||
|
||||
// TODO we should no longer need to do this once we have platforms on
|
||||
// a package repository, as we can then get precompiled hosts from there.
|
||||
let rebuild_host_start = SystemTime::now();
|
||||
rebuild_host(host_input_path.as_path());
|
||||
let rebuild_host_end = rebuild_host_start.elapsed().unwrap();
|
||||
|
||||
if emit_debug_info {
|
||||
println!(
|
||||
"Finished rebuilding the host in {} ms\n",
|
||||
rebuild_host_end.as_millis()
|
||||
);
|
||||
}
|
||||
|
||||
// Step 3: link them together
|
||||
// TODO try to move as much of this linking as possible to the precompiled
|
||||
// host, to minimize the amount of host-application linking required.
|
||||
let link_start = SystemTime::now();
|
||||
let (mut child, binary_path) = // TODO use lld
|
||||
link(
|
||||
target,
|
||||
binary_path,
|
||||
&[host_input_path.as_path().to_str().unwrap(), app_o_file.to_str().unwrap()],
|
||||
link_type
|
||||
)
|
||||
.map_err(|_| {
|
||||
let (mut child, binary_path) = {
|
||||
// TODO use lld
|
||||
|
||||
let linked = match host_build_mode {
|
||||
HostBuildMode::ObjectFile => link(
|
||||
target,
|
||||
binary_path,
|
||||
&[
|
||||
dbg!(host_input_path.as_path().to_str().unwrap()),
|
||||
app_o_file.to_str().unwrap(),
|
||||
],
|
||||
link_type,
|
||||
),
|
||||
HostBuildMode::LLVMBitcode => link(
|
||||
target,
|
||||
binary_path,
|
||||
&[app_o_file.to_str().unwrap()],
|
||||
link_type,
|
||||
),
|
||||
};
|
||||
|
||||
linked.map_err(|_| {
|
||||
todo!("gracefully handle `rustc` failing to spawn.");
|
||||
})?;
|
||||
})?
|
||||
};
|
||||
|
||||
let cmd_result = child.wait().map_err(|_| {
|
||||
todo!("gracefully handle error after `rustc` spawned");
|
||||
|
@ -63,14 +63,18 @@ fn build_zig_host(
|
||||
zig_host_src: &str,
|
||||
zig_str_path: &str,
|
||||
) -> Output {
|
||||
// "zig" "build-obj" "/home/folkertdev/roc/roc/examples/quicksort/platform/host.zig" "-femit-bin=/home/folkertdev/roc/roc/examples/quicksort/platform/host.o" "--pkg-begin" "str" "compiler/builtins/bitcode/src/str.zig" "--pkg-end" "-fcompiler-rt" "--library" "c"
|
||||
// "-femit-llvm-ir=/home/folkertdev/roc/roc/examples/quicksort/platform/host.ll",
|
||||
Command::new("zig")
|
||||
.env_clear()
|
||||
.env("PATH", env_path)
|
||||
.env("HOME", env_home)
|
||||
.args(&[
|
||||
"build-obj",
|
||||
"build-lib",
|
||||
zig_host_src,
|
||||
emit_bin,
|
||||
"-O",
|
||||
"ReleaseFast",
|
||||
"--pkg-begin",
|
||||
"str",
|
||||
zig_str_path,
|
||||
@ -158,7 +162,13 @@ fn build_zig_host(
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn rebuild_host(host_input_path: &Path) {
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum HostBuildMode {
|
||||
ObjectFile,
|
||||
LLVMBitcode,
|
||||
}
|
||||
|
||||
pub fn rebuild_host(host_input_path: &Path, host_build_mode: HostBuildMode) {
|
||||
let c_host_src = host_input_path.with_file_name("host.c");
|
||||
let c_host_dest = host_input_path.with_file_name("c_host.o");
|
||||
let zig_host_src = host_input_path.with_file_name("host.zig");
|
||||
@ -172,7 +182,17 @@ pub fn rebuild_host(host_input_path: &Path) {
|
||||
|
||||
if zig_host_src.exists() {
|
||||
// Compile host.zig
|
||||
let emit_bin = format!("-femit-bin={}", host_dest.to_str().unwrap());
|
||||
let zig_host_dest = match host_build_mode {
|
||||
HostBuildMode::LLVMBitcode => host_input_path.with_file_name("host.ll"),
|
||||
HostBuildMode::ObjectFile => host_input_path.with_file_name("host.o"),
|
||||
};
|
||||
|
||||
let emit_bin = match host_build_mode {
|
||||
HostBuildMode::LLVMBitcode => {
|
||||
format!("-femit-llvm-ir={}", zig_host_dest.to_str().unwrap())
|
||||
}
|
||||
HostBuildMode::ObjectFile => format!("-femit-bin={}", zig_host_dest.to_str().unwrap()),
|
||||
};
|
||||
|
||||
let zig_str_path = find_zig_str_path();
|
||||
|
||||
@ -193,6 +213,19 @@ pub fn rebuild_host(host_input_path: &Path) {
|
||||
zig_str_path.to_str().unwrap(),
|
||||
),
|
||||
);
|
||||
|
||||
if let HostBuildMode::LLVMBitcode = host_build_mode {
|
||||
let bc = host_input_path.with_file_name("host.bc");
|
||||
|
||||
let output = Command::new("llvm-as")
|
||||
.env_clear()
|
||||
.env("PATH", &env_path)
|
||||
.args(&[zig_host_dest.to_str().unwrap(), "-o", bc.to_str().unwrap()])
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
validate_output("host.zig", "llvm-as", output);
|
||||
}
|
||||
} else {
|
||||
// Compile host.c
|
||||
let output = Command::new("clang")
|
||||
|
@ -19,13 +19,14 @@ pub struct CodeGenTiming {
|
||||
// TODO how should imported modules factor into this? What if those use builtins too?
|
||||
// TODO this should probably use more helper functions
|
||||
// TODO make this polymorphic in the llvm functions so it can be reused for another backend.
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
#[allow(clippy::cognitive_complexity, clippy::clippy::too_many_arguments)]
|
||||
pub fn gen_from_mono_module(
|
||||
arena: &Bump,
|
||||
mut loaded: MonomorphizedModule,
|
||||
roc_file_path: &Path,
|
||||
target: Triple,
|
||||
app_o_file: &Path,
|
||||
host_llvm_ir: Option<&Path>,
|
||||
opt_level: OptLevel,
|
||||
emit_debug_info: bool,
|
||||
) -> CodeGenTiming {
|
||||
@ -191,6 +192,25 @@ pub fn gen_from_mono_module(
|
||||
// Uncomment this to see the module's optimized LLVM instruction output:
|
||||
// env.module.print_to_stderr();
|
||||
|
||||
if let Some(host_llvm_ir) = host_llvm_ir {
|
||||
let host_module =
|
||||
inkwell::module::Module::parse_bitcode_from_path(host_llvm_ir, &context).unwrap();
|
||||
|
||||
// host_module.link_in_module(module.clone()).unwrap();
|
||||
module.link_in_module(host_module).unwrap();
|
||||
|
||||
// Verify the module
|
||||
if let Err(errors) = env.module.verify() {
|
||||
// write the ll code to a file, so we can modify it
|
||||
env.module.print_to_file(&app_ll_file).unwrap();
|
||||
|
||||
panic!(
|
||||
"😱 LLVM errors linking in the HOST MODULE; I wrote the full LLVM IR to {:?}\n\n {:?}",
|
||||
app_ll_file, errors,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
mpm.run_on(module);
|
||||
|
||||
// Verify the module
|
||||
|
Loading…
Reference in New Issue
Block a user