diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
index 5ab7f756f..328c100b4 100644
--- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
+++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
@@ -50,6 +50,7 @@ namespace ICSharpCode.Decompiler.CSharp
new ILInlining(), // temporary pass, just to make the ILAst easier to read while debugging loop detection
new LoopDetection(),
new IntroduceExitPoints(),
+ new SplitVariables(),
new ConditionDetection(),
new ILInlining(),
new CopyPropagation(),
diff --git a/ICSharpCode.Decompiler/FlowAnalysis/ReachingDefinitions.cs b/ICSharpCode.Decompiler/FlowAnalysis/ReachingDefinitions.cs
index 47df34d49..b39ecc3a3 100644
--- a/ICSharpCode.Decompiler/FlowAnalysis/ReachingDefinitions.cs
+++ b/ICSharpCode.Decompiler/FlowAnalysis/ReachingDefinitions.cs
@@ -17,6 +17,12 @@
// DEALINGS IN THE SOFTWARE.
using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Diagnostics;
+using System.Linq;
+using ICSharpCode.Decompiler.IL;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
@@ -24,8 +30,523 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
/// Implements the "reaching definitions" analysis.
///
/// https://en.wikipedia.org/wiki/Reaching_definition
+ ///
+ /// By "definitions", we mean stores to local variables.
///
+ ///
+ /// Possible "definitions" that store to a variable are:
+ /// * StLoc
+ /// * TryCatchHandler (for the exception variable)
+ /// * ReachingDefinitions.UninitializedVariable for uninitialized variables.
+ /// Note that we do not keep track of LdLoca/references/pointers.
+ /// The analysis will likely be wrong/incomplete for variables with AddressCount != 0.
+ ///
public class ReachingDefinitions
{
+ #region Documentation + member fields
+ ///
+ /// A special Nop instruction that gets used as a fake store if the variable
+ /// is possibly uninitialized.
+ ///
+ public static readonly ILInstruction UninitializedVariable = new Nop();
+
+ // The reaching definition analysis tracks a 'state'.
+ // This 'state' represents the reaching definitions at a given source code position.
+ // There are many states (one per source code position, i.e. ILInstruction),
+ // but we don't store all of them.
+ // We only keep track of:
+ // a) the current state in the RDVisitor
+ // This state corresponds to the instruction currently being visited,
+ // and gets mutated as we traverse the ILAst.
+ // b) the input state for each control flow node
+ // This also gets mutated as the analysis learns about new control flow edges.
+ //
+ // A state can either be reachable, or unreachable:
+ // 1) unreachable
+ // Note that during the analysis, "unreachable" just means we have not yet found a path
+ // from the entry point to the node. States transition from unreachable to reachable as
+ // the analysis processes more control flow paths.
+ // 2) reachable
+ // In this case, the state contains, for each variable, the set of stores that might have
+ // written to the variable before the control flow reached the state's source code position.
+ // This set does not include stores that were definitely overwritten by other stores to the
+ // same variable.
+ // During the analysis, the set of stores gets extended as the analysis processes more code paths.
+ // For loops, we need to analyze the loop body before we can know the state for the loop backedge,
+ // but we need to know the input state for the loop body (to which the backedge state contributes)
+ // before we can analyze the loop body.
+ // Solution: we repeat the analysis of the loop body multiple times, until the state no longer changes.
+ // Because all state changes are irreversible (we only add to the set of stores, we never remove from it),
+ // this algorithm will eventually terminate.
+ // To make it terminate reasonably quickly, we need to process the control flow nodes in the correct order:
+ // reverse post-order. This class assumes the blocks in each block container are already sorted appropriately.
+ // The caller can use BlockContainer.SortBlocks() for this.
+
+ // The state as described above could be represented as a `Dictionary>`.
+ // To consume less memory, we instead assign an integer index to all stores in the analyzed function ("store index"),
+ // and store the state as a `BitSet` instead.
+ // Each bit in the set corresponds to one store instruction, and is `true` iff the store is a reaching definition
+ // for the variable it is storing to.
+ // The `allStores` array has the same length as the bit sets and holds the corresponding `ILInstruction` objects (store instructions).
+ // All stores for a single variable occupy a contiguous segment of the `allStores` array (and thus also of the `state`).
+
+ ///
+ /// To distinguish unreachable from reachable states, we use the first bit in the bitset to store the 'reachable bit'.
+ /// If this bit is set, the state is reachable, and the remaining bits
+ ///
+ const int ReachableBit = 0;
+
+ ///
+ /// Because bit number 0 is the ReachableBit, we start counting store indices at 1.
+ ///
+ const int FirstStoreIndex = 1;
+
+ ///
+ /// The function being analyzed.
+ ///
+ readonly ILVariableScope scope;
+
+ ///
+ /// All stores for all variables in the scope.
+ ///
+ /// state[storeIndex] is true iff allStores[storeIndex] is a reaching definition.
+ /// Invariant: state.Length == allStores.Length.
+ ///
+ readonly ILInstruction[] allStores;
+
+ ///
+ /// Maps instructions appearing in allStores to their index.
+ ///
+ /// Invariant: allStores[storeIndexMap[inst]] == inst
+ ///
+ /// Does not contain UninitializedVariable (as that special instruction has multiple store indices, one per variable)
+ ///
+ readonly Dictionary storeIndexMap = new Dictionary();
+
+ ///
+ /// For all variables v: allStores[firstStoreIndexForVariable[v.IndexInScope]] is the UninitializedVariable entry for v.
+ /// The next few stores (up to firstStoreIndexForVariable[v.IndexInScope + 1], exclusive) are the full list of stores for v.
+ ///
+ ///
+ /// Invariant: firstStoreIndexForVariable[scope.Variables.Count] == allStores.Length
+ ///
+ readonly int[] firstStoreIndexForVariable;
+
+ ///
+ /// activeVariable[v.IndexInScope] is true iff RD analysis is enabled for the variable.
+ ///
+ readonly BitSet activeVariables;
+
+ ///
+ /// Holds the state for incoming branches.
+ ///
+ ///
+ /// Only used for blocks in block containers; not for inline blocks.
+ ///
+ readonly Dictionary stateOnBranch = new Dictionary();
+
+ ///
+ /// Holds the state at the block container end-point. (=state for incoming 'leave' instructions)
+ ///
+ readonly Dictionary stateOnLeave = new Dictionary();
+ #endregion
+
+ #region Constructor
+ ///
+ /// Run reaching definitions analysis for the specified variable scope.
+ ///
+ public ReachingDefinitions(ILVariableScope scope, Predicate pred)
+ : this(scope, GetActiveVariableBitSet(scope, pred))
+ {
+ }
+
+ static BitSet GetActiveVariableBitSet(ILVariableScope scope, Predicate pred)
+ {
+ if (scope == null)
+ throw new ArgumentNullException("scope");
+ BitSet activeVariables = new BitSet(scope.Variables.Count);
+ for (int vi = 0; vi < scope.Variables.Count; vi++) {
+ activeVariables[vi] = pred(scope.Variables[vi]);
+ }
+ return activeVariables;
+ }
+
+ ///
+ /// Run reaching definitions analysis for the specified variable scope.
+ ///
+ public ReachingDefinitions(ILVariableScope scope, BitSet activeVariables)
+ {
+ if (scope == null)
+ throw new ArgumentNullException("scope");
+ if (activeVariables == null)
+ throw new ArgumentNullException("activeVariables");
+ this.scope = scope;
+ this.activeVariables = activeVariables;
+
+ // Fill `allStores` and `storeIndexMap` and `firstStoreIndexForVariable`.
+ var storesByVar = FindAllStoresByVariable(scope, activeVariables);
+ allStores = new ILInstruction[FirstStoreIndex + storesByVar.Sum(l => l != null ? l.Count : 0)];
+ firstStoreIndexForVariable = new int[scope.Variables.Count + 1];
+ int si = FirstStoreIndex;
+ for (int vi = 0; vi < storesByVar.Length; vi++) {
+ firstStoreIndexForVariable[vi] = si;
+ var stores = storesByVar[vi];
+ if (stores != null) {
+ int expectedStoreCount = scope.Variables[vi].StoreCount;
+ if (scope.Variables[vi].Kind != VariableKind.Parameter && scope.Variables[vi].Kind != VariableKind.This) {
+ // Extra store for UninitializedVariable
+ expectedStoreCount += 1;
+ // Note that for VariableKind.Parameter/This, this extra store
+ // is already accounted for in ILVariable.StoreCount.
+ }
+ Debug.Assert(stores.Count == expectedStoreCount);
+ stores.CopyTo(allStores, si);
+ // Add all stores except for UninitializedVariable to storeIndexMap.
+ for (int i = 1; i < stores.Count; i++) {
+ storeIndexMap.Add(stores[i], si + i);
+ }
+ si += stores.Count;
+ }
+ }
+ firstStoreIndexForVariable[scope.Variables.Count] = si;
+ Debug.Assert(si == allStores.Length);
+
+ this.workList = CreateWorkLists(scope);
+ InitStateDictionaries();
+ RDVisitor visitor = new RDVisitor(this);
+ scope.Children.Single().AcceptVisitor(visitor);
+ }
+
+ ///
+ /// Fill allStores and storeIndexMap.
+ ///
+ static List[] FindAllStoresByVariable(ILVariableScope scope, BitSet activeVariables)
+ {
+ // For each variable, find the list of ILInstructions storing to that variable
+ List[] storesByVar = new List[scope.Variables.Count];
+ for (int vi = 0; vi < storesByVar.Length; vi++) {
+ if (activeVariables[vi])
+ storesByVar[vi] = new List { UninitializedVariable };
+ }
+ foreach (var inst in scope.Descendants) {
+ ILVariable v;
+ if (inst.MatchStLoc(out v) || inst.MatchTryCatchHandler(out v)) {
+ if (v.Scope == scope && activeVariables[v.IndexInScope]) {
+ storesByVar[v.IndexInScope].Add(inst);
+ }
+ }
+ }
+ return storesByVar;
+ }
+ #endregion
+
+ #region State Management
+ ///
+ /// Create the initial state (reachable + all variables uninitialized).
+ ///
+ BitSet CreateInitialState()
+ {
+ BitSet initialState = new BitSet(allStores.Length);
+ initialState.Set(ReachableBit);
+ for (int vi = 0; vi < scope.Variables.Count; vi++) {
+ if (activeVariables[vi]) {
+ Debug.Assert(allStores[firstStoreIndexForVariable[vi]] == UninitializedVariable);
+ initialState.Set(firstStoreIndexForVariable[vi]);
+ }
+ }
+ return initialState;
+ }
+
+ BitSet CreateUnreachableState()
+ {
+ return new BitSet(allStores.Length);
+ }
+
+ void InitStateDictionaries()
+ {
+ foreach (var container in scope.Descendants.OfType()) {
+ foreach (var block in container.Blocks) {
+ stateOnBranch.Add(block, CreateUnreachableState());
+ }
+ stateOnLeave.Add(container, CreateUnreachableState());
+ }
+ }
+
+ ///
+ /// Merge incomingState into state.
+ ///
+ ///
+ /// Returns true if state was modified.
+ ///
+ static bool MergeState(BitSet state, BitSet incomingState)
+ {
+ if (!incomingState[ReachableBit]) {
+ // MergeState() is a no-op if the incoming state is unreachable
+ return false;
+ }
+ if (state[ReachableBit]) {
+ // both reachable: state |= incomingState;
+ if (state.IsSupersetOf(incomingState)) {
+ // UnionWith() wouldn't actually change the state,
+ // so we have to return false
+ return false;
+ }
+ state.UnionWith(incomingState);
+ } else {
+ state.ReplaceWith(incomingState);
+ }
+ return true;
+ }
+ #endregion
+
+ #region Worklist
+ ///
+ /// For each block container, stores the set of blocks (via Block.ChildIndex) that had their incoming state
+ /// changed and were not processed yet.
+ ///
+ readonly Dictionary> workList;
+
+ static Dictionary> CreateWorkLists(ILVariableScope scope)
+ {
+ var worklists = new Dictionary>();
+ foreach (var container in scope.Descendants.OfType()) {
+ worklists.Add(container, new SortedSet());
+ }
+ return worklists;
+ }
+
+ ///
+ /// The work list keeps track of which blocks had their incoming state updated,
+ /// but did not run yet.
+ ///
+ void AddToWorkList(Block block)
+ {
+ BlockContainer container = (BlockContainer)block.Parent;
+ workList[container].Add(block.ChildIndex);
+ }
+ #endregion
+
+ ///
+ /// Visitor that traverses the ILInstruction tree.
+ ///
+ class RDVisitor : ILVisitor
+ {
+ readonly ReachingDefinitions rd;
+
+ internal RDVisitor(ReachingDefinitions rd)
+ {
+ this.rd = rd;
+ this.state = rd.CreateInitialState();
+ this.stateOnException = rd.CreateUnreachableState();
+ }
+
+ ///
+ /// Combines state of all possible exceptional control flow paths in the current try block.
+ ///
+ BitSet stateOnException;
+
+ ///
+ /// Current state.
+ /// Gets mutated as the visitor traverses the ILAst.
+ ///
+ BitSet state;
+
+ protected override void Default(ILInstruction inst)
+ {
+ // This method assumes normal control flow and no branches.
+ if ((inst.DirectFlags & (InstructionFlags.ControlFlow | InstructionFlags.MayBranch | InstructionFlags.EndPointUnreachable)) != 0) {
+ throw new NotImplementedException("RDVisitor is missing implementation for " + inst.GetType().Name);
+ }
+
+ // Since this instruction has normal control flow, we can evaluate our children left-to-right.
+ foreach (var child in inst.Children) {
+ child.AcceptVisitor(this);
+ Debug.Assert(!(state[ReachableBit] && child.HasFlag(InstructionFlags.EndPointUnreachable)));
+ }
+
+ // If this instruction can throw an exception, handle the exceptional control flow edge.
+ if ((inst.DirectFlags & InstructionFlags.MayThrow) != 0) {
+ MayThrow();
+ }
+ }
+
+ ///
+ /// Handle control flow when `throwInst` throws an exception.
+ ///
+ void MayThrow()
+ {
+ MergeState(stateOnException, state);
+ }
+
+ void MarkUnreachable()
+ {
+ state.Clear(ReachableBit);
+ }
+
+ protected internal override void VisitStLoc(StLoc inst)
+ {
+ Default(inst);
+ ILVariable v = inst.Variable;
+ if (v.Scope == rd.scope && rd.activeVariables[v.IndexInScope]) {
+ // Clear the set of stores for this variable:
+ state.Clear(rd.firstStoreIndexForVariable[v.IndexInScope],
+ rd.firstStoreIndexForVariable[v.IndexInScope + 1]);
+ // And replace it with this store:
+ state.Set(rd.storeIndexMap[inst]);
+ }
+ }
+
+ protected internal override void VisitBlockContainer(BlockContainer container)
+ {
+ SortedSet worklist = rd.workList[container];
+ if (MergeState(rd.stateOnBranch[container.EntryPoint], state)) {
+ worklist.Add(0); // add container entry point to work list
+ }
+ // Because we use a SortedSet for the work list,
+ // we always process the blocks in the same order as they are in the container
+ // (usually reverse post-order).
+ while (worklist.Count > 0) {
+ int blockIndex = worklist.Min;
+ worklist.Remove(blockIndex);
+ Block block = container.Blocks[blockIndex];
+ state.ReplaceWith(rd.stateOnBranch[block]);
+ block.AcceptVisitor(this);
+ }
+ state.ReplaceWith(rd.stateOnLeave[container]);
+ }
+
+ protected internal override void VisitBranch(Branch inst)
+ {
+ if (MergeState(rd.stateOnBranch[inst.TargetBlock], state)) {
+ rd.AddToWorkList(inst.TargetBlock);
+ }
+ MarkUnreachable();
+ }
+
+ protected internal override void VisitLeave(Leave inst)
+ {
+ Debug.Assert(inst.IsDescendantOf(inst.TargetContainer));
+ MergeState(rd.stateOnLeave[inst.TargetContainer], state);
+ // Note: We don't have to put the block container onto the work queue,
+ // because it's an ancestor of the Leave instruction, and hence
+ // we are currently somewhere within the VisitBlockContainer() call.
+ MarkUnreachable();
+ }
+
+ protected internal override void VisitReturn(Return inst)
+ {
+ if (inst.ReturnValue != null)
+ inst.ReturnValue.AcceptVisitor(this);
+ MarkUnreachable();
+ }
+
+ protected internal override void VisitThrow(Throw inst)
+ {
+ inst.Argument.AcceptVisitor(this);
+ MayThrow();
+ MarkUnreachable();
+ }
+
+ protected internal override void VisitRethrow(Rethrow inst)
+ {
+ MayThrow();
+ MarkUnreachable();
+ }
+
+ ///
+ /// Visits the TryBlock.
+ ///
+ /// Returns a new BitSet representing the state of exceptional control flow transfer
+ /// out of the try block.
+ ///
+ BitSet HandleTryBlock(TryInstruction inst)
+ {
+ BitSet oldStateOnException = stateOnException;
+ BitSet newStateOnException = rd.CreateUnreachableState();
+
+ stateOnException = newStateOnException;
+ inst.TryBlock.AcceptVisitor(this);
+ stateOnException = oldStateOnException;
+
+ return newStateOnException;
+ }
+
+ protected internal override void VisitTryCatch(TryCatch inst)
+ {
+ BitSet caughtState = HandleTryBlock(inst);
+ BitSet endpointState = state.Clone();
+ // The exception might get propagated if no handler matches the type:
+ MergeState(stateOnException, caughtState);
+ foreach (var handler in inst.Handlers) {
+ state.ReplaceWith(caughtState);
+ handler.Filter.AcceptVisitor(this);
+ // if the filter return false, any mutations done by the filter
+ // will be visible by the remaining handlers
+ // (but it's also possible that the filter didn't get executed at all
+ // because the exception type doesn't match)
+ MergeState(caughtState, state);
+
+ handler.Body.AcceptVisitor(this);
+ MergeState(endpointState, state);
+ }
+ state = endpointState;
+ }
+
+ protected internal override void VisitTryFinally(TryFinally inst)
+ {
+ // I don't think there's a good way to track dataflow across finally blocks
+ // without duplicating the whole finally block.
+ // We'll just approximate 'try { .. } finally { .. }' as 'try { .. } catch {} .. if (?) rethrow; }'
+ BitSet caughtState = HandleTryBlock(inst);
+ MergeState(state, caughtState);
+ inst.FinallyBlock.AcceptVisitor(this);
+ MayThrow();
+ // Our approximation allows the impossible code path where the try block wasn't fully executed
+ // and the finally block did not rethrow the exception.
+ // This can cause us to not be marked unreachable in cases where the simple InstructionFlags
+ // know the path is unreachable -- so use the flag to fix the reachable bit.
+ if (inst.HasFlag(InstructionFlags.EndPointUnreachable)) {
+ MarkUnreachable();
+ }
+ }
+
+ protected internal override void VisitTryFault(TryFault inst)
+ {
+ // try-fault executes fault block if an exception occurs in try,
+ // and always rethrows the exception at the end.
+ BitSet caughtState = HandleTryBlock(inst);
+ BitSet noException = state;
+ state = caughtState;
+ inst.FaultBlock.AcceptVisitor(this);
+ MayThrow(); // rethrow the exception after the fault block
+
+ // try-fault exits normally only if no exception occurred
+ state = noException;
+ }
+
+ protected internal override void VisitIfInstruction(IfInstruction inst)
+ {
+ inst.Condition.AcceptVisitor(this);
+ BitSet branchState = state.Clone();
+ inst.TrueInst.AcceptVisitor(this);
+ BitSet afterTrueState = state;
+ state = branchState;
+ inst.FalseInst.AcceptVisitor(this);
+ MergeState(state, afterTrueState);
+ }
+
+ protected internal override void VisitSwitchInstruction(SwitchInstruction inst)
+ {
+ inst.Value.AcceptVisitor(this);
+ BitSet beforeSections = state.Clone();
+ BitSet afterSections = rd.CreateUnreachableState();
+ foreach (var section in inst.Sections) {
+ state.ReplaceWith(beforeSections);
+ section.AcceptVisitor(this);
+ MergeState(afterSections, state);
+ }
+ state = afterSections;
+ }
+ }
}
}
diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
index f8d1b69f2..053ab934e 100644
--- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
+++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
@@ -123,6 +123,7 @@
+
@@ -155,6 +156,7 @@
+
diff --git a/ICSharpCode.Decompiler/IL/InstructionFlags.cs b/ICSharpCode.Decompiler/IL/InstructionFlags.cs
index 2f2a1abc5..b269dfad2 100644
--- a/ICSharpCode.Decompiler/IL/InstructionFlags.cs
+++ b/ICSharpCode.Decompiler/IL/InstructionFlags.cs
@@ -66,5 +66,16 @@ namespace ICSharpCode.Decompiler.IL
/// (unless the instruction represents an infinite loop).
///
EndPointUnreachable = 0x400,
+ ///
+ /// The instruction contains some kind of internal control flow.
+ ///
+ ///
+ /// If this flag is not set, the all descendants of the instruction are fully evaluated (modulo MayThrow/MayBranch)
+ /// in left-to-right pre-order.
+ ///
+ /// Note that branch instructions don't have this flag set, because their control flow is not internal
+ /// (and they don't have any unusual argument evaluation rules).
+ ///
+ ControlFlow = 0x800,
}
}
diff --git a/ICSharpCode.Decompiler/IL/Instructions.cs b/ICSharpCode.Decompiler/IL/Instructions.cs
index 9b64b1bc6..f8eb1f4c5 100644
--- a/ICSharpCode.Decompiler/IL/Instructions.cs
+++ b/ICSharpCode.Decompiler/IL/Instructions.cs
@@ -219,6 +219,15 @@ namespace ICSharpCode.Decompiler.IL
var clone = (SimpleInstruction)ShallowClone();
return clone;
}
+ protected override InstructionFlags ComputeFlags()
+ {
+ return InstructionFlags.None;
+ }
+ public override InstructionFlags DirectFlags {
+ get {
+ return InstructionFlags.None;
+ }
+ }
}
/// Instruction with a single argument
@@ -277,7 +286,12 @@ namespace ICSharpCode.Decompiler.IL
}
protected override InstructionFlags ComputeFlags()
{
- return argument.Flags;
+ return argument.Flags | InstructionFlags.None;
+ }
+ public override InstructionFlags DirectFlags {
+ get {
+ return InstructionFlags.None;
+ }
}
public override void WriteTo(ITextOutput output)
{
@@ -362,7 +376,12 @@ namespace ICSharpCode.Decompiler.IL
}
protected override InstructionFlags ComputeFlags()
{
- return left.Flags | right.Flags;
+ return left.Flags | right.Flags | InstructionFlags.None;
+ }
+ public override InstructionFlags DirectFlags {
+ get {
+ return InstructionFlags.None;
+ }
}
public override void WriteTo(ITextOutput output)
{
@@ -422,6 +441,11 @@ namespace ICSharpCode.Decompiler.IL
{
return Arguments.Aggregate(InstructionFlags.None, (f, arg) => f | arg.Flags) | InstructionFlags.MayThrow | InstructionFlags.SideEffect;
}
+ public override InstructionFlags DirectFlags {
+ get {
+ return InstructionFlags.MayThrow | InstructionFlags.SideEffect;
+ }
+ }
}
/// No operation. Takes 0 arguments and returns void.
@@ -609,6 +633,11 @@ namespace ICSharpCode.Decompiler.IL
{
return base.ComputeFlags() | InstructionFlags.MayThrow;
}
+ public override InstructionFlags DirectFlags {
+ get {
+ return base.DirectFlags | InstructionFlags.MayThrow;
+ }
+ }
public override void AcceptVisitor(ILVisitor visitor)
{
visitor.VisitDiv(this);
@@ -630,6 +659,11 @@ namespace ICSharpCode.Decompiler.IL
{
return base.ComputeFlags() | InstructionFlags.MayThrow;
}
+ public override InstructionFlags DirectFlags {
+ get {
+ return base.DirectFlags | InstructionFlags.MayThrow;
+ }
+ }
public override void AcceptVisitor(ILVisitor visitor)
{
visitor.VisitRem(this);
@@ -1062,6 +1096,11 @@ namespace ICSharpCode.Decompiler.IL
{
return InstructionFlags.SideEffect;
}
+ public override InstructionFlags DirectFlags {
+ get {
+ return InstructionFlags.SideEffect;
+ }
+ }
public override void AcceptVisitor(ILVisitor visitor)
{
visitor.VisitDebugBreak(this);
@@ -1202,6 +1241,11 @@ namespace ICSharpCode.Decompiler.IL
{
return base.ComputeFlags() | InstructionFlags.MayThrow;
}
+ public override InstructionFlags DirectFlags {
+ get {
+ return base.DirectFlags | InstructionFlags.MayThrow;
+ }
+ }
public override void AcceptVisitor(ILVisitor visitor)
{
visitor.VisitCkfinite(this);
@@ -1346,6 +1390,11 @@ namespace ICSharpCode.Decompiler.IL
{
return value.Flags;
}
+ public override InstructionFlags DirectFlags {
+ get {
+ return InstructionFlags.None;
+ }
+ }
public override void WriteTo(ITextOutput output)
{
output.Write(OpCode);
@@ -1424,6 +1473,11 @@ namespace ICSharpCode.Decompiler.IL
{
return value.Flags;
}
+ public override InstructionFlags DirectFlags {
+ get {
+ return InstructionFlags.None;
+ }
+ }
public override void WriteTo(ITextOutput output)
{
output.Write(OpCode);
@@ -1625,6 +1679,11 @@ namespace ICSharpCode.Decompiler.IL
{
return base.ComputeFlags() | InstructionFlags.MayThrow;
}
+ public override InstructionFlags DirectFlags {
+ get {
+ return base.DirectFlags | InstructionFlags.MayThrow;
+ }
+ }
public override void WriteTo(ITextOutput output)
{
output.Write(OpCode);
@@ -1709,6 +1768,11 @@ namespace ICSharpCode.Decompiler.IL
{
return base.ComputeFlags() | InstructionFlags.MayThrow;
}
+ public override InstructionFlags DirectFlags {
+ get {
+ return base.DirectFlags | InstructionFlags.MayThrow;
+ }
+ }
public override void AcceptVisitor(ILVisitor visitor)
{
visitor.VisitLocAlloc(this);
@@ -1834,6 +1898,11 @@ namespace ICSharpCode.Decompiler.IL
{
return target.Flags | InstructionFlags.SideEffect | InstructionFlags.MayThrow;
}
+ public override InstructionFlags DirectFlags {
+ get {
+ return InstructionFlags.SideEffect | InstructionFlags.MayThrow;
+ }
+ }
public override void WriteTo(ITextOutput output)
{
if (IsVolatile)
@@ -1920,6 +1989,11 @@ namespace ICSharpCode.Decompiler.IL
{
return target.Flags | InstructionFlags.MayThrow;
}
+ public override InstructionFlags DirectFlags {
+ get {
+ return InstructionFlags.MayThrow;
+ }
+ }
public override void WriteTo(ITextOutput output)
{
output.Write(OpCode);
@@ -2024,6 +2098,11 @@ namespace ICSharpCode.Decompiler.IL
{
return target.Flags | value.Flags | InstructionFlags.SideEffect | InstructionFlags.MayThrow;
}
+ public override InstructionFlags DirectFlags {
+ get {
+ return InstructionFlags.SideEffect | InstructionFlags.MayThrow;
+ }
+ }
public override void WriteTo(ITextOutput output)
{
if (IsVolatile)
@@ -2068,6 +2147,11 @@ namespace ICSharpCode.Decompiler.IL
{
return InstructionFlags.SideEffect;
}
+ public override InstructionFlags DirectFlags {
+ get {
+ return InstructionFlags.SideEffect;
+ }
+ }
public override void WriteTo(ITextOutput output)
{
if (IsVolatile)
@@ -2182,6 +2266,11 @@ namespace ICSharpCode.Decompiler.IL
{
return value.Flags | InstructionFlags.SideEffect;
}
+ public override InstructionFlags DirectFlags {
+ get {
+ return InstructionFlags.SideEffect;
+ }
+ }
public override void WriteTo(ITextOutput output)
{
if (IsVolatile)
@@ -2220,6 +2309,11 @@ namespace ICSharpCode.Decompiler.IL
{
return base.ComputeFlags() | InstructionFlags.MayThrow;
}
+ public override InstructionFlags DirectFlags {
+ get {
+ return base.DirectFlags | InstructionFlags.MayThrow;
+ }
+ }
public override void WriteTo(ITextOutput output)
{
output.Write(OpCode);
@@ -2336,6 +2430,11 @@ namespace ICSharpCode.Decompiler.IL
{
return target.Flags | InstructionFlags.SideEffect | InstructionFlags.MayThrow;
}
+ public override InstructionFlags DirectFlags {
+ get {
+ return InstructionFlags.SideEffect | InstructionFlags.MayThrow;
+ }
+ }
public override void WriteTo(ITextOutput output)
{
if (IsVolatile)
@@ -2444,6 +2543,11 @@ namespace ICSharpCode.Decompiler.IL
{
return target.Flags | value.Flags | InstructionFlags.SideEffect | InstructionFlags.MayThrow;
}
+ public override InstructionFlags DirectFlags {
+ get {
+ return InstructionFlags.SideEffect | InstructionFlags.MayThrow;
+ }
+ }
public override void WriteTo(ITextOutput output)
{
if (IsVolatile)
@@ -2484,6 +2588,11 @@ namespace ICSharpCode.Decompiler.IL
{
return base.ComputeFlags() | InstructionFlags.SideEffect | InstructionFlags.MayThrow;
}
+ public override InstructionFlags DirectFlags {
+ get {
+ return base.DirectFlags | InstructionFlags.SideEffect | InstructionFlags.MayThrow;
+ }
+ }
public override void WriteTo(ITextOutput output)
{
output.Write(OpCode);
@@ -2518,6 +2627,11 @@ namespace ICSharpCode.Decompiler.IL
{
return base.ComputeFlags() | InstructionFlags.MayThrow;
}
+ public override InstructionFlags DirectFlags {
+ get {
+ return base.DirectFlags | InstructionFlags.MayThrow;
+ }
+ }
public override void WriteTo(ITextOutput output)
{
output.Write(OpCode);
@@ -2552,6 +2666,11 @@ namespace ICSharpCode.Decompiler.IL
{
return base.ComputeFlags() | InstructionFlags.SideEffect | InstructionFlags.MayThrow;
}
+ public override InstructionFlags DirectFlags {
+ get {
+ return base.DirectFlags | InstructionFlags.SideEffect | InstructionFlags.MayThrow;
+ }
+ }
public override void WriteTo(ITextOutput output)
{
output.Write(OpCode);
@@ -2640,6 +2759,11 @@ namespace ICSharpCode.Decompiler.IL
{
return Indices.Aggregate(InstructionFlags.None, (f, arg) => f | arg.Flags) | InstructionFlags.MayThrow;
}
+ public override InstructionFlags DirectFlags {
+ get {
+ return InstructionFlags.MayThrow;
+ }
+ }
public override void WriteTo(ITextOutput output)
{
output.Write(OpCode);
@@ -2701,6 +2825,11 @@ namespace ICSharpCode.Decompiler.IL
{
return base.ComputeFlags() | InstructionFlags.MayThrow | InstructionFlags.EndPointUnreachable;
}
+ public override InstructionFlags DirectFlags {
+ get {
+ return base.DirectFlags | InstructionFlags.MayThrow | InstructionFlags.EndPointUnreachable;
+ }
+ }
public override void AcceptVisitor(ILVisitor visitor)
{
visitor.VisitThrow(this);
@@ -2722,6 +2851,11 @@ namespace ICSharpCode.Decompiler.IL
{
return InstructionFlags.MayThrow | InstructionFlags.EndPointUnreachable;
}
+ public override InstructionFlags DirectFlags {
+ get {
+ return InstructionFlags.MayThrow | InstructionFlags.EndPointUnreachable;
+ }
+ }
public override void AcceptVisitor(ILVisitor visitor)
{
visitor.VisitRethrow(this);
@@ -2818,6 +2952,11 @@ namespace ICSharpCode.Decompiler.IL
{
return array.Flags | InstructionFlags.MayThrow;
}
+ public override InstructionFlags DirectFlags {
+ get {
+ return InstructionFlags.MayThrow;
+ }
+ }
public override void WriteTo(ITextOutput output)
{
output.Write(OpCode);
@@ -2907,6 +3046,11 @@ namespace ICSharpCode.Decompiler.IL
{
return array.Flags | Indices.Aggregate(InstructionFlags.None, (f, arg) => f | arg.Flags) | InstructionFlags.MayThrow;
}
+ public override InstructionFlags DirectFlags {
+ get {
+ return InstructionFlags.MayThrow;
+ }
+ }
public override void WriteTo(ITextOutput output)
{
if (IsReadOnly)
@@ -2994,6 +3138,11 @@ namespace ICSharpCode.Decompiler.IL
{
return base.ComputeFlags() | InstructionFlags.MayThrow;
}
+ public override InstructionFlags DirectFlags {
+ get {
+ return base.DirectFlags | InstructionFlags.MayThrow;
+ }
+ }
public override void WriteTo(ITextOutput output)
{
output.Write(OpCode);
diff --git a/ICSharpCode.Decompiler/IL/Instructions.tt b/ICSharpCode.Decompiler/IL/Instructions.tt
index 55c53a356..5310d7579 100644
--- a/ICSharpCode.Decompiler/IL/Instructions.tt
+++ b/ICSharpCode.Decompiler/IL/Instructions.tt
@@ -25,11 +25,11 @@
<#
OpCode[] baseClasses = {
new OpCode("SimpleInstruction", "Instruction without any arguments",
- AbstractBaseClass, CustomArguments(), CustomWriteTo),
+ AbstractBaseClass, CustomArguments(), CustomWriteTo, HasFlag("InstructionFlags.None")),
new OpCode("UnaryInstruction", "Instruction with a single argument",
- AbstractBaseClass, CustomArguments("argument")),
+ AbstractBaseClass, CustomArguments("argument"), HasFlag("InstructionFlags.None")),
new OpCode("BinaryInstruction", "Instruction with two arguments: Left and Right",
- AbstractBaseClass, CustomArguments("left", "right")),
+ AbstractBaseClass, CustomArguments("left", "right"), HasFlag("InstructionFlags.None")),
new OpCode("CallInstruction", "Instruction with a list of arguments.",
AbstractBaseClass, CustomChildren(new []{ new ArgumentInfo("arguments") { IsCollection = true }}),
CustomWriteTo, MayThrow, SideEffect),
@@ -239,6 +239,13 @@ namespace ICSharpCode.Decompiler.IL
return <#=string.Join(" | ", opCode.Flags)#>;
}
<# } #>
+<# if (opCode.GenerateComputeFlags && opCode.Flags.Any(f => f != "base.ComputeFlags()")) { #>
+ public override InstructionFlags DirectFlags {
+ get {
+ return <#=opCode.DirectFlags.Count > 0 ? string.Join(" | ", opCode.DirectFlags) : "InstructionFlags.None"#>;
+ }
+ }
+<# } #>
<# if (opCode.GenerateWriteTo) { #>
public override void WriteTo(ITextOutput output)
{<#=Body(opCode.WriteToBody)#>}
@@ -415,8 +422,10 @@ namespace ICSharpCode.Decompiler.IL
public List Members = new List();
public List Flags = new List();
+ public List DirectFlags = new List();
public bool GenerateComputeFlags = true;
+
public bool GenerateAcceptVisitor = true;
public bool GenerateWriteTo = false;
@@ -479,6 +488,7 @@ namespace ICSharpCode.Decompiler.IL
{
return opCode => {
opCode.Flags.Add(name);
+ opCode.DirectFlags.Add(name);
};
}
@@ -510,13 +520,19 @@ namespace ICSharpCode.Decompiler.IL
static Action MayBranch = HasFlag("InstructionFlags.MayBranch");
// UnconditionalBranch trait: the instruction does not produce a result normally; it always branches or throws an exception. Implies VoidResult.
+ // UnconditionalBranch should be paired with either MayBranch or MayThrow (or both).
static Action UnconditionalBranch = VoidResult + HasFlag("InstructionFlags.EndPointUnreachable");
+ // ControlFlow trait: the instruction involves some form of internal control flow
+ // Instructions without this trait must evaluate all arguments left-to-right before having any effect of their own.
+ static Action ControlFlow = HasFlag("InstructionFlags.ControlFlow");
+
static Action BaseClass(string name)
{
return opCode => {
opCode.BaseClass = name;
opCode.Flags.Add("base.ComputeFlags()");
+ opCode.DirectFlags.Add("base.DirectFlags");
};
}
@@ -527,8 +543,7 @@ namespace ICSharpCode.Decompiler.IL
// Unary trait: the instruction has a single argument
static Action Unary = opCode => {
- opCode.BaseClass = "UnaryInstruction";
- opCode.Flags.Add("base.ComputeFlags()");
+ BaseClass("UnaryInstruction")(opCode);
opCode.ConstructorParameters.Add("ILInstruction argument");
opCode.MatchParameters.Add(new MatchParamInfo { TypeName = "ILInstruction", Name = "argument", FieldName = "Argument" });
opCode.BaseConstructorArguments.Add("argument");
@@ -539,8 +554,7 @@ namespace ICSharpCode.Decompiler.IL
// Binary trait: the instruction has two arguments named 'Left' and 'Right'
static Action Binary = opCode => {
- opCode.BaseClass = "BinaryInstruction";
- opCode.Flags.Add("base.ComputeFlags()");
+ BaseClass("BinaryInstruction")(opCode);
opCode.ConstructorParameters.Add("ILInstruction left");
opCode.ConstructorParameters.Add("ILInstruction right");
opCode.BaseConstructorArguments.Add("left");
diff --git a/ICSharpCode.Decompiler/IL/Instructions/Block.cs b/ICSharpCode.Decompiler/IL/Instructions/Block.cs
index fe9e15c4c..aea7482fa 100644
--- a/ICSharpCode.Decompiler/IL/Instructions/Block.cs
+++ b/ICSharpCode.Decompiler/IL/Instructions/Block.cs
@@ -172,5 +172,11 @@ namespace ICSharpCode.Decompiler.IL
flags |= FinalInstruction.Flags;
return flags;
}
+
+ public override InstructionFlags DirectFlags {
+ get {
+ return InstructionFlags.None;
+ }
+ }
}
}
diff --git a/ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs b/ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs
index 0ba0d45e5..feb5852be 100644
--- a/ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs
+++ b/ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs
@@ -149,7 +149,7 @@ namespace ICSharpCode.Decompiler.IL
protected override InstructionFlags ComputeFlags()
{
- InstructionFlags flags = InstructionFlags.None;
+ InstructionFlags flags = InstructionFlags.ControlFlow;
foreach (var block in Blocks) {
flags |= block.Flags;
}
@@ -160,5 +160,48 @@ namespace ICSharpCode.Decompiler.IL
flags &= ~InstructionFlags.EndPointUnreachable;
return flags;
}
+
+ public override InstructionFlags DirectFlags {
+ get {
+ return InstructionFlags.ControlFlow;
+ }
+ }
+
+ ///
+ /// Sort the blocks in reverse post-order over the control flow graph between the blocks.
+ ///
+ public void SortBlocks(bool deleteUnreachableBlocks = false)
+ {
+ // Visit blocks in post-order
+ BitSet visited = new BitSet(Blocks.Count);
+ List postOrder = new List();
+
+ Action visit = null;
+ visit = delegate(Block block) {
+ Debug.Assert(block.Parent == this);
+ if (!visited[block.ChildIndex]) {
+ visited[block.ChildIndex] = true;
+
+ foreach (var branch in block.Descendants.OfType()) {
+ if (branch.TargetBlock.Parent == this) {
+ visit(branch.TargetBlock);
+ }
+ }
+
+ postOrder.Add(block);
+ }
+ };
+ visit(EntryPoint);
+
+ postOrder.Reverse();
+ if (!deleteUnreachableBlocks) {
+ for (int i = 0; i < Blocks.Count; i++) {
+ if (!visited[i])
+ postOrder.Add(Blocks[i]);
+ }
+ }
+ Debug.Assert(postOrder[0] == Blocks[0]);
+ Blocks.ReplaceList(postOrder);
+ }
}
}
diff --git a/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs b/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs
index f76333732..7a1784c90 100644
--- a/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs
+++ b/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs
@@ -59,7 +59,13 @@ namespace ICSharpCode.Decompiler.IL
{
// Creating a lambda may throw OutOfMemoryException
// We intentionally don't propagate any flags from the lambda body!
- return InstructionFlags.MayThrow;
+ return InstructionFlags.MayThrow | InstructionFlags.ControlFlow;
+ }
+
+ public override InstructionFlags DirectFlags {
+ get {
+ return InstructionFlags.MayThrow | InstructionFlags.ControlFlow;
+ }
}
}
}
diff --git a/ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs
index d3a7b991d..191feaffe 100644
--- a/ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs
+++ b/ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs
@@ -71,6 +71,7 @@ namespace ICSharpCode.Decompiler.IL
Debug.Assert(child.IsConnected == this.IsConnected);
child.CheckInvariant(phase);
}
+ Debug.Assert((this.DirectFlags & ~this.Flags) == 0, "All DirectFlags must also appear in this.Flags");
}
///
@@ -114,7 +115,7 @@ namespace ICSharpCode.Decompiler.IL
/// until some change to the ILAst invalidates the cache.
///
///
- /// Flag cache invalidation makes of the Parent property,
+ /// Flag cache invalidation makes use of the Parent property,
/// so it is possible for this property to return a stale value
/// if the instruction contains "stale positions" (see remarks on Parent property).
///
@@ -143,6 +144,11 @@ namespace ICSharpCode.Decompiler.IL
protected abstract InstructionFlags ComputeFlags();
+ ///
+ /// Gets the flags for this instruction only, without considering the child instructions.
+ ///
+ public abstract InstructionFlags DirectFlags { get; }
+
///
/// Gets the ILRange for this instruction alone, ignoring the operands.
///
diff --git a/ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs
index ccc723c51..2b9d636cd 100644
--- a/ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs
+++ b/ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs
@@ -52,9 +52,15 @@ namespace ICSharpCode.Decompiler.IL
}
}
+ public override InstructionFlags DirectFlags {
+ get {
+ return InstructionFlags.ControlFlow;
+ }
+ }
+
protected override InstructionFlags ComputeFlags()
{
- return condition.Flags | CombineFlags(trueInst.Flags, falseInst.Flags);
+ return InstructionFlags.ControlFlow | condition.Flags | CombineFlags(trueInst.Flags, falseInst.Flags);
}
internal static InstructionFlags CombineFlags(InstructionFlags trueFlags, InstructionFlags falseFlags)
diff --git a/ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs b/ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs
index c8f8477da..b1d7f647e 100644
--- a/ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs
+++ b/ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs
@@ -100,5 +100,16 @@ namespace ICSharpCode.Decompiler.IL
var inst = this as Leave;
return inst != null && inst.TargetContainer == targetContainer;
}
+
+ public bool MatchTryCatchHandler(out ILVariable variable)
+ {
+ var inst = this as TryCatchHandler;
+ if (inst != null) {
+ variable = inst.Variable;
+ return true;
+ }
+ variable = null;
+ return false;
+ }
}
}
diff --git a/ICSharpCode.Decompiler/IL/Instructions/Return.cs b/ICSharpCode.Decompiler/IL/Instructions/Return.cs
index d683dcfb9..053293692 100644
--- a/ICSharpCode.Decompiler/IL/Instructions/Return.cs
+++ b/ICSharpCode.Decompiler/IL/Instructions/Return.cs
@@ -71,6 +71,12 @@ namespace ICSharpCode.Decompiler.IL
return flags;
}
+ public override InstructionFlags DirectFlags {
+ get {
+ return InstructionFlags.MayBranch | InstructionFlags.EndPointUnreachable;
+ }
+ }
+
public override void WriteTo(ITextOutput output)
{
output.Write(OpCode);
diff --git a/ICSharpCode.Decompiler/IL/Instructions/SimpleInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/SimpleInstruction.cs
index f8a2e323d..528bbc6a8 100644
--- a/ICSharpCode.Decompiler/IL/Instructions/SimpleInstruction.cs
+++ b/ICSharpCode.Decompiler/IL/Instructions/SimpleInstruction.cs
@@ -33,11 +33,7 @@ namespace ICSharpCode.Decompiler.IL
public override void WriteTo(ITextOutput output)
{
output.Write(OpCode);
- }
-
- protected override InstructionFlags ComputeFlags()
- {
- return InstructionFlags.None;
+ // the non-custom WriteTo would add useless parentheses
}
}
}
diff --git a/ICSharpCode.Decompiler/IL/Instructions/SwitchInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/SwitchInstruction.cs
index f5052eb6f..d3d604961 100644
--- a/ICSharpCode.Decompiler/IL/Instructions/SwitchInstruction.cs
+++ b/ICSharpCode.Decompiler/IL/Instructions/SwitchInstruction.cs
@@ -49,13 +49,19 @@ namespace ICSharpCode.Decompiler.IL
protected override InstructionFlags ComputeFlags()
{
- var sectionFlags = InstructionFlags.None;
+ var sectionFlags = InstructionFlags.ControlFlow;
foreach (var section in Sections) {
sectionFlags = IfInstruction.CombineFlags(sectionFlags, section.Flags);
}
return value.Flags | sectionFlags;
}
+ public override InstructionFlags DirectFlags {
+ get {
+ return InstructionFlags.ControlFlow;
+ }
+ }
+
public override void WriteTo(ITextOutput output)
{
output.Write("switch (");
@@ -122,6 +128,12 @@ namespace ICSharpCode.Decompiler.IL
return body.Flags;
}
+ public override InstructionFlags DirectFlags {
+ get {
+ return InstructionFlags.None;
+ }
+ }
+
public override void WriteTo(ITextOutput output)
{
output.Write("case ");
diff --git a/ICSharpCode.Decompiler/IL/Instructions/TryInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/TryInstruction.cs
index 1e8b0be84..1b53e108e 100644
--- a/ICSharpCode.Decompiler/IL/Instructions/TryInstruction.cs
+++ b/ICSharpCode.Decompiler/IL/Instructions/TryInstruction.cs
@@ -85,7 +85,13 @@ namespace ICSharpCode.Decompiler.IL
var flags = TryBlock.Flags;
foreach (var handler in Handlers)
flags = IfInstruction.CombineFlags(flags, handler.Flags);
- return flags;
+ return flags | InstructionFlags.ControlFlow;
+ }
+
+ public override InstructionFlags DirectFlags {
+ get {
+ return InstructionFlags.ControlFlow;
+ }
}
protected override int GetChildCount()
@@ -144,7 +150,14 @@ namespace ICSharpCode.Decompiler.IL
protected override InstructionFlags ComputeFlags()
{
- return filter.Flags | body.Flags;
+ return filter.Flags | body.Flags | InstructionFlags.ControlFlow;
+ }
+
+ public override InstructionFlags DirectFlags {
+ get {
+ // the body is not evaluated if the filter returns 0
+ return InstructionFlags.ControlFlow;
+ }
}
public override void WriteTo(ITextOutput output)
@@ -217,7 +230,13 @@ namespace ICSharpCode.Decompiler.IL
protected override InstructionFlags ComputeFlags()
{
// if the endpoint of either the try or the finally is unreachable, the endpoint of the try-finally will be unreachable
- return TryBlock.Flags | finallyBlock.Flags;
+ return TryBlock.Flags | finallyBlock.Flags | InstructionFlags.ControlFlow;
+ }
+
+ public override InstructionFlags DirectFlags {
+ get {
+ return InstructionFlags.ControlFlow;
+ }
}
protected override int GetChildCount()
@@ -304,7 +323,13 @@ namespace ICSharpCode.Decompiler.IL
protected override InstructionFlags ComputeFlags()
{
// The endpoint of the try-fault is unreachable iff the try endpoint is unreachable
- return TryBlock.Flags | (faultBlock.Flags & ~InstructionFlags.EndPointUnreachable);
+ return TryBlock.Flags | (faultBlock.Flags & ~InstructionFlags.EndPointUnreachable) | InstructionFlags.ControlFlow;
+ }
+
+ public override InstructionFlags DirectFlags {
+ get {
+ return InstructionFlags.ControlFlow;
+ }
}
protected override int GetChildCount()
diff --git a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs
index 53170c5d1..f4692d8fb 100644
--- a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs
+++ b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs
@@ -35,6 +35,7 @@ namespace ICSharpCode.Decompiler.IL
foreach (var block in function.Descendants.OfType()) {
InlineAllInBlock(block);
}
+ function.Variables.RemoveDead();
}
public bool InlineAllInBlock(Block block)
diff --git a/ICSharpCode.Decompiler/IL/Transforms/SplitVariables.cs b/ICSharpCode.Decompiler/IL/Transforms/SplitVariables.cs
new file mode 100644
index 000000000..c7c787e2f
--- /dev/null
+++ b/ICSharpCode.Decompiler/IL/Transforms/SplitVariables.cs
@@ -0,0 +1,38 @@
+// Copyright (c) 2016 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.Linq;
+using ICSharpCode.Decompiler.FlowAnalysis;
+
+namespace ICSharpCode.Decompiler.IL.Transforms
+{
+ ///
+ /// Split variables where possible.
+ ///
+ public class SplitVariables : IILTransform
+ {
+ public void Run(ILFunction function, ILTransformContext context)
+ {
+ foreach (var container in function.Descendants.OfType()) {
+ container.SortBlocks();
+ }
+ var rd = new ReachingDefinitions(function, v => v.Kind != VariableKind.StackSlot);
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler/Util/BitSet.cs b/ICSharpCode.Decompiler/Util/BitSet.cs
new file mode 100644
index 000000000..196f38c14
--- /dev/null
+++ b/ICSharpCode.Decompiler/Util/BitSet.cs
@@ -0,0 +1,127 @@
+// Copyright (c) 2016 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;
+using System.Collections.Immutable;
+using System.Diagnostics;
+using System.Text;
+
+namespace ICSharpCode.Decompiler
+{
+ ///
+ /// Improved version of BitArray
+ ///
+ public class BitSet
+ {
+ readonly BitArray bits;
+
+ ///
+ /// Creates a new bitset, where initially all bits are zero.
+ ///
+ public BitSet(int capacity)
+ {
+ this.bits = new BitArray(capacity);
+ }
+
+ private BitSet(BitArray bits)
+ {
+ this.bits = bits;
+ }
+
+ public BitSet Clone()
+ {
+ return new BitSet((BitArray)bits.Clone());
+ }
+
+ public bool this[int index] {
+ get {
+ return bits[index];
+ }
+ set {
+ bits[index] = value;
+ }
+ }
+
+ ///
+ /// Gets whether this set is a subset of other, or equal.
+ ///
+ public bool IsSubsetOf(BitSet other)
+ {
+ for (int i = 0; i < bits.Length; i++) {
+ if (bits[i] && !other[i])
+ return false;
+ }
+ return true;
+ }
+
+ public bool IsSupersetOf(BitSet other)
+ {
+ return other.IsSubsetOf(this);
+ }
+
+ public void UnionWith(BitSet other)
+ {
+ bits.Or(other.bits);
+ }
+
+ public void Clear(int index)
+ {
+ bits[index] = false;
+ }
+
+ public void Clear(int startIndex, int endIndex)
+ {
+ for (int i = startIndex; i < endIndex; i++) {
+ bits[i] = false;
+ }
+ }
+
+ public void Set(int index)
+ {
+ bits[index] = true;
+ }
+
+ public void ReplaceWith(BitSet incoming)
+ {
+ Debug.Assert(bits.Length == incoming.bits.Length);
+ for (int i = 0; i < bits.Length; i++) {
+ bits[i] = incoming.bits[i];
+ }
+ }
+
+ public override string ToString()
+ {
+ StringBuilder b = new StringBuilder();
+ b.Append('{');
+ for (int i = 0; i < bits.Length; i++) {
+ if (bits[i]) {
+ if (b.Length > 1)
+ b.Append(", ");
+ if (b.Length > 500) {
+ b.Append("...");
+ break;
+ }
+ b.Append(i);
+ }
+ }
+ b.Append('}');
+ return b.ToString();
+ }
+ }
+}