merge upstream/main

This commit is contained in:
Luke Boswell 2022-11-06 09:27:46 +11:00
commit cec67721e6
No known key found for this signature in database
GPG key ID: 0E908525B2C7BD68
59 changed files with 2542 additions and 990 deletions

View file

@ -1,15 +1,13 @@
use bumpalo::Bump;
use roc_build::{
link::{link, preprocess_host_wasm32, rebuild_host, LinkType, LinkingStrategy},
program::{self, Problems},
program::{self, CodeGenOptions, Problems},
};
use roc_builtins::bitcode;
use roc_collections::VecMap;
use roc_load::{
EntryPoint, ExecutionMode, Expectations, LoadConfig, LoadMonomorphizedError, LoadedModule,
EntryPoint, ExecutionMode, ExpectMetadata, LoadConfig, LoadMonomorphizedError, LoadedModule,
LoadingProblem, Threading,
};
use roc_module::symbol::{Interns, ModuleId};
use roc_mono::ir::OptLevel;
use roc_reporting::report::RenderTarget;
use roc_target::TargetInfo;
@ -30,12 +28,11 @@ fn report_timing(buf: &mut String, label: &str, duration: Duration) {
.unwrap()
}
pub struct BuiltFile {
pub struct BuiltFile<'a> {
pub binary_path: PathBuf,
pub problems: Problems,
pub total_time: Duration,
pub expectations: VecMap<ModuleId, Expectations>,
pub interns: Interns,
pub expect_metadata: ExpectMetadata<'a>,
}
pub enum BuildOrdering {
@ -60,8 +57,7 @@ pub fn build_file<'a>(
arena: &'a Bump,
target: &Triple,
app_module_path: PathBuf,
opt_level: OptLevel,
emit_debug_info: bool,
code_gen_options: CodeGenOptions,
emit_timings: bool,
link_type: LinkType,
linking_strategy: LinkingStrategy,
@ -69,7 +65,7 @@ pub fn build_file<'a>(
threading: Threading,
wasm_dev_stack_bytes: Option<u32>,
order: BuildOrdering,
) -> Result<BuiltFile, BuildFileError<'a>> {
) -> Result<BuiltFile<'a>, BuildFileError<'a>> {
let compilation_start = Instant::now();
let target_info = TargetInfo::from(target);
@ -121,7 +117,7 @@ pub fn build_file<'a>(
match roc_target::OperatingSystem::from(target.operating_system) {
Wasi => {
if matches!(opt_level, OptLevel::Development) {
if matches!(code_gen_options.opt_level, OptLevel::Development) {
("wasm", "wasm", Some("wasm"))
} else {
("zig", "bc", Some("wasm"))
@ -180,7 +176,7 @@ pub fn build_file<'a>(
};
let rebuild_thread = spawn_rebuild_thread(
opt_level,
code_gen_options.opt_level,
linking_strategy,
prebuilt,
host_input_path.clone(),
@ -241,11 +237,8 @@ pub fn build_file<'a>(
// inside a nested scope without causing a borrow error!
let mut loaded = loaded;
let problems = program::report_problems_monomorphized(&mut loaded);
let expectations = std::mem::take(&mut loaded.expectations);
let loaded = loaded;
let interns = loaded.interns.clone();
enum HostRebuildTiming {
BeforeApp(u128),
ConcurrentWithApp(JoinHandle<u128>),
@ -266,13 +259,12 @@ pub fn build_file<'a>(
HostRebuildTiming::ConcurrentWithApp(rebuild_thread)
};
let (roc_app_bytes, code_gen_timing) = program::gen_from_mono_module(
let (roc_app_bytes, code_gen_timing, expect_metadata) = program::gen_from_mono_module(
arena,
loaded,
&app_module_path,
target,
opt_level,
emit_debug_info,
code_gen_options,
&preprocessed_host_path,
wasm_dev_stack_bytes,
);
@ -351,7 +343,7 @@ pub fn build_file<'a>(
let str_host_obj_path = bitcode::get_builtins_host_obj_path();
if matches!(opt_level, OptLevel::Development) {
if matches!(code_gen_options.backend, program::CodeGenBackend::Assembly) {
inputs.push(&str_host_obj_path);
}
@ -388,8 +380,7 @@ pub fn build_file<'a>(
binary_path,
problems,
total_time,
interns,
expectations,
expect_metadata,
})
}

View file

@ -7,11 +7,9 @@ use build::BuiltFile;
use bumpalo::Bump;
use clap::{Arg, ArgMatches, Command, ValueSource};
use roc_build::link::{LinkType, LinkingStrategy};
use roc_build::program::Problems;
use roc_collections::VecMap;
use roc_build::program::{CodeGenBackend, CodeGenOptions, Problems};
use roc_error_macros::{internal_error, user_error};
use roc_load::{Expectations, LoadingProblem, Threading};
use roc_module::symbol::{Interns, ModuleId};
use roc_load::{ExpectMetadata, LoadingProblem, Threading};
use roc_mono::ir::OptLevel;
use std::env;
use std::ffi::{CString, OsStr};
@ -432,7 +430,7 @@ pub fn test(matches: &ArgMatches, triple: Triple) -> io::Result<i32> {
let mut writer = std::io::stdout();
let (failed, passed) = roc_repl_expect::run::run_expects(
let (failed, passed) = roc_repl_expect::run::run_toplevel_expects(
&mut writer,
roc_reporting::report::RenderTarget::ColorTerminal,
arena,
@ -481,18 +479,33 @@ pub fn build(
use build::build_file;
use BuildConfig::*;
let arena = Bump::new();
let filename = matches.value_of_os(ROC_FILE).unwrap();
let opt_level = match (
matches.is_present(FLAG_OPTIMIZE),
matches.is_present(FLAG_OPT_SIZE),
matches.is_present(FLAG_DEV),
) {
(true, false, false) => OptLevel::Optimize,
(false, true, false) => OptLevel::Size,
(false, false, true) => OptLevel::Development,
(false, false, false) => OptLevel::Normal,
_ => user_error!("build can be only one of `--dev`, `--optimize`, or `--opt-size`"),
// the process will end after this function,
// so we don't want to spend time freeing these values
let arena = ManuallyDrop::new(Bump::new());
let code_gen_backend = if matches!(triple.architecture, Architecture::Wasm32) {
CodeGenBackend::Wasm
} else {
match matches.is_present(FLAG_DEV) {
true => CodeGenBackend::Assembly,
false => CodeGenBackend::Llvm,
}
};
let opt_level = if let BuildConfig::BuildAndRunIfNoErrors = config {
OptLevel::Development
} else {
match (
matches.is_present(FLAG_OPTIMIZE),
matches.is_present(FLAG_OPT_SIZE),
) {
(true, false) => OptLevel::Optimize,
(false, true) => OptLevel::Size,
(false, false) => OptLevel::Normal,
(true, true) => {
user_error!("build can be only one of `--optimize` and `--opt-size`")
}
}
};
let emit_debug_info = matches.is_present(FLAG_DEBUG);
let emit_timings = matches.is_present(FLAG_TIME);
@ -508,7 +521,7 @@ pub fn build(
};
let wasm_dev_backend = matches!(opt_level, OptLevel::Development)
&& matches!(triple.architecture, Architecture::Wasm32);
&& matches!(code_gen_backend, CodeGenBackend::Wasm);
let linking_strategy = if wasm_dev_backend {
LinkingStrategy::Additive
@ -528,6 +541,8 @@ pub fn build(
// We make an exception for Wasm, because cross-compiling is the norm in that case.
triple != Triple::host() && !matches!(triple.architecture, Architecture::Wasm32)
};
let filename = matches.value_of_os(ROC_FILE).unwrap();
let path = Path::new(filename);
// Spawn the root task
@ -559,12 +574,18 @@ pub fn build(
BuildAndRunIfNoErrors => BuildOrdering::BuildIfChecks,
_ => BuildOrdering::AlwaysBuild,
};
let code_gen_options = CodeGenOptions {
backend: code_gen_backend,
opt_level,
emit_debug_info,
};
let res_binary_path = build_file(
&arena,
&triple,
path.to_path_buf(),
opt_level,
emit_debug_info,
code_gen_options,
emit_timings,
link_type,
linking_strategy,
@ -579,8 +600,7 @@ pub fn build(
binary_path,
problems,
total_time,
expectations,
interns,
expect_metadata,
}) => {
match config {
BuildOnly => {
@ -593,7 +613,7 @@ pub fn build(
// No need to waste time freeing this memory,
// since the process is about to exit anyway.
std::mem::forget(arena);
// std::mem::forget(arena);
print_problems(problems, total_time);
println!(" while successfully building:\n\n {generated_filename}");
@ -616,7 +636,7 @@ pub fn build(
// ManuallyDrop will leak the bytes because we don't drop manually
let bytes = &ManuallyDrop::new(std::fs::read(&binary_path).unwrap());
roc_run(arena, opt_level, triple, args, bytes, expectations, interns)
roc_run(&arena, opt_level, triple, args, bytes, expect_metadata)
}
BuildAndRunIfNoErrors => {
debug_assert!(
@ -637,7 +657,7 @@ pub fn build(
// ManuallyDrop will leak the bytes because we don't drop manually
let bytes = &ManuallyDrop::new(std::fs::read(&binary_path).unwrap());
roc_run(arena, opt_level, triple, args, bytes, expectations, interns)
roc_run(&arena, opt_level, triple, args, bytes, expect_metadata)
}
}
}
@ -703,13 +723,12 @@ fn print_problems(problems: Problems, total_time: std::time::Duration) {
}
fn roc_run<'a, I: IntoIterator<Item = &'a OsStr>>(
arena: Bump, // This should be passed an owned value, not a reference, so we can usefully mem::forget it!
arena: &Bump,
opt_level: OptLevel,
triple: Triple,
args: I,
binary_bytes: &[u8],
expectations: VecMap<ModuleId, Expectations>,
interns: Interns,
expect_metadata: ExpectMetadata,
) -> io::Result<i32> {
match triple.architecture {
Architecture::Wasm32 => {
@ -720,10 +739,6 @@ fn roc_run<'a, I: IntoIterator<Item = &'a OsStr>>(
.strip_prefix(env::current_dir().unwrap())
.unwrap_or(path);
// No need to waste time freeing this memory,
// since the process is about to exit anyway.
std::mem::forget(arena);
#[cfg(target_family = "unix")]
{
use std::os::unix::ffi::OsStrExt;
@ -748,7 +763,7 @@ fn roc_run<'a, I: IntoIterator<Item = &'a OsStr>>(
Ok(0)
}
_ => roc_run_native(arena, opt_level, args, binary_bytes, expectations, interns),
_ => roc_run_native(arena, opt_level, args, binary_bytes, expect_metadata),
}
}
@ -848,35 +863,32 @@ fn make_argv_envp_windows<'a, I: IntoIterator<Item = S>, S: AsRef<OsStr>>(
/// Run on the native OS (not on wasm)
#[cfg(target_family = "unix")]
fn roc_run_native<I: IntoIterator<Item = S>, S: AsRef<OsStr>>(
arena: Bump,
arena: &Bump,
opt_level: OptLevel,
args: I,
binary_bytes: &[u8],
expectations: VecMap<ModuleId, Expectations>,
interns: Interns,
expect_metadata: ExpectMetadata,
) -> std::io::Result<i32> {
use bumpalo::collections::CollectIn;
unsafe {
let executable = roc_run_executable_file_path(binary_bytes)?;
let (argv_cstrings, envp_cstrings) = make_argv_envp(&arena, &executable, args);
let (argv_cstrings, envp_cstrings) = make_argv_envp(arena, &executable, args);
let argv: bumpalo::collections::Vec<*const c_char> = argv_cstrings
.iter()
.map(|s| s.as_ptr())
.chain([std::ptr::null()])
.collect_in(&arena);
.collect_in(arena);
let envp: bumpalo::collections::Vec<*const c_char> = envp_cstrings
.iter()
.map(|s| s.as_ptr())
.chain([std::ptr::null()])
.collect_in(&arena);
.collect_in(arena);
match opt_level {
OptLevel::Development => {
roc_run_native_debug(executable, &argv, &envp, expectations, interns)
}
OptLevel::Development => roc_dev_native(arena, executable, argv, envp, expect_metadata),
OptLevel::Normal | OptLevel::Size | OptLevel::Optimize => {
roc_run_native_fast(executable, &argv, &envp);
}
@ -950,14 +962,76 @@ impl ExecutableFile {
// with Expect
#[cfg(target_family = "unix")]
unsafe fn roc_run_native_debug(
_executable: ExecutableFile,
_argv: &[*const c_char],
_envp: &[*const c_char],
_expectations: VecMap<ModuleId, Expectations>,
_interns: Interns,
) {
todo!()
fn roc_dev_native(
arena: &Bump,
executable: ExecutableFile,
argv: bumpalo::collections::Vec<*const c_char>,
envp: bumpalo::collections::Vec<*const c_char>,
expect_metadata: ExpectMetadata,
) -> ! {
use roc_repl_expect::run::ExpectMemory;
use signal_hook::{consts::signal::SIGCHLD, consts::signal::SIGUSR1, iterator::Signals};
let ExpectMetadata {
mut expectations,
interns,
layout_interner,
} = expect_metadata;
let mut signals = Signals::new(&[SIGCHLD, SIGUSR1]).unwrap();
// let shm_name =
let shm_name = format!("/roc_expect_buffer_{}", std::process::id());
let memory = ExpectMemory::create_or_reuse_mmap(&shm_name);
let layout_interner = layout_interner.into_global();
let mut writer = std::io::stdout();
match unsafe { libc::fork() } {
0 => unsafe {
// we are the child
executable.execve(&argv, &envp);
// Display a human-friendly error message
println!("Error {:?}", std::io::Error::last_os_error());
std::process::exit(1);
},
-1 => {
// something failed
// Display a human-friendly error message
println!("Error {:?}", std::io::Error::last_os_error());
std::process::exit(1)
}
1.. => {
for sig in &mut signals {
match sig {
SIGCHLD => break,
SIGUSR1 => {
// this is the signal we use for an expect failure. Let's see what the child told us
roc_repl_expect::run::render_expects_in_memory(
&mut writer,
arena,
&mut expectations,
&interns,
&layout_interner,
&memory,
)
.unwrap();
}
_ => println!("received signal {}", sig),
}
}
std::process::exit(0)
}
_ => unreachable!(),
}
}
#[cfg(target_os = "linux")]
@ -1036,12 +1110,11 @@ fn roc_run_executable_file_path(binary_bytes: &[u8]) -> std::io::Result<Executab
/// Run on the native OS (not on wasm)
#[cfg(not(target_family = "unix"))]
fn roc_run_native<I: IntoIterator<Item = S>, S: AsRef<OsStr>>(
arena: Bump, // This should be passed an owned value, not a reference, so we can usefully mem::forget it!
arena: &Bump, // This should be passed an owned value, not a reference, so we can usefully mem::forget it!
opt_level: OptLevel,
args: I,
binary_bytes: &[u8],
_expectations: VecMap<ModuleId, Expectations>,
_interns: Interns,
_expect_metadata: ExpectMetadata,
) -> io::Result<i32> {
use bumpalo::collections::CollectIn;
@ -1055,18 +1128,18 @@ fn roc_run_native<I: IntoIterator<Item = S>, S: AsRef<OsStr>>(
.iter()
.map(|s| s.as_ptr())
.chain([std::ptr::null()])
.collect_in(&arena);
.collect_in(arena);
let envp: bumpalo::collections::Vec<*const c_char> = envp_cstrings
.iter()
.map(|s| s.as_ptr())
.chain([std::ptr::null()])
.collect_in(&arena);
.collect_in(arena);
match opt_level {
OptLevel::Development => {
// roc_run_native_debug(executable, &argv, &envp, expectations, interns)
todo!()
internal_error!("running `expect`s does not currently work on windows")
}
OptLevel::Normal | OptLevel::Size | OptLevel::Optimize => {
roc_run_native_fast(executable, &argv, &envp);

View file

@ -178,14 +178,7 @@ fn main() -> io::Result<()> {
}
}
}
Some((CMD_REPL, _)) => {
{
roc_repl_cli::main()?;
// Exit 0 if the repl exited normally
Ok(0)
}
}
Some((CMD_REPL, _)) => Ok(roc_repl_cli::main()),
Some((CMD_EDIT, matches)) => {
match matches
.values_of_os(DIRECTORY_OR_FILES)