Browse Source

Introduce 'leave' instruction for leaving block containers.

pull/728/head
Daniel Grunwald 10 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 @@ @@ -94,6 +94,7 @@
<Compile Include="IL\Instructions\ILFunction.cs" />
<Compile Include="IL\Instructions\ILInstruction.cs" />
<Compile Include="IL\Instructions\InstructionCollection.cs" />
<Compile Include="IL\Instructions\Leave.cs" />
<Compile Include="IL\Instructions\LocalVarInstructions.cs" />
<Compile Include="IL\Instructions\MemoryInstructions.cs" />
<Compile Include="IL\Instructions\PatternMatching.cs" />

25
ICSharpCode.Decompiler/IL/Instructions.cs

@ -66,6 +66,8 @@ namespace ICSharpCode.Decompiler.IL @@ -66,6 +66,8 @@ namespace ICSharpCode.Decompiler.IL
Arglist,
/// <summary>Unconditional branch. <c>goto target;</c></summary>
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>
EndFinally,
/// <summary>If statement / conditional expression. <c>if (condition) trueExpr else falseExpr</c></summary>
@ -666,6 +668,20 @@ namespace ICSharpCode.Decompiler.IL @@ -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>
public sealed partial class EndFinally : SimpleInstruction
{
@ -2626,6 +2642,10 @@ namespace ICSharpCode.Decompiler.IL @@ -2626,6 +2642,10 @@ namespace ICSharpCode.Decompiler.IL
{
Default(inst);
}
protected internal virtual void VisitLeave(Leave inst)
{
Default(inst);
}
protected internal virtual void VisitEndFinally(EndFinally inst)
{
Default(inst);
@ -2924,6 +2944,10 @@ namespace ICSharpCode.Decompiler.IL @@ -2924,6 +2944,10 @@ namespace ICSharpCode.Decompiler.IL
{
return Default(inst);
}
protected internal virtual T VisitLeave(Leave inst)
{
return Default(inst);
}
protected internal virtual T VisitEndFinally(EndFinally inst)
{
return Default(inst);
@ -3212,6 +3236,7 @@ namespace ICSharpCode.Decompiler.IL @@ -3212,6 +3236,7 @@ namespace ICSharpCode.Decompiler.IL
"bit.not",
"arglist",
"br",
"leave",
"endfinally",
"if",
"try.catch",

4
ICSharpCode.Decompiler/IL/Instructions.tt

@ -61,7 +61,9 @@ @@ -61,7 +61,9 @@
new OpCode("arglist", "Retrieves the RuntimeArgumentHandle.", NoArguments, ResultType("O")),
new OpCode("br", "Unconditional branch. <c>goto target;</c>",
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.",
CustomClassName("EndFinally"), NoArguments, UnconditionalBranch, MayBranch),
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 @@ -203,19 +203,6 @@ namespace ICSharpCode.Decompiler.IL
}
FinalInstruction = FinalInstruction.Inline(InstructionFlags.None, 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 @@ -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),
/// 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
/// of the whole block container. The return value of the block is ignored in this case, the container always
/// returns void.
/// All blocks in the container must perform unconditional control flow (falling through to the block end is not allowed).
/// To exit the block container, use the 'leave' instruction.
/// </summary>
partial class BlockContainer : ILInstruction
{
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;
/// <summary>
/// Gets the container's entry point. This is the first block in the Blocks collection.
/// </summary>
public Block EntryPoint {
get { return entryPoint; }
private set {
@ -80,7 +88,8 @@ namespace ICSharpCode.Decompiler.IL @@ -80,7 +88,8 @@ namespace ICSharpCode.Decompiler.IL
public override void WriteTo(ITextOutput output)
{
output.WriteLine("BlockContainer {");
output.WriteDefinition("BlockContainer", this);
output.WriteLine(" {");
output.Indent();
foreach (var inst in Blocks) {
inst.WriteTo(output);
@ -112,20 +121,21 @@ namespace ICSharpCode.Decompiler.IL @@ -112,20 +121,21 @@ namespace ICSharpCode.Decompiler.IL
base.CheckInvariant();
Debug.Assert(EntryPoint == Blocks[0]);
Debug.Assert(!IsConnected || EntryPoint.IncomingEdgeCount >= 1);
Debug.Assert(Blocks.All(b => b.HasFlag(InstructionFlags.EndPointUnreachable)));
}
protected override InstructionFlags ComputeFlags()
{
InstructionFlags flagsInAnyBlock = InstructionFlags.None;
InstructionFlags flagsInAllBlocks = ~InstructionFlags.None;
InstructionFlags flags = InstructionFlags.None;
foreach (var block in Blocks) {
flagsInAnyBlock |= block.Flags;
flagsInAllBlocks &= block.Flags;
flags |= block.Flags;
}
// Return EndPointUnreachable only if no block has a reachable endpoint.
// The other flags are combined from all blocks.
return (flagsInAnyBlock & ~InstructionFlags.EndPointUnreachable)
| (flagsInAllBlocks & InstructionFlags.EndPointUnreachable);
// The end point of the BlockContainer is only reachable if there's a leave instruction
if (LeaveCount == 0)
flags |= InstructionFlags.EndPointUnreachable;
else
flags &= ~InstructionFlags.EndPointUnreachable;
return flags;
}
internal override ILInstruction Inline(InstructionFlags flagsBefore, IInlineContext context)

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

@ -30,6 +30,8 @@ namespace ICSharpCode.Decompiler.IL @@ -30,6 +30,8 @@ namespace ICSharpCode.Decompiler.IL
/// 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.

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

@ -0,0 +1,123 @@ @@ -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 @@ -98,6 +98,7 @@ namespace ICSharpCode.Decompiler.IL
{
if (returnValue != null)
ReturnValue.TransformStackIntoVariables(state);
state.Variables.Clear();
}
}
}

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

@ -28,6 +28,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -28,6 +28,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// </summary>
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
/// <summary>
/// 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 @@ -36,6 +36,9 @@ namespace ICSharpCode.Decompiler.IL
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)
{
var state = new TransformStackIntoVariablesState();

Loading…
Cancel
Save