mirror of https://github.com/icsharpcode/ILSpy.git
35 changed files with 615 additions and 2827 deletions
@ -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; |
||||
} |
||||
} |
||||
} |
||||
} |
@ -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); |
||||
} |
||||
} |
||||
} |
||||
}); |
||||
} |
||||
} |
||||
} |
@ -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
|
||||
} |
||||
} |
@ -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); |
||||
} |
||||
} |
||||
} |
@ -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; |
||||
} |
||||
} |
||||
} |
@ -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; } |
||||
} |
||||
} |
@ -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); |
||||
} |
||||
} |
||||
} |
||||
} |
@ -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(); |
||||
} |
||||
} |
||||
} |
@ -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(); |
||||
} |
||||
} |
||||
} |
||||
} |
@ -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)); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
@ -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(')'); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
@ -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; |
||||
} |
||||
} |
||||
} |
||||
} |
@ -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; |
||||
} |
||||
} |
@ -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
|
||||
} |
||||
} |
@ -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); |
||||
} |
||||
} |
||||
} |
@ -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)); |
||||
} |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue