mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-03 11:52:19 +00:00
Merge pull request #5436 from JTeeuwissen/drop-specialize-over-matches
allow lowlevel and match
This commit is contained in:
commit
cf20c74eb9
7 changed files with 340 additions and 27 deletions
|
@ -108,9 +108,18 @@ fn specialize_drops_stmt<'a, 'i>(
|
|||
|
||||
alloc_let_with_continuation!(environment)
|
||||
}
|
||||
_ => {
|
||||
// TODO perhaps allow for some e.g. lowlevel functions to be called if they cannot modify the RC of the symbol.
|
||||
// Check whether the increments can be passed to the continuation.
|
||||
CallType::LowLevel { op, .. } => match low_level_no_rc(&op) {
|
||||
// It should be safe to pass the increments to the continuation.
|
||||
RC::NoRc => alloc_let_with_continuation!(environment),
|
||||
// We probably should not pass the increments to the continuation.
|
||||
RC::Rc | RC::Uknown => {
|
||||
let mut new_environment = environment.clone_without_incremented();
|
||||
|
||||
alloc_let_with_continuation!(&mut new_environment)
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
// Calls can modify the RC of the symbol.
|
||||
// If we move a increment of children after the function,
|
||||
// the function might deallocate the child before we can use it after the function.
|
||||
|
@ -222,7 +231,7 @@ fn specialize_drops_stmt<'a, 'i>(
|
|||
let new_branches = branches
|
||||
.iter()
|
||||
.map(|(label, info, branch)| {
|
||||
let mut branch_env = environment.clone_without_incremented();
|
||||
let mut branch_env = environment.clone();
|
||||
|
||||
insert_branch_info!(branch_env, info);
|
||||
|
||||
|
@ -234,7 +243,7 @@ fn specialize_drops_stmt<'a, 'i>(
|
|||
branch,
|
||||
);
|
||||
|
||||
(*label, info.clone(), new_branch.clone())
|
||||
(*label, info.clone(), new_branch.clone(), branch_env)
|
||||
})
|
||||
.collect_in::<Vec<_>>(arena)
|
||||
.into_bump_slice();
|
||||
|
@ -242,7 +251,7 @@ fn specialize_drops_stmt<'a, 'i>(
|
|||
let new_default_branch = {
|
||||
let (info, branch) = default_branch;
|
||||
|
||||
let mut branch_env = environment.clone_without_incremented();
|
||||
let mut branch_env = environment.clone();
|
||||
|
||||
insert_branch_info!(branch_env, info);
|
||||
|
||||
|
@ -254,14 +263,91 @@ fn specialize_drops_stmt<'a, 'i>(
|
|||
branch,
|
||||
);
|
||||
|
||||
(info.clone(), new_branch, branch_env)
|
||||
};
|
||||
|
||||
// Find consumed increments in each branch and make sure they are consumed in all branches.
|
||||
// By incrementing them in each branch where they were not consumed.
|
||||
{
|
||||
let branch_envs = {
|
||||
let mut branch_environments =
|
||||
Vec::with_capacity_in(new_branches.len() + 1, arena);
|
||||
|
||||
for (_, _, _, branch_env) in new_branches.iter() {
|
||||
branch_environments.push(branch_env);
|
||||
}
|
||||
|
||||
branch_environments.push(&new_default_branch.2);
|
||||
|
||||
branch_environments
|
||||
};
|
||||
|
||||
// Find the lowest symbol count for each symbol in each branch, and update the environment to match.
|
||||
for (symbol, count) in environment.incremented_symbols.iter_mut() {
|
||||
let consumed = branch_envs
|
||||
.iter()
|
||||
.map(|branch_env| branch_env.incremented_symbols.get(symbol).unwrap_or(&0))
|
||||
.min()
|
||||
.unwrap();
|
||||
|
||||
// Update the existing env to match the lowest count.
|
||||
*count = *consumed;
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! insert_incs {
|
||||
($branch_env:expr, $branch:expr ) => {{
|
||||
let symbol_differences =
|
||||
environment
|
||||
.incremented_symbols
|
||||
.iter()
|
||||
.filter_map(|(symbol, count)| {
|
||||
let branch_count =
|
||||
$branch_env.incremented_symbols.get(symbol).unwrap_or(&0);
|
||||
|
||||
match branch_count - count {
|
||||
0 => None,
|
||||
difference => Some((symbol, difference)),
|
||||
}
|
||||
});
|
||||
|
||||
symbol_differences.fold($branch, |new_branch, (symbol, difference)| {
|
||||
arena.alloc(Stmt::Refcounting(
|
||||
ModifyRc::Inc(*symbol, difference),
|
||||
new_branch,
|
||||
))
|
||||
})
|
||||
}};
|
||||
}
|
||||
|
||||
let newer_branches = new_branches
|
||||
.iter()
|
||||
.map(|(label, info, branch, branch_env)| {
|
||||
let new_branch = insert_incs!(branch_env, branch);
|
||||
|
||||
(*label, info.clone(), new_branch.clone())
|
||||
})
|
||||
.collect_in::<Vec<_>>(arena)
|
||||
.into_bump_slice();
|
||||
|
||||
let newer_default_branch = {
|
||||
let (info, branch, branch_env) = new_default_branch;
|
||||
|
||||
let new_branch = insert_incs!(branch_env, branch);
|
||||
|
||||
(info.clone(), new_branch)
|
||||
};
|
||||
|
||||
// Remove all 0 counts as cleanup.
|
||||
environment
|
||||
.incremented_symbols
|
||||
.retain(|_, count| *count > 0);
|
||||
|
||||
arena.alloc(Stmt::Switch {
|
||||
cond_symbol: *cond_symbol,
|
||||
cond_layout: *cond_layout,
|
||||
branches: new_branches,
|
||||
default_branch: new_default_branch,
|
||||
branches: newer_branches,
|
||||
default_branch: newer_default_branch,
|
||||
ret_layout: *ret_layout,
|
||||
})
|
||||
}
|
||||
|
@ -317,24 +403,32 @@ fn specialize_drops_stmt<'a, 'i>(
|
|||
continuation,
|
||||
)
|
||||
} else {
|
||||
// Collect all children that were incremented and make sure that one increment remains in the environment afterwards.
|
||||
// Collect all children (recursively) that were incremented and make sure that one increment remains in the environment afterwards.
|
||||
// To prevent
|
||||
// let a = index b; inc a; dec b; ...; dec a
|
||||
// from being translated to
|
||||
// let a = index b; dec b
|
||||
// As a might get dropped as a result of the decrement of b.
|
||||
let mut incremented_children = environment
|
||||
.get_children(symbol)
|
||||
.iter()
|
||||
.copied()
|
||||
.filter_map(|child| environment.pop_incremented(&child).then_some(child))
|
||||
.collect::<MutSet<_>>();
|
||||
let mut incremented_children = {
|
||||
let mut todo_children = bumpalo::vec![in arena; *symbol];
|
||||
let mut incremented_children = MutSet::default();
|
||||
|
||||
while let Some(child) = todo_children.pop() {
|
||||
if environment.pop_incremented(&child) {
|
||||
incremented_children.insert(child);
|
||||
} else {
|
||||
todo_children.extend(environment.get_children(&child));
|
||||
}
|
||||
}
|
||||
|
||||
incremented_children
|
||||
};
|
||||
|
||||
// This decremented symbol was not incremented before, perhaps the children were.
|
||||
let in_layout = environment.get_symbol_layout(symbol);
|
||||
let runtime_layout = layout_interner.runtime_representation(*in_layout);
|
||||
|
||||
let new_dec = match runtime_layout.repr {
|
||||
let updated_stmt = match runtime_layout.repr {
|
||||
// Layout has children, try to inline them.
|
||||
LayoutRepr::Struct(field_layouts) => specialize_struct(
|
||||
arena,
|
||||
|
@ -395,7 +489,7 @@ fn specialize_drops_stmt<'a, 'i>(
|
|||
environment.add_incremented(*child_symbol, 1)
|
||||
}
|
||||
|
||||
new_dec
|
||||
updated_stmt
|
||||
}
|
||||
}
|
||||
ModifyRc::DecRef(_) => {
|
||||
|
@ -1277,10 +1371,7 @@ impl<'a> DropSpecializationEnvironment<'a> {
|
|||
|
||||
fn pop_incremented(&mut self, symbol: &Symbol) -> bool {
|
||||
match self.incremented_symbols.get_mut(symbol) {
|
||||
Some(1) => {
|
||||
self.incremented_symbols.remove(symbol);
|
||||
true
|
||||
}
|
||||
Some(0) => false,
|
||||
Some(c) => {
|
||||
*c -= 1;
|
||||
true
|
||||
|
@ -1288,6 +1379,112 @@ impl<'a> DropSpecializationEnvironment<'a> {
|
|||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO assert that a parent is only inlined once / assert max single dec per parent.
|
||||
}
|
||||
|
||||
/**
|
||||
Reference count information
|
||||
*/
|
||||
enum RC {
|
||||
// Rc is important, moving an increment to after this function might break the program.
|
||||
// E.g. if the function checks for uniqueness and behaves differently based on that.
|
||||
Rc,
|
||||
// Rc is not important, moving an increment to after this function should have no effect.
|
||||
NoRc,
|
||||
// Rc effect is unknown.
|
||||
Uknown,
|
||||
}
|
||||
|
||||
/*
|
||||
Returns whether the reference count of arguments to this function is relevant to the program.
|
||||
*/
|
||||
fn low_level_no_rc(lowlevel: &LowLevel) -> RC {
|
||||
use LowLevel::*;
|
||||
|
||||
match lowlevel {
|
||||
Unreachable => RC::Uknown,
|
||||
ListLen | StrIsEmpty | StrToScalars | StrCountGraphemes | StrGraphemes
|
||||
| StrCountUtf8Bytes | StrGetCapacity | ListGetCapacity => RC::NoRc,
|
||||
ListWithCapacity | StrWithCapacity => RC::NoRc,
|
||||
ListReplaceUnsafe => RC::Rc,
|
||||
StrGetUnsafe | ListGetUnsafe => RC::NoRc,
|
||||
ListConcat => RC::Rc,
|
||||
StrConcat => RC::Rc,
|
||||
StrSubstringUnsafe => RC::NoRc,
|
||||
StrReserve => RC::Rc,
|
||||
StrAppendScalar => RC::Rc,
|
||||
StrGetScalarUnsafe => RC::NoRc,
|
||||
StrTrim => RC::Rc,
|
||||
StrTrimLeft => RC::Rc,
|
||||
StrTrimRight => RC::Rc,
|
||||
StrSplit => RC::NoRc,
|
||||
StrToNum => RC::NoRc,
|
||||
ListPrepend => RC::Rc,
|
||||
StrJoinWith => RC::NoRc,
|
||||
ListMap | ListMap2 | ListMap3 | ListMap4 | ListSortWith => RC::Rc,
|
||||
|
||||
ListAppendUnsafe
|
||||
| ListReserve
|
||||
| ListSublist
|
||||
| ListDropAt
|
||||
| ListSwap
|
||||
| ListReleaseExcessCapacity
|
||||
| StrReleaseExcessCapacity => RC::Rc,
|
||||
|
||||
Eq | NotEq => RC::NoRc,
|
||||
|
||||
And | Or | NumAdd | NumAddWrap | NumAddChecked | NumAddSaturated | NumSub | NumSubWrap
|
||||
| NumSubChecked | NumSubSaturated | NumMul | NumMulWrap | NumMulSaturated
|
||||
| NumMulChecked | NumGt | NumGte | NumLt | NumLte | NumCompare | NumDivFrac
|
||||
| NumDivTruncUnchecked | NumDivCeilUnchecked | NumRemUnchecked | NumIsMultipleOf
|
||||
| NumPow | NumPowInt | NumBitwiseAnd | NumBitwiseXor | NumBitwiseOr | NumShiftLeftBy
|
||||
| NumShiftRightBy | NumShiftRightZfBy => RC::NoRc,
|
||||
|
||||
NumToStr
|
||||
| NumAbs
|
||||
| NumNeg
|
||||
| NumSin
|
||||
| NumCos
|
||||
| NumSqrtUnchecked
|
||||
| NumLogUnchecked
|
||||
| NumRound
|
||||
| NumCeiling
|
||||
| NumFloor
|
||||
| NumToFrac
|
||||
| Not
|
||||
| NumIsNan
|
||||
| NumIsInfinite
|
||||
| NumIsFinite
|
||||
| NumAtan
|
||||
| NumAcos
|
||||
| NumAsin
|
||||
| NumIntCast
|
||||
| NumToIntChecked
|
||||
| NumToFloatCast
|
||||
| NumToFloatChecked
|
||||
| NumCountLeadingZeroBits
|
||||
| NumCountTrailingZeroBits
|
||||
| NumCountOneBits => RC::NoRc,
|
||||
NumBytesToU16 => RC::NoRc,
|
||||
NumBytesToU32 => RC::NoRc,
|
||||
NumBytesToU64 => RC::NoRc,
|
||||
NumBytesToU128 => RC::NoRc,
|
||||
StrStartsWith | StrEndsWith => RC::NoRc,
|
||||
StrStartsWithScalar => RC::NoRc,
|
||||
StrFromUtf8Range => RC::Rc,
|
||||
StrToUtf8 => RC::Rc,
|
||||
StrRepeat => RC::NoRc,
|
||||
StrFromInt | StrFromFloat => RC::NoRc,
|
||||
Hash => RC::NoRc,
|
||||
|
||||
ListIsUnique => RC::Rc,
|
||||
|
||||
BoxExpr | UnboxExpr => {
|
||||
unreachable!("These lowlevel operations are turned into mono Expr's")
|
||||
}
|
||||
|
||||
PtrCast | PtrWrite | RefCountIncRcPtr | RefCountDecRcPtr | RefCountIncDataPtr
|
||||
| RefCountDecDataPtr | RefCountIsUnique => {
|
||||
unreachable!("Only inserted *after* borrow checking: {:?}", lowlevel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue