From f91df962815b8ae1d0a7d6441317c0e827146aa4 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 11 Nov 2017 03:20:56 +0100 Subject: [PATCH] Improve for loop transform: split conditions --- .../IL/Transforms/HighLevelLoopTransform.cs | 56 ++++++++++++++++--- 1 file changed, 49 insertions(+), 7 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs index cbffd4c39..77eb629b9 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs @@ -48,7 +48,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms } } - bool MatchWhileLoop(BlockContainer loop, out ILInstruction condition, out Block loopBody) + bool MatchWhileLoop(BlockContainer loop, out IfInstruction condition, out Block loopBody) { // while-loop: // if (loop-condition) br loop-content-block @@ -64,7 +64,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; if (!ifInstruction.FalseInst.MatchNop()) return false; - condition = ifInstruction.Condition; + condition = ifInstruction; var trueInst = ifInstruction.TrueInst; if (!loop.EntryPoint.Instructions[1].MatchLeave(loop)) return false; @@ -90,13 +90,21 @@ namespace ICSharpCode.Decompiler.IL.Transforms return true; } + void SplitConditions(ILInstruction expression, List conditions) + { + if (expression.MatchLogicAnd(out var l, out var r)) { + SplitConditions(l, conditions); + SplitConditions(r, conditions); + } else { + conditions.Add(expression); + } + } + /// /// Matches a do-while loop and performs the following transformations: /// - combine all compatible conditions into one IfInstruction. /// - extract conditions into a condition block, or move the existing condition block to the end. /// - /// - /// bool MatchDoWhileLoop(BlockContainer loop) { (List conditions, ILInstruction exit, bool swap, bool split) = AnalyzeDoWhileConditions(loop); @@ -230,7 +238,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return true; } - bool MatchForLoop(BlockContainer loop, ILInstruction condition, Block whileLoopBody) + bool MatchForLoop(BlockContainer loop, IfInstruction whileCondition, Block whileLoopBody) { // for loops have exactly two incoming edges at the entry point. if (loop.EntryPoint.IncomingEdgeCount != 2) @@ -267,10 +275,35 @@ namespace ICSharpCode.Decompiler.IL.Transforms // the increment variable must be local/stack variable if (incrementVariable.Kind == VariableKind.Parameter) return false; - // the increment variable must be checked in the condition - if (!condition.Descendants.Any(inst => inst.MatchLdLoc(incrementVariable))) + // split conditions: + var conditions = new List(); + SplitConditions(whileCondition.Condition, conditions); + IfInstruction forCondition = null; + int numberOfConditions = 0; + foreach (var condition in conditions) { + // the increment variable must be used in the condition + if (!condition.Descendants.Any(inst => inst.MatchLdLoc(incrementVariable))) + break; + // condition should not contain an assignment + if (condition.Descendants.Any(IsAssignment)) + break; + if (forCondition == null) { + forCondition = new IfInstruction(condition, whileCondition.TrueInst, whileCondition.FalseInst); + } else { + forCondition.Condition = IfInstruction.LogicAnd(forCondition.Condition, condition); + } + numberOfConditions++; + } + if (numberOfConditions == 0) return false; context.Step("Transform to for loop", loop); + // split condition block: + whileCondition.ReplaceWith(forCondition); + new ExpressionTransforms().Run(loop.EntryPoint, forCondition.ChildIndex, new StatementTransformContext(new BlockTransformContext(context))); + for (int i = conditions.Count - 1; i >= numberOfConditions; i--) { + whileLoopBody.Instructions.Insert(0, new IfInstruction(Comp.LogicNot(conditions[i]), new Leave(loop))); + new ExpressionTransforms().Run(whileLoopBody, 0, new StatementTransformContext(new BlockTransformContext(context))); + } // create a new increment block and add it at the end: int secondToLastIndex = secondToLast.ChildIndex; var newIncremenBlock = new Block(); @@ -287,6 +320,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms return true; } + bool IsAssignment(ILInstruction inst) + { + if (inst is StLoc) + return true; + if (inst is CompoundAssignmentInstruction) + return true; + return false; + } + /// /// Returns true if the instruction is stloc v(add(ldloc v, arg)) /// or stloc v(compound.assign(ldloc v, arg))