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. 156
      ICSharpCode.Decompiler/ILAst/LoopsAndConditions.cs
  7. 26
      ICSharpCode.Decompiler/ILAst/PatternMatching.cs
  8. 321
      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 @@ -120,7 +120,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
b => b.Successors,
b => {
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
foreach (ControlFlowNode p in b.Predecessors) {
if (p != b && p.ImmediateDominator != null) {

3
ICSharpCode.Decompiler/ILAst/GotoRemoval.cs

@ -18,7 +18,8 @@ namespace ICSharpCode.Decompiler.ILAst @@ -18,7 +18,8 @@ namespace ICSharpCode.Decompiler.ILAst
foreach (ILNode node in method.GetSelfAndChildrenRecursive<ILNode>()) {
ILNode previousChild = null;
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;
if (previousChild != null)
nextSibling[previousChild] = child;

270
ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs

@ -18,34 +18,43 @@ namespace ICSharpCode.Decompiler.ILAst @@ -18,34 +18,43 @@ namespace ICSharpCode.Decompiler.ILAst
YieldReturn,
SplitToMovableBlocks,
TypeInference,
PeepholeOptimizations,
SimplifyShortCircuit,
SimplifyTernaryOperator,
SimplifyNullCoalescing,
MoreSimplifyPasses,
TransformDecimalCtorToConstant,
SimplifyLdObjAndStObj,
TransformArrayInitializers,
TransformCollectionInitializers,
MakeAssignmentExpression,
InlineVariables2,
FindLoops,
FindConditions,
FlattenNestedMovableBlocks,
RemoveRedundantCode2,
GotoRemoval,
DuplicateReturns,
FlattenIfStatements,
InlineVariables2,
PeepholeTransforms,
ReduceIfNesting,
InlineVariables3,
CachedDelegateInitialization,
IntroduceFixedStatements,
TypeInference2,
RemoveRedundantCode3,
None
}
public class ILAstOptimizer
public partial class ILAstOptimizer
{
int nextLabelIndex = 0;
DecompilerContext context;
TypeSystem typeSystem;
ILBlock method;
public void Optimize(DecompilerContext context, ILBlock method, ILAstOptimizationStep abortBeforeStep = ILAstOptimizationStep.None)
{
this.context = context;
this.typeSystem = context.CurrentMethod.Module.TypeSystem;
this.method = method;
if (abortBeforeStep == ILAstOptimizationStep.RemoveRedundantCode) return;
RemoveRedundantCode(method);
@ -77,7 +86,6 @@ namespace ICSharpCode.Decompiler.ILAst @@ -77,7 +86,6 @@ namespace ICSharpCode.Decompiler.ILAst
// Types are needed for the ternary operator optimization
TypeAnalysis.Run(context, method);
if (abortBeforeStep == ILAstOptimizationStep.PeepholeOptimizations) return;
AnalyseLabels(method);
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
bool modified;
@ -85,18 +93,48 @@ namespace ICSharpCode.Decompiler.ILAst @@ -85,18 +93,48 @@ namespace ICSharpCode.Decompiler.ILAst
modified = false;
if (abortBeforeStep == ILAstOptimizationStep.SimplifyShortCircuit) return;
modified |= block.RunPeepholeOptimization(TrySimplifyShortCircuit);
modified |= block.RunOptimization(SimplifyShortCircuit);
if (abortBeforeStep == ILAstOptimizationStep.SimplifyTernaryOperator) return;
modified |= block.RunPeepholeOptimization(TrySimplifyTernaryOperator);
modified |= block.RunOptimization(SimplifyTernaryOperator);
if (abortBeforeStep == ILAstOptimizationStep.SimplifyNullCoalescing) return;
modified |= block.RunPeepholeOptimization(TrySimplifyNullCoalescing);
modified |= block.RunOptimization(SimplifyNullCoalescing);
if (abortBeforeStep == ILAstOptimizationStep.MoreSimplifyPasses) return;
} 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;
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
new LoopsAndConditions(context).FindLoops(block);
@ -110,29 +148,44 @@ namespace ICSharpCode.Decompiler.ILAst @@ -110,29 +148,44 @@ namespace ICSharpCode.Decompiler.ILAst
if (abortBeforeStep == ILAstOptimizationStep.FlattenNestedMovableBlocks) return;
FlattenBasicBlocks(method);
if (abortBeforeStep == ILAstOptimizationStep.GotoRemoval) return;
if (abortBeforeStep == ILAstOptimizationStep.RemoveRedundantCode2) return;
RemoveRedundantCode(method);
if (abortBeforeStep == ILAstOptimizationStep.GotoRemoval) return;
new GotoRemoval().RemoveGotos(method);
if (abortBeforeStep == ILAstOptimizationStep.DuplicateReturns) return;
DuplicateReturnStatements(method);
if (abortBeforeStep == ILAstOptimizationStep.FlattenIfStatements) return;
FlattenIfStatements(method);
if (abortBeforeStep == ILAstOptimizationStep.ReduceIfNesting) return;
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
// open up additional inlining possibilities.
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;
PeepholeTransforms.Run(context, method);
if (abortBeforeStep == ILAstOptimizationStep.IntroduceFixedStatements) return;
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;
TypeAnalysis.Reset(method);
TypeAnalysis.Run(context, method);
if (abortBeforeStep == ILAstOptimizationStep.RemoveRedundantCode3) return;
GotoRemoval.RemoveRedundantCode(method);
// ReportUnassignedILRanges(method);
@ -234,59 +287,51 @@ namespace ICSharpCode.Decompiler.ILAst @@ -234,59 +287,51 @@ namespace ICSharpCode.Decompiler.ILAst
List<ILNode> basicBlocks = new List<ILNode>();
ILBasicBlock basicBlock = new ILBasicBlock() {
EntryLabel = new ILLabel() { Name = "Block_" + (nextLabelIndex++) }
EntryLabel = block.Body.FirstOrDefault() as ILLabel ?? new ILLabel() { Name = "Block_" + (nextLabelIndex++) }
};
basicBlocks.Add(basicBlock);
block.EntryGoto = new ILExpression(ILCode.Br, basicBlock.EntryLabel);
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++) {
ILNode lastNode = block.Body[i - 1];
ILNode currNode = block.Body[i];
// Insert split
// Start a new basic block if necessary
if (currNode is ILLabel ||
lastNode is ILTryCatchBlock ||
currNode is ILTryCatchBlock ||
(lastNode is ILExpression) && ((ILExpression)lastNode).IsBranch() ||
(currNode is ILExpression) && (((ILExpression)currNode).IsBranch() && ((ILExpression)currNode).Code.CanFallThough() && basicBlock.Body.Count > 0))
(lastNode is ILExpression && ((ILExpression)lastNode).IsBranch()))
{
ILBasicBlock lastBlock = basicBlock;
// Try to reuse the label
ILLabel label = currNode is ILLabel ? ((ILLabel)currNode) : new ILLabel() { Name = "Block_" + (nextLabelIndex++) };
// Terminate the last block
if (lastNode.CanFallThough()) {
// Explicit branch from one block to other
basicBlock.FallthoughGoto = new ILExpression(ILCode.Br, label);
} else if (lastNode.Match(ILCode.Br)) {
// Reuse the existing goto as FallthoughGoto
basicBlock.FallthoughGoto = (ILExpression)lastNode;
basicBlock.Body.RemoveAt(basicBlock.Body.Count - 1);
}
// Start the new block
basicBlock = new ILBasicBlock();
basicBlocks.Add(basicBlock);
basicBlock.EntryLabel = label;
}
if (currNode is ILLabel) {
// Insert as entry label
basicBlock.EntryLabel = (ILLabel)currNode;
} else {
basicBlock.EntryLabel = new ILLabel() { Name = "Block_" + (nextLabelIndex++) };
basicBlock.Body.Add(currNode);
}
// Explicit branch from one block to other
// (unless the last expression was unconditional branch)
if (!(lastNode is ILExpression) || ((ILExpression)lastNode).Code.CanFallThough()) {
lastBlock.FallthoughGoto = new ILExpression(ILCode.Br, basicBlock.EntryLabel);
}
} else {
// Add the node to the basic block
if (currNode != basicBlock.EntryLabel) {
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;
return;
}
@ -311,10 +356,9 @@ namespace ICSharpCode.Decompiler.ILAst @@ -311,10 +356,9 @@ namespace ICSharpCode.Decompiler.ILAst
}
}
// 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;
ILLabel trueLabel;
@ -327,17 +371,17 @@ namespace ICSharpCode.Decompiler.ILAst @@ -327,17 +371,17 @@ namespace ICSharpCode.Decompiler.ILAst
ILLabel falseFall;
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[falseLabel] == 1 &&
((labelToBasicBlock[trueLabel].Match(ILCode.Stloc, out trueLocVar, out trueExpr, out trueFall) &&
labelToBasicBlock[falseLabel].Match(ILCode.Stloc, out falseLocVar, out falseExpr, out falseFall)) ||
(labelToBasicBlock[trueLabel].Match(ILCode.Ret, out unused, out trueExpr, out trueFall) &&
labelToBasicBlock[falseLabel].Match(ILCode.Ret, out unused, out falseExpr, out falseFall))) &&
((labelToBasicBlock[trueLabel].MatchSingle(ILCode.Stloc, out trueLocVar, out trueExpr, out trueFall) &&
labelToBasicBlock[falseLabel].MatchSingle(ILCode.Stloc, out falseLocVar, out falseExpr, out falseFall)) ||
(labelToBasicBlock[trueLabel].MatchSingle(ILCode.Ret, out unused, out trueExpr, out trueFall) &&
labelToBasicBlock[falseLabel].MatchSingle(ILCode.Ret, out unused, out falseExpr, out falseFall))) &&
trueLocVar == falseLocVar &&
trueFall == falseFall &&
scope.Contains(labelToBasicBlock[trueLabel]) &&
scope.Contains(labelToBasicBlock[falseLabel])
body.Contains(labelToBasicBlock[trueLabel]) &&
body.Contains(labelToBasicBlock[falseLabel])
)
{
ILCode opCode = trueLocVar != null ? ILCode.Stloc : ILCode.Ret;
@ -385,12 +429,12 @@ namespace ICSharpCode.Decompiler.ILAst @@ -385,12 +429,12 @@ namespace ICSharpCode.Decompiler.ILAst
// Create ternary expression
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;
// Remove the old basic blocks
foreach(ILLabel deleteLabel in new [] { trueLabel, falseLabel }) {
scope.RemoveOrThrow(labelToBasicBlock[deleteLabel]);
body.RemoveOrThrow(labelToBasicBlock[deleteLabel]);
labelGlobalRefCount.RemoveOrThrow(deleteLabel);
labelToBasicBlock.RemoveOrThrow(deleteLabel);
}
@ -400,75 +444,62 @@ namespace ICSharpCode.Decompiler.ILAst @@ -400,75 +444,62 @@ namespace ICSharpCode.Decompiler.ILAst
return false;
}
bool TrySimplifyNullCoalescing(List<ILNode> scope, ILBasicBlock head, int index)
bool SimplifyNullCoalescing(List<ILNode> body, ILBasicBlock head, int pos)
{
// ...
// v = ldloc(leftVar)
// br(condBBLabel)
//
// condBBLabel:
// brtrue(endBBLabel, ldloc(leftVar))
// br(rightBBLabel)
//
// rightBBLabel:
// v = rightExpr
// br(endBBLabel)
//
// endBBLabel:
// ...
// =>
// ...
// v = NullCoalescing(ldloc(leftVar), rightExpr)
// br(endBBLabel)
ILVariable v, v2;
ILExpression leftExpr, leftExpr2;
ILVariable leftVar, leftVar2;
ILLabel condBBLabel;
ILBasicBlock condBB;
ILVariable leftVar;
ILLabel endBBLabel, endBBLabel2;
ILLabel rightBBLabel;
ILBasicBlock rightBB;
ILExpression rightExpr;
ILBasicBlock endBB;
if (head.Body.LastOrDefault().Match(ILCode.Stloc, out v, out leftExpr) &&
if (head.Body.Count >= 2 &&
head.Body[head.Body.Count - 2].Match(ILCode.Stloc, out v, out leftExpr) &&
leftExpr.Match(ILCode.Ldloc, out leftVar) &&
head.FallthoughGoto.Match(ILCode.Br, out condBBLabel) &&
labelToBasicBlock.TryGetValue(condBBLabel, out condBB) &&
condBB.Match(ILCode.Brtrue, out endBBLabel, out leftExpr2, out rightBBLabel) &&
leftExpr2.Match(ILCode.Ldloc, out leftVar2) &&
leftVar == leftVar2 &&
head.MatchLast(ILCode.Brtrue, out endBBLabel, out leftExpr2, out rightBBLabel) &&
leftExpr2.Match(ILCode.Ldloc, leftVar) &&
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 &&
endBBLabel == endBBLabel2 &&
labelToBasicBlock.TryGetValue(endBBLabel, out endBB) &&
labelGlobalRefCount.GetOrDefault(condBBLabel) == 1 &&
labelGlobalRefCount.GetOrDefault(rightBBLabel) == 1 &&
labelGlobalRefCount.GetOrDefault(endBBLabel) == 2 &&
scope.Contains(condBB) &&
scope.Contains(rightBB) &&
scope.Contains(endBB)
body.Contains(rightBB)
)
{
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);
foreach(ILLabel deleteLabel in new [] { condBBLabel, rightBBLabel }) {
scope.RemoveOrThrow(labelToBasicBlock[deleteLabel]);
labelGlobalRefCount.RemoveOrThrow(deleteLabel);
labelToBasicBlock.RemoveOrThrow(deleteLabel);
}
body.RemoveOrThrow(labelToBasicBlock[rightBBLabel]);
labelGlobalRefCount.RemoveOrThrow(rightBBLabel);
labelToBasicBlock.RemoveOrThrow(rightBBLabel);
return true;
}
return false;
}
// scope is modified if successful
bool TrySimplifyShortCircuit(List<ILNode> scope, ILBasicBlock head, int index)
bool SimplifyShortCircuit(List<ILNode> body, ILBasicBlock head, int pos)
{
Debug.Assert(scope.Contains(head));
Debug.Assert(body.Contains(head));
ILExpression condExpr;
ILLabel trueLabel;
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++) {
// On the second pass, swap labels and negate expression of the first branch
@ -481,24 +512,24 @@ namespace ICSharpCode.Decompiler.ILAst @@ -481,24 +512,24 @@ namespace ICSharpCode.Decompiler.ILAst
ILExpression nextCondExpr;
ILLabel nextTrueLablel;
ILLabel nextFalseLabel;
if (scope.Contains(nextBasicBlock) &&
if (body.Contains(nextBasicBlock) &&
nextBasicBlock != head &&
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))
{
// Create short cicuit branch
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 {
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);
// Remove the inlined branch from scope
labelGlobalRefCount.RemoveOrThrow(nextBasicBlock.EntryLabel);
labelToBasicBlock.RemoveOrThrow(nextBasicBlock.EntryLabel);
scope.RemoveOrThrow(nextBasicBlock);
body.RemoveOrThrow(nextBasicBlock);
return true;
}
@ -589,7 +620,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -589,7 +620,7 @@ namespace ICSharpCode.Decompiler.ILAst
/// Reduce the nesting of conditions.
/// It should be done on flat data that already had most gotos removed
/// </summary>
void FlattenIfStatements(ILNode node)
void ReduceIfNesting(ILNode node)
{
ILBlock block = node as ILBlock;
if (block != null) {
@ -624,7 +655,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -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
foreach(ILNode child in node.GetChildren()) {
if (child != null && !(child is ILExpression))
FlattenIfStatements(child);
ReduceIfNesting(child);
}
}
@ -642,7 +673,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -642,7 +673,7 @@ namespace ICSharpCode.Decompiler.ILAst
/// Perform one pass of a given optimization on this block.
/// This block must consist of only basicblocks.
/// </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;
List<ILNode> body = block.Body;
@ -657,6 +688,23 @@ namespace ICSharpCode.Decompiler.ILAst @@ -657,6 +688,23 @@ namespace ICSharpCode.Decompiler.ILAst
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)
{
ILExpression expr = node as ILExpression;
@ -689,6 +737,24 @@ namespace ICSharpCode.Decompiler.ILAst @@ -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>
/// Can the expression be used as a statement in C#?
/// </summary>

76
ICSharpCode.Decompiler/ILAst/ILInlining.cs

@ -48,41 +48,66 @@ namespace ICSharpCode.Decompiler.ILAst @@ -48,41 +48,66 @@ namespace ICSharpCode.Decompiler.ILAst
}
}
public void InlineAllVariables()
public bool InlineAllVariables()
{
bool modified = false;
ILInlining i = new ILInlining(method);
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;
for(int i = 0; i < body.Count - 1;) {
ILVariable locVar;
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
} else {
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>
/// Inlines instructions before pos into block.Body[pos].
/// </summary>
/// <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;
int count = 0;
while (--pos >= 0) {
ILExpression expr = block.Body[pos] as ILExpression;
ILExpression expr = body[pos] as ILExpression;
if (expr == null || expr.Code != ILCode.Stloc)
break;
if (InlineOneIfPossible(block, pos, aggressive))
if (InlineOneIfPossible(body, pos, aggressive))
count++;
else
break;
@ -97,10 +122,10 @@ namespace ICSharpCode.Decompiler.ILAst @@ -97,10 +122,10 @@ namespace ICSharpCode.Decompiler.ILAst
/// <remarks>
/// After the operation, pos will point to the new combined instruction.
/// </remarks>
public bool InlineIfPossible(ILBlock block, ref int pos)
public bool InlineIfPossible(List<ILNode> body, ref int pos)
{
if (InlineOneIfPossible(block, pos, true)) {
pos -= InlineInto(block, pos, false);
if (InlineOneIfPossible(body, pos, true)) {
pos -= InlineInto(body, pos, false);
return true;
}
return false;
@ -109,28 +134,28 @@ namespace ICSharpCode.Decompiler.ILAst @@ -109,28 +134,28 @@ namespace ICSharpCode.Decompiler.ILAst
/// <summary>
/// Inlines the stloc instruction at block.Body[pos] into the next instruction, if possible.
/// </summary>
public bool InlineOneIfPossible(ILBlock block, int pos, bool aggressive)
public bool InlineOneIfPossible(List<ILNode> body, int pos, bool aggressive)
{
ILVariable v;
ILExpression inlinedExpression;
if (block.Body[pos].Match(ILCode.Stloc, out v, out inlinedExpression) && !v.IsPinned) {
if (InlineIfPossible(v, inlinedExpression, block.Body.ElementAtOrDefault(pos+1), aggressive)) {
if (body[pos].Match(ILCode.Stloc, out v, out inlinedExpression) && !v.IsPinned) {
if (InlineIfPossible(v, inlinedExpression, body.ElementAtOrDefault(pos+1), aggressive)) {
// 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:
block.Body.RemoveAt(pos);
body.RemoveAt(pos);
return true;
} else if (numLdloc.GetOrDefault(v) == 0 && numLdloca.GetOrDefault(v) == 0) {
// The variable is never loaded
if (inlinedExpression.HasNoSideEffects()) {
// Remove completely
block.Body.RemoveAt(pos);
body.RemoveAt(pos);
return true;
} else if (inlinedExpression.CanBeExpressionStatement() && v.IsGenerated) {
// 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
block.Body[pos] = inlinedExpression;
body[pos] = inlinedExpression;
return true;
}
}
@ -155,7 +180,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -155,7 +180,7 @@ namespace ICSharpCode.Decompiler.ILAst
ILExpression parent;
int pos;
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;
// Assign the ranges of the ldloc instruction:
@ -168,8 +193,15 @@ namespace ICSharpCode.Decompiler.ILAst @@ -168,8 +193,15 @@ namespace ICSharpCode.Decompiler.ILAst
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) {
case ILCode.Ret:
return parent.Code == ILCode.Ret;
@ -300,7 +332,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -300,7 +332,7 @@ namespace ICSharpCode.Decompiler.ILAst
// if we un-inlined stuff; we need to update the usage counters
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;
}
}

149
ICSharpCode.Decompiler/ILAst/InitializerPeepholeTransforms.cs

@ -12,25 +12,16 @@ namespace ICSharpCode.Decompiler.ILAst @@ -12,25 +12,16 @@ namespace ICSharpCode.Decompiler.ILAst
/// <summary>
/// IL AST transformation that introduces array, object and collection initializers.
/// </summary>
public class InitializerPeepholeTransforms
public class Initializers
{
readonly ILBlock method;
#region Array Initializers
public InitializerPeepholeTransforms(ILBlock method)
{
this.method = method;
}
public void TransformArrayInitializers(ILBlock block, ref int i)
public static bool TransformArrayInitializers(List<ILNode> body, ILExpression expr, int pos)
{
ILVariable v, v2, v3;
ILExpression newarrExpr;
TypeReference arrayType;
ILExpression lengthExpr;
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) &&
lengthExpr.Match(ILCode.Ldc_I4, out arrayLength) &&
arrayLength > 0)
@ -39,7 +30,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -39,7 +30,7 @@ namespace ICSharpCode.Decompiler.ILAst
ILExpression methodArg1;
ILExpression methodArg2;
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.Name == "InitializeArray" &&
methodArg1.Match(ILCode.Ldloc, out v2) &&
@ -49,59 +40,41 @@ namespace ICSharpCode.Decompiler.ILAst @@ -49,59 +40,41 @@ namespace ICSharpCode.Decompiler.ILAst
{
ILExpression[] newArr = new ILExpression[arrayLength];
if (DecodeArrayInitializer(TypeAnalysis.GetTypeCode(arrayType), field.InitialValue, newArr)) {
block.Body[i] = new ILExpression(ILCode.Stloc, v, new ILExpression(ILCode.InitArray, arrayType, newArr));
block.Body.RemoveAt(i + 1);
i -= new ILInlining(method).InlineInto(block, i + 1, aggressive: true) - 1;
return;
body[pos] = new ILExpression(ILCode.Stloc, v, new ILExpression(ILCode.InitArray, arrayType, newArr));
body.RemoveAt(pos + 1);
return true;
}
}
const int maxConsecutiveDefaultValueExpressions = 10;
List<ILExpression> operands = new List<ILExpression>();
int numberOfInstructionsToRemove = 0;
for (int j = i + 1; j < block.Body.Count; j++) {
ILExpression expr = block.Body[j] as ILExpression;
int pos;
if (expr != null &&
IsStoreToArray(expr.Code) &&
expr.Arguments[0].Match(ILCode.Ldloc, out v3) &&
for (int j = pos + 1; j < body.Count; j++) {
ILExpression nextExpr = body[j] as ILExpression;
int arrayPos;
if (nextExpr != null &&
nextExpr.Code.IsStoreToArray() &&
nextExpr.Arguments[0].Match(ILCode.Ldloc, out v3) &&
v == v3 &&
expr.Arguments[1].Match(ILCode.Ldc_I4, out pos) &&
pos >= operands.Count &&
pos <= operands.Count + maxConsecutiveDefaultValueExpressions)
nextExpr.Arguments[1].Match(ILCode.Ldc_I4, out arrayPos) &&
arrayPos >= operands.Count &&
arrayPos <= operands.Count + maxConsecutiveDefaultValueExpressions)
{
while (operands.Count < pos)
while (operands.Count < arrayPos)
operands.Add(new ILExpression(ILCode.DefaultValue, arrayType));
operands.Add(expr.Arguments[2]);
operands.Add(nextExpr.Arguments[2]);
numberOfInstructionsToRemove++;
} else {
break;
}
}
if (operands.Count == arrayLength) {
((ILExpression)block.Body[i]).Arguments[0] = new ILExpression(ILCode.InitArray, arrayType, operands);
block.Body.RemoveRange(i + 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:
expr.Arguments[0] = new ILExpression(ILCode.InitArray, arrayType, operands);
body.RemoveRange(pos + 1, numberOfInstructionsToRemove);
return true;
default:
return false;
}
}
return false;
}
static bool DecodeArrayInitializer(TypeCode elementType, byte[] initialValue, ILExpression[] output)
@ -165,50 +138,50 @@ namespace ICSharpCode.Decompiler.ILAst @@ -165,50 +138,50 @@ namespace ICSharpCode.Decompiler.ILAst
return false;
}
}
#endregion
#region Collection Initilializers
public void TransformCollectionInitializers(ILBlock block, ref int i)
public static bool TransformCollectionInitializers(List<ILNode> body, ILExpression expr, int pos)
{
ILVariable v;
ILExpression expr;
if (!block.Body[i].Match(ILCode.Stloc, out v, out expr) || expr.Code != ILCode.Newobj)
return;
MethodReference ctor = (MethodReference)expr.Operand;
TypeDefinition td = ctor.DeclaringType.Resolve();
if (td == null || !td.Interfaces.Any(intf => intf.Name == "IEnumerable" && intf.Namespace == "System.Collections"))
return;
// This is a collection: we can convert Add() calls into a collection initializer
ILExpression collectionInitializer = new ILExpression(ILCode.InitCollection, null, expr);
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;
ILVariable v, v2;
ILExpression newObjExpr;
MethodReference ctor;
List<ILExpression> ctorArgs;
if (expr.Match(ILCode.Stloc, out v, out newObjExpr) &&
newObjExpr.Match(ILCode.Newobj, out ctor, out ctorArgs))
{
TypeDefinition td = ctor.DeclaringType.Resolve();
if (td == null || !td.Interfaces.Any(intf => intf.Name == "IEnumerable" && intf.Namespace == "System.Collections"))
return false;
// Change add methods into InitCollectionAddMethod:
for (int j = 1; j < collectionInitializer.Arguments.Count; j++) {
collectionInitializer.Arguments[j].Arguments.RemoveAt(0);
collectionInitializer.Arguments[j].Code = ILCode.InitCollectionAddMethod;
// This is a collection: we can convert Add() calls into a collection initializer
ILExpression collectionInitializer = new ILExpression(ILCode.InitCollection, null, newObjExpr);
bool anyAdded = false;
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
}
}

156
ICSharpCode.Decompiler/ILAst/LoopsAndConditions.cs

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

26
ICSharpCode.Decompiler/ILAst/PatternMatching.cs

@ -4,7 +4,7 @@ @@ -4,7 +4,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Mono.Cecil;
namespace ICSharpCode.Decompiler.ILAst
@ -28,6 +28,16 @@ namespace ICSharpCode.Decompiler.ILAst @@ -28,6 +28,16 @@ namespace ICSharpCode.Decompiler.ILAst
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)
{
ILExpression expr = node as ILExpression;
@ -88,7 +98,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -88,7 +98,7 @@ namespace ICSharpCode.Decompiler.ILAst
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[0].Match(code, out operand, out arg)) {
@ -102,6 +112,18 @@ namespace ICSharpCode.Decompiler.ILAst @@ -102,6 +112,18 @@ namespace ICSharpCode.Decompiler.ILAst
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)
{
ILVariable v;

321
ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs

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

3
ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs

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

Loading…
Cancel
Save