diff --git a/compiler/gen/src/llvm/build.rs b/compiler/gen/src/llvm/build.rs index 197ede8e3c..553e843ed4 100644 --- a/compiler/gen/src/llvm/build.rs +++ b/compiler/gen/src/llvm/build.rs @@ -810,6 +810,24 @@ pub fn build_exp_expr<'a, 'ctx, 'env>( Literal(literal) => build_exp_literal(env, literal), RunLowLevel(op, symbols) => run_low_level(env, scope, parent, layout, *op, symbols), + ForeignCall(foreign_symbol, symbols) => { + let function = get_foreign_symbol(env, foreign_symbol.clone()); + + let mut arg_vals: Vec = Vec::with_capacity_in(symbols.len(), env.arena); + + for arg in symbols.iter() { + arg_vals.push(load_symbol(env, scope, arg)); + } + + 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), full_layout, @@ -3402,6 +3420,28 @@ fn cxa_rethrow_exception<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> BasicValu call.try_as_basic_value().left().unwrap() } +fn get_foreign_symbol<'a, 'ctx, 'env>( + env: &Env<'a, 'ctx, 'env>, + foreign_symbol: roc_module::ident::ForeignSymbol, +) -> FunctionValue<'ctx> { + let module = env.module; + let context = env.context; + + match module.get_function(foreign_symbol.as_str()) { + Some(gvalue) => gvalue, + None => { + let foreign_function = module.add_function( + foreign_symbol.as_str(), + context.i64_type().fn_type(&[], false), + Some(Linkage::External), + ); + foreign_function.set_call_conventions(C_CALL_CONV); + + foreign_function + } + } +} + fn get_gxx_personality_v0<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> FunctionValue<'ctx> { let name = "__gxx_personality_v0"; diff --git a/compiler/load/src/file.rs b/compiler/load/src/file.rs index 773a4a16bd..137f2586eb 100644 --- a/compiler/load/src/file.rs +++ b/compiler/load/src/file.rs @@ -2146,9 +2146,8 @@ fn fabricate_host_exposed_def<'a>( } // TODO figure out something better for run lowlevel - use roc_module::low_level::LowLevel; - let low_level_call = Expr::RunLowLevel { - op: LowLevel::Not, + let low_level_call = Expr::ForeignCall { + foreign_symbol: "roc_fx_put_char".into(), args: linked_symbol_arguments, ret_var: var_store.fresh(), }; diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index c447a30532..4717c9d599 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -3264,7 +3264,33 @@ pub fn with_hole<'a>( foreign_symbol, args, ret_var, - } => todo!(), + } => { + let mut arg_symbols = Vec::with_capacity_in(args.len(), env.arena); + + for (_, arg_expr) in args.iter() { + arg_symbols.push(possible_reuse_symbol(env, procs, &arg_expr)); + } + let arg_symbols = arg_symbols.into_bump_slice(); + + // layout of the return type + let layout = layout_cache + .from_var(env.arena, ret_var, env.subs) + .unwrap_or_else(|err| todo!("TODO turn fn_var into a RuntimeError {:?}", err)); + + let result = Stmt::Let( + assigned, + Expr::ForeignCall(foreign_symbol.clone(), arg_symbols), + layout, + hole, + ); + + let iter = args + .into_iter() + .rev() + .map(|(a, b)| (a, Located::at_zero(b))) + .zip(arg_symbols.iter().rev()); + assign_to_symbols(env, procs, layout_cache, iter, result) + } RunLowLevel { op, args, ret_var } => { let op = optimize_low_level(env.subs, op, &args);