From 05b0b427d6e79bdff3eaaaedb13c217f3165a1f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Srbeck=C3=BD?= Date: Sat, 26 Feb 2011 19:02:41 +0000 Subject: [PATCH] Peephole detection of the ternary operator (?:) --- .../Ast/AstMethodBodyBuilder.cs | 6 + .../ILAst/ILAstOptimizer.cs | 132 ++++++++++++++---- ICSharpCode.Decompiler/ILAst/ILCodes.cs | 1 + ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs | 8 ++ 4 files changed, 123 insertions(+), 24 deletions(-) diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index d08c4972a..20f8eed57 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -285,6 +285,12 @@ namespace Decompiler new Ast.GotoStatement(((ILLabel)byteCode.Operand).Name) } }; + case ILCode.TernaryOp: + return new Ast.ConditionalExpression() { + Condition = MakeBranchCondition(byteCode.Arguments[0]), + TrueExpression = (Expression)TransformExpression(byteCode.Arguments[1]), + FalseExpression = (Expression)TransformExpression(byteCode.Arguments[2]), + }; } List args = TransformExpressionArguments(byteCode); diff --git a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs index 912292375..6fa5b760e 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs @@ -12,7 +12,7 @@ namespace Decompiler.ControlFlow public enum ILAstOptimizationStep { SplitToMovableBlocks, - ShortCircuits, + PeepholeOptimizations, FindLoops, FindConditions, FlattenNestedMovableBlocks, @@ -34,8 +34,11 @@ namespace Decompiler.ControlFlow SplitToBasicBlocks(block); } - if (abortBeforeStep == ILAstOptimizationStep.ShortCircuits) return; - OptimizeShortCircuits(method); + if (abortBeforeStep == ILAstOptimizationStep.PeepholeOptimizations) return; + AnalyseLabels(method); + foreach(ILBlock block in method.GetSelfAndChildrenRecursive().ToList()) { + PeepholeOptimizations(block); + } if (abortBeforeStep == ILAstOptimizationStep.FindLoops) return; foreach(ILBlock block in method.GetSelfAndChildrenRecursive().ToList()) { @@ -132,29 +135,21 @@ namespace Decompiler.ControlFlow } } + 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; } - void OptimizeShortCircuits(ILBlock method) - { - AnalyseLabels(method); - - foreach(ILBlock block in method.GetSelfAndChildrenRecursive().ToList()) { - bool modified; - do { - modified = false; - for (int i = 0; i < block.Body.Count;) { - if (TrySimplifyShortCircuit(block.Body, (ILBasicBlock)block.Body[i])) { - modified = true; - } else { - i++; - } - } - } while(modified); - } - } - Dictionary labelGlobalRefCount; Dictionary labelToBasicBlock; @@ -175,11 +170,34 @@ namespace Decompiler.ControlFlow } } + void PeepholeOptimizations(ILBlock block) + { + bool modified; + do { + modified = false; + for (int i = 0; i < block.Body.Count;) { + if (TrySimplifyShortCircuit(block.Body, (ILBasicBlock)block.Body[i])) { + modified = true; + continue; + } + if (TrySimplifyTernaryOperator(block.Body, (ILBasicBlock)block.Body[i])) { + modified = true; + continue; + } + i++; + } + } while(modified); + } + bool IsConditionalBranch(ILBasicBlock bb, ref ILExpression branchExpr, ref ILLabel trueLabel, ref ILLabel falseLabel) { if (bb.Body.Count == 1) { branchExpr = bb.Body[0] as ILExpression; - if (branchExpr != null && branchExpr.Operand is ILLabel && branchExpr.Arguments.Count > 0) { + if (branchExpr != null && + branchExpr.Operand is ILLabel && + branchExpr.Arguments.Count > 0 && + branchExpr.Prefixes == null) + { trueLabel = (ILLabel)branchExpr.Operand; falseLabel = (ILLabel)((ILExpression)bb.FallthoughGoto).Operand; return true; @@ -188,6 +206,71 @@ namespace Decompiler.ControlFlow return false; } + bool IsStloc(ILBasicBlock bb, ref ILVariable locVar, ref ILExpression val, ref ILLabel fallLabel) + { + if (bb.Body.Count == 1) { + ILExpression expr = bb.Body[0] as ILExpression; + if (expr != null && + expr.Code == ILCode.Stloc && + expr.Prefixes == null) + { + locVar = (ILVariable)expr.Operand; + val = expr.Arguments[0]; + fallLabel = (ILLabel)bb.FallthoughGoto.Operand; + return true; + } + } + return false; + } + + // scope is modified if successful + bool TrySimplifyTernaryOperator(List scope, ILBasicBlock head) + { + Debug.Assert(scope.Contains(head)); + + ILExpression branchExpr = null; + ILLabel trueLabel = null; + ILLabel falseLabel = null; + ILVariable trueLocVar = null; + ILExpression trueExpr = null; + ILLabel trueFall = null; + ILVariable falseLocVar = null; + ILExpression falseExpr = null; + ILLabel falseFall = null; + + if(IsConditionalBranch(head, ref branchExpr, ref trueLabel, ref falseLabel) && + labelGlobalRefCount[trueLabel] == 1 && + labelGlobalRefCount[falseLabel] == 1 && + IsStloc(labelToBasicBlock[trueLabel], ref trueLocVar, ref trueExpr, ref trueFall) && + IsStloc(labelToBasicBlock[falseLabel], ref falseLocVar, ref falseExpr, ref falseFall) && + trueLocVar == falseLocVar && + trueFall == falseFall) + { + // Create the ternary expression + head.Body = new List() { + new ILExpression(ILCode.Stloc, trueLocVar, + new ILExpression(ILCode.TernaryOp, null, + new ILExpression(branchExpr.Code, null, branchExpr.Arguments.ToArray()), + trueExpr, + falseExpr + ) + ) + }; + head.FallthoughGoto = new ILExpression(ILCode.Br, trueFall); + + // Remove the old basic blocks + scope.Remove(labelToBasicBlock[trueLabel]); + scope.Remove(labelToBasicBlock[falseLabel]); + labelToBasicBlock.Remove(trueLabel); + labelToBasicBlock.Remove(falseLabel); + labelGlobalRefCount.Remove(trueLabel); + labelGlobalRefCount.Remove(falseLabel); + + return true; + } + return false; + } + // scope is modified if successful bool TrySimplifyShortCircuit(List scope, ILBasicBlock head) { @@ -228,7 +311,8 @@ namespace Decompiler.ControlFlow head.FallthoughGoto = new ILExpression(ILCode.Br, nextFalseLabel); // Remove the inlined branch from scope - labelGlobalRefCount[nextBasicBlock.EntryLabel] = 0; + labelGlobalRefCount.Remove(nextBasicBlock.EntryLabel); + labelToBasicBlock.Remove(nextBasicBlock.EntryLabel); if (!scope.Remove(nextBasicBlock)) throw new Exception("Element not found"); diff --git a/ICSharpCode.Decompiler/ILAst/ILCodes.cs b/ICSharpCode.Decompiler/ILAst/ILCodes.cs index 8010821ba..75f9b1239 100644 --- a/ICSharpCode.Decompiler/ILAst/ILCodes.cs +++ b/ICSharpCode.Decompiler/ILAst/ILCodes.cs @@ -259,6 +259,7 @@ namespace Decompiler BrLogicAnd, BrLogicOr, InitArray, // Array Initializer + TernaryOp, // ?: Pattern // used for ILAst pattern nodes } diff --git a/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs b/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs index 1cbc9d441..14a4c346e 100644 --- a/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs +++ b/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs @@ -119,6 +119,14 @@ namespace Decompiler InferTypeForExpression(expr.Arguments[1], typeSystem.Boolean); } return null; + case ILCode.TernaryOp: + if (forceInferChildren) { + InferTypeForExpression(expr.Arguments[0], typeSystem.Boolean); + } + return TypeWithMoreInformation( + InferTypeForExpression(expr.Arguments[1], expectedType, forceInferChildren), + InferTypeForExpression(expr.Arguments[2], expectedType, forceInferChildren) + ); #endregion #region Variable load/store case ILCode.Stloc: