public override BoundNode VisitBinaryOperator(BoundBinaryOperator node) { BoundSpillSequence2 ss = null; var right = VisitExpression(ref ss, node.Right); BoundExpression left; if (ss == null) { left = VisitExpression(ref ss, node.Left); } else { var ssLeft = new BoundSpillSequence2(); left = VisitExpression(ref ssLeft, node.Left); left = Spill(ssLeft, left); if (node.OperatorKind == BinaryOperatorKind.LogicalBoolOr || node.OperatorKind == BinaryOperatorKind.LogicalBoolAnd) { ssLeft.Add(F.If( node.OperatorKind == BinaryOperatorKind.LogicalBoolAnd ? left : F.Not(left), UpdateStatement(ss, F.Assignment(left, right)) )); return(UpdateExpression(ssLeft, left)); } else { // if the right-hand-side has await, spill the left ssLeft.IncludeSequence(ss); ss = ssLeft; } } return(UpdateExpression(ss, node.Update(node.OperatorKind, left, right, node.ConstantValue, node.MethodOpt, node.ResultKind, node.Type))); }
public override BoundNode VisitAssignmentOperator(BoundAssignmentOperator node) { BoundSpillSequence2 ss = null; var right = VisitExpression(ref ss, node.Right); BoundExpression left; if (ss == null || node.Left.Kind == BoundKind.Local) { left = VisitExpression(ref ss, node.Left); } else { // if the right-hand-side has await, spill the left var ss2 = new BoundSpillSequence2(); left = VisitExpression(ref ss2, node.Left); if (left.Kind != BoundKind.Local) { left = Spill(ss2, left, RefKind.Ref); } ss2.IncludeSequence(ss); ss = ss2; } return(UpdateExpression(ss, node.Update(left, right, node.RefKind, node.Type))); }
public override BoundNode VisitArrayCreation(BoundArrayCreation node) { BoundSpillSequence2 ss = null; var init = (BoundArrayInitialization)VisitExpression(ref ss, node.InitializerOpt); ImmutableArray <BoundExpression> bounds; if (ss == null) { bounds = VisitExpressionList(ref ss, node.Bounds); } else { // spill bounds expressions if initializers contain await var ss2 = new BoundSpillSequence2(); bounds = VisitExpressionList(ref ss2, node.Bounds, forceSpill: true); ss2.IncludeSequence(ss); ss = ss2; } return(UpdateExpression(ss, node.Update(bounds, init, node.Type))); }
public override BoundNode VisitPointerElementAccess(BoundPointerElementAccess node) { BoundSpillSequence2 ss = null; var index = VisitExpression(ref ss, node.Index); BoundExpression expression; if (ss == null) { expression = VisitExpression(ref ss, node.Expression); } else { // if the right-hand-side has await, spill the left var ss2 = new BoundSpillSequence2(); expression = VisitExpression(ref ss2, node.Expression); expression = Spill(ss2, expression); ss2.IncludeSequence(ss); ss = ss2; } return(UpdateExpression(ss, node.Update(expression, index, node.Checked, node.Type))); }
public override BoundNode VisitCall(BoundCall node) { BoundSpillSequence2 ss = null; var arguments = this.VisitExpressionList(ref ss, node.Arguments, node.ArgumentRefKindsOpt); BoundExpression receiver = null; if (ss == null) { receiver = VisitExpression(ref ss, node.ReceiverOpt); } else if (!node.Method.IsStatic) { // spill the receiver if there were await expressions in the arguments var ss2 = new BoundSpillSequence2(); receiver = Spill(ss2, VisitExpression(ref ss2, node.ReceiverOpt), refKind: node.ReceiverOpt.Type.IsReferenceType ? RefKind.None : RefKind.Ref); ss2.IncludeSequence(ss); ss = ss2; } return(UpdateExpression(ss, node.Update(receiver, node.Method, arguments))); }
public override BoundNode VisitPointerElementAccess(BoundPointerElementAccess node) { BoundSpillSequence2 ss = null; var index = VisitExpression(ref ss, node.Index); BoundExpression expression; if (ss == null) { expression = VisitExpression(ref ss, node.Expression); } else { // if the right-hand-side has await, spill the left var ss2 = new BoundSpillSequence2(); expression = VisitExpression(ref ss2, node.Expression); expression = Spill(ss2, expression); ss2.IncludeSequence(ss); ss = ss2; } return UpdateExpression(ss, node.Update(expression, index, node.Checked, node.Type)); }
public override BoundNode VisitCall(BoundCall node) { BoundSpillSequence2 ss = null; var arguments = this.VisitExpressionList(ref ss, node.Arguments, node.ArgumentRefKindsOpt); BoundExpression receiver = null; if (ss == null) { receiver = VisitExpression(ref ss, node.ReceiverOpt); } else if (!node.Method.IsStatic) { // spill the receiver if there were await expressions in the arguments var ss2 = new BoundSpillSequence2(); receiver = Spill(ss2, VisitExpression(ref ss2, node.ReceiverOpt), refKind: node.ReceiverOpt.Type.IsReferenceType ? RefKind.None : RefKind.Ref); ss2.IncludeSequence(ss); ss = ss2; } return UpdateExpression(ss, node.Update(receiver, node.Method, arguments)); }
public override BoundNode VisitBinaryOperator(BoundBinaryOperator node) { BoundSpillSequence2 ss = null; var right = VisitExpression(ref ss, node.Right); BoundExpression left; if (ss == null) { left = VisitExpression(ref ss, node.Left); } else { var ssLeft = new BoundSpillSequence2(); left = VisitExpression(ref ssLeft, node.Left); left = Spill(ssLeft, left); if (node.OperatorKind == BinaryOperatorKind.LogicalBoolOr || node.OperatorKind == BinaryOperatorKind.LogicalBoolAnd) { ssLeft.Add(F.If( node.OperatorKind == BinaryOperatorKind.LogicalBoolAnd ? left : F.Not(left), UpdateStatement(ss, F.Assignment(left, right)) )); return UpdateExpression(ssLeft, left); } else { // if the right-hand-side has await, spill the left ssLeft.IncludeSequence(ss); ss = ssLeft; } } return UpdateExpression(ss, node.Update(node.OperatorKind, left, right, node.ConstantValue, node.MethodOpt, node.ResultKind, node.Type)); }
public override BoundNode VisitAssignmentOperator(BoundAssignmentOperator node) { BoundSpillSequence2 ss = null; var right = VisitExpression(ref ss, node.Right); BoundExpression left; if (ss == null || node.Left.Kind == BoundKind.Local) { left = VisitExpression(ref ss, node.Left); } else { // if the right-hand-side has await, spill the left var ss2 = new BoundSpillSequence2(); left = VisitExpression(ref ss2, node.Left); if (left.Kind != BoundKind.Local) left = Spill(ss2, left, RefKind.Ref); ss2.IncludeSequence(ss); ss = ss2; } return UpdateExpression(ss, node.Update(left, right, node.RefKind, node.Type)); }
public override BoundNode VisitArrayCreation(BoundArrayCreation node) { BoundSpillSequence2 ss = null; var init = (BoundArrayInitialization)VisitExpression(ref ss, node.InitializerOpt); ImmutableArray<BoundExpression> bounds; if (ss == null) { bounds = VisitExpressionList(ref ss, node.Bounds); } else { // spill bounds expressions if initializers contain await var ss2 = new BoundSpillSequence2(); bounds = VisitExpressionList(ref ss2, node.Bounds, forceSpill: true); ss2.IncludeSequence(ss); ss = ss2; } return UpdateExpression(ss, node.Update(bounds, init, node.Type)); }
private BoundExpression Spill( BoundSpillSequence2 spill, BoundExpression e, RefKind refKind = RefKind.None, bool sideEffectsOnly = false) { Debug.Assert(spill != null); while (true) { switch (e.Kind) { case BoundKind.ArrayInitialization: { Debug.Assert(refKind == RefKind.None); Debug.Assert(!sideEffectsOnly); var ai = (BoundArrayInitialization)e; var newInitializers = VisitExpressionList(ref spill, ai.Initializers, forceSpill: true); return ai.Update(newInitializers); } case BoundKind.ArgListOperator: { Debug.Assert(refKind == RefKind.None); Debug.Assert(!sideEffectsOnly); var al = (BoundArgListOperator)e; var newArgs = VisitExpressionList(ref spill, al.Arguments, al.ArgumentRefKindsOpt, forceSpill: true); return al.Update(newArgs, al.ArgumentRefKindsOpt, al.Type); } case SpillSequence2: { var ss = (BoundSpillSequence2)e; spill.IncludeSequence(ss); e = ss.Value; continue; } case BoundKind.Sequence: { var ss = (BoundSequence)e; spill.AddRange(ss.Locals); spill.AddRange(ss.SideEffects, MakeExpressionStatement); e = ss.Value; continue; } case BoundKind.ThisReference: case BoundKind.BaseReference: { if (refKind != RefKind.None || e.Type.IsReferenceType) return e; goto default; } case BoundKind.Parameter: { if (refKind != RefKind.None) return e; goto default; } case BoundKind.Local: { var local = (BoundLocal)e; if (writeOnceTemps.Contains(local.LocalSymbol) || refKind != RefKind.None) return local; goto default; } case BoundKind.FieldAccess: { var field = (BoundFieldAccess)e; if (field.FieldSymbol.IsReadOnly) { if (field.FieldSymbol.IsStatic) return field; if (field.FieldSymbol.ContainingType.IsValueType) goto default; // save the receiver; can get the field later. var receiver = Spill(spill, field.ReceiverOpt, (refKind != RefKind.None && field.FieldSymbol.Type.IsReferenceType) ? refKind : RefKind.None, sideEffectsOnly); return field.Update(receiver, field.FieldSymbol, field.ConstantValueOpt, field.ResultKind, field.Type); } goto default; } case BoundKind.Literal: case BoundKind.TypeExpression: return e; default: { if (e.Type.SpecialType == SpecialType.System_Void || sideEffectsOnly) { spill.Add(F.ExpressionStatement(e)); return null; } else { BoundAssignmentOperator assignToTemp; var replacement = F.StoreToTemp(e, out assignToTemp, refKind: refKind, kind: SynthesizedLocalKind.AwaitSpilledTemp); spill.Add(replacement.LocalSymbol); writeOnceTemps.Add(replacement.LocalSymbol); spill.Add(F.ExpressionStatement(assignToTemp)); return replacement; } } } } }
private BoundExpression Spill( BoundSpillSequence2 spill, BoundExpression e, RefKind refKind = RefKind.None, bool sideEffectsOnly = false) { Debug.Assert(spill != null); while (true) { switch (e.Kind) { case BoundKind.ArrayInitialization: { Debug.Assert(refKind == RefKind.None); Debug.Assert(!sideEffectsOnly); var ai = (BoundArrayInitialization)e; var newInitializers = VisitExpressionList(ref spill, ai.Initializers, forceSpill: true); return(ai.Update(newInitializers)); } case BoundKind.ArgListOperator: { Debug.Assert(refKind == RefKind.None); Debug.Assert(!sideEffectsOnly); var al = (BoundArgListOperator)e; var newArgs = VisitExpressionList(ref spill, al.Arguments, al.ArgumentRefKindsOpt, forceSpill: true); return(al.Update(newArgs, al.ArgumentRefKindsOpt, al.Type)); } case SpillSequence2: { var ss = (BoundSpillSequence2)e; spill.IncludeSequence(ss); e = ss.Value; continue; } case BoundKind.Sequence: { var ss = (BoundSequence)e; spill.AddRange(ss.Locals); spill.AddRange(ss.SideEffects, MakeExpressionStatement); e = ss.Value; continue; } case BoundKind.ThisReference: case BoundKind.BaseReference: { if (refKind != RefKind.None || e.Type.IsReferenceType) { return(e); } goto default; } case BoundKind.Parameter: { if (refKind != RefKind.None) { return(e); } goto default; } case BoundKind.Local: { var local = (BoundLocal)e; if (writeOnceTemps.Contains(local.LocalSymbol) || refKind != RefKind.None) { return(local); } goto default; } case BoundKind.FieldAccess: { var field = (BoundFieldAccess)e; if (field.FieldSymbol.IsReadOnly) { if (field.FieldSymbol.IsStatic) { return(field); } if (field.FieldSymbol.ContainingType.IsValueType) { goto default; } // save the receiver; can get the field later. var receiver = Spill(spill, field.ReceiverOpt, (refKind != RefKind.None && field.FieldSymbol.Type.IsReferenceType) ? refKind : RefKind.None, sideEffectsOnly); return(field.Update(receiver, field.FieldSymbol, field.ConstantValueOpt, field.ResultKind, field.Type)); } goto default; } case BoundKind.Literal: case BoundKind.TypeExpression: return(e); default: { if (e.Type.SpecialType == SpecialType.System_Void || sideEffectsOnly) { spill.Add(F.ExpressionStatement(e)); return(null); } else { BoundAssignmentOperator assignToTemp; var replacement = F.StoreToTemp(e, out assignToTemp, refKind: refKind, kind: SynthesizedLocalKind.AwaitSpilledTemp); spill.Add(replacement.LocalSymbol); writeOnceTemps.Add(replacement.LocalSymbol); spill.Add(F.ExpressionStatement(assignToTemp)); return(replacement); } } } } }