mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-03 00:24:34 +00:00
Introduce ll module
This commit is contained in:
parent
297c9f567a
commit
55e4ce1134
13 changed files with 1141 additions and 513 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -596,6 +596,7 @@ dependencies = [
|
||||||
"indoc 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
"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)",
|
"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)",
|
"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)",
|
"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)",
|
"petgraph 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
|
|
@ -21,6 +21,7 @@ inlinable_string = "0.1.0"
|
||||||
# but after several hours of trying unsuccessfully to fix it, `branch` is it.)
|
# but after several hours of trying unsuccessfully to fix it, `branch` is it.)
|
||||||
inkwell = { git = "https://github.com/TheDan64/inkwell", branch = "llvm8-0" }
|
inkwell = { git = "https://github.com/TheDan64/inkwell", branch = "llvm8-0" }
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
|
lazy_static = "1.4"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
pretty_assertions = "0.5.1"
|
pretty_assertions = "0.5.1"
|
||||||
|
|
|
@ -20,6 +20,18 @@ impl fmt::Debug for Symbol {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Into<InlinableString> for Symbol {
|
||||||
|
fn into(self) -> InlinableString {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<InlinableString> for Symbol {
|
||||||
|
fn from(string: InlinableString) -> Self {
|
||||||
|
Symbol(string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<&str> for Symbol {
|
impl From<&str> for Symbol {
|
||||||
fn from(string: &str) -> Self {
|
fn from(string: &str) -> Self {
|
||||||
Symbol(string.into())
|
Symbol(string.into())
|
||||||
|
|
471
src/gen/build.rs
Normal file
471
src/gen/build.rs
Normal file
|
@ -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<InlinableString, (Variable, PointerValue<'ctx>)>;
|
||||||
|
|
||||||
|
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<BasicValueEnum> = 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<BasicValueEnum> = 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::<BasicValueEnum>::into(then_val), &then_bb),
|
||||||
|
(&Into::<BasicValueEnum>::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<Variable>,
|
||||||
|
arg_names: &[InlinableString],
|
||||||
|
ret_type: BT,
|
||||||
|
body_expr: &Expr<'a>,
|
||||||
|
scope: &Scope<'ctx>,
|
||||||
|
procs: &mut Procs<'a, 'ctx>,
|
||||||
|
linkage: Option<Linkage>,
|
||||||
|
) -> 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.")
|
||||||
|
// }
|
||||||
|
// }
|
|
@ -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<Symbol, (Content, FunctionValue<'ctx>)>;
|
|
||||||
|
|
||||||
type Scope<'ctx> = ImMap<Symbol, (Content, PointerValue<'ctx>)>;
|
|
||||||
|
|
||||||
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<BasicValueEnum> = 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::<BasicValueEnum>::into(then_val), &then_bb),
|
|
||||||
(&Into::<BasicValueEnum>::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::<BasicValueEnum>::into(then_val), &then_bb),
|
|
||||||
(&Into::<BasicValueEnum>::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<Content>,
|
|
||||||
arg_names: &[Lowercase],
|
|
||||||
ret_type: BT,
|
|
||||||
body_expr: &Expr,
|
|
||||||
scope: &Scope<'ctx>,
|
|
||||||
procs: &mut Procs,
|
|
||||||
linkage: Option<Linkage>,
|
|
||||||
) -> 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)
|
|
||||||
}
|
|
|
@ -6,6 +6,6 @@ use inkwell::module::Module;
|
||||||
pub struct Env<'ctx, 'env> {
|
pub struct Env<'ctx, 'env> {
|
||||||
pub context: &'ctx Context,
|
pub context: &'ctx Context,
|
||||||
pub builder: &'env Builder<'ctx>,
|
pub builder: &'env Builder<'ctx>,
|
||||||
pub module: &'env Module<'ctx>,
|
pub module: &'ctx Module<'ctx>,
|
||||||
pub subs: Subs,
|
pub subs: Subs,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
pub mod compile;
|
pub mod build;
|
||||||
pub mod convert;
|
pub mod convert;
|
||||||
pub mod env;
|
pub mod env;
|
||||||
|
|
89
src/gen/proc.rs
Normal file
89
src/gen/proc.rs
Normal file
|
@ -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<Symbol, (Content, FunctionValue<'ctx>)>;
|
||||||
|
|
||||||
|
fn extract_procs(loc_expr: Located<Expr>, module: &Module<'ctx>, name: Option<Lowercase>, procs, &mut Procs<'ctx>) -> Located<Expr> {
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ pub mod ena;
|
||||||
pub mod fmt;
|
pub mod fmt;
|
||||||
pub mod gen;
|
pub mod gen;
|
||||||
pub mod infer;
|
pub mod infer;
|
||||||
|
pub mod ll;
|
||||||
pub mod load;
|
pub mod load;
|
||||||
pub mod module;
|
pub mod module;
|
||||||
pub mod pretty_print_types;
|
pub mod pretty_print_types;
|
||||||
|
@ -37,3 +38,6 @@ pub mod unify;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate log;
|
extern crate log;
|
||||||
|
|
||||||
|
// #[macro_use]
|
||||||
|
// extern crate lazy_static;
|
||||||
|
|
400
src/ll/expr.rs
Normal file
400
src/ll/expr.rs
Normal file
|
@ -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<InlinableString, (Option<Proc<'a>>, 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<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
|
||||||
|
//
|
||||||
|
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<Pattern>)],
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
95
src/ll/layout.rs
Normal file
95
src/ll/layout.rs
Normal file
|
@ -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<Self, ()> {
|
||||||
|
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<Layout<'a>, ()> {
|
||||||
|
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(&[])),
|
||||||
|
}
|
||||||
|
}
|
2
src/ll/mod.rs
Normal file
2
src/ll/mod.rs
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
pub mod expr;
|
||||||
|
pub mod layout;
|
|
@ -12,11 +12,12 @@ mod helpers;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test_gen {
|
mod test_gen {
|
||||||
use crate::helpers::can_expr;
|
use crate::helpers::can_expr;
|
||||||
|
use bumpalo::Bump;
|
||||||
use inkwell::context::Context;
|
use inkwell::context::Context;
|
||||||
use inkwell::execution_engine::JitFunction;
|
use inkwell::execution_engine::JitFunction;
|
||||||
use inkwell::types::BasicType;
|
use inkwell::types::BasicType;
|
||||||
use inkwell::OptimizationLevel;
|
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::convert::content_to_basic_type;
|
||||||
use roc::gen::env::Env;
|
use roc::gen::env::Env;
|
||||||
use roc::infer::infer_expr;
|
use roc::infer::infer_expr;
|
||||||
|
@ -24,6 +25,7 @@ mod test_gen {
|
||||||
|
|
||||||
macro_rules! assert_evals_to {
|
macro_rules! assert_evals_to {
|
||||||
($src:expr, $expected:expr, $ty:ty) => {
|
($src:expr, $expected:expr, $ty:ty) => {
|
||||||
|
let arena = Bump::new();
|
||||||
let (expr, _output, _problems, var_store, variable, constraint) = can_expr($src);
|
let (expr, _output, _problems, var_store, variable, constraint) = can_expr($src);
|
||||||
let mut subs = Subs::new(var_store.into());
|
let mut subs = Subs::new(var_store.into());
|
||||||
let mut unify_problems = Vec::new();
|
let mut unify_problems = Vec::new();
|
||||||
|
@ -49,9 +51,9 @@ mod test_gen {
|
||||||
subs,
|
subs,
|
||||||
builder: &builder,
|
builder: &builder,
|
||||||
context: &context,
|
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));
|
builder.build_return(Some(&ret));
|
||||||
|
|
||||||
|
@ -229,37 +231,66 @@ mod test_gen {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
// #[test]
|
||||||
fn gen_basic_fn() {
|
// fn gen_basic_fn() {
|
||||||
assert_evals_to!(
|
// assert_evals_to!(
|
||||||
indoc!(
|
// indoc!(
|
||||||
r#"
|
// r#"
|
||||||
always42 : Num.Num Int.Integer -> Num.Num Int.Integer
|
// always42 : Num.Num Int.Integer -> Num.Num Int.Integer
|
||||||
always42 = \num -> 42
|
// always42 = \num -> 42
|
||||||
|
|
||||||
always42 5
|
// always42 5
|
||||||
"#
|
// "#
|
||||||
),
|
// ),
|
||||||
42,
|
// 42,
|
||||||
i64
|
// i64
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
|
|
||||||
#[test]
|
// #[test]
|
||||||
fn gen_when_fn() {
|
// fn gen_when_fn() {
|
||||||
assert_evals_to!(
|
// assert_evals_to!(
|
||||||
indoc!(
|
// indoc!(
|
||||||
r#"
|
// r#"
|
||||||
limitedNegate = \num ->
|
// limitedNegate = \num ->
|
||||||
when num is
|
// when num is
|
||||||
1 -> -1
|
// 1 -> -1
|
||||||
0 -> 0
|
// 0 -> 0
|
||||||
|
|
||||||
limitedNegate 1
|
// limitedNegate 1
|
||||||
"#
|
// "#
|
||||||
),
|
// ),
|
||||||
42,
|
// 42,
|
||||||
i64
|
// 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
|
||||||
|
// );
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue