mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-27 05:49:08 +00:00
free or reuse unconditionally when value is unique
This commit is contained in:
parent
e3ab023f62
commit
fc3004da58
21 changed files with 381 additions and 69 deletions
|
@ -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>,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue