Browse Source

Merge branch 'master' of git://github.com/icsharpcode/ILSpy into Debugger

pull/191/merge
Eusebiu Marcu 15 years ago
parent
commit
0e1152bdca
  1. 2
      ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraph.cs
  2. 3
      ICSharpCode.Decompiler/ILAst/GotoRemoval.cs
  3. 270
      ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs
  4. 76
      ICSharpCode.Decompiler/ILAst/ILInlining.cs
  5. 149
      ICSharpCode.Decompiler/ILAst/InitializerPeepholeTransforms.cs
  6. 154
      ICSharpCode.Decompiler/ILAst/LoopsAndConditions.cs
  7. 26
      ICSharpCode.Decompiler/ILAst/PatternMatching.cs
  8. 323
      ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs
  9. 3
      ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs

2
ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraph.cs

@ -120,7 +120,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
b => b.Successors, b => b.Successors,
b => { b => {
if (b != EntryPoint) { if (b != EntryPoint) {
ControlFlowNode newIdom = b.Predecessors.First(block => block.Visited); ControlFlowNode newIdom = b.Predecessors.First(block => block.Visited && block != b);
// for all other predecessors p of b // for all other predecessors p of b
foreach (ControlFlowNode p in b.Predecessors) { foreach (ControlFlowNode p in b.Predecessors) {
if (p != b && p.ImmediateDominator != null) { if (p != b && p.ImmediateDominator != null) {

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)); if (parent.ContainsKey(child))
throw new Exception("The following expression is linked from several locations: " + child.ToString());
parent[child] = node; parent[child] = node;
if (previousChild != null) if (previousChild != null)
nextSibling[previousChild] = child; nextSibling[previousChild] = child;

270
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,44 @@ 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++) {
// TODO: Move before loops
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++) {
// TODO: Move before loops
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);
@ -234,59 +287,51 @@ namespace ICSharpCode.Decompiler.ILAst
List<ILNode> basicBlocks = new List<ILNode>(); List<ILNode> basicBlocks = new List<ILNode>();
ILBasicBlock basicBlock = new ILBasicBlock() { ILBasicBlock basicBlock = new ILBasicBlock() {
EntryLabel = new ILLabel() { Name = "Block_" + (nextLabelIndex++) } EntryLabel = block.Body.FirstOrDefault() as ILLabel ?? new ILLabel() { Name = "Block_" + (nextLabelIndex++) }
}; };
basicBlocks.Add(basicBlock); basicBlocks.Add(basicBlock);
block.EntryGoto = new ILExpression(ILCode.Br, basicBlock.EntryLabel); block.EntryGoto = new ILExpression(ILCode.Br, basicBlock.EntryLabel);
if (block.Body.Count > 0) { if (block.Body.Count > 0) {
basicBlock.Body.Add(block.Body[0]); if (block.Body[0] != basicBlock.EntryLabel)
basicBlock.Body.Add(block.Body[0]);
for (int i = 1; i < block.Body.Count; i++) { for (int i = 1; i < block.Body.Count; i++) {
ILNode lastNode = block.Body[i - 1]; ILNode lastNode = block.Body[i - 1];
ILNode currNode = block.Body[i]; ILNode currNode = block.Body[i];
// Insert split // Start a new basic block if necessary
if (currNode is ILLabel || if (currNode is ILLabel ||
lastNode is ILTryCatchBlock || lastNode is ILTryCatchBlock ||
currNode is ILTryCatchBlock || currNode is ILTryCatchBlock ||
(lastNode is ILExpression) && ((ILExpression)lastNode).IsBranch() || (lastNode is ILExpression && ((ILExpression)lastNode).IsBranch()))
(currNode is ILExpression) && (((ILExpression)currNode).IsBranch() && ((ILExpression)currNode).Code.CanFallThough() && basicBlock.Body.Count > 0))
{ {
ILBasicBlock lastBlock = basicBlock; // Try to reuse the label
basicBlock = new ILBasicBlock(); ILLabel label = currNode is ILLabel ? ((ILLabel)currNode) : new ILLabel() { Name = "Block_" + (nextLabelIndex++) };
basicBlocks.Add(basicBlock);
if (currNode is ILLabel) { // Terminate the last block
// Insert as entry label if (lastNode.CanFallThough()) {
basicBlock.EntryLabel = (ILLabel)currNode; // Explicit branch from one block to other
} else { basicBlock.FallthoughGoto = new ILExpression(ILCode.Br, label);
basicBlock.EntryLabel = new ILLabel() { Name = "Block_" + (nextLabelIndex++) }; } else if (lastNode.Match(ILCode.Br)) {
basicBlock.Body.Add(currNode); // Reuse the existing goto as FallthoughGoto
basicBlock.FallthoughGoto = (ILExpression)lastNode;
basicBlock.Body.RemoveAt(basicBlock.Body.Count - 1);
} }
// Explicit branch from one block to other // Start the new block
// (unless the last expression was unconditional branch) basicBlock = new ILBasicBlock();
if (!(lastNode is ILExpression) || ((ILExpression)lastNode).Code.CanFallThough()) { basicBlocks.Add(basicBlock);
lastBlock.FallthoughGoto = new ILExpression(ILCode.Br, basicBlock.EntryLabel); basicBlock.EntryLabel = label;
} }
} else {
// Add the node to the basic block
if (currNode != basicBlock.EntryLabel) {
basicBlock.Body.Add(currNode); basicBlock.Body.Add(currNode);
} }
} }
} }
foreach (ILBasicBlock bb in basicBlocks) {
if (bb.Body.Count > 0 &&
bb.Body.Last() is ILExpression &&
((ILExpression)bb.Body.Last()).Code == ILCode.Br)
{
Debug.Assert(bb.FallthoughGoto == null);
bb.FallthoughGoto = (ILExpression)bb.Body.Last();
bb.Body.RemoveAt(bb.Body.Count - 1);
}
}
block.Body = basicBlocks; block.Body = basicBlocks;
return; return;
} }
@ -311,10 +356,9 @@ namespace ICSharpCode.Decompiler.ILAst
} }
} }
// scope is modified if successful bool SimplifyTernaryOperator(List<ILNode> body, ILBasicBlock head, int pos)
bool TrySimplifyTernaryOperator(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;
@ -327,17 +371,17 @@ namespace ICSharpCode.Decompiler.ILAst
ILLabel falseFall; ILLabel falseFall;
object unused; object unused;
if (head.Match(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel) && if (head.MatchLast(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel) &&
labelGlobalRefCount[trueLabel] == 1 && labelGlobalRefCount[trueLabel] == 1 &&
labelGlobalRefCount[falseLabel] == 1 && labelGlobalRefCount[falseLabel] == 1 &&
((labelToBasicBlock[trueLabel].Match(ILCode.Stloc, out trueLocVar, out trueExpr, out trueFall) && ((labelToBasicBlock[trueLabel].MatchSingle(ILCode.Stloc, out trueLocVar, out trueExpr, out trueFall) &&
labelToBasicBlock[falseLabel].Match(ILCode.Stloc, out falseLocVar, out falseExpr, out falseFall)) || labelToBasicBlock[falseLabel].MatchSingle(ILCode.Stloc, out falseLocVar, out falseExpr, out falseFall)) ||
(labelToBasicBlock[trueLabel].Match(ILCode.Ret, out unused, out trueExpr, out trueFall) && (labelToBasicBlock[trueLabel].MatchSingle(ILCode.Ret, out unused, out trueExpr, out trueFall) &&
labelToBasicBlock[falseLabel].Match(ILCode.Ret, out unused, out falseExpr, out falseFall))) && labelToBasicBlock[falseLabel].MatchSingle(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;
@ -385,12 +429,12 @@ namespace ICSharpCode.Decompiler.ILAst
// Create ternary expression // Create ternary expression
newExpr = new ILExpression(ILCode.TernaryOp, null, condExpr, trueExpr, falseExpr); newExpr = new ILExpression(ILCode.TernaryOp, null, condExpr, trueExpr, falseExpr);
} }
head.Body = new List<ILNode>() { new ILExpression(opCode, trueLocVar, newExpr) }; head.Body[head.Body.Count - 1] = new ILExpression(opCode, trueLocVar, newExpr);
head.FallthoughGoto = trueFall != null ? new ILExpression(ILCode.Br, trueFall) : null; head.FallthoughGoto = trueFall != null ? new ILExpression(ILCode.Br, trueFall) : null;
// 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,75 +444,62 @@ 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)
// br(condBBLabel)
//
// condBBLabel:
// brtrue(endBBLabel, ldloc(leftVar)) // brtrue(endBBLabel, ldloc(leftVar))
// br(rightBBLabel) // br(rightBBLabel)
// //
// rightBBLabel: // rightBBLabel:
// v = rightExpr // v = rightExpr
// br(endBBLabel) // br(endBBLabel)
//
// endBBLabel:
// ... // ...
// =>
// ...
// v = NullCoalescing(ldloc(leftVar), rightExpr)
// br(endBBLabel)
ILVariable v, v2; ILVariable v, v2;
ILExpression leftExpr, leftExpr2; ILExpression leftExpr, leftExpr2;
ILVariable leftVar, leftVar2; ILVariable leftVar;
ILLabel condBBLabel;
ILBasicBlock condBB;
ILLabel endBBLabel, endBBLabel2; ILLabel endBBLabel, endBBLabel2;
ILLabel rightBBLabel; ILLabel rightBBLabel;
ILBasicBlock rightBB; ILBasicBlock rightBB;
ILExpression rightExpr; ILExpression rightExpr;
ILBasicBlock endBB; if (head.Body.Count >= 2 &&
if (head.Body.LastOrDefault().Match(ILCode.Stloc, out v, out leftExpr) && head.Body[head.Body.Count - 2].Match(ILCode.Stloc, out v, out leftExpr) &&
leftExpr.Match(ILCode.Ldloc, out leftVar) && leftExpr.Match(ILCode.Ldloc, out leftVar) &&
head.FallthoughGoto.Match(ILCode.Br, out condBBLabel) && head.MatchLast(ILCode.Brtrue, out endBBLabel, out leftExpr2, out rightBBLabel) &&
labelToBasicBlock.TryGetValue(condBBLabel, out condBB) && leftExpr2.Match(ILCode.Ldloc, leftVar) &&
condBB.Match(ILCode.Brtrue, out endBBLabel, out leftExpr2, out rightBBLabel) &&
leftExpr2.Match(ILCode.Ldloc, out leftVar2) &&
leftVar == leftVar2 &&
labelToBasicBlock.TryGetValue(rightBBLabel, out rightBB) && labelToBasicBlock.TryGetValue(rightBBLabel, out rightBB) &&
rightBB.Match(ILCode.Stloc, out v2, out rightExpr, out endBBLabel2) && rightBB.MatchSingle(ILCode.Stloc, out v2, out rightExpr, out endBBLabel2) &&
v == v2 && v == v2 &&
endBBLabel == endBBLabel2 && endBBLabel == endBBLabel2 &&
labelToBasicBlock.TryGetValue(endBBLabel, out endBB) &&
labelGlobalRefCount.GetOrDefault(condBBLabel) == 1 &&
labelGlobalRefCount.GetOrDefault(rightBBLabel) == 1 && labelGlobalRefCount.GetOrDefault(rightBBLabel) == 1 &&
labelGlobalRefCount.GetOrDefault(endBBLabel) == 2 && body.Contains(rightBB)
scope.Contains(condBB) &&
scope.Contains(rightBB) &&
scope.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 - 2] = new ILExpression(ILCode.Stloc, v, new ILExpression(ILCode.NullCoalescing, null, leftExpr, rightExpr));
head.Body.RemoveAt(head.Body.Count - 1);
head.FallthoughGoto = new ILExpression(ILCode.Br, endBBLabel); head.FallthoughGoto = new ILExpression(ILCode.Br, endBBLabel);
foreach(ILLabel deleteLabel in new [] { condBBLabel, rightBBLabel }) { body.RemoveOrThrow(labelToBasicBlock[rightBBLabel]);
scope.RemoveOrThrow(labelToBasicBlock[deleteLabel]); labelGlobalRefCount.RemoveOrThrow(rightBBLabel);
labelGlobalRefCount.RemoveOrThrow(deleteLabel); labelToBasicBlock.RemoveOrThrow(rightBBLabel);
labelToBasicBlock.RemoveOrThrow(deleteLabel);
}
return true; return true;
} }
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;
ILLabel falseLabel; ILLabel falseLabel;
if(head.Match(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel)) { if(head.MatchLast(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel)) {
for (int pass = 0; pass < 2; pass++) { for (int pass = 0; pass < 2; pass++) {
// On the second pass, swap labels and negate expression of the first branch // On the second pass, swap labels and negate expression of the first branch
@ -481,24 +512,24 @@ 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.MatchSingle(ILCode.Brtrue, out nextTrueLablel, out nextCondExpr, out nextFalseLabel) &&
(otherLablel == nextFalseLabel || otherLablel == nextTrueLablel)) (otherLablel == nextFalseLabel || otherLablel == nextTrueLablel))
{ {
// Create short cicuit branch // Create short cicuit branch
if (otherLablel == nextFalseLabel) { if (otherLablel == nextFalseLabel) {
head.Body[0] = new ILExpression(ILCode.Brtrue, nextTrueLablel, new ILExpression(ILCode.LogicAnd, null, negate ? new ILExpression(ILCode.LogicNot, null, condExpr) : condExpr, nextCondExpr)); head.Body[head.Body.Count - 1] = new ILExpression(ILCode.Brtrue, nextTrueLablel, new ILExpression(ILCode.LogicAnd, null, negate ? new ILExpression(ILCode.LogicNot, null, condExpr) : condExpr, nextCondExpr));
} else { } else {
head.Body[0] = new ILExpression(ILCode.Brtrue, nextTrueLablel, new ILExpression(ILCode.LogicOr, null, negate ? condExpr : new ILExpression(ILCode.LogicNot, null, condExpr), nextCondExpr)); head.Body[head.Body.Count - 1] = new ILExpression(ILCode.Brtrue, nextTrueLablel, new ILExpression(ILCode.LogicOr, null, negate ? condExpr : new ILExpression(ILCode.LogicNot, null, condExpr), nextCondExpr));
} }
head.FallthoughGoto = new ILExpression(ILCode.Br, nextFalseLabel); head.FallthoughGoto = new ILExpression(ILCode.Br, nextFalseLabel);
// 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 +620,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 +655,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 +673,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 +688,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 +737,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
} }
} }

154
ICSharpCode.Decompiler/ILAst/LoopsAndConditions.cs

@ -27,20 +27,24 @@ namespace ICSharpCode.Decompiler.ILAst
public void FindLoops(ILBlock block) public void FindLoops(ILBlock block)
{ {
ControlFlowGraph graph; if (block.Body.Count > 0) {
graph = BuildGraph(block.Body, (ILLabel)block.EntryGoto.Operand); ControlFlowGraph graph;
graph.ComputeDominance(context.CancellationToken); graph = BuildGraph(block.Body, (ILLabel)block.EntryGoto.Operand);
graph.ComputeDominanceFrontier(); graph.ComputeDominance(context.CancellationToken);
block.Body = FindLoops(new HashSet<ControlFlowNode>(graph.Nodes.Skip(3)), graph.EntryPoint, false); graph.ComputeDominanceFrontier();
block.Body = FindLoops(new HashSet<ControlFlowNode>(graph.Nodes.Skip(3)), graph.EntryPoint, false);
}
} }
public void FindConditions(ILBlock block) public void FindConditions(ILBlock block)
{ {
ControlFlowGraph graph; if (block.Body.Count > 0) {
graph = BuildGraph(block.Body, (ILLabel)block.EntryGoto.Operand); ControlFlowGraph graph;
graph.ComputeDominance(context.CancellationToken); graph = BuildGraph(block.Body, (ILLabel)block.EntryGoto.Operand);
graph.ComputeDominanceFrontier(); graph.ComputeDominance(context.CancellationToken);
block.Body = FindConditions(new HashSet<ControlFlowNode>(graph.Nodes.Skip(3)), graph.EntryPoint); graph.ComputeDominanceFrontier();
block.Body = FindConditions(new HashSet<ControlFlowNode>(graph.Nodes.Skip(3)), graph.EntryPoint);
}
} }
ControlFlowGraph BuildGraph(List<ILNode> nodes, ILLabel entryLabel) ControlFlowGraph BuildGraph(List<ILNode> nodes, ILLabel entryLabel)
@ -57,7 +61,7 @@ namespace ICSharpCode.Decompiler.ILAst
// Create graph nodes // Create graph nodes
labelToCfNode = new Dictionary<ILLabel, ControlFlowNode>(); labelToCfNode = new Dictionary<ILLabel, ControlFlowNode>();
Dictionary<ILNode, ControlFlowNode> astNodeToCfNode = new Dictionary<ILNode, ControlFlowNode>(); Dictionary<ILNode, ControlFlowNode> astNodeToCfNode = new Dictionary<ILNode, ControlFlowNode>();
foreach(ILNode node in nodes) { foreach(ILBasicBlock node in nodes) {
ControlFlowNode cfNode = new ControlFlowNode(index++, -1, ControlFlowNodeType.Normal); ControlFlowNode cfNode = new ControlFlowNode(index++, -1, ControlFlowNodeType.Normal);
cfNodes.Add(cfNode); cfNodes.Add(cfNode);
astNodeToCfNode[node] = cfNode; astNodeToCfNode[node] = cfNode;
@ -76,22 +80,18 @@ namespace ICSharpCode.Decompiler.ILAst
entryNode.Incoming.Add(entryEdge); entryNode.Incoming.Add(entryEdge);
// Create edges // Create edges
foreach(ILNode node in nodes) { foreach(ILBasicBlock node in nodes) {
ControlFlowNode source = astNodeToCfNode[node]; ControlFlowNode source = astNodeToCfNode[node];
// Find all branches // Find all branches
foreach(ILExpression child in node.GetSelfAndChildrenRecursive<ILExpression>()) { foreach(ILLabel target in node.GetSelfAndChildrenRecursive<ILExpression>(e => e.IsBranch()).SelectMany(e => e.GetBranchTargets())) {
IEnumerable<ILLabel> targets = child.GetBranchTargets(); ControlFlowNode destination;
if (targets != null) { // Labels which are out of out scope will not be int the collection
foreach(ILLabel target in targets) { // Insert self edge only if we are sure we are a loop
ControlFlowNode destination; if (labelToCfNode.TryGetValue(target, out destination) && (destination != source || target == node.EntryLabel)) {
// Labels which are out of out scope will not be int the collection ControlFlowEdge edge = new ControlFlowEdge(source, destination, JumpType.Normal);
if (labelToCfNode.TryGetValue(target, out destination) && destination != source) { source.Outgoing.Add(edge);
ControlFlowEdge edge = new ControlFlowEdge(source, destination, JumpType.Normal); destination.Incoming.Add(edge);
source.Outgoing.Add(edge);
destination.Incoming.Add(edge);
}
}
} }
} }
} }
@ -123,7 +123,7 @@ namespace ICSharpCode.Decompiler.ILAst
ILExpression condExpr; ILExpression condExpr;
ILLabel trueLabel; ILLabel trueLabel;
ILLabel falseLabel; ILLabel falseLabel;
if(basicBlock.Match(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel)) if(basicBlock.MatchSingle(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel))
{ {
ControlFlowNode trueTarget; ControlFlowNode trueTarget;
labelToCfNode.TryGetValue(trueLabel, out trueTarget); labelToCfNode.TryGetValue(trueLabel, out trueTarget);
@ -138,7 +138,7 @@ namespace ICSharpCode.Decompiler.ILAst
scope.RemoveOrThrow(node); scope.RemoveOrThrow(node);
// If false means enter the loop // If false means enter the loop
if (loopContents.Contains(falseTarget)) if (loopContents.Contains(falseTarget) || falseTarget == node)
{ {
// Negate the condition // Negate the condition
condExpr = new ILExpression(ILCode.LogicNot, null, condExpr); condExpr = new ILExpression(ILCode.LogicNot, null, condExpr);
@ -156,42 +156,39 @@ namespace ICSharpCode.Decompiler.ILAst
loopContents.UnionWith(pullIn); loopContents.UnionWith(pullIn);
} }
// Use loop to implement the condition // Use loop to implement the brtrue
result.Add(new ILBasicBlock() { basicBlock.Body.RemoveAt(basicBlock.Body.Count - 1);
EntryLabel = basicBlock.EntryLabel, basicBlock.Body.Add(new ILWhileLoop() {
Body = new List<ILNode>() { Condition = condExpr,
new ILWhileLoop() { BodyBlock = new ILBlock() {
Condition = condExpr, EntryGoto = new ILExpression(ILCode.Br, trueLabel),
BodyBlock = new ILBlock() { Body = FindLoops(loopContents, node, false)
EntryGoto = new ILExpression(ILCode.Br, trueLabel), }
Body = FindLoops(loopContents, node, true) });
} basicBlock.FallthoughGoto = new ILExpression(ILCode.Br, falseLabel);
}, result.Add(basicBlock);
new ILExpression(ILCode.Br, falseLabel)
}, scope.ExceptWith(loopContents);
FallthoughGoto = null
});
} }
} }
// Fallback method: while(true) // Fallback method: while(true)
if (scope.Contains(node)) { if (scope.Contains(node)) {
result.Add(new ILBasicBlock() { result.Add(new ILBasicBlock() {
EntryLabel = new ILLabel() { Name = "Loop_" + (nextLabelIndex++) }, EntryLabel = new ILLabel() { Name = "Loop_" + (nextLabelIndex++) },
Body = new List<ILNode>() { Body = new List<ILNode>() {
new ILWhileLoop() { new ILWhileLoop() {
BodyBlock = new ILBlock() { BodyBlock = new ILBlock() {
EntryGoto = new ILExpression(ILCode.Br, basicBlock.EntryLabel), EntryGoto = new ILExpression(ILCode.Br, basicBlock.EntryLabel),
Body = FindLoops(loopContents, node, true) Body = FindLoops(loopContents, node, true)
} }
}, },
}, },
FallthoughGoto = null FallthoughGoto = null
}); });
scope.ExceptWith(loopContents);
} }
// Move the content into loop block
scope.ExceptWith(loopContents);
} }
// Using the dominator tree should ensure we find the the widest loop first // Using the dominator tree should ensure we find the the widest loop first
@ -229,24 +226,20 @@ namespace ICSharpCode.Decompiler.ILAst
// Find a block that represents a simple condition // Find a block that represents a simple condition
if (scope.Contains(node)) { if (scope.Contains(node)) {
ILBasicBlock block = node.UserData as ILBasicBlock; ILBasicBlock block = (ILBasicBlock)node.UserData;
if (block != null && block.Body.Count == 1) { {
ILExpression condBranch = block.Body[0] as ILExpression;
// Switch // Switch
ILLabel[] caseLabels; ILLabel[] caseLabels;
List<ILExpression> switchArgs; ILExpression switchArg;
if (condBranch.Match(ILCode.Switch, out caseLabels, out switchArgs)) { ILLabel fallLabel;
if (block.MatchLast(ILCode.Switch, out caseLabels, out switchArg, out fallLabel)) {
ILSwitch ilSwitch = new ILSwitch() { Condition = switchArgs.Single() }; // Replace the switch code with ILSwitch
ILBasicBlock newBB = new ILBasicBlock() { ILSwitch ilSwitch = new ILSwitch() { Condition = switchArg };
EntryLabel = block.EntryLabel, // Keep the entry label block.Body.RemoveAt(block.Body.Count - 1);
Body = { ilSwitch }, block.Body.Add(ilSwitch);
FallthoughGoto = block.FallthoughGoto result.Add(block);
};
result.Add(newBB);
// Remove the item so that it is not picked up as content // Remove the item so that it is not picked up as content
scope.RemoveOrThrow(node); scope.RemoveOrThrow(node);
@ -259,19 +252,18 @@ namespace ICSharpCode.Decompiler.ILAst
} }
// Pull in code of cases // Pull in code of cases
ILLabel fallLabel = (ILLabel)block.FallthoughGoto.Operand;
ControlFlowNode fallTarget = null; ControlFlowNode fallTarget = null;
labelToCfNode.TryGetValue(fallLabel, out fallTarget); labelToCfNode.TryGetValue(fallLabel, out fallTarget);
HashSet<ControlFlowNode> frontiers = new HashSet<ControlFlowNode>(); HashSet<ControlFlowNode> frontiers = new HashSet<ControlFlowNode>();
if (fallTarget != null) if (fallTarget != null)
frontiers.UnionWith(fallTarget.DominanceFrontier); frontiers.UnionWith(fallTarget.DominanceFrontier.Except(new [] { fallTarget }));
foreach(ILLabel condLabel in caseLabels) { foreach(ILLabel condLabel in caseLabels) {
ControlFlowNode condTarget = null; ControlFlowNode condTarget = null;
labelToCfNode.TryGetValue(condLabel, out condTarget); labelToCfNode.TryGetValue(condLabel, out condTarget);
if (condTarget != null) if (condTarget != null)
frontiers.UnionWith(condTarget.DominanceFrontier); frontiers.UnionWith(condTarget.DominanceFrontier.Except(new [] { condTarget }));
} }
for (int i = 0; i < caseLabels.Length; i++) { for (int i = 0; i < caseLabels.Length; i++) {
@ -305,7 +297,7 @@ namespace ICSharpCode.Decompiler.ILAst
if (content.Any()) { if (content.Any()) {
var caseBlock = new ILSwitch.CaseBlock() { EntryGoto = new ILExpression(ILCode.Br, fallLabel) }; var caseBlock = new ILSwitch.CaseBlock() { EntryGoto = new ILExpression(ILCode.Br, fallLabel) };
ilSwitch.CaseBlocks.Add(caseBlock); ilSwitch.CaseBlocks.Add(caseBlock);
newBB.FallthoughGoto = null; block.FallthoughGoto = null;
scope.ExceptWith(content); scope.ExceptWith(content);
caseBlock.Body.AddRange(FindConditions(content, fallTarget)); caseBlock.Body.AddRange(FindConditions(content, fallTarget));
@ -319,7 +311,7 @@ namespace ICSharpCode.Decompiler.ILAst
ILExpression condExpr; ILExpression condExpr;
ILLabel trueLabel; ILLabel trueLabel;
ILLabel falseLabel; ILLabel falseLabel;
if(block.Match(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel)) { if(block.MatchLast(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel)) {
// Swap bodies since that seems to be the usual C# order // Swap bodies since that seems to be the usual C# order
ILLabel temp = trueLabel; ILLabel temp = trueLabel;
@ -327,16 +319,16 @@ namespace ICSharpCode.Decompiler.ILAst
falseLabel = temp; falseLabel = temp;
condExpr = new ILExpression(ILCode.LogicNot, null, condExpr); condExpr = new ILExpression(ILCode.LogicNot, null, condExpr);
// Convert the basic block to ILCondition // Convert the brtrue to ILCondition
ILCondition ilCond = new ILCondition() { ILCondition ilCond = new ILCondition() {
Condition = condExpr, Condition = condExpr,
TrueBlock = new ILBlock() { EntryGoto = new ILExpression(ILCode.Br, trueLabel) }, TrueBlock = new ILBlock() { EntryGoto = new ILExpression(ILCode.Br, trueLabel) },
FalseBlock = new ILBlock() { EntryGoto = new ILExpression(ILCode.Br, falseLabel) } FalseBlock = new ILBlock() { EntryGoto = new ILExpression(ILCode.Br, falseLabel) }
}; };
result.Add(new ILBasicBlock() { block.Body.RemoveAt(block.Body.Count - 1);
EntryLabel = block.EntryLabel, // Keep the entry label block.Body.Add(ilCond);
Body = { ilCond } block.FallthoughGoto = null;
}); result.Add(block);
// Remove the item immediately so that it is not picked up as content // Remove the item immediately so that it is not picked up as content
scope.RemoveOrThrow(node); scope.RemoveOrThrow(node);
@ -349,9 +341,9 @@ namespace ICSharpCode.Decompiler.ILAst
// Pull in the conditional code // Pull in the conditional code
HashSet<ControlFlowNode> frontiers = new HashSet<ControlFlowNode>(); HashSet<ControlFlowNode> frontiers = new HashSet<ControlFlowNode>();
if (trueTarget != null) if (trueTarget != null)
frontiers.UnionWith(trueTarget.DominanceFrontier); frontiers.UnionWith(trueTarget.DominanceFrontier.Except(new [] { trueTarget }));
if (falseTarget != null) if (falseTarget != null)
frontiers.UnionWith(falseTarget.DominanceFrontier); frontiers.UnionWith(falseTarget.DominanceFrontier.Except(new [] { falseTarget }));
if (trueTarget != null && !frontiers.Contains(trueTarget)) { if (trueTarget != null && !frontiers.Contains(trueTarget)) {
HashSet<ControlFlowNode> content = FindDominatedNodes(scope, trueTarget); HashSet<ControlFlowNode> content = FindDominatedNodes(scope, trueTarget);

26
ICSharpCode.Decompiler/ILAst/PatternMatching.cs

@ -4,7 +4,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq;
using Mono.Cecil; using Mono.Cecil;
namespace ICSharpCode.Decompiler.ILAst namespace ICSharpCode.Decompiler.ILAst
@ -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;
@ -88,7 +98,7 @@ namespace ICSharpCode.Decompiler.ILAst
return false; return false;
} }
public static bool Match<T>(this ILBasicBlock bb, ILCode code, out T operand, out ILExpression arg, out ILLabel fallLabel) public static bool MatchSingle<T>(this ILBasicBlock bb, ILCode code, out T operand, out ILExpression arg, out ILLabel fallLabel)
{ {
if (bb.Body.Count == 1) { if (bb.Body.Count == 1) {
if (bb.Body[0].Match(code, out operand, out arg)) { if (bb.Body[0].Match(code, out operand, out arg)) {
@ -102,6 +112,18 @@ namespace ICSharpCode.Decompiler.ILAst
return false; return false;
} }
public static bool MatchLast<T>(this ILBasicBlock bb, ILCode code, out T operand, out ILExpression arg, out ILLabel fallLabel)
{
if (bb.Body.LastOrDefault().Match(code, out operand, out arg)) {
fallLabel = bb.FallthoughGoto != null ? (ILLabel)bb.FallthoughGoto.Operand : null;
return true;
}
operand = default(T);
arg = null;
fallLabel = null;
return false;
}
public static bool MatchThis(this ILNode node) public static bool MatchThis(this ILNode node)
{ {
ILVariable v; ILVariable v;

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