Comments and cleanup

This commit is contained in:
J.Teeuwissen 2023-02-18 14:35:24 +01:00
parent 8721ae73e8
commit 09809b9bd6
No known key found for this signature in database
GPG key ID: DB5F7A1ED8D478AD

View file

@ -7,10 +7,13 @@ use roc_module::symbol::{IdentIds, ModuleId, Symbol};
use crate::{
borrow::Ownership,
ir::{BranchInfo, Call, CallType, Expr, ModifyRc, Proc, ProcLayout, Stmt, UpdateModeIds},
ir::{BranchInfo, Expr, ListLiteralElement, ModifyRc, Proc, ProcLayout, Stmt, UpdateModeIds},
layout::{InLayout, LayoutInterner, STLayoutInterner},
};
/**
Insert the reference count operations for procedures.
*/
pub fn insert_refcount_operations<'a>(
arena: &'a Bump,
layout_interner: &STLayoutInterner,
@ -20,14 +23,13 @@ pub fn insert_refcount_operations<'a>(
procedures: &mut HashMap<(Symbol, ProcLayout), Proc<'a>, BuildHasherDefault<WyHash>>,
) -> () {
// Create a VariableRcTypesEnv for the procedures as they get referenced but should be marked as non reference counted.
let proc_rc_type = procedures.keys().map(|(symbol, _layout)| *symbol);
let variable_rc_types_env =
VariableRcTypesEnv::from_proc_symbols(layout_interner, proc_rc_type);
let mut variable_rc_types_env = VariableRcTypesEnv::from_layout_interner(layout_interner);
variable_rc_types_env.insert_proc_symbols(procedures.keys().map(|(symbol, _layout)| *symbol));
for (_, proc) in procedures.iter_mut() {
// Clone the variable_rc_types_env and insert the variables in the current procedure.
// As the variables should be limited in scope for the current proc.
let variable_rc_types_env = &mut variable_rc_types_env.clone();
let mut variable_rc_types_env = variable_rc_types_env.clone();
variable_rc_types_env.insert_variables_rc_type_proc(&proc);
let mut initial_environment = Environment {
@ -56,6 +58,9 @@ type VariableRcTypes = MutMap<Symbol, VarRcType>;
type FreeRcVariables = MutSet<Symbol>;
/**
Environment to keep track which of the variables should be reference counted and which ones should not.
*/
struct VariableRcTypesEnv<'a> {
// A map keeping track of which variables are reference counted and which are not.
variables_rc_type: VariableRcTypes,
@ -65,41 +70,66 @@ struct VariableRcTypesEnv<'a> {
// TODO what would be a good way to structure a similar pattern? creating env, evaluating multiple different objects and returning an element from the env.
impl<'a> VariableRcTypesEnv<'a> {
fn from_proc_symbols(
layout_interner: &'a STLayoutInterner,
proc_symbols: impl Iterator<Item = Symbol>,
) -> VariableRcTypesEnv<'a> {
/**
Create a new VariableRcTypesEnv from a layout interner.
*/
fn from_layout_interner(layout_interner: &'a STLayoutInterner) -> VariableRcTypesEnv<'a> {
VariableRcTypesEnv {
variables_rc_type: proc_symbols
.map(|symbol| (symbol, VarRcType::NotReferenceCounted))
.collect(),
variables_rc_type: VariableRcTypes::default(),
layout_interner,
}
}
/**
Insert the reference count type of top level functions.
As functions are not reference counted, they can be marked as such.
*/
fn insert_proc_symbols(
self: &mut VariableRcTypesEnv<'a>,
proc_symbols: impl Iterator<Item = Symbol>,
) {
for proc_symbol in proc_symbols {
self.variables_rc_type
.insert(proc_symbol, VarRcType::NotReferenceCounted);
}
}
// Get the reference count types of all variables in a procedure.
/**
Insert the reference count types of all variables in a procedure.
*/
fn insert_variables_rc_type_proc(self: &mut VariableRcTypesEnv<'a>, proc: &Proc<'a>) {
// First collect the argument types.
for (layout, symbol) in proc.args.iter() {
self.insert_symbol_layout_rc_type(symbol, layout);
}
// Then collect the types of the variables in the body.
self.insert_variables_rc_type_stmt(&proc.body);
}
/**
Insert the reference count types of all variables in a statement.
*/
fn insert_variables_rc_type_stmt(self: &mut VariableRcTypesEnv<'a>, stmt: &Stmt<'a>) {
match stmt {
Stmt::Let(binding, _expr, layout, stmt) => {
Stmt::Let(
binding,
// Expressions can be omitted, as they won't create new variables.
_expr,
layout,
continuation,
) => {
self.insert_symbol_layout_rc_type(&binding, layout);
self.insert_variables_rc_type_stmt(stmt);
self.insert_variables_rc_type_stmt(continuation);
}
Stmt::Switch {
// TODO what is this symbol for and should it be handled?
cond_symbol,
cond_layout,
// The switch condition is an integer and thus not reference counted.
cond_symbol: _,
cond_layout: _,
branches,
default_branch,
ret_layout: _,
} => {
// Collect the types of the variables in all the branches, including the default one.
for (info, stmt) in branches
.iter()
.map(|(_branch, info, stmt)| (info, stmt))
@ -119,8 +149,12 @@ impl<'a> VariableRcTypesEnv<'a> {
self.insert_variables_rc_type_stmt(stmt);
}
}
Stmt::Ret(_) => {}
Stmt::Refcounting(_, _) => {}
Stmt::Ret(_symbol) => {
// The return does not introduce new variables.
}
Stmt::Refcounting(_, _) => unreachable!(
"Refcounting operations should not be present in the AST at this point."
),
Stmt::Expect {
condition,
region,
@ -128,6 +162,7 @@ impl<'a> VariableRcTypesEnv<'a> {
variables,
remainder,
} => {
//TODO deal with other variables in the expect.
self.insert_variables_rc_type_stmt(remainder);
}
Stmt::ExpectFx {
@ -137,6 +172,7 @@ impl<'a> VariableRcTypesEnv<'a> {
variables,
remainder,
} => {
//TODO deal with other variables in the expect.
self.insert_variables_rc_type_stmt(remainder);
}
Stmt::Dbg {
@ -144,19 +180,35 @@ impl<'a> VariableRcTypesEnv<'a> {
variable,
remainder,
} => {
//TODO deal with other variables in the expect.
self.insert_variables_rc_type_stmt(remainder);
}
Stmt::Join {
id,
id: _,
parameters,
body,
remainder,
} => todo!(),
Stmt::Jump(_, _) => todo!(),
Stmt::Crash(_, _) => todo!(),
remainder: continuation,
} => {
// TODO do we need to do anything with the parameter ownership parameter?
for parameter in parameters.iter() {
self.insert_symbol_layout_rc_type(&parameter.symbol, &parameter.layout);
}
self.insert_variables_rc_type_stmt(body);
self.insert_variables_rc_type_stmt(continuation);
}
Stmt::Jump(_, _) => {
// A join point does not introduce new variables.
}
Stmt::Crash(_, _) => {
// A crash does not introduce new variables.
}
}
}
/*
Insert the reference count type of a symbol given its layout.
*/
fn insert_symbol_layout_rc_type(
self: &mut VariableRcTypesEnv<'a>,
symbol: &Symbol,
@ -201,6 +253,9 @@ fn combine_variable_usage(
}
}
/**
A struct to determine the usage of variables in an expression.
*/
struct VariableUsageEnv<'a> {
variable_usage: VariableUsage,
@ -208,10 +263,13 @@ struct VariableUsageEnv<'a> {
}
impl<'a> VariableUsageEnv<'a> {
/**
Retrieve how much a variable is used in an expression.
*/
fn get_reference_counted_variable_usage_expr(
variable_rc_types: &VariableRcTypes,
expr: &Expr<'a>,
) -> HashMap<Symbol, u64, BuildHasherDefault<WyHash>> {
) -> VariableUsage {
let mut usage_env = VariableUsageEnv {
variable_usage: VariableUsage::default(),
variable_rc_types,
@ -228,8 +286,8 @@ impl<'a> VariableUsageEnv<'a> {
usage_env.insert_variable_usages(arguments.iter().copied());
}
Expr::StructAtIndex {
index,
field_layouts,
index: _,
field_layouts: _,
structure,
} => {
usage_env.insert_variable_usage(*structure);
@ -246,30 +304,41 @@ impl<'a> VariableUsageEnv<'a> {
union_layout,
index,
} => todo!(),
Expr::Array { elem_layout, elems } => todo!(),
Expr::EmptyArray => todo!(),
Expr::Array {
elem_layout: _,
elems,
} => {
// For an array creation, we insert all the used elements.
usage_env.insert_variable_usages(elems.iter().filter_map(
|element| match element {
// Literal elements are not reference counted.
ListLiteralElement::Literal(_) => None,
// Symbol elements are reference counted.
ListLiteralElement::Symbol(symbol) => Some(*symbol),
},
));
}
Expr::EmptyArray => {
// Empty arrays have no reference counted elements.
}
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::Reuse { .. } | Expr::Reset { .. } => {
unreachable!("Reset and reuse should not exist at this point")
}
Expr::RuntimeErrorFunction(_) => todo!(),
}
usage_env.variable_usage
}
/**
Insert the usage of a symbol into the env.
If the symbol is not reference counted, it will be ignored.
*/
fn insert_variable_usage(self: &mut VariableUsageEnv<'a>, symbol: Symbol) {
match {
// TODO convert this to a oneliner once panics do not occur anymore.
let this = self.variable_rc_types.get(&symbol);
match this {
Some(val) => val,
@ -288,6 +357,9 @@ impl<'a> VariableUsageEnv<'a> {
}
}
/**
Call insert_variable_usage for multiple symbols.
*/
fn insert_variable_usages(
self: &mut VariableUsageEnv<'a>,
symbols: impl Iterator<Item = Symbol>,
@ -296,6 +368,10 @@ impl<'a> VariableUsageEnv<'a> {
}
}
/**
The environment for the reference counting pass.
Contains the variable rc types and the ownership.
*/
struct Environment<'a> {
// Keep track which variables are reference counted and which are not.
variables_rc_types: &'a VariableRcTypes,
@ -313,14 +389,19 @@ impl<'a> Clone for Environment<'a> {
}
impl<'a> Environment<'a> {
/**
Retrieve the rc type of a variable.
*/
fn get_variable_rc_type(self: &mut Environment<'a>, variable: &Symbol) -> &VarRcType {
self.variables_rc_types
.get(variable)
.expect("variable should have rc type")
}
// Retrieve whether the variable is owned or borrowed.
// If it was owned, set it to borrowed.
/*
Retrieve whether the variable is owned or borrowed.
If it was owned, set it to borrowed (as it is consumed/the variable can be used as owned only once without incrementing).
*/
fn consume_variable(self: &mut Environment<'a>, variable: &Symbol) -> Ownership {
// This function should only be called on reference counted variables.
debug_assert!(matches!(
@ -337,7 +418,9 @@ impl<'a> Environment<'a> {
}
}
// Add a variable to the environment.
/*
Add a variable to the environment if it is reference counted.
*/
fn add_variable(self: &mut Environment<'a>, variable: &Symbol) {
match self.get_variable_rc_type(variable) {
VarRcType::ReferenceCounted => {
@ -349,8 +432,10 @@ impl<'a> Environment<'a> {
}
}
// Add a variable to the environment during a function call.
// Remove the variable afterwards to prevent it from being used outside the function call.
/*
Add a variable to the environment during a function call.
Remove the variable afterwards to prevent it from being used outside the function call.
*/
fn with_variable<F, R>(self: &mut Environment<'a>, variable: &Symbol, callback: F) -> R
where
F: Fn(&mut Environment) -> R,
@ -367,8 +452,9 @@ impl<'a> Environment<'a> {
}
}
// TODO take into account what values should and should not be reference counted.
// e.g. stack allocated values should not be reference counted.
/**
Given an environment, insert the reference counting operations for a statement.
*/
fn insert_refcount_operations_stmt<'a>(
arena: &'a Bump,
environment: &mut Environment,
@ -439,6 +525,7 @@ fn insert_refcount_operations_stmt<'a>(
(info.clone(), new_branch, free_variables_branch)
};
// Combine the free variables of the all branches.
let free_rc_vars = {
let mut free_rc_vars = FreeRcVariables::default();
@ -455,6 +542,9 @@ fn insert_refcount_operations_stmt<'a>(
free_rc_vars
};
// If we compare the free variables from a branch with the free variables from all branches.
// We can determine which variables are not used in that branch.
// And as such, we can drop them before continuing.
let newer_branches = Vec::from_iter_in(
new_branches
.into_iter()
@ -486,8 +576,13 @@ fn insert_refcount_operations_stmt<'a>(
(new_switch, free_rc_vars)
}
Stmt::Ret(s) => {
return (arena.alloc(Stmt::Ret(*s)), FreeRcVariables::default());
todo!()
// TODO use with_capacity if possible, or use an initializer to oneline this.
let mut free_variables = FreeRcVariables::with_capacity_and_hasher(
1,
roc_collections::all::BuildHasher::default(),
);
free_variables.insert(*s);
return (arena.alloc(Stmt::Ret(*s)), free_variables);
}
Stmt::Refcounting(_, _) => unreachable!("refcounting should not be in the AST already"),
Stmt::Expect {
@ -520,6 +615,9 @@ fn insert_refcount_operations_stmt<'a>(
}
}
/**
* Insert increment statements for the given symbols compensating for the ownership.
*/
fn insert_inc_stmts<'a>(
arena: &'a Bump,
environment: &mut Environment,
@ -533,6 +631,9 @@ fn insert_inc_stmts<'a>(
})
}
/**
Insert an increment statement for the given symbol compensating for the ownership.
*/
fn insert_inc_stmt<'a>(
arena: &'a Bump,
environment: &mut Environment,
@ -556,6 +657,9 @@ fn insert_inc_stmt<'a>(
}
}
/**
Insert decrement statements for the given symbols if they are owned.
*/
fn insert_dec_stmts<'a>(
arena: &'a Bump,
environment: &mut Environment,
@ -567,6 +671,9 @@ fn insert_dec_stmts<'a>(
})
}
/**
Insert a decrement statement for the given symbol if it is owned.
*/
fn insert_dec_stmt<'a>(
arena: &'a Bump,
environment: &mut Environment,