PageRenderTime 51ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 1ms

/mcs/class/dlr/Runtime/Microsoft.Scripting.Core/Compiler/BoundConstants.cs

https://bitbucket.org/danipen/mono
C# | 191 lines | 111 code | 26 blank | 54 comment | 16 complexity | 1678c9a1d9ba1f95e0c5f53fda6715bc MD5 | raw file
Possible License(s): Unlicense, Apache-2.0, LGPL-2.0, MPL-2.0-no-copyleft-exception, CC-BY-SA-3.0, GPL-2.0
  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. using System;
  16. using System.Collections.Generic;
  17. using System.Diagnostics;
  18. using System.Reflection.Emit;
  19. using System.Runtime.CompilerServices;
  20. using System.Dynamic.Utils;
  21. #if !FEATURE_CORE_DLR
  22. namespace Microsoft.Scripting.Ast.Compiler {
  23. #else
  24. namespace System.Linq.Expressions.Compiler {
  25. #endif
  26. /// <summary>
  27. /// This type tracks "runtime" constants--live objects that appear in
  28. /// ConstantExpression nodes and must be bound to the delegate.
  29. /// </summary>
  30. internal sealed class BoundConstants {
  31. /// <summary>
  32. /// Constants can emit themselves as different types
  33. /// For caching purposes, we need to treat each distinct Type as a
  34. /// seperate thing to cache. (If we have to cast it on the way out, it
  35. /// ends up using a JIT temp and defeats the purpose of caching the
  36. /// value in a local)
  37. /// </summary>
  38. private struct TypedConstant : IEquatable<TypedConstant> {
  39. internal readonly object Value;
  40. internal readonly Type Type;
  41. internal TypedConstant(object value, Type type) {
  42. Value = value;
  43. Type = type;
  44. }
  45. public override int GetHashCode() {
  46. return ReferenceEqualityComparer<object>.Instance.GetHashCode(Value) ^ Type.GetHashCode();
  47. }
  48. public bool Equals(TypedConstant other) {
  49. return object.ReferenceEquals(Value, other.Value) && Type.Equals(other.Type);
  50. }
  51. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2231:OverloadOperatorEqualsOnOverridingValueTypeEquals")]
  52. public override bool Equals(object obj) {
  53. return (obj is TypedConstant) && Equals((TypedConstant)obj);
  54. }
  55. }
  56. /// <summary>
  57. /// The list of constants in the order they appear in the constant array
  58. /// </summary>
  59. private readonly List<object> _values = new List<object>();
  60. /// <summary>
  61. /// The index of each constant in the constant array
  62. /// </summary>
  63. private readonly Dictionary<object, int> _indexes = new Dictionary<object, int>(ReferenceEqualityComparer<object>.Instance);
  64. /// <summary>
  65. /// Each constant referenced within this lambda, and how often it was referenced
  66. /// </summary>
  67. private readonly Dictionary<TypedConstant, int> _references = new Dictionary<TypedConstant, int>();
  68. /// <summary>
  69. /// IL locals for storing frequently used constants
  70. /// </summary>
  71. private readonly Dictionary<TypedConstant, LocalBuilder> _cache = new Dictionary<TypedConstant, LocalBuilder>();
  72. internal int Count {
  73. get { return _values.Count; }
  74. }
  75. internal object[] ToArray() {
  76. return _values.ToArray();
  77. }
  78. /// <summary>
  79. /// Called by VariableBinder. Adds the constant to the list (if needed)
  80. /// and increases the reference count by one
  81. /// </summary>
  82. internal void AddReference(object value, Type type) {
  83. if (!_indexes.ContainsKey(value)) {
  84. _indexes.Add(value, _values.Count);
  85. _values.Add(value);
  86. }
  87. Helpers.IncrementCount(new TypedConstant(value, type), _references);
  88. }
  89. /// <summary>
  90. /// Emits a live object as a constant
  91. /// </summary>
  92. internal void EmitConstant(LambdaCompiler lc, object value, Type type) {
  93. Debug.Assert(!ILGen.CanEmitConstant(value, type));
  94. if (!lc.CanEmitBoundConstants) {
  95. throw Error.CannotCompileConstant(value);
  96. }
  97. LocalBuilder local;
  98. if (_cache.TryGetValue(new TypedConstant(value, type), out local)) {
  99. lc.IL.Emit(OpCodes.Ldloc, local);
  100. return;
  101. }
  102. EmitConstantsArray(lc);
  103. EmitConstantFromArray(lc, value, type);
  104. }
  105. /// <summary>
  106. /// Emit code to cache frequently used constants into IL locals,
  107. /// instead of pulling them out of the array each time
  108. /// </summary>
  109. internal void EmitCacheConstants(LambdaCompiler lc) {
  110. int count = 0;
  111. foreach (var reference in _references) {
  112. if (!lc.CanEmitBoundConstants) {
  113. throw Error.CannotCompileConstant(reference.Key.Value);
  114. }
  115. if (ShouldCache(reference.Value)) {
  116. count++;
  117. }
  118. }
  119. if (count == 0) {
  120. return;
  121. }
  122. EmitConstantsArray(lc);
  123. // The same lambda can be in multiple places in the tree, so we
  124. // need to clear any locals from last time.
  125. _cache.Clear();
  126. foreach (var reference in _references) {
  127. if (ShouldCache(reference.Value)) {
  128. if (--count > 0) {
  129. // Dup array to keep it on the stack
  130. lc.IL.Emit(OpCodes.Dup);
  131. }
  132. LocalBuilder local = lc.IL.DeclareLocal(reference.Key.Type);
  133. EmitConstantFromArray(lc, reference.Key.Value, local.LocalType);
  134. lc.IL.Emit(OpCodes.Stloc, local);
  135. _cache.Add(reference.Key, local);
  136. }
  137. }
  138. }
  139. private static bool ShouldCache(int refCount) {
  140. // This caching is too aggressive in the face of conditionals and
  141. // switch. Also, it is too conservative for variables used inside
  142. // of loops.
  143. return refCount > 2;
  144. }
  145. private static void EmitConstantsArray(LambdaCompiler lc) {
  146. Debug.Assert(lc.CanEmitBoundConstants); // this should've been checked already
  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. }