public override BoundNode VisitConditionalOperator(BoundConditionalOperator node)
        {
            BoundSpillSequenceBuilder conditionBuilder = null;
            var condition = VisitExpression(ref conditionBuilder, node.Condition);

            BoundSpillSequenceBuilder consequenceBuilder = null;
            var consequence = VisitExpression(ref consequenceBuilder, node.Consequence);

            BoundSpillSequenceBuilder alternativeBuilder = null;
            var alternative = VisitExpression(ref alternativeBuilder, node.Alternative);

            if (consequenceBuilder == null && alternativeBuilder == null)
            {
                return(UpdateExpression(conditionBuilder, node.Update(node.IsRef, condition, consequence, alternative, node.ConstantValueOpt, node.Type)));
            }

            if (conditionBuilder == null)
            {
                conditionBuilder = new BoundSpillSequenceBuilder();
            }
            if (consequenceBuilder == null)
            {
                consequenceBuilder = new BoundSpillSequenceBuilder();
            }
            if (alternativeBuilder == null)
            {
                alternativeBuilder = new BoundSpillSequenceBuilder();
            }

            if (node.Type.IsVoidType())
            {
                conditionBuilder.AddStatement(
                    _F.If(condition,
                          UpdateStatement(consequenceBuilder, _F.ExpressionStatement(consequence)),
                          UpdateStatement(alternativeBuilder, _F.ExpressionStatement(alternative))));

                return(conditionBuilder.Update(_F.Default(node.Type)));
            }
            else
            {
                var tmp = _F.SynthesizedLocal(node.Type, kind: SynthesizedLocalKind.Spill, syntax: _F.Syntax);

                conditionBuilder.AddLocal(tmp);
                conditionBuilder.AddStatement(
                    _F.If(condition,
                          UpdateStatement(consequenceBuilder, _F.Assignment(_F.Local(tmp), consequence)),
                          UpdateStatement(alternativeBuilder, _F.Assignment(_F.Local(tmp), alternative))));

                return(conditionBuilder.Update(_F.Local(tmp)));
            }
        }
Exemple #2
0
        public override BoundNode VisitConditionalOperator(BoundConditionalOperator node)
        {
            BoundSpillSequenceBuilder conditionBuilder = null;
            var condition = VisitExpression(ref conditionBuilder, node.Condition);

            BoundSpillSequenceBuilder consequenceBuilder = null;
            var consequence = VisitExpression(ref consequenceBuilder, node.Consequence);

            BoundSpillSequenceBuilder alternativeBuilder = null;
            var alternative = VisitExpression(ref alternativeBuilder, node.Alternative);

            if (consequenceBuilder == null && alternativeBuilder == null)
            {
                return(UpdateExpression(conditionBuilder, node.Update(condition, consequence, alternative, node.ConstantValueOpt, node.Type)));
            }

            if (conditionBuilder == null)
            {
                conditionBuilder = new BoundSpillSequenceBuilder();
            }
            if (consequenceBuilder == null)
            {
                consequenceBuilder = new BoundSpillSequenceBuilder();
            }
            if (alternativeBuilder == null)
            {
                alternativeBuilder = new BoundSpillSequenceBuilder();
            }

            if (node.Type.SpecialType == SpecialType.System_Void)
            {
                conditionBuilder.AddStatement(
                    F.If(condition,
                         UpdateStatement(consequenceBuilder, F.ExpressionStatement(consequence), substituteTemps: false),
                         UpdateStatement(alternativeBuilder, F.ExpressionStatement(alternative), substituteTemps: false)));

                return(conditionBuilder.Update(F.Default(node.Type)));
            }
            else
            {
                var tmp = F.SynthesizedLocal(node.Type, kind: SynthesizedLocalKind.AwaitSpill);
                conditionBuilder.AddLocal(tmp, F.Diagnostics);
                conditionBuilder.AddStatement(
                    F.If(condition,
                         UpdateStatement(consequenceBuilder, F.Assignment(F.Local(tmp), consequence), substituteTemps: false),
                         UpdateStatement(alternativeBuilder, F.Assignment(F.Local(tmp), alternative), substituteTemps: false)));

                return(conditionBuilder.Update(F.Local(tmp)));
            }
        }
        public override BoundNode VisitSpillSequence(BoundSpillSequence node)
        {
            var builder = new BoundSpillSequenceBuilder();

            // Ensure later errors (e.g. in async rewriting) are associated with the correct node.
            _F.Syntax = node.Syntax;

            builder.AddStatements(VisitList(node.SideEffects));
            builder.AddLocals(node.Locals);
            var value = VisitExpression(ref builder, node.Value);

            value = Spill(builder, value);
            return(builder.Update(value));
        }
Exemple #4
0
        private static Expression UpdateExpression(BoundSpillSequenceBuilder builder, Expression expression)
        {
            if (builder == null)
            {
                return(expression);
            }

            Debug.Assert(builder.Value == null);
            if (!builder.HasLocals && !builder.HasStatements)
            {
                return(expression);
            }

            return(builder.Update(expression));
        }
Exemple #5
0
        public override BoundNode VisitConversion(BoundConversion node)
        {
            if (node.ConversionKind == ConversionKind.AnonymousFunction && node.Type.IsExpressionTree())
            {
                // Expression trees do not contain any code that requires spilling.
                return(node);
            }

            BoundSpillSequenceBuilder builder = null;
            var operand = VisitExpression(ref builder, node.Operand);

            return(UpdateExpression(
                       builder,
                       node.UpdateOperand(operand)));
        }
Exemple #6
0
        private BoundExpression VisitExpression(ref BoundSpillSequenceBuilder builder, BoundExpression expression)
        {
            // wrap the node in a spill sequence to mark the fact that it must be moved up the tree.
            // The caller will handle this node type if the result is discarded.
            if (expression != null && expression.Kind == BoundKind.AwaitExpression)
            {
                // we force the await expression to be assigned to a temp variable
                var awaitExpression = (BoundAwaitExpression)expression;
                awaitExpression = awaitExpression.Update(
                    VisitExpression(ref builder, awaitExpression.Expression),
                    awaitExpression.GetAwaiter,
                    awaitExpression.IsCompleted,
                    awaitExpression.GetResult,
                    awaitExpression.Type);

                BoundAssignmentOperator assignToTemp;
                var replacement = F.StoreToTemp(awaitExpression, out assignToTemp, kind: SynthesizedLocalKind.AwaitSpill);
                if (builder == null)
                {
                    builder = new BoundSpillSequenceBuilder();
                }

                builder.AddLocal(replacement.LocalSymbol, F.Diagnostics);
                F.Syntax = awaitExpression.Syntax;
                builder.AddStatement(F.ExpressionStatement(assignToTemp));
                return(replacement);
            }

            var e = (BoundExpression)this.Visit(expression);

            if (e == null || e.Kind != SpillSequenceBuilder)
            {
                return(e);
            }

            var newBuilder = (BoundSpillSequenceBuilder)e;

            if (builder == null)
            {
                builder = newBuilder.Update(null);
            }
            else
            {
                builder.Include(newBuilder);
            }

            return(newBuilder.Value);
        }
Exemple #7
0
        public override BoundNode VisitConversion(BoundConversion node)
        {
            BoundSpillSequenceBuilder builder = null;
            var operand = VisitExpression(ref builder, node.Operand);

            return(UpdateExpression(
                       builder,
                       node.Update(
                           operand,
                           node.Conversion,
                           isBaseConversion: node.IsBaseConversion,
                           @checked: node.Checked,
                           explicitCastInCode: node.ExplicitCastInCode,
                           constantValueOpt: node.ConstantValueOpt,
                           type: node.Type)));
        }
Exemple #8
0
 /// <summary>
 /// If an expression node that declares synthesized short-lived locals (currently only sequence) contains
 /// a spill sequence (from an await or switch expression), these locals become long-lived since their
 /// values may be read by code that follows. We promote these variables to long-lived of kind
 /// <see cref="SynthesizedLocalKind.Spill"/>.
 /// </summary>
 private void PromoteAndAddLocals(BoundSpillSequenceBuilder builder, ImmutableArray <LocalSymbol> locals)
 {
     foreach (var local in locals)
     {
         if (local.SynthesizedKind.IsLongLived())
         {
             builder.AddLocal(local);
         }
         else
         {
             LocalSymbol longLived = local.WithSynthesizedLocalKindAndSyntax(SynthesizedLocalKind.Spill, _F.Syntax);
             _tempSubstitution.Add(local, longLived);
             builder.AddLocal(longLived);
         }
     }
 }
Exemple #9
0
 /// <summary>
 /// If an expression node that declares synthesized short-lived locals (currently only sequence) contains an await, these locals become long-lived since their
 /// values may be read by code that follows the await. We promote these variables to long-lived of kind <see cref="SynthesizedLocalKind.AwaitSpill"/>.
 /// </summary>
 private void PromoteAndAddLocals(BoundSpillSequenceBuilder builder, ImmutableArray <LocalSymbol> locals)
 {
     foreach (var local in locals)
     {
         if (local.SynthesizedKind.IsLongLived())
         {
             builder.AddLocal(local, _F.Diagnostics);
         }
         else
         {
             Debug.Assert(_F.Syntax.IsKind(SyntaxKind.AwaitExpression));
             LocalSymbol longLived = local.WithSynthesizedLocalKindAndSyntax(SynthesizedLocalKind.AwaitSpill, _F.Syntax);
             _tempSubstitution.Add(local, longLived);
             builder.AddLocal(longLived, _F.Diagnostics);
         }
     }
 }
Exemple #10
0
        public override void OnMethodInvocationExpression(MethodInvocationExpression node)
        {
            BoundSpillSequenceBuilder builder = null;
            var entity = node.Target.Entity;

            if (entity.EntityType == EntityType.BuiltinFunction)
            {
                if (entity == BuiltinFunction.Eval)
                {
                    OnEval(node);
                }
                else if (entity == BuiltinFunction.Switch)
                {
                    throw new ArgumentException("Should be unreachable: Await spiller on switch");
                }
                else
                {
                    base.OnMethodInvocationExpression(node);
                }
                return;
            }

            var method = (IMethod)entity;
            var refs   = method.GetParameters().Select(p => p.IsByRef).ToList();

            node.Arguments = VisitExpressionList(ref builder, node.Arguments, refs);

            if (builder == null)
            {
                node.Target = VisitExpression(ref builder, node.Target);
            }
            else if (!method.IsStatic)
            {
                // spill the receiver if there were await expressions in the arguments
                var targetBuilder = new BoundSpillSequenceBuilder();

                var target = node.Target;
                var isRef  = TargetSpillRefKind(target);

                node.Target = Spill(targetBuilder, VisitExpression(ref targetBuilder, target), isRef);
                targetBuilder.Include(builder);
                builder = targetBuilder;
            }

            ReplaceCurrentNode(UpdateExpression(builder, node));
        }
Exemple #11
0
        private Expression VisitExpression(ref BoundSpillSequenceBuilder builder, Expression expression)
        {
            // wrap the node in a spill sequence to mark the fact that it must be moved up the tree.
            // The caller will handle this node type if the result is discarded.
            if (expression != null && expression.NodeType == NodeType.AwaitExpression)
            {
                // we force the await expression to be assigned to a temp variable
                var awaitExpression = (AwaitExpression)expression;
                awaitExpression.BaseExpression = VisitExpression(ref builder, awaitExpression.BaseExpression);

                var local       = _F.DeclareTempLocal(_currentMethod, awaitExpression.ExpressionType);
                var replacement = _F.CreateAssignment(
                    awaitExpression.LexicalInfo,
                    _F.CreateLocalReference(local),
                    awaitExpression);
                if (builder == null)
                {
                    builder = new BoundSpillSequenceBuilder();
                }

                builder.AddLocal(local);
                builder.AddStatement(new ExpressionStatement(replacement));
                return(_F.CreateLocalReference(local));
            }

            var e = Visit(expression);

            if (e == null || e.NodeType != SpillSequenceBuilder)
            {
                return(e);
            }

            var newBuilder = (BoundSpillSequenceBuilder)e;

            if (builder == null)
            {
                builder = newBuilder.Update(null);
            }
            else
            {
                builder.Include(newBuilder);
            }

            return(newBuilder.Value);
        }
Exemple #12
0
        /// <summary>
        /// If an expression node that declares synthesized short-lived locals (currently only sequence) contains an await, these locals become long-lived since their
        /// values may be read by code that follows the await. We promote these variables to long-lived of kind <see cref="SynthesizedLocalKind.AwaitSpill"/>.
        /// </summary>
        private void PromoteAndAddLocals(BoundSpillSequenceBuilder builder, ImmutableArray <LocalSymbol> locals)
        {
            foreach (var local in locals)
            {
                if (local.SynthesizedLocalKind.IsLongLived())
                {
                    builder.AddLocal(local, F.Diagnostics);
                }
                else
                {
                    SynthesizedLocal shortLived = (SynthesizedLocal)local;
                    SynthesizedLocal longLived  = shortLived.WithSynthesizedLocalKind(SynthesizedLocalKind.AwaitSpill);
                    tempSubstitution.Add(shortLived, longLived);

                    builder.AddLocal(longLived, F.Diagnostics);
                }
            }
        }
Exemple #13
0
        private ExpressionPairCollection VisitExpressionPairList(
            ref BoundSpillSequenceBuilder builder,
            ExpressionPairCollection args,
            bool forceSpill      = false,
            bool sideEffectsOnly = false)
        {
            var args1 = new ExpressionCollection();

            args1.AddRange(args.Select(p => p.First));
            var args2 = new ExpressionCollection();

            args2.AddRange(args.Select(p => p.Second));
            args1 = VisitExpressionList(ref builder, args1, null, forceSpill, sideEffectsOnly);
            args2 = VisitExpressionList(ref builder, args2, null, forceSpill, sideEffectsOnly);
            args.Clear();
            args.AddRange(args1.Zip(args2, (l, r) => new ExpressionPair(l.LexicalInfo, l, r)));
            return(args);
        }
Exemple #14
0
        private BoundStatement UpdateStatement(BoundSpillSequenceBuilder builder, BoundStatement statement)
        {
            if (builder == null)
            {
                Debug.Assert(statement != null);
                return(statement);
            }

            Debug.Assert(builder.Value == null);
            if (statement != null)
            {
                builder.AddStatement(statement);
            }

            var result = _F.Block(builder.GetLocals(), builder.GetStatements());

            builder.Free();
            return(result);
        }
Exemple #15
0
        private static Statement UpdateStatement(BoundSpillSequenceBuilder builder, Statement statement)
        {
            if (builder == null)
            {
                // statement doesn't contain any await
                Debug.Assert(statement != null);
                return(statement);
            }

            Debug.Assert(builder.Value == null);
            if (statement != null)
            {
                builder.AddStatement(statement);
            }

            var result = new Block(builder.GetStatements().ToArray());

            return(result);
        }
Exemple #16
0
        public override BoundNode VisitConversion(BoundConversion node)
        {
            BoundSpillSequenceBuilder builder = null;
            var operand = VisitExpression(ref builder, node.Operand);

            return(UpdateExpression(
                       builder,
                       node.Update(
                           operand,
                           node.ConversionKind,
                           node.ResultKind,
                           isBaseConversion: node.IsBaseConversion,
                           symbolOpt: node.SymbolOpt,
                           @checked: node.Checked,
                           explicitCastInCode: node.ExplicitCastInCode,
                           isExtensionMethod: node.IsExtensionMethod,
                           isArrayIndex: node.IsArrayIndex,
                           constantValueOpt: node.ConstantValueOpt,
                           type: node.Type)));
        }
Exemple #17
0
        public override BoundNode VisitPointerElementAccess(BoundPointerElementAccess node)
        {
            BoundSpillSequenceBuilder builder = null;
            var             index             = VisitExpression(ref builder, node.Index);
            BoundExpression expression;

            if (builder == null)
            {
                expression = VisitExpression(ref builder, node.Expression);
            }
            else
            {
                var expressionBuilder = new BoundSpillSequenceBuilder();
                expression = VisitExpression(ref expressionBuilder, node.Expression);
                expression = Spill(expressionBuilder, expression);
                expressionBuilder.Include(builder);
                builder = expressionBuilder;
            }

            return(UpdateExpression(builder, node.Update(expression, index, node.Checked, node.Type)));
        }
Exemple #18
0
        public override BoundNode VisitArrayCreation(BoundArrayCreation node)
        {
            BoundSpillSequenceBuilder builder = null;
            var             init = (BoundArrayInitialization)VisitExpression(ref builder, node.InitializerOpt);
            BoundExpression bound;

            if (builder == null)
            {
                bound = VisitExpression(ref builder, node.Size);
            }
            else
            {
                // spill bounds expressions if initializers contain await
                var boundsBuilder = new BoundSpillSequenceBuilder();
                bound = VisitExpression(ref boundsBuilder, node.Size);
                boundsBuilder.Include(builder);
                builder = boundsBuilder;
            }

            return(UpdateExpression(builder, node.Update(bound, init, node.Type)));
        }
Exemple #19
0
 private SliceCollection VisitSliceCollection(
     ref BoundSpillSequenceBuilder builder,
     SliceCollection args)
 {
     foreach (var arg in args)
     {
         if (arg.Begin != null)
         {
             VisitExpression(ref builder, arg.Begin);
         }
         if (arg.End != null)
         {
             VisitExpression(ref builder, arg.End);
         }
         if (arg.Step != null)
         {
             VisitExpression(ref builder, arg.Step);
         }
     }
     return(args);
 }
Exemple #20
0
        private BoundStatement UpdateStatement(BoundSpillSequenceBuilder builder, BoundStatement statement, bool substituteTemps)
        {
            if (builder == null)
            {
                // statement doesn't contain any await
                Debug.Assert(!substituteTemps || _tempSubstitution.Count == 0);
                Debug.Assert(statement != null);
                return(statement);
            }

            Debug.Assert(builder.Value == null);
            if (statement != null)
            {
                builder.AddStatement(statement);
            }

            var substituterOpt = (substituteTemps && _tempSubstitution.Count > 0) ? new LocalSubstituter(_tempSubstitution, RecursionDepth) : null;
            var result         = _F.Block(builder.GetLocals(), builder.GetStatements(substituterOpt));

            builder.Free();
            return(result);
        }
Exemple #21
0
        private BoundExpression VisitExpression(ref BoundSpillSequenceBuilder builder, BoundExpression expression)
        {
            var e = (BoundExpression)this.Visit(expression);

            if (e == null || e.Kind != SpillSequenceBuilderKind)
            {
                return(e);
            }

            var newBuilder = (BoundSpillSequenceBuilder)e;

            if (builder == null)
            {
                builder = newBuilder.Update(null);
            }
            else
            {
                builder.Include(newBuilder);
            }

            return(newBuilder.Value);
        }
Exemple #22
0
        public override BoundNode VisitExpressionStatement(BoundExpressionStatement node)
        {
            EnterStatement(node);

            BoundSpillSequenceBuilder builder = null;
            BoundExpression           expr;

            if (node.Expression.Kind == BoundKind.AwaitExpression)
            {
                // await expression with result discarded
                var awaitExpression = (BoundAwaitExpression)node.Expression;
                var expression      = VisitExpression(ref builder, awaitExpression.Expression);
                expr = awaitExpression.Update(expression, awaitExpression.GetAwaiter, awaitExpression.IsCompleted, awaitExpression.GetResult, awaitExpression.Type);
            }
            else
            {
                expr = VisitExpression(ref builder, node.Expression);
            }

            Debug.Assert(expr != null);
            Debug.Assert(builder == null || builder.Value == null);
            return(UpdateStatement(builder, node.Update(expr), substituteTemps: true));
        }
Exemple #23
0
        public override BoundNode VisitSequence(BoundSequence node)
        {
            BoundSpillSequenceBuilder valueBuilder = null;
            var value = VisitExpression(ref valueBuilder, node.Value);

            BoundSpillSequenceBuilder builder = null;
            var sideEffects = VisitExpressionList(ref builder, node.SideEffects, forceSpill: valueBuilder != null, sideEffectsOnly: true);

            if (builder == null && valueBuilder == null)
            {
                return(node.Update(node.Locals, sideEffects, value, node.Type));
            }

            if (builder == null)
            {
                builder = new BoundSpillSequenceBuilder();
            }

            PromoteAndAddLocals(builder, node.Locals);
            builder.AddExpressions(sideEffects);
            builder.Include(valueBuilder);

            return(builder.Update(value));
        }
Exemple #24
0
        public override BoundNode VisitBinaryOperator(BoundBinaryOperator node)
        {
            BoundSpillSequenceBuilder builder = null;
            var             right             = VisitExpression(ref builder, node.Right);
            BoundExpression left;

            if (builder == null)
            {
                left = VisitExpression(ref builder, node.Left);
            }
            else
            {
                var leftBuilder = new BoundSpillSequenceBuilder();
                left = VisitExpression(ref leftBuilder, node.Left);
                left = Spill(leftBuilder, left);
                if (node.OperatorKind == BinaryOperatorKind.LogicalBoolOr || node.OperatorKind == BinaryOperatorKind.LogicalBoolAnd)
                {
                    var tmp = _F.SynthesizedLocal(node.Type, kind: SynthesizedLocalKind.Spill, syntax: _F.Syntax);
                    leftBuilder.AddLocal(tmp);
                    leftBuilder.AddStatement(_F.Assignment(_F.Local(tmp), left));
                    leftBuilder.AddStatement(_F.If(
                                                 node.OperatorKind == BinaryOperatorKind.LogicalBoolAnd ? _F.Local(tmp) : _F.Not(_F.Local(tmp)),
                                                 UpdateStatement(builder, _F.Assignment(_F.Local(tmp), right))));

                    return(UpdateExpression(leftBuilder, _F.Local(tmp)));
                }
                else
                {
                    // if the right-hand-side has await, spill the left
                    leftBuilder.Include(builder);
                    builder = leftBuilder;
                }
            }

            return(UpdateExpression(builder, node.Update(node.OperatorKind, node.ConstantValue, node.MethodOpt, node.ResultKind, left, right, node.Type)));
        }
Exemple #25
0
        public override BoundNode VisitAssignmentOperator(BoundAssignmentOperator node)
        {
            BoundSpillSequenceBuilder builder = null;
            var right = VisitExpression(ref builder, node.Right);

            BoundExpression left = node.Left;

            if (builder == null)
            {
                left = VisitExpression(ref builder, left);
            }
            else
            {
                // if the right-hand-side has await, spill the left
                var leftBuilder = new BoundSpillSequenceBuilder();

                switch (left.Kind)
                {
                case BoundKind.Local:
                case BoundKind.Parameter:
                    // locals and parameters are directly assignable, LHS is not on the stack so nothing to spill
                    break;

                case BoundKind.FieldAccess:
                    var field = (BoundFieldAccess)left;
                    // static fields are directly assignable, LHS is not on the stack, nothing to spill
                    if (field.FieldSymbol.IsStatic)
                    {
                        break;
                    }

                    // instance fields are directly assignable, but receiver is pushed, so need to spill that.
                    var receiver = VisitExpression(ref leftBuilder, field.ReceiverOpt);
                    receiver = Spill(builder, receiver, field.FieldSymbol.ContainingType.IsValueType ? RefKind.Ref : RefKind.None);
                    left     = field.Update(receiver, field.FieldSymbol, field.ConstantValueOpt, field.ResultKind, field.Type);
                    break;

                case BoundKind.ArrayAccess:
                    var arrayAccess = (BoundArrayAccess)left;
                    // array and indices are pushed on stack so need to spill that
                    var expression = VisitExpression(ref leftBuilder, arrayAccess.Expression);
                    expression = Spill(builder, expression, RefKind.None);
                    var index = this.VisitExpression(ref builder, arrayAccess.Index);
                    left = arrayAccess.Update(expression, index, arrayAccess.Type);
                    break;

                default:
                    // must be something indirectly assignable, just visit and spill as an ordinary Ref  (not a RefReadOnly!!)
                    //
                    // NOTE: in some cases this will result in spiller producing an error.
                    //       For example if the LHS is a ref-returning method like
                    //
                    //       obj.RefReturning(a, b, c) = await Something();
                    //
                    //       the spiller would eventually have to spill the evaluation result of "refReturning" call as an ordinary Ref,
                    //       which it can't.
                    left = Spill(leftBuilder, VisitExpression(ref leftBuilder, left), RefKind.Ref);
                    break;
                }

                leftBuilder.Include(builder);
                builder = leftBuilder;
            }

            return(UpdateExpression(builder, node.Update(left, right, node.IsRef, node.Type)));
        }
Exemple #26
0
        private ImmutableArray <BoundExpression> VisitExpressionList(
            ref BoundSpillSequenceBuilder builder,
            ImmutableArray <BoundExpression> args,
            ImmutableArray <RefKind> refKinds = default(ImmutableArray <RefKind>),
            bool forceSpill      = false,
            bool sideEffectsOnly = false)
        {
            Debug.Assert(!sideEffectsOnly || refKinds.IsDefault);
            Debug.Assert(refKinds.IsDefault || refKinds.Length == args.Length);

            if (args.Length == 0)
            {
                return(args);
            }

            var newList = VisitList(args);

            Debug.Assert(newList.Length == args.Length);

            int lastSpill;

            if (forceSpill)
            {
                lastSpill = newList.Length;
            }
            else
            {
                lastSpill = -1;
                for (int i = newList.Length - 1; i >= 0; i--)
                {
                    if (newList[i].Kind == SpillSequenceBuilderKind)
                    {
                        lastSpill = i;
                        break;
                    }
                }
            }

            if (lastSpill == -1)
            {
                return(newList);
            }

            if (builder == null)
            {
                builder = new BoundSpillSequenceBuilder();
            }

            var result = ArrayBuilder <BoundExpression> .GetInstance(newList.Length);

            // everything up until the last spill must be spilled entirely
            for (int i = 0; i < lastSpill; i++)
            {
                var refKind     = refKinds.IsDefault ? RefKind.None : refKinds[i];
                var replacement = Spill(builder, newList[i], refKind, sideEffectsOnly);

                Debug.Assert(sideEffectsOnly || replacement != null);

                if (!sideEffectsOnly)
                {
                    result.Add(replacement);
                }
            }

            // the value of the last spill and everything that follows is not spilled
            if (lastSpill < newList.Length)
            {
                var lastSpillNode = (BoundSpillSequenceBuilder)newList[lastSpill];
                builder.Include(lastSpillNode);
                result.Add(lastSpillNode.Value);

                for (int i = lastSpill + 1; i < newList.Length; i++)
                {
                    result.Add(newList[i]);
                }
            }

            return(result.ToImmutableAndFree());
        }
Exemple #27
0
        private BoundExpression Spill(
            BoundSpillSequenceBuilder builder,
            BoundExpression expression,
            RefKind refKind      = RefKind.None,
            bool sideEffectsOnly = false)
        {
            Debug.Assert(builder != null);

            while (true)
            {
                switch (expression.Kind)
                {
                case BoundKind.ArrayInitialization:
                    Debug.Assert(refKind == RefKind.None);
                    Debug.Assert(!sideEffectsOnly);
                    var arrayInitialization = (BoundArrayInitialization)expression;
                    var newInitializers     = VisitExpressionList(ref builder, arrayInitialization.Initializers, forceSpill: true);
                    return(arrayInitialization.Update(newInitializers));

                case BoundKind.ArgListOperator:
                    Debug.Assert(refKind == RefKind.None);
                    Debug.Assert(!sideEffectsOnly);
                    var argumentList = (BoundArgListOperator)expression;
                    var newArgs      = VisitExpressionList(ref builder, argumentList.Arguments, argumentList.ArgumentRefKindsOpt, forceSpill: true);
                    return(argumentList.Update(newArgs, argumentList.ArgumentRefKindsOpt, argumentList.Type));

                case SpillSequenceBuilderKind:
                    var sequenceBuilder = (BoundSpillSequenceBuilder)expression;
                    builder.Include(sequenceBuilder);
                    expression = sequenceBuilder.Value;
                    continue;

                case BoundKind.Sequence:
                    // neither the side-effects nor the value of the sequence contains await
                    // (otherwise it would be converted to a SpillSequenceBuilder).
                    if (refKind != RefKind.None)
                    {
                        return(expression);
                    }

                    goto default;

                case BoundKind.ThisReference:
                case BoundKind.BaseReference:
                    if (refKind != RefKind.None || expression.Type.IsReferenceType)
                    {
                        return(expression);
                    }

                    goto default;

                case BoundKind.Parameter:
                    if (refKind != RefKind.None)
                    {
                        return(expression);
                    }

                    goto default;

                case BoundKind.Local:
                    var local = (BoundLocal)expression;
                    if (local.LocalSymbol.SynthesizedKind == SynthesizedLocalKind.Spill || refKind != RefKind.None)
                    {
                        return(local);
                    }

                    goto default;

                case BoundKind.FieldAccess:
                    var field       = (BoundFieldAccess)expression;
                    var fieldSymbol = field.FieldSymbol;
                    if (fieldSymbol.IsStatic)
                    {
                        // no need to spill static fields if used as locations or if readonly
                        if (refKind != RefKind.None || fieldSymbol.IsLet)
                        {
                            return(field);
                        }
                        goto default;
                    }

                    if (refKind == RefKind.None)
                    {
                        goto default;
                    }

                    var receiver = Spill(builder, field.ReceiverOpt, fieldSymbol.ContainingType.IsValueType ? refKind : RefKind.None);
                    return(field.Update(receiver, fieldSymbol, field.ConstantValueOpt, field.ResultKind, field.Type));

                case BoundKind.Literal:
                case BoundKind.TypeExpression:
                    return(expression);

                case BoundKind.ConditionalReceiver:
                    // we will rewrite this as a part of rewriting whole LoweredConditionalAccess
                    // later, if needed
                    return(expression);

                default:
                    if (expression.Type.SpecialType == SpecialType.System_Void || sideEffectsOnly)
                    {
                        builder.AddStatement(_F.ExpressionStatement(expression));
                        return(null);
                    }
                    else
                    {
                        BoundAssignmentOperator assignToTemp;

                        var replacement = _F.StoreToTemp(
                            expression,
                            out assignToTemp,
                            refKind: refKind,
                            kind: SynthesizedLocalKind.Spill,
                            syntaxOpt: _F.Syntax);

                        builder.AddLocal(replacement.LocalSymbol);
                        builder.AddStatement(_F.ExpressionStatement(assignToTemp));
                        return(replacement);
                    }
                }
            }
        }
