/// <summary>
        /// Spill an expression list with a receiver (e.g. array access, method call), where at least one of the
        /// receiver or the arguments contains an await expression.
        /// </summary>
        private Tuple<BoundExpression, ReadOnlyArray<BoundExpression>> SpillExpressionsWithReceiver(
            BoundExpression receiverOpt,
            ReadOnlyArray<BoundExpression> expressions,
            SpillBuilder spillBuilder,
            ReadOnlyArray<RefKind> refKindsOpt)
        {
            if (receiverOpt == null)
            {
                return Tuple.Create(default(BoundExpression), SpillExpressionList(spillBuilder, expressions));
            }

            // We have a non-null receiver, and an expression of the form:
            //     receiver[index1, index2, ..., indexN]
            //     or:
            //     receiver(arg1, arg2, ... argN)

            // Build a list containing the receiver and all expressions (in that order)
            var allExpressions = ReadOnlyArray<BoundExpression>.CreateFrom(receiverOpt).Concat(expressions);
            var allRefKinds = (refKindsOpt != null)
                ? ReadOnlyArray<RefKind>.CreateFrom(RefKind.None).Concat(refKindsOpt)
                : ReadOnlyArray<RefKind>.Empty;

            // Spill the expressions (and possibly the receiver):
            var allSpilledExpressions = SpillExpressionList(spillBuilder, allExpressions, allRefKinds);

            var spilledReceiver = allSpilledExpressions.First();
            var spilledArguments = allSpilledExpressions.RemoveFirst();
            return Tuple.Create(spilledReceiver, spilledArguments);
        }
        public override BoundNode VisitBinaryOperator(BoundBinaryOperator node)
        {
            BoundExpression left  = (BoundExpression)this.Visit(node.Left);
            BoundExpression right = (BoundExpression)this.Visit(node.Right);
            TypeSymbol      type  = this.VisitType(node.Type);

            if (!RequiresSpill(left, right))
            {
                return(node.Update(node.OperatorKind, left, right, node.ConstantValueOpt, node.MethodOpt, node.ResultKind, type));
            }

            var subExprs = ReadOnlyArray <BoundExpression> .CreateFrom(left, right);

            var spillBuilder = new SpillBuilder();
            var newArgs      = SpillExpressionList(spillBuilder, subExprs);

            Debug.Assert(newArgs.Count == 2);

            var newBinaryOperator = node.Update(
                node.OperatorKind,
                newArgs[0],
                newArgs[1],
                node.ConstantValueOpt,
                node.MethodOpt,
                node.ResultKind,
                type);

            return(spillBuilder.BuildSequenceAndFree(F, newBinaryOperator));
        }
        /// <summary>
        /// Spill an expression list with a receiver (e.g. array access, method call), where at least one of the
        /// receiver or the arguments contains an await expression.
        /// </summary>
        private Tuple <BoundExpression, ReadOnlyArray <BoundExpression> > SpillExpressionsWithReceiver(
            BoundExpression receiverOpt,
            ReadOnlyArray <BoundExpression> expressions,
            SpillBuilder spillBuilder,
            ReadOnlyArray <RefKind> refKindsOpt)
        {
            if (receiverOpt == null)
            {
                return(Tuple.Create(default(BoundExpression), SpillExpressionList(spillBuilder, expressions)));
            }

            // We have a non-null receiver, and an expression of the form:
            //     receiver[index1, index2, ..., indexN]
            //     or:
            //     receiver(arg1, arg2, ... argN)

            // Build a list containing the receiver and all expressions (in that order)
            var allExpressions = ReadOnlyArray <BoundExpression> .CreateFrom(receiverOpt).Concat(expressions);

            var allRefKinds = (refKindsOpt != null)
                ? ReadOnlyArray <RefKind> .CreateFrom(RefKind.None).Concat(refKindsOpt)
                : ReadOnlyArray <RefKind> .Empty;

            // Spill the expressions (and possibly the receiver):
            var allSpilledExpressions = SpillExpressionList(spillBuilder, allExpressions, allRefKinds);

            var spilledReceiver  = allSpilledExpressions.First();
            var spilledArguments = allSpilledExpressions.RemoveFirst();

            return(Tuple.Create(spilledReceiver, spilledArguments));
        }
        /// <summary>
        /// Spill a list of expressions (e.g. the arguments of a method call).
        ///
        /// The expressions are processed right-to-left. Once an expression has been found that contains an await
        /// expression, all subsequent expressions are spilled.
        ///
        /// Example:
        ///
        ///     (1 + 2, await t1, Foo(), await t2, 3 + 4)
        ///
        ///     becomes:
        ///
        ///     Spill(
        ///         spill1 = 1 + 2,
        ///         spill2 = await t1,
        ///         spill3 = Foo(),
        ///         (spill1, spill2, spill3, await t2, 3 + 4))
        ///
        /// NOTE: Consider nested array initializers:
        ///
        ///     new int[] {
        ///         { 1, await t1 },
        ///         { 3, await t2 }
        ///     }
        ///
        /// If the arguments of the top-level initializer had already been spilled, we would end up trying to spill
        /// something like this:
        ///
        ///     new int[] {
        ///         Spill(
        ///             spill1 = 1,
        ///             { spill1, await t1 }),
        ///         Spill(
        ///             spill2 = 3,
        ///             { spill2, await t2 })
        ///     }
        ///
        /// The normal rewriting would produce:
        ///
        ///     Spill(
        ///         spill1 = 1,
        ///         spill3 = { spill1, await t1 },
        ///         spill2 = 3,
        ///         int[] a = new int[] {
        ///             spill3,
        ///             { spill2, await t2 }))
        ///
        /// Which is invalid, because spill3 does not have a type.
        ///
        /// To solve this problem the expression list spilled descends into nested array initializers.
        ///
        /// </summary>
        private ReadOnlyArray <BoundExpression> SpillExpressionList(
            SpillBuilder outerSpillBuilder,
            ReadOnlyArray <BoundExpression> expressions,
            ReadOnlyArray <RefKind> refKindsOpt = default(ReadOnlyArray <RefKind>))
        {
            var spillBuilders = ArrayBuilder <SpillBuilder> .GetInstance();

            bool spilledFirstArg = false;

            ReadOnlyArray <BoundExpression> newArgs = SpillArgumentListInner(expressions, refKindsOpt, spillBuilders, ref spilledFirstArg);

            var spillBuilder = new SpillBuilder();

            spillBuilders.Reverse();
            foreach (var spill in spillBuilders)
            {
                spillBuilder.AddSpill(spill);
                spill.Free();
            }
            spillBuilders.Free();

            outerSpillBuilder.AddSpill(spillBuilder);
            spillBuilder.Free();

            return(newArgs);
        }
 private BoundNode SpillAssignmentOperator(BoundAssignmentOperator node, BoundExpression left, BoundSpillSequence right)
 {
     var spillBuilder = new SpillBuilder();
     var spilledLeftNode = SpillLValue(left, spillBuilder);
     var innerSpill = node.Update(spilledLeftNode, right.Value, node.RefKind, node.Type);
     spillBuilder.AddSpill(right);
     return spillBuilder.BuildSequenceAndFree(F, innerSpill);
 }
        private BoundNode SpillAssignmentOperator(BoundAssignmentOperator node, BoundExpression left, BoundSpillSequence right)
        {
            var spillBuilder    = new SpillBuilder();
            var spilledLeftNode = SpillLValue(left, spillBuilder);
            var innerSpill      = node.Update(spilledLeftNode, right.Value, node.RefKind, node.Type);

            spillBuilder.AddSpill(right);
            return(spillBuilder.BuildSequenceAndFree(F, innerSpill));
        }
        public override BoundNode VisitArrayAccess(BoundArrayAccess node)
        {
            BoundExpression expression = (BoundExpression)this.Visit(node.Expression);
            ReadOnlyArray <BoundExpression> indices = (ReadOnlyArray <BoundExpression>) this.VisitList(node.Indices);
            TypeSymbol type = this.VisitType(node.Type);

            if (!RequiresSpill(indices) && expression.Kind != BoundKind.SpillSequence)
            {
                return(node.Update(expression, indices, type));
            }

            var             spillBuilder        = new SpillBuilder();
            var             spillResult         = SpillExpressionsWithReceiver(expression, indices, spillBuilder, refKindsOpt: ReadOnlyArray <RefKind> .Empty);
            BoundExpression newBoundArrayAccess = node.Update(spillResult.Item1, spillResult.Item2, type);

            return(spillBuilder.BuildSequenceAndFree(F, newBoundArrayAccess));
        }
        public override BoundNode VisitArrayCreation(BoundArrayCreation node)
        {
            ReadOnlyArray <BoundExpression> bounds             = this.VisitList(node.Bounds);
            BoundArrayInitialization        visitedInitializer = (BoundArrayInitialization)this.Visit(node.InitializerOpt);
            TypeSymbol type = this.VisitType(node.Type);

            if (!RequiresSpill(bounds) && (visitedInitializer == null || !RequiresSpill(visitedInitializer.Initializers)))
            {
                return(node.Update(bounds, visitedInitializer, type));
            }

            var spillBuilder = new SpillBuilder();
            ReadOnlyArray <BoundExpression> newBounds         = SpillExpressionList(spillBuilder, bounds);
            BoundArrayInitialization        newInitializerOpt = (visitedInitializer == null) ? visitedInitializer :
                                                                visitedInitializer.Update(SpillExpressionList(spillBuilder, visitedInitializer.Initializers));
            BoundArrayCreation newArrayCreation = node.Update(newBounds, newInitializerOpt, type);

            return(spillBuilder.BuildSequenceAndFree(F, newArrayCreation));
        }
        public override BoundNode VisitObjectCreationExpression(BoundObjectCreationExpression node)
        {
            ReadOnlyArray <BoundExpression> arguments = (ReadOnlyArray <BoundExpression>) this.VisitList(node.Arguments);
            BoundExpression initializerExpressionOpt  = (BoundExpression)this.Visit(node.InitializerExpressionOpt);
            TypeSymbol      type = this.VisitType(node.Type);

            if (!RequiresSpill(arguments) && (initializerExpressionOpt == null || initializerExpressionOpt.Kind != BoundKind.SpillSequence))
            {
                return(node.Update(node.Constructor, arguments, node.ArgumentNamesOpt, node.ArgumentRefKindsOpt, node.Expanded, node.ArgsToParamsOpt, node.ConstantValueOpt, initializerExpressionOpt, type));
            }

            var spillBuilder = new SpillBuilder();
            ReadOnlyArray <BoundExpression> newArguments = SpillExpressionList(spillBuilder, arguments);

            BoundExpression newInitializerExpressionOpt;

            if (initializerExpressionOpt != null && initializerExpressionOpt.Kind == BoundKind.SpillSequence)
            {
                var spill = (BoundSpillSequence)initializerExpressionOpt;
                spillBuilder.AddSpill(spill);
                newInitializerExpressionOpt = spill.Value;
            }
            else
            {
                newInitializerExpressionOpt = initializerExpressionOpt;
            }

            BoundObjectCreationExpression newObjectCreation = node.Update(
                node.Constructor,
                newArguments,
                node.ArgumentNamesOpt,
                node.ArgumentRefKindsOpt,
                node.Expanded,
                node.ArgsToParamsOpt,
                node.ConstantValueOpt,
                newInitializerExpressionOpt,
                type);

            return(spillBuilder.BuildSequenceAndFree(F, newObjectCreation));
        }
        public override BoundNode VisitCall(BoundCall node)
        {
            BoundExpression receiverOpt = (BoundExpression)this.Visit(node.ReceiverOpt);
            ReadOnlyArray <BoundExpression> arguments = this.VisitList(node.Arguments);
            TypeSymbol type = this.VisitType(node.Type);

            if (!RequiresSpill(arguments) && !RequiresSpill(receiverOpt))
            {
                return(node.Update(
                           receiverOpt,
                           node.Method,
                           arguments,
                           node.ArgumentNamesOpt,
                           node.ArgumentRefKindsOpt,
                           node.IsDelegateCall,
                           node.Expanded,
                           node.InvokedAsExtensionMethod,
                           node.ArgsToParamsOpt,
                           node.ResultKind, type));
            }

            var spillBuilder = new SpillBuilder();
            var spillResult  = SpillExpressionsWithReceiver(receiverOpt, arguments, spillBuilder, node.Method.ParameterRefKinds);

            var newCall = node.Update(
                spillResult.Item1,
                node.Method,
                spillResult.Item2,
                node.ArgumentNamesOpt,
                node.ArgumentRefKindsOpt,
                node.IsDelegateCall,
                node.Expanded,
                node.InvokedAsExtensionMethod,
                node.ArgsToParamsOpt,
                node.ResultKind,
                type);

            return(spillBuilder.BuildSequenceAndFree(F, newCall));
        }
        public override BoundNode VisitConditionalOperator(BoundConditionalOperator node)
        {
            BoundExpression condition   = (BoundExpression)this.Visit(node.Condition);
            BoundExpression consequence = (BoundExpression)this.Visit(node.Consequence);
            BoundExpression alternative = (BoundExpression)this.Visit(node.Alternative);
            TypeSymbol      type        = this.VisitType(node.Type);

            if (!RequiresSpill(condition, consequence, alternative))
            {
                return(node.Update(condition, consequence, alternative, node.ConstantValueOpt, type));
            }

            var spillBuilder = new SpillBuilder();

            LocalSymbol resultLocal = F.SynthesizedLocal(type, null);

            spillBuilder.Locals.Add(resultLocal);

            BoundExpression newCondition;

            if (condition.Kind == BoundKind.SpillSequence)
            {
                var spill = (BoundSpillSequence)condition;
                spillBuilder.AddSpill(spill);
                newCondition = spill.Value;
            }
            else
            {
                newCondition = condition;
            }

            spillBuilder.Statements.Add(
                F.If(
                    condition: newCondition,
                    thenClause: CrushExpression(consequence, resultLocal),
                    elseClause: CrushExpression(alternative, resultLocal)));

            return(spillBuilder.BuildSequenceAndFree(F, F.Local(resultLocal)));
        }
        public override BoundNode VisitSequence(BoundSequence node)
        {
            ReadOnlyArray <BoundExpression> sideEffects = (ReadOnlyArray <BoundExpression>) this.VisitList(node.SideEffects);
            BoundExpression value = (BoundExpression)this.Visit(node.Value);
            TypeSymbol      type  = this.VisitType(node.Type);

            if (!RequiresSpill(sideEffects) && value.Kind != BoundKind.SpillSequence)
            {
                return(node.Update(node.Locals, sideEffects, value, type));
            }

            var spillBuilder = new SpillBuilder();

            spillBuilder.Locals.AddRange(node.Locals);

            foreach (var sideEffect in sideEffects)
            {
                spillBuilder.Statements.Add(
                    (sideEffect.Kind == BoundKind.SpillSequence)
                    ? RewriteSpillSequenceAsBlock((BoundSpillSequence)sideEffect)
                    : F.ExpressionStatement(sideEffect));
            }

            BoundExpression newValue;

            if (value.Kind == BoundKind.SpillSequence)
            {
                var awaitEffect = (BoundSpillSequence)value;
                spillBuilder.AddSpill(awaitEffect);
                newValue = awaitEffect.Value;
            }
            else
            {
                newValue = value;
            }

            return(spillBuilder.BuildSequenceAndFree(F, newValue));
        }
        public override BoundNode VisitSequence(BoundSequence node)
        {
            ReadOnlyArray<BoundExpression> sideEffects = (ReadOnlyArray<BoundExpression>)this.VisitList(node.SideEffects);
            BoundExpression value = (BoundExpression)this.Visit(node.Value);
            TypeSymbol type = this.VisitType(node.Type);

            if (!RequiresSpill(sideEffects) && value.Kind != BoundKind.SpillSequence)
            {
                return node.Update(node.Locals, sideEffects, value, type);
            }

            var spillBuilder = new SpillBuilder();

            spillBuilder.Locals.AddRange(node.Locals);

            foreach (var sideEffect in sideEffects)
            {
                spillBuilder.Statements.Add(
                    (sideEffect.Kind == BoundKind.SpillSequence)
                    ? RewriteSpillSequenceAsBlock((BoundSpillSequence)sideEffect)
                    : F.ExpressionStatement(sideEffect));
            }

            BoundExpression newValue;
            if (value.Kind == BoundKind.SpillSequence)
            {
                var awaitEffect = (BoundSpillSequence)value;
                spillBuilder.AddSpill(awaitEffect);
                newValue = awaitEffect.Value;
            }
            else
            {
                newValue = value;
            }

            return spillBuilder.BuildSequenceAndFree(F, newValue);
        }
        public override BoundNode VisitArrayCreation(BoundArrayCreation node)
        {
            ReadOnlyArray<BoundExpression> bounds = this.VisitList(node.Bounds);
            BoundArrayInitialization visitedInitializer = (BoundArrayInitialization)this.Visit(node.InitializerOpt);
            TypeSymbol type = this.VisitType(node.Type);

            if (!RequiresSpill(bounds) && (visitedInitializer == null || !RequiresSpill(visitedInitializer.Initializers)))
            {
                return node.Update(bounds, visitedInitializer, type);
            }

            var spillBuilder = new SpillBuilder();
            ReadOnlyArray<BoundExpression> newBounds = SpillExpressionList(spillBuilder, bounds);
            BoundArrayInitialization newInitializerOpt = (visitedInitializer == null) ? visitedInitializer :
                visitedInitializer.Update(SpillExpressionList(spillBuilder, visitedInitializer.Initializers));
            BoundArrayCreation newArrayCreation = node.Update(newBounds, newInitializerOpt, type);
            return spillBuilder.BuildSequenceAndFree(F, newArrayCreation);
        }
Exemple #15
0
 internal void AddSpill(SpillBuilder spill)
 {
     locals.AddRange(spill.locals);
     temps.AddRange(spill.temps);
     statements.AddRange(spill.statements);
 }
        /// <summary>
        /// Spill a list of expressions (e.g. the arguments of a method call).
        /// 
        /// The expressions are processed right-to-left. Once an expression has been found that contains an await
        /// expression, all subsequent expressions are spilled.
        /// 
        /// Example:
        /// 
        ///     (1 + 2, await t1, Foo(), await t2, 3 + 4)
        /// 
        ///     becomes:
        /// 
        ///     Spill(
        ///         spill1 = 1 + 2,
        ///         spill2 = await t1,
        ///         spill3 = Foo(),
        ///         (spill1, spill2, spill3, await t2, 3 + 4))
        /// 
        /// NOTE: Consider nested array initializers:
        /// 
        ///     new int[] {
        ///         { 1, await t1 },
        ///         { 3, await t2 }
        ///     }
        /// 
        /// If the arguments of the top-level initializer had already been spilled, we would end up trying to spill
        /// something like this:
        /// 
        ///     new int[] {
        ///         Spill(
        ///             spill1 = 1,
        ///             { spill1, await t1 }),
        ///         Spill(
        ///             spill2 = 3,
        ///             { spill2, await t2 })
        ///     }
        /// 
        /// The normal rewriting would produce:
        /// 
        ///     Spill(
        ///         spill1 = 1,
        ///         spill3 = { spill1, await t1 },
        ///         spill2 = 3,
        ///         int[] a = new int[] {
        ///             spill3,
        ///             { spill2, await t2 }))
        /// 
        /// Which is invalid, because spill3 does not have a type.
        /// 
        /// To solve this problem the expression list spilled descends into nested array initializers.
        /// 
        /// </summary>
        private ReadOnlyArray<BoundExpression> SpillExpressionList(
            SpillBuilder outerSpillBuilder,
            ReadOnlyArray<BoundExpression> expressions,
            ReadOnlyArray<RefKind> refKindsOpt = default(ReadOnlyArray<RefKind>))
        {
            var spillBuilders = ArrayBuilder<SpillBuilder>.GetInstance();
            bool spilledFirstArg = false;

            ReadOnlyArray<BoundExpression> newArgs = SpillArgumentListInner(expressions, refKindsOpt, spillBuilders, ref spilledFirstArg);

            var spillBuilder = new SpillBuilder();

            spillBuilders.Reverse();
            foreach (var spill in spillBuilders)
            {
                spillBuilder.AddSpill(spill);
                spill.Free();
            }
            spillBuilders.Free();

            outerSpillBuilder.AddSpill(spillBuilder);
            spillBuilder.Free();

            return newArgs;
        }
        public override BoundNode VisitConditionalOperator(BoundConditionalOperator node)
        {
            BoundExpression condition = (BoundExpression)this.Visit(node.Condition);
            BoundExpression consequence = (BoundExpression)this.Visit(node.Consequence);
            BoundExpression alternative = (BoundExpression)this.Visit(node.Alternative);
            TypeSymbol type = this.VisitType(node.Type);

            if (!RequiresSpill(condition, consequence, alternative))
            {
                return node.Update(condition, consequence, alternative, node.ConstantValueOpt, type);
            }

            var spillBuilder = new SpillBuilder();

            LocalSymbol resultLocal = F.SynthesizedLocal(type, null);
            spillBuilder.Locals.Add(resultLocal);

            BoundExpression newCondition;
            if (condition.Kind == BoundKind.SpillSequence)
            {
                var spill = (BoundSpillSequence)condition;
                spillBuilder.AddSpill(spill);
                newCondition = spill.Value;
            }
            else
            {
                newCondition = condition;
            }

            spillBuilder.Statements.Add(
                F.If(
                    condition: newCondition,
                    thenClause: CrushExpression(consequence, resultLocal),
                    elseClause: CrushExpression(alternative, resultLocal)));

            return spillBuilder.BuildSequenceAndFree(F, F.Local(resultLocal));

        }
        public override BoundNode VisitBinaryOperator(BoundBinaryOperator node)
        {
            BoundExpression left = (BoundExpression)this.Visit(node.Left);
            BoundExpression right = (BoundExpression)this.Visit(node.Right);
            TypeSymbol type = this.VisitType(node.Type);
            
            if (!RequiresSpill(left, right))
            {
                return node.Update(node.OperatorKind, left, right, node.ConstantValueOpt, node.MethodOpt, node.ResultKind, type);
            }

            var subExprs = ReadOnlyArray<BoundExpression>.CreateFrom(left, right);
            var spillBuilder = new SpillBuilder();
            var newArgs = SpillExpressionList(spillBuilder, subExprs);
            Debug.Assert(newArgs.Count == 2);

            var newBinaryOperator = node.Update(
                node.OperatorKind,
                newArgs[0],
                newArgs[1],
                node.ConstantValueOpt,
                node.MethodOpt,
                node.ResultKind,
                type);

            return spillBuilder.BuildSequenceAndFree(F, newBinaryOperator);
        }
        public override BoundNode VisitArrayAccess(BoundArrayAccess node)
        {
            BoundExpression expression = (BoundExpression)this.Visit(node.Expression);
            ReadOnlyArray<BoundExpression> indices = (ReadOnlyArray<BoundExpression>)this.VisitList(node.Indices);
            TypeSymbol type = this.VisitType(node.Type);

            if (!RequiresSpill(indices) && expression.Kind != BoundKind.SpillSequence)
            {
                return node.Update(expression, indices, type);
            }

            var spillBuilder = new SpillBuilder();
            var spillResult = SpillExpressionsWithReceiver(expression, indices, spillBuilder, refKindsOpt: ReadOnlyArray<RefKind>.Empty);
            BoundExpression newBoundArrayAccess = node.Update(spillResult.Item1, spillResult.Item2, type);
            return spillBuilder.BuildSequenceAndFree(F, newBoundArrayAccess);
        }
        public override BoundNode VisitCall(BoundCall node)
        {
            BoundExpression receiverOpt = (BoundExpression)this.Visit(node.ReceiverOpt);
            ReadOnlyArray<BoundExpression> arguments = this.VisitList(node.Arguments);
            TypeSymbol type = this.VisitType(node.Type);

            if (!RequiresSpill(arguments) && !RequiresSpill(receiverOpt))
            {
                return node.Update(
                    receiverOpt, 
                    node.Method, 
                    arguments, 
                    node.ArgumentNamesOpt, 
                    node.ArgumentRefKindsOpt, 
                    node.IsDelegateCall,
                    node.Expanded, 
                    node.InvokedAsExtensionMethod, 
                    node.ArgsToParamsOpt, 
                    node.ResultKind, type);
            }

            var spillBuilder = new SpillBuilder();
            var spillResult = SpillExpressionsWithReceiver(receiverOpt, arguments, spillBuilder, node.Method.ParameterRefKinds);

            var newCall = node.Update(
                spillResult.Item1,
                node.Method,
                spillResult.Item2,
                node.ArgumentNamesOpt,
                node.ArgumentRefKindsOpt,
                node.IsDelegateCall,
                node.Expanded,
                node.InvokedAsExtensionMethod,
                node.ArgsToParamsOpt,
                node.ResultKind,
                type);

            return spillBuilder.BuildSequenceAndFree(F, newCall);
        }
        public override BoundNode VisitObjectCreationExpression(BoundObjectCreationExpression node)
        {
            ReadOnlyArray<BoundExpression> arguments = (ReadOnlyArray<BoundExpression>)this.VisitList(node.Arguments);
            BoundExpression initializerExpressionOpt = (BoundExpression)this.Visit(node.InitializerExpressionOpt);
            TypeSymbol type = this.VisitType(node.Type);

            if (!RequiresSpill(arguments) && (initializerExpressionOpt == null || initializerExpressionOpt.Kind != BoundKind.SpillSequence))
            {
                return node.Update(node.Constructor, arguments, node.ArgumentNamesOpt, node.ArgumentRefKindsOpt, node.Expanded, node.ArgsToParamsOpt, node.ConstantValueOpt, initializerExpressionOpt, type);
            }

            var spillBuilder = new SpillBuilder();
            ReadOnlyArray<BoundExpression> newArguments = SpillExpressionList(spillBuilder, arguments);

            BoundExpression newInitializerExpressionOpt;
            if (initializerExpressionOpt != null && initializerExpressionOpt.Kind == BoundKind.SpillSequence)
            {
                var spill = (BoundSpillSequence)initializerExpressionOpt;
                spillBuilder.AddSpill(spill);
                newInitializerExpressionOpt = spill.Value;
            }
            else
            {
                newInitializerExpressionOpt = initializerExpressionOpt;
            }

            BoundObjectCreationExpression newObjectCreation = node.Update(
                node.Constructor,
                newArguments,
                node.ArgumentNamesOpt,
                node.ArgumentRefKindsOpt,
                node.Expanded,
                node.ArgsToParamsOpt,
                node.ConstantValueOpt,
                newInitializerExpressionOpt,
                type);

            return spillBuilder.BuildSequenceAndFree(F, newObjectCreation);
        }
Exemple #22
0
 internal void AddSpill(SpillBuilder spill)
 {
     locals.AddRange(spill.locals);
     temps.AddRange(spill.temps);
     statements.AddRange(spill.statements);
 }
        private BoundExpression SpillLValue(BoundExpression left, SpillBuilder spillBuilder)
        {
            switch (left.Kind)
            {
                case BoundKind.Sequence:
                    {
                        var sequence = (BoundSequence)left;
                        spillBuilder.AddSequence(F, sequence);
                        return SpillLValue(sequence.Value, spillBuilder);
                    }

                case BoundKind.SpillSequence:
                    {
                        var spill = (BoundSpillSequence)left;
                        spillBuilder.AddSpill(spill);
                        return SpillLValue(spill.Value, spillBuilder);
                    }

                case BoundKind.ArrayAccess:
                    {
                        // Case 3.b -

                        var array = (BoundArrayAccess)left;

                        var spillReceiver = F.SpillTemp(array.Expression.Type, array.Expression);
                        spillBuilder.Statements.Add(GenerateSpillInit(spillReceiver));
                        spillBuilder.Temps.Add(spillReceiver);

                        var spilledIndices = ArrayBuilder<BoundExpression>.GetInstance();
                        foreach (var index in array.Indices)
                        {
                            var indexTemp = F.SpillTemp(index.Type, index);
                            spillBuilder.Statements.Add(GenerateSpillInit(indexTemp));
                            spillBuilder.Temps.Add(indexTemp);

                            spilledIndices.Add(indexTemp);
                        }

                        return array.Update(spillReceiver, spilledIndices.ToReadOnlyAndFree(), array.Type);
                    }

                case BoundKind.FieldAccess:
                    {
                        // Case 3.c -

                        var field = (BoundFieldAccess)left;
                        if (Unspillable(field.ReceiverOpt))
                        {
                            return field;
                        }

                        BoundExpression newReceiver;
                        if (field.ReceiverOpt.Type.IsReferenceType)
                        {
                            var receiverTemp = F.SpillTemp(field.ReceiverOpt.Type, field.ReceiverOpt);
                            spillBuilder.Statements.Add(GenerateSpillInit(receiverTemp));
                            spillBuilder.Temps.Add(receiverTemp);

                            newReceiver = receiverTemp;
                        }
                        else
                        {
                            Debug.Assert(field.ReceiverOpt.Type.IsValueType, "Don't spill unconstrained type parameters.");
                            newReceiver = SpillLValue(field.ReceiverOpt, spillBuilder);
                        }

                        return field.Update(
                            newReceiver,
                            field.FieldSymbol,
                            field.ConstantValueOpt,
                            field.ResultKind,
                            field.Type);
                    }

                case BoundKind.Local:
                    {
                        // Case 3.d -
                        return left;
                    }

                default:
                    throw new NotImplementedException("stack spilling for lvalue: " + left.Kind.ToString());
            }
        }
        private ReadOnlyArray<BoundExpression> SpillArgumentListInner(
            ReadOnlyArray<BoundExpression> arguments,
            ReadOnlyArray<RefKind> refKindsOpt,
            ArrayBuilder<SpillBuilder> spillBuilders,
            ref bool spilledFirstArg)
        {
            var newArgsBuilder = ArrayBuilder<BoundExpression>.GetInstance();

            var refKindIterator = refKindsOpt != null ? refKindsOpt.AsReverseEnumerable().GetEnumerator() : null;

            foreach (var arg in arguments.AsReverseEnumerable())
            {
                RefKind refKind = (refKindIterator == null) ? RefKind.None :
                    (refKindIterator.MoveNext()) ? refKindIterator.Current :
                    RefKind.None;

                if (arg.Kind == BoundKind.ArrayInitialization)
                {
                    // Descend into a nested array initializer:
                    var nestedInitializer = ((BoundArrayInitialization)arg);
                    var newInitializers = SpillArgumentListInner(nestedInitializer.Initializers, ReadOnlyArray<RefKind>.Empty, spillBuilders, ref spilledFirstArg);
                    newArgsBuilder.Add(nestedInitializer.Update(newInitializers));
                    continue;
                }

                if (arg.Kind == BoundKind.ArgListOperator)
                {
                    // Descend into arglist:
                    var argList = (BoundArgListOperator)arg;
                    var newArgs = SpillArgumentListInner(argList.Arguments, argList.ArgumentRefKindsOpt, spillBuilders, ref spilledFirstArg);
                    newArgsBuilder.Add(argList.Update(newArgs, argList.ArgumentRefKindsOpt, argList.Type));
                    continue;
                }

                var spillBuilder = new SpillBuilder();

                BoundExpression newExpression;
                if (!spilledFirstArg)
                {
                    if (arg.Kind == BoundKind.SpillSequence)
                    {
                        // We have found the right-most expression containing an await expression. Save the await
                        // result to a temp local
                        spilledFirstArg = true;
                        var spill = (BoundSpillSequence)arg;
                        spillBuilder.AddSpill(spill);
                        newExpression = spill.Value;
                    }
                    else
                    {
                        // We are to the right of any await-containing expressions. The args do not yet need to be
                        // spilled.
                        newExpression = arg;    
                    }
                }
                else
                {
                    // We are to the left of an await-containing expression. Spill the arg.

                    if (Unspillable(arg) ||
                        (arg.Kind == BoundKind.FieldAccess && Unspillable(((BoundFieldAccess)arg).ReceiverOpt)))
                    {
                        newExpression = arg;
                    }
                    else if (refKind != RefKind.None)
                    {
                        newExpression = SpillLValue(arg, spillBuilder);
                    }
                    else
                    {
                        var spillTemp = F.SpillTemp(arg.Type, arg);
                        spillBuilder.Temps.Add(spillTemp);
                        spillBuilder.Statements.Add(GenerateSpillInit(spillTemp));
                        newExpression = spillTemp;
                    }
                }

                newArgsBuilder.Add(newExpression);
                spillBuilders.Add(spillBuilder);
            }

            newArgsBuilder.Reverse();
            return newArgsBuilder.ToReadOnlyAndFree();
        }
        private BoundExpression SpillLValue(BoundExpression left, SpillBuilder spillBuilder)
        {
            switch (left.Kind)
            {
            case BoundKind.Sequence:
            {
                var sequence = (BoundSequence)left;
                spillBuilder.AddSequence(F, sequence);
                return(SpillLValue(sequence.Value, spillBuilder));
            }

            case BoundKind.SpillSequence:
            {
                var spill = (BoundSpillSequence)left;
                spillBuilder.AddSpill(spill);
                return(SpillLValue(spill.Value, spillBuilder));
            }

            case BoundKind.ArrayAccess:
            {
                // Case 3.b -

                var array = (BoundArrayAccess)left;

                var spillReceiver = F.SpillTemp(array.Expression.Type, array.Expression);
                spillBuilder.Statements.Add(GenerateSpillInit(spillReceiver));
                spillBuilder.Temps.Add(spillReceiver);

                var spilledIndices = ArrayBuilder <BoundExpression> .GetInstance();

                foreach (var index in array.Indices)
                {
                    var indexTemp = F.SpillTemp(index.Type, index);
                    spillBuilder.Statements.Add(GenerateSpillInit(indexTemp));
                    spillBuilder.Temps.Add(indexTemp);

                    spilledIndices.Add(indexTemp);
                }

                return(array.Update(spillReceiver, spilledIndices.ToReadOnlyAndFree(), array.Type));
            }

            case BoundKind.FieldAccess:
            {
                // Case 3.c -

                var field = (BoundFieldAccess)left;
                if (Unspillable(field.ReceiverOpt))
                {
                    return(field);
                }

                BoundExpression newReceiver;
                if (field.ReceiverOpt.Type.IsReferenceType)
                {
                    var receiverTemp = F.SpillTemp(field.ReceiverOpt.Type, field.ReceiverOpt);
                    spillBuilder.Statements.Add(GenerateSpillInit(receiverTemp));
                    spillBuilder.Temps.Add(receiverTemp);

                    newReceiver = receiverTemp;
                }
                else
                {
                    Debug.Assert(field.ReceiverOpt.Type.IsValueType, "Don't spill unconstrained type parameters.");
                    newReceiver = SpillLValue(field.ReceiverOpt, spillBuilder);
                }

                return(field.Update(
                           newReceiver,
                           field.FieldSymbol,
                           field.ConstantValueOpt,
                           field.ResultKind,
                           field.Type));
            }

            case BoundKind.Local:
            {
                // Case 3.d -
                return(left);
            }

            default:
                throw new NotImplementedException("stack spilling for lvalue: " + left.Kind.ToString());
            }
        }
        private ReadOnlyArray <BoundExpression> SpillArgumentListInner(
            ReadOnlyArray <BoundExpression> arguments,
            ReadOnlyArray <RefKind> refKindsOpt,
            ArrayBuilder <SpillBuilder> spillBuilders,
            ref bool spilledFirstArg)
        {
            var newArgsBuilder = ArrayBuilder <BoundExpression> .GetInstance();

            var refKindIterator = refKindsOpt != null?refKindsOpt.AsReverseEnumerable().GetEnumerator() : null;

            foreach (var arg in arguments.AsReverseEnumerable())
            {
                RefKind refKind = (refKindIterator == null) ? RefKind.None :
                                  (refKindIterator.MoveNext()) ? refKindIterator.Current :
                                  RefKind.None;

                if (arg.Kind == BoundKind.ArrayInitialization)
                {
                    // Descend into a nested array initializer:
                    var nestedInitializer = ((BoundArrayInitialization)arg);
                    var newInitializers   = SpillArgumentListInner(nestedInitializer.Initializers, ReadOnlyArray <RefKind> .Empty, spillBuilders, ref spilledFirstArg);
                    newArgsBuilder.Add(nestedInitializer.Update(newInitializers));
                    continue;
                }

                if (arg.Kind == BoundKind.ArgListOperator)
                {
                    // Descend into arglist:
                    var argList = (BoundArgListOperator)arg;
                    var newArgs = SpillArgumentListInner(argList.Arguments, argList.ArgumentRefKindsOpt, spillBuilders, ref spilledFirstArg);
                    newArgsBuilder.Add(argList.Update(newArgs, argList.ArgumentRefKindsOpt, argList.Type));
                    continue;
                }

                var spillBuilder = new SpillBuilder();

                BoundExpression newExpression;
                if (!spilledFirstArg)
                {
                    if (arg.Kind == BoundKind.SpillSequence)
                    {
                        // We have found the right-most expression containing an await expression. Save the await
                        // result to a temp local
                        spilledFirstArg = true;
                        var spill = (BoundSpillSequence)arg;
                        spillBuilder.AddSpill(spill);
                        newExpression = spill.Value;
                    }
                    else
                    {
                        // We are to the right of any await-containing expressions. The args do not yet need to be
                        // spilled.
                        newExpression = arg;
                    }
                }
                else
                {
                    // We are to the left of an await-containing expression. Spill the arg.

                    if (Unspillable(arg) ||
                        (arg.Kind == BoundKind.FieldAccess && Unspillable(((BoundFieldAccess)arg).ReceiverOpt)))
                    {
                        newExpression = arg;
                    }
                    else if (refKind != RefKind.None)
                    {
                        newExpression = SpillLValue(arg, spillBuilder);
                    }
                    else
                    {
                        var spillTemp = F.SpillTemp(arg.Type, arg);
                        spillBuilder.Temps.Add(spillTemp);
                        spillBuilder.Statements.Add(GenerateSpillInit(spillTemp));
                        newExpression = spillTemp;
                    }
                }

                newArgsBuilder.Add(newExpression);
                spillBuilders.Add(spillBuilder);
            }

            newArgsBuilder.Reverse();
            return(newArgsBuilder.ToReadOnlyAndFree());
        }