Implement optimization where lambdas can be replaced by direct method-group they...
authorMarek Safar <marek.safar@gmail.com>
Fri, 15 Mar 2013 14:25:25 +0000 (15:25 +0100)
committerMarek Safar <marek.safar@gmail.com>
Fri, 15 Mar 2013 14:28:49 +0000 (15:28 +0100)
mcs/mcs/anonymous.cs
mcs/mcs/convert.cs
mcs/mcs/delegate.cs
mcs/mcs/ecore.cs
mcs/mcs/expression.cs
mcs/mcs/statement.cs
mcs/tests/gtest-lambda-31.cs [new file with mode: 0644]

index 0c855c51cbd2b12ff5d40023100beedaa2cee698..fe16344ae0dccfeac67792434e9433441d1ded99 100644 (file)
@@ -1224,6 +1224,17 @@ namespace Mono.CSharp {
                                        }
                                } else {
                                        am = body.Compatible (ec);
+
+                                       if (body.DirectMethodGroupConversion != null) {
+                                               var errors_printer = new SessionReportPrinter ();
+                                               var old = ec.Report.SetPrinter (errors_printer);
+                                               var expr = new ImplicitDelegateCreation (delegate_type, body.DirectMethodGroupConversion, loc) {
+                                                       AllowSpecialMethodsInvocation = true
+                                               }.Resolve (ec);
+                                               ec.Report.SetPrinter (old);
+                                               if (expr != null && errors_printer.ErrorsCount == 0)
+                                                       am = expr;
+                                       }
                                }
                        } catch (CompletionResult) {
                                throw;
@@ -1562,6 +1573,14 @@ namespace Mono.CSharp {
                        get { return "anonymous method"; }
                }
 
+               //
+               // Method-group instance for lambdas which can be replaced with
+               // simple method group call
+               //
+               public MethodGroupExpr DirectMethodGroupConversion {
+                       get; set;
+               }
+
                public override bool IsIterator {
                        get {
                                return false;
@@ -1584,7 +1603,9 @@ namespace Mono.CSharp {
                }
 
                public override AnonymousMethodStorey Storey {
-                       get { return storey; }
+                       get {
+                               return storey;
+                       }
                }
 
                #endregion
index 3b4a4c77c66c053db162c7b938c7ada1612104da..3c5ebeae4b3cbeb7ec73acdd7fe7c1cd6a122f3e 100644 (file)
@@ -1310,8 +1310,7 @@ namespace Mono.CSharp {
                                if (ec.Module.Compiler.Settings.Version != LanguageVersion.ISO_1){
                                        MethodGroupExpr mg = expr as MethodGroupExpr;
                                        if (mg != null)
-                                               return ImplicitDelegateCreation.Create (
-                                                       ec, mg, target_type, loc);
+                                               return new ImplicitDelegateCreation (target_type, mg, loc).Resolve (ec);
                                }
                        }
 
index 8e10593e383ef9043d2bd151a2f57fbf96086d54..2731635683d37c21926e1656fec4f6f08714a01a 100644 (file)
@@ -436,6 +436,8 @@ namespace Mono.CSharp {
                protected MethodSpec constructor_method;
                protected MethodGroupExpr method_group;
 
+               public bool AllowSpecialMethodsInvocation { get; set; }
+
                public override bool ContainsEmitWithAwait ()
                {
                        return false;
@@ -511,7 +513,8 @@ namespace Mono.CSharp {
                                return null;
                        }               
                        
-                       Invocation.IsSpecialMethodInvocation (ec, delegate_method, loc);
+                       if (!AllowSpecialMethodsInvocation)
+                               Invocation.IsSpecialMethodInvocation (ec, delegate_method, loc);
 
                        ExtensionMethodGroupExpr emg = method_group as ExtensionMethodGroupExpr;
                        if (emg != null) {
@@ -642,18 +645,11 @@ namespace Mono.CSharp {
        //
        public class ImplicitDelegateCreation : DelegateCreation
        {
-               ImplicitDelegateCreation (TypeSpec t, MethodGroupExpr mg, Location l)
+               public ImplicitDelegateCreation (TypeSpec delegateType, MethodGroupExpr mg, Location loc)
                {
-                       type = t;
+                       type = delegateType;
                        this.method_group = mg;
-                       loc = l;
-               }
-
-               static public Expression Create (ResolveContext ec, MethodGroupExpr mge,
-                                                TypeSpec target_type, Location loc)
-               {
-                       ImplicitDelegateCreation d = new ImplicitDelegateCreation (target_type, mge, loc);
-                       return d.DoResolve (ec);
+                       this.loc = loc;
                }
        }
        
index 9a91c0d21e5b9c4c50e7d9082d78a7fafcd876e1..ee025092c4411518faece3de3eb6a921000ffa94 100644 (file)
@@ -162,6 +162,25 @@ namespace Mono.CSharp {
                        }
                }
 
+               public virtual MethodGroupExpr CanReduceLambda (AnonymousMethodBody body)
+               {
+                       //
+                       // Return method-group expression when the expression can be used as
+                       // lambda replacement. A good example is array sorting where instead of
+                       // code like
+                       //
+                       //  Array.Sort (s, (a, b) => String.Compare (a, b));
+                       //
+                       // we can use method group directly
+                       //
+                       //  Array.Sort (s, String.Compare);
+                       //
+                       // Correct overload will be used because we do the reduction after
+                       // best candidate was found.
+                       //
+                       return null;
+               }
+
                //
                // Returns true when the expression during Emit phase breaks stack
                // by using await expression
@@ -5944,6 +5963,21 @@ namespace Mono.CSharp {
 
                #endregion
 
+               public override MethodGroupExpr CanReduceLambda (AnonymousMethodBody body)
+               {
+                       if (best_candidate == null || !(best_candidate.IsStatic || InstanceExpression is This))
+                               return null;
+
+                       var args_count = arguments == null ? 0 : arguments.Count;
+                       if (args_count != body.Parameters.Count && args_count == 0)
+                               return null;
+
+                       var mg = MethodGroupExpr.CreatePredefined (best_candidate.Get, DeclaringType, loc);
+                       mg.InstanceExpression = InstanceExpression;
+
+                       return mg;
+               }
+
                public static PropertyExpr CreatePredefined (PropertySpec spec, Location loc)
                {
                        return new PropertyExpr (spec, loc) {
index 4d59b263e5c3478e4a11e08e24938b5ce8987b19..a43e4ab7dc84ee792573907ed88c4e4add0954b9 100644 (file)
@@ -5335,6 +5335,40 @@ namespace Mono.CSharp
 
                #endregion
 
+               public override MethodGroupExpr CanReduceLambda (AnonymousMethodBody body)
+               {
+                       if (MethodGroup == null)
+                               return null;
+
+                       var candidate = MethodGroup.BestCandidate;
+                       if (candidate == null || !(candidate.IsStatic || Exp is This))
+                               return null;
+
+                       var args_count = arguments == null ? 0 : arguments.Count;
+                       if (args_count != body.Parameters.Count)
+                               return null;
+
+                       var lambda_parameters = body.Block.Parameters.FixedParameters;
+                       for (int i = 0; i < args_count; ++i) {
+                               var pr = arguments[i].Expr as ParameterReference;
+                               if (pr == null)
+                                       return null;
+
+                               if (lambda_parameters[i] != pr.Parameter)
+                                       return null;
+
+                               if ((lambda_parameters[i].ModFlags & Parameter.Modifier.RefOutMask) != (pr.Parameter.ModFlags & Parameter.Modifier.RefOutMask))
+                                       return null;
+                       }
+
+                       var emg = MethodGroup as ExtensionMethodGroupExpr;
+                       if (emg != null) {
+                               return MethodGroupExpr.CreatePredefined (candidate, candidate.DeclaringType, MethodGroup.Location);
+                       }
+
+                       return MethodGroup;
+               }
+
                protected override void CloneTo (CloneContext clonectx, Expression t)
                {
                        Invocation target = (Invocation) t;
index 03dd709d9e285ac534992203e86390b8668b12cc..eec04bb687b4a7d633138b887e467c9ffe249044 100644 (file)
@@ -931,9 +931,19 @@ namespace Mono.CSharp {
                                        }
 
                                        var l = am as AnonymousMethodBody;
-                                       if (l != null && l.ReturnTypeInference != null && expr != null) {
-                                               l.ReturnTypeInference.AddCommonTypeBound (expr.Type);
-                                               return true;
+                                       if (l != null && expr != null) {
+                                               if (l.ReturnTypeInference != null) {
+                                                       l.ReturnTypeInference.AddCommonTypeBound (expr.Type);
+                                                       return true;
+                                               }
+
+                                               //
+                                               // Try to optimize simple lambda. Only when optimizations are enabled not to cause
+                                               // unexpected debugging experience
+                                               //
+                                               if (this is ContextualReturn && !ec.IsInProbingMode && ec.Module.Compiler.Settings.Optimize) {
+                                                       l.DirectMethodGroupConversion = expr.CanReduceLambda (l);
+                                               }
                                        }
                                }
                        }
@@ -3490,9 +3500,7 @@ namespace Mono.CSharp {
                        if (Report.Errors > 0)
                                return;
 
-#if PRODUCTION
                        try {
-#endif
                        if (IsCompilerGenerated) {
                                using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
                                        base.Emit (ec);
@@ -3526,15 +3534,9 @@ namespace Mono.CSharp {
                                ec.Emit (OpCodes.Ret);
                        }
 
-#if PRODUCTION
-                       } catch (Exception e){
-                               Console.WriteLine ("Exception caught by the compiler while emitting:");
-                               Console.WriteLine ("   Block that caused the problem begin at: " + block.loc);
-                                       
-                               Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
-                               throw;
+                       } catch (Exception e) {
+                               throw new InternalErrorException (e, StartLocation);
                        }
-#endif
                }
        }
        
diff --git a/mcs/tests/gtest-lambda-31.cs b/mcs/tests/gtest-lambda-31.cs
new file mode 100644 (file)
index 0000000..8836a96
--- /dev/null
@@ -0,0 +1,42 @@
+// Compiler options: -optimize
+
+// Check lambdas to method group optimization, no lambdas should be created for any code int this test
+
+using System;
+using System.Collections;
+using System.Linq;
+
+class Test
+{
+       static int Prop {
+               get {
+                       return 4;
+               }
+       }
+
+       public static int Main ()
+       {
+               var parsed = from t in new[] { "2" } select int.Parse (t);
+               if (parsed.First () != 2)
+                       return 1;
+
+               var s = new string[] { "x", "a" };
+               Array.Sort (s, (a, b) => String.Compare (a, b));
+               if (s[0] != "a")
+                       return 10;
+
+               if (s[1] != "x")
+                       return 11;
+
+               Func<int> i = () => Prop;
+               if (i () != 4)
+                       return 20;
+
+               var em = new IEnumerable[] { new int[] { 1 } }.Select (l => l.Cast<int> ()).First ().First ();
+               if (em != 1)
+                       return 30;
+
+               Console.WriteLine ("ok");
+               return 0;
+       }
+}
\ No newline at end of file