Exemple #28
0
        private ImmutableArray <BoundExpression> VisitExpressionList(
            ref BoundSpillSequenceBuilder builder,
            ImmutableArray <BoundExpression> args,
            ImmutableArray <RefKind> refKinds = default(ImmutableArray <RefKind>),
            bool forceSpill      = false,
            bool sideEffectsOnly = false)
        {
            var newList = VisitList(args);

            Debug.Assert(newList.Length == args.Length);

            int lastSpill;

            if (forceSpill)
            {
                lastSpill = newList.Length - 1;
            }
            else
            {
                lastSpill = -1;
                for (int i = newList.Length - 1; i >= 0; i--)
                {
                    if (newList[i].Kind == SpillSequenceBuilder)
                    {
                        lastSpill = i;
                        break;
                    }
                }
            }

            if (lastSpill == -1)
            {
                return(newList);
            }

            if (builder == null)
            {
                builder = new BoundSpillSequenceBuilder();
            }

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

            for (int i = 0; i <= lastSpill; i++)
            {
                var refKind     = (!refKinds.IsDefaultOrEmpty && refKinds.Length > i && refKinds[i] != RefKind.None) ? RefKind.Ref : RefKind.None;
                var replacement = Spill(builder, newList[i], refKind, sideEffectsOnly);

                Debug.Assert(sideEffectsOnly || replacement != null);
                if (!sideEffectsOnly)
                {
                    result.Add(replacement);
                }
            }

            for (int i = lastSpill + 1; i < newList.Length; i++)
            {
                result.Add(newList[i]);
            }

            return(result.ToImmutableAndFree());
        }
Exemple #29
0
        public override BoundNode VisitLoweredConditionalAccess(BoundLoweredConditionalAccess node)
        {
            var receiverRefKind = ReceiverSpillRefKind(node.Receiver);

            BoundSpillSequenceBuilder receiverBuilder = null;
            var receiver = VisitExpression(ref receiverBuilder, node.Receiver);

            BoundSpillSequenceBuilder whenNotNullBuilder = null;
            var whenNotNull = VisitExpression(ref whenNotNullBuilder, node.WhenNotNull);

            BoundSpillSequenceBuilder whenNullBuilder = null;
            var whenNullOpt = VisitExpression(ref whenNullBuilder, node.WhenNullOpt);

            if (whenNotNullBuilder == null && whenNullBuilder == null)
            {
                return(UpdateExpression(receiverBuilder, node.Update(receiver, node.HasValueMethodOpt, whenNotNull, whenNullOpt, node.Id, node.Type)));
            }

            if (receiverBuilder == null)
            {
                receiverBuilder = new BoundSpillSequenceBuilder();
            }
            if (whenNotNullBuilder == null)
            {
                whenNotNullBuilder = new BoundSpillSequenceBuilder();
            }
            if (whenNullBuilder == null)
            {
                whenNullBuilder = new BoundSpillSequenceBuilder();
            }


            BoundExpression condition;

            if (receiver.Type.IsReferenceType || receiver.Type.IsValueType || receiverRefKind == RefKind.None)
            {
                // spill to a clone
                receiver = Spill(receiverBuilder, receiver, RefKind.None);
                var hasValueOpt = node.HasValueMethodOpt;

                if (hasValueOpt == null)
                {
                    condition = _F.ObjectNotEqual(
                        _F.Convert(_F.SpecialType(SpecialType.System_Object), receiver),
                        _F.Null(_F.SpecialType(SpecialType.System_Object)));
                }
                else
                {
                    condition = _F.Call(receiver, hasValueOpt);
                }
            }
            else
            {
                Debug.Assert(node.HasValueMethodOpt == null);
                receiver = Spill(receiverBuilder, receiver, RefKind.Ref);

                var clone = _F.SynthesizedLocal(receiver.Type, _F.Syntax, refKind: RefKind.None, kind: SynthesizedLocalKind.Spill);
                receiverBuilder.AddLocal(clone);

                //  (object)default(T) != null
                var isNotClass = _F.ObjectNotEqual(
                    _F.Convert(_F.SpecialType(SpecialType.System_Object), _F.Default(receiver.Type)),
                    _F.Null(_F.SpecialType(SpecialType.System_Object)));

                // isNotCalss || {clone = receiver; (object)clone != null}
                condition = _F.LogicalOr(
                    isNotClass,
                    _F.MakeSequence(
                        _F.AssignmentExpression(_F.Local(clone), receiver),
                        _F.ObjectNotEqual(
                            _F.Convert(_F.SpecialType(SpecialType.System_Object), _F.Local(clone)),
                            _F.Null(_F.SpecialType(SpecialType.System_Object))))
                    );

                receiver = _F.ComplexConditionalReceiver(receiver, _F.Local(clone));
            }

            if (node.Type.SpecialType == SpecialType.System_Void)
            {
                var whenNotNullStatement = UpdateStatement(whenNotNullBuilder, _F.ExpressionStatement(whenNotNull));
                whenNotNullStatement = ConditionalReceiverReplacer.Replace(whenNotNullStatement, receiver, node.Id, RecursionDepth);

                Debug.Assert(whenNullOpt == null || !LocalRewriter.ReadIsSideeffecting(whenNullOpt));

                receiverBuilder.AddStatement(_F.If(condition, whenNotNullStatement));

                return(receiverBuilder.Update(_F.Default(node.Type)));
            }
            else
            {
                var tmp = _F.SynthesizedLocal(node.Type, kind: SynthesizedLocalKind.Spill, syntax: _F.Syntax);
                var whenNotNullStatement = UpdateStatement(whenNotNullBuilder, _F.Assignment(_F.Local(tmp), whenNotNull));
                whenNotNullStatement = ConditionalReceiverReplacer.Replace(whenNotNullStatement, receiver, node.Id, RecursionDepth);

                whenNullOpt = whenNullOpt ?? _F.Default(node.Type);

                receiverBuilder.AddLocal(tmp);
                receiverBuilder.AddStatement(
                    _F.If(condition,
                          whenNotNullStatement,
                          UpdateStatement(whenNullBuilder, _F.Assignment(_F.Local(tmp), whenNullOpt))));

                return(receiverBuilder.Update(_F.Local(tmp)));
            }
        }
