From cb28e533b82e2758045336bbc23d437df8673e25 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 4 Nov 2020 23:04:52 +0100 Subject: [PATCH] so close --- compiler/build/src/program.rs | 2 +- compiler/gen/src/llvm/build.rs | 133 ++++++++++++++++++++++++++- compiler/load/src/file.rs | 2 - compiler/mono/src/ir.rs | 91 ++++++++++++------ examples/closure/Closure.roc | 6 +- examples/closure/platform/src/lib.rs | 50 +++++++--- 6 files changed, 234 insertions(+), 50 deletions(-) diff --git a/compiler/build/src/program.rs b/compiler/build/src/program.rs index 2836704ae7..9eb2b5cfd5 100644 --- a/compiler/build/src/program.rs +++ b/compiler/build/src/program.rs @@ -125,7 +125,7 @@ pub fn gen_from_mono_module( } // Uncomment this to see the module's optimized LLVM instruction output: - // env.module.print_to_stderr(); + env.module.print_to_stderr(); mpm.run_on(module); diff --git a/compiler/gen/src/llvm/build.rs b/compiler/gen/src/llvm/build.rs index 42b767024a..6df18a3329 100644 --- a/compiler/gen/src/llvm/build.rs +++ b/compiler/gen/src/llvm/build.rs @@ -34,7 +34,7 @@ use roc_collections::all::{ImMap, MutSet}; use roc_module::low_level::LowLevel; use roc_module::symbol::{Interns, ModuleId, Symbol}; use roc_mono::ir::{JoinPointId, Wrapped}; -use roc_mono::layout::{Builtin, Layout, MemoryMode}; +use roc_mono::layout::{Builtin, ClosureLayout, Layout, MemoryMode}; use target_lexicon::CallingConvention; /// This is for Inkwell's FunctionValue::verify - we want to know the verification @@ -2099,6 +2099,27 @@ pub fn build_proc_header<'a, 'ctx, 'env>( let arena = env.arena; let context = &env.context; + let fn_name = layout_ids + .get(symbol, layout) + .to_symbol_string(symbol, &env.interns); + + use roc_mono::ir::HostExposedLayouts; + match &proc.host_exposed_layouts { + HostExposedLayouts::NotHostExposed => {} + HostExposedLayouts::HostExposed { rigids: _, aliases } => { + for (name, layout) in aliases { + match layout { + Layout::Closure(arguments, closure, result) => { + build_closure_caller(env, &fn_name, *name, arguments, closure, result) + } + _ => { + // TODO + } + } + } + } + } + let ret_type = basic_type_from_layout(arena, context, &proc.ret_layout, env.ptr_bytes); let mut arg_basic_types = Vec::with_capacity_in(args.len(), arena); @@ -2110,9 +2131,6 @@ pub fn build_proc_header<'a, 'ctx, 'env>( let fn_type = get_fn_type(&ret_type, &arg_basic_types); - let fn_name = layout_ids - .get(symbol, layout) - .to_symbol_string(symbol, &env.interns); let fn_val = env .module .add_function(fn_name.as_str(), fn_type, Some(Linkage::Private)); @@ -2126,8 +2144,113 @@ pub fn build_proc_header<'a, 'ctx, 'env>( fn_val } -#[allow(dead_code)] pub fn build_closure_caller<'a, 'ctx, 'env>( + env: &'a Env<'a, 'ctx, 'env>, + def_name: &str, + alias_symbol: Symbol, + arguments: &[Layout<'a>], + closure: &ClosureLayout<'a>, + result: &Layout<'a>, +) { + use inkwell::types::BasicType; + + let arena = env.arena; + let context = &env.context; + let builder = env.builder; + + let function_name = format!( + "{}_{}_caller", + def_name, + alias_symbol.ident_string(&env.interns) + ); + + let mut argument_types = Vec::with_capacity_in(arguments.len() + 3, env.arena); + + for layout in arguments { + argument_types.push(basic_type_from_layout( + arena, + context, + layout, + env.ptr_bytes, + )); + } + + let function_pointer_type = { + let function_layout = + ClosureLayout::extend_function_layout(arena, arguments, closure.clone(), result); + let basic_type = basic_type_from_layout(arena, context, &function_layout, env.ptr_bytes); + + // this is already a (function) pointer type + basic_type + }; + argument_types.push(function_pointer_type.into()); + + let closure_argument_type = { + let basic_type = basic_type_from_layout( + arena, + context, + &closure.as_block_of_memory_layout(), + env.ptr_bytes, + ); + + basic_type.ptr_type(AddressSpace::Generic) + }; + argument_types.push(closure_argument_type.into()); + + let result_type = basic_type_from_layout(arena, context, result, env.ptr_bytes); + + let roc_call_result_type = + context.struct_type(&[context.i64_type().into(), result_type], false); + + let output_type = { roc_call_result_type.ptr_type(AddressSpace::Generic) }; + argument_types.push(output_type.into()); + + let function_type = context.void_type().fn_type(&argument_types, false); + + let function_value = env.module.add_function( + function_name.as_str(), + function_type, + Some(Linkage::External), + ); + + function_value.set_call_conventions(C_CALL_CONV); + + // BUILD FUNCTION BODY + + let entry = context.append_basic_block(function_value, "entry"); + + builder.position_at_end(entry); + + let mut parameters = function_value.get_params(); + let output = parameters.pop().unwrap().into_pointer_value(); + let closure_data_ptr = parameters.pop().unwrap().into_pointer_value(); + let function_ptr = parameters.pop().unwrap().into_pointer_value(); + + let closure_data = builder.build_load(closure_data_ptr, "load_closure_data"); + + let mut arguments = parameters; + arguments.push(closure_data); + let call = builder.build_call(function_ptr, &arguments, "call_closure"); + call.set_call_convention(FAST_CALL_CONV); + + let result = call.try_as_basic_value().left().unwrap(); + + unsafe { + let tag_ptr = builder.build_struct_gep(output, 0, "gep"); + let result_ptr = builder.build_struct_gep(output, 1, "gep"); + + // let v33 = context.i64_type().const_int(33, false); + // builder.build_store(tag_ptr, v33); + builder.build_store(result_ptr, result); + } + + //builder.build_store(output, roc_call_result); + + builder.build_return(None); +} + +#[allow(dead_code)] +pub fn build_closure_caller_old<'a, 'ctx, 'env>( env: &'a Env<'a, 'ctx, 'env>, closure_function: FunctionValue<'ctx>, ) { diff --git a/compiler/load/src/file.rs b/compiler/load/src/file.rs index 88af416b3c..fd4a092df0 100644 --- a/compiler/load/src/file.rs +++ b/compiler/load/src/file.rs @@ -2331,7 +2331,6 @@ fn add_def_to_module<'a>( ) { use roc_can::expr::Expr::*; use roc_can::pattern::Pattern::*; - use roc_mono::ir::HostExposedVariables; match def.loc_pattern.value { Identifier(symbol) => { @@ -2425,7 +2424,6 @@ fn add_def_to_module<'a>( body, // This is a 0-arity thunk, so it cannot be recursive is_self_recursive: false, - host_exposed_variables: HostExposedVariables::default(), }; procs.partial_procs.insert(symbol, proc); diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index ec35465ec7..3ff462ff8f 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -26,7 +26,6 @@ pub struct PartialProc<'a> { pub captured_symbols: CapturedSymbols<'a>, pub body: roc_can::expr::Expr, pub is_self_recursive: bool, - pub host_exposed_variables: HostExposedVariables, } #[derive(Clone, Debug, PartialEq, Default)] @@ -382,7 +381,6 @@ impl<'a> Procs<'a> { captured_symbols, body: body.value, is_self_recursive, - host_exposed_variables: HostExposedVariables::default(), }, ); } @@ -456,7 +454,6 @@ impl<'a> Procs<'a> { captured_symbols, body: body.value, is_self_recursive, - host_exposed_variables: HostExposedVariables::default(), }, ); } @@ -468,7 +465,6 @@ impl<'a> Procs<'a> { captured_symbols, body: body.value, is_self_recursive, - host_exposed_variables: HostExposedVariables::default(), }; // Mark this proc as in-progress, so if we're dealing with @@ -1351,6 +1347,7 @@ pub fn specialize_all<'a>( name, layout_cache, solved_type, + MutMap::default(), partial_proc, ) { Ok((proc, layout)) => { @@ -1436,6 +1433,7 @@ fn specialize_external<'a>( proc_name: Symbol, layout_cache: &mut LayoutCache<'a>, fn_var: Variable, + host_exposed_variables: &[(Symbol, Variable)], partial_proc: PartialProc<'a>, ) -> Result, LayoutProblem> { let PartialProc { @@ -1444,7 +1442,6 @@ fn specialize_external<'a>( captured_symbols, body, is_self_recursive, - host_exposed_variables, } = partial_proc; // unify the called function with the specialized signature, then specialize the function body @@ -1501,12 +1498,23 @@ fn specialize_external<'a>( } } - // - let host_exposed_layouts = { - if host_exposed_variables == HostExposedVariables::default() { - HostExposedLayouts::NotHostExposed - } else { - todo!() + // TODO host exposed thngs + // host_exposed_variables: &[(Symbol, Variable)], + let host_exposed_layouts = if host_exposed_variables.is_empty() { + HostExposedLayouts::NotHostExposed + } else { + let mut aliases = MutMap::default(); + + for (symbol, variable) in host_exposed_variables { + let layout = layout_cache + .from_var(env.arena, *variable, env.subs) + .unwrap(); + aliases.insert(*symbol, layout); + } + + HostExposedLayouts::HostExposed { + rigids: MutMap::default(), + aliases, } }; @@ -1764,44 +1772,71 @@ fn specialize<'a>( proc_name, layout_cache, solved_type, + host_exposed_aliases, partial_proc, ) } +fn introduce_solved_type_to_subs<'a>(env: &mut Env<'a, '_>, solved_type: &SolvedType) -> Variable { + use roc_solve::solve::insert_type_into_subs; + use roc_types::solved_types::{to_type, FreeVars}; + use roc_types::subs::VarStore; + let mut free_vars = FreeVars::default(); + let mut var_store = VarStore::new_from_subs(env.subs); + + let before = var_store.peek(); + + let normal_type = to_type(solved_type, &mut free_vars, &mut var_store); + + let after = var_store.peek(); + let variables_introduced = after - before; + + env.subs.extend_by(variables_introduced as usize); + + let result = insert_type_into_subs(env.subs, &normal_type); + + result +} + fn specialize_solved_type<'a>( env: &mut Env<'a, '_>, procs: &mut Procs<'a>, proc_name: Symbol, layout_cache: &mut LayoutCache<'a>, solved_type: SolvedType, + host_exposed_aliases: MutMap, partial_proc: PartialProc<'a>, ) -> Result<(Proc<'a>, Layout<'a>), LayoutProblem> { // add the specializations that other modules require of us - use roc_solve::solve::{insert_type_into_subs, instantiate_rigids}; - use roc_types::solved_types::{to_type, FreeVars}; - use roc_types::subs::VarStore; + use roc_solve::solve::instantiate_rigids; let snapshot = env.subs.snapshot(); let cache_snapshot = layout_cache.snapshot(); - let mut free_vars = FreeVars::default(); - let mut var_store = VarStore::new_from_subs(env.subs); - - let before = var_store.peek(); - - let normal_type = to_type(&solved_type, &mut free_vars, &mut var_store); - - let after = var_store.peek(); - let variables_introduced = after - before; - - env.subs.extend_by(variables_introduced as usize); - - let fn_var = insert_type_into_subs(env.subs, &normal_type); + let fn_var = introduce_solved_type_to_subs(env, &solved_type); // make sure rigid variables in the annotation are converted to flex variables instantiate_rigids(env.subs, partial_proc.annotation); - match specialize_external(env, procs, proc_name, layout_cache, fn_var, partial_proc) { + let mut host_exposed_variables = Vec::with_capacity_in(host_exposed_aliases.len(), env.arena); + + for (symbol, solved_type) in host_exposed_aliases { + let alias_var = introduce_solved_type_to_subs(env, &solved_type); + + host_exposed_variables.push((symbol, alias_var)); + } + + let specialized = specialize_external( + env, + procs, + proc_name, + layout_cache, + fn_var, + &host_exposed_variables, + partial_proc, + ); + + match specialized { Ok(proc) => { let layout = layout_cache .from_var(&env.arena, fn_var, env.subs) diff --git a/examples/closure/Closure.roc b/examples/closure/Closure.roc index 5f29fb2240..9d90d9c40d 100644 --- a/examples/closure/Closure.roc +++ b/examples/closure/Closure.roc @@ -1,7 +1,7 @@ -app Closure provides [ closure ] imports [] +app Closure provides [ makeClosure ] imports [] -closure : ({} -> Int) as MyClosure -closure = +makeClosure : ({} -> Int) as MyClosure +makeClosure = x = 42 \{} -> x diff --git a/examples/closure/platform/src/lib.rs b/examples/closure/platform/src/lib.rs index e0f10ace7c..6a1b1c4d86 100644 --- a/examples/closure/platform/src/lib.rs +++ b/examples/closure/platform/src/lib.rs @@ -6,16 +6,45 @@ use std::time::SystemTime; use RocCallResult::*; extern "C" { - #[link_name = "closure_1_exposed"] + #[link_name = "makeClosure_1_exposed"] fn make_closure(output: *mut u8) -> (); - // #[link_name = "0_1_caller"] - // fn call_closure_0(unit: (), closure_data: *const u8, output: *mut u8) -> (); + #[link_name = "makeClosure_1_MyClosure_caller"] + fn call_MyClosure( + unit: (), + function_pointer: *const u8, + closure_data: *const u8, + output: *mut u8, + ) -> (); - #[link_name = "closure_1_size"] + #[link_name = "makeClosure_1_size"] fn closure_size() -> i64; } +unsafe fn call_the_closure(function_pointer: *const u8, closure_data_ptr: *const u8) -> i64 { + // wow + let layout = Layout::array::(100).unwrap(); + // let buffer = std::alloc::alloc(layout); + let mut foo = (0, 0); + let buffer: *mut (i64, i64) = &mut foo; + + call_MyClosure( + (), + function_pointer, + closure_data_ptr as *const u8, + buffer as *mut u8, + ); + + dbg!(*buffer); + + let output = &*(buffer as *mut RocCallResult); + + match output.into() { + Ok(v) => v, + Err(e) => panic!("failed with {}", e), + } +} + #[no_mangle] pub fn rust_main() -> isize { println!("Running Roc closure"); @@ -23,19 +52,19 @@ pub fn rust_main() -> isize { let size = unsafe { closure_size() } as usize; let layout = Layout::array::(size).unwrap(); - let roc_closure = unsafe { + let answer = unsafe { let buffer = std::alloc::alloc(layout); make_closure(buffer); - type CLOSURE_DATA = i64; - let output = &*(buffer as *mut RocCallResult<(fn(CLOSURE_DATA) -> i64, CLOSURE_DATA)>); + let output = &*(buffer as *mut RocCallResult<(*const u8, ())>); match output.into() { - Ok((function_pointer, closure_data)) => { - std::alloc::dealloc(buffer, layout); + Ok((function_pointer, _)) => { + let closure_data_ptr = buffer.offset(16); + dbg!(*closure_data_ptr); - move || function_pointer(closure_data) + call_the_closure(function_pointer as *const u8, closure_data_ptr as *const u8) } Err(msg) => { std::alloc::dealloc(buffer, layout); @@ -44,7 +73,6 @@ pub fn rust_main() -> isize { } } }; - let answer = roc_closure(); let end_time = SystemTime::now(); let duration = end_time.duration_since(start_time).unwrap();