Browse Source

Add loop detection.

pull/728/head
Daniel Grunwald 11 years ago
parent
commit
b2dfdea68e
  1. 19
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  2. 5
      ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
  3. 78
      ICSharpCode.Decompiler/FlowAnalysis/ControlFlowEdge.cs
  4. 191
      ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraph.cs
  5. 439
      ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraphBuilder.cs
  6. 218
      ICSharpCode.Decompiler/FlowAnalysis/ControlFlowNode.cs
  7. 241
      ICSharpCode.Decompiler/FlowAnalysis/ControlStructureDetector.cs
  8. 116
      ICSharpCode.Decompiler/FlowAnalysis/Dominance.cs
  9. 312
      ICSharpCode.Decompiler/FlowAnalysis/OpCodeInfo.cs
  10. 174
      ICSharpCode.Decompiler/FlowAnalysis/SimplifyByRefCalls.cs
  11. 60
      ICSharpCode.Decompiler/FlowAnalysis/SsaBlock.cs
  12. 162
      ICSharpCode.Decompiler/FlowAnalysis/SsaForm.cs
  13. 257
      ICSharpCode.Decompiler/FlowAnalysis/SsaFormBuilder.cs
  14. 191
      ICSharpCode.Decompiler/FlowAnalysis/SsaInstruction.cs
  15. 138
      ICSharpCode.Decompiler/FlowAnalysis/SsaOptimization.cs
  16. 91
      ICSharpCode.Decompiler/FlowAnalysis/SsaVariable.cs
  17. 254
      ICSharpCode.Decompiler/FlowAnalysis/TransformToSsa.cs
  18. 13
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  19. 1
      ICSharpCode.Decompiler/IL/BlockBuilder.cs
  20. 38
      ICSharpCode.Decompiler/IL/Instructions.cs
  21. 5
      ICSharpCode.Decompiler/IL/Instructions.tt
  22. 47
      ICSharpCode.Decompiler/IL/Instructions/Block.cs
  23. 38
      ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs
  24. 8
      ICSharpCode.Decompiler/IL/Instructions/Branch.cs
  25. 28
      ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs
  26. 15
      ICSharpCode.Decompiler/IL/Instructions/InstructionCollection.cs
  27. 50
      ICSharpCode.Decompiler/IL/Instructions/Loop.cs
  28. 206
      ICSharpCode.Decompiler/IL/Transforms/LoopDetection.cs
  29. 2
      ICSharpCode.Decompiler/IL/Transforms/TransformStackIntoVariables.cs
  30. 2
      ICSharpCode.Decompiler/IL/Transforms/TransformStackIntoVariablesState.cs
  31. 0
      ICSharpCode.Decompiler/IL/Transforms/TransformingVisitor.cs
  32. 3
      ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj
  33. 2
      ICSharpCode.Decompiler/Tests/ILTransforms/InliningTests.cs
  34. 13
      ICSharpCode.Decompiler/Tests/Loops.cs
  35. 25
      ILSpy/Languages/ILAstLanguage.cs

19
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -32,6 +32,7 @@ using ICSharpCode.NRefactory.Utils; @@ -32,6 +32,7 @@ using ICSharpCode.NRefactory.Utils;
using Mono.Cecil;
using ICSharpCode.Decompiler.CSharp.Transforms;
using ICSharpCode.Decompiler.IL;
using ICSharpCode.Decompiler.IL.Transforms;
namespace ICSharpCode.Decompiler.CSharp
{
@ -39,6 +40,12 @@ namespace ICSharpCode.Decompiler.CSharp @@ -39,6 +40,12 @@ namespace ICSharpCode.Decompiler.CSharp
{
readonly DecompilerTypeSystem typeSystem;
List<IILTransform> ilTransforms = new List<IILTransform> {
new LoopDetection(),
new TransformingVisitor(),
new TransformStackIntoVariables()
};
List<IAstTransform> astTransforms = new List<IAstTransform> {
//new PushNegation(),
//new DelegateConstruction(context),
@ -56,13 +63,15 @@ namespace ICSharpCode.Decompiler.CSharp @@ -56,13 +63,15 @@ namespace ICSharpCode.Decompiler.CSharp
//new FlattenSwitchBlocks(),
};
List<IILTransform> ilTransforms = new List<IILTransform> {
new TransformingVisitor(),
new TransformStackIntoVariables()
};
public CancellationToken CancellationToken { get; set; }
/// <summary>
/// IL transforms.
/// </summary>
public IList<IILTransform> ILTransforms {
get { return ilTransforms; }
}
/// <summary>
/// C# AST transforms.
/// </summary>

5
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

@ -73,6 +73,11 @@ namespace ICSharpCode.Decompiler.CSharp @@ -73,6 +73,11 @@ namespace ICSharpCode.Decompiler.CSharp
var falseStatement = inst.FalseInst.OpCode == OpCode.Nop ? null : Convert(inst.FalseInst);
return new IfElseStatement(condition, trueStatement, falseStatement);
}
protected internal override Statement VisitLoop(Loop inst)
{
return new WhileStatement(new PrimitiveExpression(true), Convert(inst.Body));
}
protected internal override Statement VisitBranch(Branch inst)
{

78
ICSharpCode.Decompiler/FlowAnalysis/ControlFlowEdge.cs

@ -1,78 +0,0 @@ @@ -1,78 +0,0 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// 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;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
/// <summary>
/// Describes the type of a control flow egde.
/// </summary>
public enum JumpType
{
/// <summary>
/// A regular control flow edge.
/// </summary>
Normal,
/// <summary>
/// Jump to exception handler (an exception occurred)
/// </summary>
JumpToExceptionHandler,
/// <summary>
/// Jump from try block to leave target:
/// This is not a real jump, as the finally handler is executed first!
/// </summary>
LeaveTry,
/// <summary>
/// Jump at endfinally (to any of the potential leave targets).
/// For any leave-instruction, control flow enters the finally block - the edge to the leave target (LeaveTry) is not a real control flow edge.
/// EndFinally edges are inserted at the end of the finally block, jumping to any of the targets of the leave instruction.
/// This edge type is only used when copying of finally blocks is disabled (with copying, a normal deterministic edge is used at each copy of the endfinally node).
/// </summary>
EndFinally
}
/// <summary>
/// Represents an edge in the control flow graph, pointing from Source to Target.
/// </summary>
public sealed class ControlFlowEdge
{
public readonly ControlFlowNode Source;
public readonly ControlFlowNode Target;
public readonly JumpType Type;
public ControlFlowEdge(ControlFlowNode source, ControlFlowNode target, JumpType type)
{
this.Source = source;
this.Target = target;
this.Type = type;
}
public override string ToString()
{
switch (Type) {
case JumpType.Normal:
return "#" + Target.BlockIndex;
case JumpType.JumpToExceptionHandler:
return "e:#" + Target.BlockIndex;
default:
return Type.ToString() + ":#" + Target.BlockIndex;
}
}
}
}

191
ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraph.cs

@ -1,191 +0,0 @@ @@ -1,191 +0,0 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// 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.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using ICSharpCode.NRefactory.Utils;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
/// <summary>
/// Contains the control flow graph.
/// </summary>
/// <remarks>Use ControlFlowGraph builder to create instances of the ControlFlowGraph.</remarks>
public sealed class ControlFlowGraph
{
readonly ReadOnlyCollection<ControlFlowNode> nodes;
public ControlFlowNode EntryPoint {
get { return nodes[0]; }
}
public ControlFlowNode RegularExit {
get { return nodes[1]; }
}
public ControlFlowNode ExceptionalExit {
get { return nodes[2]; }
}
public ReadOnlyCollection<ControlFlowNode> Nodes {
get { return nodes; }
}
internal ControlFlowGraph(ControlFlowNode[] nodes)
{
this.nodes = new ReadOnlyCollection<ControlFlowNode>(nodes);
Debug.Assert(EntryPoint.NodeType == ControlFlowNodeType.EntryPoint);
Debug.Assert(RegularExit.NodeType == ControlFlowNodeType.RegularExit);
Debug.Assert(ExceptionalExit.NodeType == ControlFlowNodeType.ExceptionalExit);
}
public GraphVizGraph ExportGraph()
{
GraphVizGraph graph = new GraphVizGraph();
foreach (ControlFlowNode node in nodes) {
graph.AddNode(new GraphVizNode(node.BlockIndex) { label = node.ToString(), shape = "box" });
}
foreach (ControlFlowNode node in nodes) {
foreach (ControlFlowEdge edge in node.Outgoing) {
GraphVizEdge e = new GraphVizEdge(edge.Source.BlockIndex, edge.Target.BlockIndex);
switch (edge.Type) {
case JumpType.Normal:
break;
case JumpType.LeaveTry:
case JumpType.EndFinally:
e.color = "red";
break;
default:
e.color = "gray";
//e.constraint = false;
break;
}
graph.AddEdge(e);
}
if (node.ImmediateDominator != null) {
graph.AddEdge(new GraphVizEdge(node.ImmediateDominator.BlockIndex, node.BlockIndex) { color = "green", constraint = false });
}
}
return graph;
}
/// <summary>
/// Resets "Visited" to false for all nodes in this graph.
/// </summary>
public void ResetVisited()
{
foreach (ControlFlowNode node in nodes) {
node.Visited = false;
}
}
/// <summary>
/// Computes the dominator tree.
/// </summary>
public void ComputeDominance(CancellationToken cancellationToken = default(CancellationToken))
{
// A Simple, Fast Dominance Algorithm
// Keith D. Cooper, Timothy J. Harvey and Ken Kennedy
EntryPoint.ImmediateDominator = EntryPoint;
bool changed = true;
while (changed) {
changed = false;
ResetVisited();
cancellationToken.ThrowIfCancellationRequested();
// for all nodes b except the entry point
EntryPoint.TraversePreOrder(
b => b.Successors,
b => {
if (b != EntryPoint) {
ControlFlowNode newIdom = b.Predecessors.First(block => block.Visited && block != b);
// for all other predecessors p of b
foreach (ControlFlowNode p in b.Predecessors) {
if (p != b && p.ImmediateDominator != null) {
newIdom = FindCommonDominator(p, newIdom);
}
}
if (b.ImmediateDominator != newIdom) {
b.ImmediateDominator = newIdom;
changed = true;
}
}
});
}
EntryPoint.ImmediateDominator = null;
foreach (ControlFlowNode node in nodes) {
if (node.ImmediateDominator != null)
node.ImmediateDominator.DominatorTreeChildren.Add(node);
}
}
static ControlFlowNode FindCommonDominator(ControlFlowNode b1, ControlFlowNode b2)
{
// Here we could use the postorder numbers to get rid of the hashset, see "A Simple, Fast Dominance Algorithm"
HashSet<ControlFlowNode> path1 = new HashSet<ControlFlowNode>();
while (b1 != null && path1.Add(b1))
b1 = b1.ImmediateDominator;
while (b2 != null) {
if (path1.Contains(b2))
return b2;
else
b2 = b2.ImmediateDominator;
}
throw new Exception("No common dominator found!");
}
/// <summary>
/// Computes dominance frontiers.
/// This method requires that the dominator tree is already computed!
/// </summary>
public void ComputeDominanceFrontier()
{
ResetVisited();
EntryPoint.TraversePostOrder(
b => b.DominatorTreeChildren,
n => {
//logger.WriteLine("Calculating dominance frontier for " + n.Name);
n.DominanceFrontier = new HashSet<ControlFlowNode>();
// DF_local computation
foreach (ControlFlowNode succ in n.Successors) {
if (succ.ImmediateDominator != n) {
//logger.WriteLine(" local: " + succ.Name);
n.DominanceFrontier.Add(succ);
}
}
// DF_up computation
foreach (ControlFlowNode child in n.DominatorTreeChildren) {
foreach (ControlFlowNode p in child.DominanceFrontier) {
if (p.ImmediateDominator != n) {
//logger.WriteLine(" DF_up: " + p.Name + " (child=" + child.Name);
n.DominanceFrontier.Add(p);
}
}
}
});
}
}
}

439
ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraphBuilder.cs

