public override BoundNode VisitSequence(BoundSequence node) { BoundSpillSequenceBuilder valueBuilder = null; var value = VisitExpression(ref valueBuilder, node.Value); BoundSpillSequenceBuilder builder = null; var sideEffects = VisitExpressionList(ref builder, node.SideEffects, forceSpill: valueBuilder != null, sideEffectsOnly: true); if (builder == null && valueBuilder == null) { return(node.Update(node.Locals, sideEffects, value, node.Type)); } if (builder == null) { builder = new BoundSpillSequenceBuilder(); } PromoteAndAddLocals(builder, node.Locals); builder.AddExpressions(sideEffects); builder.Include(valueBuilder); return(builder.Update(value)); }
public override BoundNode VisitSequence(BoundSequence node) { AddAll(node.Locals); base.VisitSequence(node); RemoveAll(node.Locals); return(null); }
public override BoundNode VisitObjectCreationExpression(BoundObjectCreationExpression node) { Debug.Assert(node != null); // Rewrite the arguments. // NOTE: We may need additional argument rewriting such as generating a params array, // re-ordering arguments based on argsToParamsOpt map, inserting arguments for optional parameters, etc. // NOTE: This is done later by MakeArguments, for now we just lower each argument. var rewrittenArguments = VisitList(node.Arguments); // We have already lowered each argument, but we may need some additional rewriting for the arguments, // such as generating a params array, re-ordering arguments based on argsToParamsOpt map, inserting arguments for optional parameters, etc. ImmutableArray <LocalSymbol> temps; ImmutableArray <RefKind> argumentRefKindsOpt = node.ArgumentRefKindsOpt; rewrittenArguments = MakeArguments( node.Syntax, rewrittenArguments, node.Constructor, node.Constructor, node.Expanded, node.ArgsToParamsOpt, ref argumentRefKindsOpt, out temps); BoundExpression rewrittenObjectCreation; rewrittenObjectCreation = node.UpdateArgumentsAndInitializer(rewrittenArguments, argumentRefKindsOpt, newInitializerExpression: null, changeTypeOpt: node.Type); // replace "new S()" with a default struct ctor with "default(S)" if (node.Constructor.IsDefaultValueTypeConstructor()) { rewrittenObjectCreation = new BoundDefaultExpression(rewrittenObjectCreation.Syntax, rewrittenObjectCreation.Type); } if (!temps.IsDefaultOrEmpty) { rewrittenObjectCreation = new BoundSequence( node.Syntax, temps, ImmutableArray <BoundExpression> .Empty, rewrittenObjectCreation, node.Type); } if (node.Type.IsInterfaceType()) { rewrittenObjectCreation = MakeConversionNode(rewrittenObjectCreation, node.Type, false, false); } if (node.InitializerExpressionOpt == null || node.InitializerExpressionOpt.HasErrors) { return(rewrittenObjectCreation); } return(MakeObjectCreationWithInitializer(node.Syntax, rewrittenObjectCreation, node.InitializerExpressionOpt, node.Type)); }
public override BoundNode VisitSequence(BoundSequence node) { // Spilled local temps do not appear here in a sequence expression, because any temps in a // sequence expression that need to be spilled would have been moved up to the // statement level by the AwaitLiftingRewriter. foreach (var local in node.Locals) { Debug.Assert(!NeedsProxy(local) || proxies.ContainsKey(local)); } return(base.VisitSequence(node)); }
private BoundNode RewriteWithRefOperand( bool isPrefix, bool isChecked, ArrayBuilder <LocalSymbol> tempSymbols, ArrayBuilder <BoundExpression> tempInitializers, SyntaxNode syntax, BoundExpression operand, TypeSymbol operandType, BoundExpression boundTemp, BoundExpression newValue) { var tempValue = isPrefix ? newValue : MakeRValue(operand); var tempAssignment = MakeAssignmentOperator(syntax, boundTemp, tempValue, operandType, used: false, isChecked: isChecked, isCompoundAssignment: false); var operandValue = isPrefix ? boundTemp : newValue; var tempAssignedAndOperandValue = new BoundSequence( syntax, ImmutableArray <LocalSymbol> .Empty, ImmutableArray.Create <BoundExpression>(tempAssignment), operandValue, tempValue.Type); // prefix: operand = Seq{temp = (T)(operand + 1); temp;} // postfix: operand = Seq{temp = operand; ; (T)(temp + 1);} BoundExpression operandAssignment = MakeAssignmentOperator(syntax, operand, tempAssignedAndOperandValue, operandType, used: false, isChecked: isChecked, isCompoundAssignment: false); // prefix: Seq{operand initializers; operand = Seq{temp = (T)(operand + 1); temp;} result: temp} // postfix: Seq{operand initializers; operand = Seq{temp = operand; ; (T)(temp + 1);} result: temp} tempInitializers.Add(operandAssignment); return(new BoundSequence( syntax: syntax, locals: tempSymbols.ToImmutableAndFree(), sideEffects: tempInitializers.ToImmutableAndFree(), value: boundTemp, type: operandType)); }
private BoundExpression OptimizeLiftedUnaryOperator( UnaryOperatorKind operatorKind, SyntaxNode syntax, MethodSymbol method, BoundExpression loweredOperand, TypeSymbol type) { if (NullableNeverHasValue(loweredOperand)) { return(new BoundDefaultExpression(syntax, null, type)); } // Second, another simple optimization. If we know that the operand is never null // then we can obtain the non-null value and skip generating the temporary. That is, // "~(new int?(M()))" is the same as "new int?(~M())". BoundExpression neverNull = NullableAlwaysHasValue(loweredOperand); if (neverNull != null) { return(GetLiftedUnaryOperatorConsequence(operatorKind, syntax, method, type, neverNull)); } var conditionalLeft = loweredOperand as BoundLoweredConditionalAccess; // NOTE: we could in theory handle side-effecting loweredRight here too // by including it as a part of whenNull, but there is a concern // that it can lead to code duplication var optimize = conditionalLeft != null && (conditionalLeft.WhenNullOpt == null || conditionalLeft.WhenNullOpt.IsDefaultValue()); if (optimize) { var result = LowerLiftedUnaryOperator(operatorKind, syntax, method, conditionalLeft.WhenNotNull, type); return(conditionalLeft.Update( conditionalLeft.Receiver, conditionalLeft.HasValueMethodOpt, whenNotNull: result, whenNullOpt: null, id: conditionalLeft.Id, type: result.Type )); } // This optimization is analogous to DistributeLiftedConversionIntoLiftedOperand. // Suppose we have a lifted unary conversion whose operand is itself a lifted operation. // That is, we have something like: // // int? r = - (M() + N()); // // where M() and N() return nullable ints. We would simply codegen this as first // creating the nullable int result of M() + N(), then checking it for nullity, // and then doing the unary minus. That is: // // int? m = M(); // int? n = N(); // int? t = m.HasValue && n.HasValue ? new int?(m.Value + n.Value) : new int?(); // int? r = t.HasValue ? new int?(-t.Value) : new int?(); // // However, we also observe that we can distribute the unary minus into both branches of // the conditional: // // int? m = M(); // int? n = N(); // int? r = m.HasValue && n.HasValue ? - (new int?(m.Value + n.Value))) : - new int?(); // // And we already optimize those! So we could reduce this to: // // int? m = M(); // int? n = N(); // int? r = m.HasValue && n.HasValue ? new int?(- (m.Value + n.Value)) : new int?()); // // which avoids entirely the creation of the unnecessary nullable int and the unnecessary // extra null check. if (loweredOperand.Kind == BoundKind.Sequence) { BoundSequence seq = (BoundSequence)loweredOperand; if (seq.Value.Kind == BoundKind.ConditionalOperator) { BoundConditionalOperator conditional = (BoundConditionalOperator)seq.Value; Debug.Assert(TypeSymbol.Equals(seq.Type, conditional.Type, TypeCompareKind.ConsiderEverything2)); Debug.Assert(TypeSymbol.Equals(conditional.Type, conditional.Consequence.Type, TypeCompareKind.ConsiderEverything2)); Debug.Assert(TypeSymbol.Equals(conditional.Type, conditional.Alternative.Type, TypeCompareKind.ConsiderEverything2)); if (NullableAlwaysHasValue(conditional.Consequence) != null && NullableNeverHasValue(conditional.Alternative)) { return(new BoundSequence( syntax, seq.Locals, seq.SideEffects, RewriteConditionalOperator( syntax, conditional.Condition, MakeUnaryOperator(operatorKind, syntax, method, conditional.Consequence, type), MakeUnaryOperator(operatorKind, syntax, method, conditional.Alternative, type), ConstantValue.NotAvailable, type, isRef: false), type)); } } } return(null); }
private BoundExpression VisitCompoundAssignmentOperator(BoundCompoundAssignmentOperator node, bool used) { Debug.Assert(TypeSymbol.Equals(node.Right.Type, node.Operator.RightType, TypeCompareKind.ConsiderEverything2)); BoundExpression loweredRight = VisitExpression(node.Right); var temps = ArrayBuilder <LocalSymbol> .GetInstance(); var stores = ArrayBuilder <BoundExpression> .GetInstance(); var kind = node.Operator.Kind; bool isChecked = kind.IsChecked(); bool isDynamic = kind.IsDynamic(); var binaryOperator = kind.Operator(); // This will be filled in with the LHS that uses temporaries to prevent // double-evaluation of side effects. BoundExpression transformedLHS = TransformCompoundAssignmentLHS(node.Left, stores, temps, isDynamic); var lhsRead = MakeRValue(transformedLHS); BoundExpression rewrittenAssignment; if (node.Left.Kind == BoundKind.DynamicMemberAccess && (binaryOperator == BinaryOperatorKind.Addition || binaryOperator == BinaryOperatorKind.Subtraction)) { // If this could be an event assignment at runtime, we need to rewrite to the following form: // Original: // receiver.EV += handler // Rewritten: // dynamic memberAccessReceiver = receiver; // bool isEvent = Runtime.IsEvent(memberAccessReceiver, "EV"); // dynamic storeNonEvent = !isEvent ? memberAccessReceiver.EV : null; // var loweredRight = handler; // Only necessary if handler can change values, or is something like a lambda // isEvent ? add_Event(memberAccessReceiver, "EV", loweredRight) : transformedLHS = storeNonEvent + loweredRight; // // This is to ensure that if handler is something like a lambda, we evaluate fully evaluate the left // side before storing the lambda to a temp for use in both possible branches. // The first store to memberAccessReceiver has already been taken care of above by TransformCompoundAssignmentLHS var eventTemps = ArrayBuilder <LocalSymbol> .GetInstance(); var sequence = ArrayBuilder <BoundExpression> .GetInstance(); // dynamic memberAccessReceiver = receiver; var memberAccess = (BoundDynamicMemberAccess)transformedLHS; // bool isEvent = Runtime.IsEvent(memberAccessReceiver, "EV"); var isEvent = _factory.StoreToTemp(_dynamicFactory.MakeDynamicIsEventTest(memberAccess.Name, memberAccess.Receiver).ToExpression(), out BoundAssignmentOperator isEventAssignment); eventTemps.Add(isEvent.LocalSymbol); sequence.Add(isEventAssignment); // dynamic storeNonEvent = !isEvent ? memberAccessReceiver.EV : null; lhsRead = _factory.StoreToTemp(lhsRead, out BoundAssignmentOperator receiverAssignment); eventTemps.Add(((BoundLocal)lhsRead).LocalSymbol); var storeNonEvent = _factory.StoreToTemp(_factory.Conditional(_factory.Not(isEvent), receiverAssignment, _factory.Null(receiverAssignment.Type), receiverAssignment.Type), out BoundAssignmentOperator nonEventStore); eventTemps.Add(storeNonEvent.LocalSymbol); sequence.Add(nonEventStore); // var loweredRight = handler; if (CanChangeValueBetweenReads(loweredRight)) { loweredRight = _factory.StoreToTemp(loweredRight, out BoundAssignmentOperator possibleHandlerAssignment); eventTemps.Add(((BoundLocal)loweredRight).LocalSymbol); sequence.Add(possibleHandlerAssignment); } // add_Event(t1, "add_EV"); var invokeEventAccessor = _dynamicFactory.MakeDynamicEventAccessorInvocation( (binaryOperator == BinaryOperatorKind.Addition ? "add_" : "remove_") + memberAccess.Name, memberAccess.Receiver, loweredRight); // transformedLHS = storeNonEvent + loweredRight rewrittenAssignment = rewriteAssignment(lhsRead); // Final conditional var condition = _factory.Conditional(isEvent, invokeEventAccessor.ToExpression(), rewrittenAssignment, rewrittenAssignment.Type); rewrittenAssignment = new BoundSequence(node.Syntax, eventTemps.ToImmutableAndFree(), sequence.ToImmutableAndFree(), condition, condition.Type); } else { rewrittenAssignment = rewriteAssignment(lhsRead); } BoundExpression result = (temps.Count == 0 && stores.Count == 0) ? rewrittenAssignment : new BoundSequence( node.Syntax, temps.ToImmutable(), stores.ToImmutable(), rewrittenAssignment, rewrittenAssignment.Type); temps.Free(); stores.Free(); return(result); BoundExpression rewriteAssignment(BoundExpression leftRead) { SyntaxNode syntax = node.Syntax; // 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 = isDynamic ? leftRead : MakeConversionNode( syntax: syntax, rewrittenOperand: leftRead, conversion: node.LeftConversion, rewrittenType: node.Operator.LeftType, @checked: isChecked); BoundExpression operand = MakeBinaryOperator(syntax, node.Operator.Kind, opLHS, loweredRight, node.Operator.ReturnType, node.Operator.Method, isCompoundAssignment: true); BoundExpression opFinal = MakeConversionNode( syntax: syntax, rewrittenOperand: operand, conversion: node.FinalConversion, rewrittenType: node.Left.Type, explicitCastInCode: isDynamic, @checked: isChecked); return(MakeAssignmentOperator(syntax, transformedLHS, opFinal, node.Left.Type, used: used, isChecked: isChecked, isCompoundAssignment: true)); } }