Skip to content

Commit 59b6296

Browse files
stroxlerfacebook-github-bot
authored andcommitted
Store Vec<Flow> in MergeItem
Summary: This diff begins changing how flow and flow merging is represented in a way that will allow us to optimize narrows out of flow merges in some cases. When we do this, it will no longer work very well to optimize *adding* merge items the way we've been doing, because we're no longer just comparing raw `Idx`'s, there's more structure. So this diff simplifies `MergeItem`'s data structure to just store a `Vec<Flow>` and pushes more of the semantic decisions like optimization until actual merge time. Note that we were alredy consuming `FlowInfo`s, so there are no additional allocations needed here other than maybe one extra iterator when we collect the deduplicated `branch_idxs`. Reviewed By: samwgoldman Differential Revision: D83355913 fbshipit-source-id: d6848fd093b5b420f3a2645eab9bb22225acb6c1
1 parent 9a372ca commit 59b6296

File tree

1 file changed

+39
-39
lines changed

1 file changed

+39
-39
lines changed

pyrefly/lib/binding/scope.rs

Lines changed: 39 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -450,10 +450,9 @@ pub enum FlowStyle {
450450
}
451451

452452
impl FlowStyle {
453-
fn merged(styles: Vec<FlowStyle>) -> FlowStyle {
454-
let mut it = styles.into_iter();
455-
let mut merged = it.next().unwrap_or(FlowStyle::Other);
456-
for x in it {
453+
fn merged(mut styles: impl Iterator<Item = FlowStyle>) -> FlowStyle {
454+
let mut merged = styles.next().unwrap_or(FlowStyle::Other);
455+
for x in styles {
457456
match (&merged, x) {
458457
// If they're identical, keep it
459458
(l, r) if l == &r => {}
@@ -1704,17 +1703,8 @@ struct MergeItem {
17041703
// in our data structure, this one does not refer to a pre-existing binding coming
17051704
// from upstream but rather the *output* of the merge.
17061705
phi_idx: Idx<Key>,
1707-
// Key of the default binding. This is only used in loops, where it is used
1708-
// to say that when in doubt, the loop recursive Phi should solve to either
1709-
// the binding lives at the top of the loop (if any) or the first assignment
1710-
// in the first branch we encountered.
1711-
default: Idx<Key>,
1712-
// The set of bindings live at the end of each branch. This will not include
1713-
// `merged_key` itself (which might be live if a branch of a loop does not
1714-
// modify anything).
1715-
branch_idxs: SmallSet<Idx<Key>>,
1716-
// The flow styles from each branch in the merge
1717-
flow_styles: Vec<FlowStyle>,
1706+
// The flows to merge.
1707+
flows: Vec<FlowInfo>,
17181708
}
17191709

17201710
impl MergeItem {
@@ -1732,22 +1722,15 @@ impl MergeItem {
17321722
let phi_idx = idx_for_promise(Key::Phi(name, range));
17331723
let mut myself = Self {
17341724
phi_idx,
1735-
default: info.default,
1736-
branch_idxs: SmallSet::new(),
1737-
flow_styles: Vec::with_capacity(n_branches),
1725+
flows: Vec::with_capacity(n_branches),
17381726
};
17391727
myself.add_branch(info);
17401728
myself
17411729
}
17421730

17431731
/// Add the flow info at the end of a branch to our merge item.
17441732
fn add_branch(&mut self, info: FlowInfo) {
1745-
if info.idx != self.phi_idx {
1746-
// Optimization: instead of x = phi(x, ...), we can skip the x.
1747-
// Avoids a recursive solving step later.
1748-
self.branch_idxs.insert(info.idx);
1749-
}
1750-
self.flow_styles.push(info.style);
1733+
self.flows.push(info);
17511734
}
17521735

17531736
/// Get the flow info for an item in the merged flow, which is a combination
@@ -1767,40 +1750,57 @@ impl MergeItem {
17671750
contained_in_loop: bool,
17681751
insert_binding_idx: impl FnOnce(Idx<Key>, Binding),
17691752
) -> FlowInfo {
1753+
// In a loop, an invariant is that if a name was defined above the loop, the
1754+
// default may be taken from any of the Flows and will not differ.
1755+
//
1756+
// If a name is first defined inside a loop, the defaults might
1757+
// differ but for valid code it won't matter because the phi won't appear
1758+
// recursively. Invalid code where assignment tries to use an
1759+
// uninitialized local can produce a cycle through Anywhere, but that's
1760+
// true even for straight-line control flow.
1761+
let default = self.flows.first().unwrap().default;
1762+
// Collect the branch idxs. Skip over the Phi itself (which may appear in loops)
1763+
// both so that we can eliminate the Phi binding entirely when it isn't needed
1764+
// and so that Phi does not depend on itself and cause recursion in the solver.
1765+
let branch_idxs: SmallSet<_> = self
1766+
.flows
1767+
.iter()
1768+
.filter_map(|flow| {
1769+
if flow.idx != self.phi_idx {
1770+
Some(flow.idx)
1771+
} else {
1772+
None
1773+
}
1774+
})
1775+
.collect();
17701776
let downstream_idx = {
1771-
if self.branch_idxs.len() == 1 {
1772-
// There is only one key no matter the branch. Use a forward for the
1773-
// phi idx (which might be unused, but because of speculative phi if this
1774-
// is a loop we may have to put something at the phi) and use the original
1775-
// idx downstream.
1776-
//
1777-
// For ordinary branching, this happens when no branch narrows or assigns.
1777+
if branch_idxs.len() == 1 {
1778+
// We hit this case if no branch assigned or narrowed the name.
17781779
//
1779-
// For loops, we always have at least two different `idx`s in merged flows,
1780-
// the one from above the loop and the Phi. This only works because
1781-
// `add_branch` skips over the phi.
1782-
let upstream_idx = self.branch_idxs.into_iter().next().unwrap();
1780+
// In the case of loops, it depends on the removal of `self.phi_idx` above.
1781+
let flow = self.flows.first().unwrap();
1782+
let upstream_idx = flow.idx;
17831783
insert_binding_idx(self.phi_idx, Binding::Forward(upstream_idx));
17841784
upstream_idx
17851785
} else if current_is_loop {
17861786
insert_binding_idx(
17871787
self.phi_idx,
1788-
Binding::Default(self.default, Box::new(Binding::Phi(self.branch_idxs))),
1788+
Binding::Default(default, Box::new(Binding::Phi(branch_idxs))),
17891789
);
17901790
self.phi_idx
17911791
} else {
1792-
insert_binding_idx(self.phi_idx, Binding::Phi(self.branch_idxs));
1792+
insert_binding_idx(self.phi_idx, Binding::Phi(branch_idxs));
17931793
self.phi_idx
17941794
}
17951795
};
17961796
FlowInfo {
17971797
idx: downstream_idx,
17981798
default: if contained_in_loop {
1799-
self.default
1799+
default
18001800
} else {
18011801
downstream_idx
18021802
},
1803-
style: FlowStyle::merged(self.flow_styles),
1803+
style: FlowStyle::merged(self.flows.into_iter().map(|flow| flow.style)),
18041804
}
18051805
}
18061806
}

0 commit comments

Comments
 (0)