Browse Source

Refactored some peephole transforms; Moved some of the transforms before loop and condition detection

pull/100/head
David Srbecký 15 years ago
parent
commit
b3f1d599f8
  1. 3
      ICSharpCode.Decompiler/ILAst/GotoRemoval.cs
  2. 157
      ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs
  3. 76
      ICSharpCode.Decompiler/ILAst/ILInlining.cs
  4. 149
      ICSharpCode.Decompiler/ILAst/InitializerPeepholeTransforms.cs
  5. 10
      ICSharpCode.Decompiler/ILAst/PatternMatching.cs
  6. 323
      ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs
  7. 3
      ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs

3
ICSharpCode.Decompiler/ILAst/GotoRemoval.cs

@ -18,7 +18,8 @@ namespace ICSharpCode.Decompiler.ILAst
foreach (ILNode node in method.GetSelfAndChildrenRecursive<ILNode>()) { foreach (ILNode node in method.GetSelfAndChildrenRecursive<ILNode>()) {
ILNode previousChild = null; ILNode previousChild = null;
foreach (ILNode child in node.GetChildren()) { foreach (ILNode child in node.GetChildren()) {
Debug.Assert(!parent.ContainsKey(child)); // TODO: Add back
// Debug.Assert(!parent.ContainsKey(child));
parent[child] = node; parent[child] = node;
if (previousChild != null) if (previousChild != null)
nextSibling[previousChild] = child; nextSibling[previousChild] = child;

157
ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs

@ -18,34 +18,43 @@ namespace ICSharpCode.Decompiler.ILAst
YieldReturn, YieldReturn,
SplitToMovableBlocks, SplitToMovableBlocks,
TypeInference, TypeInference,
PeepholeOptimizations,
SimplifyShortCircuit, SimplifyShortCircuit,
SimplifyTernaryOperator, SimplifyTernaryOperator,
SimplifyNullCoalescing, SimplifyNullCoalescing,
MoreSimplifyPasses, TransformDecimalCtorToConstant,
SimplifyLdObjAndStObj,
TransformArrayInitializers,
TransformCollectionInitializers,
MakeAssignmentExpression,
InlineVariables2,
FindLoops, FindLoops,
FindConditions, FindConditions,
FlattenNestedMovableBlocks, FlattenNestedMovableBlocks,
RemoveRedundantCode2,
GotoRemoval, GotoRemoval,
DuplicateReturns, DuplicateReturns,
FlattenIfStatements, ReduceIfNesting,
InlineVariables2, InlineVariables3,
PeepholeTransforms, CachedDelegateInitialization,
IntroduceFixedStatements,
TypeInference2, TypeInference2,
RemoveRedundantCode3,
None None
} }
public class ILAstOptimizer public partial class ILAstOptimizer
{ {
int nextLabelIndex = 0; int nextLabelIndex = 0;
DecompilerContext context; DecompilerContext context;
TypeSystem typeSystem; TypeSystem typeSystem;
ILBlock method;
public void Optimize(DecompilerContext context, ILBlock method, ILAstOptimizationStep abortBeforeStep = ILAstOptimizationStep.None) public void Optimize(DecompilerContext context, ILBlock method, ILAstOptimizationStep abortBeforeStep = ILAstOptimizationStep.None)
{ {
this.context = context; this.context = context;
this.typeSystem = context.CurrentMethod.Module.TypeSystem; this.typeSystem = context.CurrentMethod.Module.TypeSystem;
this.method = method;
if (abortBeforeStep == ILAstOptimizationStep.RemoveRedundantCode) return; if (abortBeforeStep == ILAstOptimizationStep.RemoveRedundantCode) return;
RemoveRedundantCode(method); RemoveRedundantCode(method);
@ -77,7 +86,6 @@ namespace ICSharpCode.Decompiler.ILAst
// Types are needed for the ternary operator optimization // Types are needed for the ternary operator optimization
TypeAnalysis.Run(context, method); TypeAnalysis.Run(context, method);
if (abortBeforeStep == ILAstOptimizationStep.PeepholeOptimizations) return;
AnalyseLabels(method); AnalyseLabels(method);
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) { foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
bool modified; bool modified;
@ -85,18 +93,48 @@ namespace ICSharpCode.Decompiler.ILAst
modified = false; modified = false;
if (abortBeforeStep == ILAstOptimizationStep.SimplifyShortCircuit) return; if (abortBeforeStep == ILAstOptimizationStep.SimplifyShortCircuit) return;
modified |= block.RunPeepholeOptimization(TrySimplifyShortCircuit); modified |= block.RunOptimization(SimplifyShortCircuit);
if (abortBeforeStep == ILAstOptimizationStep.SimplifyTernaryOperator) return; if (abortBeforeStep == ILAstOptimizationStep.SimplifyTernaryOperator) return;
modified |= block.RunPeepholeOptimization(TrySimplifyTernaryOperator); modified |= block.RunOptimization(SimplifyTernaryOperator);
if (abortBeforeStep == ILAstOptimizationStep.SimplifyNullCoalescing) return; if (abortBeforeStep == ILAstOptimizationStep.SimplifyNullCoalescing) return;
modified |= block.RunPeepholeOptimization(TrySimplifyNullCoalescing); modified |= block.RunOptimization(SimplifyNullCoalescing);
if (abortBeforeStep == ILAstOptimizationStep.MoreSimplifyPasses) return;
} while(modified); } while(modified);
} }
ILInlining inlining2 = new ILInlining(method);
inlining2.InlineAllVariables();
inlining2.CopyPropagation();
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
// Intentionaly outside the while(modifed) loop,
// I will put it there later after more testing
bool modified = false;
if (abortBeforeStep == ILAstOptimizationStep.TransformDecimalCtorToConstant) return;
modified |= block.RunOptimization(TransformDecimalCtorToConstant);
if (abortBeforeStep == ILAstOptimizationStep.SimplifyLdObjAndStObj) return;
modified |= block.RunOptimization(SimplifyLdObjAndStObj);
if (abortBeforeStep == ILAstOptimizationStep.TransformArrayInitializers) return;
modified |= block.RunOptimization(Initializers.TransformArrayInitializers);
modified |= block.RunOptimization(Initializers.TransformArrayInitializers);
if (abortBeforeStep == ILAstOptimizationStep.TransformCollectionInitializers) return;
modified |= block.RunOptimization(Initializers.TransformCollectionInitializers);
if (abortBeforeStep == ILAstOptimizationStep.MakeAssignmentExpression) return;
modified |= block.RunOptimization(MakeAssignmentExpression);
if (abortBeforeStep == ILAstOptimizationStep.InlineVariables2) return;
modified |= new ILInlining(method).InlineAllInBlock(block);
}
if (abortBeforeStep == ILAstOptimizationStep.FindLoops) return; if (abortBeforeStep == ILAstOptimizationStep.FindLoops) return;
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) { foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
new LoopsAndConditions(context).FindLoops(block); new LoopsAndConditions(context).FindLoops(block);
@ -110,29 +148,42 @@ namespace ICSharpCode.Decompiler.ILAst
if (abortBeforeStep == ILAstOptimizationStep.FlattenNestedMovableBlocks) return; if (abortBeforeStep == ILAstOptimizationStep.FlattenNestedMovableBlocks) return;
FlattenBasicBlocks(method); FlattenBasicBlocks(method);
if (abortBeforeStep == ILAstOptimizationStep.GotoRemoval) return; if (abortBeforeStep == ILAstOptimizationStep.RemoveRedundantCode2) return;
RemoveRedundantCode(method); RemoveRedundantCode(method);
if (abortBeforeStep == ILAstOptimizationStep.GotoRemoval) return;
new GotoRemoval().RemoveGotos(method); new GotoRemoval().RemoveGotos(method);
if (abortBeforeStep == ILAstOptimizationStep.DuplicateReturns) return; if (abortBeforeStep == ILAstOptimizationStep.DuplicateReturns) return;
DuplicateReturnStatements(method); DuplicateReturnStatements(method);
if (abortBeforeStep == ILAstOptimizationStep.FlattenIfStatements) return; if (abortBeforeStep == ILAstOptimizationStep.ReduceIfNesting) return;
FlattenIfStatements(method); ReduceIfNesting(method);
if (abortBeforeStep == ILAstOptimizationStep.InlineVariables2) return; if (abortBeforeStep == ILAstOptimizationStep.InlineVariables3) return;
// The 2nd inlining pass is necessary because DuplicateReturns and the introduction of ternary operators // The 2nd inlining pass is necessary because DuplicateReturns and the introduction of ternary operators
// open up additional inlining possibilities. // open up additional inlining possibilities.
new ILInlining(method).InlineAllVariables(); new ILInlining(method).InlineAllVariables();
TypeAnalysis.Reset(method); if (abortBeforeStep == ILAstOptimizationStep.CachedDelegateInitialization) return;
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
for (int i = 0; i < block.Body.Count; i++) {
CachedDelegateInitialization(block, ref i);
}
}
if (abortBeforeStep == ILAstOptimizationStep.PeepholeTransforms) return; if (abortBeforeStep == ILAstOptimizationStep.IntroduceFixedStatements) return;
PeepholeTransforms.Run(context, method); foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
for (int i = 0; i < block.Body.Count; i++) {
IntroduceFixedStatements(block.Body, i);
}
}
if (abortBeforeStep == ILAstOptimizationStep.TypeInference2) return; if (abortBeforeStep == ILAstOptimizationStep.TypeInference2) return;
TypeAnalysis.Reset(method);
TypeAnalysis.Run(context, method); TypeAnalysis.Run(context, method);
if (abortBeforeStep == ILAstOptimizationStep.RemoveRedundantCode3) return;
GotoRemoval.RemoveRedundantCode(method); GotoRemoval.RemoveRedundantCode(method);
// ReportUnassignedILRanges(method); // ReportUnassignedILRanges(method);
@ -312,9 +363,9 @@ namespace ICSharpCode.Decompiler.ILAst
} }
// scope is modified if successful // scope is modified if successful
bool TrySimplifyTernaryOperator(List<ILNode> scope, ILBasicBlock head, int index) bool SimplifyTernaryOperator(List<ILNode> body, ILBasicBlock head, int pos)
{ {
Debug.Assert(scope.Contains(head)); Debug.Assert(body.Contains(head));
ILExpression condExpr; ILExpression condExpr;
ILLabel trueLabel; ILLabel trueLabel;
@ -336,8 +387,8 @@ namespace ICSharpCode.Decompiler.ILAst
labelToBasicBlock[falseLabel].Match(ILCode.Ret, out unused, out falseExpr, out falseFall))) && labelToBasicBlock[falseLabel].Match(ILCode.Ret, out unused, out falseExpr, out falseFall))) &&
trueLocVar == falseLocVar && trueLocVar == falseLocVar &&
trueFall == falseFall && trueFall == falseFall &&
scope.Contains(labelToBasicBlock[trueLabel]) && body.Contains(labelToBasicBlock[trueLabel]) &&
scope.Contains(labelToBasicBlock[falseLabel]) body.Contains(labelToBasicBlock[falseLabel])
) )
{ {
ILCode opCode = trueLocVar != null ? ILCode.Stloc : ILCode.Ret; ILCode opCode = trueLocVar != null ? ILCode.Stloc : ILCode.Ret;
@ -390,7 +441,7 @@ namespace ICSharpCode.Decompiler.ILAst
// Remove the old basic blocks // Remove the old basic blocks
foreach(ILLabel deleteLabel in new [] { trueLabel, falseLabel }) { foreach(ILLabel deleteLabel in new [] { trueLabel, falseLabel }) {
scope.RemoveOrThrow(labelToBasicBlock[deleteLabel]); body.RemoveOrThrow(labelToBasicBlock[deleteLabel]);
labelGlobalRefCount.RemoveOrThrow(deleteLabel); labelGlobalRefCount.RemoveOrThrow(deleteLabel);
labelToBasicBlock.RemoveOrThrow(deleteLabel); labelToBasicBlock.RemoveOrThrow(deleteLabel);
} }
@ -400,7 +451,7 @@ namespace ICSharpCode.Decompiler.ILAst
return false; return false;
} }
bool TrySimplifyNullCoalescing(List<ILNode> scope, ILBasicBlock head, int index) bool SimplifyNullCoalescing(List<ILNode> body, ILBasicBlock head, int pos)
{ {
// ... // ...
// v = ldloc(leftVar) // v = ldloc(leftVar)
@ -442,16 +493,16 @@ namespace ICSharpCode.Decompiler.ILAst
labelGlobalRefCount.GetOrDefault(condBBLabel) == 1 && labelGlobalRefCount.GetOrDefault(condBBLabel) == 1 &&
labelGlobalRefCount.GetOrDefault(rightBBLabel) == 1 && labelGlobalRefCount.GetOrDefault(rightBBLabel) == 1 &&
labelGlobalRefCount.GetOrDefault(endBBLabel) == 2 && labelGlobalRefCount.GetOrDefault(endBBLabel) == 2 &&
scope.Contains(condBB) && body.Contains(condBB) &&
scope.Contains(rightBB) && body.Contains(rightBB) &&
scope.Contains(endBB) body.Contains(endBB)
) )
{ {
head.Body[head.Body.Count - 1] = new ILExpression(ILCode.Stloc, v, new ILExpression(ILCode.NullCoalescing, null, leftExpr, rightExpr)); head.Body[head.Body.Count - 1] = new ILExpression(ILCode.Stloc, v, new ILExpression(ILCode.NullCoalescing, null, leftExpr, rightExpr));
head.FallthoughGoto = new ILExpression(ILCode.Br, endBBLabel); head.FallthoughGoto = new ILExpression(ILCode.Br, endBBLabel);
foreach(ILLabel deleteLabel in new [] { condBBLabel, rightBBLabel }) { foreach(ILLabel deleteLabel in new [] { condBBLabel, rightBBLabel }) {
scope.RemoveOrThrow(labelToBasicBlock[deleteLabel]); body.RemoveOrThrow(labelToBasicBlock[deleteLabel]);
labelGlobalRefCount.RemoveOrThrow(deleteLabel); labelGlobalRefCount.RemoveOrThrow(deleteLabel);
labelToBasicBlock.RemoveOrThrow(deleteLabel); labelToBasicBlock.RemoveOrThrow(deleteLabel);
} }
@ -460,10 +511,9 @@ namespace ICSharpCode.Decompiler.ILAst
return false; return false;
} }
// scope is modified if successful bool SimplifyShortCircuit(List<ILNode> body, ILBasicBlock head, int pos)
bool TrySimplifyShortCircuit(List<ILNode> scope, ILBasicBlock head, int index)
{ {
Debug.Assert(scope.Contains(head)); Debug.Assert(body.Contains(head));
ILExpression condExpr; ILExpression condExpr;
ILLabel trueLabel; ILLabel trueLabel;
@ -481,7 +531,7 @@ namespace ICSharpCode.Decompiler.ILAst
ILExpression nextCondExpr; ILExpression nextCondExpr;
ILLabel nextTrueLablel; ILLabel nextTrueLablel;
ILLabel nextFalseLabel; ILLabel nextFalseLabel;
if (scope.Contains(nextBasicBlock) && if (body.Contains(nextBasicBlock) &&
nextBasicBlock != head && nextBasicBlock != head &&
labelGlobalRefCount[nextBasicBlock.EntryLabel] == 1 && labelGlobalRefCount[nextBasicBlock.EntryLabel] == 1 &&
nextBasicBlock.Match(ILCode.Brtrue, out nextTrueLablel, out nextCondExpr, out nextFalseLabel) && nextBasicBlock.Match(ILCode.Brtrue, out nextTrueLablel, out nextCondExpr, out nextFalseLabel) &&
@ -498,7 +548,7 @@ namespace ICSharpCode.Decompiler.ILAst
// Remove the inlined branch from scope // Remove the inlined branch from scope
labelGlobalRefCount.RemoveOrThrow(nextBasicBlock.EntryLabel); labelGlobalRefCount.RemoveOrThrow(nextBasicBlock.EntryLabel);
labelToBasicBlock.RemoveOrThrow(nextBasicBlock.EntryLabel); labelToBasicBlock.RemoveOrThrow(nextBasicBlock.EntryLabel);
scope.RemoveOrThrow(nextBasicBlock); body.RemoveOrThrow(nextBasicBlock);
return true; return true;
} }
@ -589,7 +639,7 @@ namespace ICSharpCode.Decompiler.ILAst
/// Reduce the nesting of conditions. /// Reduce the nesting of conditions.
/// It should be done on flat data that already had most gotos removed /// It should be done on flat data that already had most gotos removed
/// </summary> /// </summary>
void FlattenIfStatements(ILNode node) void ReduceIfNesting(ILNode node)
{ {
ILBlock block = node as ILBlock; ILBlock block = node as ILBlock;
if (block != null) { if (block != null) {
@ -624,7 +674,7 @@ namespace ICSharpCode.Decompiler.ILAst
// We are changing the number of blocks so we use plain old recursion to get all blocks // We are changing the number of blocks so we use plain old recursion to get all blocks
foreach(ILNode child in node.GetChildren()) { foreach(ILNode child in node.GetChildren()) {
if (child != null && !(child is ILExpression)) if (child != null && !(child is ILExpression))
FlattenIfStatements(child); ReduceIfNesting(child);
} }
} }
@ -642,7 +692,7 @@ namespace ICSharpCode.Decompiler.ILAst
/// Perform one pass of a given optimization on this block. /// Perform one pass of a given optimization on this block.
/// This block must consist of only basicblocks. /// This block must consist of only basicblocks.
/// </summary> /// </summary>
public static bool RunPeepholeOptimization(this ILBlock block, Func<List<ILNode>, ILBasicBlock, int, bool> optimization) public static bool RunOptimization(this ILBlock block, Func<List<ILNode>, ILBasicBlock, int, bool> optimization)
{ {
bool modified = false; bool modified = false;
List<ILNode> body = block.Body; List<ILNode> body = block.Body;
@ -657,6 +707,23 @@ namespace ICSharpCode.Decompiler.ILAst
return modified; return modified;
} }
public static bool RunOptimization(this ILBlock block, Func<List<ILNode>, ILExpression, int, bool> optimization)
{
bool modified = false;
foreach (ILBasicBlock bb in block.Body) {
for (int j = 0; j < bb.Body.Count;) {
ILExpression expr = bb.Body[j] as ILExpression;
if (expr != null && optimization(bb.Body, expr, j)) {
modified = true;
j = Math.Max(0, j - 1); // Go back one step
} else {
j++;
}
}
}
return modified;
}
public static bool CanFallThough(this ILNode node) public static bool CanFallThough(this ILNode node)
{ {
ILExpression expr = node as ILExpression; ILExpression expr = node as ILExpression;
@ -689,6 +756,24 @@ namespace ICSharpCode.Decompiler.ILAst
} }
} }
public static bool IsStoreToArray(this ILCode code)
{
switch (code) {
case ILCode.Stelem_Any:
case ILCode.Stelem_I:
case ILCode.Stelem_I1:
case ILCode.Stelem_I2:
case ILCode.Stelem_I4:
case ILCode.Stelem_I8:
case ILCode.Stelem_R4:
case ILCode.Stelem_R8:
case ILCode.Stelem_Ref:
return true;
default:
return false;
}
}
/// <summary> /// <summary>
/// Can the expression be used as a statement in C#? /// Can the expression be used as a statement in C#?
/// </summary> /// </summary>

76
ICSharpCode.Decompiler/ILAst/ILInlining.cs

@ -48,41 +48,66 @@ namespace ICSharpCode.Decompiler.ILAst
} }
} }
public void InlineAllVariables() public bool InlineAllVariables()
{ {
bool modified = false;
ILInlining i = new ILInlining(method); ILInlining i = new ILInlining(method);
foreach (ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) foreach (ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>())
i.InlineAllInBlock(block); modified |= i.InlineAllInBlock(block);
return modified;
} }
public void InlineAllInBlock(ILBlock block) public bool InlineAllInBlock(ILBlock block)
{ {
bool modified = false;
List<ILNode> body = block.Body; List<ILNode> body = block.Body;
for(int i = 0; i < body.Count - 1;) { for(int i = 0; i < body.Count - 1;) {
ILVariable locVar; ILVariable locVar;
ILExpression expr; ILExpression expr;
if (body[i].Match(ILCode.Stloc, out locVar, out expr) && InlineOneIfPossible(block, i, aggressive: false)) { if (body[i].Match(ILCode.Stloc, out locVar, out expr) && InlineOneIfPossible(block.Body, i, aggressive: false)) {
modified = true;
i = Math.Max(0, i - 1); // Go back one step i = Math.Max(0, i - 1); // Go back one step
} else { } else {
i++; i++;
} }
} }
foreach(ILBasicBlock bb in body.OfType<ILBasicBlock>()) {
modified |= InlineAllInBasicBlock(bb);
}
return modified;
}
public bool InlineAllInBasicBlock(ILBasicBlock bb)
{
bool modified = false;
List<ILNode> body = bb.Body;
for(int i = 0; i < body.Count - 1;) {
ILVariable locVar;
ILExpression expr;
if (body[i].Match(ILCode.Stloc, out locVar, out expr) && InlineOneIfPossible(bb.Body, i, aggressive: false)) {
modified = true;
i = Math.Max(0, i - 1); // Go back one step
} else {
i++;
}
}
return modified;
} }
/// <summary> /// <summary>
/// Inlines instructions before pos into block.Body[pos]. /// Inlines instructions before pos into block.Body[pos].
/// </summary> /// </summary>
/// <returns>The number of instructions that were inlined.</returns> /// <returns>The number of instructions that were inlined.</returns>
public int InlineInto(ILBlock block, int pos, bool aggressive) public int InlineInto(List<ILNode> body, int pos, bool aggressive)
{ {
if (pos >= block.Body.Count) if (pos >= body.Count)
return 0; return 0;
int count = 0; int count = 0;
while (--pos >= 0) { while (--pos >= 0) {
ILExpression expr = block.Body[pos] as ILExpression; ILExpression expr = body[pos] as ILExpression;
if (expr == null || expr.Code != ILCode.Stloc) if (expr == null || expr.Code != ILCode.Stloc)
break; break;
if (InlineOneIfPossible(block, pos, aggressive)) if (InlineOneIfPossible(body, pos, aggressive))
count++; count++;
else else
break; break;
@ -97,10 +122,10 @@ namespace ICSharpCode.Decompiler.ILAst
/// <remarks> /// <remarks>
/// After the operation, pos will point to the new combined instruction. /// After the operation, pos will point to the new combined instruction.
/// </remarks> /// </remarks>
public bool InlineIfPossible(ILBlock block, ref int pos) public bool InlineIfPossible(List<ILNode> body, ref int pos)
{ {
if (InlineOneIfPossible(block, pos, true)) { if (InlineOneIfPossible(body, pos, true)) {
pos -= InlineInto(block, pos, false); pos -= InlineInto(body, pos, false);
return true; return true;
} }
return false; return false;
@ -109,28 +134,28 @@ namespace ICSharpCode.Decompiler.ILAst
/// <summary> /// <summary>
/// Inlines the stloc instruction at block.Body[pos] into the next instruction, if possible. /// Inlines the stloc instruction at block.Body[pos] into the next instruction, if possible.
/// </summary> /// </summary>
public bool InlineOneIfPossible(ILBlock block, int pos, bool aggressive) public bool InlineOneIfPossible(List<ILNode> body, int pos, bool aggressive)
{ {
ILVariable v; ILVariable v;
ILExpression inlinedExpression; ILExpression inlinedExpression;
if (block.Body[pos].Match(ILCode.Stloc, out v, out inlinedExpression) && !v.IsPinned) { if (body[pos].Match(ILCode.Stloc, out v, out inlinedExpression) && !v.IsPinned) {
if (InlineIfPossible(v, inlinedExpression, block.Body.ElementAtOrDefault(pos+1), aggressive)) { if (InlineIfPossible(v, inlinedExpression, body.ElementAtOrDefault(pos+1), aggressive)) {
// Assign the ranges of the stloc instruction: // Assign the ranges of the stloc instruction:
inlinedExpression.ILRanges.AddRange(((ILExpression)block.Body[pos]).ILRanges); inlinedExpression.ILRanges.AddRange(((ILExpression)body[pos]).ILRanges);
// Remove the stloc instruction: // Remove the stloc instruction:
block.Body.RemoveAt(pos); body.RemoveAt(pos);
return true; return true;
} else if (numLdloc.GetOrDefault(v) == 0 && numLdloca.GetOrDefault(v) == 0) { } else if (numLdloc.GetOrDefault(v) == 0 && numLdloca.GetOrDefault(v) == 0) {
// The variable is never loaded // The variable is never loaded
if (inlinedExpression.HasNoSideEffects()) { if (inlinedExpression.HasNoSideEffects()) {
// Remove completely // Remove completely
block.Body.RemoveAt(pos); body.RemoveAt(pos);
return true; return true;
} else if (inlinedExpression.CanBeExpressionStatement() && v.IsGenerated) { } else if (inlinedExpression.CanBeExpressionStatement() && v.IsGenerated) {
// Assign the ranges of the stloc instruction: // Assign the ranges of the stloc instruction:
inlinedExpression.ILRanges.AddRange(((ILExpression)block.Body[pos]).ILRanges); inlinedExpression.ILRanges.AddRange(((ILExpression)body[pos]).ILRanges);
// Remove the stloc, but keep the inner expression // Remove the stloc, but keep the inner expression
block.Body[pos] = inlinedExpression; body[pos] = inlinedExpression;
return true; return true;
} }
} }
@ -155,7 +180,7 @@ namespace ICSharpCode.Decompiler.ILAst
ILExpression parent; ILExpression parent;
int pos; int pos;
if (FindLoadInNext(next as ILExpression, v, inlinedExpression, out parent, out pos) == true) { if (FindLoadInNext(next as ILExpression, v, inlinedExpression, out parent, out pos) == true) {
if (!aggressive && !v.IsGenerated && !NonAggressiveInlineInto((ILExpression)next, parent)) if (!aggressive && !v.IsGenerated && !NonAggressiveInlineInto((ILExpression)next, parent, inlinedExpression))
return false; return false;
// Assign the ranges of the ldloc instruction: // Assign the ranges of the ldloc instruction:
@ -168,8 +193,15 @@ namespace ICSharpCode.Decompiler.ILAst
return false; return false;
} }
bool NonAggressiveInlineInto(ILExpression next, ILExpression parent) bool NonAggressiveInlineInto(ILExpression next, ILExpression parent, ILExpression inlinedExpression)
{ {
switch (inlinedExpression.Code) {
case ILCode.InitArray:
case ILCode.InitCollection:
case ILCode.DefaultValue:
return true;
}
switch (next.Code) { switch (next.Code) {
case ILCode.Ret: case ILCode.Ret:
return parent.Code == ILCode.Ret; return parent.Code == ILCode.Ret;
@ -300,7 +332,7 @@ namespace ICSharpCode.Decompiler.ILAst
// if we un-inlined stuff; we need to update the usage counters // if we un-inlined stuff; we need to update the usage counters
AnalyzeMethod(); AnalyzeMethod();
} }
InlineInto(block, i, aggressive: false); // maybe inlining gets possible after the removal of block.Body[i] InlineInto(block.Body, i, aggressive: false); // maybe inlining gets possible after the removal of block.Body[i]
i -= uninlinedArgs.Length + 1; i -= uninlinedArgs.Length + 1;
} }
} }

149
ICSharpCode.Decompiler/ILAst/InitializerPeepholeTransforms.cs

@ -12,25 +12,16 @@ namespace ICSharpCode.Decompiler.ILAst
/// <summary> /// <summary>
/// IL AST transformation that introduces array, object and collection initializers. /// IL AST transformation that introduces array, object and collection initializers.
/// </summary> /// </summary>
public class InitializerPeepholeTransforms public class Initializers
{ {
readonly ILBlock method; public static bool TransformArrayInitializers(List<ILNode> body, ILExpression expr, int pos)
#region Array Initializers
public InitializerPeepholeTransforms(ILBlock method)
{
this.method = method;
}
public void TransformArrayInitializers(ILBlock block, ref int i)
{ {
ILVariable v, v2, v3; ILVariable v, v2, v3;
ILExpression newarrExpr; ILExpression newarrExpr;
TypeReference arrayType; TypeReference arrayType;
ILExpression lengthExpr; ILExpression lengthExpr;
int arrayLength; int arrayLength;
if (block.Body[i].Match(ILCode.Stloc, out v, out newarrExpr) && if (expr.Match(ILCode.Stloc, out v, out newarrExpr) &&
newarrExpr.Match(ILCode.Newarr, out arrayType, out lengthExpr) && newarrExpr.Match(ILCode.Newarr, out arrayType, out lengthExpr) &&
lengthExpr.Match(ILCode.Ldc_I4, out arrayLength) && lengthExpr.Match(ILCode.Ldc_I4, out arrayLength) &&
arrayLength > 0) arrayLength > 0)
@ -39,7 +30,7 @@ namespace ICSharpCode.Decompiler.ILAst
ILExpression methodArg1; ILExpression methodArg1;
ILExpression methodArg2; ILExpression methodArg2;
FieldDefinition field; FieldDefinition field;
if (block.Body.ElementAtOrDefault(i + 1).Match(ILCode.Call, out methodRef, out methodArg1, out methodArg2) && if (body.ElementAtOrDefault(pos + 1).Match(ILCode.Call, out methodRef, out methodArg1, out methodArg2) &&
methodRef.DeclaringType.FullName == "System.Runtime.CompilerServices.RuntimeHelpers" && methodRef.DeclaringType.FullName == "System.Runtime.CompilerServices.RuntimeHelpers" &&
methodRef.Name == "InitializeArray" && methodRef.Name == "InitializeArray" &&
methodArg1.Match(ILCode.Ldloc, out v2) && methodArg1.Match(ILCode.Ldloc, out v2) &&
@ -49,59 +40,41 @@ namespace ICSharpCode.Decompiler.ILAst
{ {
ILExpression[] newArr = new ILExpression[arrayLength]; ILExpression[] newArr = new ILExpression[arrayLength];
if (DecodeArrayInitializer(TypeAnalysis.GetTypeCode(arrayType), field.InitialValue, newArr)) { if (DecodeArrayInitializer(TypeAnalysis.GetTypeCode(arrayType), field.InitialValue, newArr)) {
block.Body[i] = new ILExpression(ILCode.Stloc, v, new ILExpression(ILCode.InitArray, arrayType, newArr)); body[pos] = new ILExpression(ILCode.Stloc, v, new ILExpression(ILCode.InitArray, arrayType, newArr));
block.Body.RemoveAt(i + 1); body.RemoveAt(pos + 1);
i -= new ILInlining(method).InlineInto(block, i + 1, aggressive: true) - 1; return true;
return;
} }
} }
const int maxConsecutiveDefaultValueExpressions = 10; const int maxConsecutiveDefaultValueExpressions = 10;
List<ILExpression> operands = new List<ILExpression>(); List<ILExpression> operands = new List<ILExpression>();
int numberOfInstructionsToRemove = 0; int numberOfInstructionsToRemove = 0;
for (int j = i + 1; j < block.Body.Count; j++) { for (int j = pos + 1; j < body.Count; j++) {
ILExpression expr = block.Body[j] as ILExpression; ILExpression nextExpr = body[j] as ILExpression;
int pos; int arrayPos;
if (expr != null && if (nextExpr != null &&
IsStoreToArray(expr.Code) && nextExpr.Code.IsStoreToArray() &&
expr.Arguments[0].Match(ILCode.Ldloc, out v3) && nextExpr.Arguments[0].Match(ILCode.Ldloc, out v3) &&
v == v3 && v == v3 &&
expr.Arguments[1].Match(ILCode.Ldc_I4, out pos) && nextExpr.Arguments[1].Match(ILCode.Ldc_I4, out arrayPos) &&
pos >= operands.Count && arrayPos >= operands.Count &&
pos <= operands.Count + maxConsecutiveDefaultValueExpressions) arrayPos <= operands.Count + maxConsecutiveDefaultValueExpressions)
{ {
while (operands.Count < pos) while (operands.Count < arrayPos)
operands.Add(new ILExpression(ILCode.DefaultValue, arrayType)); operands.Add(new ILExpression(ILCode.DefaultValue, arrayType));
operands.Add(expr.Arguments[2]); operands.Add(nextExpr.Arguments[2]);
numberOfInstructionsToRemove++; numberOfInstructionsToRemove++;
} else { } else {
break; break;
} }
} }
if (operands.Count == arrayLength) { if (operands.Count == arrayLength) {
((ILExpression)block.Body[i]).Arguments[0] = new ILExpression(ILCode.InitArray, arrayType, operands); expr.Arguments[0] = new ILExpression(ILCode.InitArray, arrayType, operands);
block.Body.RemoveRange(i + 1, numberOfInstructionsToRemove); body.RemoveRange(pos + 1, numberOfInstructionsToRemove);
i -= new ILInlining(method).InlineInto(block, i + 1, aggressive: true) - 1;
}
}
}
static bool IsStoreToArray(ILCode code)
{
switch (code) {
case ILCode.Stelem_Any:
case ILCode.Stelem_I:
case ILCode.Stelem_I1:
case ILCode.Stelem_I2:
case ILCode.Stelem_I4:
case ILCode.Stelem_I8:
case ILCode.Stelem_R4:
case ILCode.Stelem_R8:
case ILCode.Stelem_Ref:
return true; return true;
default: }
return false;
} }
return false;
} }
static bool DecodeArrayInitializer(TypeCode elementType, byte[] initialValue, ILExpression[] output) static bool DecodeArrayInitializer(TypeCode elementType, byte[] initialValue, ILExpression[] output)
@ -165,50 +138,50 @@ namespace ICSharpCode.Decompiler.ILAst
return false; return false;
} }
} }
#endregion
#region Collection Initilializers public static bool TransformCollectionInitializers(List<ILNode> body, ILExpression expr, int pos)
public void TransformCollectionInitializers(ILBlock block, ref int i)
{ {
ILVariable v; ILVariable v, v2;
ILExpression expr; ILExpression newObjExpr;
if (!block.Body[i].Match(ILCode.Stloc, out v, out expr) || expr.Code != ILCode.Newobj) MethodReference ctor;
return; List<ILExpression> ctorArgs;
MethodReference ctor = (MethodReference)expr.Operand; if (expr.Match(ILCode.Stloc, out v, out newObjExpr) &&
TypeDefinition td = ctor.DeclaringType.Resolve(); newObjExpr.Match(ILCode.Newobj, out ctor, out ctorArgs))
if (td == null || !td.Interfaces.Any(intf => intf.Name == "IEnumerable" && intf.Namespace == "System.Collections")) {
return; TypeDefinition td = ctor.DeclaringType.Resolve();
// This is a collection: we can convert Add() calls into a collection initializer if (td == null || !td.Interfaces.Any(intf => intf.Name == "IEnumerable" && intf.Namespace == "System.Collections"))
ILExpression collectionInitializer = new ILExpression(ILCode.InitCollection, null, expr); return false;
for (int j = i + 1; j < block.Body.Count; j++) {
MethodReference addMethod;
List<ILExpression> args;
if (!block.Body[j].Match(ILCode.Callvirt, out addMethod, out args))
break;
if (addMethod.Name != "Add" || !addMethod.HasThis || args.Count < 2 || args[0].Code != ILCode.Ldloc || args[0].Operand != v)
break;
collectionInitializer.Arguments.Add((ILExpression)block.Body[j]);
}
// ensure we added at least one additional arg to the collection initializer:
if (collectionInitializer.Arguments.Count == 1)
return;
ILInlining inline = new ILInlining(method);
ILExpression followingExpr = block.Body.ElementAtOrDefault(i + collectionInitializer.Arguments.Count) as ILExpression;
if (inline.CanInlineInto(followingExpr, v, collectionInitializer)) {
block.Body.RemoveRange(i + 1, collectionInitializer.Arguments.Count - 1);
((ILExpression)block.Body[i]).Arguments[0] = collectionInitializer;
// Change add methods into InitCollectionAddMethod: // This is a collection: we can convert Add() calls into a collection initializer
for (int j = 1; j < collectionInitializer.Arguments.Count; j++) { ILExpression collectionInitializer = new ILExpression(ILCode.InitCollection, null, newObjExpr);
collectionInitializer.Arguments[j].Arguments.RemoveAt(0); bool anyAdded = false;
collectionInitializer.Arguments[j].Code = ILCode.InitCollectionAddMethod; while(pos + 1 < body.Count) {
ILExpression nextExpr = body[pos + 1] as ILExpression;
MethodReference addMethod;
List<ILExpression> args;
if (nextExpr.Match(ILCode.Callvirt, out addMethod, out args) &&
addMethod.Name == "Add" &&
addMethod.HasThis &&
args.Count == 2 &&
args[0].Match(ILCode.Ldloc, out v2) &&
v == v2)
{
nextExpr.Code = ILCode.InitCollectionAddMethod;
nextExpr.Arguments.RemoveAt(0);
collectionInitializer.Arguments.Add(nextExpr);
body.RemoveAt(pos + 1);
anyAdded = true;
} else {
break;
}
}
// ensure we added at least one additional arg to the collection initializer:
if (anyAdded) {
expr.Arguments[0] = collectionInitializer;
return true;
} }
inline = new ILInlining(method); // refresh variable usage info
if (inline.InlineIfPossible(block, ref i))
i++; // retry transformations on the new combined instruction
} }
return false;
} }
#endregion
} }
} }

10
ICSharpCode.Decompiler/ILAst/PatternMatching.cs

@ -28,6 +28,16 @@ namespace ICSharpCode.Decompiler.ILAst
return false; return false;
} }
public static bool Match<T>(this ILNode node, ILCode code, T operand)
{
ILExpression expr = node as ILExpression;
if (expr != null && expr.Prefixes == null && expr.Code == code) {
Debug.Assert(expr.Arguments.Count == 0);
return operand.Equals(expr.Operand);
}
return false;
}
public static bool Match(this ILNode node, ILCode code, out List<ILExpression> args) public static bool Match(this ILNode node, ILCode code, out List<ILExpression> args)
{ {
ILExpression expr = node as ILExpression; ILExpression expr = node as ILExpression;

323
ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs

@ -11,179 +11,83 @@ using Mono.Cecil;
namespace ICSharpCode.Decompiler.ILAst namespace ICSharpCode.Decompiler.ILAst
{ {
public delegate void PeepholeTransform(ILBlock block, ref int i); public partial class ILAstOptimizer
/// <summary>
/// Handles peephole transformations on the ILAst.
/// </summary>
public class PeepholeTransforms
{ {
DecompilerContext context; static bool TransformDecimalCtorToConstant(List<ILNode> body, ILExpression expr, int pos)
ILBlock method;
public static void Run(DecompilerContext context, ILBlock method)
{ {
PeepholeTransforms transforms = new PeepholeTransforms(); MethodReference r;
transforms.context = context; List<ILExpression> args;
transforms.method = method; if (expr.Match(ILCode.Newobj, out r, out args) &&
r.DeclaringType.Namespace == "System" &&
InitializerPeepholeTransforms initializerTransforms = new InitializerPeepholeTransforms(method); r.DeclaringType.Name == "Decimal")
PeepholeTransform[] blockTransforms = { {
initializerTransforms.TransformArrayInitializers, if (args.Count == 1) {
initializerTransforms.TransformCollectionInitializers, int val;
transforms.CachedDelegateInitialization, if (args[0].Match(ILCode.Ldc_I4, out val)) {
transforms.MakeAssignmentExpression, expr.Code = ILCode.Ldc_Decimal;
transforms.IntroduceFixedStatements expr.Operand = new decimal(val);
}; expr.InferredType = r.DeclaringType;
Func<ILExpression, ILExpression>[] exprTransforms = { expr.Arguments.Clear();
HandleDecimalConstants, return true;
SimplifyLdObjAndStObj
};
// Traverse in post order so that nested blocks are transformed first. This is required so that
// patterns on the parent block can assume that all nested blocks are already transformed.
foreach (var node in TreeTraversal.PostOrder<ILNode>(method, c => c != null ? c.GetChildren() : null)) {
ILBlock block = node as ILBlock;
ILExpression expr;
if (block != null) {
// go through the instructions in reverse so that transforms can build up nested structures inside-out
for (int i = block.Body.Count - 1; i >= 0; i--) {
context.CancellationToken.ThrowIfCancellationRequested();
expr = block.Body[i] as ILExpression;
if (expr != null) {
// apply expr transforms to top-level expr in block
bool modified = ApplyExpressionTransforms(ref expr, exprTransforms);
block.Body[i] = expr;
if (modified) {
ILInlining inlining = new ILInlining(method);
if (inlining.InlineIfPossible(block, ref i)) {
i++; // retry all transforms on the new combined instruction
continue;
}
}
}
// apply block transforms
foreach (var t in blockTransforms) {
t(block, ref i);
Debug.Assert(i <= block.Body.Count && i >= 0);
if (i == block.Body.Count) // special case: retry all transforms
break;
}
} }
} } else if (args.Count == 5) {
expr = node as ILExpression; int lo, mid, hi, isNegative, scale;
if (expr != null) { if (expr.Arguments[0].Match(ILCode.Ldc_I4, out lo) &&
// apply expr transforms to all arguments expr.Arguments[1].Match(ILCode.Ldc_I4, out mid) &&
for (int i = 0; i < expr.Arguments.Count; i++) { expr.Arguments[2].Match(ILCode.Ldc_I4, out hi) &&
ILExpression arg = expr.Arguments[i]; expr.Arguments[3].Match(ILCode.Ldc_I4, out isNegative) &&
ApplyExpressionTransforms(ref arg, exprTransforms); expr.Arguments[4].Match(ILCode.Ldc_I4, out scale))
expr.Arguments[i] = arg; {
expr.Code = ILCode.Ldc_Decimal;
expr.Operand = new decimal(lo, mid, hi, isNegative != 0, (byte)scale);
expr.InferredType = r.DeclaringType;
expr.Arguments.Clear();
return true;
} }
} }
} }
} bool modified = false;
foreach(ILExpression arg in expr.Arguments) {
static bool ApplyExpressionTransforms(ref ILExpression expr, Func<ILExpression, ILExpression>[] exprTransforms) modified |= TransformDecimalCtorToConstant(null, arg, -1);
{
bool modifiedInAnyIteration = false;
bool modified;
do {
modified = false;
ILExpression oldExpr = expr;
ILCode oldOpCode = oldExpr.Code;
foreach (var t in exprTransforms)
expr = t(expr);
if (expr != oldExpr || oldOpCode != expr.Code) {
modified = true;
modifiedInAnyIteration = true;
}
} while (modified);
return modifiedInAnyIteration;
}
#region HandleDecimalConstants
static ILExpression HandleDecimalConstants(ILExpression expr)
{
if (expr.Code == ILCode.Newobj) {
MethodReference r = (MethodReference)expr.Operand;
if (r.DeclaringType.Name == "Decimal" && r.DeclaringType.Namespace == "System") {
if (expr.Arguments.Count == 1) {
int? val = GetI4Constant(expr.Arguments[0]);
if (val != null) {
expr.Arguments.Clear();
expr.Code = ILCode.Ldc_Decimal;
expr.Operand = new decimal(val.Value);
expr.InferredType = r.DeclaringType;
}
} else if (expr.Arguments.Count == 5) {
int? lo = GetI4Constant(expr.Arguments[0]);
int? mid = GetI4Constant(expr.Arguments[1]);
int? hi = GetI4Constant(expr.Arguments[2]);
int? isNegative = GetI4Constant(expr.Arguments[3]);
int? scale = GetI4Constant(expr.Arguments[4]);
if (lo != null && mid != null && hi != null && isNegative != null && scale != null) {
expr.Arguments.Clear();
expr.Code = ILCode.Ldc_Decimal;
expr.Operand = new decimal(lo.Value, mid.Value, hi.Value, isNegative.Value != 0, (byte)scale);
expr.InferredType = r.DeclaringType;
}
}
}
} }
return expr; return modified;
}
static int? GetI4Constant(ILExpression expr)
{
if (expr != null && expr.Code == ILCode.Ldc_I4)
return (int)expr.Operand;
else
return null;
} }
#endregion
#region SimplifyLdObjAndStObj static bool SimplifyLdObjAndStObj(List<ILNode> body, ILExpression expr, int pos)
static ILExpression SimplifyLdObjAndStObj(ILExpression expr)
{ {
if (expr.Code == ILCode.Initobj) { if (expr.Code == ILCode.Initobj) {
expr.Code = ILCode.Stobj; expr.Code = ILCode.Stobj;
expr.Arguments.Add(new ILExpression(ILCode.DefaultValue, expr.Operand)); expr.Arguments.Add(new ILExpression(ILCode.DefaultValue, expr.Operand));
return true;
} }
if (expr.Code == ILCode.Stobj) { ILExpression arg, arg2;
switch (expr.Arguments[0].Code) { TypeReference type;
case ILCode.Ldelema: ILCode? newCode = null;
return SimplifyLdObjOrStObj(expr, ILCode.Stelem_Any); if (expr.Match(ILCode.Stobj, out type, out arg, out arg2)) {
case ILCode.Ldloca: switch (arg.Code) {
return SimplifyLdObjOrStObj(expr, ILCode.Stloc); case ILCode.Ldelema: newCode = ILCode.Stelem_Any; break;
case ILCode.Ldflda: case ILCode.Ldloca: newCode = ILCode.Stloc; break;
return SimplifyLdObjOrStObj(expr, ILCode.Stfld); case ILCode.Ldflda: newCode = ILCode.Stfld; break;
case ILCode.Ldsflda: case ILCode.Ldsflda: newCode = ILCode.Stsfld; break;
return SimplifyLdObjOrStObj(expr, ILCode.Stsfld);
} }
} else if (expr.Code == ILCode.Ldobj) { } else if (expr.Match(ILCode.Ldobj, out type, out arg)) {
switch (expr.Arguments[0].Code) { switch (arg.Code) {
case ILCode.Ldelema: case ILCode.Ldelema: newCode = ILCode.Ldelem_Any; break;
return SimplifyLdObjOrStObj(expr, ILCode.Ldelem_Any); case ILCode.Ldloca: newCode = ILCode.Ldloc; break;
case ILCode.Ldloca: case ILCode.Ldflda: newCode = ILCode.Ldfld; break;
return SimplifyLdObjOrStObj(expr, ILCode.Ldloc); case ILCode.Ldsflda: newCode = ILCode.Ldsfld; break;
case ILCode.Ldflda:
return SimplifyLdObjOrStObj(expr, ILCode.Ldfld);
case ILCode.Ldsflda:
return SimplifyLdObjOrStObj(expr, ILCode.Ldsfld);
} }
} }
return expr; if (newCode != null) {
} arg.Code = newCode.Value;
if (expr.Code == ILCode.Stobj)
static ILExpression SimplifyLdObjOrStObj(ILExpression expr, ILCode newCode) arg.Arguments.Add(arg2);
{ arg.ILRanges.AddRange(expr.ILRanges);
ILExpression lda = expr.Arguments[0]; body[pos] = arg;
lda.Code = newCode; return true;
if (expr.Code == ILCode.Stobj) }
lda.Arguments.Add(expr.Arguments[1]); return false;
lda.ILRanges.AddRange(expr.ILRanges);
return lda;
} }
#endregion
#region CachedDelegateInitialization #region CachedDelegateInitialization
void CachedDelegateInitialization(ILBlock block, ref int i) void CachedDelegateInitialization(ILBlock block, ref int i)
@ -230,7 +134,7 @@ namespace ICSharpCode.Decompiler.ILAst
if (parent.Arguments[j].Code == ILCode.Ldsfld && ((FieldReference)parent.Arguments[j].Operand).ResolveWithinSameModule() == field) { if (parent.Arguments[j].Code == ILCode.Ldsfld && ((FieldReference)parent.Arguments[j].Operand).ResolveWithinSameModule() == field) {
parent.Arguments[j] = newObj; parent.Arguments[j] = newObj;
block.Body.RemoveAt(i); block.Body.RemoveAt(i);
i -= new ILInlining(method).InlineInto(block, i, aggressive: true); i -= new ILInlining(method).InlineInto(block.Body, i, aggressive: true);
return; return;
} }
} }
@ -240,51 +144,48 @@ namespace ICSharpCode.Decompiler.ILAst
#endregion #endregion
#region MakeAssignmentExpression #region MakeAssignmentExpression
void MakeAssignmentExpression(ILBlock block, ref int i) bool MakeAssignmentExpression(List<ILNode> body, ILExpression expr, int pos)
{ {
// expr_44 = ... // exprVar = ...
// stloc(v, expr_44) // stloc(v, exprVar)
// -> // ->
// expr_44 = stloc(v, ...)) // exprVar = stloc(v, ...))
ILExpression nextExpr = body.ElementAtOrDefault(pos + 1) as ILExpression;
ILVariable exprVar; ILVariable exprVar;
ILExpression initializer; ILExpression initializer;
if (!(block.Body[i].Match(ILCode.Stloc, out exprVar, out initializer) && exprVar.IsGenerated)) ILVariable v;
return; ILExpression stLocArg;
ILExpression stloc1 = block.Body.ElementAtOrDefault(i + 1) as ILExpression; if (expr.Match(ILCode.Stloc, out exprVar, out initializer) &&
if (!(stloc1 != null && stloc1.Code == ILCode.Stloc && stloc1.Arguments[0].Code == ILCode.Ldloc && stloc1.Arguments[0].Operand == exprVar)) exprVar.IsGenerated &&
return; nextExpr.Match(ILCode.Stloc, out v, out stLocArg) &&
stLocArg.Match(ILCode.Ldloc, exprVar))
ILInlining inlining; {
ILExpression store2 = block.Body.ElementAtOrDefault(i + 2) as ILExpression; ILExpression store2 = body.ElementAtOrDefault(pos + 2) as ILExpression;
if (StoreCanBeConvertedToAssignment(store2, exprVar)) { if (StoreCanBeConvertedToAssignment(store2, exprVar)) {
// expr_44 = ... // expr_44 = ...
// stloc(v1, expr_44) // stloc(v1, expr_44)
// anystore(v2, expr_44) // anystore(v2, expr_44)
// -> // ->
// stloc(v1, anystore(v2, ...)) // stloc(v1, anystore(v2, ...))
inlining = new ILInlining(method); ILInlining inlining = new ILInlining(method);
if (inlining.numLdloc.GetOrDefault(exprVar) == 2 && inlining.numStloc.GetOrDefault(exprVar) == 1) { if (inlining.numLdloc.GetOrDefault(exprVar) == 2 && inlining.numStloc.GetOrDefault(exprVar) == 1) {
block.Body.RemoveAt(i + 2); // remove store2 body.RemoveAt(pos + 2); // remove store2
block.Body.RemoveAt(i); // remove expr = ... body.RemoveAt(pos); // remove expr = ...
stloc1.Arguments[0] = store2; nextExpr.Arguments[0] = store2;
store2.Arguments[store2.Arguments.Count - 1] = initializer; store2.Arguments[store2.Arguments.Count - 1] = initializer;
if (inlining.InlineIfPossible(block, ref i)) { inlining.InlineIfPossible(body, ref pos);
i++; // retry transformations on the new combined instruction
return true;
} }
return;
} }
body.RemoveAt(pos + 1); // remove stloc
nextExpr.Arguments[0] = initializer;
((ILExpression)body[pos]).Arguments[0] = nextExpr;
return true;
} }
return false;
block.Body.RemoveAt(i + 1); // remove stloc
stloc1.Arguments[0] = initializer;
((ILExpression)block.Body[i]).Arguments[0] = stloc1;
inlining = new ILInlining(method);
if (inlining.InlineIfPossible(block, ref i)) {
i++; // retry transformations on the new combined instruction
}
} }
bool StoreCanBeConvertedToAssignment(ILExpression store, ILVariable exprVar) bool StoreCanBeConvertedToAssignment(ILExpression store, ILVariable exprVar)
@ -297,35 +198,35 @@ namespace ICSharpCode.Decompiler.ILAst
#endregion #endregion
#region IntroduceFixedStatements #region IntroduceFixedStatements
void IntroduceFixedStatements(ILBlock block, ref int i) bool IntroduceFixedStatements(List<ILNode> body, int i)
{ {
ILExpression initValue; ILExpression initValue;
ILVariable pinnedVar; ILVariable pinnedVar;
int initEndPos; int initEndPos;
if (!MatchFixedInitializer(block, i, out pinnedVar, out initValue, out initEndPos)) if (!MatchFixedInitializer(body, i, out pinnedVar, out initValue, out initEndPos))
return; return false;
ILFixedStatement fixedStmt = block.Body.ElementAtOrDefault(initEndPos) as ILFixedStatement; ILFixedStatement fixedStmt = body.ElementAtOrDefault(initEndPos) as ILFixedStatement;
if (fixedStmt != null) { if (fixedStmt != null) {
ILExpression expr = fixedStmt.BodyBlock.Body.LastOrDefault() as ILExpression; ILExpression expr = fixedStmt.BodyBlock.Body.LastOrDefault() as ILExpression;
if (expr != null && expr.Code == ILCode.Stloc && expr.Operand == pinnedVar && IsNullOrZero(expr.Arguments[0])) { if (expr != null && expr.Code == ILCode.Stloc && expr.Operand == pinnedVar && IsNullOrZero(expr.Arguments[0])) {
// we found a second initializer for the existing fixed statement // we found a second initializer for the existing fixed statement
fixedStmt.Initializers.Insert(0, initValue); fixedStmt.Initializers.Insert(0, initValue);
block.Body.RemoveRange(i, initEndPos - i); body.RemoveRange(i, initEndPos - i);
fixedStmt.BodyBlock.Body.RemoveAt(fixedStmt.BodyBlock.Body.Count - 1); fixedStmt.BodyBlock.Body.RemoveAt(fixedStmt.BodyBlock.Body.Count - 1);
if (pinnedVar.Type.IsByReference) if (pinnedVar.Type.IsByReference)
pinnedVar.Type = new PointerType(((ByReferenceType)pinnedVar.Type).ElementType); pinnedVar.Type = new PointerType(((ByReferenceType)pinnedVar.Type).ElementType);
return; return true;
} }
} }
// find where pinnedVar is reset to 0: // find where pinnedVar is reset to 0:
int j; int j;
for (j = initEndPos; j < block.Body.Count; j++) { for (j = initEndPos; j < body.Count; j++) {
ILVariable v2; ILVariable v2;
ILExpression storedVal; ILExpression storedVal;
// stloc(pinned_Var, conv.u(ldc.i4(0))) // stloc(pinned_Var, conv.u(ldc.i4(0)))
if (block.Body[j].Match(ILCode.Stloc, out v2, out storedVal) && v2 == pinnedVar) { if (body[j].Match(ILCode.Stloc, out v2, out storedVal) && v2 == pinnedVar) {
if (IsNullOrZero(storedVal)) { if (IsNullOrZero(storedVal)) {
break; break;
} }
@ -334,11 +235,13 @@ namespace ICSharpCode.Decompiler.ILAst
// Create fixed statement from i to j // Create fixed statement from i to j
fixedStmt = new ILFixedStatement(); fixedStmt = new ILFixedStatement();
fixedStmt.Initializers.Add(initValue); fixedStmt.Initializers.Add(initValue);
fixedStmt.BodyBlock = new ILBlock(block.Body.GetRange(initEndPos, j - initEndPos)); // from initEndPos to j-1 (inclusive) fixedStmt.BodyBlock = new ILBlock(body.GetRange(initEndPos, j - initEndPos)); // from initEndPos to j-1 (inclusive)
block.Body.RemoveRange(i + 1, Math.Min(j, block.Body.Count - 1) - i); // from i+1 to j (inclusive) body.RemoveRange(i + 1, Math.Min(j, body.Count - 1) - i); // from i+1 to j (inclusive)
block.Body[i] = fixedStmt; body[i] = fixedStmt;
if (pinnedVar.Type.IsByReference) if (pinnedVar.Type.IsByReference)
pinnedVar.Type = new PointerType(((ByReferenceType)pinnedVar.Type).ElementType); pinnedVar.Type = new PointerType(((ByReferenceType)pinnedVar.Type).ElementType);
return true;
} }
bool IsNullOrZero(ILExpression expr) bool IsNullOrZero(ILExpression expr)
@ -348,15 +251,15 @@ namespace ICSharpCode.Decompiler.ILAst
return (expr.Code == ILCode.Ldc_I4 && (int)expr.Operand == 0) || expr.Code == ILCode.Ldnull; return (expr.Code == ILCode.Ldc_I4 && (int)expr.Operand == 0) || expr.Code == ILCode.Ldnull;
} }
bool MatchFixedInitializer(ILBlock block, int i, out ILVariable pinnedVar, out ILExpression initValue, out int nextPos) bool MatchFixedInitializer(List<ILNode> body, int i, out ILVariable pinnedVar, out ILExpression initValue, out int nextPos)
{ {
if (block.Body[i].Match(ILCode.Stloc, out pinnedVar, out initValue) && pinnedVar.IsPinned && !IsNullOrZero(initValue)) { if (body[i].Match(ILCode.Stloc, out pinnedVar, out initValue) && pinnedVar.IsPinned && !IsNullOrZero(initValue)) {
initValue = (ILExpression)block.Body[i]; initValue = (ILExpression)body[i];
nextPos = i + 1; nextPos = i + 1;
HandleStringFixing(pinnedVar, block.Body, ref nextPos, ref initValue); HandleStringFixing(pinnedVar, body, ref nextPos, ref initValue);
return true; return true;
} }
ILCondition ifStmt = block.Body[i] as ILCondition; ILCondition ifStmt = body[i] as ILCondition;
ILExpression arrayLoadingExpr; ILExpression arrayLoadingExpr;
if (ifStmt != null && MatchFixedArrayInitializerCondition(ifStmt.Condition, out arrayLoadingExpr)) { if (ifStmt != null && MatchFixedArrayInitializerCondition(ifStmt.Condition, out arrayLoadingExpr)) {
ILVariable arrayVariable = (ILVariable)arrayLoadingExpr.Operand; ILVariable arrayVariable = (ILVariable)arrayLoadingExpr.Operand;

3
ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs

@ -490,8 +490,7 @@ namespace ICSharpCode.Decompiler.ILAst
case ILCode.Ldc_R8: case ILCode.Ldc_R8:
return typeSystem.Double; return typeSystem.Double;
case ILCode.Ldc_Decimal: case ILCode.Ldc_Decimal:
Debug.Assert(expr.InferredType != null && expr.InferredType.FullName == "System.Decimal"); return new TypeReference("System", "Decimal", module, module, true);
return expr.InferredType;
case ILCode.Ldtoken: case ILCode.Ldtoken:
if (expr.Operand is TypeReference) if (expr.Operand is TypeReference)
return new TypeReference("System", "RuntimeTypeHandle", module, module, true); return new TypeReference("System", "RuntimeTypeHandle", module, module, true);

Loading…
Cancel
Save