From c3e3166d8fc0b69b1d4a20e258b800eb9a756d46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Srbeck=C3=BD?= Date: Sun, 27 Feb 2011 23:03:39 +0000 Subject: [PATCH] Reimplemented condition "unnesting" to work on the fattened data with gotos already cleaned up. Use left over gotos and other unconditional flow to guide the unnesting. --- ICSharpCode.Decompiler/ILAst/GotoRemoval.cs | 18 +-- .../ILAst/ILAstOptimizer.cs | 106 ++++++++++++------ 2 files changed, 83 insertions(+), 41 deletions(-) diff --git a/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs b/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs index 365dc4464..cd30046e4 100644 --- a/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs +++ b/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs @@ -34,8 +34,16 @@ namespace Decompiler TrySimplifyGoto(gotoExpr); } + RemoveRedundantCode(method); + } + + public static void RemoveRedundantCode(ILBlock method) + { // Remove dead lables and nops - RemoveDeadLabels(method); + HashSet liveLabels = new HashSet(method.GetSelfAndChildrenRecursive().SelectMany(e => e.GetBranchTargets())); + foreach(ILBlock block in method.GetSelfAndChildrenRecursive().ToList()) { + block.Body = block.Body.Where(n => !n.Match(ILCode.Nop) && !(n is ILLabel && !liveLabels.Contains((ILLabel)n))).ToList(); + } // Remove redundant continue foreach(ILWhileLoop loop in method.GetSelfAndChildrenRecursive()) { @@ -51,14 +59,6 @@ namespace Decompiler } } - public static void RemoveDeadLabels(ILNode method) - { - HashSet liveLabels = new HashSet(method.GetSelfAndChildrenRecursive().SelectMany(e => e.GetBranchTargets())); - foreach(ILBlock block in method.GetSelfAndChildrenRecursive().ToList()) { - block.Body = block.Body.Where(n => !n.Match(ILCode.Nop) && !(n is ILLabel && !liveLabels.Contains((ILLabel)n))).ToList(); - } - } - bool TrySimplifyGoto(ILExpression gotoExpr) { Debug.Assert(gotoExpr.Code == ILCode.Br || gotoExpr.Code == ILCode.Leave); diff --git a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs index 668644821..73760eb7f 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs @@ -18,6 +18,7 @@ namespace Decompiler.ControlFlow FlattenNestedMovableBlocks, GotoRemoval, DuplicateReturns, + FlattenIfStatements, HandleArrayInitializers, TypeInference, None @@ -71,13 +72,17 @@ namespace Decompiler.ControlFlow if (abortBeforeStep == ILAstOptimizationStep.DuplicateReturns) return; DuplicateReturnStatements(method); - GotoRemoval.RemoveDeadLabels(method); + + if (abortBeforeStep == ILAstOptimizationStep.FlattenIfStatements) return; + FlattenIfStatements(method); if (abortBeforeStep == ILAstOptimizationStep.HandleArrayInitializers) return; ArrayInitializers.Transform(method); if (abortBeforeStep == ILAstOptimizationStep.TypeInference) return; TypeAnalysis.Run(context, method); + + GotoRemoval.RemoveRedundantCode(method); } /// @@ -594,6 +599,12 @@ namespace Decompiler.ControlFlow // The branch label will not be used - kill it branchExpr.Operand = null; + // Swap bodies since that seems to be the usual C# order + ILLabel temp = trueLabel; + trueLabel = falseLabel; + falseLabel = temp; + branchExpr = new ILExpression(ILCode.LogicNot, null, branchExpr); + // Convert the basic block to ILCondition ILCondition ilCond = new ILCondition() { Condition = branchExpr, @@ -601,8 +612,8 @@ namespace Decompiler.ControlFlow FalseBlock = new ILBlock() { EntryGoto = new ILExpression(ILCode.Br, falseLabel) } }; result.Add(new ILBasicBlock() { - EntryLabel = block.EntryLabel, // Keep the entry label - Body = { ilCond } + EntryLabel = block.EntryLabel, // Keep the entry label + Body = { ilCond } }); // Remove the item immediately so that it is not picked up as content @@ -631,35 +642,6 @@ namespace Decompiler.ControlFlow scope.ExceptWith(content); ilCond.FalseBlock.Body.AddRange(FindConditions(content, falseTarget)); } - - if (scope.Count == 0) { - // We have removed the whole scope - eliminte one of the condition bodies - int trueSize = ilCond.TrueBlock.GetSelfAndChildrenRecursive().Count(); - int falseSize = ilCond.FalseBlock.GetSelfAndChildrenRecursive().Count(); - - // The block are protected - Debug.Assert(ilCond.TrueBlock.EntryGoto != null); - Debug.Assert(ilCond.FalseBlock.EntryGoto != null); - - if (falseSize > trueSize) { - // Move the false body out - result.AddRange(ilCond.FalseBlock.Body); - ilCond.FalseBlock.Body.Clear(); - } else { - // Move the true body out - result.AddRange(ilCond.TrueBlock.Body); - ilCond.TrueBlock.Body.Clear(); - } - } - - // If true body is empty, swap bodies. - // Might happend because there was not any to start with or we moved it out. - if (ilCond.TrueBlock.Body.Count == 0 && ilCond.FalseBlock.Body.Count > 0) { - ILBlock tmp = ilCond.TrueBlock; - ilCond.TrueBlock = ilCond.FalseBlock; - ilCond.FalseBlock = tmp; - ilCond.Condition = new ILExpression(ILCode.LogicNot, null, ilCond.Condition); - } } } @@ -753,6 +735,49 @@ namespace Decompiler.ControlFlow } } } + + /// + /// Reduce the nesting of conditions. + /// It should be done on flat data that already had most gotos removed + /// + void FlattenIfStatements(ILNode node) + { + ILBlock block = node as ILBlock; + if (block != null) { + for (int i = 0; i < block.Body.Count; i++) { + ILCondition cond = block.Body[i] as ILCondition; + if (cond != null) { + bool trueExits = cond.TrueBlock.Body.Count > 0 && !cond.TrueBlock.Body.Last().CanFallthough(); + bool falseExits = cond.FalseBlock.Body.Count > 0 && !cond.FalseBlock.Body.Last().CanFallthough(); + + if (trueExits) { + // Move the false block after the condition + block.Body.InsertRange(i + 1, cond.FalseBlock.GetChildren()); + cond.FalseBlock = new ILBlock(); + } else if (falseExits) { + // Move the true block after the condition + block.Body.InsertRange(i + 1, cond.TrueBlock.GetChildren()); + cond.TrueBlock = new ILBlock(); + } + + // Eliminate empty true block + if (!cond.TrueBlock.GetChildren().Any() && cond.FalseBlock.GetChildren().Any()) { + // Swap bodies + ILBlock tmp = cond.TrueBlock; + cond.TrueBlock = cond.FalseBlock; + cond.FalseBlock = tmp; + cond.Condition = new ILExpression(ILCode.LogicNot, null, cond.Condition); + } + } + } + } + + // 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); + } + } } public static class ILAstOptimizerExtensionMethods @@ -780,5 +805,22 @@ namespace Decompiler.ControlFlow return false; } } + + public static bool CanFallthough(this ILNode node) + { + ILExpression expr = node as ILExpression; + if (expr != null) { + switch(expr.Code) { + case ILCode.Br: + case ILCode.Ret: + case ILCode.Throw: + case ILCode.Rethrow: + case ILCode.LoopContinue: + case ILCode.LoopBreak: + return false; + } + } + return true; + } } }