mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-02 08:11:12 +00:00
Fix function pointers for Cranelift and LLVM
This commit is contained in:
parent
68a46b4433
commit
d74aff960a
8 changed files with 168 additions and 481 deletions
|
@ -11,7 +11,7 @@ use cranelift_module::{Backend, FuncId, Linkage, Module};
|
|||
use inlinable_string::InlinableString;
|
||||
|
||||
use crate::collections::ImMap;
|
||||
use crate::crane::convert::{content_to_crane_type, type_from_layout};
|
||||
use crate::crane::convert::{content_to_crane_type, sig_from_layout, type_from_layout};
|
||||
use crate::mono::expr::{Expr, Proc, Procs};
|
||||
use crate::mono::layout::Layout;
|
||||
use crate::subs::Subs;
|
||||
|
@ -23,7 +23,7 @@ pub enum ScopeEntry {
|
|||
Stack { expr_type: Type, slot: StackSlot },
|
||||
Heap { expr_type: Type, ptr: Value },
|
||||
Arg { expr_type: Type, param: Value },
|
||||
FuncId(FuncId),
|
||||
Func { sig: Signature, func_id: FuncId },
|
||||
}
|
||||
|
||||
pub struct Env<'a> {
|
||||
|
@ -78,7 +78,7 @@ pub fn build_expr<'a, B: Backend>(
|
|||
let content = subs.get_without_compacting(*var).content;
|
||||
let layout = Layout::from_content(arena, content, subs)
|
||||
.unwrap_or_else(|()| panic!("TODO generate a runtime error here!"));
|
||||
let expr_type = type_from_layout(&layout, subs);
|
||||
let expr_type = type_from_layout(cfg, &layout, subs);
|
||||
|
||||
let slot = builder.create_stack_slot(StackSlotData::new(
|
||||
StackSlotKind::ExplicitSlot,
|
||||
|
@ -117,7 +117,7 @@ pub fn build_expr<'a, B: Backend>(
|
|||
}
|
||||
|
||||
let fn_id = match scope.get(name) {
|
||||
Some(ScopeEntry::FuncId(id)) => *id,
|
||||
Some(ScopeEntry::Func{ func_id, .. }) => *func_id,
|
||||
other => panic!(
|
||||
"CallByName could not find function named {:?} in scope; instead, found {:?} in scope {:?}",
|
||||
name, other, scope
|
||||
|
@ -132,43 +132,40 @@ pub fn build_expr<'a, B: Backend>(
|
|||
results[0]
|
||||
}
|
||||
}
|
||||
// FunctionPointer(ref fn_name) => {
|
||||
// let ptr = env
|
||||
// .module
|
||||
// .get_function(fn_name)
|
||||
// .unwrap_or_else(|| {
|
||||
// panic!("Could not get pointer to unknown function {:?}", fn_name)
|
||||
// })
|
||||
// .as_global_value()
|
||||
// .as_pointer_value();
|
||||
FunctionPointer(ref name) => {
|
||||
let fn_id = match scope.get(name) {
|
||||
Some(ScopeEntry::Func{ func_id, .. }) => *func_id,
|
||||
other => panic!(
|
||||
"FunctionPointer could not find function named {:?} in scope; instead, found {:?} in scope {:?}",
|
||||
name, other, scope
|
||||
),
|
||||
};
|
||||
|
||||
// BasicValueEnum::PointerValue(ptr)
|
||||
// }
|
||||
// CallByPointer(ref _ptr, ref args) => {
|
||||
// let mut arg_vals: Vec<BasicValueEnum> = Vec::with_capacity(args.len());
|
||||
let func_ref = module.declare_func_in_func(fn_id, &mut builder.func);
|
||||
|
||||
// for arg in args.iter() {
|
||||
// arg_vals.push(build_expr(env, scope, arg, procs));
|
||||
// }
|
||||
builder.ins().func_addr(env.cfg.pointer_type(), func_ref)
|
||||
}
|
||||
CallByPointer(ref sub_expr, ref args, ref fn_var) => {
|
||||
let subs = &env.subs;
|
||||
let mut arg_vals = Vec::with_capacity(args.len());
|
||||
|
||||
// panic!("TODO do a load(ptr) to get back the pointer, then pass *that* in here!");
|
||||
for arg in args.iter() {
|
||||
arg_vals.push(build_expr(env, scope, module, builder, arg, procs));
|
||||
}
|
||||
|
||||
// // let call = match build_expr(env, scope, 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
|
||||
// // );
|
||||
// // }
|
||||
// // };
|
||||
let content = subs.get_without_compacting(*fn_var).content;
|
||||
let layout = Layout::from_content(env.arena, content, &subs)
|
||||
.unwrap_or_else(|()| panic!("TODO generate a runtime error here!"));
|
||||
let sig = sig_from_layout(env.cfg, module, layout, &subs);
|
||||
let callee = build_expr(env, scope, module, builder, sub_expr, procs);
|
||||
let sig_ref = builder.import_signature(sig);
|
||||
let call = builder.ins().call_indirect(sig_ref, callee, &arg_vals);
|
||||
let results = builder.inst_results(call);
|
||||
|
||||
// // call.try_as_basic_value()
|
||||
// // .left()
|
||||
// // .unwrap_or_else(|| panic!("LLVM error: Invalid call by pointer."))
|
||||
// }
|
||||
debug_assert!(results.len() == 1);
|
||||
|
||||
results[0]
|
||||
}
|
||||
Load(name) => match scope.get(name) {
|
||||
Some(ScopeEntry::Stack { expr_type, slot }) => {
|
||||
builder
|
||||
|
@ -181,7 +178,7 @@ pub fn build_expr<'a, B: Backend>(
|
|||
.ins()
|
||||
.load(*expr_type, MemFlags::new(), *ptr, Offset32::new(0))
|
||||
}
|
||||
Some(ScopeEntry::FuncId(_fn_id)) => {
|
||||
Some(ScopeEntry::Func { .. }) => {
|
||||
panic!("TODO I don't yet know how to return fn pointers")
|
||||
}
|
||||
None => panic!("Could not find a var for {:?} in scope {:?}", name, scope),
|
||||
|
@ -341,6 +338,7 @@ pub fn declare_proc<'a, B: Backend>(
|
|||
) -> (FuncId, Signature) {
|
||||
let args = proc.args;
|
||||
let subs = &env.subs;
|
||||
let cfg = env.cfg;
|
||||
let ret_content = subs.get_without_compacting(proc.ret_var).content;
|
||||
// TODO this content_to_crane_type is duplicated when building this Proc
|
||||
let ret_type = content_to_crane_type(&ret_content, subs, env.cfg).unwrap_or_else(|err| {
|
||||
|
@ -358,7 +356,7 @@ pub fn declare_proc<'a, B: Backend>(
|
|||
|
||||
// Add params to the signature
|
||||
for (layout, _name, _var) in args.iter() {
|
||||
let arg_type = type_from_layout(&layout, subs);
|
||||
let arg_type = type_from_layout(cfg, &layout, subs);
|
||||
|
||||
sig.params.push(AbiParam::new(arg_type));
|
||||
}
|
||||
|
@ -385,6 +383,7 @@ pub fn define_proc_body<'a, B: Backend>(
|
|||
) {
|
||||
let args = proc.args;
|
||||
let subs = &env.subs;
|
||||
let cfg = env.cfg;
|
||||
|
||||
// Build the body of the function
|
||||
{
|
||||
|
@ -409,7 +408,7 @@ pub fn define_proc_body<'a, B: Backend>(
|
|||
//
|
||||
let layout = Layout::from_content(arena, content, subs)
|
||||
.unwrap_or_else(|()| panic!("TODO generate a runtime error here!"));
|
||||
let expr_type = type_from_layout(&layout, subs);
|
||||
let expr_type = type_from_layout(cfg, &layout, subs);
|
||||
|
||||
scope.insert(arg_name.clone(), ScopeEntry::Arg { expr_type, param });
|
||||
}
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
use cranelift_codegen::ir::{types, Type};
|
||||
use cranelift::prelude::AbiParam;
|
||||
use cranelift_codegen::ir::{types, Signature, Type};
|
||||
use cranelift_codegen::isa::TargetFrontendConfig;
|
||||
|
||||
use crate::mono::layout::Layout;
|
||||
use crate::subs::FlatType::*;
|
||||
use crate::subs::{Content, Subs};
|
||||
use cranelift_module::{Backend, Module};
|
||||
|
||||
pub fn content_to_crane_type(
|
||||
content: &Content,
|
||||
|
@ -82,20 +84,15 @@ fn num_to_crane_type(content: Content) -> Result<Type, String> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn type_from_layout(layout: &Layout<'_>, _subs: &Subs) -> Type {
|
||||
pub fn type_from_layout(cfg: TargetFrontendConfig, layout: &Layout<'_>, _subs: &Subs) -> Type {
|
||||
use crate::mono::layout::Builtin::*;
|
||||
use crate::mono::layout::Layout::*;
|
||||
|
||||
match layout {
|
||||
FunctionPointer(_arg_layouts, _ret_layout) => {
|
||||
panic!("TODO function poitner");
|
||||
}
|
||||
Pointer(_) | FunctionPointer(_, _) => cfg.pointer_type(),
|
||||
Struct(_fields) => {
|
||||
panic!("TODO layout_to_crane_type for Struct");
|
||||
}
|
||||
Pointer(_layout) => {
|
||||
panic!("TODO layout_to_crane_type for Pointer");
|
||||
}
|
||||
Builtin(builtin) => match builtin {
|
||||
Int64 => types::I64,
|
||||
Float64 => types::F64,
|
||||
|
@ -105,3 +102,32 @@ pub fn type_from_layout(layout: &Layout<'_>, _subs: &Subs) -> Type {
|
|||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sig_from_layout<B: Backend>(
|
||||
cfg: TargetFrontendConfig,
|
||||
module: &mut Module<B>,
|
||||
layout: Layout,
|
||||
subs: &Subs,
|
||||
) -> Signature {
|
||||
match layout {
|
||||
Layout::FunctionPointer(args, ret) => {
|
||||
let ret_type = type_from_layout(cfg, &ret, subs);
|
||||
let mut sig = module.make_signature();
|
||||
|
||||
// Add return type to the signature
|
||||
sig.returns.push(AbiParam::new(ret_type));
|
||||
|
||||
// Add params to the signature
|
||||
for layout in args.iter() {
|
||||
let arg_type = type_from_layout(cfg, &layout, subs);
|
||||
|
||||
sig.params.push(AbiParam::new(arg_type));
|
||||
}
|
||||
|
||||
sig
|
||||
}
|
||||
_ => {
|
||||
panic!("Could not make Signature from Layout {:?}", layout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -116,9 +116,9 @@ pub fn build_expr<'a, 'ctx, 'env>(
|
|||
|
||||
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."))
|
||||
call.try_as_basic_value().left().unwrap_or_else(|| {
|
||||
panic!("LLVM error: Invalid call by name for name {:?}", name)
|
||||
})
|
||||
}
|
||||
}
|
||||
FunctionPointer(ref fn_name) => {
|
||||
|
@ -133,30 +133,28 @@ pub fn build_expr<'a, 'ctx, 'env>(
|
|||
|
||||
BasicValueEnum::PointerValue(ptr)
|
||||
}
|
||||
CallByPointer(ref _ptr, ref args) => {
|
||||
CallByPointer(ref sub_expr, ref args, _var) => {
|
||||
let mut arg_vals: Vec<BasicValueEnum> = Vec::with_capacity(args.len());
|
||||
|
||||
for arg in args.iter() {
|
||||
arg_vals.push(build_expr(env, scope, parent, arg, procs));
|
||||
}
|
||||
|
||||
panic!("TODO do a load(ptr) to get back the pointer, then pass *that* in here!");
|
||||
let call = match build_expr(env, scope, parent, sub_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
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// 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."))
|
||||
call.try_as_basic_value()
|
||||
.left()
|
||||
.unwrap_or_else(|| panic!("LLVM error: Invalid call by pointer."))
|
||||
}
|
||||
|
||||
Load(name) => match scope.get(name) {
|
||||
|
@ -368,6 +366,7 @@ pub fn build_proc<'a, 'ctx, 'env>(
|
|||
}
|
||||
|
||||
let fn_type = get_fn_type(&ret_type, &arg_basic_types);
|
||||
|
||||
let fn_val = env
|
||||
.module
|
||||
.add_function(&name, fn_type, Some(Linkage::Private));
|
||||
|
@ -401,9 +400,7 @@ pub fn build_proc<'a, 'ctx, 'env>(
|
|||
}
|
||||
|
||||
pub fn verify_fn(fn_val: FunctionValue<'_>) {
|
||||
if fn_val.verify(PRINT_FN_VERIFICATION_OUTPUT) {
|
||||
// TODO call pass_manager.run_on(&fn_val) to optimize it!
|
||||
} else {
|
||||
if !fn_val.verify(PRINT_FN_VERIFICATION_OUTPUT) {
|
||||
unsafe {
|
||||
fn_val.delete();
|
||||
}
|
||||
|
|
|
@ -46,8 +46,9 @@ pub fn content_to_basic_type<'ctx>(
|
|||
arg_basic_types.push(content_to_basic_type(&arg_content, subs, context)?);
|
||||
}
|
||||
let fn_type = get_fn_type(&ret_type, arg_basic_types.as_slice());
|
||||
let ptr_type = fn_type.ptr_type(AddressSpace::Generic);
|
||||
|
||||
Ok(fn_type.ptr_type(AddressSpace::Global).as_basic_type_enum())
|
||||
Ok(ptr_type.as_basic_type_enum())
|
||||
}
|
||||
other => panic!("TODO handle content_to_basic_type for {:?}", other),
|
||||
},
|
||||
|
|
342
src/llvm/expr.rs
342
src/llvm/expr.rs
|
@ -1,342 +0,0 @@
|
|||
use crate::can;
|
||||
use crate::can::pattern::Pattern;
|
||||
use crate::collections::MutMap;
|
||||
use crate::mono::layout::Layout;
|
||||
use crate::region::Located;
|
||||
use crate::subs::{Subs, Variable};
|
||||
use bumpalo::collections::Vec;
|
||||
use bumpalo::Bump;
|
||||
use inlinable_string::InlinableString;
|
||||
|
||||
pub type Procs<'a> = MutMap<InlinableString, Option<Proc<'a>>>;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Proc<'a> {
|
||||
pub args: &'a [(Layout<'a>, InlinableString, Variable)],
|
||||
pub body: Expr<'a>,
|
||||
pub closes_over: Layout<'a>,
|
||||
pub ret_var: Variable,
|
||||
}
|
||||
|
||||
struct Env<'a> {
|
||||
pub arena: &'a Bump,
|
||||
pub subs: &'a Subs,
|
||||
}
|
||||
|
||||
#[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(&'a Expr<'a>, &'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(
|
||||
arena: &'a Bump,
|
||||
subs: &'a Subs,
|
||||
can_expr: can::expr::Expr,
|
||||
procs: &mut Procs<'a>,
|
||||
) -> Self {
|
||||
let env = Env { arena, subs };
|
||||
|
||||
from_can(&env, can_expr, procs, None)
|
||||
}
|
||||
}
|
||||
|
||||
fn from_can<'a>(
|
||||
env: &Env<'a>,
|
||||
can_expr: can::expr::Expr,
|
||||
procs: &mut Procs<'a>,
|
||||
name: Option<InlinableString>,
|
||||
) -> 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
|
||||
//
|
||||
if let Identifier(name) = &loc_pattern.value {
|
||||
if let Closure(_, _, _, _, _) = &loc_expr.value {
|
||||
// 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()),
|
||||
ptr => {
|
||||
// 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(&*env.arena.alloc(ptr), args.into_bump_slice())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
When {
|
||||
cond_var,
|
||||
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_cond.value,
|
||||
cond_var,
|
||||
procs,
|
||||
&mut stored,
|
||||
);
|
||||
|
||||
let ret = from_can(env, loc_branch.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>(
|
||||
env: &Env<'a>,
|
||||
name: InlinableString,
|
||||
can_body: can::expr::Expr,
|
||||
ret_var: Variable,
|
||||
loc_args: &[(Variable, Located<Pattern>)],
|
||||
procs: &mut Procs<'a>,
|
||||
) -> Expr<'a> {
|
||||
let subs = &env.subs;
|
||||
let arena = env.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;
|
||||
|
||||
let layout = match Layout::from_content(arena, content, subs) {
|
||||
Ok(layout) => layout,
|
||||
Err(()) => {
|
||||
// Invalid closure!
|
||||
procs.insert(name.clone(), None);
|
||||
|
||||
return Expr::FunctionPointer(name);
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
proc_args.push((layout, arg_name, *arg_var));
|
||||
}
|
||||
|
||||
let proc = Proc {
|
||||
args: proc_args.into_bump_slice(),
|
||||
body: from_can(env, can_body, procs, None),
|
||||
closes_over: Layout::Struct(&[]),
|
||||
ret_var,
|
||||
};
|
||||
|
||||
procs.insert(name.clone(), Some(proc));
|
||||
|
||||
Expr::FunctionPointer(name)
|
||||
}
|
||||
|
||||
fn store_pattern<'a>(
|
||||
env: &Env<'a>,
|
||||
can_pat: Pattern,
|
||||
can_expr: can::expr::Expr,
|
||||
var: Variable,
|
||||
procs: &mut Procs<'a>,
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -45,8 +45,8 @@ pub enum Expr<'a> {
|
|||
|
||||
// Functions
|
||||
FunctionPointer(InlinableString),
|
||||
CallByPointer(&'a Expr<'a>, &'a [Expr<'a>]),
|
||||
CallByName(InlinableString, &'a [Expr<'a>]),
|
||||
CallByPointer(&'a Expr<'a>, &'a [Expr<'a>], Variable),
|
||||
|
||||
// Exactly two conditional branches, e.g. if/else
|
||||
Cond {
|
||||
|
@ -159,16 +159,13 @@ fn from_can<'a>(
|
|||
|
||||
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());
|
||||
let name = name.unwrap_or_else(|| gen_closure_name(procs));
|
||||
|
||||
add_closure(env, name, loc_body.value, ret_var, &loc_args, procs)
|
||||
}
|
||||
|
||||
Call(boxed, loc_args, _) => {
|
||||
let (_, loc_expr, _) = *boxed;
|
||||
let (fn_var, loc_expr, _) = *boxed;
|
||||
let mut args = Vec::with_capacity_in(loc_args.len(), env.arena);
|
||||
|
||||
for (_, loc_arg) in loc_args {
|
||||
|
@ -185,7 +182,7 @@ fn from_can<'a>(
|
|||
// It might even be the anonymous result of a conditional:
|
||||
//
|
||||
// ((if x > 0 then \a -> a else \_ -> 0) 5)
|
||||
Expr::CallByPointer(&*env.arena.alloc(ptr), args.into_bump_slice())
|
||||
Expr::CallByPointer(&*env.arena.alloc(ptr), args.into_bump_slice(), fn_var)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -355,3 +352,9 @@ fn store_pattern<'a>(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_closure_name(procs: &Procs<'_>) -> InlinableString {
|
||||
// Give the closure a name like "_0" or "_1".
|
||||
// We know procs.len() will be unique!
|
||||
format!("_{}", procs.len()).into()
|
||||
}
|
||||
|
|
|
@ -75,14 +75,14 @@ mod test_crane {
|
|||
let mut declared = Vec::with_capacity(procs.len());
|
||||
|
||||
// Declare all the Procs, then insert them into scope so their bodies
|
||||
// can look up their FuncIds later when calling each other by value.
|
||||
// can look up their Funcs in scope later when calling each other by value.
|
||||
for (name, opt_proc) in procs.iter() {
|
||||
if let Some(proc) = opt_proc {
|
||||
let (fn_id, sig) = declare_proc(&env, &mut module, name.clone(), proc);
|
||||
let (func_id, sig) = declare_proc(&env, &mut module, name.clone(), proc);
|
||||
|
||||
scope.insert(name.clone(), ScopeEntry::FuncId(fn_id));
|
||||
declared.push((proc.clone(), sig.clone(), func_id));
|
||||
|
||||
declared.push((proc.clone(), sig, fn_id));
|
||||
scope.insert(name.clone(), ScopeEntry::Func { func_id, sig });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -346,34 +346,33 @@ mod test_crane {
|
|||
// );
|
||||
// }
|
||||
|
||||
// #[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 apply_unnamed_fn() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
(\a -> a) 5
|
||||
"#
|
||||
),
|
||||
5,
|
||||
i64
|
||||
);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn return_unnamed_fn() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// alwaysIdentity : Num.Num Int.Integer -> (Num.Num Float.FloatingPoint -> Num.Num Float.FloatingPoint)
|
||||
// alwaysIdentity = \num ->
|
||||
// (\a -> a)
|
||||
#[test]
|
||||
fn return_unnamed_fn() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
alwaysIdentity : Num.Num Int.Integer -> (Num.Num Float.FloatingPoint -> Num.Num Float.FloatingPoint)
|
||||
alwaysIdentity = \num ->
|
||||
(\a -> a)
|
||||
|
||||
// (alwaysIdentity 2) 3.14
|
||||
// "#
|
||||
// ),
|
||||
// 3.14,
|
||||
// f64
|
||||
// );
|
||||
// }
|
||||
(alwaysIdentity 2) 3.14
|
||||
"#
|
||||
),
|
||||
3.14,
|
||||
f64
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -75,12 +75,17 @@ mod test_llvm {
|
|||
// Add all the Procs to the module
|
||||
for (name, opt_proc) in procs.clone() {
|
||||
if let Some(proc) = opt_proc {
|
||||
// NOTE: This is here to be uncommented in case verification fails.
|
||||
// (This approach means we don't have to defensively clone name here.)
|
||||
//
|
||||
// println!("\n\nBuilding and then verifying function {}\n\n", name);
|
||||
let fn_val = build_proc(&env, name, proc, &procs);
|
||||
|
||||
if fn_val.verify(true) {
|
||||
fpm.run_on(&fn_val);
|
||||
} else {
|
||||
panic!("Function {} failed LLVM verification.", main_fn_name);
|
||||
// NOTE: If this fails, uncomment the above println to debug
|
||||
panic!("Non-main function failed LLVM verification.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -332,34 +337,33 @@ mod test_llvm {
|
|||
);
|
||||
}
|
||||
|
||||
// #[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 apply_unnamed_fn() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
(\a -> a) 5
|
||||
"#
|
||||
),
|
||||
5,
|
||||
i64
|
||||
);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn return_unnamed_fn() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// alwaysIdentity : Num.Num Int.Integer -> (Num.Num Float.FloatingPoint -> Num.Num Float.FloatingPoint)
|
||||
// alwaysIdentity = \num ->
|
||||
// (\a -> a)
|
||||
#[test]
|
||||
fn return_unnamed_fn() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
alwaysIdentity : Num.Num Int.Integer -> (Num.Num Float.FloatingPoint -> Num.Num Float.FloatingPoint)
|
||||
alwaysIdentity = \num ->
|
||||
(\a -> a)
|
||||
|
||||
// (alwaysIdentity 2) 3.14
|
||||
// "#
|
||||
// ),
|
||||
// 3.14,
|
||||
// f64
|
||||
// );
|
||||
// }
|
||||
(alwaysIdentity 2) 3.14
|
||||
"#
|
||||
),
|
||||
3.14,
|
||||
f64
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue