mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-03 16:44:33 +00:00
Get Switch running on Crane
This commit is contained in:
parent
750a9f9ca3
commit
e49b608157
6 changed files with 568 additions and 281 deletions
|
@ -1,4 +1,6 @@
|
||||||
|
use bumpalo::collections::Vec;
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
|
use cranelift::frontend::Switch;
|
||||||
use cranelift::prelude::{
|
use cranelift::prelude::{
|
||||||
AbiParam, ExternalName, FunctionBuilder, FunctionBuilderContext, MemFlags,
|
AbiParam, ExternalName, FunctionBuilder, FunctionBuilderContext, MemFlags,
|
||||||
};
|
};
|
||||||
|
@ -11,10 +13,10 @@ use cranelift_module::{Backend, FuncId, Linkage, Module};
|
||||||
use inlinable_string::InlinableString;
|
use inlinable_string::InlinableString;
|
||||||
|
|
||||||
use crate::collections::ImMap;
|
use crate::collections::ImMap;
|
||||||
use crate::crane::convert::{content_to_crane_type, sig_from_layout, type_from_layout};
|
use crate::crane::convert::{sig_from_layout, type_from_layout, type_from_var};
|
||||||
use crate::mono::expr::{Expr, Proc, Procs};
|
use crate::mono::expr::{Expr, Proc, Procs};
|
||||||
use crate::mono::layout::Layout;
|
use crate::mono::layout::Layout;
|
||||||
use crate::subs::Subs;
|
use crate::subs::{Content, FlatType, Subs, Variable};
|
||||||
|
|
||||||
type Scope = ImMap<InlinableString, ScopeEntry>;
|
type Scope = ImMap<InlinableString, ScopeEntry>;
|
||||||
|
|
||||||
|
@ -64,8 +66,23 @@ pub fn build_expr<'a, B: Backend>(
|
||||||
|
|
||||||
// build_cond(env, scope, cond, procs)
|
// build_cond(env, scope, cond, procs)
|
||||||
// }
|
// }
|
||||||
Branches { .. } => {
|
Switch {
|
||||||
panic!("TODO build_branches(env, scope, cond_lhs, branches, procs)");
|
cond,
|
||||||
|
branches,
|
||||||
|
default_branch,
|
||||||
|
ret_var,
|
||||||
|
cond_var,
|
||||||
|
} => {
|
||||||
|
let ret_type = type_from_var(*ret_var, &env.subs, env.cfg);
|
||||||
|
let switch_args = SwitchArgs {
|
||||||
|
cond_var: *cond_var,
|
||||||
|
cond_expr: cond,
|
||||||
|
branches: branches,
|
||||||
|
default_branch,
|
||||||
|
ret_type,
|
||||||
|
};
|
||||||
|
|
||||||
|
build_switch(env, scope, module, builder, switch_args, procs)
|
||||||
}
|
}
|
||||||
Store(ref stores, ref ret) => {
|
Store(ref stores, ref ret) => {
|
||||||
let mut scope = im_rc::HashMap::clone(scope);
|
let mut scope = im_rc::HashMap::clone(scope);
|
||||||
|
@ -77,7 +94,7 @@ pub fn build_expr<'a, B: Backend>(
|
||||||
let val = build_expr(env, &scope, module, builder, &expr, procs);
|
let val = build_expr(env, &scope, module, builder, &expr, procs);
|
||||||
let content = subs.get_without_compacting(*var).content;
|
let content = subs.get_without_compacting(*var).content;
|
||||||
let layout = Layout::from_content(arena, content, subs)
|
let layout = Layout::from_content(arena, content, subs)
|
||||||
.unwrap_or_else(|()| panic!("TODO generate a runtime error here!"));
|
.unwrap_or_else(|()| panic!("TODO generate a runtime error for this Store!"));
|
||||||
let expr_type = type_from_layout(cfg, &layout, subs);
|
let expr_type = type_from_layout(cfg, &layout, subs);
|
||||||
|
|
||||||
let slot = builder.create_stack_slot(StackSlotData::new(
|
let slot = builder.create_stack_slot(StackSlotData::new(
|
||||||
|
@ -110,7 +127,7 @@ pub fn build_expr<'a, B: Backend>(
|
||||||
} else if name == "Bool.and" {
|
} else if name == "Bool.and" {
|
||||||
panic!("TODO create a branch for &&");
|
panic!("TODO create a branch for &&");
|
||||||
} else {
|
} else {
|
||||||
let mut arg_vals = Vec::with_capacity(args.len());
|
let mut arg_vals = Vec::with_capacity_in(args.len(), env.arena);
|
||||||
|
|
||||||
for arg in args.iter() {
|
for arg in args.iter() {
|
||||||
arg_vals.push(build_expr(env, scope, module, builder, arg, procs));
|
arg_vals.push(build_expr(env, scope, module, builder, arg, procs));
|
||||||
|
@ -147,7 +164,7 @@ pub fn build_expr<'a, B: Backend>(
|
||||||
}
|
}
|
||||||
CallByPointer(ref sub_expr, ref args, ref fn_var) => {
|
CallByPointer(ref sub_expr, ref args, ref fn_var) => {
|
||||||
let subs = &env.subs;
|
let subs = &env.subs;
|
||||||
let mut arg_vals = Vec::with_capacity(args.len());
|
let mut arg_vals = Vec::with_capacity_in(args.len(), env.arena);
|
||||||
|
|
||||||
for arg in args.iter() {
|
for arg in args.iter() {
|
||||||
arg_vals.push(build_expr(env, scope, module, builder, arg, procs));
|
arg_vals.push(build_expr(env, scope, module, builder, arg, procs));
|
||||||
|
@ -204,97 +221,29 @@ pub fn build_expr<'a, B: Backend>(
|
||||||
// cond: Cond2<'a>,
|
// cond: Cond2<'a>,
|
||||||
// procs: &Procs<'a>,
|
// procs: &Procs<'a>,
|
||||||
// ) -> BasicValueEnum<'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_crane_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, cond.cond_lhs, procs);
|
|
||||||
// let rhs = build_expr(env, scope, 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, 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, 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: &Procs<'a, 'ctx>,
|
|
||||||
// ) -> BasicValueEnum<'ctx> {
|
|
||||||
// let builder = env.builder;
|
// let builder = env.builder;
|
||||||
// let context = env.context;
|
// let context = env.context;
|
||||||
// let lhs = build_expr(env, scope, cond_lhs, procs);
|
// let subs = &env.subs;
|
||||||
// let mut branch_iter = branches.into_iter();
|
|
||||||
// let content = subs.get_without_compacting(cond.ret_var).content;
|
// let content = subs.get_without_compacting(cond.ret_var).content;
|
||||||
// let ret_type = content_to_crane_type(&content, subs, context).unwrap_or_else(|err| {
|
// let ret_type = type_from_content(&content, subs, context).unwrap_or_else(|err| {
|
||||||
// panic!(
|
// panic!(
|
||||||
// "Error converting cond branch ret_type content {:?} to basic type: {:?}",
|
// "Error converting cond branch ret_type content {:?} to basic type: {:?}",
|
||||||
// cond.pass, err
|
// cond.pass, err
|
||||||
// )
|
// )
|
||||||
// });
|
// });
|
||||||
|
|
||||||
// for (cond_rhs, cond_pass, cond_else) in branches {
|
// let lhs = build_expr(env, scope, cond.cond_lhs, procs);
|
||||||
// let rhs = build_expr(env, scope, cond_rhs, procs);
|
// let rhs = build_expr(env, scope, cond.cond_rhs, procs);
|
||||||
// let pass = build_expr(env, scope, cond_pass, procs);
|
|
||||||
// let fail = build_expr(env, scope, cond_else, procs);
|
|
||||||
|
|
||||||
// let cond = Cond {
|
// match (lhs_layout, rhs_layout) {
|
||||||
// lhs,
|
// // TODO do this based on lhs_type and rhs_type
|
||||||
// rhs,
|
// (Layout::Float64, Layout::Float64) => {
|
||||||
// pass,
|
// let then_ebb = builder.create_ebb();
|
||||||
// fail,
|
// builder.switch_to_block(then_ebb);
|
||||||
// ret_type,
|
|
||||||
// };
|
|
||||||
|
|
||||||
// build_cond(env, scope, cond, procs)
|
// let else_ebb = builder.create_ebb();
|
||||||
// }
|
// builder.switch_to_block(else_ebb);
|
||||||
// }
|
|
||||||
|
|
||||||
// TODO trim down these arguments
|
|
||||||
// #[allow(clippy::too_many_arguments)]
|
|
||||||
// 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: &Procs<'a>,
|
|
||||||
// ) -> BasicValueEnum<'ctx> {
|
|
||||||
// let builder = env.builder;
|
|
||||||
// let context = env.context;
|
|
||||||
|
|
||||||
// // build branch
|
// // build branch
|
||||||
// let then_bb = context.append_basic_block("then");
|
// let then_bb = context.append_basic_block("then");
|
||||||
|
@ -328,7 +277,149 @@ pub fn build_expr<'a, B: Backend>(
|
||||||
// ]);
|
// ]);
|
||||||
|
|
||||||
// phi.as_basic_value()
|
// phi.as_basic_value()
|
||||||
|
|
||||||
|
// builder.ins().fcmp(FloatCC::Equal, lhs, rhs, ebb, &[]);
|
||||||
|
|
||||||
|
// build_phi2(
|
||||||
|
// env, scope, comparison, cond.pass, cond.fail, ret_type, procs,
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
|
||||||
|
// (Layout::Int64, Layout::Int64) => {
|
||||||
|
// let ebb = builder.create_ebb();
|
||||||
|
|
||||||
|
// builder.ins().icmp(IntCC::Equal, lhs, rhs, ebb, &[]);
|
||||||
|
|
||||||
|
// build_phi2(
|
||||||
|
// env, scope, 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
|
||||||
|
// ),
|
||||||
|
// }
|
||||||
// }
|
// }
|
||||||
|
struct SwitchArgs<'a> {
|
||||||
|
pub cond_expr: &'a Expr<'a>,
|
||||||
|
pub cond_var: Variable,
|
||||||
|
pub branches: &'a [(u64, Expr<'a>)],
|
||||||
|
pub default_branch: &'a Expr<'a>,
|
||||||
|
pub ret_type: Type,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_switch<'a, B: Backend>(
|
||||||
|
env: &Env<'a>,
|
||||||
|
scope: &Scope,
|
||||||
|
module: &mut Module<B>,
|
||||||
|
builder: &mut FunctionBuilder,
|
||||||
|
switch_args: SwitchArgs<'a>,
|
||||||
|
procs: &Procs<'a>,
|
||||||
|
) -> Value {
|
||||||
|
let subs = &env.subs;
|
||||||
|
let mut switch = Switch::new();
|
||||||
|
let SwitchArgs {
|
||||||
|
branches,
|
||||||
|
cond_expr,
|
||||||
|
cond_var,
|
||||||
|
default_branch,
|
||||||
|
ret_type,
|
||||||
|
} = switch_args;
|
||||||
|
let mut blocks = Vec::with_capacity_in(branches.len(), env.arena);
|
||||||
|
|
||||||
|
// Declare a variable which each branch will mutate to be the value of that branch.
|
||||||
|
// At the end of the expression, we will evaluate to this.
|
||||||
|
let ret = cranelift::frontend::Variable::with_u32(0);
|
||||||
|
|
||||||
|
builder.declare_var(ret, ret_type);
|
||||||
|
|
||||||
|
// The block for the conditional's default branch.
|
||||||
|
let default_block = builder.create_ebb();
|
||||||
|
|
||||||
|
// The block we'll jump to once the switch has completed.
|
||||||
|
let ret_block = builder.create_ebb();
|
||||||
|
|
||||||
|
// Build the blocks for each branch, and register them in the switch.
|
||||||
|
// Do this before emitting the switch, because it needs to be emitted at the front.
|
||||||
|
for (int, _) in branches {
|
||||||
|
let block = builder.create_ebb();
|
||||||
|
|
||||||
|
blocks.push(block);
|
||||||
|
|
||||||
|
switch.set_entry(*int, block);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the switch. Each branch will mutate ret and then jump to ret_ebb.
|
||||||
|
let cond = build_expr(env, scope, module, builder, cond_expr, procs);
|
||||||
|
|
||||||
|
// If necessary, convert cond from Float to Int using a bitcast.
|
||||||
|
let cond = match subs.get_without_compacting(cond_var).content {
|
||||||
|
Content::Structure(FlatType::Apply {
|
||||||
|
module_name,
|
||||||
|
name,
|
||||||
|
args,
|
||||||
|
}) if module_name.as_str() == crate::types::MOD_NUM
|
||||||
|
&& name.as_str() == crate::types::TYPE_NUM =>
|
||||||
|
{
|
||||||
|
let arg = *args.iter().next().unwrap();
|
||||||
|
|
||||||
|
match subs.get_without_compacting(arg).content {
|
||||||
|
Content::Structure(FlatType::Apply {
|
||||||
|
module_name, name, ..
|
||||||
|
}) if module_name.as_str() == crate::types::MOD_FLOAT => {
|
||||||
|
debug_assert!(name.as_str() == crate::types::TYPE_FLOATINGPOINT);
|
||||||
|
|
||||||
|
// This is an f64, but switch only works on u64.
|
||||||
|
//
|
||||||
|
// Since it's the same size, we could theoretically use raw_bitcast
|
||||||
|
// which doesn't actually change the bits, just allows
|
||||||
|
// them to be used as a different type from its register.
|
||||||
|
//
|
||||||
|
// However, in practice, this fails Cranelift verification.
|
||||||
|
builder.ins().bitcast(types::I64, cond)
|
||||||
|
}
|
||||||
|
_ => cond,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
other => panic!("Cannot Switch on type {:?}", other),
|
||||||
|
};
|
||||||
|
|
||||||
|
switch.emit(builder, cond, default_block);
|
||||||
|
|
||||||
|
let mut build_branch = |block, expr| {
|
||||||
|
builder.switch_to_block(block);
|
||||||
|
// TODO re-enable this once Switch stops making unsealed
|
||||||
|
// EBBs, e.g. https://docs.rs/cranelift-frontend/0.52.0/src/cranelift_frontend/switch.rs.html#143
|
||||||
|
// builder.seal_block(block);
|
||||||
|
|
||||||
|
// Mutate the ret variable to be the outcome of this branch.
|
||||||
|
let value = build_expr(env, scope, module, builder, expr, procs);
|
||||||
|
|
||||||
|
builder.def_var(ret, value);
|
||||||
|
|
||||||
|
// Unconditionally jump to ret_block, making the whole expression evaluate to ret.
|
||||||
|
builder.ins().jump(ret_block, &[]);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Build the blocks for each branch
|
||||||
|
for ((_, expr), block) in branches.iter().zip(blocks) {
|
||||||
|
build_branch(block, expr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the block for the default branch
|
||||||
|
build_branch(default_block, default_branch);
|
||||||
|
|
||||||
|
// Finally, build ret_block - which contains our terminator instruction.
|
||||||
|
{
|
||||||
|
builder.switch_to_block(ret_block);
|
||||||
|
// TODO re-enable this once Switch stops making unsealed
|
||||||
|
// EBBs, e.g. https://docs.rs/cranelift-frontend/0.52.0/src/cranelift_frontend/switch.rs.html#143
|
||||||
|
// builder.seal_block(block);
|
||||||
|
|
||||||
|
// Now that ret has been mutated by the switch statement, evaluate to it.
|
||||||
|
builder.use_var(ret)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn declare_proc<'a, B: Backend>(
|
pub fn declare_proc<'a, B: Backend>(
|
||||||
env: &Env<'a>,
|
env: &Env<'a>,
|
||||||
|
@ -339,14 +430,8 @@ pub fn declare_proc<'a, B: Backend>(
|
||||||
let args = proc.args;
|
let args = proc.args;
|
||||||
let subs = &env.subs;
|
let subs = &env.subs;
|
||||||
let cfg = env.cfg;
|
let cfg = env.cfg;
|
||||||
let ret_content = subs.get_without_compacting(proc.ret_var).content;
|
// TODO this rtype_from_var is duplicated when building this Proc
|
||||||
// TODO this content_to_crane_type is duplicated when building this Proc
|
let ret_type = type_from_var(proc.ret_var, subs, env.cfg);
|
||||||
let ret_type = content_to_crane_type(&ret_content, subs, env.cfg).unwrap_or_else(|err| {
|
|
||||||
panic!(
|
|
||||||
"Error converting function return value content to basic type: {:?}",
|
|
||||||
err
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create a signature for the function
|
// Create a signature for the function
|
||||||
let mut sig = module.make_signature();
|
let mut sig = module.make_signature();
|
||||||
|
@ -396,15 +481,15 @@ pub fn define_proc_body<'a, B: Backend>(
|
||||||
let mut func_ctx = FunctionBuilderContext::new();
|
let mut func_ctx = FunctionBuilderContext::new();
|
||||||
let mut builder: FunctionBuilder = FunctionBuilder::new(&mut ctx.func, &mut func_ctx);
|
let mut builder: FunctionBuilder = FunctionBuilder::new(&mut ctx.func, &mut func_ctx);
|
||||||
|
|
||||||
let ebb = builder.create_ebb();
|
let block = builder.create_ebb();
|
||||||
|
|
||||||
builder.switch_to_block(ebb);
|
builder.switch_to_block(block);
|
||||||
builder.append_ebb_params_for_function_params(ebb);
|
builder.append_ebb_params_for_function_params(block);
|
||||||
|
|
||||||
// Add args to scope
|
// Add args to scope
|
||||||
for (¶m, (_, arg_name, var)) in builder.ebb_params(ebb).iter().zip(args) {
|
for (¶m, (_, arg_name, var)) in builder.ebb_params(block).iter().zip(args) {
|
||||||
let content = subs.get_without_compacting(*var).content;
|
let content = subs.get_without_compacting(*var).content;
|
||||||
// TODO this content_to_crane_type is duplicated when building this Proc
|
// TODO this type_from_content is duplicated when building this Proc
|
||||||
//
|
//
|
||||||
let layout = Layout::from_content(arena, content, subs)
|
let layout = Layout::from_content(arena, content, subs)
|
||||||
.unwrap_or_else(|()| panic!("TODO generate a runtime error here!"));
|
.unwrap_or_else(|()| panic!("TODO generate a runtime error here!"));
|
||||||
|
@ -416,7 +501,11 @@ pub fn define_proc_body<'a, B: Backend>(
|
||||||
let body = build_expr(env, &scope, module, &mut builder, &proc.body, procs);
|
let body = build_expr(env, &scope, module, &mut builder, &proc.body, procs);
|
||||||
|
|
||||||
builder.ins().return_(&[body]);
|
builder.ins().return_(&[body]);
|
||||||
|
// TODO re-enable this once Switch stops making unsealed
|
||||||
|
// EBBs, e.g. https://docs.rs/cranelift-frontend/0.52.0/src/cranelift_frontend/switch.rs.html#143
|
||||||
|
// builder.seal_block(block);
|
||||||
builder.seal_all_blocks();
|
builder.seal_all_blocks();
|
||||||
|
|
||||||
builder.finalize();
|
builder.finalize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,14 +4,16 @@ use cranelift_codegen::isa::TargetFrontendConfig;
|
||||||
|
|
||||||
use crate::mono::layout::Layout;
|
use crate::mono::layout::Layout;
|
||||||
use crate::subs::FlatType::*;
|
use crate::subs::FlatType::*;
|
||||||
use crate::subs::{Content, Subs};
|
use crate::subs::{Content, Subs, Variable};
|
||||||
use cranelift_module::{Backend, Module};
|
use cranelift_module::{Backend, Module};
|
||||||
|
|
||||||
pub fn content_to_crane_type(
|
pub fn type_from_var(var: Variable, subs: &Subs, cfg: TargetFrontendConfig) -> Type {
|
||||||
content: &Content,
|
let content = subs.get_without_compacting(var).content;
|
||||||
subs: &Subs,
|
|
||||||
cfg: TargetFrontendConfig,
|
type_from_content(&content, subs, cfg)
|
||||||
) -> Result<Type, String> {
|
}
|
||||||
|
|
||||||
|
pub fn type_from_content(content: &Content, subs: &Subs, cfg: TargetFrontendConfig) -> Type {
|
||||||
match content {
|
match content {
|
||||||
Content::Structure(flat_type) => match flat_type {
|
Content::Structure(flat_type) => match flat_type {
|
||||||
Apply {
|
Apply {
|
||||||
|
@ -29,19 +31,19 @@ pub fn content_to_crane_type(
|
||||||
num_to_crane_type(arg_content)
|
num_to_crane_type(arg_content)
|
||||||
} else {
|
} else {
|
||||||
panic!(
|
panic!(
|
||||||
"TODO handle content_to_crane_type for FlatType::Apply of {}.{} with args {:?}",
|
"TODO handle type_from_content for FlatType::Apply of {}.{} with args {:?}",
|
||||||
module_name, name, args
|
module_name, name, args
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Func(_, _) => Ok(cfg.pointer_type()),
|
Func(_, _) => cfg.pointer_type(),
|
||||||
other => panic!("TODO handle content_to_crane_type for {:?}", other),
|
other => panic!("TODO handle type_from_content for {:?}", other),
|
||||||
},
|
},
|
||||||
other => Err(format!("Cannot convert {:?} to BasicTypeEnum", other)),
|
other => panic!("Cannot convert {:?} to Crane Type", other),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn num_to_crane_type(content: Content) -> Result<Type, String> {
|
fn num_to_crane_type(content: Content) -> Type {
|
||||||
match content {
|
match content {
|
||||||
Content::Structure(flat_type) => match flat_type {
|
Content::Structure(flat_type) => match flat_type {
|
||||||
Apply {
|
Apply {
|
||||||
|
@ -57,18 +59,18 @@ fn num_to_crane_type(content: Content) -> Result<Type, String> {
|
||||||
&& args.is_empty()
|
&& args.is_empty()
|
||||||
{
|
{
|
||||||
debug_assert!(args.is_empty());
|
debug_assert!(args.is_empty());
|
||||||
Ok(types::F64)
|
types::F64
|
||||||
} else if module_name == crate::types::MOD_INT
|
} else if module_name == crate::types::MOD_INT
|
||||||
&& name == crate::types::TYPE_INTEGER
|
&& name == crate::types::TYPE_INTEGER
|
||||||
&& args.is_empty()
|
&& args.is_empty()
|
||||||
{
|
{
|
||||||
debug_assert!(args.is_empty());
|
debug_assert!(args.is_empty());
|
||||||
Ok(types::I64)
|
types::I64
|
||||||
} else {
|
} else {
|
||||||
Err(format!(
|
panic!(
|
||||||
"Unrecognized numeric type: {}.{} with args {:?}",
|
"Unrecognized numeric type: {}.{} with args {:?}",
|
||||||
module_name, name, args
|
module_name, name, args
|
||||||
))
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
other => panic!(
|
other => panic!(
|
||||||
|
|
231
src/mono/expr.rs
231
src/mono/expr.rs
|
@ -64,9 +64,23 @@ pub enum Expr<'a> {
|
||||||
Branches {
|
Branches {
|
||||||
/// The left-hand side of the conditional. We compile this to LLVM once,
|
/// 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.
|
/// then reuse it to test against each different compiled cond_rhs value.
|
||||||
cond_lhs: &'a Expr<'a>,
|
cond: &'a Expr<'a>,
|
||||||
/// ( cond_rhs, pass, fail )
|
/// ( cond_rhs, pass, fail )
|
||||||
branches: &'a [(Expr<'a>, Expr<'a>, Expr<'a>)],
|
branches: &'a [(Expr<'a>, Expr<'a>, Expr<'a>)],
|
||||||
|
default: &'a Expr<'a>,
|
||||||
|
ret_var: Variable,
|
||||||
|
},
|
||||||
|
/// Conditional branches for integers. These are more efficient.
|
||||||
|
Switch {
|
||||||
|
/// This *must* be an integer, because Switch potentially compiles to a jump table.
|
||||||
|
cond: &'a Expr<'a>,
|
||||||
|
cond_var: Variable,
|
||||||
|
/// The u64 in the tuple will be compared directly to the condition Expr.
|
||||||
|
/// If they are equal, this branch will be taken.
|
||||||
|
branches: &'a [(u64, Expr<'a>)],
|
||||||
|
/// If no other branches pass, this default branch will be taken.
|
||||||
|
default_branch: &'a Expr<'a>,
|
||||||
|
/// Each branch must return a value of this type.
|
||||||
ret_var: Variable,
|
ret_var: Variable,
|
||||||
},
|
},
|
||||||
Tag {
|
Tag {
|
||||||
|
@ -193,77 +207,172 @@ fn from_can<'a>(
|
||||||
loc_cond,
|
loc_cond,
|
||||||
branches,
|
branches,
|
||||||
} => {
|
} => {
|
||||||
debug_assert!(!branches.is_empty());
|
match branches.len() {
|
||||||
|
0 => {
|
||||||
|
// A when-expression with no branches is a runtime error.
|
||||||
|
// We can't know what to return!
|
||||||
|
panic!("TODO compile a 0-branch when-expression to a RuntimeError");
|
||||||
|
}
|
||||||
|
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();
|
||||||
|
|
||||||
if branches.len() == 2 {
|
store_pattern(
|
||||||
let arena = env.arena;
|
env,
|
||||||
let mut iter = branches.into_iter();
|
loc_pattern.value,
|
||||||
let (loc_pat1, loc_then) = iter.next().unwrap();
|
loc_cond.value,
|
||||||
let (loc_pat2, loc_else) = iter.next().unwrap();
|
cond_var,
|
||||||
|
procs,
|
||||||
|
&mut stored,
|
||||||
|
);
|
||||||
|
|
||||||
match (&loc_pat1.value, &loc_pat2.value) {
|
let ret = from_can(env, loc_branch.value, procs, None);
|
||||||
(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 {
|
Expr::Store(stored.into_bump_slice(), arena.alloc(ret))
|
||||||
cond_lhs,
|
}
|
||||||
cond_rhs,
|
2 => {
|
||||||
pass,
|
// A when-expression with exactly 2 branches compiles to a Cond.
|
||||||
fail,
|
let arena = env.arena;
|
||||||
ret_var: expr_var,
|
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), FloatLiteral(_)) | (FloatLiteral(float), Underscore) => {
|
| (FloatLiteral(float), Underscore) => {
|
||||||
let cond_lhs = arena.alloc(from_can(env, loc_cond.value, procs, None));
|
let cond_lhs = arena.alloc(from_can(env, loc_cond.value, procs, None));
|
||||||
let cond_rhs = arena.alloc(Expr::Float(*float));
|
let cond_rhs = arena.alloc(Expr::Float(*float));
|
||||||
let pass = arena.alloc(from_can(env, loc_then.value, procs, None));
|
let pass = arena.alloc(from_can(env, loc_then.value, procs, None));
|
||||||
let fail = arena.alloc(from_can(env, loc_else.value, procs, None));
|
let fail = arena.alloc(from_can(env, loc_else.value, procs, None));
|
||||||
|
|
||||||
Expr::Cond {
|
Expr::Cond {
|
||||||
cond_lhs,
|
cond_lhs,
|
||||||
cond_rhs,
|
cond_rhs,
|
||||||
pass,
|
pass,
|
||||||
fail,
|
fail,
|
||||||
ret_var: expr_var,
|
ret_var: expr_var,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
panic!("TODO handle more conds");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
panic!("TODO handle more conds");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if branches.len() == 1 {
|
_ => {
|
||||||
// A when-expression with exactly 1 branch is essentially a LetNonRec.
|
// This is a when-expression with 3+ branches.
|
||||||
// As such, we can compile it direcly to a Store.
|
let arena = env.arena;
|
||||||
let arena = env.arena;
|
let cond = from_can(env, loc_cond.value, procs, None);
|
||||||
let mut stored = Vec::with_capacity_in(1, arena);
|
let content = env.subs.get_without_compacting(cond_var).content;
|
||||||
let (loc_pattern, loc_branch) = branches.into_iter().next().unwrap();
|
|
||||||
|
|
||||||
store_pattern(
|
// If the condition is an Int or Float, we can potentially use
|
||||||
env,
|
// a Switch for more efficiency.
|
||||||
loc_pattern.value,
|
if content.is_number() {
|
||||||
loc_cond.value,
|
// These are integer literals or underscore patterns,
|
||||||
cond_var,
|
// so they're eligible for user in a jump table.
|
||||||
procs,
|
let mut jumpable_branches = Vec::with_capacity_in(branches.len(), arena);
|
||||||
&mut stored,
|
let mut opt_default_branch = None;
|
||||||
);
|
|
||||||
|
|
||||||
let ret = from_can(env, loc_branch.value, procs, None);
|
for (loc_pat, loc_expr) in branches {
|
||||||
|
let mono_expr = from_can(env, loc_expr.value, procs, None);
|
||||||
|
|
||||||
Expr::Store(stored.into_bump_slice(), arena.alloc(ret))
|
match &loc_pat.value {
|
||||||
} else {
|
IntLiteral(int) => {
|
||||||
// /// More than two conditional branches, e.g. a 3-way when-expression
|
// Switch only compares the condition to the
|
||||||
// Expr::Branches {
|
// alternatives based on their bit patterns,
|
||||||
// /// The left-hand side of the conditional. We compile this to LLVM once,
|
// so casting from i64 to u64 makes no difference here.
|
||||||
// /// then reuse it to test against each different compiled cond_rhs value.
|
jumpable_branches.push((*int as u64, mono_expr));
|
||||||
// cond_lhs: &'a Expr<'a>,
|
}
|
||||||
// /// ( cond_rhs, pass, fail )
|
FloatLiteral(float) => {
|
||||||
// branches: &'a [(Expr<'a>, Expr<'a>, Expr<'a>)],
|
// Switch only compares the condition to the
|
||||||
// ret_var: Variable,
|
// alternatives based on their bit patterns,
|
||||||
// },
|
// so casting from f64 to u64 makes no difference here.
|
||||||
panic!("TODO support when-expressions of more than 2 branches.");
|
jumpable_branches.push((*float as u64, mono_expr));
|
||||||
|
}
|
||||||
|
Identifier(_symbol) => {
|
||||||
|
// Since this is an ident, it must be
|
||||||
|
// the last pattern in the `when`.
|
||||||
|
// We can safely treat this like an `_`
|
||||||
|
// except that we need to wrap this branch
|
||||||
|
// in a `Store` so the identifier is in scope!
|
||||||
|
|
||||||
|
opt_default_branch = Some(arena.alloc(if true {
|
||||||
|
// Using `if true` for this TODO panic to avoid a warning
|
||||||
|
panic!(
|
||||||
|
"TODO wrap this expr in an Expr::Store: {:?}",
|
||||||
|
mono_expr
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
mono_expr
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
Underscore => {
|
||||||
|
// We should always have exactly one default branch!
|
||||||
|
debug_assert!(opt_default_branch.is_none());
|
||||||
|
|
||||||
|
opt_default_branch = Some(arena.alloc(mono_expr));
|
||||||
|
}
|
||||||
|
Shadowed(_loc_ident) => {
|
||||||
|
panic!("TODO runtime error for shadowing in a pattern");
|
||||||
|
}
|
||||||
|
// Example: (5 = 1 + 2) is an unsupported pattern in an assignment; Int patterns aren't allowed in assignments!
|
||||||
|
UnsupportedPattern(_region) => {
|
||||||
|
panic!("TODO runtime error for unsupported pattern");
|
||||||
|
}
|
||||||
|
AppliedTag(_, _, _) | StrLiteral(_) | RecordDestructure(_, _) => {
|
||||||
|
// The type checker should have converted these mismatches into RuntimeErrors already!
|
||||||
|
if cfg!(debug_assetions) {
|
||||||
|
panic!("A type mismatch in a pattern was not converted to a runtime error: {:?}", loc_pat);
|
||||||
|
} else {
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the default branch was never set, that means
|
||||||
|
// our canonical Expr didn't have one. An earlier
|
||||||
|
// step in the compilation process should have
|
||||||
|
// ruled this out!
|
||||||
|
debug_assert!(opt_default_branch.is_some());
|
||||||
|
let default_branch = opt_default_branch.unwrap();
|
||||||
|
|
||||||
|
Expr::Switch {
|
||||||
|
cond: arena.alloc(cond),
|
||||||
|
branches: jumpable_branches.into_bump_slice(),
|
||||||
|
default_branch,
|
||||||
|
ret_var: expr_var,
|
||||||
|
cond_var,
|
||||||
|
}
|
||||||
|
} 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 3+ branches whose conditions aren't numbers.");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -769,7 +769,7 @@ fn loc_pattern<'a>(min_indent: u16) -> impl Parser<'a, Located<Pattern<'a>>> {
|
||||||
loc!(ident_pattern()),
|
loc!(ident_pattern()),
|
||||||
loc!(record_destructure(min_indent)),
|
loc!(record_destructure(min_indent)),
|
||||||
loc!(string_pattern()),
|
loc!(string_pattern()),
|
||||||
loc!(int_pattern())
|
loc!(number_pattern())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -781,7 +781,7 @@ fn loc_parenthetical_pattern<'a>(min_indent: u16) -> impl Parser<'a, Located<Pat
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn int_pattern<'a>() -> impl Parser<'a, Pattern<'a>> {
|
fn number_pattern<'a>() -> impl Parser<'a, Pattern<'a>> {
|
||||||
map_with_arena!(number_literal(), |arena, expr| {
|
map_with_arena!(number_literal(), |arena, expr| {
|
||||||
expr_to_pattern(arena, &expr).unwrap()
|
expr_to_pattern(arena, &expr).unwrap()
|
||||||
})
|
})
|
||||||
|
@ -1016,49 +1016,61 @@ fn unary_negate_function_arg<'a>(min_indent: u16) -> impl Parser<'a, Located<Exp
|
||||||
then(
|
then(
|
||||||
// Spaces, then '-', then *not* more spaces.
|
// Spaces, then '-', then *not* more spaces.
|
||||||
not_followed_by(
|
not_followed_by(
|
||||||
and!(space1(min_indent), loc!(char('-'))),
|
and!(
|
||||||
|
space1(min_indent),
|
||||||
|
either!(
|
||||||
|
// Try to parse a number literal *before* trying to parse unary negate,
|
||||||
|
// because otherwise (foo -1) will parse as (foo (Num.neg 1))
|
||||||
|
loc!(number_literal()),
|
||||||
|
loc!(char('-'))
|
||||||
|
)
|
||||||
|
),
|
||||||
one_of!(char(' '), char('#'), char('\n')),
|
one_of!(char(' '), char('#'), char('\n')),
|
||||||
),
|
),
|
||||||
move |arena, state, (spaces, loc_minus_char)| {
|
move |arena, state, (spaces, num_or_minus_char)| {
|
||||||
let region = loc_minus_char.region;
|
match num_or_minus_char {
|
||||||
let loc_op = Located {
|
Either::First(loc_num_literal) => Ok((loc_num_literal, state)),
|
||||||
region,
|
Either::Second(Located { region, .. }) => {
|
||||||
value: UnaryOp::Negate,
|
let loc_op = Located {
|
||||||
};
|
region,
|
||||||
|
value: UnaryOp::Negate,
|
||||||
|
};
|
||||||
|
|
||||||
// Continue parsing the function arg as normal.
|
// Continue parsing the function arg as normal.
|
||||||
let (loc_expr, state) = loc_function_arg(min_indent).parse(arena, state)?;
|
let (loc_expr, state) = loc_function_arg(min_indent).parse(arena, state)?;
|
||||||
let region = Region {
|
let region = Region {
|
||||||
start_col: loc_op.region.start_col,
|
start_col: loc_op.region.start_col,
|
||||||
start_line: loc_op.region.start_line,
|
start_line: loc_op.region.start_line,
|
||||||
end_col: loc_expr.region.end_col,
|
end_col: loc_expr.region.end_col,
|
||||||
end_line: loc_expr.region.end_line,
|
end_line: loc_expr.region.end_line,
|
||||||
};
|
};
|
||||||
let value = Expr::UnaryOp(arena.alloc(loc_expr), loc_op);
|
let value = Expr::UnaryOp(arena.alloc(loc_expr), loc_op);
|
||||||
let loc_expr = Located {
|
let loc_expr = Located {
|
||||||
// Start from where the unary op started,
|
// Start from where the unary op started,
|
||||||
// and end where its argument expr ended.
|
// and end where its argument expr ended.
|
||||||
// This is relevant in case (for example)
|
// This is relevant in case (for example)
|
||||||
// we have an expression involving parens,
|
// we have an expression involving parens,
|
||||||
// for example `-(foo bar)`
|
// for example `-(foo bar)`
|
||||||
region,
|
region,
|
||||||
value,
|
value,
|
||||||
};
|
};
|
||||||
|
|
||||||
// spaces can be empy if it's all space characters (no newlines or comments).
|
// spaces can be empy if it's all space characters (no newlines or comments).
|
||||||
let value = if spaces.is_empty() {
|
let value = if spaces.is_empty() {
|
||||||
loc_expr.value
|
loc_expr.value
|
||||||
} else {
|
} else {
|
||||||
Expr::SpaceBefore(arena.alloc(loc_expr.value), spaces)
|
Expr::SpaceBefore(arena.alloc(loc_expr.value), spaces)
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
Located {
|
Located {
|
||||||
region: loc_expr.region,
|
region: loc_expr.region,
|
||||||
value,
|
value,
|
||||||
},
|
},
|
||||||
state,
|
state,
|
||||||
))
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
13
src/subs.rs
13
src/subs.rs
|
@ -2,6 +2,7 @@ use crate::can::ident::{Lowercase, ModuleName, Uppercase};
|
||||||
use crate::can::symbol::Symbol;
|
use crate::can::symbol::Symbol;
|
||||||
use crate::collections::{ImMap, ImSet, MutSet, SendMap};
|
use crate::collections::{ImMap, ImSet, MutSet, SendMap};
|
||||||
use crate::ena::unify::{InPlace, UnificationTable, UnifyKey};
|
use crate::ena::unify::{InPlace, UnificationTable, UnifyKey};
|
||||||
|
use crate::types;
|
||||||
use crate::types::{name_type_var, ErrorType, Problem, RecordFieldLabel, TypeExt};
|
use crate::types::{name_type_var, ErrorType, Problem, RecordFieldLabel, TypeExt};
|
||||||
use crate::uniqueness::boolean_algebra;
|
use crate::uniqueness::boolean_algebra;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
@ -427,6 +428,18 @@ pub enum Content {
|
||||||
Error,
|
Error,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Content {
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn is_number(&self) -> bool {
|
||||||
|
match &self {
|
||||||
|
Content::Structure(FlatType::Apply {
|
||||||
|
module_name, name, ..
|
||||||
|
}) => module_name.as_str() == types::MOD_NUM && name.as_str() == types::TYPE_NUM,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum FlatType {
|
pub enum FlatType {
|
||||||
Apply {
|
Apply {
|
||||||
|
|
|
@ -15,12 +15,13 @@ mod test_crane {
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use cranelift::prelude::*;
|
use cranelift::prelude::*;
|
||||||
use cranelift_codegen::isa;
|
use cranelift_codegen::isa;
|
||||||
use cranelift_codegen::settings::{self};
|
use cranelift_codegen::settings;
|
||||||
|
use cranelift_codegen::verifier::verify_function;
|
||||||
use cranelift_module::{default_libcall_names, Linkage, Module};
|
use cranelift_module::{default_libcall_names, Linkage, Module};
|
||||||
use cranelift_simplejit::{SimpleJITBackend, SimpleJITBuilder};
|
use cranelift_simplejit::{SimpleJITBackend, SimpleJITBuilder};
|
||||||
use roc::collections::{ImMap, MutMap};
|
use roc::collections::{ImMap, MutMap};
|
||||||
use roc::crane::build::{build_expr, declare_proc, define_proc_body, Env, ScopeEntry};
|
use roc::crane::build::{build_expr, declare_proc, define_proc_body, Env, ScopeEntry};
|
||||||
use roc::crane::convert::content_to_crane_type;
|
use roc::crane::convert::type_from_content;
|
||||||
use roc::infer::infer_expr;
|
use roc::infer::infer_expr;
|
||||||
use roc::mono::expr::Expr;
|
use roc::mono::expr::Expr;
|
||||||
use roc::subs::Subs;
|
use roc::subs::Subs;
|
||||||
|
@ -49,7 +50,7 @@ mod test_crane {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Ok(isa_builder) => {
|
Ok(isa_builder) => {
|
||||||
let isa = isa_builder.finish(shared_flags);
|
let isa = isa_builder.finish(shared_flags.clone());
|
||||||
|
|
||||||
isa.frontend_config()
|
isa.frontend_config()
|
||||||
}
|
}
|
||||||
|
@ -58,8 +59,7 @@ mod test_crane {
|
||||||
let main_fn_name = "$Test.main";
|
let main_fn_name = "$Test.main";
|
||||||
|
|
||||||
// Compute main_fn_ret_type before moving subs to Env
|
// Compute main_fn_ret_type before moving subs to Env
|
||||||
let main_ret_type = content_to_crane_type(&content, &mut subs, cfg)
|
let main_ret_type = type_from_content(&content, &mut subs, cfg);
|
||||||
.expect("Unable to infer type for test expr");
|
|
||||||
|
|
||||||
// Compile and add all the Procs before adding main
|
// Compile and add all the Procs before adding main
|
||||||
let mut procs = MutMap::default();
|
let mut procs = MutMap::default();
|
||||||
|
@ -98,6 +98,14 @@ mod test_crane {
|
||||||
proc,
|
proc,
|
||||||
&procs,
|
&procs,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Verify the function we just defined
|
||||||
|
if let Err(errors) = verify_function(&ctx.func, &shared_flags) {
|
||||||
|
// NOTE: We don't include proc here because it's already
|
||||||
|
// been moved. If you need to know which proc failed, go back
|
||||||
|
// and add some logging.
|
||||||
|
panic!("Errors defining proc: {}", errors);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add main itself
|
// Add main itself
|
||||||
|
@ -114,16 +122,19 @@ mod test_crane {
|
||||||
{
|
{
|
||||||
let mut builder: FunctionBuilder =
|
let mut builder: FunctionBuilder =
|
||||||
FunctionBuilder::new(&mut ctx.func, &mut func_ctx);
|
FunctionBuilder::new(&mut ctx.func, &mut func_ctx);
|
||||||
let ebb = builder.create_ebb();
|
let block = builder.create_ebb();
|
||||||
|
|
||||||
builder.switch_to_block(ebb);
|
builder.switch_to_block(block);
|
||||||
// TODO try deleting this line and seeing if everything still works.
|
// TODO try deleting this line and seeing if everything still works.
|
||||||
builder.append_ebb_params_for_function_params(ebb);
|
builder.append_ebb_params_for_function_params(block);
|
||||||
|
|
||||||
let main_body =
|
let main_body =
|
||||||
build_expr(&env, &scope, &mut module, &mut builder, &mono_expr, &procs);
|
build_expr(&env, &scope, &mut module, &mut builder, &mono_expr, &procs);
|
||||||
|
|
||||||
builder.ins().return_(&[main_body]);
|
builder.ins().return_(&[main_body]);
|
||||||
|
// TODO re-enable this once Switch stops making unsealed
|
||||||
|
// EBBs, e.g. https://docs.rs/cranelift-frontend/0.52.0/src/cranelift_frontend/switch.rs.html#143
|
||||||
|
// builder.seal_block(block);
|
||||||
builder.seal_all_blocks();
|
builder.seal_all_blocks();
|
||||||
builder.finalize();
|
builder.finalize();
|
||||||
}
|
}
|
||||||
|
@ -134,6 +145,11 @@ mod test_crane {
|
||||||
// Perform linking
|
// Perform linking
|
||||||
module.finalize_definitions();
|
module.finalize_definitions();
|
||||||
|
|
||||||
|
// Verify the main function
|
||||||
|
if let Err(errors) = verify_function(&ctx.func, &shared_flags) {
|
||||||
|
panic!("Errors defining {} - {}", main_fn_name, errors);
|
||||||
|
}
|
||||||
|
|
||||||
let main_ptr = module.get_finalized_function(main_fn);
|
let main_ptr = module.get_finalized_function(main_fn);
|
||||||
let run_main = unsafe { mem::transmute::<_, fn() -> $ty>(main_ptr) };
|
let run_main = unsafe { mem::transmute::<_, fn() -> $ty>(main_ptr) };
|
||||||
|
|
||||||
|
@ -151,48 +167,93 @@ mod test_crane {
|
||||||
assert_evals_to!("1234.0", 1234.0, f64);
|
assert_evals_to!("1234.0", 1234.0, f64);
|
||||||
}
|
}
|
||||||
|
|
||||||
// #[test]
|
// #[test]
|
||||||
// fn gen_when_take_first_branch() {
|
// fn gen_when_take_first_branch() {
|
||||||
// assert_evals_to!(
|
// assert_evals_to!(
|
||||||
// indoc!(
|
// indoc!(
|
||||||
// r#"
|
// r#"
|
||||||
// when 1 is
|
// when 1 is
|
||||||
// 1 -> 12
|
// 1 -> 12
|
||||||
// _ -> 34
|
// _ -> 34
|
||||||
// "#
|
// "#
|
||||||
// ),
|
// ),
|
||||||
// 12,
|
// 12,
|
||||||
// i64
|
// i64
|
||||||
// );
|
// );
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// #[test]
|
// #[test]
|
||||||
// fn gen_when_take_second_branch() {
|
// fn gen_when_take_second_branch() {
|
||||||
// assert_evals_to!(
|
// assert_evals_to!(
|
||||||
// indoc!(
|
// indoc!(
|
||||||
// r#"
|
// r#"
|
||||||
// when 2 is
|
// when 2 is
|
||||||
// 1 -> 63
|
// 1 -> 63
|
||||||
// _ -> 48
|
// _ -> 48
|
||||||
// "#
|
// "#
|
||||||
// ),
|
// ),
|
||||||
// 48,
|
// 48,
|
||||||
// i64
|
// i64
|
||||||
// );
|
// );
|
||||||
// }
|
// }
|
||||||
// #[test]
|
|
||||||
// fn gen_when_one_branch() {
|
#[test]
|
||||||
// assert_evals_to!(
|
fn gen_when_one_branch() {
|
||||||
// indoc!(
|
assert_evals_to!(
|
||||||
// r#"
|
indoc!(
|
||||||
// when 3.14 is
|
r#"
|
||||||
// _ -> 23
|
when 3.14 is
|
||||||
// "#
|
_ -> 23
|
||||||
// ),
|
"#
|
||||||
// 23,
|
),
|
||||||
// i64
|
23,
|
||||||
// );
|
i64
|
||||||
// }
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn gen_large_when_int() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
foo = \num ->
|
||||||
|
when num is
|
||||||
|
0 -> 200
|
||||||
|
-3 -> 111
|
||||||
|
3 -> 789
|
||||||
|
1 -> 123
|
||||||
|
2 -> 456
|
||||||
|
_ -> 1000
|
||||||
|
|
||||||
|
foo -3
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
111,
|
||||||
|
i64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn gen_large_when_float() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
foo = \num ->
|
||||||
|
when num is
|
||||||
|
0.5 -> 200.1
|
||||||
|
-3.6 -> 111.2
|
||||||
|
3.6 -> 789.5
|
||||||
|
1.7 -> 123.3
|
||||||
|
2.8 -> 456.4
|
||||||
|
_ -> 1000.6
|
||||||
|
|
||||||
|
foo -3.6
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
111.2,
|
||||||
|
f64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn gen_basic_def() {
|
fn gen_basic_def() {
|
||||||
|
@ -328,23 +389,24 @@ mod test_crane {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// #[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
|
||||||
// _ -> num
|
-1 -> 1
|
||||||
|
_ -> num
|
||||||
|
|
||||||
// limitedNegate 1
|
limitedNegate 1
|
||||||
// "#
|
"#
|
||||||
// ),
|
),
|
||||||
// -1,
|
-1,
|
||||||
// i64
|
i64
|
||||||
// );
|
);
|
||||||
// }
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn apply_unnamed_fn() {
|
fn apply_unnamed_fn() {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue