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); } }
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); }
/// <summary> /// Creates a lambda compiler that will compile into the provided Methodbuilder /// </summary> private LambdaCompiler(AnalyzedTree tree, LambdaExpression lambda, MethodBuilder method) { var scope = tree.Scopes[lambda]; var hasClosureArgument = scope.NeedsClosure; Type[] paramTypes = GetParameterTypes(lambda); if (hasClosureArgument) { paramTypes = paramTypes.AddFirst(typeof(Closure)); } method.SetReturnType(lambda.ReturnType); method.SetParameters(paramTypes); var paramNames = lambda.Parameters.Map(p => p.Name); // parameters are index from 1, with closure argument we need to skip the first arg int startIndex = hasClosureArgument ? 2 : 1; for (int i = 0; i < paramNames.Length; i++) { method.DefineParameter(i + startIndex, ParameterAttributes.None, paramNames[i]); } _tree = tree; _lambda = lambda; _typeBuilder = (TypeBuilder)method.DeclaringType.GetTypeInfo(); _method = method; _hasClosureArgument = hasClosureArgument; _ilg = method.GetILGenerator(); // These are populated by AnalyzeTree/VariableBinder _scope = scope; _boundConstants = tree.Constants[lambda]; InitializeMethod(); }
private Storage ResolveVariable(ParameterExpression variable, HoistedLocals hoistedLocals) { for (CompilerScope scope = this; scope != null; scope = scope._parent) { Storage storage; if (scope._locals.TryGetValue(variable, out storage)) { return(storage); } if (scope.IsMethod) { break; } } for (HoistedLocals locals = hoistedLocals; locals != null; locals = locals.Parent) { int num; if (locals.Indexes.TryGetValue(variable, out num)) { return(new ElementBoxStorage(this.ResolveVariable(locals.SelfVariable, hoistedLocals), num, variable)); } } throw Error.UndefinedVariable(variable.Name, variable.Type, this.CurrentLambdaName); }
/// <summary> /// Resolve a local variable in this scope or a closed over scope /// Throws if the variable is defined /// </summary> private Storage ResolveVariable(ParameterExpression variable, HoistedLocals hoistedLocals) { // Search IL locals and arguments, but only in this lambda for (CompilerScope s = this; s != null; s = s._parent) { Storage storage; if (s._locals.TryGetValue(variable, out storage)) { return(storage); } // if this is a lambda, we're done if (s.IsLambda) { break; } } // search hoisted locals for (HoistedLocals h = hoistedLocals; h != null; h = h.Parent) { int index; if (h.Indexes.TryGetValue(variable, out index)) { return(new ElementBoxStorage( ResolveVariable(h.SelfVariable, hoistedLocals), index, variable )); } } // If this is a genuine unbound variable, the error should be // thrown in VariableBinder. throw Error.UndefinedVariable(variable.Name, variable.Type, CurrentLambdaName); }
internal virtual void EmitStore(CompilerScope.Storage value) { value.EmitLoad(); this.EmitStore(); }
internal override void EmitStore(CompilerScope.Storage value) { base.Compiler.IL.Emit(OpCodes.Ldloc, this._boxLocal); value.EmitLoad(); base.Compiler.IL.Emit(OpCodes.Stfld, this._boxValueField); }
internal ElementBoxStorage(CompilerScope.Storage array, int index, ParameterExpression variable) : base(array.Compiler, variable) { this._array = array; this._index = index; this._boxType = typeof(StrongBox<>).MakeGenericType(new Type[] { variable.Type }); this._boxValueField = this._boxType.GetField("Value"); }
private void SetParent(LambdaCompiler lc, CompilerScope parent) { this._parent = parent; if (this.NeedsClosure && (this._parent != null)) { this._closureHoistedLocals = this._parent.NearestHoistedLocals; } ReadOnlyCollection<ParameterExpression> vars = (from p in this.GetVariables() where ((VariableStorageKind) this.Definitions[p]) == VariableStorageKind.Hoisted select p).ToReadOnly<ParameterExpression>(); if (vars.Count > 0) { this._hoistedLocals = new HoistedLocals(this._closureHoistedLocals, vars); this.AddLocal(lc, this._hoistedLocals.SelfVariable); } }
internal CompilerScope Exit() { if (!this.IsMethod) { foreach (Storage storage in this._locals.Values) { storage.FreeLocal(); } } CompilerScope scope = this._parent; this._parent = null; this._hoistedLocals = null; this._closureHoistedLocals = null; this._locals.Clear(); return scope; }
internal CompilerScope Enter(LambdaCompiler lc, CompilerScope parent) { this.SetParent(lc, parent); this.AllocateLocals(lc); if (this.IsMethod && (this._closureHoistedLocals != null)) { this.EmitClosureAccess(lc, this._closureHoistedLocals); } this.EmitNewHoistedLocals(lc); if (this.IsMethod) { this.EmitCachedVariables(); } return this; }
private void SetParent(LambdaCompiler lc, CompilerScope parent) { Debug.Assert(_parent == null && parent != this); _parent = parent; if (NeedsClosure && _parent != null) { _closureHoistedLocals = _parent.NearestHoistedLocals; } ReadOnlyCollection<ParameterExpression> 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; }
private (CompilerScope parent, CompilerScope child)? GetInnerScope(object node, CompilerScope 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. // return(!HasVariables(node) || scope.MergedScopes?.Contains(node as BlockExpression) == true ? default : ( scope, _tree.Scopes.TryGetValue(node, out var innerScope) ? innerScope : new CompilerScope(node, false) { NeedsClosure = scope.NeedsClosure } )); }