mirror of
https://github.com/roc-lang/roc.git
synced 2024-09-22 00:09:33 +03:00
clippy warnings
This commit is contained in:
parent
55eff50e36
commit
e3356dea7b
@ -1,4 +1,4 @@
|
||||
use crate::ir::{Expr, JoinPointId, Stmt};
|
||||
use crate::ir::{Expr, JoinPointId, Param, Stmt};
|
||||
use crate::layout::Layout;
|
||||
use bumpalo::collections::Vec;
|
||||
use bumpalo::Bump;
|
||||
@ -89,7 +89,6 @@ pub fn occuring_variables(stmt: &Stmt<'_>) -> (MutSet<Symbol>, MutSet<Symbol>) {
|
||||
}
|
||||
|
||||
pub fn occuring_variables_expr(expr: &Expr<'_>, result: &mut MutSet<Symbol>) {
|
||||
use crate::ir::CallType;
|
||||
use Expr::*;
|
||||
|
||||
match expr {
|
||||
@ -101,15 +100,10 @@ pub fn occuring_variables_expr(expr: &Expr<'_>, result: &mut MutSet<Symbol>) {
|
||||
result.insert(*symbol);
|
||||
}
|
||||
|
||||
FunctionCall {
|
||||
call_type, args, ..
|
||||
} => {
|
||||
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());
|
||||
|
||||
match call_type {
|
||||
CallType::ByName(s) => result.insert(*s),
|
||||
CallType::ByPointer(s) => result.insert(*s),
|
||||
};
|
||||
}
|
||||
|
||||
RunLowLevel(_, arguments)
|
||||
@ -125,455 +119,6 @@ pub fn occuring_variables_expr(expr: &Expr<'_>, result: &mut MutSet<Symbol>) {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
pub enum Ownership {
|
||||
Owned,
|
||||
Borrowed,
|
||||
}
|
||||
|
||||
pub struct Env<'a> {
|
||||
pub arena: &'a Bump,
|
||||
pub beta: MutMap<Symbol, &'a [Ownership]>,
|
||||
pub beta_l: MutMap<Symbol, Ownership>,
|
||||
pub join_points: MutMap<JoinPointId, MutSet<Symbol>>,
|
||||
}
|
||||
|
||||
impl<'a> Env<'a> {
|
||||
pub fn new(arena: &'a Bump) -> Self {
|
||||
Self {
|
||||
arena,
|
||||
beta: MutMap::default(),
|
||||
beta_l: MutMap::default(),
|
||||
join_points: MutMap::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn ownership(&self, symbol: &Symbol) -> Ownership {
|
||||
// default to owned
|
||||
match self.beta_l.get(symbol) {
|
||||
None => Ownership::Owned,
|
||||
Some(o) => *o,
|
||||
}
|
||||
}
|
||||
|
||||
fn borrow_signature(&self, symbol: &Symbol, arguments: &[Symbol]) -> &'a [(Symbol, Ownership)] {
|
||||
use Ownership::*;
|
||||
|
||||
let signature = match self.beta.get(symbol) {
|
||||
None => &[] as &[_],
|
||||
Some(o) => o,
|
||||
};
|
||||
|
||||
let mut result = Vec::with_capacity_in(arguments.len(), self.arena);
|
||||
|
||||
for (i, arg) in arguments.iter().enumerate() {
|
||||
let ownership = match signature.get(i) {
|
||||
None => Owned,
|
||||
Some(o) => *o,
|
||||
};
|
||||
result.push((*arg, ownership));
|
||||
}
|
||||
|
||||
result.into_bump_slice()
|
||||
}
|
||||
}
|
||||
|
||||
fn function_o_minus_x<'a>(
|
||||
arena: &'a Bump,
|
||||
x: Symbol,
|
||||
f: &'a Stmt<'a>,
|
||||
ownership: Ownership,
|
||||
) -> &'a Stmt<'a> {
|
||||
match ownership {
|
||||
Ownership::Owned if !free_variables(&f).contains(&x) => arena.alloc(Stmt::Dec(x, f)),
|
||||
_ => f,
|
||||
}
|
||||
}
|
||||
|
||||
fn function_o_minus<'a>(arena: &'a Bump, xs: &[Symbol], mut f: &'a Stmt<'a>) -> &'a Stmt<'a> {
|
||||
for x in xs.iter() {
|
||||
f = function_o_minus_x(arena, *x, f, Ownership::Owned);
|
||||
}
|
||||
|
||||
f
|
||||
}
|
||||
|
||||
fn function_o_plus_x<'a>(
|
||||
arena: &'a Bump,
|
||||
x: Symbol,
|
||||
v: &MutSet<Symbol>,
|
||||
f: &'a Stmt<'a>,
|
||||
ownership: Ownership,
|
||||
) -> &'a Stmt<'a> {
|
||||
match ownership {
|
||||
Ownership::Owned if !v.contains(&x) => f,
|
||||
_ => arena.alloc(Stmt::Inc(x, f)),
|
||||
}
|
||||
}
|
||||
|
||||
fn function_c_app<'a>(
|
||||
arena: &'a Bump,
|
||||
arguments: &[(Symbol, Ownership)],
|
||||
stmt: &'a Stmt<'a>,
|
||||
) -> &'a Stmt<'a> {
|
||||
use Ownership::*;
|
||||
use Stmt::*;
|
||||
|
||||
match (arguments.get(0), stmt) {
|
||||
(Some((y, Owned)), Let(z, _, e, f)) => {
|
||||
let ybar = &arguments[1..];
|
||||
|
||||
let mut v = free_variables(f);
|
||||
v.extend(ybar.iter().map(|(s, _)| s).copied());
|
||||
|
||||
let rest = function_c_app(arena, ybar, stmt);
|
||||
function_o_plus_x(arena, *y, &v, rest, Owned)
|
||||
}
|
||||
(Some((y, Borrowed)), Let(z, l, e, f)) => {
|
||||
let ybar = &arguments[1..];
|
||||
|
||||
let v = ybar.iter().map(|(s, _)| s).copied().collect::<MutSet<_>>();
|
||||
|
||||
let rest = Stmt::Let(
|
||||
*z,
|
||||
l.clone(),
|
||||
e.clone(),
|
||||
function_o_minus_x(arena, *y, f, Owned),
|
||||
);
|
||||
|
||||
function_c_app(arena, ybar, arena.alloc(rest))
|
||||
}
|
||||
_ => stmt,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn function_c<'a>(env: &mut Env<'a>, stmt: &'a Stmt<'a>) -> &'a Stmt<'a> {
|
||||
use Expr::*;
|
||||
use Ownership::*;
|
||||
use Stmt::*;
|
||||
|
||||
let arena = env.arena;
|
||||
|
||||
dbg!(stmt);
|
||||
|
||||
/*
|
||||
| b@(FnBody.jmp j xs), ctx =>
|
||||
let jLiveVars := getJPLiveVars ctx j;
|
||||
let ps := getJPParams ctx j;
|
||||
let b := addIncBefore ctx xs ps b jLiveVars;
|
||||
let bLiveVars := collectLiveVars b ctx.jpLiveVarMap;
|
||||
(b, bLiveVars)
|
||||
|
||||
| FnBody.jdecl j xs v b, ctx =>
|
||||
let (v, vLiveVars) := visitFnBody v (updateVarInfoWithParams ctx xs);
|
||||
let v := addDecForDeadParams ctx xs v vLiveVars;
|
||||
let ctx := { ctx with jpLiveVarMap := updateJPLiveVarMap j xs v ctx.jpLiveVarMap };
|
||||
let (b, bLiveVars) := visitFnBody b ctx;
|
||||
(FnBody.jdecl j xs v b, bLiveVars)
|
||||
*/
|
||||
|
||||
match stmt {
|
||||
Ret(x) => function_o_plus_x(arena, *x, &MutSet::default(), stmt, Owned),
|
||||
|
||||
Cond {
|
||||
cond_symbol,
|
||||
cond_layout,
|
||||
branching_symbol,
|
||||
branching_layout,
|
||||
pass,
|
||||
fail,
|
||||
ret_layout,
|
||||
} => {
|
||||
let ybar: Vec<Symbol> = Vec::from_iter_in(
|
||||
std::iter::once(free_variables(pass))
|
||||
.chain(std::iter::once(free_variables(fail)))
|
||||
.flatten(),
|
||||
arena,
|
||||
);
|
||||
|
||||
let new_pass = function_o_minus(arena, &ybar, function_c(env, pass));
|
||||
let new_fail = function_o_minus(arena, &ybar, function_c(env, fail));
|
||||
|
||||
let cond = Cond {
|
||||
cond_symbol: *cond_symbol,
|
||||
cond_layout: cond_layout.clone(),
|
||||
branching_symbol: *branching_symbol,
|
||||
branching_layout: branching_layout.clone(),
|
||||
pass: new_pass,
|
||||
fail: new_fail,
|
||||
ret_layout: ret_layout.clone(),
|
||||
};
|
||||
|
||||
arena.alloc(cond)
|
||||
}
|
||||
Switch {
|
||||
cond_symbol,
|
||||
branches,
|
||||
default_branch,
|
||||
cond_layout,
|
||||
ret_layout,
|
||||
} => {
|
||||
let ybar: Vec<Symbol> = Vec::from_iter_in(
|
||||
std::iter::once(free_variables(default_branch))
|
||||
.chain(branches.iter().map(|(_, b)| free_variables(b)))
|
||||
.flatten(),
|
||||
arena,
|
||||
);
|
||||
|
||||
let new_default_branch =
|
||||
function_o_minus(arena, &ybar, function_c(env, default_branch));
|
||||
let new_branches: &'a [(u64, Stmt<'a>)] = Vec::from_iter_in(
|
||||
branches.iter().map(|(label, branch)| {
|
||||
(
|
||||
*label,
|
||||
function_o_minus(arena, &ybar, function_c(env, branch)).clone(),
|
||||
)
|
||||
}),
|
||||
arena,
|
||||
)
|
||||
.into_bump_slice();
|
||||
|
||||
arena.alloc(Switch {
|
||||
cond_symbol: *cond_symbol,
|
||||
branches: new_branches,
|
||||
default_branch: new_default_branch,
|
||||
cond_layout: cond_layout.clone(),
|
||||
ret_layout: ret_layout.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
Let(y, e, l, f) => match e {
|
||||
AccessAtIndex { structure, .. } => match env.ownership(structure) {
|
||||
Owned => {
|
||||
let foo = function_c(env, f);
|
||||
let cont = function_o_minus_x(arena, *structure, arena.alloc(foo), Owned);
|
||||
let rest = arena.alloc(Inc(*y, cont));
|
||||
|
||||
arena.alloc(Let(*y, e.clone(), l.clone(), rest))
|
||||
}
|
||||
Borrowed => {
|
||||
let old_y = env.beta_l.insert(*y, Borrowed);
|
||||
|
||||
let rest = function_c(env, f);
|
||||
|
||||
match old_y {
|
||||
Some(old) => env.beta_l.insert(*y, old),
|
||||
None => env.beta_l.remove(y),
|
||||
};
|
||||
|
||||
arena.alloc(Let(*y, e.clone(), l.clone(), rest))
|
||||
}
|
||||
},
|
||||
Tag { arguments, .. }
|
||||
| Struct(arguments)
|
||||
| Array {
|
||||
elems: arguments, ..
|
||||
} => {
|
||||
let rest = function_c(env, f);
|
||||
let let_stmt = arena.alloc(Let(*y, e.clone(), l.clone(), rest));
|
||||
|
||||
let y_owned = Vec::from_iter_in(arguments.iter().map(|s| (*s, Owned)), arena);
|
||||
|
||||
function_c_app(arena, &y_owned, let_stmt)
|
||||
}
|
||||
EmptyArray => {
|
||||
let rest = function_c(env, f);
|
||||
let let_stmt = arena.alloc(Let(*y, e.clone(), l.clone(), rest));
|
||||
|
||||
let y_owned = &[] as &[_];
|
||||
|
||||
function_c_app(arena, &y_owned, let_stmt)
|
||||
}
|
||||
FunctionCall {
|
||||
call_type, args, ..
|
||||
} => {
|
||||
use crate::ir::CallType;
|
||||
|
||||
let c = match call_type {
|
||||
CallType::ByName(s) => s,
|
||||
CallType::ByPointer(s) => s,
|
||||
};
|
||||
|
||||
let rest = function_c(env, f);
|
||||
let let_stmt = arena.alloc(Let(*y, e.clone(), l.clone(), rest));
|
||||
|
||||
let y_owned = env.borrow_signature(&c, args);
|
||||
|
||||
function_c_app(arena, &y_owned, let_stmt)
|
||||
}
|
||||
RunLowLevel(_, args) => {
|
||||
use crate::ir::CallType;
|
||||
|
||||
let rest = function_c(env, f);
|
||||
let let_stmt = arena.alloc(Let(*y, e.clone(), l.clone(), rest));
|
||||
|
||||
let y_owned = {
|
||||
let signature = &[] as &[_];
|
||||
|
||||
let mut result = Vec::with_capacity_in(args.len(), env.arena);
|
||||
|
||||
for (i, arg) in args.iter().enumerate() {
|
||||
let ownership = match signature.get(i) {
|
||||
None => Owned,
|
||||
Some(o) => *o,
|
||||
};
|
||||
result.push((*arg, ownership));
|
||||
}
|
||||
|
||||
result.into_bump_slice()
|
||||
};
|
||||
|
||||
function_c_app(arena, &y_owned, let_stmt)
|
||||
}
|
||||
Literal(_) | Alias(_) | FunctionPointer(_, _) | RuntimeErrorFunction(_) => {
|
||||
// leaves in terms of RC
|
||||
let rest = function_c(env, f);
|
||||
arena.alloc(Let(*y, e.clone(), l.clone(), rest))
|
||||
}
|
||||
},
|
||||
Inc(_, _) | Dec(_, _) => stmt,
|
||||
Join { .. } | Jump(_, _) => stmt,
|
||||
RuntimeError(_) => stmt,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
fn visit_fn_body<'a>(env: &mut Env<'a>, stmt: &'a Stmt<'a>) -> (&'a Stmt<'a>, LiveVarSet) {
|
||||
use Stmt::*;
|
||||
|
||||
todo!()
|
||||
}
|
||||
*/
|
||||
|
||||
mod live_vars {
|
||||
use crate::ir::{Expr, JoinPointId, Stmt};
|
||||
use bumpalo::collections::Vec;
|
||||
use bumpalo::Bump;
|
||||
use roc_collections::all::{MutMap, MutSet};
|
||||
use roc_module::symbol::Symbol;
|
||||
|
||||
type LiveVarSet = MutSet<Symbol>;
|
||||
type JPLiveVarMap = MutMap<JoinPointId, LiveVarSet>;
|
||||
|
||||
pub fn collect_live_vars(b: &Stmt<'_>, _m: &JPLiveVarMap, v: &mut LiveVarSet) {
|
||||
// inefficient, but it works
|
||||
let (mut occuring, _) = crate::inc_dec::occuring_variables(b);
|
||||
|
||||
v.extend(occuring);
|
||||
|
||||
v.retain(|s| is_live(&MutMap::default(), *s, b));
|
||||
}
|
||||
|
||||
fn is_live<'a>(
|
||||
join_points: &MutMap<JoinPointId, (&'a [Symbol], Stmt<'a>)>,
|
||||
needle: Symbol,
|
||||
stmt: &Stmt<'a>,
|
||||
) -> bool {
|
||||
use Stmt::*;
|
||||
|
||||
let needle = &needle;
|
||||
|
||||
let mut stack = std::vec![stmt];
|
||||
let mut result = MutSet::default();
|
||||
|
||||
while let Some(stmt) = stack.pop() {
|
||||
use Stmt::*;
|
||||
|
||||
match stmt {
|
||||
Let(symbol, expr, _, cont) => {
|
||||
result.clear();
|
||||
crate::inc_dec::occuring_variables_expr(expr, &mut result);
|
||||
if result.contains(needle) {
|
||||
return true;
|
||||
}
|
||||
stack.push(cont);
|
||||
}
|
||||
Ret(symbol) => {
|
||||
if symbol == needle {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Inc(symbol, cont) | Dec(symbol, cont) => {
|
||||
if symbol == needle {
|
||||
return true;
|
||||
}
|
||||
stack.push(cont);
|
||||
}
|
||||
|
||||
Jump(j, _arguments) => {
|
||||
match join_points.get(j) {
|
||||
Some((_, b)) => {
|
||||
// `j` is not a local join point since we assume we cannot shadow join point declarations.
|
||||
// Instead of marking the join points that we have already been visited, we permanently remove `j` from the context.
|
||||
let mut join_points = join_points.clone();
|
||||
join_points.remove(j);
|
||||
|
||||
if is_live(&join_points, *needle, b) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
None => {
|
||||
// `j` must be a local join point. So do nothing since we have already visite its body.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Join {
|
||||
parameters,
|
||||
continuation,
|
||||
remainder,
|
||||
..
|
||||
} => {
|
||||
if parameters
|
||||
.iter()
|
||||
.map(|p| p.symbol)
|
||||
.find(|s| s == needle)
|
||||
.is_some()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
stack.push(continuation);
|
||||
stack.push(remainder);
|
||||
}
|
||||
|
||||
Switch {
|
||||
cond_symbol,
|
||||
branches,
|
||||
default_branch,
|
||||
..
|
||||
} => {
|
||||
if cond_symbol == needle {
|
||||
return true;
|
||||
}
|
||||
|
||||
stack.extend(branches.iter().map(|(_, s)| s));
|
||||
stack.push(default_branch);
|
||||
}
|
||||
|
||||
Cond {
|
||||
cond_symbol,
|
||||
branching_symbol,
|
||||
pass,
|
||||
fail,
|
||||
..
|
||||
} => {
|
||||
if cond_symbol == needle || branching_symbol == needle {
|
||||
return true;
|
||||
}
|
||||
stack.push(pass);
|
||||
stack.push(fail);
|
||||
}
|
||||
|
||||
RuntimeError(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Insert explicit RC instructions. So, it assumes the input code does not contain `inc` nor `dec` instructions.
|
||||
This transformation is applied before lower level optimizations
|
||||
that introduce the instructions `release` and `set`
|
||||
@ -590,20 +135,21 @@ type VarMap = MutMap<Symbol, VarInfo>;
|
||||
type LiveVarSet = MutSet<Symbol>;
|
||||
type JPLiveVarMap = MutMap<JoinPointId, LiveVarSet>;
|
||||
|
||||
// TODO this should contain more information ( e.g. make it contain layout, and whether it is
|
||||
// borrowed)
|
||||
type Param = Symbol;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct Context<'a> {
|
||||
pub struct Context<'a> {
|
||||
arena: &'a Bump,
|
||||
vars: VarMap,
|
||||
jp_live_vars: JPLiveVarMap, // map: join point => live variables
|
||||
local_context: LocalContext<'a>, // we use it to store the join point declarations
|
||||
function_params: MutMap<Symbol, &'a [Param<'a>]>,
|
||||
}
|
||||
|
||||
fn update_live_vars<'a>(e: &Expr<'a>, v: &LiveVarSet) -> LiveVarSet {
|
||||
todo!()
|
||||
fn update_live_vars<'a>(expr: &Expr<'a>, v: &LiveVarSet) -> LiveVarSet {
|
||||
let mut v = v.clone();
|
||||
|
||||
occuring_variables_expr(expr, &mut v);
|
||||
|
||||
v
|
||||
}
|
||||
|
||||
fn is_first_occurence(xs: &[Symbol], i: usize) -> bool {
|
||||
@ -613,7 +159,7 @@ fn is_first_occurence(xs: &[Symbol], i: usize) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_num_consumptions<F>(x: Symbol, ys: &[Symbol], mut consume_param_pred: F) -> usize
|
||||
fn get_num_consumptions<F>(x: Symbol, ys: &[Symbol], consume_param_pred: F) -> usize
|
||||
where
|
||||
F: Fn(usize) -> bool,
|
||||
{
|
||||
@ -627,7 +173,7 @@ where
|
||||
n
|
||||
}
|
||||
|
||||
fn is_borrow_param_help<F>(x: Symbol, ys: &[Symbol], mut consume_param_pred: F) -> bool
|
||||
fn is_borrow_param_help<F>(x: Symbol, ys: &[Symbol], consume_param_pred: F) -> bool
|
||||
where
|
||||
F: Fn(usize) -> bool,
|
||||
{
|
||||
@ -636,9 +182,13 @@ where
|
||||
.any(|(i, y)| x == *y && !consume_param_pred(i))
|
||||
}
|
||||
|
||||
fn is_borrow_param(x: Symbol, ys: &[Symbol], ps: &[Symbol]) -> bool {
|
||||
// Lean: isBorrowParamAux x ys (fun i => not (ps.get! i).borrow)
|
||||
is_borrow_param_help(x, ys, |_| true)
|
||||
fn is_borrow_param(x: Symbol, ys: &[Symbol], ps: &[Param]) -> bool {
|
||||
// default to owned arguments
|
||||
let pred = |i: usize| match ps.get(i) {
|
||||
Some(param) => !param.borrow,
|
||||
None => true,
|
||||
};
|
||||
is_borrow_param_help(x, ys, pred)
|
||||
}
|
||||
|
||||
// We do not need to consume the projection of a variable that is not consumed
|
||||
@ -652,6 +202,15 @@ fn consume_expr(m: &VarMap, e: &Expr<'_>) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
fn is_layout_refcounted(layout: &Layout<'_>) -> bool {
|
||||
use crate::layout::Builtin;
|
||||
|
||||
match layout {
|
||||
Layout::Builtin(Builtin::List(_, _)) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Context<'a> {
|
||||
pub fn new(arena: &'a Bump) -> Self {
|
||||
Self {
|
||||
@ -659,11 +218,18 @@ impl<'a> Context<'a> {
|
||||
vars: MutMap::default(),
|
||||
jp_live_vars: MutMap::default(),
|
||||
local_context: LocalContext::default(),
|
||||
function_params: MutMap::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_var_info(&self, symbol: Symbol) -> VarInfo {
|
||||
*self.vars.get(&symbol).unwrap()
|
||||
match self.vars.get(&symbol) {
|
||||
Some(info) => *info,
|
||||
None => panic!(
|
||||
"Symbol {:?} {} has no info in {:?}",
|
||||
symbol, symbol, self.vars
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn add_inc(&self, symbol: Symbol, stmt: &'a Stmt<'a>) -> &'a Stmt<'a> {
|
||||
@ -750,15 +316,15 @@ impl<'a> Context<'a> {
|
||||
if !info.reference || !is_first_occurence(xs, i) {
|
||||
// do nothing
|
||||
} else {
|
||||
let numConsuptions = get_num_consumptions(*x, xs, consume_param_pred.clone()); // number of times the argument is used
|
||||
let num_consumptions = get_num_consumptions(*x, xs, consume_param_pred.clone()); // number of times the argument is used
|
||||
let num_incs = if !info.consume || // `x` is not a variable that must be consumed by the current procedure
|
||||
live_vars_after.contains(x) || // `x` is live after executing instruction
|
||||
is_borrow_param_help( *x ,xs, consume_param_pred.clone())
|
||||
// `x` is used in a position that is passed as a borrow reference
|
||||
{
|
||||
numConsuptions
|
||||
num_consumptions
|
||||
} else {
|
||||
numConsuptions - 1
|
||||
num_consumptions - 1
|
||||
};
|
||||
|
||||
// verify that this is indeed always 1
|
||||
@ -774,11 +340,15 @@ impl<'a> Context<'a> {
|
||||
&self,
|
||||
xs: &[Symbol],
|
||||
ps: &[Param],
|
||||
mut b: &'a Stmt<'a>,
|
||||
b: &'a Stmt<'a>,
|
||||
live_vars_after: &LiveVarSet,
|
||||
) -> &'a Stmt<'a> {
|
||||
// TODO closure is actuall (fun i => not (ps.get! i).borrow)
|
||||
self.add_inc_before_help(xs, |x| true, b, live_vars_after)
|
||||
// default to owned arguments
|
||||
let pred = |i: usize| match ps.get(i) {
|
||||
Some(param) => !param.borrow,
|
||||
None => true,
|
||||
};
|
||||
self.add_inc_before_help(xs, pred, b, live_vars_after)
|
||||
}
|
||||
|
||||
fn add_dec_if_needed(
|
||||
@ -802,7 +372,7 @@ impl<'a> Context<'a> {
|
||||
fn add_dec_after_application(
|
||||
&self,
|
||||
xs: &[Symbol],
|
||||
ps: &[Symbol],
|
||||
ps: &[Param],
|
||||
mut b: &'a Stmt<'a>,
|
||||
b_live_vars: &LiveVarSet,
|
||||
) -> &'a Stmt<'a> {
|
||||
@ -823,6 +393,7 @@ impl<'a> Context<'a> {
|
||||
b
|
||||
}
|
||||
|
||||
#[allow(clippy::many_single_char_names)]
|
||||
fn visit_variable_declaration(
|
||||
&self,
|
||||
z: Symbol,
|
||||
@ -845,7 +416,13 @@ impl<'a> Context<'a> {
|
||||
),
|
||||
AccessAtIndex { structure: x, .. } => {
|
||||
let b = self.add_dec_if_needed(x, b, b_live_vars);
|
||||
let b = if self.get_var_info(x).consume {
|
||||
// NOTE deviation from Lean. I think lean assumes all structure elements live on
|
||||
// the heap. Therefore any access to a Tag/Struct element must increment its
|
||||
// refcount. But in roc, structure elements can be unboxed.
|
||||
let info_x = self.get_var_info(x);
|
||||
// let info_z = self.get_var_info(z);
|
||||
let b = if info_x.consume {
|
||||
println!("inc on {}", z);
|
||||
self.add_inc(z, b)
|
||||
} else {
|
||||
b
|
||||
@ -854,11 +431,17 @@ impl<'a> Context<'a> {
|
||||
self.arena.alloc(Stmt::Let(z, v, l, b))
|
||||
}
|
||||
|
||||
RunLowLevel(_, ys) => {
|
||||
// this is where the borrow signature would come in
|
||||
//let ps := (getDecl ctx f).params;
|
||||
let ps = &[] as &[_];
|
||||
let b = self.add_dec_after_application(ys, ps, b, b_live_vars);
|
||||
RunLowLevel(_, _) => {
|
||||
// THEORY: runlowlevel only occurs
|
||||
//
|
||||
// - in a custom hard-coded function
|
||||
// - when we insert them as compiler authors
|
||||
//
|
||||
// if we're carefule to only use RunLowLevel for non-rc'd types
|
||||
// (e.g. when building a cond/switch, we check equality on integers, and to boolean and)
|
||||
// then RunLowLevel should not change in any way the refcounts.
|
||||
|
||||
// let b = self.add_dec_after_application(ys, ps, b, b_live_vars);
|
||||
self.arena.alloc(Stmt::Let(z, v, l, b))
|
||||
}
|
||||
|
||||
@ -886,11 +469,7 @@ impl<'a> Context<'a> {
|
||||
// TODO actually make these non-constant
|
||||
|
||||
// can this type be reference-counted at runtime?
|
||||
use crate::layout::Builtin;
|
||||
let reference = match layout {
|
||||
Layout::Builtin(Builtin::List(_, _)) => true,
|
||||
_ => false,
|
||||
};
|
||||
let reference = is_layout_refcounted(layout);
|
||||
|
||||
// is this value a constant?
|
||||
let persistent = false;
|
||||
@ -913,33 +492,60 @@ impl<'a> Context<'a> {
|
||||
//def updateVarInfoWithParams (ctx : Context) (ps : Array Param) : Context :=
|
||||
//let m := ps.foldl (fun (m : VarMap) p => m.insert p.x { ref := p.ty.isObj, consume := !p.borrow }) ctx.varMap;
|
||||
//{ ctx with varMap := m }
|
||||
todo!()
|
||||
let mut ctx = self.clone();
|
||||
|
||||
for p in ps.iter() {
|
||||
let info = VarInfo {
|
||||
reference: is_layout_refcounted(&p.layout),
|
||||
consume: !p.borrow,
|
||||
persistent: false,
|
||||
};
|
||||
ctx.vars.insert(p.symbol, info);
|
||||
}
|
||||
|
||||
ctx
|
||||
}
|
||||
|
||||
/* Add `dec` instructions for parameters that are references, are not alive in `b`, and are not borrow.
|
||||
That is, we must make sure these parameters are consumed. */
|
||||
fn add_dec_for_dead_params(
|
||||
&self,
|
||||
ps: &[Symbol],
|
||||
b: &'a Stmt<'a>,
|
||||
ps: &[Param<'a>],
|
||||
mut b: &'a Stmt<'a>,
|
||||
b_live_vars: &LiveVarSet,
|
||||
) -> &'a Stmt<'a> {
|
||||
/*
|
||||
for p in ps.iter() {
|
||||
if !p.borrow && is_layout_refcounted(&p.layout) && !b_live_vars.contains(&p.symbol) {
|
||||
b = self.add_dec(p.symbol, b)
|
||||
}
|
||||
}
|
||||
|
||||
ps.foldl
|
||||
(fun b p => if !p.borrow && p.ty.isObj && !bLiveVars.contains p.x then addDec ctx p.x b else b)
|
||||
b
|
||||
*/
|
||||
todo!()
|
||||
b
|
||||
}
|
||||
|
||||
fn visit_stmt(&self, stmt: &'a Stmt<'a>) -> (&'a Stmt<'a>, LiveVarSet) {
|
||||
fn add_dec_for_alt(
|
||||
&self,
|
||||
case_live_vars: &LiveVarSet,
|
||||
alt_live_vars: &LiveVarSet,
|
||||
mut b: &'a Stmt<'a>,
|
||||
) -> &'a Stmt<'a> {
|
||||
for x in case_live_vars.iter() {
|
||||
if !alt_live_vars.contains(x) && self.must_consume(*x) {
|
||||
b = self.add_dec(*x, b);
|
||||
}
|
||||
}
|
||||
|
||||
b
|
||||
}
|
||||
|
||||
// TODO should not be pub
|
||||
pub fn visit_stmt(&self, stmt: &'a Stmt<'a>) -> (&'a Stmt<'a>, LiveVarSet) {
|
||||
use Stmt::*;
|
||||
|
||||
match stmt {
|
||||
Let(symbol, expr, layout, cont) => {
|
||||
let ctx = self.update_var_info(*symbol, layout, expr);
|
||||
let (b, b_live_vars) = self.visit_stmt(cont);
|
||||
let (b, b_live_vars) = ctx.visit_stmt(cont);
|
||||
ctx.visit_variable_declaration(
|
||||
*symbol,
|
||||
expr.clone(),
|
||||
@ -951,12 +557,14 @@ impl<'a> Context<'a> {
|
||||
|
||||
Join {
|
||||
id: j,
|
||||
parameters,
|
||||
remainder: v,
|
||||
continuation: b,
|
||||
parameters: xs,
|
||||
remainder: b,
|
||||
continuation: v,
|
||||
} => {
|
||||
let xs = Vec::from_iter_in(parameters.iter().map(|p| p.symbol), self.arena)
|
||||
.into_bump_slice();
|
||||
let xs = *xs;
|
||||
|
||||
let v_orig = v;
|
||||
|
||||
let (v, v_live_vars) = {
|
||||
let ctx = self.update_var_info_with_params(xs);
|
||||
ctx.visit_stmt(v)
|
||||
@ -964,16 +572,20 @@ impl<'a> Context<'a> {
|
||||
|
||||
let v = self.add_dec_for_dead_params(xs, v, &v_live_vars);
|
||||
let mut ctx = self.clone();
|
||||
ctx.local_context.join_points.insert(*j, (xs, v));
|
||||
|
||||
// NOTE deviation from lean, insert into local context
|
||||
ctx.local_context.join_points.insert(*j, (xs, v_orig));
|
||||
|
||||
update_jp_live_vars(*j, xs, v, &mut ctx.jp_live_vars);
|
||||
|
||||
let (b, b_live_vars) = ctx.visit_stmt(b);
|
||||
|
||||
(
|
||||
ctx.arena.alloc(Join {
|
||||
id: *j,
|
||||
parameters,
|
||||
remainder: v,
|
||||
continuation: b,
|
||||
parameters: xs,
|
||||
remainder: b,
|
||||
continuation: v,
|
||||
}),
|
||||
b_live_vars,
|
||||
)
|
||||
@ -1001,16 +613,89 @@ impl<'a> Context<'a> {
|
||||
let ps = self.local_context.join_points.get(j).unwrap().0;
|
||||
let b = self.add_inc_before(xs, ps, stmt, j_live_vars);
|
||||
|
||||
let mut b_live_vars = MutSet::default();
|
||||
crate::inc_dec::live_vars::collect_live_vars(
|
||||
b,
|
||||
&self.jp_live_vars,
|
||||
&mut b_live_vars,
|
||||
);
|
||||
let b_live_vars = collect_stmt(b, &self.jp_live_vars, MutSet::default());
|
||||
|
||||
(b, b_live_vars)
|
||||
}
|
||||
|
||||
Cond {
|
||||
pass,
|
||||
fail,
|
||||
cond_symbol,
|
||||
cond_layout,
|
||||
branching_symbol,
|
||||
branching_layout,
|
||||
ret_layout,
|
||||
} => {
|
||||
let case_live_vars = collect_stmt(stmt, &self.jp_live_vars, MutSet::default());
|
||||
|
||||
let pass = {
|
||||
// TODO should we use ctor info like Lean?
|
||||
let ctx = self.clone();
|
||||
let (b, alt_live_vars) = ctx.visit_stmt(pass);
|
||||
ctx.add_dec_for_alt(&case_live_vars, &alt_live_vars, b)
|
||||
};
|
||||
|
||||
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)
|
||||
};
|
||||
|
||||
let cond = self.arena.alloc(Cond {
|
||||
cond_symbol: *cond_symbol,
|
||||
cond_layout: cond_layout.clone(),
|
||||
branching_symbol: *branching_symbol,
|
||||
branching_layout: branching_layout.clone(),
|
||||
pass,
|
||||
fail,
|
||||
ret_layout: ret_layout.clone(),
|
||||
});
|
||||
|
||||
(cond, case_live_vars)
|
||||
}
|
||||
|
||||
Switch {
|
||||
cond_symbol,
|
||||
cond_layout,
|
||||
branches,
|
||||
default_branch,
|
||||
ret_layout,
|
||||
} => {
|
||||
let case_live_vars = collect_stmt(stmt, &self.jp_live_vars, MutSet::default());
|
||||
|
||||
let branches = Vec::from_iter_in(
|
||||
branches.iter().map(|(label, branch)| {
|
||||
// TODO should we use ctor info like Lean?
|
||||
let ctx = self.clone();
|
||||
let (b, alt_live_vars) = ctx.visit_stmt(branch);
|
||||
let b = ctx.add_dec_for_alt(&case_live_vars, &alt_live_vars, b);
|
||||
|
||||
(*label, b.clone())
|
||||
}),
|
||||
self.arena,
|
||||
)
|
||||
.into_bump_slice();
|
||||
|
||||
let default_branch = {
|
||||
// TODO should we use ctor info like Lean?
|
||||
let ctx = self.clone();
|
||||
let (b, alt_live_vars) = ctx.visit_stmt(default_branch);
|
||||
ctx.add_dec_for_alt(&case_live_vars, &alt_live_vars, b)
|
||||
};
|
||||
|
||||
let switch = self.arena.alloc(Switch {
|
||||
cond_symbol: *cond_symbol,
|
||||
branches,
|
||||
default_branch,
|
||||
cond_layout: cond_layout.clone(),
|
||||
ret_layout: ret_layout.clone(),
|
||||
});
|
||||
|
||||
(switch, case_live_vars)
|
||||
}
|
||||
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
@ -1018,5 +703,111 @@ impl<'a> Context<'a> {
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
struct LocalContext<'a> {
|
||||
join_points: MutMap<JoinPointId, (&'a [Symbol], &'a Stmt<'a>)>,
|
||||
join_points: MutMap<JoinPointId, (&'a [Param<'a>], &'a Stmt<'a>)>,
|
||||
}
|
||||
|
||||
pub fn collect_stmt(
|
||||
stmt: &Stmt<'_>,
|
||||
jp_live_vars: &JPLiveVarMap,
|
||||
mut vars: LiveVarSet,
|
||||
) -> LiveVarSet {
|
||||
use Stmt::*;
|
||||
|
||||
match stmt {
|
||||
Let(symbol, expr, _, cont) => {
|
||||
vars = collect_stmt(cont, jp_live_vars, vars);
|
||||
vars.remove(symbol);
|
||||
let mut result = MutSet::default();
|
||||
occuring_variables_expr(expr, &mut result);
|
||||
vars.extend(result);
|
||||
|
||||
vars
|
||||
}
|
||||
Ret(symbol) => {
|
||||
vars.insert(*symbol);
|
||||
vars
|
||||
}
|
||||
|
||||
Inc(symbol, cont) | Dec(symbol, cont) => {
|
||||
vars.insert(*symbol);
|
||||
collect_stmt(cont, jp_live_vars, vars)
|
||||
}
|
||||
|
||||
Jump(_, arguments) => {
|
||||
vars.extend(arguments.iter().copied());
|
||||
vars
|
||||
}
|
||||
|
||||
Join {
|
||||
id: j,
|
||||
parameters,
|
||||
remainder: b,
|
||||
continuation: v,
|
||||
} => {
|
||||
let mut j_live_vars = collect_stmt(v, jp_live_vars, MutSet::default());
|
||||
for param in parameters.iter() {
|
||||
j_live_vars.remove(¶m.symbol);
|
||||
}
|
||||
|
||||
let mut jp_live_vars = jp_live_vars.clone();
|
||||
jp_live_vars.insert(*j, j_live_vars);
|
||||
|
||||
collect_stmt(b, &jp_live_vars, vars)
|
||||
}
|
||||
|
||||
Switch {
|
||||
cond_symbol,
|
||||
branches,
|
||||
default_branch,
|
||||
..
|
||||
} => {
|
||||
vars.insert(*cond_symbol);
|
||||
|
||||
for (_, branch) in branches.iter() {
|
||||
vars.extend(collect_stmt(branch, jp_live_vars, vars.clone()));
|
||||
}
|
||||
|
||||
vars.extend(collect_stmt(default_branch, jp_live_vars, vars.clone()));
|
||||
|
||||
vars
|
||||
}
|
||||
|
||||
Cond {
|
||||
cond_symbol,
|
||||
branching_symbol,
|
||||
pass,
|
||||
fail,
|
||||
..
|
||||
} => {
|
||||
vars.insert(*cond_symbol);
|
||||
vars.insert(*branching_symbol);
|
||||
|
||||
vars.extend(collect_stmt(pass, jp_live_vars, vars.clone()));
|
||||
vars.extend(collect_stmt(fail, jp_live_vars, vars.clone()));
|
||||
|
||||
vars
|
||||
}
|
||||
|
||||
RuntimeError(_) => vars,
|
||||
}
|
||||
}
|
||||
|
||||
fn update_jp_live_vars(j: JoinPointId, ys: &[Param], v: &Stmt<'_>, m: &mut JPLiveVarMap) {
|
||||
let j_live_vars = MutSet::default();
|
||||
let mut j_live_vars = collect_stmt(v, m, j_live_vars);
|
||||
|
||||
for param in ys {
|
||||
j_live_vars.remove(¶m.symbol);
|
||||
}
|
||||
|
||||
m.insert(j, j_live_vars);
|
||||
}
|
||||
|
||||
pub fn visit_declaration<'a>(arena: &'a Bump, stmt: &'a Stmt<'a>) -> &'a Stmt<'a> {
|
||||
let ctx = Context::new(arena);
|
||||
|
||||
let params = &[] as &[_];
|
||||
let ctx = ctx.update_var_info_with_params(params);
|
||||
let (b, b_live_vars) = ctx.visit_stmt(stmt);
|
||||
ctx.add_dec_for_dead_params(params, b, &b_live_vars)
|
||||
}
|
||||
|
@ -1288,14 +1288,31 @@ pub fn with_hole<'a>(
|
||||
.from_var(env.arena, cond_var, env.subs, env.pointer_size)
|
||||
.expect("invalid cond_layout");
|
||||
|
||||
let assigned_in_jump = env.unique_symbol();
|
||||
let id = JoinPointId(env.unique_symbol());
|
||||
let jump = env.arena.alloc(Stmt::Jump(id, env.arena.alloc([assigned])));
|
||||
let jump = env
|
||||
.arena
|
||||
.alloc(Stmt::Jump(id, env.arena.alloc([assigned_in_jump])));
|
||||
|
||||
let mut stmt = with_hole(env, final_else.value, procs, layout_cache, assigned, jump);
|
||||
let mut stmt = with_hole(
|
||||
env,
|
||||
final_else.value,
|
||||
procs,
|
||||
layout_cache,
|
||||
assigned_in_jump,
|
||||
jump,
|
||||
);
|
||||
|
||||
for (loc_cond, loc_then) in branches.into_iter().rev() {
|
||||
let branching_symbol = env.unique_symbol();
|
||||
let then = with_hole(env, loc_then.value, procs, layout_cache, assigned, jump);
|
||||
let then = with_hole(
|
||||
env,
|
||||
loc_then.value,
|
||||
procs,
|
||||
layout_cache,
|
||||
assigned_in_jump,
|
||||
jump,
|
||||
);
|
||||
|
||||
stmt = Stmt::Cond {
|
||||
cond_symbol: branching_symbol,
|
||||
|
@ -57,11 +57,8 @@ mod test_mono {
|
||||
roc_mono::ir::from_can(&mut mono_env, loc_expr.value, &mut procs, &mut layout_cache);
|
||||
|
||||
// apply inc/dec
|
||||
use roc_collections::all::MutMap;
|
||||
use roc_mono::inc_dec::function_c;
|
||||
let mut inc_dec_env = roc_mono::inc_dec::Env::new(mono_env.arena);
|
||||
|
||||
let ir_expr = function_c(&mut inc_dec_env, mono_env.arena.alloc(ir_expr));
|
||||
let stmt = mono_env.arena.alloc(ir_expr);
|
||||
let ir_expr = roc_mono::inc_dec::visit_declaration(mono_env.arena, stmt);
|
||||
|
||||
// let mono_expr = Expr::new(&mut mono_env, loc_expr.value, &mut procs);
|
||||
let procs = roc_mono::ir::specialize_all(&mut mono_env, procs, &mut LayoutCache::default());
|
||||
@ -675,6 +672,31 @@ mod test_mono {
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_if() {
|
||||
compiles_to_ir(
|
||||
r#"
|
||||
if True then
|
||||
1
|
||||
else
|
||||
2
|
||||
"#,
|
||||
indoc!(
|
||||
r#"
|
||||
let Test.3 = true;
|
||||
if Test.3 then
|
||||
let Test.1 = 1i64;
|
||||
jump Test.2 Test.1;
|
||||
else
|
||||
let Test.1 = 2i64;
|
||||
jump Test.2 Test.1;
|
||||
joinpoint Test.2 Test.0:
|
||||
ret Test.0;
|
||||
"#
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn if_multi_branch() {
|
||||
compiles_to_ir(
|
||||
@ -688,21 +710,21 @@ mod test_mono {
|
||||
"#,
|
||||
indoc!(
|
||||
r#"
|
||||
let Test.4 = true;
|
||||
if Test.4 then
|
||||
let Test.0 = 1i64;
|
||||
jump Test.1 Test.0;
|
||||
let Test.6 = true;
|
||||
if Test.6 then
|
||||
let Test.1 = 1i64;
|
||||
jump Test.2 Test.1;
|
||||
else
|
||||
let Test.3 = false;
|
||||
if Test.3 then
|
||||
let Test.0 = 2i64;
|
||||
jump Test.2 Test.0;
|
||||
let Test.5 = false;
|
||||
if Test.5 then
|
||||
let Test.3 = 2i64;
|
||||
jump Test.4 Test.3;
|
||||
else
|
||||
let Test.0 = 3i64;
|
||||
jump Test.2 Test.0;
|
||||
joinpoint Test.2 Test.0:
|
||||
jump Test.1 Test.0;
|
||||
joinpoint Test.1 Test.0:
|
||||
let Test.3 = 3i64;
|
||||
jump Test.4 Test.3;
|
||||
joinpoint Test.4 Test.1:
|
||||
jump Test.2 Test.1;
|
||||
joinpoint Test.2 Test.0:
|
||||
ret Test.0;
|
||||
"#
|
||||
),
|
||||
|
Loading…
Reference in New Issue
Block a user