From 55e4ce11346e51a4abadee3083a267a8ee94a873 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Fri, 3 Jan 2020 20:18:07 -0500 Subject: [PATCH] Introduce ll module --- Cargo.lock | 1 + Cargo.toml | 1 + src/can/symbol.rs | 12 ++ src/gen/build.rs | 471 ++++++++++++++++++++++++++++++++++++++++++++ src/gen/compile.rs | 478 --------------------------------------------- src/gen/env.rs | 2 +- src/gen/mod.rs | 2 +- src/gen/proc.rs | 89 +++++++++ src/lib.rs | 4 + src/ll/expr.rs | 400 +++++++++++++++++++++++++++++++++++++ src/ll/layout.rs | 95 +++++++++ src/ll/mod.rs | 2 + tests/test_eval.rs | 97 +++++---- 13 files changed, 1141 insertions(+), 513 deletions(-) create mode 100644 src/gen/build.rs delete mode 100644 src/gen/compile.rs create mode 100644 src/gen/proc.rs create mode 100644 src/ll/expr.rs create mode 100644 src/ll/layout.rs create mode 100644 src/ll/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 655c0ba8f5..f59e5b3e71 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -596,6 +596,7 @@ dependencies = [ "indoc 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "inkwell 0.1.0 (git+https://github.com/TheDan64/inkwell?branch=llvm8-0)", "inlinable_string 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "petgraph 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index 667a69e40b..706b8de9b9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ inlinable_string = "0.1.0" # but after several hours of trying unsuccessfully to fix it, `branch` is it.) inkwell = { git = "https://github.com/TheDan64/inkwell", branch = "llvm8-0" } futures = "0.3" +lazy_static = "1.4" [dev-dependencies] pretty_assertions = "0.5.1" diff --git a/src/can/symbol.rs b/src/can/symbol.rs index 2b31db354f..d747ec3cae 100644 --- a/src/can/symbol.rs +++ b/src/can/symbol.rs @@ -20,6 +20,18 @@ impl fmt::Debug for Symbol { } } +impl Into for Symbol { + fn into(self) -> InlinableString { + self.0 + } +} + +impl From for Symbol { + fn from(string: InlinableString) -> Self { + Symbol(string) + } +} + impl From<&str> for Symbol { fn from(string: &str) -> Self { Symbol(string.into()) diff --git a/src/gen/build.rs b/src/gen/build.rs new file mode 100644 index 0000000000..d84b253988 --- /dev/null +++ b/src/gen/build.rs @@ -0,0 +1,471 @@ +use bumpalo::Bump; +use inkwell::module::Linkage; +use inkwell::types::BasicType; +use inkwell::types::BasicTypeEnum; +use inkwell::values::BasicValueEnum::{self, *}; +use inkwell::values::{FunctionValue, IntValue, PointerValue}; +use inkwell::{FloatPredicate, IntPredicate}; + +use crate::can; +use crate::collections::{ImMap, MutMap}; +use crate::gen::convert::content_to_basic_type; +use crate::gen::env::Env; +use crate::ll::expr::{Expr, Procs}; +use crate::subs::Variable; +use inlinable_string::InlinableString; + +/// This is for Inkwell's FunctionValue::verify - we want to know the verification +/// output in debug builds, but we don't want it to print to stdout in release builds! +#[cfg(debug_assertions)] +const PRINT_FN_VERIFICATION_OUTPUT: bool = true; + +#[cfg(not(debug_assertions))] +const PRINT_FN_VERIFICATION_OUTPUT: bool = false; + +type Scope<'ctx> = ImMap)>; + +pub fn build_can_expr<'ctx, 'env>( + env: &Env<'ctx, 'env>, + parent: FunctionValue<'ctx>, + can_expr: can::expr::Expr, +) -> BasicValueEnum<'ctx> { + let arena = Bump::new(); + + let mut procs = MutMap::default(); + let expr = Expr::new( + &arena, + &env.subs, + env.module, + env.context, + can_expr, + &mut procs, + ); + + build_expr( + env, + &ImMap::default(), + parent, + &expr, + &mut MutMap::default(), + ) +} + +fn build_expr<'a, 'ctx, 'env>( + env: &Env<'ctx, 'env>, + scope: &Scope<'ctx>, + parent: FunctionValue<'ctx>, + expr: &Expr<'a>, + procs: &mut Procs<'a, 'ctx>, +) -> BasicValueEnum<'ctx> { + use crate::ll::expr::Expr::*; + + match expr { + Int(num) => env.context.i64_type().const_int(*num as u64, false).into(), + Float(num) => env.context.f64_type().const_float(*num).into(), + Cond { + cond_lhs, + cond_rhs, + pass, + fail, + ret_var, + } => { + let cond = Cond2 { + cond_lhs, + cond_rhs, + pass, + fail, + ret_var: *ret_var, + }; + + build_cond(env, scope, parent, cond, procs) + } + Branches { .. } => { + panic!("TODO build_branches(env, scope, parent, cond_lhs, branches, procs)"); + } + Store(ref stores, ref ret) => { + let mut scope = im_rc::HashMap::clone(scope); + let subs = &env.subs; + let context = &env.context; + + for (name, var, expr) in stores.iter() { + let content = subs.get_without_compacting(*var).content; + let val = build_expr(env, &scope, parent, &expr, procs); + let expr_bt = + content_to_basic_type(&content, subs, context).unwrap_or_else(|err| { + panic!( + "Error converting symbol {:?} to basic type: {:?} - scope was: {:?}", + name, err, scope + ) + }); + let alloca = create_entry_block_alloca(env, parent, expr_bt, &name); + + env.builder.build_store(alloca, val); + + // Make a new scope which includes the binding we just encountered. + // This should be done *after* compiling the bound expr, since any + // recursive (in the LetRec sense) bindings should already have + // been extracted as procedures. Nothing in here should need to + // access itself! + scope = im_rc::HashMap::clone(&scope); + + scope.insert(name.clone(), (*var, alloca)); + } + + build_expr(env, &scope, parent, ret, procs) + } + CallByName(ref name, ref args) => { + // TODO try one of these alternative strategies: + // + // 1. use SIMD string comparison to compare these strings faster + // 2. pre-register Bool.or using module.add_function, and see if LLVM inlines it + if name == "Bool.or" { + panic!("TODO create a phi node for ||"); + } else if name == "Bool.and" { + panic!("TODO create a phi node for &&"); + } else { + let mut arg_vals: Vec = Vec::with_capacity(args.len()); + + for arg in args.iter() { + arg_vals.push(build_expr(env, scope, parent, arg, procs).into()); + } + + let fn_val = env + .module + .get_function(name) + .unwrap_or_else(|| panic!("Unrecognized function: {:?}", name)); + + let call = env.builder.build_call(fn_val, arg_vals.as_slice(), "tmp"); + + call.try_as_basic_value() + .left() + .unwrap_or_else(|| panic!("LLVM error: Invalid call by name.")) + } + } + CallByPointer(ref _ptr, ref args) => { + let mut arg_vals: Vec = Vec::with_capacity(args.len()); + + for arg in args.iter() { + arg_vals.push(build_expr(env, scope, parent, arg, procs).into()); + } + + panic!("TODO do a load(ptr) to get back the pointer, then pass *that* in here!"); + + // let call = match build_expr(env, scope, parent, expr, procs) { + // BasicValueEnum::PointerValue(ptr) => { + // env.builder.build_call(ptr, arg_vals.as_slice(), "tmp") + // } + // non_ptr => { + // panic!( + // "Tried to call by pointer, but encountered a non-pointer: {:?}", + // non_ptr + // ); + // } + // }; + + // call.try_as_basic_value() + // .left() + // .unwrap_or_else(|| panic!("LLVM error: Invalid call by pointer.")) + } + + Load(name) => match scope.get(name) { + Some((_, ptr)) => env.builder.build_load(*ptr, name), + None => panic!("Could not find a var for {:?} in scope {:?}", name, scope), + }, + _ => { + panic!("I don't yet know how to build {:?}", expr); + } + } +} + +struct Cond2<'a> { + cond_lhs: &'a Expr<'a>, + cond_rhs: &'a Expr<'a>, + pass: &'a Expr<'a>, + fail: &'a Expr<'a>, + ret_var: Variable, +} + +fn build_cond<'a, 'ctx, 'env>( + env: &Env<'ctx, 'env>, + scope: &Scope<'ctx>, + parent: FunctionValue<'ctx>, + cond: Cond2<'a>, + procs: &mut Procs<'a, 'ctx>, +) -> BasicValueEnum<'ctx> { + let builder = env.builder; + let context = env.context; + let subs = &env.subs; + + let content = subs.get_without_compacting(cond.ret_var).content; + let ret_type = content_to_basic_type(&content, subs, context).unwrap_or_else(|err| { + panic!( + "Error converting cond branch ret_type content {:?} to basic type: {:?}", + cond.pass, err + ) + }); + + let lhs = build_expr(env, scope, parent, cond.cond_lhs, procs); + let rhs = build_expr(env, scope, parent, cond.cond_rhs, procs); + + match (lhs, rhs) { + (FloatValue(lhs_float), FloatValue(rhs_float)) => { + let comparison = + builder.build_float_compare(FloatPredicate::OEQ, lhs_float, rhs_float, "cond"); + + build_phi2( + env, scope, parent, comparison, cond.pass, cond.fail, ret_type, procs, + ) + } + + (IntValue(lhs_int), IntValue(rhs_int)) => { + let comparison = builder.build_int_compare(IntPredicate::EQ, lhs_int, rhs_int, "cond"); + + build_phi2( + env, scope, parent, comparison, cond.pass, cond.fail, ret_type, procs, + ) + } + _ => panic!( + "Tried to make a branch out of incompatible conditions: lhs = {:?} and rhs = {:?}", + cond.cond_lhs, cond.cond_rhs + ), + } +} + +// fn build_branches<'a, 'ctx, 'env>( +// env: &Env<'ctx, 'env>, +// scope: &Scope<'ctx>, +// parent: FunctionValue<'ctx>, +// cond_lhs: &'a Expr<'a>, +// branches: &'a [(Expr<'a>, Expr<'a>, Expr<'a>)], +// ret_type: BasicValueEnum<'ctx>, +// procs: &mut Procs<'a, 'ctx>, +// ) -> BasicValueEnum<'ctx> { +// let builder = env.builder; +// let context = env.context; +// let lhs = build_expr(env, scope, parent, cond_lhs, procs); +// let mut branch_iter = branches.into_iter(); +// let content = subs.get_without_compacting(cond.ret_var).content; +// let ret_type = content_to_basic_type(&content, subs, context).unwrap_or_else(|err| { +// panic!( +// "Error converting cond branch ret_type content {:?} to basic type: {:?}", +// cond.pass, err +// ) +// }); + +// for (cond_rhs, cond_pass, cond_else) in branches { +// let rhs = build_expr(env, scope, parent, cond_rhs, procs); +// let pass = build_expr(env, scope, parent, cond_pass, procs); +// let fail = build_expr(env, scope, parent, cond_else, procs); + +// let cond = Cond { +// lhs, +// rhs, +// pass, +// fail, +// ret_type, +// }; + +// build_cond(env, scope, parent, cond, procs) +// } +// } + +fn build_phi2<'a, 'ctx, 'env>( + env: &Env<'ctx, 'env>, + scope: &Scope<'ctx>, + parent: FunctionValue<'ctx>, + comparison: IntValue<'ctx>, + pass: &'a Expr<'a>, + fail: &'a Expr<'a>, + ret_type: BasicTypeEnum<'ctx>, + procs: &mut Procs<'a, 'ctx>, +) -> BasicValueEnum<'ctx> { + let builder = env.builder; + let context = env.context; + + // build branch + let then_bb = context.append_basic_block(parent, "then"); + let else_bb = context.append_basic_block(parent, "else"); + let cont_bb = context.append_basic_block(parent, "branchcont"); + + builder.build_conditional_branch(comparison, &then_bb, &else_bb); + + // build then block + builder.position_at_end(&then_bb); + let then_val = build_expr(env, scope, parent, pass, procs); + builder.build_unconditional_branch(&cont_bb); + + let then_bb = builder.get_insert_block().unwrap(); + + // build else block + builder.position_at_end(&else_bb); + let else_val = build_expr(env, scope, parent, fail, procs); + builder.build_unconditional_branch(&cont_bb); + + let else_bb = builder.get_insert_block().unwrap(); + + // emit merge block + builder.position_at_end(&cont_bb); + + let phi = builder.build_phi(ret_type, "branch"); + + phi.add_incoming(&[ + (&Into::::into(then_val), &then_bb), + (&Into::::into(else_val), &else_bb), + ]); + + phi.as_basic_value() +} + +/// TODO could this be added to Inkwell itself as a method on BasicValueEnum? +fn set_name(bv_enum: BasicValueEnum<'_>, name: &str) { + match bv_enum { + ArrayValue(val) => val.set_name(name), + IntValue(val) => val.set_name(name), + FloatValue(val) => val.set_name(name), + PointerValue(val) => val.set_name(name), + StructValue(val) => val.set_name(name), + VectorValue(val) => val.set_name(name), + } +} + +pub fn build_closure<'a, 'ctx, BT>( + env: &Env<'ctx, '_>, + name: InlinableString, + arg_vars: Vec, + arg_names: &[InlinableString], + ret_type: BT, + body_expr: &Expr<'a>, + scope: &Scope<'ctx>, + procs: &mut Procs<'a, 'ctx>, + linkage: Option, +) -> FunctionValue<'ctx> +where + BT: BasicType<'ctx>, +{ + // We need these to be separate, but they must have the same length! + debug_assert!(arg_vars.len() == arg_names.len()); + + let subs = &env.subs; + + // Register the function value in the module + let mut arg_basic_types = Vec::with_capacity(arg_vars.len()); + + for var in arg_vars.iter() { + let content = subs.get_without_compacting(*var).content; + + arg_basic_types.push( + content_to_basic_type(&content, &env.subs, env.context).unwrap_or_else(|err| { + panic!( + "Error converting function arg content to basic type: {:?}", + err + ) + }), + ); + } + + let fn_type = ret_type.fn_type(arg_basic_types.as_slice(), false); + let fn_val = env.module.add_function(&name, fn_type, linkage); + + let entry = env.context.append_basic_block(fn_val, "entry"); + let builder = env.builder; + + builder.position_at_end(&entry); + + let mut scope = scope.clone(); + + // Add args to scope + for (((arg_val, arg_name), arg_type), var) in fn_val + .get_param_iter() + .zip(arg_names) + .zip(arg_basic_types) + .zip(arg_vars.into_iter()) + { + set_name(arg_val, arg_name); + + let alloca = create_entry_block_alloca(env, fn_val, arg_type, arg_name); + + builder.build_store(alloca, arg_val); + + scope.insert(arg_name.clone(), (var, alloca)); + } + + let body = build_expr(env, &scope, fn_val, body_expr, procs); + + builder.build_return(Some(&body)); + + fn_val +} + +/// Creates a new stack allocation instruction in the entry block of the function. +pub fn create_entry_block_alloca<'ctx>( + env: &Env<'ctx, '_>, + parent: FunctionValue<'_>, + basic_type: BasicTypeEnum<'ctx>, + name: &str, +) -> PointerValue<'ctx> { + let builder = env.context.create_builder(); + let entry = parent.get_first_basic_block().unwrap(); + + match entry.get_first_instruction() { + Some(first_instr) => builder.position_before(&first_instr), + None => builder.position_at_end(&entry), + } + + builder.build_alloca(basic_type, name) +} + +// fn build_proc() { +// let (ref loc_body_expr, ret_var) = **body; +// let subs = &env.subs; +// let mut arg_types = Vec::new(); +// let mut arg_names = Vec::new(); +// let ret_content = subs.get_without_compacting(ret_var).content; +// let ret_type = +// content_to_basic_type(&ret_content, &env.subs, env.context).unwrap_or_else(|err| { +// panic!( +// "Error converting symbol {:?} to basic type: {:?} - scope was: {:?}", +// symbol, err, scope +// ) +// }); + +// for (var, loc_pat) in args { +// let content = subs.get_without_compacting(*var).content; +// let name = match &loc_pat.value { +// Pattern::Identifier(ident) => ident.as_str().into(), +// pat => { +// panic!("TODO code gen function arg for pattern {:?}", pat); +// } +// }; + +// arg_types.push(content); +// arg_names.push(name); +// } + +// let fn_val = build_closure( +// env, +// symbol.as_str().into(), +// arg_types, +// arg_names.as_slice(), +// ret_type, +// &loc_body_expr.value, +// scope, +// procs, +// None, +// ); + +// if fn_val.verify(PRINT_FN_VERIFICATION_OUTPUT) { +// // TODO call pass_manager.run_on(&fn_val) to optimize it! + +// // The closure evaluates to a pointer to the function. +// fn_val +// .as_global_value() +// .as_pointer_value() +// .as_basic_value_enum() +// } else { +// unsafe { +// fn_val.delete(); +// } + +// panic!("Invalid generated fn_val.") +// } +// } diff --git a/src/gen/compile.rs b/src/gen/compile.rs deleted file mode 100644 index 34af16b7ce..0000000000 --- a/src/gen/compile.rs +++ /dev/null @@ -1,478 +0,0 @@ -use inkwell::basic_block::BasicBlock; -use inkwell::module::Linkage; -use inkwell::types::BasicType; -use inkwell::types::BasicTypeEnum; -use inkwell::values::BasicValueEnum::{self, *}; -use inkwell::values::{BasicValue, FunctionValue, IntValue, PointerValue}; -use inkwell::{FloatPredicate, IntPredicate}; - -use crate::can::expr::Expr; -use crate::can::ident::Lowercase; -use crate::can::pattern::Pattern::{self, *}; -use crate::can::symbol::Symbol; -use crate::collections::ImMap; -use crate::collections::MutMap; -use crate::gen::convert::content_to_basic_type; -use crate::gen::env::Env; -use crate::subs::{Content, FlatType, Subs}; - -/// This is for Inkwell's FunctionValue::verify - we want to know the verification -/// output in debug builds, but we don't want it to print to stdout in release builds! -#[cfg(debug_assertions)] -const PRINT_FN_VERIFICATION_OUTPUT: bool = true; - -#[cfg(not(debug_assertions))] -const PRINT_FN_VERIFICATION_OUTPUT: bool = false; - -type Procs<'ctx> = MutMap)>; - -type Scope<'ctx> = ImMap)>; - -pub fn compile_standalone_expr<'ctx, 'env>( - env: &Env<'ctx, 'env>, - parent: FunctionValue<'ctx>, - expr: &Expr, -) -> BasicValueEnum<'ctx> { - compile_expr(env, &ImMap::default(), parent, expr, &mut MutMap::default()) -} - -fn compile_expr<'ctx, 'env>( - env: &Env<'ctx, 'env>, - scope: &Scope<'ctx>, - parent: FunctionValue<'ctx>, - expr: &Expr, - procs: &mut Procs, -) -> BasicValueEnum<'ctx> { - use crate::can::expr::Expr::*; - - match *expr { - Int(_, num) => env.context.i64_type().const_int(num as u64, false).into(), - Float(_, num) => env.context.f64_type().const_float(num).into(), - When { - ref loc_cond, - ref branches, - .. - } => { - if branches.len() < 2 { - panic!("TODO support when-expressions of fewer than 2 branches."); - } - if branches.len() == 2 { - let mut iter = branches.iter(); - - let (pattern, branch_expr) = iter.next().unwrap(); - let (_, else_expr) = iter.next().unwrap(); - - compile_when_branch( - env, - scope, - parent, - &loc_cond.value, - pattern.value.clone(), - &branch_expr.value, - &else_expr.value, - procs, - ) - } else { - panic!("TODO support when-expressions of more than 2 branches."); - } - } - LetNonRec(ref def, ref loc_ret, _) => { - match &def.loc_pattern.value { - Pattern::Identifier(symbol) => { - let expr = &def.loc_expr.value; - let subs = &env.subs; - let context = &env.context; - let content = content_from_expr(scope, subs, expr); - let val = compile_expr(env, &scope, parent, &expr, procs); - let expr_bt = content_to_basic_type(&content, subs, context).unwrap_or_else(|err| panic!("Error converting symbol {:?} to basic type: {:?} - scope was: {:?}", symbol, err, scope)); - let alloca = create_entry_block_alloca(env, parent, expr_bt, symbol.as_str()); - - env.builder.build_store(alloca, val); - - // Make a new scope which includes the binding we just encountered. - // This should be done *after* compiling the bound expr, since this is a - // LetNonRec rather than a LetRec. It shouldn't need to access itself! - let mut scope = scope.clone(); - - scope.insert(symbol.clone(), (content.clone(), alloca)); - - compile_expr(env, &scope, parent, &loc_ret.value, procs) - } - pat => { - panic!("TODO code gen Def pattern {:?}", pat); - } - } - } - Closure(_, ref symbol, ref _recursive, ref args, ref body) => { - let (ref loc_body_expr, ret_var) = **body; - let subs = &env.subs; - let mut arg_types = Vec::new(); - let mut arg_names = Vec::new(); - let ret_content = subs.get_without_compacting(ret_var).content; - let ret_type = content_to_basic_type(&ret_content, &env.subs, env.context) - .unwrap_or_else(|err| { - panic!( - "Error converting symbol {:?} to basic type: {:?} - scope was: {:?}", - symbol, err, scope - ) - }); - - for (var, loc_pat) in args { - let content = subs.get_without_compacting(*var).content; - let name = match &loc_pat.value { - Pattern::Identifier(ident) => ident.as_str().into(), - pat => { - panic!("TODO code gen function arg for pattern {:?}", pat); - } - }; - - arg_types.push(content); - arg_names.push(name); - } - - let fn_val = compile_closure( - env, - symbol.as_str().into(), - arg_types, - arg_names.as_slice(), - ret_type, - &loc_body_expr.value, - scope, - procs, - None, - ); - - if fn_val.verify(PRINT_FN_VERIFICATION_OUTPUT) { - // TODO call pass_manager.run_on(&fn_val) to optimize it! - - // The closure evaluates to a pointer to the function. - fn_val - .as_global_value() - .as_pointer_value() - .as_basic_value_enum() - } else { - unsafe { - fn_val.delete(); - } - - panic!("Invalid generated fn_val.") - } - } - Call(ref boxed, ref loc_args, _) => { - let (_, ref loc_expr, _) = **boxed; - let mut arg_vars: Vec = Vec::with_capacity(loc_args.len()); - - for (_var, loc_arg) in loc_args.iter() { - let arg = compile_expr(env, scope, parent, &loc_arg.value, procs); - - arg_vars.push(arg.into()); - } - - let call = match &loc_expr.value { - // TODO fix and then re-enable this. The problem is that it fails on - // symbol lookup, because the closure's stored Symbol is always the - // auto-generated one like "Test.blah$1" whereas the lookup is always - // something like "Test.blah$identity" - so if we want to call by - // name directly, we need to detect the closure's name *during code gen* - // (while processing defs) and insert it into scope accordingly. - // Afterwards, we should remove that Symbol from Expr. - // - // Var { - // resolved_symbol, .. - // } => { - // // Call by name - // let fn_val = env - // .module - // .get_function(resolved_symbol.as_str()) - // .unwrap_or_else(|| panic!("Unrecognized function: {:?}", resolved_symbol)); - - // env.builder.build_call(fn_val, arg_vars.as_slice(), "tmp") - // } - expr => { - // Call by pointer - the closure was anonymous, e.g. ((\a -> a) 5) - match compile_expr(env, scope, parent, expr, procs) { - BasicValueEnum::PointerValue(ptr) => { - env.builder.build_call(ptr, arg_vars.as_slice(), "tmp") - } - non_ptr => { - panic!( - "Tried to call by pointer, but encountered a non-pointer: {:?}", - non_ptr - ); - } - } - } - }; - - call.try_as_basic_value() - .left() - .unwrap_or_else(|| panic!("Invalid call produced.")) - } - - Var { - ref resolved_symbol, - .. - } => match scope.get(resolved_symbol) { - Some((_, ptr)) => env.builder.build_load(*ptr, resolved_symbol.as_str()), - None => panic!( - "Could not find a var for {:?} in scope {:?}", - resolved_symbol, scope - ), - }, - _ => { - panic!("I don't yet know how to compile {:?}", expr); - } - } -} - -fn content_from_expr(scope: &Scope<'_>, subs: &Subs, expr: &Expr) -> Content { - use crate::can::expr::Expr::*; - - match expr { - Int(var, _) - | Float(var, _) - | When { expr_var: var, .. } - | LetNonRec(_, _, var) - | LetRec(_, _, var) - | Closure(var, _, _, _, _) => subs.get_without_compacting(*var).content, - Str(_) | BlockStr(_) => Content::Structure(FlatType::Apply { - module_name: "Str".into(), - name: "Str".into(), - args: Vec::new(), - }), - Var { - ref resolved_symbol, - .. - } => { - let (content, _) = scope.get(resolved_symbol).unwrap_or_else(|| { - panic!( - "Code gen problem: Couldn't find {:?} in scope {:?}", - resolved_symbol, scope - ) - }); - - content.clone() - } - other => panic!("TODO handle content_from_expr for {:?}", other), - } -} - -fn compile_when_branch<'ctx, 'env>( - env: &Env<'ctx, 'env>, - scope: &Scope<'ctx>, - parent: FunctionValue<'ctx>, - cond_expr: &Expr, - pattern: Pattern, - branch_expr: &Expr, - else_expr: &Expr, - procs: &mut Procs, -) -> BasicValueEnum<'ctx> { - let builder = env.builder; - let context = env.context; - - match compile_expr(env, scope, parent, cond_expr, procs) { - FloatValue(float_val) => match pattern { - FloatLiteral(target_val) => { - let comparison = builder.build_float_compare( - FloatPredicate::OEQ, - float_val, - context.f64_type().const_float(target_val), - "whencond", - ); - - let (then_bb, else_bb, then_val, else_val) = two_way_branch( - env, - scope, - parent, - comparison, - branch_expr, - else_expr, - procs, - ); - let phi = builder.build_phi(context.f64_type(), "whenbranch"); - - phi.add_incoming(&[ - (&Into::::into(then_val), &then_bb), - (&Into::::into(else_val), &else_bb), - ]); - - phi.as_basic_value().into_float_value().into() - } - - _ => panic!("TODO support pattern matching on floats other than literals."), - }, - - IntValue(int_val) => match pattern { - IntLiteral(target_val) => { - let comparison = builder.build_int_compare( - IntPredicate::EQ, - int_val, - context.i64_type().const_int(target_val as u64, false), - "whencond", - ); - - let (then_bb, else_bb, then_val, else_val) = two_way_branch( - env, - scope, - parent, - comparison, - branch_expr, - else_expr, - procs, - ); - let phi = builder.build_phi(context.i64_type(), "whenbranch"); - - phi.add_incoming(&[ - (&Into::::into(then_val), &then_bb), - (&Into::::into(else_val), &else_bb), - ]); - - phi.as_basic_value().into_int_value().into() - } - _ => panic!("TODO support pattern matching on ints other than literals."), - }, - _ => panic!( - "TODO handle pattern matching on conditionals other than int and float literals." - ), - } -} - -fn two_way_branch<'ctx, 'env>( - env: &Env<'ctx, 'env>, - scope: &Scope<'ctx>, - parent: FunctionValue<'ctx>, - comparison: IntValue<'ctx>, - branch_expr: &Expr, - else_expr: &Expr, - procs: &mut Procs, -) -> ( - BasicBlock, - BasicBlock, - BasicValueEnum<'ctx>, - BasicValueEnum<'ctx>, -) { - let builder = env.builder; - let context = env.context; - - // build branch - let then_bb = context.append_basic_block(parent, "then"); - let else_bb = context.append_basic_block(parent, "else"); - let cont_bb = context.append_basic_block(parent, "casecont"); - - builder.build_conditional_branch(comparison, &then_bb, &else_bb); - - // build then block - builder.position_at_end(&then_bb); - let then_val = compile_expr(env, scope, parent, branch_expr, procs); - builder.build_unconditional_branch(&cont_bb); - - let then_bb = builder.get_insert_block().unwrap(); - - // build else block - builder.position_at_end(&else_bb); - let else_val = compile_expr(env, scope, parent, else_expr, procs); - builder.build_unconditional_branch(&cont_bb); - - let else_bb = builder.get_insert_block().unwrap(); - - // emit merge block - builder.position_at_end(&cont_bb); - - (then_bb, else_bb, then_val, else_val) -} - -/// TODO could this be added to Inkwell itself as a method on BasicValueEnum? -fn set_name(bv_enum: BasicValueEnum<'_>, name: &str) { - match bv_enum { - ArrayValue(val) => val.set_name(name), - IntValue(val) => val.set_name(name), - FloatValue(val) => val.set_name(name), - PointerValue(val) => val.set_name(name), - StructValue(val) => val.set_name(name), - VectorValue(val) => val.set_name(name), - } -} - -pub fn compile_closure<'ctx, BT>( - env: &Env<'ctx, '_>, - name: Lowercase, - arg_types: Vec, - arg_names: &[Lowercase], - ret_type: BT, - body_expr: &Expr, - scope: &Scope<'ctx>, - procs: &mut Procs, - linkage: Option, -) -> FunctionValue<'ctx> -where - BT: BasicType<'ctx>, -{ - // We need these to be separate, but they must have the same length! - debug_assert!(arg_types.len() == arg_names.len()); - - // Register the function value in the module - let mut arg_basic_types = Vec::with_capacity(arg_types.len()); - - for content in arg_types.iter() { - arg_basic_types.push( - content_to_basic_type(content, &env.subs, env.context).unwrap_or_else(|err| { - panic!( - "Error converting function arg content to basic type: {:?}", - err - ) - }), - ); - } - - let fn_type = ret_type.fn_type(arg_basic_types.as_slice(), false); - let fn_val = env.module.add_function(name.as_str(), fn_type, linkage); - - let entry = env.context.append_basic_block(fn_val, "entry"); - let builder = env.builder; - - builder.position_at_end(&entry); - - let mut scope = scope.clone(); - - // Add args to scope - for (((arg_val, arg_name), arg_type), content) in fn_val - .get_param_iter() - .zip(arg_names) - .zip(arg_basic_types) - .zip(arg_types.into_iter()) - { - let arg_name = arg_name.as_str(); - - set_name(arg_val, arg_name); - - let alloca = create_entry_block_alloca(env, fn_val, arg_type, arg_name); - - builder.build_store(alloca, arg_val); - - scope.insert(arg_name.into(), (content, alloca)); - } - - let body = compile_expr(env, &scope, fn_val, body_expr, procs); - - builder.build_return(Some(&body)); - - fn_val -} - -/// Creates a new stack allocation instruction in the entry block of the function. -pub fn create_entry_block_alloca<'ctx>( - env: &Env<'ctx, '_>, - parent: FunctionValue<'_>, - basic_type: BasicTypeEnum<'ctx>, - name: &str, -) -> PointerValue<'ctx> { - let builder = env.context.create_builder(); - let entry = parent.get_first_basic_block().unwrap(); - - match entry.get_first_instruction() { - Some(first_instr) => builder.position_before(&first_instr), - None => builder.position_at_end(&entry), - } - - builder.build_alloca(basic_type, name) -} diff --git a/src/gen/env.rs b/src/gen/env.rs index 1e387c19a3..5270842390 100644 --- a/src/gen/env.rs +++ b/src/gen/env.rs @@ -6,6 +6,6 @@ use inkwell::module::Module; pub struct Env<'ctx, 'env> { pub context: &'ctx Context, pub builder: &'env Builder<'ctx>, - pub module: &'env Module<'ctx>, + pub module: &'ctx Module<'ctx>, pub subs: Subs, } diff --git a/src/gen/mod.rs b/src/gen/mod.rs index 1e70380454..a5edbec50e 100644 --- a/src/gen/mod.rs +++ b/src/gen/mod.rs @@ -1,3 +1,3 @@ -pub mod compile; +pub mod build; pub mod convert; pub mod env; diff --git a/src/gen/proc.rs b/src/gen/proc.rs new file mode 100644 index 0000000000..1080b87591 --- /dev/null +++ b/src/gen/proc.rs @@ -0,0 +1,89 @@ +use inkwell::basic_block::BasicBlock; +use inkwell::module::Linkage; +use inkwell::types::BasicType; +use inkwell::types::BasicTypeEnum; +use inkwell::values::BasicValueEnum::{self, *}; +use inkwell::values::{BasicValue, FunctionValue, IntValue, PointerValue}; +use inkwell::{FloatPredicate, IntPredicate}; + +use crate::can::expr::Expr; +use crate::can::ident::Lowercase; +use crate::can::pattern::Pattern::{self, *}; +use crate::can::symbol::Symbol; +use crate::collections::ImMap; +use crate::collections::MutMap; +use crate::gen::convert::content_to_basic_type; +use crate::gen::env::Env; +use crate::subs::{Content, FlatType, Subs}; + +type Procs<'ctx> = MutMap)>; + +fn extract_procs(loc_expr: Located, module: &Module<'ctx>, name: Option, procs, &mut Procs<'ctx>) -> Located { + let mut procs = Vec::new(); + + match expr { + LetNonRec(def, ret_expr, var) => { + let loc_pattern = def.loc_pattern; + let loc_expr = def.loc_expr; + + // If we're defining a named closure, insert it into Procs and then + // remove the Let. When code later goes to look it up, it'll be in Procs! + // + // Before: + // + // identity = \a -> a + // + // identity 5 + // + // After: (`identity` is now in Procs) + // + // identity 5 + // + let pattern = match loc_pattern.value { + Identifier(name) => { + match &loc_expr.value { + Closure(_, _, _, _, _) => { + // Extract Procs, but discard the resulting Expr::Var. + // That Var looks up the pointer, which we won't use here! + extract_procs(loc_expr, Some(name), procs); + + // Discard this LetNonRec by replacing it with its ret_expr. + return ret_expr; + } + _ => { + // If this isn't a Closure, proceed as normal. + Identifier(name) + } + } + } + pat => pat + } + + // At this point, it's safe to assume we aren't assigning a Closure to a def. + // Extract Procs from the def body and the ret expression, and return the result! + let ret_expr = extract_procs(ret_expr, None, procs); + let loc_expr = extract_procs(def.loc_expr, None, procs); + let loc_pattern = Located { region: def.loc_pattern.region, value: pattern }; + let def = Def { loc_pattern, loc_expr, ..def }; + + LetNonRec(def, ret_expr, var) + } + + Closure(var, symbol, recursive, loc_args, boxed_ret) => { + let (loc_ret, var) = boxed_ret; + let name = match name { + Some(name) => name.as_str(), + None => { + // Give the closure a name like "_0" or "_1". + // We know procs.len() will be unique! + format!("_{}", procs.len()).as_str(); + } + }; + + let fn_val = module.add_function(name, fn_type, linkage); + + panic!("push to procs"); + } + }; +} + diff --git a/src/lib.rs b/src/lib.rs index ea6eca0213..856367f195 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,6 +27,7 @@ pub mod ena; pub mod fmt; pub mod gen; pub mod infer; +pub mod ll; pub mod load; pub mod module; pub mod pretty_print_types; @@ -37,3 +38,6 @@ pub mod unify; #[macro_use] extern crate log; + +// #[macro_use] +// extern crate lazy_static; diff --git a/src/ll/expr.rs b/src/ll/expr.rs new file mode 100644 index 0000000000..ef63e7ea51 --- /dev/null +++ b/src/ll/expr.rs @@ -0,0 +1,400 @@ +use crate::can; +use crate::can::pattern::Pattern; +use crate::collections::MutMap; +use crate::gen::convert::content_to_basic_type; +use crate::ll::layout::Layout; +use crate::region::Located; +use crate::subs::{Subs, Variable}; +use bumpalo::collections::Vec; +use bumpalo::Bump; +use inkwell::context::Context; +use inkwell::module::Module; +use inkwell::types::{BasicType, BasicTypeEnum}; +use inkwell::values::FunctionValue; +use inlinable_string::InlinableString; + +pub type Procs<'a, 'ctx> = MutMap>, FunctionValue<'ctx>)>; + +#[derive(Clone, Debug, PartialEq)] +pub struct Proc<'a> { + args: &'a [(Layout<'a>, InlinableString)], + body: Expr<'a>, + closes_over: Layout<'a>, +} + +struct Env<'a, 'ctx> { + arena: &'a Bump, + subs: &'a Subs, + module: &'ctx Module<'ctx>, + context: &'ctx Context, +} + +#[derive(Clone, Debug, PartialEq)] +pub enum Expr<'a> { + // Literals + Int(i64), + Float(f64), + Str(&'a str), + + // Load/Store + Load(InlinableString), + Store(&'a [(InlinableString, Variable, Expr<'a>)], &'a Expr<'a>), + + // Functions + FunctionPointer(InlinableString), + CallByPointer(InlinableString, &'a [Expr<'a>]), + CallByName(InlinableString, &'a [Expr<'a>]), + + // Exactly two conditional branches, e.g. if/else + Cond { + // The left-hand side of the conditional comparison and the right-hand side. + // These are stored separately because there are different machine instructions + // for e.g. "compare float and jump" vs. "compare integer and jump" + cond_lhs: &'a Expr<'a>, + cond_rhs: &'a Expr<'a>, + // What to do if the condition either passes or fails + pass: &'a Expr<'a>, + fail: &'a Expr<'a>, + ret_var: Variable, + }, + /// More than two conditional branches, e.g. a 3-way when-expression + Branches { + /// The left-hand side of the conditional. We compile this to LLVM once, + /// then reuse it to test against each different compiled cond_rhs value. + cond_lhs: &'a Expr<'a>, + /// ( cond_rhs, pass, fail ) + branches: &'a [(Expr<'a>, Expr<'a>, Expr<'a>)], + ret_var: Variable, + }, + + Struct(&'a [(InlinableString, Expr<'a>)]), + + RuntimeError(&'a str), +} + +impl<'a> Expr<'a> { + pub fn new<'ctx>( + arena: &'a Bump, + subs: &'a Subs, + module: &'ctx Module<'ctx>, + context: &'ctx Context, + can_expr: can::expr::Expr, + procs: &mut Procs<'a, 'ctx>, + ) -> Self { + let env = Env { + arena, + subs, + module, + context, + }; + + from_can(&env, can_expr, procs, None) + } +} + +fn from_can<'a, 'ctx>( + env: &Env<'a, 'ctx>, + can_expr: can::expr::Expr, + procs: &mut Procs<'a, 'ctx>, + name: Option, +) -> Expr<'a> { + use crate::can::expr::Expr::*; + use crate::can::pattern::Pattern::*; + + match can_expr { + Int(_, val) => Expr::Int(val), + Float(_, val) => Expr::Float(val), + Str(string) | BlockStr(string) => Expr::Str(env.arena.alloc(string)), + Var { + resolved_symbol, .. + } => Expr::Load(resolved_symbol.into()), + LetNonRec(def, ret_expr, _) => { + let arena = env.arena; + let loc_pattern = def.loc_pattern; + let loc_expr = def.loc_expr; + let mut stored = Vec::with_capacity_in(1, arena); + + // If we're defining a named closure, insert it into Procs and then + // remove the Let. When code gen later goes to look it up, it'll be in Procs! + // + // Before: + // + // identity = \a -> a + // + // identity 5 + // + // After: (`identity` is now in Procs) + // + // identity 5 + // + match &loc_pattern.value { + Identifier(name) => { + match &loc_expr.value { + Closure(_, _, _, _, _) => { + // Extract Procs, but discard the resulting Expr::Load. + // That Load looks up the pointer, which we won't use here! + from_can(env, loc_expr.value, procs, Some(name.clone().into())); + + // Discard this LetNonRec by replacing it with its ret_expr. + return from_can(env, ret_expr.value, procs, None); + } + _ => (), + } + } + _ => (), + } + + // If it wasn't specifically an Identifier & Closure, proceed as normal. + store_pattern( + env, + loc_pattern.value, + loc_expr.value, + def.expr_var, + procs, + &mut stored, + ); + + // At this point, it's safe to assume we aren't assigning a Closure to a def. + // Extract Procs from the def body and the ret expression, and return the result! + let ret = from_can(env, ret_expr.value, procs, None); + + Expr::Store(stored.into_bump_slice(), arena.alloc(ret)) + } + + Closure(_, _symbol, _, loc_args, boxed_body) => { + let (loc_body, ret_var) = *boxed_body; + let name = name.unwrap_or_else(|| + // Give the closure a name like "_0" or "_1". + // We know procs.len() will be unique! + format!("_{}", procs.len()).into()); + + add_closure(env, name, loc_body.value, ret_var, &loc_args, procs) + } + + Call(boxed, loc_args, _) => { + let (_, loc_expr, _) = *boxed; + let mut args = Vec::with_capacity_in(loc_args.len(), env.arena); + + for (_, loc_arg) in loc_args { + args.push(from_can(env, loc_arg.value, procs, None)); + } + + match from_can(env, loc_expr.value, procs, None) { + Expr::Load(proc_name) => Expr::CallByName(proc_name, args.into_bump_slice()), + Expr::FunctionPointer(proc_name) => { + // Call by pointer - the closure was anonymous, e.g. + // + // ((\a -> a) 5) + // + // It might even be the anonymous result of a conditional: + // + // ((if x > 0 then \a -> a else \_ -> 0) 5) + Expr::CallByPointer(proc_name, args.into_bump_slice()) + } + non_ptr => { + panic!( + "Tried to call by pointer, but encountered a non-pointer: {:?}", + non_ptr + ); + } + } + } + + When { + expr_var, + loc_cond, + branches, + .. + } => { + debug_assert!(!branches.is_empty()); + + if branches.len() == 2 { + let arena = env.arena; + let mut iter = branches.into_iter(); + let (loc_pat1, loc_then) = iter.next().unwrap(); + let (loc_pat2, loc_else) = iter.next().unwrap(); + + match (&loc_pat1.value, &loc_pat2.value) { + (IntLiteral(int), IntLiteral(_)) | (IntLiteral(int), Underscore) => { + let cond_lhs = arena.alloc(from_can(env, loc_cond.value, procs, None)); + let cond_rhs = arena.alloc(Expr::Int(*int)); + let pass = arena.alloc(from_can(env, loc_then.value, procs, None)); + let fail = arena.alloc(from_can(env, loc_else.value, procs, None)); + + Expr::Cond { + cond_lhs, + cond_rhs, + pass, + fail, + ret_var: expr_var, + } + } + (FloatLiteral(float), FloatLiteral(_)) | (FloatLiteral(float), Underscore) => { + let cond_lhs = arena.alloc(from_can(env, loc_cond.value, procs, None)); + let cond_rhs = arena.alloc(Expr::Float(*float)); + let pass = arena.alloc(from_can(env, loc_then.value, procs, None)); + let fail = arena.alloc(from_can(env, loc_else.value, procs, None)); + + Expr::Cond { + cond_lhs, + cond_rhs, + pass, + fail, + ret_var: expr_var, + } + } + _ => { + panic!("TODO handle more conds"); + } + } + } else if branches.len() == 1 { + // A when-expression with exactly 1 branch is essentially a LetNonRec. + // As such, we can compile it direcly to a Store. + let arena = env.arena; + let mut stored = Vec::with_capacity_in(1, arena); + let (loc_pattern, loc_branch) = branches.into_iter().next().unwrap(); + + store_pattern( + env, + loc_pattern.value, + loc_branch.value, + expr_var, + procs, + &mut stored, + ); + + let ret = from_can(env, loc_cond.value, procs, None); + + Expr::Store(stored.into_bump_slice(), arena.alloc(ret)) + } else { + // /// More than two conditional branches, e.g. a 3-way when-expression + // Expr::Branches { + // /// The left-hand side of the conditional. We compile this to LLVM once, + // /// then reuse it to test against each different compiled cond_rhs value. + // cond_lhs: &'a Expr<'a>, + // /// ( cond_rhs, pass, fail ) + // branches: &'a [(Expr<'a>, Expr<'a>, Expr<'a>)], + // ret_var: Variable, + // }, + panic!("TODO support when-expressions of more than 2 branches."); + } + } + + other => panic!("TODO convert canonicalized {:?} to ll::Expr", other), + } +} + +fn add_closure<'a, 'ctx>( + env: &Env<'a, 'ctx>, + name: InlinableString, + can_body: can::expr::Expr, + ret_var: Variable, + loc_args: &[(Variable, Located)], + procs: &mut Procs<'a, 'ctx>, +) -> Expr<'a> { + let subs = &env.subs; + let context = env.context; + let arena = env.arena; + let ret_content = subs.get_without_compacting(ret_var).content; + let ret_type = content_to_basic_type(&ret_content, subs, context).unwrap_or_else(|err| { + panic!( + "Error converting function return value content to basic type: {:?}", + err + ) + }); + + let mut arg_names = Vec::with_capacity_in(loc_args.len(), arena); + let mut arg_basic_types = Vec::with_capacity_in(loc_args.len(), arena); + let mut proc_args = Vec::with_capacity_in(loc_args.len(), arena); + + for (arg_var, loc_arg) in loc_args.iter() { + let content = subs.get_without_compacting(*arg_var).content; + + arg_basic_types.push( + content_to_basic_type(&content, subs, context).unwrap_or_else(|err| { + panic!( + "Error converting function arg content to basic type: {:?}", + err + ) + }), + ); + + let layout = match Layout::from_content(arena, content, subs) { + Ok(layout) => layout, + Err(()) => { + return invalid_closure(env, name, ret_type, procs); + } + }; + + let arg_name: InlinableString = match &loc_arg.value { + Pattern::Identifier(name) => name.as_str().into(), + _ => { + panic!("TODO determine arg_name for pattern {:?}", loc_arg.value); + } + }; + + arg_names.push(arg_name.clone()); + proc_args.push((layout, arg_name)); + } + + let fn_type = ret_type.fn_type(arg_basic_types.into_bump_slice(), false); + let fn_val = env.module.add_function(&name, fn_type, None); + let proc = Proc { + args: proc_args.into_bump_slice(), + body: from_can(env, can_body, procs, None), + closes_over: Layout::ZeroSized, + }; + + procs.insert(name.clone(), (Some(proc), fn_val)); + + Expr::FunctionPointer(name) +} + +fn invalid_closure<'a, 'ctx>( + env: &Env<'a, 'ctx>, + name: InlinableString, + ret_type: BasicTypeEnum<'ctx>, + procs: &mut Procs<'a, 'ctx>, +) -> Expr<'a> { + let fn_type = ret_type.fn_type(&[], false); + let fn_val = env.module.add_function(&name, fn_type, None); + + procs.insert(name.clone(), (None, fn_val)); + + Expr::FunctionPointer(name) +} + +fn store_pattern<'a, 'ctx>( + env: &Env<'a, 'ctx>, + can_pat: Pattern, + can_expr: can::expr::Expr, + var: Variable, + procs: &mut Procs<'a, 'ctx>, + stored: &mut Vec<'a, (InlinableString, Variable, Expr<'a>)>, +) { + use crate::can::pattern::Pattern::*; + + // If we're defining a named closure, insert it into Procs and then + // remove the Let. When code gen later goes to look it up, it'll be in Procs! + // + // Before: + // + // identity = \a -> a + // + // identity 5 + // + // After: (`identity` is now in Procs) + // + // identity 5 + // + match can_pat { + Identifier(name) => stored.push((name.into(), var, from_can(env, can_expr, procs, None))), + Underscore => { + // Since _ is never read, it's safe to reassign it. + stored.push(("_".into(), var, from_can(env, can_expr, procs, None))) + } + _ => { + panic!("TODO store_pattern for {:?}", can_pat); + } + } +} diff --git a/src/ll/layout.rs b/src/ll/layout.rs new file mode 100644 index 0000000000..18580dc02b --- /dev/null +++ b/src/ll/layout.rs @@ -0,0 +1,95 @@ +use crate::subs::{Content, FlatType, Subs}; +use bumpalo::collections::Vec; +use bumpalo::Bump; +use inlinable_string::InlinableString; + +/// Types for code gen must be monomorphic. No type variables allowed! +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum Layout<'a> { + /// A unary type like {} or [ NoOtherTags ] - this does not need to be a value at runtime. + ZeroSized, + /// A function. The types of its arguments, then the type of its return value. + FunctionPointer(&'a [Layout<'a>], &'a Layout<'a>), + Struct(&'a [(InlinableString, Layout<'a>)]), + + /// Applying a type to some arguments (e.g. Map.Map String Int) + Apply { + module_name: InlinableString, + name: InlinableString, + args: &'a [Layout<'a>], + }, + + Pointer(&'a Layout<'a>), +} + +impl<'a> Layout<'a> { + /// Returns Err(()) if given an error, or Ok(Layout) if given a non-erroneous Structure. + /// Panics if given a FlexVar or RigidVar, since those should have been + /// monomorphized away already! + pub fn from_content(arena: &'a Bump, content: Content, subs: &Subs) -> Result { + use crate::subs::Content::*; + + match content { + var @ FlexVar(_) | var @ RigidVar(_) => { + panic!("Layout::from_content encountered an unresolved {:?}", var); + } + Structure(flat_type) => layout_from_flat_type(arena, flat_type, subs), + Alias(_, _, _, _) => { + panic!("TODO recursively resolve type aliases in Layout::from_content"); + } + Error => Err(()), + } + } +} + +fn layout_from_flat_type<'a>( + arena: &'a Bump, + flat_type: FlatType, + subs: &Subs, +) -> Result, ()> { + use crate::subs::FlatType::*; + + match flat_type { + Apply { + module_name, + name, + args, + } => { + let mut layout_args = Vec::with_capacity_in(args.len(), arena); + + for arg_var in args { + let arg_content = subs.get_without_compacting(arg_var).content; + + layout_args.push(Layout::from_content(arena, arg_content, subs)?); + } + + Ok(Layout::Apply { + module_name: module_name.as_str().into(), + name: name.as_str().into(), + args: layout_args.into_bump_slice(), + }) + } + Func(args, ret_var) => { + let mut fn_args = Vec::with_capacity_in(args.len(), arena); + + for arg_var in args { + let arg_content = subs.get_without_compacting(arg_var).content; + + fn_args.push(Layout::from_content(arena, arg_content, subs)?); + } + + let ret_content = subs.get_without_compacting(ret_var).content; + let ret = Layout::from_content(arena, ret_content, subs)?; + + Ok(Layout::FunctionPointer( + fn_args.into_bump_slice(), + arena.alloc(ret), + )) + } + Record(_, _) => { + panic!("TODO make Layout for non-empty Record"); + } + Erroneous(_) => Err(()), + EmptyRecord => Ok(Layout::Struct(&[])), + } +} diff --git a/src/ll/mod.rs b/src/ll/mod.rs new file mode 100644 index 0000000000..ef0ddc4d01 --- /dev/null +++ b/src/ll/mod.rs @@ -0,0 +1,2 @@ +pub mod expr; +pub mod layout; diff --git a/tests/test_eval.rs b/tests/test_eval.rs index b58372cd11..87bd879187 100644 --- a/tests/test_eval.rs +++ b/tests/test_eval.rs @@ -12,11 +12,12 @@ mod helpers; #[cfg(test)] mod test_gen { use crate::helpers::can_expr; + use bumpalo::Bump; use inkwell::context::Context; use inkwell::execution_engine::JitFunction; use inkwell::types::BasicType; use inkwell::OptimizationLevel; - use roc::gen::compile::compile_standalone_expr; + use roc::gen::build::build_can_expr; use roc::gen::convert::content_to_basic_type; use roc::gen::env::Env; use roc::infer::infer_expr; @@ -24,6 +25,7 @@ mod test_gen { macro_rules! assert_evals_to { ($src:expr, $expected:expr, $ty:ty) => { + let arena = Bump::new(); let (expr, _output, _problems, var_store, variable, constraint) = can_expr($src); let mut subs = Subs::new(var_store.into()); let mut unify_problems = Vec::new(); @@ -49,9 +51,9 @@ mod test_gen { subs, builder: &builder, context: &context, - module: &module, + module: arena.alloc(module), }; - let ret = compile_standalone_expr(&env, function, &expr); + let ret = build_can_expr(&env, function, expr); builder.build_return(Some(&ret)); @@ -229,37 +231,66 @@ mod test_gen { ); } - #[test] - fn gen_basic_fn() { - assert_evals_to!( - indoc!( - r#" - always42 : Num.Num Int.Integer -> Num.Num Int.Integer - always42 = \num -> 42 + // #[test] + // fn gen_basic_fn() { + // assert_evals_to!( + // indoc!( + // r#" + // always42 : Num.Num Int.Integer -> Num.Num Int.Integer + // always42 = \num -> 42 - always42 5 - "# - ), - 42, - i64 - ); - } + // always42 5 + // "# + // ), + // 42, + // i64 + // ); + // } - #[test] - fn gen_when_fn() { - assert_evals_to!( - indoc!( - r#" - limitedNegate = \num -> - when num is - 1 -> -1 - 0 -> 0 + // #[test] + // fn gen_when_fn() { + // assert_evals_to!( + // indoc!( + // r#" + // limitedNegate = \num -> + // when num is + // 1 -> -1 + // 0 -> 0 - limitedNegate 1 - "# - ), - 42, - i64 - ); - } + // limitedNegate 1 + // "# + // ), + // 42, + // i64 + // ); + // } + + // #[test] + // fn apply_unnamed_fn() { + // assert_evals_to!( + // // We could improve the perf of this scenario by + // indoc!( + // r#" + // (\a -> a) 5 + // "# + // ), + // 5, + // i64 + // ); + // } + + // #[test] + // fn return_unnamed_fn() { + // assert_evals_to!( + // indoc!( + // r#" + // alwaysIdentity = \_ -> (\a -> a) + + // (alwaysIdentity 1) 3.14 + // "# + // ), + // 3.14, + // f64 + // ); + // } }