Example #1
0
        protected internal override Expression VisitParameter(ParameterExpression node)
        {
            Reference(node, VariableStorageKind.Local);

            //
            // Track reference count so we can emit it in a more optimal way if
            // it is used a lot.
            //
            CompilerScope referenceScope = null;

            foreach (CompilerScope scope in _scopes)
            {
                //
                // There are two times we care about references:
                //   1. When we enter a lambda, we want to cache frequently
                //      used variables
                //   2. When we enter a scope with closed-over variables, we
                //      want to cache it immediately when we allocate the
                //      closure slot for it
                //
                if (scope.IsMethod || scope.Definitions.ContainsKey(node))
                {
                    referenceScope = scope;
                    break;
                }
            }

            Debug.Assert(referenceScope != null);
            if (referenceScope.ReferenceCount == null)
            {
                referenceScope.ReferenceCount = new Dictionary <ParameterExpression, int>();
            }

            Helpers.IncrementCount(node, referenceScope.ReferenceCount);
            return(node);
        }
        private void EnterScope(object node) {
            if (HasVariables(node) &&
                (_scope.MergedScopes == null || !_scope.MergedScopes.Contains(node))) {

                CompilerScope scope;
                if (!_tree.Scopes.TryGetValue(node, out scope)) {
                    //
                    // Very often, we want to compile nodes as reductions
                    // rather than as IL, but usually they need to allocate
                    // some IL locals. To support this, we allow emitting a
                    // BlockExpression that was not bound by VariableBinder.
                    // This works as long as the variables are only used
                    // locally -- i.e. not closed over.
                    //
                    // User-created blocks will never hit this case; only our
                    // internally reduced nodes will.
                    //
                    scope = new CompilerScope(node, false) { NeedsClosure = _scope.NeedsClosure };
                }

                _scope = scope.Enter(this, _scope);
                Debug.Assert(_scope.Node == node);
            }
        }
        private void SetParent(LambdaCompiler lc, CompilerScope parent) {
            Debug.Assert(_parent == null && parent != this);
            _parent = parent;

            if (NeedsClosure && _parent != null) {
                _closureHoistedLocals = _parent.NearestHoistedLocals;
            }

            var hoistedVars = GetVariables().Where(p => Definitions[p] == VariableStorageKind.Hoisted).ToReadOnly();

            if (hoistedVars.Count > 0) {
                _hoistedLocals = new HoistedLocals(_closureHoistedLocals, hoistedVars);
                AddLocal(lc, _hoistedLocals.SelfVariable);
            }
        }
        /// <summary>
        /// Frees unnamed locals, clears state associated with this compiler
        /// </summary>
        internal CompilerScope Exit() {
            // free scope's variables
            if (!IsMethod) {
                foreach (Storage storage in _locals.Values) {
                    storage.FreeLocal();
                }
            }
            
            // Clear state that is associated with this parent
            // (because the scope can be reused in another context)
            CompilerScope parent = _parent;
            _parent = null;
            _hoistedLocals = null;
            _closureHoistedLocals = null;
            _locals.Clear();

            return parent;
        }
        /// <summary>
        /// Called when entering a lambda/block. Performs all variable allocation
        /// needed, including creating hoisted locals and IL locals for accessing
        /// parent locals
        /// </summary>
        internal CompilerScope Enter(LambdaCompiler lc, CompilerScope parent) {
            SetParent(lc, parent);

            AllocateLocals(lc);

            if (IsMethod && _closureHoistedLocals != null) {
                EmitClosureAccess(lc, _closureHoistedLocals);
            }

            EmitNewHoistedLocals(lc);

            if (IsMethod) {
                EmitCachedVariables();
            }

            return this;
        }
Example #6
0
 public CompilerScope(CompilerScope parent, CompilerScopeType type)
 {
     this.Parent = parent;
     this.Type   = type;
 }
Example #7
0
            /// <summary>
            /// Visit lambda expression for scope tracking and closure emitting purposes.
            /// </summary>
            /// <typeparam name="T">The type of the delegate represented by the lambda expression.</typeparam>
            /// <param name="node">The lambda expression to rewrite.</param>
            /// <returns>The rewritten lambda expression.</returns>
            protected override Expression VisitLambda <T>(Expression <T> node)
            {
                //
                // Allocate a slot for information about the rewritten lambda expression and thunk
                // type. We add a temporary empty value to the dictionary slot here in order to
                // boost the count of entries. This causes the index to be logically numbered in
                // the order the lambda expressions were visited.
                //
                var index = _lambdas.Count;

                _lambdas[index] = default;

                //
                // Introduce a new scope and keep track of the original scope in order to restore
                // it after visiting child expressions.
                //
                var currentScope = _scope;

                try
                {
                    var scope = _analysis[node];
                    _scope = new CompilerScope(currentScope, scope);

                    var body = default(Expression);

                    if (!scope.HasHoistedLocals && !scope.NeedsClosure)
                    {
                        body = Visit(node.Body);
                    }
                    else
                    {
                        //
                        // Visit the body and append it to the builder. Note that hoisted locals
                        // (i.e. parameters) are copied into the closure.
                        //
                        var builder = _scope.Enter(count: 1, copyLocals: true);
                        builder.Append(Visit(node.Body));
                        body = builder.Finish(declareLocals: false);
                    }

                    //
                    // Use the closure parameter of the original scope (i.e. the parent to the
                    // scope representing the lambda) to derive information about the thunk type.
                    //
                    var closureParam      = currentScope.Closure;
                    var thunkType         = _thunkFactory.GetThunkType(typeof(T), closureParam.Type);
                    var innerDelegateType = GetInnerDelegateType(thunkType);

                    //
                    // Construct a lambda that's parameterized on the closure that's passed in,
                    // using the inner delegate type that the thunk expects. This becomes the
                    // rewritten lambda expression, which will be passed to the constructor of
                    // the thunk type in GetMethodTable.
                    //
                    var parameters = new ParameterExpression[node.Parameters.Count + 1];
                    parameters[0] = closureParam;
                    node.Parameters.CopyTo(parameters, index: 1);

                    var lambda = Expression.Lambda(innerDelegateType, body, parameters);
                    _lambdas[index] = new LambdaInfo(lambda, thunkType);

                    //
                    // Inside the expression tree, replace the lambda by a call to CreateDelegate
                    // on the thunk. The thunk is retrieved from the method table that's passed to
                    // the top-level lambda by indexing into the Thunks array.
                    //
                    var createDelegate = thunkType.GetMethod(nameof(ActionThunk <object> .CreateDelegate));
                    var methodTable    = currentScope.Bind(_methodTable);

                    var createDelegateCall =
                        Expression.Call(
                            Expression.Convert(
                                Expression.ArrayIndex(
                                    Expression.Field(
                                        methodTable,
                                        nameof(MethodTable.Thunks)
                                        ),
                                    Expression.Constant(index)
                                    ),
                                thunkType
                                ),
                            createDelegate,
                            closureParam
                            );

                    return(createDelegateCall);
                }
                finally
                {
                    _scope = currentScope;
                }
            }
Example #8
0
            /// <summary>
            /// Visit catch blocks for scope tracking and closure emitting purposes.
            /// </summary>
            /// <param name="node">The catch block to rewrite.</param>
            /// <returns>The rewritten catch block.</returns>
            protected override CatchBlock VisitCatchBlock(CatchBlock node)
            {
                //
                // Store the filter and body in two locals. These will get overwritten by the
                // result of recursive visits and/or rewrites using the builder (see below), prior
                // to their usage to update the original expression.
                //
                var filter = node.Filter;
                var body   = node.Body;

                //
                // Introduce a new scope and keep track of the original scope in order to restore
                // it after visiting child expressions.
                //
                var currentScope = _scope;

                try
                {
                    var scope = _analysis[node];
                    _scope = new CompilerScope(currentScope, scope);

                    if (!scope.HasHoistedLocals)
                    {
                        //
                        // In case there is no hoisted local, we don't need to use a builder to
                        // instantiate a closure. Simply visit the child expressions.
                        //
                        filter = Visit(filter);
                        body   = Visit(body);
                    }
                    else
                    {
                        //
                        // CONSIDER: Refine the analysis step in order to keep track of the child
                        //           expressions where the variable needs to be hoisted into a
                        //           closure. Right now, we may use an unnecessary closure.
                        //

                        //
                        // In case there is a hoisted local, enter the scope to obtain a builder,
                        // append the visited child expressions, and return a new block. This will
                        // add the statements needed to instantiate the closure and link it to its
                        // parent (if any).
                        //
                        // Note that the original variable holding the caught exception instance is
                        // retained, so:
                        //
                        // * if hoisted, we copy it to the closure in Enter -and-
                        // * we don't re-declare it in Finish.
                        //
                        if (filter != null)
                        {
                            var builder = _scope.Enter(count: 1, copyLocals: true);
                            builder.Append(Visit(filter));
                            filter = builder.Finish(declareLocals: false);
                        }

                        if (body != null)
                        {
                            var builder = _scope.Enter(count: 1, copyLocals: true);
                            builder.Append(Visit(body));
                            body = builder.Finish(declareLocals: false);
                        }
                    }
                }
                finally
                {
                    _scope = currentScope;
                }

                //
                // Simply update the expression using the rewritten child expressions. Note that
                // the variable remains unchanged; its contents could be copied by the rewritten
                // child expressions in order to hoist it into a closure.
                //
                return(node.Update(node.Variable, filter, body));
            }