Fix XMM scanning on Mac x86.
[mono.git] / mcs / class / dlr / Runtime / Microsoft.Scripting.Core / Compiler / LambdaCompiler.cs
1 /* ****************************************************************************
2  *
3  * Copyright (c) Microsoft Corporation. 
4  *
5  * This source code is subject to terms and conditions of the Apache License, Version 2.0. A 
6  * copy of the license can be found in the License.html file at the root of this distribution. If 
7  * you cannot locate the  Apache License, Version 2.0, please send an email to 
8  * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
9  * by the terms of the Apache License, Version 2.0.
10  *
11  * You must not remove this notice, or any other, from this software.
12  *
13  *
14  * ***************************************************************************/
15
16 using System;
17 using System.Collections.Generic;
18 using System.Collections.ObjectModel;
19 using System.Diagnostics;
20 using System.Dynamic.Utils;
21 using System.Reflection;
22 using System.Reflection.Emit;
23 using System.Runtime.CompilerServices;
24 using System.Threading;
25
26 #if !FEATURE_CORE_DLR
27 namespace Microsoft.Scripting.Ast.Compiler {
28 #else
29 namespace System.Linq.Expressions.Compiler {
30 #endif
31 #if !FEATURE_CORE_DLR || SILVERLIGHT
32     using ILGenerator = OffsetTrackingILGenerator;
33 #endif
34
35     /// <summary>
36     /// LambdaCompiler is responsible for compiling individual lambda (LambdaExpression). The complete tree may
37     /// contain multiple lambdas, the Compiler class is reponsible for compiling the whole tree, individual
38     /// lambdas are then compiled by the LambdaCompiler.
39     /// </summary>
40     [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")]
41     internal sealed partial class LambdaCompiler {
42
43         private delegate void WriteBack();
44
45         // Information on the entire lambda tree currently being compiled
46         private readonly AnalyzedTree _tree;
47
48         private readonly ILGenerator _ilg;
49
50 #if FEATURE_REFEMIT
51         // The TypeBuilder backing this method, if any
52         private readonly TypeBuilder _typeBuilder;
53 #endif
54         private readonly MethodInfo _method;
55
56         // Currently active LabelTargets and their mapping to IL labels
57         private LabelScopeInfo _labelBlock = new LabelScopeInfo(null, LabelScopeKind.Lambda);
58         // Mapping of labels used for "long" jumps (jumping out and into blocks)
59         private readonly Dictionary<LabelTarget, LabelInfo> _labelInfo = new Dictionary<LabelTarget, LabelInfo>();
60
61         // The currently active variable scope
62         private CompilerScope _scope;
63
64         // The lambda we are compiling
65         private readonly LambdaExpression _lambda;
66
67         // True if the method's first argument is of type Closure
68         private readonly bool _hasClosureArgument;
69
70         // True if we want to emitting debug symbols
71         private bool EmitDebugSymbols { get { return _tree.DebugInfoGenerator != null; } }
72
73         // Runtime constants bound to the delegate
74         private readonly BoundConstants _boundConstants;
75
76         // Free list of locals, so we reuse them rather than creating new ones
77         private readonly KeyedQueue<Type, LocalBuilder> _freeLocals = new KeyedQueue<Type, LocalBuilder>();
78
79         /// <summary>
80         /// The value is true if a clearance was emitted and no new sequence point
81         /// has been emitted since that.
82         /// </summary>
83         bool _sequencePointCleared;
84
85         /// <summary>
86         /// Creates a lambda compiler that will compile to a dynamic method
87         /// </summary>
88         private LambdaCompiler(AnalyzedTree tree, LambdaExpression lambda) {
89             Type[] parameterTypes = GetParameterTypes(lambda).AddFirst(typeof(Closure));
90
91 #if (SILVERLIGHT && CLR2) || WP75
92             var method = new DynamicMethod(lambda.Name ?? "lambda_method", lambda.ReturnType, parameterTypes);
93 #else
94             var method = new DynamicMethod(lambda.Name ?? "lambda_method", lambda.ReturnType, parameterTypes, true);
95 #endif
96
97             _tree = tree;
98             _lambda = lambda;
99             _method = method;
100
101 #if !FEATURE_CORE_DLR || SILVERLIGHT
102             _ilg = new OffsetTrackingILGenerator(method.GetILGenerator());
103 #else
104             _ilg = method.GetILGenerator();
105 #endif
106
107             _hasClosureArgument = true;
108
109             // These are populated by AnalyzeTree/VariableBinder
110             _scope = tree.Scopes[lambda];
111             _boundConstants = tree.Constants[lambda];
112
113             InitializeMethod();
114         }
115
116 #if FEATURE_REFEMIT
117         /// <summary>
118         /// Creates a lambda compiler that will compile into the provided Methodbuilder
119         /// </summary>
120         private LambdaCompiler(AnalyzedTree tree, LambdaExpression lambda, MethodBuilder method) {
121             _hasClosureArgument = tree.Scopes[lambda].NeedsClosure;
122             Type[] paramTypes = GetParameterTypes(lambda);
123             if (_hasClosureArgument) {
124                 paramTypes = paramTypes.AddFirst(typeof(Closure));
125             }
126
127             method.SetReturnType(lambda.ReturnType);
128             method.SetParameters(paramTypes);
129             var paramNames = lambda.Parameters.Map(p => p.Name);
130             // parameters are index from 1, with closure argument we need to skip the first arg
131             int startIndex = _hasClosureArgument ? 2 : 1;
132             for (int i = 0; i < paramNames.Length; i++) {
133                 method.DefineParameter(i + startIndex, ParameterAttributes.None, paramNames[i]);
134             }
135
136             _tree = tree;
137             _lambda = lambda;
138             _typeBuilder = (TypeBuilder)method.DeclaringType;
139             _method = method;
140
141 #if !FEATURE_CORE_DLR || SILVERLIGHT
142             _ilg = new OffsetTrackingILGenerator(method.GetILGenerator());
143 #else
144             _ilg = method.GetILGenerator();
145 #endif
146
147             // These are populated by AnalyzeTree/VariableBinder
148             _scope = tree.Scopes[lambda];
149             _boundConstants = tree.Constants[lambda];
150
151             InitializeMethod();
152         }
153 #endif
154
155         /// <summary>
156         /// Creates a lambda compiler for an inlined lambda
157         /// </summary>
158         private LambdaCompiler(LambdaCompiler parent, LambdaExpression lambda) {
159             _tree = parent._tree;
160             _lambda = lambda;
161             _method = parent._method;
162             _ilg = parent._ilg;
163             _hasClosureArgument = parent._hasClosureArgument;
164             _scope = _tree.Scopes[lambda];
165             _boundConstants = parent._boundConstants;
166 #if FEATURE_REFEMIT
167             _typeBuilder = parent._typeBuilder;
168 #endif
169         }
170
171         private void InitializeMethod() {
172             // See if we can find a return label, so we can emit better IL
173             AddReturnLabel(_lambda);
174             _boundConstants.EmitCacheConstants(this);
175         }
176
177         public override string ToString() {
178             return _method.ToString();
179         }
180
181         internal ILGenerator IL {
182             get { return _ilg; }
183         }
184
185         internal ReadOnlyCollection<ParameterExpression> Parameters {
186             get { return _lambda.Parameters; }
187         }
188
189         internal bool CanEmitBoundConstants {
190             get { return _method is DynamicMethod; }
191         }
192
193         #region Compiler entry points
194         
195         /// <summary>
196         /// Compiler entry point
197         /// </summary>
198         /// <param name="lambda">LambdaExpression to compile.</param>
199         /// <param name="debugInfoGenerator">Debug info generator.</param>
200         /// <returns>The compiled delegate.</returns>
201         internal static Delegate Compile(LambdaExpression lambda, DebugInfoGenerator debugInfoGenerator) {
202             // 1. Bind lambda
203             AnalyzedTree tree = AnalyzeLambda(ref lambda);
204
205             tree.DebugInfoGenerator = debugInfoGenerator;
206             
207             // 2. Create lambda compiler
208             LambdaCompiler c = new LambdaCompiler(tree, lambda);
209
210             // 3. Emit
211             c.EmitLambdaBody();
212
213             // 4. Return the delegate.
214             return c.CreateDelegate();
215         }
216
217 #if FEATURE_REFEMIT
218         /// <summary>
219         /// Mutates the MethodBuilder parameter, filling in IL, parameters,
220         /// and return type.
221         /// 
222         /// (probably shouldn't be modifying parameters/return type...)
223         /// </summary>
224         internal static void Compile(LambdaExpression lambda, MethodBuilder method, DebugInfoGenerator debugInfoGenerator) {
225             // 1. Bind lambda
226             AnalyzedTree tree = AnalyzeLambda(ref lambda);
227
228             tree.DebugInfoGenerator = debugInfoGenerator;
229             
230             // 2. Create lambda compiler
231             LambdaCompiler c = new LambdaCompiler(tree, lambda, method);
232
233             // 3. Emit
234             c.EmitLambdaBody();
235         }
236 #endif
237         #endregion
238
239         private static AnalyzedTree AnalyzeLambda(ref LambdaExpression lambda) {
240             // Spill the stack for any exception handling blocks or other
241             // constructs which require entering with an empty stack
242             lambda = StackSpiller.AnalyzeLambda(lambda);
243
244             // Bind any variable references in this lambda
245             return VariableBinder.Bind(lambda);
246         }
247
248         internal LocalBuilder GetLocal(Type type) {
249             Debug.Assert(type != null);
250
251             LocalBuilder local;
252             if (_freeLocals.TryDequeue(type, out local)) {
253                 Debug.Assert(type == local.LocalType);
254                 return local;
255             }
256
257             return _ilg.DeclareLocal(type);
258         }
259
260         internal void FreeLocal(LocalBuilder local) {
261             if (local != null) {
262                 _freeLocals.Enqueue(local.LocalType, local);
263             }
264         }
265
266         internal LocalBuilder GetNamedLocal(Type type, ParameterExpression variable) {
267             Debug.Assert(type != null && variable != null);
268
269             LocalBuilder lb = _ilg.DeclareLocal(type);
270             if (EmitDebugSymbols && variable.Name != null) {
271                 _tree.DebugInfoGenerator.SetLocalName(lb, variable.Name);
272             }
273             return lb;
274         }
275
276         /// <summary>
277         /// Gets the argument slot corresponding to the parameter at the given
278         /// index. Assumes that the method takes a certain number of prefix
279         /// arguments, followed by the real parameters stored in Parameters
280         /// </summary>
281         internal int GetLambdaArgument(int index) {
282             return index + (_hasClosureArgument ? 1 : 0) + (_method.IsStatic ? 0 : 1);
283         }
284
285         /// <summary>
286         /// Returns the index-th argument. This method provides access to the actual arguments
287         /// defined on the lambda itself, and excludes the possible 0-th closure argument.
288         /// </summary>
289         internal void EmitLambdaArgument(int index) {
290             _ilg.EmitLoadArg(GetLambdaArgument(index));
291         }
292
293         internal void EmitClosureArgument() {
294             Debug.Assert(_hasClosureArgument, "must have a Closure argument");
295             Debug.Assert(_method.IsStatic, "must be a static method");
296             _ilg.EmitLoadArg(0);
297         }
298
299         private Delegate CreateDelegate() {
300             Debug.Assert(_method is DynamicMethod);
301
302             return _method.CreateDelegate(_lambda.Type, new Closure(_boundConstants.ToArray(), null));
303         }
304
305 #if FEATURE_REFEMIT
306         private FieldBuilder CreateStaticField(string name, Type type) {
307             // We are emitting into someone else's type. We don't want name
308             // conflicts, so choose a long name that is unlikely to confict.
309             // Naming scheme chosen here is similar to what the C# compiler
310             // uses.
311             return _typeBuilder.DefineField("<ExpressionCompilerImplementationDetails>{" + Interlocked.Increment(ref _Counter) + "}" + name, type, FieldAttributes.Static | FieldAttributes.Private);
312         }
313 #endif
314         /// <summary>
315         /// Creates an unitialized field suitible for private implementation details
316         /// Works with DynamicMethods or TypeBuilders.
317         /// </summary>
318         private MemberExpression CreateLazyInitializedField<T>(string name) {
319 #if FEATURE_REFEMIT
320             if (!(_method is DynamicMethod)) {
321                 return Expression.Field(null, CreateStaticField(name, typeof(T)));
322             } 
323 #endif
324             return Expression.Field(Expression.Constant(new StrongBox<T>(default(T))), "Value");
325         }
326     }
327 }