mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-01 07:41:12 +00:00
Merge branch 'trunk' into convert-bitcode-gen-to-c
This commit is contained in:
commit
21d4f8026e
18 changed files with 360 additions and 266 deletions
6
Cargo.lock
generated
6
Cargo.lock
generated
|
@ -2217,6 +2217,7 @@ dependencies = [
|
||||||
"indoc",
|
"indoc",
|
||||||
"inkwell",
|
"inkwell",
|
||||||
"inlinable_string",
|
"inlinable_string",
|
||||||
|
"libloading",
|
||||||
"maplit",
|
"maplit",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"quickcheck",
|
"quickcheck",
|
||||||
|
@ -2238,7 +2239,7 @@ dependencies = [
|
||||||
"roc_unify",
|
"roc_unify",
|
||||||
"roc_uniq",
|
"roc_uniq",
|
||||||
"target-lexicon",
|
"target-lexicon",
|
||||||
"tokio",
|
"tempfile",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -2291,6 +2292,7 @@ dependencies = [
|
||||||
"inkwell",
|
"inkwell",
|
||||||
"inlinable_string",
|
"inlinable_string",
|
||||||
"libc",
|
"libc",
|
||||||
|
"libloading",
|
||||||
"maplit",
|
"maplit",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"quickcheck",
|
"quickcheck",
|
||||||
|
@ -2425,10 +2427,12 @@ dependencies = [
|
||||||
"inkwell",
|
"inkwell",
|
||||||
"inlinable_string",
|
"inlinable_string",
|
||||||
"libc",
|
"libc",
|
||||||
|
"libloading",
|
||||||
"maplit",
|
"maplit",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"quickcheck",
|
"quickcheck",
|
||||||
"quickcheck_macros",
|
"quickcheck_macros",
|
||||||
|
"roc_build",
|
||||||
"roc_builtins",
|
"roc_builtins",
|
||||||
"roc_can",
|
"roc_can",
|
||||||
"roc_collections",
|
"roc_collections",
|
||||||
|
|
|
@ -58,6 +58,7 @@ bumpalo = { version = "3.2", features = ["collections"] }
|
||||||
inlinable_string = "0.1"
|
inlinable_string = "0.1"
|
||||||
tokio = { version = "0.2", features = ["blocking", "fs", "sync", "rt-threaded", "process", "io-driver"] }
|
tokio = { version = "0.2", features = ["blocking", "fs", "sync", "rt-threaded", "process", "io-driver"] }
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
|
libloading = "0.6"
|
||||||
|
|
||||||
# NOTE: rtfeldman/inkwell is a fork of TheDan64/inkwell which does not change anything.
|
# NOTE: rtfeldman/inkwell is a fork of TheDan64/inkwell which does not change anything.
|
||||||
#
|
#
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use roc_build::{link::link, program};
|
use roc_build::{
|
||||||
|
link::{link, rebuild_host, LinkType},
|
||||||
|
program,
|
||||||
|
};
|
||||||
use roc_collections::all::MutMap;
|
use roc_collections::all::MutMap;
|
||||||
use roc_gen::llvm::build::OptLevel;
|
use roc_gen::llvm::build::OptLevel;
|
||||||
use roc_load::file::LoadingProblem;
|
use roc_load::file::LoadingProblem;
|
||||||
|
@ -19,8 +22,9 @@ fn report_timing(buf: &mut String, label: &str, duration: Duration) {
|
||||||
pub fn build_file(
|
pub fn build_file(
|
||||||
target: &Triple,
|
target: &Triple,
|
||||||
src_dir: PathBuf,
|
src_dir: PathBuf,
|
||||||
filename: PathBuf,
|
roc_file_path: PathBuf,
|
||||||
opt_level: OptLevel,
|
opt_level: OptLevel,
|
||||||
|
link_type: LinkType,
|
||||||
) -> Result<PathBuf, LoadingProblem> {
|
) -> Result<PathBuf, LoadingProblem> {
|
||||||
let compilation_start = SystemTime::now();
|
let compilation_start = SystemTime::now();
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
|
@ -35,12 +39,12 @@ pub fn build_file(
|
||||||
};
|
};
|
||||||
let loaded = roc_load::file::load_and_monomorphize(
|
let loaded = roc_load::file::load_and_monomorphize(
|
||||||
&arena,
|
&arena,
|
||||||
filename.clone(),
|
roc_file_path.clone(),
|
||||||
stdlib,
|
stdlib,
|
||||||
src_dir.as_path(),
|
src_dir.as_path(),
|
||||||
subs_by_module,
|
subs_by_module,
|
||||||
)?;
|
)?;
|
||||||
let dest_filename = filename.with_file_name("roc_app.o");
|
let app_o_file = roc_file_path.with_file_name("roc_app.o");
|
||||||
let buf = &mut String::with_capacity(1024);
|
let buf = &mut String::with_capacity(1024);
|
||||||
|
|
||||||
for (module_id, module_timing) in loaded.timings.iter() {
|
for (module_id, module_timing) in loaded.timings.iter() {
|
||||||
|
@ -69,12 +73,14 @@ pub fn build_file(
|
||||||
program::gen_from_mono_module(
|
program::gen_from_mono_module(
|
||||||
&arena,
|
&arena,
|
||||||
loaded,
|
loaded,
|
||||||
filename,
|
roc_file_path,
|
||||||
Triple::host(),
|
Triple::host(),
|
||||||
&dest_filename,
|
&app_o_file,
|
||||||
opt_level,
|
opt_level,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
println!("\nSuccess! 🎉\n\n\t➡ {}\n", app_o_file.display());
|
||||||
|
|
||||||
let compilation_end = compilation_start.elapsed().unwrap();
|
let compilation_end = compilation_start.elapsed().unwrap();
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
|
@ -82,33 +88,37 @@ pub fn build_file(
|
||||||
compilation_end.as_millis()
|
compilation_end.as_millis()
|
||||||
);
|
);
|
||||||
|
|
||||||
let cwd = dest_filename.parent().unwrap();
|
let cwd = app_o_file.parent().unwrap();
|
||||||
|
|
||||||
// Step 2: link the precompiled host and compiled app
|
// Step 2: link the precompiled host and compiled app
|
||||||
let host_input_path = cwd.join("platform").join("host.o");
|
let host_input_path = cwd.join("platform").join("host.o");
|
||||||
let binary_path = cwd.join("app"); // TODO should be app.exe on Windows
|
let binary_path = cwd.join("app"); // TODO should be app.exe on Windows
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
rebuild_host(host_input_path.as_path());
|
||||||
|
|
||||||
// TODO try to move as much of this linking as possible to the precompiled
|
// TODO try to move as much of this linking as possible to the precompiled
|
||||||
// host, to minimize the amount of host-application linking required.
|
// host, to minimize the amount of host-application linking required.
|
||||||
let cmd_result = // TODO use lld
|
let (mut child, binary_path) = // TODO use lld
|
||||||
link(
|
link(
|
||||||
target,
|
target,
|
||||||
binary_path.as_path(),
|
binary_path,
|
||||||
host_input_path.as_path(),
|
&[host_input_path.as_path().to_str().unwrap(), app_o_file.as_path().to_str().unwrap()],
|
||||||
dest_filename.as_path(),
|
link_type
|
||||||
)
|
)
|
||||||
.map_err(|_| {
|
.map_err(|_| {
|
||||||
todo!("gracefully handle `rustc` failing to spawn.");
|
todo!("gracefully handle `rustc` failing to spawn.");
|
||||||
})?
|
})?;
|
||||||
.wait()
|
|
||||||
.map_err(|_| {
|
let cmd_result = child.wait().map_err(|_| {
|
||||||
todo!("gracefully handle error after `rustc` spawned");
|
todo!("gracefully handle error after `rustc` spawned");
|
||||||
});
|
});
|
||||||
|
|
||||||
// Clean up the leftover .o file from the Roc, if possible.
|
// Clean up the leftover .o file from the Roc, if possible.
|
||||||
// (If cleaning it up fails, that's fine. No need to take action.)
|
// (If cleaning it up fails, that's fine. No need to take action.)
|
||||||
// TODO compile the dest_filename to a tmpdir, as an extra precaution.
|
// TODO compile the app_o_file to a tmpdir, as an extra precaution.
|
||||||
let _ = fs::remove_file(dest_filename);
|
let _ = fs::remove_file(app_o_file);
|
||||||
|
|
||||||
// If the cmd errored out, return the Err.
|
// If the cmd errored out, return the Err.
|
||||||
cmd_result?;
|
cmd_result?;
|
||||||
|
|
|
@ -3,6 +3,7 @@ extern crate clap;
|
||||||
|
|
||||||
use clap::ArgMatches;
|
use clap::ArgMatches;
|
||||||
use clap::{App, Arg};
|
use clap::{App, Arg};
|
||||||
|
use roc_build::link::LinkType;
|
||||||
use roc_gen::llvm::build::OptLevel;
|
use roc_gen::llvm::build::OptLevel;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
@ -91,7 +92,7 @@ pub fn build(target: &Triple, matches: &ArgMatches, run_after_build: bool) -> io
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let binary_path = build::build_file(target, src_dir, path, opt_level)
|
let binary_path = build::build_file(target, src_dir, path, opt_level, LinkType::Executable)
|
||||||
.expect("TODO gracefully handle build_file failing");
|
.expect("TODO gracefully handle build_file failing");
|
||||||
|
|
||||||
if run_after_build {
|
if run_after_build {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use inkwell::context::Context;
|
use inkwell::context::Context;
|
||||||
use inkwell::execution_engine::ExecutionEngine;
|
use roc_build::link::module_to_dylib;
|
||||||
use inkwell::OptimizationLevel;
|
|
||||||
use roc_builtins::unique::uniq_stdlib;
|
use roc_builtins::unique::uniq_stdlib;
|
||||||
use roc_can::constraint::Constraint;
|
use roc_can::constraint::Constraint;
|
||||||
use roc_can::expected::Expected;
|
use roc_can::expected::Expected;
|
||||||
|
@ -284,14 +283,6 @@ fn gen(src: &[u8], target: Triple, opt_level: OptLevel) -> Result<ReplOutput, Fa
|
||||||
let (module_pass, function_pass) =
|
let (module_pass, function_pass) =
|
||||||
roc_gen::llvm::build::construct_optimization_passes(module, opt_level);
|
roc_gen::llvm::build::construct_optimization_passes(module, opt_level);
|
||||||
|
|
||||||
let execution_engine = module
|
|
||||||
.create_jit_execution_engine(OptimizationLevel::None)
|
|
||||||
.expect("Error creating JIT execution engine for test");
|
|
||||||
|
|
||||||
// Without calling this, we get a linker error when building this crate
|
|
||||||
// in --release mode and then trying to eval anything in the repl.
|
|
||||||
ExecutionEngine::link_in_mc_jit();
|
|
||||||
|
|
||||||
// Compile and add all the Procs before adding main
|
// Compile and add all the Procs before adding main
|
||||||
let env = roc_gen::llvm::build::Env {
|
let env = roc_gen::llvm::build::Env {
|
||||||
arena: &arena,
|
arena: &arena,
|
||||||
|
@ -386,10 +377,12 @@ fn gen(src: &[u8], target: Triple, opt_level: OptLevel) -> Result<ReplOutput, Fa
|
||||||
// Uncomment this to see the module's optimized LLVM instruction output:
|
// Uncomment this to see the module's optimized LLVM instruction output:
|
||||||
// env.module.print_to_stderr();
|
// env.module.print_to_stderr();
|
||||||
|
|
||||||
|
let lib = module_to_dylib(&env.module, &target, opt_level)
|
||||||
|
.expect("Error loading compiled dylib for test");
|
||||||
let answer = unsafe {
|
let answer = unsafe {
|
||||||
eval::jit_to_ast(
|
eval::jit_to_ast(
|
||||||
&arena,
|
&arena,
|
||||||
execution_engine,
|
lib,
|
||||||
main_fn_name,
|
main_fn_name,
|
||||||
&main_fn_layout,
|
&main_fn_layout,
|
||||||
&content,
|
&content,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use bumpalo::collections::Vec;
|
use bumpalo::collections::Vec;
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use inkwell::execution_engine::ExecutionEngine;
|
use libloading::Library;
|
||||||
use roc_collections::all::MutMap;
|
use roc_collections::all::MutMap;
|
||||||
use roc_gen::{run_jit_function, run_jit_function_dynamic_type};
|
use roc_gen::{run_jit_function, run_jit_function_dynamic_type};
|
||||||
use roc_module::ident::{Lowercase, TagName};
|
use roc_module::ident::{Lowercase, TagName};
|
||||||
|
@ -31,7 +31,7 @@ struct Env<'a, 'env> {
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub unsafe fn jit_to_ast<'a>(
|
pub unsafe fn jit_to_ast<'a>(
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
execution_engine: ExecutionEngine,
|
lib: Library,
|
||||||
main_fn_name: &str,
|
main_fn_name: &str,
|
||||||
layout: &Layout<'a>,
|
layout: &Layout<'a>,
|
||||||
content: &Content,
|
content: &Content,
|
||||||
|
@ -48,42 +48,43 @@ pub unsafe fn jit_to_ast<'a>(
|
||||||
interns,
|
interns,
|
||||||
};
|
};
|
||||||
|
|
||||||
jit_to_ast_help(&env, execution_engine, main_fn_name, layout, content)
|
jit_to_ast_help(&env, lib, main_fn_name, layout, content)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn jit_to_ast_help<'a>(
|
fn jit_to_ast_help<'a>(
|
||||||
env: &Env<'a, '_>,
|
env: &Env<'a, '_>,
|
||||||
execution_engine: ExecutionEngine,
|
lib: Library,
|
||||||
main_fn_name: &str,
|
main_fn_name: &str,
|
||||||
layout: &Layout<'a>,
|
layout: &Layout<'a>,
|
||||||
content: &Content,
|
content: &Content,
|
||||||
) -> Expr<'a> {
|
) -> Expr<'a> {
|
||||||
match layout {
|
match layout {
|
||||||
Layout::Builtin(Builtin::Int64) => run_jit_function!(
|
Layout::Builtin(Builtin::Int64) => {
|
||||||
execution_engine,
|
run_jit_function!(lib, main_fn_name, i64, |num| num_to_ast(
|
||||||
main_fn_name,
|
env,
|
||||||
i64,
|
i64_to_ast(env.arena, num),
|
||||||
|num| num_to_ast(env, i64_to_ast(env.arena, num), content)
|
content
|
||||||
),
|
))
|
||||||
Layout::Builtin(Builtin::Float64) => run_jit_function!(
|
}
|
||||||
execution_engine,
|
Layout::Builtin(Builtin::Float64) => {
|
||||||
main_fn_name,
|
run_jit_function!(lib, main_fn_name, f64, |num| num_to_ast(
|
||||||
f64,
|
env,
|
||||||
|num| num_to_ast(env, f64_to_ast(env.arena, num), content)
|
f64_to_ast(env.arena, num),
|
||||||
),
|
content
|
||||||
Layout::Builtin(Builtin::Str) | Layout::Builtin(Builtin::EmptyStr) => run_jit_function!(
|
))
|
||||||
execution_engine,
|
}
|
||||||
main_fn_name,
|
Layout::Builtin(Builtin::Str) | Layout::Builtin(Builtin::EmptyStr) => {
|
||||||
&'static str,
|
run_jit_function!(lib, main_fn_name, &'static str, |string: &'static str| {
|
||||||
|string: &'static str| { str_to_ast(env.arena, env.arena.alloc(string)) }
|
str_to_ast(env.arena, env.arena.alloc(string))
|
||||||
),
|
})
|
||||||
|
}
|
||||||
Layout::Builtin(Builtin::EmptyList) => {
|
Layout::Builtin(Builtin::EmptyList) => {
|
||||||
run_jit_function!(execution_engine, main_fn_name, &'static str, |_| {
|
run_jit_function!(lib, main_fn_name, &'static str, |_| {
|
||||||
Expr::List(Vec::new_in(env.arena))
|
Expr::List(Vec::new_in(env.arena))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Layout::Builtin(Builtin::List(_, elem_layout)) => run_jit_function!(
|
Layout::Builtin(Builtin::List(_, elem_layout)) => run_jit_function!(
|
||||||
execution_engine,
|
lib,
|
||||||
main_fn_name,
|
main_fn_name,
|
||||||
(*const libc::c_void, usize),
|
(*const libc::c_void, usize),
|
||||||
|(ptr, len): (*const libc::c_void, usize)| {
|
|(ptr, len): (*const libc::c_void, usize)| {
|
||||||
|
@ -111,31 +112,21 @@ fn jit_to_ast_help<'a>(
|
||||||
8 => match layout.stack_size(env.ptr_bytes) {
|
8 => match layout.stack_size(env.ptr_bytes) {
|
||||||
8 => {
|
8 => {
|
||||||
// just one eightbyte, returned as-is
|
// just one eightbyte, returned as-is
|
||||||
run_jit_function!(
|
run_jit_function!(lib, main_fn_name, [u8; 8], |bytes: [u8; 8]| {
|
||||||
execution_engine,
|
|
||||||
main_fn_name,
|
|
||||||
[u8; 8],
|
|
||||||
|bytes: [u8; 8]| {
|
|
||||||
ptr_to_ast((&bytes).as_ptr() as *const libc::c_void)
|
ptr_to_ast((&bytes).as_ptr() as *const libc::c_void)
|
||||||
}
|
})
|
||||||
)
|
|
||||||
}
|
}
|
||||||
16 => {
|
16 => {
|
||||||
// two eightbytes, returned as-is
|
// two eightbytes, returned as-is
|
||||||
run_jit_function!(
|
run_jit_function!(lib, main_fn_name, [u8; 16], |bytes: [u8; 16]| {
|
||||||
execution_engine,
|
|
||||||
main_fn_name,
|
|
||||||
[u8; 16],
|
|
||||||
|bytes: [u8; 16]| {
|
|
||||||
ptr_to_ast((&bytes).as_ptr() as *const libc::c_void)
|
ptr_to_ast((&bytes).as_ptr() as *const libc::c_void)
|
||||||
}
|
})
|
||||||
)
|
|
||||||
}
|
}
|
||||||
larger_size => {
|
larger_size => {
|
||||||
// anything more than 2 eightbytes
|
// anything more than 2 eightbytes
|
||||||
// the return "value" is a pointer to the result
|
// the return "value" is a pointer to the result
|
||||||
run_jit_function_dynamic_type!(
|
run_jit_function_dynamic_type!(
|
||||||
execution_engine,
|
lib,
|
||||||
main_fn_name,
|
main_fn_name,
|
||||||
larger_size as usize,
|
larger_size as usize,
|
||||||
|bytes: *const u8| { ptr_to_ast(bytes as *const libc::c_void) }
|
|bytes: *const u8| { ptr_to_ast(bytes as *const libc::c_void) }
|
||||||
|
@ -150,31 +141,21 @@ fn jit_to_ast_help<'a>(
|
||||||
match layout.stack_size(env.ptr_bytes) {
|
match layout.stack_size(env.ptr_bytes) {
|
||||||
4 => {
|
4 => {
|
||||||
// just one fourbyte, returned as-is
|
// just one fourbyte, returned as-is
|
||||||
run_jit_function!(
|
run_jit_function!(lib, main_fn_name, [u8; 4], |bytes: [u8; 4]| {
|
||||||
execution_engine,
|
|
||||||
main_fn_name,
|
|
||||||
[u8; 4],
|
|
||||||
|bytes: [u8; 4]| {
|
|
||||||
ptr_to_ast((&bytes).as_ptr() as *const libc::c_void)
|
ptr_to_ast((&bytes).as_ptr() as *const libc::c_void)
|
||||||
}
|
})
|
||||||
)
|
|
||||||
}
|
}
|
||||||
8 => {
|
8 => {
|
||||||
// just one fourbyte, returned as-is
|
// just one fourbyte, returned as-is
|
||||||
run_jit_function!(
|
run_jit_function!(lib, main_fn_name, [u8; 8], |bytes: [u8; 8]| {
|
||||||
execution_engine,
|
|
||||||
main_fn_name,
|
|
||||||
[u8; 8],
|
|
||||||
|bytes: [u8; 8]| {
|
|
||||||
ptr_to_ast((&bytes).as_ptr() as *const libc::c_void)
|
ptr_to_ast((&bytes).as_ptr() as *const libc::c_void)
|
||||||
}
|
})
|
||||||
)
|
|
||||||
}
|
}
|
||||||
larger_size => {
|
larger_size => {
|
||||||
// anything more than 2 fourbytes
|
// anything more than 2 fourbytes
|
||||||
// the return "value" is a pointer to the result
|
// the return "value" is a pointer to the result
|
||||||
run_jit_function_dynamic_type!(
|
run_jit_function_dynamic_type!(
|
||||||
execution_engine,
|
lib,
|
||||||
main_fn_name,
|
main_fn_name,
|
||||||
larger_size as usize,
|
larger_size as usize,
|
||||||
|bytes: *const u8| { ptr_to_ast(bytes as *const libc::c_void) }
|
|bytes: *const u8| { ptr_to_ast(bytes as *const libc::c_void) }
|
||||||
|
|
|
@ -26,7 +26,8 @@ im = "14" # im and im-rc should always have the same version!
|
||||||
im-rc = "14" # im and im-rc should always have the same version!
|
im-rc = "14" # im and im-rc should always have the same version!
|
||||||
bumpalo = { version = "3.2", features = ["collections"] }
|
bumpalo = { version = "3.2", features = ["collections"] }
|
||||||
inlinable_string = "0.1.0"
|
inlinable_string = "0.1.0"
|
||||||
tokio = { version = "0.2", features = ["blocking", "fs", "sync", "rt-threaded", "process", "io-driver"] }
|
libloading = "0.6"
|
||||||
|
tempfile = "3.1.0"
|
||||||
# NOTE: rtfeldman/inkwell is a fork of TheDan64/inkwell which does not change anything.
|
# NOTE: rtfeldman/inkwell is a fork of TheDan64/inkwell which does not change anything.
|
||||||
#
|
#
|
||||||
# The reason for this fork is that the way Inkwell is designed, you have to use
|
# The reason for this fork is that the way Inkwell is designed, you have to use
|
||||||
|
|
|
@ -1,35 +1,44 @@
|
||||||
|
use crate::target;
|
||||||
use crate::target::arch_str;
|
use crate::target::arch_str;
|
||||||
|
use inkwell::module::Module;
|
||||||
|
use inkwell::targets::{CodeModel, FileType, RelocMode};
|
||||||
|
use libloading::{Error, Library};
|
||||||
|
use roc_gen::llvm::build::OptLevel;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::path::Path;
|
use std::path::{Path, PathBuf};
|
||||||
use std::process::{Child, Command};
|
use std::process::{Child, Command};
|
||||||
use target_lexicon::{Architecture, OperatingSystem, Triple};
|
use target_lexicon::{Architecture, OperatingSystem, Triple};
|
||||||
|
use tempfile::tempdir;
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
|
pub enum LinkType {
|
||||||
|
Executable,
|
||||||
|
Dylib,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// input_paths can include the host as well as the app. e.g. &["host.o", "roc_app.o"]
|
||||||
pub fn link(
|
pub fn link(
|
||||||
target: &Triple,
|
target: &Triple,
|
||||||
binary_path: &Path,
|
output_path: PathBuf,
|
||||||
host_input_path: &Path,
|
input_paths: &[&str],
|
||||||
dest_filename: &Path,
|
link_type: LinkType,
|
||||||
) -> io::Result<Child> {
|
) -> io::Result<(Child, PathBuf)> {
|
||||||
// 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.
|
|
||||||
rebuild_host(host_input_path);
|
|
||||||
|
|
||||||
match target {
|
match target {
|
||||||
Triple {
|
Triple {
|
||||||
architecture: Architecture::X86_64,
|
architecture: Architecture::X86_64,
|
||||||
operating_system: OperatingSystem::Linux,
|
operating_system: OperatingSystem::Linux,
|
||||||
..
|
..
|
||||||
} => link_linux(target, binary_path, host_input_path, dest_filename),
|
} => link_linux(target, output_path, input_paths, link_type),
|
||||||
Triple {
|
Triple {
|
||||||
architecture: Architecture::X86_64,
|
architecture: Architecture::X86_64,
|
||||||
operating_system: OperatingSystem::Darwin,
|
operating_system: OperatingSystem::Darwin,
|
||||||
..
|
..
|
||||||
} => link_macos(target, binary_path, host_input_path, dest_filename),
|
} => link_macos(target, output_path, input_paths, link_type),
|
||||||
_ => panic!("TODO gracefully handle unsupported target: {:?}", target),
|
_ => panic!("TODO gracefully handle unsupported target: {:?}", target),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rebuild_host(host_input_path: &Path) {
|
pub fn rebuild_host(host_input_path: &Path) {
|
||||||
let c_host_src = host_input_path.with_file_name("host.c");
|
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 c_host_dest = host_input_path.with_file_name("c_host.o");
|
||||||
let rust_host_src = host_input_path.with_file_name("host.rs");
|
let rust_host_src = host_input_path.with_file_name("host.rs");
|
||||||
|
@ -118,15 +127,16 @@ fn rebuild_host(host_input_path: &Path) {
|
||||||
|
|
||||||
fn link_linux(
|
fn link_linux(
|
||||||
target: &Triple,
|
target: &Triple,
|
||||||
binary_path: &Path,
|
output_path: PathBuf,
|
||||||
host_input_path: &Path,
|
input_paths: &[&str],
|
||||||
dest_filename: &Path,
|
link_type: LinkType,
|
||||||
) -> io::Result<Child> {
|
) -> io::Result<(Child, PathBuf)> {
|
||||||
let libcrt_path = if Path::new("/usr/lib/x86_64-linux-gnu").exists() {
|
let libcrt_path = if Path::new("/usr/lib/x86_64-linux-gnu").exists() {
|
||||||
Path::new("/usr/lib/x86_64-linux-gnu")
|
Path::new("/usr/lib/x86_64-linux-gnu")
|
||||||
} else {
|
} else {
|
||||||
Path::new("/usr/lib")
|
Path::new("/usr/lib")
|
||||||
};
|
};
|
||||||
|
|
||||||
let libgcc_path = if Path::new("/lib/x86_64-linux-gnu/libgcc_s.so.1").exists() {
|
let libgcc_path = if Path::new("/lib/x86_64-linux-gnu/libgcc_s.so.1").exists() {
|
||||||
Path::new("/lib/x86_64-linux-gnu/libgcc_s.so.1")
|
Path::new("/lib/x86_64-linux-gnu/libgcc_s.so.1")
|
||||||
} else if Path::new("/usr/lib/x86_64-linux-gnu/libgcc_s.so.1").exists() {
|
} else if Path::new("/usr/lib/x86_64-linux-gnu/libgcc_s.so.1").exists() {
|
||||||
|
@ -134,8 +144,45 @@ fn link_linux(
|
||||||
} else {
|
} else {
|
||||||
Path::new("/usr/lib/libgcc_s.so.1")
|
Path::new("/usr/lib/libgcc_s.so.1")
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut soname;
|
||||||
|
let (base_args, output_path) = match link_type {
|
||||||
|
LinkType::Executable => (
|
||||||
|
// Presumably this S stands for Static, since if we include Scrt1.o
|
||||||
|
// in the linking for dynamic builds, linking fails.
|
||||||
|
vec![libcrt_path.join("Scrt1.o").to_str().unwrap().to_string()],
|
||||||
|
output_path,
|
||||||
|
),
|
||||||
|
LinkType::Dylib => {
|
||||||
|
// TODO: do we acually need the version number on this?
|
||||||
|
// Do we even need the "-soname" argument?
|
||||||
|
//
|
||||||
|
// See https://software.intel.com/content/www/us/en/develop/articles/create-a-unix-including-linux-shared-library.html
|
||||||
|
|
||||||
|
soname = output_path.clone();
|
||||||
|
soname.set_extension("so.1");
|
||||||
|
|
||||||
|
let mut output_path = output_path;
|
||||||
|
|
||||||
|
output_path.set_extension("so.1.0");
|
||||||
|
|
||||||
|
(
|
||||||
|
// TODO: find a way to avoid using a vec! here - should theoretically be
|
||||||
|
// able to do this somehow using &[] but the borrow checker isn't having it.
|
||||||
|
// Also find a way to have these be string slices instead of Strings.
|
||||||
|
vec![
|
||||||
|
"-shared".to_string(),
|
||||||
|
"-soname".to_string(),
|
||||||
|
soname.as_path().to_str().unwrap().to_string(),
|
||||||
|
],
|
||||||
|
output_path,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// NOTE: order of arguments to `ld` matters here!
|
// NOTE: order of arguments to `ld` matters here!
|
||||||
// The `-l` flags should go after the `.o` arguments
|
// The `-l` flags should go after the `.o` arguments
|
||||||
|
Ok((
|
||||||
Command::new("ld")
|
Command::new("ld")
|
||||||
// Don't allow LD_ env vars to affect this
|
// Don't allow LD_ env vars to affect this
|
||||||
.env_clear()
|
.env_clear()
|
||||||
|
@ -144,12 +191,11 @@ fn link_linux(
|
||||||
arch_str(target),
|
arch_str(target),
|
||||||
libcrt_path.join("crti.o").to_str().unwrap(),
|
libcrt_path.join("crti.o").to_str().unwrap(),
|
||||||
libcrt_path.join("crtn.o").to_str().unwrap(),
|
libcrt_path.join("crtn.o").to_str().unwrap(),
|
||||||
libcrt_path.join("Scrt1.o").to_str().unwrap(),
|
])
|
||||||
"-dynamic-linker",
|
.args(&base_args)
|
||||||
"/lib64/ld-linux-x86-64.so.2",
|
.args(&["-dynamic-linker", "/lib64/ld-linux-x86-64.so.2"])
|
||||||
// Inputs
|
.args(input_paths)
|
||||||
host_input_path.to_str().unwrap(), // host.o
|
.args(&[
|
||||||
dest_filename.to_str().unwrap(), // app.o
|
|
||||||
// Libraries - see https://github.com/rtfeldman/roc/pull/554#discussion_r496365925
|
// Libraries - see https://github.com/rtfeldman/roc/pull/554#discussion_r496365925
|
||||||
// for discussion and further references
|
// for discussion and further references
|
||||||
"-lc",
|
"-lc",
|
||||||
|
@ -164,28 +210,43 @@ fn link_linux(
|
||||||
libgcc_path.to_str().unwrap(),
|
libgcc_path.to_str().unwrap(),
|
||||||
// Output
|
// Output
|
||||||
"-o",
|
"-o",
|
||||||
binary_path.to_str().unwrap(), // app
|
output_path.as_path().to_str().unwrap(), // app (or app.so or app.dylib etc.)
|
||||||
])
|
])
|
||||||
.spawn()
|
.spawn()?,
|
||||||
|
output_path,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn link_macos(
|
fn link_macos(
|
||||||
target: &Triple,
|
target: &Triple,
|
||||||
binary_path: &Path,
|
output_path: PathBuf,
|
||||||
host_input_path: &Path,
|
input_paths: &[&str],
|
||||||
dest_filename: &Path,
|
link_type: LinkType,
|
||||||
) -> io::Result<Child> {
|
) -> io::Result<(Child, PathBuf)> {
|
||||||
|
let (link_type_arg, output_path) = match link_type {
|
||||||
|
LinkType::Executable => ("-execute", output_path),
|
||||||
|
LinkType::Dylib => {
|
||||||
|
let mut output_path = output_path;
|
||||||
|
|
||||||
|
output_path.set_extension("dylib");
|
||||||
|
|
||||||
|
("-dylib", output_path)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok((
|
||||||
// NOTE: order of arguments to `ld` matters here!
|
// NOTE: order of arguments to `ld` matters here!
|
||||||
// The `-l` flags should go after the `.o` arguments
|
// The `-l` flags should go after the `.o` arguments
|
||||||
Command::new("ld")
|
Command::new("ld")
|
||||||
// Don't allow LD_ env vars to affect this
|
// Don't allow LD_ env vars to affect this
|
||||||
.env_clear()
|
.env_clear()
|
||||||
.args(&[
|
.args(&[
|
||||||
|
link_type_arg,
|
||||||
"-arch",
|
"-arch",
|
||||||
target.architecture.to_string().as_str(),
|
target.architecture.to_string().as_str(),
|
||||||
// Inputs
|
])
|
||||||
host_input_path.to_str().unwrap(), // host.o
|
.args(input_paths)
|
||||||
dest_filename.to_str().unwrap(), // roc_app.o
|
.args(&[
|
||||||
// Libraries - see https://github.com/rtfeldman/roc/pull/554#discussion_r496392274
|
// Libraries - see https://github.com/rtfeldman/roc/pull/554#discussion_r496392274
|
||||||
// for discussion and further references
|
// for discussion and further references
|
||||||
"-lSystem",
|
"-lSystem",
|
||||||
|
@ -198,7 +259,47 @@ fn link_macos(
|
||||||
"-lc++", // TODO shouldn't we need this?
|
"-lc++", // TODO shouldn't we need this?
|
||||||
// Output
|
// Output
|
||||||
"-o",
|
"-o",
|
||||||
binary_path.to_str().unwrap(), // app
|
output_path.to_str().unwrap(), // app
|
||||||
])
|
])
|
||||||
.spawn()
|
.spawn()?,
|
||||||
|
output_path,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn module_to_dylib(
|
||||||
|
module: &Module,
|
||||||
|
target: &Triple,
|
||||||
|
opt_level: OptLevel,
|
||||||
|
) -> Result<Library, Error> {
|
||||||
|
let dir = tempdir().unwrap();
|
||||||
|
let filename = PathBuf::from("Test.roc");
|
||||||
|
let file_path = dir.path().join(filename);
|
||||||
|
let mut app_o_file = file_path;
|
||||||
|
|
||||||
|
app_o_file.set_file_name("app.o");
|
||||||
|
|
||||||
|
// Emit the .o file using position-indepedent code (PIC) - needed for dylibs
|
||||||
|
let reloc = RelocMode::PIC;
|
||||||
|
let model = CodeModel::Default;
|
||||||
|
let target_machine = target::target_machine(target, opt_level.into(), reloc, model).unwrap();
|
||||||
|
|
||||||
|
target_machine
|
||||||
|
.write_to_file(module, FileType::Object, &app_o_file)
|
||||||
|
.expect("Writing .o file failed");
|
||||||
|
|
||||||
|
// Link app.o into a dylib - e.g. app.so or app.dylib
|
||||||
|
let (mut child, dylib_path) = link(
|
||||||
|
&Triple::host(),
|
||||||
|
app_o_file.clone(),
|
||||||
|
&[app_o_file.to_str().unwrap()],
|
||||||
|
LinkType::Dylib,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
child.wait().unwrap();
|
||||||
|
|
||||||
|
// Load the dylib
|
||||||
|
let path = dylib_path.as_path().to_str().unwrap();
|
||||||
|
|
||||||
|
Library::new(path)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ use crate::target;
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use inkwell::context::Context;
|
use inkwell::context::Context;
|
||||||
use inkwell::targets::{CodeModel, FileType, RelocMode};
|
use inkwell::targets::{CodeModel, FileType, RelocMode};
|
||||||
use inkwell::OptimizationLevel;
|
|
||||||
use roc_gen::layout_id::LayoutIds;
|
use roc_gen::layout_id::LayoutIds;
|
||||||
use roc_gen::llvm::build::{build_proc, build_proc_header, module_from_builtins, OptLevel, Scope};
|
use roc_gen::llvm::build::{build_proc, build_proc_header, module_from_builtins, OptLevel, Scope};
|
||||||
use roc_load::file::MonomorphizedModule;
|
use roc_load::file::MonomorphizedModule;
|
||||||
|
@ -16,9 +15,9 @@ use target_lexicon::Triple;
|
||||||
pub fn gen_from_mono_module(
|
pub fn gen_from_mono_module(
|
||||||
arena: &Bump,
|
arena: &Bump,
|
||||||
loaded: MonomorphizedModule,
|
loaded: MonomorphizedModule,
|
||||||
filename: PathBuf,
|
file_path: PathBuf,
|
||||||
target: Triple,
|
target: Triple,
|
||||||
dest_filename: &Path,
|
app_o_file: &Path,
|
||||||
opt_level: OptLevel,
|
opt_level: OptLevel,
|
||||||
) {
|
) {
|
||||||
use roc_reporting::report::{can_problem, type_problem, RocDocAllocator, DEFAULT_PALETTE};
|
use roc_reporting::report::{can_problem, type_problem, RocDocAllocator, DEFAULT_PALETTE};
|
||||||
|
@ -32,7 +31,7 @@ pub fn gen_from_mono_module(
|
||||||
let alloc = RocDocAllocator::new(&src_lines, home, &loaded.interns);
|
let alloc = RocDocAllocator::new(&src_lines, home, &loaded.interns);
|
||||||
|
|
||||||
for problem in loaded.can_problems.into_iter() {
|
for problem in loaded.can_problems.into_iter() {
|
||||||
let report = can_problem(&alloc, filename.clone(), problem);
|
let report = can_problem(&alloc, file_path.clone(), problem);
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
|
|
||||||
report.render_color_terminal(&mut buf, &alloc, &palette);
|
report.render_color_terminal(&mut buf, &alloc, &palette);
|
||||||
|
@ -41,7 +40,7 @@ pub fn gen_from_mono_module(
|
||||||
}
|
}
|
||||||
|
|
||||||
for problem in loaded.type_problems.into_iter() {
|
for problem in loaded.type_problems.into_iter() {
|
||||||
let report = type_problem(&alloc, filename.clone(), problem);
|
let report = type_problem(&alloc, file_path.clone(), problem);
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
|
|
||||||
report.render_color_terminal(&mut buf, &alloc, &palette);
|
report.render_color_terminal(&mut buf, &alloc, &palette);
|
||||||
|
@ -126,14 +125,11 @@ pub fn gen_from_mono_module(
|
||||||
|
|
||||||
// Emit the .o file
|
// Emit the .o file
|
||||||
|
|
||||||
let opt = OptimizationLevel::Aggressive;
|
|
||||||
let reloc = RelocMode::Default;
|
let reloc = RelocMode::Default;
|
||||||
let model = CodeModel::Default;
|
let model = CodeModel::Default;
|
||||||
let target_machine = target::target_machine(&target, opt, reloc, model).unwrap();
|
let target_machine = target::target_machine(&target, opt_level.into(), reloc, model).unwrap();
|
||||||
|
|
||||||
target_machine
|
target_machine
|
||||||
.write_to_file(&env.module, FileType::Object, &dest_filename)
|
.write_to_file(&env.module, FileType::Object, &app_o_file)
|
||||||
.expect("Writing .o file failed");
|
.expect("Writing .o file failed");
|
||||||
|
|
||||||
println!("\nSuccess! 🎉\n\n\t➡ {}\n", dest_filename.display());
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use crate::builtins;
|
||||||
use crate::def::{canonicalize_defs, sort_can_defs, Declaration};
|
use crate::def::{canonicalize_defs, sort_can_defs, Declaration};
|
||||||
use crate::env::Env;
|
use crate::env::Env;
|
||||||
use crate::expr::Output;
|
use crate::expr::Output;
|
||||||
|
@ -259,6 +260,15 @@ pub fn canonicalize_module_defs<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add builtin defs (e.g. List.get) to the module's defs
|
||||||
|
let builtin_defs = builtins::builtin_defs(var_store);
|
||||||
|
|
||||||
|
for (symbol, def) in builtin_defs {
|
||||||
|
if references.contains(&symbol) {
|
||||||
|
declarations.push(Declaration::Builtin(def));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(ModuleOutput {
|
Ok(ModuleOutput {
|
||||||
aliases,
|
aliases,
|
||||||
rigid_variables,
|
rigid_variables,
|
||||||
|
|
|
@ -40,12 +40,15 @@ inlinable_string = "0.1"
|
||||||
# This way, GitHub Actions works and nobody's builds get broken.
|
# This way, GitHub Actions works and nobody's builds get broken.
|
||||||
inkwell = { git = "https://github.com/rtfeldman/inkwell", tag = "llvm10-0.release2" }
|
inkwell = { git = "https://github.com/rtfeldman/inkwell", tag = "llvm10-0.release2" }
|
||||||
target-lexicon = "0.10"
|
target-lexicon = "0.10"
|
||||||
|
libloading = "0.6"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
roc_can = { path = "../can" }
|
roc_can = { path = "../can" }
|
||||||
roc_parse = { path = "../parse" }
|
roc_parse = { path = "../parse" }
|
||||||
roc_load = { path = "../load" }
|
roc_load = { path = "../load" }
|
||||||
roc_reporting = { path = "../reporting" }
|
roc_reporting = { path = "../reporting" }
|
||||||
|
roc_build = { path = "../build" }
|
||||||
|
roc_std = { path = "../../roc_std" }
|
||||||
pretty_assertions = "0.5.1"
|
pretty_assertions = "0.5.1"
|
||||||
maplit = "1.0.1"
|
maplit = "1.0.1"
|
||||||
indoc = "0.3.3"
|
indoc = "0.3.3"
|
||||||
|
@ -54,4 +57,3 @@ quickcheck_macros = "0.8"
|
||||||
tokio = { version = "0.2", features = ["blocking", "fs", "sync", "rt-threaded"] }
|
tokio = { version = "0.2", features = ["blocking", "fs", "sync", "rt-threaded"] }
|
||||||
bumpalo = { version = "3.2", features = ["collections"] }
|
bumpalo = { version = "3.2", features = ["collections"] }
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
roc_std = { path = "../../roc_std" }
|
|
||||||
|
|
|
@ -50,6 +50,15 @@ pub enum OptLevel {
|
||||||
Optimize,
|
Optimize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Into<OptimizationLevel> for OptLevel {
|
||||||
|
fn into(self) -> OptimizationLevel {
|
||||||
|
match self {
|
||||||
|
OptLevel::Normal => OptimizationLevel::None,
|
||||||
|
OptLevel::Optimize => OptimizationLevel::Aggressive,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone, PartialEq)]
|
#[derive(Default, Debug, Clone, PartialEq)]
|
||||||
pub struct Scope<'a, 'ctx> {
|
pub struct Scope<'a, 'ctx> {
|
||||||
symbols: ImMap<Symbol, (Layout<'a>, PointerValue<'ctx>)>,
|
symbols: ImMap<Symbol, (Layout<'a>, PointerValue<'ctx>)>,
|
||||||
|
|
|
@ -28,24 +28,23 @@ impl<T: Sized> Into<Result<T, String>> for RocCallResult<T> {
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! run_jit_function {
|
macro_rules! run_jit_function {
|
||||||
($execution_engine: expr, $main_fn_name: expr, $ty:ty, $transform:expr) => {{
|
($lib: expr, $main_fn_name: expr, $ty:ty, $transform:expr) => {{
|
||||||
let v: std::vec::Vec<roc_problem::can::Problem> = std::vec::Vec::new();
|
let v: std::vec::Vec<roc_problem::can::Problem> = std::vec::Vec::new();
|
||||||
run_jit_function!($execution_engine, $main_fn_name, $ty, $transform, v)
|
run_jit_function!($lib, $main_fn_name, $ty, $transform, v)
|
||||||
}};
|
}};
|
||||||
|
|
||||||
($execution_engine: expr, $main_fn_name: expr, $ty:ty, $transform:expr, $errors:expr) => {{
|
($lib: expr, $main_fn_name: expr, $ty:ty, $transform:expr, $errors:expr) => {{
|
||||||
use inkwell::context::Context;
|
use inkwell::context::Context;
|
||||||
use inkwell::execution_engine::JitFunction;
|
|
||||||
use roc_gen::run_roc::RocCallResult;
|
use roc_gen::run_roc::RocCallResult;
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let main: JitFunction<unsafe extern "C" fn() -> RocCallResult<$ty>> = $execution_engine
|
let main: libloading::Symbol<unsafe extern "C" fn() -> RocCallResult<$ty>> = $lib
|
||||||
.get_function($main_fn_name)
|
.get($main_fn_name.as_bytes())
|
||||||
.ok()
|
.ok()
|
||||||
.ok_or(format!("Unable to JIT compile `{}`", $main_fn_name))
|
.ok_or(format!("Unable to JIT compile `{}`", $main_fn_name))
|
||||||
.expect("errored");
|
.expect("errored");
|
||||||
|
|
||||||
match main.call().into() {
|
match main().into() {
|
||||||
Ok(success) => {
|
Ok(success) => {
|
||||||
// only if there are no exceptions thrown, check for errors
|
// only if there are no exceptions thrown, check for errors
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -68,26 +67,25 @@ macro_rules! run_jit_function {
|
||||||
/// It explicitly allocates a buffer that the roc main function can write its result into.
|
/// It explicitly allocates a buffer that the roc main function can write its result into.
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! run_jit_function_dynamic_type {
|
macro_rules! run_jit_function_dynamic_type {
|
||||||
($execution_engine: expr, $main_fn_name: expr, $bytes:expr, $transform:expr) => {{
|
($lib: expr, $main_fn_name: expr, $bytes:expr, $transform:expr) => {{
|
||||||
let v: std::vec::Vec<roc_problem::can::Problem> = std::vec::Vec::new();
|
let v: std::vec::Vec<roc_problem::can::Problem> = std::vec::Vec::new();
|
||||||
run_jit_function_dynamic_type!($execution_engine, $main_fn_name, $bytes, $transform, v)
|
run_jit_function_dynamic_type!($lib, $main_fn_name, $bytes, $transform, v)
|
||||||
}};
|
}};
|
||||||
|
|
||||||
($execution_engine: expr, $main_fn_name: expr, $bytes:expr, $transform:expr, $errors:expr) => {{
|
($lib: expr, $main_fn_name: expr, $bytes:expr, $transform:expr, $errors:expr) => {{
|
||||||
use inkwell::context::Context;
|
use inkwell::context::Context;
|
||||||
use inkwell::execution_engine::JitFunction;
|
|
||||||
use roc_gen::run_roc::RocCallResult;
|
use roc_gen::run_roc::RocCallResult;
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let main: JitFunction<unsafe extern "C" fn(*const u8)> = $execution_engine
|
let main: libloading::Symbol<unsafe extern "C" fn(*const u8)> = $lib
|
||||||
.get_function($main_fn_name)
|
.get($main_fn_name.as_bytes())
|
||||||
.ok()
|
.ok()
|
||||||
.ok_or(format!("Unable to JIT compile `{}`", $main_fn_name))
|
.ok_or(format!("Unable to JIT compile `{}`", $main_fn_name))
|
||||||
.expect("errored");
|
.expect("errored");
|
||||||
|
|
||||||
let layout = std::alloc::Layout::array::<u8>($bytes).unwrap();
|
let layout = std::alloc::Layout::array::<u8>($bytes).unwrap();
|
||||||
let result = std::alloc::alloc(layout);
|
let result = std::alloc::alloc(layout);
|
||||||
main.call(result);
|
main(result);
|
||||||
|
|
||||||
let flag = *result;
|
let flag = *result;
|
||||||
|
|
||||||
|
|
|
@ -691,19 +691,19 @@ mod gen_num {
|
||||||
assert_evals_to!("Num.atan 10", 1.4711276743037345, f64);
|
assert_evals_to!("Num.atan 10", 1.4711276743037345, f64);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
// #[test]
|
||||||
#[should_panic(expected = r#"Roc failed with message: "integer addition overflowed!"#)]
|
// #[should_panic(expected = r#"Roc failed with message: "integer addition overflowed!"#)]
|
||||||
fn int_overflow() {
|
// fn int_overflow() {
|
||||||
assert_evals_to!(
|
// assert_evals_to!(
|
||||||
indoc!(
|
// indoc!(
|
||||||
r#"
|
// r#"
|
||||||
9_223_372_036_854_775_807 + 1
|
// 9_223_372_036_854_775_807 + 1
|
||||||
"#
|
// "#
|
||||||
),
|
// ),
|
||||||
0,
|
// 0,
|
||||||
i64
|
// i64
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn int_add_checked() {
|
fn int_add_checked() {
|
||||||
|
@ -775,17 +775,17 @@ mod gen_num {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
// #[test]
|
||||||
#[should_panic(expected = r#"Roc failed with message: "float addition overflowed!"#)]
|
// #[should_panic(expected = r#"Roc failed with message: "float addition overflowed!"#)]
|
||||||
fn float_overflow() {
|
// fn float_overflow() {
|
||||||
assert_evals_to!(
|
// assert_evals_to!(
|
||||||
indoc!(
|
// indoc!(
|
||||||
r#"
|
// r#"
|
||||||
1.7976931348623157e308 + 1.7976931348623157e308
|
// 1.7976931348623157e308 + 1.7976931348623157e308
|
||||||
"#
|
// "#
|
||||||
),
|
// ),
|
||||||
0.0,
|
// 0.0,
|
||||||
f64
|
// f64
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
|
@ -884,22 +884,22 @@ mod gen_primitives {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
// #[test]
|
||||||
#[should_panic(expected = "Roc failed with message: ")]
|
// #[should_panic(expected = "Roc failed with message: ")]
|
||||||
fn exception() {
|
// fn exception() {
|
||||||
assert_evals_to!(
|
// assert_evals_to!(
|
||||||
indoc!(
|
// indoc!(
|
||||||
r#"
|
// r#"
|
||||||
if True then
|
// if True then
|
||||||
x + z
|
// x + z
|
||||||
else
|
// else
|
||||||
y + z
|
// y + z
|
||||||
"#
|
// "#
|
||||||
),
|
// ),
|
||||||
3,
|
// 3,
|
||||||
i64
|
// i64
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn closure() {
|
fn closure() {
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use libloading::Library;
|
||||||
|
use roc_build::link::module_to_dylib;
|
||||||
use roc_collections::all::{MutMap, MutSet};
|
use roc_collections::all::{MutMap, MutSet};
|
||||||
|
|
||||||
fn promote_expr_to_module(src: &str) -> String {
|
fn promote_expr_to_module(src: &str) -> String {
|
||||||
|
@ -19,12 +21,7 @@ pub fn helper<'a>(
|
||||||
stdlib: roc_builtins::std::StdLib,
|
stdlib: roc_builtins::std::StdLib,
|
||||||
leak: bool,
|
leak: bool,
|
||||||
context: &'a inkwell::context::Context,
|
context: &'a inkwell::context::Context,
|
||||||
) -> (
|
) -> (&'static str, Vec<roc_problem::can::Problem>, Library) {
|
||||||
&'static str,
|
|
||||||
Vec<roc_problem::can::Problem>,
|
|
||||||
inkwell::execution_engine::ExecutionEngine<'a>,
|
|
||||||
) {
|
|
||||||
use inkwell::OptimizationLevel;
|
|
||||||
use roc_gen::llvm::build::{build_proc, build_proc_header, Scope};
|
use roc_gen::llvm::build::{build_proc, build_proc_header, Scope};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
@ -166,10 +163,6 @@ pub fn helper<'a>(
|
||||||
let (module_pass, function_pass) =
|
let (module_pass, function_pass) =
|
||||||
roc_gen::llvm::build::construct_optimization_passes(module, opt_level);
|
roc_gen::llvm::build::construct_optimization_passes(module, opt_level);
|
||||||
|
|
||||||
let execution_engine = module
|
|
||||||
.create_jit_execution_engine(OptimizationLevel::None)
|
|
||||||
.expect("Error creating JIT execution engine for test");
|
|
||||||
|
|
||||||
// Compile and add all the Procs before adding main
|
// Compile and add all the Procs before adding main
|
||||||
let env = roc_gen::llvm::build::Env {
|
let env = roc_gen::llvm::build::Env {
|
||||||
arena: &arena,
|
arena: &arena,
|
||||||
|
@ -265,7 +258,10 @@ pub fn helper<'a>(
|
||||||
// Uncomment this to see the module's optimized LLVM instruction output:
|
// Uncomment this to see the module's optimized LLVM instruction output:
|
||||||
// env.module.print_to_stderr();
|
// env.module.print_to_stderr();
|
||||||
|
|
||||||
(main_fn_name, errors, execution_engine.clone())
|
let lib = module_to_dylib(&env.module, &target, opt_level)
|
||||||
|
.expect("Error loading compiled dylib for test");
|
||||||
|
|
||||||
|
(main_fn_name, errors, lib)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO this is almost all code duplication with assert_llvm_evals_to
|
// TODO this is almost all code duplication with assert_llvm_evals_to
|
||||||
|
@ -284,7 +280,7 @@ macro_rules! assert_opt_evals_to {
|
||||||
|
|
||||||
let stdlib = roc_builtins::unique::uniq_stdlib();
|
let stdlib = roc_builtins::unique::uniq_stdlib();
|
||||||
|
|
||||||
let (main_fn_name, errors, execution_engine) =
|
let (main_fn_name, errors, lib) =
|
||||||
$crate::helpers::eval::helper(&arena, $src, stdlib, $leak, &context);
|
$crate::helpers::eval::helper(&arena, $src, stdlib, $leak, &context);
|
||||||
|
|
||||||
let transform = |success| {
|
let transform = |success| {
|
||||||
|
@ -292,7 +288,7 @@ macro_rules! assert_opt_evals_to {
|
||||||
let given = $transform(success);
|
let given = $transform(success);
|
||||||
assert_eq!(&given, &expected);
|
assert_eq!(&given, &expected);
|
||||||
};
|
};
|
||||||
run_jit_function!(execution_engine, main_fn_name, $ty, transform, errors)
|
run_jit_function!(lib, main_fn_name, $ty, transform, errors)
|
||||||
};
|
};
|
||||||
|
|
||||||
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
|
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
|
||||||
|
@ -312,7 +308,7 @@ macro_rules! assert_llvm_evals_to {
|
||||||
let context = Context::create();
|
let context = Context::create();
|
||||||
let stdlib = roc_builtins::std::standard_stdlib();
|
let stdlib = roc_builtins::std::standard_stdlib();
|
||||||
|
|
||||||
let (main_fn_name, errors, execution_engine) =
|
let (main_fn_name, errors, lib) =
|
||||||
$crate::helpers::eval::helper(&arena, $src, stdlib, $leak, &context);
|
$crate::helpers::eval::helper(&arena, $src, stdlib, $leak, &context);
|
||||||
|
|
||||||
let transform = |success| {
|
let transform = |success| {
|
||||||
|
@ -320,7 +316,7 @@ macro_rules! assert_llvm_evals_to {
|
||||||
let given = $transform(success);
|
let given = $transform(success);
|
||||||
assert_eq!(&given, &expected);
|
assert_eq!(&given, &expected);
|
||||||
};
|
};
|
||||||
run_jit_function!(execution_engine, main_fn_name, $ty, transform, errors)
|
run_jit_function!(lib, main_fn_name, $ty, transform, errors)
|
||||||
};
|
};
|
||||||
|
|
||||||
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
|
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
|
||||||
|
|
|
@ -2020,7 +2020,8 @@ fn parse_and_constrain<'a>(
|
||||||
let module_id = header.module_id;
|
let module_id = header.module_id;
|
||||||
|
|
||||||
// Generate documentation information
|
// Generate documentation information
|
||||||
// TODO: store timing information?
|
// TODO: store timing information
|
||||||
|
// TODO: only run this if we're doing a doc gen pass!
|
||||||
let module_docs = crate::docs::generate_module_docs(
|
let module_docs = crate::docs::generate_module_docs(
|
||||||
header.module_name,
|
header.module_name,
|
||||||
&header.exposed_ident_ids,
|
&header.exposed_ident_ids,
|
||||||
|
@ -2041,17 +2042,7 @@ fn parse_and_constrain<'a>(
|
||||||
);
|
);
|
||||||
let canonicalize_end = SystemTime::now();
|
let canonicalize_end = SystemTime::now();
|
||||||
let (module, declarations, ident_ids, constraint, problems) = match canonicalized {
|
let (module, declarations, ident_ids, constraint, problems) = match canonicalized {
|
||||||
Ok(mut module_output) => {
|
Ok(module_output) => {
|
||||||
// Add builtin defs (e.g. List.get) to the module's defs
|
|
||||||
let builtin_defs = roc_can::builtins::builtin_defs(&mut var_store);
|
|
||||||
let references = &module_output.references;
|
|
||||||
|
|
||||||
for (symbol, def) in builtin_defs {
|
|
||||||
if references.contains(&symbol) {
|
|
||||||
module_output.declarations.push(Declaration::Builtin(def));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let constraint = constrain_module(&module_output, module_id, mode, &mut var_store);
|
let constraint = constrain_module(&module_output, module_id, mode, &mut var_store);
|
||||||
|
|
||||||
// Now that we're done with parsing, canonicalization, and constraint gen,
|
// Now that we're done with parsing, canonicalization, and constraint gen,
|
||||||
|
|
|
@ -25,7 +25,7 @@ roc_solve = { path = "../solve" }
|
||||||
pretty_assertions = "0.5.1"
|
pretty_assertions = "0.5.1"
|
||||||
maplit = "1.0.1"
|
maplit = "1.0.1"
|
||||||
indoc = "0.3.3"
|
indoc = "0.3.3"
|
||||||
tempfile = "3.0.1"
|
tempfile = "3.1.0"
|
||||||
quickcheck = "0.8"
|
quickcheck = "0.8"
|
||||||
quickcheck_macros = "0.8"
|
quickcheck_macros = "0.8"
|
||||||
bumpalo = { version = "3.2", features = ["collections"] }
|
bumpalo = { version = "3.2", features = ["collections"] }
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue