Example #1
0
        private bool TryRewriteLocal(LocalSymbol local, out LocalSymbol newLocal)
        {
            if (VariablesCaptured.Contains(local))
            {
                // no longer a local symbol
                newLocal = null;
                return(false);
            }

            if (localMap.TryGetValue(local, out newLocal))
            {
                return(true);
            }

            var newType = VisitType(local.Type);

            if (newType == local.Type)
            {
                newLocal = local;
            }
            else
            {
                newLocal = new SynthesizedLocal(CurrentMethod, newType, local.Name);
                localMap.Add(local, newLocal);
            }

            return(true);
        }
        public override BoundNode VisitSequence(BoundSequence node)
        {
            // Spilled local temps do not appear here in a sequence expression, because any temps in a
            // sequence expression that need to be spilled would have been moved up to the
            // statement level by the AwaitLiftingRewriter.
            if (!node.Locals.IsDefaultOrEmpty)
            {
                foreach (var local in node.Locals)
                {
                    Debug.Assert(!VariablesCaptured.Contains(local) || proxies.ContainsKey(local));
                }
            }

            return(base.VisitSequence(node));
        }
Example #3
0
        public override BoundNode VisitLocal(BoundLocal node)
        {
            CapturedSymbolReplacement proxy;

            if (proxies.TryGetValue(node.LocalSymbol, out proxy))
            {
                return(proxy.Replacement(node.Syntax, frameType => FramePointer(node.Syntax, frameType)));
            }

            Debug.Assert(!VariablesCaptured.Contains(node.LocalSymbol));
            LocalSymbol replacementLocal;

            if (this.localMap.TryGetValue(node.LocalSymbol, out replacementLocal))
            {
                return(new BoundLocal(node.Syntax, replacementLocal, node.ConstantValueOpt, replacementLocal.Type, node.HasErrors));
            }

            return(base.VisitLocal(node));
        }
        /// <summary>
        /// Translate a statement that declares a given set of locals.  Also allocates and frees hoisted temps as
        /// required for the translation.
        /// </summary>
        /// <param name="locals">The set of locals declared in the original version of this statement</param>
        /// <param name="wrapped">A delegate to return the translation of the body of this statement</param>
        private BoundNode PossibleIteratorScope(ImmutableArray <LocalSymbol> locals, Func <BoundStatement> wrapped)
        {
            if (locals.IsDefaultOrEmpty)
            {
                return(wrapped());
            }
            var proxyFields = ArrayBuilder <SynthesizedFieldSymbolBase> .GetInstance();

            foreach (var local in locals)
            {
                if (!VariablesCaptured.Contains(local))
                {
                    continue;
                }
                CapturedSymbolReplacement proxy;
                if (proxies.TryGetValue(local, out proxy))
                {
                    // All of the user-declared variables have pre-allocated proxies
                    var field = proxy.HoistedField;
                    Debug.Assert((object)field != null);
                    Debug.Assert(local.DeclarationKind != LocalDeclarationKind.CompilerGenerated); // temps have lazily allocated proxies
                    if (local.DeclarationKind != LocalDeclarationKind.CompilerGenerated)
                    {
                        proxyFields.Add(field);
                    }
                }
                else
                {
                    if (local.RefKind == RefKind.None)
                    {
                        SynthesizedFieldSymbolBase field = MakeHoistedTemp(local, TypeMap.SubstituteType(local.Type));
                        proxy = new CapturedToFrameSymbolReplacement(field);
                        proxies.Add(local, proxy);
                    }
                    // ref temporary variables have proxies that are allocated on demand
                    // See VisitAssignmentOperator.
                }
            }

            var translatedStatement = wrapped();

            // produce code to free (e.g. mark as available for reuse) and clear (e.g. set to null) the proxies for any temps for these locals
            var clearTemps = ArrayBuilder <BoundAssignmentOperator> .GetInstance();

            foreach (var local in locals)
            {
                ArrayBuilder <SynthesizedFieldSymbolBase> frees;
                if (freeTempsMap.TryGetValue(local, out frees))
                {
                    Debug.Assert(local.DeclarationKind == LocalDeclarationKind.CompilerGenerated); // only temps are managed this way
                    freeTempsMap.Remove(local);
                    foreach (var field in frees)
                    {
                        if (MightContainReferences(field.Type))
                        {
                            clearTemps.Add(F.AssignmentExpression(F.Field(F.This(), field), F.NullOrDefault(field.Type)));
                        }
                        FreeTemp(field);
                    }
                    frees.Free();
                }
            }

            if (clearTemps.Count != 0)
            {
                translatedStatement = F.Block(
                    translatedStatement,
                    F.Block(clearTemps.Select(e => F.ExpressionStatement(e)).AsImmutable <BoundStatement>())
                    );
            }
            clearTemps.Free();

            // wrap the node in an iterator scope for debugging
            if (proxyFields.Count != 0)
            {
                translatedStatement = new BoundIteratorScope(F.Syntax, proxyFields.ToImmutable(), translatedStatement);
            }
            proxyFields.Free();

            return(translatedStatement);
        }
Example #5
0
        public override BoundNode VisitAssignmentOperator(BoundAssignmentOperator node)
        {
            BoundExpression originalLeft = node.Left;

            if (originalLeft.Kind != BoundKind.Local)
            {
                return(base.VisitAssignmentOperator(node));
            }

            var leftLocal = (BoundLocal)originalLeft;

            BoundExpression originalRight = node.Right;

            if (leftLocal.LocalSymbol.RefKind != RefKind.None &&
                node.RefKind != RefKind.None &&
                VariablesCaptured.Contains(leftLocal.LocalSymbol))
            {
                Debug.Assert(!proxies.ContainsKey(leftLocal.LocalSymbol)); // we need to create the proxy at this time
                Debug.Assert(!IsStackAlloc(originalRight));
                //spilling ref local variables
                throw ExceptionUtilities.Unreachable;
            }

            if (VariablesCaptured.Contains(leftLocal.LocalSymbol) && !proxies.ContainsKey(leftLocal.LocalSymbol))
            {
                Debug.Assert(leftLocal.LocalSymbol.DeclarationKind == LocalDeclarationKind.CompilerGenerated);
                //spilling temp variables
                throw ExceptionUtilities.Unreachable;
            }

            BoundExpression rewrittenLeft  = (BoundExpression)this.Visit(leftLocal);
            BoundExpression rewrittenRight = (BoundExpression)this.Visit(originalRight);
            TypeSymbol      rewrittenType  = VisitType(node.Type);

            // Check if we're assigning the result of stackalloc to a hoisted local.
            // If we are, we need to store the result in a temp local and then assign
            // the value of the local to the field corresponding to the hoisted local.
            // If the receiver of the field is on the stack when the stackalloc happens,
            // popping it will free the memory (?) or otherwise cause verification issues.
            // DevDiv Bugs 59454
            if (rewrittenLeft.Kind != BoundKind.Local && IsStackAlloc(originalRight))
            {
                // From ILGENREC::genAssign:
                // DevDiv Bugs 59454: Handle hoisted local initialized with a stackalloc
                // NOTE: Need to check for cast of stackalloc on RHS.
                // If LHS isLocal, then genAddr is a noop so regular case works fine.

                SyntheticBoundNodeFactory factory = new SyntheticBoundNodeFactory(this.CurrentMethod, rewrittenLeft.Syntax, this.CompilationState, this.Diagnostics);
                BoundAssignmentOperator   tempAssignment;
                BoundLocal tempLocal = factory.StoreToTemp(rewrittenRight, out tempAssignment);

                Debug.Assert(node.RefKind == RefKind.None);
                BoundAssignmentOperator rewrittenAssignment = node.Update(rewrittenLeft, tempLocal, node.RefKind, rewrittenType);

                return(new BoundSequence(
                           node.Syntax,
                           ImmutableArray.Create <LocalSymbol>(tempLocal.LocalSymbol),
                           ImmutableArray.Create <BoundExpression>(tempAssignment),
                           rewrittenAssignment,
                           rewrittenType));
            }

            return(node.Update(rewrittenLeft, rewrittenRight, node.RefKind, rewrittenType));
        }