From d518b07f8bd6d9f6965791b7a2357d18de8a973d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Srbeck=C3=BD?= Date: Sat, 12 Mar 2011 15:42:52 +0000 Subject: [PATCH 1/4] Use short circuit logic instead of ternary operator if possible --- .../ILAst/ILAstOptimizer.cs | 95 +++++++++++++------ 1 file changed, 68 insertions(+), 27 deletions(-) diff --git a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs index 95efd0ef8..7238c15f9 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs @@ -17,6 +17,7 @@ namespace ICSharpCode.Decompiler.ILAst CopyPropagation, YieldReturn, SplitToMovableBlocks, + TypeInference, PeepholeOptimizations, FindLoops, FindConditions, @@ -26,7 +27,7 @@ namespace ICSharpCode.Decompiler.ILAst FlattenIfStatements, InlineVariables2, PeepholeTransforms, - TypeInference, + TypeInference2, None } @@ -36,8 +37,12 @@ namespace ICSharpCode.Decompiler.ILAst Dictionary labelToCfNode = new Dictionary(); + TypeSystem typeSystem; + public void Optimize(DecompilerContext context, ILBlock method, ILAstOptimizationStep abortBeforeStep = ILAstOptimizationStep.None) { + this.typeSystem = context.CurrentMethod.Module.TypeSystem; + if (abortBeforeStep == ILAstOptimizationStep.SimpleGotoAndNopRemoval) return; SimpleGotoAndNopRemoval(method); @@ -64,10 +69,19 @@ namespace ICSharpCode.Decompiler.ILAst SplitToBasicBlocks(block); } + if (abortBeforeStep == ILAstOptimizationStep.TypeInference) return; + // 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().ToList()) { - PeepholeOptimizations(block); + bool modified; + do { + modified = false; + modified |= block.RunPeepholeOptimization(TrySimplifyShortCircuit); + modified |= block.RunPeepholeOptimization(TrySimplifyTernaryOperator); + } while(modified); } if (abortBeforeStep == ILAstOptimizationStep.FindLoops) return; @@ -109,10 +123,15 @@ namespace ICSharpCode.Decompiler.ILAst // open up additional inlining possibilities. new ILInlining(method).InlineAllVariables(); + foreach (ILExpression expr in method.GetSelfAndChildrenRecursive()) { + expr.InferredType = null; + expr.ExpectedType = null; + } + if (abortBeforeStep == ILAstOptimizationStep.PeepholeTransforms) return; PeepholeTransforms.Run(context, method); - if (abortBeforeStep == ILAstOptimizationStep.TypeInference) return; + if (abortBeforeStep == ILAstOptimizationStep.TypeInference2) return; TypeAnalysis.Run(context, method); GotoRemoval.RemoveRedundantCode(method); @@ -265,28 +284,8 @@ namespace ICSharpCode.Decompiler.ILAst } } - void PeepholeOptimizations(ILBlock block) - { - bool modified; - do { - modified = false; - for (int i = 0; i < block.Body.Count;) { - ILBasicBlock bb = (ILBasicBlock)block.Body[i]; - if (TrySimplifyShortCircuit(block.Body, bb)) { - modified = true; - continue; - } - if (TrySimplifyTernaryOperator(block.Body, bb)) { - modified = true; - continue; - } - i++; - } - } while(modified); - } - // scope is modified if successful - bool TrySimplifyTernaryOperator(List scope, ILBasicBlock head) + bool TrySimplifyTernaryOperator(List scope, ILBasicBlock head, int index) { Debug.Assert(scope.Contains(head)); @@ -308,8 +307,31 @@ namespace ICSharpCode.Decompiler.ILAst trueLocVar == falseLocVar && trueFall == falseFall) { - // Create the ternary expression - head.Body = new List() { new ILExpression(ILCode.Stloc, trueLocVar, new ILExpression(ILCode.TernaryOp, null, condExpr, trueExpr, falseExpr)) }; + int boolVal; + ILExpression newExpr; + // a ? true : b is equvalent to a || b + // a ? b : true is equvalent to !a || b + // a ? b : false is equvalent to a && b + // a ? false : b is equvalent to !a && b + if (trueLocVar.Type == typeSystem.Boolean && trueExpr.Match(ILCode.Ldc_I4, out boolVal)) { + // It can be expressed as logical expression + if (boolVal != 0) { + newExpr = new ILExpression(ILCode.LogicOr, null, condExpr, falseExpr); + } else { + newExpr = new ILExpression(ILCode.LogicAnd, null, new ILExpression(ILCode.LogicNot, null, condExpr), falseExpr); + } + } else if (trueLocVar.Type == typeSystem.Boolean && falseExpr.Match(ILCode.Ldc_I4, out boolVal)) { + // It can be expressed as logical expression + if (boolVal != 0) { + newExpr = new ILExpression(ILCode.LogicOr, null, new ILExpression(ILCode.LogicNot, null, condExpr), trueExpr); + } else { + newExpr = new ILExpression(ILCode.LogicAnd, null, condExpr, trueExpr); + } + } else { + // Create ternary expression + newExpr = new ILExpression(ILCode.TernaryOp, null, condExpr, trueExpr, falseExpr); + } + head.Body = new List() { new ILExpression(ILCode.Stloc, trueLocVar, newExpr) }; head.FallthoughGoto = new ILExpression(ILCode.Br, trueFall); // Remove the old basic blocks @@ -326,7 +348,7 @@ namespace ICSharpCode.Decompiler.ILAst } // scope is modified if successful - bool TrySimplifyShortCircuit(List scope, ILBasicBlock head) + bool TrySimplifyShortCircuit(List scope, ILBasicBlock head, int index) { Debug.Assert(scope.Contains(head)); @@ -977,6 +999,25 @@ namespace ICSharpCode.Decompiler.ILAst return false; } + /// + /// Perform one pass of a given optimization on this block. + /// This block must consist of only basicblocks. + /// + public static bool RunPeepholeOptimization(this ILBlock block, Func, ILBasicBlock, int, bool> optimization) + { + bool modified = false; + List body = block.Body; + for (int i = 0; i < body.Count;) { + if (optimization(body, (ILBasicBlock)body[i], i)) { + modified = true; + i = Math.Max(0, i - 1); // Go back one step + } else { + i++; + } + } + return modified; + } + public static bool CanFallThough(this ILNode node) { ILExpression expr = node as ILExpression; From 93d1a30ebd6e7adaf79387e6b235ed7240ee4afd Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 12 Mar 2011 16:36:52 +0100 Subject: [PATCH 2/4] Fix creation of attribute argument for [MarshalAs]. Closes #78. --- ICSharpCode.Decompiler/Ast/AstBuilder.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/ICSharpCode.Decompiler/Ast/AstBuilder.cs b/ICSharpCode.Decompiler/Ast/AstBuilder.cs index 8fc292410..a7b5128e1 100644 --- a/ICSharpCode.Decompiler/Ast/AstBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstBuilder.cs @@ -992,12 +992,8 @@ namespace ICSharpCode.Decompiler.Ast { MarshalInfo marshalInfo = marshalInfoProvider.MarshalInfo; Ast.Attribute attr = CreateNonCustomAttribute(typeof(MarshalAsAttribute), module); - string memberName; - if (marshalInfo.NativeType == NativeType.Boolean) - memberName = "Bool"; - else - memberName = marshalInfo.NativeType.ToString(); - attr.Arguments.Add(new IdentifierExpression("UnmanagedType").Member(memberName)); + var unmanagedType = new TypeReference("System.Runtime.InteropServices", "UnmanagedType", module, module.TypeSystem.Corlib); + attr.Arguments.Add(MakePrimitive((int)marshalInfo.NativeType, unmanagedType)); return attr; } #endregion From a642c61a4e31b928ce48984ecd0d807e686d29f7 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 12 Mar 2011 17:31:41 +0100 Subject: [PATCH 3/4] Enable using patterns in place of catch clauses. --- .../CSharp/Ast/PatternMatching/Match.cs | 2 ++ .../CSharp/Ast/PatternMatching/Pattern.cs | 5 ++++ .../CSharp/Ast/PatternMatching/Placeholder.cs | 29 +++++++++++++++++++ 3 files changed, 36 insertions(+) diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Match.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Match.cs index 9b1ff905f..5e76a428c 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Match.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Match.cs @@ -12,6 +12,8 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching /// public sealed class Match { + // TODO: maybe we should add an implicit Match->bool conversion? (operator implicit bool(Match m) { return m != null; }) + List> results = new List>(); internal int CheckPoint() diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Pattern.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Pattern.cs index aa8d95fb7..6d893b58c 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Pattern.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Pattern.cs @@ -79,6 +79,11 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching return p != null ? new SwitchSectionPlaceholder(p) : null; } + public static implicit operator CatchClause(Pattern p) + { + return p != null ? new CatchClausePlaceholder(p) : null; + } + // Make debugging easier by giving Patterns a ToString() implementation public override string ToString() { diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Placeholder.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Placeholder.cs index 2e3c2b592..b30ca58f2 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Placeholder.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Placeholder.cs @@ -210,4 +210,33 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching return child.DoMatchCollection(role, pos, match, backtrackingStack); } } + + sealed class CatchClausePlaceholder : CatchClause + { + readonly AstNode child; + + public CatchClausePlaceholder(AstNode child) + { + this.child = child; + } + + public override NodeType NodeType { + get { return NodeType.Placeholder; } + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return ((IPatternAstVisitor)visitor).VisitPlaceholder(this, child, data); + } + + protected internal override bool DoMatch(AstNode other, Match match) + { + return child.DoMatch(other, match); + } + + internal override bool DoMatchCollection(Role role, AstNode pos, Match match, Stack backtrackingStack) + { + return child.DoMatchCollection(role, pos, match, backtrackingStack); + } + } } From bf6e4bbfd95d87e1da64137fbcb5c12f4df79754 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 12 Mar 2011 17:31:47 +0100 Subject: [PATCH 4/4] Transform 'try { try {} catch {} } finally {}' to 'try {} catch {} finally {}'. Closes #77. --- .../Transforms/PatternStatementTransform.cs | 29 +++++++++++++++++++ .../Tests/ExceptionHandling.cs | 24 +++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs b/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs index 33786fcc2..862856915 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs @@ -40,6 +40,8 @@ namespace ICSharpCode.Decompiler.Ast.Transforms TransformAutomaticProperties(compilationUnit); if (context.Settings.AutomaticEvents) TransformAutomaticEvents(compilationUnit); + + TransformTryCatchFinally(compilationUnit); } /// @@ -652,6 +654,33 @@ namespace ICSharpCode.Decompiler.Ast.Transforms } #endregion + #region Try-Catch-Finally + static readonly TryCatchStatement tryCatchFinallyPattern = new TryCatchStatement { + TryBlock = new BlockStatement { + new TryCatchStatement { + TryBlock = new AnyNode(), + CatchClauses = { new Repeat(new AnyNode()) } + } + }, + FinallyBlock = new AnyNode() + }; + + /// + /// Simplify nested 'try { try {} catch {} } finally {}'. + /// This transformation must run after the using/lock tranformations. + /// + void TransformTryCatchFinally(AstNode compilationUnit) + { + foreach (var tryFinally in compilationUnit.Descendants.OfType()) { + if (tryCatchFinallyPattern.Match(tryFinally) != null) { + TryCatchStatement tryCatch = (TryCatchStatement)tryFinally.TryBlock.Statements.Single(); + tryFinally.TryBlock = tryCatch.TryBlock.Detach(); + tryCatch.CatchClauses.MoveTo(tryFinally.CatchClauses); + } + } + } + #endregion + #region Pattern Matching Helpers sealed class TypePattern : Pattern { diff --git a/ICSharpCode.Decompiler/Tests/ExceptionHandling.cs b/ICSharpCode.Decompiler/Tests/ExceptionHandling.cs index 21b259126..65717b2bb 100644 --- a/ICSharpCode.Decompiler/Tests/ExceptionHandling.cs +++ b/ICSharpCode.Decompiler/Tests/ExceptionHandling.cs @@ -22,4 +22,28 @@ public class ExceptionHandling throw; } } + + public void TryCatchFinally() + { + try { + Console.WriteLine("Try"); + } catch (Exception ex) { + Console.WriteLine(ex.Message); + } finally { + Console.WriteLine("Finally"); + } + } + + public void TryCatchMultipleHandlers() + { + try { + Console.WriteLine("Try"); + } catch (InvalidOperationException ex) { + Console.WriteLine(ex.Message); + } catch (Exception ex) { + Console.WriteLine(ex.Message); + } catch { + Console.WriteLine("other"); + } + } }