inserted reset/reuse

This commit is contained in:
J.Teeuwissen 2023-03-14 11:41:45 +01:00
parent ca4615929b
commit c6b29bc9b3
No known key found for this signature in database
GPG key ID: DB5F7A1ED8D478AD
2 changed files with 309 additions and 75 deletions

View file

@ -1,11 +1,12 @@
// Frame limited reuse // Frame limited reuse
// Based on Reference Counting with Frame Limited Reuse // Based on Reference Counting with Frame Limited Reuse
use crate::ir::{BranchInfo, Expr, ModifyRc, Proc, ProcLayout, Stmt, UpdateModeIds}; use crate::ir::{BranchInfo, Expr, ModifyRc, Proc, ProcLayout, Stmt, UpdateModeId, UpdateModeIds};
use crate::layout::{InLayout, Layout, LayoutInterner, STLayoutInterner, UnionLayout}; use crate::layout::{InLayout, Layout, LayoutInterner, STLayoutInterner, UnionLayout};
use bumpalo::Bump; use bumpalo::Bump;
use bumpalo::collections::vec::Vec;
use roc_collections::MutMap; use roc_collections::MutMap;
use roc_module::symbol::{IdentIds, ModuleId, Symbol}; use roc_module::symbol::{IdentIds, ModuleId, Symbol};
@ -21,6 +22,11 @@ pub fn insert_reset_reuse_operations<'a, 'i>(
update_mode_ids: &'i mut UpdateModeIds, update_mode_ids: &'i mut UpdateModeIds,
procs: &mut MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>, procs: &mut MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>,
) { ) {
let mut global_layouts = SymbolLayout::default();
for (symbol, layout) in procs.keys() {
global_layouts.insert(*symbol, LayoutOption::GloballyDefined);
}
for proc in procs.values_mut() { for proc in procs.values_mut() {
let new_proc = insert_reset_reuse_operations_proc( let new_proc = insert_reset_reuse_operations_proc(
arena, arena,
@ -28,21 +34,31 @@ pub fn insert_reset_reuse_operations<'a, 'i>(
home, home,
ident_ids, ident_ids,
update_mode_ids, update_mode_ids,
global_layouts.clone(),
proc.clone(), proc.clone(),
); );
*proc = new_proc; *proc = new_proc;
} }
} }
pub fn insert_reset_reuse_operations_proc<'a, 'i>( fn insert_reset_reuse_operations_proc<'a, 'i>(
arena: &'a Bump, arena: &'a Bump,
layout_interner: &'i mut STLayoutInterner<'a>, layout_interner: &'i mut STLayoutInterner<'a>,
home: ModuleId, home: ModuleId,
ident_ids: &'i mut IdentIds, ident_ids: &'i mut IdentIds,
update_mode_ids: &'i mut UpdateModeIds, update_mode_ids: &'i mut UpdateModeIds,
mut symbol_layout: SymbolLayout<'a>,
mut proc: Proc<'a>, mut proc: Proc<'a>,
) -> Proc<'a> { ) -> Proc<'a> {
let env = ReuseEnvironment::default(); for (layout, symbol) in proc.args {
symbol_layout.insert(*symbol, LayoutOption::Layout(layout));
}
let mut env = ReuseEnvironment {
layout_tags: MutMap::default(),
reuse_tokens: MutMap::default(),
symbol_layouts: symbol_layout,
};
let new_body = insert_reset_reuse_operations_stmt( let new_body = insert_reset_reuse_operations_stmt(
arena, arena,
@ -50,10 +66,13 @@ pub fn insert_reset_reuse_operations_proc<'a, 'i>(
home, home,
ident_ids, ident_ids,
update_mode_ids, update_mode_ids,
env, &mut env,
arena.alloc(proc.body), arena.alloc(proc.body),
); );
// All reuse tokens either have to be used by reuse or not be inserted at all at the reset (and removed from the environment).
debug_assert!(env.reuse_tokens.is_empty());
proc.body = new_body.clone(); proc.body = new_body.clone();
proc proc
} }
@ -64,22 +83,41 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
home: ModuleId, home: ModuleId,
ident_ids: &'i mut IdentIds, ident_ids: &'i mut IdentIds,
update_mode_ids: &'i mut UpdateModeIds, update_mode_ids: &'i mut UpdateModeIds,
mut environment: ReuseEnvironment<'a>, environment: &mut ReuseEnvironment<'a>,
stmt: &'a Stmt<'a>, stmt: &'a Stmt<'a>,
) -> &'a Stmt<'a> { ) -> &'a Stmt<'a> {
match stmt { match stmt {
Stmt::Let(binding, expr, layout, continuation) => { Stmt::Let(binding, expr, layout, continuation) => {
// TODO store the layout for the binding in the environment. let new_expr = match expr {
// TODO deal with fresh allocations w/ reuse. Expr::Tag {
if let Expr::Tag { tag_layout,
tag_layout, tag_id,
tag_id, arguments,
arguments, } => {
} = expr // The value of the tag is currently only used in the case of nullable recursive unions.
{ // But for completeness we add every kind of union to the layout_tags.
// The value of the tag is currently only used in the case of nullable recursive unions. environment.add_layout_tag(layout, *tag_id);
// But for completeness we add every kind of union to the layout_tags.
environment.add_layout_tag(layout, *tag_id); // See if we have a reuse token
match environment.pop_reuse_token(layout) {
// We have a reuse token for this layout, use it.
Some(reuse_token) => {
Expr::Reuse {
symbol: reuse_token.symbol,
update_mode: reuse_token.update_mode_id,
// for now, always overwrite the tag ID just to be sure
update_tag_id: true,
tag_layout: *tag_layout,
tag_id: *tag_id,
arguments,
}
}
// We have no reuse token available, keep the old expression with a fresh allocation.
None => expr.clone(),
}
}
_ => expr.clone(),
}; };
environment.add_symbol_layout(*binding, layout); environment.add_symbol_layout(*binding, layout);
@ -94,11 +132,14 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
continuation, continuation,
); );
arena.alloc(Stmt::Let(*binding, expr.clone(), *layout, new_continuation)) // // for now, always overwrite the tag ID just to be sure
// let update_tag_id = true;
arena.alloc(Stmt::Let(*binding, new_expr, *layout, new_continuation))
} }
Stmt::Switch { Stmt::Switch {
cond_symbol, cond_symbol,
cond_layout: layout, cond_layout,
branches, branches,
default_branch, default_branch,
ret_layout, ret_layout,
@ -108,7 +149,7 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
.map(|(tag_id, info, branch)| { .map(|(tag_id, info, branch)| {
let mut branch_env = environment.clone(); let mut branch_env = environment.clone();
if let BranchInfo::Constructor { tag_id: tag, .. } = info { if let BranchInfo::Constructor { tag_id: tag, .. } = info {
branch_env.add_layout_tag(layout, *tag); branch_env.add_layout_tag(cond_layout, *tag);
} }
let new_branch = insert_reset_reuse_operations_stmt( let new_branch = insert_reset_reuse_operations_stmt(
@ -117,11 +158,11 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
home, home,
ident_ids, ident_ids,
update_mode_ids, update_mode_ids,
environment.clone(), &mut environment.clone(),
branch, branch,
); );
(*tag_id, info, new_branch, branch_env) (*tag_id, info.clone(), new_branch, branch_env)
}) })
.collect::<std::vec::Vec<_>>(); .collect::<std::vec::Vec<_>>();
@ -130,7 +171,7 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
let mut branch_env = environment.clone(); let mut branch_env = environment.clone();
if let BranchInfo::Constructor { tag_id: tag, .. } = info { if let BranchInfo::Constructor { tag_id: tag, .. } = info {
branch_env.add_layout_tag(layout, *tag); branch_env.add_layout_tag(cond_layout, *tag);
} }
let new_branch = insert_reset_reuse_operations_stmt( let new_branch = insert_reset_reuse_operations_stmt(
@ -139,40 +180,126 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
home, home,
ident_ids, ident_ids,
update_mode_ids, update_mode_ids,
environment.clone(), &mut environment.clone(),
branch, branch,
); );
(info.clone(), new_branch, branch_env) (info.clone(), new_branch, branch_env)
}; };
// TODO collect the used reuse token from each branch and drop the unused ones. // First we determine the minimum of reuse tokens available for each layout.
// Make sure the current env is updated to have the correct reuse tokens consumed. let layout_min_reuse_tokens = {
let branch_envs = {
let mut branch_environments =
Vec::with_capacity_in(new_branches.len() + 1, arena);
// TODO add value to layout_tags for (_, _, _, branch_env) in new_branches.iter() {
todo!() branch_environments.push(branch_env);
} }
Stmt::Refcounting(rc, continuation) => {
match rc {
ModifyRc::Inc(_, _) => todo!(),
// TODO verify whether reuse works for a decref as well. It should be.
ModifyRc::Dec(symbol) | ModifyRc::DecRef(symbol) => {
// TODO insert actual reuse statements. if the reuse token is consumed (check by checking the top of the stack)
// Get the layout of the symbol from where it is defined. branch_environments.push(&new_default_branch.2);
let layout = environment.get_symbol_layout(*symbol);
// If the layout is reusable, add a reuse token for the layout to the environment. branch_environments
if matches!( };
can_reuse_layout_tag(layout_interner, &environment, &layout),
Reuse::Reusable let layout_min_reuse_tokens =
) { environment.reuse_tokens.keys().copied().map(|layout| {
environment.push_reuse_token(&layout, *symbol); let min_reuse_tokens = branch_envs
}; .iter()
} .map(|branch_environment| {
branch_environment
.reuse_tokens
.get(&layout)
.map_or(0, |tokens| tokens.len())
})
.min()
.expect("There should be at least one branch");
(layout, min_reuse_tokens)
});
layout_min_reuse_tokens.collect::<MutMap<_, _>>()
};
// Then we drop any unused reuse tokens in branches where the minimum is not reached.
let newer_branches = Vec::from_iter_in(
new_branches
.into_iter()
.map(|(label, info, branch, branch_env)| {
let unused_tokens= branch_env.reuse_tokens.iter().flat_map(|(layout, reuse_tokens) |{
let min_reuse_tokens = layout_min_reuse_tokens.get(&layout).expect("All layouts in the environment should be in the layout_min_reuse_tokens map.");
let unused_tokens = &reuse_tokens[*min_reuse_tokens..];
unused_tokens
});
let newer_branch = drop_unused_reuse_tokens(arena, unused_tokens.copied(), branch);
(label, info, newer_branch.clone())
}),
arena,
)
.into_bump_slice();
let newer_default_branch = {
let (info, branch, branch_env) = new_default_branch;
let unused_tokens= branch_env.reuse_tokens.iter().flat_map(|(layout, reuse_tokens) |{
let min_reuse_tokens = layout_min_reuse_tokens.get(&layout).expect("All layouts in the environment should be in the layout_min_reuse_tokens map.");
let unused_tokens = &reuse_tokens[*min_reuse_tokens..];
unused_tokens
});
let newer_branch = drop_unused_reuse_tokens(arena, unused_tokens.copied(), branch);
(info, newer_branch)
};
// And finally we update the current environment to reflect the correct number of reuse tokens.
for (layout, reuse_tokens) in environment.reuse_tokens.iter_mut() {
let min_reuse_tokens = layout_min_reuse_tokens.get(&layout).expect(
"All layouts in the environment should be in the layout_min_reuse_tokens map.",
);
reuse_tokens.truncate(*min_reuse_tokens)
} }
insert_reset_reuse_operations_stmt( arena.alloc(Stmt::Switch {
cond_symbol: *cond_symbol,
cond_layout: *cond_layout,
branches: newer_branches,
default_branch: newer_default_branch,
ret_layout: *ret_layout,
})
}
Stmt::Refcounting(rc, continuation) => {
let reuse_pair = match rc {
ModifyRc::Inc(_, _) => {
// We don't need to do anything for an inc.
None
}
ModifyRc::Dec(symbol) | ModifyRc::DecRef(symbol) => {
// Get the layout of the symbol from where it is defined.
let layout_option = environment.get_symbol_layout(*symbol);
// If the symbol is defined in the current proc, we can use the layout from the environment.
match layout_option {
LayoutOption::Layout(layout)
if matches!(
can_reuse_layout_tag(layout_interner, &environment, &layout),
Reuse::Reusable
) =>
{
let layout_clone = layout.clone();
let reuse_token = ReuseToken {
symbol: Symbol::new(home, ident_ids.gen_unique()),
update_mode_id: update_mode_ids.next_id(),
};
environment.push_reuse_token(&layout_clone, reuse_token);
Some((layout_clone, *symbol, reuse_token))
}
_ => None,
}
}
};
let new_continuation = insert_reset_reuse_operations_stmt(
arena, arena,
layout_interner, layout_interner,
home, home,
@ -180,11 +307,43 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
update_mode_ids, update_mode_ids,
environment, environment,
continuation, continuation,
) );
// If we inserted a reuse token, we need to insert a reset reuse operation if the reuse token is consumed.
if let Some((layout, symbol, reuse_token)) = reuse_pair {
let stack_reuse_token = environment.peek_reuse_token(&layout);
match stack_reuse_token {
Some(reuse_symbol) if reuse_symbol == reuse_token => {
// The token we inserted is still on the stack, so we don't need to insert a reset operation.
// We do need to remove the token from the environment. To prevent errors higher in the tree.
let _ = environment.pop_reuse_token(&layout);
}
_ => {
// The token we inserted is no longer on the stack, it must have been consumed.
// So we need to insert a reset operation.
let reset_expr = Expr::Reset {
symbol,
update_mode: reuse_token.update_mode_id,
};
// If we generate a reuse token, we no longer want to use the drop statement anymore. So we just return the reset expression.
// TODO verify if this works for both dec and decref.
return arena.alloc(Stmt::Let(
reuse_token.symbol,
reset_expr,
// TODO not sure what the layout should be for a reset token. Currently it is the layout of the symbol.
*layout,
new_continuation,
));
}
}
}
arena.alloc(Stmt::Refcounting(rc.clone(), new_continuation))
} }
Stmt::Ret(symbol) => Stmt::Ret(symbol) => {
// The return statement just doesn't consume any tokens. Dropping these tokens will be handled before. // The return statement just doesn't consume any tokens. Dropping these tokens will be handled before.
{
stmt stmt
} }
Stmt::Expect { Stmt::Expect {
@ -289,27 +448,49 @@ enum Reuse {
Map containing the curren't known tag of a layout. Map containing the curren't known tag of a layout.
A layout with a tag will be inserted e.g. after pattern matching. where the tag is known. A layout with a tag will be inserted e.g. after pattern matching. where the tag is known.
*/ */
type LayoutTags<'a> = MutMap<&'a InLayout<'a>, FooBar>; type LayoutTags<'a> = MutMap<&'a InLayout<'a>, Tag>;
/** /**
Map containing the reuse tokens of a layout. Map containing the reuse tokens of a layout.
A vec is used as a stack as we want to use the latest reuse token available. A vec is used as a stack as we want to use the latest reuse token available.
*/ */
type ReuseTokens<'a> = MutMap<InLayout<'a>, Vec<ResetToken>>; type ReuseTokens<'a> = MutMap<InLayout<'a>, std::vec::Vec<ReuseToken>>;
/** /**
A reuse token is a symbol that is used to reset a layout. A reuse token is a symbol that is used to reset a layout.
Matches variables that are pointers. Matches variables that are pointers.
*/ */
type ResetToken = Symbol; #[derive(Clone, Copy, PartialEq, Eq)]
struct ReuseToken {
// The symbol of the reuse token.
symbol: Symbol,
type FooBar = u16; // TODO figure out what this is.
update_mode_id: UpdateModeId,
}
type Tag = u16;
/**
Map containing the layout of a symbol.
Used to determine whether the pointer of a symbol can be reused, if it is reference counted (heap allocated).
*/
type SymbolLayout<'a> = MutMap<Symbol, LayoutOption<'a>>;
#[derive(Clone)]
enum LayoutOption<'a> {
// A normal layout defined in the current function.
Layout(&'a InLayout<'a>),
// No layout as this symbol is defined in a global scope and should not be reused.
GloballyDefined,
}
#[derive(Default, Clone)] #[derive(Default, Clone)]
struct ReuseEnvironment<'a> { struct ReuseEnvironment<'a> {
layout_tags: LayoutTags<'a>, layout_tags: LayoutTags<'a>,
reuse_tokens: ReuseTokens<'a>, reuse_tokens: ReuseTokens<'a>,
symbol_layouts: MutMap<Symbol, &'a InLayout<'a>>, symbol_layouts: SymbolLayout<'a>,
} }
impl<'a> ReuseEnvironment<'a> { impl<'a> ReuseEnvironment<'a> {
@ -317,21 +498,21 @@ impl<'a> ReuseEnvironment<'a> {
Add the known tag for a layout. Add the known tag for a layout.
Used to optimize reuse of unions that are know to have a null pointer. Used to optimize reuse of unions that are know to have a null pointer.
*/ */
fn add_layout_tag(&mut self, layout: &'a InLayout<'a>, tag: FooBar) { fn add_layout_tag(&mut self, layout: &'a InLayout<'a>, tag: Tag) {
self.layout_tags.insert(layout, tag); self.layout_tags.insert(layout, tag);
} }
/** /**
Retrieve the known tag for a layout. Retrieve the known tag for a layout.
*/ */
fn get_layout_tag(&self, layout: &InLayout<'a>) -> Option<FooBar> { fn get_layout_tag(&self, layout: &InLayout<'a>) -> Option<Tag> {
self.layout_tags.get(layout).copied() self.layout_tags.get(layout).copied()
} }
/** /**
Retrieve a reuse token for a layout from the stack for said layout. Retrieve a reuse token for a layout from the stack for said layout.
*/ */
fn get_reuse_token(&mut self, layout: &InLayout<'a>) -> Option<ResetToken> { fn pop_reuse_token(&mut self, layout: &InLayout<'a>) -> Option<ReuseToken> {
let reuse_tokens = self.reuse_tokens.get_mut(layout)?; let reuse_tokens = self.reuse_tokens.get_mut(layout)?;
// If the layout is in the map, pop the token from the stack. // If the layout is in the map, pop the token from the stack.
let reuse_token = reuse_tokens.pop(); let reuse_token = reuse_tokens.pop();
@ -342,10 +523,21 @@ impl<'a> ReuseEnvironment<'a> {
reuse_token reuse_token
} }
/**
Retrieve a reuse token for a layout from the stack for said layout.
Without consuming the token.
*/
fn peek_reuse_token(&mut self, layout: &InLayout<'a>) -> Option<ReuseToken> {
let reuse_tokens = self.reuse_tokens.get(layout)?;
// If the layout is in the map, pop the token from the stack.
let reuse_token = reuse_tokens.last();
reuse_token.copied()
}
/** /**
Push a reuse token for a layout on the stack for said layout. Push a reuse token for a layout on the stack for said layout.
*/ */
fn push_reuse_token(&mut self, layout: &InLayout<'a>, token: ResetToken) { fn push_reuse_token(&mut self, layout: &InLayout<'a>, token: ReuseToken) {
match self.reuse_tokens.get_mut(layout) { match self.reuse_tokens.get_mut(layout) {
Some(reuse_tokens) => { Some(reuse_tokens) => {
// If the layout is already in the map, push the token on the stack. // If the layout is already in the map, push the token on the stack.
@ -362,13 +554,14 @@ impl<'a> ReuseEnvironment<'a> {
Add the layout of a symbol. Add the layout of a symbol.
*/ */
fn add_symbol_layout(&mut self, symbol: Symbol, layout: &'a InLayout<'a>) { fn add_symbol_layout(&mut self, symbol: Symbol, layout: &'a InLayout<'a>) {
self.symbol_layouts.insert(symbol, layout); self.symbol_layouts
.insert(symbol, LayoutOption::Layout(layout));
} }
/** /**
Retrieve the layout of a symbol. Retrieve the layout of a symbol.
*/ */
fn get_symbol_layout(&self, symbol: Symbol) -> &'a InLayout<'a> { fn get_symbol_layout(&self, symbol: Symbol) -> &LayoutOption<'a> {
self.symbol_layouts.get(&symbol).expect("Expected symbol to have a layout. It should have been inserted in the environment already.") self.symbol_layouts.get(&symbol).expect("Expected symbol to have a layout. It should have been inserted in the environment already.")
} }
} }
@ -407,7 +600,26 @@ fn can_reuse_layout_tag<'a, 'i>(
} }
} }
}, },
// TODO why are strings/lists not reusable? // Strings literals are constants.
// Arrays are probably given to functions and reused there. Little use to reuse them here.
_ => Reuse::Nonreusable, _ => Reuse::Nonreusable,
} }
} }
/**
Drop the reuse tokens that are not used anymore.
Usefull when reuse tokens are used in a branch, and thus should be created.
But not in all branches, and thus should be dropped in those branches.
*/
fn drop_unused_reuse_tokens<'a>(
arena: &'a Bump,
unused_tokens: impl Iterator<Item = ReuseToken>,
continuation: &'a Stmt<'a>,
) -> &'a Stmt<'a> {
unused_tokens.fold(continuation, |continuation, reuse_token| {
arena.alloc(Stmt::Refcounting(
ModifyRc::Dec(reuse_token.symbol),
continuation,
))
})
}