@ -1,439 +0,0 @@ @@ -1,439 +0,0 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// 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.Diagnostics;
using System.Linq;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
/// <summary>
/// Constructs the Control Flow Graph from a Cecil method body.
/// </summary>
public sealed class ControlFlowGraphBuilder
{
public static ControlFlowGraph Build(MethodBody methodBody)
{
return new ControlFlowGraphBuilder(methodBody).Build();
}
// This option controls how finally blocks are handled:
// false means that the endfinally instruction will jump to any of the leave targets (EndFinally edge type).
// true means that a copy of the whole finally block is created for each leave target. In this case, each endfinally node will be connected with the leave
// target using a normal edge.
bool copyFinallyBlocks = false;
MethodBody methodBody;
int[] offsets; // array index = instruction index; value = IL offset
bool[] hasIncomingJumps; // array index = instruction index
List<ControlFlowNode> nodes = new List<ControlFlowNode>();
ControlFlowNode entryPoint;
ControlFlowNode regularExit;
ControlFlowNode exceptionalExit;
private ControlFlowGraphBuilder(MethodBody methodBody)
{
this.methodBody = methodBody;
offsets = methodBody.Instructions.Select(i => i.Offset).ToArray();
hasIncomingJumps = new bool[methodBody.Instructions.Count];
entryPoint = new ControlFlowNode(0, 0, ControlFlowNodeType.EntryPoint);
nodes.Add(entryPoint);
regularExit = new ControlFlowNode(1, -1, ControlFlowNodeType.RegularExit);
nodes.Add(regularExit);
exceptionalExit = new ControlFlowNode(2, -1, ControlFlowNodeType.ExceptionalExit);
nodes.Add(exceptionalExit);
Debug.Assert(nodes.Count == 3);
}
/// <summary>
/// Determines the index of the instruction (for use with the hasIncomingJumps array)
/// </summary>
int GetInstructionIndex(Instruction inst)
{
int index = Array.BinarySearch(offsets, inst.Offset);
Debug.Assert(index >= 0);
return index;
}
/// <summary>
/// Builds the ControlFlowGraph.
/// </summary>
public ControlFlowGraph Build()
{
CalculateHasIncomingJumps();
CreateNodes();
CreateRegularControlFlow();
CreateExceptionalControlFlow();
if (copyFinallyBlocks)
CopyFinallyBlocksIntoLeaveEdges();
else
TransformLeaveEdges();
return new ControlFlowGraph(nodes.ToArray());
}
#region Step 1: calculate which instructions are the targets of jump instructions.
void CalculateHasIncomingJumps()
{
foreach (Instruction inst in methodBody.Instructions) {
if (inst.OpCode.OperandType == OperandType.InlineBrTarget || inst.OpCode.OperandType == OperandType.ShortInlineBrTarget) {
hasIncomingJumps[GetInstructionIndex((Instruction)inst.Operand)] = true;
} else if (inst.OpCode.OperandType == OperandType.InlineSwitch) {
foreach (Instruction i in (Instruction[])inst.Operand)
hasIncomingJumps[GetInstructionIndex(i)] = true;
}
}
foreach (ExceptionHandler eh in methodBody.ExceptionHandlers) {
if (eh.FilterStart != null) {
hasIncomingJumps[GetInstructionIndex(eh.FilterStart)] = true;
}
hasIncomingJumps[GetInstructionIndex(eh.HandlerStart)] = true;
}
}
#endregion
#region Step 2: create nodes
void CreateNodes()
{
// Step 2a: find basic blocks and create nodes for them
for (int i = 0; i < methodBody.Instructions.Count; i++) {
Instruction blockStart = methodBody.Instructions[i];
ExceptionHandler blockStartEH = FindInnermostExceptionHandler(blockStart.Offset);
// try and see how big we can make that block:
for (; i + 1 < methodBody.Instructions.Count; i++) {
Instruction inst = methodBody.Instructions[i];
if (IsBranch(inst.OpCode) || CanThrowException(inst.OpCode))
break;
if (hasIncomingJumps[i + 1])
break;
if (inst.Next != null) {
// ensure that blocks never contain instructions from different try blocks
ExceptionHandler instEH = FindInnermostExceptionHandler(inst.Next.Offset);
if (instEH != blockStartEH)
break;
}
}
nodes.Add(new ControlFlowNode(nodes.Count, blockStart, methodBody.Instructions[i]));
}
// Step 2b: Create special nodes for the exception handling constructs
foreach (ExceptionHandler handler in methodBody.ExceptionHandlers) {
if (handler.HandlerType == ExceptionHandlerType.Filter)
throw new NotSupportedException();
ControlFlowNode endFinallyOrFaultNode = null;
if (handler.HandlerType == ExceptionHandlerType.Finally || handler.HandlerType == ExceptionHandlerType.Fault) {
endFinallyOrFaultNode = new ControlFlowNode(nodes.Count, handler.HandlerEnd.Offset, ControlFlowNodeType.EndFinallyOrFault);
nodes.Add(endFinallyOrFaultNode);
}
nodes.Add(new ControlFlowNode(nodes.Count, handler, endFinallyOrFaultNode));
}
}
#endregion
#region Step 3: create edges for the normal flow of control (assuming no exceptions thrown)
void CreateRegularControlFlow()
{
CreateEdge(entryPoint, methodBody.Instructions[0], JumpType.Normal);
foreach (ControlFlowNode node in nodes) {
if (node.End != null) {
// create normal edges from one instruction to the next
if (!OpCodeInfo.IsUnconditionalBranch(node.End.OpCode))
CreateEdge(node, node.End.Next, JumpType.Normal);
// create edges for branch instructions
if (node.End.OpCode.OperandType == OperandType.InlineBrTarget || node.End.OpCode.OperandType == OperandType.ShortInlineBrTarget) {
if (node.End.OpCode == OpCodes.Leave || node.End.OpCode == OpCodes.Leave_S) {
var handlerBlock = FindInnermostHandlerBlock(node.End.Offset);
if (handlerBlock.NodeType == ControlFlowNodeType.FinallyOrFaultHandler)
CreateEdge(node, (Instruction)node.End.Operand, JumpType.LeaveTry);
else
CreateEdge(node, (Instruction)node.End.Operand, JumpType.Normal);
} else {
CreateEdge(node, (Instruction)node.End.Operand, JumpType.Normal);
}
} else if (node.End.OpCode.OperandType == OperandType.InlineSwitch) {
foreach (Instruction i in (Instruction[])node.End.Operand)
CreateEdge(node, i, JumpType.Normal);
}
// create edges for return instructions
if (node.End.OpCode.FlowControl == FlowControl.Return) {
switch (node.End.OpCode.Code) {
case Code.Ret:
CreateEdge(node, regularExit, JumpType.Normal);
break;
case Code.Endfinally:
ControlFlowNode handlerBlock = FindInnermostHandlerBlock(node.End.Offset);
if (handlerBlock.EndFinallyOrFaultNode == null)
throw new InvalidProgramException("Found endfinally in block " + handlerBlock);
CreateEdge(node, handlerBlock.EndFinallyOrFaultNode, JumpType.Normal);
break;
default:
throw new NotSupportedException(node.End.OpCode.ToString());
}
}
}
}
}
#endregion
#region Step 4: create edges for the exceptional control flow (from instructions that might throw, to the innermost containing exception handler)
void CreateExceptionalControlFlow()
{
foreach (ControlFlowNode node in nodes) {
if (node.End != null && CanThrowException(node.End.OpCode)) {
CreateEdge(node, FindInnermostExceptionHandlerNode(node.End.Offset), JumpType.JumpToExceptionHandler);
}
if (node.ExceptionHandler != null) {
if (node.EndFinallyOrFaultNode != null) {
// For Fault and Finally blocks, create edge from "EndFinally" to next exception handler.
// This represents the exception bubbling up after finally block was executed.
CreateEdge(node.EndFinallyOrFaultNode, FindParentExceptionHandlerNode(node), JumpType.JumpToExceptionHandler);
} else {
// For Catch blocks, create edge from "CatchHandler" block (at beginning) to next exception handler.
// This represents the exception bubbling up because it did not match the type of the catch block.
CreateEdge(node, FindParentExceptionHandlerNode(node), JumpType.JumpToExceptionHandler);
}
CreateEdge(node, node.ExceptionHandler.HandlerStart, JumpType.Normal);
}
}
}
ExceptionHandler FindInnermostExceptionHandler(int instructionOffsetInTryBlock)
{
foreach (ExceptionHandler h in methodBody.ExceptionHandlers) {
if (h.TryStart.Offset <= instructionOffsetInTryBlock && instructionOffsetInTryBlock < h.TryEnd.Offset) {
return h;
}
}
return null;
}
ControlFlowNode FindInnermostExceptionHandlerNode(int instructionOffsetInTryBlock)
{
ExceptionHandler h = FindInnermostExceptionHandler(instructionOffsetInTryBlock);
if (h != null)
return nodes.Single(n => n.ExceptionHandler == h && n.CopyFrom == null);
else
return exceptionalExit;
}
ControlFlowNode FindInnermostHandlerBlock(int instructionOffset)
{
foreach (ExceptionHandler h in methodBody.ExceptionHandlers) {
if (h.TryStart.Offset <= instructionOffset && instructionOffset < h.TryEnd.Offset
|| h.HandlerStart.Offset <= instructionOffset && instructionOffset < h.HandlerEnd.Offset)
{
return nodes.Single(n => n.ExceptionHandler == h && n.CopyFrom == null);
}
}
return exceptionalExit;
}
ControlFlowNode FindParentExceptionHandlerNode(ControlFlowNode exceptionHandler)
{
Debug.Assert(exceptionHandler.NodeType == ControlFlowNodeType.CatchHandler
|| exceptionHandler.NodeType == ControlFlowNodeType.FinallyOrFaultHandler);
int offset = exceptionHandler.ExceptionHandler.TryStart.Offset;
for (int i = exceptionHandler.BlockIndex + 1; i < nodes.Count; i++) {
ExceptionHandler h = nodes[i].ExceptionHandler;
if (h != null && h.TryStart.Offset <= offset && offset < h.TryEnd.Offset)
return nodes[i];
}
return exceptionalExit;
}
#endregion
#region Step 5a: replace LeaveTry edges with EndFinally edges
// this is used only for copyFinallyBlocks==false; see Step 5b otherwise
void TransformLeaveEdges()
{
for (int i = nodes.Count - 1; i >= 0; i--) {
ControlFlowNode node = nodes[i];
if (node.End != null && node.Outgoing.Count == 1 && node.Outgoing[0].Type == JumpType.LeaveTry) {
Debug.Assert(node.End.OpCode == OpCodes.Leave || node.End.OpCode == OpCodes.Leave_S);
ControlFlowNode target = node.Outgoing[0].Target;
// remove the edge
target.Incoming.Remove(node.Outgoing[0]);
node.Outgoing.Clear();
ControlFlowNode handler = FindInnermostExceptionHandlerNode(node.End.Offset);
Debug.Assert(handler.NodeType == ControlFlowNodeType.FinallyOrFaultHandler);
CreateEdge(node, handler, JumpType.Normal);
CreateEdge(handler.EndFinallyOrFaultNode, target, JumpType.EndFinally);
}
}
}
#endregion
#region Step 5b: copy finally blocks into the LeaveTry edges
void CopyFinallyBlocksIntoLeaveEdges()
{
// We need to process try-finally blocks inside-out.
// We'll do that by going through all instructions in reverse order
for (int i = nodes.Count - 1; i >= 0; i--) {
ControlFlowNode node = nodes[i];
if (node.End != null && node.Outgoing.Count == 1 && node.Outgoing[0].Type == JumpType.LeaveTry) {
Debug.Assert(node.End.OpCode == OpCodes.Leave || node.End.OpCode == OpCodes.Leave_S);
ControlFlowNode target = node.Outgoing[0].Target;
// remove the edge
target.Incoming.Remove(node.Outgoing[0]);
node.Outgoing.Clear();
ControlFlowNode handler = FindInnermostExceptionHandlerNode(node.End.Offset);
Debug.Assert(handler.NodeType == ControlFlowNodeType.FinallyOrFaultHandler);
ControlFlowNode copy = CopyFinallySubGraph(handler, handler.EndFinallyOrFaultNode, target);
CreateEdge(node, copy, JumpType.Normal);
}
}
}
/// <summary>
/// Creates a copy of all nodes pointing to 'end' and replaces those references with references to 'newEnd'.
/// Nodes pointing to the copied node are copied recursively to update those references, too.
/// This recursion stops at 'start'. The modified version of start is returned.
/// </summary>
ControlFlowNode CopyFinallySubGraph(ControlFlowNode start, ControlFlowNode end, ControlFlowNode newEnd)
{
return new CopyFinallySubGraphLogic(this, start, end, newEnd).CopyFinallySubGraph();
}
class CopyFinallySubGraphLogic
{
readonly ControlFlowGraphBuilder builder;
readonly Dictionary<ControlFlowNode, ControlFlowNode> oldToNew = new Dictionary<ControlFlowNode, ControlFlowNode>();
readonly ControlFlowNode start;
readonly ControlFlowNode end;
readonly ControlFlowNode newEnd;
public CopyFinallySubGraphLogic(ControlFlowGraphBuilder builder, ControlFlowNode start, ControlFlowNode end, ControlFlowNode newEnd)
{
this.builder = builder;
this.start = start;
this.end = end;
this.newEnd = newEnd;
}
internal ControlFlowNode CopyFinallySubGraph()
{
foreach (ControlFlowNode n in end.Predecessors) {
CollectNodes(n);
}
foreach (var pair in oldToNew)
ReconstructEdges(pair.Key, pair.Value);
return GetNew(start);
}
void CollectNodes(ControlFlowNode node)
{
if (node == end || node == newEnd)
throw new InvalidOperationException("unexpected cycle involving finally construct");
if (!oldToNew.ContainsKey(node)) {
int newBlockIndex = builder.nodes.Count;
ControlFlowNode copy;
switch (node.NodeType) {
case ControlFlowNodeType.Normal:
copy = new ControlFlowNode(newBlockIndex, node.Start, node.End);
break;
case ControlFlowNodeType.FinallyOrFaultHandler:
copy = new ControlFlowNode(newBlockIndex, node.ExceptionHandler, node.EndFinallyOrFaultNode);
break;
default:
// other nodes shouldn't occur when copying finally blocks
throw new NotSupportedException(node.NodeType.ToString());
}
copy.CopyFrom = node;
builder.nodes.Add(copy);
oldToNew.Add(node, copy);
if (node != start) {
foreach (ControlFlowNode n in node.Predecessors) {
CollectNodes(n);
}
}
}
}
void ReconstructEdges(ControlFlowNode oldNode, ControlFlowNode newNode)
{
foreach (ControlFlowEdge oldEdge in oldNode.Outgoing) {
builder.CreateEdge(newNode, GetNew(oldEdge.Target), oldEdge.Type);
}
}
ControlFlowNode GetNew(ControlFlowNode oldNode)
{
if (oldNode == end)
return newEnd;
ControlFlowNode newNode;
if (oldToNew.TryGetValue(oldNode, out newNode))
return newNode;
return oldNode;
}
}
#endregion
#region CreateEdge methods
void CreateEdge(ControlFlowNode fromNode, Instruction toInstruction, JumpType type)
{
CreateEdge(fromNode, nodes.Single(n => n.Start == toInstruction), type);
}
void CreateEdge(ControlFlowNode fromNode, ControlFlowNode toNode, JumpType type)
{
ControlFlowEdge edge = new ControlFlowEdge(fromNode, toNode, type);
fromNode.Outgoing.Add(edge);
toNode.Incoming.Add(edge);
}
#endregion
#region OpCode info
static bool CanThrowException(OpCode opcode)
{
if (opcode.OpCodeType == OpCodeType.Prefix)
return false;
return OpCodeInfo.Get(opcode).CanThrow;
}
static bool IsBranch(OpCode opcode)
{
if (opcode.OpCodeType == OpCodeType.Prefix)
return false;
switch (opcode.FlowControl) {
case FlowControl.Cond_Branch:
case FlowControl.Branch:
case FlowControl.Throw:
case FlowControl.Return:
return true;
case FlowControl.Next:
case FlowControl.Call:
return false;
default:
throw new NotSupportedException(opcode.FlowControl.ToString());
}
}
#endregion
}
}

218
ICSharpCode.Decompiler/FlowAnalysis/ControlFlowNode.cs

@ -22,95 +22,37 @@ using System.Diagnostics; @@ -22,95 +22,37 @@ using System.Diagnostics;
using System.IO;
using System.Linq;
using ICSharpCode.NRefactory.Utils;
using ICSharpCode.Decompiler.Disassembler;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
/// <summary>
/// Type of the control flow node
/// </summary>
public enum ControlFlowNodeType
{
/// <summary>
/// A normal node represents a basic block.
/// </summary>
Normal,
/// <summary>
/// The entry point of the method.
/// </summary>
EntryPoint,
/// <summary>
/// The exit point of the method (every ret instruction branches to this node)
/// </summary>
RegularExit,
/// <summary>
/// This node represents leaving a method irregularly by throwing an exception.
/// </summary>
ExceptionalExit,
/// <summary>
/// This node is used as a header for exception handler blocks.
/// </summary>
CatchHandler,
/// <summary>
/// This node is used as a header for finally blocks and fault blocks.
/// Every leave instruction in the try block leads to the handler of the containing finally block;
/// and exceptional control flow also leads to this handler.
/// </summary>
FinallyOrFaultHandler,
/// <summary>
/// This node is used as footer for finally blocks and fault blocks.
/// Depending on the "copyFinallyBlocks" option used when creating the graph, it is connected with all leave targets using
/// EndFinally edges (when not copying); or with a specific leave target using a normal edge (when copying).
/// For fault blocks, an exception edge is used to represent the "re-throwing" of the exception.
/// </summary>
EndFinallyOrFault
}
/// <summary>
/// Represents a block in the control flow graph.
/// </summary>
public sealed class ControlFlowNode
public class ControlFlowNode
{
/// <summary>
/// Index of this node in the ControlFlowGraph.Nodes collection.
/// </summary>
public readonly int BlockIndex;
/// <summary>
/// Gets the IL offset of this node.
/// </summary>
public readonly int Offset;
/// <summary>
/// Type of the node.
/// </summary>
public readonly ControlFlowNodeType NodeType;
public object UserData;
/// <summary>
/// If this node is a FinallyOrFaultHandler node, this field points to the corresponding EndFinallyOrFault node.
/// Otherwise, this field is null.
/// Visited flag, used in various algorithms.
/// </summary>
public readonly ControlFlowNode EndFinallyOrFaultNode;
public bool Visited;
/// <summary>
/// Visited flag, used in various algorithms.
/// Before using it in your algorithm, reset it to false by calling ControlFlowGraph.ResetVisited();
/// Gets the node index in a post-order traversal of the control flow graph, starting at the
/// entry point. This field gets computed by dominance analysis.
/// </summary>
public bool Visited;
public int PostOrderNumber;
/// <summary>
/// Gets whether this node is reachable. Requires that dominance is computed!
/// </summary>
public bool IsReachable {
get { return ImmediateDominator != null || NodeType == ControlFlowNodeType.EntryPoint; }
get { return DominatorTreeChildren != null; }
}
/// <summary>
/// Signalizes that this node is a copy of another node.
/// </summary>
public ControlFlowNode CopyFrom { get; internal set; }
/// <summary>
/// Gets the immediate dominator (the parent in the dominator tree).
/// Null if dominance has not been calculated; or if the node is unreachable.
@ -119,113 +61,24 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -119,113 +61,24 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
/// <summary>
/// List of children in the dominator tree.
/// Null if dominance has not been calculated; or if the node is unreachable.
/// </summary>
public readonly List<ControlFlowNode> DominatorTreeChildren = new List<ControlFlowNode>();
/// <summary>
/// The dominance frontier of this node.
/// This is the set of nodes for which this node dominates a predecessor, but which are not strictly dominated by this node.
/// </summary>
/// <remarks>
/// b.DominanceFrontier = { y in CFG; (exists p in predecessors(y): b dominates p) and not (b strictly dominates y)}
/// </remarks>
public HashSet<ControlFlowNode> DominanceFrontier;
/// <summary>
/// Start of code block represented by this node. Only set for nodetype == Normal.
/// </summary>
public readonly Instruction Start;
/// <summary>
/// End of the code block represented by this node. Only set for nodetype == Normal.
/// The end is exclusive, the end instruction itself does not belong to this block.
/// </summary>
public readonly Instruction End;
/// <summary>
/// Gets the exception handler associated with this node.
/// Only set for nodetype == CatchHandler or nodetype == FinallyOrFaultHandler.
/// </summary>
public readonly ExceptionHandler ExceptionHandler;
public List<ControlFlowNode> DominatorTreeChildren { get; internal set; }
/// <summary>
/// List of incoming control flow edges.
/// </summary>
public readonly List<ControlFlowEdge> Incoming = new List<ControlFlowEdge>();
public readonly List<ControlFlowNode> Predecessors = new List<ControlFlowNode>();
/// <summary>
/// List of outgoing control flow edges.
/// </summary>
public readonly List<ControlFlowEdge> Outgoing = new List<ControlFlowEdge>();
/// <summary>
/// Any user data
/// </summary>
public object UserData;
internal ControlFlowNode(int blockIndex, int offset, ControlFlowNodeType nodeType)
{
this.BlockIndex = blockIndex;
this.Offset = offset;
this.NodeType = nodeType;
}
internal ControlFlowNode(int blockIndex, Instruction start, Instruction end)
{
if (start == null)
throw new ArgumentNullException("start");
if (end == null)
throw new ArgumentNullException("end");
this.BlockIndex = blockIndex;
this.NodeType = ControlFlowNodeType.Normal;
this.Start = start;
this.End = end;
this.Offset = start.Offset;
}
public readonly List<ControlFlowNode> Successors = new List<ControlFlowNode>();
internal ControlFlowNode(int blockIndex, ExceptionHandler exceptionHandler, ControlFlowNode endFinallyOrFaultNode)
public void AddEdgeTo(ControlFlowNode target)
{
this.BlockIndex = blockIndex;
this.NodeType = endFinallyOrFaultNode != null ? ControlFlowNodeType.FinallyOrFaultHandler : ControlFlowNodeType.CatchHandler;
this.ExceptionHandler = exceptionHandler;
this.EndFinallyOrFaultNode = endFinallyOrFaultNode;
Debug.Assert((exceptionHandler.HandlerType == ExceptionHandlerType.Finally || exceptionHandler.HandlerType == ExceptionHandlerType.Fault) == (endFinallyOrFaultNode != null));
this.Offset = exceptionHandler.HandlerStart.Offset;
}
/// <summary>
/// Gets all predecessors (=sources of incoming edges)
/// </summary>
public IEnumerable<ControlFlowNode> Predecessors {
get {
return Incoming.Select(e => e.Source);
}
}
/// <summary>
/// Gets all successors (=targets of outgoing edges)
/// </summary>
public IEnumerable<ControlFlowNode> Successors {
get {
return Outgoing.Select(e => e.Target);
}
}
/// <summary>
/// Gets all instructions in this node.
/// Returns an empty list for special nodes that don't have any instructions.
/// </summary>
public IEnumerable<Instruction> Instructions {
get {
Instruction inst = Start;
if (inst != null) {
yield return inst;
while (inst != End) {
inst = inst.Next;
yield return inst;
}
}
}
this.Successors.Add(target);
target.Predecessors.Add(this);
}
public void TraversePreOrder(Func<ControlFlowNode, IEnumerable<ControlFlowNode>> children, Action<ControlFlowNode> visitAction)
@ -248,45 +101,6 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -248,45 +101,6 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
visitAction(this);
}
public override string ToString()
{
StringWriter writer = new StringWriter();
switch (NodeType) {
case ControlFlowNodeType.Normal:
writer.Write("Block #{0}", BlockIndex);
if (Start != null)
writer.Write(": IL_{0:x4}", Start.Offset);
if (End != null)
writer.Write(" to IL_{0:x4}", End.GetEndOffset());
break;
case ControlFlowNodeType.CatchHandler:
case ControlFlowNodeType.FinallyOrFaultHandler:
writer.Write("Block #{0}: {1}: ", BlockIndex, NodeType);
Disassembler.DisassemblerHelpers.WriteTo(ExceptionHandler, new PlainTextOutput(writer));
break;
default:
writer.Write("Block #{0}: {1}", BlockIndex, NodeType);
break;
}
// if (ImmediateDominator != null) {
// writer.WriteLine();
// writer.Write("ImmediateDominator: #{0}", ImmediateDominator.BlockIndex);
// }
if (DominanceFrontier != null && DominanceFrontier.Any()) {
writer.WriteLine();
writer.Write("DominanceFrontier: " + string.Join(",", DominanceFrontier.OrderBy(d => d.BlockIndex).Select(d => d.BlockIndex.ToString())));
}
foreach (Instruction inst in this.Instructions) {
writer.WriteLine();
Disassembler.DisassemblerHelpers.WriteTo(inst, new PlainTextOutput(writer));
}
if (UserData != null) {
writer.WriteLine();
writer.Write(UserData.ToString());
}
return writer.ToString();
}
/// <summary>
/// Gets whether <c>this</c> dominates <paramref name="node"/>.
/// </summary>

241
ICSharpCode.Decompiler/FlowAnalysis/ControlStructureDetector.cs

@ -1,241 +0,0 @@ @@ -1,241 +0,0 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// 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.Diagnostics;
using System.Linq;
using System.Threading;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
/// <summary>
/// Detects the structure of the control flow (exception blocks and loops).
/// </summary>
public class ControlStructureDetector
{
public static ControlStructure DetectStructure(ControlFlowGraph g, IEnumerable<ExceptionHandler> exceptionHandlers, CancellationToken cancellationToken)
{
ControlStructure root = new ControlStructure(new HashSet<ControlFlowNode>(g.Nodes), g.EntryPoint, ControlStructureType.Root);
// First build a structure tree out of the exception table
DetectExceptionHandling(root, g, exceptionHandlers);
// Then run the loop detection.
DetectLoops(g, root, cancellationToken);
return root;
}
#region Exception Handling
static void DetectExceptionHandling(ControlStructure current, ControlFlowGraph g, IEnumerable<ExceptionHandler> exceptionHandlers)
{
// We rely on the fact that the exception handlers are sorted so that the innermost come first.
// For each exception handler, we determine the nodes and substructures inside that handler, and move them into a new substructure.
// This is always possible because exception handlers are guaranteed (by the CLR spec) to be properly nested and non-overlapping;
// so they directly form the tree that we need.
foreach (ExceptionHandler eh in exceptionHandlers) {
var tryNodes = FindNodes(current, eh.TryStart, eh.TryEnd);
current.Nodes.ExceptWith(tryNodes);
ControlStructure tryBlock = new ControlStructure(
tryNodes,
g.Nodes.Single(n => n.Start == eh.TryStart),
ControlStructureType.Try);
tryBlock.ExceptionHandler = eh;
MoveControlStructures(current, tryBlock, eh.TryStart, eh.TryEnd);
current.Children.Add(tryBlock);
if (eh.FilterStart != null) {
throw new NotSupportedException();
}
var handlerNodes = FindNodes(current, eh.HandlerStart, eh.HandlerEnd);
var handlerNode = current.Nodes.Single(n => n.ExceptionHandler == eh);
handlerNodes.Add(handlerNode);
if (handlerNode.EndFinallyOrFaultNode != null)
handlerNodes.Add(handlerNode.EndFinallyOrFaultNode);
current.Nodes.ExceptWith(handlerNodes);
ControlStructure handlerBlock = new ControlStructure(
handlerNodes, handlerNode, ControlStructureType.Handler);
handlerBlock.ExceptionHandler = eh;
MoveControlStructures(current, handlerBlock, eh.HandlerStart, eh.HandlerEnd);
current.Children.Add(handlerBlock);
}
}
/// <summary>
/// Removes all nodes from start to end (exclusive) from this ControlStructure and moves them to the target structure.
/// </summary>
static HashSet<ControlFlowNode> FindNodes(ControlStructure current, Instruction startInst, Instruction endInst)
{
HashSet<ControlFlowNode> result = new HashSet<ControlFlowNode>();
int start = startInst.Offset;
int end = endInst.Offset;
foreach (var node in current.Nodes.ToArray()) {
if (node.Start != null && start <= node.Start.Offset && node.Start.Offset < end) {
result.Add(node);
}
}
return result;
}
static void MoveControlStructures(ControlStructure current, ControlStructure target, Instruction startInst, Instruction endInst)
{
for (int i = 0; i < current.Children.Count; i++) {
var child = current.Children[i];
if (startInst.Offset <= child.EntryPoint.Offset && child.EntryPoint.Offset < endInst.Offset) {
current.Children.RemoveAt(i--);
target.Children.Add(child);
target.AllNodes.UnionWith(child.AllNodes);
}
}
}
#endregion
#region Loop Detection
// Loop detection works like this:
// We find a top-level loop by looking for its entry point, which is characterized by a node dominating its own predecessor.
// Then we determine all other nodes that belong to such a loop (all nodes which lead to the entry point, and are dominated by it).
// Finally, we check whether our result conforms with potential existing exception structures, and create the substructure for the loop if successful.
// This algorithm is applied recursively for any substructures (both detected loops and exception blocks)
// But maybe we should get rid of this complex stuff and instead treat every backward jump as a loop?
// That should still work with the IL produced by compilers, and has the advantage that the detected loop bodies are consecutive IL regions.
static void DetectLoops(ControlFlowGraph g, ControlStructure current, CancellationToken cancellationToken)
{
if (!current.EntryPoint.IsReachable)
return;
g.ResetVisited();
cancellationToken.ThrowIfCancellationRequested();
FindLoops(current, current.EntryPoint);
foreach (ControlStructure loop in current.Children)
DetectLoops(g, loop, cancellationToken);
}
static void FindLoops(ControlStructure current, ControlFlowNode node)
{
if (node.Visited)
return;
node.Visited = true;
if (current.Nodes.Contains(node)
&& node.DominanceFrontier.Contains(node)
&& !(node == current.EntryPoint && current.Type == ControlStructureType.Loop))
{
HashSet<ControlFlowNode> loopContents = new HashSet<ControlFlowNode>();
FindLoopContents(current, loopContents, node, node);
List<ControlStructure> containedChildStructures = new List<ControlStructure>();
bool invalidNesting = false;
foreach (ControlStructure childStructure in current.Children) {
if (childStructure.AllNodes.IsSubsetOf(loopContents)) {
containedChildStructures.Add(childStructure);
} else if (childStructure.AllNodes.Intersect(loopContents).Any()) {
invalidNesting = true;
}
}
if (!invalidNesting) {
current.Nodes.ExceptWith(loopContents);
ControlStructure ctl = new ControlStructure(loopContents, node, ControlStructureType.Loop);
foreach (ControlStructure childStructure in containedChildStructures) {
ctl.Children.Add(childStructure);
current.Children.Remove(childStructure);
ctl.Nodes.ExceptWith(childStructure.AllNodes);
}
current.Children.Add(ctl);
}
}
foreach (var edge in node.Outgoing) {
FindLoops(current, edge.Target);
}
}
static void FindLoopContents(ControlStructure current, HashSet<ControlFlowNode> loopContents, ControlFlowNode loopHead, ControlFlowNode node)
{
if (current.AllNodes.Contains(node) && loopHead.Dominates(node) && loopContents.Add(node)) {
foreach (var edge in node.Incoming) {
FindLoopContents(current, loopContents, loopHead, edge.Source);
}
}
}
#endregion
}
public enum ControlStructureType
{
/// <summary>
/// The root block of the method
/// </summary>
Root,
/// <summary>
/// A nested control structure representing a loop.
/// </summary>
Loop,
/// <summary>
/// A nested control structure representing a try block.
/// </summary>
Try,
/// <summary>
/// A nested control structure representing a catch, finally, or fault block.
/// </summary>
Handler,
/// <summary>
/// A nested control structure representing an exception filter block.
/// </summary>
Filter
}
/// <summary>
/// Represents the structure detected by the <see cref="ControlStructureDetector"/>.
///
/// This is a tree of ControlStructure nodes. Each node contains a set of CFG nodes, and every CFG node is contained in exactly one ControlStructure node.
/// </summary>
public class ControlStructure
{
public readonly ControlStructureType Type;
public readonly List<ControlStructure> Children = new List<ControlStructure>();
/// <summary>
/// The nodes in this control structure.
/// </summary>
public readonly HashSet<ControlFlowNode> Nodes;
/// <summary>
/// The nodes in this control structure and in all child control structures.
/// </summary>
public readonly HashSet<ControlFlowNode> AllNodes;
/// <summary>
/// The entry point of this control structure.
/// </summary>
public readonly ControlFlowNode EntryPoint;
/// <summary>
/// The exception handler associated with this Try,Handler or Finally structure.
/// </summary>
public ExceptionHandler ExceptionHandler;
public ControlStructure(HashSet<ControlFlowNode> nodes, ControlFlowNode entryPoint, ControlStructureType type)
{
if (nodes == null)
throw new ArgumentNullException("nodes");
this.Nodes = nodes;
this.EntryPoint = entryPoint;
this.Type = type;
this.AllNodes = new HashSet<ControlFlowNode>(nodes);
}
}
}

116
ICSharpCode.Decompiler/FlowAnalysis/Dominance.cs

@ -0,0 +1,116 @@ @@ -0,0 +1,116 @@
// 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.Diagnostics;
using System.Linq;
using System.Threading;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
/// <summary>
/// Description of Dominance.
/// </summary>
public static class Dominance
{
/// <summary>
/// Computes the dominator tree.
/// </summary>
/// <remarks>
/// Precondition: the dominance tree is not already computed for some nodes reachable from entryPoint
/// (i.e. ImmediateDominator and DominatorTreeChildren are both null),
/// and the visited flag is false for any nodes reachable from entryPoint.
///
/// Postcondition: a dominator tree is constructed for all nodes reachable from entryPoint,
/// and the visited flag remains false.
/// </remarks>
public static void ComputeDominance(ControlFlowNode entryPoint, CancellationToken cancellationToken = default(CancellationToken))
{
// A Simple, Fast Dominance Algorithm
// Keith D. Cooper, Timothy J. Harvey and Ken Kennedy
var nodes = new List<ControlFlowNode>();
entryPoint.TraversePostOrder(n => n.Successors, nodes.Add);
Debug.Assert(nodes.Last() == entryPoint);
for (int i = 0; i < nodes.Count; i++) {
nodes[i].PostOrderNumber = i;
}
// For the purpose of this algorithm, make the entry point its own dominator.
// We'll reset it back to null at the end of this function.
entryPoint.ImmediateDominator = entryPoint;
bool changed;
do {
changed = false;
cancellationToken.ThrowIfCancellationRequested();
// For all nodes b except the entry point (in reverse post-order)
for (int i = nodes.Count - 2; i >= 0; i--) {
ControlFlowNode b = nodes[i];
// Compute new immediate dominator:
ControlFlowNode newIdom = null;
foreach (var p in b.Predecessors) {
// Ignore predecessors that were not processed yet
if (p.ImmediateDominator != null) {
if (newIdom == null)
newIdom = p;
else
newIdom = FindCommonDominator(p, newIdom);
}
}
// The reverse post-order ensures at least one of our predecessors was processed.
Debug.Assert(newIdom != null);
if (newIdom != b.ImmediateDominator) {
b.ImmediateDominator = newIdom;
changed = true;
}
}
} while(changed);
// Create dominator tree for all reachable nodes:
foreach (ControlFlowNode node in nodes) {
if (node.ImmediateDominator != null)
node.DominatorTreeChildren = new List<ControlFlowNode>();
}
entryPoint.ImmediateDominator = null;
foreach (ControlFlowNode node in nodes) {
// Create list of children in dominator tree
if (node.ImmediateDominator != null)
node.ImmediateDominator.DominatorTreeChildren.Add(node);
// Also reset the visited flag
node.Visited = false;
}
}
/// <summary>
/// Returns the common ancestor of a and b in the dominator tree.
///
/// Precondition: a and b are part of the same dominator tree.
/// </summary>
public static ControlFlowNode FindCommonDominator(ControlFlowNode a, ControlFlowNode b)
{
while (a != b) {
while (a.PostOrderNumber < b.PostOrderNumber)
a = a.ImmediateDominator;
while (b.PostOrderNumber < a.PostOrderNumber)
b = b.ImmediateDominator;
}
return a;
}
}
}

312
ICSharpCode.Decompiler/FlowAnalysis/OpCodeInfo.cs

@ -1,312 +0,0 @@ @@ -1,312 +0,0 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// 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.Linq;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
/// <summary>
/// Additional info about opcodes.
/// </summary>
sealed class OpCodeInfo
{
public static bool IsUnconditionalBranch(OpCode opcode)
{
if (opcode.OpCodeType == OpCodeType.Prefix)
return false;
switch (opcode.FlowControl) {
case FlowControl.Branch:
case FlowControl.Throw:
case FlowControl.Return:
return true;
case FlowControl.Next:
case FlowControl.Call:
case FlowControl.Cond_Branch:
return false;
default:
throw new NotSupportedException(opcode.FlowControl.ToString());
}
}
static readonly OpCodeInfo[] knownOpCodes = {
#region Base Instructions
new OpCodeInfo(OpCodes.Add) { CanThrow = false },
new OpCodeInfo(OpCodes.Add_Ovf) { CanThrow = true },
new OpCodeInfo(OpCodes.Add_Ovf_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.And) { CanThrow = false },
new OpCodeInfo(OpCodes.Arglist) { CanThrow = false },
new OpCodeInfo(OpCodes.Beq) { CanThrow = false },
new OpCodeInfo(OpCodes.Beq_S) { CanThrow = false },
new OpCodeInfo(OpCodes.Bge) { CanThrow = false },
new OpCodeInfo(OpCodes.Bge_S) { CanThrow = false },
new OpCodeInfo(OpCodes.Bge_Un) { CanThrow = false },
new OpCodeInfo(OpCodes.Bge_Un_S) { CanThrow = false },
new OpCodeInfo(OpCodes.Bgt) { CanThrow = false },
new OpCodeInfo(OpCodes.Bgt_S) { CanThrow = false },
new OpCodeInfo(OpCodes.Bgt_Un) { CanThrow = false },
new OpCodeInfo(OpCodes.Bgt_Un_S) { CanThrow = false },
new OpCodeInfo(OpCodes.Ble) { CanThrow = false },
new OpCodeInfo(OpCodes.Ble_S) { CanThrow = false },
new OpCodeInfo(OpCodes.Ble_Un) { CanThrow = false },
new OpCodeInfo(OpCodes.Ble_Un_S) { CanThrow = false },
new OpCodeInfo(OpCodes.Blt) { CanThrow = false },
new OpCodeInfo(OpCodes.Blt_S) { CanThrow = false },
new OpCodeInfo(OpCodes.Blt_Un) { CanThrow = false },
new OpCodeInfo(OpCodes.Blt_Un_S) { CanThrow = false },
new OpCodeInfo(OpCodes.Bne_Un) { CanThrow = false },
new OpCodeInfo(OpCodes.Bne_Un_S) { CanThrow = false },
new OpCodeInfo(OpCodes.Br) { CanThrow = false },
new OpCodeInfo(OpCodes.Br_S) { CanThrow = false },
new OpCodeInfo(OpCodes.Break) { CanThrow = true },
new OpCodeInfo(OpCodes.Brfalse) { CanThrow = false },
new OpCodeInfo(OpCodes.Brfalse_S) { CanThrow = false },
new OpCodeInfo(OpCodes.Brtrue) { CanThrow = false },
new OpCodeInfo(OpCodes.Brtrue_S) { CanThrow = false },
new OpCodeInfo(OpCodes.Call) { CanThrow = true },
new OpCodeInfo(OpCodes.Calli) { CanThrow = true },
new OpCodeInfo(OpCodes.Ceq) { CanThrow = false },
new OpCodeInfo(OpCodes.Cgt) { CanThrow = false },
new OpCodeInfo(OpCodes.Cgt_Un) { CanThrow = false },
new OpCodeInfo(OpCodes.Ckfinite) { CanThrow = true },
new OpCodeInfo(OpCodes.Clt) { CanThrow = false },
new OpCodeInfo(OpCodes.Clt_Un) { CanThrow = false },
// conv.<to type>
new OpCodeInfo(OpCodes.Conv_I1) { CanThrow = false },
new OpCodeInfo(OpCodes.Conv_I2) { CanThrow = false },
new OpCodeInfo(OpCodes.Conv_I4) { CanThrow = false },
new OpCodeInfo(OpCodes.Conv_I8) { CanThrow = false },
new OpCodeInfo(OpCodes.Conv_R4) { CanThrow = false },
new OpCodeInfo(OpCodes.Conv_R8) { CanThrow = false },
new OpCodeInfo(OpCodes.Conv_U1) { CanThrow = false },
new OpCodeInfo(OpCodes.Conv_U2) { CanThrow = false },
new OpCodeInfo(OpCodes.Conv_U4) { CanThrow = false },
new OpCodeInfo(OpCodes.Conv_U8) { CanThrow = false },
new OpCodeInfo(OpCodes.Conv_I) { CanThrow = false },
new OpCodeInfo(OpCodes.Conv_U) { CanThrow = false },
new OpCodeInfo(OpCodes.Conv_R_Un) { CanThrow = false },
// conv.ovf.<to type>
new OpCodeInfo(OpCodes.Conv_Ovf_I1) { CanThrow = true},
new OpCodeInfo(OpCodes.Conv_Ovf_I2) { CanThrow = true},
new OpCodeInfo(OpCodes.Conv_Ovf_I4) { CanThrow = true},
new OpCodeInfo(OpCodes.Conv_Ovf_I8) { CanThrow = true},
new OpCodeInfo(OpCodes.Conv_Ovf_U1) { CanThrow = true},
new OpCodeInfo(OpCodes.Conv_Ovf_U2) { CanThrow = true},
new OpCodeInfo(OpCodes.Conv_Ovf_U4) { CanThrow = true},
new OpCodeInfo(OpCodes.Conv_Ovf_U8) { CanThrow = true},
new OpCodeInfo(OpCodes.Conv_Ovf_I) { CanThrow = true},
new OpCodeInfo(OpCodes.Conv_Ovf_U) { CanThrow = true},
// conv.ovf.<to type>.un
new OpCodeInfo(OpCodes.Conv_Ovf_I1_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.Conv_Ovf_I2_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.Conv_Ovf_I4_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.Conv_Ovf_I8_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.Conv_Ovf_U1_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.Conv_Ovf_U2_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.Conv_Ovf_U4_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.Conv_Ovf_U8_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.Conv_Ovf_I_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.Conv_Ovf_U_Un) { CanThrow = true },
//new OpCodeInfo(OpCodes.Cpblk) { CanThrow = true }, - no idea whether this might cause trouble for the type system, C# shouldn't use it so I'll disable it
new OpCodeInfo(OpCodes.Div) { CanThrow = true },
new OpCodeInfo(OpCodes.Div_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.Dup) { CanThrow = true, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Endfilter) { CanThrow = false },
new OpCodeInfo(OpCodes.Endfinally) { CanThrow = false },
//new OpCodeInfo(OpCodes.Initblk) { CanThrow = true }, - no idea whether this might cause trouble for the type system, C# shouldn't use it so I'll disable it
//new OpCodeInfo(OpCodes.Jmp) { CanThrow = true } - We don't support non-local control transfers.
new OpCodeInfo(OpCodes.Ldarg) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Ldarg_0) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Ldarg_1) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Ldarg_2) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Ldarg_3) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Ldarg_S) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Ldarga) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldarga_S) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldc_I4) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldc_I4_M1) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldc_I4_0) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldc_I4_1) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldc_I4_2) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldc_I4_3) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldc_I4_4) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldc_I4_5) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldc_I4_6) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldc_I4_7) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldc_I4_8) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldc_I4_S) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldc_I8) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldc_R4) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldc_R8) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldftn) { CanThrow = false },
// ldind.<type>
new OpCodeInfo(OpCodes.Ldind_I1) { CanThrow = true },
new OpCodeInfo(OpCodes.Ldind_I2) { CanThrow = true },
new OpCodeInfo(OpCodes.Ldind_I4) { CanThrow = true },
new OpCodeInfo(OpCodes.Ldind_I8) { CanThrow = true },
new OpCodeInfo(OpCodes.Ldind_U1) { CanThrow = true },
new OpCodeInfo(OpCodes.Ldind_U2) { CanThrow = true },
new OpCodeInfo(OpCodes.Ldind_U4) { CanThrow = true },
new OpCodeInfo(OpCodes.Ldind_R4) { CanThrow = true },
new OpCodeInfo(OpCodes.Ldind_R8) { CanThrow = true },
new OpCodeInfo(OpCodes.Ldind_I) { CanThrow = true },
new OpCodeInfo(OpCodes.Ldind_Ref) { CanThrow = true },
// the ldloc exceptions described in the spec can only occur on methods without .localsinit - but csc always sets that flag
new OpCodeInfo(OpCodes.Ldloc) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Ldloc_0) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Ldloc_1) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Ldloc_2) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Ldloc_3) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Ldloc_S) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Ldloca) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldloca_S) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldnull) { CanThrow = false },
new OpCodeInfo(OpCodes.Leave) { CanThrow = false },
new OpCodeInfo(OpCodes.Leave_S) { CanThrow = false },
new OpCodeInfo(OpCodes.Localloc) { CanThrow = true },
new OpCodeInfo(OpCodes.Mul) { CanThrow = false },
new OpCodeInfo(OpCodes.Mul_Ovf) { CanThrow = true },
new OpCodeInfo(OpCodes.Mul_Ovf_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.Neg) { CanThrow = false },
new OpCodeInfo(OpCodes.Nop) { CanThrow = false },
new OpCodeInfo(OpCodes.Not) { CanThrow = false },
new OpCodeInfo(OpCodes.Or) { CanThrow = false },
new OpCodeInfo(OpCodes.Pop) { CanThrow = false },
new OpCodeInfo(OpCodes.Rem) { CanThrow = true },
new OpCodeInfo(OpCodes.Rem_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.Ret) { CanThrow = false },
new OpCodeInfo(OpCodes.Shl) { CanThrow = false },
new OpCodeInfo(OpCodes.Shr) { CanThrow = false },
new OpCodeInfo(OpCodes.Shr_Un) { CanThrow = false },
new OpCodeInfo(OpCodes.Starg) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Starg_S) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Stind_I1) { CanThrow = true },
new OpCodeInfo(OpCodes.Stind_I2) { CanThrow = true },
new OpCodeInfo(OpCodes.Stind_I4) { CanThrow = true },
new OpCodeInfo(OpCodes.Stind_I8) { CanThrow = true },
new OpCodeInfo(OpCodes.Stind_R4) { CanThrow = true },
new OpCodeInfo(OpCodes.Stind_R8) { CanThrow = true },
new OpCodeInfo(OpCodes.Stind_I) { CanThrow = true },
new OpCodeInfo(OpCodes.Stind_Ref) { CanThrow = true },
new OpCodeInfo(OpCodes.Stloc) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Stloc_0) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Stloc_1) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Stloc_2) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Stloc_3) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Stloc_S) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Sub) { CanThrow = false },
new OpCodeInfo(OpCodes.Sub_Ovf) { CanThrow = true },
new OpCodeInfo(OpCodes.Sub_Ovf_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.Switch) { CanThrow = false },
new OpCodeInfo(OpCodes.Xor) { CanThrow = false },
#endregion
#region Object model instructions
// CanThrow is true by default - most OO instructions can throw, so we don't specify CanThrow all of the time
new OpCodeInfo(OpCodes.Box),
new OpCodeInfo(OpCodes.Callvirt),
new OpCodeInfo(OpCodes.Castclass),
new OpCodeInfo(OpCodes.Cpobj),
new OpCodeInfo(OpCodes.Initobj) { CanThrow = false },
new OpCodeInfo(OpCodes.Isinst) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldelem_Any),
// ldelem.<type>
new OpCodeInfo(OpCodes.Ldelem_I) ,
new OpCodeInfo(OpCodes.Ldelem_I1),
new OpCodeInfo(OpCodes.Ldelem_I2),
new OpCodeInfo(OpCodes.Ldelem_I4),
new OpCodeInfo(OpCodes.Ldelem_I8),
new OpCodeInfo(OpCodes.Ldelem_R4),
new OpCodeInfo(OpCodes.Ldelem_R8),
new OpCodeInfo(OpCodes.Ldelem_Ref),
new OpCodeInfo(OpCodes.Ldelem_U1),
new OpCodeInfo(OpCodes.Ldelem_U2),
new OpCodeInfo(OpCodes.Ldelem_U4),
new OpCodeInfo(OpCodes.Ldelema) ,
new OpCodeInfo(OpCodes.Ldfld) ,
new OpCodeInfo(OpCodes.Ldflda),
new OpCodeInfo(OpCodes.Ldlen) ,
new OpCodeInfo(OpCodes.Ldobj) ,
new OpCodeInfo(OpCodes.Ldsfld),
new OpCodeInfo(OpCodes.Ldsflda),
new OpCodeInfo(OpCodes.Ldstr) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldtoken) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldvirtftn),
new OpCodeInfo(OpCodes.Mkrefany),
new OpCodeInfo(OpCodes.Newarr),
new OpCodeInfo(OpCodes.Newobj),
new OpCodeInfo(OpCodes.Refanytype) { CanThrow = false },
new OpCodeInfo(OpCodes.Refanyval),
new OpCodeInfo(OpCodes.Rethrow),
new OpCodeInfo(OpCodes.Sizeof) { CanThrow = false },
new OpCodeInfo(OpCodes.Stelem_Any),
new OpCodeInfo(OpCodes.Stelem_I1),
new OpCodeInfo(OpCodes.Stelem_I2),
new OpCodeInfo(OpCodes.Stelem_I4),
new OpCodeInfo(OpCodes.Stelem_I8),
new OpCodeInfo(OpCodes.Stelem_R4),
new OpCodeInfo(OpCodes.Stelem_R8),
new OpCodeInfo(OpCodes.Stelem_Ref),
new OpCodeInfo(OpCodes.Stfld),
new OpCodeInfo(OpCodes.Stobj),
new OpCodeInfo(OpCodes.Stsfld),
new OpCodeInfo(OpCodes.Throw),
new OpCodeInfo(OpCodes.Unbox),
new OpCodeInfo(OpCodes.Unbox_Any),
#endregion
};
static readonly Dictionary<Code, OpCodeInfo> knownOpCodeDict = knownOpCodes.ToDictionary(info => info.OpCode.Code);
public static OpCodeInfo Get(OpCode opCode)
{
return Get(opCode.Code);
}
public static OpCodeInfo Get(Code code)
{
OpCodeInfo info;
if (knownOpCodeDict.TryGetValue(code, out info))
return info;
else
throw new NotSupportedException(code.ToString());
}
OpCode opcode;
private OpCodeInfo(OpCode opcode)
{
this.opcode = opcode;
this.CanThrow = true;
}
public OpCode OpCode { get { return opcode; } }
/// <summary>
/// 'Move' kind of instructions have one input (may be stack or local variable) and copy that value to all outputs (again stack or local variable).
/// </summary>
public bool IsMoveInstruction { get; private set; }
/// <summary>
/// Specifies whether this opcode is capable of throwing exceptions.
/// </summary>
public bool CanThrow { get; private set; }
}
}

174
ICSharpCode.Decompiler/FlowAnalysis/SimplifyByRefCalls.cs

