diff --git a/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraph.cs b/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraph.cs index 64b866c54..7cc815a60 100644 --- a/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraph.cs +++ b/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraph.cs @@ -120,7 +120,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis b => b.Successors, b => { if (b != EntryPoint) { - ControlFlowNode newIdom = b.Predecessors.First(block => block.Visited); + ControlFlowNode newIdom = b.Predecessors.First(block => block.Visited && block != b); // for all other predecessors p of b foreach (ControlFlowNode p in b.Predecessors) { if (p != b && p.ImmediateDominator != null) { diff --git a/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs b/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs index a3b935ac2..6fb4acf8e 100644 --- a/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs +++ b/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs @@ -18,7 +18,8 @@ namespace ICSharpCode.Decompiler.ILAst foreach (ILNode node in method.GetSelfAndChildrenRecursive()) { ILNode previousChild = null; foreach (ILNode child in node.GetChildren()) { - Debug.Assert(!parent.ContainsKey(child)); + if (parent.ContainsKey(child)) + throw new Exception("The following expression is linked from several locations: " + child.ToString()); parent[child] = node; if (previousChild != null) nextSibling[previousChild] = child; diff --git a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs index 609c5f763..eb19e00a8 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs @@ -18,34 +18,43 @@ namespace ICSharpCode.Decompiler.ILAst YieldReturn, SplitToMovableBlocks, TypeInference, - PeepholeOptimizations, SimplifyShortCircuit, SimplifyTernaryOperator, SimplifyNullCoalescing, - MoreSimplifyPasses, + TransformDecimalCtorToConstant, + SimplifyLdObjAndStObj, + TransformArrayInitializers, + TransformCollectionInitializers, + MakeAssignmentExpression, + InlineVariables2, FindLoops, FindConditions, FlattenNestedMovableBlocks, + RemoveRedundantCode2, GotoRemoval, DuplicateReturns, - FlattenIfStatements, - InlineVariables2, - PeepholeTransforms, + ReduceIfNesting, + InlineVariables3, + CachedDelegateInitialization, + IntroduceFixedStatements, TypeInference2, + RemoveRedundantCode3, None } - public class ILAstOptimizer + public partial class ILAstOptimizer { int nextLabelIndex = 0; DecompilerContext context; TypeSystem typeSystem; + ILBlock method; public void Optimize(DecompilerContext context, ILBlock method, ILAstOptimizationStep abortBeforeStep = ILAstOptimizationStep.None) { this.context = context; this.typeSystem = context.CurrentMethod.Module.TypeSystem; + this.method = method; if (abortBeforeStep == ILAstOptimizationStep.RemoveRedundantCode) return; RemoveRedundantCode(method); @@ -77,7 +86,6 @@ namespace ICSharpCode.Decompiler.ILAst // Types are needed for the ternary operator optimization TypeAnalysis.Run(context, method); - if (abortBeforeStep == ILAstOptimizationStep.PeepholeOptimizations) return; AnalyseLabels(method); foreach(ILBlock block in method.GetSelfAndChildrenRecursive()) { bool modified; @@ -85,18 +93,48 @@ namespace ICSharpCode.Decompiler.ILAst modified = false; if (abortBeforeStep == ILAstOptimizationStep.SimplifyShortCircuit) return; - modified |= block.RunPeepholeOptimization(TrySimplifyShortCircuit); + modified |= block.RunOptimization(SimplifyShortCircuit); if (abortBeforeStep == ILAstOptimizationStep.SimplifyTernaryOperator) return; - modified |= block.RunPeepholeOptimization(TrySimplifyTernaryOperator); + modified |= block.RunOptimization(SimplifyTernaryOperator); if (abortBeforeStep == ILAstOptimizationStep.SimplifyNullCoalescing) return; - modified |= block.RunPeepholeOptimization(TrySimplifyNullCoalescing); + modified |= block.RunOptimization(SimplifyNullCoalescing); - if (abortBeforeStep == ILAstOptimizationStep.MoreSimplifyPasses) return; } while(modified); } + ILInlining inlining2 = new ILInlining(method); + inlining2.InlineAllVariables(); + inlining2.CopyPropagation(); + + foreach(ILBlock block in method.GetSelfAndChildrenRecursive()) { + + // Intentionaly outside the while(modifed) loop, + // I will put it there later after more testing + + bool modified = false; + + if (abortBeforeStep == ILAstOptimizationStep.TransformDecimalCtorToConstant) return; + modified |= block.RunOptimization(TransformDecimalCtorToConstant); + + if (abortBeforeStep == ILAstOptimizationStep.SimplifyLdObjAndStObj) return; + modified |= block.RunOptimization(SimplifyLdObjAndStObj); + + if (abortBeforeStep == ILAstOptimizationStep.TransformArrayInitializers) return; + modified |= block.RunOptimization(Initializers.TransformArrayInitializers); + modified |= block.RunOptimization(Initializers.TransformArrayInitializers); + + if (abortBeforeStep == ILAstOptimizationStep.TransformCollectionInitializers) return; + modified |= block.RunOptimization(Initializers.TransformCollectionInitializers); + + if (abortBeforeStep == ILAstOptimizationStep.MakeAssignmentExpression) return; + modified |= block.RunOptimization(MakeAssignmentExpression); + + if (abortBeforeStep == ILAstOptimizationStep.InlineVariables2) return; + modified |= new ILInlining(method).InlineAllInBlock(block); + } + if (abortBeforeStep == ILAstOptimizationStep.FindLoops) return; foreach(ILBlock block in method.GetSelfAndChildrenRecursive()) { new LoopsAndConditions(context).FindLoops(block); @@ -110,29 +148,44 @@ namespace ICSharpCode.Decompiler.ILAst if (abortBeforeStep == ILAstOptimizationStep.FlattenNestedMovableBlocks) return; FlattenBasicBlocks(method); - if (abortBeforeStep == ILAstOptimizationStep.GotoRemoval) return; + if (abortBeforeStep == ILAstOptimizationStep.RemoveRedundantCode2) return; RemoveRedundantCode(method); + + if (abortBeforeStep == ILAstOptimizationStep.GotoRemoval) return; new GotoRemoval().RemoveGotos(method); if (abortBeforeStep == ILAstOptimizationStep.DuplicateReturns) return; DuplicateReturnStatements(method); - if (abortBeforeStep == ILAstOptimizationStep.FlattenIfStatements) return; - FlattenIfStatements(method); + if (abortBeforeStep == ILAstOptimizationStep.ReduceIfNesting) return; + ReduceIfNesting(method); - if (abortBeforeStep == ILAstOptimizationStep.InlineVariables2) return; + if (abortBeforeStep == ILAstOptimizationStep.InlineVariables3) return; // The 2nd inlining pass is necessary because DuplicateReturns and the introduction of ternary operators // open up additional inlining possibilities. new ILInlining(method).InlineAllVariables(); - TypeAnalysis.Reset(method); + if (abortBeforeStep == ILAstOptimizationStep.CachedDelegateInitialization) return; + foreach(ILBlock block in method.GetSelfAndChildrenRecursive()) { + for (int i = 0; i < block.Body.Count; i++) { + // TODO: Move before loops + CachedDelegateInitialization(block, ref i); + } + } - if (abortBeforeStep == ILAstOptimizationStep.PeepholeTransforms) return; - PeepholeTransforms.Run(context, method); + if (abortBeforeStep == ILAstOptimizationStep.IntroduceFixedStatements) return; + foreach(ILBlock block in method.GetSelfAndChildrenRecursive()) { + for (int i = 0; i < block.Body.Count; i++) { + // TODO: Move before loops + IntroduceFixedStatements(block.Body, i); + } + } if (abortBeforeStep == ILAstOptimizationStep.TypeInference2) return; + TypeAnalysis.Reset(method); TypeAnalysis.Run(context, method); + if (abortBeforeStep == ILAstOptimizationStep.RemoveRedundantCode3) return; GotoRemoval.RemoveRedundantCode(method); // ReportUnassignedILRanges(method); @@ -234,59 +287,51 @@ namespace ICSharpCode.Decompiler.ILAst List basicBlocks = new List(); ILBasicBlock basicBlock = new ILBasicBlock() { - EntryLabel = new ILLabel() { Name = "Block_" + (nextLabelIndex++) } + EntryLabel = block.Body.FirstOrDefault() as ILLabel ?? new ILLabel() { Name = "Block_" + (nextLabelIndex++) } }; basicBlocks.Add(basicBlock); block.EntryGoto = new ILExpression(ILCode.Br, basicBlock.EntryLabel); if (block.Body.Count > 0) { - basicBlock.Body.Add(block.Body[0]); + if (block.Body[0] != basicBlock.EntryLabel) + basicBlock.Body.Add(block.Body[0]); for (int i = 1; i < block.Body.Count; i++) { ILNode lastNode = block.Body[i - 1]; ILNode currNode = block.Body[i]; - // Insert split + // Start a new basic block if necessary if (currNode is ILLabel || lastNode is ILTryCatchBlock || currNode is ILTryCatchBlock || - (lastNode is ILExpression) && ((ILExpression)lastNode).IsBranch() || - (currNode is ILExpression) && (((ILExpression)currNode).IsBranch() && ((ILExpression)currNode).Code.CanFallThough() && basicBlock.Body.Count > 0)) + (lastNode is ILExpression && ((ILExpression)lastNode).IsBranch())) { - ILBasicBlock lastBlock = basicBlock; - basicBlock = new ILBasicBlock(); - basicBlocks.Add(basicBlock); + // Try to reuse the label + ILLabel label = currNode is ILLabel ? ((ILLabel)currNode) : new ILLabel() { Name = "Block_" + (nextLabelIndex++) }; - if (currNode is ILLabel) { - // Insert as entry label - basicBlock.EntryLabel = (ILLabel)currNode; - } else { - basicBlock.EntryLabel = new ILLabel() { Name = "Block_" + (nextLabelIndex++) }; - basicBlock.Body.Add(currNode); + // Terminate the last block + if (lastNode.CanFallThough()) { + // Explicit branch from one block to other + basicBlock.FallthoughGoto = new ILExpression(ILCode.Br, label); + } else if (lastNode.Match(ILCode.Br)) { + // Reuse the existing goto as FallthoughGoto + basicBlock.FallthoughGoto = (ILExpression)lastNode; + basicBlock.Body.RemoveAt(basicBlock.Body.Count - 1); } - // Explicit branch from one block to other - // (unless the last expression was unconditional branch) - if (!(lastNode is ILExpression) || ((ILExpression)lastNode).Code.CanFallThough()) { - lastBlock.FallthoughGoto = new ILExpression(ILCode.Br, basicBlock.EntryLabel); - } - } else { + // Start the new block + basicBlock = new ILBasicBlock(); + basicBlocks.Add(basicBlock); + basicBlock.EntryLabel = label; + } + + // Add the node to the basic block + if (currNode != basicBlock.EntryLabel) { basicBlock.Body.Add(currNode); } } } - foreach (ILBasicBlock bb in basicBlocks) { - if (bb.Body.Count > 0 && - bb.Body.Last() is ILExpression && - ((ILExpression)bb.Body.Last()).Code == ILCode.Br) - { - Debug.Assert(bb.FallthoughGoto == null); - bb.FallthoughGoto = (ILExpression)bb.Body.Last(); - bb.Body.RemoveAt(bb.Body.Count - 1); - } - } - block.Body = basicBlocks; return; } @@ -311,10 +356,9 @@ namespace ICSharpCode.Decompiler.ILAst } } - // scope is modified if successful - bool TrySimplifyTernaryOperator(List scope, ILBasicBlock head, int index) + bool SimplifyTernaryOperator(List body, ILBasicBlock head, int pos) { - Debug.Assert(scope.Contains(head)); + Debug.Assert(body.Contains(head)); ILExpression condExpr; ILLabel trueLabel; @@ -327,17 +371,17 @@ namespace ICSharpCode.Decompiler.ILAst ILLabel falseFall; object unused; - if (head.Match(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel) && + if (head.MatchLast(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel) && labelGlobalRefCount[trueLabel] == 1 && labelGlobalRefCount[falseLabel] == 1 && - ((labelToBasicBlock[trueLabel].Match(ILCode.Stloc, out trueLocVar, out trueExpr, out trueFall) && - labelToBasicBlock[falseLabel].Match(ILCode.Stloc, out falseLocVar, out falseExpr, out falseFall)) || - (labelToBasicBlock[trueLabel].Match(ILCode.Ret, out unused, out trueExpr, out trueFall) && - labelToBasicBlock[falseLabel].Match(ILCode.Ret, out unused, out falseExpr, out falseFall))) && + ((labelToBasicBlock[trueLabel].MatchSingle(ILCode.Stloc, out trueLocVar, out trueExpr, out trueFall) && + labelToBasicBlock[falseLabel].MatchSingle(ILCode.Stloc, out falseLocVar, out falseExpr, out falseFall)) || + (labelToBasicBlock[trueLabel].MatchSingle(ILCode.Ret, out unused, out trueExpr, out trueFall) && + labelToBasicBlock[falseLabel].MatchSingle(ILCode.Ret, out unused, out falseExpr, out falseFall))) && trueLocVar == falseLocVar && trueFall == falseFall && - scope.Contains(labelToBasicBlock[trueLabel]) && - scope.Contains(labelToBasicBlock[falseLabel]) + body.Contains(labelToBasicBlock[trueLabel]) && + body.Contains(labelToBasicBlock[falseLabel]) ) { ILCode opCode = trueLocVar != null ? ILCode.Stloc : ILCode.Ret; @@ -385,12 +429,12 @@ namespace ICSharpCode.Decompiler.ILAst // Create ternary expression newExpr = new ILExpression(ILCode.TernaryOp, null, condExpr, trueExpr, falseExpr); } - head.Body = new List() { new ILExpression(opCode, trueLocVar, newExpr) }; + head.Body[head.Body.Count - 1] = new ILExpression(opCode, trueLocVar, newExpr); head.FallthoughGoto = trueFall != null ? new ILExpression(ILCode.Br, trueFall) : null; // Remove the old basic blocks foreach(ILLabel deleteLabel in new [] { trueLabel, falseLabel }) { - scope.RemoveOrThrow(labelToBasicBlock[deleteLabel]); + body.RemoveOrThrow(labelToBasicBlock[deleteLabel]); labelGlobalRefCount.RemoveOrThrow(deleteLabel); labelToBasicBlock.RemoveOrThrow(deleteLabel); } @@ -400,75 +444,62 @@ namespace ICSharpCode.Decompiler.ILAst return false; } - bool TrySimplifyNullCoalescing(List scope, ILBasicBlock head, int index) + bool SimplifyNullCoalescing(List body, ILBasicBlock head, int pos) { // ... // v = ldloc(leftVar) - // br(condBBLabel) - // - // condBBLabel: // brtrue(endBBLabel, ldloc(leftVar)) // br(rightBBLabel) // // rightBBLabel: // v = rightExpr // br(endBBLabel) - // - // endBBLabel: // ... + // => + // ... + // v = NullCoalescing(ldloc(leftVar), rightExpr) + // br(endBBLabel) ILVariable v, v2; ILExpression leftExpr, leftExpr2; - ILVariable leftVar, leftVar2; - ILLabel condBBLabel; - ILBasicBlock condBB; + ILVariable leftVar; ILLabel endBBLabel, endBBLabel2; ILLabel rightBBLabel; ILBasicBlock rightBB; ILExpression rightExpr; - ILBasicBlock endBB; - if (head.Body.LastOrDefault().Match(ILCode.Stloc, out v, out leftExpr) && + if (head.Body.Count >= 2 && + head.Body[head.Body.Count - 2].Match(ILCode.Stloc, out v, out leftExpr) && leftExpr.Match(ILCode.Ldloc, out leftVar) && - head.FallthoughGoto.Match(ILCode.Br, out condBBLabel) && - labelToBasicBlock.TryGetValue(condBBLabel, out condBB) && - condBB.Match(ILCode.Brtrue, out endBBLabel, out leftExpr2, out rightBBLabel) && - leftExpr2.Match(ILCode.Ldloc, out leftVar2) && - leftVar == leftVar2 && + head.MatchLast(ILCode.Brtrue, out endBBLabel, out leftExpr2, out rightBBLabel) && + leftExpr2.Match(ILCode.Ldloc, leftVar) && labelToBasicBlock.TryGetValue(rightBBLabel, out rightBB) && - rightBB.Match(ILCode.Stloc, out v2, out rightExpr, out endBBLabel2) && + rightBB.MatchSingle(ILCode.Stloc, out v2, out rightExpr, out endBBLabel2) && v == v2 && endBBLabel == endBBLabel2 && - labelToBasicBlock.TryGetValue(endBBLabel, out endBB) && - labelGlobalRefCount.GetOrDefault(condBBLabel) == 1 && labelGlobalRefCount.GetOrDefault(rightBBLabel) == 1 && - labelGlobalRefCount.GetOrDefault(endBBLabel) == 2 && - scope.Contains(condBB) && - scope.Contains(rightBB) && - scope.Contains(endBB) + body.Contains(rightBB) ) { - head.Body[head.Body.Count - 1] = new ILExpression(ILCode.Stloc, v, new ILExpression(ILCode.NullCoalescing, null, leftExpr, rightExpr)); + head.Body[head.Body.Count - 2] = new ILExpression(ILCode.Stloc, v, new ILExpression(ILCode.NullCoalescing, null, leftExpr, rightExpr)); + head.Body.RemoveAt(head.Body.Count - 1); head.FallthoughGoto = new ILExpression(ILCode.Br, endBBLabel); - foreach(ILLabel deleteLabel in new [] { condBBLabel, rightBBLabel }) { - scope.RemoveOrThrow(labelToBasicBlock[deleteLabel]); - labelGlobalRefCount.RemoveOrThrow(deleteLabel); - labelToBasicBlock.RemoveOrThrow(deleteLabel); - } + body.RemoveOrThrow(labelToBasicBlock[rightBBLabel]); + labelGlobalRefCount.RemoveOrThrow(rightBBLabel); + labelToBasicBlock.RemoveOrThrow(rightBBLabel); return true; } return false; } - // scope is modified if successful - bool TrySimplifyShortCircuit(List scope, ILBasicBlock head, int index) + bool SimplifyShortCircuit(List body, ILBasicBlock head, int pos) { - Debug.Assert(scope.Contains(head)); + Debug.Assert(body.Contains(head)); ILExpression condExpr; ILLabel trueLabel; ILLabel falseLabel; - if(head.Match(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel)) { + if(head.MatchLast(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel)) { for (int pass = 0; pass < 2; pass++) { // On the second pass, swap labels and negate expression of the first branch @@ -481,24 +512,24 @@ namespace ICSharpCode.Decompiler.ILAst ILExpression nextCondExpr; ILLabel nextTrueLablel; ILLabel nextFalseLabel; - if (scope.Contains(nextBasicBlock) && + if (body.Contains(nextBasicBlock) && nextBasicBlock != head && labelGlobalRefCount[nextBasicBlock.EntryLabel] == 1 && - nextBasicBlock.Match(ILCode.Brtrue, out nextTrueLablel, out nextCondExpr, out nextFalseLabel) && + nextBasicBlock.MatchSingle(ILCode.Brtrue, out nextTrueLablel, out nextCondExpr, out nextFalseLabel) && (otherLablel == nextFalseLabel || otherLablel == nextTrueLablel)) { // Create short cicuit branch if (otherLablel == nextFalseLabel) { - head.Body[0] = new ILExpression(ILCode.Brtrue, nextTrueLablel, new ILExpression(ILCode.LogicAnd, null, negate ? new ILExpression(ILCode.LogicNot, null, condExpr) : condExpr, nextCondExpr)); + head.Body[head.Body.Count - 1] = new ILExpression(ILCode.Brtrue, nextTrueLablel, new ILExpression(ILCode.LogicAnd, null, negate ? new ILExpression(ILCode.LogicNot, null, condExpr) : condExpr, nextCondExpr)); } else { - head.Body[0] = new ILExpression(ILCode.Brtrue, nextTrueLablel, new ILExpression(ILCode.LogicOr, null, negate ? condExpr : new ILExpression(ILCode.LogicNot, null, condExpr), nextCondExpr)); + head.Body[head.Body.Count - 1] = new ILExpression(ILCode.Brtrue, nextTrueLablel, new ILExpression(ILCode.LogicOr, null, negate ? condExpr : new ILExpression(ILCode.LogicNot, null, condExpr), nextCondExpr)); } head.FallthoughGoto = new ILExpression(ILCode.Br, nextFalseLabel); // Remove the inlined branch from scope labelGlobalRefCount.RemoveOrThrow(nextBasicBlock.EntryLabel); labelToBasicBlock.RemoveOrThrow(nextBasicBlock.EntryLabel); - scope.RemoveOrThrow(nextBasicBlock); + body.RemoveOrThrow(nextBasicBlock); return true; } @@ -589,7 +620,7 @@ namespace ICSharpCode.Decompiler.ILAst /// Reduce the nesting of conditions. /// It should be done on flat data that already had most gotos removed /// - void FlattenIfStatements(ILNode node) + void ReduceIfNesting(ILNode node) { ILBlock block = node as ILBlock; if (block != null) { @@ -624,7 +655,7 @@ namespace ICSharpCode.Decompiler.ILAst // We are changing the number of blocks so we use plain old recursion to get all blocks foreach(ILNode child in node.GetChildren()) { if (child != null && !(child is ILExpression)) - FlattenIfStatements(child); + ReduceIfNesting(child); } } @@ -642,7 +673,7 @@ namespace ICSharpCode.Decompiler.ILAst /// Perform one pass of a given optimization on this block. /// This block must consist of only basicblocks. /// - public static bool RunPeepholeOptimization(this ILBlock block, Func, ILBasicBlock, int, bool> optimization) + public static bool RunOptimization(this ILBlock block, Func, ILBasicBlock, int, bool> optimization) { bool modified = false; List body = block.Body; @@ -657,6 +688,23 @@ namespace ICSharpCode.Decompiler.ILAst return modified; } + public static bool RunOptimization(this ILBlock block, Func, ILExpression, int, bool> optimization) + { + bool modified = false; + foreach (ILBasicBlock bb in block.Body) { + for (int j = 0; j < bb.Body.Count;) { + ILExpression expr = bb.Body[j] as ILExpression; + if (expr != null && optimization(bb.Body, expr, j)) { + modified = true; + j = Math.Max(0, j - 1); // Go back one step + } else { + j++; + } + } + } + return modified; + } + public static bool CanFallThough(this ILNode node) { ILExpression expr = node as ILExpression; @@ -689,6 +737,24 @@ namespace ICSharpCode.Decompiler.ILAst } } + public static bool IsStoreToArray(this ILCode code) + { + switch (code) { + case ILCode.Stelem_Any: + case ILCode.Stelem_I: + case ILCode.Stelem_I1: + case ILCode.Stelem_I2: + case ILCode.Stelem_I4: + case ILCode.Stelem_I8: + case ILCode.Stelem_R4: + case ILCode.Stelem_R8: + case ILCode.Stelem_Ref: + return true; + default: + return false; + } + } + /// /// Can the expression be used as a statement in C#? /// diff --git a/ICSharpCode.Decompiler/ILAst/ILInlining.cs b/ICSharpCode.Decompiler/ILAst/ILInlining.cs index 00978db58..79cf33365 100644 --- a/ICSharpCode.Decompiler/ILAst/ILInlining.cs +++ b/ICSharpCode.Decompiler/ILAst/ILInlining.cs @@ -48,41 +48,66 @@ namespace ICSharpCode.Decompiler.ILAst } } - public void InlineAllVariables() + public bool InlineAllVariables() { + bool modified = false; ILInlining i = new ILInlining(method); foreach (ILBlock block in method.GetSelfAndChildrenRecursive()) - i.InlineAllInBlock(block); + modified |= i.InlineAllInBlock(block); + return modified; } - public void InlineAllInBlock(ILBlock block) + public bool InlineAllInBlock(ILBlock block) { + bool modified = false; List body = block.Body; for(int i = 0; i < body.Count - 1;) { ILVariable locVar; ILExpression expr; - if (body[i].Match(ILCode.Stloc, out locVar, out expr) && InlineOneIfPossible(block, i, aggressive: false)) { + if (body[i].Match(ILCode.Stloc, out locVar, out expr) && InlineOneIfPossible(block.Body, i, aggressive: false)) { + modified = true; i = Math.Max(0, i - 1); // Go back one step } else { i++; } } + foreach(ILBasicBlock bb in body.OfType()) { + modified |= InlineAllInBasicBlock(bb); + } + return modified; + } + + public bool InlineAllInBasicBlock(ILBasicBlock bb) + { + bool modified = false; + List body = bb.Body; + for(int i = 0; i < body.Count - 1;) { + ILVariable locVar; + ILExpression expr; + if (body[i].Match(ILCode.Stloc, out locVar, out expr) && InlineOneIfPossible(bb.Body, i, aggressive: false)) { + modified = true; + i = Math.Max(0, i - 1); // Go back one step + } else { + i++; + } + } + return modified; } /// /// Inlines instructions before pos into block.Body[pos]. /// /// The number of instructions that were inlined. - public int InlineInto(ILBlock block, int pos, bool aggressive) + public int InlineInto(List body, int pos, bool aggressive) { - if (pos >= block.Body.Count) + if (pos >= body.Count) return 0; int count = 0; while (--pos >= 0) { - ILExpression expr = block.Body[pos] as ILExpression; + ILExpression expr = body[pos] as ILExpression; if (expr == null || expr.Code != ILCode.Stloc) break; - if (InlineOneIfPossible(block, pos, aggressive)) + if (InlineOneIfPossible(body, pos, aggressive)) count++; else break; @@ -97,10 +122,10 @@ namespace ICSharpCode.Decompiler.ILAst /// /// After the operation, pos will point to the new combined instruction. /// - public bool InlineIfPossible(ILBlock block, ref int pos) + public bool InlineIfPossible(List body, ref int pos) { - if (InlineOneIfPossible(block, pos, true)) { - pos -= InlineInto(block, pos, false); + if (InlineOneIfPossible(body, pos, true)) { + pos -= InlineInto(body, pos, false); return true; } return false; @@ -109,28 +134,28 @@ namespace ICSharpCode.Decompiler.ILAst /// /// Inlines the stloc instruction at block.Body[pos] into the next instruction, if possible. /// - public bool InlineOneIfPossible(ILBlock block, int pos, bool aggressive) + public bool InlineOneIfPossible(List body, int pos, bool aggressive) { ILVariable v; ILExpression inlinedExpression; - if (block.Body[pos].Match(ILCode.Stloc, out v, out inlinedExpression) && !v.IsPinned) { - if (InlineIfPossible(v, inlinedExpression, block.Body.ElementAtOrDefault(pos+1), aggressive)) { + if (body[pos].Match(ILCode.Stloc, out v, out inlinedExpression) && !v.IsPinned) { + if (InlineIfPossible(v, inlinedExpression, body.ElementAtOrDefault(pos+1), aggressive)) { // Assign the ranges of the stloc instruction: - inlinedExpression.ILRanges.AddRange(((ILExpression)block.Body[pos]).ILRanges); + inlinedExpression.ILRanges.AddRange(((ILExpression)body[pos]).ILRanges); // Remove the stloc instruction: - block.Body.RemoveAt(pos); + body.RemoveAt(pos); return true; } else if (numLdloc.GetOrDefault(v) == 0 && numLdloca.GetOrDefault(v) == 0) { // The variable is never loaded if (inlinedExpression.HasNoSideEffects()) { // Remove completely - block.Body.RemoveAt(pos); + body.RemoveAt(pos); return true; } else if (inlinedExpression.CanBeExpressionStatement() && v.IsGenerated) { // Assign the ranges of the stloc instruction: - inlinedExpression.ILRanges.AddRange(((ILExpression)block.Body[pos]).ILRanges); + inlinedExpression.ILRanges.AddRange(((ILExpression)body[pos]).ILRanges); // Remove the stloc, but keep the inner expression - block.Body[pos] = inlinedExpression; + body[pos] = inlinedExpression; return true; } } @@ -155,7 +180,7 @@ namespace ICSharpCode.Decompiler.ILAst ILExpression parent; int pos; if (FindLoadInNext(next as ILExpression, v, inlinedExpression, out parent, out pos) == true) { - if (!aggressive && !v.IsGenerated && !NonAggressiveInlineInto((ILExpression)next, parent)) + if (!aggressive && !v.IsGenerated && !NonAggressiveInlineInto((ILExpression)next, parent, inlinedExpression)) return false; // Assign the ranges of the ldloc instruction: @@ -168,8 +193,15 @@ namespace ICSharpCode.Decompiler.ILAst return false; } - bool NonAggressiveInlineInto(ILExpression next, ILExpression parent) + bool NonAggressiveInlineInto(ILExpression next, ILExpression parent, ILExpression inlinedExpression) { + switch (inlinedExpression.Code) { + case ILCode.InitArray: + case ILCode.InitCollection: + case ILCode.DefaultValue: + return true; + } + switch (next.Code) { case ILCode.Ret: return parent.Code == ILCode.Ret; @@ -300,7 +332,7 @@ namespace ICSharpCode.Decompiler.ILAst // if we un-inlined stuff; we need to update the usage counters AnalyzeMethod(); } - InlineInto(block, i, aggressive: false); // maybe inlining gets possible after the removal of block.Body[i] + InlineInto(block.Body, i, aggressive: false); // maybe inlining gets possible after the removal of block.Body[i] i -= uninlinedArgs.Length + 1; } } diff --git a/ICSharpCode.Decompiler/ILAst/InitializerPeepholeTransforms.cs b/ICSharpCode.Decompiler/ILAst/InitializerPeepholeTransforms.cs index 9e4dbf8c7..e66cf577b 100644 --- a/ICSharpCode.Decompiler/ILAst/InitializerPeepholeTransforms.cs +++ b/ICSharpCode.Decompiler/ILAst/InitializerPeepholeTransforms.cs @@ -12,25 +12,16 @@ namespace ICSharpCode.Decompiler.ILAst /// /// IL AST transformation that introduces array, object and collection initializers. /// - public class InitializerPeepholeTransforms + public class Initializers { - readonly ILBlock method; - - #region Array Initializers - - public InitializerPeepholeTransforms(ILBlock method) - { - this.method = method; - } - - public void TransformArrayInitializers(ILBlock block, ref int i) + public static bool TransformArrayInitializers(List body, ILExpression expr, int pos) { ILVariable v, v2, v3; ILExpression newarrExpr; TypeReference arrayType; ILExpression lengthExpr; int arrayLength; - if (block.Body[i].Match(ILCode.Stloc, out v, out newarrExpr) && + if (expr.Match(ILCode.Stloc, out v, out newarrExpr) && newarrExpr.Match(ILCode.Newarr, out arrayType, out lengthExpr) && lengthExpr.Match(ILCode.Ldc_I4, out arrayLength) && arrayLength > 0) @@ -39,7 +30,7 @@ namespace ICSharpCode.Decompiler.ILAst ILExpression methodArg1; ILExpression methodArg2; FieldDefinition field; - if (block.Body.ElementAtOrDefault(i + 1).Match(ILCode.Call, out methodRef, out methodArg1, out methodArg2) && + if (body.ElementAtOrDefault(pos + 1).Match(ILCode.Call, out methodRef, out methodArg1, out methodArg2) && methodRef.DeclaringType.FullName == "System.Runtime.CompilerServices.RuntimeHelpers" && methodRef.Name == "InitializeArray" && methodArg1.Match(ILCode.Ldloc, out v2) && @@ -49,59 +40,41 @@ namespace ICSharpCode.Decompiler.ILAst { ILExpression[] newArr = new ILExpression[arrayLength]; if (DecodeArrayInitializer(TypeAnalysis.GetTypeCode(arrayType), field.InitialValue, newArr)) { - block.Body[i] = new ILExpression(ILCode.Stloc, v, new ILExpression(ILCode.InitArray, arrayType, newArr)); - block.Body.RemoveAt(i + 1); - i -= new ILInlining(method).InlineInto(block, i + 1, aggressive: true) - 1; - return; + body[pos] = new ILExpression(ILCode.Stloc, v, new ILExpression(ILCode.InitArray, arrayType, newArr)); + body.RemoveAt(pos + 1); + return true; } } const int maxConsecutiveDefaultValueExpressions = 10; List operands = new List(); int numberOfInstructionsToRemove = 0; - for (int j = i + 1; j < block.Body.Count; j++) { - ILExpression expr = block.Body[j] as ILExpression; - int pos; - if (expr != null && - IsStoreToArray(expr.Code) && - expr.Arguments[0].Match(ILCode.Ldloc, out v3) && + for (int j = pos + 1; j < body.Count; j++) { + ILExpression nextExpr = body[j] as ILExpression; + int arrayPos; + if (nextExpr != null && + nextExpr.Code.IsStoreToArray() && + nextExpr.Arguments[0].Match(ILCode.Ldloc, out v3) && v == v3 && - expr.Arguments[1].Match(ILCode.Ldc_I4, out pos) && - pos >= operands.Count && - pos <= operands.Count + maxConsecutiveDefaultValueExpressions) + nextExpr.Arguments[1].Match(ILCode.Ldc_I4, out arrayPos) && + arrayPos >= operands.Count && + arrayPos <= operands.Count + maxConsecutiveDefaultValueExpressions) { - while (operands.Count < pos) + while (operands.Count < arrayPos) operands.Add(new ILExpression(ILCode.DefaultValue, arrayType)); - operands.Add(expr.Arguments[2]); + operands.Add(nextExpr.Arguments[2]); numberOfInstructionsToRemove++; } else { break; } } if (operands.Count == arrayLength) { - ((ILExpression)block.Body[i]).Arguments[0] = new ILExpression(ILCode.InitArray, arrayType, operands); - block.Body.RemoveRange(i + 1, numberOfInstructionsToRemove); - i -= new ILInlining(method).InlineInto(block, i + 1, aggressive: true) - 1; - } - } - } - - static bool IsStoreToArray(ILCode code) - { - switch (code) { - case ILCode.Stelem_Any: - case ILCode.Stelem_I: - case ILCode.Stelem_I1: - case ILCode.Stelem_I2: - case ILCode.Stelem_I4: - case ILCode.Stelem_I8: - case ILCode.Stelem_R4: - case ILCode.Stelem_R8: - case ILCode.Stelem_Ref: + expr.Arguments[0] = new ILExpression(ILCode.InitArray, arrayType, operands); + body.RemoveRange(pos + 1, numberOfInstructionsToRemove); return true; - default: - return false; + } } + return false; } static bool DecodeArrayInitializer(TypeCode elementType, byte[] initialValue, ILExpression[] output) @@ -165,50 +138,50 @@ namespace ICSharpCode.Decompiler.ILAst return false; } } - #endregion - #region Collection Initilializers - public void TransformCollectionInitializers(ILBlock block, ref int i) + public static bool TransformCollectionInitializers(List body, ILExpression expr, int pos) { - ILVariable v; - ILExpression expr; - if (!block.Body[i].Match(ILCode.Stloc, out v, out expr) || expr.Code != ILCode.Newobj) - return; - MethodReference ctor = (MethodReference)expr.Operand; - TypeDefinition td = ctor.DeclaringType.Resolve(); - if (td == null || !td.Interfaces.Any(intf => intf.Name == "IEnumerable" && intf.Namespace == "System.Collections")) - return; - // This is a collection: we can convert Add() calls into a collection initializer - ILExpression collectionInitializer = new ILExpression(ILCode.InitCollection, null, expr); - for (int j = i + 1; j < block.Body.Count; j++) { - MethodReference addMethod; - List args; - if (!block.Body[j].Match(ILCode.Callvirt, out addMethod, out args)) - break; - if (addMethod.Name != "Add" || !addMethod.HasThis || args.Count < 2 || args[0].Code != ILCode.Ldloc || args[0].Operand != v) - break; - collectionInitializer.Arguments.Add((ILExpression)block.Body[j]); - } - // ensure we added at least one additional arg to the collection initializer: - if (collectionInitializer.Arguments.Count == 1) - return; - ILInlining inline = new ILInlining(method); - ILExpression followingExpr = block.Body.ElementAtOrDefault(i + collectionInitializer.Arguments.Count) as ILExpression; - if (inline.CanInlineInto(followingExpr, v, collectionInitializer)) { - block.Body.RemoveRange(i + 1, collectionInitializer.Arguments.Count - 1); - ((ILExpression)block.Body[i]).Arguments[0] = collectionInitializer; + ILVariable v, v2; + ILExpression newObjExpr; + MethodReference ctor; + List ctorArgs; + if (expr.Match(ILCode.Stloc, out v, out newObjExpr) && + newObjExpr.Match(ILCode.Newobj, out ctor, out ctorArgs)) + { + TypeDefinition td = ctor.DeclaringType.Resolve(); + if (td == null || !td.Interfaces.Any(intf => intf.Name == "IEnumerable" && intf.Namespace == "System.Collections")) + return false; - // Change add methods into InitCollectionAddMethod: - for (int j = 1; j < collectionInitializer.Arguments.Count; j++) { - collectionInitializer.Arguments[j].Arguments.RemoveAt(0); - collectionInitializer.Arguments[j].Code = ILCode.InitCollectionAddMethod; + // This is a collection: we can convert Add() calls into a collection initializer + ILExpression collectionInitializer = new ILExpression(ILCode.InitCollection, null, newObjExpr); + bool anyAdded = false; + while(pos + 1 < body.Count) { + ILExpression nextExpr = body[pos + 1] as ILExpression; + MethodReference addMethod; + List args; + if (nextExpr.Match(ILCode.Callvirt, out addMethod, out args) && + addMethod.Name == "Add" && + addMethod.HasThis && + args.Count == 2 && + args[0].Match(ILCode.Ldloc, out v2) && + v == v2) + { + nextExpr.Code = ILCode.InitCollectionAddMethod; + nextExpr.Arguments.RemoveAt(0); + collectionInitializer.Arguments.Add(nextExpr); + body.RemoveAt(pos + 1); + anyAdded = true; + } else { + break; + } + } + // ensure we added at least one additional arg to the collection initializer: + if (anyAdded) { + expr.Arguments[0] = collectionInitializer; + return true; } - - inline = new ILInlining(method); // refresh variable usage info - if (inline.InlineIfPossible(block, ref i)) - i++; // retry transformations on the new combined instruction } + return false; } - #endregion } } diff --git a/ICSharpCode.Decompiler/ILAst/LoopsAndConditions.cs b/ICSharpCode.Decompiler/ILAst/LoopsAndConditions.cs index 7676bdb86..79516ceac 100644 --- a/ICSharpCode.Decompiler/ILAst/LoopsAndConditions.cs +++ b/ICSharpCode.Decompiler/ILAst/LoopsAndConditions.cs @@ -27,20 +27,24 @@ namespace ICSharpCode.Decompiler.ILAst public void FindLoops(ILBlock block) { - ControlFlowGraph graph; - graph = BuildGraph(block.Body, (ILLabel)block.EntryGoto.Operand); - graph.ComputeDominance(context.CancellationToken); - graph.ComputeDominanceFrontier(); - block.Body = FindLoops(new HashSet(graph.Nodes.Skip(3)), graph.EntryPoint, false); + if (block.Body.Count > 0) { + ControlFlowGraph graph; + graph = BuildGraph(block.Body, (ILLabel)block.EntryGoto.Operand); + graph.ComputeDominance(context.CancellationToken); + graph.ComputeDominanceFrontier(); + block.Body = FindLoops(new HashSet(graph.Nodes.Skip(3)), graph.EntryPoint, false); + } } public void FindConditions(ILBlock block) { - ControlFlowGraph graph; - graph = BuildGraph(block.Body, (ILLabel)block.EntryGoto.Operand); - graph.ComputeDominance(context.CancellationToken); - graph.ComputeDominanceFrontier(); - block.Body = FindConditions(new HashSet(graph.Nodes.Skip(3)), graph.EntryPoint); + if (block.Body.Count > 0) { + ControlFlowGraph graph; + graph = BuildGraph(block.Body, (ILLabel)block.EntryGoto.Operand); + graph.ComputeDominance(context.CancellationToken); + graph.ComputeDominanceFrontier(); + block.Body = FindConditions(new HashSet(graph.Nodes.Skip(3)), graph.EntryPoint); + } } ControlFlowGraph BuildGraph(List nodes, ILLabel entryLabel) @@ -57,7 +61,7 @@ namespace ICSharpCode.Decompiler.ILAst // Create graph nodes labelToCfNode = new Dictionary(); Dictionary astNodeToCfNode = new Dictionary(); - foreach(ILNode node in nodes) { + foreach(ILBasicBlock node in nodes) { ControlFlowNode cfNode = new ControlFlowNode(index++, -1, ControlFlowNodeType.Normal); cfNodes.Add(cfNode); astNodeToCfNode[node] = cfNode; @@ -76,22 +80,18 @@ namespace ICSharpCode.Decompiler.ILAst entryNode.Incoming.Add(entryEdge); // Create edges - foreach(ILNode node in nodes) { + foreach(ILBasicBlock node in nodes) { ControlFlowNode source = astNodeToCfNode[node]; // Find all branches - foreach(ILExpression child in node.GetSelfAndChildrenRecursive()) { - IEnumerable targets = child.GetBranchTargets(); - if (targets != null) { - foreach(ILLabel target in targets) { - ControlFlowNode destination; - // Labels which are out of out scope will not be int the collection - if (labelToCfNode.TryGetValue(target, out destination) && destination != source) { - ControlFlowEdge edge = new ControlFlowEdge(source, destination, JumpType.Normal); - source.Outgoing.Add(edge); - destination.Incoming.Add(edge); - } - } + foreach(ILLabel target in node.GetSelfAndChildrenRecursive(e => e.IsBranch()).SelectMany(e => e.GetBranchTargets())) { + ControlFlowNode destination; + // Labels which are out of out scope will not be int the collection + // Insert self edge only if we are sure we are a loop + if (labelToCfNode.TryGetValue(target, out destination) && (destination != source || target == node.EntryLabel)) { + ControlFlowEdge edge = new ControlFlowEdge(source, destination, JumpType.Normal); + source.Outgoing.Add(edge); + destination.Incoming.Add(edge); } } } @@ -123,7 +123,7 @@ namespace ICSharpCode.Decompiler.ILAst ILExpression condExpr; ILLabel trueLabel; ILLabel falseLabel; - if(basicBlock.Match(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel)) + if(basicBlock.MatchSingle(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel)) { ControlFlowNode trueTarget; labelToCfNode.TryGetValue(trueLabel, out trueTarget); @@ -138,7 +138,7 @@ namespace ICSharpCode.Decompiler.ILAst scope.RemoveOrThrow(node); // If false means enter the loop - if (loopContents.Contains(falseTarget)) + if (loopContents.Contains(falseTarget) || falseTarget == node) { // Negate the condition condExpr = new ILExpression(ILCode.LogicNot, null, condExpr); @@ -156,42 +156,39 @@ namespace ICSharpCode.Decompiler.ILAst loopContents.UnionWith(pullIn); } - // Use loop to implement the condition - result.Add(new ILBasicBlock() { - EntryLabel = basicBlock.EntryLabel, - Body = new List() { - new ILWhileLoop() { - Condition = condExpr, - BodyBlock = new ILBlock() { - EntryGoto = new ILExpression(ILCode.Br, trueLabel), - Body = FindLoops(loopContents, node, true) - } - }, - new ILExpression(ILCode.Br, falseLabel) - }, - FallthoughGoto = null - }); + // Use loop to implement the brtrue + basicBlock.Body.RemoveAt(basicBlock.Body.Count - 1); + basicBlock.Body.Add(new ILWhileLoop() { + Condition = condExpr, + BodyBlock = new ILBlock() { + EntryGoto = new ILExpression(ILCode.Br, trueLabel), + Body = FindLoops(loopContents, node, false) + } + }); + basicBlock.FallthoughGoto = new ILExpression(ILCode.Br, falseLabel); + result.Add(basicBlock); + + scope.ExceptWith(loopContents); } } // Fallback method: while(true) if (scope.Contains(node)) { result.Add(new ILBasicBlock() { - EntryLabel = new ILLabel() { Name = "Loop_" + (nextLabelIndex++) }, - Body = new List() { - new ILWhileLoop() { - BodyBlock = new ILBlock() { - EntryGoto = new ILExpression(ILCode.Br, basicBlock.EntryLabel), - Body = FindLoops(loopContents, node, true) - } - }, - }, - FallthoughGoto = null - }); + EntryLabel = new ILLabel() { Name = "Loop_" + (nextLabelIndex++) }, + Body = new List() { + new ILWhileLoop() { + BodyBlock = new ILBlock() { + EntryGoto = new ILExpression(ILCode.Br, basicBlock.EntryLabel), + Body = FindLoops(loopContents, node, true) + } + }, + }, + FallthoughGoto = null + }); + + scope.ExceptWith(loopContents); } - - // Move the content into loop block - scope.ExceptWith(loopContents); } // Using the dominator tree should ensure we find the the widest loop first @@ -229,24 +226,20 @@ namespace ICSharpCode.Decompiler.ILAst // Find a block that represents a simple condition if (scope.Contains(node)) { - ILBasicBlock block = node.UserData as ILBasicBlock; + ILBasicBlock block = (ILBasicBlock)node.UserData; - if (block != null && block.Body.Count == 1) { - - ILExpression condBranch = block.Body[0] as ILExpression; - + { // Switch ILLabel[] caseLabels; - List switchArgs; - if (condBranch.Match(ILCode.Switch, out caseLabels, out switchArgs)) { + ILExpression switchArg; + ILLabel fallLabel; + if (block.MatchLast(ILCode.Switch, out caseLabels, out switchArg, out fallLabel)) { - ILSwitch ilSwitch = new ILSwitch() { Condition = switchArgs.Single() }; - ILBasicBlock newBB = new ILBasicBlock() { - EntryLabel = block.EntryLabel, // Keep the entry label - Body = { ilSwitch }, - FallthoughGoto = block.FallthoughGoto - }; - result.Add(newBB); + // Replace the switch code with ILSwitch + ILSwitch ilSwitch = new ILSwitch() { Condition = switchArg }; + block.Body.RemoveAt(block.Body.Count - 1); + block.Body.Add(ilSwitch); + result.Add(block); // Remove the item so that it is not picked up as content scope.RemoveOrThrow(node); @@ -259,19 +252,18 @@ namespace ICSharpCode.Decompiler.ILAst } // Pull in code of cases - ILLabel fallLabel = (ILLabel)block.FallthoughGoto.Operand; ControlFlowNode fallTarget = null; labelToCfNode.TryGetValue(fallLabel, out fallTarget); HashSet frontiers = new HashSet(); if (fallTarget != null) - frontiers.UnionWith(fallTarget.DominanceFrontier); + frontiers.UnionWith(fallTarget.DominanceFrontier.Except(new [] { fallTarget })); foreach(ILLabel condLabel in caseLabels) { ControlFlowNode condTarget = null; labelToCfNode.TryGetValue(condLabel, out condTarget); if (condTarget != null) - frontiers.UnionWith(condTarget.DominanceFrontier); + frontiers.UnionWith(condTarget.DominanceFrontier.Except(new [] { condTarget })); } for (int i = 0; i < caseLabels.Length; i++) { @@ -305,7 +297,7 @@ namespace ICSharpCode.Decompiler.ILAst if (content.Any()) { var caseBlock = new ILSwitch.CaseBlock() { EntryGoto = new ILExpression(ILCode.Br, fallLabel) }; ilSwitch.CaseBlocks.Add(caseBlock); - newBB.FallthoughGoto = null; + block.FallthoughGoto = null; scope.ExceptWith(content); caseBlock.Body.AddRange(FindConditions(content, fallTarget)); @@ -319,7 +311,7 @@ namespace ICSharpCode.Decompiler.ILAst ILExpression condExpr; ILLabel trueLabel; ILLabel falseLabel; - if(block.Match(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel)) { + if(block.MatchLast(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel)) { // Swap bodies since that seems to be the usual C# order ILLabel temp = trueLabel; @@ -327,16 +319,16 @@ namespace ICSharpCode.Decompiler.ILAst falseLabel = temp; condExpr = new ILExpression(ILCode.LogicNot, null, condExpr); - // Convert the basic block to ILCondition + // Convert the brtrue to ILCondition ILCondition ilCond = new ILCondition() { Condition = condExpr, TrueBlock = new ILBlock() { EntryGoto = new ILExpression(ILCode.Br, trueLabel) }, FalseBlock = new ILBlock() { EntryGoto = new ILExpression(ILCode.Br, falseLabel) } }; - result.Add(new ILBasicBlock() { - EntryLabel = block.EntryLabel, // Keep the entry label - Body = { ilCond } - }); + block.Body.RemoveAt(block.Body.Count - 1); + block.Body.Add(ilCond); + block.FallthoughGoto = null; + result.Add(block); // Remove the item immediately so that it is not picked up as content scope.RemoveOrThrow(node); @@ -349,9 +341,9 @@ namespace ICSharpCode.Decompiler.ILAst // Pull in the conditional code HashSet frontiers = new HashSet(); if (trueTarget != null) - frontiers.UnionWith(trueTarget.DominanceFrontier); + frontiers.UnionWith(trueTarget.DominanceFrontier.Except(new [] { trueTarget })); if (falseTarget != null) - frontiers.UnionWith(falseTarget.DominanceFrontier); + frontiers.UnionWith(falseTarget.DominanceFrontier.Except(new [] { falseTarget })); if (trueTarget != null && !frontiers.Contains(trueTarget)) { HashSet content = FindDominatedNodes(scope, trueTarget); diff --git a/ICSharpCode.Decompiler/ILAst/PatternMatching.cs b/ICSharpCode.Decompiler/ILAst/PatternMatching.cs index 747d3780a..b8d1f3651 100644 --- a/ICSharpCode.Decompiler/ILAst/PatternMatching.cs +++ b/ICSharpCode.Decompiler/ILAst/PatternMatching.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; - +using System.Linq; using Mono.Cecil; namespace ICSharpCode.Decompiler.ILAst @@ -28,6 +28,16 @@ namespace ICSharpCode.Decompiler.ILAst return false; } + public static bool Match(this ILNode node, ILCode code, T operand) + { + ILExpression expr = node as ILExpression; + if (expr != null && expr.Prefixes == null && expr.Code == code) { + Debug.Assert(expr.Arguments.Count == 0); + return operand.Equals(expr.Operand); + } + return false; + } + public static bool Match(this ILNode node, ILCode code, out List args) { ILExpression expr = node as ILExpression; @@ -88,7 +98,7 @@ namespace ICSharpCode.Decompiler.ILAst return false; } - public static bool Match(this ILBasicBlock bb, ILCode code, out T operand, out ILExpression arg, out ILLabel fallLabel) + public static bool MatchSingle(this ILBasicBlock bb, ILCode code, out T operand, out ILExpression arg, out ILLabel fallLabel) { if (bb.Body.Count == 1) { if (bb.Body[0].Match(code, out operand, out arg)) { @@ -102,6 +112,18 @@ namespace ICSharpCode.Decompiler.ILAst return false; } + public static bool MatchLast(this ILBasicBlock bb, ILCode code, out T operand, out ILExpression arg, out ILLabel fallLabel) + { + if (bb.Body.LastOrDefault().Match(code, out operand, out arg)) { + fallLabel = bb.FallthoughGoto != null ? (ILLabel)bb.FallthoughGoto.Operand : null; + return true; + } + operand = default(T); + arg = null; + fallLabel = null; + return false; + } + public static bool MatchThis(this ILNode node) { ILVariable v; diff --git a/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs b/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs index d895ba03c..891357ca1 100644 --- a/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs +++ b/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs @@ -11,179 +11,83 @@ using Mono.Cecil; namespace ICSharpCode.Decompiler.ILAst { - public delegate void PeepholeTransform(ILBlock block, ref int i); - - /// - /// Handles peephole transformations on the ILAst. - /// - public class PeepholeTransforms + public partial class ILAstOptimizer { - DecompilerContext context; - ILBlock method; - - public static void Run(DecompilerContext context, ILBlock method) + static bool TransformDecimalCtorToConstant(List body, ILExpression expr, int pos) { - PeepholeTransforms transforms = new PeepholeTransforms(); - transforms.context = context; - transforms.method = method; - - InitializerPeepholeTransforms initializerTransforms = new InitializerPeepholeTransforms(method); - PeepholeTransform[] blockTransforms = { - initializerTransforms.TransformArrayInitializers, - initializerTransforms.TransformCollectionInitializers, - transforms.CachedDelegateInitialization, - transforms.MakeAssignmentExpression, - transforms.IntroduceFixedStatements - }; - Func[] exprTransforms = { - HandleDecimalConstants, - SimplifyLdObjAndStObj - }; - // Traverse in post order so that nested blocks are transformed first. This is required so that - // patterns on the parent block can assume that all nested blocks are already transformed. - foreach (var node in TreeTraversal.PostOrder(method, c => c != null ? c.GetChildren() : null)) { - ILBlock block = node as ILBlock; - ILExpression expr; - if (block != null) { - // go through the instructions in reverse so that transforms can build up nested structures inside-out - for (int i = block.Body.Count - 1; i >= 0; i--) { - context.CancellationToken.ThrowIfCancellationRequested(); - expr = block.Body[i] as ILExpression; - if (expr != null) { - // apply expr transforms to top-level expr in block - bool modified = ApplyExpressionTransforms(ref expr, exprTransforms); - block.Body[i] = expr; - if (modified) { - ILInlining inlining = new ILInlining(method); - if (inlining.InlineIfPossible(block, ref i)) { - i++; // retry all transforms on the new combined instruction - continue; - } - } - } - // apply block transforms - foreach (var t in blockTransforms) { - t(block, ref i); - Debug.Assert(i <= block.Body.Count && i >= 0); - if (i == block.Body.Count) // special case: retry all transforms - break; - } + MethodReference r; + List args; + if (expr.Match(ILCode.Newobj, out r, out args) && + r.DeclaringType.Namespace == "System" && + r.DeclaringType.Name == "Decimal") + { + if (args.Count == 1) { + int val; + if (args[0].Match(ILCode.Ldc_I4, out val)) { + expr.Code = ILCode.Ldc_Decimal; + expr.Operand = new decimal(val); + expr.InferredType = r.DeclaringType; + expr.Arguments.Clear(); + return true; } - } - expr = node as ILExpression; - if (expr != null) { - // apply expr transforms to all arguments - for (int i = 0; i < expr.Arguments.Count; i++) { - ILExpression arg = expr.Arguments[i]; - ApplyExpressionTransforms(ref arg, exprTransforms); - expr.Arguments[i] = arg; + } else if (args.Count == 5) { + int lo, mid, hi, isNegative, scale; + if (expr.Arguments[0].Match(ILCode.Ldc_I4, out lo) && + expr.Arguments[1].Match(ILCode.Ldc_I4, out mid) && + expr.Arguments[2].Match(ILCode.Ldc_I4, out hi) && + expr.Arguments[3].Match(ILCode.Ldc_I4, out isNegative) && + expr.Arguments[4].Match(ILCode.Ldc_I4, out scale)) + { + expr.Code = ILCode.Ldc_Decimal; + expr.Operand = new decimal(lo, mid, hi, isNegative != 0, (byte)scale); + expr.InferredType = r.DeclaringType; + expr.Arguments.Clear(); + return true; } } } - } - - static bool ApplyExpressionTransforms(ref ILExpression expr, Func[] exprTransforms) - { - bool modifiedInAnyIteration = false; - bool modified; - do { - modified = false; - ILExpression oldExpr = expr; - ILCode oldOpCode = oldExpr.Code; - foreach (var t in exprTransforms) - expr = t(expr); - if (expr != oldExpr || oldOpCode != expr.Code) { - modified = true; - modifiedInAnyIteration = true; - } - } while (modified); - return modifiedInAnyIteration; - } - - #region HandleDecimalConstants - static ILExpression HandleDecimalConstants(ILExpression expr) - { - if (expr.Code == ILCode.Newobj) { - MethodReference r = (MethodReference)expr.Operand; - if (r.DeclaringType.Name == "Decimal" && r.DeclaringType.Namespace == "System") { - if (expr.Arguments.Count == 1) { - int? val = GetI4Constant(expr.Arguments[0]); - if (val != null) { - expr.Arguments.Clear(); - expr.Code = ILCode.Ldc_Decimal; - expr.Operand = new decimal(val.Value); - expr.InferredType = r.DeclaringType; - } - } else if (expr.Arguments.Count == 5) { - int? lo = GetI4Constant(expr.Arguments[0]); - int? mid = GetI4Constant(expr.Arguments[1]); - int? hi = GetI4Constant(expr.Arguments[2]); - int? isNegative = GetI4Constant(expr.Arguments[3]); - int? scale = GetI4Constant(expr.Arguments[4]); - if (lo != null && mid != null && hi != null && isNegative != null && scale != null) { - expr.Arguments.Clear(); - expr.Code = ILCode.Ldc_Decimal; - expr.Operand = new decimal(lo.Value, mid.Value, hi.Value, isNegative.Value != 0, (byte)scale); - expr.InferredType = r.DeclaringType; - } - } - } + bool modified = false; + foreach(ILExpression arg in expr.Arguments) { + modified |= TransformDecimalCtorToConstant(null, arg, -1); } - return expr; - } - - static int? GetI4Constant(ILExpression expr) - { - if (expr != null && expr.Code == ILCode.Ldc_I4) - return (int)expr.Operand; - else - return null; + return modified; } - #endregion - #region SimplifyLdObjAndStObj - static ILExpression SimplifyLdObjAndStObj(ILExpression expr) + static bool SimplifyLdObjAndStObj(List body, ILExpression expr, int pos) { if (expr.Code == ILCode.Initobj) { expr.Code = ILCode.Stobj; expr.Arguments.Add(new ILExpression(ILCode.DefaultValue, expr.Operand)); + return true; } - if (expr.Code == ILCode.Stobj) { - switch (expr.Arguments[0].Code) { - case ILCode.Ldelema: - return SimplifyLdObjOrStObj(expr, ILCode.Stelem_Any); - case ILCode.Ldloca: - return SimplifyLdObjOrStObj(expr, ILCode.Stloc); - case ILCode.Ldflda: - return SimplifyLdObjOrStObj(expr, ILCode.Stfld); - case ILCode.Ldsflda: - return SimplifyLdObjOrStObj(expr, ILCode.Stsfld); + ILExpression arg, arg2; + TypeReference type; + ILCode? newCode = null; + if (expr.Match(ILCode.Stobj, out type, out arg, out arg2)) { + switch (arg.Code) { + case ILCode.Ldelema: newCode = ILCode.Stelem_Any; break; + case ILCode.Ldloca: newCode = ILCode.Stloc; break; + case ILCode.Ldflda: newCode = ILCode.Stfld; break; + case ILCode.Ldsflda: newCode = ILCode.Stsfld; break; } - } else if (expr.Code == ILCode.Ldobj) { - switch (expr.Arguments[0].Code) { - case ILCode.Ldelema: - return SimplifyLdObjOrStObj(expr, ILCode.Ldelem_Any); - case ILCode.Ldloca: - return SimplifyLdObjOrStObj(expr, ILCode.Ldloc); - case ILCode.Ldflda: - return SimplifyLdObjOrStObj(expr, ILCode.Ldfld); - case ILCode.Ldsflda: - return SimplifyLdObjOrStObj(expr, ILCode.Ldsfld); + } else if (expr.Match(ILCode.Ldobj, out type, out arg)) { + switch (arg.Code) { + case ILCode.Ldelema: newCode = ILCode.Ldelem_Any; break; + case ILCode.Ldloca: newCode = ILCode.Ldloc; break; + case ILCode.Ldflda: newCode = ILCode.Ldfld; break; + case ILCode.Ldsflda: newCode = ILCode.Ldsfld; break; } } - return expr; - } - - static ILExpression SimplifyLdObjOrStObj(ILExpression expr, ILCode newCode) - { - ILExpression lda = expr.Arguments[0]; - lda.Code = newCode; - if (expr.Code == ILCode.Stobj) - lda.Arguments.Add(expr.Arguments[1]); - lda.ILRanges.AddRange(expr.ILRanges); - return lda; + if (newCode != null) { + arg.Code = newCode.Value; + if (expr.Code == ILCode.Stobj) + arg.Arguments.Add(arg2); + arg.ILRanges.AddRange(expr.ILRanges); + body[pos] = arg; + return true; + } + return false; } - #endregion #region CachedDelegateInitialization void CachedDelegateInitialization(ILBlock block, ref int i) @@ -230,7 +134,7 @@ namespace ICSharpCode.Decompiler.ILAst if (parent.Arguments[j].Code == ILCode.Ldsfld && ((FieldReference)parent.Arguments[j].Operand).ResolveWithinSameModule() == field) { parent.Arguments[j] = newObj; block.Body.RemoveAt(i); - i -= new ILInlining(method).InlineInto(block, i, aggressive: true); + i -= new ILInlining(method).InlineInto(block.Body, i, aggressive: true); return; } } @@ -240,51 +144,48 @@ namespace ICSharpCode.Decompiler.ILAst #endregion #region MakeAssignmentExpression - void MakeAssignmentExpression(ILBlock block, ref int i) + bool MakeAssignmentExpression(List body, ILExpression expr, int pos) { - // expr_44 = ... - // stloc(v, expr_44) + // exprVar = ... + // stloc(v, exprVar) // -> - // expr_44 = stloc(v, ...)) + // exprVar = stloc(v, ...)) + ILExpression nextExpr = body.ElementAtOrDefault(pos + 1) as ILExpression; ILVariable exprVar; ILExpression initializer; - if (!(block.Body[i].Match(ILCode.Stloc, out exprVar, out initializer) && exprVar.IsGenerated)) - return; - ILExpression stloc1 = block.Body.ElementAtOrDefault(i + 1) as ILExpression; - if (!(stloc1 != null && stloc1.Code == ILCode.Stloc && stloc1.Arguments[0].Code == ILCode.Ldloc && stloc1.Arguments[0].Operand == exprVar)) - return; - - ILInlining inlining; - ILExpression store2 = block.Body.ElementAtOrDefault(i + 2) as ILExpression; - if (StoreCanBeConvertedToAssignment(store2, exprVar)) { - // expr_44 = ... - // stloc(v1, expr_44) - // anystore(v2, expr_44) - // -> - // stloc(v1, anystore(v2, ...)) - inlining = new ILInlining(method); - if (inlining.numLdloc.GetOrDefault(exprVar) == 2 && inlining.numStloc.GetOrDefault(exprVar) == 1) { - block.Body.RemoveAt(i + 2); // remove store2 - block.Body.RemoveAt(i); // remove expr = ... - stloc1.Arguments[0] = store2; - store2.Arguments[store2.Arguments.Count - 1] = initializer; - - if (inlining.InlineIfPossible(block, ref i)) { - i++; // retry transformations on the new combined instruction + ILVariable v; + ILExpression stLocArg; + if (expr.Match(ILCode.Stloc, out exprVar, out initializer) && + exprVar.IsGenerated && + nextExpr.Match(ILCode.Stloc, out v, out stLocArg) && + stLocArg.Match(ILCode.Ldloc, exprVar)) + { + ILExpression store2 = body.ElementAtOrDefault(pos + 2) as ILExpression; + if (StoreCanBeConvertedToAssignment(store2, exprVar)) { + // expr_44 = ... + // stloc(v1, expr_44) + // anystore(v2, expr_44) + // -> + // stloc(v1, anystore(v2, ...)) + ILInlining inlining = new ILInlining(method); + if (inlining.numLdloc.GetOrDefault(exprVar) == 2 && inlining.numStloc.GetOrDefault(exprVar) == 1) { + body.RemoveAt(pos + 2); // remove store2 + body.RemoveAt(pos); // remove expr = ... + nextExpr.Arguments[0] = store2; + store2.Arguments[store2.Arguments.Count - 1] = initializer; + + inlining.InlineIfPossible(body, ref pos); + + return true; } - return; } + + body.RemoveAt(pos + 1); // remove stloc + nextExpr.Arguments[0] = initializer; + ((ILExpression)body[pos]).Arguments[0] = nextExpr; + return true; } - - - block.Body.RemoveAt(i + 1); // remove stloc - stloc1.Arguments[0] = initializer; - ((ILExpression)block.Body[i]).Arguments[0] = stloc1; - - inlining = new ILInlining(method); - if (inlining.InlineIfPossible(block, ref i)) { - i++; // retry transformations on the new combined instruction - } + return false; } bool StoreCanBeConvertedToAssignment(ILExpression store, ILVariable exprVar) @@ -297,35 +198,35 @@ namespace ICSharpCode.Decompiler.ILAst #endregion #region IntroduceFixedStatements - void IntroduceFixedStatements(ILBlock block, ref int i) + bool IntroduceFixedStatements(List body, int i) { ILExpression initValue; ILVariable pinnedVar; int initEndPos; - if (!MatchFixedInitializer(block, i, out pinnedVar, out initValue, out initEndPos)) - return; + if (!MatchFixedInitializer(body, i, out pinnedVar, out initValue, out initEndPos)) + return false; - ILFixedStatement fixedStmt = block.Body.ElementAtOrDefault(initEndPos) as ILFixedStatement; + ILFixedStatement fixedStmt = body.ElementAtOrDefault(initEndPos) as ILFixedStatement; if (fixedStmt != null) { ILExpression expr = fixedStmt.BodyBlock.Body.LastOrDefault() as ILExpression; if (expr != null && expr.Code == ILCode.Stloc && expr.Operand == pinnedVar && IsNullOrZero(expr.Arguments[0])) { // we found a second initializer for the existing fixed statement fixedStmt.Initializers.Insert(0, initValue); - block.Body.RemoveRange(i, initEndPos - i); + body.RemoveRange(i, initEndPos - i); fixedStmt.BodyBlock.Body.RemoveAt(fixedStmt.BodyBlock.Body.Count - 1); if (pinnedVar.Type.IsByReference) pinnedVar.Type = new PointerType(((ByReferenceType)pinnedVar.Type).ElementType); - return; + return true; } } // find where pinnedVar is reset to 0: int j; - for (j = initEndPos; j < block.Body.Count; j++) { + for (j = initEndPos; j < body.Count; j++) { ILVariable v2; ILExpression storedVal; // stloc(pinned_Var, conv.u(ldc.i4(0))) - if (block.Body[j].Match(ILCode.Stloc, out v2, out storedVal) && v2 == pinnedVar) { + if (body[j].Match(ILCode.Stloc, out v2, out storedVal) && v2 == pinnedVar) { if (IsNullOrZero(storedVal)) { break; } @@ -334,11 +235,13 @@ namespace ICSharpCode.Decompiler.ILAst // Create fixed statement from i to j fixedStmt = new ILFixedStatement(); fixedStmt.Initializers.Add(initValue); - fixedStmt.BodyBlock = new ILBlock(block.Body.GetRange(initEndPos, j - initEndPos)); // from initEndPos to j-1 (inclusive) - block.Body.RemoveRange(i + 1, Math.Min(j, block.Body.Count - 1) - i); // from i+1 to j (inclusive) - block.Body[i] = fixedStmt; + fixedStmt.BodyBlock = new ILBlock(body.GetRange(initEndPos, j - initEndPos)); // from initEndPos to j-1 (inclusive) + body.RemoveRange(i + 1, Math.Min(j, body.Count - 1) - i); // from i+1 to j (inclusive) + body[i] = fixedStmt; if (pinnedVar.Type.IsByReference) pinnedVar.Type = new PointerType(((ByReferenceType)pinnedVar.Type).ElementType); + + return true; } bool IsNullOrZero(ILExpression expr) @@ -348,15 +251,15 @@ namespace ICSharpCode.Decompiler.ILAst return (expr.Code == ILCode.Ldc_I4 && (int)expr.Operand == 0) || expr.Code == ILCode.Ldnull; } - bool MatchFixedInitializer(ILBlock block, int i, out ILVariable pinnedVar, out ILExpression initValue, out int nextPos) + bool MatchFixedInitializer(List body, int i, out ILVariable pinnedVar, out ILExpression initValue, out int nextPos) { - if (block.Body[i].Match(ILCode.Stloc, out pinnedVar, out initValue) && pinnedVar.IsPinned && !IsNullOrZero(initValue)) { - initValue = (ILExpression)block.Body[i]; + if (body[i].Match(ILCode.Stloc, out pinnedVar, out initValue) && pinnedVar.IsPinned && !IsNullOrZero(initValue)) { + initValue = (ILExpression)body[i]; nextPos = i + 1; - HandleStringFixing(pinnedVar, block.Body, ref nextPos, ref initValue); + HandleStringFixing(pinnedVar, body, ref nextPos, ref initValue); return true; } - ILCondition ifStmt = block.Body[i] as ILCondition; + ILCondition ifStmt = body[i] as ILCondition; ILExpression arrayLoadingExpr; if (ifStmt != null && MatchFixedArrayInitializerCondition(ifStmt.Condition, out arrayLoadingExpr)) { ILVariable arrayVariable = (ILVariable)arrayLoadingExpr.Operand; diff --git a/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs b/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs index ddeea40cc..768257c06 100644 --- a/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs +++ b/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs @@ -490,8 +490,7 @@ namespace ICSharpCode.Decompiler.ILAst case ILCode.Ldc_R8: return typeSystem.Double; case ILCode.Ldc_Decimal: - Debug.Assert(expr.InferredType != null && expr.InferredType.FullName == "System.Decimal"); - return expr.InferredType; + return new TypeReference("System", "Decimal", module, module, true); case ILCode.Ldtoken: if (expr.Operand is TypeReference) return new TypeReference("System", "RuntimeTypeHandle", module, module, true);