@ -303,7 +303,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
@@ -303,7 +303,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
// We need to pick our exit point so that all paths from the loop head
// to the reachable exits run through that exit point.
var cfg = context . ControlFlowGraph . cfg ;
var revCfg = PrepareReverseCFG ( loopHead , treatBackEdgesAsExits ) ;
var revCfg = PrepareReverseCFG ( loopHead , treatBackEdgesAsExits , out int exitNodeArity ) ;
//ControlFlowNode.ExportGraph(cfg).Show("cfg");
//ControlFlowNode.ExportGraph(revCfg).Show("rev");
ControlFlowNode commonAncestor = revCfg [ loopHead . UserIndex ] ;
@ -330,7 +330,19 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
@@ -330,7 +330,19 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
commonAncestor = commonAncestor . ImmediateDominator ;
}
// least common post-dominator is the artificial exit node
return null ;
// This means we're in one of two cases:
// * The loop might have multiple exit points.
// -> we should return null
// * The loop has a single exit point that wasn't considered during post-dominance analysis.
// (which means the single exit isn't dominated by the loop head)
// -> we should return NoExitPoint so that all code dominated by the loop head is included into the loop
if ( exitNodeArity > 1 ) {
return null ;
} else {
// If exitNodeArity == 0, we should maybe look test if our exits out of the block container are all compatible?
// but I don't think it hurts to have a bit too much code inside the loop in this rare case.
return NoExitPoint ;
}
}
}
@ -407,14 +419,33 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
@@ -407,14 +419,33 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
PickExitPoint ( child , ref exitPoint , ref exitPointILOffset ) ;
}
}
ControlFlowNode [ ] PrepareReverseCFG ( ControlFlowNode loopHead , bool treatBackEdgesAsExits )
/// <summary>
/// Constructs a new control flow graph.
/// Each node cfg[i] has a corresponding node rev[i].
/// Edges are only created for nodes dominated by loopHead, and are in reverse from their direction
/// in the primary CFG.
/// An artificial exit node is used for edges that leave the set of nodes dominated by loopHead,
/// or that leave the block Container.
/// </summary>
/// <param name="loopHead">Entry point of the loop.</param>
/// <param name="treatBackEdgesAsExits">Whether to treat loop back edges as exit points.</param>
/// <param name="exitNodeArity">out: The number of different CFG nodes.
/// Possible values:
/// 0 = no CFG nodes used as exit nodes (although edges leaving the block container might still be exits);
/// 1 = a single CFG node (not dominated by loopHead) was used as an exit node;
/// 2 = more than one CFG node (not dominated by loopHead) was used as an exit node.
/// </param>
/// <returns></returns>
ControlFlowNode [ ] PrepareReverseCFG ( ControlFlowNode loopHead , bool treatBackEdgesAsExits , out int exitNodeArity )
{
ControlFlowNode [ ] cfg = context . ControlFlowGraph . cfg ;
ControlFlowNode [ ] rev = new ControlFlowNode [ cfg . Length + 1 ] ;
for ( int i = 0 ; i < cfg . Length ; i + + ) {
rev [ i ] = new ControlFlowNode { UserIndex = i , UserData = cfg [ i ] . UserData } ;
}
ControlFlowNode nodeTreatedAsExitNode = null ;
bool multipleNodesTreatedAsExitNodes = false ;
ControlFlowNode exitNode = new ControlFlowNode { UserIndex = - 1 } ;
rev [ cfg . Length ] = exitNode ;
for ( int i = 0 ; i < cfg . Length ; i + + ) {
@ -425,6 +456,10 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
@@ -425,6 +456,10 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
if ( loopHead . Dominates ( succ ) & & ( ! treatBackEdgesAsExits | | loopHead ! = succ ) ) {
rev [ succ . UserIndex ] . AddEdgeTo ( rev [ i ] ) ;
} else {
if ( nodeTreatedAsExitNode = = null )
nodeTreatedAsExitNode = succ ;
if ( nodeTreatedAsExitNode ! = succ )
multipleNodesTreatedAsExitNodes = true ;
exitNode . AddEdgeTo ( rev [ i ] ) ;
}
}
@ -432,6 +467,12 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
@@ -432,6 +467,12 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
exitNode . AddEdgeTo ( rev [ i ] ) ;
}
}
if ( multipleNodesTreatedAsExitNodes )
exitNodeArity = 2 ; // more than 1
else if ( nodeTreatedAsExitNode ! = null )
exitNodeArity = 1 ;
else
exitNodeArity = 0 ;
Dominance . ComputeDominance ( exitNode , context . CancellationToken ) ;
return rev ;
}