diff --git a/compiler/gen/src/llvm/build.rs b/compiler/gen/src/llvm/build.rs index 99c7cd85cc..a32b2a1bc3 100644 --- a/compiler/gen/src/llvm/build.rs +++ b/compiler/gen/src/llvm/build.rs @@ -618,29 +618,66 @@ pub fn build_exp_expr<'a, 'ctx, 'env>( let mut arg_vals: Vec = Vec::with_capacity_in(arguments.len(), env.arena); - let mut arg_types = Vec::with_capacity_in(arguments.len(), env.arena); + let mut arg_types = Vec::with_capacity_in(arguments.len() + 1, env.arena); - for arg in arguments.iter() { - let (value, layout) = load_symbol_and_layout(env, scope, arg); - arg_vals.push(value); - let arg_type = - basic_type_from_layout(env.arena, env.context, layout, env.ptr_bytes); - arg_types.push(arg_type); + // crude approximation of the C calling convention + let pass_result_by_pointer = ret_layout.stack_size(env.ptr_bytes) > 2 * env.ptr_bytes; + + if pass_result_by_pointer { + // the return value is too big to pass through a register, so the caller must + // allocate space for it on its stack, and provide a pointer to write the result into + let ret_type = + basic_type_from_layout(env.arena, env.context, ret_layout, env.ptr_bytes); + + let ret_ptr_type = get_ptr_type(&ret_type, AddressSpace::Generic); + + let ret_ptr = env.builder.build_alloca(ret_type, "return_value"); + + arg_vals.push(ret_ptr.into()); + arg_types.push(ret_ptr_type.into()); + + for arg in arguments.iter() { + let (value, layout) = load_symbol_and_layout(env, scope, arg); + arg_vals.push(value); + let arg_type = + basic_type_from_layout(env.arena, env.context, layout, env.ptr_bytes); + arg_types.push(arg_type); + } + + let function_type = env.context.void_type().fn_type(&arg_types, false); + let function = get_foreign_symbol(env, foreign_symbol.clone(), function_type); + + let call = env.builder.build_call(function, arg_vals.as_slice(), "tmp"); + + // this is a foreign function, use c calling convention + call.set_call_convention(C_CALL_CONV); + + call.try_as_basic_value(); + + env.builder.build_load(ret_ptr, "read_result") + } else { + for arg in arguments.iter() { + let (value, layout) = load_symbol_and_layout(env, scope, arg); + arg_vals.push(value); + let arg_type = + basic_type_from_layout(env.arena, env.context, layout, env.ptr_bytes); + arg_types.push(arg_type); + } + + let ret_type = + basic_type_from_layout(env.arena, env.context, ret_layout, env.ptr_bytes); + let function_type = get_fn_type(&ret_type, &arg_types); + let function = get_foreign_symbol(env, foreign_symbol.clone(), function_type); + + let call = env.builder.build_call(function, arg_vals.as_slice(), "tmp"); + + // this is a foreign function, use c calling convention + call.set_call_convention(C_CALL_CONV); + + call.try_as_basic_value() + .left() + .unwrap_or_else(|| panic!("LLVM error: Invalid call by pointer.")) } - - let ret_type = - basic_type_from_layout(env.arena, env.context, ret_layout, env.ptr_bytes); - let function_type = get_fn_type(&ret_type, &arg_types); - let function = get_foreign_symbol(env, foreign_symbol.clone(), function_type); - - let call = env.builder.build_call(function, arg_vals.as_slice(), "tmp"); - - // this is a foreign function, use c calling convention - call.set_call_convention(C_CALL_CONV); - - call.try_as_basic_value() - .left() - .unwrap_or_else(|| panic!("LLVM error: Invalid call by pointer.")) } FunctionCall { call_type: ByName(name), diff --git a/compiler/load/src/file.rs b/compiler/load/src/file.rs index 7c1668807b..af237965a0 100644 --- a/compiler/load/src/file.rs +++ b/compiler/load/src/file.rs @@ -1773,7 +1773,7 @@ fn update<'a>( .notify(module_id, Phase::MakeSpecializations); if state.dependencies.solved_all() && state.goal_phase == Phase::MakeSpecializations { - debug_assert!(work.is_empty()); + debug_assert!(work.is_empty(), "still work remaining {:?}", &work); Proc::insert_refcount_operations(arena, &mut state.procedures);