Browse Source

A bit more documentation for my FlowAnalysis code.

pull/1/head
Daniel Grunwald 14 years ago
parent
commit
ae26637949
  1. 16
      ICSharpCode.Decompiler/FlowAnalysis/ControlFlowEdge.cs
  2. 13
      ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraph.cs
  3. 46
      ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraphBuilder.cs
  4. 86
      ICSharpCode.Decompiler/FlowAnalysis/ControlFlowNode.cs
  5. 50
      ICSharpCode.Decompiler/FlowAnalysis/ControlStructureDetector.cs
  6. 5
      ICSharpCode.Decompiler/FlowAnalysis/SimplifyByRefCalls.cs
  7. 10
      ICSharpCode.Decompiler/FlowAnalysis/SsaBlock.cs
  8. 3
      ICSharpCode.Decompiler/FlowAnalysis/SsaForm.cs
  9. 3
      ICSharpCode.Decompiler/FlowAnalysis/SsaOptimization.cs
  10. 6
      ICSharpCode.Decompiler/FlowAnalysis/SsaVariable.cs

16
ICSharpCode.Decompiler/FlowAnalysis/ControlFlowEdge.cs

@ -20,8 +20,14 @@ using System; @@ -20,8 +20,14 @@ using System;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
/// <summary>
///
/// </summary>
public enum JumpType
{
/// <summary>
/// A regular control flow edge.
/// </summary>
Normal,
/// <summary>
/// Jump to exception handler (an exception occurred)
@ -33,15 +39,21 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -33,15 +39,21 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
/// </summary>
LeaveTry,
/// <summary>
/// Jump from one catch block to its sibling
/// Jump from one catch block to its sibling (e.g. in "try {} catch (A) {} catch (B) {}")
/// </summary>
MutualProtection,
/// <summary>
/// non-determistic jump at end finally (to any of the potential leave targets)
/// 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;

13
ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraph.cs

@ -31,6 +31,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -31,6 +31,7 @@ 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;
@ -90,13 +91,19 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -90,13 +91,19 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
}
#endif
internal void ResetVisited()
/// <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
@ -151,6 +158,10 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -151,6 +158,10 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
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();

46
ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraphBuilder.cs

@ -25,6 +25,9 @@ using Mono.Cecil.Cil; @@ -25,6 +25,9 @@ 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)
@ -32,7 +35,12 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -32,7 +35,12 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
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
@ -56,6 +64,9 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -56,6 +64,9 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
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);
@ -63,6 +74,9 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -63,6 +74,9 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
return index;
}
/// <summary>
/// Builds the ControlFlowGraph.
/// </summary>
public ControlFlowGraph Build()
{
CalculateHasIncomingJumps();
@ -76,6 +90,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -76,6 +90,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
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) {
@ -93,9 +108,12 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -93,9 +108,12 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
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);
@ -116,6 +134,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -116,6 +134,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
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();
@ -127,7 +146,9 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -127,7 +146,9 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
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);
@ -172,7 +193,9 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -172,7 +193,9 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
}
}
}
#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) {
@ -237,11 +260,12 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -237,11 +260,12 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
}
return exceptionalExit;
}
#endregion
void CopyFinallyBlocksIntoLeaveEdges()
#region Step 5a: replace LeaveTry edges with EndFinally edges
// this is used only for copyFinallyBlocks==false; see Step 5b otherwise
void TransformLeaveEdges()
{
// 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) {
@ -255,15 +279,18 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -255,15 +279,18 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
ControlFlowNode handler = FindInnermostExceptionHandlerNode(node.End.Offset);
Debug.Assert(handler.NodeType == ControlFlowNodeType.FinallyOrFaultHandler);
ControlFlowNode copy = CopyFinallySubGraph(handler, handler.EndFinallyOrFaultNode, target);
CreateEdge(node, copy, JumpType.Normal);
CreateEdge(node, handler, JumpType.Normal);
CreateEdge(handler.EndFinallyOrFaultNode, target, JumpType.EndFinally);
}
}
}
#endregion
void TransformLeaveEdges()
#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) {
@ -277,8 +304,8 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -277,8 +304,8 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
ControlFlowNode handler = FindInnermostExceptionHandlerNode(node.End.Offset);
Debug.Assert(handler.NodeType == ControlFlowNodeType.FinallyOrFaultHandler);
CreateEdge(node, handler, JumpType.Normal);
CreateEdge(handler.EndFinallyOrFaultNode, target, JumpType.EndFinally);
ControlFlowNode copy = CopyFinallySubGraph(handler, handler.EndFinallyOrFaultNode, target);
CreateEdge(node, copy, JumpType.Normal);
}
}
}
@ -366,6 +393,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -366,6 +393,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
return oldNode;
}
}
#endregion
#region CreateEdge methods
void CreateEdge(ControlFlowNode fromNode, Instruction toInstruction, JumpType type)

