示例#1
0
        private LocalSymbol DigForValueLocal(BoundSequence topSequence, BoundExpression value)
        {
            switch (value.Kind)
            {
            case BoundKind.Local:
                var local  = (BoundLocal)value;
                var symbol = local.LocalSymbol;
                if (topSequence.Locals.Contains(symbol))
                {
                    return(symbol);
                }
                break;

            case BoundKind.Sequence:
                return(DigForValueLocal(topSequence, ((BoundSequence)value).Value));

            case BoundKind.FieldAccess:
                var fieldAccess = (BoundFieldAccess)value;
                if (!fieldAccess.FieldSymbol.IsStatic)
                {
                    var receiver = fieldAccess.ReceiverOpt;
                    if (!receiver.Type.IsReferenceType)
                    {
                        return(DigForValueLocal(topSequence, receiver));
                    }
                }
                break;
            }

            return(null);
        }
示例#2
0
        private static IEnumerable <BoundSequence> ReadSequences(XmlReader reader, IEnumerable <MapLocation> locations)
        {
            var locationDictionary = locations.ToDictionary(x => x.LocationName);

            using (XmlReader sequenceReader = reader.ReadSubtree())
            {
                while (sequenceReader.Read())
                {
                    if (sequenceReader.Name == "Sequence")
                    {
                        var sequence = new BoundSequence
                        {
                            WaitTime = int.Parse(sequenceReader.GetAttribute("WaitTime"))
                        };
                        XmlReader sequenceLocationReader = sequenceReader.ReadSubtree();
                        while (sequenceLocationReader.Read())
                        {
                            if (sequenceLocationReader.Name == "Location")
                            {
                                sequence.SetLocationState(locationDictionary[sequenceLocationReader.GetAttribute("LocationName")], sequenceLocationReader.GetAttribute("State"));
                            }
                        }
                        yield return(sequence);
                    }
                }
            }
        }
示例#3
0
 private void RemoveSequenceEventHandler(object sender, RoutedEventArgs e)
 {
     if (GlobalState.Sequences.Count > 1)
     {
         int idx = GlobalState.Sequences.IndexOf(CurrentSequence);
         GlobalState.Sequences.Remove(CurrentSequence);
         if (idx < GlobalState.Sequences.Count)
         {
             CurrentSequence = GlobalState.Sequences[idx];
         }
         else
         {
             CurrentSequence = GlobalState.Sequences[idx - 1];
         }
     }
     else
     {
         for (int i = 0; i < CurrentSequence.States.Count; i++)
         {
             CurrentSequence.States[CurrentSequence.States.ElementAt(i).Key] = "default";
             CurrentSequence.UpdateLocationColors();
         }
     }
     GlobalState.HasMadeChanges = true;
     mainWindow.RefreshTitle();
     UpdateNavigation();
 }
        internal bool Parse(BoundSequence boundSequence)
        {
            base.Parse(boundSequence);
            ParseLocals(boundSequence.Locals, this.Locals);
            foreach (var sideEffect in boundSequence.SideEffects)
            {
                var expression = Deserialize(sideEffect) as Expression;
                Debug.Assert(expression != null);
                this.SideEffects.Add(expression);
            }

            var boundSequencePointExpression = boundSequence.Value as BoundSequencePointExpression;

            if (boundSequencePointExpression != null)
            {
                var boundLocal = boundSequencePointExpression.Expression as BoundLocal;
                if (boundLocal != null && boundSequence.SideEffects != null && boundSequence.SideEffects.Length == 1)
                {
                    this.specialCaseSingleExpression = true;
                    var boundAssignmentOperator = boundSequence.SideEffects.First() as BoundAssignmentOperator;
                    if (boundAssignmentOperator != null)
                    {
                        this.Value = Deserialize(boundAssignmentOperator.Right) as Expression;
                        return(true);
                    }
                }
            }

            this.Value = Deserialize(boundSequence.Value) as Expression;

            return(true);
        }
示例#5
0
        /// <summary>
        /// May introduce a temp which it will return. (otherwise returns null)
        /// </summary>
        private LocalDefinition EmitSequenceAddress(BoundSequence sequence, AddressKind addressKind)
        {
            var hasLocals = !sequence.Locals.IsEmpty;

            if (hasLocals)
            {
                _builder.OpenLocalScope();

                foreach (var local in sequence.Locals)
                {
                    DefineLocal(local, sequence.Syntax);
                }
            }

            EmitSideEffects(sequence);
            var tempOpt = EmitAddress(sequence.Value, addressKind);

            // when a sequence is happened to be a byref receiver
            // we may need to extend the life time of the target until we are done accessing it
            // {.v ; v = Foo(); v}.Bar()     // v should be released only after Bar() is done.
            LocalSymbol doNotRelease = null;

            if (tempOpt == null)
            {
                doNotRelease = DigForValueLocal(sequence);
                if (doNotRelease != null)
                {
                    tempOpt = GetLocal(doNotRelease);
                }
            }

            FreeLocals(sequence, doNotRelease);
            return(tempOpt);
        }
示例#6
0
 public override BoundNode VisitSequence(BoundSequence node)
 {
     var newLocals = RewriteLocals(node.Locals);
     var newSideEffects = VisitList<BoundExpression>(node.SideEffects);
     var newValue = (BoundExpression)this.Visit(node.Value);
     var newType = this.VisitType(node.Type);
     return node.Update(newLocals, newSideEffects, newValue, newType);
 }
示例#7
0
 internal void AddSequence(SyntheticBoundNodeFactory F, BoundSequence sequence)
 {
     locals.AddRange(sequence.Locals);
     foreach (var sideEffect in sequence.SideEffects)
     {
         statements.Add(F.ExpressionStatement(sideEffect));
     }
 }
示例#8
0
                public override BoundNode VisitSequence(BoundSequence node)
                {
                    var oldScope = _currentScope;

                    _currentScope = CreateOrReuseScope(node, node.Locals);
                    var result = base.VisitSequence(node);

                    _currentScope = oldScope;
                    return(result);
                }
示例#9
0
        /// <summary>
        /// May introduce a temp which it will return. (otherwise returns null)
        /// </summary>
        private LocalDefinition EmitSequenceAddress(BoundSequence sequence, AddressKind addressKind)
        {
            DefineAndRecordLocals(sequence);
            EmitSideEffects(sequence);
            var result = EmitAddress(sequence.Value, addressKind);

            CloseScopeAndKeepLocals(sequence);

            return(result);
        }
示例#10
0
        private void SequenceNavigationLastButtonEventHandler(object sender, RoutedEventArgs e)
        {
            if (CurrentSequence.IsBlank())
            {
                GlobalState.Sequences.Remove(CurrentSequence);
            }

            CurrentSequence = GlobalState.Sequences.Last();
            UpdateNavigation();
        }
示例#11
0
        private void SequenceNavigationPreviousButtonEventHandler(object sender, RoutedEventArgs e)
        {
            int seqIndex = GlobalState.Sequences.IndexOf(CurrentSequence);

            if (CurrentSequence.IsBlank())
            {
                GlobalState.Sequences.Remove(CurrentSequence);
            }

            CurrentSequence = GlobalState.Sequences[seqIndex - 1];
            UpdateNavigation();
        }
示例#12
0
        internal void Parse(BoundSequence boundSequence)
        {
            base.Parse(boundSequence);
            ParseLocals(boundSequence.Locals, this.Locals);
            foreach (var sideEffect in boundSequence.SideEffects)
            {
                var expression = Deserialize(sideEffect) as Expression;
                Debug.Assert(expression != null);
                this.SideEffects.Add(expression);
            }

            this.Value = Deserialize(boundSequence.Value) as Expression;
        }
        internal bool Parse(BoundSequence boundSequence)
        {
            base.Parse(boundSequence);

            if (boundSequence.SideEffects.Length > 2 || !boundSequence.SideEffects.All(se => se is BoundAssignmentOperator) || !boundSequence.Locals.Any())
            {
                return(false);
            }

            var boundAssignmentOperator = boundSequence.SideEffects.First() as BoundAssignmentOperator;

            if (boundAssignmentOperator != null)
            {
                var boundExpression = FindValue(boundAssignmentOperator.Left, boundAssignmentOperator.Right);
                if (boundExpression == null)
                {
                    return(false);
                }

                this.Value = Deserialize(boundExpression) as Expression;
            }

            var prefixUnaryExpressionSyntax = boundSequence.Syntax.Green as PrefixUnaryExpressionSyntax;

            if (prefixUnaryExpressionSyntax != null)
            {
                this.OperatorKind = prefixUnaryExpressionSyntax.OperatorToken.Kind;
            }
            else
            {
                var postfixUnaryExpressionSyntax = boundSequence.Syntax.Green as PostfixUnaryExpressionSyntax;
                if (postfixUnaryExpressionSyntax != null)
                {
                    this.OperatorKind = postfixUnaryExpressionSyntax.OperatorToken.Kind;
                }
            }

            Debug.Assert(this.OperatorKind != SyntaxKind.None);

            if (this.Value.Type.OriginalDefinition != null && this.Value.Type.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T)
            {
                return(false);
            }

            if (this.Value.Type.SpecialType == SpecialType.System_Decimal)
            {
                return(false);
            }

            return(true);
        }
示例#14
0
        public override object VisitSequence(BoundSequence node, object arg)
        {
            var sideEffects = node.SideEffects;

            if (!sideEffects.IsNullOrEmpty)
            {
                foreach (var se in sideEffects)
                {
                    VisitExpression(se);
                }
            }

            VisitExpression(node.Value);
            return(null);
        }
示例#15
0
        private void SequenceNavigationNextButtonEventHandler(object sender, RoutedEventArgs e)
        {
            if (CurrentSequence == GlobalState.Sequences.Last())
            {
                int wait = CurrentSequence.WaitTime;
                CurrentSequence = new BoundSequence(GlobalState.Locations, wait);
                GlobalState.Sequences.Add(CurrentSequence);
            }
            else if (CurrentSequence.IsBlank())
            {
                int seqIndex = GlobalState.Sequences.IndexOf(CurrentSequence);
                GlobalState.Sequences.Remove(CurrentSequence);
                CurrentSequence = GlobalState.Sequences[seqIndex];
            }
            else
            {
                CurrentSequence = GlobalState.Sequences[GlobalState.Sequences.IndexOf(CurrentSequence) + 1];
            }

            UpdateNavigation();
        }
示例#16
0
        private void SequenceNavigationInsertButtonEventHandler(object sender, RoutedEventArgs e)
        {
            int seqIndex = GlobalState.Sequences.IndexOf(CurrentSequence);
            int wait     = CurrentSequence.WaitTime;

            if (CurrentSequence.IsBlank())
            {
                GlobalState.Sequences.Remove(CurrentSequence);
            }

            CurrentSequence = new BoundSequence();
            GlobalState.Sequences.Add(CurrentSequence);
            while (GlobalState.Sequences.IndexOf(CurrentSequence) > seqIndex)
            {
                int           idx  = GlobalState.Sequences.IndexOf(CurrentSequence);
                BoundSequence temp = GlobalState.Sequences[idx - 1];
                GlobalState.Sequences[idx - 1] = CurrentSequence;
                GlobalState.Sequences[idx]     = temp;
            }
            CurrentSequence.WaitTime = wait;
            UpdateNavigation();
        }
示例#17
0
        private void PlayExplosionsEventHandler(object sender, EventArgs e)
        {
            if (GlobalState.Sequences.Count > 1 || (GlobalState.Sequences.Count > 0 && !CurrentSequence.IsBlank()))
            {
                if (CurrentSequence.IsBlank())
                {
                    GlobalState.Sequences.Remove(CurrentSequence);
                    CurrentSequence = GlobalState.Sequences.First();
                    if (CurrentSequence == null)
                    {
                        CurrentSequence = new BoundSequence();
                    }
                }
                CurrentSequence.MakeExplosions(mainWindow.MapCanvas);
                int lapover = 42;
                if (CurrentSequence == GlobalState.Sequences.Last())
                {
                    lapover += 84;
                }

                SetWaitTime(CurrentSequence.WaitTime);
                if (CurrentSequence.WaitTime < 42)
                {
                    lapover += 36;
                }

                SequenceCounterDisplay.Content = "Sequence #" + (GlobalState.Sequences.IndexOf(CurrentSequence) + 1);
                sequenceTimer.Interval         = ((int)Math.Ceiling(CurrentSequence.WaitTime / 42.0)) * 42 + lapover;
                if (CurrentSequence != GlobalState.Sequences.Last())
                {
                    CurrentSequence = GlobalState.Sequences[GlobalState.Sequences.IndexOf(CurrentSequence) + 1];
                }
                else
                {
                    CurrentSequence = GlobalState.Sequences.First();
                }
            }
        }
        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);
        }
示例#19
0
        private void EmitSequenceExpression(BoundSequence sequence, bool used)
        {
            var hasLocals = !sequence.Locals.IsEmpty;

            if (hasLocals)
            {
                builder.OpenLocalScope();

                foreach (var local in sequence.Locals)
                {
                    DefineLocal(local, sequence.Syntax);
                }
            }

            EmitSideEffects(sequence);

            // CONSIDER:    LocalRewriter.RewriteNestedObjectOrCollectionInitializerExpression may create a bound sequence with an unused BoundTypeExpression as the value,
            // CONSIDER:    which must be ignored by codegen. See comments in RewriteNestedObjectOrCollectionInitializerExpression for details and an example.
            // CONSIDER:    We may want to instead consider making the Value field of BoundSequence node optional to allow a sequence with
            // CONSIDER:    only side effects and no value. Note that VB's BoundSequence node has an optional value field.
            // CONSIDER:    This will allow us to remove the below check before emitting the value.

            Debug.Assert(sequence.Value.Kind != BoundKind.TypeExpression || !used);
            if (sequence.Value.Kind != BoundKind.TypeExpression)
            {
                EmitExpression(sequence.Value, used);
            }

            if (hasLocals)
            {
                builder.CloseLocalScope();

                foreach (var local in sequence.Locals)
                {
                    FreeLocal(local);
                }
            }
        }
示例#20
0
 private void EmitSideEffects(BoundSequence sequence)
 {
     var sideEffects = sequence.SideEffects;
     if (!sideEffects.IsDefaultOrEmpty)
     {
         foreach (var se in sideEffects)
         {
             EmitExpression(se, false);
         }
     }
 }
示例#21
0
        private void DefineLocals(BoundSequence sequence)
        {
            if (sequence.Locals.IsEmpty)
            {
                return;
            }

            _builder.OpenLocalScope();

            foreach (var local in sequence.Locals)
            {
                DefineLocal(local, sequence.Syntax);
            }
        }
示例#22
0
        public override BoundNode VisitSequence(BoundSequence node)
        {
            BoundSpillSequence2 ss2 = null;
            var value = VisitExpression(ref ss2, node.Value);

            BoundSpillSequence2 ss1 = null;
            var sideEffects = VisitExpressionList(ref ss1, node.SideEffects, forceSpill: ss2 != null, sideEffectsOnly: true);

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

            if (ss1 == null) ss1 = new BoundSpillSequence2(); // possible if sideEffects is empty
            ss1.AddRange(sideEffects, MakeExpressionStatement);
            ss1.AddRange(node.Locals);
            ss1.IncludeSequence(ss2);
            return ss1.Update(value);
        }
示例#23
0
        // here we have a case of indirect assignment:  *t1 = expr;
        // normally we would need to push t1 and that will cause spilling of t2
        //
        // TODO: an interesting case arises in unused x[i]++  and ++x[i] :
        //       we have trees that look like:
        //
        //  t1 = &(x[0])
        //  t2 = *t1
        //  *t1 = t2 + 1
        //
        //  t1 = &(x[0])
        //  t2 = *t1 + 1
        //  *t1 = t2
        //
        //  in these cases, we could keep t2 on stack (dev10 does).
        //  we are dealing with exactly 2 locals and access them in strict order 
        //  t1, t2, t1, t2  and we are not using t2 after that.
        //  We may consider detecting exactly these cases and pretend that we do not need 
        //  to push either t1 or t2 in this case.
        //
        public override BoundNode VisitSequence(BoundSequence node)
        {
            // Normally we can only use stack for local scheduling if stack is not used for evaluation.
            // In a context of a regular block that simply means that eval stack must be empty.
            // Sequences, can be entered on a nonempty evaluation stack
            // Ex:
            //      a.b = Seq{var y, y = 1, y}  // a is on the stack for the duration of the sequence.
            //
            // However evaluation stack at the entry cannot be used inside the sequence, so such stack 
            // works as effective "empty" for locals declared in sequence.
            // Therefore sequence locals can be stack scheduled at same stack as at the entry to the sequence.

            // it may seem attractive to relax the stack requirement to be: 
            // "all uses must agree on stack depth".
            // The following example illustrates a case where x is safely used at "declarationStack + 1" 
            // Ex: 
            //      Seq{var x; y.a = Seq{x = 1; x}; y}  // x is used while y is on the eval stack
            //
            // It is, however not safe assumption in general since eval stack may be accessed between usages.
            // Ex:
            //      Seq{var x; y.a = Seq{x = 1; x}; y.z = x; y} // x blocks access to y
            // 

            // There is one case where we want to tweak the "use at declaration stack" rule - in the case of 
            // compound assignment that involves ByRef operand captures (like:   x[y]++ ) . 
            //
            // Those cases produce specific sequences of the shapes:
            //
            //      prefix:  Seq{var temp, ref operand; operand initializers; *operand = Seq{temp = (T)(operand + 1);  temp;}          result: temp}
            //      postfix: Seq{var temp, ref operand; operand initializers; *operand = Seq{temp = operand;        ;  (T)(temp + 1);} result: temp}
            //
            //  1) temp is used as the result of the sequence (and that is the only reason why it is declared in the outer sequence).
            //  2) all side-effects except the last one do not use the temp.
            //  3) last side-effect is an indirect assignment of a sequence (and target does not involve the temp).
            //            
            //  Note that in a case of side-effects context, the result value will be ignored and therefore
            //  all usages of the nested temp will be confined to the nested sequence that is executed at +1 stack.
            //
            //  We will detect such case and indicate +1 as the desired stack depth at local accesses.
            //
            var declarationStack = StackDepth();

            var locals = node.Locals;
            if (!locals.IsDefaultOrEmpty)
            {
                if (_context == ExprContext.Sideeffects)
                {
                    foreach (var local in locals)
                    {
                        if (IsNestedLocalOfCompoundOperator(local, node))
                        {
                            // special case
                            DeclareLocal(local, declarationStack + 1);
                        }
                        else
                        {
                            DeclareLocal(local, declarationStack);
                        }
                    }
                }
                else
                {
                    DeclareLocals(locals, declarationStack);
                }
            }

            // rewrite operands

            var origContext = _context;

            var sideeffects = node.SideEffects;
            ArrayBuilder<BoundExpression> rewrittenSideeffects = null;
            if (!sideeffects.IsDefault)
            {
                for (int i = 0; i < sideeffects.Length; i++)
                {
                    var sideeffect = sideeffects[i];
                    var rewrittenSideeffect = this.VisitExpression(sideeffect, ExprContext.Sideeffects);

                    if (rewrittenSideeffects == null && rewrittenSideeffect != sideeffect)
                    {
                        rewrittenSideeffects = ArrayBuilder<BoundExpression>.GetInstance();
                        rewrittenSideeffects.AddRange(sideeffects, i);
                    }

                    if (rewrittenSideeffects != null)
                    {
                        rewrittenSideeffects.Add(rewrittenSideeffect);
                    }
                }
            }

            var value = this.VisitExpression(node.Value, origContext);

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

            return base.VisitSequence(node);
        }
示例#26
0
        /// <summary>
        /// Process tempStores and add them as sideeffects to arguments where needed. The return
        /// value tells how many temps are actually needed. For unnecesary temps the corresponding
        /// temp store will be cleared.
        /// </summary>
        private static int MergeArgumentsAndSideEffects(
            ArrayBuilder <BoundAssignmentOperator> tempStores,
            BoundExpression[] arguments)
        {
            Debug.Assert(tempStores != null);
            Debug.Assert(arguments != null);

            int tempsRemainedInUse = tempStores.Count;

            // Suppose we've got temporaries: t0 = A(), t1 = B(), t2 = C(), t4 = D(), t5 = E()
            // and arguments: t0, t2, t1, t4, 10, t5
            // We wish to produce arguments list: A(), SEQ(t2=B(), C()), t2, D(), 10, E()
            //
            // Our algorithm essentially finds temp stores that must happen before given argument load,
            // and if there are any they become sideefects of the given load
            //
            // Constraints:
            //    Stores must happen before corresponding loads. Casuality.
            //    Stores cannot move relative to other stores. If arg was movable it would not need a temp.

            // So for each argument:
            // t0: emit t0 = A(), t0.               ===>  A()
            // t2: emit t1 = B(), t2 = C(), t2.     ===>  SEQ{ B(), C(), t2 }
            // t1: emit t1;                         ===>  t1          //all the dependencies of t1 must be already emitted
            // t4: emit t4 = D(), t4.               ===>  D()
            // t5: emit t5 = E(), t5.               ===>  E()

            int firstUnclaimedStore = 0;

            for (int a = 0; a < arguments.Length; ++a)
            {
                var argument = arguments[a];

                // if argument is a load, search for corresponding store. if store is found, extract
                // the actual expression we were storing and add it as an argument - this one does
                // not need a temp. if there are any unclaimed stores before the found one, add them
                // as sideeffects that preceed this arg, they cannot happen later.
                if (argument.Kind == BoundKind.Local)
                {
                    var correspondingStore = -1;
                    for (int i = firstUnclaimedStore; i < tempStores.Count; i++)
                    {
                        if (tempStores[i].Left == argument)
                        {
                            correspondingStore = i;
                            break;
                        }
                    }

                    // store found?
                    if (correspondingStore != -1)
                    {
                        var value = tempStores[correspondingStore].Right;

                        // the matched store will not need to go into sideffects, only ones before it will
                        // remove the store to signal that we are not using its temp.
                        tempStores[correspondingStore] = null;
                        tempsRemainedInUse--;

                        // no need for sideeffects?
                        // just combine store and load
                        if (correspondingStore == firstUnclaimedStore)
                        {
                            arguments[a] = value;
                        }
                        else
                        {
                            var sideffects = new BoundExpression[correspondingStore - firstUnclaimedStore];
                            for (int s = 0; s < sideffects.Length; s++)
                            {
                                sideffects[s] = tempStores[firstUnclaimedStore + s];
                            }

                            arguments[a] = new BoundSequence(
                                null,
                                null,
                                // this sequence does not own locals. Note that temps that
                                // we use for the rewrite are stored in one arg and loaded
                                // in another so they must live in a scope above.
                                ReadOnlyArray <LocalSymbol> .Empty,
                                sideffects.AsReadOnlyWrap(),
                                value,
                                value.Type);
                        }

                        firstUnclaimedStore = correspondingStore + 1;
                    }
                }
            }

            Debug.Assert(firstUnclaimedStore == tempStores.Count, "not all sideeffects were claimed");
            return(tempsRemainedInUse);
        }
        public override BoundNode VisitObjectCreationExpression(BoundObjectCreationExpression node)
        {
            Debug.Assert(node != null);

            // Rewrite the arguments.
            // NOTE: We may need additional argument rewriting such as generating a params array,
            //       re-ordering arguments based on argsToParamsOpt map, inserting arguments for optional parameters, etc.
            // NOTE: This is done later by MakeArguments, for now we just lower each argument.
            var rewrittenArguments = VisitList(node.Arguments);

            // We have already lowered each argument, but we may need some additional rewriting for the arguments,
            // such as generating a params array, re-ordering arguments based on argsToParamsOpt map, inserting arguments for optional parameters, etc.
            ImmutableArray<LocalSymbol> temps;
            ImmutableArray<RefKind> argumentRefKindsOpt = node.ArgumentRefKindsOpt;
            rewrittenArguments = MakeArguments(node.Syntax, rewrittenArguments, node.Constructor, node.Constructor, node.Expanded, node.ArgsToParamsOpt, ref argumentRefKindsOpt, out temps);

            BoundExpression rewrittenObjectCreation;

            if (_inExpressionLambda)
            {
                if (!temps.IsDefaultOrEmpty)
                {
                    throw ExceptionUtilities.UnexpectedValue(temps.Length);
                }

                rewrittenObjectCreation = node.UpdateArgumentsAndInitializer(rewrittenArguments, MakeObjectCreationInitializerForExpressionTree(node.InitializerExpressionOpt), changeTypeOpt: node.Constructor.ContainingType);

                if (node.Type.IsInterfaceType())
                {
                    Debug.Assert(rewrittenObjectCreation.Type == ((NamedTypeSymbol)node.Type).ComImportCoClass);
                    rewrittenObjectCreation = MakeConversion(rewrittenObjectCreation, node.Type, false, false);
                }

                return rewrittenObjectCreation;
            }

            rewrittenObjectCreation = node.UpdateArgumentsAndInitializer(rewrittenArguments, newInitializerExpression: null, changeTypeOpt: node.Constructor.ContainingType);

            // replace "new S()" with a default struct ctor with "default(S)"
            if (node.Constructor.IsDefaultValueTypeConstructor())
            {
                rewrittenObjectCreation = new BoundDefaultOperator(rewrittenObjectCreation.Syntax, rewrittenObjectCreation.Type);
            }

            if (!temps.IsDefaultOrEmpty)
            {
                rewrittenObjectCreation = new BoundSequence(
                    node.Syntax,
                    temps,
                    ImmutableArray<BoundExpression>.Empty,
                    rewrittenObjectCreation,
                    node.Type);
            }

            if (node.Type.IsInterfaceType())
            {
                Debug.Assert(rewrittenObjectCreation.Type == ((NamedTypeSymbol)node.Type).ComImportCoClass);
                rewrittenObjectCreation = MakeConversion(rewrittenObjectCreation, node.Type, false, false);
            }

            if (node.InitializerExpressionOpt == null || node.InitializerExpressionOpt.HasErrors)
            {
                return rewrittenObjectCreation;
            }

            return MakeObjectCreationWithInitializer(node.Syntax, rewrittenObjectCreation, node.InitializerExpressionOpt, node.Type);
        }
示例#28
0
        private void FreeLocals(BoundSequence sequence)
        {
            if (sequence.Locals.IsEmpty)
            {
                return;
            }

            builder.CloseLocalScope();

            foreach (var local in sequence.Locals)
            {
                FreeLocal(local);
            }
        }
示例#29
0
        /// <summary>
        /// Process tempStores and add them as side-effects to arguments where needed. The return
        /// value tells how many temps are actually needed. For unnecessary temps the corresponding
        /// temp store will be cleared.
        /// </summary>
        private static int MergeArgumentsAndSideEffects(
            BoundExpression[] arguments,
            ArrayBuilder<RefKind> refKinds,
            ArrayBuilder<BoundAssignmentOperator> tempStores)
        {
            Debug.Assert(arguments != null);
            Debug.Assert(refKinds != null);
            Debug.Assert(tempStores != null);

            int tempsRemainedInUse = tempStores.Count;

            // Suppose we've got temporaries: t0 = A(), t1 = B(), t2 = C(), t4 = D(), t5 = E()
            // and arguments: t0, t2, t1, t4, 10, t5
            //
            // We wish to produce arguments list: A(), SEQ(t1=B(), C()), t1, D(), 10, E()
            //
            // Our algorithm essentially finds temp stores that must happen before given argument
            // load, and if there are any they become side effects of the given load.
            // Stores immediately followed by loads of the same thing can be eliminated.
            //
            // Constraints:
            //    Stores must happen before corresponding loads.
            //    Stores cannot move relative to other stores. If arg was movable it would not need a temp.

            int firstUnclaimedStore = 0;

            for (int a = 0; a < arguments.Length; ++a)
            {
                var argument = arguments[a];

                // if argument is a load, search for corresponding store. if store is found, extract
                // the actual expression we were storing and add it as an argument - this one does
                // not need a temp. if there are any unclaimed stores before the found one, add them
                // as side effects that precede this arg, they cannot happen later.
                // NOTE: missing optional parameters are not filled yet and therefore nulls - no need to do anything for them
                if (argument?.Kind == BoundKind.Local)
                {
                    var correspondingStore = -1;
                    for (int i = firstUnclaimedStore; i < tempStores.Count; i++)
                    {
                        if (tempStores[i].Left == argument)
                        {
                            correspondingStore = i;
                            break;
                        }
                    }

                    // store found?
                    if (correspondingStore != -1)
                    {
                        var value = tempStores[correspondingStore].Right;

                        // When we created the temp, we dropped the argument RefKind
                        // since the local contained its own RefKind. Since we're removing
                        // the temp, the argument RefKind needs to be restored.
                        refKinds[a] = ((BoundLocal)argument).LocalSymbol.RefKind;

                        // the matched store will not need to go into side-effects, only ones before it will
                        // remove the store to signal that we are not using its temp.
                        tempStores[correspondingStore] = null;
                        tempsRemainedInUse--;

                        // no need for side-effects?
                        // just combine store and load
                        if (correspondingStore == firstUnclaimedStore)
                        {
                            arguments[a] = value;
                        }
                        else
                        {
                            var sideeffects = new BoundExpression[correspondingStore - firstUnclaimedStore];
                            for (int s = 0; s < sideeffects.Length; s++)
                            {
                                sideeffects[s] = tempStores[firstUnclaimedStore + s];
                            }

                            arguments[a] = new BoundSequence(
                                        value.Syntax,
                                        // this sequence does not own locals. Note that temps that
                                        // we use for the rewrite are stored in one arg and loaded
                                        // in another so they must live in a scope above.
                                        ImmutableArray<LocalSymbol>.Empty,
                                        sideeffects.AsImmutableOrNull(),
                                        value,
                                        value.Type);
                        }

                        firstUnclaimedStore = correspondingStore + 1;
                    }
                }
            }

            Debug.Assert(firstUnclaimedStore == tempStores.Count, "not all side-effects were claimed");
            return tempsRemainedInUse;
        }
