Browse Source

Add Block.FinalInstruction

pull/728/head
Daniel Grunwald 10 years ago
parent
commit
6cb55ef16d
  1. 4
      ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
  2. 71
      ICSharpCode.Decompiler/IL/Instructions/Block.cs
  3. 60
      ICSharpCode.Decompiler/IL/TransformingVisitor.cs
  4. 46
      ICSharpCode.Decompiler/Tests/ILTransforms/Inlining.cs

4
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

@ -145,6 +145,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -145,6 +145,7 @@ namespace ICSharpCode.Decompiler.CSharp
foreach (var inst in block.Instructions) {
blockStatement.Add(Convert(inst));
}
blockStatement.Add(Convert(block.FinalInstruction));
return blockStatement;
}
@ -159,6 +160,9 @@ namespace ICSharpCode.Decompiler.CSharp @@ -159,6 +160,9 @@ namespace ICSharpCode.Decompiler.CSharp
foreach (var inst in block.Instructions) {
blockStatement.Add(Convert(inst));
}
if (block.FinalInstruction.OpCode != OpCode.Nop) {
blockStatement.Add(Convert(block.FinalInstruction));
}
}
return blockStatement;
}

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

@ -35,21 +35,17 @@ namespace ICSharpCode.Decompiler.IL @@ -35,21 +35,17 @@ namespace ICSharpCode.Decompiler.IL
/// </para>
/// <code>
/// foreach (var inst in Instructions) {
/// inst.Phase1();
/// var result = inst.Phase2();
/// var result = inst.Phase1().Phase2();
/// if (result != void) evalStack.Push(result);
/// }
/// return FinalInstruction.Phase1().Phase2();
/// </code>
/// <para>
/// If the execution reaches the end of the block, the block returns the result value of the last instruction.
/// Note: if execution reaches the end of the instruction list,
/// the FinalInstruction (which is not part of the list) will be executed.
/// The block returns returns the result value of the FinalInstruction.
/// For blocks returning void, the FinalInstruction will usually be 'nop'.
/// </para>
/// TODO: actually I think it's a good idea to implement a small ILAst interpreter
/// public virtual ILInstruction Phase1(InterpreterState state);
/// public virtual InterpreterResult ILInstruction Phase2(InterpreterState state);
/// It's probably the easiest solution for specifying clear semantics, and
/// we might be able to use the interpreter logic for symbolic execution.
/// In fact, I think we'll need at least Phase1() in order to implement TransformStackIntoVariablesState()
/// without being incorrect about the pop-order in nested blocks.
/// </summary>
/// <remarks>
/// Fun fact: the empty block acts like a phase-2 pop instruction,
@ -57,23 +53,32 @@ namespace ICSharpCode.Decompiler.IL @@ -57,23 +53,32 @@ namespace ICSharpCode.Decompiler.IL
/// However, this is just of theoretical interest; we currently don't plan to use inline blocks that
/// pop elements that they didn't push themselves.
/// </remarks>
partial class Block : ILInstruction, IEnumerable<ILInstruction>
partial class Block : ILInstruction
{
public readonly InstructionCollection<ILInstruction> Instructions;
ILInstruction finalInstruction;
public ILInstruction FinalInstruction {
get {
return finalInstruction;
}
set {
ValidateChild(value);
SetChildInstruction(ref finalInstruction, value);
}
}
public int IncomingEdgeCount;
public Block() : base(OpCode.Block)
{
this.Instructions = new InstructionCollection<ILInstruction>(this);
this.FinalInstruction = new Nop();
}
public override StackType ResultType {
get {
if (Instructions.Count == 0)
return StackType.Void;
else
return Instructions.Last().ResultType;
return finalInstruction.ResultType;
}
}
@ -97,12 +102,21 @@ namespace ICSharpCode.Decompiler.IL @@ -97,12 +102,21 @@ namespace ICSharpCode.Decompiler.IL
inst.WriteTo(output);
output.WriteLine();
}
if (finalInstruction.OpCode != OpCode.Nop) {
output.Write("final: ");
finalInstruction.WriteTo(output);
output.WriteLine();
}
output.Unindent();
output.Write("}");
}
public override IEnumerable<ILInstruction> Children {
get { return Instructions; }
get {
foreach (var inst in Instructions)
yield return inst;
yield return finalInstruction;
}
}
public override void TransformChildren(ILVisitor<ILInstruction> visitor)
@ -110,22 +124,21 @@ namespace ICSharpCode.Decompiler.IL @@ -110,22 +124,21 @@ namespace ICSharpCode.Decompiler.IL
for (int i = 0; i < Instructions.Count; i++) {
Instructions[i] = Instructions[i].AcceptVisitor(visitor);
}
FinalInstruction = FinalInstruction.AcceptVisitor(visitor);
}
protected override InstructionFlags ComputeFlags()
{
var flags = InstructionFlags.None;
foreach (var inst in Instructions) {
flags |= inst.Flags;
flags |= Phase1Boundary(inst.Flags);
if (inst.ResultType != StackType.Void) {
// implicit push
flags |= InstructionFlags.MayWriteEvaluationStack;
}
}
// implicit pop at end of block
if ((flags & InstructionFlags.EndPointUnreachable) == 0)
flags |= InstructionFlags.MayWriteEvaluationStack;
return Phase1Boundary(flags);
flags |= Phase1Boundary(FinalInstruction.Flags);
return flags;
}
/// <summary>
@ -150,21 +163,5 @@ namespace ICSharpCode.Decompiler.IL @@ -150,21 +163,5 @@ namespace ICSharpCode.Decompiler.IL
// an inline block has no phase-1 effects, so we're immediately done with inlining
return this;
}
// Add() and GetEnumerator() for collection initializer support:
public void Add(ILInstruction instruction)
{
this.Instructions.Add(instruction);
}
IEnumerator<ILInstruction> IEnumerable<ILInstruction>.GetEnumerator()
{
return this.Instructions.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.Instructions.GetEnumerator();
}
}
}

60
ICSharpCode.Decompiler/IL/TransformingVisitor.cs

@ -87,23 +87,32 @@ namespace ICSharpCode.Decompiler.IL @@ -87,23 +87,32 @@ namespace ICSharpCode.Decompiler.IL
}
}
ILInstruction DoInline(InliningStack stack, ILInstruction inst)
{
do {
inst = inst.AcceptVisitor(this);
stack.didInline = false;
stack.error = false;
inst = inst.Inline(InstructionFlags.None, stack);
Debug.Assert(stack.error == inst.HasFlag(InstructionFlags.MayPeek | InstructionFlags.MayPop));
} while (stack.didInline); // repeat transformations when something was inlined
return inst;
}
protected internal override ILInstruction VisitBlock(Block block)
{
var stack = new InliningStack();
List<ILInstruction> output = new List<ILInstruction>();
for (int i = 0; i < block.Instructions.Count; i++) {
var inst = block.Instructions[i];
do {
inst = inst.AcceptVisitor(this);
stack.didInline = false;
stack.error = false;
inst = inst.Inline(InstructionFlags.None, stack);
Debug.Assert(stack.error == inst.HasFlag(InstructionFlags.MayPeek | InstructionFlags.MayPop));
} while (stack.didInline); // repeat transformations when something was inlined
if (stack.error || inst.HasFlag(InstructionFlags.MayBranch)) {
inst = DoInline(stack, inst);
if (inst.HasFlag(InstructionFlags.MayBranch
| InstructionFlags.MayPeek | InstructionFlags.MayPop
| InstructionFlags.MayReadEvaluationStack | InstructionFlags.MayWriteEvaluationStack)) {
// Values currently on the stack might be used on both sides of the branch,
// so we can't inline them.
// We also have to flush the stack if some occurrence of 'pop' or 'peek' remains in the current instruction.
// We also have to flush the stack if the instruction still accesses the evaluation stack,
// no matter whether in phase-1 or phase-2.
FlushInstructionStack(stack, output);
}
if (inst.ResultType == StackType.Void) {
@ -132,14 +141,13 @@ namespace ICSharpCode.Decompiler.IL @@ -132,14 +141,13 @@ namespace ICSharpCode.Decompiler.IL
stack.Push(inst);
}
}
// Allow inlining into the final instruction
block.FinalInstruction = DoInline(stack, block.FinalInstruction);
FlushInstructionStack(stack, output);
block.Instructions.ReplaceList(output);
if (!(block.Parent is BlockContainer)) {
// We can't unpack an instruction from a block if there's the potential that this changes
// the pop-order in the phase 1 evaluation of the parent block.
if (output.Count == 1 && !output[0].HasFlag(InstructionFlags.MayPeek | InstructionFlags.MayPop))
return output[0];
return TrySimplifyBlock(block);
}
block.Instructions.ReplaceList(output);
return block;
}
@ -160,14 +168,26 @@ namespace ICSharpCode.Decompiler.IL @@ -160,14 +168,26 @@ namespace ICSharpCode.Decompiler.IL
// 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 the block has only one instruction, we can remove the block too
// (but only if this doesn't change the pop-order in the phase 1 evaluation of the parent block)
var instructions = container.EntryPoint.Instructions;
if (instructions.Count == 1 && !instructions[0].HasFlag(InstructionFlags.MayPeek | InstructionFlags.MayPop))
return instructions[0];
return container.EntryPoint;
return TrySimplifyBlock(container.EntryPoint);
}
return container;
}
/// <summary>
/// If a block has only one instruction, replace it with that instruction.
/// </summary>
ILInstruction TrySimplifyBlock(Block block)
{
// If the block has only one instruction, we can remove the block too
// (but only if this doesn't change the pop-order in the phase 1 evaluation of the parent block)
if (block.Instructions.Count == 0) {
if (!block.FinalInstruction.HasFlag(InstructionFlags.MayPeek | InstructionFlags.MayPop))
return block.FinalInstruction;
} else if (block.Instructions.Count == 1 && block.FinalInstruction.OpCode == OpCode.Nop) {
if (block.Instructions[0].ResultType == StackType.Void && !block.Instructions[0].HasFlag(InstructionFlags.MayPeek | InstructionFlags.MayPop))
return block.Instructions[0];
}
return block;
}
}
}

