From: Marek Safar Date: Fri, 15 Mar 2013 14:25:25 +0000 (+0100) Subject: Implement optimization where lambdas can be replaced by direct method-group they... X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=commitdiff_plain;h=663861741f78f9f0c3ace703ce0201122a60b92f;p=mono.git Implement optimization where lambdas can be replaced by direct method-group they are wrapping. Fixes #10663 --- diff --git a/mcs/mcs/anonymous.cs b/mcs/mcs/anonymous.cs index 0c855c51cbd..fe16344ae0d 100644 --- a/mcs/mcs/anonymous.cs +++ b/mcs/mcs/anonymous.cs @@ -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 diff --git a/mcs/mcs/convert.cs b/mcs/mcs/convert.cs index 3b4a4c77c66..3c5ebeae4b3 100644 --- a/mcs/mcs/convert.cs +++ b/mcs/mcs/convert.cs @@ -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); } } diff --git a/mcs/mcs/delegate.cs b/mcs/mcs/delegate.cs index 8e10593e383..2731635683d 100644 --- a/mcs/mcs/delegate.cs +++ b/mcs/mcs/delegate.cs @@ -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; } } diff --git a/mcs/mcs/ecore.cs b/mcs/mcs/ecore.cs index 9a91c0d21e5..ee025092c44 100644 --- a/mcs/mcs/ecore.cs +++ b/mcs/mcs/ecore.cs @@ -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) { diff --git a/mcs/mcs/expression.cs b/mcs/mcs/expression.cs index 4d59b263e5c..a43e4ab7dc8 100644 --- a/mcs/mcs/expression.cs +++ b/mcs/mcs/expression.cs @@ -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; diff --git a/mcs/mcs/statement.cs b/mcs/mcs/statement.cs index 03dd709d9e2..eec04bb687b 100644 --- a/mcs/mcs/statement.cs +++ b/mcs/mcs/statement.cs @@ -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 index 00000000000..8836a96e00b --- /dev/null +++ b/mcs/tests/gtest-lambda-31.cs @@ -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 i = () => Prop; + if (i () != 4) + return 20; + + var em = new IEnumerable[] { new int[] { 1 } }.Select (l => l.Cast ()).First ().First (); + if (em != 1) + return 30; + + Console.WriteLine ("ok"); + return 0; + } +} \ No newline at end of file