Browse Source

DetectExitPoints: introduce exit points for loops+switch

This allows reverting the changes to HighLevelLoopTransform+ReduceNestingTransform from the previous commit, which fixes a bug in loop detection (the previous commit did not handle loops where the loop BlockContainer didn't have a Block as parent).
pull/2425/head
Daniel Grunwald 4 years ago
parent
commit
685a79dc31
  1. 26
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/ReduceNesting.cs
  2. 4
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  3. 17
      ICSharpCode.Decompiler/IL/ControlFlow/ExitPoints.cs
  4. 25
      ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs
  5. 4
      ICSharpCode.Decompiler/IL/Transforms/ReduceNestingTransform.cs

26
ICSharpCode.Decompiler.Tests/TestCases/Pretty/ReduceNesting.cs

@ -198,15 +198,16 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
case 1: case 1:
Console.WriteLine("case 1"); Console.WriteLine("case 1");
return; return;
default: }
if (B(1)) if (B(1))
{ {
Console.WriteLine(1); Console.WriteLine(1);
}
return;
} }
} }
Console.WriteLine("else"); else
{
Console.WriteLine("else");
}
} }
// nesting should not be reduced as maximum nesting level is 1 // nesting should not be reduced as maximum nesting level is 1
@ -346,13 +347,12 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
Console.WriteLine(); Console.WriteLine();
if (!B(1)) if (B(1))
{
return;
}
for (int i = 0; i < 10; i++)
{ {
Console.WriteLine(i); for (int i = 0; i < 10; i++)
{
Console.WriteLine(i);
}
} }
} }
catch catch

4
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -93,7 +93,7 @@ namespace ICSharpCode.Decompiler.CSharp
new YieldReturnDecompiler(), // must run after inlining but before loop detection new YieldReturnDecompiler(), // must run after inlining but before loop detection
new AsyncAwaitDecompiler(), // must run after inlining but before loop detection new AsyncAwaitDecompiler(), // must run after inlining but before loop detection
new DetectCatchWhenConditionBlocks(), // must run after inlining but before loop detection new DetectCatchWhenConditionBlocks(), // must run after inlining but before loop detection
new DetectExitPoints(canIntroduceExitForReturn: false), new DetectExitPoints(),
new EarlyExpressionTransforms(), new EarlyExpressionTransforms(),
// RemoveDeadVariableInit must run after EarlyExpressionTransforms so that stobj(ldloca V, ...) // RemoveDeadVariableInit must run after EarlyExpressionTransforms so that stobj(ldloca V, ...)
// is already collapsed into stloc(V, ...). // is already collapsed into stloc(V, ...).
@ -117,7 +117,7 @@ namespace ICSharpCode.Decompiler.CSharp
} }
}, },
// re-run DetectExitPoints after loop detection // re-run DetectExitPoints after loop detection
new DetectExitPoints(canIntroduceExitForReturn: false), new DetectExitPoints(),
new BlockILTransform { // per-block transforms new BlockILTransform { // per-block transforms
PostOrderTransforms = { PostOrderTransforms = {
new ConditionDetection(), new ConditionDetection(),

17
ICSharpCode.Decompiler/IL/ControlFlow/ExitPoints.cs

@ -52,13 +52,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
static readonly Nop ExitNotYetDetermined = new Nop { Comment = "ExitNotYetDetermined" }; static readonly Nop ExitNotYetDetermined = new Nop { Comment = "ExitNotYetDetermined" };
static readonly Nop NoExit = new Nop { Comment = "NoExit" }; static readonly Nop NoExit = new Nop { Comment = "NoExit" };
bool canIntroduceExitForReturn;
public DetectExitPoints(bool canIntroduceExitForReturn)
{
this.canIntroduceExitForReturn = canIntroduceExitForReturn;
}
/// <summary> /// <summary>
/// Gets the next instruction after <paramref name="inst"/> is executed. /// Gets the next instruction after <paramref name="inst"/> is executed.
/// Returns NoExit when the next instruction cannot be identified; /// Returns NoExit when the next instruction cannot be identified;
@ -214,12 +207,12 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
static ILInstruction ChooseExit(List<ILInstruction> potentialExits) static ILInstruction ChooseExit(List<ILInstruction> potentialExits)
{ {
ILInstruction first = potentialExits[0]; ILInstruction first = potentialExits[0];
if (first is Leave l && l.IsLeavingFunction) if (first is Leave { IsLeavingFunction: true })
{ {
for (int i = 1; i < potentialExits.Count; i++) for (int i = 1; i < potentialExits.Count; i++)
{ {
var exit = potentialExits[i]; var exit = potentialExits[i];
if (!(exit is Leave l2 && l2.IsLeavingFunction)) if (!(exit is Leave { IsLeavingFunction: true }))
return exit; return exit;
} }
} }
@ -256,9 +249,11 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
// we can't introduce any additional exits // we can't introduce any additional exits
return false; return false;
} }
if (inst is Leave l && l.IsLeavingFunction) if (inst is Leave { IsLeavingFunction: true })
{ {
return canIntroduceExitForReturn; // Only convert 'return;' to an exit in a context where we can turn it into 'break;'.
// In other contexts we risk turning it into 'goto'.
return currentContainer.Kind != ContainerKind.Normal;
} }
else else
{ {

25
ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs

@ -38,25 +38,18 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{ {
this.context = context; this.context = context;
foreach (var block in function.Descendants.OfType<Block>()) foreach (BlockContainer loop in function.Descendants.OfType<BlockContainer>())
{ {
for (int i = 0; i < block.Instructions.Count; i++) if (loop.Kind != ContainerKind.Loop)
continue;
if (MatchWhileLoop(loop, out var condition, out var loopBody))
{ {
var loop = block.Instructions[i] as BlockContainer; if (context.Settings.ForStatement)
if (loop == null || loop.Kind != ContainerKind.Loop) MatchForLoop(loop, condition, loopBody);
continue; continue;
// convert a "return" to a "break" so that we can match the high-level
// loop patterns
RemoveRedundantReturn.ReturnToBreak(block, loop, context);
if (MatchWhileLoop(loop, out var condition, out var loopBody))
{
if (context.Settings.ForStatement)
MatchForLoop(loop, condition, loopBody);
continue;
}
if (context.Settings.DoWhileStatement && MatchDoWhileLoop(loop))
continue;
} }
if (context.Settings.DoWhileStatement && MatchDoWhileLoop(loop))
continue;
} }
} }

4
ICSharpCode.Decompiler/IL/Transforms/ReduceNestingTransform.cs

@ -103,10 +103,6 @@ namespace ICSharpCode.Decompiler.IL
// visit the contents of the container // visit the contents of the container
Visit(container, continueTarget); Visit(container, continueTarget);
if (container.Kind == ContainerKind.Switch)
{
RemoveRedundantReturn.ReturnToBreak(block, container, context);
}
// reduce nesting in switch blocks // reduce nesting in switch blocks
if (container.Kind == ContainerKind.Switch && if (container.Kind == ContainerKind.Switch &&
CanDuplicateExit(NextInsn(), continueTarget, out var keywordExit1) && CanDuplicateExit(NextInsn(), continueTarget, out var keywordExit1) &&

Loading…
Cancel
Save