Use specialize_all

This commit is contained in:
Richard Feldman 2020-05-21 22:09:11 -04:00
parent 639c132ce0
commit 42e3d35564
4 changed files with 81 additions and 47 deletions

View file

@ -5,7 +5,6 @@ use inkwell::passes::PassManager;
use inkwell::types::BasicType;
use inkwell::OptimizationLevel;
use roc_collections::all::ImMap;
use roc_collections::all::MutMap;
use roc_gen::layout_id::LayoutIds;
use roc_gen::llvm::build::{
build_proc, build_proc_header, get_call_conventions, module_from_builtins, OptLevel,
@ -14,7 +13,7 @@ use roc_gen::llvm::convert::basic_type_from_layout;
use roc_load::file::LoadedModule;
use roc_module::symbol::Symbol;
use roc_mono::expr::{Env, Expr, PartialProc, Procs};
use roc_mono::layout::Layout;
use roc_mono::layout::{Layout, LayoutCache};
use inkwell::targets::{
CodeModel, FileType, InitializationConfig, RelocMode, Target, TargetTriple,
@ -23,6 +22,7 @@ use std::path::{Path, PathBuf};
use target_lexicon::{Architecture, OperatingSystem, Triple, Vendor};
// 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)]
pub fn build(
arena: &Bump,
@ -225,25 +225,28 @@ pub fn build(
// Populate Procs further and get the low-level Expr from the canonical Expr
let main_body = Expr::new(&mut mono_env, loc_expr.value, &mut procs);
// Put this module's ident_ids back in the interns, so we can use them in env.
env.interns.all_ident_ids.insert(home, ident_ids);
let mut headers = Vec::with_capacity(procs.pending_specializations.len());
let mut layout_cache = LayoutCache::default();
let mut headers = Vec::with_capacity(procs.len());
let (mut proc_map, runtime_errors) = procs.into_map();
let (mut specializations, runtime_errors) =
roc_mono::expr::specialize_all(&mut mono_env, procs, &mut layout_cache);
assert_eq!(runtime_errors, roc_collections::all::MutSet::default());
// Put this module's ident_ids back in the interns, so we can use them in env.
// This must happen *after* building the headers, because otherwise there's
// a conflicting mutable borrow on ident_ids.
env.interns.all_ident_ids.insert(home, ident_ids);
// Add all the Proc headers to the module.
// We have to do this in a separate pass first,
// because their bodies may reference each other.
for (symbol, mut procs_by_layout) in proc_map.drain() {
for (layout, proc) in procs_by_layout.drain() {
for (symbol, layout, proc) in specializations.drain(..) {
let (fn_val, arg_basic_types) =
build_proc_header(&env, &mut layout_ids, symbol, &layout, &proc);
headers.push((proc, fn_val, arg_basic_types));
}
}
// Build each proc using its header info.
for (proc, fn_val, arg_basic_types) in headers {

View file

@ -75,24 +75,28 @@ macro_rules! assert_llvm_evals_to {
};
let main_body = Expr::new(&mut mono_env, loc_expr.value, &mut procs);
// Put this module's ident_ids back in the interns, so we can use them in Env.
env.interns.all_ident_ids.insert(home, ident_ids);
let mut headers = Vec::with_capacity(procs.pending_specializations.len());
let mut layout_cache = roc_mono::layout::LayoutCache::default();
let mut headers = Vec::with_capacity(procs.len());
let (mut proc_map, runtime_errors) = procs.into_map();
let (mut specializations, runtime_errors) =
roc_mono::expr::specialize_all(&mut mono_env, procs, &mut layout_cache);
assert_eq!(runtime_errors, roc_collections::all::MutSet::default());
// Put this module's ident_ids back in the interns, so we can use them in env.
// This must happen *after* building the headers, because otherwise there's
// a conflicting mutable borrow on ident_ids.
env.interns.all_ident_ids.insert(home, ident_ids);
// Add all the Proc headers to the module.
// We have to do this in a separate pass first,
// because their bodies may reference each other.
for (symbol, mut procs_by_layout) in proc_map.drain() {
for (layout, proc) in procs_by_layout.drain() {
let (fn_val, arg_basic_types) = build_proc_header(&env, &mut layout_ids, symbol, &layout, &proc);
for (symbol, layout, proc) in specializations.drain(..) {
let (fn_val, arg_basic_types) =
build_proc_header(&env, &mut layout_ids, symbol, &layout, &proc);
headers.push((proc, fn_val, arg_basic_types));
}
}
// Build each proc using its header info.
for (proc, fn_val, arg_basic_types) in headers {
@ -106,7 +110,9 @@ macro_rules! assert_llvm_evals_to {
fpm.run_on(&fn_val);
} else {
// NOTE: If this fails, uncomment the above println to debug.
panic!("Non-main function failed LLVM verification. Uncomment the above println to debug!");
panic!(
"Non-main function failed LLVM verification. Uncomment the above println to debug!"
);
}
}
@ -240,24 +246,28 @@ macro_rules! assert_opt_evals_to {
};
let main_body = Expr::new(&mut mono_env, loc_expr.value, &mut procs);
// Put this module's ident_ids back in the interns, so we can use them in Env.
env.interns.all_ident_ids.insert(home, ident_ids);
let mut headers = Vec::with_capacity(procs.pending_specializations.len());
let mut layout_cache = roc_mono::layout::LayoutCache::default();
let mut headers = Vec::with_capacity(procs.len());
let (mut proc_map, runtime_errors) = procs.into_map();
let (mut specializations, runtime_errors) =
roc_mono::expr::specialize_all(&mut mono_env, procs, &mut layout_cache);
assert_eq!(runtime_errors, roc_collections::all::MutSet::default());
// Put this module's ident_ids back in the interns, so we can use them in env.
// This must happen *after* building the headers, because otherwise there's
// a conflicting mutable borrow on ident_ids.
env.interns.all_ident_ids.insert(home, ident_ids);
// Add all the Proc headers to the module.
// We have to do this in a separate pass first,
// because their bodies may reference each other.
for (symbol, mut procs_by_layout) in proc_map.drain() {
for (layout, proc) in procs_by_layout.drain() {
let (fn_val, arg_basic_types) = build_proc_header(&env, &mut layout_ids, symbol, &layout, &proc);
for (symbol, layout, proc) in specializations.drain(..) {
let (fn_val, arg_basic_types) =
build_proc_header(&env, &mut layout_ids, symbol, &layout, &proc);
headers.push((proc, fn_val, arg_basic_types));
}
}
// Build each proc using its header info.
for (proc, fn_val, arg_basic_types) in headers {
@ -271,7 +281,9 @@ macro_rules! assert_opt_evals_to {
fpm.run_on(&fn_val);
} else {
// NOTE: If this fails, uncomment the above println to debug.
panic!("Non-main function failed LLVM verification. Uncomment the above println to debug!");
panic!(
"Non-main function failed LLVM verification. Uncomment the above println to debug!"
);
}
}
@ -398,21 +410,28 @@ macro_rules! emit_expr {
};
let main_body = Expr::new(&mut mono_env, loc_expr.value, &mut procs);
// Put this module's ident_ids back in the interns, so we can use them in Env.
env.interns.all_ident_ids.insert(home, ident_ids);
let mut headers = Vec::with_capacity(procs.pending_specializations.len());
let mut layout_cache = roc_mono::layout::LayoutCache::default();
let mut headers = Vec::with_capacity(procs.len());
let (mut specializations, runtime_errors) =
roc_mono::expr::specialize_all(&mut mono_env, procs, &mut layout_cache);
assert_eq!(runtime_errors, roc_collections::all::MutSet::default());
// Put this module's ident_ids back in the interns, so we can use them in env.
// This must happen *after* building the headers, because otherwise there's
// a conflicting mutable borrow on ident_ids.
env.interns.all_ident_ids.insert(home, ident_ids);
// Add all the Proc headers to the module.
// We have to do this in a separate pass first,
// because their bodies may reference each other.
for (symbol, opt_proc) in procs.as_map().into_iter() {
if let Some(proc) = opt_proc {
let (fn_val, arg_basic_types) = build_proc_header(&env, symbol, &proc);
for (symbol, layout, proc) in specializations.drain(..) {
let (fn_val, arg_basic_types) =
build_proc_header(&env, &mut layout_ids, symbol, &layout, &proc);
headers.push((proc, fn_val, arg_basic_types));
}
}
// Build each proc using its header info.
for (proc, fn_val, arg_basic_types) in headers {
@ -420,13 +439,15 @@ macro_rules! emit_expr {
// (This approach means we don't have to defensively clone name here.)
//
// println!("\n\nBuilding and then verifying function {}\n\n", name);
build_proc(&env, proc, &procs, fn_val, arg_basic_types);
build_proc(&env, &mut layout_ids, proc, fn_val, arg_basic_types);
if fn_val.verify(true) {
fpm.run_on(&fn_val);
} else {
// NOTE: If this fails, uncomment the above println to debug.
panic!("Non-main function failed LLVM verification. Uncomment the above println to debug!");
panic!(
"Non-main function failed LLVM verification. Uncomment the above println to debug!"
);
}
}

View file

@ -58,7 +58,7 @@ impl Symbol {
.get_name(self.module_id())
.unwrap_or_else(|| {
panic!(
"module_string could not find IdentIds for {:?} in interns {:?}",
"module_string could not find IdentIds for module {:?} in {:?}",
self.module_id(),
interns
)
@ -71,7 +71,7 @@ impl Symbol {
.get(&self.module_id())
.unwrap_or_else(|| {
panic!(
"ident_string could not find IdentIds for {:?} in interns {:?}",
"ident_string could not find IdentIds for module {:?} in {:?}",
self.module_id(),
interns
)

View file

@ -1432,9 +1432,17 @@ pub fn specialize_all<'a>(
env: &mut Env<'a, '_>,
mut procs: Procs<'a>,
layout_cache: &mut LayoutCache<'a>,
) {
) -> (Vec<'a, (Symbol, Layout<'a>, Proc<'a>)>, MutSet<Symbol>) {
let mut answer = Vec::with_capacity_in(procs.pending_specializations.len(), env.arena);
let mut runtime_errors = MutSet::default();
let mut is_finished = procs.pending_specializations.is_empty();
// TODO replace this synchronous loop with a work-stealing queue which
// processes each entry in pending_specializations in parallel, one
// module at a time (because the &mut env will need exclusive access to
// that module's IdentIds; the only reason Env is &mut in specialize is
// that we need to generate unique symbols and register them in them module's
// IdentIds).
while !is_finished {
let Procs {
partial_procs,
@ -1457,10 +1465,10 @@ pub fn specialize_all<'a>(
match specialize(env, &mut procs, name, layout_cache, pending, partial_proc) {
Ok(proc) => {
// TODO stuff
answer.push((name, layout, proc));
}
Err(()) => {
// TODO runtime error
runtime_errors.insert(name);
}
}
}
@ -1468,6 +1476,8 @@ pub fn specialize_all<'a>(
is_finished = procs.pending_specializations.is_empty();
}
(answer, runtime_errors)
}
fn specialize<'a>(