@@ -450,10 +450,9 @@ pub enum FlowStyle {
450450}
451451
452452impl 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
17201710impl 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