Merge remote-tracking branch 'origin/trunk' into tail-call-elimination

This commit is contained in:
Folkert 2020-08-12 15:41:35 +02:00
commit 380e34d296
10 changed files with 1529 additions and 1804 deletions

View file

@ -10,7 +10,7 @@ use roc_can::expected::Expected;
use roc_can::expr::{canonicalize_expr, Output}; use roc_can::expr::{canonicalize_expr, Output};
use roc_can::operator; use roc_can::operator;
use roc_can::scope::Scope; use roc_can::scope::Scope;
use roc_collections::all::{ImMap, ImSet, MutMap, SendMap, SendSet}; use roc_collections::all::{ImMap, ImSet, MutMap, MutSet, SendMap, SendSet};
use roc_constrain::expr::constrain_expr; use roc_constrain::expr::constrain_expr;
use roc_constrain::module::{constrain_imported_values, load_builtin_aliases, Import}; use roc_constrain::module::{constrain_imported_values, load_builtin_aliases, Import};
use roc_gen::layout_id::LayoutIds; use roc_gen::layout_id::LayoutIds;
@ -241,6 +241,7 @@ pub fn gen(src: &[u8], target: Triple, opt_level: OptLevel) -> Result<(String, S
module, module,
ptr_bytes, ptr_bytes,
leak: false, leak: false,
exposed_to_host: MutSet::default(),
}; };
let mut procs = Procs::default(); let mut procs = Procs::default();
let mut ident_ids = env.interns.all_ident_ids.remove(&home).unwrap(); let mut ident_ids = env.interns.all_ident_ids.remove(&home).unwrap();
@ -321,8 +322,7 @@ pub fn gen(src: &[u8], target: Triple, opt_level: OptLevel) -> Result<(String, S
// Add main to the module. // Add main to the module.
let main_fn = env.module.add_function(main_fn_name, main_fn_type, None); let main_fn = env.module.add_function(main_fn_name, main_fn_type, None);
let cc = let cc = roc_gen::llvm::build::FAST_CALL_CONV;
roc_gen::llvm::build::get_call_conventions(target.default_calling_convention().unwrap());
main_fn.set_call_conventions(cc); main_fn.set_call_conventions(cc);

View file

@ -113,7 +113,7 @@ mod cli_run {
assert_eq!(&out.stderr, ""); assert_eq!(&out.stderr, "");
assert!(&out assert!(&out
.stdout .stdout
.ends_with("[5, 6, 10, 12, 20, 21, 22, 23, 24, 32, 33, 42, 45, 54]\n")); .ends_with("[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]\n"));
assert!(out.status.success()); assert!(out.status.success());
} }
} }

View file