Exemple #30
0
        private BoundExpression Spill(
            BoundSpillSequenceBuilder builder,
            BoundExpression expression,
            RefKind refKind      = RefKind.None,
            bool sideEffectsOnly = false)
        {
            Debug.Assert(builder != null);

            while (true)
            {
                switch (expression.Kind)
                {
                case BoundKind.ArrayInitialization:
                    Debug.Assert(refKind == RefKind.None);
                    Debug.Assert(!sideEffectsOnly);
                    var arrayInitialization = (BoundArrayInitialization)expression;
                    var newInitializers     = VisitExpressionList(ref builder, arrayInitialization.Initializers, forceSpill: true);
                    return(arrayInitialization.Update(newInitializers));

                case BoundKind.ArgListOperator:
                    Debug.Assert(refKind == RefKind.None);
                    Debug.Assert(!sideEffectsOnly);
                    var argumentList = (BoundArgListOperator)expression;
                    var newArgs      = VisitExpressionList(ref builder, argumentList.Arguments, argumentList.ArgumentRefKindsOpt, forceSpill: true);
                    return(argumentList.Update(newArgs, argumentList.ArgumentRefKindsOpt, argumentList.Type));

                case SpillSequenceBuilder:
                    var sequenceBuilder = (BoundSpillSequenceBuilder)expression;
                    builder.Include(sequenceBuilder);
                    expression = sequenceBuilder.Value;
                    continue;

                case BoundKind.Sequence:
                    // We don't need promote short-lived variables defined by the sequence to long-lived,
                    // since neither the side-effects nor the value of the sequence contains await
                    // (otherwise it would be converted to a SpillSequenceBuilder).
                    var sequence = (BoundSequence)expression;
                    builder.AddLocals(sequence.Locals);
                    builder.AddExpressions(sequence.SideEffects);
                    expression = sequence.Value;
                    continue;

                case BoundKind.ThisReference:
                case BoundKind.BaseReference:
                    if (refKind != RefKind.None || expression.Type.IsReferenceType)
                    {
                        return(expression);
                    }

                    goto default;

                case BoundKind.Parameter:
                    if (refKind != RefKind.None)
                    {
                        return(expression);
                    }

                    goto default;

                case BoundKind.Local:
                    var local = (BoundLocal)expression;
                    if (local.LocalSymbol.SynthesizedKind == SynthesizedLocalKind.AwaitSpill || refKind != RefKind.None)
                    {
                        return(local);
                    }

                    goto default;

                case BoundKind.FieldAccess:
                    var field = (BoundFieldAccess)expression;
                    if (field.FieldSymbol.IsReadOnly)
                    {
                        if (field.FieldSymbol.IsStatic)
                        {
                            return(field);
                        }
                        if (field.FieldSymbol.ContainingType.IsValueType)
                        {
                            goto default;
                        }
                        // save the receiver; can get the field later.
                        var receiver = Spill(builder, field.ReceiverOpt, (refKind != RefKind.None && field.FieldSymbol.Type.IsReferenceType) ? refKind : RefKind.None, sideEffectsOnly);
                        return(field.Update(receiver, field.FieldSymbol, field.ConstantValueOpt, field.ResultKind, field.Type));
                    }
                    goto default;

                case BoundKind.Call:
                    var call = (BoundCall)expression;
                    if (refKind != RefKind.None)
                    {
                        Debug.Assert(call.Method.RefKind != RefKind.None);
                        _F.Diagnostics.Add(ErrorCode.ERR_RefReturningCallAndAwait, _F.Syntax.Location, call.Method);
                        refKind = RefKind.None;     // Switch the RefKind to avoid asserting later in the pipeline
                    }
                    goto default;

                case BoundKind.Literal:
                case BoundKind.TypeExpression:
                    return(expression);

                case BoundKind.ConditionalReceiver:
                    // we will rewrite this as a part of rewriting whole LoweredConditionalAccess
                    // later, if needed
                    return(expression);

                default:
                    if (expression.Type.SpecialType == SpecialType.System_Void || sideEffectsOnly)
                    {
                        builder.AddStatement(_F.ExpressionStatement(expression));
                        return(null);
                    }
                    else
                    {
                        BoundAssignmentOperator assignToTemp;
                        Debug.Assert(_F.Syntax.IsKind(SyntaxKind.AwaitExpression));

                        var replacement = _F.StoreToTemp(
                            expression,
                            out assignToTemp,
                            refKind: refKind,
                            kind: SynthesizedLocalKind.AwaitSpill,
                            syntaxOpt: _F.Syntax);

                        builder.AddLocal(replacement.LocalSymbol, _F.Diagnostics);
                        builder.AddStatement(_F.ExpressionStatement(assignToTemp));
                        return(replacement);
                    }
                }
            }
        }