@ -1,174 +0,0 @@ @@ -1,174 +0,0 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// 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.Diagnostics;
using Mono.Cecil;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
/// <summary>
/// This is a transformation working on SSA form.
/// It removes ldloca instructions and replaces them with SpecialOpCode.PrepareByOutCall or SpecialOpCode.PrepareByRefCall.
/// This then allows the variable that had its address taken to also be transformed into SSA.
/// </summary>
sealed class SimplifyByRefCalls
{
public static bool MakeByRefCallsSimple(SsaForm ssaForm)
{
SimplifyByRefCalls instance = new SimplifyByRefCalls(ssaForm);
foreach (SsaBlock block in ssaForm.Blocks) {
for (int i = 0; i < block.Instructions.Count; i++) {
SsaInstruction inst = block.Instructions[i];
if (inst.Instruction != null) {
switch (inst.Instruction.OpCode.Code) {
case Code.Call:
case Code.Callvirt:
instance.MakeByRefCallSimple(block, ref i, (IMethodSignature)inst.Instruction.Operand);
break;
case Code.Initobj:
instance.MakeInitObjCallSimple(block, ref i);
break;
case Code.Ldfld:
instance.MakeLoadFieldCallSimple(block, ref i);
break;
}
}
}
}
instance.RemoveRedundantInstructions();
if (instance.couldSimplifySomething)
ssaForm.ComputeVariableUsage();
return instance.couldSimplifySomething;
}
readonly SsaForm ssaForm;
bool couldSimplifySomething;
// the list of ldloca instructions we will remove
readonly List<SsaInstruction> redundantLoadAddressInstructions = new List<SsaInstruction>();
private SimplifyByRefCalls(SsaForm ssaForm)
{
this.ssaForm = ssaForm;
}
void MakeByRefCallSimple(SsaBlock block, ref int instructionIndexInBlock, IMethodSignature targetMethod)
{
SsaInstruction inst = block.Instructions[instructionIndexInBlock];
for (int i = 0; i < inst.Operands.Length; i++) {
SsaVariable operand = inst.Operands[i];
if (operand.IsSingleAssignment && operand.Usage.Count == 1 && IsLoadAddress(operand.Definition)) {
// address is used for this method call only
Instruction loadAddressInstruction = operand.Definition.Instruction;
// find target parameter type:
bool isOut;
if (i == 0 && targetMethod.HasThis) {
isOut = false;
} else {
ParameterDefinition parameter = targetMethod.Parameters[i - (targetMethod.HasThis ? 1 : 0)];
isOut = parameter.IsOut;
}
SsaVariable addressTakenOf = GetVariableFromLoadAddressInstruction(loadAddressInstruction);
// insert "Prepare" instruction on front
SpecialOpCode loadOpCode = isOut ? SpecialOpCode.PrepareByOutCall : SpecialOpCode.PrepareByRefCall;
block.Instructions.Insert(instructionIndexInBlock++, new SsaInstruction(
block, null, operand, new SsaVariable[] { addressTakenOf }, specialOpCode: loadOpCode));
// insert "WriteAfterByRefOrOutCall" instruction after call
block.Instructions.Insert(instructionIndexInBlock + 1, new SsaInstruction(
block, null, addressTakenOf, new SsaVariable[] { operand }, specialOpCode: SpecialOpCode.WriteAfterByRefOrOutCall));
couldSimplifySomething = true;
// remove the loadAddressInstruction later
// (later because it might be defined in the current block and we don't want instructionIndex to become invalid)
redundantLoadAddressInstructions.Add(operand.Definition);
}
}
}
SsaVariable GetVariableFromLoadAddressInstruction(Instruction loadAddressInstruction)
{
if (loadAddressInstruction.OpCode == OpCodes.Ldloca) {
return ssaForm.GetOriginalVariable((VariableReference)loadAddressInstruction.Operand);
} else {
Debug.Assert(loadAddressInstruction.OpCode == OpCodes.Ldarga);
return ssaForm.GetOriginalVariable((ParameterReference)loadAddressInstruction.Operand);
}
}
static bool IsLoadAddress(SsaInstruction inst)
{
return inst.Instruction != null && (inst.Instruction.OpCode == OpCodes.Ldloca || inst.Instruction.OpCode == OpCodes.Ldarga);
}
void MakeInitObjCallSimple(SsaBlock block, ref int instructionIndexInBlock)
{
SsaInstruction inst = block.Instructions[instructionIndexInBlock];
Debug.Assert(inst.Operands.Length == 1);
SsaVariable operand = inst.Operands[0];
if (operand.IsSingleAssignment && operand.Usage.Count == 1 && IsLoadAddress(operand.Definition)) {
// replace instruction with special "InitObj" instruction
block.Instructions[instructionIndexInBlock] = new SsaInstruction(
inst.ParentBlock, null, GetVariableFromLoadAddressInstruction(operand.Definition.Instruction), null,
specialOpCode: SpecialOpCode.InitObj,
typeOperand: (TypeReference)inst.Instruction.Operand);
couldSimplifySomething = true;
// remove the loadAddressInstruction later
redundantLoadAddressInstructions.Add(operand.Definition);
}
}
void MakeLoadFieldCallSimple(SsaBlock block, ref int instructionIndexInBlock)
{
SsaInstruction inst = block.Instructions[instructionIndexInBlock];
Debug.Assert(inst.Operands.Length == 1);
SsaVariable operand = inst.Operands[0];
if (operand.IsSingleAssignment && operand.Usage.Count == 1 && IsLoadAddress(operand.Definition)) {
// insert special "PrepareForFieldAccess" instruction in front
block.Instructions.Insert(instructionIndexInBlock++, new SsaInstruction(
inst.ParentBlock, null, operand,
new SsaVariable[] { GetVariableFromLoadAddressInstruction(operand.Definition.Instruction) },
specialOpCode: SpecialOpCode.PrepareForFieldAccess));
couldSimplifySomething = true;
// remove the loadAddressInstruction later
redundantLoadAddressInstructions.Add(operand.Definition);
}
}
void RemoveRedundantInstructions()
{
foreach (SsaInstruction inst in redundantLoadAddressInstructions) {
inst.ParentBlock.Instructions.Remove(inst);
}
}
}
}

60
ICSharpCode.Decompiler/FlowAnalysis/SsaBlock.cs

@ -1,60 +0,0 @@ @@ -1,60 +0,0 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// 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.IO;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
/// <summary>
/// A block in a control flow graph; with instructions represented by "SsaInstructions" (instructions use variables, no evaluation stack).
/// Usually these variables are in SSA form to make analysis easier.
/// </summary>
public sealed class SsaBlock
{
public readonly List<SsaBlock> Successors = new List<SsaBlock>();
public readonly List<SsaBlock> Predecessors = new List<SsaBlock>();
public readonly ControlFlowNodeType NodeType;
public readonly List<SsaInstruction> Instructions = new List<SsaInstruction>();
/// <summary>
/// The block index in the control flow graph.
/// This correspons to the node index in ControlFlowGraph.Nodes, so it can be used to retrieve the original CFG node and look
/// up additional information (e.g. dominance).
/// </summary>
public readonly int BlockIndex;
internal SsaBlock(ControlFlowNode node)
{
this.NodeType = node.NodeType;
this.BlockIndex = node.BlockIndex;
}
public override string ToString()
{
StringWriter writer = new StringWriter();
writer.Write("Block #{0} ({1})", BlockIndex, NodeType);
foreach (SsaInstruction inst in Instructions) {
writer.WriteLine();
inst.WriteTo(writer);
}
return writer.ToString();
}
}
}

162
ICSharpCode.Decompiler/FlowAnalysis/SsaForm.cs

@ -1,162 +0,0 @@ @@ -1,162 +0,0 @@
// Copyright (c) 2010 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.ObjectModel;
using System.Diagnostics;
using System.Linq;
using ICSharpCode.NRefactory.Utils;
using Mono.Cecil;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
/// <summary>
/// Represents a graph of SsaBlocks.
/// </summary>
public sealed class SsaForm
{
readonly SsaVariable[] parameters;
readonly SsaVariable[] locals;
public readonly ReadOnlyCollection<SsaVariable> OriginalVariables;
public readonly ReadOnlyCollection<SsaBlock> Blocks;
readonly bool methodHasThis;
public SsaBlock EntryPoint {
get { return this.Blocks[0]; }
}
public SsaBlock RegularExit {
get { return this.Blocks[1]; }
}
public SsaBlock ExceptionalExit {
get { return this.Blocks[2]; }
}
internal SsaForm(SsaBlock[] blocks, SsaVariable[] parameters, SsaVariable[] locals, SsaVariable[] stackLocations, bool methodHasThis)
{
this.parameters = parameters;
this.locals = locals;
this.Blocks = new ReadOnlyCollection<SsaBlock>(blocks);
this.OriginalVariables = new ReadOnlyCollection<SsaVariable>(parameters.Concat(locals).Concat(stackLocations).ToList());
this.methodHasThis = methodHasThis;
Debug.Assert(EntryPoint.NodeType == ControlFlowNodeType.EntryPoint);
Debug.Assert(RegularExit.NodeType == ControlFlowNodeType.RegularExit);
Debug.Assert(ExceptionalExit.NodeType == ControlFlowNodeType.ExceptionalExit);
for (int i = 0; i < this.OriginalVariables.Count; i++) {
this.OriginalVariables[i].OriginalVariableIndex = i;
}
}
public GraphVizGraph ExportBlockGraph(Func<SsaBlock, string> labelProvider = null)
{
if (labelProvider == null)
labelProvider = b => b.ToString();
GraphVizGraph graph = new GraphVizGraph();
foreach (SsaBlock block in this.Blocks) {
graph.AddNode(new GraphVizNode(block.BlockIndex) { label = labelProvider(block), shape = "box" });
}
foreach (SsaBlock block in this.Blocks) {
foreach (SsaBlock s in block.Successors) {
graph.AddEdge(new GraphVizEdge(block.BlockIndex, s.BlockIndex));
}
}
return graph;
}
public GraphVizGraph ExportVariableGraph(Func<SsaVariable, string> labelProvider = null)
{
if (labelProvider == null)
labelProvider = v => v.ToString();
GraphVizGraph graph = new GraphVizGraph();
foreach (SsaVariable v in this.AllVariables) {
graph.AddNode(new GraphVizNode(v.Name) { label = labelProvider(v) });
}
int instructionIndex = 0;
foreach (SsaBlock block in this.Blocks) {
foreach (SsaInstruction inst in block.Instructions) {
if (inst.Operands.Length == 0 && inst.Target == null)
continue;
string id = "instruction" + (++instructionIndex);
graph.AddNode(new GraphVizNode(id) { label = inst.ToString(), shape = "box" });
foreach (SsaVariable op in inst.Operands)
graph.AddEdge(new GraphVizEdge(op.Name, id));
if (inst.Target != null)
graph.AddEdge(new GraphVizEdge(id, inst.Target.Name));
}
}
return graph;
}
public SsaVariable GetOriginalVariable(ParameterReference parameter)
{
if (methodHasThis)
return parameters[parameter.Index + 1];
else
return parameters[parameter.Index];
}
public SsaVariable GetOriginalVariable(VariableReference variable)
{
return locals[variable.Index];
}
#region ComputeVariableUsage
public void ComputeVariableUsage()
{
// clear data from previous runs
foreach (SsaBlock block in this.Blocks) {
foreach (SsaInstruction inst in block.Instructions) {
foreach (SsaVariable v in inst.Operands) {
if (v.Usage != null)
v.Usage.Clear();
}
if (inst.Target != null && inst.Target.Usage != null)
inst.Target.Usage.Clear();
}
}
foreach (SsaBlock block in this.Blocks) {
foreach (SsaInstruction inst in block.Instructions) {
foreach (SsaVariable v in inst.Operands) {
if (v.Usage == null)
v.Usage = new List<SsaInstruction>();
v.Usage.Add(inst);
}
if (inst.Target != null && inst.Target.Usage == null)
inst.Target.Usage = new List<SsaInstruction>();
}
}
}
#endregion
public IEnumerable<SsaVariable> AllVariables {
get {
return (
from block in this.Blocks
from instruction in block.Instructions
where instruction.Target != null
select instruction.Target
).Distinct();
}
}
}
}

257
ICSharpCode.Decompiler/FlowAnalysis/SsaFormBuilder.cs

@ -1,257 +0,0 @@ @@ -1,257 +0,0 @@
// Copyright (c) 2010 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.Diagnostics;
using Mono.Cecil;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
/// <summary>
/// Constructs "SsaForm" graph for a CFG.
/// This class transforms the method from stack-based IL to a register-based IL language.
/// Then it calls into TransformToSsa to convert the resulting graph to static single assignment form.
/// </summary>
public sealed class SsaFormBuilder
{
public static SsaForm Build(MethodDefinition method)
{
if (method == null)
throw new ArgumentNullException("method");
var cfg = ControlFlowGraphBuilder.Build(method.Body);
cfg.ComputeDominance();
cfg.ComputeDominanceFrontier();
var ssa = BuildRegisterIL(method, cfg);
TransformToSsa.Transform(cfg, ssa);
return ssa;
}
public static SsaForm BuildRegisterIL(MethodDefinition method, ControlFlowGraph cfg)
{
if (method == null)
throw new ArgumentNullException("method");
if (cfg == null)
throw new ArgumentNullException("cfg");
return new SsaFormBuilder(method, cfg).Build();
}
readonly MethodDefinition method;
readonly ControlFlowGraph cfg;
readonly SsaBlock[] blocks; // array index = block index
readonly int[] stackSizeAtBlockStart; // array index = block index
readonly SsaVariable[] parameters; // array index = parameter number
readonly SsaVariable[] locals; // array index = local number
readonly SsaVariable[] stackLocations; // array index = position on the IL evaluation stack
SsaForm ssaForm;
private SsaFormBuilder(MethodDefinition method, ControlFlowGraph cfg)
{
this.method = method;
this.cfg = cfg;
this.blocks = new SsaBlock[cfg.Nodes.Count];
this.stackSizeAtBlockStart = new int[cfg.Nodes.Count];
for (int i = 0; i < stackSizeAtBlockStart.Length; i++) {
stackSizeAtBlockStart[i] = -1;
}
stackSizeAtBlockStart[cfg.EntryPoint.BlockIndex] = 0;
this.parameters = new SsaVariable[method.Parameters.Count + (method.HasThis ? 1 : 0)];
if (method.HasThis)
parameters[0] = new SsaVariable(method.Body.ThisParameter);
for (int i = 0; i < method.Parameters.Count; i++)
parameters[i + (method.HasThis ? 1 : 0)] = new SsaVariable(method.Parameters[i]);
this.locals = new SsaVariable[method.Body.Variables.Count];
for (int i = 0; i < locals.Length; i++)
locals[i] = new SsaVariable(method.Body.Variables[i]);
this.stackLocations = new SsaVariable[method.Body.MaxStackSize];
for (int i = 0; i < stackLocations.Length; i++) {
stackLocations[i] = new SsaVariable(i);
}
}
internal SsaForm Build()
{
CreateGraphStructure();
this.ssaForm = new SsaForm(blocks, parameters, locals, stackLocations, method.HasThis);
CreateInstructions(cfg.EntryPoint.BlockIndex);
CreateSpecialInstructions();
return ssaForm;
}
void CreateGraphStructure()
{
for (int i = 0; i < blocks.Length; i++) {
blocks[i] = new SsaBlock(cfg.Nodes[i]);
}
for (int i = 0; i < blocks.Length; i++) {
foreach (ControlFlowNode node in cfg.Nodes[i].Successors) {
blocks[i].Successors.Add(blocks[node.BlockIndex]);
blocks[node.BlockIndex].Predecessors.Add(blocks[i]);
}
}
}
void CreateInstructions(int blockIndex)
{
ControlFlowNode cfgNode = cfg.Nodes[blockIndex];
SsaBlock block = blocks[blockIndex];
int stackSize = stackSizeAtBlockStart[blockIndex];
Debug.Assert(stackSize >= 0);
List<Instruction> prefixes = new List<Instruction>();
foreach (Instruction inst in cfgNode.Instructions) {
if (inst.OpCode.OpCodeType == OpCodeType.Prefix) {
prefixes.Add(inst);
continue;
}
int popCount = inst.GetPopDelta(method) ?? stackSize;
stackSize -= popCount;
if (stackSize < 0)
throw new InvalidProgramException("IL stack underflow");
int pushCount = inst.GetPushDelta();
if (stackSize + pushCount > stackLocations.Length)
throw new InvalidProgramException("IL stack overflow");
SsaVariable target;
SsaVariable[] operands;
DetermineOperands(stackSize, inst, popCount, pushCount, out target, out operands);
Instruction[] prefixArray = prefixes.Count > 0 ? prefixes.ToArray() : null;
prefixes.Clear();
// ignore NOP instructions
if (!(inst.OpCode == OpCodes.Nop || inst.OpCode == OpCodes.Pop)) {
block.Instructions.Add(new SsaInstruction(block, inst, target, operands, prefixArray));
}
stackSize += pushCount;
}
foreach (ControlFlowEdge edge in cfgNode.Outgoing) {
int newStackSize;
switch (edge.Type) {
case JumpType.Normal:
newStackSize = stackSize;
break;
case JumpType.EndFinally:
if (stackSize != 0)
throw new NotSupportedException("stacksize must be 0 in endfinally edge");
newStackSize = 0;
break;
case JumpType.JumpToExceptionHandler:
switch (edge.Target.NodeType) {
case ControlFlowNodeType.FinallyOrFaultHandler:
newStackSize = 0;
break;
case ControlFlowNodeType.ExceptionalExit:
case ControlFlowNodeType.CatchHandler:
newStackSize = 1;
break;
default:
throw new NotSupportedException("unsupported target node type: " + edge.Target.NodeType);
}
break;
default:
throw new NotSupportedException("unsupported jump type: " + edge.Type);
}
int nextStackSize = stackSizeAtBlockStart[edge.Target.BlockIndex];
if (nextStackSize == -1) {
stackSizeAtBlockStart[edge.Target.BlockIndex] = newStackSize;
CreateInstructions(edge.Target.BlockIndex);
} else if (nextStackSize != newStackSize) {
throw new InvalidProgramException("Stack size doesn't match");
}
}
}
void DetermineOperands(int stackSize, Instruction inst, int popCount, int pushCount, out SsaVariable target, out SsaVariable[] operands)
{
switch (inst.OpCode.Code) {
case Code.Ldarg:
operands = new SsaVariable[] { ssaForm.GetOriginalVariable((ParameterReference)inst.Operand) };
target = stackLocations[stackSize];
break;
case Code.Starg:
operands = new SsaVariable[] { stackLocations[stackSize] };
target = ssaForm.GetOriginalVariable((ParameterReference)inst.Operand);
break;
case Code.Ldloc:
operands = new SsaVariable[] { ssaForm.GetOriginalVariable((VariableReference)inst.Operand) };
target = stackLocations[stackSize];
break;
case Code.Stloc:
operands = new SsaVariable[] { stackLocations[stackSize] };
target = ssaForm.GetOriginalVariable((VariableReference)inst.Operand);
break;
case Code.Dup:
operands = new SsaVariable[] { stackLocations[stackSize] };
target = stackLocations[stackSize + 1];
break;
default:
operands = new SsaVariable[popCount];
for (int i = 0; i < popCount; i++) {
operands[i] = stackLocations[stackSize + i];
}
switch (pushCount) {
case 0:
target = null;
break;
case 1:
target = stackLocations[stackSize];
break;
default:
throw new NotSupportedException("unsupported pushCount=" + pushCount);
}
break;
}
}
void CreateSpecialInstructions()
{
// Everything needs an initial write for the SSA transformation to work correctly.
foreach (SsaVariable v in parameters) {
ssaForm.EntryPoint.Instructions.Add(new SsaInstruction(ssaForm.EntryPoint, null, v, null, specialOpCode: SpecialOpCode.Parameter));
}
foreach (SsaVariable v in locals) {
ssaForm.EntryPoint.Instructions.Add(new SsaInstruction(ssaForm.EntryPoint, null, v, null, specialOpCode: SpecialOpCode.Uninitialized));
}
foreach (SsaVariable v in stackLocations) {
ssaForm.EntryPoint.Instructions.Add(new SsaInstruction(ssaForm.EntryPoint, null, v, null, specialOpCode: SpecialOpCode.Uninitialized));
}
foreach (SsaBlock b in blocks) {
if (b.NodeType == ControlFlowNodeType.CatchHandler) {
b.Instructions.Add(new SsaInstruction(b, null, stackLocations[0], null,
specialOpCode: SpecialOpCode.Exception,
typeOperand: cfg.Nodes[b.BlockIndex].ExceptionHandler.CatchType));
}
}
}
}
}

191
ICSharpCode.Decompiler/FlowAnalysis/SsaInstruction.cs

@ -1,191 +0,0 @@ @@ -1,191 +0,0 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// 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;
using System.IO;
using ICSharpCode.Decompiler.Disassembler;
using Mono.Cecil;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
public enum SpecialOpCode
{
/// <summary>
/// No special op code: SsaInstruction has a normal IL instruction
/// </summary>
None,
/// <summary>
/// Φ function: chooses the appropriate variable based on which CFG edge was used to enter this block
/// </summary>
Phi,
/// <summary>
/// Variable is read from before passing it by ref.
/// This instruction constructs a managed reference to the variable.
/// </summary>
PrepareByRefCall,
/// <summary>
/// This instruction constructs a managed reference to the variable.
/// The variable is not really read from.
/// </summary>
PrepareByOutCall,
/// <summary>
/// This instruction constructs a managed reference to the variable.
/// The reference is used for a field access on a value type.
/// </summary>
PrepareForFieldAccess,
/// <summary>
/// Variable is written to after passing it by ref or out.
/// </summary>
WriteAfterByRefOrOutCall,
/// <summary>
/// Variable is not initialized.
/// </summary>
Uninitialized,
/// <summary>
/// Value is passed in as parameter
/// </summary>
Parameter,
/// <summary>
/// Value is a caught exception.
/// TypeOperand is set to the exception type.
/// </summary>
Exception,
/// <summary>
/// Initialize a value type. Unlike the real initobj instruction, this one does not take an address
/// but assigns to the target variable.
/// TypeOperand is set to the type being created.
/// </summary>
InitObj
}
public sealed class SsaInstruction
{
public readonly SsaBlock ParentBlock;
public readonly SpecialOpCode SpecialOpCode;
/// <summary>
/// The original IL instruction.
/// May be null for "invented" instructions (SpecialOpCode != None).
/// </summary>
public readonly Instruction Instruction;
/// <summary>
/// Prefixes in front of the IL instruction.
/// </summary>
public readonly Instruction[] Prefixes;
/// <summary>
/// Gets the type operand. This is used only in combination with some special opcodes.
/// </summary>
public readonly TypeReference TypeOperand;
public SsaVariable Target;
public SsaVariable[] Operands;
static readonly SsaVariable[] emptyVariableArray = {};
static readonly Instruction[] emptyInstructionArray = {};
public SsaInstruction(SsaBlock parentBlock, Instruction instruction, SsaVariable target, SsaVariable[] operands,
Instruction[] prefixes = null, SpecialOpCode specialOpCode = SpecialOpCode.None,
TypeReference typeOperand = null)
{
this.ParentBlock = parentBlock;
this.Instruction = instruction;
this.Prefixes = prefixes ?? emptyInstructionArray;
this.Target = target;
this.Operands = operands ?? emptyVariableArray;
this.SpecialOpCode = specialOpCode;
this.TypeOperand = typeOperand;
Debug.Assert((typeOperand != null) == (specialOpCode == SpecialOpCode.Exception || specialOpCode == SpecialOpCode.InitObj));
}
/// <summary>
/// Gets whether this instruction is a simple assignment from one variable to another.
/// </summary>
public bool IsMoveInstruction {
get {
return Target != null && Operands.Length == 1 && Instruction != null && OpCodeInfo.Get(Instruction.OpCode).IsMoveInstruction;
}
}
public void ReplaceVariableInOperands(SsaVariable oldVar, SsaVariable newVar)
{
for (int i = 0; i < this.Operands.Length; i++) {
if (this.Operands[i] == oldVar)
this.Operands[i] = newVar;
}
}
public override string ToString()
{
StringWriter w = new StringWriter();
WriteTo(w);
return w.ToString();
}
public void WriteTo(TextWriter writer)
{
foreach (Instruction prefix in this.Prefixes) {
Disassembler.DisassemblerHelpers.WriteTo(prefix, new PlainTextOutput(writer));
writer.WriteLine();
}
if (Instruction != null && Instruction.Offset >= 0) {
writer.Write(CecilExtensions.OffsetToString(Instruction.Offset));
writer.Write(": ");
}
if (Target != null) {
writer.Write(Target.ToString());
writer.Write(" = ");
}
if (IsMoveInstruction) {
writer.Write(Operands[0].ToString());
if (Instruction != null) {
writer.Write(" (" + Instruction.OpCode.Name + ")");
}
} else {
if (Instruction == null) {
writer.Write(SpecialOpCode.ToString());
} else {
writer.Write(Instruction.OpCode.Name);
if(null != Instruction.Operand) {
writer.Write(' ');
Disassembler.DisassemblerHelpers.WriteOperand(new PlainTextOutput(writer), Instruction.Operand);
writer.Write(' ');
}
}
if (TypeOperand != null) {
writer.Write(' ');
writer.Write(TypeOperand.ToString());
writer.Write(' ');
}
if (Operands.Length > 0) {
writer.Write('(');
for (int i = 0; i < Operands.Length; i++) {
if (i > 0)
writer.Write(", ");
writer.Write(Operands[i].ToString());
}
writer.Write(')');
}
}
}
}
}

138
ICSharpCode.Decompiler/FlowAnalysis/SsaOptimization.cs

@ -1,138 +0,0 @@ @@ -1,138 +0,0 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// 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.Diagnostics;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
/// <summary>
/// Contains some very simple optimizations that work on the SSA form.
/// </summary>
static class SsaOptimization
{
public static void Optimize(SsaForm ssaForm)
{
DirectlyStoreToVariables(ssaForm);
SimpleCopyPropagation(ssaForm);
RemoveDeadAssignments(ssaForm);
}
/// <summary>
/// When any instructions stores its result in a stack location that's used only once in a 'stloc' or 'starg' instruction,
/// we optimize this to directly store in the target location.
/// As optimization this is redundant (does the same as copy propagation), but it'll make us keep the variables named
/// after locals instead of keeping the temps as using only the simple copy propagation would do.
/// </summary>
public static void DirectlyStoreToVariables(SsaForm ssaForm)
{
foreach (SsaBlock block in ssaForm.Blocks) {
block.Instructions.RemoveAll(
inst => {
if (inst.Instruction != null && (inst.Instruction.OpCode == OpCodes.Stloc || inst.Instruction.OpCode == OpCodes.Starg)) {
SsaVariable target = inst.Target;
SsaVariable temp = inst.Operands[0];
if (target.IsSingleAssignment && temp.IsSingleAssignment && temp.Usage.Count == 1 && temp.IsStackLocation) {
temp.Definition.Target = target;
return true;
}
}
return false;
});
}
ssaForm.ComputeVariableUsage(); // update usage after we modified stuff
}
public static void SimpleCopyPropagation(SsaForm ssaForm, bool onlyForStackLocations = true)
{
foreach (SsaBlock block in ssaForm.Blocks) {
foreach (SsaInstruction inst in block.Instructions) {
if (inst.IsMoveInstruction && inst.Target.IsSingleAssignment && inst.Operands[0].IsSingleAssignment) {
if (inst.Target.IsStackLocation || !onlyForStackLocations) {
// replace all uses of 'target' with 'operands[0]'.
foreach (SsaInstruction useInstruction in inst.Target.Usage) {
useInstruction.ReplaceVariableInOperands(inst.Target, inst.Operands[0]);
}
}
}
}
}
ssaForm.ComputeVariableUsage(); // update usage after we modified stuff
}
public static void RemoveDeadAssignments(SsaForm ssaForm)
{
HashSet<SsaVariable> liveVariables = new HashSet<SsaVariable>();
// find variables that are used directly
foreach (SsaBlock block in ssaForm.Blocks) {
foreach (SsaInstruction inst in block.Instructions) {
if (!CanRemoveAsDeadCode(inst)) {
if (inst.Target != null)
liveVariables.Add(inst.Target);
foreach (SsaVariable op in inst.Operands) {
liveVariables.Add(op);
}
}
}
}
Queue<SsaVariable> queue = new Queue<SsaVariable>(liveVariables);
// find variables that are used indirectly
while (queue.Count > 0) {
SsaVariable v = queue.Dequeue();
if (v.IsSingleAssignment) {
foreach (SsaVariable op in v.Definition.Operands) {
if (liveVariables.Add(op))
queue.Enqueue(op);
}
}
}
// remove assignments to all unused variables
foreach (SsaBlock block in ssaForm.Blocks) {
block.Instructions.RemoveAll(
inst => {
if (inst.Target != null && !liveVariables.Contains(inst.Target)) {
Debug.Assert(inst.Target.IsSingleAssignment);
return true;
}
return false;
});
}
ssaForm.ComputeVariableUsage(); // update usage after we modified stuff
}
static bool CanRemoveAsDeadCode(SsaInstruction inst)
{
if (inst.Target != null && !inst.Target.IsSingleAssignment)
return false;
switch (inst.SpecialOpCode) {
case SpecialOpCode.Phi:
case SpecialOpCode.Exception:
case SpecialOpCode.Parameter:
case SpecialOpCode.Uninitialized:
return true;
case SpecialOpCode.None:
return inst.IsMoveInstruction;
default:
return false;
}
}
}
}

91
ICSharpCode.Decompiler/FlowAnalysis/SsaVariable.cs

@ -1,91 +0,0 @@ @@ -1,91 +0,0 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// 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 Mono.Cecil;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
/// <summary>
/// Represents a variable used with the SsaInstruction register-based instructions.
/// Despite what the name suggests, the variable is not necessarily in single-assignment form - take a look at "bool IsSingleAssignment".
/// </summary>
public sealed class SsaVariable
{
public int OriginalVariableIndex;
public readonly string Name;
public readonly bool IsStackLocation;
public readonly ParameterDefinition Parameter;
public readonly VariableDefinition Variable;
public SsaVariable(ParameterDefinition p)
{
this.Name = string.IsNullOrEmpty(p.Name) ? "param" + p.Index : p.Name;
this.Parameter = p;
}
public SsaVariable(VariableDefinition v)
{
this.Name = string.IsNullOrEmpty(v.Name) ? "V_" + v.Index : v.Name;
this.Variable = v;
}
public SsaVariable(int stackLocation)
{
this.Name = "stack" + stackLocation;
this.IsStackLocation = true;
}
public SsaVariable(SsaVariable original, string newName)
{
this.Name = newName;
this.IsStackLocation = original.IsStackLocation;
this.OriginalVariableIndex = original.OriginalVariableIndex;
this.Parameter = original.Parameter;
this.Variable = original.Variable;
}
public override string ToString()
{
return Name;
}
/// <summary>
/// Gets whether this variable has only a single assignment.
/// This field is initialized in TransformToSsa step.
/// </summary>
/// <remarks>Not all variables can be transformed to single assignment form: variables that have their address taken
/// cannot be represented in SSA (although SimplifyByRefCalls will get rid of the address-taking instruction in almost all cases)</remarks>
public bool IsSingleAssignment;
/// <summary>
/// Gets the instruction defining the variable.
/// This field is initialized in TransformToSsa step. It is only set for variables with a single assignment.
/// </summary>
public SsaInstruction Definition;
/// <summary>
/// Gets the places where a variable is used.
/// If a single instruction reads a variable 2 times (e.g. adding to itself), then it must be included 2 times in this list!
/// </summary>
public List<SsaInstruction> Usage;
}
}

254
ICSharpCode.Decompiler/FlowAnalysis/TransformToSsa.cs

