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
new ILInlining(), // temporary pass, just to make the ILAst easier to read while debugging loop detection new ILInlining(), // temporary pass, just to make the ILAst easier to read while debugging loop detection
new LoopDetection(), new LoopDetection(),
new IntroduceExitPoints(), new IntroduceExitPoints(),
new SplitVariables(),
new ConditionDetection(), new ConditionDetection(),
new ILInlining(), new ILInlining(),
new CopyPropagation(), new CopyPropagation(),

521
ICSharpCode.Decompiler/FlowAnalysis/ReachingDefinitions.cs

@ -17,6 +17,12 @@
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
using System; 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 namespace ICSharpCode.Decompiler.FlowAnalysis
{ {
@ -24,8 +30,523 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
/// Implements the "reaching definitions" analysis. /// Implements the "reaching definitions" analysis.
/// ///
/// https://en.wikipedia.org/wiki/Reaching_definition /// https://en.wikipedia.org/wiki/Reaching_definition
///
/// By "definitions", we mean stores to local variables.
/// </summary> /// </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 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 @@
<Compile Include="IL\Transforms\ILInlining.cs" /> <Compile Include="IL\Transforms\ILInlining.cs" />
<Compile Include="IL\Transforms\ExpressionTransforms.cs" /> <Compile Include="IL\Transforms\ExpressionTransforms.cs" />
<Compile Include="IL\Transforms\InlineCompilerGeneratedVariables.cs" /> <Compile Include="IL\Transforms\InlineCompilerGeneratedVariables.cs" />
<Compile Include="IL\Transforms\SplitVariables.cs" />
<Compile Include="IL\Transforms\TransformArrayInitializers.cs" /> <Compile Include="IL\Transforms\TransformArrayInitializers.cs" />
<Compile Include="IL\Transforms\TransformingVisitor.cs" /> <Compile Include="IL\Transforms\TransformingVisitor.cs" />
<Compile Include="CecilExtensions.cs" /> <Compile Include="CecilExtensions.cs" />
@ -155,6 +156,7 @@
<Compile Include="TypeSystem\ReferenceResolvingException.cs" /> <Compile Include="TypeSystem\ReferenceResolvingException.cs" />
<Compile Include="TypeSystem\TypesHierarchyHelpers.cs" /> <Compile Include="TypeSystem\TypesHierarchyHelpers.cs" />
<Compile Include="Util\CollectionExtensions.cs" /> <Compile Include="Util\CollectionExtensions.cs" />
<Compile Include="Util\BitSet.cs" />
<Compile Include="Util\Interval.cs" /> <Compile Include="Util\Interval.cs" />
<Compile Include="Util\UnionFind.cs" /> <Compile Include="Util\UnionFind.cs" />
<None Include="IL\ILOpCodes.tt"> <None Include="IL\ILOpCodes.tt">

11
ICSharpCode.Decompiler/IL/InstructionFlags.cs

@ -66,5 +66,16 @@ namespace ICSharpCode.Decompiler.IL
/// (unless the instruction represents an infinite loop). /// (unless the instruction represents an infinite loop).
/// </remarks> /// </remarks>
EndPointUnreachable = 0x400, 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
var clone = (SimpleInstruction)ShallowClone(); var clone = (SimpleInstruction)ShallowClone();
return clone; return clone;
} }
protected override InstructionFlags ComputeFlags()
{
return InstructionFlags.None;
}
public override InstructionFlags DirectFlags {
get {
return InstructionFlags.None;
}
}
} }
/// <summary>Instruction with a single argument</summary> /// <summary>Instruction with a single argument</summary>
@ -277,7 +286,12 @@ namespace ICSharpCode.Decompiler.IL
} }
protected override InstructionFlags ComputeFlags() 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) public override void WriteTo(ITextOutput output)
{ {
@ -362,7 +376,12 @@ namespace ICSharpCode.Decompiler.IL
} }
protected override InstructionFlags ComputeFlags() 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) 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; 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> /// <summary>No operation. Takes 0 arguments and returns void.</summary>
@ -609,6 +633,11 @@ namespace ICSharpCode.Decompiler.IL
{ {
return base.ComputeFlags() | InstructionFlags.MayThrow; return base.ComputeFlags() | InstructionFlags.MayThrow;
} }
public override InstructionFlags DirectFlags {
get {
return base.DirectFlags | InstructionFlags.MayThrow;
}
}
public override void AcceptVisitor(ILVisitor visitor) public override void AcceptVisitor(ILVisitor visitor)
{ {
visitor.VisitDiv(this); visitor.VisitDiv(this);
@ -630,6 +659,11 @@ namespace ICSharpCode.Decompiler.IL
{ {
return base.ComputeFlags() | InstructionFlags.MayThrow; return base.ComputeFlags() | InstructionFlags.MayThrow;
} }
public override InstructionFlags DirectFlags {
get {
return base.DirectFlags | InstructionFlags.MayThrow;
}
}
public override void AcceptVisitor(ILVisitor visitor) public override void AcceptVisitor(ILVisitor visitor)
{ {
visitor.VisitRem(this); visitor.VisitRem(this);
@ -1062,6 +1096,11 @@ namespace ICSharpCode.Decompiler.IL
{ {
return InstructionFlags.SideEffect; return InstructionFlags.SideEffect;
} }
public override InstructionFlags DirectFlags {
get {
return InstructionFlags.SideEffect;
}
}
public override void AcceptVisitor(ILVisitor visitor) public override void AcceptVisitor(ILVisitor visitor)
{ {
visitor.VisitDebugBreak(this); visitor.VisitDebugBreak(this);
@ -1202,6 +1241,11 @@ namespace ICSharpCode.Decompiler.IL
{ {
return base.ComputeFlags() | InstructionFlags.MayThrow; return base.ComputeFlags() | InstructionFlags.MayThrow;
} }
public override InstructionFlags DirectFlags {
get {
return base.DirectFlags | InstructionFlags.MayThrow;
}
}
public override void AcceptVisitor(ILVisitor visitor) public override void AcceptVisitor(ILVisitor visitor)
{ {
visitor.VisitCkfinite(this); visitor.VisitCkfinite(this);
@ -1346,6 +1390,11 @@ namespace ICSharpCode.Decompiler.IL
{ {
return value.Flags; return value.Flags;
} }
public override InstructionFlags DirectFlags {
get {
return InstructionFlags.None;
}
}
public override void WriteTo(ITextOutput output) public override void WriteTo(ITextOutput output)
{ {
output.Write(OpCode); output.Write(OpCode);
@ -1424,6 +1473,11 @@ namespace ICSharpCode.Decompiler.IL
{ {
return value.Flags; return value.Flags;
} }
public override InstructionFlags DirectFlags {
get {
return InstructionFlags.None;
}
}
public override void WriteTo(ITextOutput output) public override void WriteTo(ITextOutput output)
{ {
output.Write(OpCode); output.Write(OpCode);
@ -1625,6 +1679,11 @@ namespace ICSharpCode.Decompiler.IL
{ {
return base.ComputeFlags() | InstructionFlags.MayThrow; return base.ComputeFlags() | InstructionFlags.MayThrow;
} }
public override InstructionFlags DirectFlags {
get {
return base.DirectFlags | InstructionFlags.MayThrow;
}
}
public override void WriteTo(ITextOutput output) public override void WriteTo(ITextOutput output)
{ {
output.Write(OpCode); output.Write(OpCode);
@ -1709,6 +1768,11 @@ namespace ICSharpCode.Decompiler.IL
{ {
return base.ComputeFlags() | InstructionFlags.MayThrow; return base.ComputeFlags() | InstructionFlags.MayThrow;
} }
public override InstructionFlags DirectFlags {
get {
return base.DirectFlags | InstructionFlags.MayThrow;
}
}
public override void AcceptVisitor(ILVisitor visitor) public override void AcceptVisitor(ILVisitor visitor)
{ {
visitor.VisitLocAlloc(this); visitor.VisitLocAlloc(this);
@ -1834,6 +1898,11 @@ namespace ICSharpCode.Decompiler.IL
{ {
return target.Flags | InstructionFlags.SideEffect | InstructionFlags.MayThrow; return target.Flags | InstructionFlags.SideEffect | InstructionFlags.MayThrow;
} }
public override InstructionFlags DirectFlags {
get {
return InstructionFlags.SideEffect | InstructionFlags.MayThrow;
}
}
public override void WriteTo(ITextOutput output) public override void WriteTo(ITextOutput output)
{ {
if (IsVolatile) if (IsVolatile)
@ -1920,6 +1989,11 @@ namespace ICSharpCode.Decompiler.IL
{ {
return target.Flags | InstructionFlags.MayThrow; return target.Flags | InstructionFlags.MayThrow;
} }
public override InstructionFlags DirectFlags {
get {
return InstructionFlags.MayThrow;
}
}
public override void WriteTo(ITextOutput output) public override void WriteTo(ITextOutput output)
{ {
output.Write(OpCode); output.Write(OpCode);
@ -2024,6 +2098,11 @@ namespace ICSharpCode.Decompiler.IL
{ {
return target.Flags | value.Flags | InstructionFlags.SideEffect | InstructionFlags.MayThrow; 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) public override void WriteTo(ITextOutput output)
{ {
if (IsVolatile) if (IsVolatile)
@ -2068,6 +2147,11 @@ namespace ICSharpCode.Decompiler.IL
{ {
return InstructionFlags.SideEffect; return InstructionFlags.SideEffect;
} }
public override InstructionFlags DirectFlags {
get {
return InstructionFlags.SideEffect;
}
}
public override void WriteTo(ITextOutput output) public override void WriteTo(ITextOutput output)
{ {
if (IsVolatile) if (IsVolatile)
@ -2182,6 +2266,11 @@ namespace ICSharpCode.Decompiler.IL
{ {
return value.Flags | InstructionFlags.SideEffect; return value.Flags | InstructionFlags.SideEffect;
} }
public override InstructionFlags DirectFlags {
get {
return InstructionFlags.SideEffect;
}
}
public override void WriteTo(ITextOutput output) public override void WriteTo(ITextOutput output)
{ {
if (IsVolatile) if (IsVolatile)
@ -2220,6 +2309,11 @@ namespace ICSharpCode.Decompiler.IL
{ {
return base.ComputeFlags() | InstructionFlags.MayThrow; return base.ComputeFlags() | InstructionFlags.MayThrow;
} }
public override InstructionFlags DirectFlags {
get {
return base.DirectFlags | InstructionFlags.MayThrow;
}
}
public override void WriteTo(ITextOutput output) public override void WriteTo(ITextOutput output)
{ {
output.Write(OpCode); output.Write(OpCode);
@ -2336,6 +2430,11 @@ namespace ICSharpCode.Decompiler.IL
{ {
return target.Flags | InstructionFlags.SideEffect | InstructionFlags.MayThrow; return target.Flags | InstructionFlags.SideEffect | InstructionFlags.MayThrow;
} }
public override InstructionFlags DirectFlags {
get {
return InstructionFlags.SideEffect | InstructionFlags.MayThrow;
}
}
public override void WriteTo(ITextOutput output) public override void WriteTo(ITextOutput output)
{ {
if (IsVolatile) if (IsVolatile)
@ -2444,6 +2543,11 @@ namespace ICSharpCode.Decompiler.IL
{ {
return target.Flags | value.Flags | InstructionFlags.SideEffect | InstructionFlags.MayThrow; 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) public override void WriteTo(ITextOutput output)
{ {
if (IsVolatile) if (IsVolatile)
@ -2484,6 +2588,11 @@ namespace ICSharpCode.Decompiler.IL
{ {
return base.ComputeFlags() | InstructionFlags.SideEffect | InstructionFlags.MayThrow; 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) public override void WriteTo(ITextOutput output)
{ {
output.Write(OpCode); output.Write(OpCode);
@ -2518,6 +2627,11 @@ namespace ICSharpCode.Decompiler.IL
{ {
return base.ComputeFlags() | InstructionFlags.MayThrow; return base.ComputeFlags() | InstructionFlags.MayThrow;
} }
public override InstructionFlags DirectFlags {
get {
return base.DirectFlags | InstructionFlags.MayThrow;
}
}
public override void WriteTo(ITextOutput output) public override void WriteTo(ITextOutput output)
{ {
output.Write(OpCode); output.Write(OpCode);
@ -2552,6 +2666,11 @@ namespace ICSharpCode.Decompiler.IL
{ {
return base.ComputeFlags() | InstructionFlags.SideEffect | InstructionFlags.MayThrow; 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) public override void WriteTo(ITextOutput output)
{ {
output.Write(OpCode); output.Write(OpCode);
@ -2640,6 +2759,11 @@ namespace ICSharpCode.Decompiler.IL
{ {
return Indices.Aggregate(InstructionFlags.None, (f, arg) => f | arg.Flags) | InstructionFlags.MayThrow; 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) public override void WriteTo(ITextOutput output)
{ {
output.Write(OpCode); output.Write(OpCode);
@ -2701,6 +2825,11 @@ namespace ICSharpCode.Decompiler.IL
{ {
return base.ComputeFlags() | InstructionFlags.MayThrow | InstructionFlags.EndPointUnreachable; 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) public override void AcceptVisitor(ILVisitor visitor)
{ {
visitor.VisitThrow(this); visitor.VisitThrow(this);
@ -2722,6 +2851,11 @@ namespace ICSharpCode.Decompiler.IL
{ {
return InstructionFlags.MayThrow | InstructionFlags.EndPointUnreachable; return InstructionFlags.MayThrow | InstructionFlags.EndPointUnreachable;
} }
public override InstructionFlags DirectFlags {
get {
return InstructionFlags.MayThrow | InstructionFlags.EndPointUnreachable;
}
}
public override void AcceptVisitor(ILVisitor visitor) public override void AcceptVisitor(ILVisitor visitor)
{ {
visitor.VisitRethrow(this); visitor.VisitRethrow(this);
@ -2818,6 +2952,11 @@ namespace ICSharpCode.Decompiler.IL
{ {
return array.Flags | InstructionFlags.MayThrow; return array.Flags | InstructionFlags.MayThrow;
} }
public override InstructionFlags DirectFlags {
get {
return InstructionFlags.MayThrow;
}
}
public override void WriteTo(ITextOutput output) public override void WriteTo(ITextOutput output)
{ {
output.Write(OpCode); 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; 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) public override void WriteTo(ITextOutput output)
{ {
if (IsReadOnly) if (IsReadOnly)
@ -2994,6 +3138,11 @@ namespace ICSharpCode.Decompiler.IL
{ {
return base.ComputeFlags() | InstructionFlags.MayThrow; return base.ComputeFlags() | InstructionFlags.MayThrow;
} }
public override InstructionFlags DirectFlags {
get {
return base.DirectFlags | InstructionFlags.MayThrow;
}
}
public override void WriteTo(ITextOutput output) public override void WriteTo(ITextOutput output)
{ {
output.Write(OpCode); output.Write(OpCode);

28
ICSharpCode.Decompiler/IL/Instructions.tt

@ -25,11 +25,11 @@
<# <#
OpCode[] baseClasses = { OpCode[] baseClasses = {
new OpCode("SimpleInstruction", "Instruction without any arguments", new OpCode("SimpleInstruction", "Instruction without any arguments",
AbstractBaseClass, CustomArguments(), CustomWriteTo), AbstractBaseClass, CustomArguments(), CustomWriteTo, HasFlag("InstructionFlags.None")),
new OpCode("UnaryInstruction", "Instruction with a single argument", 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", 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.", new OpCode("CallInstruction", "Instruction with a list of arguments.",
AbstractBaseClass, CustomChildren(new []{ new ArgumentInfo("arguments") { IsCollection = true }}), AbstractBaseClass, CustomChildren(new []{ new ArgumentInfo("arguments") { IsCollection = true }}),
CustomWriteTo, MayThrow, SideEffect), CustomWriteTo, MayThrow, SideEffect),
@ -239,6 +239,13 @@ namespace ICSharpCode.Decompiler.IL
return <#=string.Join(" | ", opCode.Flags)#>; 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) { #> <# if (opCode.GenerateWriteTo) { #>
public override void WriteTo(ITextOutput output) public override void WriteTo(ITextOutput output)
{<#=Body(opCode.WriteToBody)#>} {<#=Body(opCode.WriteToBody)#>}
@ -415,8 +422,10 @@ namespace ICSharpCode.Decompiler.IL
public List<string> Members = new List<string>(); public List<string> Members = new List<string>();
public List<string> Flags = new List<string>(); public List<string> Flags = new List<string>();
public List<string> DirectFlags = new List<string>();
public bool GenerateComputeFlags = true; public bool GenerateComputeFlags = true;
public bool GenerateAcceptVisitor = true; public bool GenerateAcceptVisitor = true;
public bool GenerateWriteTo = false; public bool GenerateWriteTo = false;
@ -479,6 +488,7 @@ namespace ICSharpCode.Decompiler.IL
{ {
return opCode => { return opCode => {
opCode.Flags.Add(name); opCode.Flags.Add(name);
opCode.DirectFlags.Add(name);
}; };
} }
@ -510,13 +520,19 @@ namespace ICSharpCode.Decompiler.IL
static Action<OpCode> MayBranch = HasFlag("InstructionFlags.MayBranch"); 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 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"); 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) static Action<OpCode> BaseClass(string name)
{ {
return opCode => { return opCode => {
opCode.BaseClass = name; opCode.BaseClass = name;
opCode.Flags.Add("base.ComputeFlags()"); 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 // Unary trait: the instruction has a single argument
static Action<OpCode> Unary = opCode => { static Action<OpCode> Unary = opCode => {
opCode.BaseClass = "UnaryInstruction"; BaseClass("UnaryInstruction")(opCode);
opCode.Flags.Add("base.ComputeFlags()");
opCode.ConstructorParameters.Add("ILInstruction argument"); opCode.ConstructorParameters.Add("ILInstruction argument");
opCode.MatchParameters.Add(new MatchParamInfo { TypeName = "ILInstruction", Name = "argument", FieldName = "Argument" }); opCode.MatchParameters.Add(new MatchParamInfo { TypeName = "ILInstruction", Name = "argument", FieldName = "Argument" });
opCode.BaseConstructorArguments.Add("argument"); opCode.BaseConstructorArguments.Add("argument");
@ -539,8 +554,7 @@ namespace ICSharpCode.Decompiler.IL
// Binary trait: the instruction has two arguments named 'Left' and 'Right' // Binary trait: the instruction has two arguments named 'Left' and 'Right'
static Action<OpCode> Binary = opCode => { static Action<OpCode> Binary = opCode => {
opCode.BaseClass = "BinaryInstruction"; BaseClass("BinaryInstruction")(opCode);
opCode.Flags.Add("base.ComputeFlags()");
opCode.ConstructorParameters.Add("ILInstruction left"); opCode.ConstructorParameters.Add("ILInstruction left");
opCode.ConstructorParameters.Add("ILInstruction right"); opCode.ConstructorParameters.Add("ILInstruction right");
opCode.BaseConstructorArguments.Add("left"); opCode.BaseConstructorArguments.Add("left");

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

@ -172,5 +172,11 @@ namespace ICSharpCode.Decompiler.IL
flags |= FinalInstruction.Flags; flags |= FinalInstruction.Flags;
return 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
protected override InstructionFlags ComputeFlags() protected override InstructionFlags ComputeFlags()
{ {
InstructionFlags flags = InstructionFlags.None; InstructionFlags flags = InstructionFlags.ControlFlow;
foreach (var block in Blocks) { foreach (var block in Blocks) {
flags |= block.Flags; flags |= block.Flags;
} }
@ -160,5 +160,48 @@ namespace ICSharpCode.Decompiler.IL
flags &= ~InstructionFlags.EndPointUnreachable; flags &= ~InstructionFlags.EndPointUnreachable;
return flags; 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
{ {
// Creating a lambda may throw OutOfMemoryException // Creating a lambda may throw OutOfMemoryException
// We intentionally don't propagate any flags from the lambda body! // 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
Debug.Assert(child.IsConnected == this.IsConnected); Debug.Assert(child.IsConnected == this.IsConnected);
child.CheckInvariant(phase); child.CheckInvariant(phase);
} }
Debug.Assert((this.DirectFlags & ~this.Flags) == 0, "All DirectFlags must also appear in this.Flags");
} }
/// <summary> /// <summary>
@ -114,7 +115,7 @@ namespace ICSharpCode.Decompiler.IL
/// until some change to the ILAst invalidates the cache. /// until some change to the ILAst invalidates the cache.
/// </summary> /// </summary>
/// <remarks> /// <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 /// so it is possible for this property to return a stale value
/// if the instruction contains "stale positions" (see remarks on Parent property). /// if the instruction contains "stale positions" (see remarks on Parent property).
/// </remarks> /// </remarks>
@ -143,6 +144,11 @@ namespace ICSharpCode.Decompiler.IL
protected abstract InstructionFlags ComputeFlags(); protected abstract InstructionFlags ComputeFlags();
/// <summary>
/// Gets the flags for this instruction only, without considering the child instructions.
/// </summary>
public abstract InstructionFlags DirectFlags { get; }
/// <summary> /// <summary>
/// Gets the ILRange for this instruction alone, ignoring the operands. /// Gets the ILRange for this instruction alone, ignoring the operands.
/// </summary> /// </summary>

8
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() 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) internal static InstructionFlags CombineFlags(InstructionFlags trueFlags, InstructionFlags falseFlags)

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

@ -100,5 +100,16 @@ namespace ICSharpCode.Decompiler.IL
var inst = this as Leave; var inst = this as Leave;
return inst != null && inst.TargetContainer == targetContainer; 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
return flags; return flags;
} }
public override InstructionFlags DirectFlags {
get {
return InstructionFlags.MayBranch | InstructionFlags.EndPointUnreachable;
}
}
public override void WriteTo(ITextOutput output) public override void WriteTo(ITextOutput output)
{ {
output.Write(OpCode); output.Write(OpCode);

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

@ -33,11 +33,7 @@ namespace ICSharpCode.Decompiler.IL
public override void WriteTo(ITextOutput output) public override void WriteTo(ITextOutput output)
{ {
output.Write(OpCode); output.Write(OpCode);
} // the non-custom WriteTo would add useless parentheses
protected override InstructionFlags ComputeFlags()
{
return InstructionFlags.None;
} }
} }
} }

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

@ -49,13 +49,19 @@ namespace ICSharpCode.Decompiler.IL
protected override InstructionFlags ComputeFlags() protected override InstructionFlags ComputeFlags()
{ {
var sectionFlags = InstructionFlags.None; var sectionFlags = InstructionFlags.ControlFlow;
foreach (var section in Sections) { foreach (var section in Sections) {
sectionFlags = IfInstruction.CombineFlags(sectionFlags, section.Flags); sectionFlags = IfInstruction.CombineFlags(sectionFlags, section.Flags);
} }
return value.Flags | sectionFlags; return value.Flags | sectionFlags;
} }
public override InstructionFlags DirectFlags {
get {
return InstructionFlags.ControlFlow;
}
}
public override void WriteTo(ITextOutput output) public override void WriteTo(ITextOutput output)
{ {
output.Write("switch ("); output.Write("switch (");
@ -122,6 +128,12 @@ namespace ICSharpCode.Decompiler.IL
return body.Flags; return body.Flags;
} }
public override InstructionFlags DirectFlags {
get {
return InstructionFlags.None;
}
}
public override void WriteTo(ITextOutput output) public override void WriteTo(ITextOutput output)
{ {
output.Write("case "); output.Write("case ");

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

@ -85,7 +85,13 @@ namespace ICSharpCode.Decompiler.IL
var flags = TryBlock.Flags; var flags = TryBlock.Flags;
foreach (var handler in Handlers) foreach (var handler in Handlers)
flags = IfInstruction.CombineFlags(flags, handler.Flags); flags = IfInstruction.CombineFlags(flags, handler.Flags);
return flags; return flags | InstructionFlags.ControlFlow;
}
public override InstructionFlags DirectFlags {
get {
return InstructionFlags.ControlFlow;
}
} }
protected override int GetChildCount() protected override int GetChildCount()
@ -144,7 +150,14 @@ namespace ICSharpCode.Decompiler.IL
protected override InstructionFlags ComputeFlags() 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) public override void WriteTo(ITextOutput output)
@ -217,7 +230,13 @@ namespace ICSharpCode.Decompiler.IL
protected override InstructionFlags ComputeFlags() 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 // 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() protected override int GetChildCount()
@ -304,7 +323,13 @@ namespace ICSharpCode.Decompiler.IL
protected override InstructionFlags ComputeFlags() protected override InstructionFlags ComputeFlags()
{ {
// The endpoint of the try-fault is unreachable iff the try endpoint is unreachable // 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() protected override int GetChildCount()

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

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

38
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
{
/// <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 @@
// 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