46
ICSharpCode.Decompiler/Tests/ILTransforms/Inlining.cs

@ -39,13 +39,19 @@ namespace ICSharpCode.Decompiler.Tests.ILTransforms @@ -39,13 +39,19 @@ namespace ICSharpCode.Decompiler.Tests.ILTransforms
{
var f = MakeFunction(
new Block {
new LdcI4(1),
new LdcI4(2),
new Add(new Pop(StackType.I4), new Pop(StackType.I4), false, Sign.Signed)
Instructions = {
new LdcI4(1),
new LdcI4(2),
new Add(new Pop(StackType.I4), new Pop(StackType.I4), false, Sign.Signed)
}
});
f.AcceptVisitor(new TransformingVisitor());
Assert.AreEqual(
new Add(new LdcI4(1), new LdcI4(2), false, Sign.Signed).ToString(),
new Block {
Instructions = {
new Add(new LdcI4(1), new LdcI4(2), false, Sign.Signed)
}
}.ToString(),
f.Body.ToString()
);
}
@ -55,26 +61,30 @@ namespace ICSharpCode.Decompiler.Tests.ILTransforms @@ -55,26 +61,30 @@ namespace ICSharpCode.Decompiler.Tests.ILTransforms
{
var f = MakeFunction(
new Block {
new LdcI4(1),
new LdcI4(2),
new LdcI4(3),
new Call(TypeSystem.Action<int, int, int>()) {
Arguments = {
new Pop(StackType.I4),
new Block { new Pop(StackType.I4) },
new Pop(StackType.I4),
Instructions = {
new LdcI4(1),
new LdcI4(2),
new LdcI4(3),
new Call(TypeSystem.Action<int, int, int>()) {
Arguments = {
new Pop(StackType.I4),
new Block { FinalInstruction = new Pop(StackType.I4) },
new Pop(StackType.I4),
}
}
}
});
f.AcceptVisitor(new TransformingVisitor());
Assert.AreEqual(
new Block {
new LdcI4(1),
new Call(TypeSystem.Action<int, int, int>()) {
Arguments = {
new LdcI4(2),
new Block { new Pop(StackType.I4) },
new LdcI4(3)
Instructions = {
new LdcI4(1),
new Call(TypeSystem.Action<int, int, int>()) {
Arguments = {
new LdcI4(2),
new Block { FinalInstruction = new Pop(StackType.I4) },
new LdcI4(3)
}
}
}
}.ToString(),

Loading…
Cancel
Save