mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-30 07:14:46 +00:00
Merge branch 'trunk' of github.com:rtfeldman/roc into dict
This commit is contained in:
commit
c7f6de2afe
93 changed files with 6706 additions and 5317 deletions
|
@ -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 => {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -37,7 +37,7 @@ pub enum Pattern {
|
|||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Literal {
|
||||
Int(i64),
|
||||
Int(i128),
|
||||
Bit(bool),
|
||||
Byte(u8),
|
||||
Float(u64),
|
||||
|
|
|
@ -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
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue