Fix XMM scanning on Mac x86.
[mono.git] / mcs / class / dlr / Runtime / Microsoft.Scripting.Core / Compiler / BoundConstants.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.Diagnostics;
19 using System.Reflection.Emit;
20 using System.Runtime.CompilerServices;
21 using System.Dynamic.Utils;
22
23 #if !FEATURE_CORE_DLR
24 namespace Microsoft.Scripting.Ast.Compiler {
25 #else
26 namespace System.Linq.Expressions.Compiler {
27 #endif
28     /// <summary>
29     /// This type tracks "runtime" constants--live objects that appear in
30     /// ConstantExpression nodes and must be bound to the delegate.
31     /// </summary>
32     internal sealed class BoundConstants {
33
34         /// <summary>
35         /// Constants can emit themselves as different types
36         /// For caching purposes, we need to treat each distinct Type as a
37         /// seperate thing to cache. (If we have to cast it on the way out, it
38         /// ends up using a JIT temp and defeats the purpose of caching the
39         /// value in a local)
40         /// </summary>
41         private struct TypedConstant : IEquatable<TypedConstant> {
42             internal readonly object Value;
43             internal readonly Type Type;
44
45             internal TypedConstant(object value, Type type) {
46                 Value = value;
47                 Type = type;
48             }
49
50             public override int GetHashCode() {
51                 return ReferenceEqualityComparer<object>.Instance.GetHashCode(Value) ^ Type.GetHashCode();
52             }
53
54             public bool Equals(TypedConstant other) {
55                 return object.ReferenceEquals(Value, other.Value) && Type.Equals(other.Type);
56             }
57
58             [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2231:OverloadOperatorEqualsOnOverridingValueTypeEquals")]
59             public override bool Equals(object obj) {
60                 return (obj is TypedConstant) && Equals((TypedConstant)obj);
61             }
62         }
63
64         /// <summary>
65         /// The list of constants in the order they appear in the constant array
66         /// </summary>
67         private readonly List<object> _values = new List<object>();
68
69         /// <summary>
70         /// The index of each constant in the constant array
71         /// </summary>
72         private readonly Dictionary<object, int> _indexes = new Dictionary<object, int>(ReferenceEqualityComparer<object>.Instance);
73
74         /// <summary>
75         /// Each constant referenced within this lambda, and how often it was referenced
76         /// </summary>
77         private readonly Dictionary<TypedConstant, int> _references = new Dictionary<TypedConstant, int>();
78
79         /// <summary>
80         /// IL locals for storing frequently used constants
81         /// </summary>
82         private readonly Dictionary<TypedConstant, LocalBuilder> _cache = new Dictionary<TypedConstant, LocalBuilder>();
83
84         internal int Count {
85             get { return _values.Count; }
86         }
87
88         internal object[] ToArray() {
89             return _values.ToArray();
90         }
91
92         /// <summary>
93         /// Called by VariableBinder. Adds the constant to the list (if needed)
94         /// and increases the reference count by one
95         /// </summary>
96         internal void AddReference(object value, Type type) {
97             if (!_indexes.ContainsKey(value)) {
98                 _indexes.Add(value, _values.Count);
99                 _values.Add(value);
100             }
101             Helpers.IncrementCount(new TypedConstant(value, type), _references);
102         }
103
104         /// <summary>
105         /// Emits a live object as a constant
106         /// </summary>
107         internal void EmitConstant(LambdaCompiler lc, object value, Type type) {
108             Debug.Assert(!ILGen.CanEmitConstant(value, type));
109
110             if (!lc.CanEmitBoundConstants) {
111                 throw Error.CannotCompileConstant(value);
112             }
113
114             LocalBuilder local;
115             if (_cache.TryGetValue(new TypedConstant(value, type), out local)) {
116                 lc.IL.Emit(OpCodes.Ldloc, local);
117                 return;
118             }
119             EmitConstantsArray(lc);
120             EmitConstantFromArray(lc, value, type);
121         }
122
123         /// <summary>
124         /// Emit code to cache frequently used constants into IL locals,
125         /// instead of pulling them out of the array each time
126         /// </summary>
127         internal void EmitCacheConstants(LambdaCompiler lc) {
128             int count = 0;
129             foreach (var reference in _references) {
130                 if (!lc.CanEmitBoundConstants) {
131                     throw Error.CannotCompileConstant(reference.Key.Value);
132                 }
133
134                 if (ShouldCache(reference.Value)) {
135                     count++;
136                 }
137             }
138             if (count == 0) {
139                 return;
140             }
141             EmitConstantsArray(lc);
142             
143             // The same lambda can be in multiple places in the tree, so we
144             // need to clear any locals from last time.
145             _cache.Clear();
146
147             foreach (var reference in _references) {
148                 if (ShouldCache(reference.Value)) {
149                     if (--count > 0) {
150                         // Dup array to keep it on the stack
151                         lc.IL.Emit(OpCodes.Dup);
152                     }
153                     LocalBuilder local = lc.IL.DeclareLocal(reference.Key.Type);
154                     EmitConstantFromArray(lc, reference.Key.Value, local.LocalType);
155                     lc.IL.Emit(OpCodes.Stloc, local);
156                     _cache.Add(reference.Key, local);
157                 }
158             }
159         }
160
161         private static bool ShouldCache(int refCount) {
162             // This caching is too aggressive in the face of conditionals and
163             // switch. Also, it is too conservative for variables used inside
164             // of loops.
165             return refCount > 2;
166         }
167
168         private static void EmitConstantsArray(LambdaCompiler lc) {
169             Debug.Assert(lc.CanEmitBoundConstants); // this should've been checked already
170
171             lc.EmitClosureArgument();
172             lc.IL.Emit(OpCodes.Ldfld, typeof(Closure).GetField("Constants"));
173         }
174
175         private void EmitConstantFromArray(LambdaCompiler lc, object value, Type type) {
176             int index;
177             if (!_indexes.TryGetValue(value, out index)) {
178                 _indexes.Add(value, index = _values.Count);
179                 _values.Add(value);
180             }
181
182             lc.IL.EmitInt(index);
183             lc.IL.Emit(OpCodes.Ldelem_Ref);
184             if (type.IsValueType) {
185                 lc.IL.Emit(OpCodes.Unbox_Any, type);
186             } else if (type != typeof(object)) {
187                 lc.IL.Emit(OpCodes.Castclass, type);
188             }
189         }
190     }
191 }