Browse Source

Introduce 'leave' instruction for leaving block containers.

pull/728/head
Daniel Grunwald 11 years ago
parent
commit
6b4ea94a2e
  1. 1
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  2. 25
      ICSharpCode.Decompiler/IL/Instructions.cs
  3. 4
      ICSharpCode.Decompiler/IL/Instructions.tt
  4. 13
      ICSharpCode.Decompiler/IL/Instructions/Block.cs
  5. 34
      ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs
  6. 2
      ICSharpCode.Decompiler/IL/Instructions/Branch.cs
  7. 123
      ICSharpCode.Decompiler/IL/Instructions/Leave.cs
  8. 1
      ICSharpCode.Decompiler/IL/Instructions/Return.cs
  9. 4
      ICSharpCode.Decompiler/IL/Transforms/LoopDetection.cs
  10. 3
      ICSharpCode.Decompiler/IL/Transforms/TransformStackIntoVariables.cs

1
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -94,6 +94,7 @@
<Compile Include="IL\Instructions\ILFunction.cs" /> <Compile Include="IL\Instructions\ILFunction.cs" />
<Compile Include="IL\Instructions\ILInstruction.cs" /> <Compile Include="IL\Instructions\ILInstruction.cs" />
<Compile Include="IL\Instructions\InstructionCollection.cs" /> <Compile Include="IL\Instructions\InstructionCollection.cs" />
<Compile Include="IL\Instructions\Leave.cs" />
<Compile Include="IL\Instructions\LocalVarInstructions.cs" /> <Compile Include="IL\Instructions\LocalVarInstructions.cs" />
<Compile Include="IL\Instructions\MemoryInstructions.cs" /> <Compile Include="IL\Instructions\MemoryInstructions.cs" />
<Compile Include="IL\Instructions\PatternMatching.cs" /> <Compile Include="IL\Instructions\PatternMatching.cs" />

25
ICSharpCode.Decompiler/IL/Instructions.cs

@ -66,6 +66,8 @@ namespace ICSharpCode.Decompiler.IL
Arglist, Arglist,
/// <summary>Unconditional branch. <c>goto target;</c></summary> /// <summary>Unconditional branch. <c>goto target;</c></summary>
Branch, Branch,
/// <summary>Unconditional branch to end of block container. <c>goto container_end;</c>, often <c>break;</c></summary>
Leave,
/// <summary>Marks the end of an finally, fault or exception filter block.</summary> /// <summary>Marks the end of an finally, fault or exception filter block.</summary>
EndFinally, EndFinally,
/// <summary>If statement / conditional expression. <c>if (condition) trueExpr else falseExpr</c></summary> /// <summary>If statement / conditional expression. <c>if (condition) trueExpr else falseExpr</c></summary>
@ -666,6 +668,20 @@ namespace ICSharpCode.Decompiler.IL
} }
} }
/// <summary>Unconditional branch to end of block container. <c>goto container_end;</c>, often <c>break;</c></summary>
public sealed partial class Leave : SimpleInstruction
{
public override StackType ResultType { get { return StackType.Void; } }
public override void AcceptVisitor(ILVisitor visitor)
{
visitor.VisitLeave(this);
}
public override T AcceptVisitor<T>(ILVisitor<T> visitor)
{
return visitor.VisitLeave(this);
}
}
/// <summary>Marks the end of an finally, fault or exception filter block.</summary> /// <summary>Marks the end of an finally, fault or exception filter block.</summary>
public sealed partial class EndFinally : SimpleInstruction public sealed partial class EndFinally : SimpleInstruction
{ {
@ -2626,6 +2642,10 @@ namespace ICSharpCode.Decompiler.IL
{ {
Default(inst); Default(inst);
} }
protected internal virtual void VisitLeave(Leave inst)
{
Default(inst);
}
protected internal virtual void VisitEndFinally(EndFinally inst) protected internal virtual void VisitEndFinally(EndFinally inst)
{ {
Default(inst); Default(inst);
@ -2924,6 +2944,10 @@ namespace ICSharpCode.Decompiler.IL
{ {
return Default(inst); return Default(inst);
} }
protected internal virtual T VisitLeave(Leave inst)
{
return Default(inst);
}
protected internal virtual T VisitEndFinally(EndFinally inst) protected internal virtual T VisitEndFinally(EndFinally inst)
{ {
return Default(inst); return Default(inst);
@ -3212,6 +3236,7 @@ namespace ICSharpCode.Decompiler.IL
"bit.not", "bit.not",
"arglist", "arglist",
"br", "br",
"leave",
"endfinally", "endfinally",
"if", "if",
"try.catch", "try.catch",

4
ICSharpCode.Decompiler/IL/Instructions.tt

@ -61,7 +61,9 @@
new OpCode("arglist", "Retrieves the RuntimeArgumentHandle.", NoArguments, ResultType("O")), new OpCode("arglist", "Retrieves the RuntimeArgumentHandle.", NoArguments, ResultType("O")),
new OpCode("br", "Unconditional branch. <c>goto target;</c>", new OpCode("br", "Unconditional branch. <c>goto target;</c>",
CustomClassName("Branch"), NoArguments, CustomConstructor, UnconditionalBranch, MayBranch, CustomComputeFlags), CustomClassName("Branch"), NoArguments, CustomConstructor, UnconditionalBranch, MayBranch, CustomComputeFlags),
// TODO: get rid of endfinally, we can represent those with a normal branch to the end of the filter/finally block instead new OpCode("leave", "Unconditional branch to end of block container. <c>goto container_end;</c>, often <c>break;</c>",
NoArguments, CustomConstructor, UnconditionalBranch, MayBranch, CustomComputeFlags),
// TODO: get rid of endfinally, we can represent those with 'leave blockcontainer' instead
new OpCode("endfinally", "Marks the end of an finally, fault or exception filter block.", new OpCode("endfinally", "Marks the end of an finally, fault or exception filter block.",
CustomClassName("EndFinally"), NoArguments, UnconditionalBranch, MayBranch), CustomClassName("EndFinally"), NoArguments, UnconditionalBranch, MayBranch),
new OpCode("if", "If statement / conditional expression. <c>if (condition) trueExpr else falseExpr</c>", new OpCode("if", "If statement / conditional expression. <c>if (condition) trueExpr else falseExpr</c>",

13
ICSharpCode.Decompiler/IL/Instructions/Block.cs

@ -203,19 +203,6 @@ namespace ICSharpCode.Decompiler.IL
} }
FinalInstruction = FinalInstruction.Inline(InstructionFlags.None, state); FinalInstruction = FinalInstruction.Inline(InstructionFlags.None, state);
FinalInstruction.TransformStackIntoVariables(state); FinalInstruction.TransformStackIntoVariables(state);
if (FinalInstruction.HasFlag(InstructionFlags.EndPointUnreachable))
return;
var bc = Parent as BlockContainer;
if (bc != null) {
// If this block allows us to fall out of the container,
// remember the variable stack in state.FinalVariables.
ImmutableArray<ILVariable> variables;
if (state.FinalVariables.TryGetValue(bc, out variables)) {
state.MergeVariables(state.Variables, variables.ToStack());
} else {
state.FinalVariables.Add(bc, state.Variables.ToImmutableArray());
}
}
} }
} }
} }

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

