Browse Source

Add Block.FinalInstruction

pull/728/head
Daniel Grunwald 11 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
foreach (var inst in block.Instructions) { foreach (var inst in block.Instructions) {
blockStatement.Add(Convert(inst)); blockStatement.Add(Convert(inst));
} }
blockStatement.Add(Convert(block.FinalInstruction));
return blockStatement; return blockStatement;
} }
@ -159,6 +160,9 @@ namespace ICSharpCode.Decompiler.CSharp
foreach (var inst in block.Instructions) { foreach (var inst in block.Instructions) {
blockStatement.Add(Convert(inst)); blockStatement.Add(Convert(inst));
} }
if (block.FinalInstruction.OpCode != OpCode.Nop) {
blockStatement.Add(Convert(block.FinalInstruction));
}
} }
return blockStatement; return blockStatement;
} }

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

@ -35,21 +35,17 @@ namespace ICSharpCode.Decompiler.IL
/// </para> /// </para>
/// <code> /// <code>
/// foreach (var inst in Instructions) { /// foreach (var inst in Instructions) {
/// inst.Phase1(); /// var result = inst.Phase1().Phase2();
/// var result = inst.Phase2();
/// if (result != void) evalStack.Push(result); /// if (result != void) evalStack.Push(result);
/// } /// }
/// return FinalInstruction.Phase1().Phase2();
/// </code> /// </code>
/// <para> /// <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> /// </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> /// </summary>
/// <remarks> /// <remarks>
/// Fun fact: the empty block acts like a phase-2 pop instruction, /// Fun fact: the empty block acts like a phase-2 pop instruction,
@ -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 /// 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. /// pop elements that they didn't push themselves.
/// </remarks> /// </remarks>
partial class Block : ILInstruction, IEnumerable<ILInstruction> partial class Block : ILInstruction
{ {
public readonly InstructionCollection<ILInstruction> Instructions; public readonly InstructionCollection<ILInstruction> Instructions;
ILInstruction finalInstruction;
public ILInstruction FinalInstruction {
get {
return finalInstruction;
}
set {
ValidateChild(value);
SetChildInstruction(ref finalInstruction, value);
}
}
public int IncomingEdgeCount; public int IncomingEdgeCount;
public Block() : base(OpCode.Block) public Block() : base(OpCode.Block)
{ {
this.Instructions = new InstructionCollection<ILInstruction>(this); this.Instructions = new InstructionCollection<ILInstruction>(this);
this.FinalInstruction = new Nop();
} }
public override StackType ResultType { public override StackType ResultType {
get { get {
if (Instructions.Count == 0) return finalInstruction.ResultType;
return StackType.Void;
else
return Instructions.Last().ResultType;
} }
} }
@ -97,12 +102,21 @@ namespace ICSharpCode.Decompiler.IL
inst.WriteTo(output); inst.WriteTo(output);
output.WriteLine(); output.WriteLine();
} }
if (finalInstruction.OpCode != OpCode.Nop) {
output.Write("final: ");
finalInstruction.WriteTo(output);
output.WriteLine();
}
output.Unindent(); output.Unindent();
output.Write("}"); output.Write("}");
} }
public override IEnumerable<ILInstruction> Children { 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) public override void TransformChildren(ILVisitor<ILInstruction> visitor)
@ -110,22 +124,21 @@ namespace ICSharpCode.Decompiler.IL
for (int i = 0; i < Instructions.Count; i++) { for (int i = 0; i < Instructions.Count; i++) {
Instructions[i] = Instructions[i].AcceptVisitor(visitor); Instructions[i] = Instructions[i].AcceptVisitor(visitor);
} }
FinalInstruction = FinalInstruction.AcceptVisitor(visitor);
} }
protected override InstructionFlags ComputeFlags() protected override InstructionFlags ComputeFlags()
{ {
var flags = InstructionFlags.None; var flags = InstructionFlags.None;
foreach (var inst in Instructions) { foreach (var inst in Instructions) {
flags |= inst.Flags; flags |= Phase1Boundary(inst.Flags);
if (inst.ResultType != StackType.Void) { if (inst.ResultType != StackType.Void) {
// implicit push // implicit push
flags |= InstructionFlags.MayWriteEvaluationStack; flags |= InstructionFlags.MayWriteEvaluationStack;
} }
} }
// implicit pop at end of block flags |= Phase1Boundary(FinalInstruction.Flags);
if ((flags & InstructionFlags.EndPointUnreachable) == 0) return flags;
flags |= InstructionFlags.MayWriteEvaluationStack;
return Phase1Boundary(flags);
} }
/// <summary> /// <summary>
@ -150,21 +163,5 @@ namespace ICSharpCode.Decompiler.IL
// an inline block has no phase-1 effects, so we're immediately done with inlining // an inline block has no phase-1 effects, so we're immediately done with inlining
return this; 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
} }
} }
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) protected internal override ILInstruction VisitBlock(Block block)
{ {
var stack = new InliningStack(); var stack = new InliningStack();
List<ILInstruction> output = new List<ILInstruction>(); List<ILInstruction> output = new List<ILInstruction>();
for (int i = 0; i < block.Instructions.Count; i++) { for (int i = 0; i < block.Instructions.Count; i++) {
var inst = block.Instructions[i]; var inst = block.Instructions[i];
do { inst = DoInline(stack, inst);
inst = inst.AcceptVisitor(this); if (inst.HasFlag(InstructionFlags.MayBranch
stack.didInline = false; | InstructionFlags.MayPeek | InstructionFlags.MayPop
stack.error = false; | InstructionFlags.MayReadEvaluationStack | InstructionFlags.MayWriteEvaluationStack)) {
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)) {
// Values currently on the stack might be used on both sides of the branch, // Values currently on the stack might be used on both sides of the branch,
// so we can't inline them. // 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); FlushInstructionStack(stack, output);
} }
if (inst.ResultType == StackType.Void) { if (inst.ResultType == StackType.Void) {
@ -132,14 +141,13 @@ namespace ICSharpCode.Decompiler.IL
stack.Push(inst); stack.Push(inst);
} }
} }
// Allow inlining into the final instruction
block.FinalInstruction = DoInline(stack, block.FinalInstruction);
FlushInstructionStack(stack, output); FlushInstructionStack(stack, output);
block.Instructions.ReplaceList(output);
if (!(block.Parent is BlockContainer)) { if (!(block.Parent is BlockContainer)) {
// We can't unpack an instruction from a block if there's the potential that this changes return TrySimplifyBlock(block);
// 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];
} }
block.Instructions.ReplaceList(output);
return block; return block;
} }
@ -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, // 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. // we can remove the container.
if (container.Blocks.Count == 1 && container.EntryPoint.IncomingEdgeCount == 1) { if (container.Blocks.Count == 1 && container.EntryPoint.IncomingEdgeCount == 1) {
// If the block has only one instruction, we can remove the block too return TrySimplifyBlock(container.EntryPoint);
// (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 container; 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
{ {
var f = MakeFunction( var f = MakeFunction(
new Block { new Block {
new LdcI4(1), Instructions = {
new LdcI4(2), new LdcI4(1),
new Add(new Pop(StackType.I4), new Pop(StackType.I4), false, Sign.Signed) new LdcI4(2),
new Add(new Pop(StackType.I4), new Pop(StackType.I4), false, Sign.Signed)
}
}); });
f.AcceptVisitor(new TransformingVisitor()); f.AcceptVisitor(new TransformingVisitor());
Assert.AreEqual( 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() f.Body.ToString()
); );
} }
@ -55,26 +61,30 @@ namespace ICSharpCode.Decompiler.Tests.ILTransforms
{ {
var f = MakeFunction( var f = MakeFunction(
new Block { new Block {
new LdcI4(1), Instructions = {
new LdcI4(2), new LdcI4(1),
new LdcI4(3), new LdcI4(2),
new Call(TypeSystem.Action<int, int, int>()) { new LdcI4(3),
Arguments = { new Call(TypeSystem.Action<int, int, int>()) {
new Pop(StackType.I4), Arguments = {
new Block { new Pop(StackType.I4) }, new Pop(StackType.I4),
new Pop(StackType.I4), new Block { FinalInstruction = new Pop(StackType.I4) },
new Pop(StackType.I4),
}
} }
} }
}); });
f.AcceptVisitor(new TransformingVisitor()); f.AcceptVisitor(new TransformingVisitor());
Assert.AreEqual( Assert.AreEqual(
new Block { new Block {
new LdcI4(1), Instructions = {
new Call(TypeSystem.Action<int, int, int>()) { new LdcI4(1),
Arguments = { new Call(TypeSystem.Action<int, int, int>()) {
new LdcI4(2), Arguments = {
new Block { new Pop(StackType.I4) }, new LdcI4(2),
new LdcI4(3) new Block { FinalInstruction = new Pop(StackType.I4) },
new LdcI4(3)
}
} }
} }
}.ToString(), }.ToString(),

Loading…
Cancel
Save