start right to left evaluation with environment

This commit is contained in:
J.Teeuwissen 2023-02-08 15:01:35 +01:00
parent 1fa96257ed
commit ae499137e4
No known key found for this signature in database
GPG key ID: DB5F7A1ED8D478AD

View file

@ -5,7 +5,8 @@ use roc_collections::{all::WyHash, MutMap, MutSet};
use roc_module::symbol::{IdentIds, ModuleId, Symbol}; use roc_module::symbol::{IdentIds, ModuleId, Symbol};
use crate::{ use crate::{
ir::{Call, CallType, Expr, Proc, ProcLayout, Stmt, UpdateModeIds}, borrow::Ownership,
ir::{Call, CallType, Expr, ModifyRc, Proc, ProcLayout, Stmt, UpdateModeIds},
layout::STLayoutInterner, layout::STLayoutInterner,
}; };
@ -18,12 +19,15 @@ pub fn insert_refcount_operations<'a>(
procedures: &mut HashMap<(Symbol, ProcLayout), Proc<'a>, BuildHasherDefault<WyHash>>, procedures: &mut HashMap<(Symbol, ProcLayout), Proc<'a>, BuildHasherDefault<WyHash>>,
) -> () { ) -> () {
for (_, proc) in procedures.iter_mut() { for (_, proc) in procedures.iter_mut() {
// TODO use params to fill environment. Probably should all be owned... let mut initial_environment = Environment {
let initial_environment = Environment { // All parameters are owned.
borrowed: MutSet::default(), variables_ownership: proc
owned: MutSet::default(), .args
.iter()
.map(|(_layout, symbol)| (*symbol, Ownership::Owned))
.collect(),
}; };
let new_body = insert_refcount_operations_stmt(arena, initial_environment, &proc.body); let new_body = insert_refcount_operations_stmt(arena, &mut initial_environment, &proc.body);
proc.body = new_body.clone(); proc.body = new_body.clone();
} }
} }
@ -34,10 +38,25 @@ type VariableMap = MutMap<Symbol, VarType>;
type Variables = MutSet<Symbol>; type Variables = MutSet<Symbol>;
type VariablesOwnership = MutMap<Symbol, Ownership>;
// A map keeping track of how many times a variable is used.
type VariableUsage = MutMap<Symbol, u64>;
fn insert_variable_usage(usage: &mut VariableUsage, symbol: Symbol) {
match usage.get(&symbol) {
Some(count) => usage.insert(symbol, count + 1),
None => usage.insert(symbol, 1),
};
}
fn insert_variable_usages(usage: &mut VariableUsage, symbols: impl Iterator<Item = Symbol>) {
symbols.for_each(|symbol| insert_variable_usage(usage, symbol));
}
struct Environment { struct Environment {
// The Koka implementation assumes everything that is not owned to be borrowed. // The Koka implementation assumes everything that is not owned to be borrowed.
borrowed: Variables, variables_ownership: VariablesOwnership,
owned: Variables,
} }
struct Context { struct Context {
@ -49,7 +68,7 @@ struct Context {
// e.g. stack allocated values should not be reference counted. // e.g. stack allocated values should not be reference counted.
fn insert_refcount_operations_stmt<'a>( fn insert_refcount_operations_stmt<'a>(
arena: &'a Bump, arena: &'a Bump,
environment: Environment, environment: &mut Environment,
stmt: &Stmt<'a>, stmt: &Stmt<'a>,
) -> &'a Stmt<'a> { ) -> &'a Stmt<'a> {
// TODO: Deal with potentially stack overflowing let chains with an explicit loop. // TODO: Deal with potentially stack overflowing let chains with an explicit loop.
@ -61,42 +80,23 @@ fn insert_refcount_operations_stmt<'a>(
// TODO take into account that the bound variable might not be free in the continuation. // TODO take into account that the bound variable might not be free in the continuation.
// And as such, we can drop the value before continuing. // And as such, we can drop the value before continuing.
// TODO this is the implementation according to the paper. // INFO The Koka implementation (instead of calculating the owned environment beforehand)
// But the Koka implementation (instead of calculating the owned environment beforehand)
// First evaluates the continuation with the owned environment, setting the variables to dead (not alive). // First evaluates the continuation with the owned environment, setting the variables to dead (not alive).
// And in the rest of the code, the dead variables are treated as borrowed (because not alive). // And in the rest of the code, the dead variables are treated as borrowed (because not alive).
let mut stmt_owned: Variables = {
let mut free_variables = Stmt::free_variables(&stmt);
// remove the bound variable as it should be owned by the expression. // First evalute the continuation and let it consume it's free variables.
free_variables.remove(binding); let new_stmt = insert_refcount_operations_stmt(arena, environment, stmt);
// intersect with the given owned variables as some of free variables might be borrowed. // Then evaluate the bound expression. where the free variables from the continuation are borrowed.
environment let mut usage = VariableUsage::default();
.owned get_variable_usage_expr(arena, &mut usage, expr);
.intersection(&free_variables)
.copied()
.collect()
};
let expr_environment = Environment { // Can this clone be avoided?
borrowed: environment.borrowed.union(&stmt_owned).copied().collect(), let new_let = arena.alloc(Stmt::Let(*binding, expr.clone(), *layout, new_stmt));
owned: environment.owned.difference(&stmt_owned).copied().collect(),
};
let new_expr = insert_refcount_operations_expr(arena, expr_environment, expr); // Insert the reference count operations for the variables used in the expression.
let new_let_with_refcount = insert_refcount_stmt(arena, environment, usage, new_let);
let stmt_environment = Environment { new_let_with_refcount
borrowed: environment.borrowed,
owned: {
stmt_owned.insert(*binding);
stmt_owned
},
};
let new_stmt = insert_refcount_operations_stmt(arena, stmt_environment, stmt);
arena.alloc(Stmt::Let(*binding, new_expr, *layout, new_stmt))
} }
Stmt::Switch { Stmt::Switch {
cond_symbol, cond_symbol,
@ -123,9 +123,87 @@ fn insert_refcount_operations_stmt<'a>(
} }
} }
fn insert_refcount_stmt<'a>(
arena: &'a Bump,
environment: &mut Environment,
mut usage: VariableUsage,
continuation: &'a Stmt<'a>,
) -> &'a Stmt<'a> {
usage.iter_mut().for_each(|(symbol, mut usage_count)| {
match consume_variable(environment, *symbol) {
// If the variable is borrowed, we need to increment the reference count for each usage.
Ownership::Borrowed => {}
// If the variable is owned, we need to increment the reference count for each usage except one.
Ownership::Owned => *usage_count -= 1,
}
});
usage
.iter()
.fold(continuation, |continuation, (symbol, usage_count)| {
arena.alloc(Stmt::Refcounting(
ModifyRc::Inc(*symbol, *usage_count),
continuation,
))
})
}
fn get_variable_usage_expr<'a>(arena: &Bump, usage: &mut VariableUsage, expr: &Expr<'a>) {
match expr {
Expr::Literal(_) => {
// Literals are not reference counted.
}
Expr::Call(call) => {
insert_variable_usages(usage, call.arguments.iter().copied());
}
Expr::Tag {
tag_layout,
tag_id,
arguments,
} => todo!(),
Expr::Struct(arguments) => {
insert_variable_usages(usage, arguments.iter().copied());
}
Expr::StructAtIndex {
index,
field_layouts,
structure,
} => todo!(),
Expr::GetTagId {
structure,
union_layout,
} => {
// The arguments to get tag id are not reference counted.
}
Expr::UnionAtIndex {
structure,
tag_id,
union_layout,
index,
} => todo!(),
Expr::Array { elem_layout, elems } => todo!(),
Expr::EmptyArray => todo!(),
Expr::ExprBox { symbol } => todo!(),
Expr::ExprUnbox { symbol } => todo!(),
Expr::Reuse {
symbol,
update_tag_id,
update_mode,
tag_layout,
tag_id,
arguments,
} => todo!(),
Expr::Reset {
symbol,
update_mode,
} => todo!(),
Expr::RuntimeErrorFunction(_) => todo!(),
}
}
fn insert_refcount_operations_expr<'a>( fn insert_refcount_operations_expr<'a>(
arena: &Bump, arena: &Bump,
environment: Environment, environment: &mut Environment,
expr: &Expr<'a>, expr: &Expr<'a>,
) -> Expr<'a> { ) -> Expr<'a> {
match &expr { match &expr {
@ -133,12 +211,23 @@ fn insert_refcount_operations_expr<'a>(
return Expr::Literal(lit.clone()); return Expr::Literal(lit.clone());
todo!() todo!()
} }
Expr::Call(call) => Expr::Call(insert_refcount_operations_expr_fun(call)), Expr::Call(call) => Expr::Call(insert_refcount_operations_expr_call(
arena,
environment,
call,
)),
Expr::Tag { Expr::Tag {
tag_layout, tag_layout,
tag_id, tag_id,
arguments, arguments,
} => todo!(), } => {
return Expr::Tag {
tag_layout: *tag_layout,
tag_id: *tag_id,
arguments: arguments.clone(),
};
todo!()
}
Expr::Struct(s) => { Expr::Struct(s) => {
return Expr::Struct(s); return Expr::Struct(s);
todo!() todo!()
@ -192,8 +281,14 @@ fn insert_refcount_operations_expr<'a>(
} }
} }
fn insert_refcount_operations_expr_fun<'a>(call: &Call<'a>) -> Call<'a> { // When performing a function call, we pass the parameters as owned.
// TODO Koka makes a distinction between top level and non top level definitions. // This means that if we have these parameters in the borrowed environment, we need to increment the reference count.
fn insert_refcount_operations_expr_call<'a>(
arena: &Bump,
environment: &mut Environment,
call: &Call<'a>,
) -> Call<'a> {
// INFO Koka makes a distinction between top level and non top level definitions.
match &call.call_type { match &call.call_type {
CallType::ByName { CallType::ByName {
name, name,
@ -207,31 +302,48 @@ fn insert_refcount_operations_expr_fun<'a>(call: &Call<'a>) -> Call<'a> {
CallType::Foreign { CallType::Foreign {
foreign_symbol, foreign_symbol,
ret_layout, ret_layout,
} => todo!(), } => {
return call.clone();
todo!()
}
// E.g. lowlevel eq
CallType::LowLevel { op, update_mode } => { CallType::LowLevel { op, update_mode } => {
return call.clone(); return call.clone();
todo!() todo!()
} }
CallType::HigherOrder(_) => todo!(), CallType::HigherOrder(_) => {
} return call.clone();
}
trait FreeVariables {
fn free_variables(&self) -> Variables;
}
impl<'a> FreeVariables for Stmt<'a> {
// Return the set of variables that are free in the statement.
fn free_variables(&self) -> Variables {
return MutSet::default();
todo!() todo!()
} }
}
impl<'a> FreeVariables for Expr<'a> {
// Return the set of variables that are free in the expression.
fn free_variables(&self) -> Variables {
return MutSet::default();
todo!()
} }
} }
// Retrieve whether the variable is owned or borrowed.
// If it was owned, set it to borrowed.
fn consume_variable<'a>(environment: &mut Environment, variable: Symbol) -> Ownership {
// Consume the variable by setting it to borrowed (if it was owned before), and return the previous ownership.
environment
.variables_ownership
.insert(variable, Ownership::Borrowed)
.expect("Expected variable to be in environment")
}
// trait FreeVariables {
// fn free_variables(&self) -> Variables;
// }
// impl<'a> FreeVariables for Stmt<'a> {
// // Return the set of variables that are free in the statement.
// fn free_variables(&self) -> Variables {
// return MutSet::default();
// todo!()
// }
// }
// impl<'a> FreeVariables for Expr<'a> {
// // Return the set of variables that are free in the expression.
// fn free_variables(&self) -> Variables {
// return MutSet::default();
// todo!()
// }
// }