diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 784a0e12a..9e9a5e4f3 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -97,6 +97,7 @@ + diff --git a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs index a1555a3f9..d0b873516 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs @@ -86,20 +86,19 @@ namespace ICSharpCode.Decompiler.ILAst // Types are needed for the ternary operator optimization TypeAnalysis.Run(context, method); - AnalyseLabels(method); foreach(ILBlock block in method.GetSelfAndChildrenRecursive()) { bool modified; do { modified = false; if (abortBeforeStep == ILAstOptimizationStep.SimplifyShortCircuit) return; - modified |= block.RunOptimization(SimplifyShortCircuit); + modified |= block.RunOptimization(new SimpleControlFlow(context, method).SimplifyShortCircuit); if (abortBeforeStep == ILAstOptimizationStep.SimplifyTernaryOperator) return; - modified |= block.RunOptimization(SimplifyTernaryOperator); + modified |= block.RunOptimization(new SimpleControlFlow(context, method).SimplifyTernaryOperator); if (abortBeforeStep == ILAstOptimizationStep.SimplifyNullCoalescing) return; - modified |= block.RunOptimization(SimplifyNullCoalescing); + modified |= block.RunOptimization(new SimpleControlFlow(context, method).SimplifyNullCoalescing); } while(modified); } @@ -344,226 +343,6 @@ namespace ICSharpCode.Decompiler.ILAst return; } - Dictionary labelGlobalRefCount; - Dictionary labelToBasicBlock; - - void AnalyseLabels(ILBlock method) - { - labelGlobalRefCount = new Dictionary(); - foreach(ILLabel target in method.GetSelfAndChildrenRecursive(e => e.IsBranch()).SelectMany(e => e.GetBranchTargets())) { - if (!labelGlobalRefCount.ContainsKey(target)) - labelGlobalRefCount[target] = 0; - labelGlobalRefCount[target]++; - } - - labelToBasicBlock = new Dictionary(); - foreach(ILBasicBlock bb in method.GetSelfAndChildrenRecursive()) { - foreach(ILLabel label in bb.GetChildren().OfType()) { - labelToBasicBlock[label] = bb; - } - } - } - - bool SimplifyTernaryOperator(List body, ILBasicBlock head, int pos) - { - Debug.Assert(body.Contains(head)); - - ILExpression condExpr; - ILLabel trueLabel; - ILLabel falseLabel; - ILVariable trueLocVar = null; - ILExpression trueExpr; - ILLabel trueFall; - ILVariable falseLocVar = null; - ILExpression falseExpr; - ILLabel falseFall; - object unused; - - if (head.MatchLast(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel) && - labelGlobalRefCount[trueLabel] == 1 && - labelGlobalRefCount[falseLabel] == 1 && - ((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 && - body.Contains(labelToBasicBlock[trueLabel]) && - body.Contains(labelToBasicBlock[falseLabel]) - ) - { - ILCode opCode = trueLocVar != null ? ILCode.Stloc : ILCode.Ret; - TypeReference retType = trueLocVar != null ? trueLocVar.Type : this.context.CurrentMethod.ReturnType; - int leftBoolVal; - int rightBoolVal; - ILExpression newExpr; - // a ? true:false is equivalent to a - // a ? false:true is equivalent to !a - // a ? true : b is equivalent to a || b - // a ? b : true is equivalent to !a || b - // a ? b : false is equivalent to a && b - // a ? false : b is equivalent to !a && b - if (retType == typeSystem.Boolean && - trueExpr.Match(ILCode.Ldc_I4, out leftBoolVal) && - falseExpr.Match(ILCode.Ldc_I4, out rightBoolVal) && - ((leftBoolVal != 0 && rightBoolVal == 0) || (leftBoolVal == 0 && rightBoolVal != 0)) - ) - { - // It can be expressed as trivilal expression - if (leftBoolVal != 0) { - newExpr = condExpr; - } else { - newExpr = new ILExpression(ILCode.LogicNot, null, condExpr); - } - } else if (retType == typeSystem.Boolean && trueExpr.Match(ILCode.Ldc_I4, out leftBoolVal)) { - // It can be expressed as logical expression - if (leftBoolVal != 0) { - newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicOr, condExpr, falseExpr); - } else { - newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicAnd, new ILExpression(ILCode.LogicNot, null, condExpr), falseExpr); - } - } else if (retType == typeSystem.Boolean && falseExpr.Match(ILCode.Ldc_I4, out rightBoolVal)) { - // It can be expressed as logical expression - if (rightBoolVal != 0) { - newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicOr, new ILExpression(ILCode.LogicNot, null, condExpr), trueExpr); - } else { - newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicAnd, condExpr, trueExpr); - } - } else { - // Ternary operator tends to create long complicated return statements - if (opCode == ILCode.Ret) - return false; - - // Create ternary expression - newExpr = new ILExpression(ILCode.TernaryOp, null, condExpr, trueExpr, falseExpr); - } - - 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 }) { - body.RemoveOrThrow(labelToBasicBlock[deleteLabel]); - labelGlobalRefCount.RemoveOrThrow(deleteLabel); - labelToBasicBlock.RemoveOrThrow(deleteLabel); - } - - return true; - } - return false; - } - - bool SimplifyNullCoalescing(List body, ILBasicBlock head, int pos) - { - // ... - // v = ldloc(leftVar) - // brtrue(endBBLabel, ldloc(leftVar)) - // br(rightBBLabel) - // - // rightBBLabel: - // v = rightExpr - // br(endBBLabel) - // ... - // => - // ... - // v = NullCoalescing(ldloc(leftVar), rightExpr) - // br(endBBLabel) - - ILVariable v, v2; - ILExpression leftExpr, leftExpr2; - ILVariable leftVar; - ILLabel endBBLabel, endBBLabel2; - ILLabel rightBBLabel; - ILBasicBlock rightBB; - ILExpression rightExpr; - 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.MatchLast(ILCode.Brtrue, out endBBLabel, out leftExpr2, out rightBBLabel) && - leftExpr2.Match(ILCode.Ldloc, leftVar) && - labelToBasicBlock.TryGetValue(rightBBLabel, out rightBB) && - rightBB.MatchSingle(ILCode.Stloc, out v2, out rightExpr, out endBBLabel2) && - v == v2 && - endBBLabel == endBBLabel2 && - labelGlobalRefCount.GetOrDefault(rightBBLabel) == 1 && - body.Contains(rightBB) - ) - { - 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); - - body.RemoveOrThrow(labelToBasicBlock[rightBBLabel]); - labelGlobalRefCount.RemoveOrThrow(rightBBLabel); - labelToBasicBlock.RemoveOrThrow(rightBBLabel); - return true; - } - return false; - } - - bool SimplifyShortCircuit(List body, ILBasicBlock head, int pos) - { - Debug.Assert(body.Contains(head)); - - ILExpression condExpr; - ILLabel trueLabel; - ILLabel 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 - // It is slightly ugly, but much better then copy-pasting this whole block - ILLabel nextLabel = (pass == 0) ? trueLabel : falseLabel; - ILLabel otherLablel = (pass == 0) ? falseLabel : trueLabel; - bool negate = (pass == 1); - - ILBasicBlock nextBasicBlock = labelToBasicBlock[nextLabel]; - ILExpression nextCondExpr; - ILLabel nextTrueLablel; - ILLabel nextFalseLabel; - if (body.Contains(nextBasicBlock) && - nextBasicBlock != head && - labelGlobalRefCount[nextBasicBlock.EntryLabel] == 1 && - nextBasicBlock.MatchSingle(ILCode.Brtrue, out nextTrueLablel, out nextCondExpr, out nextFalseLabel) && - (otherLablel == nextFalseLabel || otherLablel == nextTrueLablel)) - { - // Create short cicuit branch - ILExpression logicExpr; - if (otherLablel == nextFalseLabel) { - logicExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicAnd, negate ? new ILExpression(ILCode.LogicNot, null, condExpr) : condExpr, nextCondExpr); - } else { - logicExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicOr, negate ? condExpr : new ILExpression(ILCode.LogicNot, null, condExpr), nextCondExpr); - } - head.Body[head.Body.Count - 1] = new ILExpression(ILCode.Brtrue, nextTrueLablel, logicExpr); - head.FallthoughGoto = new ILExpression(ILCode.Br, nextFalseLabel); - - // Remove the inlined branch from scope - labelGlobalRefCount.RemoveOrThrow(nextBasicBlock.EntryLabel); - labelToBasicBlock.RemoveOrThrow(nextBasicBlock.EntryLabel); - body.RemoveOrThrow(nextBasicBlock); - - return true; - } - } - } - return false; - } - - ILExpression MakeLeftAssociativeShortCircuit(ILCode code, ILExpression left, ILExpression right) - { - // Assuming that the inputs are already left associative - if (right.Match(code)) { - // Find the leftmost logical expression - ILExpression current = right; - while(current.Arguments[0].Match(code)) - current = current.Arguments[0]; - current.Arguments[0] = new ILExpression(code, null, left, current.Arguments[0]); - return right; - } else { - return new ILExpression(code, null, left, right); - } - } - void DuplicateReturnStatements(ILBlock method) { Dictionary nextSibling = new Dictionary(); diff --git a/ICSharpCode.Decompiler/ILAst/SimpleControlFlow.cs b/ICSharpCode.Decompiler/ILAst/SimpleControlFlow.cs new file mode 100644 index 000000000..456d06025 --- /dev/null +++ b/ICSharpCode.Decompiler/ILAst/SimpleControlFlow.cs @@ -0,0 +1,233 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under MIT X11 license (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; + +using Mono.Cecil; + +namespace ICSharpCode.Decompiler.ILAst +{ + public class SimpleControlFlow + { + Dictionary labelGlobalRefCount = new Dictionary(); + Dictionary labelToBasicBlock = new Dictionary(); + + DecompilerContext context; + TypeSystem typeSystem; + + public SimpleControlFlow(DecompilerContext context, ILBlock method) + { + this.context = context; + this.typeSystem = context.CurrentMethod.Module.TypeSystem; + + foreach(ILLabel target in method.GetSelfAndChildrenRecursive(e => e.IsBranch()).SelectMany(e => e.GetBranchTargets())) { + labelGlobalRefCount[target] = labelGlobalRefCount.GetOrDefault(target) + 1; + } + foreach(ILBasicBlock bb in method.GetSelfAndChildrenRecursive()) { + foreach(ILLabel label in bb.GetChildren().OfType()) { + labelToBasicBlock[label] = bb; + } + } + } + + public bool SimplifyTernaryOperator(List body, ILBasicBlock head, int pos) + { + Debug.Assert(body.Contains(head)); + + ILExpression condExpr; + ILLabel trueLabel; + ILLabel falseLabel; + ILVariable trueLocVar = null; + ILExpression trueExpr; + ILLabel trueFall; + ILVariable falseLocVar = null; + ILExpression falseExpr; + ILLabel falseFall; + object unused; + + if (head.MatchLast(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel) && + labelGlobalRefCount[trueLabel] == 1 && + labelGlobalRefCount[falseLabel] == 1 && + ((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 && + body.Contains(labelToBasicBlock[trueLabel]) && + body.Contains(labelToBasicBlock[falseLabel]) + ) + { + ILCode opCode = trueLocVar != null ? ILCode.Stloc : ILCode.Ret; + TypeReference retType = trueLocVar != null ? trueLocVar.Type : this.context.CurrentMethod.ReturnType; + int leftBoolVal; + int rightBoolVal; + ILExpression newExpr; + // a ? true:false is equivalent to a + // a ? false:true is equivalent to !a + // a ? true : b is equivalent to a || b + // a ? b : true is equivalent to !a || b + // a ? b : false is equivalent to a && b + // a ? false : b is equivalent to !a && b + if (retType == typeSystem.Boolean && + trueExpr.Match(ILCode.Ldc_I4, out leftBoolVal) && + falseExpr.Match(ILCode.Ldc_I4, out rightBoolVal) && + ((leftBoolVal != 0 && rightBoolVal == 0) || (leftBoolVal == 0 && rightBoolVal != 0)) + ) + { + // It can be expressed as trivilal expression + if (leftBoolVal != 0) { + newExpr = condExpr; + } else { + newExpr = new ILExpression(ILCode.LogicNot, null, condExpr); + } + } else if (retType == typeSystem.Boolean && trueExpr.Match(ILCode.Ldc_I4, out leftBoolVal)) { + // It can be expressed as logical expression + if (leftBoolVal != 0) { + newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicOr, condExpr, falseExpr); + } else { + newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicAnd, new ILExpression(ILCode.LogicNot, null, condExpr), falseExpr); + } + } else if (retType == typeSystem.Boolean && falseExpr.Match(ILCode.Ldc_I4, out rightBoolVal)) { + // It can be expressed as logical expression + if (rightBoolVal != 0) { + newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicOr, new ILExpression(ILCode.LogicNot, null, condExpr), trueExpr); + } else { + newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicAnd, condExpr, trueExpr); + } + } else { + // Ternary operator tends to create long complicated return statements + if (opCode == ILCode.Ret) + return false; + + // Only simplify generated variables + if (opCode == ILCode.Stloc && !trueLocVar.IsGenerated) + return false; + + // Create ternary expression + newExpr = new ILExpression(ILCode.TernaryOp, null, condExpr, trueExpr, falseExpr); + } + + 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 + body.RemoveOrThrow(labelToBasicBlock[trueLabel]); + body.RemoveOrThrow(labelToBasicBlock[falseLabel]); + + return true; + } + return false; + } + + public bool SimplifyNullCoalescing(List body, ILBasicBlock head, int pos) + { + // ... + // v = ldloc(leftVar) + // brtrue(endBBLabel, ldloc(leftVar)) + // br(rightBBLabel) + // + // rightBBLabel: + // v = rightExpr + // br(endBBLabel) + // ... + // => + // ... + // v = NullCoalescing(ldloc(leftVar), rightExpr) + // br(endBBLabel) + + ILVariable v, v2; + ILExpression leftExpr, leftExpr2; + ILVariable leftVar; + ILLabel endBBLabel, endBBLabel2; + ILLabel rightBBLabel; + ILBasicBlock rightBB; + ILExpression rightExpr; + 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.MatchLast(ILCode.Brtrue, out endBBLabel, out leftExpr2, out rightBBLabel) && + leftExpr2.Match(ILCode.Ldloc, leftVar) && + labelToBasicBlock.TryGetValue(rightBBLabel, out rightBB) && + rightBB.MatchSingle(ILCode.Stloc, out v2, out rightExpr, out endBBLabel2) && + v == v2 && + endBBLabel == endBBLabel2 && + labelGlobalRefCount.GetOrDefault(rightBBLabel) == 1 && + body.Contains(rightBB) + ) + { + 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); + + body.RemoveOrThrow(labelToBasicBlock[rightBBLabel]); + return true; + } + return false; + } + + public bool SimplifyShortCircuit(List body, ILBasicBlock head, int pos) + { + Debug.Assert(body.Contains(head)); + + ILExpression condExpr; + ILLabel trueLabel; + ILLabel 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 + // It is slightly ugly, but much better then copy-pasting this whole block + ILLabel nextLabel = (pass == 0) ? trueLabel : falseLabel; + ILLabel otherLablel = (pass == 0) ? falseLabel : trueLabel; + bool negate = (pass == 1); + + ILBasicBlock nextBasicBlock = labelToBasicBlock[nextLabel]; + ILExpression nextCondExpr; + ILLabel nextTrueLablel; + ILLabel nextFalseLabel; + if (body.Contains(nextBasicBlock) && + nextBasicBlock != head && + labelGlobalRefCount[nextBasicBlock.EntryLabel] == 1 && + nextBasicBlock.MatchSingle(ILCode.Brtrue, out nextTrueLablel, out nextCondExpr, out nextFalseLabel) && + (otherLablel == nextFalseLabel || otherLablel == nextTrueLablel)) + { + // Create short cicuit branch + ILExpression logicExpr; + if (otherLablel == nextFalseLabel) { + logicExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicAnd, negate ? new ILExpression(ILCode.LogicNot, null, condExpr) : condExpr, nextCondExpr); + } else { + logicExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicOr, negate ? condExpr : new ILExpression(ILCode.LogicNot, null, condExpr), nextCondExpr); + } + head.Body[head.Body.Count - 1] = new ILExpression(ILCode.Brtrue, nextTrueLablel, logicExpr); + head.FallthoughGoto = new ILExpression(ILCode.Br, nextFalseLabel); + + // Remove the inlined branch from scope + body.RemoveOrThrow(nextBasicBlock); + + return true; + } + } + } + return false; + } + + ILExpression MakeLeftAssociativeShortCircuit(ILCode code, ILExpression left, ILExpression right) + { + // Assuming that the inputs are already left associative + if (right.Match(code)) { + // Find the leftmost logical expression + ILExpression current = right; + while(current.Arguments[0].Match(code)) + current = current.Arguments[0]; + current.Arguments[0] = new ILExpression(code, null, left, current.Arguments[0]); + return right; + } else { + return new ILExpression(code, null, left, right); + } + } + } +}