@ -1,254 +0,0 @@ @@ -1,254 +0,0 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// 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.ObjectModel;
using System.Diagnostics;
using System.Linq;
using Mono.Cecil;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
/// <summary>
/// Convers a method to static single assignment form.
/// </summary>
sealed class TransformToSsa
{
public static void Transform(ControlFlowGraph cfg, SsaForm ssa, bool optimize = true)
{
TransformToSsa transform = new TransformToSsa(cfg, ssa);
transform.ConvertVariablesToSsa();
SsaOptimization.RemoveDeadAssignments(ssa); // required so that 'MakeByRefCallsSimple' can detect more cases
if (SimplifyByRefCalls.MakeByRefCallsSimple(ssa)) {
transform.ConvertVariablesToSsa();
}
if (optimize)
SsaOptimization.Optimize(ssa);
}
readonly ControlFlowGraph cfg;
readonly SsaForm ssaForm;
readonly List<SsaInstruction>[] writeToOriginalVariables; // array index -> SsaVariable OriginalVariableIndex
readonly bool[] addressTaken; // array index -> SsaVariable OriginalVariableIndex; value = whether ldloca instruction was used with variable
private TransformToSsa(ControlFlowGraph cfg, SsaForm ssaForm)
{
this.cfg = cfg;
this.ssaForm = ssaForm;
this.writeToOriginalVariables = new List<SsaInstruction>[ssaForm.OriginalVariables.Count];
this.addressTaken = new bool[ssaForm.OriginalVariables.Count];
}
#region CollectInformationAboutOriginalVariableUse
void CollectInformationAboutOriginalVariableUse()
{
Debug.Assert(addressTaken.Length == writeToOriginalVariables.Length);
for (int i = 0; i < writeToOriginalVariables.Length; i++) {
Debug.Assert(ssaForm.OriginalVariables[i].OriginalVariableIndex == i);
addressTaken[i] = false;
// writeToOriginalVariables is only used when placing phi functions
// we don't need to do that anymore for variables that are already in SSA form
if (ssaForm.OriginalVariables[i].IsSingleAssignment)
writeToOriginalVariables[i] = null;
else
writeToOriginalVariables[i] = new List<SsaInstruction>();
}
foreach (SsaBlock block in ssaForm.Blocks) {
foreach (SsaInstruction inst in block.Instructions) {
if (inst.Target != null ) {
var list = writeToOriginalVariables[inst.Target.OriginalVariableIndex];
if (list != null)
list.Add(inst);
}
if (inst.Instruction != null) {
if (inst.Instruction.OpCode == OpCodes.Ldloca) {
addressTaken[ssaForm.GetOriginalVariable((VariableDefinition)inst.Instruction.Operand).OriginalVariableIndex] = true;
} else if (inst.Instruction.OpCode == OpCodes.Ldarga) {
addressTaken[ssaForm.GetOriginalVariable((ParameterDefinition)inst.Instruction.Operand).OriginalVariableIndex] = true;
}
}
}
}
}
#endregion
#region ConvertToSsa
void ConvertVariablesToSsa()
{
CollectInformationAboutOriginalVariableUse();
bool[] processVariable = new bool[ssaForm.OriginalVariables.Count];
foreach (SsaVariable variable in ssaForm.OriginalVariables) {
if (!variable.IsSingleAssignment && !addressTaken[variable.OriginalVariableIndex]) {
PlacePhiFunctions(variable);
processVariable[variable.OriginalVariableIndex] = true;
}
}
RenameVariables(processVariable);
foreach (SsaVariable variable in ssaForm.OriginalVariables) {
if (!addressTaken[variable.OriginalVariableIndex]) {
Debug.Assert(variable.IsSingleAssignment && variable.Definition != null);
}
}
ssaForm.ComputeVariableUsage();
}
#endregion
#region PlacePhiFunctions
void PlacePhiFunctions(SsaVariable variable)
{
cfg.ResetVisited();
HashSet<SsaBlock> blocksWithPhi = new HashSet<SsaBlock>();
Queue<ControlFlowNode> worklist = new Queue<ControlFlowNode>();
foreach (SsaInstruction writeInstruction in writeToOriginalVariables[variable.OriginalVariableIndex]) {
ControlFlowNode cfgNode = cfg.Nodes[writeInstruction.ParentBlock.BlockIndex];
if (!cfgNode.Visited) {
cfgNode.Visited = true;
worklist.Enqueue(cfgNode);
}
}
while (worklist.Count > 0) {
ControlFlowNode cfgNode = worklist.Dequeue();
foreach (ControlFlowNode dfNode in cfgNode.DominanceFrontier) {
// we don't need phi functions in the exit node
if (dfNode.NodeType == ControlFlowNodeType.RegularExit || dfNode.NodeType == ControlFlowNodeType.ExceptionalExit)
continue;
SsaBlock y = ssaForm.Blocks[dfNode.BlockIndex];
if (blocksWithPhi.Add(y)) {
// add a phi instruction in y
SsaVariable[] operands = Enumerable.Repeat(variable, dfNode.Incoming.Count).ToArray();
y.Instructions.Insert(0, new SsaInstruction(y, null, variable, operands, specialOpCode: SpecialOpCode.Phi));
if (!dfNode.Visited) {
dfNode.Visited = true;
worklist.Enqueue(dfNode);
}
}
}
}
}
#endregion
#region RenameVariable
int tempVariableCounter = 1;
void RenameVariables(bool[] processVariable)
{
VariableRenamer r = new VariableRenamer(this, processVariable);
r.Visit(ssaForm.EntryPoint);
}
sealed class VariableRenamer
{
readonly TransformToSsa transform;
readonly ReadOnlyCollection<SsaVariable> inputVariables;
internal readonly Stack<SsaVariable>[] versionStacks;
int[] versionCounters; // specifies for each input variable the next version number
// processVariable = specifies for each input variable whether we should rename it
public VariableRenamer(TransformToSsa transform, bool[] processVariable)
{
this.transform = transform;
this.inputVariables = transform.ssaForm.OriginalVariables;
Debug.Assert(inputVariables.Count == processVariable.Length);
this.versionCounters = new int[inputVariables.Count];
this.versionStacks = new Stack<SsaVariable>[inputVariables.Count];
for (int i = 0; i < versionStacks.Length; i++) {
if (processVariable[i]) {
Debug.Assert(inputVariables[i].IsSingleAssignment == false);
// only create version stacks for the variables that we need to process and that weren't already processed earlier
versionStacks[i] = new Stack<SsaVariable>();
versionStacks[i].Push(inputVariables[i]);
}
}
}
SsaVariable MakeNewVersion(int variableIndex)
{
int versionCounter = ++versionCounters[variableIndex];
SsaVariable x = inputVariables[variableIndex];
if (versionCounter == 1) {
return x;
} else {
if (x.IsStackLocation) {
return new SsaVariable(x, "temp" + (transform.tempVariableCounter++));
} else {
return new SsaVariable(x, x.Name + "_" + versionCounter);
}
}
}
internal void Visit(SsaBlock block)
{
// duplicate top of all stacks
foreach (var stack in versionStacks) {
if (stack != null)
stack.Push(stack.Peek());
}
foreach (SsaInstruction s in block.Instructions) {
// replace all uses of variables being processed with their current version.
if (s.SpecialOpCode != SpecialOpCode.Phi) {
for (int i = 0; i < s.Operands.Length; i++) {
var stack = versionStacks[s.Operands[i].OriginalVariableIndex];
if (stack != null)
s.Operands[i] = stack.Peek();
}
}
// if we're writing to a variable we should process:
if (s.Target != null) {
int targetIndex = s.Target.OriginalVariableIndex;
if (versionStacks[targetIndex] != null) {
s.Target = MakeNewVersion(targetIndex);
s.Target.IsSingleAssignment = true;
s.Target.Definition = s;
// we already pushed our entry for this SsaBlock at the beginning (where we duplicated all stacks),
// so now replace the top element
versionStacks[targetIndex].Pop();
versionStacks[targetIndex].Push(s.Target);
}
}
}
foreach (SsaBlock succ in block.Successors) {
int j = succ.Predecessors.IndexOf(block);
Debug.Assert(j >= 0);
foreach (SsaInstruction f in succ.Instructions) {
if (f.SpecialOpCode == SpecialOpCode.Phi) {
var stack = versionStacks[f.Target.OriginalVariableIndex];
if (stack != null) {
f.Operands[j] = stack.Peek();
}
}
}
}
foreach (ControlFlowNode child in transform.cfg.Nodes[block.BlockIndex].DominatorTreeChildren)
Visit(transform.ssaForm.Blocks[child.BlockIndex]);
// restore stacks:
foreach (var stack in versionStacks) {
if (stack != null)
stack.Pop();
}
}
}
#endregion
}
}

13
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -76,6 +76,8 @@ @@ -76,6 +76,8 @@
<Compile Include="CSharp\Transforms\IAstTransform.cs" />
<Compile Include="CSharp\Transforms\IntroduceUnsafeModifier.cs" />
<Compile Include="CSharp\Transforms\ReplaceMethodCallsWithOperators.cs" />
<Compile Include="FlowAnalysis\ControlFlowNode.cs" />
<Compile Include="FlowAnalysis\Dominance.cs" />
<Compile Include="IL\Instructions.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
@ -93,17 +95,19 @@ @@ -93,17 +95,19 @@
<Compile Include="IL\Instructions\ILInstruction.cs" />
<Compile Include="IL\Instructions\InstructionCollection.cs" />
<Compile Include="IL\Instructions\LocalVarInstructions.cs" />
<Compile Include="IL\Instructions\Loop.cs" />
<Compile Include="IL\Instructions\MemoryInstructions.cs" />
<Compile Include="IL\Instructions\PatternMatching.cs" />
<Compile Include="IL\Instructions\Return.cs" />
<Compile Include="IL\Instructions\SimpleInstruction.cs" />
<Compile Include="IL\Instructions\TransformStackIntoVariablesState.cs" />
<Compile Include="IL\Instructions\TryInstruction.cs" />
<Compile Include="IL\Instructions\UnaryInstruction.cs" />
<Compile Include="IL\IInlineContext.cs" />
<Compile Include="IL\NRTypeExtensions.cs" />
<Compile Include="IL\TransformingVisitor.cs" />
<Compile Include="IL\TransformStackIntoVariables.cs" />
<Compile Include="IL\Transforms\LoopDetection.cs" />
<Compile Include="IL\Transforms\TransformingVisitor.cs" />
<Compile Include="IL\Transforms\TransformStackIntoVariables.cs" />
<Compile Include="IL\Transforms\TransformStackIntoVariablesState.cs" />
<Compile Include="IL\UnionFind.cs" />
<Compile Include="CecilExtensions.cs" />
<Compile Include="Disassembler\DisassemblerHelpers.cs" />
@ -169,6 +173,9 @@ @@ -169,6 +173,9 @@
<Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
</ItemGroup>
<ItemGroup>
<Folder Include="FlowAnalysis" />
<Folder Include="ILAst" />
<Folder Include="IL\Transforms" />
<Folder Include="TypeSystem" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.Targets" />

1
ICSharpCode.Decompiler/IL/BlockBuilder.cs

@ -172,7 +172,6 @@ namespace ICSharpCode.Decompiler.IL @@ -172,7 +172,6 @@ namespace ICSharpCode.Decompiler.IL
case OpCode.BlockContainer:
var container = (BlockContainer)inst;
containerStack.Push(container);
container.EntryPoint.IncomingEdgeCount++; // count the entry edge
foreach (var block in container.Blocks)
ConnectBranches(block);
containerStack.Pop();

38
ICSharpCode.Decompiler/IL/Instructions.cs

@ -70,6 +70,8 @@ namespace ICSharpCode.Decompiler.IL @@ -70,6 +70,8 @@ namespace ICSharpCode.Decompiler.IL
EndFinally,
/// <summary>If statement / conditional expression. <c>if (condition) trueExpr else falseExpr</c></summary>
IfInstruction,
/// <summary>Infinite loop</summary>
Loop,
/// <summary>Try-catch statement.</summary>
TryCatch,
/// <summary>Catch handler within a try-catch statement.</summary>
@ -606,6 +608,37 @@ namespace ICSharpCode.Decompiler.IL @@ -606,6 +608,37 @@ namespace ICSharpCode.Decompiler.IL
}
}
/// <summary>Infinite loop</summary>
public sealed partial class Loop : ILInstruction
{
public Loop(ILInstruction body) : base(OpCode.Loop)
{
this.Body = body;
}
ILInstruction body;
public ILInstruction Body {
get { return this.body; }
set {
ValidateChild(value);
SetChildInstruction(ref this.body, value);
}
}
public override IEnumerable<ILInstruction> Children {
get {
yield return this.body;
}
}
public override void TransformChildren(ILVisitor<ILInstruction> visitor)
{
this.Body = this.body.AcceptVisitor(visitor);
}
public override StackType ResultType { get { return StackType.Void; } }
public override T AcceptVisitor<T>(ILVisitor<T> visitor)
{
return visitor.VisitLoop(this);
}
}
/// <summary>Try-catch statement.</summary>
public sealed partial class TryCatch : TryInstruction
{
@ -2104,6 +2137,10 @@ namespace ICSharpCode.Decompiler.IL @@ -2104,6 +2137,10 @@ namespace ICSharpCode.Decompiler.IL
{
return Default(inst);
}
protected internal virtual T VisitLoop(Loop inst)
{
return Default(inst);
}
protected internal virtual T VisitTryCatch(TryCatch inst)
{
return Default(inst);
@ -2382,6 +2419,7 @@ namespace ICSharpCode.Decompiler.IL @@ -2382,6 +2419,7 @@ namespace ICSharpCode.Decompiler.IL
"br",
"endfinally",
"if",
"loop",
"try.catch",
"try.catch.handler",
"try.finally",

5
ICSharpCode.Decompiler/IL/Instructions.tt

@ -71,6 +71,11 @@ @@ -71,6 +71,11 @@
new ChildInfo("trueInst"),
new ChildInfo("falseInst"),
}), CustomConstructor, CustomComputeFlags, CustomWriteTo),
new OpCode("loop", "Infinite loop",
CustomChildren(new []{
new ChildInfo("body")
}),
CustomComputeFlags, CustomWriteTo, ResultType("Void")),
new OpCode("try.catch", "Try-catch statement.",
BaseClass("TryInstruction"), CustomConstructor, CustomComputeFlags, CustomWriteTo),
new OpCode("try.catch.handler", "Catch handler within a try-catch statement.",

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

@ -18,6 +18,7 @@ @@ -18,6 +18,7 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Text;
@ -59,7 +60,37 @@ namespace ICSharpCode.Decompiler.IL @@ -59,7 +60,37 @@ namespace ICSharpCode.Decompiler.IL
{
public readonly InstructionCollection<ILInstruction> Instructions;
ILInstruction finalInstruction;
/// <summary>
/// For blocks in a block container, this field holds
/// the number of incoming control flow edges to this block.
/// </summary>
/// <remarks>
/// This variable is automatically updated when adding/removing branch instructions from the ILAst,
/// or when adding the block as an entry point to a BlockContainer.
/// </remarks>
public int IncomingEdgeCount;
/// <summary>
/// Returns the index of the block in the parent BlockContainer's block list.
/// Returns 0 if the block is not in a BlockContainer.
/// </summary>
public int Index {
get {
// TODO: we can offer this in O(1) by making the
// parent BlockContainer store this in the blocks,
// but I'm not sure if it's worth the complexity.
// We'll have to see if the Index is useful in more than a few places.
// (otherwise those few places could use a Dictionary<Block, int>)
var bc = Parent as BlockContainer;
if (bc != null) {
return bc.Blocks.IndexOf(this);
} else {
return 0;
}
}
}
public ILInstruction FinalInstruction {
get {
return finalInstruction;
@ -70,7 +101,6 @@ namespace ICSharpCode.Decompiler.IL @@ -70,7 +101,6 @@ namespace ICSharpCode.Decompiler.IL
}
}
public int IncomingEdgeCount;
public Block() : base(OpCode.Block)
{
@ -178,9 +208,24 @@ namespace ICSharpCode.Decompiler.IL @@ -178,9 +208,24 @@ namespace ICSharpCode.Decompiler.IL
inst = new Void(new StLoc(inst, variable));
}
Instructions[i] = inst;
if (inst.HasFlag(InstructionFlags.EndPointUnreachable))
return;
}
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());
}
}
}
}
}

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

@ -40,10 +40,16 @@ namespace ICSharpCode.Decompiler.IL @@ -40,10 +40,16 @@ namespace ICSharpCode.Decompiler.IL
partial class BlockContainer : ILInstruction
{
public readonly InstructionCollection<Block> Blocks;
Block entryPoint;
public Block EntryPoint {
get {
return Blocks[0];
get { return entryPoint; }
private set {
if (entryPoint != null && IsConnected)
entryPoint.IncomingEdgeCount--;
entryPoint = value;
if (entryPoint != null && IsConnected)
entryPoint.IncomingEdgeCount++;
}
}
@ -52,6 +58,26 @@ namespace ICSharpCode.Decompiler.IL @@ -52,6 +58,26 @@ namespace ICSharpCode.Decompiler.IL
this.Blocks = new InstructionCollection<Block>(this);
}
protected internal override void InstructionCollectionUpdateComplete()
{
base.InstructionCollectionUpdateComplete();
this.EntryPoint = this.Blocks.FirstOrDefault();
}
protected override void Connected()
{
base.Connected();
if (entryPoint != null)
entryPoint.IncomingEdgeCount++;
}
protected override void Disconnected()
{
base.Disconnected();
if (entryPoint != null)
entryPoint.IncomingEdgeCount--;
}
public override void WriteTo(ITextOutput output)
{
output.WriteLine("BlockContainer {");
@ -81,7 +107,8 @@ namespace ICSharpCode.Decompiler.IL @@ -81,7 +107,8 @@ namespace ICSharpCode.Decompiler.IL
internal override void CheckInvariant()
{
base.CheckInvariant();
Debug.Assert(Blocks.Count >= 1);
Debug.Assert(EntryPoint == Blocks[0]);
Debug.Assert(!IsConnected || EntryPoint.IncomingEdgeCount >= 1);
}
protected override InstructionFlags ComputeFlags()
@ -107,6 +134,11 @@ namespace ICSharpCode.Decompiler.IL @@ -107,6 +134,11 @@ namespace ICSharpCode.Decompiler.IL
internal override void TransformStackIntoVariables(TransformStackIntoVariablesState state)
{
EntryPoint.TransformStackIntoVariables(state);
ImmutableArray<ILVariable> variables;
if (state.FinalVariables.TryGetValue(this, out variables))
state.Variables = variables.ToStack();
else
state.Variables.Clear();
}
}
}

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

@ -49,6 +49,14 @@ namespace ICSharpCode.Decompiler.IL @@ -49,6 +49,14 @@ namespace ICSharpCode.Decompiler.IL
this.targetILOffset = targetILOffset;
}
public Branch(Block targetBlock) : base(OpCode.Branch)
{
if (targetBlock == null)
throw new ArgumentNullException("targetBlock");
this.targetBlock = targetBlock;
this.targetILOffset = targetBlock.ILRange.Start;
}
protected override InstructionFlags ComputeFlags()
{
var flags = InstructionFlags.MayBranch | InstructionFlags.EndPointUnreachable;

28
ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs

@ -22,6 +22,7 @@ using System.Diagnostics; @@ -22,6 +22,7 @@ using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ICSharpCode.NRefactory.Utils;
using ICSharpCode.Decompiler.CSharp;
namespace ICSharpCode.Decompiler.IL
@ -152,6 +153,15 @@ namespace ICSharpCode.Decompiler.IL @@ -152,6 +153,15 @@ namespace ICSharpCode.Decompiler.IL
/// </summary>
public abstract void TransformChildren(ILVisitor<ILInstruction> visitor);
/// <summary>
/// Returns all descendants of the ILInstruction.
/// </summary>
public IEnumerable<ILInstruction> Descendants {
get {
return TreeTraversal.PreOrder(Children, inst => inst.Children);
}
}
/// <summary>
/// Attempts inlining from the inline context into this instruction.
/// </summary>
@ -237,18 +247,30 @@ namespace ICSharpCode.Decompiler.IL @@ -237,18 +247,30 @@ namespace ICSharpCode.Decompiler.IL
InvalidateFlags();
}
protected internal void AddChildInstruction(ILInstruction newChild)
/// <summary>
/// Called when a new child is added to a InstructionCollection.
/// </summary>
protected internal void InstructionCollectionAdded(ILInstruction newChild)
{
if (refCount > 0)
newChild.AddRef();
newChild.parent = this;
InvalidateFlags();
}
protected internal void RemoveChildInstruction(ILInstruction newChild)
/// <summary>
/// Called when a child is removed from a InstructionCollection.
/// </summary>
protected internal void InstructionCollectionRemoved(ILInstruction newChild)
{
if (refCount > 0)
newChild.ReleaseRef();
}
/// <summary>
/// Called when a series of add/remove operations on the InstructionCollection is complete.
/// </summary>
protected internal virtual void InstructionCollectionUpdateComplete()
{
InvalidateFlags();
}
}

