private static void RewriteLocalDeclaration(
            CSharpCompilation compilation,
            EENamedTypeSymbol container,
            HashSet <LocalSymbol> declaredLocals,
            ArrayBuilder <BoundStatement> statements,
            BoundLocalDeclaration node)
        {
            Debug.Assert(node.ArgumentsOpt.IsDefault);

            var local  = node.LocalSymbol;
            var syntax = node.Syntax;

            declaredLocals.Add(local);

            var voidType   = compilation.GetSpecialType(SpecialType.System_Void);
            var objectType = compilation.GetSpecialType(SpecialType.System_Object);
            var typeType   = compilation.GetWellKnownType(WellKnownType.System_Type);
            var stringType = compilation.GetSpecialType(SpecialType.System_String);

            // <>CreateVariable(Type type, string name)
            var method = container.GetOrAddSynthesizedMethod(
                ExpressionCompilerConstants.CreateVariableMethodName,
                (c, n, s) => new PlaceholderMethodSymbol(
                    c,
                    s,
                    n,
                    voidType,
                    m => ImmutableArray.Create <ParameterSymbol>(
                        new SynthesizedParameterSymbol(m, typeType, ordinal: 0, refKind: RefKind.None),
                        new SynthesizedParameterSymbol(m, stringType, ordinal: 1, refKind: RefKind.None))));
            var type = new BoundTypeOfOperator(syntax, new BoundTypeExpression(syntax, aliasOpt: null, type: local.Type), null, typeType);
            var name = new BoundLiteral(syntax, ConstantValue.Create(local.Name), stringType);
            var call = BoundCall.Synthesized(
                syntax,
                receiverOpt: null,
                method: method,
                arguments: ImmutableArray.Create <BoundExpression>(type, name));

            statements.Add(new BoundExpressionStatement(syntax, call));

            var initializer = node.InitializerOpt;

            if (initializer != null)
            {
                // Generate assignment to local. The assignment will
                // be rewritten in PlaceholderLocalRewriter.
                var assignment = new BoundAssignmentOperator(
                    syntax,
                    new BoundLocal(syntax, local, constantValueOpt: null, type: local.Type),
                    initializer,
                    RefKind.None,
                    local.Type);
                statements.Add(new BoundExpressionStatement(syntax, assignment));
            }
        }
        private static void RewriteLocalDeclaration(
            CSharpCompilation compilation,
            EENamedTypeSymbol container,
            HashSet <LocalSymbol> declaredLocals,
            ArrayBuilder <BoundStatement> statements,
            BoundLocalDeclaration node)
        {
            Debug.Assert(node.ArgumentsOpt.IsDefault);

            var local  = node.LocalSymbol;
            var syntax = node.Syntax;

            declaredLocals.Add(local);

            var typeType        = compilation.GetWellKnownType(WellKnownType.System_Type);
            var stringType      = compilation.GetSpecialType(SpecialType.System_String);
            var guidConstructor = (MethodSymbol)compilation.GetWellKnownTypeMember(WellKnownMember.System_Guid__ctor);

            // CreateVariable(Type type, string name)
            var method = PlaceholderLocalSymbol.GetIntrinsicMethod(compilation, ExpressionCompilerConstants.CreateVariableMethodName);
            var type   = new BoundTypeOfOperator(syntax, new BoundTypeExpression(syntax, aliasOpt: null, type: local.Type), null, typeType);
            var name   = new BoundLiteral(syntax, ConstantValue.Create(local.Name), stringType);

            bool hasCustomTypeInfoPayload;
            var  customTypeInfoPayload   = GetCustomTypeInfoPayload(local, syntax, compilation, out hasCustomTypeInfoPayload);
            var  customTypeInfoPayloadId = GetCustomTypeInfoPayloadId(syntax, guidConstructor, hasCustomTypeInfoPayload);
            var  call = BoundCall.Synthesized(
                syntax,
                receiverOpt: null,
                method: method,
                arguments: ImmutableArray.Create(type, name, customTypeInfoPayloadId, customTypeInfoPayload));

            statements.Add(new BoundExpressionStatement(syntax, call));

            var initializer = node.InitializerOpt;

            if (initializer != null)
            {
                // Generate assignment to local. The assignment will
                // be rewritten in PlaceholderLocalRewriter.
                var assignment = new BoundAssignmentOperator(
                    syntax,
                    new BoundLocal(syntax, local, constantValueOpt: null, type: local.Type),
                    initializer,
                    RefKind.None,
                    local.Type);
                statements.Add(new BoundExpressionStatement(syntax, assignment));
            }
        }
Beispiel #3
0
        internal void Parse(BoundAssignmentOperator boundAssignmentOperator)
        {
            base.Parse(boundAssignmentOperator);

            var boundLocal = boundAssignmentOperator.Left as BoundLocal;

            if (boundLocal != null && boundLocal.Syntax.Parent != null)
            {
                var variableDeclarationSyntax = boundLocal.Syntax.Parent.Green as VariableDeclarationSyntax;
                if (variableDeclarationSyntax != null)
                {
                    this.TypeDeclaration = true;
                    this.ApplyAutoType   = variableDeclarationSyntax.Type.ToString() == "var";
                }
            }

            var forEachStatementSyntax = boundAssignmentOperator.Left.Syntax.Green as ForEachStatementSyntax;

            if (forEachStatementSyntax != null)
            {
                if (boundLocal != null && boundLocal.LocalSymbol.SynthesizedKind == default(SynthesizedLocalKind))
                {
                    this.TypeDeclaration = true;
                    this.ApplyAutoType   = true;
                }
            }

            this.Left  = Deserialize(boundAssignmentOperator.Left) as Expression;
            this.Right = Deserialize(boundAssignmentOperator.Right) as Expression;

            if (boundLocal == null || boundLocal.LocalSymbol.IsFixed || boundLocal.LocalSymbol.IsUsing ||
                boundLocal.LocalSymbol.SynthesizedKind == SynthesizedLocalKind.LoweringTemp)
            {
                this.TypeDeclaration = false;
                this.ApplyAutoType   = false;
            }

            this.IsRef = boundAssignmentOperator.RefKind.HasFlag(RefKind.Ref);
            this.IsOut = boundAssignmentOperator.RefKind.HasFlag(RefKind.Out);
            if (this.IsRef || this.IsOut)
            {
                this.TypeDeclaration = true;
            }
        }
        private static void RewriteLocalDeclaration(
            CSharpCompilation compilation,
            EENamedTypeSymbol container,
            HashSet<LocalSymbol> declaredLocals,
            ArrayBuilder<BoundStatement> statements,
            BoundLocalDeclaration node)
        {
            Debug.Assert(node.ArgumentsOpt.IsDefault);

            var local = node.LocalSymbol;
            var syntax = node.Syntax;

            declaredLocals.Add(local);

            var typeType = compilation.GetWellKnownType(WellKnownType.System_Type);
            var stringType = compilation.GetSpecialType(SpecialType.System_String);

            // CreateVariable(Type type, string name)
            var method = PlaceholderLocalSymbol.GetIntrinsicMethod(compilation, ExpressionCompilerConstants.CreateVariableMethodName);
            var type = new BoundTypeOfOperator(syntax, new BoundTypeExpression(syntax, aliasOpt: null, type: local.Type), null, typeType);
            var name = new BoundLiteral(syntax, ConstantValue.Create(local.Name), stringType);
            var call = BoundCall.Synthesized(
                syntax,
                receiverOpt: null,
                method: method,
                arguments: ImmutableArray.Create<BoundExpression>(type, name));
            statements.Add(new BoundExpressionStatement(syntax, call));

            var initializer = node.InitializerOpt;
            if (initializer != null)
            {
                // Generate assignment to local. The assignment will
                // be rewritten in PlaceholderLocalRewriter.
                var assignment = new BoundAssignmentOperator(
                    syntax,
                    new BoundLocal(syntax, local, constantValueOpt: null, type: local.Type),
                    initializer,
                    RefKind.None,
                    local.Type);
                statements.Add(new BoundExpressionStatement(syntax, assignment));
            }
        }
        private static void RewriteLocalDeclaration(
            ArrayBuilder<BoundStatement> statements,
            BoundLocalDeclaration node)
        {
            Debug.Assert(node.ArgumentsOpt.IsDefault);

            var initializer = node.InitializerOpt;
            if (initializer != null)
            {
                var local = node.LocalSymbol;
                var syntax = node.Syntax;

                // Generate assignment to local. The assignment will
                // be rewritten in PlaceholderLocalRewriter.
                var assignment = new BoundAssignmentOperator(
                    syntax,
                    new BoundLocal(syntax, local, constantValueOpt: null, type: local.Type),
                    initializer,
                    RefKind.None,
                    local.Type);
                statements.Add(new BoundExpressionStatement(syntax, assignment));
            }
        }
Beispiel #6
0
        private static void RewriteLocalDeclaration(
            ArrayBuilder <BoundStatement> statements,
            BoundLocalDeclaration node)
        {
            Debug.Assert(node.ArgumentsOpt.IsDefault);

            var initializer = node.InitializerOpt;

            if (initializer != null)
            {
                var local  = node.LocalSymbol;
                var syntax = node.Syntax;

                // Generate assignment to local. The assignment will
                // be rewritten in PlaceholderLocalRewriter.
                var assignment = new BoundAssignmentOperator(
                    syntax,
                    new BoundLocal(syntax, local, constantValueOpt: null, type: local.Type),
                    initializer,
                    RefKind.None,
                    local.Type);
                statements.Add(new BoundExpressionStatement(syntax, assignment));
            }
        }
Beispiel #7
0
        // indirect assignment is assignment to a value referenced indirectly
        // it may only happen if 
        //      1) lhs is a reference (must be a parameter or a local)
        //      2) it is not a ref/out assignment where the reference itself would be assigned
        private static bool IsIndirectAssignment(BoundAssignmentOperator node)
        {
            var lhs = node.Left;
            switch (lhs.Kind)
            {
                case BoundKind.ThisReference:
                    Debug.Assert(lhs.Type.IsValueType && node.RefKind == RefKind.None);
                    return true;

                case BoundKind.Parameter:
                    if (((BoundParameter)lhs).ParameterSymbol.RefKind != RefKind.None)
                    {
                        bool isIndirect = node.RefKind == RefKind.None;
                        Debug.Assert(isIndirect, "direct assignment to a ref/out parameter is highly suspicious");
                        return isIndirect;
                    }

                    break;

                case BoundKind.Local:
                    if (((BoundLocal)lhs).LocalSymbol.RefKind != RefKind.None)
                    {
                        bool isIndirect = node.RefKind == RefKind.None;
                        return isIndirect;
                    }

                    break;
            }

            Debug.Assert(node.RefKind == RefKind.None, "this is not something that can be assigned indirectly");
            return false;
        }
Beispiel #8
0
        private static bool IsIndirectOrInstanceFieldAssignment(BoundAssignmentOperator node)
        {
            var lhs = node.Left;
            if (lhs.Kind == BoundKind.FieldAccess)
            {
                return !((BoundFieldAccess)lhs).FieldSymbol.IsStatic;
            }

            return IsIndirectAssignment(node);
        }
Beispiel #9
0
        public override BoundNode VisitAssignmentOperator(BoundAssignmentOperator node)
        {
            LocalDefUseInfo locInfo;
            var left = node.Left as BoundLocal;

            // store to something that is not special. (operands still could be rewritten) 
            if (left == null || !_info.TryGetValue(left.LocalSymbol, out locInfo))
            {
                return base.VisitAssignmentOperator(node);
            }

            // indirect local store is not special. (operands still could be rewritten) 
            // NOTE: if Lhs is a stack local, it will be handled as a read and possibly duped.
            var indirectStore = left.LocalSymbol.RefKind != RefKind.None && node.RefKind == RefKind.None;
            if (indirectStore)
            {
                return base.VisitAssignmentOperator(node);
            }


            // ==  here we have a regular write to a stack local
            //
            // we do not need to visit lhs, because we do not read the local,
            // just update the counter to be in sync.
            //
            // if this is the last store, we just push the rhs
            // otherwise record a store.

            // fake visiting of left
            _nodeCounter += 1;

            // visit right
            var right = (BoundExpression)Visit(node.Right);

            // do actual assignment

            Debug.Assert(locInfo.LocalDefs.Any((d) => _nodeCounter == d.start && _nodeCounter <= d.end));
            var isLast = IsLastAccess(locInfo, _nodeCounter);

            if (isLast)
            {
                // assigned local is not used later => just emit the Right 
                return right;
            }
            else
            {
                // assigned local used later - keep assignment. 
                // codegen will keep value on stack when sees assignment "stackLocal = expr"
                return node.Update(left, right, node.RefKind, node.Type);
            }
        }
Beispiel #10
0
        public override BoundNode VisitAssignmentOperator(BoundAssignmentOperator node)
        {
            var sequence = node.Left as BoundSequence;
            if (sequence != null)
            {
                // assigning to a sequence is uncommon, but could happen in a
                // case if LHS was a declaration expression.
                // 
                // Just rewrite {se1, se2, se3, val} = something
                // into ==>     {se1, se2, se3, val = something}
                BoundExpression rewritten = sequence.Update(sequence.Locals,
                                        sequence.SideEffects,
                                        node.Update(sequence.Value, node.Right, node.RefKind, node.Type),
                                        sequence.Type);

                rewritten = (BoundExpression)Visit(rewritten);

                // do not count the assignment twice
                _counter--;

                return rewritten;
            }


            var isIndirectAssignment = IsIndirectAssignment(node);

            var left = VisitExpression(node.Left, isIndirectAssignment ?
                                                    ExprContext.Address :
                                                    ExprContext.AssignmentTarget);

            // must delay recording a write until after RHS is evaluated
            var assignmentLocal = _assignmentLocal;
            _assignmentLocal = null;

            Debug.Assert(_context != ExprContext.AssignmentTarget, "assignment expression cannot be a target of another assignment");

            ExprContext rhsContext;
            if (node.RefKind != RefKind.None ||
                _context == ExprContext.Address)
            {
                // we need the address of rhs one way or another so we cannot have it on the stack.
                rhsContext = ExprContext.Address;
            }
            else
            {
                Debug.Assert(_context == ExprContext.Value ||
                             _context == ExprContext.Box ||
                             _context == ExprContext.Sideeffects, "assignment expression cannot be a target of another assignment");
                // we only need a value of rhs, so if otherwise possible it can be a stack value.
                rhsContext = ExprContext.Value;
            }

            // if right is a struct ctor, it may be optimized into in-place call
            // Such call will push the receiver ref before the arguments
            // so we need to ensure that arguments cannot use stack temps
            BoundExpression right = node.Right;
            object rhsCookie = null;
            if (right.Kind == BoundKind.ObjectCreationExpression &&
                right.Type.IsVerifierValue() &&
                ((BoundObjectCreationExpression)right).Constructor.ParameterCount != 0)
            {
                rhsCookie = this.GetStackStateCookie();
            }

            right = VisitExpression(node.Right, rhsContext);

            // if assigning to a local, now it is the time to record the Write
            if (assignmentLocal != null)
            {
                // This assert will fire if code relies on implicit CLR coercions 
                // - i.e assigns int value to a short local.
                // in that case we should force lhs to be a real local.
                Debug.Assert(
                    node.Left.Type.Equals(node.Right.Type, ignoreCustomModifiersAndArraySizesAndLowerBounds: true, ignoreDynamic: true),
                    @"type of the assignment value is not the same as the type of assignment target. 
                This is not expected by the optimizer and is typically a result of a bug somewhere else.");

                Debug.Assert(!isIndirectAssignment, "indirect assignment is a read, not a write");

                LocalSymbol localSymbol = assignmentLocal.LocalSymbol;

                // Special Case: If the RHS is a pointer conversion, then the assignment functions as
                // a conversion (because the RHS will actually be typed as a native u/int in IL), so
                // we should not optimize away the local (i.e. schedule it on the stack).
                if (CanScheduleToStack(localSymbol) &&
                    assignmentLocal.Type.IsPointerType() && right.Kind == BoundKind.Conversion &&
                    ((BoundConversion)right).ConversionKind.IsPointerConversion())
                {
                    ShouldNotSchedule(localSymbol);
                }

                RecordVarWrite(localSymbol);
                assignmentLocal = null;
            }

            if (rhsCookie != null)
            {
                // we currently have the rhs on stack, adjust for that.
                PopEvalStack();
                this.EnsureStackState(rhsCookie);
            }

            return node.Update(left, right, node.RefKind, node.Type);
        }
Beispiel #11
0
        private BoundExpression VisitAssignmentOperator(BoundAssignmentOperator node, bool used)
        {
            // Avoid rewriting if node has errors since at least
            // one of the operands is invalid.
            if (node.HasErrors)
            {
                return(node);
            }

            var propertyAccessor = node.Left as BoundPropertyAccess;

            if (propertyAccessor == null)
            {
                return((BoundExpression)base.VisitAssignmentOperator(node));
            }

            // Rewrite property assignment into call to setter.
            var property  = propertyAccessor.PropertySymbol.GetBaseProperty();
            var setMethod = property.SetMethod;

            Debug.Assert(setMethod != null);
            Debug.Assert(setMethod.Parameters.Count == 1);
            Debug.Assert(!setMethod.IsOverride);

            var rewrittenReceiver = (BoundExpression)Visit(propertyAccessor.ReceiverOpt);
            var rewrittenArgument = (BoundExpression)Visit(node.Right);

            if (used)
            {
                // Save expression value to a temporary before calling the
                // setter, and restore the temporary after the setter, so the
                // assignment can be used as an embedded expression.
                var exprType   = rewrittenArgument.Type;
                var tempSymbol = new TempLocalSymbol(exprType, RefKind.None, containingSymbol);
                var tempLocal  = new BoundLocal(null, null, tempSymbol, null, exprType);
                var saveTemp   = new BoundAssignmentOperator(
                    null,
                    null,
                    tempLocal,
                    rewrittenArgument,
                    exprType);
                var call = BoundCall.SynthesizedCall(
                    rewrittenReceiver,
                    setMethod,
                    saveTemp);
                return(new BoundSequence(
                           node.Syntax,
                           node.SyntaxTree,
                           ReadOnlyArray <LocalSymbol> .CreateFrom(tempSymbol),
                           ReadOnlyArray <BoundExpression> .CreateFrom(call),
                           tempLocal,
                           exprType));
            }
            else
            {
                return(BoundCall.SynthesizedCall(
                           rewrittenReceiver,
                           setMethod,
                           rewrittenArgument));
            }
        }
Beispiel #12
0
        public override BoundNode VisitAssignmentOperator(BoundAssignmentOperator node)
        {
            BoundExpression originalLeft = node.Left;

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

            var leftLocal = (BoundLocal)originalLeft;

            BoundExpression originalRight = node.Right;

            if (leftLocal.LocalSymbol.RefKind != RefKind.None &&
                node.RefKind != RefKind.None &&
                NeedsProxy(leftLocal.LocalSymbol))
            {
                Debug.Assert(!proxies.ContainsKey(leftLocal.LocalSymbol));
                Debug.Assert(!IsStackAlloc(originalRight));
                //spilling ref local variables
                throw ExceptionUtilities.Unreachable;
            }

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

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

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

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

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

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

            return(node.Update(rewrittenLeft, rewrittenRight, node.RefKind, rewrittenType));
        }
Beispiel #13
0
        private void EmitStore(BoundAssignmentOperator assignment)
        {
            BoundExpression expression = assignment.Left;
            switch (expression.Kind)
            {
                case BoundKind.FieldAccess:
                    EmitFieldStore((BoundFieldAccess)expression);
                    break;

                case BoundKind.Local:
                    // If we are doing a 'normal' local assignment like 'int t = 10;', or
                    // if we are initializing a temporary like 'ref int t = ref M().s;' then
                    // we just emit a local store. If we are doing an assignment through
                    // a ref local temporary then we assume that the instruction to load
                    // the address is already on the stack, and we must indirect through it.

                    // See the comments in EmitAssignmentExpression above for details.
                    BoundLocal local = (BoundLocal)expression;
                    if (local.LocalSymbol.RefKind != RefKind.None && assignment.RefKind == RefKind.None)
                    {
                        EmitIndirectStore(local.LocalSymbol.Type, local.Syntax);
                    }
                    else
                    {
                        if (IsStackLocal(local.LocalSymbol))
                        {
                            // assign to stack var == leave original value on stack
                            break;
                        }
                        else
                        {
                            _builder.EmitLocalStore(GetLocal(local));
                        }
                    }
                    break;

                case BoundKind.ArrayAccess:
                    var array = ((BoundArrayAccess)expression).Expression;
                    var arrayType = (ArrayTypeSymbol)array.Type;
                    EmitArrayElementStore(arrayType, expression.Syntax);
                    break;

                case BoundKind.ThisReference:
                    EmitThisStore((BoundThisReference)expression);
                    break;

                case BoundKind.Parameter:
                    EmitParameterStore((BoundParameter)expression);
                    break;

                case BoundKind.Dup:
                    Debug.Assert(((BoundDup)expression).RefKind != RefKind.None);
                    EmitIndirectStore(expression.Type, expression.Syntax);
                    break;

                case BoundKind.RefValueOperator:
                case BoundKind.PointerIndirectionOperator:
                case BoundKind.PseudoVariable:
                    EmitIndirectStore(expression.Type, expression.Syntax);
                    break;

                case BoundKind.Sequence:
                    {
                        var sequence = (BoundSequence)expression;
                        EmitStore(assignment.Update(sequence.Value, assignment.Right, assignment.RefKind, assignment.Type));

                        var notReleased = DigForValueLocal(sequence);
                        if (notReleased != null)
                        {
                            FreeLocal(notReleased);
                        }
                    }
                    break;

                case BoundKind.Call:
                    Debug.Assert(((BoundCall)expression).Method.RefKind != RefKind.None);
                    EmitIndirectStore(expression.Type, expression.Syntax);
                    break;

                case BoundKind.ModuleVersionId:
                    EmitModuleVersionIdStore((BoundModuleVersionId)expression);
                    break;

                case BoundKind.InstrumentationPayloadRoot:
                    EmitInstrumentationPayloadRootStore((BoundInstrumentationPayloadRoot)expression);
                    break;

                case BoundKind.PreviousSubmissionReference:
                // Script references are lowered to a this reference and a field access.
                default:
                    throw ExceptionUtilities.UnexpectedValue(expression.Kind);
            }
        }
Beispiel #14
0
        // sometimes it is possible and advantageous to get an address of the lHS and 
        // perform assignment as an in-place initialization via initobj or constructor invocation.
        //
        // 1) initobj 
        //    is used when assigning default value to T that is not a verifier reference.
        //
        // 2) in-place ctor call 
        //    is used when assigning a freshly created struct. "x = new S(arg)" can be
        //    replaced by x.S(arg) as long as partial assignment cannot be observed -
        //    i.e. target must not be on the heap and we should not be in a try block.
        private bool TryEmitAssignmentInPlace(BoundAssignmentOperator assignmentOperator, bool used)
        {
            var left = assignmentOperator.Left;

            // if result is used, and lives on heap, we must keep RHS value on the stack.
            // otherwise we can try conjuring up the RHS value directly where it belongs.
            if (used && !TargetIsNotOnHeap(left))
            {
                return false;
            }

            if (!SafeToGetWriteableReference(left))
            {
                // cannot take a ref
                return false;
            }

            var right = assignmentOperator.Right;
            var rightType = right.Type;

            // in-place is not advantageous for reference types or constants
            if (!rightType.IsTypeParameter())
            {
                if (rightType.IsReferenceType || (right.ConstantValue != null && rightType.SpecialType != SpecialType.System_Decimal))
                {
                    return false;
                }
            }

            if (right.IsDefaultValue())
            {
                InPlaceInit(left, used);
                return true;
            }

            if (right.Kind == BoundKind.ObjectCreationExpression)
            {
                // It is desirable to do in-place ctor call if possible.
                // we could do newobj/stloc, but in-place call 
                // produces same or better code in current JITs 
                if (PartialCtorResultCannotEscape(left))
                {
                    var objCreation = (BoundObjectCreationExpression)right;
                    InPlaceCtorCall(left, objCreation, used);
                    return true;
                }
            }

            return false;
        }
Beispiel #15
0
        // indirect assignment is assignment to a value referenced indirectly
        // it may only happen if 
        //      1) lhs is a reference (must be a parameter or a local)
        //      2) it is not a ref/out assignment where the reference itself would be assigned
        private static bool IsIndirectAssignment(BoundAssignmentOperator node)
        {
            var lhs = node.Left;

            Debug.Assert(node.RefKind == RefKind.None || (lhs as BoundLocal)?.LocalSymbol.RefKind == RefKind.Ref,
                                "only ref locals can be a target of a ref assignment");
            
            switch (lhs.Kind)
            {
                case BoundKind.ThisReference:
                    Debug.Assert(lhs.Type.IsValueType, "'this' is assignable only in structs");
                    return true;

                case BoundKind.Parameter:
                    if (((BoundParameter)lhs).ParameterSymbol.RefKind != RefKind.None)
                    {
                        bool isIndirect = node.RefKind == RefKind.None;
                        return isIndirect;
                    }

                    return false;

                case BoundKind.Local:
                    if (((BoundLocal)lhs).LocalSymbol.RefKind != RefKind.None)
                    {
                        bool isIndirect = node.RefKind == RefKind.None;
                        return isIndirect;
                    }

                    return false;

                case BoundKind.Call:
                    Debug.Assert(((BoundCall)lhs).Method.RefKind == RefKind.Ref, "only ref returning methods are assignable");
                    return true;

                case BoundKind.AssignmentOperator:
                    Debug.Assert(((BoundAssignmentOperator)lhs).RefKind == RefKind.Ref, "only ref assignments are assignable");
                    return true;

                case BoundKind.Sequence:
                    Debug.Assert(!IsIndirectAssignment(node.Update(((BoundSequence)node.Left).Value, node.Right, node.RefKind, node.Type)), 
                        "indirect assignment to a sequence is unexpected");
                    return false;

                case BoundKind.RefValueOperator:
                case BoundKind.PointerIndirectionOperator:
                case BoundKind.PseudoVariable:
                    return true;

                case BoundKind.ModuleVersionId:
                case BoundKind.InstrumentationPayloadRoot:
                    // these are just stores into special static fields
                    goto case BoundKind.FieldAccess;

                case BoundKind.FieldAccess:
                case BoundKind.ArrayAccess:
                    // always symbolic stores
                    return false;

                default:
                    throw ExceptionUtilities.UnexpectedValue(lhs.Kind);
            }
        }
Beispiel #16
0
        private void EmitAssignmentValue(BoundAssignmentOperator assignmentOperator)
        {
            if (assignmentOperator.RefKind == RefKind.None)
            {
                EmitExpression(assignmentOperator.Right, used: true);
            }
            else
            {
                // LEAKING A TEMP IS OK HERE 
                // generally taking a ref for the purpose of ref assignment should not be done on homeless values
                // however, there are very rare cases when we need to get a ref off a copy in synthetic code and we have to leak those.
                // fortunately these are very short-lived temps that should not cause value sharing.
                var temp = EmitAddress(assignmentOperator.Right, AddressKind.Writeable);
#if DEBUG
                Debug.Assert(temp == null || ((SynthesizedLocal)assignmentOperator.Left.ExpressionSymbol).SynthesizedKind == SynthesizedLocalKind.LoweringTemp);
#endif
            }
        }
Beispiel #17
0
        public override BoundNode VisitCompoundAssignmentOperator(BoundCompoundAssignmentOperator node)
        {
            if (node.HasErrors)
            {
                return(node);
            }

            // There are five possible cases.
            //
            // Case 1: receiver.Prop += value is transformed into
            // temp = receiver
            // temp.Prop = temp.Prop + value
            // and a later rewriting will turn that into calls to getters and setters.
            //
            // Case 2: collection[i1, i2, i3] += value is transformed into
            // tc = collection
            // t1 = i1
            // t2 = i2
            // t3 = i3
            // tc[t1, t2, t3] = tc[t1, t2, t3] + value
            // and again, a later rewriting will turn that into getters and setters of the indexer.
            //
            // Case 3: local += value (and param += value) needs no temporaries; it simply
            // becomes local = local + value.
            //
            // Case 4: staticField += value needs no temporaries either. However, classInst.field += value becomes
            // temp = classInst
            // temp.field = temp.field + value
            //
            // Case 5: otherwise, it must be structVariable.field += value or array[index] += value. Either way
            // we have a variable on the left. Transform it into:
            // ref temp = ref variable
            // temp = temp + value

            var temps = ArrayBuilder <LocalSymbol> .GetInstance();

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

            // This will be filled in with the LHS that uses temporaries to prevent
            // double-evaluation of side effects.

            BoundExpression transformedLHS = null;

            if (node.Left.Kind == BoundKind.PropertyAccess)
            {
                // We need to stash away the receiver so that it does not get evaluated twice.
                // If the receiver is classified as a value of reference type then we can simply say
                //
                // R temp = receiver
                // temp.prop = temp.prop + rhs
                //
                // But if the receiver is classified as a variable of struct type then we
                // cannot make a copy of the value; we need to make sure that we mutate
                // the original receiver, not the copy.  We have to generate
                //
                // ref R temp = ref receiver
                // temp.prop = temp.prop + rhs
                //
                // The rules of C# (in section 7.17.1) require that if you have receiver.prop
                // as the target of an assignment such that receiver is a value type, it must
                // be classified as a variable. If we've gotten this far in the rewriting,
                // assume that was the case.

                var prop = (BoundPropertyAccess)node.Left;

                // If the property is static then we can just generate prop = prop + value
                if (prop.ReceiverOpt == null)
                {
                    transformedLHS = prop;
                }
                else
                {
                    // Can we ever avoid storing the receiver in a temp? If the receiver is a variable then it
                    // might be modified by the computation of the getter, the value, or the operation.
                    // The receiver cannot be a null constant or constant of value type. It could be a
                    // constant of string type, but there are no mutable properties of a string.
                    // Similarly, there are no mutable properties of a Type object, so the receiver
                    // cannot be a typeof(T) expression. The only situation I can think of where we could
                    // optimize away the temp is if the receiver is a readonly field of reference type,
                    // we are not in a constructor, and the receiver of the *field*, if any, is also idempotent.
                    // It doesn't seem worthwhile to pursue an optimization for this exceedingly rare case.

                    var rewrittenReceiver = (BoundExpression)Visit(prop.ReceiverOpt);
                    var receiverTemp      = TempHelpers.StoreToTemp(rewrittenReceiver, rewrittenReceiver.Type.IsValueType ? RefKind.Ref : RefKind.None, containingSymbol);
                    stores.Add(receiverTemp.Item1);
                    temps.Add(receiverTemp.Item2.LocalSymbol);
                    transformedLHS = new BoundPropertyAccess(prop.Syntax, prop.SyntaxTree, receiverTemp.Item2, prop.PropertySymbol, prop.Type);
                }
            }
            else if (node.Left.Kind == BoundKind.IndexerAccess)
            {
                var             indexer             = (BoundIndexerAccess)node.Left;
                BoundExpression transformedReceiver = null;
                if (indexer.ReceiverOpt != null)
                {
                    var rewrittenReceiver = (BoundExpression)Visit(indexer.ReceiverOpt);
                    var receiverTemp      = TempHelpers.StoreToTemp(rewrittenReceiver, rewrittenReceiver.Type.IsValueType ? RefKind.Ref : RefKind.None, containingSymbol);
                    transformedReceiver = receiverTemp.Item2;
                    stores.Add(receiverTemp.Item1);
                    temps.Add(receiverTemp.Item2.LocalSymbol);
                }

                // UNDONE: Dealing with the arguments is a bit tricky because they can be named out-of-order arguments;
                // UNDONE: we have to preserve both the source-code order of the side effects and the side effects
                // UNDONE: only being executed once.
                // UNDONE:
                // UNDONE: This is a subtly different problem than the problem faced by the conventional call
                // UNDONE: rewriter; with the conventional call rewriter we already know that the side effects
                // UNDONE: will only be executed once because the arguments are only being pushed on the stack once.
                // UNDONE: In a compound equality operator on an indexer the indices are placed on the stack twice.
                // UNDONE: That is to say, if you have:
                // UNDONE:
                // UNDONE: C().M(z : Z(), x : X(), y : Y())
                // UNDONE:
                // UNDONE: then we can rewrite that into
                // UNDONE:
                // UNDONE: tempc = C()
                // UNDONE: tempz = Z()
                // UNDONE: tempc.M(X(), Y(), tempz)
                // UNDONE:
                // UNDONE: See, we can optimize away two of the temporaries, for x and y. But we cannot optimize away any of the
                // UNDONE: temporaries in
                // UNDONE:
                // UNDONE: C().Collection[z : Z(), x : X(), y : Y()] += 1;
                // UNDONE:
                // UNDONE: because we have to ensure not just that Z() happens first, but in additioan that X() and Y() are only
                // UNDONE: called once.  We have to generate this as
                // UNDONE:
                // UNDONE: tempc = C().Collection
                // UNDONE: tempz = Z()
                // UNDONE: tempx = X()
                // UNDONE: tempy = Y()
                // UNDONE: tempc[tempx, tempy, tempz] = tempc[tempx, tempy, tempz] + 1;
                // UNDONE:
                // UNDONE: Fortunately arguments to indexers are never ref or out, so we don't need to worry about that.
                // UNDONE: However, we can still do the optimization where constants are not stored in
                // UNDONE: temporaries; if we have
                // UNDONE:
                // UNDONE: C().Collection[z : 123, y : Y(), x : X()] += 1;
                // UNDONE:
                // UNDONE: Then we can generate that as
                // UNDONE:
                // UNDONE: tempc = C().Collection
                // UNDONE: tempx = X()
                // UNDONE: tempy = Y()
                // UNDONE: tempc[tempx, tempy, 123] = tempc[tempx, tempy, 123] + 1;
                // UNDONE:
                // UNDONE: For now, we'll punt on both problems, as indexers are not implemented yet anyway.
                // UNDONE: We'll just generate one temporary for each argument. This will work, but in the
                // UNDONE: subsequent rewritings will generate more unnecessary temporaries.

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

                foreach (var argument in indexer.Arguments)
                {
                    var rewrittenArgument = (BoundExpression)Visit(argument);
                    var argumentTemp      = TempHelpers.StoreToTemp(rewrittenArgument, RefKind.None, containingSymbol);
                    transformedArguments.Add(argumentTemp.Item2);
                    stores.Add(argumentTemp.Item1);
                    temps.Add(argumentTemp.Item2.LocalSymbol);
                }

                transformedLHS = new BoundIndexerAccess(indexer.Syntax, indexer.SyntaxTree, transformedArguments.ToReadOnlyAndFree(), transformedReceiver,
                                                        indexer.IndexerSymbol, indexer.Type);
            }
            else if (node.Left.Kind == BoundKind.Local || node.Left.Kind == BoundKind.Parameter)
            {
                // No temporaries are needed. Just generate local = local + value
                transformedLHS = node.Left;
            }
            else if (node.Left.Kind == BoundKind.FieldAccess)
            {
                // * If the field is static then no temporaries are needed.
                // * If the field is not static and the receiver is of reference type then generate t = r; t.f = t.f + value
                // * If the field is not static and the receiver is a variable of value type then we'll fall into the
                //   general variable case below.

                var fieldAccess = (BoundFieldAccess)node.Left;
                if (fieldAccess.ReceiverOpt == null)
                {
                    transformedLHS = fieldAccess;
                }
                else if (!fieldAccess.ReceiverOpt.Type.IsValueType)
                {
                    var rewrittenReceiver = (BoundExpression)Visit(fieldAccess.ReceiverOpt);
                    var receiverTemp      = TempHelpers.StoreToTemp(rewrittenReceiver, RefKind.None, containingSymbol);
                    stores.Add(receiverTemp.Item1);
                    temps.Add(receiverTemp.Item2.LocalSymbol);
                    transformedLHS = new BoundFieldAccess(fieldAccess.Syntax, fieldAccess.SyntaxTree, receiverTemp.Item2, fieldAccess.FieldSymbol, null);
                }
            }

            if (transformedLHS == null)
            {
                // We made no transformation above. Either we have array[index] += value or
                // structVariable.field += value; either way we have a potentially complicated variable-
                // producing expression on the left. Generate
                // ref temp = ref variable; temp = temp + value
                var rewrittenVariable = (BoundExpression)Visit(node.Left);
                var variableTemp      = TempHelpers.StoreToTemp(rewrittenVariable, RefKind.Ref, containingSymbol);
                stores.Add(variableTemp.Item1);
                temps.Add(variableTemp.Item2.LocalSymbol);
                transformedLHS = variableTemp.Item2;
            }

            // OK, we now have the temporary declarations, the temporary stores, and the transformed left hand side.
            // We need to generate
            //
            // xlhs = (FINAL)((LEFT)xlhs op rhs)
            //
            // And then wrap it up with the generated temporaries.
            //
            // (The right hand side has already been converted to the type expected by the operator.)

            BoundExpression opLHS = BoundConversion.SynthesizedConversion(transformedLHS, node.LeftConversion, node.Operator.LeftType);

            Debug.Assert(node.Right.Type == node.Operator.RightType);
            BoundExpression op         = new BoundBinaryOperator(null, null, node.Operator.Kind, opLHS, node.Right, null, node.Operator.ReturnType);
            BoundExpression opFinal    = BoundConversion.SynthesizedConversion(op, node.FinalConversion, node.Left.Type);
            BoundExpression assignment = new BoundAssignmentOperator(null, null, transformedLHS, opFinal, node.Left.Type);

            // OK, at this point we have:
            //
            // * temps evaluating and storing portions of the LHS that must be evaluated only once.
            // * the "transformed" left hand side, rebuilt to use temps where necessary
            // * the assignment "xlhs = (FINAL)((LEFT)xlhs op (RIGHT)rhs)"
            //
            // Notice that we have recursively rewritten the bound nodes that are things stored in
            // the temps, but we might have more rewriting to do on the assignment. There are three
            // conversions in there that might be lowered to method calls, an operator that might
            // be lowered to delegate combine, string concat, and so on, and don't forget, we
            // haven't lowered the right hand side at all! Let's rewrite all these things at once.

            BoundExpression rewrittenAssignment = (BoundExpression)Visit(assignment);

            BoundExpression result = (temps.Count == 0) ?
                                     rewrittenAssignment :
                                     new BoundSequence(null,
                                                       null,
                                                       temps.ToReadOnly(),
                                                       stores.ToReadOnly(),
                                                       rewrittenAssignment,
                                                       rewrittenAssignment.Type);

            temps.Free();
            stores.Free();
            return(result);
        }
Beispiel #18
0
 private void EmitAssignmentValue(BoundAssignmentOperator assignmentOperator)
 {
     if (assignmentOperator.RefKind == RefKind.None)
     {
         EmitExpression(assignmentOperator.Right, used: true);
     }
     else
     {
         // LEAKING A TEMP IS OK HERE
         // Once a reference is assigned to a byref local, there is no easy way to figure when 
         // reference is no longer in use.
         // Therefore if we had to create a temp while producing a reference, we do not know when 
         // the temp slot can be reused so we must leak it.
         var temp = EmitAddress(assignmentOperator.Right, AddressKind.Writeable);
     }
 }
        public override BoundNode VisitCompoundAssignmentOperator(BoundCompoundAssignmentOperator node)
        {
            if (node.HasErrors)
            {
                return node;
            }

            // There are five possible cases.
            //
            // Case 1: receiver.Prop += value is transformed into
            // temp = receiver
            // temp.Prop = temp.Prop + value
            // and a later rewriting will turn that into calls to getters and setters.
            //
            // Case 2: collection[i1, i2, i3] += value is transformed into
            // tc = collection
            // t1 = i1
            // t2 = i2
            // t3 = i3
            // tc[t1, t2, t3] = tc[t1, t2, t3] + value
            // and again, a later rewriting will turn that into getters and setters of the indexer.
            //
            // Case 3: local += value (and param += value) needs no temporaries; it simply
            // becomes local = local + value.
            //
            // Case 4: staticField += value needs no temporaries either. However, classInst.field += value becomes
            // temp = classInst
            // temp.field = temp.field + value 
            //
            // Case 5: otherwise, it must be structVariable.field += value or array[index] += value. Either way
            // we have a variable on the left. Transform it into:
            // ref temp = ref variable
            // temp = temp + value

            var temps = ArrayBuilder<LocalSymbol>.GetInstance();
            var stores = ArrayBuilder<BoundExpression>.GetInstance();

            // This will be filled in with the LHS that uses temporaries to prevent
            // double-evaluation of side effects.

            BoundExpression transformedLHS = null;

            if (node.Left.Kind == BoundKind.PropertyAccess)
            {
                // We need to stash away the receiver so that it does not get evaluated twice.
                // If the receiver is classified as a value of reference type then we can simply say
                //
                // R temp = receiver
                // temp.prop = temp.prop + rhs
                //
                // But if the receiver is classified as a variable of struct type then we
                // cannot make a copy of the value; we need to make sure that we mutate
                // the original receiver, not the copy.  We have to generate
                //
                // ref R temp = ref receiver
                // temp.prop = temp.prop + rhs
                //
                // The rules of C# (in section 7.17.1) require that if you have receiver.prop 
                // as the target of an assignment such that receiver is a value type, it must
                // be classified as a variable. If we've gotten this far in the rewriting,
                // assume that was the case.

                var prop = (BoundPropertyAccess)node.Left;

                // If the property is static then we can just generate prop = prop + value
                if (prop.ReceiverOpt == null)
                {
                    transformedLHS = prop;
                }
                else
                {
                    // Can we ever avoid storing the receiver in a temp? If the receiver is a variable then it 
                    // might be modified by the computation of the getter, the value, or the operation. 
                    // The receiver cannot be a null constant or constant of value type. It could be a 
                    // constant of string type, but there are no mutable properties of a string.
                    // Similarly, there are no mutable properties of a Type object, so the receiver
                    // cannot be a typeof(T) expression. The only situation I can think of where we could
                    // optimize away the temp is if the receiver is a readonly field of reference type,
                    // we are not in a constructor, and the receiver of the *field*, if any, is also idempotent.
                    // It doesn't seem worthwhile to pursue an optimization for this exceedingly rare case.

                    var rewrittenReceiver = (BoundExpression)Visit(prop.ReceiverOpt);
                    var receiverTemp = TempHelpers.StoreToTemp(rewrittenReceiver, rewrittenReceiver.Type.IsValueType ? RefKind.Ref : RefKind.None, containingSymbol);
                    stores.Add(receiverTemp.Item1);
                    temps.Add(receiverTemp.Item2.LocalSymbol);
                    transformedLHS = new BoundPropertyAccess(prop.Syntax, prop.SyntaxTree, receiverTemp.Item2, prop.PropertySymbol, prop.Type);
                }
            }
            else if (node.Left.Kind == BoundKind.IndexerAccess)
            {
                var indexer = (BoundIndexerAccess)node.Left;
                BoundExpression transformedReceiver = null;
                if (indexer.ReceiverOpt != null)
                {
                    var rewrittenReceiver = (BoundExpression)Visit(indexer.ReceiverOpt);
                    var receiverTemp = TempHelpers.StoreToTemp(rewrittenReceiver, rewrittenReceiver.Type.IsValueType ? RefKind.Ref : RefKind.None, containingSymbol);
                    transformedReceiver = receiverTemp.Item2;
                    stores.Add(receiverTemp.Item1);
                    temps.Add(receiverTemp.Item2.LocalSymbol);
                }

                // UNDONE: Dealing with the arguments is a bit tricky because they can be named out-of-order arguments;
                // UNDONE: we have to preserve both the source-code order of the side effects and the side effects
                // UNDONE: only being executed once.
                // UNDONE:
                // UNDONE: This is a subtly different problem than the problem faced by the conventional call
                // UNDONE: rewriter; with the conventional call rewriter we already know that the side effects
                // UNDONE: will only be executed once because the arguments are only being pushed on the stack once. 
                // UNDONE: In a compound equality operator on an indexer the indices are placed on the stack twice. 
                // UNDONE: That is to say, if you have:
                // UNDONE:
                // UNDONE: C().M(z : Z(), x : X(), y : Y())
                // UNDONE:
                // UNDONE: then we can rewrite that into
                // UNDONE:
                // UNDONE: tempc = C()
                // UNDONE: tempz = Z()
                // UNDONE: tempc.M(X(), Y(), tempz)
                // UNDONE:
                // UNDONE: See, we can optimize away two of the temporaries, for x and y. But we cannot optimize away any of the
                // UNDONE: temporaries in
                // UNDONE:
                // UNDONE: C().Collection[z : Z(), x : X(), y : Y()] += 1;
                // UNDONE:
                // UNDONE: because we have to ensure not just that Z() happens first, but in additioan that X() and Y() are only 
                // UNDONE: called once.  We have to generate this as
                // UNDONE:
                // UNDONE: tempc = C().Collection
                // UNDONE: tempz = Z()
                // UNDONE: tempx = X()
                // UNDONE: tempy = Y()
                // UNDONE: tempc[tempx, tempy, tempz] = tempc[tempx, tempy, tempz] + 1;
                // UNDONE:
                // UNDONE: Fortunately arguments to indexers are never ref or out, so we don't need to worry about that.
                // UNDONE: However, we can still do the optimization where constants are not stored in
                // UNDONE: temporaries; if we have
                // UNDONE: 
                // UNDONE: C().Collection[z : 123, y : Y(), x : X()] += 1;
                // UNDONE:
                // UNDONE: Then we can generate that as
                // UNDONE:
                // UNDONE: tempc = C().Collection
                // UNDONE: tempx = X()
                // UNDONE: tempy = Y()
                // UNDONE: tempc[tempx, tempy, 123] = tempc[tempx, tempy, 123] + 1;
                // UNDONE:
                // UNDONE: For now, we'll punt on both problems, as indexers are not implemented yet anyway.
                // UNDONE: We'll just generate one temporary for each argument. This will work, but in the
                // UNDONE: subsequent rewritings will generate more unnecessary temporaries. 

                var transformedArguments = ArrayBuilder<BoundExpression>.GetInstance();
                foreach (var argument in indexer.Arguments)
                {
                    var rewrittenArgument = (BoundExpression)Visit(argument);
                    var argumentTemp = TempHelpers.StoreToTemp(rewrittenArgument, RefKind.None, containingSymbol);
                    transformedArguments.Add(argumentTemp.Item2);
                    stores.Add(argumentTemp.Item1);
                    temps.Add(argumentTemp.Item2.LocalSymbol);
                }

                transformedLHS = new BoundIndexerAccess(indexer.Syntax, indexer.SyntaxTree, transformedArguments.ToReadOnlyAndFree(), transformedReceiver,
                    indexer.IndexerSymbol, indexer.Type);
            }
            else if (node.Left.Kind == BoundKind.Local || node.Left.Kind == BoundKind.Parameter)
            {
                // No temporaries are needed. Just generate local = local + value
                transformedLHS = node.Left;
            }
            else if (node.Left.Kind == BoundKind.FieldAccess)
            {
                // * If the field is static then no temporaries are needed. 
                // * If the field is not static and the receiver is of reference type then generate t = r; t.f = t.f + value
                // * If the field is not static and the receiver is a variable of value type then we'll fall into the
                //   general variable case below.

                var fieldAccess = (BoundFieldAccess)node.Left;
                if (fieldAccess.ReceiverOpt == null)
                {
                    transformedLHS = fieldAccess;
                }
                else if (!fieldAccess.ReceiverOpt.Type.IsValueType)
                {
                    var rewrittenReceiver = (BoundExpression)Visit(fieldAccess.ReceiverOpt);
                    var receiverTemp = TempHelpers.StoreToTemp(rewrittenReceiver, RefKind.None, containingSymbol);
                    stores.Add(receiverTemp.Item1);
                    temps.Add(receiverTemp.Item2.LocalSymbol);
                    transformedLHS = new BoundFieldAccess(fieldAccess.Syntax, fieldAccess.SyntaxTree, receiverTemp.Item2, fieldAccess.FieldSymbol, null);
                }
            }

            if (transformedLHS == null)
            {
                // We made no transformation above. Either we have array[index] += value or 
                // structVariable.field += value; either way we have a potentially complicated variable-
                // producing expression on the left. Generate
                // ref temp = ref variable; temp = temp + value
                var rewrittenVariable = (BoundExpression)Visit(node.Left);
                var variableTemp = TempHelpers.StoreToTemp(rewrittenVariable, RefKind.Ref, containingSymbol);
                stores.Add(variableTemp.Item1);
                temps.Add(variableTemp.Item2.LocalSymbol);
                transformedLHS = variableTemp.Item2;
            }

            // OK, we now have the temporary declarations, the temporary stores, and the transformed left hand side.
            // We need to generate 
            //
            // xlhs = (FINAL)((LEFT)xlhs op rhs)
            //
            // And then wrap it up with the generated temporaries.
            //
            // (The right hand side has already been converted to the type expected by the operator.)

            BoundExpression opLHS = BoundConversion.SynthesizedConversion(transformedLHS, node.LeftConversion, node.Operator.LeftType);
            Debug.Assert(node.Right.Type == node.Operator.RightType);
            BoundExpression op = new BoundBinaryOperator(null, null, node.Operator.Kind, opLHS, node.Right, null, node.Operator.ReturnType);
            BoundExpression opFinal = BoundConversion.SynthesizedConversion(op, node.FinalConversion, node.Left.Type);
            BoundExpression assignment = new BoundAssignmentOperator(null, null, transformedLHS, opFinal, node.Left.Type);

            // OK, at this point we have:
            //
            // * temps evaluating and storing portions of the LHS that must be evaluated only once.
            // * the "transformed" left hand side, rebuilt to use temps where necessary
            // * the assignment "xlhs = (FINAL)((LEFT)xlhs op (RIGHT)rhs)"
            // 
            // Notice that we have recursively rewritten the bound nodes that are things stored in
            // the temps, but we might have more rewriting to do on the assignment. There are three
            // conversions in there that might be lowered to method calls, an operator that might
            // be lowered to delegate combine, string concat, and so on, and don't forget, we 
            // haven't lowered the right hand side at all! Let's rewrite all these things at once.

            BoundExpression rewrittenAssignment = (BoundExpression)Visit(assignment);

            BoundExpression result = (temps.Count == 0) ?
                rewrittenAssignment :
                new BoundSequence(null,
                    null,
                    temps.ToReadOnly(),
                    stores.ToReadOnly(),
                    rewrittenAssignment,
                    rewrittenAssignment.Type);

            temps.Free();
            stores.Free();
            return result;
        }
        private BoundExpression VisitAssignmentOperator(BoundAssignmentOperator node, bool used)
        {
            // Avoid rewriting if node has errors since at least
            // one of the operands is invalid.
            if (node.HasErrors)
            {
                return node;
            }

            var propertyAccessor = node.Left as BoundPropertyAccess;
            if (propertyAccessor == null)
            {
                return (BoundExpression)base.VisitAssignmentOperator(node);
            }

            // Rewrite property assignment into call to setter.
            var property = propertyAccessor.PropertySymbol.GetBaseProperty();
            var setMethod = property.SetMethod;
            Debug.Assert(setMethod != null);
            Debug.Assert(setMethod.Parameters.Count == 1);
            Debug.Assert(!setMethod.IsOverride);

            var rewrittenReceiver = (BoundExpression)Visit(propertyAccessor.ReceiverOpt);
            var rewrittenArgument = (BoundExpression)Visit(node.Right);

            if (used)
            {
                // Save expression value to a temporary before calling the
                // setter, and restore the temporary after the setter, so the
                // assignment can be used as an embedded expression.
                var exprType = rewrittenArgument.Type;
                var tempSymbol = new TempLocalSymbol(exprType, RefKind.None, containingSymbol);
                var tempLocal = new BoundLocal(null, null, tempSymbol, null, exprType);
                var saveTemp = new BoundAssignmentOperator(
                    null,
                    null,
                    tempLocal,
                    rewrittenArgument,
                    exprType);
                var call = BoundCall.SynthesizedCall(
                    rewrittenReceiver,
                    setMethod,
                    saveTemp);
                return new BoundSequence(
                    node.Syntax,
                    node.SyntaxTree,
                    ReadOnlyArray<LocalSymbol>.CreateFrom(tempSymbol),
                    ReadOnlyArray<BoundExpression>.CreateFrom(call),
                    tempLocal,
                    exprType);
            }
            else
            {
                return BoundCall.SynthesizedCall(
                    rewrittenReceiver,
                    setMethod,
                    rewrittenArgument);
            }
        }
 public override BoundNode VisitAssignmentOperator(BoundAssignmentOperator node)
 {
     // Assume value of expression is used.
     return VisitAssignmentOperator(node, used: true);
 }
Beispiel #22
0
        private void EmitAssignmentExpression(BoundAssignmentOperator assignmentOperator, UseKind useKind)
        {
            if (TryEmitAssignmentInPlace(assignmentOperator, useKind != UseKind.Unused))
            {
                Debug.Assert(assignmentOperator.RefKind == RefKind.None);
                return;
            }

            // Assignment expression codegen has the following parts:
            //
            // * PreRHS: We need to emit instructions before the load of the right hand side if:
            //   - If the left hand side is a ref local or ref formal parameter and the right hand 
            //     side is a value then we must put the ref on the stack early so that we can store 
            //     indirectly into it.
            //   - If the left hand side is an array slot then we must evaluate the array and indices
            //     before we evaluate the right hand side. We ensure that the array and indices are 
            //     on the stack when the store is executed.
            //   - Similarly, if the left hand side is a non-static field then its receiver must be
            //     evaluated before the right hand side.
            //
            // * RHS: There are three possible ways to do an assignment with respect to "refness", 
            //   and all are found in the lowering of:
            //
            //   N().s += 10;
            //
            //   That expression is realized as 
            //
            //   ref int addr = ref N().s;   // Assign a ref on the right hand side to the left hand side.
            //   int sum = addr + 10;        // No refs at all; assign directly to sum.
            //   addr = sum;                 // Assigns indirectly through the address.
            //
            //   - If we are in the first case then assignmentOperator.RefKind is Ref and the left hand side is a 
            //     ref local temporary. We simply assign the ref on the RHS to the storage on the LHS with no indirection.
            //
            //   - If we are in the second case then nothing is ref; we have a value on one side an a local on the other.
            //     Again, there is no indirection.
            // 
            //   - If we are in the third case then we have a ref on the left and a value on the right. We must compute the
            //     value of the right hand side and then store it into the left hand side.
            //
            // * Duplication: The result of an assignment operation is the value that was assigned. It is possible that 
            //   later codegen is expecting this value to be on the stack when we're done here. This is controlled by
            //   the "used" formal parameter. There are two possible cases:
            //   - If the preamble put stuff on the stack for the usage of the store, then we must not put an extra copy
            //     of the right hand side value on the stack; that will be between the value and the stuff needed to 
            //     do the storage. In that case we put the right hand side value in a temporary and restore it later.
            //   - Otherwise we can just do a dup instruction; there's nothing before the dup on the stack that we'll need.
            // 
            // * Storage: Either direct or indirect, depending. See the RHS section above for details.
            // 
            // * Post-storage: If we stashed away the duplicated value in the temporary, we need to restore it back to the stack.

            bool lhsUsesStack = EmitAssignmentPreamble(assignmentOperator);
            EmitAssignmentValue(assignmentOperator);
            LocalDefinition temp = EmitAssignmentDuplication(assignmentOperator, useKind, lhsUsesStack);
            EmitStore(assignmentOperator);
            EmitAssignmentPostfix(assignmentOperator, temp, useKind);
        }
        public override BoundNode VisitAssignmentOperator(BoundAssignmentOperator node)
        {
            BoundExpression originalLeft = node.Left;

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

            var leftLocal = (BoundLocal)originalLeft;

            BoundExpression originalRight = node.Right;

            if (leftLocal.LocalSymbol.RefKind != RefKind.None &&
                node.RefKind != RefKind.None &&
                NeedsProxy(leftLocal.LocalSymbol))
            {
                Debug.Assert(!proxies.ContainsKey(leftLocal.LocalSymbol));
                Debug.Assert(!IsStackAlloc(originalRight));
                //spilling ref local variables
                throw ExceptionUtilities.Unreachable;
            }

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

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

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

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

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

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

            return node.Update(rewrittenLeft, rewrittenRight, node.RefKind, rewrittenType);
        }
Beispiel #24
0
        private bool EmitAssignmentPreamble(BoundAssignmentOperator assignmentOperator)
        {
            var assignmentTarget = assignmentOperator.Left;
            bool lhsUsesStack = false;

            switch (assignmentTarget.Kind)
            {
                case BoundKind.RefValueOperator:
                    EmitRefValueAddress((BoundRefValueOperator)assignmentTarget);
                    break;

                case BoundKind.FieldAccess:
                    {
                        var left = (BoundFieldAccess)assignmentTarget;
                        if (!left.FieldSymbol.IsStatic)
                        {
                            var temp = EmitReceiverRef(left.ReceiverOpt);
                            Debug.Assert(temp == null, "temp is unexpected when assigning to a field");
                            lhsUsesStack = true;
                        }
                    }
                    break;

                case BoundKind.Parameter:
                    {
                        var left = (BoundParameter)assignmentTarget;
                        if (left.ParameterSymbol.RefKind != RefKind.None)
                        {
                            _builder.EmitLoadArgumentOpcode(ParameterSlot(left));
                            lhsUsesStack = true;
                        }
                    }
                    break;

                case BoundKind.Local:
                    {
                        var left = (BoundLocal)assignmentTarget;

                        // Again, consider our earlier case:
                        //
                        // ref int addr = ref N().s;
                        // int sum = addr + 10; 
                        // addr = sum;
                        //
                        // There are three different ways we could be assigning to a local.
                        //
                        // In the first case, we want to simply call N(), take the address
                        // of s, and then store that address in addr.
                        //
                        // In the second case again we simply want to compute the sum and
                        // store the result in sum.
                        //
                        // In the third case however we want to first load the contents of
                        // addr -- the address of field s -- then put the sum on the stack,
                        // and then do an indirect store. In that case we need to have the
                        // contents of addr on the stack.

                        if (left.LocalSymbol.RefKind != RefKind.None && assignmentOperator.RefKind == RefKind.None)
                        {
                            if (!IsStackLocal(left.LocalSymbol))
                            {
                                LocalDefinition localDefinition = GetLocal(left);
                                _builder.EmitLocalLoad(localDefinition);
                            }
                            else
                            {
                                // this is a case of indirect assignment to a stack temp.
                                // currently byref temp can only be a stack local in scenarios where 
                                // there is only one assignment and it is the last one. 
                                // I do not yet know how to support cases where we assign more than once. 
                                // That where Dup of LHS would be needed, but as a general scenario 
                                // it is not always possible to handle. Fortunately all the cases where we
                                // indirectly assign to a byref temp come from rewriter and all
                                // they all are write-once cases.
                                //
                                // For now analyzer asserts that indirect writes are final reads of 
                                // a ref local. And we never need a dup here.

                                // builder.EmitOpCode(ILOpCode.Dup);
                            }

                            lhsUsesStack = true;
                        }
                    }
                    break;

                case BoundKind.ArrayAccess:
                    {
                        var left = (BoundArrayAccess)assignmentTarget;
                        EmitExpression(left.Expression, used: true);
                        EmitArrayIndices(left.Indices);
                        lhsUsesStack = true;
                    }
                    break;

                case BoundKind.ThisReference:
                    {
                        var left = (BoundThisReference)assignmentTarget;

                        var temp = EmitAddress(left, AddressKind.Writeable);
                        Debug.Assert(temp == null, "taking ref of this should not create a temp");

                        lhsUsesStack = true;
                    }
                    break;

                case BoundKind.Dup:
                    {
                        var left = (BoundDup)assignmentTarget;

                        var temp = EmitAddress(left, AddressKind.Writeable);
                        Debug.Assert(temp == null, "taking ref of Dup should not create a temp");

                        lhsUsesStack = true;
                    }
                    break;

                case BoundKind.PointerIndirectionOperator:
                    {
                        var left = (BoundPointerIndirectionOperator)assignmentTarget;

                        EmitExpression(left.Operand, used: true);

                        lhsUsesStack = true;
                    }
                    break;

                case BoundKind.Sequence:
                    {
                        var sequence = (BoundSequence)assignmentTarget;

                        DefineLocals(sequence);
                        EmitSideEffects(sequence);

                        lhsUsesStack = EmitAssignmentPreamble(assignmentOperator.Update(sequence.Value, assignmentOperator.Right, assignmentOperator.RefKind, assignmentOperator.Type));

                        // doNotRelease will be released in EmitStore after we are done with the whole assignment.
                        var doNotRelease = DigForValueLocal(sequence);
                        FreeLocals(sequence, doNotRelease);
                    }
                    break;

                case BoundKind.Call:
                    {
                        var left = (BoundCall)assignmentTarget;

                        Debug.Assert(left.Method.RefKind != RefKind.None);
                        EmitCallExpression(left, UseKind.UsedAsAddress);

                        lhsUsesStack = true;
                    }
                    break;

                case BoundKind.PropertyAccess:
                case BoundKind.IndexerAccess:
                // Property access should have been rewritten.
                case BoundKind.PreviousSubmissionReference:
                    // Script references are lowered to a this reference and a field access.
                    throw ExceptionUtilities.UnexpectedValue(assignmentTarget.Kind);

                case BoundKind.PseudoVariable:
                    EmitPseudoVariableAddress((BoundPseudoVariable)assignmentTarget);
                    lhsUsesStack = true;
                    break;

                case BoundKind.ModuleVersionId:
                case BoundKind.InstrumentationPayloadRoot:
                    break;

                default:
                    throw ExceptionUtilities.UnexpectedValue(assignmentTarget.Kind);
            }

            return lhsUsesStack;
        }
Beispiel #25
0
 public override BoundNode VisitAssignmentOperator(BoundAssignmentOperator node)
 {
     // Assume value of expression is used.
     return(VisitAssignmentOperator(node, used: true));
 }
Beispiel #26
0
        private LocalDefinition EmitAssignmentDuplication(BoundAssignmentOperator assignmentOperator, UseKind useKind, bool lhsUsesStack)
        {
            LocalDefinition temp = null;
            if (useKind != UseKind.Unused)
            {
                _builder.EmitOpCode(ILOpCode.Dup);

                if (lhsUsesStack)
                {
                    // Today we sometimes have a case where we assign a ref directly to a temporary of ref type:
                    //
                    // ref int addr = ref N().y;  <-- copies the address by value; no indirection
                    // int sum = addr + 10;
                    // addr = sum;
                    //
                    // In "Redhawk" we can write this sort of code directly as well. However, we should
                    // never have a case where the value of the assignment is "used", either in our own
                    // lowering passes or in Redhawk. We never have something like:
                    //
                    // ref int t1 = (ref int t2 = ref M().s); 
                    //
                    // or the even more odd:
                    //
                    // int t1 = (ref int t2 = ref M().s);
                    //
                    // Therefore we don't have to worry about what if the temporary value we are stashing
                    // away is of ref type.
                    //
                    // If we ever do implement this sort of feature then we will need to figure out which
                    // of the situations above we are in, and ensure that the correct kind of temporary
                    // is created here. And also that either its value or its indirected value is read out
                    // after the store, in EmitAssignmentPostfix, below.

                    Debug.Assert(assignmentOperator.RefKind == RefKind.None);

                    temp = AllocateTemp(assignmentOperator.Left.Type, assignmentOperator.Left.Syntax);
                    _builder.EmitLocalStore(temp);
                }
            }
            return temp;
        }
 public override BoundNode VisitAssignmentOperator(BoundAssignmentOperator node)
 {
     return(this.SetMayHaveSideEffects());
 }
Beispiel #28
0
        private void EmitAssignmentPostfix(BoundAssignmentOperator assignment, LocalDefinition temp, UseKind useKind)
        {
            if (temp != null)
            {
                _builder.EmitLocalLoad(temp);
                FreeTemp(temp);
            }

            if (useKind == UseKind.UsedAsValue && assignment.RefKind != RefKind.None)
            {
                EmitLoadIndirect(assignment.Type, assignment.Syntax);
            }
        }
 public override BoundNode VisitAssignmentOperator(BoundAssignmentOperator node)
 {
     return this.SetMayHaveSideEffects();
 }