86
ICSharpCode.Decompiler/FlowAnalysis/ControlFlowNode.cs

@ -27,27 +27,72 @@ using Mono.Cecil.Cil; @@ -27,27 +27,72 @@ using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
/// <summary>
/// Type of the control flow node
/// </summary>
public enum ControlFlowNodeType
{
/// <summary>
/// A normal node represents a basic block.
/// </summary>
Normal,
/// <summary>
/// The entry point of the method.
/// </summary>
EntryPoint,
/// <summary>
/// The exit point of the method (every ret instruction branches to this node)
/// </summary>
RegularExit,
/// <summary>
/// This node represents leaving a method irregularly by throwing an exception.
/// </summary>
ExceptionalExit,
/// <summary>
/// This node is used as a header for exception handler blocks.
/// </summary>
CatchHandler,
/// <summary>
/// This node is used as a header for finally blocks and fault blocks.
/// Every leave instruction in the try block leads to the handler of the containing finally block;
/// and exceptional control flow also leads to this handler.
/// </summary>
FinallyOrFaultHandler,
/// <summary>
/// This node is used as footer for finally blocks and fault blocks.
/// Depending on the "copyFinallyBlocks" option used when creating the graph, it is connected with all leave targets using
/// EndFinally edges (when not copying); or with a specific leave target using a normal edge (when copying).
/// For fault blocks, an exception edge is used to represent the "re-throwing" of the exception.
/// </summary>
EndFinallyOrFault
}
/// <summary>
/// Represents a block in the control flow graph.
/// </summary>
public sealed class ControlFlowNode
{
/// <summary>
/// Index of this node in the ControlFlowGraph.Nodes collection.
/// </summary>
public readonly int BlockIndex;
/// <summary>
/// Type of the node.
/// </summary>
public readonly ControlFlowNodeType NodeType;
/// <summary>
/// If this node is a FinallyOrFaultHandler node, this field points to the corresponding EndFinallyOrFault node.
/// Otherwise, this field is null.
/// </summary>
public readonly ControlFlowNode EndFinallyOrFaultNode;
/// <summary>
/// Visited flag that's used in various algorithms.
/// Visited flag, used in various algorithms.
/// Before using it in your algorithm, reset it to false by calling ControlFlowGraph.ResetVisited();
/// </summary>
internal bool Visited;
public bool Visited;
/// <summary>
/// Signalizes that this node is a copy of another node.
@ -55,21 +100,33 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -55,21 +100,33 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
public ControlFlowNode CopyFrom { get; internal set; }
/// <summary>
/// Gets the immediate dominator.
/// Gets the immediate dominator (the parent in the dominator tree).
/// Null if dominance has not been calculated; or if the node is unreachable.
/// </summary>
public ControlFlowNode ImmediateDominator { get; internal set; }
/// <summary>
/// List of children in the dominator tree.
/// </summary>
public readonly List<ControlFlowNode> DominatorTreeChildren = new List<ControlFlowNode>();
/// <summary>
/// The dominance frontier of this node.
/// This is the set of nodes for which this node dominates a predecessor, but which are not strictly dominated by this node.
/// </summary>
/// <remarks>
/// b.DominanceFrontier = { y in CFG; (exists p in predecessors(y): b dominates p) and not (b strictly dominates y)}
/// </remarks>
public HashSet<ControlFlowNode> DominanceFrontier;
/// <summary>
/// Start of code block represented by this node. Only set for nodetype == Normal.
/// Start of code block represented by this node. Only set for nodetype == Normal.
/// </summary>
public readonly Instruction Start;
/// <summary>
/// End of the code block represented by this node. Only set for nodetype == Normal.
/// The end is exclusive, the end instruction itself does not belong to this block.
/// </summary>
public readonly Instruction End;
@ -79,7 +136,14 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -79,7 +136,14 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
/// </summary>
public readonly ExceptionHandler ExceptionHandler;
/// <summary>
/// List of incoming control flow edges.
/// </summary>
public readonly List<ControlFlowEdge> Incoming = new List<ControlFlowEdge>();
/// <summary>
/// List of outgoing control flow edges.
/// </summary>
public readonly List<ControlFlowEdge> Outgoing = new List<ControlFlowEdge>();
internal ControlFlowNode(int blockIndex, ControlFlowNodeType nodeType)
@ -109,18 +173,28 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -109,18 +173,28 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
Debug.Assert((exceptionHandler.HandlerType == ExceptionHandlerType.Finally || exceptionHandler.HandlerType == ExceptionHandlerType.Fault) == (endFinallyOrFaultNode != null));
}
/// <summary>
/// Gets all predecessors (=sources of incoming edges)
/// </summary>
public IEnumerable<ControlFlowNode> Predecessors {
get {
return Incoming.Select(e => e.Source);
}
}
/// <summary>
/// Gets all successors (=targets of outgoing edges)
/// </summary>
public IEnumerable<ControlFlowNode> Successors {
get {
return Outgoing.Select(e => e.Target);
}
}
/// <summary>
/// Gets all instructions in this node.
/// Returns an empty list for special nodes that don't have any instructions.
/// </summary>
public IEnumerable<Instruction> Instructions {
get {
Instruction inst = Start;
@ -186,8 +260,12 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -186,8 +260,12 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
return writer.ToString();
}
/// <summary>
/// Gets whether <c>this</c> dominates <paramref name="node"/>.
/// </summary>
public bool Dominates(ControlFlowNode node)
{
// TODO: this can be made O(1) by numbering the dominator tree
ControlFlowNode tmp = node;
while (tmp != null) {
if (tmp == this)

50
ICSharpCode.Decompiler/FlowAnalysis/ControlStructureDetector.cs

@ -34,15 +34,20 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -34,15 +34,20 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
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);
g.ResetVisited();
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);
@ -112,6 +117,13 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -112,6 +117,13 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
#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)
static void DetectLoops(ControlFlowGraph g, ControlStructure current, CancellationToken cancellationToken)
{
g.ResetVisited();
@ -170,20 +182,56 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -170,20 +182,56 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
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)

5
ICSharpCode.Decompiler/FlowAnalysis/SimplifyByRefCalls.cs

@ -25,6 +25,11 @@ using Mono.Cecil.Cil; @@ -25,6 +25,11 @@ 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)

10
ICSharpCode.Decompiler/FlowAnalysis/SsaBlock.cs

@ -22,12 +22,22 @@ using System.IO; @@ -22,12 +22,22 @@ 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)

3
ICSharpCode.Decompiler/FlowAnalysis/SsaForm.cs

@ -27,6 +27,9 @@ using Mono.Cecil.Cil; @@ -27,6 +27,9 @@ using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
/// <summary>
/// Represents a graph of SsaBlocks.
/// </summary>
public sealed class SsaForm
{
readonly SsaVariable[] parameters;

3
ICSharpCode.Decompiler/FlowAnalysis/SsaOptimization.cs

@ -24,6 +24,9 @@ using Mono.Cecil.Cil; @@ -24,6 +24,9 @@ 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)

6
ICSharpCode.Decompiler/FlowAnalysis/SsaVariable.cs

@ -23,6 +23,10 @@ using Mono.Cecil.Cil; @@ -23,6 +23,10 @@ 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;
@ -68,6 +72,8 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -68,6 +72,8 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
/// 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>

Loading…
Cancel
Save