@ -33,15 +33,23 @@ namespace ICSharpCode.Decompiler.IL
/// That means that viewed from the outside, the block container has a single entry point (but possibly multiple exit points), /// That means that viewed from the outside, the block container has a single entry point (but possibly multiple exit points),
/// and the same holds for every block within the container. /// and the same holds for every block within the container.
/// ///
/// If a block within the container falls through to its end point, control flow is transferred to the end point /// All blocks in the container must perform unconditional control flow (falling through to the block end is not allowed).
/// of the whole block container. The return value of the block is ignored in this case, the container always /// To exit the block container, use the 'leave' instruction.
/// returns void.
/// </summary> /// </summary>
partial class BlockContainer : ILInstruction partial class BlockContainer : ILInstruction
{ {
public readonly InstructionCollection<Block> Blocks; public readonly InstructionCollection<Block> Blocks;
/// <summary>
/// Gets the number of 'leave' instructions that target this BlockContainer.
/// </summary>
public int LeaveCount { get; internal set; }
Block entryPoint; Block entryPoint;
/// <summary>
/// Gets the container's entry point. This is the first block in the Blocks collection.
/// </summary>
public Block EntryPoint { public Block EntryPoint {
get { return entryPoint; } get { return entryPoint; }
private set { private set {
@ -80,7 +88,8 @@ namespace ICSharpCode.Decompiler.IL
public override void WriteTo(ITextOutput output) public override void WriteTo(ITextOutput output)
{ {
output.WriteLine("BlockContainer {"); output.WriteDefinition("BlockContainer", this);
output.WriteLine(" {");
output.Indent(); output.Indent();
foreach (var inst in Blocks) { foreach (var inst in Blocks) {
inst.WriteTo(output); inst.WriteTo(output);
@ -112,20 +121,21 @@ namespace ICSharpCode.Decompiler.IL
base.CheckInvariant(); base.CheckInvariant();
Debug.Assert(EntryPoint == Blocks[0]); Debug.Assert(EntryPoint == Blocks[0]);
Debug.Assert(!IsConnected || EntryPoint.IncomingEdgeCount >= 1); Debug.Assert(!IsConnected || EntryPoint.IncomingEdgeCount >= 1);
Debug.Assert(Blocks.All(b => b.HasFlag(InstructionFlags.EndPointUnreachable)));
} }
protected override InstructionFlags ComputeFlags() protected override InstructionFlags ComputeFlags()
{ {
InstructionFlags flagsInAnyBlock = InstructionFlags.None; InstructionFlags flags = InstructionFlags.None;
InstructionFlags flagsInAllBlocks = ~InstructionFlags.None;
foreach (var block in Blocks) { foreach (var block in Blocks) {
flagsInAnyBlock |= block.Flags; flags |= block.Flags;
flagsInAllBlocks &= block.Flags;
} }
// Return EndPointUnreachable only if no block has a reachable endpoint. // The end point of the BlockContainer is only reachable if there's a leave instruction
// The other flags are combined from all blocks. if (LeaveCount == 0)
return (flagsInAnyBlock & ~InstructionFlags.EndPointUnreachable) flags |= InstructionFlags.EndPointUnreachable;
| (flagsInAllBlocks & InstructionFlags.EndPointUnreachable); else
flags &= ~InstructionFlags.EndPointUnreachable;
return flags;
} }
internal override ILInstruction Inline(InstructionFlags flagsBefore, IInlineContext context) internal override ILInstruction Inline(InstructionFlags flagsBefore, IInlineContext context)

2
ICSharpCode.Decompiler/IL/Instructions/Branch.cs

@ -30,6 +30,8 @@ namespace ICSharpCode.Decompiler.IL
/// Unconditional branch. <c>goto target;</c> /// Unconditional branch. <c>goto target;</c>
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// When jumping to the entrypoint of the current block container, the branch represents a <c>continue</c> statement.
///
/// Phase-1 execution of a branch is a no-op. /// Phase-1 execution of a branch is a no-op.
/// Phase-2 execution removes PopCount elements from the evaluation stack /// Phase-2 execution removes PopCount elements from the evaluation stack
/// and jumps to the target block. /// and jumps to the target block.

123
ICSharpCode.Decompiler/IL/Instructions/Leave.cs

@ -0,0 +1,123 @@
// 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.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ICSharpCode.Decompiler.IL
{
/// <summary>
/// Unconditional branch. <c>goto target;</c>
/// </summary>
/// <remarks>
/// When jumping to the entrypoint of the current block container, the branch represents a <c>continue</c> statement.
///
/// Phase-1 execution of a branch is a no-op.
/// Phase-2 execution removes PopCount elements from the evaluation stack
/// and jumps to the target block.
/// </remarks>
partial class Leave : SimpleInstruction
{
BlockContainer targetContainer;
/// <summary>
/// Pops the specified number of arguments from the evaluation stack during the branching operation.
/// </summary>
public int PopCount;
public Leave(BlockContainer targetContainer) : base(OpCode.Leave)
{
if (targetContainer == null)
throw new ArgumentNullException("targetContainer");
this.targetContainer = targetContainer;
}
protected override InstructionFlags ComputeFlags()
{
var flags = InstructionFlags.MayBranch | InstructionFlags.EndPointUnreachable;
if (PopCount > 0) {
// the branch pop happens during phase-2, so don't use MayPop
flags |= InstructionFlags.MayWriteEvaluationStack;
}
return flags;
}
public BlockContainer TargetContainer {
get { return targetContainer; }
set {
if (targetContainer != null && IsConnected)
targetContainer.LeaveCount--;
targetContainer = value;
if (targetContainer != null && IsConnected)
targetContainer.LeaveCount++;
}
}
protected override void Connected()
{
base.Connected();
if (targetContainer != null)
targetContainer.LeaveCount++;
}
protected override void Disconnected()
{
base.Disconnected();
if (targetContainer != null)
targetContainer.LeaveCount--;
}
public string TargetLabel {
get { return targetContainer.EntryPoint.Label; }
}
internal override void CheckInvariant()
{
base.CheckInvariant();
Debug.Assert(this.IsDescendantOf(targetContainer));
}
public override void WriteTo(ITextOutput output)
{
output.Write(OpCode);
output.Write(' ');
output.WriteReference(TargetLabel, targetContainer, isLocal: true);
if (PopCount != 0) {
output.Write(" (pops ");
output.Write(PopCount.ToString());
output.Write(" element)");
}
}
internal override void TransformStackIntoVariables(TransformStackIntoVariablesState state)
{
ImmutableArray<ILVariable> variables;
if (state.FinalVariables.TryGetValue(targetContainer, out variables)) {
state.MergeVariables(state.Variables, variables.ToStack());
} else {
state.FinalVariables.Add(targetContainer, state.Variables.ToImmutableArray());
}
state.Variables.Clear();
}
}
}

1
ICSharpCode.Decompiler/IL/Instructions/Return.cs

@ -98,6 +98,7 @@ namespace ICSharpCode.Decompiler.IL
{ {
if (returnValue != null) if (returnValue != null)
ReturnValue.TransformStackIntoVariables(state); ReturnValue.TransformStackIntoVariables(state);
state.Variables.Clear();
} }
} }
} }

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

@ -28,6 +28,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// </summary> /// </summary>
public class LoopDetection : IILTransform public class LoopDetection : IILTransform
{ {
// Transform ordering:
// LoopDetection should run before other control flow structures are detected.
// It should run after the 'return block' is duplicated.
#region Construct Control Flow Graph #region Construct Control Flow Graph
/// <summary> /// <summary>
/// Constructs a control flow graph for the blocks in the given block container. /// Constructs a control flow graph for the blocks in the given block container.

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

@ -36,6 +36,9 @@ namespace ICSharpCode.Decompiler.IL
public class TransformStackIntoVariables : IILTransform public class TransformStackIntoVariables : IILTransform
{ {
// Transform ordering:
// TransformStackIntoVariables should run after all inlining transforms, so that no unnecessary variables are created.
public void Run(ILFunction function, ILTransformContext context) public void Run(ILFunction function, ILTransformContext context)
{ {
var state = new TransformStackIntoVariablesState(); var state = new TransformStackIntoVariablesState();

Loading…
Cancel
Save