示例#30
0
        // Omit ref feature for COM interop: We can pass arguments by value for ref parameters if we are calling a method/property on an instance of a COM imported type.
        // We should have ignored the 'ref' on the parameter during overload resolution for the given method call.
        // If we had any ref omitted argument for the given call, we create a temporary local and
        // replace the argument with the following BoundSequence: { side-effects: { temp = argument }, value = { ref temp } }
        // NOTE: The temporary local must be scoped to live across the entire BoundCall node,
        // otherwise the codegen optimizer might re-use the same temporary for multiple ref-omitted arguments for this call.
        private void RewriteArgumentsForComCall(
            ImmutableArray<ParameterSymbol> parameters,
            BoundExpression[] actualArguments, //already re-ordered to match parameters
            ArrayBuilder<RefKind> argsRefKindsBuilder,
            ArrayBuilder<LocalSymbol> temporariesBuilder)
        {
            Debug.Assert(actualArguments != null);
            Debug.Assert(actualArguments.Length == parameters.Length);

            Debug.Assert(argsRefKindsBuilder == null || argsRefKindsBuilder.Count == parameters.Length);

            var argsCount = actualArguments.Length;

            for (int argIndex = 0; argIndex < argsCount; ++argIndex)
            {
                RefKind paramRefKind = parameters[argIndex].RefKind;
                RefKind argRefKind = argsRefKindsBuilder[argIndex];

                // Rewrite only if the argument was passed with no ref/out and the
                // parameter was declared ref. 
                if (argRefKind != RefKind.None || paramRefKind != RefKind.Ref)
                {
                    continue;
                }

                var argument = actualArguments[argIndex];
                if (argument.Kind == BoundKind.Local)
                {
                    var localRefKind = ((BoundLocal)argument).LocalSymbol.RefKind;
                    if (localRefKind == RefKind.Ref)
                    {
                        // Already passing an address from the ref local.
                        continue;
                    }

                    Debug.Assert(localRefKind == RefKind.None);
                }

                BoundAssignmentOperator boundAssignmentToTemp;
                BoundLocal boundTemp = _factory.StoreToTemp(argument, out boundAssignmentToTemp);

                actualArguments[argIndex] = new BoundSequence(
                    argument.Syntax,
                    locals: ImmutableArray<LocalSymbol>.Empty,
                    sideEffects: ImmutableArray.Create<BoundExpression>(boundAssignmentToTemp),
                    value: boundTemp,
                    type: boundTemp.Type);
                argsRefKindsBuilder[argIndex] = RefKind.Ref;

                temporariesBuilder.Add(boundTemp.LocalSymbol);
            }
        }
示例#31
0
 // if sequence value is a local scoped to the sequence, return that local
 private LocalSymbol DigForValueLocal(BoundSequence topSequence)
 {
     return(DigForValueLocal(topSequence, topSequence.Value));
 }
示例#32
0
 public override BoundNode VisitSequence(BoundSequence node)
 {
     var newLocals = RewriteLocals(node.Locals);
     var newSideEffects = VisitList<BoundExpression>(node.SideEffects);
     var newValue = (BoundExpression)this.Visit(node.Value);
     var newType = this.VisitType(node.Type);
     return node.Update(newLocals, newSideEffects, newValue, newType);
 }
示例#33
0
        private void EmitSequenceCondBranch(BoundSequence sequence, ref object dest, bool sense)
        {
            var hasLocals = !sequence.Locals.IsEmpty;

            if (hasLocals)
            {
                builder.OpenLocalScope();

                foreach (var local in sequence.Locals)
                {
                    DefineLocal(local, sequence.Syntax);
                }
            }

            EmitSideEffects(sequence);
            EmitCondBranch(sequence.Value, ref dest, sense);

            if (hasLocals)
            {
                builder.CloseLocalScope();

                foreach (var local in sequence.Locals)
                {
                    FreeLocal(local);
                }
            }
        }
示例#34
0
 // if sequence value is a local scoped to the sequence, return that local
 private LocalSymbol DigForValueLocal(BoundSequence topSequence)
 {
     return DigForValueLocal(topSequence, topSequence.Value);
 }
示例#35
0
        private void EmitSequenceCondBranch(BoundSequence sequence, ref object dest, bool sense)
        {
            DefineLocals(sequence);
            EmitSideEffects(sequence);
            EmitCondBranch(sequence.Value, ref dest, sense);

            // sequence is used as a value, can release all locals
            FreeLocals(sequence, doNotRelease: null);
        }
