diff --git a/compiler/build/src/program.rs b/compiler/build/src/program.rs index 2f22ebc32d..6797088b4a 100644 --- a/compiler/build/src/program.rs +++ b/compiler/build/src/program.rs @@ -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,24 +225,27 @@ 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() { - 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)); - } + headers.push((proc, fn_val, arg_basic_types)); } // Build each proc using its header info. diff --git a/compiler/gen/tests/helpers/eval.rs b/compiler/gen/tests/helpers/eval.rs index 6a42c8d117..a882c2a8f1 100644 --- a/compiler/gen/tests/helpers/eval.rs +++ b/compiler/gen/tests/helpers/eval.rs @@ -75,23 +75,27 @@ 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)); - } + headers.push((proc, fn_val, arg_basic_types)); } // Build each proc using its header info. @@ -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,23 +246,27 @@ 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)); - } + headers.push((proc, fn_val, arg_basic_types)); } // Build each proc using its header info. @@ -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,20 +410,27 @@ 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)); - } + headers.push((proc, fn_val, arg_basic_types)); } // Build each proc using its header info. @@ -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!" + ); } } diff --git a/compiler/module/src/symbol.rs b/compiler/module/src/symbol.rs index 735a43de61..bca6e35f57 100644 --- a/compiler/module/src/symbol.rs +++ b/compiler/module/src/symbol.rs @@ -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 ) diff --git a/compiler/mono/src/expr.rs b/compiler/mono/src/expr.rs index 21a50dc9e6..fdb9129704 100644 --- a/compiler/mono/src/expr.rs +++ b/compiler/mono/src/expr.rs @@ -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) { + 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>(