15
ICSharpCode.Decompiler/IL/Instructions/InstructionCollection.cs

@ -35,22 +35,25 @@ namespace ICSharpCode.Decompiler.IL @@ -35,22 +35,25 @@ namespace ICSharpCode.Decompiler.IL
protected override void ClearItems()
{
foreach (var child in this)
parentInstruction.RemoveChildInstruction(child);
parentInstruction.InstructionCollectionRemoved(child);
base.ClearItems();
parentInstruction.InstructionCollectionUpdateComplete();
}
protected override void InsertItem(int index, T item)
{
if (item == null)
throw new ArgumentNullException("item");
parentInstruction.AddChildInstruction(item);
parentInstruction.InstructionCollectionAdded(item);
base.InsertItem(index, item);
parentInstruction.InstructionCollectionUpdateComplete();
}
protected override void RemoveItem(int index)
{
parentInstruction.RemoveChildInstruction(this[index]);
parentInstruction.InstructionCollectionRemoved(this[index]);
base.RemoveItem(index);
parentInstruction.InstructionCollectionUpdateComplete();
}
protected override void SetItem(int index, T item)
@ -59,13 +62,15 @@ namespace ICSharpCode.Decompiler.IL @@ -59,13 +62,15 @@ namespace ICSharpCode.Decompiler.IL
throw new ArgumentNullException("item");
if (this[index] == item)
return;
parentInstruction.RemoveChildInstruction(this[index]);
parentInstruction.AddChildInstruction(item);
parentInstruction.InstructionCollectionRemoved(this[index]);
parentInstruction.InstructionCollectionAdded(item);
base.SetItem(index, item);
parentInstruction.InstructionCollectionUpdateComplete();
}
public int RemoveAll(Predicate<T> predicate)
{
// TODO: optimize
int removed = 0;
for (int i = 0; i < this.Count;) {
if (predicate(this[i])) {

50
ICSharpCode.Decompiler/IL/Instructions/Loop.cs

@ -0,0 +1,50 @@ @@ -0,0 +1,50 @@
// 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;
namespace ICSharpCode.Decompiler.IL
{
partial class Loop
{
public override void WriteTo(ITextOutput output)
{
output.Write("loop ");
body.WriteTo(output);
}
protected override InstructionFlags ComputeFlags()
{
return Block.Phase1Boundary(body.Flags)
| InstructionFlags.EndPointUnreachable
| InstructionFlags.SideEffect; // an infinite loop is a side effect
}
internal override ILInstruction Inline(InstructionFlags flagsBefore, IInlineContext context)
{
return this;
}
internal override void TransformStackIntoVariables(TransformStackIntoVariablesState state)
{
var initialVariables = state.Variables.Clone();
Body = Body.Inline(InstructionFlags.None, state);
Body.TransformStackIntoVariables(state);
state.MergeVariables(initialVariables, state.Variables);
}
}
}

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

@ -0,0 +1,206 @@ @@ -0,0 +1,206 @@
// 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.Diagnostics;
using System.Linq;
using ICSharpCode.Decompiler.FlowAnalysis;
namespace ICSharpCode.Decompiler.IL.Transforms
{
/// <summary>
/// Detect loops in IL AST.
/// </summary>
public class LoopDetection : IILTransform
{
#region Construct Control Flow Graph
/// <summary>
/// Constructs a control flow graph for the blocks in the given block container.
/// The graph nodes will have the same indices as the blocks in the block container.
/// An additional exit node is used to signal when a block potentially falls through
/// to the endpoint of the BlockContainer.
/// Return statements, exceptions, or branches leaving the block container are not
/// modeled by the control flow graph.
/// </summary>
static ControlFlowNode BuildCFG(BlockContainer bc)
{
ControlFlowNode[] nodes = new ControlFlowNode[bc.Blocks.Count];
for (int i = 0; i < nodes.Length; i++) {
nodes[i] = new ControlFlowNode { UserData = bc.Blocks[i] };
}
ControlFlowNode exit = new ControlFlowNode();
// Create edges:
for (int i = 0; i < bc.Blocks.Count; i++) {
var block = bc.Blocks[i];
var sourceNode = nodes[i];
foreach (var branch in block.Descendants.OfType<Branch>()) {
if (branch.TargetBlock.Parent == bc) {
sourceNode.AddEdgeTo(nodes[branch.TargetBlock.Index]);
} else {
// Note: edges into different block containers are ignored:
// Either they point to a nested block container in the source block,
// in which case we can ignore them for control flow purposes;
// or they jump to a parent block container, in which case they act
// like a return statement or exceptional exit.
}
}
if (!block.HasFlag(InstructionFlags.EndPointUnreachable))
sourceNode.AddEdgeTo(exit);
}
if (nodes[0].Predecessors.Count != 0) {
// Create artificial entry point without predecessors:
ControlFlowNode entry = new ControlFlowNode();
entry.AddEdgeTo(nodes[0]);
return entry;
} else {
return nodes[0];
}
}
#endregion
/// <summary>
/// Run loop detection for all block containers in the function (including nested lambda functions).
/// </summary>
public void Run(ILFunction function, ILTransformContext context)
{
foreach (var blockContainer in function.Descendants.OfType<BlockContainer>().ToArray()) {
Run(blockContainer, context);
}
}
/// <summary>
/// Run loop detection for blocks in the block container.
/// </summary>
public void Run(BlockContainer blockContainer, ILTransformContext context)
{
var entryPoint = BuildCFG(blockContainer);
Dominance.ComputeDominance(entryPoint, context.CancellationToken);
FindLoops(entryPoint);
}
/// <summary>
/// Recurse into the dominator tree and find back edges/natural loops.
/// </summary>
/// <remarks>
/// A back edge is an edge t->h so that h dominates t.
/// The natural loop of the back edge is the smallest set of nodes that includes the back edge
/// and has no predecessors outside the set except for the predecessor of the header.
///
/// Preconditions:
/// * dominance was computed for h
/// * all blocks in the dominator subtree starting at h are in the same BlockContainer
/// * the visited flag is set to false
/// </remarks>
void FindLoops(ControlFlowNode h)
{
List<ControlFlowNode> loop = null;
foreach (var t in h.Predecessors) {
if (h.Dominates(t)) {
// h->t is a back edge, and h is a loop header
// Add the natural loop of t->h to the loop.
if (loop == null) {
loop = new List<ControlFlowNode>();
loop.Add(h);
// Mark loop header as visited so that the pre-order traversal
// stops at the loop header.
h.Visited = true;
}
t.TraversePreOrder(n => n.Predecessors, loop.Add);
}
}
if (loop != null) {
// loop now is the union of all natural loops with loop head h
Debug.Assert(loop[0] == h);
foreach (var node in loop) {
node.Visited = false; // reset visited flag so that we can find nested loops
Debug.Assert(h.Dominates(node), "The loop body must be dominated by the loop head");
}
ConstructLoop(loop);
}
// Recurse into the dominator tree to find other possible loop heads
foreach (var child in h.DominatorTreeChildren) {
FindLoops(child);
}
}
/// <summary>
/// Move the blocks associated with the loop into a new block container.
/// </summary>
void ConstructLoop(List<ControlFlowNode> loop)
{
Block oldEntryPoint = (Block)loop[0].UserData;
BlockContainer oldContainer = (BlockContainer)oldEntryPoint.Parent;
BlockContainer body = new BlockContainer();
Block newEntryPoint = new Block();
body.Blocks.Add(newEntryPoint);
// Move contents of oldEntryPoint to newEntryPoint
// (we can't move the block itself because it might be the target of branch instructions outside the loop)
newEntryPoint.Instructions.ReplaceList(oldEntryPoint.Instructions);
newEntryPoint.FinalInstruction = oldEntryPoint.FinalInstruction;
newEntryPoint.ILRange = oldEntryPoint.ILRange;
oldEntryPoint.Instructions.ReplaceList(new[] { new Loop(body) });
oldEntryPoint.FinalInstruction = new Nop();
// Move other blocks into the loop body: they're all dominated by the loop header,
// and thus cannot be the target of branch instructions outside the loop.
for (int i = 1; i < loop.Count; i++) {
Block block = (Block)loop[i].UserData;
Debug.Assert(block.Parent == oldContainer);
body.Blocks.Add(block);
}
// Remove all blocks that were moved into the body from the old container
oldContainer.Blocks.RemoveAll(b => b.Parent != oldContainer);
// Rewrite branches within the loop from oldEntryPoint to newEntryPoint:
foreach (var branch in body.Descendants.OfType<Branch>()) {
if (branch.TargetBlock == oldEntryPoint)
branch.TargetBlock = newEntryPoint;
}
// Because we use extended basic blocks, not just basic blocks, it's possible
// that we moved too many instructions into the loop body.
// In cases where those additional instructions branch, that's not really a problem
// (and is more readable with the instructions being inside the loop, anyways)
// But in cases where those additional instructions fall through to the end of
// the block, they previously had the semantics of falling out of the oldContainer,
// thus leaving the loop.
// Now they just fall out of the body block container, and thus get executed repeatedly.
// To fix up this semantic difference, we'll patch up these blocks to branch to
// a new empty block in the old container.
Block fallthrough_helper_block = null;
foreach (var block in body.Blocks) {
if (!block.HasFlag(InstructionFlags.EndPointUnreachable)) {
if (block.FinalInstruction.OpCode != OpCode.Nop) {
// move final instruction into the block
block.Instructions.Add(new Void(block.FinalInstruction));
block.FinalInstruction = new Nop();
}
if (fallthrough_helper_block == null) {
fallthrough_helper_block = new Block();
oldContainer.Blocks.Add(fallthrough_helper_block);
}
block.Instructions.Add(new Branch(fallthrough_helper_block));
}
Debug.Assert(block.HasFlag(InstructionFlags.EndPointUnreachable));
}
}
}
}

2
ICSharpCode.Decompiler/IL/TransformStackIntoVariables.cs → ICSharpCode.Decompiler/IL/Transforms/TransformStackIntoVariables.cs

@ -19,12 +19,14 @@ @@ -19,12 +19,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
namespace ICSharpCode.Decompiler.IL
{
public class ILTransformContext
{
public IDecompilerTypeSystem TypeSystem { get; set; }
public CancellationToken CancellationToken { get; set; }
}
public interface IILTransform

2
ICSharpCode.Decompiler/IL/Instructions/TransformStackIntoVariablesState.cs → ICSharpCode.Decompiler/IL/Transforms/TransformStackIntoVariablesState.cs

@ -29,6 +29,7 @@ namespace ICSharpCode.Decompiler.IL @@ -29,6 +29,7 @@ namespace ICSharpCode.Decompiler.IL
public Stack<ILVariable> Variables { get; set; }
public UnionFind<ILVariable> UnionFind { get; set; }
public Dictionary<Block, ImmutableArray<ILVariable>> InitialVariables { get; set; }
public Dictionary<BlockContainer, ImmutableArray<ILVariable>> FinalVariables { get; set; }
public IDecompilerTypeSystem TypeSystem { get; set; }
public TransformStackIntoVariablesState()
@ -36,6 +37,7 @@ namespace ICSharpCode.Decompiler.IL @@ -36,6 +37,7 @@ namespace ICSharpCode.Decompiler.IL
Variables = new Stack<ILVariable>();
UnionFind = new UnionFind<ILVariable>();
InitialVariables = new Dictionary<Block, ImmutableArray<ILVariable>>();
FinalVariables = new Dictionary<BlockContainer, ImmutableArray<ILVariable>>();
}
public void MergeVariables(Stack<ILVariable> a, Stack<ILVariable> b)

0
ICSharpCode.Decompiler/IL/TransformingVisitor.cs → ICSharpCode.Decompiler/IL/Transforms/TransformingVisitor.cs

3
ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj

@ -100,7 +100,8 @@ @@ -100,7 +100,8 @@
<Compile Include="Helpers\RemoveCompilerAttribute.cs" />
<Compile Include="Helpers\Tester.cs" />
<Compile Include="Helpers\TypeSystemHelper.cs" />
<Compile Include="ILTransforms\Inlining.cs" />
<Compile Include="ILTransforms\InliningTests.cs" />
<Compile Include="Loops.cs" />
<Compile Include="StackToVariablesTests.cs" />
<Compile Include="TestCases\CompoundAssignment.cs" />
<Compile Include="TestCases\ControlFlow.cs" />

2
ICSharpCode.Decompiler/Tests/ILTransforms/Inlining.cs → ICSharpCode.Decompiler/Tests/ILTransforms/InliningTests.cs

@ -26,7 +26,7 @@ namespace ICSharpCode.Decompiler.Tests.ILTransforms @@ -26,7 +26,7 @@ namespace ICSharpCode.Decompiler.Tests.ILTransforms
/// <summary>
/// IL Inlining unit tests.
/// </summary>
public class Inlining
public class InliningTests
{
static ILFunction MakeFunction(ILInstruction body)
{

13
ICSharpCode.Decompiler/Tests/Loops.cs

@ -70,5 +70,18 @@ public class Loops @@ -70,5 +70,18 @@ public class Loops
array[i].ToLower();
}
}
public void NestedLoops()
{
for (int i = 0; i < 10; i++) {
if (i % 2 == 0) {
for (int j = 0; j < 5; j++) {
Console.WriteLine("Y");
}
} else {
Console.WriteLine("X");
}
}
}
}

25
ILSpy/Languages/ILAstLanguage.cs

@ -21,6 +21,7 @@ using System.Collections.Generic; @@ -21,6 +21,7 @@ using System.Collections.Generic;
using System.Linq;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.CSharp;
using ICSharpCode.Decompiler.Disassembler;
using ICSharpCode.Decompiler.IL;
using Mono.Cecil;
@ -88,14 +89,10 @@ namespace ICSharpCode.ILSpy @@ -88,14 +89,10 @@ namespace ICSharpCode.ILSpy
internal static IEnumerable<ILAstLanguage> GetDebugLanguages()
{
yield return new TypedIL();
yield return new BlockIL(null);
yield return new BlockIL(new TransformingVisitor());
//yield return new ILAstLanguage { name = "ILAst (unoptimized)", inlineVariables = false };
//string nextName = "ILAst (variable splitting)";
//foreach (ILAstOptimizationStep step in Enum.GetValues(typeof(ILAstOptimizationStep))) {
// yield return new ILAstLanguage { name = nextName, abortBeforeStep = step };
// nextName = "ILAst (after " + step + ")";
//}
CSharpDecompiler decompiler = new CSharpDecompiler(ModuleDefinition.CreateModule("Dummy", ModuleKind.Dll));
for (int i = 0; i <= decompiler.ILTransforms.Count; i++) {
yield return new BlockIL(decompiler.ILTransforms.Take(i).ToList());
}
}
public override string FileExtension {
@ -136,11 +133,11 @@ namespace ICSharpCode.ILSpy @@ -136,11 +133,11 @@ namespace ICSharpCode.ILSpy
class BlockIL : ILAstLanguage
{
readonly ILVisitor<ILInstruction> transformer;
readonly IReadOnlyList<IILTransform> transforms;
public BlockIL(ILVisitor<ILInstruction> transformer) : base(transformer == null ? "ILAst (blocks)" : "ILAst (" + transformer.ToString() + ")")
public BlockIL(IReadOnlyList<IILTransform> transforms) : base(transforms.Count == 0 ? "ILAst (blocks)" : "ILAst (" + transforms.Last().GetType().Name + ")")
{
this.transformer = transformer;
this.transforms = transforms;
}
public override void DecompileMethod(MethodDefinition method, ITextOutput output, DecompilationOptions options)
@ -150,9 +147,9 @@ namespace ICSharpCode.ILSpy @@ -150,9 +147,9 @@ namespace ICSharpCode.ILSpy
return;
var typeSystem = new DecompilerTypeSystem(method.Module);
ILReader reader = new ILReader(typeSystem);
ILInstruction il = reader.ReadIL(method.Body, options.CancellationToken);
if (transformer != null)
il = il.AcceptVisitor(transformer);
ILFunction il = reader.ReadIL(method.Body, options.CancellationToken);
foreach (var transform in transforms)
transform.Run(il, new ILTransformContext { TypeSystem = typeSystem });
il.WriteTo(output);
}
}

Loading…
Cancel
Save