示例#36
0
        private LocalSymbol DigForValueLocal(BoundSequence topSequence, BoundExpression value)
        {
            switch (value.Kind)
            {
                case BoundKind.Local:
                    var local = (BoundLocal)value;
                    var symbol = local.LocalSymbol;
                    if (topSequence.Locals.Contains(symbol))
                    {
                        return symbol;
                    }
                    break;

                case BoundKind.Sequence:
                    return DigForValueLocal(topSequence, ((BoundSequence)value).Value);

                case BoundKind.FieldAccess:
                    var fieldAccess = (BoundFieldAccess)value;
                    if (!fieldAccess.FieldSymbol.IsStatic)
                    {
                        var receiver = fieldAccess.ReceiverOpt;
                        if (!receiver.Type.IsReferenceType)
                        {
                            return DigForValueLocal(topSequence, receiver);
                        }
                    }
                    break;
            }

            return null;
        }
        /// <summary>
        /// Process tempStores and add them as sideeffects to arguments where needed. The return
        /// value tells how many temps are actually needed. For unnecesary temps the corresponding
        /// temp store will be cleared.
        /// </summary>
        private static int MergeArgumentsAndSideEffects(
            ArrayBuilder<BoundAssignmentOperator> tempStores,
            BoundExpression[] arguments)
        {
            Debug.Assert(tempStores != null);
            Debug.Assert(arguments != null);

            int tempsRemainedInUse = tempStores.Count;

            // Suppose we've got temporaries: t0 = A(), t1 = B(), t2 = C(), t4 = D(), t5 = E()
            // and arguments: t0, t2, t1, t4, 10, t5
            // We wish to produce arguments list: A(), SEQ(t2=B(), C()), t2, D(), 10, E()
            //
            // Our algorithm essentially finds temp stores that must happen before given argument load,
            // and if there are any they become sideefects of the given load
            //
            // Constraints:
            //    Stores must happen before corresponding loads. Casuality. 
            //    Stores cannot move relative to other stores. If arg was movable it would not need a temp.

            // So for each argument:
            // t0: emit t0 = A(), t0.               ===>  A()
            // t2: emit t1 = B(), t2 = C(), t2.     ===>  SEQ{ B(), C(), t2 }
            // t1: emit t1;                         ===>  t1          //all the dependencies of t1 must be already emitted
            // t4: emit t4 = D(), t4.               ===>  D()
            // t5: emit t5 = E(), t5.               ===>  E()

            int firstUnclaimedStore = 0;

            for (int a = 0; a < arguments.Length; ++a)
            {
                var argument = arguments[a];

                // if argument is a load, search for corresponding store. if store is found, extract
                // the actual expression we were storing and add it as an argument - this one does
                // not need a temp. if there are any unclaimed stores before the found one, add them
                // as sideeffects that preceed this arg, they cannot happen later.
                if (argument.Kind == BoundKind.Local)
                {
                    var correspondingStore = -1;
                    for (int i = firstUnclaimedStore; i < tempStores.Count; i++)
                    {
                        if (tempStores[i].Left == argument)
                        {
                            correspondingStore = i;
                            break;
                        }
                    }

                    // store found?
                    if (correspondingStore != -1)
                    {
                        var value = tempStores[correspondingStore].Right;

                        // the matched store will not need to go into sideffects, only ones before it will
                        // remove the store to signal that we are not using its temp.
                        tempStores[correspondingStore] = null;
                        tempsRemainedInUse--;

                        // no need for sideeffects?
                        // just combine store and load
                        if (correspondingStore == firstUnclaimedStore)
                        {
                            arguments[a] = value;
                        }
                        else
                        {
                            var sideffects = new BoundExpression[correspondingStore - firstUnclaimedStore];
                            for (int s = 0; s < sideffects.Length; s++)
                            {
                                sideffects[s] = tempStores[firstUnclaimedStore + s];
                            }

                            arguments[a] = new BoundSequence(
                                        null,
                                        null,
                                // this sequence does not own locals. Note that temps that
                                // we use for the rewrite are stored in one arg and loaded
                                // in another so they must live in a scope above.
                                        ReadOnlyArray<LocalSymbol>.Empty,
                                        sideffects.AsReadOnlyWrap(),
                                        value,
                                        value.Type);
                        }

                        firstUnclaimedStore = correspondingStore + 1;
                    }
                }
            }

            Debug.Assert(firstUnclaimedStore == tempStores.Count, "not all sideeffects were claimed");
            return tempsRemainedInUse;
        }
        private BoundNode RewriteWithRefOperand(
            bool isPrefix,
            bool isChecked,
            ArrayBuilder<LocalSymbol> tempSymbols,
            ArrayBuilder<BoundExpression> tempInitializers,
            CSharpSyntaxNode syntax,
            BoundExpression operand,
            TypeSymbol operandType,
            BoundExpression boundTemp,
            BoundExpression newValue)
        {
            var tempValue = isPrefix ? newValue : MakeRValue(operand);
            var tempAssignment = MakeAssignmentOperator(syntax, boundTemp, tempValue, operandType, used: false, isChecked: isChecked, isCompoundAssignment: false);

            var operandValue = isPrefix ? boundTemp : newValue;
            var tempAssignedAndOperandValue = new BoundSequence(
                    syntax,
                    ImmutableArray<LocalSymbol>.Empty,
                    ImmutableArray.Create<BoundExpression>(tempAssignment),
                    operandValue,
                    tempValue.Type);

            // prefix:  operand = Seq{temp = (T)(operand + 1);  temp;}
            // postfix: operand = Seq{temp = operand;        ;  (T)(temp + 1);}
            BoundExpression operandAssignment = MakeAssignmentOperator(syntax, operand, tempAssignedAndOperandValue, operandType, used: false, isChecked: isChecked, isCompoundAssignment: false);

            // prefix:  Seq{operand initializers; operand = Seq{temp = (T)(operand + 1);  temp;}          result: temp}
            // postfix: Seq{operand initializers; operand = Seq{temp = operand;        ;  (T)(temp + 1);} result: temp}
            tempInitializers.Add(operandAssignment);
            return new BoundSequence(
                syntax: syntax,
                locals: tempSymbols.ToImmutableAndFree(),
                sideEffects: tempInitializers.ToImmutableAndFree(),
                value: boundTemp,
                type: operandType);
        }
示例#39
0
        // detect a pattern used in compound operators
        // where a temp is declared in the outer sequence
        // only because it must be returned, otherwise all uses are 
        // confined to the nested sequence that is assigned indirectly of to an instance field (and therefore has +1 stack)
        // in such case the desired stack for this local is +1
        private static bool IsNestedLocalOfCompoundOperator(LocalSymbol local, BoundSequence node)
        {
            var value = node.Value;

            // local must be used as the value of the sequence.
            if (value != null && value.Kind == BoundKind.Local && ((BoundLocal)value).LocalSymbol == local)
            {
                var sideeffects = node.SideEffects;
                var lastSideeffect = sideeffects.LastOrDefault();

                if (lastSideeffect != null)
                {
                    // last side-effect must be an indirect assignment of a sequence.
                    if (lastSideeffect.Kind == BoundKind.AssignmentOperator)
                    {
                        var assignment = (BoundAssignmentOperator)lastSideeffect;
                        if (IsIndirectOrInstanceFieldAssignment(assignment) &&
                            assignment.Right.Kind == BoundKind.Sequence)
                        {
                            // and no other side-effects should use the variable
                            var localUsedWalker = new LocalUsedWalker(local);
                            for (int i = 0; i < sideeffects.Length - 1; i++)
                            {
                                if (localUsedWalker.IsLocalUsedIn(sideeffects[i]))
                                {
                                    return false;
                                }
                            }

                            // and local is not used on the left of the assignment 
                            // (extra check, but better be safe)
                            if (localUsedWalker.IsLocalUsedIn(assignment.Left))
                            {
                                return false;
                            }

                            // it should be used somewhere
                            Debug.Assert(localUsedWalker.IsLocalUsedIn(assignment.Right), "who assigns the temp?");

                            return true;
                        }
                    }
                }
            }

            return false;
        }
示例#40
0
        /// <summary>
        /// May introduce a temp which it will return. (otherwise returns null)
        /// </summary>
        private LocalDefinition EmitSequenceAddress(BoundSequence sequence, AddressKind addressKind)
        {
            var hasLocals = !sequence.Locals.IsEmpty;

            if (hasLocals)
            {
                builder.OpenLocalScope();

                foreach (var local in sequence.Locals)
                {
                    DefineLocal(local, sequence.Syntax);
                }
            }

            EmitSideEffects(sequence);
            var tempOpt = EmitAddress(sequence.Value, addressKind);

            // when a sequence is happened to be a byref receiver
            // we may need to extend the life time of the target until we are done accessing it
            // {.v ; v = Foo(); v}.Bar()     // v should be released after Bar() is over.
            LocalSymbol doNotRelease = null;
            if (tempOpt == null)
            {
                BoundLocal referencedLocal = DigForLocal(sequence.Value);
                if (referencedLocal != null)
                {
                    doNotRelease = referencedLocal.LocalSymbol;
                }
            }

            if (hasLocals)
            {
                builder.CloseLocalScope();

                foreach (var local in sequence.Locals)
                {
                    if (local != doNotRelease)
                    {
                        FreeLocal(local);
                    }
                    else
                    {
                        tempOpt = GetLocal(doNotRelease);
                    }
                }
            }

            return tempOpt;
        }
