PageRenderTime 99ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

/Microsoft.Scripting.Core/Compiler/BoundConstants.cs

https://bitbucket.org/stefanrusek/xronos
C# | 189 lines | 113 code | 24 blank | 52 comment | 15 complexity | 4cf60a2f70b8593239f9dd5b14c7ddf5 MD5 | raw file
  1. /* ****************************************************************************
  2. *
  3. * Copyright (c) Microsoft Corporation.
  4. *
  5. * This source code is subject to terms and conditions of the Microsoft Public License. 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 Microsoft Public License, 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 Microsoft Public License.
  10. *
  11. * You must not remove this notice, or any other, from this software.
  12. *
  13. *
  14. * ***************************************************************************/
  15. using System; using Microsoft;
  16. using System.Collections.Generic;
  17. using System.Diagnostics;
  18. using System.Reflection.Emit;
  19. using System.Runtime.CompilerServices;
  20. #if !CODEPLEX_40
  21. using Microsoft.Runtime.CompilerServices;
  22. #endif
  23. #if CODEPLEX_40
  24. using System.Dynamic.Utils;
  25. #else
  26. using Microsoft.Scripting.Utils;
  27. #endif
  28. #if CODEPLEX_40
  29. namespace System.Linq.Expressions.Compiler {
  30. #else
  31. namespace Microsoft.Linq.Expressions.Compiler {
  32. #endif
  33. /// <summary>
  34. /// This type tracks "runtime" constants--live objects that appear in
  35. /// ConstantExpression nodes and must be bound to the delegate.
  36. /// </summary>
  37. internal sealed class BoundConstants {
  38. /// <summary>
  39. /// Constants can emit themselves as different types
  40. /// For caching purposes, we need to treat each distinct Type as a
  41. /// seperate thing to cache. (If we have to cast it on the way out, it
  42. /// ends up using a JIT temp and defeats the purpose of caching the
  43. /// value in a local)
  44. /// </summary>
  45. private struct TypedConstant : IEquatable<TypedConstant> {
  46. internal readonly object Value;
  47. internal readonly Type Type;
  48. internal TypedConstant(object value, Type type) {
  49. Debug.Assert(type == TypeUtils.GetConstantType(type));
  50. Value = value;
  51. Type = type;
  52. }
  53. public override int GetHashCode() {
  54. return RuntimeHelpers.GetHashCode(Value) ^ Type.GetHashCode();
  55. }
  56. public bool Equals(TypedConstant other) {
  57. return object.ReferenceEquals(Value, other.Value) && Type.Equals(other.Type);
  58. }
  59. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2231:OverloadOperatorEqualsOnOverridingValueTypeEquals")]
  60. public override bool Equals(object obj) {
  61. return (obj is TypedConstant) && Equals((TypedConstant)obj);
  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. /// <summary>
  69. /// The index of each constant in the constant array
  70. /// </summary>
  71. private readonly Dictionary<object, int> _indexes = new Dictionary<object, int>(ReferenceEqualityComparer<object>.Instance);
  72. /// <summary>
  73. /// Each constant referenced within this lambda, and how often it was referenced
  74. /// </summary>
  75. private readonly Dictionary<TypedConstant, int> _references = new Dictionary<TypedConstant, int>();
  76. /// <summary>
  77. /// IL locals for storing frequently used constants
  78. /// </summary>
  79. private readonly Dictionary<TypedConstant, LocalBuilder> _cache = new Dictionary<TypedConstant, LocalBuilder>();
  80. internal int Count {
  81. get { return _values.Count; }
  82. }
  83. internal object[] ToArray() {
  84. return _values.ToArray();
  85. }
  86. /// <summary>
  87. /// Called by VariableBinder. Adds the constant to the list (if needed)
  88. /// and increases the reference count by one
  89. /// </summary>
  90. internal void AddReference(object value, Type type) {
  91. type = TypeUtils.GetConstantType(type);
  92. if (!_indexes.ContainsKey(value)) {
  93. _indexes.Add(value, _values.Count);
  94. _values.Add(value);
  95. }
  96. Helpers.IncrementCount(new TypedConstant(value, type), _references);
  97. }
  98. /// <summary>
  99. /// Emits a live object as a constant
  100. /// </summary>
  101. internal void EmitConstant(LambdaCompiler lc, object value, Type type) {
  102. Debug.Assert(!ILGen.CanEmitConstant(value, type));
  103. type = TypeUtils.GetConstantType(type);
  104. LocalBuilder local;
  105. if (_cache.TryGetValue(new TypedConstant(value, type), out local)) {
  106. lc.IL.Emit(OpCodes.Ldloc, local);
  107. return;
  108. }
  109. EmitConstantsArray(lc);
  110. EmitConstantFromArray(lc, value, type);
  111. }
  112. /// <summary>
  113. /// Emit code to cache frequently used constants into IL locals,
  114. /// instead of pulling them out of the array each time
  115. /// </summary>
  116. internal void EmitCacheConstants(LambdaCompiler lc) {
  117. int count = 0;
  118. foreach (var reference in _references) {
  119. if (ShouldCache(reference.Value)) {
  120. count++;
  121. }
  122. }
  123. if (count == 0) {
  124. return;
  125. }
  126. EmitConstantsArray(lc);
  127. foreach (var reference in _references) {
  128. if (ShouldCache(reference.Value)) {
  129. if (--count > 0) {
  130. // Dup array to keep it on the stack
  131. lc.IL.Emit(OpCodes.Dup);
  132. }
  133. LocalBuilder local = lc.IL.DeclareLocal(reference.Key.Type);
  134. EmitConstantFromArray(lc, reference.Key.Value, local.LocalType);
  135. lc.IL.Emit(OpCodes.Stloc, local);
  136. _cache.Add(reference.Key, local);
  137. }
  138. }
  139. }
  140. private static bool ShouldCache(int refCount) {
  141. // This caching is too aggressive in the face of conditionals and
  142. // switch. Also, it is too conservative for variables used inside
  143. // of loops.
  144. return refCount > 2;
  145. }
  146. private static void EmitConstantsArray(LambdaCompiler lc) {
  147. lc.EmitClosureArgument();
  148. lc.IL.Emit(OpCodes.Ldfld, typeof(Closure).GetField("Constants"));
  149. }
  150. private void EmitConstantFromArray(LambdaCompiler lc, object value, Type type) {
  151. int index;
  152. if (!_indexes.TryGetValue(value, out index)) {
  153. _indexes.Add(value, index = _values.Count);
  154. _values.Add(value);
  155. }
  156. lc.IL.EmitInt(index);
  157. lc.IL.Emit(OpCodes.Ldelem_Ref);
  158. if (type.IsValueType) {
  159. lc.IL.Emit(OpCodes.Unbox_Any, type);
  160. } else if (type != typeof(object)) {
  161. lc.IL.Emit(OpCodes.Castclass, type);
  162. }
  163. }
  164. }
  165. }