Browse Source

IntroduceExitPoints

pull/728/head
Daniel Grunwald 10 years ago
parent
commit
0d6424bd12
  1. 1
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  2. 10
      ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
  3. 1
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  4. 4
      ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs
  5. 12
      ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs
  6. 14
      ICSharpCode.Decompiler/IL/Transforms/ControlFlowSimplification.cs
  7. 126
      ICSharpCode.Decompiler/IL/Transforms/IntroduceExitPoints.cs
  8. 3
      ICSharpCode.Decompiler/IL/Transforms/LoopDetection.cs

1
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -50,6 +50,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -50,6 +50,7 @@ namespace ICSharpCode.Decompiler.CSharp
List<IILTransform> ilTransforms = new List<IILTransform> {
new OptimizingTransform(),
new LoopDetection(),
new IntroduceExitPoints(),
new ControlFlowSimplification(),
new ILInlining(),
new TransformingVisitor(),

10
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

@ -119,6 +119,8 @@ namespace ICSharpCode.Decompiler.CSharp @@ -119,6 +119,8 @@ namespace ICSharpCode.Decompiler.CSharp
{
if (inst.TargetContainer == breakTarget)
return new BreakStatement();
if (inst.TargetContainer.SlotInfo == ILFunction.BodySlot)
return new ReturnStatement();
string label;
if (!endContainerLabels.TryGetValue(inst.TargetContainer, out label)) {
label = "end_" + inst.TargetLabel;
@ -212,7 +214,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -212,7 +214,7 @@ namespace ICSharpCode.Decompiler.CSharp
continueTarget = container.EntryPoint;
continueCount = 0;
breakTarget = container;
var blockStatement = ConvertBlockContainer(container);
var blockStatement = ConvertBlockContainer(container, true);
Debug.Assert(continueCount < container.EntryPoint.IncomingEdgeCount);
Debug.Assert(blockStatement.Statements.First() is LabelStatement);
if (container.EntryPoint.IncomingEdgeCount == continueCount + 1) {
@ -224,11 +226,11 @@ namespace ICSharpCode.Decompiler.CSharp @@ -224,11 +226,11 @@ namespace ICSharpCode.Decompiler.CSharp
breakTarget = oldBreakTarget;
return new WhileStatement(new PrimitiveExpression(true), blockStatement);
} else {
return ConvertBlockContainer(container);
return ConvertBlockContainer(container, false);
}
}
BlockStatement ConvertBlockContainer(BlockContainer container)
BlockStatement ConvertBlockContainer(BlockContainer container, bool isLoop)
{
BlockStatement blockStatement = new BlockStatement();
foreach (var block in container.Blocks) {
@ -237,7 +239,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -237,7 +239,7 @@ namespace ICSharpCode.Decompiler.CSharp
blockStatement.Add(new LabelStatement { Label = block.Label });
}
foreach (var inst in block.Instructions) {
if (inst.OpCode == OpCode.Leave && IsFinalLeave((Leave)inst)) {
if (!isLoop && inst.OpCode == OpCode.Leave && IsFinalLeave((Leave)inst)) {
// skip the final 'leave' instruction and just fall out of the BlockStatement
continue;
}

1
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -111,6 +111,7 @@ @@ -111,6 +111,7 @@
<Compile Include="IL\Transforms\ControlFlowSimplification.cs" />
<Compile Include="IL\Transforms\IILTransform.cs" />
<Compile Include="IL\Transforms\ILInlining.cs" />
<Compile Include="IL\Transforms\IntroduceExitPoints.cs" />
<Compile Include="IL\Transforms\LoopDetection.cs" />
<Compile Include="IL\Transforms\OptimizingTransform.cs" />
<Compile Include="IL\Transforms\ExpressionTransforms.cs" />

4
ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs

@ -38,7 +38,7 @@ namespace ICSharpCode.Decompiler.IL @@ -38,7 +38,7 @@ namespace ICSharpCode.Decompiler.IL
/// </summary>
partial class BlockContainer : ILInstruction
{
public readonly SlotInfo BlockSlot = new SlotInfo("Block", isCollection: true);
public static readonly SlotInfo BlockSlot = new SlotInfo("Block", isCollection: true);
public readonly InstructionCollection<Block> Blocks;
/// <summary>
@ -162,5 +162,3 @@ namespace ICSharpCode.Decompiler.IL @@ -162,5 +162,3 @@ namespace ICSharpCode.Decompiler.IL
}
}
}

12
ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs

@ -66,5 +66,17 @@ namespace ICSharpCode.Decompiler.IL @@ -66,5 +66,17 @@ namespace ICSharpCode.Decompiler.IL
value = null;
return false;
}
public bool MatchBranch(Block targetBlock)
{
var inst = this as Branch;
return inst != null && inst.TargetBlock == targetBlock;
}
public bool MatchLeave(BlockContainer targetContainer)
{
var inst = this as Leave;
return inst != null && inst.TargetContainer == targetContainer;
}
}
}

14
ICSharpCode.Decompiler/IL/Transforms/ControlFlowSimplification.cs

@ -38,18 +38,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -38,18 +38,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms
foreach (var container in function.Descendants.OfType<BlockContainer>()) {
Run(container, context);
}
// Remove "return;" at end of method
var funcBody = function.Body as BlockContainer;
if (funcBody != null)
{
var lastBlock = funcBody.Blocks.LastOrDefault();
var lastInst = lastBlock.Instructions.LastOrDefault() as Return;
if (lastInst != null && lastInst.ReturnValue == null)
{
lastInst.ReplaceWith(new Leave(funcBody));
}
}
}
BlockContainer currentContainer;
@ -159,7 +147,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -159,7 +147,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
&& targetBlock.IncomingEdgeCount == 1 && targetBlock.FinalInstruction.OpCode == OpCode.Nop;
}
static bool CompatibleExitInstruction(ILInstruction exit1, ILInstruction exit2)
internal static bool CompatibleExitInstruction(ILInstruction exit1, ILInstruction exit2)
{
if (exit1 == null || exit2 == null || exit1.OpCode != exit2.OpCode)
return false;

126
ICSharpCode.Decompiler/IL/Transforms/IntroduceExitPoints.cs

@ -0,0 +1,126 @@ @@ -0,0 +1,126 @@
// Copyright (c) 2014 Daniel Grunwald
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Diagnostics;
namespace ICSharpCode.Decompiler.IL.Transforms
{
/// <summary>
/// Control flow transform: use 'leave' instructions instead of 'br' where possible.
/// </summary>
public class IntroduceExitPoints : ILVisitor, IILTransform
{
public void Run(ILFunction function, ILTransformContext context)
{
function.AcceptVisitor(this);
}
static readonly Nop NoExit = new Nop();
static readonly Return ReturnExit = new Return();
protected override void Default(ILInstruction inst)
{
foreach (var child in inst.Children)
child.AcceptVisitor(this);
}
BlockContainer currentContainer;
ILInstruction currentExit = NoExit;
protected internal override void VisitBlockContainer(BlockContainer container)
{
var oldExit = currentExit;
var oldContainer = currentContainer;
var thisExit = GetExit(container);
currentExit = thisExit;
currentContainer = container;
base.VisitBlockContainer(container);
if (thisExit == null && currentExit != null) {
Debug.Assert(!currentExit.MatchLeave(currentContainer));
ILInstruction inst = container;
// traverse up to the block (we'll always find one because GetExit only returns null if there's a block)
while (inst.Parent.OpCode != OpCode.Block)
inst = inst.Parent;
Block block = (Block)inst.Parent;
block.Instructions.Add(currentExit);
} else {
Debug.Assert(thisExit == currentExit);
}
currentExit = oldExit;
currentContainer = oldContainer;
}
/// <summary>
/// Gets the next instruction after <paramref name="inst"/> is executed.
/// Returns NoExit when the next instruction cannot be identified;
/// returns <c>null</c> when the end of a Block is reached (so that we could insert an arbitrary instruction)
/// </summary>
ILInstruction GetExit(ILInstruction inst)
{
SlotInfo slot = inst.SlotInfo;
if (slot == Block.InstructionSlot) {
Block block = (Block)inst.Parent;
return block.Instructions.ElementAtOrDefault(inst.ChildIndex + 1);
} else if (slot == TryInstruction.TryBlockSlot || slot == TryCatchHandler.BodySlot || slot == TryCatch.HandlerSlot) {
return GetExit(inst.Parent);
} else if (slot == ILFunction.BodySlot) {
return ReturnExit;
}
return NoExit;
}
protected internal override void VisitBlock(Block block)
{
// Don't use foreach loop, because the children might add to the block
for (int i = 0; i < block.Instructions.Count; i++) {
block.Instructions[i].AcceptVisitor(this);
}
}
void HandleExit(ILInstruction inst)
{
if (currentExit == null) {
currentExit = inst;
inst.ReplaceWith(new Leave(currentContainer));
} else if (ControlFlowSimplification.CompatibleExitInstruction(inst, currentExit)) {
inst.ReplaceWith(new Leave(currentContainer));
}
}
protected internal override void VisitBranch(Branch inst)
{
if (!inst.TargetBlock.IsDescendantOf(currentContainer)) {
HandleExit(inst);
}
}
protected internal override void VisitLeave(Leave inst)
{
HandleExit(inst);
}
protected internal override void VisitReturn(Return inst)
{
if (inst.ReturnValue == null) {
HandleExit(inst);
} else {
base.VisitReturn(inst);
}
}
}
}

3
ICSharpCode.Decompiler/IL/Transforms/LoopDetection.cs

@ -181,6 +181,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -181,6 +181,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// Also, if the only way to leave the loop is through 'ret' or 'leave' instructions, or 'br' instructions
/// that leave the block container, this method has the effect of adding more code than necessary to the loop,
/// as those instructions do not have corresponding control flow edges.
/// Ideally, 'leave' and 'br' should be also considered exit points; and if there are no other exit points,
/// we can afford to introduce an additional exit point so that 'ret' instructions and nested infinite loops
/// don't have to be moved into the loop.
/// </remarks>
void ExtendLoop(ControlFlowNode loopHead, List<ControlFlowNode> loop, ControlFlowNode candidate)
{

Loading…
Cancel
Save