/// <summary> /// Spill a list of expressions (e.g. the arguments of a method call). /// /// The expressions are processed right-to-left. Once an expression has been found that contains an await /// expression, all subsequent expressions are spilled. /// /// Example: /// /// (1 + 2, await t1, Foo(), await t2, 3 + 4) /// /// becomes: /// /// Spill( /// spill1 = 1 + 2, /// spill2 = await t1, /// spill3 = Foo(), /// (spill1, spill2, spill3, await t2, 3 + 4)) /// /// NOTE: Consider nested array initializers: /// /// new int[] { /// { 1, await t1 }, /// { 3, await t2 } /// } /// /// If the arguments of the top-level initializer had already been spilled, we would end up trying to spill /// something like this: /// /// new int[] { /// Spill( /// spill1 = 1, /// { spill1, await t1 }), /// Spill( /// spill2 = 3, /// { spill2, await t2 }) /// } /// /// The normal rewriting would produce: /// /// Spill( /// spill1 = 1, /// spill3 = { spill1, await t1 }, /// spill2 = 3, /// int[] a = new int[] { /// spill3, /// { spill2, await t2 })) /// /// Which is invalid, because spill3 does not have a type. /// /// To solve this problem the expression list spilled descends into nested array initializers. /// /// </summary> private ReadOnlyArray <BoundExpression> SpillExpressionList( SpillBuilder outerSpillBuilder, ReadOnlyArray <BoundExpression> expressions, ReadOnlyArray <RefKind> refKindsOpt = default(ReadOnlyArray <RefKind>)) { var spillBuilders = ArrayBuilder <SpillBuilder> .GetInstance(); bool spilledFirstArg = false; ReadOnlyArray <BoundExpression> newArgs = SpillArgumentListInner(expressions, refKindsOpt, spillBuilders, ref spilledFirstArg); var spillBuilder = new SpillBuilder(); spillBuilders.Reverse(); foreach (var spill in spillBuilders) { spillBuilder.AddSpill(spill); spill.Free(); } spillBuilders.Free(); outerSpillBuilder.AddSpill(spillBuilder); spillBuilder.Free(); return(newArgs); }
private BoundNode SpillAssignmentOperator(BoundAssignmentOperator node, BoundExpression left, BoundSpillSequence right) { var spillBuilder = new SpillBuilder(); var spilledLeftNode = SpillLValue(left, spillBuilder); var innerSpill = node.Update(spilledLeftNode, right.Value, node.RefKind, node.Type); spillBuilder.AddSpill(right); return spillBuilder.BuildSequenceAndFree(F, innerSpill); }
private BoundNode SpillAssignmentOperator(BoundAssignmentOperator node, BoundExpression left, BoundSpillSequence right) { var spillBuilder = new SpillBuilder(); var spilledLeftNode = SpillLValue(left, spillBuilder); var innerSpill = node.Update(spilledLeftNode, right.Value, node.RefKind, node.Type); spillBuilder.AddSpill(right); return(spillBuilder.BuildSequenceAndFree(F, innerSpill)); }
public override BoundNode VisitObjectCreationExpression(BoundObjectCreationExpression node) { ReadOnlyArray <BoundExpression> arguments = (ReadOnlyArray <BoundExpression>) this.VisitList(node.Arguments); BoundExpression initializerExpressionOpt = (BoundExpression)this.Visit(node.InitializerExpressionOpt); TypeSymbol type = this.VisitType(node.Type); if (!RequiresSpill(arguments) && (initializerExpressionOpt == null || initializerExpressionOpt.Kind != BoundKind.SpillSequence)) { return(node.Update(node.Constructor, arguments, node.ArgumentNamesOpt, node.ArgumentRefKindsOpt, node.Expanded, node.ArgsToParamsOpt, node.ConstantValueOpt, initializerExpressionOpt, type)); } var spillBuilder = new SpillBuilder(); ReadOnlyArray <BoundExpression> newArguments = SpillExpressionList(spillBuilder, arguments); BoundExpression newInitializerExpressionOpt; if (initializerExpressionOpt != null && initializerExpressionOpt.Kind == BoundKind.SpillSequence) { var spill = (BoundSpillSequence)initializerExpressionOpt; spillBuilder.AddSpill(spill); newInitializerExpressionOpt = spill.Value; } else { newInitializerExpressionOpt = initializerExpressionOpt; } BoundObjectCreationExpression newObjectCreation = node.Update( node.Constructor, newArguments, node.ArgumentNamesOpt, node.ArgumentRefKindsOpt, node.Expanded, node.ArgsToParamsOpt, node.ConstantValueOpt, newInitializerExpressionOpt, type); return(spillBuilder.BuildSequenceAndFree(F, newObjectCreation)); }
public override BoundNode VisitConditionalOperator(BoundConditionalOperator node) { BoundExpression condition = (BoundExpression)this.Visit(node.Condition); BoundExpression consequence = (BoundExpression)this.Visit(node.Consequence); BoundExpression alternative = (BoundExpression)this.Visit(node.Alternative); TypeSymbol type = this.VisitType(node.Type); if (!RequiresSpill(condition, consequence, alternative)) { return(node.Update(condition, consequence, alternative, node.ConstantValueOpt, type)); } var spillBuilder = new SpillBuilder(); LocalSymbol resultLocal = F.SynthesizedLocal(type, null); spillBuilder.Locals.Add(resultLocal); BoundExpression newCondition; if (condition.Kind == BoundKind.SpillSequence) { var spill = (BoundSpillSequence)condition; spillBuilder.AddSpill(spill); newCondition = spill.Value; } else { newCondition = condition; } spillBuilder.Statements.Add( F.If( condition: newCondition, thenClause: CrushExpression(consequence, resultLocal), elseClause: CrushExpression(alternative, resultLocal))); return(spillBuilder.BuildSequenceAndFree(F, F.Local(resultLocal))); }
public override BoundNode VisitSequence(BoundSequence node) { ReadOnlyArray <BoundExpression> sideEffects = (ReadOnlyArray <BoundExpression>) this.VisitList(node.SideEffects); BoundExpression value = (BoundExpression)this.Visit(node.Value); TypeSymbol type = this.VisitType(node.Type); if (!RequiresSpill(sideEffects) && value.Kind != BoundKind.SpillSequence) { return(node.Update(node.Locals, sideEffects, value, type)); } var spillBuilder = new SpillBuilder(); spillBuilder.Locals.AddRange(node.Locals); foreach (var sideEffect in sideEffects) { spillBuilder.Statements.Add( (sideEffect.Kind == BoundKind.SpillSequence) ? RewriteSpillSequenceAsBlock((BoundSpillSequence)sideEffect) : F.ExpressionStatement(sideEffect)); } BoundExpression newValue; if (value.Kind == BoundKind.SpillSequence) { var awaitEffect = (BoundSpillSequence)value; spillBuilder.AddSpill(awaitEffect); newValue = awaitEffect.Value; } else { newValue = value; } return(spillBuilder.BuildSequenceAndFree(F, newValue)); }
public override BoundNode VisitSequence(BoundSequence node) { ReadOnlyArray<BoundExpression> sideEffects = (ReadOnlyArray<BoundExpression>)this.VisitList(node.SideEffects); BoundExpression value = (BoundExpression)this.Visit(node.Value); TypeSymbol type = this.VisitType(node.Type); if (!RequiresSpill(sideEffects) && value.Kind != BoundKind.SpillSequence) { return node.Update(node.Locals, sideEffects, value, type); } var spillBuilder = new SpillBuilder(); spillBuilder.Locals.AddRange(node.Locals); foreach (var sideEffect in sideEffects) { spillBuilder.Statements.Add( (sideEffect.Kind == BoundKind.SpillSequence) ? RewriteSpillSequenceAsBlock((BoundSpillSequence)sideEffect) : F.ExpressionStatement(sideEffect)); } BoundExpression newValue; if (value.Kind == BoundKind.SpillSequence) { var awaitEffect = (BoundSpillSequence)value; spillBuilder.AddSpill(awaitEffect); newValue = awaitEffect.Value; } else { newValue = value; } return spillBuilder.BuildSequenceAndFree(F, newValue); }
/// <summary> /// Spill a list of expressions (e.g. the arguments of a method call). /// /// The expressions are processed right-to-left. Once an expression has been found that contains an await /// expression, all subsequent expressions are spilled. /// /// Example: /// /// (1 + 2, await t1, Foo(), await t2, 3 + 4) /// /// becomes: /// /// Spill( /// spill1 = 1 + 2, /// spill2 = await t1, /// spill3 = Foo(), /// (spill1, spill2, spill3, await t2, 3 + 4)) /// /// NOTE: Consider nested array initializers: /// /// new int[] { /// { 1, await t1 }, /// { 3, await t2 } /// } /// /// If the arguments of the top-level initializer had already been spilled, we would end up trying to spill /// something like this: /// /// new int[] { /// Spill( /// spill1 = 1, /// { spill1, await t1 }), /// Spill( /// spill2 = 3, /// { spill2, await t2 }) /// } /// /// The normal rewriting would produce: /// /// Spill( /// spill1 = 1, /// spill3 = { spill1, await t1 }, /// spill2 = 3, /// int[] a = new int[] { /// spill3, /// { spill2, await t2 })) /// /// Which is invalid, because spill3 does not have a type. /// /// To solve this problem the expression list spilled descends into nested array initializers. /// /// </summary> private ReadOnlyArray<BoundExpression> SpillExpressionList( SpillBuilder outerSpillBuilder, ReadOnlyArray<BoundExpression> expressions, ReadOnlyArray<RefKind> refKindsOpt = default(ReadOnlyArray<RefKind>)) { var spillBuilders = ArrayBuilder<SpillBuilder>.GetInstance(); bool spilledFirstArg = false; ReadOnlyArray<BoundExpression> newArgs = SpillArgumentListInner(expressions, refKindsOpt, spillBuilders, ref spilledFirstArg); var spillBuilder = new SpillBuilder(); spillBuilders.Reverse(); foreach (var spill in spillBuilders) { spillBuilder.AddSpill(spill); spill.Free(); } spillBuilders.Free(); outerSpillBuilder.AddSpill(spillBuilder); spillBuilder.Free(); return newArgs; }
public override BoundNode VisitConditionalOperator(BoundConditionalOperator node) { BoundExpression condition = (BoundExpression)this.Visit(node.Condition); BoundExpression consequence = (BoundExpression)this.Visit(node.Consequence); BoundExpression alternative = (BoundExpression)this.Visit(node.Alternative); TypeSymbol type = this.VisitType(node.Type); if (!RequiresSpill(condition, consequence, alternative)) { return node.Update(condition, consequence, alternative, node.ConstantValueOpt, type); } var spillBuilder = new SpillBuilder(); LocalSymbol resultLocal = F.SynthesizedLocal(type, null); spillBuilder.Locals.Add(resultLocal); BoundExpression newCondition; if (condition.Kind == BoundKind.SpillSequence) { var spill = (BoundSpillSequence)condition; spillBuilder.AddSpill(spill); newCondition = spill.Value; } else { newCondition = condition; } spillBuilder.Statements.Add( F.If( condition: newCondition, thenClause: CrushExpression(consequence, resultLocal), elseClause: CrushExpression(alternative, resultLocal))); return spillBuilder.BuildSequenceAndFree(F, F.Local(resultLocal)); }
public override BoundNode VisitObjectCreationExpression(BoundObjectCreationExpression node) { ReadOnlyArray<BoundExpression> arguments = (ReadOnlyArray<BoundExpression>)this.VisitList(node.Arguments); BoundExpression initializerExpressionOpt = (BoundExpression)this.Visit(node.InitializerExpressionOpt); TypeSymbol type = this.VisitType(node.Type); if (!RequiresSpill(arguments) && (initializerExpressionOpt == null || initializerExpressionOpt.Kind != BoundKind.SpillSequence)) { return node.Update(node.Constructor, arguments, node.ArgumentNamesOpt, node.ArgumentRefKindsOpt, node.Expanded, node.ArgsToParamsOpt, node.ConstantValueOpt, initializerExpressionOpt, type); } var spillBuilder = new SpillBuilder(); ReadOnlyArray<BoundExpression> newArguments = SpillExpressionList(spillBuilder, arguments); BoundExpression newInitializerExpressionOpt; if (initializerExpressionOpt != null && initializerExpressionOpt.Kind == BoundKind.SpillSequence) { var spill = (BoundSpillSequence)initializerExpressionOpt; spillBuilder.AddSpill(spill); newInitializerExpressionOpt = spill.Value; } else { newInitializerExpressionOpt = initializerExpressionOpt; } BoundObjectCreationExpression newObjectCreation = node.Update( node.Constructor, newArguments, node.ArgumentNamesOpt, node.ArgumentRefKindsOpt, node.Expanded, node.ArgsToParamsOpt, node.ConstantValueOpt, newInitializerExpressionOpt, type); return spillBuilder.BuildSequenceAndFree(F, newObjectCreation); }
private ReadOnlyArray<BoundExpression> SpillArgumentListInner( ReadOnlyArray<BoundExpression> arguments, ReadOnlyArray<RefKind> refKindsOpt, ArrayBuilder<SpillBuilder> spillBuilders, ref bool spilledFirstArg) { var newArgsBuilder = ArrayBuilder<BoundExpression>.GetInstance(); var refKindIterator = refKindsOpt != null ? refKindsOpt.AsReverseEnumerable().GetEnumerator() : null; foreach (var arg in arguments.AsReverseEnumerable()) { RefKind refKind = (refKindIterator == null) ? RefKind.None : (refKindIterator.MoveNext()) ? refKindIterator.Current : RefKind.None; if (arg.Kind == BoundKind.ArrayInitialization) { // Descend into a nested array initializer: var nestedInitializer = ((BoundArrayInitialization)arg); var newInitializers = SpillArgumentListInner(nestedInitializer.Initializers, ReadOnlyArray<RefKind>.Empty, spillBuilders, ref spilledFirstArg); newArgsBuilder.Add(nestedInitializer.Update(newInitializers)); continue; } if (arg.Kind == BoundKind.ArgListOperator) { // Descend into arglist: var argList = (BoundArgListOperator)arg; var newArgs = SpillArgumentListInner(argList.Arguments, argList.ArgumentRefKindsOpt, spillBuilders, ref spilledFirstArg); newArgsBuilder.Add(argList.Update(newArgs, argList.ArgumentRefKindsOpt, argList.Type)); continue; } var spillBuilder = new SpillBuilder(); BoundExpression newExpression; if (!spilledFirstArg) { if (arg.Kind == BoundKind.SpillSequence) { // We have found the right-most expression containing an await expression. Save the await // result to a temp local spilledFirstArg = true; var spill = (BoundSpillSequence)arg; spillBuilder.AddSpill(spill); newExpression = spill.Value; } else { // We are to the right of any await-containing expressions. The args do not yet need to be // spilled. newExpression = arg; } } else { // We are to the left of an await-containing expression. Spill the arg. if (Unspillable(arg) || (arg.Kind == BoundKind.FieldAccess && Unspillable(((BoundFieldAccess)arg).ReceiverOpt))) { newExpression = arg; } else if (refKind != RefKind.None) { newExpression = SpillLValue(arg, spillBuilder); } else { var spillTemp = F.SpillTemp(arg.Type, arg); spillBuilder.Temps.Add(spillTemp); spillBuilder.Statements.Add(GenerateSpillInit(spillTemp)); newExpression = spillTemp; } } newArgsBuilder.Add(newExpression); spillBuilders.Add(spillBuilder); } newArgsBuilder.Reverse(); return newArgsBuilder.ToReadOnlyAndFree(); }
private BoundExpression SpillLValue(BoundExpression left, SpillBuilder spillBuilder) { switch (left.Kind) { case BoundKind.Sequence: { var sequence = (BoundSequence)left; spillBuilder.AddSequence(F, sequence); return(SpillLValue(sequence.Value, spillBuilder)); } case BoundKind.SpillSequence: { var spill = (BoundSpillSequence)left; spillBuilder.AddSpill(spill); return(SpillLValue(spill.Value, spillBuilder)); } case BoundKind.ArrayAccess: { // Case 3.b - var array = (BoundArrayAccess)left; var spillReceiver = F.SpillTemp(array.Expression.Type, array.Expression); spillBuilder.Statements.Add(GenerateSpillInit(spillReceiver)); spillBuilder.Temps.Add(spillReceiver); var spilledIndices = ArrayBuilder <BoundExpression> .GetInstance(); foreach (var index in array.Indices) { var indexTemp = F.SpillTemp(index.Type, index); spillBuilder.Statements.Add(GenerateSpillInit(indexTemp)); spillBuilder.Temps.Add(indexTemp); spilledIndices.Add(indexTemp); } return(array.Update(spillReceiver, spilledIndices.ToReadOnlyAndFree(), array.Type)); } case BoundKind.FieldAccess: { // Case 3.c - var field = (BoundFieldAccess)left; if (Unspillable(field.ReceiverOpt)) { return(field); } BoundExpression newReceiver; if (field.ReceiverOpt.Type.IsReferenceType) { var receiverTemp = F.SpillTemp(field.ReceiverOpt.Type, field.ReceiverOpt); spillBuilder.Statements.Add(GenerateSpillInit(receiverTemp)); spillBuilder.Temps.Add(receiverTemp); newReceiver = receiverTemp; } else { Debug.Assert(field.ReceiverOpt.Type.IsValueType, "Don't spill unconstrained type parameters."); newReceiver = SpillLValue(field.ReceiverOpt, spillBuilder); } return(field.Update( newReceiver, field.FieldSymbol, field.ConstantValueOpt, field.ResultKind, field.Type)); } case BoundKind.Local: { // Case 3.d - return(left); } default: throw new NotImplementedException("stack spilling for lvalue: " + left.Kind.ToString()); } }
private BoundExpression SpillLValue(BoundExpression left, SpillBuilder spillBuilder) { switch (left.Kind) { case BoundKind.Sequence: { var sequence = (BoundSequence)left; spillBuilder.AddSequence(F, sequence); return SpillLValue(sequence.Value, spillBuilder); } case BoundKind.SpillSequence: { var spill = (BoundSpillSequence)left; spillBuilder.AddSpill(spill); return SpillLValue(spill.Value, spillBuilder); } case BoundKind.ArrayAccess: { // Case 3.b - var array = (BoundArrayAccess)left; var spillReceiver = F.SpillTemp(array.Expression.Type, array.Expression); spillBuilder.Statements.Add(GenerateSpillInit(spillReceiver)); spillBuilder.Temps.Add(spillReceiver); var spilledIndices = ArrayBuilder<BoundExpression>.GetInstance(); foreach (var index in array.Indices) { var indexTemp = F.SpillTemp(index.Type, index); spillBuilder.Statements.Add(GenerateSpillInit(indexTemp)); spillBuilder.Temps.Add(indexTemp); spilledIndices.Add(indexTemp); } return array.Update(spillReceiver, spilledIndices.ToReadOnlyAndFree(), array.Type); } case BoundKind.FieldAccess: { // Case 3.c - var field = (BoundFieldAccess)left; if (Unspillable(field.ReceiverOpt)) { return field; } BoundExpression newReceiver; if (field.ReceiverOpt.Type.IsReferenceType) { var receiverTemp = F.SpillTemp(field.ReceiverOpt.Type, field.ReceiverOpt); spillBuilder.Statements.Add(GenerateSpillInit(receiverTemp)); spillBuilder.Temps.Add(receiverTemp); newReceiver = receiverTemp; } else { Debug.Assert(field.ReceiverOpt.Type.IsValueType, "Don't spill unconstrained type parameters."); newReceiver = SpillLValue(field.ReceiverOpt, spillBuilder); } return field.Update( newReceiver, field.FieldSymbol, field.ConstantValueOpt, field.ResultKind, field.Type); } case BoundKind.Local: { // Case 3.d - return left; } default: throw new NotImplementedException("stack spilling for lvalue: " + left.Kind.ToString()); } }
private ReadOnlyArray <BoundExpression> SpillArgumentListInner( ReadOnlyArray <BoundExpression> arguments, ReadOnlyArray <RefKind> refKindsOpt, ArrayBuilder <SpillBuilder> spillBuilders, ref bool spilledFirstArg) { var newArgsBuilder = ArrayBuilder <BoundExpression> .GetInstance(); var refKindIterator = refKindsOpt != null?refKindsOpt.AsReverseEnumerable().GetEnumerator() : null; foreach (var arg in arguments.AsReverseEnumerable()) { RefKind refKind = (refKindIterator == null) ? RefKind.None : (refKindIterator.MoveNext()) ? refKindIterator.Current : RefKind.None; if (arg.Kind == BoundKind.ArrayInitialization) { // Descend into a nested array initializer: var nestedInitializer = ((BoundArrayInitialization)arg); var newInitializers = SpillArgumentListInner(nestedInitializer.Initializers, ReadOnlyArray <RefKind> .Empty, spillBuilders, ref spilledFirstArg); newArgsBuilder.Add(nestedInitializer.Update(newInitializers)); continue; } if (arg.Kind == BoundKind.ArgListOperator) { // Descend into arglist: var argList = (BoundArgListOperator)arg; var newArgs = SpillArgumentListInner(argList.Arguments, argList.ArgumentRefKindsOpt, spillBuilders, ref spilledFirstArg); newArgsBuilder.Add(argList.Update(newArgs, argList.ArgumentRefKindsOpt, argList.Type)); continue; } var spillBuilder = new SpillBuilder(); BoundExpression newExpression; if (!spilledFirstArg) { if (arg.Kind == BoundKind.SpillSequence) { // We have found the right-most expression containing an await expression. Save the await // result to a temp local spilledFirstArg = true; var spill = (BoundSpillSequence)arg; spillBuilder.AddSpill(spill); newExpression = spill.Value; } else { // We are to the right of any await-containing expressions. The args do not yet need to be // spilled. newExpression = arg; } } else { // We are to the left of an await-containing expression. Spill the arg. if (Unspillable(arg) || (arg.Kind == BoundKind.FieldAccess && Unspillable(((BoundFieldAccess)arg).ReceiverOpt))) { newExpression = arg; } else if (refKind != RefKind.None) { newExpression = SpillLValue(arg, spillBuilder); } else { var spillTemp = F.SpillTemp(arg.Type, arg); spillBuilder.Temps.Add(spillTemp); spillBuilder.Statements.Add(GenerateSpillInit(spillTemp)); newExpression = spillTemp; } } newArgsBuilder.Add(newExpression); spillBuilders.Add(spillBuilder); } newArgsBuilder.Reverse(); return(newArgsBuilder.ToReadOnlyAndFree()); }