@ -1,15 +1,12 @@
use bumpalo::Bump; use bumpalo::Bump;
use inkwell::context::Context; use inkwell::context::Context;
use inkwell::module::Linkage;
use inkwell::targets::{ use inkwell::targets::{
CodeModel, FileType, InitializationConfig, RelocMode, Target, TargetTriple, CodeModel, FileType, InitializationConfig, RelocMode, Target, TargetTriple,
}; };
use inkwell::OptimizationLevel; use inkwell::OptimizationLevel;
use roc_collections::all::default_hasher; use roc_collections::all::default_hasher;
use roc_gen::layout_id::LayoutIds; use roc_gen::layout_id::LayoutIds;
use roc_gen::llvm::build::{ use roc_gen::llvm::build::{build_proc, build_proc_header, module_from_builtins, OptLevel};
build_proc, build_proc_header, get_call_conventions, module_from_builtins, OptLevel,
};
use roc_load::file::LoadedModule; use roc_load::file::LoadedModule;
use roc_mono::ir::{Env, PartialProc, Procs}; use roc_mono::ir::{Env, PartialProc, Procs};
use roc_mono::layout::{Layout, LayoutCache}; use roc_mono::layout::{Layout, LayoutCache};
@ -23,7 +20,7 @@ use target_lexicon::{Architecture, OperatingSystem, Triple, Vendor};
#[allow(clippy::cognitive_complexity)] #[allow(clippy::cognitive_complexity)]
pub fn gen( pub fn gen(
arena: &Bump, arena: &Bump,
loaded: LoadedModule, mut loaded: LoadedModule,
filename: PathBuf, filename: PathBuf,
target: Triple, target: Triple,
dest_filename: &Path, dest_filename: &Path,
@ -74,17 +71,14 @@ pub fn gen(
let ptr_bytes = target.pointer_width().unwrap().bytes() as u32; let ptr_bytes = target.pointer_width().unwrap().bytes() as u32;
// Compile and add all the Procs before adding main let mut exposed_to_host =
let mut env = roc_gen::llvm::build::Env { HashSet::with_capacity_and_hasher(loaded.exposed_vars_by_symbol.len(), default_hasher());
arena: &arena,
builder: &builder, for (symbol, _) in loaded.exposed_vars_by_symbol {
context: &context, exposed_to_host.insert(symbol);
interns: loaded.interns, }
module,
ptr_bytes, let mut ident_ids = loaded.interns.all_ident_ids.remove(&home).unwrap();
leak: false,
};
let mut ident_ids = env.interns.all_ident_ids.remove(&home).unwrap();
let mut layout_ids = LayoutIds::default(); let mut layout_ids = LayoutIds::default();
let mut procs = Procs::default(); let mut procs = Procs::default();
let mut mono_problems = std::vec::Vec::new(); let mut mono_problems = std::vec::Vec::new();
@ -96,12 +90,6 @@ pub fn gen(
home, home,
ident_ids: &mut ident_ids, ident_ids: &mut ident_ids,
}; };
let mut exposed_symbols =
HashSet::with_capacity_and_hasher(loaded.exposed_vars_by_symbol.len(), default_hasher());
for (symbol, _) in loaded.exposed_vars_by_symbol {
exposed_symbols.insert(symbol);
}
// Add modules' decls to Procs // Add modules' decls to Procs
for (_, mut decls) in decls_by_id for (_, mut decls) in decls_by_id
@ -127,7 +115,7 @@ pub fn gen(
// register it as such. Otherwise, since it // register it as such. Otherwise, since it
// never gets called by Roc code, it will never // never gets called by Roc code, it will never
// get specialized! // get specialized!
if exposed_symbols.contains(&symbol) { if exposed_to_host.contains(&symbol) {
let mut pattern_vars = let mut pattern_vars =
bumpalo::collections::Vec::with_capacity_in( bumpalo::collections::Vec::with_capacity_in(
loc_args.len(), loc_args.len(),
@ -178,7 +166,7 @@ pub fn gen(
// register it as such. Otherwise, since it // register it as such. Otherwise, since it
// never gets called by Roc code, it will never // never gets called by Roc code, it will never
// get specialized! // get specialized!
if exposed_symbols.contains(&symbol) { if exposed_to_host.contains(&symbol) {
let pattern_vars = bumpalo::collections::Vec::new_in(arena); let pattern_vars = bumpalo::collections::Vec::new_in(arena);
let ret_layout = layout_cache.from_var(mono_env.arena, annotation, mono_env.subs).unwrap_or_else(|err| let ret_layout = layout_cache.from_var(mono_env.arena, annotation, mono_env.subs).unwrap_or_else(|err|
todo!("TODO gracefully handle the situation where we expose a function to the host which doesn't have a valid layout (e.g. maybe the function wasn't monomorphic): {:?}", err) todo!("TODO gracefully handle the situation where we expose a function to the host which doesn't have a valid layout (e.g. maybe the function wasn't monomorphic): {:?}", err)
@ -225,6 +213,18 @@ pub fn gen(
} }
} }
// Compile and add all the Procs before adding main
let mut env = roc_gen::llvm::build::Env {
arena: &arena,
builder: &builder,
context: &context,
interns: loaded.interns,
module,
ptr_bytes,
leak: false,
exposed_to_host,
};
// Populate Procs further and get the low-level Expr from the canonical Expr // Populate Procs further and get the low-level Expr from the canonical Expr
let mut headers = { let mut headers = {
let num_headers = match &procs.pending_specializations { let num_headers = match &procs.pending_specializations {
@ -274,27 +274,6 @@ pub fn gen(
} }
} }
// Set exposed functions to external linkage and C calling conventions
{
let cc = get_call_conventions(target.default_calling_convention().unwrap());
let interns = &env.interns;
for symbol in exposed_symbols {
// Since it was exposed, it must have been monomorphic,
// meaning its LLVM name will be ___#1 (e.g. "main#1")
let fn_name = format!("{}#1", symbol.ident_string(interns));
let fn_val = env.module.get_function(&fn_name).unwrap_or_else(|| {
panic!(
"module.get_function({:?}) did not find a function registered with LLVM",
fn_name
)
});
fn_val.set_linkage(Linkage::External);
fn_val.set_call_conventions(cc);
}
}
mpm.run_on(module); mpm.run_on(module);
// Verify the module // Verify the module

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,3 +1,4 @@
pub mod build; pub mod build;
pub mod build_list;
pub mod compare; pub mod compare;
pub mod convert; pub mod convert;

View file

@ -1,3 +1,4 @@
use roc_collections::all::MutSet;
use roc_types::subs::Subs; use roc_types::subs::Subs;
pub fn helper_without_uniqueness<'a>( pub fn helper_without_uniqueness<'a>(
@ -88,6 +89,7 @@ pub fn helper_without_uniqueness<'a>(
module, module,
ptr_bytes, ptr_bytes,
leak: leak, leak: leak,
exposed_to_host: MutSet::default(),
}; };
let mut procs = roc_mono::ir::Procs::default(); let mut procs = roc_mono::ir::Procs::default();
let mut ident_ids = env.interns.all_ident_ids.remove(&home).unwrap(); let mut ident_ids = env.interns.all_ident_ids.remove(&home).unwrap();
@ -160,8 +162,7 @@ pub fn helper_without_uniqueness<'a>(
// Add main to the module. // Add main to the module.
let main_fn = env.module.add_function(main_fn_name, main_fn_type, None); let main_fn = env.module.add_function(main_fn_name, main_fn_type, None);
let cc = let cc = roc_gen::llvm::build::FAST_CALL_CONV;
roc_gen::llvm::build::get_call_conventions(target.default_calling_convention().unwrap());
main_fn.set_call_conventions(cc); main_fn.set_call_conventions(cc);
@ -279,6 +280,7 @@ pub fn helper_with_uniqueness<'a>(
module, module,
ptr_bytes, ptr_bytes,
leak: leak, leak: leak,
exposed_to_host: MutSet::default(),
}; };
let mut procs = roc_mono::ir::Procs::default(); let mut procs = roc_mono::ir::Procs::default();
let mut ident_ids = env.interns.all_ident_ids.remove(&home).unwrap(); let mut ident_ids = env.interns.all_ident_ids.remove(&home).unwrap();
@ -350,8 +352,7 @@ pub fn helper_with_uniqueness<'a>(
// Add main to the module. // Add main to the module.
let main_fn = env.module.add_function(main_fn_name, main_fn_type, None); let main_fn = env.module.add_function(main_fn_name, main_fn_type, None);
let cc = let cc = roc_gen::llvm::build::FAST_CALL_CONV;
roc_gen::llvm::build::get_call_conventions(target.default_calling_convention().unwrap());
main_fn.set_call_conventions(cc); main_fn.set_call_conventions(cc);

View file

@ -12,8 +12,6 @@ mod helpers;
// Test monomorphization // Test monomorphization
#[cfg(test)] #[cfg(test)]
mod test_mono { mod test_mono {
use crate::helpers::with_larger_debug_stack;
// NOTE because the Show instance of module names is different in --release mode, // NOTE because the Show instance of module names is different in --release mode,
// these tests would all fail. In the future, when we do interesting optimizations, // these tests would all fail. In the future, when we do interesting optimizations,
// we'll likely want some tests for --release too. // we'll likely want some tests for --release too.
@ -932,7 +930,7 @@ mod test_mono {
#[allow(dead_code)] #[allow(dead_code)]
fn quicksort_help() { fn quicksort_help() {
with_larger_debug_stack(|| { crate::helpers::with_larger_debug_stack(|| {
compiles_to_ir( compiles_to_ir(
indoc!( indoc!(
r#" r#"
@ -960,7 +958,7 @@ mod test_mono {
#[allow(dead_code)] #[allow(dead_code)]
fn quicksort_partition_help() { fn quicksort_partition_help() {
with_larger_debug_stack(|| { crate::helpers::with_larger_debug_stack(|| {
compiles_to_ir( compiles_to_ir(
indoc!( indoc!(
r#" r#"
@ -994,7 +992,7 @@ mod test_mono {
#[allow(dead_code)] #[allow(dead_code)]
fn quicksort_full() { fn quicksort_full() {
with_larger_debug_stack(|| { crate::helpers::with_larger_debug_stack(|| {
compiles_to_ir( compiles_to_ir(
indoc!( indoc!(
r#" r#"

View file

@ -1,8 +1,3 @@
use std::env;
use std::fs::File;
use std::io::prelude::*;
use std::io::BufReader;
use std::path::PathBuf;
use std::time::SystemTime; use std::time::SystemTime;
#[link(name = "roc_app", kind = "static")] #[link(name = "roc_app", kind = "static")]
@ -12,84 +7,31 @@ extern "C" {
fn quicksort(list: Box<[i64]>) -> Box<[i64]>; fn quicksort(list: Box<[i64]>) -> Box<[i64]>;
} }
pub fn example_dir(dir_name: &str) -> PathBuf { const NUM_NUMS: usize = 1_000_000;
let mut path = env::current_exe().ok().unwrap();
// Get rid of the filename in target/debug/deps/cli_run-99c65e4e9a1fbd06
path.pop();
// If we're in deps/ get rid of deps/ in target/debug/deps/
if path.ends_with("deps") {
path.pop();
}
// Get rid of target/debug/ so we're back at the project root
path.pop();
path.pop();
// Descend into examples/{dir_name}
path.push("examples");
path.push(dir_name);
path
}
pub fn example_file(dir_name: &str, file_name: &str) -> PathBuf {
let mut path = example_dir(dir_name);
path.push(file_name);
path
}
pub fn main() { pub fn main() {
let filename = example_file("quicksort", "unsorted.csv"); let nums: Box<[i64]> = {
let mut nums = Vec::with_capacity(NUM_NUMS);
let mut nums = { for index in 0..nums.capacity() {
match File::open(filename.clone()) { let num = index as i64 % 12345;
Ok(file) => {
let mut contents = String::new();
let mut buf_reader = BufReader::new(file);
buf_reader nums.push(num);
.read_to_string(&mut contents)
.expect("error reading file to string");
contents
.split(",")
.map(|string| {
string.trim().parse::<i64>().unwrap_or_else(|err| {
panic!("Invalid number: {:?} - error was: {:?}", string, err)
})
})
.collect::<Vec<i64>>()
}
Err(err) => {
println!(
"INFO: Couldn't open the CSV file {:?} because {:?}, so falling back on a hardcoded list of numbers.", err, filename
);
vec![10, 24, 54, 23, 21, 22, 45, 5, 32, 33, 6, 20, 12, 42]
}
} }
nums.into()
}; };
// TODO FIXME don't truncate! This is just for testing.
nums.truncate(1_000_000);
let nums: Box<[i64]> = nums.into();
println!("Running Roc quicksort on {} numbers...", nums.len()); println!("Running Roc quicksort on {} numbers...", nums.len());
let start_time = SystemTime::now(); let start_time = SystemTime::now();
let answer = unsafe { quicksort(nums) }; let answer = unsafe { quicksort(nums) };
let end_time = SystemTime::now(); let end_time = SystemTime::now();
let duration = end_time.duration_since(start_time).unwrap(); let duration = end_time.duration_since(start_time).unwrap();
// hardcode test output, so stdout is not swamped
println!( println!(
"Roc quicksort took {:.4} ms to compute this answer: {:?}", "Roc quicksort took {:.4} ms to compute this answer: {:?}",
duration.as_secs_f64() * 1000.0, duration.as_secs_f64() * 1000.0,
vec![5, 6, 10, 12, 20, 21, 22, 23, 24, 32, 33, 42, 45, 54] // truncate the answer, so stdout is not swamped
&answer[0..20]
); );
// the pointer is to the first _element_ of the list, // the pointer is to the first _element_ of the list,

View file

@ -1 +0,0 @@
10,24,54,23,21,22,45,5,32,33,6,20,12,42
1 10 24 54 23 21 22 45 5 32 33 6 20 12 42