示例#41
0
        private void EmitSequenceExpression(BoundSequence sequence, bool used)
        {
            DefineLocals(sequence);
            EmitSideEffects(sequence);

            // CONSIDER:    LocalRewriter.RewriteNestedObjectOrCollectionInitializerExpression may create a bound sequence with an unused BoundTypeExpression as the value,
            // CONSIDER:    which must be ignored by codegen. See comments in RewriteNestedObjectOrCollectionInitializerExpression for details and an example.
            // CONSIDER:    We may want to instead consider making the Value field of BoundSequence node optional to allow a sequence with
            // CONSIDER:    only side effects and no value. Note that VB's BoundSequence node has an optional value field.
            // CONSIDER:    This will allow us to remove the below check before emitting the value.

            Debug.Assert(sequence.Value.Kind != BoundKind.TypeExpression || !used);
            if (sequence.Value.Kind != BoundKind.TypeExpression)
            {
                EmitExpression(sequence.Value, used);
            }

            // sequence is used as a value, can release all locals
            FreeLocals(sequence, doNotRelease: null);
        }
示例#42
0
            public override BoundNode VisitSequence(BoundSequence node)
            {
                if (node.Locals.IsDefaultOrEmpty)
                {
                    // ignore blocks that declare no variables.
                    return base.VisitSequence(node);
                }

                var previousBlock = PushBlock(node, node.Locals);
                var result = base.VisitSequence(node);
                PopBlock(previousBlock);
                return result;
            }
示例#43
0
        private void FreeLocals(BoundSequence sequence, LocalSymbol doNotRelease)
        {
            if (sequence.Locals.IsEmpty)
            {
                return;
            }

            _builder.CloseLocalScope();

            foreach (var local in sequence.Locals)
            {
                if ((object)local != doNotRelease)
                {
                    FreeLocal(local);
                }
            }
        }
示例#44
0
 private void EmitSequenceCondBranch(BoundSequence sequence, ref object dest, bool sense)
 {
     DefineLocals(sequence);
     EmitSideEffects(sequence);
     EmitCondBranch(sequence.Value, ref dest, sense);
     FreeLocals(sequence);
 }
        private BoundExpression TrivialLiftedComparisonOperatorOptimizations(
            CSharpSyntaxNode syntax,
            BinaryOperatorKind kind,
            BoundExpression left,
            BoundExpression right,
            MethodSymbol method)
        {
            Debug.Assert(left != null);
            Debug.Assert(right != null);

            // Optimization #1: if both sides are null then the result 
            // is either true (for equality) or false (for everything else.)

            bool leftAlwaysNull = NullableNeverHasValue(left);
            bool rightAlwaysNull = NullableNeverHasValue(right);

            TypeSymbol boolType = _compilation.GetSpecialType(SpecialType.System_Boolean);

            if (leftAlwaysNull && rightAlwaysNull)
            {
                return MakeLiteral(syntax, ConstantValue.Create(kind.Operator() == BinaryOperatorKind.Equal), boolType);
            }

            // Optimization #2: If both sides are non-null then we can again eliminate the lifting entirely.

            BoundExpression leftNonNull = NullableAlwaysHasValue(left);
            BoundExpression rightNonNull = NullableAlwaysHasValue(right);

            if (leftNonNull != null && rightNonNull != null)
            {
                return MakeBinaryOperator(
                    syntax: syntax,
                    operatorKind: kind.Unlifted(),
                    loweredLeft: leftNonNull,
                    loweredRight: rightNonNull,
                    type: boolType,
                    method: method);
            }

            // Optimization #3: If one side is null and the other is definitely not, then we generate the side effects
            // of the non-null side and result in true (for not-equals) or false (for everything else.)

            BinaryOperatorKind operatorKind = kind.Operator();

            if (leftAlwaysNull && rightNonNull != null || rightAlwaysNull && leftNonNull != null)
            {
                BoundExpression result = MakeLiteral(syntax, ConstantValue.Create(operatorKind == BinaryOperatorKind.NotEqual), boolType);

                BoundExpression nonNull = leftAlwaysNull ? rightNonNull : leftNonNull;

                if (ReadIsSideeffecting(nonNull))
                {
                    result = new BoundSequence(
                                    syntax: syntax,
                                    locals: ImmutableArray<LocalSymbol>.Empty,
                                    sideEffects: ImmutableArray.Create<BoundExpression>(nonNull),
                                    value: result,
                                    type: boolType);
                }

                return result;
            }

            // Optimization #4: If one side is null and the other is unknown, then we have three cases:
            // #4a: If we have x == null then that becomes !x.HasValue.
            // #4b: If we have x != null then that becomes x.HasValue.
            // #4c: If we have x OP null then that becomes side effects of x, result in false.

            if (leftAlwaysNull || rightAlwaysNull)
            {
                BoundExpression maybeNull = leftAlwaysNull ? right : left;

                if (operatorKind == BinaryOperatorKind.Equal || operatorKind == BinaryOperatorKind.NotEqual)
                {
                    BoundExpression callHasValue = MakeNullableHasValue(syntax, maybeNull);
                    BoundExpression result = operatorKind == BinaryOperatorKind.Equal ?
                        MakeUnaryOperator(UnaryOperatorKind.BoolLogicalNegation, syntax, null, callHasValue, boolType) :
                        callHasValue;
                    return result;
                }
                else
                {
                    BoundExpression falseExpr = MakeBooleanConstant(syntax, operatorKind == BinaryOperatorKind.NotEqual);
                    return _factory.Sequence(maybeNull, falseExpr);
                }
            }

            return null;
        }
 public override BoundNode VisitSequence(BoundSequence node)
 {
     AddVariables(node.Locals);
     return(base.VisitSequence(node));
 }