From 179b6351858f5173bb3fce4faf689bbdfb8b6609 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sun, 22 Mar 2015 19:24:45 +0100 Subject: [PATCH] Perform some control flow simplifications in OptimizingTransform --- .../IL/Transforms/OptimizingTransform.cs | 55 ++++++++++++++++++- .../IL/Transforms/TransformingVisitor.cs | 6 +- ILSpy/Languages/ILAstLanguage.cs | 3 +- 3 files changed, 57 insertions(+), 7 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/OptimizingTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/OptimizingTransform.cs index bd586a8ca..25b4d3922 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/OptimizingTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/OptimizingTransform.cs @@ -28,7 +28,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// /// The optimizations performed are: /// * 'nop' instructions are removed - /// * TODO branches that lead to a 'return block' are replaced with a return instruction + /// * branches that lead to other branches are replaced with branches that directly jump to the destination + /// * branches that lead to a 'return block' are replaced with a return instruction + /// * basic blocks are combined where possible /// public class OptimizingTransform : IILTransform { @@ -37,7 +39,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms foreach (var block in function.Descendants.OfType()) { // Remove 'nop' instructions block.Instructions.RemoveAll(inst => inst.OpCode == OpCode.Nop); - /* TODO we might need this for the 'return block' optimization // Ensure return blocks are inlined: if (block.Instructions.Count == 2 && block.Instructions[1].OpCode == OpCode.Return) { Return ret = (Return)block.Instructions[1]; @@ -46,8 +47,56 @@ namespace ICSharpCode.Decompiler.IL.Transforms block.Instructions.RemoveAt(0); } } - */ } + foreach (var branch in function.Descendants.OfType()) { + // Resolve indirect branches + var targetBlock = branch.TargetBlock; + while (targetBlock.Instructions.Count == 1 && targetBlock.Instructions[0].OpCode == OpCode.Branch) { + var nextBranch = (Branch)targetBlock.Instructions[0]; + branch.TargetBlock = nextBranch.TargetBlock; + branch.PopCount += nextBranch.PopCount; + if (targetBlock.IncomingEdgeCount == 0) + targetBlock.Instructions.Clear(); // mark the block for deletion + targetBlock = branch.TargetBlock; + } + if (IsReturnBlock(targetBlock)) { + // Replace branches to 'return blocks' with the return instruction + branch.ReplaceWith(targetBlock.Instructions[0].Clone()); + } else if (branch.TargetBlock.Instructions.Count == 1 && branch.TargetBlock.Instructions[0].OpCode == OpCode.Leave) { + // Replace branches to 'leave' instruction with the leave instruction + Leave leave = (Leave)branch.TargetBlock.Instructions[0]; + branch.ReplaceWith(new Leave(leave.TargetContainer) { PopCount = branch.PopCount + leave.PopCount, ILRange = branch.ILRange }); + } + if (targetBlock.IncomingEdgeCount == 0) + targetBlock.Instructions.Clear(); // mark the block for deletion + } + foreach (var container in function.Descendants.OfType()) { + foreach (var block in container.Blocks) { + if (block.Instructions.Count == 0) + continue; // block is already marked for deletion + Branch br = block.Instructions.Last() as Branch; + if (br != null && br.TargetBlock.Parent == container && br.TargetBlock.IncomingEdgeCount == 1) { + // We could inline the target block into this block + // Do so only if the block will stay a basic block -- we don't want extended basic blocks prior to LoopDetection. + var targetBlock = br.TargetBlock; + if (targetBlock.Instructions.Count == 1 || !targetBlock.Instructions[targetBlock.Instructions.Count - 2].HasFlag(InstructionFlags.MayBranch)) { + block.Instructions.Remove(br); + block.Instructions.AddRange(targetBlock.Instructions); + targetBlock.Instructions.Clear(); // mark targetBlock for deletion + } + } + } + // Remove return blocks that are no longer reachable: + container.Blocks.RemoveAll(b => b.IncomingEdgeCount == 0 && b.Instructions.Count == 0); + } + } + + static bool IsReturnBlock(Block targetBlock) + { + if (targetBlock.Instructions.Count != 1 || targetBlock.FinalInstruction.OpCode != OpCode.Nop) + return false; + var ret = targetBlock.Instructions[0] as Return; + return ret != null && (ret.ReturnValue == null || ret.ReturnValue.OpCode == OpCode.LdLoc); } } } diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformingVisitor.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformingVisitor.cs index ac491830a..2e276ef10 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformingVisitor.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformingVisitor.cs @@ -204,12 +204,12 @@ namespace ICSharpCode.Decompiler.IL } // VisitBranch() 'steals' blocks from containers. Remove all blocks that were stolen from the block list: - Debug.Assert(container.EntryPoint.IncomingEdgeCount > 0); - container.Blocks.RemoveAll(b => b.IncomingEdgeCount == 0); +// Debug.Assert(container.EntryPoint.IncomingEdgeCount > 0); +// container.Blocks.RemoveAll(b => b.IncomingEdgeCount == 0); // If the container only contains a single block, and the block contents do not jump back to the block start, // we can remove the container. - if (container.Blocks.Count == 1 && container.EntryPoint.IncomingEdgeCount == 1) { + if (container.Blocks.Count == 1 && container.LeaveCount == 0 && container.EntryPoint.IncomingEdgeCount == 1) { return TrySimplifyBlock(container.EntryPoint); } return container; diff --git a/ILSpy/Languages/ILAstLanguage.cs b/ILSpy/Languages/ILAstLanguage.cs index 62bdca0c7..e4d1f4c5c 100644 --- a/ILSpy/Languages/ILAstLanguage.cs +++ b/ILSpy/Languages/ILAstLanguage.cs @@ -148,8 +148,9 @@ namespace ICSharpCode.ILSpy var typeSystem = new DecompilerTypeSystem(method.Module); ILReader reader = new ILReader(typeSystem); ILFunction il = reader.ReadIL(method.Body, options.CancellationToken); - foreach (var transform in transforms) + foreach (var transform in transforms) { transform.Run(il, new ILTransformContext { TypeSystem = typeSystem }); + } il.WriteTo(output); } }