Browse Source

Add reaching definitions analysis.

pull/728/head
Daniel Grunwald 9 years ago
parent
commit
b55775e2cc
  1. 1
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  2. 521
      ICSharpCode.Decompiler/FlowAnalysis/ReachingDefinitions.cs
  3. 2
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  4. 11
      ICSharpCode.Decompiler/IL/InstructionFlags.cs
  5. 153
      ICSharpCode.Decompiler/IL/Instructions.cs
  6. 28
      ICSharpCode.Decompiler/IL/Instructions.tt
  7. 6
      ICSharpCode.Decompiler/IL/Instructions/Block.cs
  8. 45
      ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs
  9. 8
      ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs
  10. 8
      ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs
  11. 8
      ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs
  12. 11
      ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs
  13. 6
      ICSharpCode.Decompiler/IL/Instructions/Return.cs
  14. 6
      ICSharpCode.Decompiler/IL/Instructions/SimpleInstruction.cs
  15. 14
      ICSharpCode.Decompiler/IL/Instructions/SwitchInstruction.cs
  16. 33
      ICSharpCode.Decompiler/IL/Instructions/TryInstruction.cs
  17. 1
      ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs
  18. 38
      ICSharpCode.Decompiler/IL/Transforms/SplitVariables.cs
  19. 127
      ICSharpCode.Decompiler/Util/BitSet.cs

1
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -50,6 +50,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -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(),

521
ICSharpCode.Decompiler/FlowAnalysis/ReachingDefinitions.cs

@ -17,6 +17,12 @@ @@ -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 @@ -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.
/// </summary>
/// <remarks>
/// Possible "definitions" that store to a variable are:
/// * <c>StLoc</c>
/// * <c>TryCatchHandler</c> (for the exception variable)
/// * <c>ReachingDefinitions.UninitializedVariable</c> for uninitialized variables.
/// Note that we do not keep track of <c>LdLoca</c>/references/pointers.
/// The analysis will likely be wrong/incomplete for variables with <c>AddressCount != 0</c>.
/// </remarks>
public class ReachingDefinitions
{
#region Documentation + member fields
/// <summary>
/// A special Nop instruction that gets used as a fake store if the variable
/// is possibly uninitialized.
/// </summary>
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<ILVariable, ImmutableHashSet<ILInstruction>>`.
// 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`).
/// <summary>
/// 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
/// </summary>
const int ReachableBit = 0;
/// <summary>
/// Because bit number 0 is the ReachableBit, we start counting store indices at 1.
/// </summary>
const int FirstStoreIndex = 1;
/// <summary>
/// The function being analyzed.
/// </summary>
readonly ILVariableScope scope;
/// <summary>
/// All stores for all variables in the scope.
///
/// <c>state[storeIndex]</c> is true iff <c>allStores[storeIndex]</c> is a reaching definition.
/// Invariant: <c>state.Length == allStores.Length</c>.
/// </summary>
readonly ILInstruction[] allStores;
/// <summary>
/// Maps instructions appearing in <c>allStores</c> to their index.
///
/// Invariant: <c>allStores[storeIndexMap[inst]] == inst</c>
///
/// Does not contain <c>UninitializedVariable</c> (as that special instruction has multiple store indices, one per variable)
/// </summary>
readonly Dictionary<ILInstruction, int> storeIndexMap = new Dictionary<ILInstruction, int>();
/// <summary>
/// For all variables <c>v</c>: <c>allStores[firstStoreIndexForVariable[v.IndexInScope]]</c> is the <c>UninitializedVariable</c> entry for <c>v</c>.
/// The next few stores (up to <c>firstStoreIndexForVariable[v.IndexInScope + 1]</c>, exclusive) are the full list of stores for <c>v</c>.
/// </summary>
/// <remarks>
/// Invariant: <c>firstStoreIndexForVariable[scope.Variables.Count] == allStores.Length</c>
/// </remarks>
readonly int[] firstStoreIndexForVariable;
/// <summary>
/// <c>activeVariable[v.IndexInScope]</c> is true iff RD analysis is enabled for the variable.
/// </summary>
readonly BitSet activeVariables;
/// <summary>
/// Holds the state for incoming branches.
/// </summary>
/// <remarks>
/// Only used for blocks in block containers; not for inline blocks.
/// </remarks>
readonly Dictionary<Block, BitSet> stateOnBranch = new Dictionary<Block, BitSet>();
/// <summary>
/// Holds the state at the block container end-point. (=state for incoming 'leave' instructions)
/// </summary>
readonly Dictionary<BlockContainer, BitSet> stateOnLeave = new Dictionary<BlockContainer, BitSet>();
#endregion
#region Constructor
/// <summary>
/// Run reaching definitions analysis for the specified variable scope.
/// </summary>
public ReachingDefinitions(ILVariableScope scope, Predicate<ILVariable> pred)
: this(scope, GetActiveVariableBitSet(scope, pred))
{
}
static BitSet GetActiveVariableBitSet(ILVariableScope scope, Predicate<ILVariable> 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;
}
/// <summary>
/// Run reaching definitions analysis for the specified variable scope.
/// </summary>
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);
}
/// <summary>
/// Fill <c>allStores</c> and <c>storeIndexMap</c>.
/// </summary>
static List<ILInstruction>[] FindAllStoresByVariable(ILVariableScope scope, BitSet activeVariables)
{
// For each variable, find the list of ILInstructions storing to that variable
List<ILInstruction>[] storesByVar = new List<ILInstruction>[scope.Variables.Count];
for (int vi = 0; vi < storesByVar.Length; vi++) {
if (activeVariables[vi])
storesByVar[vi] = new List<ILInstruction> { 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
/// <summary>
/// Create the initial state (reachable + all variables uninitialized).
/// </summary>
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<BlockContainer>()) {
foreach (var block in container.Blocks) {
stateOnBranch.Add(block, CreateUnreachableState());
}
stateOnLeave.Add(container, CreateUnreachableState());
}
}
/// <summary>
/// Merge <c>incomingState</c> into <c>state</c>.
/// </summary>
/// <returns>
/// Returns true if <c>state</c> was modified.
/// </returns>
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
/// <summary>
/// For each block container, stores the set of blocks (via Block.ChildIndex) that had their incoming state
/// changed and were not processed yet.
/// </summary>
readonly Dictionary<BlockContainer, SortedSet<int>> workList;
static Dictionary<BlockContainer, SortedSet<int>> CreateWorkLists(ILVariableScope scope)
{
var worklists = new Dictionary<BlockContainer, SortedSet<int>>();
foreach (var container in scope.Descendants.OfType<BlockContainer>()) {
worklists.Add(container, new SortedSet<int>());
}
return worklists;
}
/// <summary>
/// The work list keeps track of which blocks had their incoming state updated,
/// but did not run yet.
/// </summary>
void AddToWorkList(Block block)
{
BlockContainer container = (BlockContainer)block.Parent;
workList[container].Add(block.ChildIndex);
}
#endregion
/// <summary>
/// Visitor that traverses the ILInstruction tree.
/// </summary>
class RDVisitor : ILVisitor
{
readonly ReachingDefinitions rd;
internal RDVisitor(ReachingDefinitions rd)
{
this.rd = rd;
this.state = rd.CreateInitialState();
this.stateOnException = rd.CreateUnreachableState();
}
/// <summary>
/// Combines state of all possible exceptional control flow paths in the current try block.
/// </summary>
BitSet stateOnException;
/// <summary>
/// Current state.
/// Gets mutated as the visitor traverses the ILAst.
/// </summary>
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();
}
}
/// <summary>
/// Handle control flow when `throwInst` throws an exception.
/// </summary>
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<int> 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();
}
/// <summary>
/// Visits the TryBlock.
///
/// Returns a new BitSet representing the state of exceptional control flow transfer
/// out of the try block.
/// </summary>
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;
}
}
}
}

2
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -123,6 +123,7 @@ @@ -123,6 +123,7 @@
<Compile Include="IL\Transforms\ILInlining.cs" />
<Compile Include="IL\Transforms\ExpressionTransforms.cs" />
<Compile Include="IL\Transforms\InlineCompilerGeneratedVariables.cs" />
<Compile Include="IL\Transforms\SplitVariables.cs" />
<Compile Include="IL\Transforms\TransformArrayInitializers.cs" />
<Compile Include="IL\Transforms\TransformingVisitor.cs" />
<Compile Include="CecilExtensions.cs" />
@ -155,6 +156,7 @@ @@ -155,6 +156,7 @@
<Compile Include="TypeSystem\ReferenceResolvingException.cs" />
<Compile Include="TypeSystem\TypesHierarchyHelpers.cs" />
<Compile Include="Util\CollectionExtensions.cs" />
<Compile Include="Util\BitSet.cs" />
<Compile Include="Util\Interval.cs" />
<Compile Include="Util\UnionFind.cs" />
<None Include="IL\ILOpCodes.tt">

11
ICSharpCode.Decompiler/IL/InstructionFlags.cs

@ -66,5 +66,16 @@ namespace ICSharpCode.Decompiler.IL @@ -66,5 +66,16 @@ namespace ICSharpCode.Decompiler.IL
/// (unless the instruction represents an infinite loop).
/// </remarks>
EndPointUnreachable = 0x400,
/// <summary>
/// The instruction contains some kind of internal control flow.
/// </summary>
/// <remarks>
/// 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).
/// </remarks>
ControlFlow = 0x800,
}
}

153
ICSharpCode.Decompiler/IL/Instructions.cs

@ -219,6 +219,15 @@ namespace ICSharpCode.Decompiler.IL @@ -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;
}
}
}
/// <summary>Instruction with a single argument</summary>
@ -277,7 +286,12 @@ namespace ICSharpCode.Decompiler.IL @@ -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 @@ -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 @@ -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;
}
}
}
/// <summary>No operation. Takes 0 arguments and returns void.</summary>
@ -609,6 +633,11 @@ namespace ICSharpCode.Decompiler.IL @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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);

28
ICSharpCode.Decompiler/IL/Instructions.tt

@ -25,11 +25,11 @@ @@ -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 @@ -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 @@ -415,8 +422,10 @@ namespace ICSharpCode.Decompiler.IL
public List<string> Members = new List<string>();
public List<string> Flags = new List<string>();
public List<string> DirectFlags = new List<string>();
public bool GenerateComputeFlags = true;
public bool GenerateAcceptVisitor = true;
public bool GenerateWriteTo = false;
@ -479,6 +488,7 @@ namespace ICSharpCode.Decompiler.IL @@ -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 @@ -510,13 +520,19 @@ namespace ICSharpCode.Decompiler.IL
static Action<OpCode> 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<OpCode> 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<OpCode> ControlFlow = HasFlag("InstructionFlags.ControlFlow");
static Action<OpCode> 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 @@ -527,8 +543,7 @@ namespace ICSharpCode.Decompiler.IL
// Unary trait: the instruction has a single argument
static Action<OpCode> 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 @@ -539,8 +554,7 @@ namespace ICSharpCode.Decompiler.IL
// Binary trait: the instruction has two arguments named 'Left' and 'Right'
static Action<OpCode> 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");

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

@ -172,5 +172,11 @@ namespace ICSharpCode.Decompiler.IL @@ -172,5 +172,11 @@ namespace ICSharpCode.Decompiler.IL
flags |= FinalInstruction.Flags;
return flags;
}
public override InstructionFlags DirectFlags {
get {
return InstructionFlags.None;
}
}
}
}

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

@ -149,7 +149,7 @@ namespace ICSharpCode.Decompiler.IL @@ -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 @@ -160,5 +160,48 @@ namespace ICSharpCode.Decompiler.IL
flags &= ~InstructionFlags.EndPointUnreachable;
return flags;
}
public override InstructionFlags DirectFlags {
get {
return InstructionFlags.ControlFlow;
}
}
/// <summary>
/// Sort the blocks in reverse post-order over the control flow graph between the blocks.
/// </summary>
public void SortBlocks(bool deleteUnreachableBlocks = false)
{
// Visit blocks in post-order
BitSet visited = new BitSet(Blocks.Count);
List<Block> postOrder = new List<Block>();
Action<Block> 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<Branch>()) {
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);
}
}
}

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

@ -59,7 +59,13 @@ namespace ICSharpCode.Decompiler.IL @@ -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;
}
}
}
}

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

@ -71,6 +71,7 @@ namespace ICSharpCode.Decompiler.IL @@ -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");
}
/// <summary>
@ -114,7 +115,7 @@ namespace ICSharpCode.Decompiler.IL @@ -114,7 +115,7 @@ namespace ICSharpCode.Decompiler.IL
/// until some change to the ILAst invalidates the cache.
/// </summary>
/// <remarks>
/// Flag cache invalidation makes of the <c>Parent</c> property,
/// Flag cache invalidation makes use of the <c>Parent</c> property,
/// so it is possible for this property to return a stale value
/// if the instruction contains "stale positions" (see remarks on Parent property).
/// </remarks>
@ -143,6 +144,11 @@ namespace ICSharpCode.Decompiler.IL @@ -143,6 +144,11 @@ namespace ICSharpCode.Decompiler.IL
protected abstract InstructionFlags ComputeFlags();
/// <summary>
/// Gets the flags for this instruction only, without considering the child instructions.
/// </summary>
public abstract InstructionFlags DirectFlags { get; }
/// <summary>
/// Gets the ILRange for this instruction alone, ignoring the operands.
/// </summary>

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

@ -52,9 +52,15 @@ namespace ICSharpCode.Decompiler.IL @@ -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)

11
ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs

@ -100,5 +100,16 @@ namespace ICSharpCode.Decompiler.IL @@ -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;
}
}
}

6
ICSharpCode.Decompiler/IL/Instructions/Return.cs

@ -71,6 +71,12 @@ namespace ICSharpCode.Decompiler.IL @@ -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);

6
ICSharpCode.Decompiler/IL/Instructions/SimpleInstruction.cs

@ -33,11 +33,7 @@ namespace ICSharpCode.Decompiler.IL @@ -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
}
}
}

14
ICSharpCode.Decompiler/IL/Instructions/SwitchInstruction.cs

@ -49,13 +49,19 @@ namespace ICSharpCode.Decompiler.IL @@ -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 @@ -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 ");

33
ICSharpCode.Decompiler/IL/Instructions/TryInstruction.cs

@ -85,7 +85,13 @@ namespace ICSharpCode.Decompiler.IL @@ -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 @@ -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 @@ -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 @@ -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()

1
ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs

@ -35,6 +35,7 @@ namespace ICSharpCode.Decompiler.IL @@ -35,6 +35,7 @@ namespace ICSharpCode.Decompiler.IL
foreach (var block in function.Descendants.OfType<Block>()) {
InlineAllInBlock(block);
}
function.Variables.RemoveDead();
}
public bool InlineAllInBlock(Block block)

38
ICSharpCode.Decompiler/IL/Transforms/SplitVariables.cs

@ -0,0 +1,38 @@ @@ -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
{
/// <summary>
/// Split variables where possible.
/// </summary>
public class SplitVariables : IILTransform
{
public void Run(ILFunction function, ILTransformContext context)
{
foreach (var container in function.Descendants.OfType<BlockContainer>()) {
container.SortBlocks();
}
var rd = new ReachingDefinitions(function, v => v.Kind != VariableKind.StackSlot);
}
}
}

127
ICSharpCode.Decompiler/Util/BitSet.cs

@ -0,0 +1,127 @@ @@ -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
{
/// <summary>
/// Improved version of BitArray
/// </summary>
public class BitSet
{
readonly BitArray bits;
/// <summary>
/// Creates a new bitset, where initially all bits are zero.
/// </summary>
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;
}
}
/// <summary>
/// Gets whether this set is a subset of other, or equal.
/// </summary>
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();
}
}
}
Loading…
Cancel
Save