View file

@ -31,7 +31,6 @@ pub fn insert_refcount_operations<'a, 'i>(
// Create a VariableRcTypesEnv for the procedures as they get referenced but should be marked as non reference counted. // Create a VariableRcTypesEnv for the procedures as they get referenced but should be marked as non reference counted.
let mut variable_rc_types_env = VariableRcTypesEnv::from_layout_interner(layout_interner); 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)); variable_rc_types_env.insert_proc_symbols(procedures.keys().map(|(symbol, _layout)| *symbol));
for (_, proc) in procedures.iter_mut() { for (_, proc) in procedures.iter_mut() {
// Clone the variable_rc_types_env and insert the variables in the current procedure. // 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. // As the variables should be limited in scope for the current proc.
@ -297,18 +296,15 @@ impl VariableUsage {
variable_rc_types, variable_rc_types,
owned_arguments.iter().map(|(symbol, _)| symbol).copied(), owned_arguments.iter().map(|(symbol, _)| symbol).copied(),
), ),
borrowed: borrowed_arguments borrowed: Self::borrowed_usages(
.iter() variable_rc_types,
.map(|(symbol, _)| symbol) borrowed_arguments.iter().map(|(symbol, _)| symbol).copied(),
.copied() ),
.collect(),
}; };
} }
CallType::HigherOrder(HigherOrderLowLevel { CallType::HigherOrder(HigherOrderLowLevel {
op: operator, op: operator,
/// TODO I _think_ we can get rid of this, perhaps only keeping track of
/// the layout of the closure argument, if any
closure_env_layout, closure_env_layout,
/// update mode of the higher order lowlevel itself /// update mode of the higher order lowlevel itself
@ -319,8 +315,12 @@ impl VariableUsage {
// Functions always take their arguments as owned. // Functions always take their arguments as owned.
// (Except lowlevels, but those are wrapped in functions that take their arguments as owned and perform rc.) // (Except lowlevels, but those are wrapped in functions that take their arguments as owned and perform rc.)
// TODO do something with these variables. I think there should be only one argument for the closure.
let closure_arguments = &arguments[operator.function_index()..]; let closure_arguments = &arguments[operator.function_index()..];
// This should always be true, not sure where this could be set to false.
debug_assert!(passed_function.owns_captured_environment);
match operator { match operator {
crate::low_level::HigherOrder::ListMap { xs } => VariableUsage { crate::low_level::HigherOrder::ListMap { xs } => VariableUsage {
owned: Self::owned_usages(variable_rc_types, [*xs].into_iter()), owned: Self::owned_usages(variable_rc_types, [*xs].into_iter()),
@ -351,7 +351,9 @@ impl VariableUsage {
// But functions assume that they are called with owned arguments, this creates a problem. // But functions assume that they are called with owned arguments, this creates a problem.
// We need to treat them as owned by incrementing the reference count before calling the function, // We need to treat them as owned by incrementing the reference count before calling the function,
{ {
// TODO probably update the list sort implementation to assume the functions take their argument as owned.
todo!() todo!()
// TODO sort will perform sort in place (if unique), take this into account.
} }
} }
} }
@ -373,11 +375,7 @@ impl VariableUsage {
// VariableUsage::Borrowed(*structure) // VariableUsage::Borrowed(*structure)
VariableUsage { VariableUsage {
owned: MutMap::default(), owned: MutMap::default(),
borrowed: { borrowed: Self::borrowed_usages(variable_rc_types, iter::once(*structure)),
let mut set = MutSet::with_capacity_and_hasher(1, default_hasher());
set.insert(*structure);
set
},
} }
} }
Expr::Array { Expr::Array {
@ -433,6 +431,30 @@ impl VariableUsage {
} }
variable_usage variable_usage
} }
/**
Filter the given symbols to only contain reference counted symbols.
*/
fn borrowed_usages(
variable_rc_types: &VariableRcTypes,
symbols: impl IntoIterator<Item = Symbol>,
) -> MutSet<Symbol> {
symbols
.into_iter()
.filter_map(|symbol| {
match {
variable_rc_types
.get(&symbol)
.expect("Expected variable to be in the map")
} {
// If the variable is reference counted, we need to increment the usage count.
VarRcType::ReferenceCounted => Some(symbol),
// If the variable is not reference counted, we don't need to do anything.
VarRcType::NotReferenceCounted => None,
}
})
.collect()
}
} }
/** /**
@ -1134,7 +1156,7 @@ fn insert_dec_stmt<'a, 's>(
/** /**
Taken from the original inc_dec borrow implementation. Taken from the original inc_dec borrow implementation.
*/ */
pub fn lowlevel_borrow_signature<'a>(arena: &'a Bump, op: &LowLevel) -> &'a [Ownership] { fn lowlevel_borrow_signature<'a>(arena: &'a Bump, op: &LowLevel) -> &'a [Ownership] {
use LowLevel::*; use LowLevel::*;
let irrelevant = Ownership::Owned; let irrelevant = Ownership::Owned;