diff --git a/cli/src/repl/gen.rs b/cli/src/repl/gen.rs index cb417541e8..76843c9d2d 100644 --- a/cli/src/repl/gen.rs +++ b/cli/src/repl/gen.rs @@ -179,7 +179,7 @@ pub fn gen_and_eval<'a>( interns, module, ptr_bytes, - leak: false, + is_gen_test: false, // important! we don't want any procedures to get the C calling convention exposed_to_host: MutSet::default(), }; diff --git a/compiler/build/src/program.rs b/compiler/build/src/program.rs index 047f936fb2..fc6af6b3e5 100644 --- a/compiler/build/src/program.rs +++ b/compiler/build/src/program.rs @@ -144,13 +144,12 @@ pub fn gen_from_mono_module( interns: loaded.interns, module, ptr_bytes, - leak: false, + // in gen_tests, the compiler provides roc_panic + // and sets up the setjump/longjump exception handling + is_gen_test: false, exposed_to_host: loaded.exposed_to_host.keys().copied().collect(), }; - // TODO remove for debug only - // roc_gen_llvm::llvm::externs::add_sjlj_roc_panic(&env); - roc_gen_llvm::llvm::build::build_procedures( &env, opt_level, @@ -161,6 +160,9 @@ pub fn gen_from_mono_module( env.dibuilder.finalize(); + // we don't use the debug info, and it causes weird errors. + module.strip_debug_info(); + // Uncomment this to see the module's optimized LLVM instruction output: // env.module.print_to_stderr(); diff --git a/compiler/gen_llvm/src/llvm/build.rs b/compiler/gen_llvm/src/llvm/build.rs index 772ebf8e16..073fffd982 100644 --- a/compiler/gen_llvm/src/llvm/build.rs +++ b/compiler/gen_llvm/src/llvm/build.rs @@ -155,7 +155,7 @@ pub struct Env<'a, 'ctx, 'env> { pub module: &'ctx Module<'ctx>, pub interns: Interns, pub ptr_bytes: u32, - pub leak: bool, + pub is_gen_test: bool, pub exposed_to_host: MutSet, } @@ -547,9 +547,6 @@ static LLVM_STACK_SAVE: &str = "llvm.stacksave"; static LLVM_SETJMP: &str = "llvm.eh.sjlj.setjmp"; pub static LLVM_LONGJMP: &str = "llvm.eh.sjlj.longjmp"; -// static LLVM_SETJMP: &str = "setjmp"; -// pub static LLVM_LONGJMP: &str = "longjmp"; - pub static LLVM_SADD_WITH_OVERFLOW_I8: &str = "llvm.sadd.with.overflow.i8"; pub static LLVM_SADD_WITH_OVERFLOW_I16: &str = "llvm.sadd.with.overflow.i16"; pub static LLVM_SADD_WITH_OVERFLOW_I32: &str = "llvm.sadd.with.overflow.i32"; @@ -3026,8 +3023,6 @@ fn expose_function_to_host_help<'a, 'ctx, 'env>( false, ); - let roc_wrapper_function = make_exception_catcher(env, roc_function); - // STEP 1: turn `f : a,b,c -> d` into `f : a,b,c, &d -> {}` let mut argument_types = roc_function.get_type().get_param_types(); let return_type = wrapper_return_type; @@ -3063,12 +3058,28 @@ fn expose_function_to_host_help<'a, 'ctx, 'env>( let args = &args[..args.len() - 1]; debug_assert_eq!(args.len(), roc_function.get_params().len()); - debug_assert_eq!(args.len(), roc_wrapper_function.get_params().len()); - let call_wrapped = builder.build_call(roc_wrapper_function, args, "call_wrapped_function"); - call_wrapped.set_call_convention(FAST_CALL_CONV); + let call_result = { + if env.is_gen_test { + let roc_wrapper_function = make_exception_catcher(env, roc_function); + debug_assert_eq!(args.len(), roc_wrapper_function.get_params().len()); - let call_result = call_wrapped.try_as_basic_value().left().unwrap(); + builder.position_at_end(entry); + + let call_wrapped = + builder.build_call(roc_wrapper_function, args, "call_wrapped_function"); + call_wrapped.set_call_convention(FAST_CALL_CONV); + + call_wrapped.try_as_basic_value().left().unwrap() + } else { + let call_unwrapped = builder.build_call(roc_function, args, "call_unwrapped_function"); + call_unwrapped.set_call_convention(FAST_CALL_CONV); + + let call_unwrapped_result = call_unwrapped.try_as_basic_value().left().unwrap(); + + make_good_roc_result(env, call_unwrapped_result) + } + }; let output_arg = c_function .get_nth_param(output_arg_index as u32) @@ -3140,20 +3151,6 @@ pub fn get_sjlj_message_buffer<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> Poi global.as_pointer_value() } -pub fn get_catcher_static<'a, 'ctx, 'env>( - env: &Env<'a, 'ctx, 'env>, - type_: BasicTypeEnum<'ctx>, -) -> PointerValue<'ctx> { - let global = match env.module.get_global("catcher_static") { - Some(global) => global, - None => env.module.add_global(type_, None, "catcher_static"), - }; - - global.set_initializer(&type_.const_zero()); - - global.as_pointer_value() -} - fn set_jump_and_catch_long_jump<'a, 'ctx, 'env, F, T>( env: &Env<'a, 'ctx, 'env>, parent: FunctionValue<'ctx>, @@ -3247,18 +3244,7 @@ where let call_result = call.try_as_basic_value().left().unwrap(); - let return_value = { - let v1 = call_result_type.const_zero(); - - let v2 = builder - .build_insert_value(v1, context.i64_type().const_zero(), 0, "set_no_error") - .unwrap(); - let v3 = builder - .build_insert_value(v2, call_result, 1, "set_call_result") - .unwrap(); - - v3 - }; + let return_value = make_good_roc_result(env, call_result); builder.build_store(result_alloca, return_value); @@ -3339,6 +3325,30 @@ fn make_exception_catcher<'a, 'ctx, 'env>( function_value } +fn make_good_roc_result<'a, 'ctx, 'env>( + env: &Env<'a, 'ctx, 'env>, + return_value: BasicValueEnum<'ctx>, +) -> BasicValueEnum<'ctx> { + let context = env.context; + let builder = env.builder; + + let content_type = return_value.get_type(); + let wrapper_return_type = + context.struct_type(&[context.i64_type().into(), content_type], false); + + let v1 = wrapper_return_type.const_zero(); + + let v2 = builder + .build_insert_value(v1, context.i64_type().const_zero(), 0, "set_no_error") + .unwrap(); + + let v3 = builder + .build_insert_value(v2, return_value, 1, "set_call_result") + .unwrap(); + + v3.into_struct_value().into() +} + fn make_exception_catching_wrapper<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, roc_function: FunctionValue<'ctx>, diff --git a/compiler/gen_llvm/src/llvm/refcounting.rs b/compiler/gen_llvm/src/llvm/refcounting.rs index 6aa30320ab..67f5a4b117 100644 --- a/compiler/gen_llvm/src/llvm/refcounting.rs +++ b/compiler/gen_llvm/src/llvm/refcounting.rs @@ -278,7 +278,7 @@ impl<'ctx> PointerToRefcount<'ctx> { // build then block { builder.position_at_end(then_block); - if !env.leak { + if !env.is_gen_test { let ptr = builder.build_pointer_cast( refcount_ptr.value, ctx.i8_type().ptr_type(AddressSpace::Generic), diff --git a/compiler/test_gen/src/gen_primitives.rs b/compiler/test_gen/src/gen_primitives.rs index b8ee1d2422..2171d6dca5 100644 --- a/compiler/test_gen/src/gen_primitives.rs +++ b/compiler/test_gen/src/gen_primitives.rs @@ -2393,7 +2393,6 @@ fn call_invalid_layout() { 3, i64, |x| x, - false, true ); } diff --git a/compiler/test_gen/src/helpers/eval.rs b/compiler/test_gen/src/helpers/eval.rs index 1f420eff40..ce0547432d 100644 --- a/compiler/test_gen/src/helpers/eval.rs +++ b/compiler/test_gen/src/helpers/eval.rs @@ -33,7 +33,7 @@ pub fn helper<'a>( arena: &'a bumpalo::Bump, src: &str, stdlib: &'a roc_builtins::std::StdLib, - leak: bool, + is_gen_test: bool, ignore_problems: bool, context: &'a inkwell::context::Context, ) -> (&'static str, String, Library) { @@ -212,7 +212,7 @@ pub fn helper<'a>( interns, module, ptr_bytes, - leak, + is_gen_test, // important! we don't want any procedures to get the C calling convention exposed_to_host: MutSet::default(), }; @@ -263,7 +263,7 @@ pub fn helper<'a>( #[macro_export] macro_rules! assert_llvm_evals_to { - ($src:expr, $expected:expr, $ty:ty, $transform:expr, $leak:expr, $ignore_problems:expr) => { + ($src:expr, $expected:expr, $ty:ty, $transform:expr, $ignore_problems:expr) => { use bumpalo::Bump; use inkwell::context::Context; use roc_gen_llvm::run_jit_function; @@ -274,8 +274,15 @@ macro_rules! assert_llvm_evals_to { // NOTE the stdlib must be in the arena; just taking a reference will segfault let stdlib = arena.alloc(roc_builtins::std::standard_stdlib()); - let (main_fn_name, errors, lib) = - $crate::helpers::eval::helper(&arena, $src, stdlib, $leak, $ignore_problems, &context); + let is_gen_test = true; + let (main_fn_name, errors, lib) = $crate::helpers::eval::helper( + &arena, + $src, + stdlib, + is_gen_test, + $ignore_problems, + &context, + ); let transform = |success| { let expected = $expected; @@ -287,7 +294,7 @@ macro_rules! assert_llvm_evals_to { }; ($src:expr, $expected:expr, $ty:ty, $transform:expr) => { - assert_llvm_evals_to!($src, $expected, $ty, $transform, true, false); + assert_llvm_evals_to!($src, $expected, $ty, $transform, false); }; } @@ -299,20 +306,7 @@ macro_rules! assert_evals_to { ($src:expr, $expected:expr, $ty:ty, $transform:expr) => { // Same as above, except with an additional transformation argument. { - assert_evals_to!($src, $expected, $ty, $transform, true); - } - }; - ($src:expr, $expected:expr, $ty:ty, $transform:expr, $leak:expr) => { - // Run un-optimized tests, and then optimized tests, in separate scopes. - // These each rebuild everything from scratch, starting with - // parsing the source, so that there's no chance their passing - // or failing depends on leftover state from the previous one. - { - assert_llvm_evals_to!($src, $expected, $ty, $transform, $leak, false); - } - { - // NOTE at the moment, the optimized tests do the same thing - // assert_opt_evals_to!($src, $expected, $ty, $transform, $leak); + assert_llvm_evals_to!($src, $expected, $ty, $transform, false); } }; } @@ -325,10 +319,10 @@ macro_rules! assert_non_opt_evals_to { ($src:expr, $expected:expr, $ty:ty, $transform:expr) => { // Same as above, except with an additional transformation argument. { - assert_llvm_evals_to!($src, $expected, $ty, $transform, true, false); + assert_llvm_evals_to!($src, $expected, $ty, $transform, false); } }; - ($src:expr, $expected:expr, $ty:ty, $transform:expr, $leak:expr) => {{ - assert_llvm_evals_to!($src, $expected, $ty, $transform, $leak); + ($src:expr, $expected:expr, $ty:ty, $transform:expr) => {{ + assert_llvm_evals_to!($src, $expected, $ty, $transform); }}; }