diff --git a/compiler/gen/src/llvm/build.rs b/compiler/gen/src/llvm/build.rs index 153b9ede9f..4c14b9963d 100644 --- a/compiler/gen/src/llvm/build.rs +++ b/compiler/gen/src/llvm/build.rs @@ -791,8 +791,33 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>( // This doesn't currently do anything context.i64_type().const_zero().into() } - Inc(symbol, cont) | Dec(symbol, cont) => { - build_exp_stmt(env, layout_ids, scope, parent, cont) + Inc(symbol, cont) => { + let (value, layout) = load_symbol_and_layout(env, scope, symbol); + let layout = layout.clone(); + // TODO exclude unique lists in the future + match layout { + Layout::Builtin(Builtin::List(_, _)) => { + increment_refcount_list(env, value.into_struct_value()); + build_exp_stmt(env, layout_ids, scope, parent, cont) + } + _ => build_exp_stmt(env, layout_ids, scope, parent, cont), + } + } + Dec(symbol, cont) => { + let (value, layout) = load_symbol_and_layout(env, scope, symbol); + let layout = layout.clone(); + // TODO exclude unique lists in the future + match layout { + Layout::Builtin(Builtin::List(_, _)) => decrement_refcount_list( + env, + layout_ids, + scope, + parent, + cont, + value.into_struct_value(), + ), + _ => build_exp_stmt(env, layout_ids, scope, parent, cont), + } } _ => todo!("unsupported expr {:?}", stmt), } @@ -854,8 +879,7 @@ fn list_get_refcount_ptr<'a, 'ctx, 'env>( fn increment_refcount_list<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, original_wrapper: StructValue<'ctx>, - body: BasicValueEnum<'ctx>, -) -> BasicValueEnum<'ctx> { +) { let builder = env.builder; let ctx = env.context; @@ -875,16 +899,16 @@ fn increment_refcount_list<'a, 'ctx, 'env>( // Mutate the new array in-place to change the element. builder.build_store(refcount_ptr, decremented); - - body } #[allow(dead_code)] fn decrement_refcount_list<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, + layout_ids: &mut LayoutIds<'a>, + scope: &mut Scope<'a, 'ctx>, parent: FunctionValue<'ctx>, + stmt: &roc_mono::ir::Stmt<'a>, original_wrapper: StructValue<'ctx>, - body: BasicValueEnum<'ctx>, ) -> BasicValueEnum<'ctx> { let builder = env.builder; let ctx = env.context; @@ -898,8 +922,16 @@ fn decrement_refcount_list<'a, 'ctx, 'env>( let comparison = refcount_is_one_comparison(builder, env.context, refcount); - // the refcount is higher than 1, write the decremented value - let build_then = || { + // build blocks + let then_block = ctx.append_basic_block(parent, "then"); + let else_block = ctx.append_basic_block(parent, "else"); + let cont_block = ctx.append_basic_block(parent, "branchcont"); + + builder.build_conditional_branch(comparison, then_block, else_block); + + // build then block + { + builder.position_at_end(then_block); // our refcount 0 is actually usize::MAX, so decrementing the refcount means incrementing this value. let decremented = env.builder.build_int_add( ctx.i64_type().const_int(1 as u64, false), @@ -910,21 +942,22 @@ fn decrement_refcount_list<'a, 'ctx, 'env>( // Mutate the new array in-place to change the element. builder.build_store(refcount_ptr, decremented); - body - }; + builder.build_unconditional_branch(cont_block); + } - // refcount is one, and will be decremented. This list can be freed - let build_else = || { + // build else block + { + builder.position_at_end(else_block); if !env.leak { let free = builder.build_free(refcount_ptr); builder.insert_instruction(&free, None); } + builder.build_unconditional_branch(cont_block); + } - body - }; - let ret_type = body.get_type(); - - build_basic_phi2(env, parent, comparison, build_then, build_else, ret_type) + // emit merge block + builder.position_at_end(cont_block); + build_exp_stmt(env, layout_ids, scope, parent, stmt) } fn load_symbol<'a, 'ctx, 'env>( @@ -1539,7 +1572,6 @@ fn call_with_args<'a, 'ctx, 'env>( .get(symbol, layout) .to_symbol_string(symbol, &env.interns); - dbg!(symbol, layout, &fn_name); let fn_val = env .module .get_function(fn_name.as_str()) diff --git a/compiler/gen/tests/gen_list.rs b/compiler/gen/tests/gen_list.rs index 6009310fad..1d78ba87fe 100644 --- a/compiler/gen/tests/gen_list.rs +++ b/compiler/gen/tests/gen_list.rs @@ -13,17 +13,10 @@ mod helpers; #[cfg(test)] mod gen_list { - use crate::helpers::{can_expr, infer_expr, uniq_expr, with_larger_debug_stack, CanExprOut}; + use crate::helpers::with_larger_debug_stack; use bumpalo::Bump; use inkwell::context::Context; use inkwell::execution_engine::JitFunction; - use inkwell::passes::PassManager; - use inkwell::types::BasicType; - use inkwell::OptimizationLevel; - use roc_gen::llvm::build::{build_proc, build_proc_header}; - use roc_gen::llvm::convert::basic_type_from_layout; - use roc_mono::layout::Layout; - use roc_types::subs::Subs; #[test] fn empty_list_literal() { diff --git a/compiler/gen/tests/helpers/eval.rs b/compiler/gen/tests/helpers/eval.rs index 284ca0d3c6..065a4c30a5 100644 --- a/compiler/gen/tests/helpers/eval.rs +++ b/compiler/gen/tests/helpers/eval.rs @@ -1,16 +1,35 @@ -// TODO this is almost all code duplication with assert_llvm_evals_to -// the only difference is that this calls uniq_expr instead of can_expr. -// Should extract the common logic into test helpers. -#[macro_export] -macro_rules! assert_opt_evals_to { - ($src:expr, $expected:expr, $ty:ty, $transform:expr, $leak:expr) => { - use roc_gen::llvm::build::Scope; +use roc_types::subs::Subs; - let arena = Bump::new(); - let target = target_lexicon::Triple::host(); - let ptr_bytes = target.pointer_width().unwrap().bytes() as u32; - let (loc_expr, _output, problems, subs, var, constraint, home, interns) = uniq_expr($src); - let errors = problems.into_iter().filter(|problem| { +pub fn helper_without_uniqueness<'a>( + arena: &'a bumpalo::Bump, + src: &str, + leak: bool, + context: &'a inkwell::context::Context, +) -> (&'static str, inkwell::execution_engine::ExecutionEngine<'a>) { + use crate::helpers::{can_expr, infer_expr, CanExprOut}; + use inkwell::passes::PassManager; + use inkwell::types::BasicType; + use inkwell::OptimizationLevel; + use roc_gen::llvm::build::Scope; + use roc_gen::llvm::build::{build_proc, build_proc_header}; + use roc_gen::llvm::convert::basic_type_from_layout; + use roc_mono::layout::Layout; + + let target = target_lexicon::Triple::host(); + let ptr_bytes = target.pointer_width().unwrap().bytes() as u32; + let CanExprOut { + loc_expr, + var_store, + var, + constraint, + home, + interns, + problems, + .. + } = can_expr(src); + let errors = problems + .into_iter() + .filter(|problem| { use roc_problem::can::Problem::*; // Ignore "unused" problems @@ -18,162 +37,375 @@ macro_rules! assert_opt_evals_to { UnusedDef(_, _) | UnusedArgument(_, _, _) | UnusedImport(_, _) => false, _ => true, } - }).collect::>(); + }) + .collect::>(); - assert_eq!(errors, Vec::new(), "Encountered errors: {:?}", errors); + assert_eq!(errors, Vec::new(), "Encountered errors: {:?}", errors); - let mut unify_problems = Vec::new(); - let (content, mut subs) = infer_expr(subs, &mut unify_problems, &constraint, var); + let subs = Subs::new(var_store.into()); + let mut unify_problems = Vec::new(); + let (content, mut subs) = infer_expr(subs, &mut unify_problems, &constraint, var); - assert_eq!(unify_problems, Vec::new(), "Encountered one or more type mismatches: {:?}", unify_problems); + assert_eq!( + unify_problems, + Vec::new(), + "Encountered type mismatches: {:?}", + unify_problems + ); + + let module = roc_gen::llvm::build::module_from_builtins(context, "app"); + let builder = context.create_builder(); + let opt_level = if cfg!(debug_assertions) { + roc_gen::llvm::build::OptLevel::Normal + } else { + roc_gen::llvm::build::OptLevel::Optimize + }; + let fpm = PassManager::create(&module); + + roc_gen::llvm::build::add_passes(&fpm, opt_level); + + fpm.initialize(); + + // Compute main_fn_type before moving subs to Env + let layout = Layout::new(&arena, content, &subs, ptr_bytes).unwrap_or_else(|err| { + panic!( + "Code gen error in NON-OPTIMIZED test: could not convert to layout. Err was {:?}", + err + ) + }); + let execution_engine = module + .create_jit_execution_engine(OptimizationLevel::None) + .expect("Error creating JIT execution engine for test"); + + let main_fn_type = + basic_type_from_layout(&arena, context, &layout, ptr_bytes).fn_type(&[], false); + let main_fn_name = "$Test.main"; + + // Compile and add all the Procs before adding main + let mut env = roc_gen::llvm::build::Env { + arena: &arena, + builder: &builder, + context: context, + interns, + module: arena.alloc(module), + ptr_bytes, + leak: leak, + }; + let mut procs = roc_mono::ir::Procs::default(); + let mut ident_ids = env.interns.all_ident_ids.remove(&home).unwrap(); + let mut layout_ids = roc_gen::layout_id::LayoutIds::default(); + + // Populate Procs and get the low-level Expr from the canonical Expr + let mut mono_problems = Vec::new(); + let mut mono_env = roc_mono::ir::Env { + arena: &arena, + subs: &mut subs, + problems: &mut mono_problems, + home, + ident_ids: &mut ident_ids, + pointer_size: ptr_bytes, + jump_counter: arena.alloc(0), + }; + + let main_body = roc_mono::ir::Stmt::new(&mut mono_env, loc_expr.value, &mut procs); + let mut headers = { + let num_headers = match &procs.pending_specializations { + Some(map) => map.len(), + None => 0, + }; + + Vec::with_capacity(num_headers) + }; + let mut layout_cache = roc_mono::layout::LayoutCache::default(); + let procs = roc_mono::ir::specialize_all(&mut mono_env, procs, &mut layout_cache); + + assert_eq!( + procs.runtime_errors, + roc_collections::all::MutMap::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, layout), proc) in procs.to_specialized_procs(env.arena).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 { + build_proc(&env, &mut layout_ids, proc, fn_val, arg_basic_types); + + if fn_val.verify(true) { + fpm.run_on(&fn_val); + } else { + eprintln!( + "\n\nFunction {:?} failed LLVM verification in NON-OPTIMIZED build. Its content was:\n", fn_val.get_name().to_str().unwrap() + ); + + fn_val.print_to_stderr(); + + panic!( + "The preceding code was from {:?}, which failed LLVM verification in NON-OPTIMIZED build.", fn_val.get_name().to_str().unwrap() + ); + } + } + + // Add main to the module. + let main_fn = env.module.add_function(main_fn_name, main_fn_type, None); + let cc = + roc_gen::llvm::build::get_call_conventions(target.default_calling_convention().unwrap()); + + main_fn.set_call_conventions(cc); + + // Add main's body + let basic_block = context.append_basic_block(main_fn, "entry"); + + builder.position_at_end(basic_block); + + let ret = roc_gen::llvm::build::build_exp_stmt( + &env, + &mut layout_ids, + &mut Scope::default(), + main_fn, + &main_body, + ); + + builder.build_return(Some(&ret)); + + // Uncomment this to see the module's un-optimized LLVM instruction output: + // env.module.print_to_stderr(); + + if main_fn.verify(true) { + fpm.run_on(&main_fn); + } else { + panic!("Main function {} failed LLVM verification in NON-OPTIMIZED build. Uncomment things nearby to see more details.", main_fn_name); + } + + // Verify the module + if let Err(errors) = env.module.verify() { + panic!("Errors defining module: {:?}", errors); + } + + // Uncomment this to see the module's optimized LLVM instruction output: + // env.module.print_to_stderr(); + + (main_fn_name, execution_engine.clone()) +} + +pub fn helper_with_uniqueness<'a>( + arena: &'a bumpalo::Bump, + src: &str, + leak: bool, + context: &'a inkwell::context::Context, +) -> (&'static str, inkwell::execution_engine::ExecutionEngine<'a>) { + use crate::helpers::{infer_expr, uniq_expr}; + use inkwell::passes::PassManager; + use inkwell::types::BasicType; + use inkwell::OptimizationLevel; + use roc_gen::llvm::build::Scope; + use roc_gen::llvm::build::{build_proc, build_proc_header}; + use roc_gen::llvm::convert::basic_type_from_layout; + use roc_mono::layout::Layout; + + let target = target_lexicon::Triple::host(); + let ptr_bytes = target.pointer_width().unwrap().bytes() as u32; + let (loc_expr, _output, problems, subs, var, constraint, home, interns) = uniq_expr(src); + let errors = problems + .into_iter() + .filter(|problem| { + use roc_problem::can::Problem::*; + + // Ignore "unused" problems + match problem { + UnusedDef(_, _) | UnusedArgument(_, _, _) | UnusedImport(_, _) => false, + _ => true, + } + }) + .collect::>(); + + assert_eq!(errors, Vec::new(), "Encountered errors: {:?}", errors); + + let mut unify_problems = Vec::new(); + let (content, mut subs) = infer_expr(subs, &mut unify_problems, &constraint, var); + + assert_eq!( + unify_problems, + Vec::new(), + "Encountered one or more type mismatches: {:?}", + unify_problems + ); + + let module = roc_gen::llvm::build::module_from_builtins(context, "app"); + let builder = context.create_builder(); + let opt_level = if cfg!(debug_assertions) { + roc_gen::llvm::build::OptLevel::Normal + } else { + roc_gen::llvm::build::OptLevel::Optimize + }; + let fpm = PassManager::create(&module); + + roc_gen::llvm::build::add_passes(&fpm, opt_level); + + fpm.initialize(); + + // Compute main_fn_type before moving subs to Env + let layout = Layout::new(&arena, content, &subs, ptr_bytes).unwrap_or_else(|err| { + panic!( + "Code gen error in OPTIMIZED test: could not convert to layout. Err was {:?}", + err + ) + }); + + let execution_engine = module + .create_jit_execution_engine(OptimizationLevel::None) + .expect("Error creating JIT execution engine for test"); + + let main_fn_type = basic_type_from_layout(&arena, context, &layout, ptr_bytes) + .fn_type(&[], false) + .clone(); + let main_fn_name = "$Test.main"; + + // Compile and add all the Procs before adding main + let mut env = roc_gen::llvm::build::Env { + arena: &arena, + builder: &builder, + context: context, + interns, + module: arena.alloc(module), + ptr_bytes, + leak: leak, + }; + let mut procs = roc_mono::ir::Procs::default(); + let mut ident_ids = env.interns.all_ident_ids.remove(&home).unwrap(); + let mut layout_ids = roc_gen::layout_id::LayoutIds::default(); + + // Populate Procs and get the low-level Expr from the canonical Expr + let mut mono_problems = Vec::new(); + let mut mono_env = roc_mono::ir::Env { + arena: &arena, + subs: &mut subs, + problems: &mut mono_problems, + home, + ident_ids: &mut ident_ids, + pointer_size: ptr_bytes, + jump_counter: arena.alloc(0), + }; + + let main_body = roc_mono::ir::Stmt::new(&mut mono_env, loc_expr.value, &mut procs); + let mut headers = { + let num_headers = match &procs.pending_specializations { + Some(map) => map.len(), + None => 0, + }; + + Vec::with_capacity(num_headers) + }; + let mut layout_cache = roc_mono::layout::LayoutCache::default(); + let procs = roc_mono::ir::specialize_all(&mut mono_env, procs, &mut layout_cache); + + assert_eq!( + procs.runtime_errors, + roc_collections::all::MutMap::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, layout), proc) in procs.to_specialized_procs(env.arena).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 { + build_proc(&env, &mut layout_ids, proc, fn_val, arg_basic_types); + + if fn_val.verify(true) { + fpm.run_on(&fn_val); + } else { + eprintln!( + "\n\nFunction {:?} failed LLVM verification in OPTIMIZED build. Its content was:\n", + fn_val.get_name().to_str().unwrap() + ); + + fn_val.print_to_stderr(); + + panic!( + "The preceding code was from {:?}, which failed LLVM verification in OPTIMIZED build.", fn_val.get_name().to_str().unwrap() + ); + } + } + + // Add main to the module. + let main_fn = env.module.add_function(main_fn_name, main_fn_type, None); + let cc = + roc_gen::llvm::build::get_call_conventions(target.default_calling_convention().unwrap()); + + main_fn.set_call_conventions(cc); + + // Add main's body + let basic_block = context.append_basic_block(main_fn, "entry"); + + builder.position_at_end(basic_block); + + let ret = roc_gen::llvm::build::build_exp_stmt( + &env, + &mut layout_ids, + &mut Scope::default(), + main_fn, + &main_body, + ); + + builder.build_return(Some(&ret)); + + // Uncomment this to see the module's un-optimized LLVM instruction output: + // env.module.print_to_stderr(); + + if main_fn.verify(true) { + fpm.run_on(&main_fn); + } else { + panic!("main function {} failed LLVM verification in OPTIMIZED build. Uncomment nearby statements to see more details.", main_fn_name); + } + + // Verify the module + if let Err(errors) = env.module.verify() { + panic!("Errors defining module: {:?}", errors); + } + + // Uncomment this to see the module's optimized LLVM instruction output: + // env.module.print_to_stderr(); + + (main_fn_name, execution_engine) +} + +// TODO this is almost all code duplication with assert_llvm_evals_to +// the only difference is that this calls uniq_expr instead of can_expr. +// Should extract the common logic into test helpers. +#[macro_export] +macro_rules! assert_opt_evals_to { + ($src:expr, $expected:expr, $ty:ty, $transform:expr, $leak:expr) => { + let arena = Bump::new(); let context = Context::create(); - let module = roc_gen::llvm::build::module_from_builtins(&context, "app"); - let builder = context.create_builder(); - let opt_level = if cfg!(debug_assertions) { - roc_gen::llvm::build::OptLevel::Normal - } else { - roc_gen::llvm::build::OptLevel::Optimize - }; - let fpm = PassManager::create(&module); - roc_gen::llvm::build::add_passes(&fpm, opt_level); - - fpm.initialize(); - - // Compute main_fn_type before moving subs to Env - let layout = Layout::new(&arena, content, &subs, ptr_bytes) - .unwrap_or_else(|err| panic!("Code gen error in OPTIMIZED test: could not convert to layout. Err was {:?}", err)); - - let execution_engine = - module - .create_jit_execution_engine(OptimizationLevel::None) - .expect("Error creating JIT execution engine for test"); - - let main_fn_type = basic_type_from_layout(&arena, &context, &layout, ptr_bytes) - .fn_type(&[], false); - let main_fn_name = "$Test.main"; - - // Compile and add all the Procs before adding main - let mut env = roc_gen::llvm::build::Env { - arena: &arena, - builder: &builder, - context: &context, - interns, - module: arena.alloc(module), - ptr_bytes, - leak: $leak - }; - let mut procs = roc_mono::ir::Procs::default(); - let mut ident_ids = env.interns.all_ident_ids.remove(&home).unwrap(); - let mut layout_ids = roc_gen::layout_id::LayoutIds::default(); - - // Populate Procs and get the low-level Expr from the canonical Expr - let mut mono_problems = Vec::new(); - let mut mono_env = roc_mono::ir::Env { - arena: &arena, - subs: &mut subs, - problems: &mut mono_problems, - home, - ident_ids: &mut ident_ids, - pointer_size: ptr_bytes, - jump_counter: arena.alloc(0), - }; - - let main_body = roc_mono::ir::Stmt::new(&mut mono_env, loc_expr.value, &mut procs); - let mut headers = { - let num_headers = match &procs.pending_specializations { - Some(map) => map.len(), - None => 0 - }; - - Vec::with_capacity(num_headers) - }; - let mut layout_cache = roc_mono::layout::LayoutCache::default(); - let mut procs = roc_mono::ir::specialize_all(&mut mono_env, procs, &mut layout_cache); - - assert_eq!(procs.runtime_errors, roc_collections::all::MutMap::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, layout), proc) in procs.specialized.drain() { - use roc_mono::ir::InProgressProc::*; - - match proc { - InProgress => { - panic!("A specialization was still marked InProgress after monomorphization had completed: {:?} with layout {:?}", symbol, layout); - } - Done(proc) => { - 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 { - build_proc(&env, &mut layout_ids, proc, fn_val, arg_basic_types); - - if fn_val.verify(true) { - fpm.run_on(&fn_val); - } else { - eprintln!( - "\n\nFunction {:?} failed LLVM verification in OPTIMIZED build. Its content was:\n", fn_val.get_name().to_str().unwrap() - ); - - fn_val.print_to_stderr(); - - panic!( - "The preceding code was from {:?}, which failed LLVM verification in OPTIMIZED build.", fn_val.get_name().to_str().unwrap() - ); - } - } - - // Add main to the module. - let main_fn = env.module.add_function(main_fn_name, main_fn_type, None); - let cc = roc_gen::llvm::build::get_call_conventions(target.default_calling_convention().unwrap()); - - main_fn.set_call_conventions(cc); - - // Add main's body - let basic_block = context.append_basic_block(main_fn, "entry"); - - builder.position_at_end(basic_block); - - let ret = roc_gen::llvm::build::build_exp_stmt( - &env, - &mut layout_ids, - &mut Scope::default(), - main_fn, - &main_body, - ); - - builder.build_return(Some(&ret)); - - // Uncomment this to see the module's un-optimized LLVM instruction output: - // env.module.print_to_stderr(); - - if main_fn.verify(true) { - fpm.run_on(&main_fn); - } else { - panic!("main function {} failed LLVM verification in OPTIMIZED build. Uncomment nearby statements to see more details.", main_fn_name); - } - - // Verify the module - if let Err(errors) = env.module.verify() { - panic!("Errors defining module: {:?}", errors); - } - - // Uncomment this to see the module's optimized LLVM instruction output: - // env.module.print_to_stderr(); + let (main_fn_name, execution_engine) = + $crate::helpers::eval::helper_with_uniqueness(&arena, $src, $leak, &context); unsafe { let main: JitFunction $ty> = execution_engine @@ -194,169 +426,12 @@ macro_rules! assert_opt_evals_to { #[macro_export] macro_rules! assert_llvm_evals_to { ($src:expr, $expected:expr, $ty:ty, $transform:expr, $leak:expr) => { - let target = target_lexicon::Triple::host(); - let ptr_bytes = target.pointer_width().unwrap().bytes() as u32; let arena = Bump::new(); - let CanExprOut { loc_expr, var_store, var, constraint, home, interns, problems, .. } = can_expr($src); - let errors = problems.into_iter().filter(|problem| { - use roc_problem::can::Problem::*; - - // Ignore "unused" problems - match problem { - UnusedDef(_, _) | UnusedArgument(_, _, _) | UnusedImport(_, _) => false, - _ => true, - } - }).collect::>(); - - assert_eq!(errors, Vec::new(), "Encountered errors: {:?}", errors); - - let subs = Subs::new(var_store.into()); - let mut unify_problems = Vec::new(); - let (content, mut subs) = infer_expr(subs, &mut unify_problems, &constraint, var); - - assert_eq!(unify_problems, Vec::new(), "Encountered type mismatches: {:?}", unify_problems); let context = Context::create(); - let module = roc_gen::llvm::build::module_from_builtins(&context, "app"); - let builder = context.create_builder(); - let opt_level = if cfg!(debug_assertions) { - roc_gen::llvm::build::OptLevel::Normal - } else { - roc_gen::llvm::build::OptLevel::Optimize - }; - let fpm = PassManager::create(&module); - roc_gen::llvm::build::add_passes(&fpm, opt_level); - - fpm.initialize(); - - // Compute main_fn_type before moving subs to Env - let layout = Layout::new(&arena, content, &subs, ptr_bytes) - .unwrap_or_else(|err| panic!("Code gen error in NON-OPTIMIZED test: could not convert to layout. Err was {:?}", err)); - let execution_engine = - module - .create_jit_execution_engine(OptimizationLevel::None) - .expect("Error creating JIT execution engine for test"); - - let main_fn_type = basic_type_from_layout(&arena, &context, &layout, ptr_bytes) - .fn_type(&[], false); - let main_fn_name = "$Test.main"; - - // Compile and add all the Procs before adding main - let mut env = roc_gen::llvm::build::Env { - arena: &arena, - builder: &builder, - context: &context, - interns, - module: arena.alloc(module), - ptr_bytes, - leak: $leak - - }; - let mut procs = roc_mono::ir::Procs::default(); - let mut ident_ids = env.interns.all_ident_ids.remove(&home).unwrap(); - let mut layout_ids = roc_gen::layout_id::LayoutIds::default(); - - // Populate Procs and get the low-level Expr from the canonical Expr - let mut mono_problems = Vec::new(); - let mut mono_env = roc_mono::ir::Env { - arena: &arena, - subs: &mut subs, - problems: &mut mono_problems, - home, - ident_ids: &mut ident_ids, - pointer_size: ptr_bytes, - jump_counter: arena.alloc(0), - }; - - let main_body = roc_mono::ir::Stmt::new(&mut mono_env, loc_expr.value, &mut procs); - let mut headers = { - let num_headers = match &procs.pending_specializations { - Some(map) => map.len(), - None => 0 - }; - - Vec::with_capacity(num_headers) - }; - let mut layout_cache = roc_mono::layout::LayoutCache::default(); - let mut procs = roc_mono::ir::specialize_all(&mut mono_env, procs, &mut layout_cache); - - assert_eq!(procs.runtime_errors, roc_collections::all::MutMap::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); - - use roc_gen::llvm::build::{build_proc_header, build_proc }; - // 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, layout), proc) in procs.to_specialized_procs(env.arena).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 { - build_proc(&env, &mut layout_ids, proc, fn_val, arg_basic_types); - - if fn_val.verify(true) { - fpm.run_on(&fn_val); - } else { - eprintln!( - "\n\nFunction {:?} failed LLVM verification in NON-OPTIMIZED build. Its content was:\n", fn_val.get_name().to_str().unwrap() - ); - - fn_val.print_to_stderr(); - - panic!( - "The preceding code was from {:?}, which failed LLVM verification in NON-OPTIMIZED build.", fn_val.get_name().to_str().unwrap() - ); - } - } - - // Add main to the module. - let main_fn = env.module.add_function(main_fn_name, main_fn_type, None); - let cc = roc_gen::llvm::build::get_call_conventions(target.default_calling_convention().unwrap()); - - main_fn.set_call_conventions(cc); - - // Add main's body - let basic_block = context.append_basic_block(main_fn, "entry"); - - builder.position_at_end(basic_block); - - use roc_gen::llvm::build::Scope; - let ret = roc_gen::llvm::build::build_exp_stmt( - &env, - &mut layout_ids, - &mut Scope::default(), - main_fn, - &main_body, - ); - - builder.build_return(Some(&ret)); - - // Uncomment this to see the module's un-optimized LLVM instruction output: - // env.module.print_to_stderr(); - - if main_fn.verify(true) { - fpm.run_on(&main_fn); - } else { - panic!("Main function {} failed LLVM verification in NON-OPTIMIZED build. Uncomment things nearby to see more details.", main_fn_name); - } - - // Verify the module - if let Err(errors) = env.module.verify() { - panic!("Errors defining module: {:?}", errors); - } - - // Uncomment this to see the module's optimized LLVM instruction output: - // env.module.print_to_stderr(); + let (main_fn_name, execution_engine) = + $crate::helpers::eval::helper_without_uniqueness(&arena, $src, $leak, &context); unsafe { let main: JitFunction $ty> = execution_engine diff --git a/compiler/mono/src/inc_dec.rs b/compiler/mono/src/inc_dec.rs index 28e5b7ca07..639e3e2727 100644 --- a/compiler/mono/src/inc_dec.rs +++ b/compiler/mono/src/inc_dec.rs @@ -271,11 +271,16 @@ impl<'a> Context<'a> { // number of times the argument is used (in the body?) let num_consumptions = get_num_consumptions(*x, xs, consume_param_pred.clone()); - let num_incs = if !info.consume || // `x` is not a variable that must be consumed by the current procedure - live_vars_after.contains(x) || // `x` is live after executing instruction - is_borrow_param_help(*x, xs, consume_param_pred.clone()) - // `x` is used in a position that is passed as a borrow reference - { + let lives_on = + // `x` is not a variable that must be consumed by the current procedure + !info.consume || + // `x` is live after executing instruction + live_vars_after.contains(x) || + // `x` is used in a position that is passed as a borrow reference + is_borrow_param_help(*x, xs, consume_param_pred.clone()); + + + let num_incs = if lives_on { num_consumptions } else { num_consumptions - 1 @@ -450,9 +455,9 @@ impl<'a> Context<'a> { FunctionCall { args: ys, - ref layout, call_type, arg_layouts, + .. } => { // this is where the borrow signature would come in //let ps := (getDecl ctx f).params; diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index d64c2ad0f9..edcc879d6e 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -96,9 +96,9 @@ impl<'a> Procs<'a> { for (key, in_prog_proc) in self.specialized.into_iter() { match in_prog_proc { - InProgress => unreachable!("should be done by now"), + InProgress => unreachable!("The procedure {:?} should have be done by now", key), Done(mut proc) => { - crate::inc_dec::visit_proc(arena, &mut proc).clone(); + crate::inc_dec::visit_proc(arena, &mut proc); result.insert(key, proc); } }