/Microsoft.Scripting.Core/Compiler/BoundConstants.cs
C# | 189 lines | 113 code | 24 blank | 52 comment | 15 complexity | 4cf60a2f70b8593239f9dd5b14c7ddf5 MD5 | raw file
- /* ****************************************************************************
- *
- * Copyright (c) Microsoft Corporation.
- *
- * This source code is subject to terms and conditions of the Microsoft Public License. A
- * copy of the license can be found in the License.html file at the root of this distribution. If
- * you cannot locate the Microsoft Public License, please send an email to
- * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
- * by the terms of the Microsoft Public License.
- *
- * You must not remove this notice, or any other, from this software.
- *
- *
- * ***************************************************************************/
- using System; using Microsoft;
-
-
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Reflection.Emit;
- using System.Runtime.CompilerServices;
- #if !CODEPLEX_40
- using Microsoft.Runtime.CompilerServices;
- #endif
-
- #if CODEPLEX_40
- using System.Dynamic.Utils;
- #else
- using Microsoft.Scripting.Utils;
- #endif
-
- #if CODEPLEX_40
- namespace System.Linq.Expressions.Compiler {
- #else
- namespace Microsoft.Linq.Expressions.Compiler {
- #endif
- /// <summary>
- /// This type tracks "runtime" constants--live objects that appear in
- /// ConstantExpression nodes and must be bound to the delegate.
- /// </summary>
- internal sealed class BoundConstants {
-
- /// <summary>
- /// Constants can emit themselves as different types
- /// For caching purposes, we need to treat each distinct Type as a
- /// seperate thing to cache. (If we have to cast it on the way out, it
- /// ends up using a JIT temp and defeats the purpose of caching the
- /// value in a local)
- /// </summary>
- private struct TypedConstant : IEquatable<TypedConstant> {
- internal readonly object Value;
- internal readonly Type Type;
-
- internal TypedConstant(object value, Type type) {
- Debug.Assert(type == TypeUtils.GetConstantType(type));
-
- Value = value;
- Type = type;
- }
-
- public override int GetHashCode() {
- return RuntimeHelpers.GetHashCode(Value) ^ Type.GetHashCode();
- }
- public bool Equals(TypedConstant other) {
- return object.ReferenceEquals(Value, other.Value) && Type.Equals(other.Type);
- }
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2231:OverloadOperatorEqualsOnOverridingValueTypeEquals")]
- public override bool Equals(object obj) {
- return (obj is TypedConstant) && Equals((TypedConstant)obj);
- }
- }
-
- /// <summary>
- /// The list of constants in the order they appear in the constant array
- /// </summary>
- private readonly List<object> _values = new List<object>();
-
- /// <summary>
- /// The index of each constant in the constant array
- /// </summary>
- private readonly Dictionary<object, int> _indexes = new Dictionary<object, int>(ReferenceEqualityComparer<object>.Instance);
-
- /// <summary>
- /// Each constant referenced within this lambda, and how often it was referenced
- /// </summary>
- private readonly Dictionary<TypedConstant, int> _references = new Dictionary<TypedConstant, int>();
-
- /// <summary>
- /// IL locals for storing frequently used constants
- /// </summary>
- private readonly Dictionary<TypedConstant, LocalBuilder> _cache = new Dictionary<TypedConstant, LocalBuilder>();
-
- internal int Count {
- get { return _values.Count; }
- }
-
- internal object[] ToArray() {
- return _values.ToArray();
- }
-
- /// <summary>
- /// Called by VariableBinder. Adds the constant to the list (if needed)
- /// and increases the reference count by one
- /// </summary>
- internal void AddReference(object value, Type type) {
- type = TypeUtils.GetConstantType(type);
-
- if (!_indexes.ContainsKey(value)) {
- _indexes.Add(value, _values.Count);
- _values.Add(value);
- }
- Helpers.IncrementCount(new TypedConstant(value, type), _references);
- }
-
- /// <summary>
- /// Emits a live object as a constant
- /// </summary>
- internal void EmitConstant(LambdaCompiler lc, object value, Type type) {
- Debug.Assert(!ILGen.CanEmitConstant(value, type));
-
- type = TypeUtils.GetConstantType(type);
-
- LocalBuilder local;
- if (_cache.TryGetValue(new TypedConstant(value, type), out local)) {
- lc.IL.Emit(OpCodes.Ldloc, local);
- return;
- }
- EmitConstantsArray(lc);
- EmitConstantFromArray(lc, value, type);
- }
-
- /// <summary>
- /// Emit code to cache frequently used constants into IL locals,
- /// instead of pulling them out of the array each time
- /// </summary>
- internal void EmitCacheConstants(LambdaCompiler lc) {
- int count = 0;
- foreach (var reference in _references) {
- if (ShouldCache(reference.Value)) {
- count++;
- }
- }
- if (count == 0) {
- return;
- }
- EmitConstantsArray(lc);
- foreach (var reference in _references) {
- if (ShouldCache(reference.Value)) {
- if (--count > 0) {
- // Dup array to keep it on the stack
- lc.IL.Emit(OpCodes.Dup);
- }
- LocalBuilder local = lc.IL.DeclareLocal(reference.Key.Type);
- EmitConstantFromArray(lc, reference.Key.Value, local.LocalType);
- lc.IL.Emit(OpCodes.Stloc, local);
- _cache.Add(reference.Key, local);
- }
- }
- }
-
- private static bool ShouldCache(int refCount) {
- // This caching is too aggressive in the face of conditionals and
- // switch. Also, it is too conservative for variables used inside
- // of loops.
- return refCount > 2;
- }
-
- private static void EmitConstantsArray(LambdaCompiler lc) {
- lc.EmitClosureArgument();
- lc.IL.Emit(OpCodes.Ldfld, typeof(Closure).GetField("Constants"));
- }
-
- private void EmitConstantFromArray(LambdaCompiler lc, object value, Type type) {
- int index;
- if (!_indexes.TryGetValue(value, out index)) {
- _indexes.Add(value, index = _values.Count);
- _values.Add(value);
- }
-
- lc.IL.EmitInt(index);
- lc.IL.Emit(OpCodes.Ldelem_Ref);
- if (type.IsValueType) {
- lc.IL.Emit(OpCodes.Unbox_Any, type);
- } else if (type != typeof(object)) {
- lc.IL.Emit(OpCodes.Castclass, type);
- }
- }
- }
- }