Merge branch 'trunk' of github.com:rtfeldman/roc into dict

This commit is contained in:
Chadtech 2021-01-16 14:55:09 -05:00
commit c7f6de2afe
93 changed files with 6706 additions and 5317 deletions

View file

@ -156,6 +156,10 @@ impl<'a> ParamMap<'a> {
Let(_, _, _, cont) => {
stack.push(cont);
}
Invoke { pass, fail, .. } => {
stack.push(pass);
stack.push(fail);
}
Switch {
branches,
default_branch,
@ -166,7 +170,7 @@ impl<'a> ParamMap<'a> {
}
Inc(_, _) | Dec(_, _) => unreachable!("these have not been introduced yet"),
Ret(_) | Jump(_, _) | RuntimeError(_) => {
Ret(_) | Rethrow | Jump(_, _) | RuntimeError(_) => {
// these are terminal, do nothing
}
}
@ -295,6 +299,62 @@ impl<'a> BorrowInfState<'a> {
///
/// and determines whether z and which of the symbols used in e
/// must be taken as owned paramters
fn collect_call(&mut self, z: Symbol, e: &crate::ir::Call<'a>) {
use crate::ir::CallType::*;
let crate::ir::Call {
call_type,
arguments,
} = e;
match call_type {
ByName {
name, arg_layouts, ..
}
| ByPointer {
name, arg_layouts, ..
} => {
// get the borrow signature of the applied function
let ps = match self.param_map.get_symbol(*name) {
Some(slice) => slice,
None => Vec::from_iter_in(
arg_layouts.iter().cloned().map(|layout| Param {
symbol: Symbol::UNDERSCORE,
borrow: false,
layout,
}),
self.arena,
)
.into_bump_slice(),
};
// the return value will be owned
self.own_var(z);
// if the function exects an owned argument (ps), the argument must be owned (args)
self.own_args_using_params(arguments, ps);
}
LowLevel { op } => {
// very unsure what demand RunLowLevel should place upon its arguments
self.own_var(z);
let ps = lowlevel_borrow_signature(self.arena, *op);
self.own_args_using_bools(arguments, ps);
}
Foreign { .. } => {
// very unsure what demand ForeignCall should place upon its arguments
self.own_var(z);
let ps = foreign_borrow_signature(self.arena, arguments.len());
self.own_args_using_bools(arguments, ps);
}
}
}
fn collect_expr(&mut self, z: Symbol, e: &Expr<'a>) {
use Expr::*;
@ -334,73 +394,40 @@ impl<'a> BorrowInfState<'a> {
}
}
FunctionCall {
call_type,
args,
arg_layouts,
..
} => {
// get the borrow signature of the applied function
let ps = match self.param_map.get_symbol(call_type.get_inner()) {
Some(slice) => slice,
None => Vec::from_iter_in(
arg_layouts.iter().cloned().map(|layout| Param {
symbol: Symbol::UNDERSCORE,
borrow: false,
layout,
}),
self.arena,
)
.into_bump_slice(),
};
// the return value will be owned
self.own_var(z);
// if the function exects an owned argument (ps), the argument must be owned (args)
self.own_args_using_params(args, ps);
}
RunLowLevel(op, args) => {
// very unsure what demand RunLowLevel should place upon its arguments
self.own_var(z);
let ps = lowlevel_borrow_signature(self.arena, *op);
self.own_args_using_bools(args, ps);
}
ForeignCall { arguments, .. } => {
// very unsure what demand ForeignCall should place upon its arguments
self.own_var(z);
let ps = foreign_borrow_signature(self.arena, arguments.len());
self.own_args_using_bools(arguments, ps);
}
Call(call) => self.collect_call(z, call),
Literal(_) | FunctionPointer(_, _) | RuntimeErrorFunction(_) => {}
}
}
#[allow(clippy::many_single_char_names)]
fn preserve_tail_call(&mut self, x: Symbol, v: &Expr<'a>, b: &Stmt<'a>) {
if let (
Expr::FunctionCall {
call_type,
args: ys,
..
},
Stmt::Ret(z),
) = (v, b)
{
let g = call_type.get_inner();
if self.current_proc == g && x == *z {
// anonymous functions (for which the ps may not be known)
// can never be tail-recursive, so this is fine
if let Some(ps) = self.param_map.get_symbol(g) {
self.own_params_using_args(ys, ps)
match (v, b) {
(
Expr::Call(crate::ir::Call {
call_type: crate::ir::CallType::ByName { name: g, .. },
arguments: ys,
..
}),
Stmt::Ret(z),
)
| (
Expr::Call(crate::ir::Call {
call_type: crate::ir::CallType::ByPointer { name: g, .. },
arguments: ys,
..
}),
Stmt::Ret(z),
) => {
if self.current_proc == *g && x == *z {
// anonymous functions (for which the ps may not be known)
// can never be tail-recursive, so this is fine
if let Some(ps) = self.param_map.get_symbol(*g) {
self.own_params_using_args(ys, ps)
}
}
}
_ => {}
}
}
@ -444,11 +471,29 @@ impl<'a> BorrowInfState<'a> {
self.collect_stmt(b);
self.preserve_tail_call(*x, &Expr::FunctionPointer(*fsymbol, layout.clone()), b);
}
Let(x, v, _, b) => {
self.collect_stmt(b);
self.collect_expr(*x, v);
self.preserve_tail_call(*x, v, b);
}
Invoke {
symbol,
call,
layout: _,
pass,
fail,
} => {
self.collect_stmt(pass);
self.collect_stmt(fail);
self.collect_call(*symbol, call);
// TODO how to preserve the tail call of an invoke?
// self.preserve_tail_call(*x, v, b);
}
Jump(j, ys) => {
let ps = self.param_map.get_join_point(*j);
@ -470,7 +515,7 @@ impl<'a> BorrowInfState<'a> {
}
Inc(_, _) | Dec(_, _) => unreachable!("these have not been introduced yet"),
Ret(_) | RuntimeError(_) => {
Ret(_) | RuntimeError(_) | Rethrow => {
// these are terminal, do nothing
}
}
@ -533,9 +578,8 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
Eq | NotEq | And | Or | NumAdd | NumAddWrap | NumAddChecked | NumSub | NumSubWrap
| NumSubChecked | NumMul | NumMulWrap | NumMulChecked | NumGt | NumGte | NumLt | NumLte
| NumCompare | NumDivUnchecked | NumRemUnchecked | NumPow | NumPowInt | NumBitwiseAnd => {
arena.alloc_slice_copy(&[irrelevant, irrelevant])
}
| NumCompare | NumDivUnchecked | NumRemUnchecked | NumPow | NumPowInt | NumBitwiseAnd
| NumBitwiseXor => arena.alloc_slice_copy(&[irrelevant, irrelevant]),
NumAbs | NumNeg | NumSin | NumCos | NumSqrtUnchecked | NumRound | NumCeiling | NumFloor
| NumToFloat | Not | NumIsFinite | NumAtan | NumAcos | NumAsin => {

View file

@ -66,7 +66,7 @@ pub enum Test<'a> {
union: crate::exhaustive::Union,
arguments: Vec<(Pattern<'a>, Layout<'a>)>,
},
IsInt(i64),
IsInt(i128),
// float patterns are stored as u64 so they are comparable/hashable
IsFloat(u64),
IsStr(Box<str>),
@ -902,7 +902,10 @@ pub fn optimize_when<'a>(
let decision_tree = compile(patterns);
let decider = tree_to_decider(decision_tree);
let target_counts = count_targets(&decider);
// for each target (branch body), count in how many ways it can be reached
let mut target_counts = bumpalo::vec![in env.arena; 0; indexed_branches.len()];
count_targets(&mut target_counts, &decider);
let mut choices = MutMap::default();
let mut jumps = Vec::new();
@ -910,8 +913,9 @@ pub fn optimize_when<'a>(
for (index, branch) in indexed_branches.into_iter() {
let ((branch_index, choice), opt_jump) = create_choices(&target_counts, index, branch);
if let Some(jump) = opt_jump {
jumps.push(jump);
if let Some((index, body)) = opt_jump {
let id = JoinPointId(env.unique_symbol());
jumps.push((index, id, body));
}
choices.insert(branch_index, choice);
@ -919,7 +923,7 @@ pub fn optimize_when<'a>(
let choice_decider = insert_choices(&choices, decider);
decide_to_branching(
let mut stmt = decide_to_branching(
env,
procs,
layout_cache,
@ -928,7 +932,18 @@ pub fn optimize_when<'a>(
ret_layout,
choice_decider,
&jumps,
)
);
for (_, id, body) in jumps.into_iter() {
stmt = Stmt::Join {
id,
parameters: &[],
continuation: env.arena.alloc(body),
remainder: env.arena.alloc(stmt),
};
}
stmt
}
#[derive(Debug)]
@ -1092,7 +1107,9 @@ fn test_to_equality<'a>(
)
}
Test::IsInt(test_int) => {
let lhs = Expr::Literal(Literal::Int(test_int));
// TODO don't downcast i128 here
debug_assert!(test_int <= i64::MAX as i128);
let lhs = Expr::Literal(Literal::Int(test_int as i64));
let lhs_symbol = env.unique_symbol();
stores.push((lhs_symbol, Layout::Builtin(Builtin::Int64), lhs));
@ -1277,7 +1294,10 @@ fn compile_test<'a>(
ret_layout,
);
let test = Expr::RunLowLevel(LowLevel::Eq, arena.alloc([lhs, rhs]));
let test = Expr::Call(crate::ir::Call {
call_type: crate::ir::CallType::LowLevel { op: LowLevel::Eq },
arguments: arena.alloc([lhs, rhs]),
});
// write to the test symbol
cond = Stmt::Let(
@ -1328,7 +1348,7 @@ fn decide_to_branching<'a>(
cond_layout: Layout<'a>,
ret_layout: Layout<'a>,
decider: Decider<'a, Choice<'a>>,
jumps: &Vec<(u64, Stmt<'a>)>,
jumps: &Vec<(u64, JoinPointId, Stmt<'a>)>,
) -> Stmt<'a> {
use Choice::*;
use Decider::*;
@ -1337,12 +1357,11 @@ fn decide_to_branching<'a>(
match decider {
Leaf(Jump(label)) => {
// we currently inline the jumps: does fewer jumps but produces a larger artifact
let (_, expr) = jumps
.iter()
.find(|(l, _)| l == &label)
let index = jumps
.binary_search_by_key(&label, |ref r| r.0)
.expect("jump not in list of jumps");
expr.clone()
Stmt::Jump(jumps[index].1, &[])
}
Leaf(Inline(expr)) => expr,
Chain {
@ -1619,39 +1638,32 @@ fn to_chain<'a>(
/// If a target appears exactly once in a Decider, the corresponding expression
/// can be inlined. Whether things are inlined or jumps is called a "choice".
fn count_targets(decision_tree: &Decider<u64>) -> MutMap<u64, u64> {
let mut result = MutMap::default();
count_targets_help(decision_tree, &mut result);
result
}
fn count_targets_help(decision_tree: &Decider<u64>, targets: &mut MutMap<u64, u64>) {
fn count_targets(targets: &mut bumpalo::collections::Vec<u64>, initial: &Decider<u64>) {
use Decider::*;
match decision_tree {
Leaf(target) => match targets.get_mut(target) {
None => {
targets.insert(*target, 1);
let mut stack = vec![initial];
while let Some(decision_tree) = stack.pop() {
match decision_tree {
Leaf(target) => {
targets[*target as usize] += 1;
}
Some(current) => {
*current += 1;
Chain {
success, failure, ..
} => {
stack.push(success);
stack.push(failure);
}
},
Chain {
success, failure, ..
} => {
count_targets_help(success, targets);
count_targets_help(failure, targets);
}
FanOut {
tests, fallback, ..
} => {
stack.push(fallback);
FanOut {
tests, fallback, ..
} => {
count_targets_help(fallback, targets);
for (_, decider) in tests {
count_targets_help(decider, targets);
for (_, decider) in tests {
stack.push(decider);
}
}
}
}
@ -1659,11 +1671,11 @@ fn count_targets_help(decision_tree: &Decider<u64>, targets: &mut MutMap<u64, u6
#[allow(clippy::type_complexity)]
fn create_choices<'a>(
target_counts: &MutMap<u64, u64>,
target_counts: &bumpalo::collections::Vec<'a, u64>,
target: u64,
branch: Stmt<'a>,
) -> ((u64, Choice<'a>), Option<(u64, Stmt<'a>)>) {
match target_counts.get(&target) {
match target_counts.get(target as usize) {
None => unreachable!(
"this should never happen: {:?} not in {:?}",
target, target_counts

View file

@ -37,7 +37,7 @@ pub enum Pattern {
#[derive(Clone, Debug, PartialEq)]
pub enum Literal {
Int(i64),
Int(i128),
Bit(bool),
Byte(u8),
Float(u64),

View file

@ -31,10 +31,27 @@ pub fn occuring_variables(stmt: &Stmt<'_>) -> (MutSet<Symbol>, MutSet<Symbol>) {
bound_variables.insert(*symbol);
stack.push(cont);
}
Invoke {
symbol,
call,
pass,
fail,
..
} => {
occuring_variables_call(call, &mut result);
result.insert(*symbol);
bound_variables.insert(*symbol);
stack.push(pass);
stack.push(fail);
}
Ret(symbol) => {
result.insert(*symbol);
}
Rethrow => {}
Inc(symbol, cont) | Dec(symbol, cont) => {
result.insert(*symbol);
stack.push(cont);
@ -75,6 +92,12 @@ pub fn occuring_variables(stmt: &Stmt<'_>) -> (MutSet<Symbol>, MutSet<Symbol>) {
(result, bound_variables)
}
fn occuring_variables_call(call: &crate::ir::Call<'_>, result: &mut MutSet<Symbol>) {
// NOTE though the function name does occur, it is a static constant in the program
// for liveness, it should not be included here.
result.extend(call.arguments.iter().copied());
}
pub fn occuring_variables_expr(expr: &Expr<'_>, result: &mut MutSet<Symbol>) {
use Expr::*;
@ -86,11 +109,7 @@ pub fn occuring_variables_expr(expr: &Expr<'_>, result: &mut MutSet<Symbol>) {
result.insert(*symbol);
}
FunctionCall { args, .. } => {
// NOTE thouth the function name does occur, it is a static constant in the program
// for liveness, it should not be included here.
result.extend(args.iter().copied());
}
Call(call) => occuring_variables_call(call, result),
Tag { arguments, .. }
| Struct(arguments)
@ -108,12 +127,6 @@ pub fn occuring_variables_expr(expr: &Expr<'_>, result: &mut MutSet<Symbol>) {
Reset(x) => {
result.insert(*x);
}
RunLowLevel(_, args) => {
result.extend(args.iter());
}
ForeignCall { arguments, .. } => {
result.extend(arguments.iter());
}
EmptyArray | RuntimeErrorFunction(_) | Literal(_) => {}
}
@ -208,6 +221,11 @@ fn consume_expr(m: &VarMap, e: &Expr<'_>) -> bool {
}
}
fn consume_call(_: &VarMap, _: &crate::ir::Call<'_>) -> bool {
// variables bound by a call (or invoke) must always be consumed
true
}
impl<'a> Context<'a> {
pub fn new(arena: &'a Bump, param_map: &'a ParamMap<'a>) -> Self {
let mut vars = MutMap::default();
@ -410,6 +428,75 @@ impl<'a> Context<'a> {
b
}
fn visit_call(
&self,
z: Symbol,
call_type: crate::ir::CallType<'a>,
arguments: &'a [Symbol],
l: Layout<'a>,
b: &'a Stmt<'a>,
b_live_vars: &LiveVarSet,
) -> &'a Stmt<'a> {
use crate::ir::CallType::*;
match &call_type {
LowLevel { op } => {
let ps = crate::borrow::lowlevel_borrow_signature(self.arena, *op);
let b = self.add_dec_after_lowlevel(arguments, ps, b, b_live_vars);
let v = Expr::Call(crate::ir::Call {
call_type,
arguments,
});
&*self.arena.alloc(Stmt::Let(z, v, l, b))
}
Foreign { .. } => {
let ps = crate::borrow::foreign_borrow_signature(self.arena, arguments.len());
let b = self.add_dec_after_lowlevel(arguments, ps, b, b_live_vars);
let v = Expr::Call(crate::ir::Call {
call_type,
arguments,
});
&*self.arena.alloc(Stmt::Let(z, v, l, b))
}
ByName {
name, arg_layouts, ..
}
| ByPointer {
name, arg_layouts, ..
} => {
// get the borrow signature
let ps = match self.param_map.get_symbol(*name) {
Some(slice) => slice,
None => Vec::from_iter_in(
arg_layouts.iter().cloned().map(|layout| Param {
symbol: Symbol::UNDERSCORE,
borrow: false,
layout,
}),
self.arena,
)
.into_bump_slice(),
};
let v = Expr::Call(crate::ir::Call {
call_type,
arguments,
});
let b = self.add_dec_after_application(arguments, ps, b, b_live_vars);
let b = self.arena.alloc(Stmt::Let(z, v, l, b));
self.add_inc_before(arguments, ps, b, b_live_vars)
}
}
}
#[allow(clippy::many_single_char_names)]
fn visit_variable_declaration(
&self,
@ -445,45 +532,10 @@ impl<'a> Context<'a> {
self.arena.alloc(Stmt::Let(z, v, l, b))
}
RunLowLevel(op, args) => {
let ps = crate::borrow::lowlevel_borrow_signature(self.arena, op);
let b = self.add_dec_after_lowlevel(args, ps, b, b_live_vars);
self.arena.alloc(Stmt::Let(z, v, l, b))
}
ForeignCall { arguments, .. } => {
let ps = crate::borrow::foreign_borrow_signature(self.arena, arguments.len());
let b = self.add_dec_after_lowlevel(arguments, ps, b, b_live_vars);
self.arena.alloc(Stmt::Let(z, v, l, b))
}
FunctionCall {
args: ys,
arg_layouts,
Call(crate::ir::Call {
call_type,
..
} => {
// get the borrow signature
let ps = match self.param_map.get_symbol(call_type.get_inner()) {
Some(slice) => slice,
None => Vec::from_iter_in(
arg_layouts.iter().cloned().map(|layout| Param {
symbol: Symbol::UNDERSCORE,
borrow: false,
layout,
}),
self.arena,
)
.into_bump_slice(),
};
let b = self.add_dec_after_application(ys, ps, b, b_live_vars);
let b = self.arena.alloc(Stmt::Let(z, v, l, b));
self.add_inc_before(ys, ps, b, b_live_vars)
}
arguments,
}) => self.visit_call(z, call_type, arguments, l, b, b_live_vars),
EmptyArray
| FunctionPointer(_, _)
@ -499,21 +551,45 @@ impl<'a> Context<'a> {
(new_b, live_vars)
}
fn update_var_info_invoke(
&self,
symbol: Symbol,
layout: &Layout<'a>,
call: &crate::ir::Call<'a>,
) -> Self {
// is this value a constant?
// TODO do function pointers also fall into this category?
let persistent = call.arguments.is_empty();
// must this value be consumed?
let consume = consume_call(&self.vars, call);
self.update_var_info_help(symbol, layout, persistent, consume)
}
fn update_var_info(&self, symbol: Symbol, layout: &Layout<'a>, expr: &Expr<'a>) -> Self {
let mut ctx = self.clone();
// can this type be reference-counted at runtime?
let reference = layout.contains_refcounted();
// is this value a constant?
// TODO do function pointers also fall into this category?
let persistent = match expr {
Expr::FunctionCall { args, .. } => args.is_empty(),
Expr::Call(crate::ir::Call { arguments, .. }) => arguments.is_empty(),
_ => false,
};
// must this value be consumed?
let consume = consume_expr(&ctx.vars, expr);
let consume = consume_expr(&self.vars, expr);
self.update_var_info_help(symbol, layout, persistent, consume)
}
fn update_var_info_help(
&self,
symbol: Symbol,
layout: &Layout<'a>,
persistent: bool,
consume: bool,
) -> Self {
// can this type be reference-counted at runtime?
let reference = layout.contains_refcounted();
let info = VarInfo {
reference,
@ -521,6 +597,8 @@ impl<'a> Context<'a> {
consume,
};
let mut ctx = self.clone();
ctx.vars.insert(symbol, info);
ctx
@ -628,6 +706,47 @@ impl<'a> Context<'a> {
)
}
Invoke {
symbol,
call,
pass,
fail,
layout,
} => {
// TODO this combines parts of Let and Switch. Did this happen correctly?
let mut case_live_vars = collect_stmt(stmt, &self.jp_live_vars, MutSet::default());
case_live_vars.remove(symbol);
let fail = {
// TODO should we use ctor info like Lean?
let ctx = self.clone();
let (b, alt_live_vars) = ctx.visit_stmt(fail);
ctx.add_dec_for_alt(&case_live_vars, &alt_live_vars, b)
};
case_live_vars.insert(*symbol);
let pass = {
// TODO should we use ctor info like Lean?
let ctx = self.clone();
let ctx = ctx.update_var_info_invoke(*symbol, layout, call);
let (b, alt_live_vars) = ctx.visit_stmt(pass);
ctx.add_dec_for_alt(&case_live_vars, &alt_live_vars, b)
};
let invoke = Invoke {
symbol: *symbol,
call: call.clone(),
pass,
fail,
layout: layout.clone(),
};
let stmt = self.arena.alloc(invoke);
(stmt, case_live_vars)
}
Join {
id: j,
parameters: _,
@ -673,6 +792,8 @@ impl<'a> Context<'a> {
}
}
Rethrow => (stmt, MutSet::default()),
Jump(j, xs) => {
let empty = MutSet::default();
let j_live_vars = match self.jp_live_vars.get(j) {
@ -757,6 +878,25 @@ pub fn collect_stmt(
vars
}
Invoke {
symbol,
call,
pass,
fail,
..
} => {
vars = collect_stmt(pass, jp_live_vars, vars);
vars = collect_stmt(fail, jp_live_vars, vars);
vars.remove(symbol);
let mut result = MutSet::default();
occuring_variables_call(call, &mut result);
vars.extend(result);
vars
}
Ret(symbol) => {
vars.insert(*symbol);
vars
@ -813,6 +953,8 @@ pub fn collect_stmt(
vars
}
Rethrow => vars,
RuntimeError(_) => vars,
}
}

File diff suppressed because it is too large Load diff

View file

@ -11,6 +11,7 @@ pub const MAX_ENUM_SIZE: usize = (std::mem::size_of::<u8>() * 8) as usize;
/// If a (Num *) gets translated to a Layout, this is the numeric type it defaults to.
const DEFAULT_NUM_BUILTIN: Builtin<'_> = Builtin::Int64;
pub const TAG_SIZE: Builtin<'_> = Builtin::Int64;
#[derive(Debug, Clone)]
pub enum LayoutProblem {
@ -157,7 +158,13 @@ impl<'a> ClosureLayout<'a> {
Ok(Some(closure_layout))
}
Wrapped(tags) => {
Wrapped {
sorted_tag_layouts: tags,
is_recursive,
} => {
// TODO handle recursive closures
debug_assert!(!is_recursive);
let closure_layout =
ClosureLayout::from_tag_union(arena, tags.into_bump_slice());
@ -312,6 +319,7 @@ pub enum Builtin<'a> {
Int16,
Int8,
Int1,
Usize,
Float128,
Float64,
Float32,
@ -362,14 +370,60 @@ impl<'a> Layout<'a> {
}
Structure(flat_type) => layout_from_flat_type(env, flat_type),
// Ints
Alias(Symbol::NUM_I128, args, _) => {
debug_assert!(args.is_empty());
Ok(Layout::Builtin(Builtin::Int128))
}
Alias(Symbol::NUM_I64, args, _) => {
debug_assert!(args.is_empty());
Ok(Layout::Builtin(Builtin::Int64))
}
Alias(Symbol::NUM_I32, args, _) => {
debug_assert!(args.is_empty());
Ok(Layout::Builtin(Builtin::Int32))
}
Alias(Symbol::NUM_I16, args, _) => {
debug_assert!(args.is_empty());
Ok(Layout::Builtin(Builtin::Int16))
}
Alias(Symbol::NUM_I8, args, _) => {
debug_assert!(args.is_empty());
Ok(Layout::Builtin(Builtin::Int8))
}
// I think unsigned and signed use the same layout
Alias(Symbol::NUM_U128, args, _) => {
debug_assert!(args.is_empty());
Ok(Layout::Builtin(Builtin::Int128))
}
Alias(Symbol::NUM_U64, args, _) => {
debug_assert!(args.is_empty());
Ok(Layout::Builtin(Builtin::Int64))
}
Alias(Symbol::NUM_U32, args, _) => {
debug_assert!(args.is_empty());
Ok(Layout::Builtin(Builtin::Int32))
}
Alias(Symbol::NUM_U16, args, _) => {
debug_assert!(args.is_empty());
Ok(Layout::Builtin(Builtin::Int16))
}
Alias(Symbol::NUM_U8, args, _) => {
debug_assert!(args.is_empty());
Ok(Layout::Builtin(Builtin::Int8))
}
// Floats
Alias(Symbol::NUM_F64, args, _) => {
debug_assert!(args.is_empty());
Ok(Layout::Builtin(Builtin::Float64))
}
Alias(Symbol::NUM_F32, args, _) => {
debug_assert!(args.is_empty());
Ok(Layout::Builtin(Builtin::Float32))
}
Alias(_, _, var) => Self::from_var(env, var),
Error => Err(LayoutProblem::Erroneous),
}
@ -654,6 +708,7 @@ impl<'a> Builtin<'a> {
const I16_SIZE: u32 = std::mem::size_of::<i16>() as u32;
const I8_SIZE: u32 = std::mem::size_of::<i8>() as u32;
const I1_SIZE: u32 = std::mem::size_of::<bool>() as u32;
const USIZE_SIZE: u32 = std::mem::size_of::<usize>() as u32;
const F128_SIZE: u32 = 16;
const F64_SIZE: u32 = std::mem::size_of::<f64>() as u32;
const F32_SIZE: u32 = std::mem::size_of::<f32>() as u32;
@ -682,6 +737,7 @@ impl<'a> Builtin<'a> {
Int16 => Builtin::I16_SIZE,
Int8 => Builtin::I8_SIZE,
Int1 => Builtin::I1_SIZE,
Usize => Builtin::USIZE_SIZE,
Float128 => Builtin::F128_SIZE,
Float64 => Builtin::F64_SIZE,
Float32 => Builtin::F32_SIZE,
@ -707,6 +763,7 @@ impl<'a> Builtin<'a> {
Int16 => align_of::<i16>() as u32,
Int8 => align_of::<i8>() as u32,
Int1 => align_of::<bool>() as u32,
Usize => align_of::<usize>() as u32,
Float128 => align_of::<i128>() as u32,
Float64 => align_of::<f64>() as u32,
Float32 => align_of::<f32>() as u32,
@ -722,7 +779,7 @@ impl<'a> Builtin<'a> {
use Builtin::*;
match self {
Int128 | Int64 | Int32 | Int16 | Int8 | Int1 | Float128 | Float64 | Float32
Int128 | Int64 | Int32 | Int16 | Int8 | Int1 | Usize | Float128 | Float64 | Float32
| Float16 | EmptyStr | EmptyDict | EmptyList | EmptySet => true,
Str | Dict(_, _) | Set(_) | List(_, _) => false,
}
@ -733,7 +790,7 @@ impl<'a> Builtin<'a> {
use Builtin::*;
match self {
Int128 | Int64 | Int32 | Int16 | Int8 | Int1 | Float128 | Float64 | Float32
Int128 | Int64 | Int32 | Int16 | Int8 | Int1 | Usize | Float128 | Float64 | Float32
| Float16 | EmptyStr | EmptyDict | EmptyList | EmptySet => false,
List(mode, element_layout) => match mode {
MemoryMode::Refcounted => true,
@ -757,14 +814,64 @@ fn layout_from_flat_type<'a>(
match flat_type {
Apply(symbol, args) => {
match symbol {
// Ints
Symbol::NUM_NAT => {
debug_assert_eq!(args.len(), 0);
Ok(Layout::Builtin(Builtin::Usize))
}
Symbol::NUM_I128 => {
debug_assert_eq!(args.len(), 0);
Ok(Layout::Builtin(Builtin::Int128))
}
Symbol::NUM_I64 => {
debug_assert_eq!(args.len(), 0);
Ok(Layout::Builtin(Builtin::Int64))
}
Symbol::NUM_I32 => {
debug_assert_eq!(args.len(), 0);
Ok(Layout::Builtin(Builtin::Int32))
}
Symbol::NUM_I16 => {
debug_assert_eq!(args.len(), 0);
Ok(Layout::Builtin(Builtin::Int16))
}
Symbol::NUM_I8 => {
debug_assert_eq!(args.len(), 0);
Ok(Layout::Builtin(Builtin::Int8))
}
Symbol::NUM_U128 => {
debug_assert_eq!(args.len(), 0);
Ok(Layout::Builtin(Builtin::Int128))
}
Symbol::NUM_U64 => {
debug_assert_eq!(args.len(), 0);
Ok(Layout::Builtin(Builtin::Int64))
}
Symbol::NUM_U32 => {
debug_assert_eq!(args.len(), 0);
Ok(Layout::Builtin(Builtin::Int32))
}
Symbol::NUM_U16 => {
debug_assert_eq!(args.len(), 0);
Ok(Layout::Builtin(Builtin::Int16))
}
Symbol::NUM_U8 => {
debug_assert_eq!(args.len(), 0);
Ok(Layout::Builtin(Builtin::Int8))
}
// Floats
Symbol::NUM_F64 => {
debug_assert_eq!(args.len(), 0);
Ok(Layout::Builtin(Builtin::Float64))
}
Symbol::NUM_F32 => {
debug_assert_eq!(args.len(), 0);
Ok(Layout::Builtin(Builtin::Float32))
}
Symbol::NUM_NUM | Symbol::NUM_AT_NUM => {
// Num.Num should only ever have 1 argument, e.g. Num.Num Int.Integer
debug_assert_eq!(args.len(), 1);
@ -774,6 +881,7 @@ fn layout_from_flat_type<'a>(
layout_from_num_content(content)
}
Symbol::STR_STR => Ok(Layout::Builtin(Builtin::Str)),
Symbol::LIST_LIST => list_layout_from_elem(env, args[0]),
Symbol::ATTR_ATTR => {
@ -888,7 +996,7 @@ fn layout_from_flat_type<'a>(
let mut tag_layout = Vec::with_capacity_in(variables.len() + 1, arena);
// store the discriminant
tag_layout.push(Layout::Builtin(Builtin::Int64));
tag_layout.push(Layout::Builtin(TAG_SIZE));
for var in variables {
// TODO does this cause problems with mutually recursive unions?
@ -993,10 +1101,16 @@ pub enum UnionVariant<'a> {
Never,
Unit,
UnitWithArguments,
BoolUnion { ttrue: TagName, ffalse: TagName },
BoolUnion {
ttrue: TagName,
ffalse: TagName,
},
ByteUnion(Vec<'a, TagName>),
Unwrapped(Vec<'a, Layout<'a>>),
Wrapped(Vec<'a, (TagName, &'a [Layout<'a>])>),
Wrapped {
sorted_tag_layouts: Vec<'a, (TagName, &'a [Layout<'a>])>,
is_recursive: bool,
},
}
pub fn union_sorted_tags<'a>(arena: &'a Bump, var: Variable, subs: &Subs) -> UnionVariant<'a> {
@ -1119,7 +1233,7 @@ pub fn union_sorted_tags_help<'a>(
let mut arg_layouts = Vec::with_capacity_in(arguments.len() + 1, arena);
// add the tag discriminant (size currently always hardcoded to i64)
arg_layouts.push(Layout::Builtin(Builtin::Int64));
arg_layouts.push(Layout::Builtin(TAG_SIZE));
for var in arguments {
match Layout::from_var(&mut env, var) {
@ -1175,7 +1289,10 @@ pub fn union_sorted_tags_help<'a>(
UnionVariant::ByteUnion(tag_names)
}
_ => UnionVariant::Wrapped(answer),
_ => UnionVariant::Wrapped {
sorted_tag_layouts: answer,
is_recursive: opt_rec_var.is_some(),
},
}
}
}
@ -1214,13 +1331,21 @@ pub fn layout_from_tag_union<'a>(
Layout::Struct(field_layouts.into_bump_slice())
}
}
Wrapped(tags) => {
Wrapped {
sorted_tag_layouts: tags,
is_recursive,
} => {
let mut tag_layouts = Vec::with_capacity_in(tags.len(), arena);
for (_, tag_layout) in tags {
tag_layouts.push(tag_layout);
}
Layout::Union(tag_layouts.into_bump_slice())
if is_recursive {
Layout::RecursiveUnion(tag_layouts.into_bump_slice())
} else {
Layout::Union(tag_layouts.into_bump_slice())
}
}
}
}
@ -1257,8 +1382,27 @@ fn layout_from_num_content<'a>(content: Content) -> Result<Layout<'a>, LayoutPro
Ok(Layout::Builtin(DEFAULT_NUM_BUILTIN))
}
Structure(Apply(symbol, args)) => match symbol {
// Ints
Symbol::NUM_NAT => Ok(Layout::Builtin(Builtin::Usize)),
Symbol::NUM_INTEGER => Ok(Layout::Builtin(Builtin::Int64)),
Symbol::NUM_I128 => Ok(Layout::Builtin(Builtin::Int128)),
Symbol::NUM_I64 => Ok(Layout::Builtin(Builtin::Int64)),
Symbol::NUM_I32 => Ok(Layout::Builtin(Builtin::Int32)),
Symbol::NUM_I16 => Ok(Layout::Builtin(Builtin::Int16)),
Symbol::NUM_I8 => Ok(Layout::Builtin(Builtin::Int8)),
Symbol::NUM_U128 => Ok(Layout::Builtin(Builtin::Int128)),
Symbol::NUM_U64 => Ok(Layout::Builtin(Builtin::Int64)),
Symbol::NUM_U32 => Ok(Layout::Builtin(Builtin::Int32)),
Symbol::NUM_U16 => Ok(Layout::Builtin(Builtin::Int16)),
Symbol::NUM_U8 => Ok(Layout::Builtin(Builtin::Int8)),
// Floats
Symbol::NUM_FLOATINGPOINT => Ok(Layout::Builtin(Builtin::Float64)),
Symbol::NUM_F64 => Ok(Layout::Builtin(Builtin::Float64)),
Symbol::NUM_F32 => Ok(Layout::Builtin(Builtin::Float32)),
_ => {
panic!(
"Invalid Num.Num type application: {:?}",
@ -1293,16 +1437,62 @@ fn unwrap_num_tag<'a>(subs: &Subs, var: Variable) -> Result<Layout<'a>, LayoutPr
Content::Alias(Symbol::NUM_INTEGER, args, _) => {
debug_assert!(args.len() == 1);
// TODO: we probably need to match on the type of the arg
// and return the correct builtin ex: Builtin::{Int32, Int16}
Ok(Layout::Builtin(Builtin::Int64))
let (_, precision_var) = args[0];
let precision = subs.get_without_compacting(precision_var).content;
match precision {
Content::Alias(symbol, args, _) => {
debug_assert!(args.is_empty());
let builtin = match symbol {
Symbol::NUM_SIGNED128 => Builtin::Int128,
Symbol::NUM_SIGNED64 => Builtin::Int64,
Symbol::NUM_SIGNED32 => Builtin::Int32,
Symbol::NUM_SIGNED16 => Builtin::Int16,
Symbol::NUM_SIGNED8 => Builtin::Int8,
Symbol::NUM_UNSIGNED128 => Builtin::Int128,
Symbol::NUM_UNSIGNED64 => Builtin::Int64,
Symbol::NUM_UNSIGNED32 => Builtin::Int32,
Symbol::NUM_UNSIGNED16 => Builtin::Int16,
Symbol::NUM_UNSIGNED8 => Builtin::Int8,
Symbol::NUM_NATURAL => Builtin::Usize,
_ => unreachable!("not a valid int variant: {:?} {:?}", symbol, args),
};
Ok(Layout::Builtin(builtin))
}
Content::FlexVar(_) | Content::RigidVar(_) => {
// default to i64
Ok(Layout::Builtin(Builtin::Int64))
}
_ => unreachable!("not a valid int variant: {:?}", precision),
}
}
Content::Alias(Symbol::NUM_FLOATINGPOINT, args, _) => {
debug_assert!(args.len() == 1);
// TODO: we probably need to match on the type of the arg
// and return the correct builtin ex: Builtin::Float32
Ok(Layout::Builtin(Builtin::Float64))
let (_, precision_var) = args[0];
let precision = subs.get_without_compacting(precision_var).content;
match precision {
Content::Alias(Symbol::NUM_BINARY32, args, _) => {
debug_assert!(args.is_empty());
Ok(Layout::Builtin(Builtin::Float32))
}
Content::Alias(Symbol::NUM_BINARY64, args, _) => {
debug_assert!(args.is_empty());
Ok(Layout::Builtin(Builtin::Float64))
}
Content::FlexVar(_) | Content::RigidVar(_) => {
// default to f64
Ok(Layout::Builtin(Builtin::Float64))
}
_ => unreachable!("not a valid float variant: {:?}", precision),
}
}
Content::FlexVar(_) | Content::RigidVar(_) => {
// If this was still a (Num *) then default to compiling it to i64

View file

@ -75,17 +75,38 @@ fn insert_jumps<'a>(
match stmt {
Let(
symbol,
Expr::FunctionCall {
call_type: CallType::ByName(fsym),
args,
Expr::Call(crate::ir::Call {
call_type: CallType::ByName { name: fsym, .. },
arguments,
..
},
}),
_,
Stmt::Ret(rsym),
) if needle == *fsym && symbol == rsym => {
// replace the call and return with a jump
let jump = Stmt::Jump(goal_id, args);
let jump = Stmt::Jump(goal_id, arguments);
Some(arena.alloc(jump))
}
Invoke {
symbol,
call:
crate::ir::Call {
call_type: CallType::ByName { name: fsym, .. },
arguments,
..
},
fail,
pass: Stmt::Ret(rsym),
..
} if needle == *fsym && symbol == rsym => {
debug_assert_eq!(fail, &&Stmt::Rethrow);
// replace the call and return with a jump
let jump = Stmt::Jump(goal_id, arguments);
Some(arena.alloc(jump))
}
@ -101,6 +122,35 @@ fn insert_jumps<'a>(
None
}
}
Invoke {
symbol,
call,
fail,
pass,
layout,
} => {
let opt_pass = insert_jumps(arena, pass, goal_id, needle);
let opt_fail = insert_jumps(arena, fail, goal_id, needle);
if opt_pass.is_some() || opt_fail.is_some() {
let pass = opt_pass.unwrap_or(pass);
let fail = opt_fail.unwrap_or(fail);
let stmt = Invoke {
symbol: *symbol,
call: call.clone(),
layout: layout.clone(),
pass,
fail,
};
Some(arena.alloc(stmt))
} else {
None
}
}
Join {
id,
parameters,
@ -187,6 +237,7 @@ fn insert_jumps<'a>(
None => None,
},
Rethrow => None,
Ret(_) => None,
Jump(_, _) => None,
RuntimeError(_) => None,