/Runtime/Microsoft.Scripting.Core/Compiler/BoundConstants.cs
C# | 191 lines | 111 code | 26 blank | 54 comment | 16 complexity | 1678c9a1d9ba1f95e0c5f53fda6715bc MD5 | raw file
Possible License(s): CPL-1.0, BSD-3-Clause, ISC, GPL-2.0, MPL-2.0-no-copyleft-exception
- /* ****************************************************************************
- *
- * Copyright (c) Microsoft Corporation.
- *
- * This source code is subject to terms and conditions of the Apache License, Version 2.0. A
- * copy of the license can be found in the License.html file at the root of this distribution. If
- * you cannot locate the Apache License, Version 2.0, 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 Apache License, Version 2.0.
- *
- * You must not remove this notice, or any other, from this software.
- *
- *
- * ***************************************************************************/
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Reflection.Emit;
- using System.Runtime.CompilerServices;
- using System.Dynamic.Utils;
- #if !FEATURE_CORE_DLR
- namespace Microsoft.Scripting.Ast.Compiler {
- #else
- namespace System.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) {
- Value = value;
- Type = type;
- }
- public override int GetHashCode() {
- return ReferenceEqualityComparer<object>.Instance.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) {
- 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));
- if (!lc.CanEmitBoundConstants) {
- throw Error.CannotCompileConstant(value);
- }
- 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 (!lc.CanEmitBoundConstants) {
- throw Error.CannotCompileConstant(reference.Key.Value);
- }
- if (ShouldCache(reference.Value)) {
- count++;
- }
- }
- if (count == 0) {
- return;
- }
- EmitConstantsArray(lc);
-
- // The same lambda can be in multiple places in the tree, so we
- // need to clear any locals from last time.
- _cache.Clear();
- 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) {
- Debug.Assert(lc.CanEmitBoundConstants); // this should've been checked already
- 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);
- }
- }
- }
- }