free or reuse unconditionally when value is unique

This commit is contained in:
Folkert 2023-06-27 17:33:53 +02:00
parent e3ab023f62
commit fc3004da58
No known key found for this signature in database
GPG key ID: 1F17F6FFD112B97C
21 changed files with 381 additions and 69 deletions

View file

@ -19,7 +19,9 @@ use bumpalo::Bump;
use bumpalo::collections::vec::Vec;
use bumpalo::collections::CollectIn;
use roc_collections::{MutMap, MutSet};
use roc_module::low_level::LowLevel;
use roc_module::symbol::{IdentIds, ModuleId, Symbol};
use roc_target::TargetInfo;
/**
Insert reset and reuse operations into the IR.
@ -29,6 +31,7 @@ pub fn insert_reset_reuse_operations<'a, 'i>(
arena: &'a Bump,
layout_interner: &'i STLayoutInterner<'a>,
home: ModuleId,
target_info: TargetInfo,
ident_ids: &'i mut IdentIds,
update_mode_ids: &'i mut UpdateModeIds,
procs: &mut MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>,
@ -42,6 +45,7 @@ pub fn insert_reset_reuse_operations<'a, 'i>(
let new_proc = insert_reset_reuse_operations_proc(
arena,
layout_interner,
target_info,
home,
ident_ids,
update_mode_ids,
@ -55,6 +59,7 @@ pub fn insert_reset_reuse_operations<'a, 'i>(
fn insert_reset_reuse_operations_proc<'a, 'i>(
arena: &'a Bump,
layout_interner: &'i STLayoutInterner<'a>,
target_info: TargetInfo,
home: ModuleId,
ident_ids: &'i mut IdentIds,
update_mode_ids: &'i mut UpdateModeIds,
@ -66,6 +71,7 @@ fn insert_reset_reuse_operations_proc<'a, 'i>(
}
let mut env = ReuseEnvironment {
target_info,
symbol_tags: MutMap::default(),
non_unique_symbols: MutSet::default(),
reuse_tokens: MutMap::default(),
@ -398,33 +404,83 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
})
}
Stmt::Refcounting(rc, continuation) => {
let reuse_pair = match rc {
ModifyRc::Dec(symbol) | ModifyRc::DecRef(symbol)
if !environment.non_unique_symbols.contains(symbol) =>
{
enum SymbolIsUnique {
Never,
Always(Symbol),
MustCheck(Symbol),
}
let can_reuse = match rc {
ModifyRc::Dec(symbol) | ModifyRc::DecRef(symbol) => {
// can only reuse if the symbol is (potentially) unique
if environment.non_unique_symbols.contains(symbol) {
SymbolIsUnique::Never
} else {
SymbolIsUnique::MustCheck(*symbol)
}
}
ModifyRc::Free(symbol) => {
// a free'd symbol is guaranteed to be unique
SymbolIsUnique::Always(*symbol)
}
ModifyRc::Inc(_, _) => {
// an incremented symbol is never unique
SymbolIsUnique::Never
}
};
enum ResetOperation {
Reset,
ResetRef,
ClearTagId,
Nothing,
}
let reuse_pair = match can_reuse {
SymbolIsUnique::MustCheck(symbol) | SymbolIsUnique::Always(symbol) => {
// Get the layout of the symbol from where it is defined.
let layout_option = environment.get_symbol_layout(*symbol);
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.clone() {
match layout_option {
LayoutOption::Layout(layout) => {
match symbol_layout_reusability(
layout_interner,
environment,
symbol,
&symbol,
layout,
) {
Reuse::Reusable(union_layout) => {
let (reuse_symbol, reset_op) = match rc {
ModifyRc::Dec(_) => (
Symbol::new(home, ident_ids.gen_unique()),
ResetOperation::Reset,
),
ModifyRc::DecRef(_) => (
Symbol::new(home, ident_ids.gen_unique()),
ResetOperation::ResetRef,
),
ModifyRc::Free(_) => {
if union_layout
.stores_tag_id_in_pointer(environment.target_info)
{
(
Symbol::new(home, ident_ids.gen_unique()),
ResetOperation::ClearTagId,
)
} else {
(symbol, ResetOperation::Nothing)
}
}
_ => unreachable!(),
};
let reuse_token = ReuseToken {
symbol: Symbol::new(home, ident_ids.gen_unique()),
symbol: reuse_symbol,
update_mode_id: update_mode_ids.next_id(),
};
let dec_ref = match rc {
ModifyRc::Dec(_) => false,
ModifyRc::DecRef(_) => true,
_ => unreachable!(),
};
let owned_layout = **layout;
environment.push_reuse_token(
arena,
@ -432,7 +488,14 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
reuse_token,
layout,
);
Some((layout, union_layout, *symbol, reuse_token, dec_ref))
Some((
owned_layout,
union_layout,
symbol,
reuse_token,
reset_op,
))
}
Reuse::Nonreusable => None,
}
@ -440,7 +503,7 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
_ => None,
}
}
_ => {
SymbolIsUnique::Never => {
// We don't need to do anything for an inc or symbols known to be non-unique.
None
}
@ -457,7 +520,7 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
);
// If we inserted a reuse token, we need to insert a reset reuse operation if the reuse token is consumed.
if let Some((layout, union_layout, symbol, reuse_token, dec_ref)) = reuse_pair {
if let Some((layout, union_layout, symbol, reuse_token, reset_op)) = reuse_pair {
let stack_reuse_token = environment
.peek_reuse_token(&get_reuse_layout_info(layout_interner, union_layout));
@ -471,29 +534,56 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
_ => {
// 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 = match dec_ref {
// A decref will be replaced by a resetref.
true => Expr::ResetRef {
symbol,
update_mode: reuse_token.update_mode_id,
},
// And a dec will be replaced by a reset.
false => Expr::Reset {
symbol,
update_mode: reuse_token.update_mode_id,
},
};
match reset_op {
ResetOperation::Reset => {
// a dec will be replaced by a reset.
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.
// TODO reset probably decrements it's children. So we probably need to create a resetref that only does the token.
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,
));
return arena.alloc(Stmt::Let(
reuse_token.symbol,
reset_expr,
layout,
new_continuation,
));
}
ResetOperation::ResetRef => {
// a decref will be replaced by a resetref.
let reset_expr = Expr::ResetRef {
symbol,
update_mode: reuse_token.update_mode_id,
};
return arena.alloc(Stmt::Let(
reuse_token.symbol,
reset_expr,
layout,
new_continuation,
));
}
ResetOperation::ClearTagId => {
let reset_expr = Expr::Call(crate::ir::Call {
call_type: crate::ir::CallType::LowLevel {
op: LowLevel::PtrClearTagId,
update_mode: update_mode_ids.next_id(),
},
arguments: arena.alloc([symbol]),
});
return arena.alloc(Stmt::Let(
reuse_token.symbol,
reset_expr,
layout,
new_continuation,
));
}
ResetOperation::Nothing => {
// the reuse token is already in a valid state
return new_continuation;
}
}
}
}
}
@ -661,6 +751,7 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
// Create a new environment for the body. With everything but the jump reuse tokens. As those should be given by the jump.
let mut first_pass_body_environment = ReuseEnvironment {
target_info: environment.target_info,
symbol_tags: environment.symbol_tags.clone(),
non_unique_symbols: environment.non_unique_symbols.clone(),
reuse_tokens: max_reuse_token_symbols.clone(),
@ -824,6 +915,7 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
let (second_pass_body_environment, second_pass_body) = {
// Create a new environment for the body. With everything but the jump reuse tokens. As those should be given by the jump.
let mut body_environment = ReuseEnvironment {
target_info: environment.target_info,
symbol_tags: environment.symbol_tags.clone(),
non_unique_symbols: environment.non_unique_symbols.clone(),
reuse_tokens: used_reuse_tokens.clone(),
@ -889,7 +981,8 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
let mut void_pointer_layout_symbols = Vec::new_in(arena);
// See what tokens we can get from the env, if none are available, use a void pointer.
// We process the tokens in reverse order, so that when we consume the tokens we last added, we consume the tokens that are most likely not to be null.
// We process the tokens in reverse order, so that when we consume the tokens we last added,
// we consume the tokens that are most likely not to be null.
let tokens = token_layouts_clone
.iter()
.rev()
@ -1002,7 +1095,7 @@ fn insert_reset_reuse_operations_stmt<'a, 'i>(
fn create_ptr_cast(arena: &Bump, symbol: Symbol) -> Expr {
Expr::Call(crate::ir::Call {
call_type: crate::ir::CallType::LowLevel {
op: roc_module::low_level::LowLevel::PtrCast,
op: LowLevel::PtrCast,
update_mode: UpdateModeId::BACKEND_DUMMY,
},
arguments: Vec::from_iter_in([symbol], arena).into_bump_slice(),
@ -1092,8 +1185,9 @@ enum JoinPointReuseTokens<'a> {
RemainderSecond(Vec<'a, (&'a InLayout<'a>, TokenLayout)>),
}
#[derive(Default, Clone)]
#[derive(Clone)]
struct ReuseEnvironment<'a> {
target_info: TargetInfo,
symbol_tags: MutMap<Symbol, Tag>,
non_unique_symbols: MutSet<Symbol>,
reuse_tokens: ReuseTokens<'a>,